]> git.sur5r.net Git - openldap/blob - servers/slapd/syslog.c
Merge remote-tracking branch 'origin/mdb.RE/0.9'
[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
51 static int      LogType = SOCK_DGRAM;   /* type of socket connection */
52 static int      LogFile = -1;           /* fd for log */
53 static int      connected;              /* have done connect */
54 static int      LogStat;                /* status bits, set by openlog() */
55 static const char *LogTag;              /* string to tag the entry with */
56 static int      LogFacility = LOG_USER; /* default facility code */
57 static int      LogMask = 0xff;         /* mask of priorities to be logged */
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 /*
65  * syslog
66  *      print message on log file; output is intended for syslogd(8).
67  */
68 void
69 syslog(int pri, const char *fmt, ...)
70 {
71         va_list ap;
72         char *p, *t;
73 #define TBUF_LEN        2048
74 #define FMT_LEN         1024
75         char *stdp, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
76         time_t now;
77         int cnt;
78         int fd, saved_errno, error;
79         char ch;
80         int tbuf_left, fmt_left, prlen;
81         struct tm tm;
82
83         va_start(ap, fmt);
84
85         /* Check for invalid bits. */
86         if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
87                 if (LogTest(LOG_ERR))
88                         lutil_debug(slap_debug, LOG_ERR, 
89                             "syslog: unknown facility/priority: %x", pri);
90                 pri &= LOG_PRIMASK|LOG_FACMASK;
91         }
92
93         saved_errno = errno;
94
95         /* Set default facility if none specified. */
96         if ((pri & LOG_FACMASK) == 0)
97                 pri |= LogFacility;
98
99         (void)time(&now);
100
101         p = tbuf;
102         tbuf_left = TBUF_LEN;
103
104 #define DEC()   \
105         do {                                    \
106                 if (prlen < 0)                  \
107                         prlen = 0;              \
108                 if (prlen >= tbuf_left)         \
109                         prlen = tbuf_left - 1;  \
110                 p += prlen;                     \
111                 tbuf_left -= prlen;             \
112         } while (0)
113
114         prlen = snprintf(p, tbuf_left, "<%d>", pri);
115         DEC();
116
117         my_localtime(&now, &tm);
118         prlen = strftime(p, tbuf_left, "%h %e %T ", &tm);
119         DEC();
120
121         if (LogTag != NULL) {
122                 prlen = snprintf(p, tbuf_left, "%s", LogTag);
123                 DEC();
124         }
125         if (LogStat & LOG_PID) {
126                 prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid());
127                 DEC();
128         }
129         if (LogTag != NULL) {
130                 if (tbuf_left > 1) {
131                         *p++ = ':';
132                         tbuf_left--;
133                 }
134                 if (tbuf_left > 1) {
135                         *p++ = ' ';
136                         tbuf_left--;
137                 }
138         }
139
140         /* strerror() is not reentrant */
141
142         for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
143                 if (ch == '%' && fmt[1] == 'm') {
144                         ++fmt;
145                         prlen = snprintf(t, fmt_left, "%s",
146                             strerror(saved_errno)); 
147                         if (prlen < 0)
148                                 prlen = 0;
149                         if (prlen >= fmt_left)
150                                 prlen = fmt_left - 1;
151                         t += prlen;
152                         fmt_left -= prlen;
153                 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
154                         *t++ = '%';
155                         *t++ = '%';
156                         fmt++;
157                         fmt_left -= 2;
158                 } else {
159                         if (fmt_left > 1) {
160                                 *t++ = ch;
161                                 fmt_left--;
162                         }
163                 }
164         }
165         *t = '\0';
166
167         prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
168         DEC();
169         cnt = p - tbuf;
170         va_end(ap);
171
172         /* Get connected, output the message to the local logger. */
173         if (LogFile == -1)
174                 openlog(LogTag, LogStat, 0);
175         connectlog();
176
177         /*
178          * If the send() failed, there are two likely scenarios:
179          *  1) syslogd was restarted
180          *  2) /dev/log is out of socket buffer space
181          * We attempt to reconnect to /dev/log to take care of
182          * case #1 and keep send()ing data to cover case #2
183          * to give syslogd a chance to empty its socket buffer.
184          */
185         if ((error = send(LogFile, tbuf, cnt, 0)) < 0) {
186                 if (errno != ENOBUFS) {
187                         disconnectlog();
188                         connectlog();
189                 }
190                 do {
191                         usleep(1);
192                         if ((error = send(LogFile, tbuf, cnt, 0)) >= 0)
193                                 break;
194                 } while (errno == ENOBUFS);
195         }
196 }
197
198 static void
199 disconnectlog(void)
200 {
201         /*
202          * If the user closed the FD and opened another in the same slot,
203          * that's their problem.  They should close it before calling on
204          * system services.
205          */
206         if (LogFile != -1) {
207                 close(LogFile);
208                 LogFile = -1;
209         }
210         connected = 0;          /* retry connect */
211 }
212
213 static void
214 connectlog(void)
215 {
216         struct sockaddr_un SyslogAddr;  /* AF_UNIX address of local logger */
217
218         if (LogFile == -1) {
219                 if ((LogFile = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
220                         return;
221                 (void)fcntl(LogFile, F_SETFD, FD_CLOEXEC);
222         }
223         if (LogFile != -1 && !connected) {
224                 memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
225 #ifdef _BSD
226                 SyslogAddr.sun_len = sizeof(SyslogAddr);
227 #endif
228                 SyslogAddr.sun_family = AF_UNIX;
229                 strncpy(SyslogAddr.sun_path, _PATH_LOG,
230                     sizeof(SyslogAddr.sun_path));
231                 if (connect(LogFile, (struct sockaddr *)&SyslogAddr,
232                     sizeof(SyslogAddr)) == -1) {
233                         (void)close(LogFile);
234                         LogFile = -1;
235                 } else
236                         connected = 1;
237         }
238 }
239
240 void
241 openlog(const char *ident, int logstat, int logfac)
242 {
243         if (ident != NULL)
244                 LogTag = ident;
245         LogStat = logstat;
246         if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
247                 LogFacility = logfac;
248
249         if (LogStat & LOG_NDELAY)       /* open immediately */
250                 connectlog();
251 }
252
253 void
254 closelog()
255 {
256         (void)close(LogFile);
257         LogFile = -1;
258         connected = 0;
259         LogTag = NULL;
260 }
261
262 #define SECS_PER_HOUR   (60 * 60)
263 #define SECS_PER_DAY    (SECS_PER_HOUR * 24)
264
265 /* How many days come before each month (0-12).  */
266 static const unsigned short int __mon_yday[2][13] =
267   {
268     /* Normal years.  */
269     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
270     /* Leap years.  */
271     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
272   };
273
274 /* Compute the `struct tm' representation of *T,
275    and store year, yday, mon, mday, wday, hour, min, sec into *TP */
276 static void my_localtime(const time_t *t, struct tm *tm)
277 {
278   time_t days, rem, y;
279   const unsigned short int *ip;
280   int leap;
281
282   days = *t / SECS_PER_DAY;
283   rem = *t % SECS_PER_DAY;
284   rem -= timezone;
285   while (rem < 0)
286     {
287       rem += SECS_PER_DAY;
288       --days;
289     }
290   while (rem >= SECS_PER_DAY)
291     {
292       rem -= SECS_PER_DAY;
293       ++days;
294     }
295   tm->tm_hour = rem / SECS_PER_HOUR;
296   rem %= SECS_PER_HOUR;
297   tm->tm_min = rem / 60;
298   tm->tm_sec = rem % 60;
299   /* January 1, 1970 was a Thursday.  */
300   tm->tm_wday = (4 + days) % 7;
301   if (tm->tm_wday < 0)
302     tm->tm_wday += 7;
303   y = 1970;
304
305 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
306 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
307 #define ISLEAP(y)       ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
308
309   leap = ISLEAP(y);
310   while (days < 0 || days >= (leap ? 366 : 365))
311     {
312       /* Guess a corrected year, assuming 365 days per year.  */
313       time_t yg = y + days / 365 - (days % 365 < 0);
314
315       /* Adjust DAYS and Y to match the guessed year.  */
316       days -= ((yg - y) * 365
317                + LEAPS_THRU_END_OF (yg - 1)
318                - LEAPS_THRU_END_OF (y - 1));
319       y = yg;
320     }
321   tm->tm_year = y - 1900;
322   tm->tm_yday = days;
323   ip = __mon_yday[leap];
324   for (y = 11; days < (long int) ip[y]; --y)
325     continue;
326   days -= ip[y];
327   tm->tm_mon = y;
328   tm->tm_mday = days + 1;
329 }