int main (void) { pid_t pid; save_parent = getpid (); /* Don't run forever. */ alarm (180); /* The parent and child should basically run forever without tripping on any debug event. We want to check that GDB updates the parent and child running states correctly right after the fork. */ pid = fork (); if (pid > 0) return fork_parent (); else if (pid == 0) return fork_child (); else { perror ("fork"); return 1; } }
int daemon(int nochdir, int noclose) { int fd; if (fork_parent() == -1) return -1; if (setsid() == -1) return -1; if (!nochdir) chdir("/"); if (!noclose) { struct STAT st; if ((fd = open_not_cancel(_PATH_DEVNULL, O_RDWR, 0)) != -1 && (__builtin_expect (FSTAT (fd, &st), 0) == 0)) { if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } else { /* We must set an errno value since no function call actually failed. */ close_not_cancel_no_status (fd); __set_errno (ENODEV); return -1; } } else { close_not_cancel_no_status (fd); return -1; } } return 0; }
int daemon( int nochdir, int noclose ) { int fd; if (fork_parent() == -1) return -1; if (setsid() == -1) return(-1); if (!nochdir) chdir("/"); if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } 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; }
int main(int argc, char **argv) { #define BUFFER 64 char buffer[BUFFER]; char *level, *app, *server, *output; int run, fd, i, j, c, verbose, attempts, detach, result, truncate, flags; struct katcl_parse *p; struct katcl_line *ls, *lo; struct sigaction sa; time_t now; struct tm *local; i = j = 1; app = argv[0]; verbose = 0; attempts = 2; detach = 0; truncate = 0; server = getenv("KATCP_SERVER"); if(server == NULL){ server = "localhost"; } output = NULL; level = NULL; flags = 0; /* placate -Wall */ while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); return EX_OK; case 'v' : verbose++; j++; break; case 'd' : detach = 1; j++; break; case 'f' : detach = 0; j++; break; case 't' : truncate = 1; j++; break; case 'q' : verbose = 0; j++; break; case 'l' : case 'o' : case 'a' : case 's' : j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fprintf(stderr, "%s: usage: argument needs a parameter\n", app); return EX_USAGE; } switch(c){ case 'l' : level = argv[i] + j; break; case 'o' : output = argv[i] + j; break; case 'a' : attempts = atoi(argv[i] + j); break; case 's' : server = argv[i] + j; break; } i++; j = 1; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: fprintf(stderr, "%s: usage: unknown option -%c\n", app, argv[i][j]); return EX_USAGE; } } else { if(output){ fprintf(stderr, "%s: usage: unexpected extra argument %s (can only save to one file)\n", app, argv[i]); return EX_USAGE; } output = argv[i]; i++; } } if(detach){ if(fork_parent() < 0){ fprintf(stderr, "%s: unable to detach process\n", app); return EX_OSERR; } } sa.sa_handler = handle_signal; #if 0 sa.sa_flags = SA_RESTART; #endif sa.sa_flags = 0; sigemptyset(&(sa.sa_mask)); sigaction(SIGHUP, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); if(server == NULL){ server = "localhost:7147"; } if(level){ log_changed = 1; log_level = log_to_code_katcl(level); if(log_level < 0){ fprintf(stderr, "%s: usage: invalid initial log priority %s\n", app, level); return EX_USAGE; } } if(output == NULL){ if(detach == 1){ fprintf(stderr, "%s: usage: need a filename as target\n", app); return EX_USAGE; } fd = STDOUT_FILENO; } else { flags = O_CREAT | O_WRONLY; if(truncate == 0){ flags |= O_APPEND; } fd = open(output, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if(fd < 0){ fprintf(stderr, "%s: unable to open file %s: %s\n", app, output, strerror(errno)); return EX_OSERR; } } lo = create_katcl(fd); if(lo == NULL){ fprintf(stderr, "%s: unable to allocate log state\n", app); return EX_OSERR; } /**********************/ while((attempts-- > 0) && ((fd = net_connect(server, 0, 0)) < 0)){ sleep(1); } if(attempts <= 0){ sync_message_katcl(lo, KATCP_LEVEL_FATAL, NAME, "unable to connect to %s", server); return EX_UNAVAILABLE; } ls = create_katcl(fd); if(ls == NULL){ sync_message_katcl(lo, KATCP_LEVEL_FATAL, NAME, "unable to allocate parser state"); return EX_OSERR; } if(detach){ fclose(stderr); } time(&now); local = localtime(&now); strftime(buffer, BUFFER - 1, "%Y-%m-%dT%H:%M:%S", local); sync_message_katcl(lo, KATCP_LEVEL_INFO, NAME, "monitor start for %s at %s", server, buffer); for(run = 1; run > 0;){ if(log_reload > 0){ if(output){ fd = open(output, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if(fd >= 0){ exchange_katcl(lo, fd); } } log_reload = 0; } /* WARNING: will only run after the next message - may have to interrupt syscall to get past this */ if(log_changed > 0){ level = log_to_string_katcl(log_level); if(level){ p = create_parse_katcl(); if(p){ add_string_parse_katcl(p, KATCP_FLAG_STRING | KATCP_FLAG_FIRST, "?log-level"); add_string_parse_katcl(p, KATCP_FLAG_STRING | KATCP_FLAG_LAST, level); append_parse_katcl(lo, p); /* dodgy refcount dealings: p is created with refcount = 0, so can only do write at end, otherwise may end up being deallocated */ append_parse_katcl(ls, p); write_katcl(ls); } } else { sync_message_katcl(lo, KATCP_LEVEL_ERROR, NAME, "invalid log priority number %d", level); } log_changed = 0; } result = read_katcl(ls); if(result < 0){ sync_message_katcl(lo, KATCP_LEVEL_FATAL, NAME, "read from network failed: %s", strerror(errno)); return EX_OSERR; } if(result == 1){ run = 0; } while(have_katcl(ls)){ p = ready_katcl(ls); if(p){ append_parse_katcl(lo, p); } } write_katcl(lo); } return EX_OK; #undef BUFFER }