/* * Called to set up the pty. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, char *host, int port, char **realhost, int nodelay) { int slavefd; pid_t pid, pgrp; long windowid; pty_frontend = frontend; *backend_handle = NULL; /* we can't sensibly use this, sadly */ pty_cfg = *cfg; /* structure copy */ pty_term_width = cfg->width; pty_term_height = cfg->height; if (pty_master_fd < 0) pty_open_master(); /* * Set the backspace character to be whichever of ^H and ^? is * specified by bksp_is_delete. */ { struct termios attrs; tcgetattr(pty_master_fd, &attrs); attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010'; tcsetattr(pty_master_fd, TCSANOW, &attrs); } /* * Stamp utmp (that is, tell the utmp helper process to do so), * or not. */ if (!cfg->stamp_utmp) { close(pty_utmp_helper_pipe); /* just let the child process die */ pty_utmp_helper_pipe = -1; } else { char *location = get_x_display(pty_frontend); int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ while (pos < len) { int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); if (ret < 0) { perror("pterm: writing to utmp helper process"); close(pty_utmp_helper_pipe); /* arrgh, just give up */ pty_utmp_helper_pipe = -1; break; } pos += ret; } } windowid = get_windowid(pty_frontend); /* * Fork and execute the command. */ pid = fork(); if (pid < 0) { perror("fork"); exit(1); } if (pid == 0) { int i; /* * We are the child. */ slavefd = open(pty_name, O_RDWR); if (slavefd < 0) { perror("slave pty: open"); _exit(1); } close(pty_master_fd); fcntl(slavefd, F_SETFD, 0); /* don't close on exec */ dup2(slavefd, 0); dup2(slavefd, 1); dup2(slavefd, 2); setsid(); ioctl(slavefd, TIOCSCTTY, 1); pgrp = getpid(); tcsetpgrp(slavefd, pgrp); setpgrp(); close(open(pty_name, O_WRONLY, 0)); setpgrp(); /* Close everything _else_, for tidiness. */ for (i = 3; i < 1024; i++) close(i); { char term_env_var[10 + sizeof(cfg->termtype)]; sprintf(term_env_var, "TERM=%s", cfg->termtype); putenv(term_env_var); } { char windowid_env_var[40]; sprintf(windowid_env_var, "WINDOWID=%ld", windowid); putenv(windowid_env_var); } /* * SIGINT and SIGQUIT may have been set to ignored by our * parent, particularly by things like sh -c 'pterm &' and * some window managers. Reverse this for our child process. */ putty_signal(SIGINT, SIG_DFL); putty_signal(SIGQUIT, SIG_DFL); if (pty_argv) execvp(pty_argv[0], pty_argv); else { char *shell = getenv("SHELL"); char *shellname; if (cfg->login_shell) { char *p = strrchr(shell, '/'); shellname = snewn(2+strlen(shell), char); p = p ? p+1 : shell; sprintf(shellname, "-%s", p); } else
void init_subshell (void) { /* This must be remembered across calls to init_subshell() */ static char pty_name[BUF_SMALL]; char precmd[BUF_MEDIUM]; switch (check_sid ()) { case 1: mc_global.tty.use_subshell = FALSE; return; case 2: mc_global.tty.use_subshell = FALSE; mc_global.midnight_shutdown = TRUE; return; default: break; } /* Take the current (hopefully pristine) tty mode and make */ /* a raw mode based on it now, before we do anything else with it */ init_raw_mode (); if (mc_global.tty.subshell_pty == 0) { /* First time through */ /* Find out what type of shell we have */ if (strstr (mc_global.tty.shell, "/zsh") || getenv ("ZSH_VERSION")) subshell_type = ZSH; else if (strstr (mc_global.tty.shell, "/tcsh")) subshell_type = TCSH; else if (strstr (mc_global.tty.shell, "/csh")) subshell_type = TCSH; else if (strstr (mc_global.tty.shell, "/bash") || getenv ("BASH")) subshell_type = BASH; else if (strstr (mc_global.tty.shell, "/fish")) subshell_type = FISH; else { mc_global.tty.use_subshell = FALSE; return; } /* Open a pty for talking to the subshell */ /* FIXME: We may need to open a fresh pty each time on SVR4 */ mc_global.tty.subshell_pty = pty_open_master (pty_name); if (mc_global.tty.subshell_pty == -1) { fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno)); mc_global.tty.use_subshell = FALSE; return; } subshell_pty_slave = pty_open_slave (pty_name); if (subshell_pty_slave == -1) { fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n", pty_name, unix_error_string (errno)); mc_global.tty.use_subshell = FALSE; return; } /* Create a pipe for receiving the subshell's CWD */ if (subshell_type == TCSH) { g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d", mc_tmpdir (), (int) getpid ()); if (mkfifo (tcsh_fifo, 0600) == -1) { fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno)); mc_global.tty.use_subshell = FALSE; return; } /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */ if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1 || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1) { fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo); perror (__FILE__ ": open"); mc_global.tty.use_subshell = FALSE; return; } } else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe)) { perror (__FILE__ ": couldn't create pipe"); mc_global.tty.use_subshell = FALSE; return; } } /* Fork the subshell */ subshell_alive = TRUE; subshell_stopped = FALSE; subshell_pid = fork (); if (subshell_pid == -1) { fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno)); /* We exit here because, if the process table is full, the */ /* other method of running user commands won't work either */ exit (EXIT_FAILURE); } if (subshell_pid == 0) { /* We are in the child process */ init_subshell_child (pty_name); } /* Set up 'precmd' or equivalent for reading the subshell's CWD */ switch (subshell_type) { case BASH: g_snprintf (precmd, sizeof (precmd), " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]); break; case ZSH: g_snprintf (precmd, sizeof (precmd), " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n", subshell_pipe[WRITE]); break; case TCSH: g_snprintf (precmd, sizeof (precmd), "set echo_style=both;" "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo); break; case FISH: /* Use fish_prompt_mc function for prompt, if not present then copy fish_prompt to it. */ g_snprintf (precmd, sizeof (precmd), "if not functions -q fish_prompt_mc;" "functions -c fish_prompt fish_prompt_mc; end;" "function fish_prompt; echo $PWD>&%d; fish_prompt_mc; kill -STOP %%self; end\n", subshell_pipe[WRITE]); break; default: break; } write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd)); /* Wait until the subshell has started up and processed the command */ subshell_state = RUNNING_COMMAND; tty_enable_interrupt_key (); if (!feed_subshell (QUIETLY, TRUE)) { mc_global.tty.use_subshell = FALSE; } tty_disable_interrupt_key (); if (!subshell_alive) mc_global.tty.use_subshell = FALSE; /* Subshell died instantly, so don't use it */ }
/* * Pre-initialisation. This is here to get around the fact that GTK * doesn't like being run in setuid/setgid programs (probably * sensibly). So before we initialise GTK - and therefore before we * even process the command line - we check to see if we're running * set[ug]id. If so, we open our pty master _now_, chown it as * necessary, and drop privileges. We can always close it again * later. If we're potentially going to be doing utmp as well, we * also fork off a utmp helper process and communicate with it by * means of a pipe; the utmp helper will keep privileges in order * to clean up utmp when we exit (i.e. when its end of our pipe * closes). */ void pty_pre_init(void) { pid_t pid; int pipefd[2]; /* set the child signal handler straight away; it needs to be set * before we ever fork. */ putty_signal(SIGCHLD, sigchld_handler); pty_master_fd = -1; if (geteuid() != getuid() || getegid() != getgid()) { pty_open_master(); } #ifndef OMIT_UTMP /* * Fork off the utmp helper. */ if (pipe(pipefd) < 0) { perror("pterm: pipe"); exit(1); } pid = fork(); if (pid < 0) { perror("pterm: fork"); exit(1); } else if (pid == 0) { char display[128], buffer[128]; int dlen, ret; close(pipefd[1]); /* * Now sit here until we receive a display name from the * other end of the pipe, and then stamp utmp. Unstamp utmp * again, and exit, when the pipe closes. */ dlen = 0; while (1) { ret = read(pipefd[0], buffer, lenof(buffer)); if (ret <= 0) { cleanup_utmp(); _exit(0); } else if (!pty_stamped_utmp) { if (dlen < lenof(display)) memcpy(display+dlen, buffer, min(ret, lenof(display)-dlen)); if (buffer[ret-1] == '\0') { /* * Now we have a display name. NUL-terminate * it, and stamp utmp. */ display[lenof(display)-1] = '\0'; /* * Trap as many fatal signals as we can in the * hope of having the best possible chance to * clean up utmp before termination. We are * unfortunately unprotected against SIGKILL, * but that's life. */ putty_signal(SIGHUP, fatal_sig_handler); putty_signal(SIGINT, fatal_sig_handler); putty_signal(SIGQUIT, fatal_sig_handler); putty_signal(SIGILL, fatal_sig_handler); putty_signal(SIGABRT, fatal_sig_handler); putty_signal(SIGFPE, fatal_sig_handler); putty_signal(SIGPIPE, fatal_sig_handler); putty_signal(SIGALRM, fatal_sig_handler); putty_signal(SIGTERM, fatal_sig_handler); putty_signal(SIGSEGV, fatal_sig_handler); putty_signal(SIGUSR1, fatal_sig_handler); putty_signal(SIGUSR2, fatal_sig_handler); #ifdef SIGBUS putty_signal(SIGBUS, fatal_sig_handler); #endif #ifdef SIGPOLL putty_signal(SIGPOLL, fatal_sig_handler); #endif #ifdef SIGPROF putty_signal(SIGPROF, fatal_sig_handler); #endif #ifdef SIGSYS putty_signal(SIGSYS, fatal_sig_handler); #endif #ifdef SIGTRAP putty_signal(SIGTRAP, fatal_sig_handler); #endif #ifdef SIGVTALRM putty_signal(SIGVTALRM, fatal_sig_handler); #endif #ifdef SIGXCPU putty_signal(SIGXCPU, fatal_sig_handler); #endif #ifdef SIGXFSZ putty_signal(SIGXFSZ, fatal_sig_handler); #endif #ifdef SIGIO putty_signal(SIGIO, fatal_sig_handler); #endif setup_utmp(pty_name, display); } } } } else { close(pipefd[0]); pty_utmp_helper_pid = pid; pty_utmp_helper_pipe = pipefd[1]; } #endif /* Drop privs. */ { int gid = getgid(), uid = getuid(); #ifndef HAVE_NO_SETRESUID int setresgid(gid_t, gid_t, gid_t); int setresuid(uid_t, uid_t, uid_t); setresgid(gid, gid, gid); setresuid(uid, uid, uid); #else setgid(getgid()); setuid(getuid()); #endif } }