1 /*
2 * Copyright (c) 2009 Sippy Software, Inc., http://www.sippysoft.com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28 #include <pthread.h>
29 #include <syslog.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "config.h"
36
37 #ifndef HAVE_STRLCPY
38 #include "rtpp_util.h"
39 #endif
40
41 #include "rtpp_debug.h"
42 #include "rtpp_syslog_async.h"
43
44 #define SYSLOG_WI_POOL_SIZE 64
45 #define SYSLOG_WI_DATA_LEN 2048
46
47 typedef enum {
48 SYSLOG_ITEM_ASYNC_WRITE,
49 SYSLOG_ITEM_ASYNC_EXIT
50 } item_types;
51
52 struct syslog_wi
53 {
54 item_types item_type;
55 char data[SYSLOG_WI_DATA_LEN];
56 int len;
57 int priority;
58 struct syslog_wi *next;
59 };
60
61 #define SYSLOG_WI_NOWAIT 0
62 #define SYSLOG_WI_WAIT 1
63
64 static pthread_mutex_t syslog_init_mutex = PTHREAD_MUTEX_INITIALIZER;
65 static int syslog_queue_inited = 0;
66 static pthread_t syslog_queue;
67 static pthread_cond_t syslog_queue_cond;
68 static pthread_mutex_t syslog_queue_mutex;
69 static pthread_cond_t syslog_wi_free_cond;
70 static pthread_mutex_t syslog_wi_free_mutex;
71
72 static int syslog_dropped_items;
73
74 static struct syslog_wi syslog_wi_pool[SYSLOG_WI_POOL_SIZE];
75 static struct syslog_wi *syslog_wi_free;
76 static struct syslog_wi *syslog_wi_queue, *syslog_wi_queue_tail;
77
78 static void
syslog_queue_run(void)79 syslog_queue_run(void)
80 {
81 struct syslog_wi *wi;
82
83 for (;;) {
84 pthread_mutex_lock(&syslog_queue_mutex);
85 while (syslog_wi_queue == NULL) {
86 pthread_cond_wait(&syslog_queue_cond, &syslog_queue_mutex);
87 }
88 wi = syslog_wi_queue;
89 syslog_wi_queue = wi->next;
90 pthread_mutex_unlock(&syslog_queue_mutex);
91
92 /* main work here */
93 switch (wi->item_type) {
94 case SYSLOG_ITEM_ASYNC_WRITE:
95 syslog(wi->priority, "%s", wi->data);
96 break;
97
98 case SYSLOG_ITEM_ASYNC_EXIT:
99 return;
100
101 default:
102 break;
103 }
104
105 /* put wi into syslog_wi_free' tail */
106 pthread_mutex_lock(&syslog_wi_free_mutex);
107
108 wi->next = syslog_wi_free;
109 syslog_wi_free = wi;
110
111 pthread_cond_signal(&syslog_wi_free_cond);
112 pthread_mutex_unlock(&syslog_wi_free_mutex);
113 }
114 }
115
116 static int
syslog_queue_init(void)117 syslog_queue_init(void)
118 {
119 int i;
120
121 memset(syslog_wi_pool, 0, sizeof(syslog_wi_pool));
122 for (i = 0; i < SYSLOG_WI_POOL_SIZE - 1; i++) {
123 syslog_wi_pool[i].next = &syslog_wi_pool[i + 1];
124 }
125 syslog_wi_pool[SYSLOG_WI_POOL_SIZE - 1].next = NULL;
126
127 syslog_wi_free = syslog_wi_pool;
128 syslog_wi_queue = NULL;
129 syslog_wi_queue_tail = NULL;
130
131 syslog_dropped_items = 0;
132
133 pthread_cond_init(&syslog_queue_cond, NULL);
134 pthread_mutex_init(&syslog_queue_mutex, NULL);
135 pthread_cond_init(&syslog_wi_free_cond, NULL);
136 pthread_mutex_init(&syslog_wi_free_mutex, NULL);
137
138 if (pthread_create(&syslog_queue, NULL, (void *(*)(void *))&syslog_queue_run, NULL) != 0)
139 return -1;
140
141 return 0;
142 }
143
144 static struct syslog_wi *
syslog_queue_get_free_item(int wait)145 syslog_queue_get_free_item(int wait)
146 {
147 struct syslog_wi *wi;
148
149 pthread_mutex_lock(&syslog_wi_free_mutex);
150 while (syslog_wi_free == NULL) {
151 /* no free work items, return if no wait is requested */
152 if (wait == 0) {
153 syslog_dropped_items++;
154 pthread_mutex_unlock(&syslog_wi_free_mutex);
155 return NULL;
156 }
157 pthread_cond_wait(&syslog_wi_free_cond, &syslog_wi_free_mutex);
158 }
159
160 wi = syslog_wi_free;
161
162 /* move up syslog_wi_free */
163 syslog_wi_free = syslog_wi_free->next;
164 pthread_mutex_unlock(&syslog_wi_free_mutex);
165
166 return wi;
167 }
168
169 static void
syslog_queue_put_item(struct syslog_wi * wi)170 syslog_queue_put_item(struct syslog_wi *wi)
171 {
172
173 pthread_mutex_lock(&syslog_queue_mutex);
174
175 wi->next = NULL;
176 if (syslog_wi_queue == NULL) {
177 syslog_wi_queue = wi;
178 syslog_wi_queue_tail = wi;
179 } else {
180 syslog_wi_queue_tail->next = wi;
181 syslog_wi_queue_tail = wi;
182 }
183
184 /* notify worker thread */
185 pthread_cond_signal(&syslog_queue_cond);
186
187 pthread_mutex_unlock(&syslog_queue_mutex);
188 }
189
190 static void
syslog_async_atexit(void)191 syslog_async_atexit(void)
192 {
193 struct syslog_wi *wi;
194
195 if (syslog_queue_inited == 0)
196 return;
197
198 /* Wait for the worker thread to exit */
199 wi = syslog_queue_get_free_item(SYSLOG_WI_WAIT);
200 wi->item_type = SYSLOG_ITEM_ASYNC_EXIT;
201 syslog_queue_put_item(wi);
202 pthread_join(syslog_queue, NULL);
203 }
204
205 int
syslog_async_init(const char * app,int facility)206 syslog_async_init(const char *app, int facility)
207 {
208
209 pthread_mutex_lock(&syslog_init_mutex);
210 if (syslog_queue_inited == 0) {
211 if (syslog_queue_init() != 0) {
212 pthread_mutex_unlock(&syslog_init_mutex);
213 return -1;
214 }
215 }
216 syslog_queue_inited = 1;
217 pthread_mutex_unlock(&syslog_init_mutex);
218
219 openlog(app, LOG_PID | LOG_CONS, facility);
220 atexit(syslog_async_atexit);
221
222 return 0;
223 }
224
225 void
vsyslog_async(int priority,const char * pre,const char * post,const char * user_fmt,va_list ap)226 vsyslog_async(int priority, const char *pre, const char *post,
227 const char *user_fmt, va_list ap)
228 {
229 struct syslog_wi *wi;
230 char *p;
231 int s1, s2, s3, l, m;
232
233 wi = syslog_queue_get_free_item(SYSLOG_WI_NOWAIT);
234 if (wi == NULL)
235 return;
236
237 m = l = sizeof(wi->data);
238 s1 = strlcpy(wi->data, pre, l);
239 if (s1 >= l) {
240 s1 = l - 1;
241 }
242 p = wi->data + s1;
243 l -= s1;
244 if (l <= 1)
245 goto truncate;
246 s2 = vsnprintf(p, l, user_fmt, ap);
247 if (s2 >= l) {
248 /* message was truncated */
249 s2 = l - 1;
250 p[s2] = '\0';
251 }
252 p += s2;
253 l -= s2;
254 if (l <= 1 || post == NULL)
255 goto truncate;
256 s3 = strlcpy(p, post, l);
257 if (s3 >= l) {
258 /* message was truncated */
259 s3 = l - 1;
260 }
261 l -= s3;
262 truncate:
263 RTPP_DBG_ASSERT(l >= 0 && l <= m);
264 wi->len = m - l;
265 RTPP_DBG_ASSERT(wi->data[wi->len] == '\0' && strlen(wi->data) == wi->len);
266 wi->priority = priority;
267 wi->item_type = SYSLOG_ITEM_ASYNC_WRITE;
268 syslog_queue_put_item(wi);
269 }
270