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 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 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