1 /* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
2  *
3  * ecolog - Reusable application logging library.
4  *
5  * Copyright (C) 2000 - 2003 by Martin Pool <mbp@samba.org>
6  * Copyright 2007 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 
23                                      /*
24                                       | Finality is death.
25                                       | Perfection is finality.
26                                       | Nothing is perfect.
27                                       | There are lumps in it.
28                                       */
29 
30 
31 #include <config.h>
32 
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <sys/file.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include <syslog.h>
41 
42 #include "distcc.h"
43 #include "trace.h"
44 #include "snprintf.h"
45 #include "va_copy.h"
46 
47 struct rs_logger_list {
48     rs_logger_fn               *fn;
49     void                        *private_ptr;
50     int                         private_int;
51     int                         max_level;
52     struct rs_logger_list       *next;
53 };
54 
55 static struct rs_logger_list *logger_list = NULL;
56 
57 /* really bool */
58 int rs_trace_syslog = FALSE;
59 int rs_trace_level = RS_LOG_NOTICE;
60 
61 #ifdef UNUSED
62 /* nothing */
63 #elif defined(__GNUC__)
64 #  define UNUSED(x) x __attribute__((unused))
65 #elif defined(__LCLINT__)
66 #  define UNUSED(x) /*@unused@*/ x
67 #else                /* !__GNUC__ && !__LCLINT__ */
68 #  define UNUSED(x) x
69 #endif                /* !__GNUC__ && !__LCLINT__ */
70 
71 
72 static void rs_log_va(int level, char const *fn, char const *fmt, va_list va);
73 
74 #if SIZEOF_SIZE_T > SIZEOF_LONG
75 #  warning size_t is larger than a long integer, values in trace messages may be wrong
76 #endif
77 
78 
79 /**
80  * Log severity strings, if any.  Must match ordering in
81  * ::rs_loglevel.
82  */
83 static const char *rs_severities[] = {
84     "EMERGENCY! ", "ALERT! ", "CRITICAL! ", "ERROR: ", "Warning: ",
85     "", "", ""
86 };
87 
88 
89 /**********************************************************************
90  * Functions for manipulating the list of loggers
91  **********************************************************************/
92 
rs_remove_all_loggers(void)93 void rs_remove_all_loggers(void)
94 {
95     struct rs_logger_list *l, *next;
96 
97     for (l = logger_list; l; l = next) {
98         next = l -> next;       /* save before destruction */
99         free(l);
100     }
101     logger_list = NULL;
102 }
103 
104 
rs_add_logger(rs_logger_fn fn,int max_level,void * private_ptr,int private_int)105 void rs_add_logger(rs_logger_fn fn,
106                    int max_level,
107                    void *private_ptr,
108                    int private_int)
109 {
110     struct rs_logger_list *l;
111 
112     if ((l = malloc(sizeof *l)) == NULL)
113         return;
114 
115     l->fn = fn;
116     l->max_level = max_level;
117     l->private_ptr = private_ptr;
118     l->private_int = private_int;
119 
120     l->next = logger_list;
121     logger_list = l;
122 }
123 
124 
125 /**
126  * Remove only the logger that exactly matches the specified parameters
127  **/
rs_remove_logger(rs_logger_fn fn,int max_level,void * private_ptr,int private_int)128 void rs_remove_logger(rs_logger_fn fn,
129                       int max_level,
130                       void *private_ptr,
131                       int private_int)
132 {
133     struct rs_logger_list *l, **pl;
134 
135     for (pl = &logger_list; *pl; pl = &((*pl)->next)) {
136         l = *pl;
137         if (l->fn == fn
138             && l->max_level == max_level
139             && l->private_ptr == private_ptr
140             && l->private_int == private_int) {
141             /* unhook from list by adjusting whoever points to this. */
142             *pl = l->next;
143             free(l);
144             return;
145         }
146     }
147 }
148 
149 
150 /**
151  * Set the least important (i.e. largest) message severity that
152  * will be output.
153  */
154 void
rs_trace_set_level(rs_loglevel level)155 rs_trace_set_level(rs_loglevel level)
156 {
157     rs_trace_level = level;
158 }
159 
160 
161 
162 /**
163  * Work out a log level from a string name.
164  *
165  * Returns -1 for invalid names.
166  */
167 int
rs_loglevel_from_name(const char * name)168 rs_loglevel_from_name(const char *name)
169 {
170     if (!strcmp(name, "emerg") || !strcmp(name, "emergency"))
171         return RS_LOG_EMERG;
172     else if (!strcmp(name, "alert"))
173         return RS_LOG_ALERT;
174     else if (!strcmp(name, "critical") || !strcmp(name, "crit"))
175         return RS_LOG_CRIT;
176     else if (!strcmp(name, "error") || !strcmp(name, "err"))
177         return RS_LOG_ERR;
178     else if (!strcmp(name, "warning") || !strcmp(name, "warn"))
179         return RS_LOG_WARNING;
180     else if (!strcmp(name, "notice") || !strcmp(name, "note"))
181         return RS_LOG_NOTICE;
182     else if (!strcmp(name, "info"))
183         return RS_LOG_INFO;
184     else if (!strcmp(name, "debug"))
185         return RS_LOG_DEBUG;
186 
187     return -1;
188 }
189 
190 
191 /**
192  * If you don't initialize a logger before first logging, then we
193  * write to stderr by default.
194  **/
rs_lazy_default(void)195 static void rs_lazy_default(void)
196 {
197     static int called;
198 
199     if (called)
200         return;
201 
202     called = 1;
203     if (logger_list == NULL)
204         rs_add_logger(rs_logger_file, RS_LOG_WARNING, NULL, STDERR_FILENO);
205 }
206 
207 /* Heart of the matter */
208 static void
rs_log_va(int flags,char const * caller_fn_name,char const * fmt,va_list va)209 rs_log_va(int flags, char const *caller_fn_name, char const *fmt, va_list va)
210 
211 
212 {
213     int level = flags & RS_LOG_PRIMASK;
214     struct rs_logger_list *l;
215 
216     rs_lazy_default();
217 
218     if (level <= rs_trace_level)
219       for (l = logger_list; l; l = l->next)
220           if (level <= l->max_level) {
221               /* We need to use va_copy() here, because functions like vsprintf
222                * may destructively modify their va_list argument, but we need
223                * to ensure that it's still valid next time around the loop. */
224               va_list copied_va;
225               VA_COPY(copied_va, va);
226               l->fn(flags, caller_fn_name,
227                     fmt, copied_va, l->private_ptr, l->private_int);
228               VA_COPY_END(copied_va);
229           }
230 }
231 
232 
rs_format_msg(char * buf,size_t buf_len,int flags,const char * fn,const char * fmt,va_list va)233 void rs_format_msg(char *buf,
234                    size_t buf_len,
235                    int flags,
236                    const char *fn,
237                    const char *fmt,
238                    va_list va)
239 {
240     unsigned level = flags & RS_LOG_PRIMASK;
241     int len;
242     const char *sv;
243 
244     *buf = '\0';
245     len = 0;
246 
247     if (!(flags & RS_LOG_NO_PROGRAM)) {
248         strcpy(buf, rs_program_name);
249         len = strlen(buf);
250     }
251 
252     if (!(flags & RS_LOG_NO_PID)) {
253         /* You might like to cache the pid, but that would cause trouble when we fork. */
254         sprintf(buf+len, "[%d] ", (int) getpid());
255     } else if (~flags & RS_LOG_NO_PROGRAM) {
256         strcat(buf+len, ": ");
257     }
258     len = strlen(buf);
259 
260     if (!(flags & RS_LOG_NONAME) && fn) {
261         sprintf(buf+len, "(%s) ", fn);
262         len = strlen(buf);
263     }
264 
265     sv = rs_severities[level];
266     if (*sv) {
267         strcpy(buf + len, sv);
268         len = strlen(buf);
269     }
270 
271     vsnprintf(buf + len, buf_len - len, fmt, va);
272 }
273 
274 
275 
276 /**
277  * Called by a macro, used on platforms where we can't determine the
278  * calling function name.
279  */
280 void
rs_log0_nofn(int level,char const * fmt,...)281 rs_log0_nofn(int level, char const *fmt, ...)
282 {
283     va_list         va;
284 
285     va_start(va, fmt);
286     rs_log_va(level, NULL, fmt, va);
287     va_end(va);
288 }
289 
290 
rs_log0(int level,char const * fn,char const * fmt,...)291 void rs_log0(int level, char const *fn, char const *fmt, ...)
292 {
293     va_list         va;
294 
295     va_start(va, fmt);
296     rs_log_va(level, fn, fmt, va);
297     va_end(va);
298 }
299 
300 
301 void
rs_logger_syslog(int flags,const char * fn,char const * fmt,va_list va,void * UNUSED (private_ptr),int UNUSED (private_int))302 rs_logger_syslog(int flags, const char *fn, char const *fmt, va_list va,
303                  void * UNUSED(private_ptr), int UNUSED(private_int))
304 {
305     /* NOTE NO TRAILING NUL */
306     char buf[4090];
307 
308     /* you're never going to want program or pid in a syslog message,
309      * because it's redundant. */
310     rs_format_msg(buf, sizeof buf,
311                   flags | RS_LOG_NO_PROGRAM | RS_LOG_NO_PID,
312                   fn, fmt, va);
313     syslog(flags & RS_LOG_PRIMASK, "%s", buf);
314 }
315 
316 
317 void
rs_logger_file(int flags,const char * fn,char const * fmt,va_list va,void * UNUSED (private_ptr),int log_fd)318 rs_logger_file(int flags, const char *fn, char const *fmt, va_list va,
319                void * UNUSED(private_ptr), int log_fd)
320 {
321     /* NOTE NO TRAILING NUL */
322     char buf[4090];
323     size_t len;
324     ssize_t ret;
325 
326     rs_format_msg(buf, sizeof buf, flags, fn, fmt, va);
327 
328     len = strlen(buf);
329     if (len > (int) sizeof buf - 2)
330         len = (int) sizeof buf - 2;
331     strcpy(&buf[len], "\n");
332 
333     ret = write(log_fd, buf, len + 1);
334     if (ret == -1) {
335       ret = write(/* stderr */ 2, buf, len + 1);
336     }
337 }
338 
339 
340 
341 /* ======================================================================== */
342 /* functions for handling compilers without varargs macros */
343 
344 /* This is called directly if the machine doesn't allow varargs
345  * macros. */
346 void
rs_log_error_nofn(char const * s,...)347 rs_log_error_nofn(char const *s, ...)
348 {
349     va_list    va;
350 
351     va_start(va, s);
352     rs_log_va(RS_LOG_ERR, NULL, s, va);
353     va_end(va);
354 }
355 
356 /* This is called directly if the machine doesn't allow varargs
357  * macros. */
358 void
rs_log_warning_nofn(char const * s,...)359 rs_log_warning_nofn(char const *s, ...)
360 {
361     va_list    va;
362 
363     va_start(va, s);
364     rs_log_va(RS_LOG_WARNING, NULL, s, va);
365     va_end(va);
366 }
367 
368 
369 /* This is called directly if the machine doesn't allow varargs
370  * macros. */
371 void
rs_log_critical_nofn(char const * s,...)372 rs_log_critical_nofn(char const *s, ...)
373 {
374     va_list    va;
375 
376     va_start(va, s);
377     rs_log_va(RS_LOG_CRIT, NULL, s, va);
378     va_end(va);
379 }
380 
381 /* This is called directly if the machine doesn't allow varargs
382  * macros. */
383 void
rs_log_info_nofn(char const * s,...)384 rs_log_info_nofn(char const *s, ...)
385 {
386     va_list    va;
387 
388     va_start(va, s);
389     rs_log_va(RS_LOG_INFO, NULL, s, va);
390     va_end(va);
391 }
392 
393 
394 /* This is called directly if the machine doesn't allow varargs
395  * macros. */
396 void
rs_log_notice_nofn(char const * s,...)397 rs_log_notice_nofn(char const *s, ...)
398 {
399     va_list    va;
400 
401     va_start(va, s);
402     rs_log_va(RS_LOG_NOTICE, NULL, s, va);
403     va_end(va);
404 }
405 
406 
407 /* This is called directly if the machine doesn't allow varargs
408  * macros. */
409 void
rs_log_trace_nofn(char const * s,...)410 rs_log_trace_nofn(char const *s, ...)
411 {
412     va_list    va;
413 
414     va_start(va, s);
415     rs_log_va(RS_LOG_DEBUG, NULL, s, va);
416     va_end(va);
417 }
418 
419 
420 /**
421  * Return true if the library contains trace code; otherwise false.
422  * If this returns false, then trying to turn trace on will achieve
423  * nothing.
424  */
425 int
rs_supports_trace(void)426 rs_supports_trace(void)
427 {
428 #ifdef DO_RS_TRACE
429     return 1;
430 #else
431     return 0;
432 #endif                /* !DO_RS_TRACE */
433 }
434 
435 
436 static char job_summary[4096*4];
dcc_job_summary_clear(void)437 void dcc_job_summary_clear(void) {
438     job_summary[0] = 0;
439     job_summary[sizeof(job_summary) - 1] = '\0';
440 }
441 
dcc_job_summary(void)442 void dcc_job_summary(void) {
443     rs_log_notice("%s", job_summary);
444 }
445 
dcc_job_summary_append(const char * s)446 void dcc_job_summary_append(const char *s) {
447     int64_t len = (4096 * 4 - 1) - strlen(job_summary);
448     if (len > 0)
449         strncat(job_summary, s, len);
450 }
451