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