xref: /csrg-svn/usr.sbin/sendmail/src/err.c (revision 68693)
122705Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362525Sbostic  * Copyright (c) 1988, 1993
462525Sbostic  *	The Regents of the University of California.  All rights reserved.
533729Sbostic  *
642826Sbostic  * %sccs.include.redist.c%
733729Sbostic  */
822705Sdist 
922705Sdist #ifndef lint
10*68693Seric static char sccsid[] = "@(#)err.c	8.32 (Berkeley) 03/31/95";
1133729Sbostic #endif /* not lint */
1222705Sdist 
133311Seric # include "sendmail.h"
1424943Seric # include <errno.h>
15295Seric 
16295Seric /*
171514Seric **  SYSERR -- Print error message.
18295Seric **
19295Seric **	Prints an error message via printf to the diagnostic
20295Seric **	output.  If LOG is defined, it logs it also.
21295Seric **
2258690Seric **	If the first character of the syserr message is `!' it will
2358690Seric **	log this as an ALERT message and exit immediately.  This can
2458690Seric **	leave queue files in an indeterminate state, so it should not
2558690Seric **	be used lightly.
2658690Seric **
27295Seric **	Parameters:
2867818Seric **		fmt -- the format string.  If it does not begin with
2967818Seric **			a three-digit SMTP reply code, either 554 or
3067818Seric **			451 is assumed depending on whether errno
3167818Seric **			is set.
3267818Seric **		(others) -- parameters
33295Seric **
34295Seric **	Returns:
354084Seric **		none
367762Seric **		Through TopFrame if QuickAbort is set.
37295Seric **
38295Seric **	Side Effects:
391514Seric **		increments Errors.
401514Seric **		sets ExitStat.
41295Seric */
42295Seric 
4368692Seric char	MsgBuf[BUFSIZ*2];		/* text of most recent message */
4468692Seric char	HeldMessageBuf[sizeof MsgBuf];	/* for held messages */
454084Seric 
4663969Seric static void	fmtmsg();
4746928Sbostic 
4866334Seric #if NAMED_BIND && !defined(NO_DATA)
4963969Seric # define NO_DATA	NO_ADDRESS
5063969Seric #endif
5163969Seric 
5258824Seric void
53295Seric /*VARARGS1*/
5457642Seric #ifdef __STDC__
5560094Seric syserr(const char *fmt, ...)
5657642Seric #else
5757642Seric syserr(fmt, va_alist)
5860094Seric 	const char *fmt;
5957642Seric 	va_dcl
6057642Seric #endif
61295Seric {
6216901Seric 	register char *p;
6316901Seric 	int olderrno = errno;
6458690Seric 	bool panic;
6566006Seric #ifdef LOG
6666006Seric 	char *uname;
6766006Seric 	struct passwd *pw;
6866006Seric 	char ubuf[80];
6966006Seric #endif
7056852Seric 	VA_LOCAL_DECL
71295Seric 
7258690Seric 	panic = *fmt == '!';
7358690Seric 	if (panic)
7458690Seric 		fmt++;
7558690Seric 
767525Seric 	/* format and output the error message */
7716901Seric 	if (olderrno == 0)
7858151Seric 		p = "554";
797957Seric 	else
8058151Seric 		p = "451";
8156852Seric 	VA_START(fmt);
8256852Seric 	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
8356852Seric 	VA_END;
849389Seric 	puterrmsg(MsgBuf);
854063Seric 
8667818Seric 	/* save this message for mailq printing */
8767818Seric 	if (!panic)
8867818Seric 	{
8967818Seric 		if (CurEnv->e_message != NULL)
9067818Seric 			free(CurEnv->e_message);
9167818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
9267818Seric 	}
9367818Seric 
94295Seric 	/* determine exit status if not already set */
95295Seric 	if (ExitStat == EX_OK)
96295Seric 	{
9716901Seric 		if (olderrno == 0)
98295Seric 			ExitStat = EX_SOFTWARE;
99295Seric 		else
1001598Seric 			ExitStat = EX_OSERR;
10166323Seric 		if (tTd(54, 1))
10266323Seric 			printf("syserr: ExitStat = %d\n", ExitStat);
103295Seric 	}
104295Seric 
105295Seric # ifdef LOG
106*68693Seric 	pw = sm_getpwuid(getuid());
10766006Seric 	if (pw != NULL)
10866006Seric 		uname = pw->pw_name;
10966006Seric 	else
11066006Seric 	{
11166006Seric 		uname = ubuf;
11266006Seric 		sprintf(ubuf, "UID%d", getuid());
11366006Seric 	}
11466006Seric 
1157674Seric 	if (LogLevel > 0)
11666006Seric 		syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s",
11725277Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
11866006Seric 			uname, &MsgBuf[4]);
11956795Seric # endif /* LOG */
12064725Seric 	if (olderrno == EMFILE)
12164731Seric 	{
12264725Seric 		printopenfds(TRUE);
12364731Seric 		mci_dump_all(TRUE);
12464731Seric 	}
12558690Seric 	if (panic)
12659156Seric 	{
12759156Seric #ifdef XLA
12859156Seric 		xla_all_end();
12959156Seric #endif
13058690Seric 		exit(EX_OSERR);
13159156Seric 	}
132295Seric 	errno = 0;
1337762Seric 	if (QuickAbort)
1347762Seric 		longjmp(TopFrame, 2);
135295Seric }
136295Seric /*
137295Seric **  USRERR -- Signal user error.
138295Seric **
139295Seric **	This is much like syserr except it is for user errors.
140295Seric **
141295Seric **	Parameters:
14267818Seric **		fmt -- the format string.  If it does not begin with
14367818Seric **			a three-digit SMTP reply code, 501 is assumed.
14467818Seric **		(others) -- printf strings
145295Seric **
146295Seric **	Returns:
1474084Seric **		none
1487762Seric **		Through TopFrame if QuickAbort is set.
149295Seric **
150295Seric **	Side Effects:
1511514Seric **		increments Errors.
152295Seric */
153295Seric 
154295Seric /*VARARGS1*/
15558824Seric void
15657642Seric #ifdef __STDC__
15760094Seric usrerr(const char *fmt, ...)
15857642Seric #else
15957642Seric usrerr(fmt, va_alist)
16060094Seric 	const char *fmt;
16157642Seric 	va_dcl
16257642Seric #endif
163295Seric {
16456852Seric 	VA_LOCAL_DECL
165295Seric 
166295Seric 	if (SuprErrs)
1674084Seric 		return;
168295Seric 
16956852Seric 	VA_START(fmt);
17058524Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
17156852Seric 	VA_END;
1729389Seric 	puterrmsg(MsgBuf);
1738239Seric 
17467818Seric 	/* save this message for mailq printing */
17567818Seric 	if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
17667818Seric 	{
17767818Seric 		if (CurEnv->e_message != NULL)
17867818Seric 			free(CurEnv->e_message);
17967818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
18067818Seric 	}
18167818Seric 
18251951Seric # ifdef LOG
18358020Seric 	if (LogLevel > 3 && LogUsrErrs)
18451951Seric 		syslog(LOG_NOTICE, "%s: %s",
18551951Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
18651951Seric 			&MsgBuf[4]);
18756795Seric # endif /* LOG */
18851951Seric 
1897762Seric 	if (QuickAbort)
1907762Seric 		longjmp(TopFrame, 1);
1914063Seric }
1924063Seric /*
1934063Seric **  MESSAGE -- print message (not necessarily an error)
1944063Seric **
1954063Seric **	Parameters:
19659581Seric **		msg -- the message (printf fmt) -- it can begin with
19759581Seric **			an SMTP reply code.  If not, 050 is assumed.
19867818Seric **		(others) -- printf arguments
1994063Seric **
2004063Seric **	Returns:
2014063Seric **		none
2024063Seric **
2034063Seric **	Side Effects:
2044063Seric **		none.
2054063Seric */
2064063Seric 
2074084Seric /*VARARGS2*/
20858826Seric void
20957642Seric #ifdef __STDC__
21060094Seric message(const char *msg, ...)
21157642Seric #else
21258151Seric message(msg, va_alist)
21360094Seric 	const char *msg;
21457642Seric 	va_dcl
21557642Seric #endif
2164063Seric {
21756852Seric 	VA_LOCAL_DECL
21856852Seric 
2194711Seric 	errno = 0;
22056852Seric 	VA_START(msg);
22158151Seric 	fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
22256852Seric 	VA_END;
22368692Seric 	putoutmsg(MsgBuf, FALSE, FALSE);
22467818Seric 
22567818Seric 	/* save this message for mailq printing */
22667818Seric 	if (MsgBuf[0] == '5' || (CurEnv->e_message == NULL && MsgBuf[0] == '4'))
22767818Seric 	{
22867818Seric 		if (CurEnv->e_message != NULL)
22967818Seric 			free(CurEnv->e_message);
23067818Seric 		CurEnv->e_message = newstr(MsgBuf + 4);
23167818Seric 	}
2327613Seric }
2337613Seric /*
2348239Seric **  NMESSAGE -- print message (not necessarily an error)
2358239Seric **
2368239Seric **	Just like "message" except it never puts the to... tag on.
2378239Seric **
2388239Seric **	Parameters:
2398239Seric **		msg -- the message (printf fmt) -- if it begins
24067818Seric **			with a three digit SMTP reply code, that is used,
24167818Seric **			otherwise 050 is assumed.
24267818Seric **		(others) -- printf arguments
2438239Seric **
2448239Seric **	Returns:
2458239Seric **		none
2468239Seric **
2478239Seric **	Side Effects:
2488239Seric **		none.
2498239Seric */
2508239Seric 
2518239Seric /*VARARGS2*/
25258826Seric void
25357642Seric #ifdef __STDC__
25460094Seric nmessage(const char *msg, ...)
25557642Seric #else
25658151Seric nmessage(msg, va_alist)
25760094Seric 	const char *msg;
25857642Seric 	va_dcl
25957642Seric #endif
2608239Seric {
26156852Seric 	VA_LOCAL_DECL
26256852Seric 
2638239Seric 	errno = 0;
26456852Seric 	VA_START(msg);
26558151Seric 	fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
26656852Seric 	VA_END;
26768692Seric 	putoutmsg(MsgBuf, FALSE, FALSE);
2688239Seric }
2698239Seric /*
27063753Seric **  PUTOUTMSG -- output error message to transcript and channel
2717613Seric **
2727613Seric **	Parameters:
2737613Seric **		msg -- message to output (in SMTP format).
2749108Seric **		holdmsg -- if TRUE, don't output a copy of the message to
2759108Seric **			our output channel.
27668692Seric **		heldmsg -- if TRUE, this is a previously held message;
27768692Seric **			don't log it to the transcript file.
2787613Seric **
2797613Seric **	Returns:
2807613Seric **		none.
2817613Seric **
2827613Seric **	Side Effects:
2837613Seric **		Outputs msg to the transcript.
2847613Seric **		If appropriate, outputs it to the channel.
2857613Seric **		Deletes SMTP reply code number as appropriate.
2867613Seric */
2874711Seric 
28868692Seric putoutmsg(msg, holdmsg, heldmsg)
2897613Seric 	char *msg;
2909108Seric 	bool holdmsg;
29168692Seric 	bool heldmsg;
2927613Seric {
29364249Seric 	/* display for debugging */
29464249Seric 	if (tTd(54, 8))
29568692Seric 		printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
29668692Seric 			heldmsg ? " (held)" : "");
29764249Seric 
29814900Seric 	/* output to transcript if serious */
29968692Seric 	if (!heldmsg && CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
30014900Seric 		fprintf(CurEnv->e_xfp, "%s\n", msg);
3014711Seric 
3024711Seric 	/* output to channel if appropriate */
30368692Seric 	if (!Verbose && msg[0] == '0')
30460421Seric 		return;
30568692Seric 	if (holdmsg)
30668692Seric 	{
30768692Seric 		/* save for possible future display */
30868692Seric 		strcpy(HeldMessageBuf, msg);
30968692Seric 		return;
31068692Seric 	}
31160421Seric 
31263848Seric 	/* map warnings to something SMTP can handle */
31363848Seric 	if (msg[0] == '6')
31463848Seric 		msg[0] = '5';
31563848Seric 
31660421Seric 	(void) fflush(stdout);
31766017Seric 
31866017Seric 	/* if DisConnected, OutChannel now points to the transcript */
31966017Seric 	if (!DisConnected &&
32066017Seric 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
32160421Seric 		fprintf(OutChannel, "%s\r\n", msg);
32260421Seric 	else
32360421Seric 		fprintf(OutChannel, "%s\n", &msg[4]);
32463753Seric 	if (TrafficLogFile != NULL)
32563753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(),
32665580Seric 			(OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
32760421Seric 	if (msg[3] == ' ')
32860421Seric 		(void) fflush(OutChannel);
32966017Seric 	if (!ferror(OutChannel) || DisConnected)
33060421Seric 		return;
33160421Seric 
33264499Seric 	/*
33364499Seric 	**  Error on output -- if reporting lost channel, just ignore it.
33464499Seric 	**  Also, ignore errors from QUIT response (221 message) -- some
33564499Seric 	**	rude servers don't read result.
33664499Seric 	*/
33764499Seric 
33864499Seric 	if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0)
33960421Seric 		return;
34060421Seric 
34160421Seric 	/* can't call syserr, 'cause we are using MsgBuf */
34260421Seric 	HoldErrs = TRUE;
34360283Seric #ifdef LOG
34460421Seric 	if (LogLevel > 0)
34560421Seric 		syslog(LOG_CRIT,
34666864Seric 			"%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
34760421Seric 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
34864123Seric 			CurHostName == NULL ? "NO-HOST" : CurHostName,
34966864Seric 			msg, errstring(errno));
35060283Seric #endif
3519389Seric }
3529389Seric /*
35363753Seric **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
3549389Seric **
3559389Seric **	Parameters:
3569389Seric **		msg -- the message to output.
3579389Seric **
3589389Seric **	Returns:
3599389Seric **		none.
3609389Seric **
3619389Seric **	Side Effects:
3629389Seric **		Sets the fatal error bit in the envelope as appropriate.
3639389Seric */
3648239Seric 
3659389Seric puterrmsg(msg)
3669389Seric 	char *msg;
3679389Seric {
36863848Seric 	char msgcode = msg[0];
36963848Seric 
3709389Seric 	/* output the message as usual */
37168692Seric 	putoutmsg(msg, HoldErrs, FALSE);
3729389Seric 
3739389Seric 	/* signal the error */
37464773Seric 	Errors++;
37563848Seric 	if (msgcode == '6')
37663848Seric 	{
37763848Seric 		/* notify the postmaster */
37863848Seric 		CurEnv->e_flags |= EF_PM_NOTIFY;
37963848Seric 	}
38067786Seric 	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
38164773Seric 	{
38264773Seric 		/* mark long-term fatal errors */
38364773Seric 		CurEnv->e_flags |= EF_FATALERRS;
38464773Seric 	}
3854711Seric }
3864711Seric /*
3874711Seric **  FMTMSG -- format a message into buffer.
3884711Seric **
3894711Seric **	Parameters:
3904711Seric **		eb -- error buffer to get result.
3914711Seric **		to -- the recipient tag for this message.
3924711Seric **		num -- arpanet error number.
39316901Seric **		en -- the error number to display.
3944711Seric **		fmt -- format of string.
3954711Seric **		a, b, c, d, e -- arguments.
3964711Seric **
3974711Seric **	Returns:
3984711Seric **		none.
3994711Seric **
4004711Seric **	Side Effects:
4014711Seric **		none.
4024711Seric */
4034063Seric 
40446928Sbostic static void
40556852Seric fmtmsg(eb, to, num, eno, fmt, ap)
4064711Seric 	register char *eb;
4074711Seric 	char *to;
4084711Seric 	char *num;
40916904Seric 	int eno;
4104711Seric 	char *fmt;
41156852Seric 	va_list ap;
4124711Seric {
4134711Seric 	char del;
41459596Seric 	char *meb;
4154711Seric 
4164711Seric 	/* output the reply code */
41724943Seric 	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
4184577Seric 	{
4194711Seric 		num = fmt;
4204711Seric 		fmt += 4;
4214711Seric 	}
4224711Seric 	if (num[3] == '-')
4234711Seric 		del = '-';
4244711Seric 	else
4254711Seric 		del = ' ';
4264711Seric 	(void) sprintf(eb, "%3.3s%c", num, del);
4274711Seric 	eb += 4;
4284063Seric 
4299372Seric 	/* output the file name and line number */
4309372Seric 	if (FileName != NULL)
4319372Seric 	{
4329372Seric 		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
4339372Seric 		eb += strlen(eb);
4349372Seric 	}
4359372Seric 
4364711Seric 	/* output the "to" person */
4374711Seric 	if (to != NULL && to[0] != '\0')
4384711Seric 	{
43966297Seric 		(void) sprintf(eb, "%s... ", shortenstring(to, 203));
4405201Seric 		while (*eb != '\0')
4415201Seric 			*eb++ &= 0177;
4424711Seric 	}
4434711Seric 
44459596Seric 	meb = eb;
44559596Seric 
4464711Seric 	/* output the message */
44756852Seric 	(void) vsprintf(eb, fmt, ap);
4485201Seric 	while (*eb != '\0')
4495201Seric 		*eb++ &= 0177;
4504711Seric 
4514711Seric 	/* output the error code, if any */
45216904Seric 	if (eno != 0)
4534711Seric 	{
45416904Seric 		(void) sprintf(eb, ": %s", errstring(eno));
4554711Seric 		eb += strlen(eb);
4564577Seric 	}
457295Seric }
45815136Seric /*
45968692Seric **  BUFFER_ERRORS -- arrange to buffer future error messages
46068692Seric **
46168692Seric **	Parameters:
46268692Seric **		none
46368692Seric **
46468692Seric **	Returns:
46568692Seric **		none.
46668692Seric */
46768692Seric 
46868692Seric void
46968692Seric buffer_errors()
47068692Seric {
47168692Seric 	HeldMessageBuf[0] = '\0';
47268692Seric 	HoldErrs = TRUE;
47368692Seric }
47468692Seric /*
47568692Seric **  FLUSH_ERRORS -- flush the held error message buffer
47668692Seric **
47768692Seric **	Parameters:
47868692Seric **		print -- if set, print the message, otherwise just
47968692Seric **			delete it.
48068692Seric **
48168692Seric **	Returns:
48268692Seric **		none.
48368692Seric */
48468692Seric 
48568692Seric void
48668692Seric flush_errors(print)
48768692Seric 	bool print;
48868692Seric {
48968692Seric 	if (print && HeldMessageBuf[0] != '\0')
49068692Seric 		putoutmsg(HeldMessageBuf, FALSE, TRUE);
49168692Seric 	HeldMessageBuf[0] = '\0';
49268692Seric 	HoldErrs = FALSE;
49368692Seric }
49468692Seric /*
49515136Seric **  ERRSTRING -- return string description of error code
49615136Seric **
49715136Seric **	Parameters:
49865751Seric **		errnum -- the error number to translate
49915136Seric **
50015136Seric **	Returns:
50165751Seric **		A string description of errnum.
50215136Seric **
50315136Seric **	Side Effects:
50415136Seric **		none.
50515136Seric */
50615136Seric 
50760089Seric const char *
50865751Seric errstring(errnum)
50965751Seric 	int errnum;
51015136Seric {
51165168Seric 	char *dnsmsg;
51263839Seric 	static char buf[MAXLINE];
51363839Seric # ifndef ERRLIST_PREDEFINED
51463839Seric 	extern char *sys_errlist[];
51515136Seric 	extern int sys_nerr;
51663839Seric # endif
51724943Seric # ifdef SMTP
51824943Seric 	extern char *SmtpPhase;
51956795Seric # endif /* SMTP */
52015136Seric 
52124943Seric 	/*
52224943Seric 	**  Handle special network error codes.
52324943Seric 	**
52424943Seric 	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
52524943Seric 	*/
52624943Seric 
52765168Seric 	dnsmsg = NULL;
52865751Seric 	switch (errnum)
52924943Seric 	{
53065067Seric # if defined(DAEMON) && defined(ETIMEDOUT)
53124943Seric 	  case ETIMEDOUT:
53224943Seric 	  case ECONNRESET:
53365751Seric 		(void) strcpy(buf, sys_errlist[errnum]);
53424943Seric 		if (SmtpPhase != NULL)
53524943Seric 		{
53624943Seric 			(void) strcat(buf, " during ");
53724943Seric 			(void) strcat(buf, SmtpPhase);
53824943Seric 		}
53925050Seric 		if (CurHostName != NULL)
54024943Seric 		{
54124943Seric 			(void) strcat(buf, " with ");
54225050Seric 			(void) strcat(buf, CurHostName);
54324943Seric 		}
54424943Seric 		return (buf);
54524943Seric 
54624943Seric 	  case EHOSTDOWN:
54725050Seric 		if (CurHostName == NULL)
54824943Seric 			break;
54925050Seric 		(void) sprintf(buf, "Host %s is down", CurHostName);
55024943Seric 		return (buf);
55124943Seric 
55224943Seric 	  case ECONNREFUSED:
55325050Seric 		if (CurHostName == NULL)
55424943Seric 			break;
55525050Seric 		(void) sprintf(buf, "Connection refused by %s", CurHostName);
55624943Seric 		return (buf);
55765067Seric # endif
55825526Smiriam 
55963993Seric 	  case EOPENTIMEOUT:
56063993Seric 		return "Timeout on file open";
56163993Seric 
56266334Seric # if NAMED_BIND
56363993Seric 	  case HOST_NOT_FOUND + E_DNSBASE:
56465168Seric 		dnsmsg = "host not found";
56565168Seric 		break;
56658010Seric 
56763993Seric 	  case TRY_AGAIN + E_DNSBASE:
56865168Seric 		dnsmsg = "host name lookup failure";
56965168Seric 		break;
57058010Seric 
57163993Seric 	  case NO_RECOVERY + E_DNSBASE:
57265168Seric 		dnsmsg = "non-recoverable error";
57365168Seric 		break;
57458010Seric 
57563993Seric 	  case NO_DATA + E_DNSBASE:
57665168Seric 		dnsmsg = "no data known";
57765168Seric 		break;
57857736Seric # endif
57965067Seric 
58065067Seric 	  case EPERM:
58165067Seric 		/* SunOS gives "Not owner" -- this is the POSIX message */
58265067Seric 		return "Operation not permitted";
58324943Seric 	}
58424943Seric 
58565168Seric 	if (dnsmsg != NULL)
58665168Seric 	{
58765168Seric 		(void) strcpy(buf, "Name server: ");
58865168Seric 		if (CurHostName != NULL)
58965168Seric 		{
59065168Seric 			(void) strcat(buf, CurHostName);
59165168Seric 			(void) strcat(buf, ": ");
59265168Seric 		}
59365168Seric 		(void) strcat(buf, dnsmsg);
59465168Seric 		return buf;
59565168Seric 	}
59665168Seric 
59765751Seric 	if (errnum > 0 && errnum < sys_nerr)
59865751Seric 		return (sys_errlist[errnum]);
59915136Seric 
60065751Seric 	(void) sprintf(buf, "Error %d", errnum);
60115136Seric 	return (buf);
60215136Seric }
603