Line | Count | Source |
1 | | /* -*- mode: c; c-file-style: "openbsd" -*- */ |
2 | | /* $OpenBSD: log.c,v 1.11 2007/12/07 17:17:00 reyk Exp $ */ |
3 | | |
4 | | /* |
5 | | * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> |
6 | | * |
7 | | * Permission to use, copy, modify, and/or distribute this software for any |
8 | | * purpose with or without fee is hereby granted, provided that the above |
9 | | * copyright notice and this permission notice appear in all copies. |
10 | | * |
11 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
16 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
17 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | | */ |
19 | | |
20 | | #include <unistd.h> |
21 | | #include <stdio.h> |
22 | | #include <stdlib.h> |
23 | | #include <stdarg.h> |
24 | | #include <syslog.h> |
25 | | #include <sys/types.h> |
26 | | #include <string.h> |
27 | | #include <errno.h> |
28 | | #include <time.h> |
29 | | #include "log.h" |
30 | | |
31 | | /* By default, logging is done on stderr. */ |
32 | | static int use_syslog = 0; |
33 | | /* Default debug level */ |
34 | | static int debug = 0; |
35 | | |
36 | | /* Logging can be modified by providing an appropriate log handler. */ |
37 | | static void (*logh)(int severity, const char *msg) = NULL; |
38 | | |
39 | | static void vlog(int, const char *, const char *, va_list); |
40 | | static void logit(int, const char *, const char *, ...); |
41 | | |
42 | 0 | #define MAX_DBG_TOKENS 40 |
43 | | static const char *tokens[MAX_DBG_TOKENS + 1] = { NULL }; |
44 | | |
45 | | void |
46 | | log_init(int n_syslog, int n_debug, const char *progname) |
47 | 0 | { |
48 | 0 | use_syslog = n_syslog; |
49 | 0 | debug = n_debug; |
50 | |
|
51 | 0 | if (use_syslog) openlog(progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); |
52 | |
|
53 | 0 | tzset(); |
54 | 0 | } |
55 | | |
56 | | void |
57 | | log_level(int n_debug) |
58 | 0 | { |
59 | 0 | if (n_debug >= 0) debug = n_debug; |
60 | 0 | } |
61 | | |
62 | | void |
63 | | log_register(void (*cb)(int, const char *)) |
64 | 133 | { |
65 | 133 | logh = cb; |
66 | 133 | } |
67 | | |
68 | | void |
69 | | log_accept(const char *token) |
70 | 0 | { |
71 | 0 | int i; |
72 | 0 | for (i = 0; i < MAX_DBG_TOKENS; i++) { |
73 | 0 | if (tokens[i] == NULL) { |
74 | 0 | tokens[i + 1] = NULL; |
75 | 0 | tokens[i] = token; |
76 | 0 | return; |
77 | 0 | } |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | static void |
82 | | logit(int pri, const char *token, const char *fmt, ...) |
83 | 0 | { |
84 | 0 | va_list ap; |
85 | |
|
86 | 0 | va_start(ap, fmt); |
87 | 0 | vlog(pri, token, fmt, ap); |
88 | 0 | va_end(ap); |
89 | 0 | } |
90 | | |
91 | | static char * |
92 | | date() |
93 | 0 | { |
94 | | /* Return the current date as incomplete ISO 8601 (2012-12-12T16:13:30) */ |
95 | 0 | static char date[] = "2012-12-12T16:13:30"; |
96 | 0 | time_t t = time(NULL); |
97 | 0 | struct tm *tmp = localtime(&t); |
98 | 0 | strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S", tmp); |
99 | 0 | return date; |
100 | 0 | } |
101 | | |
102 | | static const char * |
103 | | translate(int fd, int priority) |
104 | 0 | { |
105 | | /* Translate a syslog priority to a string. With colors if the output is a |
106 | | * terminal. */ |
107 | 0 | int tty = isatty(fd); |
108 | 0 | switch (tty) { |
109 | 0 | case 1: |
110 | 0 | switch (priority) { |
111 | 0 | case LOG_EMERG: |
112 | 0 | return "\033[1;37;41m[EMRG"; |
113 | 0 | case LOG_ALERT: |
114 | 0 | return "\033[1;37;41m[ALRT"; |
115 | 0 | case LOG_CRIT: |
116 | 0 | return "\033[1;37;41m[CRIT"; |
117 | 0 | case LOG_ERR: |
118 | 0 | return "\033[1;31m[ ERR"; |
119 | 0 | case LOG_WARNING: |
120 | 0 | return "\033[1;33m[WARN"; |
121 | 0 | case LOG_NOTICE: |
122 | 0 | return "\033[1;34m[NOTI"; |
123 | 0 | case LOG_INFO: |
124 | 0 | return "\033[1;34m[INFO"; |
125 | 0 | case LOG_DEBUG: |
126 | 0 | return "\033[36m[ DBG"; |
127 | 0 | } |
128 | 0 | break; |
129 | 0 | default: |
130 | 0 | switch (priority) { |
131 | 0 | case LOG_EMERG: |
132 | 0 | return "[EMRG"; |
133 | 0 | case LOG_ALERT: |
134 | 0 | return "[ALRT"; |
135 | 0 | case LOG_CRIT: |
136 | 0 | return "[CRIT"; |
137 | 0 | case LOG_ERR: |
138 | 0 | return "[ ERR"; |
139 | 0 | case LOG_WARNING: |
140 | 0 | return "[WARN"; |
141 | 0 | case LOG_NOTICE: |
142 | 0 | return "[NOTI"; |
143 | 0 | case LOG_INFO: |
144 | 0 | return "[INFO"; |
145 | 0 | case LOG_DEBUG: |
146 | 0 | return "[ DBG"; |
147 | 0 | } |
148 | 0 | } |
149 | 0 | return "[UNKN]"; |
150 | 0 | } |
151 | | |
152 | | static void |
153 | | vlog(int pri, const char *token, const char *fmt, va_list ap) |
154 | 483 | { |
155 | 483 | if (logh) { |
156 | 483 | char *result = NULL; |
157 | 483 | if (vasprintf(&result, fmt, ap) != -1) { |
158 | 483 | logh(pri, result); |
159 | 483 | free(result); |
160 | 483 | return; |
161 | 483 | } |
162 | | /* Otherwise, abort. We don't know if "ap" is still OK. We could |
163 | | * have made a copy, but this is too much overhead for a |
164 | | * situation that shouldn't happen. */ |
165 | 0 | return; |
166 | 483 | } |
167 | | |
168 | | /* Log to syslog if requested */ |
169 | 0 | if (use_syslog) { |
170 | 0 | va_list ap2; |
171 | 0 | va_copy(ap2, ap); |
172 | 0 | vsyslog(pri, fmt, ap2); |
173 | 0 | va_end(ap2); |
174 | 0 | } |
175 | | |
176 | | /* Log to standard error in all cases */ |
177 | 0 | char *nfmt; |
178 | | /* best effort in out of mem situations */ |
179 | 0 | if (asprintf(&nfmt, "%s %s%s%s]%s %s\n", date(), translate(STDERR_FILENO, pri), |
180 | 0 | token ? "/" : "", token ? token : "", |
181 | 0 | isatty(STDERR_FILENO) ? "\033[0m" : "", fmt) == -1) { |
182 | 0 | vfprintf(stderr, fmt, ap); |
183 | 0 | fprintf(stderr, "\n"); |
184 | 0 | } else { |
185 | 0 | vfprintf(stderr, nfmt, ap); |
186 | 0 | free(nfmt); |
187 | 0 | } |
188 | 0 | fflush(stderr); |
189 | 0 | } |
190 | | |
191 | | void |
192 | | log_warn(const char *token, const char *emsg, ...) |
193 | 0 | { |
194 | 0 | char *nfmt = NULL; |
195 | 0 | va_list ap; |
196 | | |
197 | | /* best effort to even work in out of memory situations */ |
198 | 0 | if (emsg == NULL) |
199 | 0 | logit(LOG_WARNING, "%s", strerror(errno)); |
200 | 0 | else { |
201 | 0 | va_start(ap, emsg); |
202 | |
|
203 | 0 | if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { |
204 | | /* we tried it... */ |
205 | 0 | vlog(LOG_WARNING, token, emsg, ap); |
206 | 0 | logit(LOG_WARNING, "%s", strerror(errno)); |
207 | 0 | } else { |
208 | 0 | vlog(LOG_WARNING, token, nfmt, ap); |
209 | 0 | free(nfmt); |
210 | 0 | } |
211 | 0 | va_end(ap); |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | void |
216 | | log_warnx(const char *token, const char *emsg, ...) |
217 | 12 | { |
218 | 12 | va_list ap; |
219 | | |
220 | 12 | va_start(ap, emsg); |
221 | 12 | vlog(LOG_WARNING, token, emsg, ap); |
222 | 12 | va_end(ap); |
223 | 12 | } |
224 | | |
225 | | void |
226 | | log_info(const char *token, const char *emsg, ...) |
227 | 0 | { |
228 | 0 | va_list ap; |
229 | |
|
230 | 0 | if (use_syslog || debug > 0 || logh) { |
231 | 0 | va_start(ap, emsg); |
232 | 0 | vlog(LOG_INFO, token, emsg, ap); |
233 | 0 | va_end(ap); |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | static int |
238 | | log_debug_accept_token(const char *token) |
239 | 0 | { |
240 | 0 | int i; |
241 | 0 | if (tokens[0] == NULL) return 1; |
242 | 0 | for (i = 0; (i < MAX_DBG_TOKENS) && (tokens[i] != NULL); i++) { |
243 | 0 | if (!strcmp(tokens[i], token)) return 1; |
244 | 0 | } |
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | | void |
249 | | log_debug(const char *token, const char *emsg, ...) |
250 | 471 | { |
251 | 471 | va_list ap; |
252 | | |
253 | 471 | if ((debug > 1 && log_debug_accept_token(token)) || logh) { |
254 | 471 | va_start(ap, emsg); |
255 | 471 | vlog(LOG_DEBUG, token, emsg, ap); |
256 | 471 | va_end(ap); |
257 | 471 | } |
258 | 471 | } |
259 | | |
260 | | void |
261 | | fatal(const char *token, const char *emsg) |
262 | 0 | { |
263 | 0 | if (emsg == NULL) |
264 | 0 | logit(LOG_CRIT, token ? token : "fatal", "%s", strerror(errno)); |
265 | 0 | else if (errno) |
266 | 0 | logit(LOG_CRIT, token ? token : "fatal", "%s: %s", emsg, |
267 | 0 | strerror(errno)); |
268 | 0 | else |
269 | 0 | logit(LOG_CRIT, token ? token : "fatal", "%s", emsg); |
270 | |
|
271 | 0 | exit(1); |
272 | 0 | } |
273 | | |
274 | | void |
275 | | fatalx(const char *token, const char *emsg) |
276 | 0 | { |
277 | | errno = 0; |
278 | 0 | fatal(token, emsg); |
279 | 0 | } |