View | Details | Raw Unified | Return to bug 109478
Collapse All | Expand All

(-)sys/sys/syslog.h (+20 lines)
Lines 151-156 Link Here
151
};
151
};
152
#endif
152
#endif
153
153
154
/* Used by reentrant functions */
155
156
struct syslog_data {
157
        int     log_file;
158
        int     connected;
159
        int     opened;
160
        int     log_stat;
161
        const char      *log_tag;
162
        int     log_fac;
163
        int     log_mask;
164
};
165
166
#define SYSLOG_DATA_INIT {-1, 0, 0, 0, (const char *)0, LOG_USER, 0xff}
167
154
#ifdef _KERNEL
168
#ifdef _KERNEL
155
#define	LOG_PRINTF	-1	/* pseudo-priority to indicate use of printf */
169
#define	LOG_PRINTF	-1	/* pseudo-priority to indicate use of printf */
156
#endif
170
#endif
Lines 194-199 Link Here
194
int	setlogmask(int);
208
int	setlogmask(int);
195
void	syslog(int, const char *, ...) __printflike(2, 3);
209
void	syslog(int, const char *, ...) __printflike(2, 3);
196
void	vsyslog(int, const char *, __va_list) __printflike(2, 0);
210
void	vsyslog(int, const char *, __va_list) __printflike(2, 0);
211
void    closelog_r(struct syslog_data *);
212
void    openlog_r(const char *, int, int, struct syslog_data *);
213
int     setlogmask_r(int, struct syslog_data *);
214
void    syslog_r(int, struct syslog_data *, const char *, ...)
215
     __printflike(3, 4);
216
void    vsyslog_r(int, struct syslog_data *, const char *, __va_list);
197
__END_DECLS
217
__END_DECLS
198
218
199
#endif /* !_KERNEL */
219
#endif /* !_KERNEL */
(-)lib/libc/gen/syslog.c (-253 / +211 lines)
Lines 56-69 Link Here
56
56
57
#include "libc_private.h"
57
#include "libc_private.h"
58
58
59
static int	LogFile = -1;		/* fd for log */
59
static struct syslog_data sdata = SYSLOG_DATA_INIT;
60
static int	status;			/* connection status */
61
static int	opened;			/* have done openlog() */
62
static int	LogStat = 0;		/* status bits, set by openlog() */
63
static const char *LogTag = NULL;	/* string to tag the entry with */
64
static int	LogFacility = LOG_USER;	/* default facility code */
65
static int	LogMask = 0xff;		/* mask of priorities to be logged */
66
static pthread_mutex_t	syslog_mutex = PTHREAD_MUTEX_INITIALIZER;
67
60
68
#define	THREAD_LOCK()							\
61
#define	THREAD_LOCK()							\
69
	do { 								\
62
	do { 								\
Lines 74-82 Link Here
74
		if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex);	\
67
		if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex);	\
75
	} while(0)
68
	} while(0)
76
69
77
static void	disconnectlog(void); /* disconnect from syslogd */
70
static void	connectlog_r(struct syslog_data *data);
78
static void	connectlog(void);	/* (re)connect to syslogd */
71
static void	disconnectlog_r(struct syslog_data *data);
79
static void	openlog_unlocked(const char *, int, int);
80
72
81
enum {
73
enum {
82
	NOCONN = 0,
74
	NOCONN = 0,
Lines 93-121 Link Here
93
};
85
};
94
86
95
/*
87
/*
96
 * stdio write hook for writing to a static string buffer
97
 * XXX: Maybe one day, dynamically allocate it so that the line length
98
 *      is `unlimited'.
99
 */
