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