aboutsummaryrefslogtreecommitdiffstats
path: root/init/log.c
blob: 83702c675c75fc3bc11ffcf2ff221627a657572f (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include <execinfo.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <unistd.h>
#include <log.h>
#include <util.h>

extern int subsystem_change_mode(int pid, char mode);
extern char *subsystem_get_name(int pid);
extern int mainpid;

static const char *colors[] = {
    [EMERG_LOGLEVEL] = ANSI_BLINK ANSI_REVERSE ANSI_BOLD ANSI_RED,
    [ALERT_LOGLEVEL] = ANSI_REVERSE ANSI_BOLD ANSI_RED,
    [CRIT_LOGLEVEL] = ANSI_BOLD ANSI_RED,
    [ERR_LOGLEVEL] = ANSI_RED,
    [WARNING_LOGLEVEL] = ANSI_BOLD,
    [NOTICE_LOGLEVEL] = ANSI_BRIGHT_WHITE,
    [INFO_LOGLEVEL] = ANSI_RESET,
    [DEBUG_LOGLEVEL] = ANSI_ITALIC ANSI_BRIGHT_BLUE,
};

static const char *mode_to_string[] = {
    [PANICMODE_DEBUGONLY] = "subsystem OOPS",
    [PANICMODE_RESPAWN] = "subsystem failure",
    [PANICMODE_DIE] = "catastrophic failure",
};

static int console_lock = 0;

#define MAX_TRY_COUNT 2 << 16
static void obtain_console_lock(void)
{
    int try_count = 0;
    register int rax asm("rax");
retry:
    while(console_lock && try_count <= MAX_TRY_COUNT)
        try_count += 1;

    asm("mov %0, 1 \n"
        "xchg %0, %1" : "=r" (rax), "=m" (console_lock));

    if(rax > 0 && try_count <= MAX_TRY_COUNT)
        goto retry;

    if(try_count > MAX_TRY_COUNT) {
        print(LOG_SOH "\3" "4" "log: broken console lock");
    }

    return;
}

static int vaprint(const char *fmt, va_list ap)
{
    int loglevel = DEFAULT_LOGLEVEL;
    int dolocks = 1;
    int parsecolon = 1;
    if(fmt[0] == LOG_SOH_ASCII) {
        loglevel = (fmt[2] - 0x30) % 10;
        char flags = fmt[1];
        if(flags & 1 << 1)
            dolocks = 0;
        if(flags & 1 << 2)
            parsecolon = 0;
        fmt += 3;
    }

    /* not going to be printed? dont bother! */
    if(loglevel > CONSOLE_LOGLEVEL)
        return 0;

    /* we essentially print the user's raw input to its own buffer,
       later we will parse it and print out ANSI colors and what not */
    char buf[512];

    vsnprintf(buf, 512, fmt, ap);
    buf[512 - 1] = '\0';

    size_t colon = 0;
    if(parsecolon) {
        for(; colon < strlen(buf); ++colon) {
            if(buf[colon] == ':')
                break;
        }
    }

    char tsbuf[64] = "\0";
    struct timeval time;
    gettimeofday(&time, NULL);
    snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld] ",
            (long)time.tv_sec % 100000, (long)time.tv_usec);

    /* spin lock, at the cost of architecture portability
       concurrency is something that we need to adjust for, and the
       console will be scrambled and unreadable if we allow writing all
       at the same time. I considered simply writing all at once, but
       ended up just not caring enough to the point where spinlocks
       prevail. */
    if(dolocks)
        obtain_console_lock();


    /* we want to support stuff without colons, but frankly I havent
       tested this at time of writing. will find out later */
    writeputs(ANSI_RESET ANSI_GREEN);
    writeputs(tsbuf);
    writeputs(ANSI_RESET);
    if(parsecolon && buf[colon] == ':') {
        writeputs(colors[loglevel]);
        writeputs(ANSI_YELLOW);
        write(STDOUT_FILENO, buf, colon);
        writeputs(ANSI_RESET);
    }
    writeputs(colors[loglevel]);
    if(colon && *(buf + colon)) {
        writeputs(buf + colon);
    } else {
        writeputs(buf);
    }
    writeputs(ANSI_RESET);
    write(STDOUT_FILENO, "\n", 1);
    if(dolocks)
        console_lock = 0;
    return 0;
}

void _panic(const char *fileorigin,
        const int lineorigin,
        const char *fmt, ...)
{
    char mode = PANICMODE_DIE;
    int pid = getpid();
    if(fmt[0] == LOG_SOH_ASCII) {
        mode = fmt[1];
        /* cannot respawn main thread */
        if(pid == mainpid && mode == PANICMODE_RESPAWN)
            mode = PANICMODE_DIE;
        fmt += 2;
    }

#define NOLOCK(loglevel) LOG_SOH "\3" loglevel
    va_list ap;
    va_start(ap, fmt);
    char *_fmt = malloc(strlen(fmt) + 4 * sizeof(char));
    sprintf(_fmt, NOLOCK("1") "%s", fmt);

    void **backtrace_addresses = malloc(sizeof(void*) * 32);
    int backtrace_count = backtrace(backtrace_addresses, 32);
    char **backtrace_symbolnames =
        backtrace_symbols(backtrace_addresses, backtrace_count);

    obtain_console_lock();

    print(NOLOCK("5") "------------[ cut here ]------------");
    print(LOG_SOH "\7""0"  "%s at %s:%d", mode_to_string[(int)mode],
            fileorigin, lineorigin);
    vaprint(_fmt, ap);
    print(LOG_SOH "\7""7" "Call Trace:");
    for(int i = 0; i < backtrace_count; ++i) {
        print(NOLOCK("7") " [0x%016x]  %s", backtrace_addresses[i],
                backtrace_symbolnames[i]);
    }
    if(mainpid == pid){
        print(NOLOCK("7") "                       <start of main thread>");
    } else {
        print(NOLOCK("7") "                       <start of %s[%d]>",
                subsystem_get_name(pid), pid);
    }

    /* if we are going to die, we dont really need to clean up */
    if(mode == PANICMODE_DIE) {
        kill(-getpgid(pid), SIGTERM);
        raise(SIGTERM);
        exit(0);
    }

    print(NOLOCK("5") "------------[ cut here ]------------");

    console_lock = 0;
    free(_fmt);
    free(backtrace_symbolnames);
    free(backtrace_addresses);
    va_end(ap);

    if(mode == PANICMODE_DEBUGONLY)
        return;

    if(pid != mainpid && mode == PANICMODE_RESPAWN) {
        /* we want to let the main process handle the rest */
        subsystem_change_mode(pid, mode);
        syscall(SYS_exit_group, 0);
    }
}

int print(const char *fmt, ...)
{
    int ret = 0;
    va_list ap;
    va_start(ap, fmt);
    ret = vaprint(fmt, ap);
    va_end(ap);
    return ret;
}