Home | History | Annotate | Line # | Download | only in src
      1 /* error.c -- error handler for noninteractive utilities
      2    Copyright (C) 1990-1992 Free Software Foundation, Inc.
      3 
      4    This program is free software; you can redistribute it and/or modify
      5    it under the terms of the GNU General Public License as published by
      6    the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12    GNU General Public License for more details.  */
     13 #include <sys/cdefs.h>
     14 __RCSID("$NetBSD: error.c,v 1.3 2016/05/17 14:00:09 christos Exp $");
     15 
     16 /* David MacKenzie */
     17 /* Brian Berliner added support for CVS */
     18 
     19 #include "cvs.h"
     20 #include "vasnprintf.h"
     21 
     22 /* Out of memory errors which could not be forwarded to the client are sent to
     23  * the syslog when it is available.
     24  */
     25 #ifdef HAVE_SYSLOG_H
     26 # include <syslog.h>
     27 # ifndef LOG_DAEMON   /* for ancient syslogs */
     28 #   define LOG_DAEMON 0
     29 # endif
     30 #endif /* HAVE_SYSLOG_H */
     31 
     32 
     33 
     34 /* If non-zero, error will use the CVS protocol to stdout to report error
     35  * messages.  This will only be set in the CVS server parent process.
     36  *
     37  * Most other code is run via do_cvs_command, which forks off a child
     38  * process and packages up its stderr in the protocol.
     39  */
     40 int error_use_protocol;
     41 
     42 #ifndef strerror
     43 extern char *strerror (int);
     44 #endif
     45 
     46 
     47 
     48 /* Print the program name and error message MESSAGE, which is a printf-style
     49  * format string with optional args, like:
     50  *
     51  *   PROGRAM_NAME CVS_CMD_NAME: MESSAGE: ERRNUM
     52  *
     53  * or, when STATUS is non-zero:
     54  *
     55  *   PROGRAM_NAME [CVS_CMD_NAME aborted]: MESSAGE: ERRNUM
     56  *
     57  * CVS_CMD_NAME & ERRMSG may or may not appear in the output (the `:' before
     58  * ERRMSG will disappear as well when ERRNUM is not present).  ERRMSG
     59  * represents the system dependent message returned by strerror (ERRNUM), when
     60  * ERRNUM is non-zero.
     61  *
     62  * Exit with status EXIT_FAILURE if STATUS is nonzero.
     63  *
     64  * If this function fails to get any memory it might request, it attempts to
     65  * log a "memory exhausted" message to the syslog, when syslog is available,
     66  * without any further attempts to allocate memory, before exiting.  See NOTES
     67  * below for more information on this functions memory allocation.
     68  *
     69  * INPUTS
     70  *   status	When non-zero, exit with EXIT_FAILURE rather than returning.
     71  *   errnum	When non-zero, interpret as global ERRNO for the purpose of
     72  *		generating additional error text.
     73  *   message	A printf style format string.
     74  *   ...	Variable number of args, as printf.
     75  *
     76  * GLOBALS
     77  *   program_name	The name of this executable, for the output message.
     78  *   cvs_cmd_name	Output in the error message, when it exists.
     79  *   errno		Accessed simply to save and restore it before
     80  *			returning.
     81  *
     82  * NOTES
     83  *   This function goes to fairly great lengths to avoid allocating memory so
     84  *   that it can relay out-of-memory error messages to the client.  Any error
     85  *   messages which fit in under 256 characters (after expanding MESSAGE with
     86  *   ARGS but before adding any ERRNUM text) should not require memory
     87  *   allocation before they are sent on to cvs_outerr().  Unfortunately,
     88  *   cvs_outerr() and the buffer functions it uses to send messages to the
     89  *   client still don't make this same sort of effort, so in local mode
     90  *   out-of-memory errors will probably get printed properly to stderr but if a
     91  *   memory outage happens on the server, the admin will need to consult the
     92  *   syslog to find out what went wrong.
     93  *
     94  *   I think this is largely cleaned up to the point where it does the right
     95  *   thing for the server, whether the normal server_active (child process)
     96  *   case or the error_use_protocol (parent process) case.  The one exception
     97  *   is that STATUS nonzero for error_use_protocol probably doesn't work yet;
     98  *   in that case still need to use the pending_error machinery in server.c.
     99  *
    100  *   error() does not molest errno; some code (e.g. Entries_Open) depends
    101  *   on being able to say something like:
    102  *
    103  *      error (0, 0, "foo");
    104  *      error (0, errno, "bar");
    105  *
    106  * RETURNS
    107  *   Sometimes.  ;)
    108  */
    109 void
    110 error (int status, int errnum, const char *message, ...)
    111 {
    112     va_list args;
    113     int save_errno = errno;
    114 
    115     /* Various buffers we attempt to use to generate the error message.  */
    116     char statbuf[256];
    117     char *buf;
    118     size_t length;
    119     char statbuf2[384];
    120     char *buf2;
    121     char statcmdbuf[32];
    122     char *cmdbuf;
    123     char *emptybuf = "";
    124 
    125     static const char *last_message = NULL;
    126     static int last_status;
    127     static int last_errnum;
    128 
    129     /* Initialize these to avoid a lot of special case error handling.  */
    130     buf = statbuf;
    131     buf2 = statbuf2;
    132     cmdbuf = emptybuf;
    133 
    134     /* Expand the message the user passed us.  */
    135     length = sizeof (statbuf);
    136     va_start (args, message);
    137     buf = vasnprintf (statbuf, &length, message, args);
    138     va_end (args);
    139     if (!buf) goto memerror;
    140 
    141     /* Expand the cvs commmand name to <cmd> or [<cmd> aborted].
    142      *
    143      * I could squeeze this into the buf2 printf below, but this makes the code
    144      * easier to read and I don't think error messages are printed often enough
    145      * to make this a major performance hit.  I think the memory cost is about
    146      * 40 bytes.
    147      */
    148     if (cvs_cmd_name)
    149     {
    150 	length = sizeof (statcmdbuf);
    151 	cmdbuf = asnprintf (statcmdbuf, &length, " %s%s%s",
    152 			    status ? "[" : "",
    153 			    cvs_cmd_name,
    154 			    status ? " aborted]" : "");
    155 	/* Else cmdbuf still = emptybuf.  */
    156 	if (!cmdbuf) goto memerror;
    157     }
    158     /* Else cmdbuf still = emptybuf.  */
    159 
    160     /* Now put it all together.  */
    161     length = sizeof (statbuf2);
    162     buf2 = asnprintf (statbuf2, &length, "%s%s: %s%s%s\n",
    163                       program_name, cmdbuf, buf,
    164                       errnum ? ": " : "", errnum ? strerror (errnum) : "");
    165     if (!buf2) goto memerror;
    166 
    167     /* Send the final message to the client or log it.
    168      *
    169      * Set this recursion block first since this is the only function called
    170      * here which can cause error() to be called a second time.
    171      */
    172     if (last_message) goto recursion_error;
    173     last_message = buf2;
    174     last_status = status;
    175     last_errnum = errnum;
    176     cvs_outerr (buf2, length);
    177 
    178     /* Reset our recursion lock.  This needs to be done before the call to
    179      * exit() to allow the exit handlers to make calls to error().
    180      */
    181     last_message = NULL;
    182 
    183     /* Done, if we're exiting.  */
    184     if (status)
    185 	exit (EXIT_FAILURE);
    186 
    187     /* Free anything we may have allocated.  */
    188     if (buf != statbuf) free (buf);
    189     if (buf2 != statbuf2) free (buf2);
    190     if (cmdbuf != statcmdbuf && cmdbuf != emptybuf) free (cmdbuf);
    191 
    192     /* Restore errno per our charter.  */
    193     errno = save_errno;
    194 
    195     /* Done.  */
    196     return;
    197 
    198 memerror:
    199     /* Make one last attempt to log the problem in the syslog since that
    200      * should not require new memory, then abort.
    201      *
    202      * No second attempt is made to send the information to the client - if we
    203      * got here then that already failed once and this prevents us from
    204      * entering an infinite loop.
    205      *
    206      * FIXME
    207      *   If the buffer routines can be altered in such a way that a single,
    208      *   short, statically allocated message could be sent without needing to
    209      *   allocate new memory, then it would still be safe to call cvs_outerr
    210      *   with the message here.
    211      */
    212 #if HAVE_SYSLOG_H
    213     syslog (LOG_DAEMON | LOG_ERR, "Memory exhausted.  Aborting.");
    214 #endif /* HAVE_SYSLOG_H */
    215 
    216     goto sidestep_done;
    217 
    218 recursion_error:
    219 #if HAVE_SYSLOG_H
    220     /* Syslog the problem since recursion probably means that we encountered an
    221      * error while attempting to send the last error message to the client.
    222      */
    223 
    224     syslog (LOG_DAEMON | LOG_ERR,
    225 	    "error (%d, %d) called recursively.  Original message was:",
    226 	    last_status, last_errnum);
    227     syslog (LOG_DAEMON | LOG_ERR, "%s", last_message);
    228 
    229 
    230     syslog (LOG_DAEMON | LOG_ERR,
    231             "error (%d, %d) called recursively.  Second message was:",
    232 	    status, errnum);
    233     syslog (LOG_DAEMON | LOG_ERR, "%s", buf2);
    234 
    235     syslog (LOG_DAEMON | LOG_ERR, "Aborting.");
    236 #endif /* HAVE_SYSLOG_H */
    237 
    238 sidestep_done:
    239     /* Reset our recursion lock.  This needs to be done before the call to
    240      * exit() to allow the exit handlers to make calls to error().
    241      */
    242     last_message = NULL;
    243 
    244     exit (EXIT_FAILURE);
    245 }
    246 
    247 
    248 
    249 /* Print the program name and error message MESSAGE, which is a printf-style
    250    format string with optional args to the file specified by FP.
    251    If ERRNUM is nonzero, print its corresponding system error message.
    252    Exit with status EXIT_FAILURE if STATUS is nonzero.  */
    253 /* VARARGS */
    254 void
    255 fperrmsg (FILE *fp, int status, int errnum, char *message, ...)
    256 {
    257     va_list args;
    258 
    259     fprintf (fp, "%s: ", program_name);
    260     va_start (args, message);
    261     vfprintf (fp, message, args);
    262     va_end (args);
    263     if (errnum)
    264 	fprintf (fp, ": %s", strerror (errnum));
    265     putc ('\n', fp);
    266     fflush (fp);
    267     if (status)
    268 	exit (EXIT_FAILURE);
    269 }
    270