1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <time.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 
12 #include "slog.h"
13 #include "server.h"
14 #include "conf.h"
15 
16 #if SLOG_MSG_MAX_LEN < 64
17 #error "SLOG_MSG_MAX_LEN must be at least 64"
18 #endif
19 
20 /**
21  * Initialize log writer.
22  */
23 void
slog_init(struct server * s)24 slog_init(struct server *s) {
25 
26 	s->log.self = getpid();
27 
28 	if(s->cfg->logfile) {
29 
30 		int old_fd = s->log.fd;
31 
32 		s->log.fd = open(s->cfg->logfile,
33 			O_WRONLY | O_APPEND | O_CREAT, S_IRUSR|S_IWUSR);
34 
35 		/* close old log */
36 		if (old_fd != -1) {
37 			close(old_fd);
38 		}
39 
40 		if (s->log.fd != -1)
41 			return;
42 
43 		fprintf(stderr, "Could not open %s: %s\n", s->cfg->logfile,
44 				strerror(errno));
45 	}
46 	s->log.fd = 2; /* stderr */
47 }
48 
49 static void
slog_fsync_tick(int fd,short event,void * data)50 slog_fsync_tick(int fd, short event, void *data) {
51 	struct server *s = data;
52 	int ret = fsync(s->log.fd);
53 	(void)fd;
54 	(void)event;
55 	(void)ret;
56 }
57 
58 void
slog_fsync_init(struct server * s)59 slog_fsync_init(struct server *s) {
60 	if (s->cfg->log_fsync.mode != LOG_FSYNC_MILLIS) {
61 		return;
62 	}
63 	/* install a libevent timer for handling fsync */
64 	s->log.fsync_ev = event_new(s->base, -1, EV_PERSIST, slog_fsync_tick, s);
65 	if(s->log.fsync_ev == NULL) {
66 		const char evnew_error[] = "fsync timer could not be created";
67 		slog(s, WEBDIS_ERROR, evnew_error, sizeof(evnew_error)-1);
68 		return;
69 	}
70 
71 	int period_usec =s->cfg->log_fsync.period_millis * 1000;
72 	s->log.fsync_tv.tv_sec = period_usec / 1000000;
73 	s->log.fsync_tv.tv_usec = period_usec % 1000000;
74 	int ret_ta = evtimer_add(s->log.fsync_ev, &s->log.fsync_tv);
75 	if(ret_ta != 0) {
76 		const char reason[] = "fsync timer could not be added: %d";
77 		char error_msg[sizeof(reason) + 16]; /* plenty of extra space */
78 		int error_len = snprintf(error_msg, sizeof(error_msg), reason, ret_ta);
79 		slog(s, WEBDIS_ERROR, error_msg, (size_t)error_len);
80 	}
81 }
82 
83 /**
84  * Returns whether this log level is enabled.
85  */
86 int
slog_enabled(struct server * s,log_level level)87 slog_enabled(struct server *s, log_level level) {
88 	return level <= s->cfg->verbosity ? 1 : 0;
89 }
90 
91 /**
92  * Write log message to disk, or stderr.
93  */
94 static void
slog_internal(struct server * s,log_level level,const char * body,size_t sz)95 slog_internal(struct server *s, log_level level,
96 		const char *body, size_t sz) {
97 
98 	const char *c = "EWNIDT";
99 	time_t now;
100 	struct tm now_tm, *lt_ret;
101 	char time_buf[64];
102 	char msg[1 + SLOG_MSG_MAX_LEN];
103 	char line[2 * SLOG_MSG_MAX_LEN]; /* bounds are checked. */
104 	int line_sz, ret;
105 
106 	if(!s->log.fd) return;
107 
108 	/* limit message size */
109 	sz = sz ? sz:strlen(body);
110 	snprintf(msg, sz + 1 > sizeof(msg) ? sizeof(msg) : sz + 1, "%s", body);
111 
112 	/* get current time */
113 	now = time(NULL);
114 	lt_ret = localtime_r(&now, &now_tm);
115 	if(lt_ret) {
116 		strftime(time_buf, sizeof(time_buf), "%d %b %H:%M:%S", lt_ret);
117 	} else {
118 		const char err_msg[] = "(NO TIME AVAILABLE)";
119 		memcpy(time_buf, err_msg, sizeof(err_msg));
120 	}
121 
122 	/* generate output line. */
123 	char letter = (level == WEBDIS_TRACE ? 5 : c[level]);
124 	line_sz = snprintf(line, sizeof(line),
125 		"[%d] %s %c %s\n", (int)s->log.self, time_buf, letter, msg);
126 
127 	/* write to log and maybe flush to disk. */
128 	ret = write(s->log.fd, line, line_sz);
129 	if(s->cfg->log_fsync.mode == LOG_FSYNC_ALL) {
130 		ret = fsync(s->log.fd);
131 	}
132 
133 	(void)ret;
134 }
135 
136 /**
137  * Thin wrapper around slog_internal that first checks the log level.
138  */
139 void
slog(struct server * s,log_level level,const char * body,size_t sz)140 slog(struct server *s, log_level level,
141 		const char *body, size_t sz) {
142 	if(level <= s->cfg->verbosity) { /* check log level first */
143 		slog_internal(s, level, body, sz);
144 	}
145 }
146