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