1 /*
2  * Copyright (c) 2009 NLNet Labs. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 /**
28  * Threading and locking.
29  *
30  */
31 
32 #include "config.h"
33 #include "locks.h"
34 #include "log.h"
35 
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <limits.h>
39 #include <syslog.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <signal.h> /* sigfillset(), sigprocmask() */
43 #include <string.h> /* strerror() */
44 #include <time.h> /* gettimeofday() */
45 
46 static const char* lock_str = "lock";
47 
48 int
ods_thread_wait(pthread_cond_t * cond,pthread_mutex_t * lock,time_t wait)49 ods_thread_wait(pthread_cond_t* cond, pthread_mutex_t* lock, time_t wait)
50 {
51     struct timespec ts;
52 
53     if (wait <= 0)
54         return pthread_cond_wait(cond, lock);
55 
56     if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
57         ods_log_error("[%s] clock_gettime() error: %s", lock_str,
58             strerror(errno));
59         return 1;
60     }
61 
62     ts.tv_sec += wait;
63     return pthread_cond_timedwait(cond, lock, &ts);
64 }
65 
66 janitor_threadclass_t detachedthreadclass;
67 janitor_threadclass_t workerthreadclass;
68 janitor_threadclass_t handlerthreadclass;
69 janitor_threadclass_t cmdhandlerthreadclass;
70 
71 struct alertbuffer_struct {
72     char buffer[1024];
73     int index;
74 };
75 static void alert(struct alertbuffer_struct* buffer, const char *format, ...)
76 #ifdef HAVE___ATTRIBUTE__
77      __attribute__ ((format (printf, 2, 3)))
78 #endif
79 ;
80 static void alertsyslog(const char* format, ...)
81 #ifdef HAVE___ATTRIBUTE__
82      __attribute__ ((format (printf, 1, 2)))
83 #endif
84 ;
85 
86 inline static int
alertoutput(struct alertbuffer_struct * buffer,int ch)87 alertoutput(struct alertbuffer_struct* buffer, int ch)
88 {
89     if (buffer->index < sizeof(buffer->buffer)) {
90         buffer->buffer[buffer->index++] = ch;
91         return 0;
92     } else
93         return -1;
94 }
95 
96 static void
alertinteger(struct alertbuffer_struct * buffer,unsigned long value,int base)97 alertinteger(struct alertbuffer_struct* buffer, unsigned long value, int base)
98 {
99     char ch;
100     if (value > base - 1)
101         alertinteger(buffer, value / base, base);
102     ch = "0123456789abcdef"[value % base];
103     alertoutput(buffer, ch);
104 }
105 
106 static void
valert(struct alertbuffer_struct * buffer,const char * format,va_list args)107 valert(struct alertbuffer_struct* buffer, const char* format, va_list args)
108 {
109     int idx, len;
110     const char* stringarg;
111     void* pointerarg;
112     int integerarg;
113     long longarg;
114     idx = 0;
115     while (format[idx]) {
116         if (format[idx] == '%') {
117             switch (format[idx + 1]) {
118                 case '%':
119                     alertoutput(buffer, '%');
120                     idx += 2;
121                     break;
122                 case 's':
123                     stringarg = va_arg(args, char*);
124                     if (stringarg == NULL)
125                         stringarg = "(null)";
126                     while(*stringarg)
127                         if(alertoutput(buffer, *(stringarg++)))
128                             break;
129                     idx += 2;
130                     break;
131                 case 'p':
132                     pointerarg = va_arg(args, void*);
133                     if (pointerarg == NULL) {
134                         stringarg = "(null)";
135                         while(stringarg)
136                             alertoutput(buffer, *(stringarg++));
137                     } else {
138                         alertoutput(buffer, '0');
139                         alertoutput(buffer, 'x');
140                         alertinteger(buffer, (unsigned long) pointerarg, 16);
141                     }
142                     idx += 2;
143                     break;
144                 case 'l':
145                     switch (format[idx + 2]) {
146                         case 'd':
147                             longarg = va_arg(args, long);
148                             if (longarg < 0) {
149                                 alertoutput(buffer, '-');
150                                 alertinteger(buffer, 1UL + ~((unsigned long) longarg), 10);
151                             } else
152                                 alertinteger(buffer, longarg, 10);
153                             idx += 3;
154                             break;
155                         case '\0':
156                             alertoutput(buffer, format[idx++]);
157                             break;
158                         default:
159                             alertoutput(buffer, format[idx++]);
160                             alertoutput(buffer, format[idx++]);
161                             alertoutput(buffer, format[idx++]);
162                     }
163                     break;
164                 case 'd':
165                     integerarg = va_arg(args, int);
166                     alertinteger(buffer, (long) integerarg, 10);
167                     idx += 2;
168                     break;
169                 case '\0':
170                     alertoutput(buffer, '%');
171                     idx += 1;
172                     break;
173                 default:
174                     alertoutput(buffer, format[idx++]);
175                     alertoutput(buffer, format[idx++]);
176             }
177         } else {
178             alertoutput(buffer, format[idx++]);
179         }
180     }
181 }
182 
183 static void
alertsyslog(const char * format,...)184 alertsyslog(const char* format, ...)
185 {
186     va_list args;
187     struct alertbuffer_struct buffer;
188     va_start(args, format);
189     buffer.index = 0;
190     valert(&buffer, format, args);
191     va_end(args);
192     if (buffer.index < sizeof(buffer.buffer)) {
193         buffer.buffer[buffer.index] = '\0';
194     } else {
195         strcpy(&buffer.buffer[buffer.index - strlen("...\n") -1], "...\n");
196     }
197     (void)write(2, buffer.buffer, strlen(buffer.buffer));
198     syslog(LOG_CRIT, "%s", buffer.buffer);
199 }
200 
201 void
ods_janitor_initialize(char * argv0)202 ods_janitor_initialize(char*argv0)
203 {
204     janitor_initialize(alertsyslog, ods_log_error);
205 
206     janitor_threadclass_create(&detachedthreadclass, "daemonthreads");
207     janitor_threadclass_setautorun(detachedthreadclass);
208     janitor_threadclass_setblockedsignals(detachedthreadclass);
209     janitor_threadclass_setdetached(detachedthreadclass);
210     janitor_threadclass_setminstacksize(detachedthreadclass, ODS_MINIMUM_STACKSIZE);
211 
212     janitor_threadclass_create(&workerthreadclass, "workerthreads");
213     janitor_threadclass_setautorun(workerthreadclass);
214     janitor_threadclass_setblockedsignals(workerthreadclass);
215     janitor_threadclass_setminstacksize(workerthreadclass, ODS_MINIMUM_STACKSIZE);
216 
217     janitor_threadclass_create(&handlerthreadclass, "handlerthreads");
218     janitor_threadclass_setautorun(handlerthreadclass);
219     janitor_threadclass_setminstacksize(handlerthreadclass, ODS_MINIMUM_STACKSIZE);
220 
221     janitor_threadclass_create(&cmdhandlerthreadclass, "cmdhandlerthreads");
222     janitor_threadclass_setautorun(cmdhandlerthreadclass);
223     janitor_threadclass_setminstacksize(cmdhandlerthreadclass, ODS_MINIMUM_STACKSIZE);
224 
225     janitor_trapsignals(argv0);
226 }
227