1 /* @(#)comerr.c 1.44 17/03/13 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 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 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 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 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 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 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 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 294 _ex_clash(exc) 295 int exc; 296 { 297 int exmod = exc % 256; 298 299 /* 300 * On a recent POSIX System that supports waitid(), siginfo.si_status 301 * holds the exit(2) value as an int. So if waitid() is used to wait 302 * for the process, we do not have problems from folded exit codes. 303 * All other wait*() functions fold the exit code by masking it 304 * with 0377. 305 * 306 * Exit codes used with comerr*()/comexit() are frequently errno values 307 * that have been in the range 0..31 with UNIX.V5 in the mid 1970s and 308 * that now are in the range 0..151 on Solaris. These values do not 309 * cause problems from folding to 8 bits, but "sysexits.h" contains 310 * definitions in the range 64..79 that cause (even unfolded) clashes 311 * with errno values. 312 * 313 * To avoid clashes with errno values, "schily/standard.h" defines 314 * EX_BAD (-1) as default error exit code and 315 * EX_CLASH (-64) as marker for clashes. 316 * Exit codes in the range -2..-63 (254..193 seen as unsigned two's 317 * complement) are available as software specific exit codes. 318 * We map all other negative exit codes to EX_CLASH if they would fold 319 * to -2..-63. 320 */ 321 if (exc != exmod && exmod <= 0 && exmod >= EX_CLASH) 322 exc = EX_CLASH; 323 return (exc); 324 } 325 326 /* 327 * Do a program exit() with previously calling functions registered via 328 * on_comerr(). 329 */ 330 EXPORT void 331 comexit(err) 332 int err; 333 { 334 while (exfuncs) { 335 ex_t *fp; 336 337 (*exfuncs->func)(err, exfuncs->arg); 338 fp = exfuncs; 339 exfuncs = exfuncs->next; 340 free(fp); 341 } 342 exit(err); 343 /* NOTREACHED */ 344 } 345 346 /* 347 * Wrapper around the strange POSIX strerror(). 348 * If there is a problem with retrieving the correct error text, 349 * return NULL. 350 */ 351 EXPORT char * 352 errmsgstr(err) 353 int err; 354 { 355 #ifdef HAVE_STRERROR 356 /* 357 * POSIX compliance may look strange... 358 */ 359 int errsav = geterrno(); 360 char *ret; 361 362 seterrno(0); 363 ret = strerror(err); 364 err = geterrno(); 365 seterrno(errsav); 366 367 if (ret == NULL || err) 368 return (NULL); 369 return (ret); 370 #else 371 if (err < 0 || err >= sys_nerr) { 372 return (NULL); 373 } else { 374 return (sys_errlist[err]); 375 } 376 #endif 377 } 378