1 /* @(#)comerr.c	1.47 19/09/27 Copyright 1985-1989, 1995-2017 J. Schilling */
2 /*
3  *	Routines for printing command errors
4  *
5  *	Copyright (c) 1985-1989, 1995-2017 J. Schilling
6  */
7 /*
8  * The contents of this file are subject to the terms of the
9  * Common Development and Distribution License, Version 1.0 only
10  * (the "License").  You may not use this file except in compliance
11  * with the License.
12  *
13  * See the file CDDL.Schily.txt in this distribution for details.
14  * A copy of the CDDL is also available via the Internet at
15  * http://www.opensource.org/licenses/cddl1.txt
16  *
17  * When distributing Covered Code, include this CDDL HEADER in each
18  * file and include the License file CDDL.Schily.txt from this distribution.
19  */
20 
21 #include <schily/mconfig.h>
22 #include <schily/unistd.h>	/* include <sys/types.h> try to get size_t */
23 #include <schily/stdio.h>	/* Try again for size_t	*/
24 #include <schily/stdlib.h>	/* Try again for size_t	*/
25 #include <schily/standard.h>
26 #include <schily/varargs.h>
27 #include <schily/string.h>
28 #include <schily/schily.h>
29 #include <schily/errno.h>
30 
31 EXPORT	int	on_comerr	__PR((void (*fun)(int, void *), void *arg));
32 EXPORT	void	comerr		__PR((const char *, ...));
33 EXPORT	void	xcomerr		__PR((int, const char *, ...));
34 EXPORT	void	comerrno	__PR((int, const char *, ...));
35 EXPORT	void	xcomerrno	__PR((int, int, const char *, ...));
36 EXPORT	int	errmsg		__PR((const char *, ...));
37 EXPORT	int	errmsgno	__PR((int, const char *, ...));
38 EXPORT	int	_comerr		__PR((FILE *, int, int, int,
39 						const char *, va_list));
40 LOCAL	int	_ex_clash	__PR((int));
41 EXPORT	void	comexit		__PR((int));
42 EXPORT	char	*errmsgstr	__PR((int));
43 
44 typedef	struct ex {
45 	struct ex *next;
46 	void	(*func) __PR((int, void *));
47 	void	*arg;
48 } ex_t;
49 
50 LOCAL	ex_t	*exfuncs;
51 
52 /*
53  * Set list of callback functions to call with *comerr() and comexit().
54  * The function set up last with on_comerr() is called first on exit;
55  * in other words: call order is the reverse order of registration.
56  */
57 EXPORT	int
58 on_comerr(func, arg)
59 	void	(*func) __PR((int, void *));
60 	void	*arg;
61 {
62 	ex_t	*fp;
63 
64 	fp = malloc(sizeof (*fp));
65 	if (fp == NULL)
66 		return (-1);
67 
68 	fp->func = func;
69 	fp->arg  = arg;
70 	fp->next = exfuncs;
71 	exfuncs = fp;
72 	return (0);
73 }
74 
75 /*
76  * Fetch current errno, print a related message and exit(errno).
77  */
78 /* VARARGS1 */
79 #ifdef	PROTOTYPES
80 EXPORT void
comerr(const char * msg,...)81 comerr(const char *msg, ...)
82 #else
83 EXPORT void
84 comerr(msg, va_alist)
85 	char	*msg;
86 	va_dcl
87 #endif
88 {
89 	va_list	args;
90 
91 #ifdef	PROTOTYPES
92 	va_start(args, msg);
93 #else
94 	va_start(args);
95 #endif
96 	(void) _comerr(stderr, COMERR_EXIT, 0, geterrno(), msg, args);
97 	/* NOTREACHED */
98 	va_end(args);
99 }
100 
101 /*
102  * Fetch current errno, print a related message and exit(exc).
103  */
104 /* VARARGS2 */
105 #ifdef	PROTOTYPES
106 EXPORT void
xcomerr(int exc,const char * msg,...)107 xcomerr(int exc, const char *msg, ...)
108 #else
109 EXPORT void
110 xcomerr(exc, msg, va_alist)
111 	int	exc;
112 	char	*msg;
113 	va_dcl
114 #endif
115 {
116 	va_list	args;
117 
118 #ifdef	PROTOTYPES
119 	va_start(args, msg);
120 #else
121 	va_start(args);
122 #endif
123 	(void) _comerr(stderr, COMERR_EXCODE, exc, geterrno(), msg, args);
124 	/* NOTREACHED */
125 	va_end(args);
126 }
127 
128 /*
129  * Print a message related to err and exit(err).
130  */
131 /* VARARGS2 */
132 #ifdef	PROTOTYPES
133 EXPORT void
comerrno(int err,const char * msg,...)134 comerrno(int err, const char *msg, ...)
135 #else
136 EXPORT void
137 comerrno(err, msg, va_alist)
138 	int	err;
139 	char	*msg;
140 	va_dcl
141 #endif
142 {
143 	va_list	args;
144 
145 #ifdef	PROTOTYPES
146 	va_start(args, msg);
147 #else
148 	va_start(args);
149 #endif
150 	(void) _comerr(stderr, COMERR_EXIT, 0, err, msg, args);
151 	/* NOTREACHED */
152 	va_end(args);
153 }
154 
155 /*
156  * Print a message related to err and exit(exc).
157  */
158 /* VARARGS3 */
159 #ifdef	PROTOTYPES
160 EXPORT void
xcomerrno(int exc,int err,const char * msg,...)161 xcomerrno(int exc, int err, const char *msg, ...)
162 #else
163 EXPORT void
164 xcomerrno(exc, err, msg, va_alist)
165 	int	exc;
166 	int	err;
167 	char	*msg;
168 	va_dcl
169 #endif
170 {
171 	va_list	args;
172 
173 #ifdef	PROTOTYPES
174 	va_start(args, msg);
175 #else
176 	va_start(args);
177 #endif
178 	(void) _comerr(stderr, COMERR_EXCODE, exc, err, msg, args);
179 	/* NOTREACHED */
180 	va_end(args);
181 }
182 
183 /*
184  * Fetch current errno, print a related message and return(errno).
185  */
186 /* VARARGS1 */
187 #ifdef	PROTOTYPES
188 EXPORT int
errmsg(const char * msg,...)189 errmsg(const char *msg, ...)
190 #else
191 EXPORT int
192 errmsg(msg, va_alist)
193 	char	*msg;
194 	va_dcl
195 #endif
196 {
197 	va_list	args;
198 	int	ret;
199 
200 #ifdef	PROTOTYPES
201 	va_start(args, msg);
202 #else
203 	va_start(args);
204 #endif
205 	ret = _comerr(stderr, COMERR_RETURN, 0, geterrno(), msg, args);
206 	va_end(args);
207 	return (ret);
208 }
209 
210 /*
211  * Print a message related to err and return(err).
212  */
213 /* VARARGS2 */
214 #ifdef	PROTOTYPES
215 EXPORT int
errmsgno(int err,const char * msg,...)216 errmsgno(int err, const char *msg, ...)
217 #else
218 EXPORT int
219 errmsgno(err, msg, va_alist)
220 	int	err;
221 	char	*msg;
222 	va_dcl
223 #endif
224 {
225 	va_list	args;
226 	int	ret;
227 
228 #ifdef	PROTOTYPES
229 	va_start(args, msg);
230 #else
231 	va_start(args);
232 #endif
233 	ret = _comerr(stderr, COMERR_RETURN, 0, err, msg, args);
234 	va_end(args);
235 	return (ret);
236 }
237 
238 #if defined(__BEOS__) || defined(__HAIKU__)
239 	/*
240 	 * On BeOS errno is a big negative number (0x80000000 + small number).
241 	 * We assume that small negative numbers are safe to be used as special
242 	 * values that prevent printing the errno text.
243 	 *
244 	 * We tried to use #if EIO < 0 but this does not work because EIO is
245 	 * defined to a enum. ENODEV may work as ENODEV is defined to a number
246 	 * directly.
247 	 */
248 #define	silent_error(e)		((e) < 0 && (e) >= -1024)
249 #else
250 	/*
251 	 * On UNIX errno is a small non-negative number, so we assume that
252 	 * negative values cannot be a valid errno and don't print the error
253 	 * string in this case. However the value may still be used as exit()
254 	 * code if 'exflg' is set.
255 	 */
256 #define	silent_error(e)		((e) < 0)
257 #endif
258 EXPORT int
_comerr(f,exflg,exc,err,msg,args)259 _comerr(f, exflg, exc, err, msg, args)
260 	FILE		*f;	/* FILE * to print to */
261 	int		exflg;	/* COMERR_RETURN, COMERR_EXIT, COMERR_EXCODE */
262 	int		exc;	/* Use for exit() if exflg & COMERR_EXCODE */
263 	int		err;	/* Errno for text, exit(err) if !COMERR_EXIT */
264 	const char	*msg;	/* printf() format */
265 	va_list		args;	/* printf() args for format */
266 {
267 	char	errbuf[20];
268 	char	*errnam;
269 	char	*prognam = get_progname();
270 
271 	if (silent_error(err)) {
272 		js_fprintf(f, "%s: %r", prognam, msg, args);
273 	} else {
274 		errnam = errmsgstr(err);
275 		if (errnam == NULL) {
276 			(void) js_snprintf(errbuf, sizeof (errbuf),
277 						"Error %d", err);
278 			errnam = errbuf;
279 		}
280 		js_fprintf(f, "%s: %s. %r", prognam, errnam, msg, args);
281 	}
282 	if (exflg) {
283 		if (exflg & COMERR_EXCODE)
284 			err = exc;
285 		else
286 			err = _ex_clash(err);
287 		comexit(err);
288 		/* NOTREACHED */
289 	}
290 	return (err);
291 }
292 
293 LOCAL int
_ex_clash(exc)294 _ex_clash(exc)
295 	int	exc;
296 {
297 	int	exmod = exc % 256;
298 	char	*p;
299 
300 	/*
301 	 * On a recent POSIX System that supports waitid(), siginfo.si_status
302 	 * holds the exit(2) value as an int. So if waitid() is used to wait
303 	 * for the process, we do not have problems from folded exit codes.
304 	 * All other wait*() functions fold the exit code by masking it
305 	 * with 0377.
306 	 *
307 	 * Exit codes used with comerr*()/comexit() are frequently errno values
308 	 * that have been in the range 0..31 with UNIX.V5 in the mid 1970s and
309 	 * that now are in the range 0..151 on Solaris. These values do not
310 	 * cause problems from folding to 8 bits, but "sysexits.h" contains
311 	 * definitions in the range 64..79 that cause (even unfolded) clashes
312 	 * with errno values.
313 	 *
314 	 * To avoid clashes with errno values, "schily/standard.h" defines
315 	 * EX_BAD (-1) (255) as default error exit code and
316 	 * EX_CLASH (-64) (192) as marker for clashes.
317 	 * Exit codes in the range -2..-63 (254..193 seen as unsigned two's
318 	 * complement) are available as software specific exit codes.
319 	 * We map all other negative exit codes to EX_CLASH.
320 	 *
321 	 * Do not map exit codes in case that the "COMERR_EXCODE" environment
322 	 * is present.
323 	 */
324 	if ((p = getenv("COMERR_EXCODE")) != NULL) {
325 		if (*p == '0') {
326 			/*
327 			 * Modern shells that use waitpid() still interpret
328 			 * excode % 256 == 0 as a zero exit code with respect
329 			 * to conditional execution for compatibility with
330 			 * historic shells. Use "export COMERR_EXCODE=0" to
331 			 * request support for this problem.
332 			 */
333 			if (exmod == 0 && exc != 0)
334 				return(EX_CLASH);
335 		}
336 		return (exc);
337 	}
338 
339 	if ((exc != exmod) ||
340 	    (exmod < EX_CLASH) ||
341 	    ((exmod > 0) && (exmod & 0377) >= (EX_CLASH & 0377)))
342 		exc = EX_CLASH;
343 	return (exc);
344 }
345 
346 /*
347  * Do a program exit() with previously calling functions registered via
348  * on_comerr().
349  */
350 EXPORT void
comexit(err)351 comexit(err)
352 	int	err;
353 {
354 	while (exfuncs) {
355 		ex_t	*fp;
356 
357 		(*exfuncs->func)(err, exfuncs->arg);
358 		fp = exfuncs;
359 		exfuncs = exfuncs->next;
360 		free(fp);
361 	}
362 	exit(err);
363 	/* NOTREACHED */
364 }
365 
366 /*
367  * Wrapper around the strange POSIX strerror().
368  * If there is a problem with retrieving the correct error text,
369  * return NULL.
370  */
371 EXPORT char *
errmsgstr(err)372 errmsgstr(err)
373 	int	err;
374 {
375 #ifdef	HAVE_STRERROR
376 	/*
377 	 * POSIX compliance may look strange...
378 	 */
379 	int	errsav = geterrno();
380 	char	*ret;
381 
382 	seterrno(0);
383 	ret = strerror(err);
384 	err = geterrno();
385 	seterrno(errsav);
386 
387 	if (ret == NULL || err)
388 		return (NULL);
389 	return (ret);
390 #else
391 	if (err < 0 || err >= sys_nerr) {
392 		return (NULL);
393 	} else {
394 		return (sys_errlist[err]);
395 	}
396 #endif
397 }
398