Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
dc45ba8c | 2 | * Copyright (c) 1983 Eric P. Allman |
eb0bafab KB |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
bee79b64 | 5 | * |
417f7a11 | 6 | * %sccs.include.redist.c% |
bee79b64 | 7 | */ |
b2a81223 DF |
8 | |
9 | #ifndef lint | |
486825cf | 10 | static char sccsid[] = "@(#)err.c 8.29 (Berkeley) %G%"; |
bee79b64 | 11 | #endif /* not lint */ |
b2a81223 | 12 | |
96faada8 | 13 | # include "sendmail.h" |
2e3062fe | 14 | # include <errno.h> |
9fd1518e | 15 | # include <netdb.h> |
41be8f1c | 16 | # include <pwd.h> |
b3cbe40f EA |
17 | |
18 | /* | |
d916f0ca | 19 | ** SYSERR -- Print error message. |
b3cbe40f EA |
20 | ** |
21 | ** Prints an error message via printf to the diagnostic | |
22 | ** output. If LOG is defined, it logs it also. | |
23 | ** | |
e0539260 EA |
24 | ** If the first character of the syserr message is `!' it will |
25 | ** log this as an ALERT message and exit immediately. This can | |
26 | ** leave queue files in an indeterminate state, so it should not | |
27 | ** be used lightly. | |
28 | ** | |
b3cbe40f EA |
29 | ** Parameters: |
30 | ** f -- the format string | |
31 | ** a, b, c, d, e -- parameters | |
32 | ** | |
33 | ** Returns: | |
29871fef | 34 | ** none |
633a2e02 | 35 | ** Through TopFrame if QuickAbort is set. |
b3cbe40f EA |
36 | ** |
37 | ** Side Effects: | |
d916f0ca EA |
38 | ** increments Errors. |
39 | ** sets ExitStat. | |
b3cbe40f EA |
40 | */ |
41 | ||
044ab67a | 42 | char MsgBuf[BUFSIZ*2]; /* text of most recent message */ |
29871fef | 43 | |
35f1f39e EA |
44 | static void fmtmsg(); |
45 | ||
efe7f723 | 46 | #if NAMED_BIND && !defined(NO_DATA) |
35f1f39e EA |
47 | # define NO_DATA NO_ADDRESS |
48 | #endif | |
0df908a9 | 49 | |
36335805 | 50 | void |
b3cbe40f | 51 | /*VARARGS1*/ |
6e99f903 | 52 | #ifdef __STDC__ |
c51d6c4c | 53 | syserr(const char *fmt, ...) |
6e99f903 EA |
54 | #else |
55 | syserr(fmt, va_alist) | |
c51d6c4c | 56 | const char *fmt; |
6e99f903 EA |
57 | va_dcl |
58 | #endif | |
b3cbe40f | 59 | { |
177b9da3 EA |
60 | register char *p; |
61 | int olderrno = errno; | |
e0539260 | 62 | bool panic; |
41be8f1c EA |
63 | #ifdef LOG |
64 | char *uname; | |
65 | struct passwd *pw; | |
66 | char ubuf[80]; | |
67 | #endif | |
5229f34d | 68 | VA_LOCAL_DECL |
b3cbe40f | 69 | |
e0539260 EA |
70 | panic = *fmt == '!'; |
71 | if (panic) | |
72 | fmt++; | |
73 | ||
d5dba2cd | 74 | /* format and output the error message */ |
177b9da3 | 75 | if (olderrno == 0) |
b6edea3d | 76 | p = "554"; |
314d2f52 | 77 | else |
b6edea3d | 78 | p = "451"; |
5229f34d EA |
79 | VA_START(fmt); |
80 | fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); | |
81 | VA_END; | |
80482eb5 | 82 | puterrmsg(MsgBuf); |
f4e91ea7 | 83 | |
b3cbe40f EA |
84 | /* determine exit status if not already set */ |
85 | if (ExitStat == EX_OK) | |
86 | { | |
177b9da3 | 87 | if (olderrno == 0) |
b3cbe40f EA |
88 | ExitStat = EX_SOFTWARE; |
89 | else | |
21ec078e | 90 | ExitStat = EX_OSERR; |
ed3d2b02 EA |
91 | if (tTd(54, 1)) |
92 | printf("syserr: ExitStat = %d\n", ExitStat); | |
b3cbe40f EA |
93 | } |
94 | ||
95 | # ifdef LOG | |
41be8f1c EA |
96 | pw = getpwuid(getuid()); |
97 | if (pw != NULL) | |
98 | uname = pw->pw_name; | |
99 | else | |
100 | { | |
101 | uname = ubuf; | |
102 | sprintf(ubuf, "UID%d", getuid()); | |
103 | } | |
104 | ||
9678c96d | 105 | if (LogLevel > 0) |
41be8f1c | 106 | syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s", |
3312f93c | 107 | CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, |
41be8f1c | 108 | uname, &MsgBuf[4]); |
f3d8f6d6 | 109 | # endif /* LOG */ |
35f14050 | 110 | if (olderrno == EMFILE) |
deff97fd | 111 | { |
35f14050 | 112 | printopenfds(TRUE); |
deff97fd EA |
113 | mci_dump_all(TRUE); |
114 | } | |
e0539260 | 115 | if (panic) |
b4f81c5d EA |
116 | { |
117 | #ifdef XLA | |
118 | xla_all_end(); | |
119 | #endif | |
e0539260 | 120 | exit(EX_OSERR); |
b4f81c5d | 121 | } |
b3cbe40f | 122 | errno = 0; |
633a2e02 EA |
123 | if (QuickAbort) |
124 | longjmp(TopFrame, 2); | |
b3cbe40f EA |
125 | } |
126 | \f/* | |
127 | ** USRERR -- Signal user error. | |
128 | ** | |
129 | ** This is much like syserr except it is for user errors. | |
130 | ** | |
131 | ** Parameters: | |
132 | ** fmt, a, b, c, d -- printf strings | |
133 | ** | |
134 | ** Returns: | |
29871fef | 135 | ** none |
633a2e02 | 136 | ** Through TopFrame if QuickAbort is set. |
b3cbe40f EA |
137 | ** |
138 | ** Side Effects: | |
d916f0ca | 139 | ** increments Errors. |
b3cbe40f EA |
140 | */ |
141 | ||
142 | /*VARARGS1*/ | |
94bc039a | 143 | void |
36335805 | 144 | #ifdef __STDC__ |
c51d6c4c | 145 | usrerr(const char *fmt, ...) |
6e99f903 EA |
146 | #else |
147 | usrerr(fmt, va_alist) | |
c51d6c4c | 148 | const char *fmt; |
6e99f903 EA |
149 | va_dcl |
150 | #endif | |
b3cbe40f | 151 | { |
5229f34d | 152 | VA_LOCAL_DECL |
b3cbe40f EA |
153 | |
154 | if (SuprErrs) | |
29871fef | 155 | return; |
f4e91ea7 | 156 | |
5229f34d | 157 | VA_START(fmt); |
75593899 | 158 | fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); |
5229f34d | 159 | VA_END; |
80482eb5 | 160 | puterrmsg(MsgBuf); |
21f25858 | 161 | |
f61c3c40 | 162 | # ifdef LOG |
68f7099c | 163 | if (LogLevel > 3 && LogUsrErrs) |
f61c3c40 EA |
164 | syslog(LOG_NOTICE, "%s: %s", |
165 | CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, | |
166 | &MsgBuf[4]); | |
f3d8f6d6 | 167 | # endif /* LOG */ |
f61c3c40 | 168 | |
633a2e02 EA |
169 | if (QuickAbort) |
170 | longjmp(TopFrame, 1); | |
f4e91ea7 EA |
171 | } |
172 | \f/* | |
173 | ** MESSAGE -- print message (not necessarily an error) | |
174 | ** | |
175 | ** Parameters: | |
62869b50 EA |
176 | ** msg -- the message (printf fmt) -- it can begin with |
177 | ** an SMTP reply code. If not, 050 is assumed. | |
f4e91ea7 EA |
178 | ** a, b, c, d, e -- printf arguments |
179 | ** | |
180 | ** Returns: | |
181 | ** none | |
182 | ** | |
183 | ** Side Effects: | |
184 | ** none. | |
185 | */ | |
186 | ||
29871fef | 187 | /*VARARGS2*/ |
94bc039a | 188 | void |
319b1ec0 | 189 | #ifdef __STDC__ |
c51d6c4c | 190 | message(const char *msg, ...) |
6e99f903 | 191 | #else |
b6edea3d | 192 | message(msg, va_alist) |
c51d6c4c | 193 | const char *msg; |
6e99f903 EA |
194 | va_dcl |
195 | #endif | |
f4e91ea7 | 196 | { |
5229f34d EA |
197 | VA_LOCAL_DECL |
198 | ||
49086753 | 199 | errno = 0; |
5229f34d | 200 | VA_START(msg); |
b6edea3d | 201 | fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); |
5229f34d | 202 | VA_END; |
8e5c6745 | 203 | putoutmsg(MsgBuf, FALSE); |
015bb651 EA |
204 | } |
205 | \f/* | |
21f25858 EA |
206 | ** NMESSAGE -- print message (not necessarily an error) |
207 | ** | |
208 | ** Just like "message" except it never puts the to... tag on. | |
209 | ** | |
210 | ** Parameters: | |
211 | ** num -- the default ARPANET error number (in ascii) | |
212 | ** msg -- the message (printf fmt) -- if it begins | |
2e3062fe | 213 | ** with three digits, this number overrides num. |
21f25858 EA |
214 | ** a, b, c, d, e -- printf arguments |
215 | ** | |
216 | ** Returns: | |
217 | ** none | |
218 | ** | |
219 | ** Side Effects: | |
220 | ** none. | |
221 | */ | |
222 | ||
223 | /*VARARGS2*/ | |
94bc039a | 224 | void |
319b1ec0 | 225 | #ifdef __STDC__ |
c51d6c4c | 226 | nmessage(const char *msg, ...) |
6e99f903 | 227 | #else |
b6edea3d | 228 | nmessage(msg, va_alist) |
c51d6c4c | 229 | const char *msg; |
6e99f903 EA |
230 | va_dcl |
231 | #endif | |
21f25858 | 232 | { |
5229f34d EA |
233 | VA_LOCAL_DECL |
234 | ||
21f25858 | 235 | errno = 0; |
5229f34d | 236 | VA_START(msg); |
b6edea3d | 237 | fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); |
5229f34d | 238 | VA_END; |
8e5c6745 | 239 | putoutmsg(MsgBuf, FALSE); |
21f25858 EA |
240 | } |
241 | \f/* | |
8e5c6745 | 242 | ** PUTOUTMSG -- output error message to transcript and channel |
015bb651 EA |
243 | ** |
244 | ** Parameters: | |
245 | ** msg -- message to output (in SMTP format). | |
6a766659 EA |
246 | ** holdmsg -- if TRUE, don't output a copy of the message to |
247 | ** our output channel. | |
015bb651 EA |
248 | ** |
249 | ** Returns: | |
250 | ** none. | |
251 | ** | |
252 | ** Side Effects: | |
253 | ** Outputs msg to the transcript. | |
254 | ** If appropriate, outputs it to the channel. | |
255 | ** Deletes SMTP reply code number as appropriate. | |
256 | */ | |
49086753 | 257 | |
8e5c6745 | 258 | putoutmsg(msg, holdmsg) |
015bb651 | 259 | char *msg; |
6a766659 | 260 | bool holdmsg; |
015bb651 | 261 | { |
9acae8cd EA |
262 | /* display for debugging */ |
263 | if (tTd(54, 8)) | |
264 | printf("--- %s%s\n", msg, holdmsg ? " (held)" : ""); | |
265 | ||
d7dbe85b | 266 | /* output to transcript if serious */ |
5e2168d0 | 267 | if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) |
d7dbe85b | 268 | fprintf(CurEnv->e_xfp, "%s\n", msg); |
49086753 EA |
269 | |
270 | /* output to channel if appropriate */ | |
d907841d EA |
271 | if (holdmsg || (!Verbose && msg[0] == '0')) |
272 | return; | |
273 | ||
5e2168d0 EA |
274 | /* map warnings to something SMTP can handle */ |
275 | if (msg[0] == '6') | |
276 | msg[0] = '5'; | |
277 | ||
d907841d | 278 | (void) fflush(stdout); |
33cbaada EA |
279 | |
280 | /* if DisConnected, OutChannel now points to the transcript */ | |
281 | if (!DisConnected && | |
282 | (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) | |
d907841d EA |
283 | fprintf(OutChannel, "%s\r\n", msg); |
284 | else | |
285 | fprintf(OutChannel, "%s\n", &msg[4]); | |
8e5c6745 EA |
286 | if (TrafficLogFile != NULL) |
287 | fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), | |
198d9e9c | 288 | (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); |
d907841d EA |
289 | if (msg[3] == ' ') |
290 | (void) fflush(OutChannel); | |
33cbaada | 291 | if (!ferror(OutChannel) || DisConnected) |
d907841d EA |
292 | return; |
293 | ||
b3454503 EA |
294 | /* |
295 | ** Error on output -- if reporting lost channel, just ignore it. | |
296 | ** Also, ignore errors from QUIT response (221 message) -- some | |
297 | ** rude servers don't read result. | |
298 | */ | |
299 | ||
300 | if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) | |
d907841d EA |
301 | return; |
302 | ||
303 | /* can't call syserr, 'cause we are using MsgBuf */ | |
304 | HoldErrs = TRUE; | |
a9145676 | 305 | #ifdef LOG |
d907841d EA |
306 | if (LogLevel > 0) |
307 | syslog(LOG_CRIT, | |
67d76dc4 | 308 | "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", |
d907841d | 309 | CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, |
548e1aec | 310 | CurHostName == NULL ? "NO-HOST" : CurHostName, |
67d76dc4 | 311 | msg, errstring(errno)); |
a9145676 | 312 | #endif |
80482eb5 EA |
313 | } |
314 | \f/* | |
8e5c6745 | 315 | ** PUTERRMSG -- like putoutmsg, but does special processing for error messages |
80482eb5 EA |
316 | ** |
317 | ** Parameters: | |
318 | ** msg -- the message to output. | |
319 | ** | |
320 | ** Returns: | |
321 | ** none. | |
322 | ** | |
323 | ** Side Effects: | |
324 | ** Sets the fatal error bit in the envelope as appropriate. | |
325 | */ | |
21f25858 | 326 | |
80482eb5 EA |
327 | puterrmsg(msg) |
328 | char *msg; | |
329 | { | |
5e2168d0 EA |
330 | char msgcode = msg[0]; |
331 | ||
80482eb5 | 332 | /* output the message as usual */ |
8e5c6745 | 333 | putoutmsg(msg, HoldErrs); |
21f25858 | 334 | |
80482eb5 | 335 | /* signal the error */ |
e67e6cf6 | 336 | Errors++; |
2aad48ef EA |
337 | if (msgcode == '6') |
338 | { | |
339 | /* notify the postmaster */ | |
340 | CurEnv->e_flags |= EF_PM_NOTIFY; | |
341 | } | |
b5c513a7 EA |
342 | else if ((msgcode == '4' || msgcode == '5') && |
343 | bitset(EF_GLOBALERRS, CurEnv->e_flags)) | |
e67e6cf6 EA |
344 | { |
345 | /* mark long-term fatal errors */ | |
346 | CurEnv->e_flags |= EF_FATALERRS; | |
347 | } | |
49086753 EA |
348 | } |
349 | \f/* | |
350 | ** FMTMSG -- format a message into buffer. | |
351 | ** | |
352 | ** Parameters: | |
353 | ** eb -- error buffer to get result. | |
354 | ** to -- the recipient tag for this message. | |
355 | ** num -- arpanet error number. | |
177b9da3 | 356 | ** en -- the error number to display. |
49086753 EA |
357 | ** fmt -- format of string. |
358 | ** a, b, c, d, e -- arguments. | |
359 | ** | |
360 | ** Returns: | |
361 | ** none. | |
362 | ** | |
363 | ** Side Effects: | |
364 | ** none. | |
365 | */ | |
366 | ||
0df908a9 | 367 | static void |
5229f34d | 368 | fmtmsg(eb, to, num, eno, fmt, ap) |
49086753 EA |
369 | register char *eb; |
370 | char *to; | |
371 | char *num; | |
e1e01376 | 372 | int eno; |
49086753 | 373 | char *fmt; |
5229f34d | 374 | va_list ap; |
49086753 EA |
375 | { |
376 | char del; | |
c281eee3 | 377 | char *meb; |
49086753 EA |
378 | |
379 | /* output the reply code */ | |
2e3062fe | 380 | if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) |
f4e91ea7 | 381 | { |
49086753 EA |
382 | num = fmt; |
383 | fmt += 4; | |
f4e91ea7 | 384 | } |
49086753 EA |
385 | if (num[3] == '-') |
386 | del = '-'; | |
387 | else | |
388 | del = ' '; | |
389 | (void) sprintf(eb, "%3.3s%c", num, del); | |
390 | eb += 4; | |
f4e91ea7 | 391 | |
7338e3d4 EA |
392 | /* output the file name and line number */ |
393 | if (FileName != NULL) | |
394 | { | |
395 | (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); | |
396 | eb += strlen(eb); | |
397 | } | |
398 | ||
49086753 EA |
399 | /* output the "to" person */ |
400 | if (to != NULL && to[0] != '\0') | |
34d37b7d | 401 | { |
bab2ae18 | 402 | (void) sprintf(eb, "%s... ", shortenstring(to, 203)); |
6593367a EA |
403 | while (*eb != '\0') |
404 | *eb++ &= 0177; | |
49086753 EA |
405 | } |
406 | ||
c281eee3 EA |
407 | meb = eb; |
408 | ||
49086753 | 409 | /* output the message */ |
5229f34d | 410 | (void) vsprintf(eb, fmt, ap); |
6593367a EA |
411 | while (*eb != '\0') |
412 | *eb++ &= 0177; | |
34d37b7d | 413 | |
49086753 | 414 | /* output the error code, if any */ |
e1e01376 | 415 | if (eno != 0) |
49086753 | 416 | { |
e1e01376 | 417 | (void) sprintf(eb, ": %s", errstring(eno)); |
49086753 | 418 | eb += strlen(eb); |
34d37b7d | 419 | } |
c281eee3 | 420 | |
f6a83527 EA |
421 | if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4')) |
422 | { | |
423 | if (CurEnv->e_message != NULL) | |
424 | free(CurEnv->e_message); | |
c281eee3 | 425 | CurEnv->e_message = newstr(meb); |
f6a83527 | 426 | } |
b3cbe40f | 427 | } |
e41c463e EA |
428 | \f/* |
429 | ** ERRSTRING -- return string description of error code | |
430 | ** | |
431 | ** Parameters: | |
c583011b | 432 | ** errnum -- the error number to translate |
e41c463e EA |
433 | ** |
434 | ** Returns: | |
c583011b | 435 | ** A string description of errnum. |
e41c463e EA |
436 | ** |
437 | ** Side Effects: | |
438 | ** none. | |
439 | */ | |
440 | ||
713c523f | 441 | const char * |
c583011b EA |
442 | errstring(errnum) |
443 | int errnum; | |
e41c463e | 444 | { |
7b3d4602 | 445 | char *dnsmsg; |
f43b04ce | 446 | static char buf[MAXLINE]; |
d4f6a25e EA |
447 | # ifndef ERRLIST_PREDEFINED |
448 | extern char *sys_errlist[]; | |
449 | extern int sys_nerr; | |
450 | # endif | |
2e3062fe EA |
451 | # ifdef SMTP |
452 | extern char *SmtpPhase; | |
f3d8f6d6 | 453 | # endif /* SMTP */ |
2e3062fe | 454 | |
2e3062fe EA |
455 | /* |
456 | ** Handle special network error codes. | |
457 | ** | |
458 | ** These are 4.2/4.3bsd specific; they should be in daemon.c. | |
459 | */ | |
460 | ||
7b3d4602 | 461 | dnsmsg = NULL; |
c583011b | 462 | switch (errnum) |
2e3062fe | 463 | { |
7763037f | 464 | # if defined(DAEMON) && defined(ETIMEDOUT) |
2e3062fe EA |
465 | case ETIMEDOUT: |
466 | case ECONNRESET: | |
c583011b | 467 | (void) strcpy(buf, sys_errlist[errnum]); |
2e3062fe EA |
468 | if (SmtpPhase != NULL) |
469 | { | |
470 | (void) strcat(buf, " during "); | |
471 | (void) strcat(buf, SmtpPhase); | |
472 | } | |
57c97d4a | 473 | if (CurHostName != NULL) |
2e3062fe EA |
474 | { |
475 | (void) strcat(buf, " with "); | |
57c97d4a | 476 | (void) strcat(buf, CurHostName); |
2e3062fe EA |
477 | } |
478 | return (buf); | |
479 | ||
480 | case EHOSTDOWN: | |
57c97d4a | 481 | if (CurHostName == NULL) |
2e3062fe | 482 | break; |
57c97d4a | 483 | (void) sprintf(buf, "Host %s is down", CurHostName); |
2e3062fe EA |
484 | return (buf); |
485 | ||
486 | case ECONNREFUSED: | |
57c97d4a | 487 | if (CurHostName == NULL) |
2e3062fe | 488 | break; |
57c97d4a | 489 | (void) sprintf(buf, "Connection refused by %s", CurHostName); |
2e3062fe | 490 | return (buf); |
7763037f | 491 | # endif |
9fd1518e | 492 | |
92f2b65e EA |
493 | case EOPENTIMEOUT: |
494 | return "Timeout on file open"; | |
495 | ||
efe7f723 | 496 | # if NAMED_BIND |
92f2b65e | 497 | case HOST_NOT_FOUND + E_DNSBASE: |
7b3d4602 EA |
498 | dnsmsg = "host not found"; |
499 | break; | |
49a282c6 | 500 | |
92f2b65e | 501 | case TRY_AGAIN + E_DNSBASE: |
7b3d4602 EA |
502 | dnsmsg = "host name lookup failure"; |
503 | break; | |
49a282c6 | 504 | |
92f2b65e | 505 | case NO_RECOVERY + E_DNSBASE: |
7b3d4602 EA |
506 | dnsmsg = "non-recoverable error"; |
507 | break; | |
49a282c6 | 508 | |
92f2b65e | 509 | case NO_DATA + E_DNSBASE: |
7b3d4602 EA |
510 | dnsmsg = "no data known"; |
511 | break; | |
6feb509e | 512 | # endif |
7763037f EA |
513 | |
514 | case EPERM: | |
515 | /* SunOS gives "Not owner" -- this is the POSIX message */ | |
516 | return "Operation not permitted"; | |
2e3062fe | 517 | } |
e41c463e | 518 | |
7b3d4602 EA |
519 | if (dnsmsg != NULL) |
520 | { | |
521 | (void) strcpy(buf, "Name server: "); | |
522 | if (CurHostName != NULL) | |
523 | { | |
524 | (void) strcat(buf, CurHostName); | |
525 | (void) strcat(buf, ": "); | |
526 | } | |
527 | (void) strcat(buf, dnsmsg); | |
528 | return buf; | |
529 | } | |
530 | ||
c583011b EA |
531 | if (errnum > 0 && errnum < sys_nerr) |
532 | return (sys_errlist[errnum]); | |
e41c463e | 533 | |
c583011b | 534 | (void) sprintf(buf, "Error %d", errnum); |
e41c463e EA |
535 | return (buf); |
536 | } |