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