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 31 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. 34 * 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. 37 */ 38 int error_use_protocol; 39 40 #ifndef strerror 41 extern char *strerror (int); 42 #endif 43 44 45 46 /* Print the program name and error message MESSAGE, which is a printf-style 47 * format string with optional args, like: 48 * 49 * PROGRAM_NAME CVS_CMD_NAME: MESSAGE: ERRNUM 50 * 51 * or, when STATUS is non-zero: 52 * 53 * PROGRAM_NAME [CVS_CMD_NAME aborted]: MESSAGE: ERRNUM 54 * 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. 59 * 60 * Exit with status EXIT_FAILURE if STATUS is nonzero. 61 * 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. 66 * 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. 73 * 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. 79 * 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. 91 * 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. 97 * 98 * error() does not molest errno; some code (e.g. Entries_Open) depends 99 * on being able to say something like: 100 * 101 * error (0, 0, "foo"); 102 * error (0, errno, "bar"); 103 * 104 * RETURNS 105 * Sometimes. ;) 106 */ 107 void 108 error (int status, int errnum, const char *message, ...) 109 { 110 va_list args; 111 int save_errno = errno; 112 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 = ""; 122 123 static const char *last_message = NULL; 124 static int last_status; 125 static int last_errnum; 126 127 /* Initialize these to avoid a lot of special case error handling. */ 128 buf = statbuf; 129 buf2 = statbuf2; 130 cmdbuf = emptybuf; 131 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; 138 139 /* Expand the cvs commmand name to <cmd> or [<cmd> aborted]. 140 * 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. 145 */ 146 if (cvs_cmd_name) 147 { 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; 155 } 156 /* Else cmdbuf still = emptybuf. */ 157 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; 164 165 /* Send the final message to the client or log it. 166 * 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. 169 */ 170 if (last_message) goto recursion_error; 171 last_message = buf2; 172 last_status = status; 173 last_errnum = errnum; 174 cvs_outerr (buf2, length); 175 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(). 178 */ 179 last_message = NULL; 180 181 /* Done, if we're exiting. */ 182 if (status) 183 exit (EXIT_FAILURE); 184 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); 189 190 /* Restore errno per our charter. */ 191 errno = save_errno; 192 193 /* Done. */ 194 return; 195 196 memerror: 197 /* Make one last attempt to log the problem in the syslog since that 198 * should not require new memory, then abort. 199 * 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. 203 * 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. 209 */ 210 #if HAVE_SYSLOG_H 211 syslog (LOG_DAEMON | LOG_EMERG, "Memory exhausted. Aborting."); 212 #endif /* HAVE_SYSLOG_H */ 213 214 goto sidestep_done; 215 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. 220 */ 221 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); 226 227 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); 232 233 syslog (LOG_DAEMON | LOG_EMERG, "Aborting."); 234 #endif /* HAVE_SYSLOG_H */ 235 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(). 239 */ 240 last_message = NULL; 241 242 exit (EXIT_FAILURE); 243 } 244 245 246 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, ...) 254 { 255 va_list args; 256 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); 267 } 268