xref: /netbsd-src/bin/sh/error.c (revision 001c68bd94f75ce9270b69227c4199fbf34ee396)
1 /*	$NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl 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.30 2003/01/22 20:36:03 dsl 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 nsigset;
111 
112 	if (suppressint) {
113 		intpending = 1;
114 		return;
115 	}
116 	intpending = 0;
117 	sigemptyset(&nsigset);
118 	sigprocmask(SIG_SETMASK, &nsigset, 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, \"", cond));
166 		TRACEV((msg, ap));
167 		TRACE(("\") pid=%d\n", getpid()));
168 	} else
169 		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
170 #endif
171 	if (msg)
172 		exvwarning(-1, msg, ap);
173 
174 	flushall();
175 	exraise(cond);
176 	/* NOTREACHED */
177 }
178 
179 
180 void
181 error(const char *msg, ...)
182 {
183 	va_list ap;
184 
185 	va_start(ap, msg);
186 	exverror(EXERROR, msg, ap);
187 	/* NOTREACHED */
188 	va_end(ap);
189 }
190 
191 
192 void
193 exerror(int cond, const char *msg, ...)
194 {
195 	va_list ap;
196 
197 	va_start(ap, msg);
198 	exverror(cond, msg, ap);
199 	/* NOTREACHED */
200 	va_end(ap);
201 }
202 
203 /*
204  * error/warning routines for external builtins
205  */
206 
207 void
208 sh_exit(int rval)
209 {
210 	exerrno = rval & 255;
211 	exraise(EXEXEC);
212 }
213 
214 void
215 sh_err(int status, const char *fmt, ...)
216 {
217 	va_list ap;
218 
219 	va_start(ap, fmt);
220 	exvwarning(errno, fmt, ap);
221 	va_end(ap);
222 	sh_exit(status);
223 }
224 
225 void
226 sh_verr(int status, const char *fmt, va_list ap)
227 {
228 	exvwarning(errno, fmt, ap);
229 	sh_exit(status);
230 }
231 
232 void
233 sh_errx(int status, const char *fmt, ...)
234 {
235 	va_list ap;
236 
237 	va_start(ap, fmt);
238 	exvwarning(-1, fmt, ap);
239 	va_end(ap);
240 	sh_exit(status);
241 }
242 
243 void
244 sh_verrx(int status, const char *fmt, va_list ap)
245 {
246 	exvwarning(-1, fmt, ap);
247 	sh_exit(status);
248 }
249 
250 void
251 sh_warn(const char *fmt, ...)
252 {
253 	va_list ap;
254 
255 	va_start(ap, fmt);
256 	exvwarning(errno, fmt, ap);
257 	va_end(ap);
258 }
259 
260 void
261 sh_vwarn(const char *fmt, va_list ap)
262 {
263 	exvwarning(errno, fmt, ap);
264 }
265 
266 void
267 sh_warnx(const char *fmt, ...)
268 {
269 	va_list ap;
270 
271 	va_start(ap, fmt);
272 	exvwarning(-1, fmt, ap);
273 	va_end(ap);
274 }
275 
276 void
277 sh_vwarnx(const char *fmt, va_list ap)
278 {
279 	exvwarning(-1, fmt, ap);
280 }
281 
282 
283 /*
284  * Table of error messages.
285  */
286 
287 struct errname {
288 	short errcode;		/* error number */
289 	short action;		/* operation which encountered the error */
290 	const char *msg;	/* text describing the error */
291 };
292 
293 
294 #define ALL (E_OPEN|E_CREAT|E_EXEC)
295 
296 STATIC const struct errname errormsg[] = {
297 	{ EINTR,	ALL,	"interrupted" },
298 	{ EACCES,	ALL,	"permission denied" },
299 	{ EIO,		ALL,	"I/O error" },
300 	{ EEXIST,	ALL,	"file exists" },
301 	{ ENOENT,	E_OPEN,	"no such file" },
302 	{ ENOENT,	E_CREAT,"directory nonexistent" },
303 	{ ENOENT,	E_EXEC,	"not found" },
304 	{ ENOTDIR,	E_OPEN,	"no such file" },
305 	{ ENOTDIR,	E_CREAT,"directory nonexistent" },
306 	{ ENOTDIR,	E_EXEC,	"not found" },
307 	{ EISDIR,	ALL,	"is a directory" },
308 #ifdef EMFILE
309 	{ EMFILE,	ALL,	"too many open files" },
310 #endif
311 	{ ENFILE,	ALL,	"file table overflow" },
312 	{ ENOSPC,	ALL,	"file system full" },
313 #ifdef EDQUOT
314 	{ EDQUOT,	ALL,	"disk quota exceeded" },
315 #endif
316 #ifdef ENOSR
317 	{ ENOSR,	ALL,	"no streams resources" },
318 #endif
319 	{ ENXIO,	ALL,	"no such device or address" },
320 	{ EROFS,	ALL,	"read-only file system" },
321 	{ ETXTBSY,	ALL,	"text busy" },
322 #ifdef EAGAIN
323 	{ EAGAIN,	E_EXEC,	"not enough memory" },
324 #endif
325 	{ ENOMEM,	ALL,	"not enough memory" },
326 #ifdef ENOLINK
327 	{ ENOLINK,	ALL,	"remote access failed" },
328 #endif
329 #ifdef EMULTIHOP
330 	{ EMULTIHOP,	ALL,	"remote access failed" },
331 #endif
332 #ifdef ECOMM
333 	{ ECOMM,	ALL,	"remote access failed" },
334 #endif
335 #ifdef ESTALE
336 	{ ESTALE,	ALL,	"remote access failed" },
337 #endif
338 #ifdef ETIMEDOUT
339 	{ ETIMEDOUT,	ALL,	"remote access failed" },
340 #endif
341 #ifdef ELOOP
342 	{ ELOOP,	ALL,	"symbolic link loop" },
343 #endif
344 	{ E2BIG,	E_EXEC,	"argument list too long" },
345 #ifdef ELIBACC
346 	{ ELIBACC,	E_EXEC,	"shared library missing" },
347 #endif
348 	{ 0,		0,	NULL },
349 };
350 
351 
352 /*
353  * Return a string describing an error.  The returned string may be a
354  * pointer to a static buffer that will be overwritten on the next call.
355  * Action describes the operation that got the error.
356  */
357 
358 const char *
359 errmsg(int e, int action)
360 {
361 	struct errname const *ep;
362 	static char buf[12];
363 
364 	for (ep = errormsg ; ep->errcode ; ep++) {
365 		if (ep->errcode == e && (ep->action & action) != 0)
366 			return ep->msg;
367 	}
368 	fmtstr(buf, sizeof buf, "error %d", e);
369 	return buf;
370 }
371