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
error(int status,int errnum,const char * message,...)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
fperrmsg(FILE * fp,int status,int errnum,char * message,...)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