xref: /titanic_52/usr/src/lib/efcode/engine/log.c (revision d583b39bfb4e2571d3e41097c5c357ffe353ad45)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Daemon log message.  This can direct log messages to either stdout,
31  * an error log file or syslog (or any combination).
32  */
33 
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <syslog.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 
42 #include <fcode/private.h>
43 #include <fcode/log.h>
44 
45 #define	LOG_LINESIZE		256
46 #define	LOG_EMIT_BUFSIZE	LOG_LINESIZE
47 
48 static FILE *error_log_fp = NULL;
49 static int syslog_opened = 0;
50 static int error_log_flags;
51 static int syslog_log_flags;
52 static int do_emit_flag = -1;
53 static int daemon_log_flag;
54 static int min_syslog_level = LOG_ERR;
55 
56 static int log_to_stdout(int);
57 static int log_to_error_log(int);
58 static int log_to_syslog(int);
59 static int msg_level_to_syslog(int);
60 
61 /*
62  * Complicated by not wanting to do any emit processing if no one's actually
63  * going to see it.
64  */
65 void
66 log_emit(char c)
67 {
68 	static char emit_buf[LOG_EMIT_BUFSIZE];
69 	static char *emit_p = emit_buf;
70 	static int lastnl = 1;
71 
72 	/*
73 	 * No one has set the do_emit_flag, go ahead and figure it out.
74 	 */
75 	if (do_emit_flag < 0) {
76 		do_emit_flag = (log_to_stdout(MSG_EMIT) |
77 		    log_to_error_log(MSG_EMIT) | log_to_syslog(MSG_EMIT));
78 	}
79 
80 	if (!do_emit_flag)
81 		return;
82 
83 	/*
84 	 * Check for buffer overflow.
85 	 */
86 	if (emit_p >= &emit_buf[LOG_EMIT_BUFSIZE - 1]) {
87 		*emit_p = '\0';
88 		log_message(MSG_EMIT, "emit: %s\n", emit_buf);
89 		emit_p = emit_buf;
90 		lastnl = 1;
91 	}
92 
93 	/*
94 	 * Fcode emit's may output both CR/LF, we go ahead and eat multiple
95 	 * ones in succession.
96 	 */
97 	if (c == '\n' || c == '\r') {
98 		if (!lastnl) {
99 			*emit_p = '\0';
100 			log_message(MSG_EMIT, "emit: %s\n", emit_buf);
101 			emit_p = emit_buf;
102 		}
103 		lastnl = 1;
104 	} else {
105 		lastnl = 0;
106 		*emit_p++ = c;
107 	}
108 }
109 
110 /*
111  * If stdout is a tty and this is MSG_EMIT, we should have alredy output it.
112  * If running as daemon, output to stdout is a waste of time.
113  */
114 static int
115 log_to_stdout(int msg_level)
116 {
117 	if (isatty(fileno(stdin)) && (msg_level & MSG_EMIT) != 0)
118 		return (0);
119 	return (daemon_log_flag == 0);
120 }
121 
122 /*
123  * Can't turn off FATAL or ERROR messages to error log file.
124  */
125 static int
126 log_to_error_log(int msg_level)
127 {
128 	if (!error_log_fp)
129 		return (0);
130 	if (msg_level & (MSG_FATAL|MSG_ERROR))
131 		return (1);
132 	return (msg_level & error_log_flags);
133 }
134 
135 /*
136  * Can't turn off FATAL or ERROR messages to syslog.
137  */
138 static int
139 log_to_syslog(int msg_level)
140 {
141 	if (!syslog_opened)
142 		return (0);
143 	if (msg_level & (MSG_FATAL|MSG_ERROR))
144 		return (1);
145 	return (msg_level & syslog_log_flags);
146 }
147 
148 /*
149  * Turn internal MSG level to syslog msg level.  Don't return a msg level
150  * lower priority than min_syslog_level.
151  */
152 static int
153 msg_level_to_syslog(int msg_level)
154 {
155 	if (min_syslog_level <= LOG_ERR)
156 		return (min_syslog_level);
157 	if (msg_level & (MSG_FATAL|MSG_ERROR))
158 		return (LOG_ERR);
159 	if (min_syslog_level <= LOG_WARNING)
160 		return (min_syslog_level);
161 	if (msg_level & MSG_WARN)
162 		return (LOG_WARNING);
163 	if (min_syslog_level <= LOG_NOTICE)
164 		return (min_syslog_level);
165 	if (msg_level & MSG_NOTE)
166 		return (LOG_NOTICE);
167 	if (min_syslog_level <= LOG_INFO)
168 		return (min_syslog_level);
169 	if (msg_level & MSG_INFO)
170 		return (LOG_INFO);
171 	return (min(min_syslog_level, LOG_DEBUG));
172 }
173 
174 /*
175  * Log a message to the appropriate places.
176  */
177 void
178 log_message(int msg_level, char *fmt, ...)
179 {
180 	va_list ap;
181 	char msg[LOG_LINESIZE], *p;
182 	static char log_msg[LOG_LINESIZE];
183 
184 	va_start(ap, fmt);
185 
186 	vsprintf(msg, fmt, ap);
187 
188 	if (log_to_stdout(msg_level)) {
189 		printf(msg);
190 		fflush(stdout);
191 	}
192 	if (log_to_error_log(msg_level)) {
193 		fprintf(error_log_fp, msg);
194 		fflush(error_log_fp);
195 	}
196 	if (log_to_syslog(msg_level)) {
197 		if (strlen(log_msg) + strlen(msg) > LOG_LINESIZE - 1) {
198 			syslog(msg_level_to_syslog(msg_level), log_msg);
199 			log_msg[0] = '\0';
200 		}
201 		strcat(log_msg, msg);
202 		if ((p = strchr(log_msg, '\n')) != NULL) {
203 			*p = '\0';
204 			syslog(msg_level_to_syslog(msg_level), log_msg);
205 			log_msg[0] = '\0';
206 		}
207 	}
208 }
209 
210 /*
211  * Output debug message
212  */
213 void
214 debug_msg(int debug_level, char *fmt, ...)
215 {
216 	va_list ap;
217 	char msg[LOG_LINESIZE];
218 
219 	if ((debug_level & get_interpreter_debug_level()) == 0)
220 		return;
221 
222 	va_start(ap, fmt);
223 
224 	vsprintf(msg, fmt, ap);
225 
226 	log_message(MSG_DEBUG, msg);
227 }
228 
229 /*
230  * Log a perror message to the appropriate places.
231  */
232 void
233 log_perror(int msg_level, char *fmt, ...)
234 {
235 	va_list ap;
236 	char msg[LOG_LINESIZE], tmp[LOG_LINESIZE];
237 
238 	va_start(ap, fmt);
239 
240 	vsprintf(msg, fmt, ap);
241 	sprintf(tmp, "%s: %s\n", msg, strerror(errno));
242 	log_message(msg_level, tmp);
243 }
244 
245 void
246 set_min_syslog_level(int level)
247 {
248 	min_syslog_level = level;
249 }
250 
251 char *error_log_name;
252 
253 void
254 open_error_log(char *fname, int errflags)
255 {
256 	if ((error_log_fp = fopen(fname, "a")) == NULL) {
257 		log_perror(MSG_FATAL, fname);
258 		exit(1);
259 	}
260 	error_log_name = STRDUP(fname);
261 	error_log_flags = errflags;
262 	do_emit_flag = (log_to_stdout(MSG_EMIT) | log_to_error_log(MSG_EMIT) |
263 	    log_to_syslog(MSG_EMIT));
264 }
265 
266 void
267 open_syslog_log(char *logname, int logflags)
268 {
269 	openlog(logname, LOG_PID, LOG_DAEMON);
270 	syslog_log_flags = logflags;
271 	do_emit_flag = (log_to_stdout(MSG_EMIT) | log_to_error_log(MSG_EMIT) |
272 	    log_to_syslog(MSG_EMIT));
273 	syslog_opened = 1;
274 }
275 
276 /*
277  * Turn on/off syslog LOG_DAEMON flag to syslog messages.  Also controls
278  * outputting to stdout, which is a waste of time when we're running as a
279  * daemon.
280  */
281 void
282 set_daemon_log_flag(int flag)
283 {
284 	if (flag)
285 		daemon_log_flag = LOG_DAEMON;
286 	else
287 		daemon_log_flag = 0;
288 }
289 
290 int
291 parse_msg_flags(char *flags)
292 {
293 	int msgflags = 0;
294 	char c;
295 
296 	while ((c = *flags++) != '\0') {
297 		switch (c) {
298 		case 'f': msgflags |= MSG_FATAL; break;
299 		case 'e': msgflags |= MSG_ERROR; break;
300 		case 'w': msgflags |= MSG_WARN;  break;
301 		case 'i': msgflags |= MSG_INFO;  break;
302 		case 'd': msgflags |= MSG_DEBUG; break;
303 		case 'D': msgflags |= MSG_FC_DEBUG; break;
304 
305 		default:
306 			log_message(MSG_ERROR, "Invalid msglvl flag: %c\n", c);
307 			break;
308 		}
309 	}
310 	return (msgflags);
311 }
312 
313 #define	MAXERRBUF	256
314 char error_buffer[MAXERRBUF];
315 
316 static void
317 dot_error_buffer(fcode_env_t *env)
318 {
319 	log_message(MSG_INFO, "%s\n", error_buffer);
320 }
321 
322 static void
323 set_error_log(fcode_env_t *env)
324 {
325 	char *fname;
326 	FILE *fp;
327 
328 	parse_word(env);
329 	fname = pop_a_string(env, NULL);
330 	if (fname != NULL) {
331 		if ((fp = fopen(fname, "a")) == NULL) {
332 			log_perror(MSG_ERROR, "Can't open '%s'\n", fname);
333 			return;
334 		}
335 		if (error_log_fp)
336 			fclose(error_log_fp);
337 		if (error_log_name)
338 			FREE(error_log_name);
339 		error_log_fp = fp;
340 		error_log_name = STRDUP(fname);
341 		error_log_flags = MSG_FATAL|MSG_ERROR|MSG_WARN|MSG_INFO|
342 		    MSG_DEBUG|MSG_FC_DEBUG;
343 	} else if (error_log_name)
344 		log_message(MSG_INFO, "%s\n", error_log_name);
345 	else
346 		log_message(MSG_INFO, "NULL\n");
347 }
348 
349 #pragma init(_init)
350 
351 static void
352 _init(void)
353 {
354 	fcode_env_t *env = initial_env;
355 
356 	ASSERT(env);
357 	NOTICE;
358 
359 	FORTH(0,	".error-buffer",	dot_error_buffer);
360 	FORTH(0,	"set-error-log",	set_error_log);
361 }
362