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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <syslog.h>
31 #include <thread.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdarg.h>
35 #include <sys/synch.h>
36 #include <sys/stat.h>
37 #include <sys/errno.h>
38 #include <ctype.h>
39 #include "eventlog.h"
40 
41 #define	LOGR_SYSLOG_PARSE_ENTRY_SUCCESS	0
42 #define	LOGR_SYSLOG_PARSE_ENTRY_ERR	-1
43 #define	LOGR_SYSLOG_PARSE_NOPRI_ERR	-2
44 
45 #define	LOGR_SYSLOG_PARSE_IDTOKEN_PFX	"[ID"
46 
47 typedef enum {
48 	LOGR_SYSLOG_MONTH = 0,
49 	LOGR_SYSLOG_DAY,
50 	LOGR_SYSLOG_TIME,
51 	LOGR_SYSLOG_MACHINENAME,
52 	LOGR_SYSLOG_SOURCE,
53 	LOGR_SYSLOG_ID,
54 	LOGR_SYSLOG_PRI_FAC,
55 	LOGR_SYSLOG_NARG
56 } logr_syslog_tokens;
57 
58 typedef enum {
59 	LOGR_SYSLOG_FACILITY = 0,
60 	LOGR_SYSLOG_PRIORITY,
61 	LOGR_SYSLOG_PRI_FAC_NARG
62 } logr_syslog_pri_fac_tokens;
63 
64 /*
65  * Event code translation struct for use in processing config file
66  */
67 typedef struct logr_code_tbl {
68 	char	*c_name;
69 	int	c_val;
70 } logr_code_tbl_t;
71 
72 static logr_code_tbl_t	logr_syslog_pri_names[] = {
73 	"panic",	LOG_EMERG,
74 	"emerg",	LOG_EMERG,
75 	"alert",	LOG_ALERT,
76 	"crit",		LOG_CRIT,
77 	"err",		LOG_ERR,
78 	"error",	LOG_ERR,
79 	"warn",		LOG_WARNING,
80 	"warning",	LOG_WARNING,
81 	"notice",	LOG_NOTICE,
82 	"info",		LOG_INFO,
83 	"debug",	LOG_DEBUG
84 };
85 
86 static logr_code_tbl_t	logr_syslog_fac_names[] = {
87 	"kern",		LOG_KERN,
88 	"user",		LOG_USER,
89 	"mail",		LOG_MAIL,
90 	"daemon",	LOG_DAEMON,
91 	"auth",		LOG_AUTH,
92 	"security",	LOG_AUTH,
93 	"syslog",	LOG_SYSLOG,
94 	"lpr",		LOG_LPR,
95 	"news",		LOG_NEWS,
96 	"uucp",		LOG_UUCP,
97 	"audit",	LOG_AUDIT,
98 	"cron",		LOG_CRON,
99 	"local0",	LOG_LOCAL0,
100 	"local1",	LOG_LOCAL1,
101 	"local2",	LOG_LOCAL2,
102 	"local3",	LOG_LOCAL3,
103 	"local4",	LOG_LOCAL4,
104 	"local5",	LOG_LOCAL5,
105 	"local6",	LOG_LOCAL6,
106 	"local7",	LOG_LOCAL7
107 };
108 
109 typedef struct logr_syslog_node {
110 	list_node_t	ln_node;
111 	char		ln_logline[LOGR_MAXENTRYLEN];
112 } logr_syslog_node_t;
113 
114 /*
115  * Sets the loghost of an syslog entry.
116  * Returns 0 on success, -1 on failure.
117  */
118 static int
119 logr_syslog_set_loghost(char *log_host, logr_entry_t *le)
120 {
121 	if (log_host == NULL)
122 		return (-1);
123 
124 	(void) strlcpy(le->le_hostname, log_host, MAXHOSTNAMELEN);
125 
126 	return (0);
127 }
128 
129 /*
130  * Sets the timestamp of an syslog entry.
131  * Returns 0 on success, -1 on failure.
132  */
133 static int
134 logr_syslog_set_timestamp(char *month, char *day, char *time, logr_entry_t *le)
135 {
136 	struct timeval	now;
137 	struct tm tm, cur_tm;
138 	char buf[30];
139 
140 	if ((month == NULL) || (day == NULL) || (time == NULL))
141 		return (-1);
142 
143 	bzero(&tm, sizeof (tm));
144 	(void) snprintf(buf, 30, "%s %s %s", month, day, time);
145 	if (strptime(buf, "%b" "%d" "%H:%M:%S", &tm) == NULL)
146 		return (-1);
147 
148 	/* get the current dst, year and apply it. */
149 	if (gettimeofday(&now, NULL) != 0)
150 		return (-1);
151 
152 	if (localtime_r(&now.tv_sec, &cur_tm) == NULL)
153 		return (-1);
154 
155 	tm.tm_isdst = cur_tm.tm_isdst;
156 	tm.tm_year = cur_tm.tm_year;
157 	if (tm.tm_mon > cur_tm.tm_mon)
158 		tm.tm_year = tm.tm_year - 1;
159 
160 	if ((le->le_timestamp.tv_sec = mktime(&tm)) == -1)
161 		return (-1);
162 
163 	return (0);
164 }
165 
166 /*
167  * Sets the Priority and Facility of an syslog entry.
168  * Returns 0 on success, -1 on failure.
169  */
170 static int
171 logr_syslog_set_pri_fac(char *pf_tkn, logr_entry_t *le)
172 {
173 	int pri_fac[LOGR_SYSLOG_PRI_FAC_NARG];
174 	int j, sz = 0;
175 
176 	le->le_pri = LOG_INFO;
177 
178 	if (pf_tkn == NULL)
179 		return (-1);
180 
181 	/* Defaults */
182 	pri_fac[LOGR_SYSLOG_FACILITY] = LOG_USER;
183 	pri_fac[LOGR_SYSLOG_PRIORITY] = LOG_INFO;
184 
185 	sz = sizeof (logr_syslog_fac_names) / sizeof (logr_syslog_fac_names[0]);
186 	for (j = 0; j < sz; j++) {
187 		if (strstr(pf_tkn, logr_syslog_fac_names[j].c_name) != NULL) {
188 			pri_fac[LOGR_SYSLOG_FACILITY] =
189 			    logr_syslog_fac_names[j].c_val;
190 			break;
191 		}
192 	}
193 
194 	sz = sizeof (logr_syslog_pri_names) / sizeof (logr_syslog_pri_names[0]);
195 	for (j = 0; j < sz; j++) {
196 		if (strstr(pf_tkn, logr_syslog_pri_names[j].c_name) != NULL) {
197 			pri_fac[LOGR_SYSLOG_PRIORITY] =
198 			    logr_syslog_pri_names[j].c_val;
199 			break;
200 		}
201 	}
202 
203 	le->le_pri = pri_fac[LOGR_SYSLOG_PRIORITY];
204 
205 	return (0);
206 }
207 
208 /*
209  * Sets the messages of an syslog entry.
210  */
211 static void
212 logr_syslog_set_message(char *logline, logr_entry_t *le)
213 {
214 	char *p;
215 
216 	if ((p = strchr(logline, '\n')) != NULL)
217 		*p = '\0';
218 
219 	(void) strlcpy(le->le_msg, logline, LOGR_MAXENTRYLEN);
220 }
221 
222 /*
223  * Parses the tokens from an syslog entry. A typical syslog entry is of the
224  * following standard format,
225  *
226  *  <month> <day> <time> <loghost> <source>: [ID <ID> <facility.priority>] <msg>
227  * For Example:
228  *  Oct 29 09:49:20 pbgalaxy1 smbd[104039]: [ID 702911 daemon.info] init done.
229  *
230  * This method parses the above syslog entry and populates the log_entry_t
231  * structure from the parsed tokens. It returns the following return codes.
232  *
233  * Returns,
234  *   LOGR_SYSLOG_PARSE_ENTRY_ERR:	If the syslog entry is NULL, or there is
235  *					error in parsing the entry or entry is
236  *					not in the standard format.
237  *   LOGR_SYSLOG_PARSE_NOPRI_ERR:	If the priority of the message cannot be
238  *					obtained from the parsed tokens.
239  *   LOGR_SYSLOG_PARSE_ENTRY_SUCCESS:	If the syslog entry is sucessfully
240  *					parsed.
241  */
242 static int
243 logr_syslog_parse_tokens(char *logline, logr_entry_t *le)
244 {
245 	char *argv[LOGR_SYSLOG_NARG];
246 	int i;
247 	boolean_t no_pri = B_TRUE;
248 
249 	for (i = 0; i < LOGR_SYSLOG_NARG; ++i) {
250 		if ((argv[i] = strsep(&logline, " ")) == NULL)
251 			return (LOGR_SYSLOG_PARSE_ENTRY_ERR);
252 
253 		(void) trim_whitespace(logline);
254 
255 		if ((i == LOGR_SYSLOG_ID) &&
256 		    (strcmp(argv[i], LOGR_SYSLOG_PARSE_IDTOKEN_PFX) == 0)) {
257 			i--;
258 			no_pri = B_FALSE;
259 		}
260 	}
261 
262 	if (logr_syslog_set_timestamp(argv[LOGR_SYSLOG_MONTH],
263 	    argv[LOGR_SYSLOG_DAY], argv[LOGR_SYSLOG_TIME], le) < 0)
264 		return (LOGR_SYSLOG_PARSE_ENTRY_ERR);
265 
266 	if (logr_syslog_set_loghost(argv[LOGR_SYSLOG_MACHINENAME], le) < 0)
267 		return (LOGR_SYSLOG_PARSE_ENTRY_ERR);
268 
269 	if (no_pri)
270 		return (LOGR_SYSLOG_PARSE_NOPRI_ERR);
271 
272 	if (logr_syslog_set_pri_fac(argv[LOGR_SYSLOG_PRI_FAC], le) < 0)
273 		return (LOGR_SYSLOG_PARSE_NOPRI_ERR);
274 
275 	logr_syslog_set_message(logline, le);
276 
277 	return (LOGR_SYSLOG_PARSE_ENTRY_SUCCESS);
278 }
279 
280 /*
281  * log_syslog_parse_entry
282  *
283  * Parse the given syslog entry into a log_entry_t structure.
284  *
285  * Returns,
286  *   LOGR_SYSLOG_PARSE_ENTRY_SUCCESS:	If the parsing is successful.
287  *   An error code less than zero, if parsing fails.
288  */
289 static int
290 logr_syslog_parse_entry(char *logline, logr_entry_t *le)
291 {
292 	char *dup_logline;
293 	int ret = LOGR_SYSLOG_PARSE_ENTRY_SUCCESS;
294 
295 	if (logline == NULL)
296 		return (LOGR_SYSLOG_PARSE_ENTRY_ERR);
297 
298 	dup_logline = strdup(logline);
299 	ret = logr_syslog_parse_tokens(dup_logline, le);
300 	free(dup_logline);
301 
302 	switch (ret) {
303 	case LOGR_SYSLOG_PARSE_NOPRI_ERR:
304 		le->le_pri = LOG_INFO;
305 		logr_syslog_set_message(logline, le);
306 		ret = LOGR_SYSLOG_PARSE_ENTRY_SUCCESS;
307 		break;
308 	default:
309 		break;
310 	}
311 
312 	return (ret);
313 }
314 
315 static void
316 logr_syslog_destroy_queue(list_t *queue)
317 {
318 	logr_syslog_node_t *head;
319 
320 	while ((head = list_head(queue)) != NULL) {
321 		list_remove(queue, head);
322 		free(head);
323 	}
324 	list_destroy(queue);
325 }
326 
327 static int
328 logr_syslog_construct_queue(FILE *fp, list_t *queue)
329 {
330 	logr_syslog_node_t *node, *head;
331 	int line_num = 0;
332 	char logline[LOGR_MAXENTRYLEN];
333 
334 	list_create(queue, sizeof (logr_syslog_node_t),
335 	    offsetof(logr_syslog_node_t, ln_node));
336 
337 	bzero(logline, LOGR_MAXENTRYLEN);
338 	while (fgets(logline, LOGR_MAXENTRYLEN, fp) != NULL) {
339 		/* Read the last 1024 entries in the queue */
340 		if (line_num > LOGR_NMSGMASK) {
341 			head = list_head(queue);
342 			list_remove(queue, head);
343 			free(head);
344 		}
345 
346 		if ((node = malloc(sizeof (logr_syslog_node_t))) == NULL) {
347 			logr_syslog_destroy_queue(queue);
348 			return (-1);
349 		}
350 		bzero(node->ln_logline, LOGR_MAXENTRYLEN);
351 
352 		(void) strlcpy(node->ln_logline, logline, LOGR_MAXENTRYLEN);
353 		list_insert_tail(queue, node);
354 		bzero(logline, LOGR_MAXENTRYLEN);
355 		line_num++;
356 	}
357 
358 	return (0);
359 }
360 
361 /*
362  * logr_syslog_load
363  *
364  * Loads the given log file into log_info_t structure format.
365  *
366  * Returns pointer to the allocated log structure on success.
367  * Note that the caller is responsible for freeing the allocated
368  * memory for returned log_info_t structure.
369  */
370 static int
371 logr_syslog_load(FILE *fp, logr_info_t *log)
372 {
373 	logr_entry_t *entry;
374 	int i = 0;
375 
376 	list_t queue;
377 	logr_syslog_node_t *node;
378 
379 	if (logr_syslog_construct_queue(fp, &queue) < 0)
380 		return (-1);
381 
382 	node = list_head(&queue);
383 	while (node) {
384 		entry = &log->li_entry[i];
385 
386 		if (logr_syslog_parse_entry(node->ln_logline, entry) !=
387 		    LOGR_SYSLOG_PARSE_ENTRY_SUCCESS) {
388 			node = list_next(&queue, node);
389 			continue;
390 		}
391 
392 		if (++i > LOGR_NMSGMASK)
393 			break;
394 
395 		node = list_next(&queue, node);
396 	}
397 
398 	logr_syslog_destroy_queue(&queue);
399 	log->li_idx = i;
400 
401 	return (0);
402 }
403 
404 /*
405  * logr_syslog_snapshot
406  *
407  * Return a snapshot of the given log in the buffer
408  * provided by the caller. Returns the number of entries in
409  * the log.
410  */
411 int
412 logr_syslog_snapshot(logr_info_t *loginfo)
413 {
414 	FILE *fp;
415 
416 	if (loginfo == NULL)
417 		return (-1);
418 
419 	if ((fp = fopen("/var/adm/messages", "r")) == 0)
420 		return (-1);
421 
422 	if (logr_syslog_load(fp, loginfo) < 0) {
423 		(void) fclose(fp);
424 		return (-1);
425 	}
426 	(void) fclose(fp);
427 
428 	if (loginfo->li_idx <= LOGR_NMSGMASK)
429 		return (loginfo->li_idx);
430 
431 	return (LOGR_NMSGMASK+1);
432 }
433