// ===================================================================================================================== int main(int argc, char** argv) { libdevman_server_init(); libdevman_client_init(); libdevman_client_async_init(libdevman_server_get_port()); init_screen(); init_input(); init_pty(); // register the terminal server ipc_server_register("terminal", libdevman_server_get_port()); // run the mainloop libdevman_server_run(terminal_handler); return 0; }
/* clients. */ static void master_process(int s, char **argv, int waitattach, int statusfd) { struct client *p, *next; fd_set readfds; int highest_fd; int nullfd; int has_attached_client = 0; /* Okay, disassociate ourselves from the original terminal, as we ** don't care what happens to it. */ setsid(); /* Set a trap to unlink the socket when we die. */ atexit(unlink_socket); /* Create a pty in which the process is running. */ signal(SIGCHLD, die); if (init_pty(argv, statusfd) < 0) { if (statusfd != -1) dup2(statusfd, 1); if (errno == ENOENT) printf("%s: Could not find a pty.\n", progname); else printf("%s: init_pty: %s\n", progname, strerror(errno)); exit(1); } /* Set up some signals. */ signal(SIGPIPE, SIG_IGN); signal(SIGXFSZ, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGINT, die); signal(SIGTERM, die); /* Close statusfd, since we don't need it anymore. */ if (statusfd != -1) close(statusfd); /* Make sure stdin/stdout/stderr point to /dev/null. We are now a ** daemon. */ nullfd = open("/dev/null", O_RDWR); dup2(nullfd, 0); dup2(nullfd, 1); dup2(nullfd, 2); if (nullfd > 2) close(nullfd); /* Loop forever. */ while (1) { int new_has_attached_client = 0; /* Re-initialize the file descriptor set for select. */ FD_ZERO(&readfds); FD_SET(s, &readfds); highest_fd = s; /* ** When waitattach is set, wait until the client attaches ** before trying to read from the pty. */ if (waitattach) { if (clients && clients->attached) waitattach = 0; } else { FD_SET(the_pty.fd, &readfds); if (the_pty.fd > highest_fd) highest_fd = the_pty.fd; } for (p = clients; p; p = p->next) { FD_SET(p->fd, &readfds); if (p->fd > highest_fd) highest_fd = p->fd; if (p->attached) new_has_attached_client = 1; } /* chmod the socket if necessary. */ if (has_attached_client != new_has_attached_client) { update_socket_modes(new_has_attached_client); has_attached_client = new_has_attached_client; } /* Wait for something to happen. */ if (select(highest_fd + 1, &readfds, NULL, NULL, NULL) < 0) { if (errno == EINTR || errno == EAGAIN) continue; exit(1); } /* New client? */ if (FD_ISSET(s, &readfds)) control_activity(s); /* Activity on a client? */ for (p = clients; p; p = next) { next = p->next; if (FD_ISSET(p->fd, &readfds)) client_activity(p); } /* pty activity? */ if (FD_ISSET(the_pty.fd, &readfds)) pty_activity(s); } }
static void setup_signals(void) { struct sigaction sa = { .sa_handler = signal_handler, .sa_flags = 0 }; sigset_t ss; do { if (sigfillset(&ss) == -1) break; if (sigemptyset(&sa.sa_mask) == -1) break; if (sigaction(SIGCHLD, &sa, NULL) == -1 || sigdelset(&ss, SIGCHLD) == -1) break; if (sigaction(SIGUSR1, &sa, NULL) == -1 || sigdelset(&ss, SIGUSR1) == -1) break; if (sigaction(SIGUSR2, &sa, NULL) == -1 || sigdelset(&ss, SIGUSR2) == -1) break; if (sigprocmask(SIG_SETMASK, &ss, NULL) == -1) break; return; } while (false); fatal("Error while setting up signal handlers.\n"); } static void child_setup_signals(void) { do { struct sigaction sa = { .sa_handler = SIG_DFL, .sa_flags = 0 }; if (sigemptyset(&sa.sa_mask) == -1) break; if (sigaction(SIGCHLD, &sa, NULL) == -1) break; if (sigaction(SIGUSR1, &sa, NULL) == -1) break; if (sigaction(SIGUSR2, &sa, NULL) == -1) break; if (sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL) == -1) break; return; } while (false); fatal("<child> Error while setting up signal handlers.\n"); } static void invoke_command(char *command, char **argv, pid_t *child_pid, int *ptm) { char* slavename = open_ptm(ptm); if (slavename == NULL) fatal("Error while opening pseudo-terminal.\n"); struct winsize wsize; if (ioctl(STDIN_FILENO, TIOCGWINSZ, &wsize) == -1) fatal("Error while getting the terminal size.\n"); struct termios attr; if (tcgetattr(*ptm, &attr) == -1) fatal("Error while getting the pseudo-terminal attributes.\n"); switch (*child_pid = fork()) { case -1: fatal("Fork failed.\n"); case 0: /* child: */ drop_privileges(); if (close(*ptm) == -1) fatal("<child> Error while closing the master pseudo-terminal\n"); if (setsid() == -1) fatal("<child> Error while creating a session\n"); assert(slavename != NULL); int pts = open(slavename, O_RDWR); if (pts == -1) fatal("<child> Error while opening the slave pseudo-terminal\n"); init_pty(pts, &wsize, attr); child_setup_signals(); execvp(command, argv); fatal("<child> Execution failed.\n"); default: /* parent: */ return; } } static void clear(void) { writes(STDOUT_FILENO, "\033[0m\033[H\033[J"); }
static int setup_child(pid_t *pid, const char *term, const char *attr, char *const *argv) { int master; char ttyname[20]; DBUG_ENTER("setup_child"); switch ((*pid = pty_fork(&master, ttyname, 0, 0))) { case -1: DBUG_PRINT("startup", ("forkpty: failed")); DBUG_RETURN(-errno); /*NOTREACHED*/ case 0: { const char *shell; DBUG_PROCESS("child"); DBUG_PRINT("info", ("TERM=%s", term)); if (term) setenv("TERM", term, 1); DBUG_PRINT("info", ("attributes=%s", attr)); if (attr) init_pty(0, attr); if (!(shell = argv[0])) { char *s0; uid_t uid; struct passwd *pw; shell = "/bin/bash"; s0 = "-bash"; /* get user's login shell */ if (!(pw = getpwuid(uid = getuid()))) { DBUG_PRINT("error", ("getpwuid(%ld) failed: %s", uid, strerror(errno))); } else if (!(shell = pw->pw_shell) || *shell != '/') { DBUG_PRINT("error", ("bad shell for user id %ld", uid)); } else { DBUG_PRINT("info", ("got shell %s", shell)); s0 = strrchr(shell, '/'); s0 = str_dup(s0); assert(s0 != 0); s0[0] = '-'; } DBUG_PRINT("info", ("startup %s (%s)", shell, s0)); execl(shell, s0, (char *)0); } else { DBUG_PRINT("info", ("startup %s", *argv)); execvp(*argv, argv); } DBUG_PRINT("error", ("exec* failed: %s", strerror(errno))); perror(shell); exit(111); /*NOTREACHED*/ } default: { // Parent process. fcntl(master, F_SETFL, O_NONBLOCK); char *dev = ptsname(master); if (dev) { struct utmp ut; memset(&ut, 0, sizeof ut); if (!strncmp(dev, "/dev/", 5)) dev += 5; strlcpy(ut.ut_line, dev, sizeof ut.ut_line); if (dev[1] == 't' && dev[2] == 'y') dev += 3; if (!strncmp(dev, "pts/", 4)) dev += 4; strncpy(ut.ut_id, dev, sizeof ut.ut_id); ut.ut_type = USER_PROCESS; ut.ut_pid = (long int)*pid; ut.ut_time = time(0); strlcpy(ut.ut_user, getlogin() ?: "?", sizeof ut.ut_user); gethostname(ut.ut_host, sizeof ut.ut_host); login(&ut); } } } DBUG_PRINT("startup", ("forkpty:pid=%ld:master=%d:ttyname=%s", (long int)*pid, master, ttyname)); DBUG_RETURN(master); }