aboutsummaryrefslogtreecommitdiffstats
path: root/init/subsys.c
blob: 32b7cd3ac82293c8ae65e056e9b59fd514cb01b3 (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#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 MAX_SUBSYSTEMS 32

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

extern long stack_size;
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;
}

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

        return subsystem->fn_name;
    }
    return 0;
}

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

        subsystem->mode = mode;
        return 0;
    }

    return 1;
}

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;
    info->mode = 'o';

    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;
}