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