100
static int
101
writehook(void *cookie, const char *buf, int len)
102
{
103
	struct bufcookie *h;	/* private `handle' */
104
105
	h = (struct bufcookie *)cookie;
106
	if (len > h->left) {
107
		/* clip in case of wraparound */
108
		len = h->left;
109
	}
110
	if (len > 0) {
111
		(void)memcpy(h->base, buf, len); /* `write' it. */
112
		h->base += len;
113
		h->left -= len;
114
	}
115
	return len;
116
}
117
118
/*
119
 * syslog, vsyslog --
88
 * syslog, vsyslog --
120
 *	print message on log file; output is intended for syslogd(8).
89
 *	print message on log file; output is intended for syslogd(8).
121
 */
90
 */
Lines 132-297 Link Here
132
void
101
void
133
vsyslog(int pri, const char *fmt, va_list ap)
102
vsyslog(int pri, const char *fmt, va_list ap)
134
{
103
{
104
	vsyslog_r(pri, &sdata, fmt, ap);
105
}
106
107
void
108
openlog(const char *ident, int logstat, int logfac)
109
{
110
        openlog_r(ident, logstat, logfac, &sdata);
111
}
112
113
void
114
closelog(void)
115
{
116
        closelog_r(&sdata);
117
}
118
119
/* setlogmask -- set the log mask level */
120
int
121
setlogmask(int pmask)
122
{
123
        return setlogmask_r(pmask, &sdata);
124
}
125
126
/* Reentrant version of syslog, i.e. syslog_r() */
127
128
void
129
syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
130
{
131
        va_list ap;
132
133
        va_start(ap, fmt);
134
        vsyslog_r(pri, data, fmt, ap);
135
        va_end(ap);
136
}
137
138
void
139
vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
140
{
135
	int cnt;
141
	int cnt;
136
	char ch, *p;
142
	char ch, *p, *t;
137
	time_t now;
143
	time_t now;
138
	int fd, saved_errno;
144
	int fd, saved_errno, error;
139
	char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26], errstr[64];
145
#define	TBUF_LEN	2048
140
	FILE *fp, *fmt_fp;
146
#define	FMT_LEN		1024
141
	struct bufcookie tbuf_cookie;
147
	char *stdp, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
142
	struct bufcookie fmt_cookie;
148
	int tbuf_left, fmt_left, prlen;
143
149
144
#define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
150
#define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
145
	/* Check for invalid bits. */
151
	/* Check for invalid bits. */
146
	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
152
	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
147
		syslog(INTERNALLOG,
153
		if (data == &sdata) {
148
		    "syslog: unknown facility/priority: %x", pri);
154
			syslog(INTERNALLOG,
155
			    "syslog: unknown facility/priority: %x", pri);
156
		} else {
157
			syslog_r(INTERNALLOG, data,
158
			    "syslog_r: unknown facility/priority: %x", pri);
159
		}
149
		pri &= LOG_PRIMASK|LOG_FACMASK;
160
		pri &= LOG_PRIMASK|LOG_FACMASK;
150
	}
161
	}
151
162
152
	saved_errno = errno;
153
154
	THREAD_LOCK();
155
156
	/* Check priority against setlogmask values. */
163
	/* Check priority against setlogmask values. */
157
	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) {
164
	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
158
		THREAD_UNLOCK();
159
		return;
165
		return;
160
	}
166
167
	saved_errno = errno;
161
168
162
	/* Set default facility if none specified. */
169
	/* Set default facility if none specified. */
163
	if ((pri & LOG_FACMASK) == 0)
170
	if ((pri & LOG_FACMASK) == 0)
164
		pri |= LogFacility;
171
		pri |= data->log_fac;
165
172
166
	/* Create the primary stdio hook */
173
	/* If we have been called through syslog(), no need for reentrancy. */
167
	tbuf_cookie.base = tbuf;
174
	if (data == &sdata)
168
	tbuf_cookie.left = sizeof(tbuf);
175
		(void)time(&now);
169
	fp = fwopen(&tbuf_cookie, writehook);
176
170
	if (fp == NULL) {
177
	p = tbuf;
171
		THREAD_UNLOCK();
178
	tbuf_left = TBUF_LEN;
172
		return;
179
173
	}
