1/*
2
3   nsjail - logging
4   -----------------------------------------
5
6   Copyright 2014 Google Inc. All Rights Reserved.
7
8   Licensed under the Apache License, Version 2.0 (the "License");
9   you may not use this file except in compliance with the License.
10   You may obtain a copy of the License at
11
12     http://www.apache.org/licenses/LICENSE-2.0
13
14   Unless required by applicable law or agreed to in writing, software
15   distributed under the License is distributed on an "AS IS" BASIS,
16   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   See the License for the specific language governing permissions and
18   limitations under the License.
19
20*/
21
22#include "log.h"
23#include "common.h"
24
25#include <errno.h>
26#include <fcntl.h>
27#include <getopt.h>
28#include <limits.h>
29#include <pthread.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <time.h>
37#include <unistd.h>
38
39#include "util.h"
40
41#if defined(_HF_ARCH_LINUX)
42#include <sys/syscall.h>
43#define __hf_pid() (pid_t) syscall(__NR_gettid)
44#else /* defined(_HF_ARCH_LINUX) */
45#define __hf_pid() getpid()
46#endif /* defined(_HF_ARCH_LINUX) */
47
48static int log_fd = STDERR_FILENO;
49static bool log_fd_isatty = false;
50enum llevel_t log_level = INFO;
51static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
52
53__attribute__((constructor)) static void log_init(void) {
54    log_level = INFO;
55    log_fd = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 0);
56    if (log_fd == -1) {
57        log_fd = STDERR_FILENO;
58    }
59    log_fd_isatty = isatty(log_fd);
60}
61
62/*
63 * Log to stderr by default. Use a dup()d fd, because in the future we'll associate the
64 * connection socket with fd (0, 1, 2).
65 */
66bool logInitLogFile(const char* logfile, enum llevel_t ll) {
67    log_level = ll;
68
69    if (logfile == NULL) {
70        return true;
71    }
72
73    log_fd = open(logfile, O_CREAT | O_RDWR | O_APPEND, 0640);
74    if (log_fd == -1) {
75        log_fd = STDERR_FILENO;
76        PLOG_E("Couldn't open logfile open('%s')", logfile);
77        return false;
78    }
79    log_fd_isatty = (isatty(log_fd) == 1 ? true : false);
80    return true;
81}
82
83void logLog(enum llevel_t ll, const char* fn, int ln, bool perr, const char* fmt, ...) {
84    char strerr[512];
85    if (perr == true) {
86        snprintf(strerr, sizeof(strerr), "%s", strerror(errno));
87    }
88    struct ll_t {
89        const char* descr;
90        const char* prefix;
91        const bool print_funcline;
92        const bool print_time;
93    };
94    static const struct ll_t logLevels[] = {
95        {"F", "\033[7;35m", true, true},
96        {"E", "\033[1;31m", true, true},
97        {"W", "\033[0;33m", true, true},
98        {"I", "\033[1m", false, false},
99        {"D", "\033[0;4m", true, true},
100        {"HR", "\033[0m", false, false},
101        {"HB", "\033[1m", false, false},
102    };
103
104    time_t ltstamp = time(NULL);
105    struct tm utctime;
106    localtime_r(&ltstamp, &utctime);
107    char timestr[32];
108    if (strftime(timestr, sizeof(timestr) - 1, "%FT%T%z", &utctime) == 0) {
109        timestr[0] = '\0';
110    }
111
112    /* Start printing logs */
113    {
114        MX_LOCK(&log_mutex);
115
116        if (log_fd_isatty) {
117            dprintf(log_fd, "%s", logLevels[ll].prefix);
118        }
119        if (logLevels[ll].print_time) {
120            dprintf(log_fd, "[%s][%s][%d] ", timestr, logLevels[ll].descr, __hf_pid());
121        }
122        if (logLevels[ll].print_funcline) {
123            dprintf(log_fd, "%s():%d ", fn, ln);
124        }
125
126        va_list args;
127        va_start(args, fmt);
128        vdprintf(log_fd, fmt, args);
129        va_end(args);
130
131        if (perr == true) {
132            dprintf(log_fd, ": %s", strerr);
133        }
134        if (log_fd_isatty) {
135            dprintf(log_fd, "\033[0m");
136        }
137        dprintf(log_fd, "\n");
138
139        MX_UNLOCK(&log_mutex);
140    }
141    /* End printing logs */
142
143    if (ll == FATAL) {
144        exit(1);
145    }
146}
147
148void logStop(int sig) { LOG_I("Server stops due to fatal signal (%d) caught. Exiting", sig); }
149
150void logRedirectLogFD(int fd) { log_fd = fd; }
151
152void logDirectlyToFD(const char* msg) { dprintf(log_fd, "%s", msg); }
153
154pthread_mutex_t* logMutexGet(void) { return &log_mutex; }
155
156void logMutexReset(void) { pthread_mutex_init(&log_mutex, NULL); }
157
158bool logIsTTY(void) { return log_fd_isatty; }
159
160int logFd(void) { return log_fd; }
161