1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <time.h>
29#include <syslog.h>
30#include <thread.h>
31#include <string.h>
32#include <strings.h>
33#include <stdarg.h>
34#include <dlfcn.h>
35#include <sys/synch.h>
36#include <sys/stat.h>
37#include <sys/errno.h>
38#include <ctype.h>
39#include <smbsrv/ndl/eventlog.ndl>
40#include <smbsrv/libmlsvc.h>
41
42typedef struct logr_eventlog {
43	const char *el_name;
44	const char *el_path;
45} logr_eventlog_t;
46
47logr_eventlog_t logr_eventlog[] = {
48	{ "System",     "/var/adm/messages" },
49	{ "smbd",       "/var/smb/smbd_log.txt" },
50	{ "smbrdr",     "/var/smb/smbrdr_log.txt" }
51};
52
53typedef enum {
54	LOGR_MONTH = 0,
55	LOGR_DAY,
56	LOGR_TIME,
57	LOGR_HOST,
58	LOGR_SOURCE,
59	LOGR_IDTAG,
60	LOGR_ID,
61	LOGR_PRI_FAC,
62	LOGR_NARG
63} logr_syslog_tokens_t;
64
65/*
66 * Event code translation struct for use in processing config file
67 */
68typedef struct logr_priority {
69	char	*p_name;
70	int	p_value;
71} logr_priority_t;
72
73static logr_priority_t logr_pri_names[] = {
74	"panic",	LOG_EMERG,
75	"emerg",	LOG_EMERG,
76	"alert",	LOG_ALERT,
77	"crit",		LOG_CRIT,
78	"err",		LOG_ERR,
79	"error",	LOG_ERR,
80	"warn",		LOG_WARNING,
81	"warning",	LOG_WARNING,
82	"notice",	LOG_NOTICE,
83	"info",		LOG_INFO,
84	"debug",	LOG_DEBUG
85};
86
87typedef struct logr_syslog_node {
88	list_node_t	ln_node;
89	char		ln_logline[LOGR_MAXENTRYLEN];
90} logr_syslog_node_t;
91
92static void *logr_interposer_hdl = NULL;
93static struct {
94	boolean_t (*logr_op_supported)(char *);
95	int (*logr_op_snapshot)(logr_context_t *);
96} logr_interposer_ops;
97
98/*
99 * Set the syslog timestamp.
100 *
101 * This is a private helper for logr_syslog_parse_entry(), which
102 * must ensure that the appropriate argv entries are non-null.
103 */
104static void
105logr_syslog_set_timestamp(char **argv, logr_entry_t *le)
106{
107	char *month = argv[LOGR_MONTH];
108	char *day = argv[LOGR_DAY];
109	char *time = argv[LOGR_TIME];
110	struct timeval	now;
111	struct tm tm, cur_tm;
112	char buf[32];
113
114	bzero(&tm, sizeof (tm));
115	(void) snprintf(buf, 32, "%s %s %s", month, day, time);
116	if (strptime(buf, "%b" "%d" "%H:%M:%S", &tm) == NULL) {
117		le->le_timestamp.tv_sec = 0;
118		return;
119	}
120
121	(void) gettimeofday(&now, NULL);
122	(void) localtime_r(&now.tv_sec, &cur_tm);
123
124	tm.tm_isdst = cur_tm.tm_isdst;
125	tm.tm_year = cur_tm.tm_year;
126	if (tm.tm_mon > cur_tm.tm_mon)
127		tm.tm_year--;
128
129	le->le_timestamp.tv_sec = mktime(&tm);
130}
131
132/*
133 * Set the syslog priority.
134 *
135 * This is a private helper for logr_syslog_parse_entry(), which
136 * must ensure that the appropriate argv entries are non-null.
137 */
138static void
139logr_syslog_set_priority(char **argv, logr_entry_t *le)
140{
141	logr_priority_t *entry;
142	char *token;
143	int sz = sizeof (logr_pri_names) / sizeof (logr_pri_names[0]);
144	int i;
145
146	le->le_pri = LOG_INFO;
147
148	if ((token = argv[LOGR_PRI_FAC]) == NULL)
149		return;
150
151	for (i = 0; i < sz; i++) {
152		entry = &logr_pri_names[i];
153
154		if (strstr(token, entry->p_name) != NULL) {
155			le->le_pri = entry->p_value;
156			break;
157		}
158	}
159}
160
161/*
162 * Parse a syslog entry into a log_entry_t structure.  A typical syslog
163 * entry has one of the following formats:
164 *
165 * <month> <day> <time> <host> <msg>
166 * <month> <day> <time> <host> <source>: [ID <ID> <facility.priority>] <msg>
167 *
168 * For Example:
169 * Oct 29 09:49:20 galaxy smbd[104039]: [ID 702911 daemon.info] init done
170 */
171static int
172logr_syslog_parse_entry(char *logline, logr_entry_t *le)
173{
174	char buf[LOGR_MAXENTRYLEN];
175	char *argv[LOGR_NARG];
176	char *value;
177	char *bp;
178	int i;
179
180	(void) memset(argv, 0, sizeof (char *) * LOGR_NARG);
181	(void) strlcpy(buf, logline, LOGR_MAXENTRYLEN);
182
183	for (bp = buf, i = 0; i < LOGR_NARG; ++i) {
184		if (i == LOGR_SOURCE) {
185			/*
186			 * If the [ID key is not present, everything
187			 * that follows is the message text.
188			 */
189			if (strstr(bp, "[ID") == NULL)
190				break;
191		}
192
193		do {
194			if ((value = strsep(&bp, " \t")) == NULL)
195				break;
196		} while (*value == '\0');
197
198		if ((argv[i] = value) == NULL)
199			return (-1);
200	}
201
202	/*
203	 * bp should be pointing at the remaining message text.
204	 */
205	if ((value = strchr(bp, '\n')) != NULL)
206		*value = '\0';
207
208	(void) strlcpy(le->le_msg, bp, LOGR_MAXENTRYLEN);
209	(void) strlcpy(le->le_hostname, argv[LOGR_HOST], MAXHOSTNAMELEN);
210	logr_syslog_set_timestamp(argv, le);
211	logr_syslog_set_priority(argv, le);
212	return (0);
213}
214
215static void
216logr_syslog_destroy_queue(list_t *queue)
217{
218	logr_syslog_node_t *head;
219
220	while ((head = list_head(queue)) != NULL) {
221		list_remove(queue, head);
222		free(head);
223	}
224	list_destroy(queue);
225}
226
227static int
228logr_syslog_construct_queue(FILE *fp, list_t *queue)
229{
230	logr_syslog_node_t *node, *head;
231	int line_num = 0;
232	char logline[LOGR_MAXENTRYLEN];
233
234	list_create(queue, sizeof (logr_syslog_node_t),
235	    offsetof(logr_syslog_node_t, ln_node));
236
237	bzero(logline, LOGR_MAXENTRYLEN);
238	while (fgets(logline, LOGR_MAXENTRYLEN, fp) != NULL) {
239		/* Read the last 1024 entries in the queue */
240		if (line_num > LOGR_NMSGMASK) {
241			head = list_head(queue);
242			list_remove(queue, head);
243			free(head);
244		}
245
246		if ((node = malloc(sizeof (logr_syslog_node_t))) == NULL) {
247			logr_syslog_destroy_queue(queue);
248			return (-1);
249		}
250		bzero(node->ln_logline, LOGR_MAXENTRYLEN);
251
252		(void) strlcpy(node->ln_logline, logline, LOGR_MAXENTRYLEN);
253		list_insert_tail(queue, node);
254		bzero(logline, LOGR_MAXENTRYLEN);
255		line_num++;
256	}
257
258	return (0);
259}
260
261/*
262 * logr_syslog_load
263 *
264 * Loads the given log file into log_info_t structure format.
265 *
266 * Returns pointer to the allocated log structure on success.
267 * Note that the caller is responsible for freeing the allocated
268 * memory for returned log_info_t structure.
269 */
270static int
271logr_syslog_load(FILE *fp, logr_info_t *log)
272{
273	logr_entry_t *entry;
274	int i = 0;
275
276	list_t queue;
277	logr_syslog_node_t *node;
278
279	if (logr_syslog_construct_queue(fp, &queue) < 0)
280		return (-1);
281
282	node = list_head(&queue);
283	while (node) {
284		entry = &log->li_entry[i];
285
286		if (logr_syslog_parse_entry(node->ln_logline, entry) != 0) {
287			node = list_next(&queue, node);
288			continue;
289		}
290
291		if (++i > LOGR_NMSGMASK)
292			break;
293
294		node = list_next(&queue, node);
295	}
296
297	logr_syslog_destroy_queue(&queue);
298	log->li_idx = i;
299
300	return (0);
301}
302
303/*
304 * logr_syslog_snapshot
305 *
306 * Return a snapshot of the given log in the buffer
307 * provided by the caller. Returns the number of entries in
308 * the log.
309 */
310static int
311logr_syslog_snapshot(char *logname, logr_info_t *loginfo)
312{
313	FILE *fp;
314	char path[MAXPATHLEN];
315	int i;
316
317	if ((loginfo == NULL) || (!logr_is_supported(logname)))
318		return (-1);
319
320	path[0] = '\0';
321	for (i = 0; i < sizeof (logr_eventlog)/sizeof (logr_eventlog[0]); ++i) {
322		if (strcasecmp(logname, logr_eventlog[i].el_name) == 0)
323			(void) strlcpy(path, logr_eventlog[i].el_path,
324			    MAXPATHLEN);
325	}
326
327	if ((fp = fopen(path, "r")) == 0)
328		return (-1);
329
330	if (logr_syslog_load(fp, loginfo) < 0) {
331		(void) fclose(fp);
332		return (-1);
333	}
334	(void) fclose(fp);
335
336	if (loginfo->li_idx <= LOGR_NMSGMASK)
337		return (loginfo->li_idx);
338
339	return (LOGR_NMSGMASK+1);
340}
341
342/*
343 * logr_is_supported
344 *
345 * Determines if a given log is supported or not.
346 * Returns B_TRUE on success, B_FALSE on failure.
347 */
348boolean_t
349logr_is_supported(char *log_name)
350{
351	int i;
352
353	if (log_name == NULL)
354		return (B_FALSE);
355
356	if (logr_interposer_ops.logr_op_supported != NULL)
357		return (logr_interposer_ops.logr_op_supported(log_name));
358
359	for (i = 0; i < sizeof (logr_eventlog)/sizeof (logr_eventlog[0]); ++i) {
360		if (strcasecmp(log_name, logr_eventlog[i].el_name) == 0)
361			return (B_TRUE);
362	}
363
364	return (B_FALSE);
365}
366
367/*
368 * logr_get_snapshot
369 *
370 * Allocate memory and make a copy, as a snapshot, from system log.
371 * Returns 0 on success, -1 on failure.
372 */
373int
374logr_get_snapshot(logr_context_t *ctx)
375{
376	logr_read_data_t *data = NULL;
377
378	if (logr_interposer_ops.logr_op_snapshot != NULL)
379		return (logr_interposer_ops.logr_op_snapshot(ctx));
380
381	ctx->lc_cached_read_data = malloc(sizeof (logr_read_data_t));
382	if (ctx->lc_cached_read_data != NULL) {
383		data = ctx->lc_cached_read_data;
384
385		data->rd_log = (logr_info_t *)malloc(sizeof (logr_info_t));
386		if (data->rd_log == NULL) {
387			free(data);
388			return (-1);
389		}
390		bzero(data->rd_log, sizeof (logr_info_t));
391
392		data->rd_tot_recnum = logr_syslog_snapshot(ctx->lc_source_name,
393		    data->rd_log);
394		if (data->rd_tot_recnum < 0) {
395			free(data->rd_log);
396			free(data);
397			return (-1);
398		}
399
400		data->rd_first_read = 1;
401
402		return (0);
403	}
404
405	return (-1);
406}
407
408/*
409 * logr_init
410 *
411 * Initializes the Eventlog service.
412 * Checks to see if a event log utility library
413 * is interposed. If yes then it'll initializes logr_interposer_ops
414 * structure with function pointers from this library.
415 */
416void
417logr_init(void)
418{
419	logr_interposer_hdl = smb_dlopen();
420	if (logr_interposer_hdl == NULL)
421		return;
422
423	bzero((void *)&logr_interposer_ops, sizeof (logr_interposer_ops));
424
425	logr_interposer_ops.logr_op_supported =
426	    (boolean_t (*)())dlsym(logr_interposer_hdl, "logr_is_supported");
427
428	logr_interposer_ops.logr_op_snapshot =
429	    (int (*)())dlsym(logr_interposer_hdl, "logr_get_snapshot");
430
431	if (logr_interposer_ops.logr_op_supported == NULL ||
432	    logr_interposer_ops.logr_op_snapshot == NULL)
433		logr_fini();
434}
435
436/*
437 * logr_fini
438 *
439 * Finalizes the Eventlog service.
440 * Closes handle to interposed library.
441 */
442void
443logr_fini(void)
444{
445	smb_dlclose(logr_interposer_hdl);
446	logr_interposer_hdl = NULL;
447	bzero((void *)&logr_interposer_ops, sizeof (logr_interposer_ops));
448}
449