180
#define	DEC()	\
174
181
	do {					\
175
	/* Build the message. */
182
		if (prlen < 0)			\
176
	(void)time(&now);
183
			prlen = 0;		\
177
	(void)fprintf(fp, "<%d>", pri);
184
		if (prlen >= tbuf_left)		\
178
	(void)fprintf(fp, "%.15s ", ctime_r(&now, timbuf) + 4);
185
			prlen = tbuf_left - 1;	\
179
	if (LogStat & LOG_PERROR) {
186
		p += prlen;			\
180
		/* Transfer to string buffer */
187
		tbuf_left -= prlen;		\
181
		(void)fflush(fp);
188
	} while (0)
182
		stdp = tbuf + (sizeof(tbuf) - tbuf_cookie.left);
189
183
	}
190
	prlen = snprintf(p, tbuf_left, "<%d>", pri);
184
	if (LogTag == NULL)
191
	DEC();
185
		LogTag = _getprogname();
192
186
	if (LogTag != NULL)
193
	/* 
187
		(void)fprintf(fp, "%s", LogTag);
194
	 * syslogd will expand time automagically for reentrant case, and
188
	if (LogStat & LOG_PID)
195
	 * for normal case, just do like before
189
		(void)fprintf(fp, "[%d]", getpid());
196
	 */
190
	if (LogTag != NULL) {
197
	if (data == &sdata) {
191
		(void)fprintf(fp, ": ");
198
		prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now));
199
		DEC();
200
	}
201
202
	if (data->log_stat & LOG_PERROR)
203
		stdp = p;
204
	if (data->log_tag == NULL)
205
		data->log_tag = __progname;
206
	if (data->log_tag != NULL) {
207
		prlen = snprintf(p, tbuf_left, "%s", data->log_tag);
208
		DEC();
209
	}
210
	if (data->log_stat & LOG_PID) {
211
		prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid());
212
		DEC();
213
	}
214
	if (data->log_tag != NULL) {
215
		if (tbuf_left > 1) {
216
			*p++ = ':';
217
			tbuf_left--;
218
		}
219
		if (tbuf_left > 1) {
220
			*p++ = ' ';
221
			tbuf_left--;
222
		}
192
	}
223
	}
193
224
194
	/* Check to see if we can skip expanding the %m */
225
	/* strerror() is not reentrant */
195
	if (strstr(fmt, "%m")) {
196
197
		/* Create the second stdio hook */
198
		fmt_cookie.base = fmt_cpy;
199
		fmt_cookie.left = sizeof(fmt_cpy) - 1;
200
		fmt_fp = fwopen(&fmt_cookie, writehook);
201
		if (fmt_fp == NULL) {
202
			fclose(fp);
203
			THREAD_UNLOCK();
204
			return;
205
		}
206
226
207
		/*
227
	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
208
		 * Substitute error message for %m.  Be careful not to
228
		if (ch == '%' && fmt[1] == 'm') {
209
		 * molest an escaped percent "%%m".  We want to pass it
229
			++fmt;
210
		 * on untouched as the format is later parsed by vfprintf.
230
			if (data == &sdata) {
211
		 */
231
				prlen = snprintf(t, fmt_left, "%s",
212
		for ( ; (ch = *fmt); ++fmt) {
232
				    strerror(saved_errno)); 
213
			if (ch == '%' && fmt[1] == 'm') {
214
				++fmt;
215
				strerror_r(saved_errno, errstr, sizeof(errstr));
216
				fputs(errstr, fmt_fp);
217
			} else if (ch == '%' && fmt[1] == '%') {
218
				++fmt;
219
				fputc(ch, fmt_fp);
220
				fputc(ch, fmt_fp);
221
			} else {
233
			} else {
222
				fputc(ch, fmt_fp);
234
				prlen = snprintf(t, fmt_left, "Error %d",
235
				    saved_errno); 
236
			}
237
			if (prlen < 0)
238
				prlen = 0;
239
			if (prlen >= fmt_left)
240
				prlen = fmt_left - 1;
241
			t += prlen;
242
			fmt_left -= prlen;
243
		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
244
			*t++ = '%';
245
			*t++ = '%';
246
			fmt++;
247
			fmt_left -= 2;
248
		} else {
249
			if (fmt_left > 1) {
250
				*t++ = ch;
251
				fmt_left--;
223
			}
252
			}
224
		}
