static int idsa_risk_scan(IDSA_UNIT * u, char *s) { unsigned int x; x = idsa_risk_parse(s); memcpy(u->u_ptr, &x, sizeof(int)); return 0; }
int main(int argc, char **argv) { char *user, *group, *interface, *root, *port, *cmd; int niceinc; int nofork; int timeout; int instances; int queue; int reuse; int keepalive; int what; int value; int offset; int i, j; char c; int lfd; int nfd; struct sigaction sag; sigset_t sst; int status; int hang; int busy; int tos; int ttl; int sane; int just; #ifdef USE_IDSA int exitcode; int killcode; int flags; unsigned int arisk, crisk, irisk, risk; #endif double limit_load = 0.0; port = NULL; user = NULL; group = NULL; root = NULL; interface = NULL; cmd = NULL; just = 0; sane = 1; reuse = 1; keepalive = 1; niceinc = 0; nofork = 0; timeout = 0; instances = 0; queue = 5; tos = 0; ttl = 0; offset = 0; #ifdef USE_IDSA flags = 0; arisk = idsa_risk_make(-0.3,0.8); crisk = IDSA_R_UNKNOWN; irisk = IDSA_R_PARTIAL; #endif i = j = 1; while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'c': printf("(c) 2002,2007 Marc Welz: Distributed under the terms of the GNU General Public License\n"); exit(0); break; case 'h': /* print brief help message */ case '?': usage(argv[0]); exit(0); break; case 'v': #ifdef USE_IDSA printf("linetd %s-i\n", VERSION); #else printf("linetd %s\n", VERSION); #endif exit(0); break; /* flags */ case 'f': /* keep in foreground */ nofork = 1; j++; break; case 'a' : /* disable reuse of address */ j++; c = argv[i][j]; switch (c) { case 'r': reuse = 0; break; case 'k': keepalive = 0; break; case '\0': fatal_failure(LINET_USAGE, 0, "option -a requires a modifier"); break; default: fatal_failure(LINET_USAGE, 0, "unknown modifier -a%c", c); break; } j++; if (argv[i][j] == '\0') { j = 1; i++; } break; case 'd' : /* disable sanity checks */ sane = 0; j++; break; /* strings */ case 'u': case 'g': case 'p': case 'r': case 'b': j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fatal_failure(LINET_USAGE, 0, "option -%c requires a parameter", c); } switch (c) { case 'u': user = argv[i] + j; break; case 'g': group = argv[i] + j; break; case 'p': port = argv[i] + j; break; case 'r': root = argv[i] + j; break; case 'b': interface = argv[i] + j; break; } i++; j = 1; break; case 'l': j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fatal_failure(LINET_USAGE, 0, "option -%c requires a parameter", c); } if (!isdigit(argv[i][j])) { fatal_failure(LINET_USAGE, 0, "option -%c requires a floating point value", c); } load_fd = open(LOADAVG, O_RDONLY); if(load_fd < 0){ fatal_failure(LINET_SYSTEM, errno, "unable to open %s to read load average", LOADAVG); } limit_load = atof(argv[i] + j); i++; j = 1; break; case 'n': case 'm': case 'i': case 'j': case 'q': case 't': j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fatal_failure(LINET_USAGE, 0, "option -%c requires a parameter", c); } if (!isdigit(argv[i][j])) { fatal_failure(LINET_USAGE, 0, "option -%c requires a numeric value", c); } value = atoi(argv[i] + j); switch (c) { case 'n': niceinc = value; break; case 'm': timeout = value; break; case 'i': instances = value; break; case 'j': just = value; break; case 'q': queue = value; break; case 't': ttl = value; break; } i++; j = 1; break; case 'o': j++; c = argv[i][j]; switch (c) { case 'c': tos = IPTOS_LOWCOST; break; case 'd': tos = IPTOS_LOWDELAY; break; case 'r': tos = IPTOS_RELIABILITY; break; case 't': tos = IPTOS_THROUGHPUT; break; case '\0': fatal_failure(LINET_USAGE, 0, "option -o requires a modifier"); break; default: fatal_failure(LINET_USAGE, 0, "unknown modifier -o%c", c); break; } j++; if (argv[i][j] == '\0') { j = 1; i++; } break; #ifdef USE_IDSA case 'k' : /* risk ratings */ j++; c = argv[i][j]; j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fatal_failure(LINET_USAGE, 0, "option -k%c requires a parameter", c); } risk = idsa_risk_parse(argv[i]+j); switch (c) { case 'a': arisk = risk; break; case 'c': crisk = risk; break; case 'i': irisk = risk; break; } i++; j = 1; break; case 'x': j++; c = argv[i][j]; switch (c) { case 'e': flags |= IDSA_F_ENV; /* honour IDSA_SOCKET */ break; case 'o': flags |= IDSA_F_FAILOPEN; /* continue on failure */ break; case 'u': flags |= IDSA_F_UPLOAD; /* allow uploading of rules */ break; case '\0': fatal_failure(LINET_USAGE, 0, "option -x requires a modifier"); break; default: fatal_failure(LINET_USAGE, 0, "unknown modifier -x%c", c); break; } j++; if (argv[i][j] == '\0') { j = 1; i++; } break; #endif case 's': j++; c = argv[i][j]; what = 0; switch (c) { case 'c': what = RLIMIT_CORE; break; case 'd': what = RLIMIT_DATA; break; case 'f': what = RLIMIT_FSIZE; break; case 'l': what = RLIMIT_MEMLOCK; break; case 'm': what = RLIMIT_RSS; break; case 'n': what = RLIMIT_NOFILE; break; case 's': what = RLIMIT_STACK; break; case 't': what = RLIMIT_CPU; break; case 'u': what = RLIMIT_NPROC; break; case 'v': what = RLIMIT_AS; break; case '\0': fatal_failure(LINET_USAGE, 0, "option -s requires a modifier"); break; default: fatal_failure(LINET_USAGE, 0, "unknown modifier -s%c", c); break; } j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fatal_failure(LINET_USAGE, 0, "option -s%c requires a parameter", c); } if (!isdigit(argv[i][j])) { fatal_failure(LINET_USAGE, 0, "option -s%c requires a numeric value", c); } value = atoi(argv[i] + j); if(resource_count >= LINET_MAXRES){ fatal_failure(LINET_USAGE, 0, "too many resource restrictions", c); } resource_table[resource_count][0] = what; resource_table[resource_count][1] = value; resource_count++; i++; j = 1; break; case '-': j++; break; case '\0': j = 1; i++; break; default: fatal_failure(LINET_USAGE, 0, "unknown option -%c", argv[i][j]); break; } } else { cmd = argv[i]; offset = i + 1; if (sane) { if (i + 1 >= argc){ fprintf(stderr, "%s: warning: zeroth argument should be specified\n", argv[0]); offset = i; } } i = argc; } } if (cmd == NULL) { fatal_failure(LINET_USAGE, 0, "require a command to run"); } if (!nofork) { fork_parent(argv[0]); } #ifdef USE_IDSA if (ic == NULL) { ic = idsa_open(LINETD, NULL, flags); } if (ic == NULL) { fprintf(stderr, "%s: unable to open idsa connection\n", argv[0]); exit(EX_UNAVAILABLE); } #endif sigfillset(&(sag.sa_mask)); sag.sa_flags = 0; sag.sa_handler = handle_child; if (sigaction(SIGCHLD, &sag, NULL)) { fatal_failure(LINET_SYSTEM, errno, "unable to set signal handler"); } sag.sa_handler = handle_stop; if (sigaction(SIGTERM, &sag, NULL)) { fatal_failure(LINET_SYSTEM, errno, "unable to set signal handler"); } lfd = setup_listener(argv[0], port, interface, queue, ttl, tos, reuse, keepalive); drop_root(argv[0], user, group, root); if(sane){ if(access(cmd, X_OK)){ fatal_failure(LINET_SYSTEM, errno, "\"%s\" %s", cmd, (cmd[0] == '/') ? "appears unavailable" : "might need an absolute path"); } } if (niceinc) { nice(niceinc); } #ifdef USE_IDSA if(idsa_set(ic, LINET_DR, LINET_SCHEME, 1, IDSA_R_SUCCESS, IDSA_R_UNKNOWN, IDSA_R_UNKNOWN, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_SSTART, "version", IDSA_T_STRING, VERSION, NULL) != IDSA_L_ALLOW){ fprintf(stderr, "%s: start disallowed\n", LINETD); return EX_NOPERM; } #endif if (!nofork) { close(STDERR_FILENO); } sigemptyset(&sst); sigaddset(&sst, SIGCHLD); sigaddset(&sst, SIGTERM); sigprocmask(SIG_BLOCK, &sst, NULL); /* disable child signal for everything execpt accept and sleep */ while (run) { #ifdef USE_IDSA nfd = accept_connection(lfd, arisk, crisk, irisk); #else nfd = accept_connection(lfd); #endif if (nfd >= 0) { run_command(lfd, cmd, &argv[offset], nfd, timeout); if(just){ just--; if(just == 0){ run = 0; } } } do{ /* check children and load */ busy = 0; hang = ((instances > 0) && (child_count >= instances)); /* actually wait for child */ if (zombies || hang) { if (waitpid(WAIT_ANY, &status, hang ? 0 : WNOHANG) > 0) { /* collect pids without risk of EINTR */ #ifdef USE_IDSA /* in theory this could parse signals and exit codes. Eg SIG{SEGV,BUS} == ES_INTERNAL etc */ if (WIFEXITED(status)) { exitcode = WEXITSTATUS(status); if (exitcode == 0) { idsa_set(ic, LINET_JD, LINET_SCHEME, 0, IDSA_R_NONE, IDSA_R_UNKNOWN, IDSA_R_UNKNOWN, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_WSTOP, NULL); } else { idsa_set(ic, LINET_JE, LINET_SCHEME, 0, IDSA_R_PARTIAL, IDSA_R_UNKNOWN, IDSA_R_PARTIAL, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_WFAIL, IDSA_ES, IDSA_T_STRING, IDSA_ES_OTHER, "exit", IDSA_T_INT, &exitcode, NULL); } } else if (WIFSIGNALED(status)) { killcode = WTERMSIG(status); idsa_set(ic, LINET_JS, LINET_SCHEME, 0, IDSA_R_PARTIAL, IDSA_R_UNKNOWN, IDSA_R_PARTIAL, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_WFAIL, IDSA_ES, IDSA_T_STRING, IDSA_ES_OTHER, "signal", IDSA_T_INT, &killcode, NULL); } #endif child_count--; busy = ! hang; } else { /* nothing more to collect */ zombies = 0; } } if((load_fd >= 0) && (zombies == 0)){ /* if zombies then loop will run again, defer load check */ if(get_load() > limit_load){ /* go to sleep if overloaded */ sigprocmask(SIG_UNBLOCK, &sst, NULL); /* enable child|term signal */ sleep(LINET_SLEEP); sigprocmask(SIG_BLOCK, &sst, NULL); /* disable child|term signal */ busy = run; } } } while (busy); /* end of child and delay loop */ } /* end of main loop */ if(lfd >= 0){ close(lfd); } if(load_fd >= 0){ close(load_fd); } #ifdef USE_IDSA idsa_set(ic, LINET_DE, LINET_SCHEME, 0, IDSA_R_TOTAL, IDSA_R_NONE, IDSA_R_UNKNOWN, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_SSTOP, "version", IDSA_T_STRING, VERSION, NULL); idsa_close(ic); #endif return 0; }