xref: /netbsd/bin/sh/error.c (revision c4a72b64)
1 /*	$NetBSD: error.c,v 1.28 2002/11/24 22:35:39 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)error.c	8.2 (Berkeley) 5/4/95";
43 #else
44 __RCSID("$NetBSD: error.c,v 1.28 2002/11/24 22:35:39 christos Exp $");
45 #endif
46 #endif /* not lint */
47 
48 /*
49  * Errors and exceptions.
50  */
51 
52 #include <signal.h>
53 #include <stdlib.h>
54 #include <unistd.h>
55 #include <errno.h>
56 #include <stdio.h>
57 #include <string.h>
58 
59 #include "shell.h"
60 #include "main.h"
61 #include "options.h"
62 #include "output.h"
63 #include "error.h"
64 #include "show.h"
65 
66 
67 /*
68  * Code to handle exceptions in C.
69  */
70 
71 struct jmploc *handler;
72 int exception;
73 volatile int suppressint;
74 volatile int intpending;
75 char *commandname;
76 
77 
78 static void exverror(int, const char *, va_list)
79     __attribute__((__noreturn__));
80 
81 /*
82  * Called to raise an exception.  Since C doesn't include exceptions, we
83  * just do a longjmp to the exception handler.  The type of exception is
84  * stored in the global variable "exception".
85  */
86 
87 void
88 exraise(int e)
89 {
90 	if (handler == NULL)
91 		abort();
92 	exception = e;
93 	longjmp(handler->loc, 1);
94 }
95 
96 
97 /*
98  * Called from trap.c when a SIGINT is received.  (If the user specifies
99  * that SIGINT is to be trapped or ignored using the trap builtin, then
100  * this routine is not called.)  Suppressint is nonzero when interrupts
101  * are held using the INTOFF macro.  The call to _exit is necessary because
102  * there is a short period after a fork before the signal handlers are
103  * set to the appropriate value for the child.  (The test for iflag is
104  * just defensive programming.)
105  */
106 
107 void
108 onint(void)
109 {
110 	sigset_t sigset;
111 
112 	if (suppressint) {
113 		intpending = 1;
114 		return;
115 	}
116 	intpending = 0;
117 	sigemptyset(&sigset);
118 	sigprocmask(SIG_SETMASK, &sigset, NULL);
119 	if (rootshell && iflag)
120 		exraise(EXINT);
121 	else {
122 		signal(SIGINT, SIG_DFL);
123 		raise(SIGINT);
124 	}
125 	/* NOTREACHED */
126 }
127 
128 static void
129 exvwarning(int sv_errno, const char *msg, va_list ap)
130 {
131 	/* Partially emulate line buffered output so that:
132 	 *	printf '%d\n' 1 a 2
133 	 * and
134 	 *	printf '%d %d %d\n' 1 a 2
135 	 * both generate sensible text when stdout and stderr are merged.
136 	 */
137 	if (output.nextc != output.buf && output.nextc[-1] == '\n')
138 		flushout(&output);
139 	if (commandname)
140 		outfmt(&errout, "%s: ", commandname);
141 	if (msg != NULL) {
142 		doformat(&errout, msg, ap);
143 		if (sv_errno >= 0)
144 			outfmt(&errout, ": ");
145 	}
146 	if (sv_errno >= 0)
147 		outfmt(&errout, "%s", strerror(sv_errno));
148 	out2c('\n');
149 	flushout(&errout);
150 }
151 
152 /*
153  * Exverror is called to raise the error exception.  If the second argument
154  * is not NULL then error prints an error message using printf style
155  * formatting.  It then raises the error exception.
156  */
157 static void
158 exverror(int cond, const char *msg, va_list ap)
159 {
160 	CLEAR_PENDING_INT;
161 	INTOFF;
162 
163 #ifdef DEBUG
164 	if (msg)
165 		TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
166 	else
167 		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
168 #endif
169 	if (msg)
170 		exvwarning(-1, msg, ap);
171 
172 	flushall();
173 	exraise(cond);
174 	/* NOTREACHED */
175 }
176 
177 
178 void
179 error(const char *msg, ...)
180 {
181 	va_list ap;
182 
183 	va_start(ap, msg);
184 	exverror(EXERROR, msg, ap);
185 	/* NOTREACHED */
186 	va_end(ap);
187 }
188 
189 
190 void
191 exerror(int cond, const char *msg, ...)
192 {
193 	va_list ap;
194 
195 	va_start(ap, msg);
196 	exverror(cond, msg, ap);
197 	/* NOTREACHED */
198 	va_end(ap);
199 }
200 
201 /*
202  * error/warning routines for external builtins
203  */
204 
205 void
206 sh_exit(int rval)
207 {
208 	exerrno = rval & 255;
209 	exraise(EXEXEC);
210 }
211 
212 void
213 sh_err(int status, const char *fmt, ...)
214 {
215 	va_list ap;
216 
217 	va_start(ap, fmt);
218 	exvwarning(errno, fmt, ap);
219 	va_end(ap);
220 	sh_exit(status);
221 }
222 
223 void
224 sh_verr(int status, const char *fmt, va_list ap)
225 {
226 	exvwarning(errno, fmt, ap);
227 	sh_exit(status);
228 }
229 
230 void
231 sh_errx(int status, const char *fmt, ...)
232 {
233 	va_list ap;
234 
235 	va_start(ap, fmt);
236 	exvwarning(-1, fmt, ap);
237 	va_end(ap);
238 	sh_exit(status);
239 }
240 
241 void
242 sh_verrx(int status, const char *fmt, va_list ap)
243 {
244 	exvwarning(-1, fmt, ap);
245 	sh_exit(status);
246 }
247 
248 void
249 sh_warn(const char *fmt, ...)
250 {
251 	va_list ap;
252 
253 	va_start(ap, fmt);
254 	exvwarning(errno, fmt, ap);
255 	va_end(ap);
256 }
257 
258 void
259 sh_vwarn(const char *fmt, va_list ap)
260 {
261 	exvwarning(errno, fmt, ap);
262 }
263 
264 void
265 sh_warnx(const char *fmt, ...)
266 {
267 	va_list ap;
268 
269 	va_start(ap, fmt);
270 	exvwarning(-1, fmt, ap);
271 	va_end(ap);
272 }
273 
274 void
275 sh_vwarnx(const char *fmt, va_list ap)
276 {
277 	exvwarning(-1, fmt, ap);
278 }
279 
280 
281 /*
282  * Table of error messages.
283  */
284 
285 struct errname {
286 	short errcode;		/* error number */
287 	short action;		/* operation which encountered the error */
288 	const char *msg;	/* text describing the error */
289 };
290 
291 
292 #define ALL (E_OPEN|E_CREAT|E_EXEC)
293 
294 STATIC const struct errname errormsg[] = {
295 	{ EINTR,	ALL,	"interrupted" },
296 	{ EACCES,	ALL,	"permission denied" },
297 	{ EIO,		ALL,	"I/O error" },
298 	{ EEXIST,	ALL,	"file exists" },
299 	{ ENOENT,	E_OPEN,	"no such file" },
300 	{ ENOENT,	E_CREAT,"directory nonexistent" },
301 	{ ENOENT,	E_EXEC,	"not found" },
302 	{ ENOTDIR,	E_OPEN,	"no such file" },
303 	{ ENOTDIR,	E_CREAT,"directory nonexistent" },
304 	{ ENOTDIR,	E_EXEC,	"not found" },
305 	{ EISDIR,	ALL,	"is a directory" },
306 #ifdef EMFILE
307 	{ EMFILE,	ALL,	"too many open files" },
308 #endif
309 	{ ENFILE,	ALL,	"file table overflow" },
310 	{ ENOSPC,	ALL,	"file system full" },
311 #ifdef EDQUOT
312 	{ EDQUOT,	ALL,	"disk quota exceeded" },
313 #endif
314 #ifdef ENOSR
315 	{ ENOSR,	ALL,	"no streams resources" },
316 #endif
317 	{ ENXIO,	ALL,	"no such device or address" },
318 	{ EROFS,	ALL,	"read-only file system" },
319 	{ ETXTBSY,	ALL,	"text busy" },
320 #ifdef EAGAIN
321 	{ EAGAIN,	E_EXEC,	"not enough memory" },
322 #endif
323 	{ ENOMEM,	ALL,	"not enough memory" },
324 #ifdef ENOLINK
325 	{ ENOLINK,	ALL,	"remote access failed" },
326 #endif
327 #ifdef EMULTIHOP
328 	{ EMULTIHOP,	ALL,	"remote access failed" },
329 #endif
330 #ifdef ECOMM
331 	{ ECOMM,	ALL,	"remote access failed" },
332 #endif
333 #ifdef ESTALE
334 	{ ESTALE,	ALL,	"remote access failed" },
335 #endif
336 #ifdef ETIMEDOUT
337 	{ ETIMEDOUT,	ALL,	"remote access failed" },
338 #endif
339 #ifdef ELOOP
340 	{ ELOOP,	ALL,	"symbolic link loop" },
341 #endif
342 	{ E2BIG,	E_EXEC,	"argument list too long" },
343 #ifdef ELIBACC
344 	{ ELIBACC,	E_EXEC,	"shared library missing" },
345 #endif
346 	{ 0,		0,	NULL },
347 };
348 
349 
350 /*
351  * Return a string describing an error.  The returned string may be a
352  * pointer to a static buffer that will be overwritten on the next call.
353  * Action describes the operation that got the error.
354  */
355 
356 const char *
357 errmsg(int e, int action)
358 {
359 	struct errname const *ep;
360 	static char buf[12];
361 
362 	for (ep = errormsg ; ep->errcode ; ep++) {
363 		if (ep->errcode == e && (ep->action & action) != 0)
364 			return ep->msg;
365 	}
366 	fmtstr(buf, sizeof buf, "error %d", e);
367 	return buf;
368 }
369