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