/*======================================================================== ** The Main Procedure ** Initialization and FIFO command dispatch routine. ========================================================================*/ static int real_main(int argc, char *argv[]) { const char function[] = "real_main"; int option_foreground = FALSE; int FIFO; /* First-in-first-out which feeds us requests */ sigset_t lock_set; struct timeval next_tick; /* Initialize internation messages library. */ #ifdef INTERNATIONAL setlocale(LC_ALL, ""); bindtextdomain(PACKAGE_PPRD, LOCALEDIR); textdomain(PACKAGE_PPRD); #endif /* ** Set some environment variables, (PATH, IFS, and ** SHELL) for safety and for the convenience of the ** programs we launch (HOME, and PPR_VERSION). ** Remove unnecessary and potentially misleading ** variables. */ set_ppr_env(); prune_env(); parse_command_line(argc, argv, &option_foreground); /* Switch all UIDs to USER_PPR, all GIDS to GROUP_PPR. */ adjust_ids(); /* If the --forground switch wasn't used, then dropt into background. */ if(! option_foreground) gu_daemon(PPR_PPRD_UMASK); else umask(PPR_PPRD_UMASK); /* Change the home directory to the PPR home directory: */ chdir(HOMEDIR); /* Create /var/spool/ppr/pprd.pid. */ create_lock_file(); /* Signal handlers for silly stuff. */ signal_restarting(SIGPIPE, signal_ignore); signal_restarting(SIGHUP, signal_ignore); /* Signal handler for shutdown request. */ signal_interupting(SIGTERM, sigterm_handler); /* Arrange for child termination to be noted. */ signal_restarting(SIGCHLD, sigchld_handler); /* Move /var/spool/ppr/logs/pprd to pprd.old before we call debug() for the first time (below). */ rename_old_log_file(); /* ** This code must come after adjust_ids() and gu_daemon(). ** It makes the first log entry and tells queue-display ** programs that we are starting up. */ debug("PPRD startup, pid=%ld", (long)getpid()); state_update("STARTUP"); /* Make sure the local node gets the node id of 0. */ if(! nodeid_is_local_node(nodeid_assign(ppr_get_nodename()))) fatal(1, "%s(): line %d: assertion failed", function, __LINE__); /* Initialize other subsystems. */ question_init(); /* Load the printers database. */ DODEBUG_STARTUP(("loading printers database")); load_printers(); /* Load the groups database. */ DODEBUG_STARTUP(("loading groups database")); load_groups(); /* Set up the FIFO. */ DODEBUG_STARTUP(("opening FIFO")); FIFO = open_fifo(); /* Initialize the queue. This is likely to start printers. */ DODEBUG_STARTUP(("initializing the queue")); initialize_queue(); /* Schedule the first timer tick. */ gettimeofday(&next_tick, NULL); next_tick.tv_sec += TICK_INTERVAL; /* ** Create a signal block set which will be used to block SIGCHLD except ** when we are calling select(). */ sigemptyset(&lock_set); sigaddset(&lock_set, SIGCHLD); sigprocmask(SIG_BLOCK, &lock_set, (sigset_t*)NULL); /* ** This is the Main Loop. It runs until the sigterm_handler ** sets sigterm_received. */ while(!sigterm_received) { int readyfds; /* return value from select() */ fd_set rfds; /* list of file descriptors for select() to watch */ struct timeval time_now; /* the current time */ DODEBUG_MAINLOOP(("top of main loop")); gettimeofday(&time_now, NULL); /* If it is time for or past time for the next tick, */ if(gu_timeval_cmp(&time_now, &next_tick) >= 0) { readyfds = 0; } /* If it is not time for the next tick yet, */ else { /* Set the select() timeout so that it will return in time for the next tick(). */ gu_timeval_cpy(&select_tv, &next_tick); gu_timeval_sub(&select_tv, &time_now); /* Create a file descriptor list which contains only the descriptor of the FIFO. */ FD_ZERO(&rfds); FD_SET(FIFO, &rfds); /* Call select() with SIGCHLD unblocked. */ sigprocmask(SIG_UNBLOCK, &lock_set, (sigset_t*)NULL); readyfds = select(FIFO + 1, &rfds, NULL, NULL, &select_tv); sigprocmask(SIG_BLOCK, &lock_set, (sigset_t*)NULL); } /* If there is something to read, */ if(readyfds > 0) { if(!FD_ISSET(FIFO, &rfds)) fatal(0, "%s(): assertion failed: select() returned but FIFO not ready", function); do_command(FIFO); continue; } /* If the SIGCHLD handler set the flag, handle child termination. Once we have done that, we must go back to the top of the loop because we don't really know if it is time for a tick() call yet. */ if(sigchld_caught) { sigchld_caught = FALSE; reapchild(); continue; } /* If there was no error and no file descriptors are ready, then the timeout must have expired. Call tick(). */ if(readyfds == 0) { tick(); next_tick.tv_sec += TICK_INTERVAL; continue; } /* If interupted by a system call, restart it. */ if(errno == EINTR) continue; /* If we get this far, there was an error. */ fatal(0, "%s(): select() failed, errno=%d (%s)", function, errno, gu_strerror(errno)); } /* end of endless while() loop */ state_update("SHUTDOWN"); fatal(0, "Received SIGTERM, exiting"); } /* end of real_main() */
int serv_loop() { int cs, ss = 0; struct sockaddr_storage cl; fd_set readable; int i, n, len; char cl_addr[NI_MAXHOST]; char cl_name[NI_MAXHOST]; int error; pid_t pid; #ifdef USE_THREAD if (threading) { blocksignal(SIGHUP); blocksignal(SIGINT); blocksignal(SIGUSR1); } #endif for (;;) { readable = allsock; MUTEX_LOCK(mutex_select); n = select(maxsock+1, &readable, 0, 0, 0); if (n <= 0) { if (n < 0 && errno != EINTR) { msg_out(warn, "select: %m"); } MUTEX_UNLOCK(mutex_select); continue; } #ifdef USE_THREAD if ( ! threading ) { #endif /* handle any queued signal flags */ if (FD_ISSET(sig_queue[0], &readable)) { if (ioctl(sig_queue[0], FIONREAD, &i) != 0) { msg_out(crit, "ioctl: %m"); exit(-1); } while (--i >= 0) { char c; if (read(sig_queue[0], &c, 1) != 1) { msg_out(crit, "read: %m"); exit(-1); } switch(c) { case 'H': /* sighup */ reload(); break; case 'C': /* sigchld */ reapchild(); break; case 'T': /* sigterm */ cleanup(); break; default: break; } } } #ifdef USE_THREAD } #endif for ( i = 0; i < serv_sock_ind; i++ ) { if (FD_ISSET(serv_sock[i], &readable)) { n--; break; } } if ( n < 0 || i >= serv_sock_ind ) { MUTEX_UNLOCK(mutex_select); continue; } len = sizeof(struct sockaddr_storage); cs = accept(serv_sock[i], (struct sockaddr *)&cl, (socklen_t *)&len); if (cs < 0) { if (errno == EINTR #ifdef SOLARIS || errno == EPROTO #endif || errno == EWOULDBLOCK || errno == ECONNABORTED) { ; /* ignore */ } else { /* real accept error */ msg_out(warn, "accept: %m"); } MUTEX_UNLOCK(mutex_select); continue; } MUTEX_UNLOCK(mutex_select); #ifdef USE_THREAD if ( !threading ) { #endif if (max_child > 0 && cur_child >= max_child) { msg_out(warn, "child: cur %d; exeedeing max(%d)", cur_child, max_child); close(cs); continue; } #ifdef USE_THREAD } #endif error = getnameinfo((struct sockaddr *)&cl, len, cl_addr, sizeof(cl_addr), NULL, 0, NI_NUMERICHOST); if (resolv_client) { error = getnameinfo((struct sockaddr *)&cl, len, cl_name, sizeof(cl_name), NULL, 0, 0); msg_out(norm, "%s[%s] connected", cl_name, cl_addr); } else { msg_out(norm, "%s connected", cl_addr); strncpy(cl_name, cl_addr, sizeof(cl_name)); } i = validate_access(cl_addr, cl_name); if (i < 1) { /* access denied */ close(cs); continue; } set_blocking(cs); #ifdef USE_THREAD if (!threading ) { #endif blocksignal(SIGHUP); blocksignal(SIGCHLD); pid = fork(); switch (pid) { case -1: /* fork child failed */ printf("\nfork failed\n"); break; case 0: /* i am child */ for ( i = 0; i < serv_sock_ind; i++ ) { close(serv_sock[i]); } setsignal(SIGCHLD, SIG_DFL); setsignal(SIGHUP, SIG_DFL); releasesignal(SIGCHLD); releasesignal(SIGHUP); ss = proto_socks(cs); if ( ss == -1 ) { close(cs); /* may already be closed */ exit(1); } printf("\nrelaying\n"); relay(cs, ss); exit(0); default: /* may be parent */ printf("\nadding proc\n"); proclist_add(pid); break; } close(cs); releasesignal(SIGHUP); releasesignal(SIGCHLD); #ifdef USE_THREAD } else { ss = proto_socks(cs); if ( ss == -1 ) { close(cs); /* may already be closed */ continue; } relay(cs, ss); } #endif } }
int serv_loop() { SOCKS_STATE state; SOCK_INFO si; CL_INFO client; int cs; fd_set readable; int i, n, len; int error; pid_t pid; memset(&state, 0, sizeof(state)); memset(&si, 0, sizeof(si)); memset(&client, 0, sizeof(client)); state.si = &si; #ifdef USE_THREAD if (threading) { blocksignal(SIGHUP); blocksignal(SIGINT); blocksignal(SIGUSR1); } #endif for (;;) { readable = allsock; MUTEX_LOCK(mutex_select); n = select(maxsock+1, &readable, 0, 0, 0); if (n <= 0) { if (n < 0 && errno != EINTR) { msg_out(warn, "select: %m"); } MUTEX_UNLOCK(mutex_select); continue; } #ifdef USE_THREAD if ( ! threading ) { #endif /* handle any queued signal flags */ if (FD_ISSET(sig_queue[0], &readable)) { if (ioctl(sig_queue[0], FIONREAD, &i) != 0) { msg_out(crit, "ioctl: %m"); exit(-1); } while (--i >= 0) { char c; if (read(sig_queue[0], &c, 1) != 1) { msg_out(crit, "read: %m"); exit(-1); } switch(c) { case 'H': /* sighup */ reload(); break; case 'C': /* sigchld */ reapchild(); break; case 'T': /* sigterm */ cleanup(); break; default: break; } } } #ifdef USE_THREAD } #endif for ( i = 0; i < serv_sock_ind; i++ ) { if (FD_ISSET(serv_sock[i], &readable)) { n--; break; } } if ( n < 0 || i >= serv_sock_ind ) { MUTEX_UNLOCK(mutex_select); continue; } len = SS_LEN; cs = accept(serv_sock[i], &si.prc.addr.sa, (socklen_t *)&len); si.prc.len = len; if (cs < 0) { if (errno == EINTR #ifdef SOLARIS || errno == EPROTO #endif || errno == EWOULDBLOCK || errno == ECONNABORTED) { ; /* ignore */ } else { /* real accept error */ msg_out(warn, "accept: %m"); } MUTEX_UNLOCK(mutex_select); continue; } MUTEX_UNLOCK(mutex_select); #ifdef USE_THREAD if ( !threading ) { #endif if (max_child > 0 && cur_child >= max_child) { msg_out(warn, "child: cur %d; exeedeing max(%d)", cur_child, max_child); close(cs); continue; } #ifdef USE_THREAD } #endif /* get downstream-side socket name */ len = SS_LEN; getsockname(cs, &si.myc.addr.sa, (socklen_t *)&len); si.myc.len = len; error = getnameinfo(&si.prc.addr.sa, si.prc.len, client.addr, sizeof(client.addr), NULL, 0, NI_NUMERICHOST); if (resolv_client) { error = getnameinfo(&si.prc.addr.sa, si.prc.len, client.name, sizeof(client.name), NULL, 0, 0); msg_out(norm, "%s[%s] connected", client.name, client.addr); } else { msg_out(norm, "%s connected", client.addr); strncpy(client.name, client.addr, sizeof(client.name)); } i = validate_access(&client); if (i < 1) { /* access denied */ close(cs); continue; } set_blocking(cs); state.s = cs; #ifdef USE_THREAD if (!threading ) { #endif blocksignal(SIGHUP); blocksignal(SIGCHLD); pid = fork(); switch (pid) { case -1: /* fork child failed */ break; case 0: /* i am child */ for ( i = 0; i < serv_sock_ind; i++ ) { close(serv_sock[i]); } setsignal(SIGCHLD, SIG_DFL); setsignal(SIGHUP, SIG_DFL); releasesignal(SIGCHLD); releasesignal(SIGHUP); error = proto_socks(&state); if ( error == -1 ) { close(state.s); /* may already be closed */ exit(1); } relay(&state); exit(0); default: /* may be parent */ proclist_add(pid); break; } close(state.s); releasesignal(SIGHUP); releasesignal(SIGCHLD); #ifdef USE_THREAD } else { error = proto_socks(&state); if ( error == -1 ) { close(state.s); /* may already be closed */ /* udp may be dynamically allocated */ if (state.sr.udp != NULL) free(state.sr.udp); continue; } relay(&state); } #endif } }
int main(int argc, char **argv, char **ev) { extern char *optarg; extern int optind; char ch, hbuf[BUFSZ], /* hlogin buffer */ ptyname[FILENAME_MAX + 1], tbuf[BUFSZ], /* telnet/ssh buffer */ tbufstr[5] = {ESC, '\x07', '\r', '\n', '\0'}; int bytes, /* bytes read/written */ devnull, rval = EX_OK, ptym, /* master pty */ ptys; /* slave pty */ ssize_t idx, /* strcspan span */ hlen = 0, /* len of hbuf */ tlen = 0; /* len of tbuf */ struct pollfd pfds[3]; struct termios tios; environ = ev; /* get just the basename() of our exec() name and strip a .* off the end */ if ((progname = strrchr(argv[0], '/')) != NULL) progname += 1; else progname = argv[0]; if (strrchr(progname, '.') != NULL) *(strrchr(progname, '.')) = '\0'; while ((ch = getopt(argc, argv, "dhvt:")) != -1 ) switch (ch) { case 'd': debug++; break; case 't': timeo = atoi(optarg); if (timeo < 1) timeo = 1; break; case 'v': vers(); return(EX_OK); case 'h': default: usage(); return(EX_USAGE); } if (argc - optind < 2) { usage(); return(EX_USAGE); } unsetenv("DISPLAY"); for (sigrx = 3; sigrx < 10; sigrx++) close(sigrx); /* allocate pty for telnet/ssh, then fork and exec */ if (openpty(&ptym, &ptys, ptyname, NULL, NULL)) { fprintf(stderr, "%s: could not allocate pty: %s\n", progname, strerror(errno)); return(EX_TEMPFAIL); } /* make the pty raw */ if (tcgetattr(ptys, &tios)) { fprintf(stderr, "%s: tcgetattr() failed: %s\n", progname, strerror(errno)); return(EX_OSERR); } tios.c_lflag &= ~ECHO; tios.c_lflag &= ~ICANON; #ifdef VMIN tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; #endif if (tcsetattr(ptys, TCSANOW, &tios)) { fprintf(stderr, "%s: tcsetattr() failed: %s\n", progname, strerror(errno)); return(EX_OSERR); } /* * if a tty, make it raw as the hp echos _everything_, including * passwords. */ if (isatty(fileno(stdin))) { if (tcgetattr(fileno(stdin), &tios)) { fprintf(stderr, "%s: tcgetattr() failed: %s\n", progname, strerror(errno)); return(EX_OSERR); } tios.c_lflag &= ~ECHO; tios.c_lflag &= ~ICANON; #ifdef VMIN tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; #endif if (tcsetattr(fileno(stdin), TCSANOW, &tios)) { fprintf(stderr, "%s: tcsetattr() failed: %s\n", progname, strerror(errno)); return(EX_OSERR); } } /* zero the buffers */ memset(hbuf, 0, BUFSZ); memset(tbuf, 0, BUFSZ); /* reap our children, must be set-up *after* openpty() */ signal(SIGCHLD, reapchild); if ((child = fork()) == -1) { fprintf(stderr, "%s: fork() failed: %s\n", progname, strerror(errno)); return(EX_TEMPFAIL); } if (child == 0) { struct winsize ws; /* * Make sure our terminal length and width are something greater * than 1, for pagers on stupid boxes. */ ioctl(ptys, TIOCGWINSZ, &ws); ws.ws_row = 24; ws.ws_col = 132; ioctl(ptys, TIOCSWINSZ, &ws); signal(SIGCHLD, SIG_DFL); /* close the master pty & std* inherited from the parent */ close(ptym); if (ptys != 0) close(0); if (ptys != 1) close(1); if (ptys != 2) close(2); #ifdef TIOCSCTTY setsid(); if (ioctl(ptys, TIOCSCTTY, NULL) == -1) { snprintf(ptyname, FILENAME_MAX, "%s: could not set controlling " "tty: %s\n", progname, strerror(errno)); write(0, ptyname, strlen(ptyname)); return(EX_OSERR); } #endif /* close stdin/out/err and attach them to the pipes */ if (dup2(ptys, 0) == -1 || dup2(ptys, 1) == -1 || dup2(ptys, 2) == -1) { snprintf(ptyname, FILENAME_MAX, "%s: dup2() failed: %s\n", progname, strerror(errno)); write(0, ptyname, strlen(ptyname)); return(EX_OSERR); } if (ptys > 2) close(ptys); /* exec telnet/ssh */ execvp(argv[optind], argv + optind); snprintf(ptyname, FILENAME_MAX, "%s: execvp() failed: %s\n", progname, strerror(errno)); write(0, ptyname, strlen(ptyname)); return(EX_TEMPFAIL); /*NOTREACHED*/ } /* parent */ if (debug) fprintf(stderr, "child %d\n", (int)child); signal(SIGHUP, sighdlr); /* close the slave pty */ close(ptys); devnull = open("/dev/null", O_RDWR); /* make FDs non-blocking */ if (fcntl(ptym, F_SETFL, O_NONBLOCK) || fcntl(fileno(stdin), F_SETFL, O_NONBLOCK) || fcntl(fileno(stdout), F_SETFL, O_NONBLOCK)) { fprintf(stderr, "%s: fcntl(NONBLOCK) failed: %s\n", progname, strerror(errno)); exit(EX_OSERR); } /* loop to read on stdin and ptym */ #define POLLEXP (POLLERR | POLLHUP | POLLNVAL) pfds[0].fd = fileno(stdin); pfds[0].events = POLLIN | POLLEXP; pfds[1].fd = fileno(stdout); pfds[1].events = POLLEXP; pfds[2].fd = ptym; pfds[2].events = POLLIN | POLLEXP; /* shuffle data across the pipes until we see EOF or a read/write error */ sigrx = 0; while (1) { bytes = poll(pfds, 3, (timeo * 1000)); if (bytes == 0) { if (sigrx) break; /* timeout */ continue; } if (bytes == -1) { switch (errno) { case EAGAIN: case EINTR: break; default: rval = EX_IOERR; break; } continue; } /* * write buffers first * write hbuf (aka hlogin/stdin/pfds[0]) -> telnet (aka ptym/pfds[2]) */ if ((pfds[2].revents & POLLOUT) && hlen) { if ((bytes = write(pfds[2].fd, hbuf, hlen)) < 0 && errno != EINTR && errno != EAGAIN) { fprintf(stderr, "%s: write() failed: %s\n", progname, strerror(errno)); hlen = 0; hbuf[0] = '\0'; break; } else if (bytes > 0) { strcpy(hbuf, hbuf + bytes); hlen -= bytes; if (hlen < 1) pfds[2].events &= ~POLLOUT; } } if (pfds[2].revents & POLLEXP) { hlen = 0; hbuf[0] = '\0'; break; } /* write tbuf (aka telnet/ptym/pfds[2]) -> hlogin (stdout/pfds[1]) */ if ((pfds[1].revents & POLLOUT) && tlen) { /* * if there is an escape char that didnt get filter()'d, * we need to write only up to that point and wait for * the bits that complete the escape sequence. if at least * two bytes follow it and it doesn't look like we should expect * more data, write it anyway as filter() didnt match it. */ bytes = tlen; idx = strcspn(tbuf, tbufstr); if (idx) { if (tbuf[idx] == ESC) { if (tlen - idx < 2 || expectmore(&tbuf[idx], tlen - idx)) { bytes = idx; } } if (tbuf[idx] == '\r' || tbuf[idx] == '\n') { bytes = ++idx; if (tbuf[idx] == '\r' || tbuf[idx] == '\n') bytes++; } } else { if (tbuf[0] == ESC) { if (tlen < 2 || expectmore(tbuf, tlen)) { bytes = 0; } } if (tbuf[0] == '\r' || tbuf[0] == '\n') { bytes = 1; if (tbuf[1] == '\r' || tbuf[1] == '\n') bytes++; } } if ((bytes = write(pfds[1].fd, tbuf, bytes)) < 0 && errno != EINTR && errno != EAGAIN) { fprintf(stderr, "%s: write() failed: %s\n", progname, strerror(errno)); /* dont bother trying to flush tbuf */ tlen = 0; tbuf[0] = '\0'; break; } else if (bytes > 0) { strcpy(tbuf, tbuf + bytes); tlen -= bytes; if (tlen < 1) pfds[1].events &= ~POLLOUT; } } if (pfds[1].revents & POLLEXP) { /* dont bother trying to flush tbuf */ tlen = 0; tbuf[0] = '\0'; break; } /* read hlogin (aka stdin/pfds[0]) -> hbuf */ if (pfds[0].revents & POLLIN) { if (BUFSZ - hlen > 1) { bytes = read(pfds[0].fd, hbuf + hlen, (BUFSZ - 1) - hlen); if (bytes > 0) { hlen += bytes; hbuf[hlen] = '\0'; pfds[2].events |= POLLOUT; } else if (bytes < 0 && errno != EAGAIN && errno != EINTR) { /* read error */ break; } } } if (pfds[0].revents & POLLEXP) break; /* read telnet/ssh (aka ptym/pfds[2]) -> tbuf, then filter */ if (pfds[2].revents & POLLIN) { if (BUFSZ - tlen > 1) { bytes = read(pfds[2].fd, tbuf + tlen, (BUFSZ - 1) - tlen); if (bytes > 0) { tlen += bytes; tbuf[tlen] = '\0'; tlen = filter(tbuf, tlen); if (tlen > 0) pfds[1].events |= POLLOUT; } else if (bytes < 0 && errno != EAGAIN && errno != EINTR) { /* read error */ break; } } } if (pfds[2].revents & POLLEXP) break; } /* try to flush any remaining data from our buffers */ if (hlen) { (void)write(pfds[2].fd, hbuf, hlen); hlen = 0; } if (tlen) { (void)write(pfds[1].fd, tbuf, tlen); tlen = 0; } if ((bytes = read(pfds[2].fd, tbuf, (BUFSZ - 1))) > 0) { tbuf[bytes] = '\0'; tlen = filter(tbuf, bytes); (void)write(pfds[1].fd, tbuf, tlen); } tcdrain(pfds[1].fd); if ((hlen = read(pfds[0].fd, hbuf, (BUFSZ - 1))) > 0) { (void)write(pfds[2].fd, hbuf, hlen); } tcdrain(pfds[2].fd); if (child && ! kill(child, SIGINT)) reapchild(SIGCHLD); return(rval); }
void reapchildren(void) { for(;;) { sleep(5); reapchild(0); } }