253
		}
225
226
		/* Null terminate if room */
227
		fputc(0, fmt_fp);
228
		fclose(fmt_fp);
229
230
		/* Guarantee null termination */
231
		fmt_cpy[sizeof(fmt_cpy) - 1] = '\0';
232
233
		fmt = fmt_cpy;
234
	}
254
	}
255
	*t = '\0';
235
256
236
	(void)vfprintf(fp, fmt, ap);
257
	prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
237
	(void)fclose(fp);
258
	DEC();
238
259
	cnt = p - tbuf;
239
	cnt = sizeof(tbuf) - tbuf_cookie.left;
240
241
	/* Remove a trailing newline */
242
	if (tbuf[cnt - 1] == '\n')
243
		cnt--;
244
260
245
	/* Output to stderr if requested. */
261
	/* Output to stderr if requested. */
246
	if (LogStat & LOG_PERROR) {
262
	if (data->log_stat & LOG_PERROR) {
247
		struct iovec iov[2];
263
		struct iovec iov[2];
248
		struct iovec *v = iov;
249
264
250
		v->iov_base = stdp;
265
		iov[0].iov_base = stdp;
251
		v->iov_len = cnt - (stdp - tbuf);
266
		iov[0].iov_len = cnt - (stdp - tbuf);
252
		++v;
267
		iov[1].iov_base = "\n";
253
		v->iov_base = "\n";
268
		iov[1].iov_len = 1;
254
		v->iov_len = 1;
255
		(void)_writev(STDERR_FILENO, iov, 2);
269
		(void)_writev(STDERR_FILENO, iov, 2);
256
	}
270
	}
257
271
258
	/* Get connected, output the message to the local logger. */
272
	/* Get connected, output the message to the local logger. */
259
	if (!opened)
273
	if (!data->opened)
260
		openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0);
274
		openlog_r(data->log_tag, data->log_stat, 0, data);
261
	connectlog();
275
	connectlog_r(data);
262
276
263
	/*
277
	/*
264
	 * If the send() failed, there are two likely scenarios: 
278
	 * If the send() failed, there are two likely scenarios:
265
	 *  1) syslogd was restarted
279
	 *  1) syslogd was restarted
266
	 *  2) /var/run/log is out of socket buffer space, which
280
	 *  2) /dev/log is out of socket buffer space
267
	 *     in most cases means local DoS.
281
	 * We attempt to reconnect to /dev/log to take care of
268
	 * We attempt to reconnect to /var/run/log to take care of
269
	 * case #1 and keep send()ing data to cover case #2
282
	 * case #1 and keep send()ing data to cover case #2
270
	 * to give syslogd a chance to empty its socket buffer.
283
	 * to give syslogd a chance to empty its socket buffer.
271
	 *
272
	 * If we are working with a priveleged socket, then take
273
	 * only one attempt, because we don't want to freeze a
274
	 * critical application like su(1) or sshd(8).
275
	 *
276
	 */
284
	 */
277
285
	if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
278
	if (send(LogFile, tbuf, cnt, 0) < 0) {
279
		if (errno != ENOBUFS) {
286
		if (errno != ENOBUFS) {
280
			disconnectlog();
287
			disconnectlog_r(data);
281
			connectlog();
288
			connectlog_r(data);
282
		}
289
		}
283
		do {
290
		do {
284
			_usleep(1);
291
			_usleep(1);
285
			if (send(LogFile, tbuf, cnt, 0) >= 0) {
292
			if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
286
				THREAD_UNLOCK();
287
				return;
288
			}
289
			if (status == CONNPRIV)
290
				break;
293
				break;
291
		} while (errno == ENOBUFS);
294
		} while (errno == ENOBUFS);
292
	} else {
293
		THREAD_UNLOCK();
294
		return;
295
	}
