Example #1
0
/**
 * Launches a program and wait for it to finish.
 *
 * If uid is not UID_MASTER, the standard input and output will be redirected
 * to /dev/null.
 *
 * If uid is UID_MASTER and the program exits with non-zero status (or an error
 * occurs during the setup of the child process), the vm will be shut down.
 *
 * The umask also depends on uid. For UID_MASTER, files will be private by
 * default. For other users, files will be public by default.
 *
 * @param cmd the command to execute (will be modified)
 * @param uid the user id that will execute the program
 */
static void launch(char *cmd, uid_t uid) {
    char *argv[CONTROL_MAXARGS+1];
    pid_t pid;
    int status;

    splitargs(cmd, argv);
    pid = fork();
    if(pid < 0) {
        // Error
        die("fork", NULL);
    } else if(pid > 0) {
        // Parent
        wait(&status);
        if(uid == UID_MASTER &&
                (!WIFEXITED(status) || WEXITSTATUS(status) != 0))
            shutdown();
    } else {
        // Child
        childcheck("close /task/control", fclose(fcontrol));
        if(uid == UID_MASTER) {
            childcheck("set gid", setgid(0));
            childcheck("set uid", setuid(uid));
            // Make new files private to master by default
            umask(077);
        } else {
            childcheck("set gid", setgid(2));
            childcheck("set uid", setuid(uid));
            // Make new files public by default
            umask(000);
            // Deny access to input and output
            if(freopen("/dev/null", "r", stdin) == NULL ||
               freopen("/dev/null", "w", stdout) == NULL ||
               freopen("/dev/null", "w", stderr) == NULL)
                childcheck("reopen std streams", 1);
        }
        execve(argv[0], argv, ENVIRONMENT);
        childcheck("execve", 1);  // if we arrive here, there was an error launching cmd.
    }
}
Example #2
0
char *
mother_getlimitinfo(void)
{
    const char *function = "mother_getlimitinfo()";
    static char buf[2048];
    const int fds_per_proc = 2; /* two pipes */
    const char *limiter, *prefix = "max clients calculation will not be done";
    struct rlimit maxfd, maxproc;
    char maxprocstr[64], maxfdstr[64];
    unsigned long negc_proc, negc_fd, reqc_proc, reqc_fd, ioc_proc, ioc_fd,
             negc_limit, reqc_limit, ioc_limit,
             proc_free, proc_used, procs, fds_free;

    if (getrlimit(RLIMIT_NOFILE, &maxfd) != 0) {
        swarn("%s: getrlimit(RLIMIT_NOFILE) failed", function);
        return "";
    }

#ifdef RLIMIT_NPROC
    if (getrlimit(RLIMIT_NPROC, &maxproc) != 0) {
        swarn("%s: %s: getrlimit(RLIMIT_NPROC) failed", function, prefix);
        return "";
    }
#else /* !RLIMIT_NPROC */
    if ((maxproc.rlim_cur = (rlim_t)sysconf(_SC_CHILD_MAX)) == (rlim_t)-1) {
        swarn("%s: %s: sysconf(_SC_CHILD_MAX) failed", function, prefix);
        return "";
    }

    maxproc.rlim_max = maxproc.rlim_cur;
#endif /* !RLIMIT_NPROC */

    if (maxfd.rlim_cur   == RLIM_INFINITY
            &&  maxproc.rlim_cur == RLIM_INFINITY)
        return "no applicable environment resource limits configured";

    proc_used   = sockscf.option.serverc
                  + childcheck(-PROC_NEGOTIATE) / SOCKD_NEGOTIATEMAX
                  + childcheck(-PROC_REQUEST)   / SOCKD_REQUESTMAX
                  + childcheck(-PROC_IO)        / SOCKD_IOMAX;
    proc_free   = maxproc.rlim_cur - proc_used;

    if (maxproc.rlim_cur == RLIM_INFINITY)
        snprintf(maxprocstr, sizeof(maxprocstr), "no limit");
    else
        snprintf(maxprocstr, sizeof(maxprocstr),
                 "%lu (%lu free)",
                 (unsigned long)maxproc.rlim_cur, proc_free);

    fds_free = freedescriptors(NULL, NULL) - FDPASS_MAX;
    if (maxfd.rlim_cur == RLIM_INFINITY)
        snprintf(maxfdstr, sizeof(maxfdstr), "no limit");
    else
        snprintf(maxfdstr, sizeof(maxfdstr),
                 "%lu (%lu free)", (unsigned long)maxfd.rlim_cur, fds_free);

    /*
     * Calculate the max number of new clients we can handle based on both
     * the process resource limit and the fd limit.
     */

    /*
     * Process-based limit, disregarding any other limits.
     * Each process can handle SOCKD_{NEGOTIATE,REQUEST,IO}MAX clients.
     * We can create a max number of proc_free additional processes, so
     * the number of additional clients we can handle is the number
     * of additional clients multiplied by the number of clients each
     * process can handle.
     */
    negc_proc = proc_free * SOCKD_NEGOTIATEMAX;
    reqc_proc = proc_free * SOCKD_REQUESTMAX;
    ioc_proc  = proc_free * SOCKD_IOMAX;

    /*
     * FD-based limit, disregarding any other limits.
     * With the fds we have, we can create a given number of additional
     * processes (procs).
     * Each process needs fds_per_proc, and an additional
     * SOCKD_{NEGOTIATE,REQUEST,IO}MAX * <number of fds per client in this
     * phase> fds to handle the max number of clients, meaning we can handle
     * the following number of additional clients:
     */
    procs   = fds_free / fds_per_proc;
    negc_fd = MIN(((fds_free - fds_per_proc) / 1), SOCKD_NEGOTIATEMAX)
              * procs;
    reqc_fd = MIN(((fds_free - fds_per_proc) / FDPASS_MAX), SOCKD_REQUESTMAX)
              * procs;
    ioc_fd  = MIN(((fds_free - fds_per_proc) / FDPASS_MAX), SOCKD_IOMAX)
              * procs;

    /*
     * Different process-types could be limited by different things, but
     * ignore that here.
     */
    if (negc_proc < negc_fd
            ||  reqc_proc < reqc_fd
            ||  ioc_proc  < ioc_fd) {
        limiter = "process";

        negc_limit = negc_proc;
        reqc_limit = reqc_proc;
        ioc_limit  = ioc_proc;
    }
    else {
        limiter = "open file";

        negc_limit = negc_fd;
        reqc_limit = reqc_fd;
        ioc_limit  = ioc_fd;
    }

    snprintf(buf, sizeof(buf), "max limits: processes: %s, files: %s, "
             "%s-slots: %lu, %s-slots: %lu, %s-slots: %lu "
             "(max clients limited by %s limit)",
             maxprocstr,
             maxfdstr,
             childtype2string(PROC_NEGOTIATE),
             negc_limit,
             childtype2string(PROC_REQUEST),
             reqc_limit,
             childtype2string(PROC_IO),
             ioc_limit,
             limiter);

    return buf;
}