1 static const char rcsid[] = "$Id: error.c,v 1.80 2020/08/03 17:13:04 will Exp $";
2 
3 /*
4  *	Copyright (c) 1993 by California Institute of Technology.
5  *	Written by William Deich.  Not derived from licensed software.
6 
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9 
10  */
11 
12 /********************************************************************/
13 /*
14  * If you don't have localsys.h, supply the following:
15  * #define HAVE_STDARG_H   if you have <stdarg.h> (ie ANSI C variadic arg lists)
16  * #define HAVE_SYSLOG_H   if you have <syslog.h> and syslog() function
17  */
18 
19 /*
20  * If you don't like the following "priority" and "facility" values for
21  * use with syslog(), supply the following:
22  * #define SYSLOG_PRIORITY nnn	...priority for logging if syslog() use is
23  *					enabled; default is LOG_ERR.
24  * #define SYSLOG_FACILITY nnn	...syslog() facility code if syslog() use is
25  *					enabled; default is LOG_USER.
26 
27  * We call Strerror(e) to return error messages for error code e.  This
28  * routine is expected to simply be strerror(e) + wrapper code to ensure
29  * that e is in the valid range.
30  */
31 #include "localsys.h"
32 
33 /********************************************************************/
34 
35 /* Error -- print error message, then optionally die.
36 
37  * Usage:	Error(show_perror, die, format, args... );
38  *	Print error message according to format & args.
39  *	If show_perror != 0 && errno != 0, follow error message with perror("").
40  *	(If show_perror !=0, but errno==0, follow error msg with "\n").
41  *	If die != 0, exit with exit(die).
42 
43  * There are several external variables that a calling program can modify:
44  *	error_prog: program name preceding msgs to stderr.  Default off.
45  *	error_log_prog: program name preceding msgs to logfile.  Default off.
46  *	error_stderr: controls whether messages go to stderr.  Default enabled.
47  *	error_logfile: controls whether messages go to a logfile. Default off.
48  *	error_command: controls whether msgs go to a popen'd command. Def off.
49  *	error_syslog: controls whether messages go to [r]syslog.  Default: off.
50  *	error_rlog_host: host to receive rsyslog() messages.  Def: ourselves.
51  *	error_user: Controls username used when printing messages.
52  *	error_tag: Controls use of error_{srcfile,line,nl}.  Default off.
53  *	error_fflush: fflush(stdout) before printing error message.  Default on.
54  *	error_srcfile, error_line, error_nl: error_srcfile is a file in
55  *			which an error was found, starting at line error_line
56  *			and continuing over error_nl lines.
57  *	error_counter: incremented with every call to Error().
58  * In detail:
59 
60  * If error_prog != NULL, then the message to stderr is preceded
61  *	with "<error_prog>: ".
62 
63  * If error_stderr == 0, then the message is NOT directed to stderr.
64  *	By default error_stderr == 1.
65 
66  * If error_logfile != NULL, then the message is also directed to
67  *	that file, preceded with
68  *		error_log_prog: user@hostname timestamp
69  * 	If error_log_prog is NULL, it isn't printed.  We keep separate
70  *	error_prog and error_log_prog so that the program name can be
71  *	printed on stderr (where there might otherwise be confusion to the
72  *	user about which program it is) but optionally not printed in the
73  *	logfile, which is typically unique to the program, so that the
74  *	program name is redundant.
75 
76  * If error_user != NULL, then the username used in messages is error_user,
77  *	instead of the default name found by looking at the user's password
78  *	entry.
79 
80  * If error_command != NULL and *error_command != '\0', then error_command
81  *	is popen'd and the message is piped in.  If the popen fails, Error()
82  *	is silent about the problem.  NOTE: the command is executed separately
83  *	for each call to this routine.
84 
85  * If HAVE_SYSLOG_H is defined, and the caller sets error_syslog != 0,
86  *	then the message is passed to [r]syslog(), at priority error_priority,
87  *	using facility error_facility.  We compile with rsyslog, but if the
88  *	remote host argument is NULL at the time we open the log, we use the
89  *	syslog routines instead.
90  *	The fmt string and the printf output must be less
91  *	than MAXPRINT characters each. This is because syslog() accepts a
92  *	printf-style variadic argument list, but it doesn't have a va_list
93  *	version.  Therefore we print into a string and pass that onto syslog().
94  *	As a side effect, you can't use syslog-specific "%m" in the fmt.
95  *	If the error_prog string is non-null, then just before the first
96  *	call to syslog, openlog is called with an ident string = error_prog.
97  *	Note that this is done just once: you can't change error_prog
98  *	between messages.
99 
100  * If error_tag is !0, or if the _first_ two characters of the output
101  *	format are "%t" or "$$", then the output is preceded with a message like
102  *	the following:
103  *  (a) error_srcfile == NULL:
104  *	""				# error_line <= 0
105  *	"line %d: "			# error_line > 0,  error_nl <= 1
106  *	"lines %d..%d: "		# error_line > 0,  error_nl > 1
107  *
108  *  (b) error_srcfile == "-":
109  *	"in <stdin>: "			# error_line <= 0
110  *	"line %d in <stdin>: "		# error_line > 0,  error_nl <= 1
111  *	"lines %d..%d in <stdin>: "	# error_line > 0,  error_nl > 1
112 
113  *  (c) error_srcfile == anothername:
114  *	"in file `%s': "		# error_line <= 0
115  *	"line %d in file `%s': "	# error_line > 0,  error_nl <= 1
116  *	"lines %d..%d in file `%s': "	# error_line > 0, error_nl > 1
117 
118  * Notes:
119  *	1. If error_prog and this line information is printed, then
120  *	the program name is _not_ suffixed with ":" -- that way, the
121  *	output looks something like:
122  *		progxyz (lines 232..255 in file `.......'): errmsg
123  *	2. In the event that the first two characters are "%t" or "$$",
124  *	they enable tagline printing but are not printed.
125 
126  * Return code is -1, so you can print error messages and return an error
127  *	code with   return Error(...);
128  */
129 
130 
131 FILE *error_logfile = NULL;
132 char *error_prog = NULL;
133 char *error_log_prog = NULL;
134 char *error_command = NULL;
135 char *error_user = NULL;
136 
137 int error_counter = 0;
138 
139 int error_stderr = 1;
140 
141 int error_syslog = 0;
142 char *error_rlog_host = NULL;
143 
144 int error_line = -1;
145 int error_nl = -1;
146 char *error_srcfile = NULL;
147 int error_tag = 0;
148 int error_fflush = 1;
149 
150 #ifdef HAVE_SYSLOG_H
151 
152 /* Default error priority */
153 #ifndef SYSLOG_PRIORITY
154 #define SYSLOG_PRIORITY LOG_ERR
155 #endif
156 
157 /* Default error facility */
158 #ifndef SYSLOG_FACILITY
159 #ifdef LOG_USER
160 #define SYSLOG_FACILITY LOG_USER
161 #else
162 #define SYSLOG_FACILITY 0
163 #endif
164 #endif
165 
166 void ropenlog P__(( char *ident, int logopt, int facility, char *host ));
167 void rsyslog P__(( unsigned int level, char *fmt, ... ));
168 
169 int error_priority = SYSLOG_PRIORITY;
170 int error_facility = SYSLOG_FACILITY;
171 int openlog_done = 0;
172 
173 static int using_rsyslog = 0;
174 
175 void
OpenLog(prog,opt,fac)176 OpenLog(prog, opt, fac)
177 char *prog;
178 int opt;
179 int fac;
180 {
181     if (error_rlog_host != NULL) {
182 	ropenlog(prog, opt, fac, error_rlog_host);
183 	using_rsyslog = 1;
184     } else {
185 	openlog(prog, opt, fac);
186 	using_rsyslog = 0;
187     }
188 }
189 
190 void
SysLog(pri,buf)191 SysLog(pri, buf)
192 int pri;
193 char *buf;
194 {
195     if (using_rsyslog) {
196 	rsyslog(pri, "%s", buf);
197     } else {
198 	syslog(pri, "%s", buf);
199     }
200 }
201 
202 #endif
203 
204 static int uid = -1;
205 static char user[128] = "";
206 static char hostname[1024] = "";
207 
208 extern char *Strerror();
209 
210 #define MAXPRINT 1300
211 
212 #define StrLCat(s1, s2, maxlen)  strncat(s1, s2, ((maxlen) - strlen(s1) - 1))
213 
214 char *taglines();
215 
216 #ifdef HAVE_STDARG_H
217 /* VARARGS3 */
218 int
Error(int show_perror,int die,char * fmt,...)219 Error(
220     int show_perror,	/* If errno != 0, follow msg with perror("") */
221     int die,		/* If !0, exit with exit(die) */
222     char *fmt,		/* Print rest of args with fprintf(stderr, fmt, ...) */
223     ... )
224 {
225     va_list ap;
226     int error;
227     FILE *error_cmd = NULL;
228     char *tag;
229     int pctt;
230 
231     if (error_fflush)
232 	fflush(stdout);
233 
234     error = errno;
235 
236     error_counter++;
237 
238     /* Figure out line tagging */
239     pctt = (strncmp(fmt, "%t", 2) == 0 || strncmp(fmt, "$$", 2) == 0);
240     tag = ( pctt || error_tag ) ? taglines(3) : NULL;
241     if (pctt)
242 	fmt += 2;
243 
244     /* Program name */
245     if (error_stderr && error_prog)
246 	(void) fprintf(stderr, "%s%s ", error_prog, (tag && *tag) ? "" : ":");
247 
248     if (error_command && *error_command)
249 	error_cmd = popen(error_command, "w");
250 
251     if (error_log_prog) {
252 	if (error_logfile)
253 	    (void) fprintf(error_logfile, "%s%s ", error_log_prog,
254 						(tag && *tag) ? "" : ":");
255 	if (error_cmd)
256 	    (void) fprintf(error_cmd, "%s%s ", error_log_prog,
257 						(tag && *tag) ? "" : ":");
258     }
259 
260     if (error_logfile || error_syslog || error_command) {
261 	if (getuid() != uid || *user == '\0') {
262 	    struct passwd *pw;
263 	    int e = errno;
264 	    pw = getpwuid((uid=getuid()));
265 	    if (pw) (void) strcpy(user, pw->pw_name);
266 	    errno = e;
267 	}
268     }
269 
270     if (error_logfile || error_cmd) {
271 	/* user@hostname & timestamp */
272 	char *s;
273 	time_t tptr;
274 	if (*hostname == '\0')
275 	    (void) gethostname(hostname, sizeof(hostname));
276 	(void) time(&tptr);
277 	s = ctime(&tptr);
278 	s[strlen(s) - 1] = '\0';
279 	if (error_logfile)
280 	    (void) fprintf(error_logfile, "%s@%s %s\t",
281 			    error_user ? error_user : user,
282 			    hostname, s);
283 	if (error_cmd)
284 	    (void) fprintf(error_cmd, "%s@%s %s\t",
285 			    error_user ? error_user : user,
286 			    hostname, s);
287     }
288 
289     if (error_stderr) {
290 	if (tag)
291 	    (void) fputs(tag, stderr);
292 	/* User's msg */
293 	va_start(ap, fmt);
294 	(void) vfprintf(stderr, fmt, ap);
295 	va_end(ap);
296     }
297 
298     if (error_logfile) {
299 	if (tag)
300 	    (void) fputs(tag, error_logfile);
301 	/* User's msg */
302 	va_start(ap, fmt);
303 	(void) vfprintf(error_logfile, fmt, ap);
304 	va_end(ap);
305     }
306 
307     if (error_cmd) {
308 	if (tag)
309 	    (void) fputs(tag, error_cmd);
310 	/* User's msg */
311 	va_start(ap, fmt);
312 	(void) vfprintf(error_cmd, fmt, ap);
313 	va_end(ap);
314     }
315 
316     if (show_perror) {
317 	if (error) {
318 	    errno = error;
319 	    if (error_stderr)
320 		perror("");
321 	    if (error_logfile) {
322 		(void) fprintf(error_logfile, "%s\n", Strerror(error));
323 	    }
324 	    if (error_cmd) {
325 		(void) fprintf(error_cmd, "%s\n", Strerror(error));
326 	    }
327 	} else {
328 	    if (error_stderr)
329 		(void) fputc('\n', stderr);
330 	    if (error_logfile)
331 		(void) fputc('\n', error_logfile);
332 	    if (error_cmd)
333 		(void) fputc('\n', error_cmd);
334 	}
335     }
336 
337 #ifdef HAVE_SYSLOG_H
338     if (error_syslog) {
339 	char newfmt[MAXPRINT], buf[MAXPRINT];
340 	if (!openlog_done) {
341 	    OpenLog(error_prog ? error_prog : "", 0, error_facility);
342 	    openlog_done = 1;
343 	}
344 	sprintf(newfmt, "(%s) ", error_user ? error_user : user);
345 	StrLCat(newfmt, fmt, sizeof(newfmt));
346 	if (tag)
347 	    StrLCat(newfmt, tag, sizeof(newfmt));
348 	va_start(ap, fmt);
349 	(void) vsprintf(buf, newfmt, ap);
350 	va_end(ap);
351 	SysLog(error_priority, buf);
352     }
353 #endif
354 
355     if (die)
356 	(void) exit(die);
357 
358     if (error_cmd)
359 	pclose(error_cmd);
360 
361     return -1;
362 
363 }
364 #else
365 
366 /* VARARGS3 */
367 int
Error(va_alist)368 Error( va_alist )
369 va_dcl
370 {
371     va_list ap;
372     int die, show_perror;
373     char *fmt, *orig_fmt;
374     int error;
375     char *tag;
376     int pctt;
377     FILE *error_cmd = NULL;
378 
379     if (error_fflush)
380 	fflush(stdout);
381 
382     error = errno;
383 
384     /* Figure out line tagging */
385     va_start(ap);
386     show_perror = va_arg(ap, int);
387     die = va_arg(ap, int);
388     fmt = va_arg(ap, char *);
389     va_end(ap);
390     pctt = (strncmp(fmt, "%t", 2) == 0 || strncmp(fmt, "$$", 2) == 0);
391     tag = ( pctt || error_tag ) ? taglines(3) : NULL;
392     if (pctt)
393 	fmt += 2;
394 
395     /* Program name */
396     if (error_stderr && error_prog)
397 	(void) fprintf(stderr, "%s%s ", error_prog, (tag && *tag) ? "" : ":");
398 
399     if (error_command && *error_command)
400 	error_cmd = popen(error_command, "w");
401 
402     if (error_log_prog) {
403 	if (error_logfile)
404 	    (void) fprintf(error_logfile, "%s%s ", error_log_prog,
405 						(tag && *tag) ? "" : ":");
406 	if (error_cmd)
407 	    (void) fprintf(error_cmd, "%s%s ", error_log_prog,
408 						(tag && *tag) ? "" : ":");
409     }
410 
411     if (error_logfile || error_syslog || error_command) {
412 	if (getuid() != uid || *user == '\0') {
413 	    struct passwd *pw;
414 	    int e = errno;
415 	    pw = getpwuid((uid=getuid()));
416 	    if (pw) (void) strcpy(user, pw->pw_name);
417 	    errno = e;
418 	}
419     }
420 
421     if (error_logfile || error_cmd) {
422 	/* user@hostname & timestamp */
423 	char *s;
424 	time_t tptr;
425 	if (*hostname == '\0')
426 	    (void) gethostname(hostname, sizeof(hostname));
427 	(void) time(&tptr);
428 	s = ctime(&tptr);
429 	s[strlen(s) - 1] = '\0';
430 	if (error_logfile)
431 	    (void) fprintf(error_logfile, "%s@%s %s\t",
432 				error_user ? error_user : user,
433 				hostname, s);
434 	if (error_cmd)
435 	    (void) fprintf(error_cmd, "%s@%s %s\t",
436 				error_user ? error_user : user,
437 				hostname, s);
438     }
439 
440     if (error_stderr) {
441 	if (tag)
442 	    (void) fputs(tag, stderr);
443 	/* User's msg */
444 	va_start(ap);
445 	show_perror = va_arg(ap, int);
446 	die = va_arg(ap, int);
447 	orig_fmt = va_arg(ap, char *);
448 	(void) vfprintf(stderr, fmt, ap);
449 	va_end(ap);
450     }
451 
452     if (error_logfile) {
453 	if (tag)
454 	    (void) fputs(tag, error_logfile);
455 	/* User's msg */
456 	va_start(ap);
457 	show_perror = va_arg(ap, int);
458 	die = va_arg(ap, int);
459 	orig_fmt = va_arg(ap, char *);
460 	(void) vfprintf(error_logfile, fmt, ap);
461 	va_end(ap);
462     }
463 
464     if (error_cmd) {
465 	if (tag)
466 	    (void) fputs(tag, error_cmd);
467 	/* User's msg */
468 	va_start(ap);
469 	show_perror = va_arg(ap, int);
470 	die = va_arg(ap, int);
471 	orig_fmt = va_arg(ap, char *);
472 	(void) vfprintf(error_cmd, fmt, ap);
473 	va_end(ap);
474     }
475 
476     /* Figure out if we do show_perror */
477     va_start(ap);
478     show_perror = va_arg(ap, int);
479     die = va_arg(ap, int);
480     va_end(ap);
481 
482     if (show_perror) {
483 	if (error) {
484 	    errno = error;
485 	    if (error_stderr)
486 		perror("");
487 	    if (error_logfile)
488 		(void) fprintf(error_logfile, "%s\n", Strerror(error));
489 	    if (error_cmd)
490 		(void) fprintf(error_cmd, "%s\n", Strerror(error));
491 	} else {
492 	    if (error_stderr)
493 		(void) fputc('\n', stderr);
494 	    if (error_logfile)
495 		(void) fputc('\n', error_logfile);
496 	    if (error_cmd)
497 		(void) fputc('\n', error_cmd);
498 	}
499     }
500 
501 #ifdef HAVE_SYSLOG_H
502     if (error_syslog) {
503 	char newfmt[MAXPRINT], buf[MAXPRINT];
504 	va_start(ap);
505 	show_perror = va_arg(ap, int);
506 	die = va_arg(ap, int);
507 	orig_fmt = va_arg(ap, char *);
508 	if (!openlog_done) {
509 	    OpenLog(error_prog ? error_prog : "", 0, error_facility);
510 	    openlog_done = 1;
511 	}
512 	sprintf(newfmt, "(%s) ", error_user ? error_user : user);
513 	StrLCat(newfmt, fmt, sizeof(newfmt));
514 	if (tag)
515 	    StrLCat(newfmt, tag, sizeof(newfmt));
516 	(void) vsprintf(buf, newfmt, ap);
517 	va_end(ap);
518 	SysLog(error_priority, buf);
519     }
520 #endif
521 
522     if (die)
523 	(void) exit(die);
524 
525     if (error_cmd)
526 	pclose(error_cmd);
527 
528     return -1;
529 
530 }
531 #endif
532 
533 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
534 /* For tagging error text with a prefixing lines indicator. */
535 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
536 char *
taglines(decorations)537 taglines(decorations)
538 int decorations;	/* 1 = enclose in parens; 2 = add a ": "; 3 = both */
539 {
540     static char buf[MAXPRINT];
541     int putparens = decorations & 01;
542     int putcolon = decorations & 02;
543 
544     if (error_srcfile == NULL) {
545 	if (error_line <= 0)
546 	    return "";
547 	else if (error_nl <= 1)
548 	    (void) sprintf(buf, "%sline %d%s%s",
549 				putparens ? "(" : "",
550 				error_line,
551 				putparens ? ")" : "",
552 				putcolon ? ": " : "");
553 	else
554 	    (void) sprintf(buf, "%slines %d..%d%s%s",
555 				putparens ? "(" : "",
556 				error_line, error_line + error_nl - 1,
557 				putparens ? ")" : "",
558 				putcolon ? ": " : "");
559     } else if (strcmp(error_srcfile, "-") == 0) {
560 	if (error_line <= 0)
561 	    (void) sprintf(buf, "%sin <stdin>%s%s",
562 				putparens ? "(" : "",
563 				putparens ? ")" : "",
564 				putcolon ? ": " : "");
565 	else if (error_nl <= 1)
566 	    (void) sprintf(buf, "%sline %d in <stdin>%s%s",
567 				putparens ? "(" : "",
568 				error_line,
569 				putparens ? ")" : "",
570 				putcolon ? ": " : "");
571 	else
572 	    (void) sprintf(buf, "%slines %d..%d in <stdin>%s%s",
573 				putparens ? "(" : "",
574 				error_line, error_line + error_nl - 1,
575 				putparens ? ")" : "",
576 				putcolon ? ": " : "");
577     } else {
578 	if (error_line <= 0)
579 	    (void) sprintf(buf, "%sin file `%s'%s%s",
580 				putparens ? "(" : "",
581 				error_srcfile,
582 				putparens ? ")" : "",
583 				putcolon ? ": " : "");
584 	else if (error_nl <= 1)
585 	    (void) sprintf(buf, "%sline %d in file `%s'%s%s",
586 				putparens ? "(" : "",
587 				error_line, error_srcfile,
588 				putparens ? ")" : "",
589 				putcolon ? ": " : "");
590 	else
591 	    (void) sprintf(buf, "%slines %d..%d in file `%s'%s%s",
592 				putparens ? "(" : "",
593 				error_line, error_line + error_nl - 1,
594 				error_srcfile,
595 				putparens ? ")" : "",
596 				putcolon ? ": " : "");
597     }
598     return buf;
599 }
600