Exemplo n.º 1
0
/*
 * The dumb-init signal handler.
 *
 * The main job of this signal handler is to forward signals along to our child
 * process(es). In setsid mode, this means signaling the entire process group
 * rooted at our child. In non-setsid mode, this is just signaling the primary
 * child.
 *
 * In most cases, simply proxying the received signal is sufficient. If we
 * receive a job control signal, however, we should not only forward it, but
 * also sleep dumb-init itself.
 *
 * This allows users to run foreground processes using dumb-init and to
 * control them using normal shell job control features (e.g. Ctrl-Z to
 * generate a SIGTSTP and suspend the process).
 *
 * The libc manual is useful:
 * https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html
 *
 * When running in setsid mode, however, it is not sufficient to forward
 * SIGTSTP/SIGTTIN/SIGTTOU in most cases. If the process has not added a custom
 * signal handler for these signals, then the kernel will not apply default
 * signal handling behavior (which would be suspending the process) since it is
 * a member of an orphaned process group.
 *
 * Sadly this doesn't appear to be well documented except in the kernel itself:
 * https://github.com/torvalds/linux/blob/v4.2/kernel/signal.c#L2296-L2299
 *
 * Forwarding SIGSTOP instead is effective, though not ideal; unlike SIGTSTP,
 * SIGSTOP cannot be caught, and so it doesn't allow processes a change to
 * clean up before suspending. In non-setsid mode, we proxy the original signal
 * instead of SIGSTOP for this reason.
*/
void handle_signal(int signum) {
    DEBUG("Received signal %d.\n", signum);

    if (
        signum == SIGTSTP || // tty: background yourself
        signum == SIGTTIN || // tty: stop reading
        signum == SIGTTOU    // tty: stop writing
    ) {
        if (use_setsid) {
            DEBUG("Running in setsid mode, so forwarding SIGSTOP instead.\n");
            forward_signal(SIGSTOP);
        } else {
            DEBUG("Not running in setsid mode, so forwarding the original signal (%d).\n", signum);
            forward_signal(signum);
        }

        DEBUG("Suspending self due to TTY signal.\n");
        kill(getpid(), SIGSTOP);
    } else {
        forward_signal(signum);
    }

    signal_forwarded = 1;
}
Exemplo n.º 2
0
int main(int argc, char *argv[]) {
    int signum, opt;

    struct option long_options[] = {
        {"help",         no_argument, NULL, 'h'},
        {"single-child", no_argument, NULL, 'c'},
        {"verbose",      no_argument, NULL, 'v'},
        {"version",      no_argument, NULL, 'V'},
    };
    while ((opt = getopt_long(argc, argv, "+hvVc", long_options, NULL)) != -1) {
        switch (opt) {
            case 'h':
                print_help(argv);
                return 0;
            case 'v':
                debug = 1;
                break;
            case 'V':
                fprintf(stderr, "dumb-init v%s", VERSION);
                return 0;
            case 'c':
                use_setsid = 0;
                break;
            default:
                return 1;
        }
    }

    if (optind >= argc) {
        fprintf(
            stderr,
            "Usage: %s [option] program [args]\n"
            "Try %s --help for full usage.\n",
            argv[0], argv[0]
        );
        return 1;
    }
    char **cmd = &argv[optind];

    char *debug_env = getenv("DUMB_INIT_DEBUG");
    if (debug_env && strcmp(debug_env, "1") == 0) {
        debug = 1;
        DEBUG("Running in debug mode.\n");
    }

    char *setsid_env = getenv("DUMB_INIT_SETSID");
    if (setsid_env && strcmp(setsid_env, "0") == 0) {
        use_setsid = 0;
        DEBUG("Not running in setsid mode.\n");
    }

    /* register signal handlers */
    for (signum = 1; signum < 32; signum++) {
        if (signum == SIGKILL || signum == SIGSTOP || signum == SIGCHLD)
            continue;

        if (signal(signum, handle_signal) == SIG_ERR) {
            PRINTERR("Couldn't register signal handler for signal `%d`. Exiting.\n", signum);
            return 1;
        }
    }

    /* launch our process */
    child_pid = fork();

    if (child_pid < 0) {
        PRINTERR("Unable to fork. Exiting.\n");
        return 1;
    }

    if (child_pid == 0) {
        if (use_setsid) {
            pid_t result = setsid();
            if (result == -1) {
                PRINTERR(
                    "Unable to setsid (errno=%d %s). Exiting.\n",
                    errno,
                    strerror(errno)
                );
                exit(1);
            }
            DEBUG("setsid complete.\n");
        }

        execvp(cmd[0], &cmd[0]);

        // if this point is reached, exec failed, so we should exit nonzero
        PRINTERR("%s: %s\n", argv[1], strerror(errno));
        exit(2);
    } else {
        pid_t killed_pid;
        int child_exit_status = 0, exit_status, status;

        DEBUG("Child spawned with PID %d.\n", child_pid);

        while ((killed_pid = waitpid(-1, &status, 0))) {
            exit_status = WEXITSTATUS(status);
            DEBUG("A child with PID %d exited with exit status %d.\n", killed_pid, exit_status);

            if (killed_pid == -1) {
                if (errno == 10) {
                    DEBUG("No more child processes to wait for. Exiting.\n");
                    exit_status = child_exit_status;
                } else {
                    PRINTERR(
                        "waitpid (errno=%d %s). Exiting.\n",
                        errno,
                        strerror(errno)
                    );
                }

                exit(exit_status);

            } else if (killed_pid == child_pid) {

                DEBUG("Child exited with status %d.\n", exit_status);

                if (use_setsid) {
                    // send SIGTERM to any remaining children if not using
                    // single child mode and not done already
                    if (!signal_forwarded) {
                        forward_signal(SIGTERM);
                    }
                } else {
                    // in single child mode leave after direct child exited if
                    // we are not init
                    if (getpid() != 1) {
                        DEBUG("Goodbye.\n");
                        exit(exit_status);
                    }
                }

                child_exit_status = exit_status;
            }

        }
    }

    return 0;
}
Exemplo n.º 3
0
Arquivo: main.c Projeto: czhu12/cs317
int main(int argc, char *argv[]) {
  
  FILE *input;
  char *filename;
  char line[MAXLINE];
  char *command, *fromstr, *tostr, *signal_type, *source, *destination, *tokptr;
  system_state state;
  entity from, to;
  signal signal;
  int i;
  
  if (argc == 1) {
    filename = "/dev/stdin";
  }
  else {
    filename = argv[1];
    if (strcmp(filename, "-"))
      filename = "/dev/stdin";
  }
  
  input = fopen(filename, "r");
  if (!input) {
    perror("Could not open file for reading");
    return 2;
  }
  
  initialize_system_state(&state);
  
  while(fgets(line, MAXLINE, input)) {
    
    command = strtok_r(line, " \t\r\n", &tokptr);
    if (!command) continue;
    
    if (!strcasecmp(command, "SIGNAL")) {
      
      fromstr = strtok_r(NULL, " \t\r\n", &tokptr);
      strtok_r(NULL, " \t\r\n", &tokptr); // ignore "to"
      tostr = strtok_r(NULL, ": \t\r\n", &tokptr);
      signal_type = strtok_r(NULL, " \t\r\n", &tokptr);
      source = strtok_r(NULL, " \t\r\n", &tokptr);
      strtok_r(NULL, " \t\r\n", &tokptr); // ignore potential "to"
      destination = strtok_r(NULL, " \t\r\n", &tokptr);
      
      from = str_to_entity(fromstr);
      to = str_to_entity(tostr);
      signal.type = SIGNAL_INVALID;
      signal.source = str_to_connection(source);
      signal.destination = str_to_connection(destination);
      
      for (i = 0; i < NUM_POSSIBLE_SIGNALS; i++)
	if (!strcasecmp(signal_type, signal_name[i]))
	  signal.type = i;
      
      forward_signal(&state, from, to, signal);
      printf("\n");
    }
    else if (!strcasecmp(command, "QUIT")) {
      
      break;
    }
    else {
      
      printf("Invalid command: %s\n", command);
    }
  }
  
  print_state(&state);
  destroy_system_state(&state);
  
  if (ferror(input)) {
    perror("Could not read from input file");
    fclose(input);
    return 3;
  }
  
  fclose(input);
  
  return EXIT_SUCCESS;
}