]> git.sur5r.net Git - openldap/blob - servers/slapd/syslog.c
Rework Modify statslog too
[openldap] / servers / slapd / syslog.c
1 /*      $OpenBSD: syslog.c,v 1.29 2007/11/09 18:40:19 millert Exp $ */
2 /*
3  * Copyright (c) 1983, 1988, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include "portable.h"
32
33 #include <sys/types.h>
34 #include <ac/socket.h>
35 #include <ac/syslog.h>
36 #include <sys/uio.h>
37 #include <sys/un.h>
38 #include <netdb.h>
39
40 #include <ac/errno.h>
41 #include <fcntl.h>
42 #include <paths.h>
43 #include <stdio.h>
44 #include <ac/string.h>
45 #include <ac/time.h>
46 #include <ac/unistd.h>
47 #include <ac/stdarg.h>
48
49 #include "slap.h"
50 #include "lutil.h"
51
52 static int      LogType = SOCK_DGRAM;   /* type of socket connection */
53 static int      LogFile = -1;           /* fd for log */
54 static int      connected;              /* have done connect */
55 static int      LogStat;                /* status bits, set by openlog() */
56 static const char *LogTag;              /* string to tag the entry with */
57 static int      LogFacility = LOG_USER; /* default facility code */
58
59 static void disconnectlog(void);
60 static void connectlog(void);
61
62 static void my_localtime(const time_t *t, struct tm *tm);
63
64 static struct berval LogPfx;
65
66 /*
67  * syslog
68  *      print message on log file; output is intended for syslogd(8).
69  */
70 void
71 syslog(int pri, const char *fmt, ...)
72 {
73         va_list ap;
74         char *p, *pend;
75 #define TBUF_LEN        2048
76 #define FMT_LEN         1024
77         char tbuf[TBUF_LEN];
78         int cnt;
79         int tbuf_left, prlen;
80
81         va_start(ap, fmt);
82
83 #if 0
84         /* Check for invalid bits. */
85         if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
86                 if (LogTest(LOG_ERR))
87                         lutil_debug(slap_debug, LOG_ERR, 
88                             "syslog: unknown facility/priority: %x", pri);
89                 pri &= LOG_PRIMASK|LOG_FACMASK;
90         }
91
92         /* Set default facility if none specified. */
93         if ((pri & LOG_FACMASK) == 0)
94                 pri |= LogFacility;
95
96         p = tbuf;
97         pend = p + TBUF_LEN;
98
99         *p++ = '<';
100         p += sprintf(p, "%d", pri);
101         *p++ = '>';
102
103 #if 0
104         (void)time(&now);
105         my_localtime(&now, &tm);
106         p += strftime(p, tbuf_left, "%h %e %T ", &tm);
107 #endif
108
109         if (LogTag != NULL) {
110                 p = lutil_strcopy(p, LogTag);
111         }
112         if (LogStat & LOG_PID) {
113                 *p++ = '[';
114                 p += sprintf(p, "%ld", (long)getpid());
115                 *p++ = ']';
116         }
117         if (LogTag != NULL) {
118                 *p++ = ':';
119                 *p++ = ' ';
120         }
121 #endif
122         pend = tbuf + sizeof(tbuf);
123         p = preplog(tbuf);
124
125         tbuf_left = pend - p;
126         prlen = vsnprintf(p, tbuf_left, fmt, ap);
127         va_end(ap);
128         if (prlen < 0)
129                 prlen = 0;
130         else if (prlen >= tbuf_left)
131                 prlen = tbuf_left - 1;
132         p += prlen;
133         cnt = p - tbuf;
134
135         sendlog(tbuf, cnt);
136 }
137
138 /* Copy the syslog header into tbuf and return a pointer
139  * to end of header, where caller can begin writing the
140  * actual message.
141  */
142 char *
143 preplog(char *tbuf)
144 {
145         memcpy(tbuf, LogPfx.bv_val, LogPfx.bv_len);
146         return tbuf + LogPfx.bv_len;
147 }
148
149 void
150 sendlog(char *tbuf, int cnt)
151 {
152         int error;
153
154         /* Get connected, output the message to the local logger. */
155         if (LogFile == -1)
156                 openlog(LogTag, LogStat, 0);
157         connectlog();
158
159 #if 1
160         /*
161          * If the send() failed, there are two likely scenarios:
162          *  1) syslogd was restarted
163          *  2) /dev/log is out of socket buffer space
164          * We attempt to reconnect to /dev/log to take care of
165          * case #1 and keep send()ing data to cover case #2
166          * to give syslogd a chance to empty its socket buffer.
167          */
168         if ((error = send(LogFile, tbuf, cnt, 0)) < 0) {
169                 if (errno != ENOBUFS) {
170                         disconnectlog();
171                         connectlog();
172                 }
173                 do {
174                         usleep(1);
175                         if ((error = send(LogFile, tbuf, cnt, 0)) >= 0)
176                                 break;
177                 } while (errno == ENOBUFS);
178         }
179 #endif
180 }
181
182 static void
183 disconnectlog(void)
184 {
185         /*
186          * If the user closed the FD and opened another in the same slot,
187          * that's their problem.  They should close it before calling on
188          * system services.
189          */
190         if (LogFile != -1) {
191                 close(LogFile);
192                 LogFile = -1;
193         }
194         connected = 0;          /* retry connect */
195 }
196
197 static void
198 connectlog(void)
199 {
200         struct sockaddr_un SyslogAddr;  /* AF_UNIX address of local logger */
201
202         if (LogFile == -1) {
203                 if ((LogFile = socket(AF_UNIX, LogType, 0)) == -1)
204                         return;
205                 (void)fcntl(LogFile, F_SETFD, FD_CLOEXEC);
206         }
207         if (LogFile != -1 && !connected) {
208                 memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
209 #ifdef _BSD
210                 SyslogAddr.sun_len = sizeof(SyslogAddr);
211 #endif
212                 SyslogAddr.sun_family = AF_UNIX;
213                 strncpy(SyslogAddr.sun_path, _PATH_LOG,
214                     sizeof(SyslogAddr.sun_path));
215                 if (connect(LogFile, (struct sockaddr *)&SyslogAddr,
216                     sizeof(SyslogAddr)) == -1) {
217                         (void)close(LogFile);
218                         LogFile = -1;
219                 } else
220                         connected = 1;
221         }
222 }
223
224 void
225 openlog(const char *ident, int logstat, int logfac)
226 {
227         char buf[512], *p = buf;
228
229
230         if (ident != NULL)
231                 LogTag = ident;
232         LogStat = logstat;
233         if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
234                 LogFacility = logfac;
235
236         logfac |= LOG_DEBUG;    /* we currently hardcode severity */
237
238         /* Our facility, pid, and ident never change so
239          * just build the message header now. Avoiding
240          * sprintf() and multiple calls to getpid()
241          * saves a lot of time.
242          */
243         *p++ = '<';
244         p += sprintf(p, "%d", logfac);
245         *p++ = '>';
246         /* timestamp goes here but rsyslog ignores it, so skip it */
247         p = lutil_strcopy(p, ident);
248         *p++ = '[';
249         p += sprintf(p, "%ld", (long)getpid());
250         *p++ = ']';
251         *p++ = ':';
252         *p++ = ' ';
253         LogPfx.bv_len = p - buf;
254         LogPfx.bv_val = ch_malloc(LogPfx.bv_len);
255         memcpy(LogPfx.bv_val, buf, LogPfx.bv_len);
256
257         if (LogStat & LOG_NDELAY)       /* open immediately */
258                 connectlog();
259 }
260
261 void
262 closelog()
263 {
264         (void)close(LogFile);
265         LogFile = -1;
266         connected = 0;
267         LogTag = NULL;
268         ch_free(LogPfx.bv_val);
269 }
270
271 #if 0
272 #define SECS_PER_HOUR   (60 * 60)
273 #define SECS_PER_DAY    (SECS_PER_HOUR * 24)
274
275 /* How many days come before each month (0-12).  */
276 static const unsigned short int __mon_yday[2][13] =
277   {
278     /* Normal years.  */
279     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
280     /* Leap years.  */
281     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
282   };
283
284 /* Compute the `struct tm' representation of *T,
285    and store year, yday, mon, mday, wday, hour, min, sec into *TP */
286 static void my_localtime(const time_t *t, struct tm *tm)
287 {
288   time_t days, rem, y;
289   const unsigned short int *ip;
290   int leap;
291
292   days = *t / SECS_PER_DAY;
293   rem = *t % SECS_PER_DAY;
294   rem -= timezone;
295   while (rem < 0)
296     {
297       rem += SECS_PER_DAY;
298       --days;
299     }
300   while (rem >= SECS_PER_DAY)
301     {
302       rem -= SECS_PER_DAY;
303       ++days;
304     }
305   tm->tm_hour = rem / SECS_PER_HOUR;
306   rem %= SECS_PER_HOUR;
307   tm->tm_min = rem / 60;
308   tm->tm_sec = rem % 60;
309   /* January 1, 1970 was a Thursday.  */
310   tm->tm_wday = (4 + days) % 7;
311   if (tm->tm_wday < 0)
312     tm->tm_wday += 7;
313   y = 1970;
314
315 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
316 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
317 #define ISLEAP(y)       ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
318
319   leap = ISLEAP(y);
320   while (days < 0 || days >= (leap ? 366 : 365))
321     {
322       /* Guess a corrected year, assuming 365 days per year.  */
323       time_t yg = y + days / 365 - (days % 365 < 0);
324
325       /* Adjust DAYS and Y to match the guessed year.  */
326       days -= ((yg - y) * 365
327                + LEAPS_THRU_END_OF (yg - 1)
328                - LEAPS_THRU_END_OF (y - 1));
329       y = yg;
330     }
331   tm->tm_year = y - 1900;
332   tm->tm_yday = days;
333   ip = __mon_yday[leap];
334   for (y = 11; days < (long int) ip[y]; --y)
335     continue;
336   days -= ip[y];
337   tm->tm_mon = y;
338   tm->tm_mday = days + 1;
339 }
340 #endif