1    	/**
2    	 * Syslog wrapper that does not block
3    	 *
4    	 * Lon Hohberger, 2009
5    	 */
6    	
7    	#include "config.h"
8    	
9    	#include <pthread.h>
10   	#include <unistd.h>
11   	#include <sys/syslog.h>
12   	#include <signal.h>
13   	#include <string.h>
14   	#include <stdlib.h>
15   	#include <unistd.h>
16   	#include <stdarg.h>
17   	#include <stdio.h>
18   	#include <sys/types.h>
19   	#include <sys/time.h>
20   	#include <errno.h>
21   	
22   	#include "list.h"
23   	
24   	struct log_entry {
25   		list_head();
26   		char *message;
27   		int sev;
28   		int bufsz;
29   	};
30   	
31   	#define MAX_QUEUE_LENGTH 10
32   	#define LOGLEN 256
33   	
34   	static struct log_entry *_log_entries = NULL;
35   	static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
36   	static pthread_cond_t log_cond = PTHREAD_COND_INITIALIZER;
37   	static int log_size = 0;
38   	static int dropped = 0;
39   	static pthread_t thread_id = 0;
40   	
41   	void __real_syslog(int severity, const char *fmt, ...);
42   	void __wrap_syslog(int severity, const char *fmt, ...);
43   	void __wrap_closelog(void);
44   	
45   	static void *
46   	_log_thread(void *arg)
47   	{
48   		struct timeval tv;
49   		struct timespec ts;
50   		struct log_entry *entry;
51   	
52   		do {
53   			gettimeofday(&tv, NULL);
54   			ts.tv_sec = tv.tv_sec + 10;
55   			ts.tv_nsec = tv.tv_usec;
56   	
(1) Event lock_acquire: Calling "pthread_mutex_lock" acquires lock "log_mutex".
Also see events: [do_while_insufficient][dead_wait][remediation]
57   			pthread_mutex_lock(&log_mutex);
58   	
59   			while (!(entry = _log_entries)) {
(2) Event do_while_insufficient: Check of "pthread_cond_timedwait(&log_cond, &log_mutex, &ts) == 110" is not sufficient, as it is only checked after the first wait.
Also see events: [lock_acquire][dead_wait][remediation]
60   				if (pthread_cond_timedwait(&log_cond,
(3) Event dead_wait: A wait is performed without ensuring that the condition is not already satisfied while holding lock "log_mutex". This can cause unnecessary waiting if the notification happens before the lock is acquired.
(4) Event remediation: Acquire the lock, then check the wait condition in a loop, without releasing with the lock before the wait. This will prevent unnecessary waiting and failed conditions from spurious wakeups.
Also see events: [lock_acquire][do_while_insufficient]
61   							   &log_mutex,
62   							   &ts) == ETIMEDOUT)
63   					goto out;
64   			}
65   	
66   			list_remove(&_log_entries, entry);
67   			--log_size;
68   			if (log_size < 0)
69   				raise(SIGSEGV);
70   			pthread_mutex_unlock(&log_mutex);
71   	
72   			__real_syslog(entry->sev, entry->message);
73   			free(entry->message);
74   			free(entry);
75   		} while (1);
76   	
77   	out:
78   		thread_id = (pthread_t)0;
79   		pthread_mutex_unlock(&log_mutex);
80   		return NULL;
81   	}
82   	
83   	
84   	static int
85   	insert_entry(int sev, char *buf, int bufsz)
86   	{
87   		struct log_entry *lent;
88   		pthread_attr_t attrs;
89   	
90   		lent = malloc(sizeof(*lent));
91   		if (!lent)
92   			return -1;
93   		lent->sev = sev;
94   		lent->message = buf;
95   		lent->bufsz = bufsz;
96   	
97   		pthread_mutex_lock(&log_mutex);
98   		if (log_size >= MAX_QUEUE_LENGTH) {
99   			free(lent->message);
100  			free(lent);
101  	
102  			++dropped;
103  			lent = (struct log_entry *)(le(_log_entries)->le_prev);
104  	
105  			lent->sev = LOG_WARNING;
106  			snprintf(lent->message, lent->bufsz,
107  				 "%d message(s) lost due to syslog load\n",
108  				 dropped + 1);
109  			/* Dropped +1 because we overwrote a message to
110  			 * give the 'dropped' message */
111  		} else {
112  			++log_size;
113  			dropped = 0;
114  			list_insert(&_log_entries, lent);
115  		}
116  	
117  		if (!thread_id) {
118  			pthread_attr_init(&attrs);
119  		       	pthread_attr_setinheritsched(&attrs, PTHREAD_INHERIT_SCHED);
120  	
121  			if (pthread_create(&thread_id, &attrs, _log_thread, NULL) < 0)
122  				thread_id = 0;
123  			pthread_mutex_unlock(&log_mutex);
124  		} else {
125  			pthread_mutex_unlock(&log_mutex);
126  			pthread_cond_signal(&log_cond);
127  		}
128  	
129  		return 0;
130  	}
131  	
132  	
133  	__attribute__((__format__ (__printf__, 2, 0)))
134  	void
135  	__wrap_syslog(int severity, const char *fmt, ...)
136  	{
137  		va_list      args;
138  		char         *logmsg;
139  	
140  		logmsg = malloc(LOGLEN);
141  		if (!logmsg)
142  			return;
143  		memset(logmsg, 0, LOGLEN);
144  	
145  		va_start(args, fmt);
146  		vsnprintf(logmsg + strlen(logmsg), LOGLEN - strlen(logmsg), 
147  			  fmt, args);
148  		va_end(args);
149  	
150  		insert_entry(severity, logmsg, LOGLEN);
151  	
152  		return;
153  	}
154  	
155  	
156  	void __real_closelog(void);
157  	
158  	void
159  	__wrap_closelog(void)
160  	{
161  		struct log_entry *lent;
162  	#ifdef DEBUG
163  		int lost = 0;
164  	#endif
165  	
166  		if (thread_id != 0) {
167  			pthread_cancel(thread_id);
168  			pthread_join(thread_id, NULL);
169  			thread_id = 0;
170  		}
171  		__real_closelog();
172  		while (_log_entries) {
173  	#ifdef DEBUG
174  			++lost;
175  	#endif
176  			lent = _log_entries;
177  			list_remove(&_log_entries, lent);
178  			free(lent->message);
179  			free(lent);
180  		}
181  	
182  	#ifdef DEBUG
183  		printf("%d lost\n", lost);
184  	#endif
185  	}
186  	
187  	
188  	#ifdef STANDALONE
189  	int
190  	main(int argc, char**argv)
191  	{
192  		int x;
193  	
194  		for (x = 0; x < 100; x++) {
195  			syslog(1, "Yo %d\n", x);
196  		}
197  		sleep(1);
198  	
199  		closelog();
200  	
201  		return 0;
202  	}
203  	
204  	#endif
205