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
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