📄 subshell.c
字号:
/* Figure out whether the subshell has stopped, exited or been killed */
/* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
void sigchld_handler (int sig)
{
int pid, status;
pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
if (pid == subshell_pid) {
/* {{{ Figure out what has happened to the subshell */
if (WIFSTOPPED (status))
{
if (WSTOPSIG (status) == SIGTSTP)
/* The user has suspended the subshell. Revive it */
kill (subshell_pid, SIGCONT);
else
/* The subshell has received a SIGSTOP signal */
subshell_stopped = TRUE;
}
else /* The subshell has either exited normally or been killed */
{
subshell_alive = FALSE;
if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
quit |= SUBSHELL_EXIT; /* Exited normally */
}
/* }}} */
}
#ifndef HAVE_X
#ifndef SCO_FLAVOR
pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
if (pid == cons_saver_pid) {
/* {{{ Someone has stopped or killed cons.saver; restart it */
if (WIFSTOPPED (status))
kill (pid, SIGCONT);
else
{
handle_console (CONSOLE_DONE);
handle_console (CONSOLE_INIT);
/* Ought to do: if (in_subshell) handle_console (CONSOLE_SAVE)
Can't do this without adding a new variable `in_subshell';
it hardly seems to be worth the trouble. */
}
/* }}} */
}
#endif /* ! SCO_FLAVOR */
#endif /* ! HAVE_X */
/* If we get here, some other child exited; ignore it */
}
/* }}} */
/* {{{ feed_subshell */
/* Feed the subshell our keyboard input until it says it's finished */
static int feed_subshell (int how, int fail_on_error)
{
/* {{{ Local variables */
fd_set read_set; /* For `select' */
int bytes; /* For the return value from `read' */
int i; /* Loop counter */
struct timeval wtime; /* Maximum time we wait for the subshell */
struct timeval *wptr;
/* }}} */
/* we wait up to 10 seconds if fail_on_error */
wtime.tv_sec = 10;
wtime.tv_usec = 0;
for (wptr = fail_on_error ? &wtime : NULL;;)
{
if (!subshell_alive)
return FALSE;
/* {{{ Prepare the file-descriptor set and call `select' */
FD_ZERO (&read_set);
FD_SET (subshell_pty, &read_set);
FD_SET (subshell_pipe[READ], &read_set);
if (how == VISIBLY)
FD_SET (STDIN_FILENO, &read_set);
if (select (FD_SETSIZE, &read_set, NULL, NULL, wptr) == -1){
/* Despite using SA_RESTART, we still have to check for this */
if (errno == EINTR)
continue; /* try all over again */
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
perror ("\n"__FILE__": select (FD_SETSIZE, &read_set...)");
exit (1);
}
/* }}} */
/* From now on: block forever on the select call */
wptr = NULL;
if (FD_ISSET (subshell_pty, &read_set))
/* {{{ Read from the subshell, write to stdout */
/* This loop improves performance by reducing context switches
by a factor of 20 or so... unfortunately, it also hangs MC
randomly, because of an apparent Linux bug. Investigate. */
/* for (i=0; i<5; ++i) * FIXME -- experimental */
{
bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
if (bytes == -1 && errno != EIO)
{
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
perror ("\n"__FILE__": read (subshell_pty...)");
exit (1);
}
if (how == VISIBLY)
write (STDOUT_FILENO, pty_buffer, bytes);
}
/* }}} */
else if (FD_ISSET (subshell_pipe[READ], &read_set))
/* {{{ Read the subshell's CWD and capture its prompt */
{
bytes = read (subshell_pipe[READ], subshell_cwd, MC_MAXPATHLEN+1);
if (bytes == -1)
{
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
perror ("\n"__FILE__": read (subshell_pipe[READ]...)");
exit (1);
}
if (bytes >= 1)
subshell_cwd[bytes-1] = 0; /* Squash the final '\n' */
synchronize ();
subshell_ready = TRUE;
if (subshell_state == RUNNING_COMMAND)
{
subshell_state = INACTIVE;
return 1;
}
}
/* }}} */
else if (FD_ISSET (STDIN_FILENO, &read_set))
/* {{{ Read from stdin, write to the subshell */
{
bytes = read (STDIN_FILENO, pty_buffer, pty_buffer_size);
if (bytes == -1)
{
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
perror ("\n"__FILE__": read (STDIN_FILENO, pty_buffer...)");
exit (1);
}
for (i=0; i<bytes; ++i)
if (pty_buffer[i] == subshell_switch_key)
{
write (subshell_pty, pty_buffer, i);
if (subshell_ready)
subshell_state = INACTIVE;
return TRUE;
}
write (subshell_pty, pty_buffer, bytes);
subshell_ready = FALSE;
} else {
return FALSE;
}
/* }}} */
}
}
/* }}} */
/* {{{ synchronize */
/* Wait until the subshell dies or stops. If it stops, make it resume. */
/* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
static void synchronize (void)
{
sigset_t sigchld_mask, old_mask;
sigemptyset (&sigchld_mask);
sigaddset (&sigchld_mask, SIGCHLD);
sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
/* Wait until the subshell has stopped */
while (subshell_alive && !subshell_stopped)
sigsuspend (&old_mask);
subshell_stopped = FALSE;
kill (subshell_pid, SIGCONT);
sigprocmask (SIG_SETMASK, &old_mask, NULL);
/* We can't do any better without modifying the shell(s) */
}
/* }}} */
/* {{{ pty opening functions */
#ifdef SCO_FLAVOR
/* {{{ SCO version of pty_open_master */
static int pty_open_master (char *pty_name)
{
int pty_master;
int num;
char *ptr;
strcpy (pty_name, "/dev/ptyp");
ptr = pty_name+9;
for (num=0;;num++)
{
sprintf(ptr,"%d",num); /* surpriiise ... SCO lacks itoa() */
/* Try to open master */
if ((pty_master = open (pty_name, O_RDWR)) == -1)
if (errno == ENOENT) /* Different from EIO */
return -1; /* Out of pty devices */
else
continue; /* Try next pty device */
pty_name [5] = 't'; /* Change "pty" to "tty" */
if (access (pty_name, 6)){
close (pty_master);
pty_name [5] = 'p';
continue;
}
return pty_master;
}
return -1; /* Ran out of pty devices */
}
/* }}} */
/* {{{ SCO version of pty_open_slave */
static int pty_open_slave (const char *pty_name)
{
int pty_slave;
struct group *group_info = getgrnam ("terminal");
if (group_info != NULL)
{
/* The following two calls will only succeed if we are root */
/* [Commented out while permissions problem is investigated] */
/* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
/* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
}
if ((pty_slave = open (pty_name, O_RDWR)) == -1)
perror ("open (pty_name, O_RDWR)");
return pty_slave;
}
/* }}} */
#elif HAVE_GRANTPT
/* {{{ System V version of pty_open_master */
static int pty_open_master (char *pty_name)
{
char *slave_name;
int pty_master;
strcpy (pty_name, "/dev/ptmx");
if ((pty_master = open (pty_name, O_RDWR)) == -1
|| grantpt (pty_master) == -1 /* Grant access to slave */
|| unlockpt (pty_master) == -1 /* Clear slave's lock flag */
|| !(slave_name = ptsname (pty_master))) /* Get slave's name */
{
close (pty_master);
return -1;
}
strcpy (pty_name, slave_name);
return pty_master;
}
/* }}} */
/* {{{ System V version of pty_open_slave */
static int pty_open_slave (const char *pty_name)
{
int pty_slave = open (pty_name, O_RDWR);
if (pty_slave == -1)
{
perror ("open (pty_name, O_RDWR)");
return -1;
}
#if !defined(__osf__)
if (!ioctl (pty_slave, I_FIND, "ptem"))
if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
{
perror ("ioctl (pty_slave, I_PUSH, \"ptem\")");
close (pty_slave);
return -1;
}
if (!ioctl (pty_slave, I_FIND, "ldterm"))
if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
{
perror ("ioctl (pty_slave, I_PUSH, \"ldterm\")");
close (pty_slave);
return -1;
}
#if !defined(sgi) && !defined(__sgi)
if (!ioctl (pty_slave, I_FIND, "ttcompat"))
if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
{
perror ("ioctl (pty_slave, I_PUSH, \"ttcompat\")");
close (pty_slave);
return -1;
}
#endif /* sgi || __sgi */
#endif /* __osf__ */
return pty_slave;
}
/* }}} */
#else
/* {{{ BSD version of pty_open_master */
static int pty_open_master (char *pty_name)
{
int pty_master;
char *ptr1, *ptr2;
strcpy (pty_name, "/dev/ptyXX");
for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
{
pty_name [8] = *ptr1;
for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
{
pty_name [9] = *ptr2;
/* Try to open master */
if ((pty_master = open (pty_name, O_RDWR)) == -1)
if (errno == ENOENT) /* Different from EIO */
return -1; /* Out of pty devices */
else
continue; /* Try next pty device */
pty_name [5] = 't'; /* Change "pty" to "tty" */
if (access (pty_name, 6)){
close (pty_master);
pty_name [5] = 'p';
continue;
}
return pty_master;
}
}
return -1; /* Ran out of pty devices */
}
/* }}} */
/* {{{ BSD version of pty_open_slave */
static int pty_open_slave (const char *pty_name)
{
int pty_slave;
struct group *group_info = getgrnam ("tty");
if (group_info != NULL)
{
/* The following two calls will only succeed if we are root */
/* [Commented out while permissions problem is investigated] */
/* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
/* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
}
if ((pty_slave = open (pty_name, O_RDWR)) == -1)
perror ("open (pty_name, O_RDWR)");
return pty_slave;
}
/* }}} */
#endif
/* }}} */
#endif /* HAVE_SUBSHELL_SUPPORT */
/* {{{ Emacs local variables */
/*
Cause emacs to enter folding mode for this file:
Local variables:
end:
*/
/* }}} */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -