aboutsummaryrefslogtreecommitdiffstats
path: root/init/subsys.c
blob: a88046d29c01a2893b6bb5a1b14ff2d23970a81c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <unistd.h>

#include <config.h>
#include <log.h>
#include <util.h>

#define STACK_SIZE 8192 * 512
#define MAX_SUBSYSTEMS 32

struct subsystem_info {
    char *fn_name;
    int (*fn)(void);
    int pid;
    void *stack;
};

extern int mainpid;
static struct subsystem_info *subsystems[MAX_SUBSYSTEMS + 1];
static int subsystem_count = 0;

static int __subsystem_entry(struct subsystem_info *info)
{
    /* entry point from clone(). we setup the process name so we know
       what we are looking at from a glance in a ps view or htop or
       whatever. */
    char *name = malloc(16 * sizeof(char));
    snprintf(name, 16, NAME_SHORTHAND ": %s", info->fn_name);
    name[15] = '\0';
    prctl(PR_SET_NAME, name);
    free(name);

    print(LOG_DEBUG "subsys: starting subsystem %s (%d)", info->fn_name, getpid());

    int ret = info->fn();

    return ret;
}

int subsystem_handle_term(int pid)
{
    for(int i = 0; i < MAX_SUBSYSTEMS; ++i) {
        struct subsystem_info *subsystem = subsystems[i];
        if(!subsystem || subsystem->pid != pid)
            continue;

        print(LOG_DEBUG "subsys: subsystem terminated %s (%d)", subsystem->fn_name, pid);

        if(munmap(subsystem->stack, STACK_SIZE) < 0)
            print(LOG_EMERG "subsys: failed to deallocate stack for subsystem %s (%d) (errno %d)", subsystem->fn_name, pid, errno);
        subsystems[i] = 0;
        free(subsystem);

        return 0;
    }

    return 1;
}

int __impl_start_subsystem(char *fn_name, int (*fn)(void))
{
    if(getpid() != mainpid) {
        print(LOG_CRIT "subsys: cannot perform subsystem inception (attempted from %d)", getpid());
        return 1;
    }
    if(subsystem_count >= MAX_SUBSYSTEMS) {
        print(LOG_CRIT "subsys: cannot start subsystem %s: reached maximum number of subsystems", fn_name);
        return 1;
    }

    /* because CLONE_VM is being set, our stack is not duplicated and
       therefore we need to map a stack */
    void *stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_STACK | MAP_PRIVATE, -1, 0);
    if((long)stack <= 0) {
        print(LOG_CRIT "subsys: cannot start subsystem %s: failed to allocate stack (errno %d)", fn_name, errno);
        return 1;
    }

    /* the libc gods have graced us with the ability to pass one (1) arg
       to the function. struct required. the absence of a free is not a
       memory leak because we free it above. */
    struct subsystem_info *info = malloc(sizeof(struct subsystem_info));
    info->fn_name = fn_name;
    info->fn = fn;
    info->stack = stack;

    int pid = clone((int (*)(void *))__subsystem_entry, (void *)((long)stack + STACK_SIZE), CLONE_FILES | CLONE_VM | SIGCHLD, info);
    info->pid = pid;
    if(pid < 0) {
        print(LOG_CRIT "subsys: cannot start subsystem %s: clone failed (errno %d)", fn_name, errno);
        munmap(stack, STACK_SIZE);
        free(info);
        return 1;
    }

    for(int i = 0; i < MAX_SUBSYSTEMS; ++i) {
        if(!subsystems[i]) {
            subsystems[i] = info;
            ++subsystem_count;
            break;
        }
    }

    return 0;
}