/* * tardy - a tar post-processor * Copyright (C) 1991-1995, 1998, 1999, 2001-2004 Peter Miller; * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * MANIFEST: functions to report errors */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void error_get_id(int *uid, int *gid) { *uid = geteuid(); *gid = getegid(); } static error_id_ty errid = error_get_id; void error_set_id_func(error_id_ty f) { if (f) errid = f; else errid = error_get_id; } /* * NAME * wrap - wrap s string over lines * * SYNOPSIS * void wrap(char *); * * DESCRIPTION * The wrap function is used to print error messages onto stderr * wrapping ling lines. * * CAVEATS * Line length is assumed to be 80 characters. */ static void wrap(char *s) { static char escapes[] = "\rr\nn\ff\bb\tt"; int page_width; char tmp[200]; int first_line; char *tp; if (fflush(stdout) || ferror(stdout)) nfatal("(stdout)"); /* don't use last column, many terminals are dumb */ page_width = 79; const char *progname = progname_get(); first_line = 1; while (*s) { char *ep; int ocol; /* * Work out how many characters fit on the line. */ if (first_line) ocol = strlen(progname) + 2; else ocol = 8; for (ep = s; *ep; ++ep) { int cw; int c; c = (unsigned char)*ep; if (isprint(c)) cw = 1 + (c == '\\'); else cw = (strchr(escapes, c) ? 2 : 4); if (ocol + cw > page_width) break; ocol += cw; } /* * see if there is a better place to break the line */ if (*ep && *ep != ' ') { char *mp; for (mp = ep; mp > s; --mp) { if (strchr(" /", mp[-1])) { ep = mp; break; } } } /* * ignore trailing blanks */ while (ep > s && ep[-1] == ' ') ep--; /* * print the line */ if (first_line) snprintf(tmp, sizeof(tmp), "%s: ", progname); else strcpy(tmp, "\t"); tp = tmp + strlen(tmp); while (s < ep) { int c = (unsigned char)*s++; if (isprint(c)) { if (c == '\\') *tp++ = '\\'; *tp++ = c; } else { char *esc = strchr(escapes, c); if (esc) { *tp++ = '\\'; *tp++ = esc[1]; } else { snprintf(tp, tmp + sizeof(tmp) - tp, "\\%3.3o", c); tp += strlen(tp); } } } *tp++ = '\n'; *tp = 0; fputs(tmp, stderr); if (ferror(stderr)) break; /* * skip leading spaces for subsequent lines */ while (*s == ' ') s++; first_line = 0; } if (fflush(stderr) || ferror(stderr)) nfatal("(stderr)"); } static void double_jeopardy(void) { char buffer[200]; snprintf ( buffer, sizeof(buffer), "while attempting to construct an error message: %s (fatal)", strerror(errno) ); wrap(buffer); quit(1); } static char * copy_string(const char *s) { char *cp; errno = 0; cp = (char *)malloc(strlen(s) + 1); if (!cp) { if (!errno) errno = ENOMEM; double_jeopardy(); } strcpy(cp, s); return cp; } static char * id(void) { int uid; int gid; errid(&uid, &gid); struct passwd *pw = getpwuid(uid); char uidn[20]; if (pw) snprintf(uidn, sizeof(uidn), "user \"%.8s\"", pw->pw_name); else snprintf(uidn, sizeof(uidn), "uid %d", uid); struct group *gr = getgrgid(gid); char gidn[20]; if (gr) snprintf(gidn, sizeof(gidn), "group \"%.8s\"", gr->gr_name); else snprintf(gidn, sizeof(gidn), "gid %d", gid); char buffer[100]; snprintf(buffer, sizeof(buffer), " [%s, %s]", uidn, gidn); return copy_string(buffer); } /* * NAME * error - place a message on the error stream * * SYNOPSIS * void error(char *fmt, ...); * * DESCRIPTION * Error places a message on the error output stream. * The first argument is a printf-like format string, * optionally followed by other arguments. * The message will be prefixed by the program name and a colon, * and will be terminated with a newline, automatically. * * CAVEAT * Things like "error(filename)" blow up if the filename * contains a '%' character. */ /*VARARGS1*/ void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); char *buffer = vmprintf_errok(fmt, ap); va_end(ap); if (!buffer) double_jeopardy(); wrap(buffer); } /* * NAME * nerror - place a system fault message on the error stream * * SYNOPSIS * void nerror(char *fmt, ...); * * DESCRIPTION * Nerror places a message on the error output stream. * The first argument is a printf-like format string, * optionally followed by other arguments. * The message will be prefixed by the program name and a colon, * and will be terminated with a text description of the error * indicated by the 'errno' global variable, automatically. * * CAVEAT * Things like "nerror(filename)" blow up if the filename * contains a '%' character. */ /*VARARGS1*/ void nerror(const char *fmt, ...) { int n = errno; va_list ap; va_start(ap, fmt); char *s1 = vmprintf_errok(fmt, ap); va_end(ap); if (!s1) double_jeopardy(); s1 = copy_string(s1); char *s2; if (n == EPERM || n == EACCES) s2 = id(); else s2 = copy_string(""); error("%s: %s%s", s1, strerror(n), s2); free(s1); free(s2); } /* * NAME * nfatal - place a system fault message on the error stream and exit * * SYNOPSIS * void nfatal(char *fmt, ...); * * DESCRIPTION * Nfatal places a message on the error output stream and exits. * The first argument is a printf-like format string, * optionally followed by other arguments. * The message will be prefixed by the program name and a colon, * and will be terminated with a text description of the error * indicated by the 'errno' global variable, automatically. * * CAVEAT * Things like "nfatal(filename)" blow up if the filename * contains a '%' character. * * This function does NOT return. */ /*VARARGS1*/ void nfatal(const char *fmt, ...) { int n = errno; va_list ap; va_start(ap, fmt); char *s1 = vmprintf_errok(fmt, ap); va_end(ap); if (!s1) double_jeopardy(); s1 = copy_string(s1); char *s2; if (n == EPERM || n == EACCES) s2 = id(); else s2 = ""; fatal("%s: %s%s", s1, strerror(n), s2); } /* * NAME * fatal - place a message on the error stream and exit * * SYNOPSIS * void fatal(char *fmt, ...); * * DESCRIPTION * Fatal places a message on the error output stream and exits. * The first argument is a printf-like format string, * optionally followed by other arguments. * The message will be prefixed by the program name and a colon, * and will be terminated with a newline, automatically. * * CAVEAT * Things like "error(filename)" blow up if the filename * contains a '%' character. * * This function does NOT return. */ /*VARARGS1*/ void fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); char *buffer = vmprintf_errok(fmt, ap); va_end(ap); if (!buffer) double_jeopardy(); wrap(buffer); quit(1); } /* * NAME * signal_name - find it * * SYNOPSIS * char *signal_name(int n); * * DESCRIPTION * The signal_name function is used to find the name of a signal from its * number. * * RETURNS * char *: pointer to the signal name. * * CAVEAT * The signal name may not be written on. Subsequent calls may alter the * area pointed to. */ const char * signal_name(int n) { static char buffer[16]; switch (n) { #ifdef SIGHUP case SIGHUP: return "hang up [SIGHUP]"; #endif /* SIGHUP */ #ifdef SIGINT case SIGINT: return "user interrupt [SIGINT]"; #endif /* SIGINT */ #ifdef SIGQUIT case SIGQUIT: return "user quit [SIGQUIT]"; #endif /* SIGQUIT */ #ifdef SIGILL case SIGILL: return "illegal instruction [SIGILL]"; #endif /* SIGILL */ #ifdef SIGTRAP case SIGTRAP: return "trace trap [SIGTRAP]"; #endif /* SIGTRAP */ #ifdef SIGIOT case SIGIOT: return "abort [SIGIOT]"; #endif /* SIGIOT */ #ifdef SIGEMT case SIGEMT: return "EMT instruction [SIGEMT]"; #endif /* SIGEMT */ #ifdef SIGFPE case SIGFPE: return "floating point exception [SIGFPE]"; #endif /* SIGFPE */ #ifdef SIGKILL case SIGKILL: return "kill [SIGKILL]"; #endif /* SIGKILL */ #ifdef SIGBUS case SIGBUS: return "bus error [SIGBUS]"; #endif /* SIGBUS */ #ifdef SIGSEGV case SIGSEGV: return "segmentation violation [SIGSEGV]"; #endif /* SIGSEGV */ #ifdef SIGSYS case SIGSYS: return "bad argument to system call [SIGSYS]"; #endif /* SIGSYS */ #ifdef SIGPIPE case SIGPIPE: return "write on a pipe with no one to read it [SIGPIPE]"; #endif /* SIGPIPE */ #ifdef SIGALRM case SIGALRM: return "alarm clock [SIGALRM]"; #endif /* SIGALRM */ #ifdef SIGTERM case SIGTERM: return "software termination [SIGTERM]"; #endif /* SIGTERM */ #ifdef SIGUSR1 case SIGUSR1: return "user defined signal one [SIGUSR1]"; #endif /* SIGUSR1 */ #ifdef SIGUSR2 case SIGUSR2: return "user defined signal two [SIGUSR2]"; #endif /* SIGUSR2 */ #ifdef SIGCLD case SIGCLD: return "death of child [SIGCLD]"; #endif /* SIGCLD */ #ifdef SIGPWR case SIGPWR: return "power failure [SIGPWR]"; #endif /* SIGPWR */ default: snprintf(buffer, sizeof(buffer), "signal %d", n); return buffer; } } static quit_ty quit_list[10]; static int quit_list_len; void quit_register(quit_ty func) { assert(quit_list_len < SIZEOF(quit_list)); assert(func); quit_list[quit_list_len++] = func; } void quit(int n) { int j; static int quitting; if (quitting > 4) { fprintf ( stderr, "%s: incorrectly handled error while quitting (bug)\n", progname_get() ); exit(1); } ++quitting; for (j = quit_list_len - 1; j >= 0; --j) quit_list[j](n); exit(n); }