Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}