xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/eventlog_log.c (revision 2e0fe3efe5f9d579d4e44b3532d8e342c68b40ca)
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 
42 typedef struct logr_eventlog {
43 	const char *el_name;
44 	const char *el_path;
45 } logr_eventlog_t;
46 
47 logr_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 
53 typedef 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  */
68 typedef struct logr_priority {
69 	char	*p_name;
70 	int	p_value;
71 } logr_priority_t;
72 
73 static 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 
87 typedef struct logr_syslog_node {
88 	list_node_t	ln_node;
89 	char		ln_logline[LOGR_MAXENTRYLEN];
90 } logr_syslog_node_t;
91 
92 static void *logr_interposer_hdl = NULL;
93 static 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  */
104 static void
105 logr_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  */
138 static void
139 logr_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  */
171 static int
172 logr_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 
215 static void
216 logr_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 
227 static int
228 logr_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  */
270 static int
271 logr_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  */
310 static int
311 logr_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  */
348 boolean_t
349 logr_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  */
373 int
374 logr_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  */
416 void
417 logr_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  */
442 void
443 logr_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