/* * This is so ripe with races that you should *really* not touch this * unless you know exactly what you are doing. All the changes have to be * made atomically, or there may be incorrect pointers all over the place. */ static int init_dev(int dev) { struct tty_struct *tty, *o_tty; struct termios *tp, *o_tp, *ltp, *o_ltp; int retval; int o_dev; o_dev = PTY_OTHER(dev); tty = o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; repeat: retval = -EAGAIN; if (IS_A_PTY_MASTER(dev) && tty_table[dev] && tty_table[dev]->count) goto end_init; retval = -ENOMEM; if (!tty_table[dev] && !tty) { if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) goto end_init; initialize_tty_struct(dev, tty); goto repeat; } if (!tty_termios[dev] && !tp) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) goto end_init; initialize_termios(dev, tp); goto repeat; } if (!termios_locked[dev] && !ltp) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) goto end_init; memset(ltp, 0, sizeof(struct termios)); goto repeat; } if (IS_A_PTY(dev)) { if (!tty_table[o_dev] && !o_tty) { o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); if (!o_tty) goto end_init; initialize_tty_struct(o_dev, o_tty); goto repeat; } if (!tty_termios[o_dev] && !o_tp) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) goto end_init; initialize_termios(o_dev, o_tp); goto repeat; } if (!termios_locked[o_dev] && !o_ltp) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) goto end_init; memset(o_ltp, 0, sizeof(struct termios)); goto repeat; } } /* Now we have allocated all the structures: update all the pointers.. */ if (!tty_termios[dev]) { tty_termios[dev] = tp; tp = NULL; } if (!tty_table[dev]) { tty->termios = tty_termios[dev]; tty_table[dev] = tty; tty = NULL; } if (!termios_locked[dev]) { termios_locked[dev] = ltp; ltp = NULL; } if (IS_A_PTY(dev)) { if (!tty_termios[o_dev]) { tty_termios[o_dev] = o_tp; o_tp = NULL; } if (!termios_locked[o_dev]) { termios_locked[o_dev] = o_ltp; o_ltp = NULL; } if (!tty_table[o_dev]) { o_tty->termios = tty_termios[o_dev]; tty_table[o_dev] = o_tty; o_tty = NULL; } tty_table[dev]->link = tty_table[o_dev]; tty_table[o_dev]->link = tty_table[dev]; } tty_table[dev]->count++; if (IS_A_PTY_MASTER(dev)) tty_table[o_dev]->count++; retval = 0; end_init: if (tty) free_page((unsigned long) tty); if (o_tty) free_page((unsigned long) o_tty); if (tp) kfree_s(tp, sizeof(struct termios)); if (o_tp) kfree_s(o_tp, sizeof(struct termios)); if (ltp) kfree_s(ltp, sizeof(struct termios)); if (o_ltp) kfree_s(o_ltp, sizeof(struct termios)); return retval; }
status_t Shell::_Spawn(int row, int col, const ShellParameters& parameters) { const char** argv = (const char**)parameters.Arguments(); int argc = parameters.ArgumentCount(); const char* defaultArgs[3] = {kDefaultShell, "-l", NULL}; struct passwd passwdStruct; struct passwd *passwdResult; char stringBuffer[256]; if (argv == NULL || argc == 0) { if (!getpwuid_r(getuid(), &passwdStruct, stringBuffer, sizeof(stringBuffer), &passwdResult)) { defaultArgs[0] = passwdStruct.pw_shell; } argv = defaultArgs; argc = 2; fShellInfo.SetDefaultShell(true); } else fShellInfo.SetDefaultShell(false); fShellInfo.SetEncoding(parameters.Encoding()); signal(SIGTTOU, SIG_IGN); // get a pseudo-tty int master = posix_openpt(O_RDWR | O_NOCTTY); const char *ttyName; if (master < 0) { fprintf(stderr, "Didn't find any available pseudo ttys."); return errno; } if (grantpt(master) != 0 || unlockpt(master) != 0 || (ttyName = ptsname(master)) == NULL) { close(master); fprintf(stderr, "Failed to init pseudo tty."); return errno; } /* * Get the modes of the current terminal. We will duplicates these * on the pseudo terminal. */ thread_id terminalThread = find_thread(NULL); /* Fork a child process. */ fShellInfo.SetProcessID(fork()); if (fShellInfo.ProcessID() < 0) { close(master); return B_ERROR; } handshake_t handshake; if (fShellInfo.ProcessID() == 0) { // Now in child process. // close the PTY master side close(master); /* * Make our controlling tty the pseudo tty. This hapens because * we cleared our original controlling terminal above. */ /* Set process session leader */ if (setsid() < 0) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), "could not set session leader."); send_handshake_message(terminalThread, handshake); exit(1); } /* open slave pty */ int slave = -1; if ((slave = open(ttyName, O_RDWR)) < 0) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), "can't open tty (%s).", ttyName); send_handshake_message(terminalThread, handshake); exit(1); } /* set signal default */ signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTTOU, SIG_DFL); struct termios tio; /* get tty termios (not necessary). * TODO: so why are we doing it ? */ tcgetattr(slave, &tio); initialize_termios(tio); /* * change control tty. */ dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); /* close old slave fd. */ if (slave > 2) close(slave); /* * set terminal interface. */ if (tcsetattr(0, TCSANOW, &tio) == -1) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), "failed set terminal interface (TERMIOS)."); send_handshake_message(terminalThread, handshake); exit(1); } /* * set window size. */ handshake.status = PTY_WS; send_handshake_message(terminalThread, handshake); receive_handshake_message(handshake); if (handshake.status != PTY_WS) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), "mismatch handshake."); send_handshake_message(terminalThread, handshake); exit(1); } struct winsize ws = { handshake.row, handshake.col }; ioctl(0, TIOCSWINSZ, &ws); tcsetpgrp(0, getpgrp()); // set this process group ID as the controlling terminal set_thread_priority(find_thread(NULL), B_NORMAL_PRIORITY); /* pty open and set termios successful. */ handshake.status = PTY_OK; send_handshake_message(terminalThread, handshake); /* * setenv TERM and TTY. */ setenv("TERM", kTerminalType, true); setenv("TTY", ttyName, true); setenv("TTYPE", fShellInfo.EncodingName(), true); // set the current working directory, if one is given if (parameters.CurrentDirectory().Length() > 0) chdir(parameters.CurrentDirectory().String()); execve(argv[0], (char * const *)argv, environ); // Exec failed. // TODO: This doesn't belong here. sleep(1); BString alertCommand = "alert --stop '"; alertCommand += B_TRANSLATE("Cannot execute \"%command\":\n\t%error"); alertCommand += "' '"; alertCommand += B_TRANSLATE("Use default shell"); alertCommand += "' '"; alertCommand += B_TRANSLATE("Abort"); alertCommand += "'"; alertCommand.ReplaceFirst("%command", argv[0]); alertCommand.ReplaceFirst("%error", strerror(errno)); int returnValue = system(alertCommand.String()); if (returnValue == 0) { execl(kDefaultShell, kDefaultShell, "-l", NULL); } exit(1); } /* * In parent Process, Set up the input and output file pointers so * that they can write and read the pseudo terminal. */ /* * close parent control tty. */ int done = 0; while (!done) { receive_handshake_message(handshake); switch (handshake.status) { case PTY_OK: done = 1; break; case PTY_NG: fprintf(stderr, "%s\n", handshake.msg); done = -1; break; case PTY_WS: handshake.row = row; handshake.col = col; handshake.status = PTY_WS; send_handshake_message(fShellInfo.ProcessID(), handshake); break; } } if (done <= 0) return B_ERROR; fFd = master; return B_OK; }
status_t Shell::_Spawn(int row, int col, const char *encoding, int argc, const char **argv) { const char *kDefaultShellCommand[] = { "/bin/sh", "--login", NULL }; if (argv == NULL || argc == 0) { argv = kDefaultShellCommand; argc = 2; } signal(SIGTTOU, SIG_IGN); #ifdef __HAIKU__ // get a pseudo-tty int master = posix_openpt(O_RDWR | O_NOCTTY); const char *ttyName; #else /* __HAIKU__ */ /* * Get a pseudo-tty. We do this by cycling through files in the * directory. The operating system will not allow us to open a master * which is already in use, so we simply go until the open succeeds. */ char ttyName[B_PATH_NAME_LENGTH]; int master = -1; DIR *dir = opendir("/dev/pt/"); if (dir != NULL) { struct dirent *dirEntry; while ((dirEntry = readdir(dir)) != NULL) { // skip '.' and '..' if (dirEntry->d_name[0] == '.') continue; char ptyName[B_PATH_NAME_LENGTH]; snprintf(ptyName, sizeof(ptyName), "/dev/pt/%s", dirEntry->d_name); master = open(ptyName, O_RDWR); if (master >= 0) { // Set the tty that corresponds to the pty we found snprintf(ttyName, sizeof(ttyName), "/dev/tt/%s", dirEntry->d_name); break; } else { // B_BUSY is a normal case if (errno != B_BUSY) fprintf(stderr, B_TRANSLATE("could not open %s: %s\n"), ptyName, strerror(errno)); } } closedir(dir); } #endif /* __HAIKU__ */ if (master < 0) { fprintf(stderr, B_TRANSLATE("Didn't find any available pseudo ttys.")); return errno; } #ifdef __HAIKU__ if (grantpt(master) != 0 || unlockpt(master) != 0 || (ttyName = ptsname(master)) == NULL) { close(master); fprintf(stderr, B_TRANSLATE("Failed to init pseudo tty.")); return errno; } #endif /* __HAIKU__ */ /* * Get the modes of the current terminal. We will duplicates these * on the pseudo terminal. */ thread_id terminalThread = find_thread(NULL); /* Fork a child process. */ if ((fProcessID = fork()) < 0) { close(master); return B_ERROR; } handshake_t handshake; if (fProcessID == 0) { // Now in child process. // close the PTY master side close(master); /* * Make our controlling tty the pseudo tty. This hapens because * we cleared our original controlling terminal above. */ /* Set process session leader */ if (setsid() < 0) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), B_TRANSLATE("could not set session leader.")); send_handshake_message(terminalThread, handshake); exit(1); } /* open slave pty */ int slave = -1; if ((slave = open(ttyName, O_RDWR)) < 0) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), B_TRANSLATE("can't open tty (%s)."), ttyName); send_handshake_message(terminalThread, handshake); exit(1); } /* set signal default */ signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTTOU, SIG_DFL); struct termios tio; /* get tty termios (not necessary). * TODO: so why are we doing it ? */ tcgetattr(slave, &tio); initialize_termios(tio); /* * change control tty. */ dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); /* close old slave fd. */ if (slave > 2) close(slave); /* * set terminal interface. */ if (tcsetattr(0, TCSANOW, &tio) == -1) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), B_TRANSLATE("failed set terminal interface (TERMIOS).")); send_handshake_message(terminalThread, handshake); exit(1); } /* * set window size. */ handshake.status = PTY_WS; send_handshake_message(terminalThread, handshake); receive_handshake_message(handshake); if (handshake.status != PTY_WS) { handshake.status = PTY_NG; snprintf(handshake.msg, sizeof(handshake.msg), B_TRANSLATE("mismatch handshake.")); send_handshake_message(terminalThread, handshake); exit(1); } struct winsize ws = { handshake.row, handshake.col }; ioctl(0, TIOCSWINSZ, &ws); tcsetpgrp(0, getpgrp()); // set this process group ID as the controlling terminal #ifndef __HAIKU__ ioctl(0, 'pgid', getpid()); #endif set_thread_priority(find_thread(NULL), B_NORMAL_PRIORITY); /* pty open and set termios successful. */ handshake.status = PTY_OK; send_handshake_message(terminalThread, handshake); /* * setenv TERM and TTY. */ setenv("TERM", "xterm", true); setenv("TTY", ttyName, true); setenv("TTYPE", encoding, true); execve(argv[0], (char * const *)argv, environ); // Exec failed. // TODO: This doesn't belong here. sleep(1); BString alertCommand = "alert --stop '"; alertCommand += B_TRANSLATE("Cannot execute \"%command\":\n\t%error"); alertCommand += "' '"; alertCommand += B_TRANSLATE("Use default shell"); alertCommand += "' '"; alertCommand += B_TRANSLATE("Abort"); alertCommand += "'"; alertCommand.ReplaceFirst("%command", argv[0]); alertCommand.ReplaceFirst("%error", strerror(errno)); int returnValue = system(alertCommand.String()); if (returnValue == 0) { execl(kDefaultShellCommand[0], kDefaultShellCommand[0], kDefaultShellCommand[1], NULL); } exit(1); } /* * In parent Process, Set up the input and output file pointers so * that they can write and read the pseudo terminal. */ /* * close parent control tty. */ int done = 0; while (!done) { receive_handshake_message(handshake); switch (handshake.status) { case PTY_OK: done = 1; break; case PTY_NG: fprintf(stderr, "%s\n", handshake.msg); done = -1; break; case PTY_WS: handshake.row = row; handshake.col = col; handshake.status = PTY_WS; send_handshake_message(fProcessID, handshake); break; } } if (done <= 0) return B_ERROR; fFd = master; return B_OK; }