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