295
	}
296
296
297
	/*
297
	/*
Lines 299-435 Link Here
299
	 * as a blocking console should not stop other processes.
299
	 * as a blocking console should not stop other processes.
300
	 * Make sure the error reported is the one from the syslogd failure.
300
	 * Make sure the error reported is the one from the syslogd failure.
301
	 */
301
	 */
302
	if (LogStat & LOG_CONS &&
302
	if (error == -1 && (data->log_stat & LOG_CONS) &&
303
	    (fd = _open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
303
	    (fd = _open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
304
		struct iovec iov[2];
304
		struct iovec iov[2];
305
		struct iovec *v = iov;
305
		
306
307
		p = strchr(tbuf, '>') + 1;
306
		p = strchr(tbuf, '>') + 1;
308
		v->iov_base = p;
307
		iov[0].iov_base = p;
309
		v->iov_len = cnt - (p - tbuf);
308
		iov[0].iov_len = cnt - (p - tbuf);
310
		++v;
309
		iov[1].iov_base = "\r\n";
311
		v->iov_base = "\r\n";
310
		iov[1].iov_len = 2;
312
		v->iov_len = 2;
313
		(void)_writev(fd, iov, 2);
311
		(void)_writev(fd, iov, 2);
314
		(void)_close(fd);
312
		(void)_close(fd);
315
	}
313
	}
316
314
317
	THREAD_UNLOCK();
315
	if (data != &sdata)
316
		closelog_r(data);
318
}
317
}
319
318
320
/* Should be called with mutex acquired */
321
static void
319
static void
322
disconnectlog(void)
320
disconnectlog_r(struct syslog_data *data)
323
{
321
{
324
	/*
322
        /*
325
	 * If the user closed the FD and opened another in the same slot,
323
         * If the user closed the FD and opened another in the same slot,
326
	 * that's their problem.  They should close it before calling on
324
         * that's their problem.  They should close it before calling on
327
	 * system services.
325
         * system services.
328
	 */
326
         */
329
	if (LogFile != -1) {
327
        if (data->log_file != -1) {
330
		_close(LogFile);
328
                _close(data->log_file);
331
		LogFile = -1;
329
                data->log_file = -1;
332
	}
330
        }
333
	status = NOCONN;			/* retry connect */
331
        data->connected = 0;            /* retry connect */
334
}
332
}
335
333
336
/* Should be called with mutex acquired */
337
static void
334
static void
338
connectlog(void)
335
connectlog_r(struct syslog_data *data)
339
{
336
{
340
	struct sockaddr_un SyslogAddr;	/* AF_UNIX address of local logger */
337
        struct sockaddr_un SyslogAddr;  /* AF_UNIX address of local logger */
341
342
	if (LogFile == -1) {
343
		if ((LogFile = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
344
			return;
345
		(void)_fcntl(LogFile, F_SETFD, 1);
346
	}
347
	if (LogFile != -1 && status == NOCONN) {
348
		SyslogAddr.sun_len = sizeof(SyslogAddr);
349
		SyslogAddr.sun_family = AF_UNIX;
350
351
		/*
352
		 * First try priveleged socket. If no success,
353
		 * then try default socket.
354
		 */
355
		(void)strncpy(SyslogAddr.sun_path, _PATH_LOG_PRIV,
356
		    sizeof SyslogAddr.sun_path);
357
		if (_connect(LogFile, (struct sockaddr *)&SyslogAddr,
358
		    sizeof(SyslogAddr)) != -1)
359
			status = CONNPRIV;
360
361
		if (status == NOCONN) {
362
			(void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
363
			    sizeof SyslogAddr.sun_path);
364
			if (_connect(LogFile, (struct sockaddr *)&SyslogAddr,
365
			    sizeof(SyslogAddr)) != -1)
366
				status = CONNDEF;
367
		}
368
369
		if (status == NOCONN) {
370
			/*
371
			 * Try the old "/dev/log" path, for backward
372
			 * compatibility.
373
			 */
374
			(void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG,
375
			    sizeof SyslogAddr.sun_path);
376
			if (_connect(LogFile, (struct sockaddr *)&SyslogAddr,
377
			    sizeof(SyslogAddr)) != -1)
378
				status = CONNDEF;
379
		}
380
338
381
		if (status == NOCONN) {
339
        if (data->log_file == -1) {
382
			(void)_close(LogFile);
340
                if ((data->log_file = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
383
			LogFile = -1;
341
                        return;
384
		}
342
                (void)_fcntl(data->log_file, F_SETFD, 1);
385
	}
343
        }
344
        if (data->log_file != -1 && !data->connected) {
345
                memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
346
                SyslogAddr.sun_len = sizeof(SyslogAddr);
347
                SyslogAddr.sun_family = AF_UNIX;
348
                strlcpy(SyslogAddr.sun_path, _PATH_LOG,
349
                    sizeof(SyslogAddr.sun_path));
350
                if (_connect(data->log_file, (struct sockaddr *)&SyslogAddr,
351
                    sizeof(SyslogAddr)) == -1) {
352
                        (void)_close(data->log_file);
353
                        data->log_file = -1;
354
                } else
355
                        data->connected = 1;
356
        }
386
}
357
}
387
358
388
static void
359
void
389
openlog_unlocked(const char *ident, int logstat, int logfac)
360
openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
390
{
361
{
391
	if (ident != NULL)
362
        if (ident != NULL)
392
		LogTag = ident;
363
                data->log_tag = ident;
393
	LogStat = logstat;
364
        data->log_stat = logstat;
394
	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
365
        if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
395
		LogFacility = logfac;
366
                data->log_fac = logfac;
396
367
397
	if (LogStat & LOG_NDELAY)	/* open immediately */
368
        if (data->log_stat & LOG_NDELAY)        /* open immediately */
398
		connectlog();
369
                connectlog_r(data);
399
370
400
	opened = 1;	/* ident and facility has been set */
371
        data->opened = 1;       /* ident and facility has been set */
401
}
402
403
void
404
openlog(const char *ident, int logstat, int logfac)
405
{
406
	THREAD_LOCK();
407
	openlog_unlocked(ident, logstat, logfac);
408
	THREAD_UNLOCK();
409
}
372
}
410
373
411
412
void
374
void
413
closelog(void)
375
closelog_r(struct syslog_data *data)
414
{
376
{
415
	THREAD_LOCK();
377
        (void)_close(data->log_file);
416
	(void)_close(LogFile);
378
        data->log_file = -1;
417
	LogFile = -1;
379
        data->connected = 0;
418
	LogTag = NULL;
380
        data->log_tag = NULL;
419
	status = NOCONN;
420
	THREAD_UNLOCK();
421
}
381
}
422
382
423
/* setlogmask -- set the log mask level */
383
/* setlogmask -- set the log mask level */
424
int
384
int
425
setlogmask(int pmask)
385
setlogmask_r(int pmask, struct syslog_data *data)
426
{
386
{
427
	int omask;
387
        int omask;
428
388
429
	THREAD_LOCK();
389
        omask = data->log_mask;
430
	omask = LogMask;
390
        if (pmask != 0)
431
	if (pmask != 0)
391
                data->log_mask = pmask;
432
		LogMask = pmask;
392
        return (omask);
433
	THREAD_UNLOCK();
434
	return (omask);
435
}
393
}

Return to bug 109478