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; }