1/* 2 * Copyright (c) 2000-2002 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 */ 10 11#include <sm/gen.h> 12SM_RCSID("@(#)$Id: exc.c,v 1.50 2013-11-22 20:51:42 ca Exp $") 13 14/* 15** exception handling 16** For documentation, see exc.html 17*/ 18 19#include <ctype.h> 20#include <string.h> 21 22#include <sm/errstring.h> 23#include <sm/exc.h> 24#include <sm/heap.h> 25#include <sm/string.h> 26#include <sm/varargs.h> 27#include <sm/io.h> 28 29const char SmExcMagic[] = "sm_exc"; 30const char SmExcTypeMagic[] = "sm_exc_type"; 31 32/* 33** SM_ETYPE_PRINTF -- printf for exception types. 34** 35** Parameters: 36** exc -- exception. 37** stream -- file for output. 38** 39** Returns: 40** none. 41*/ 42 43/* 44** A simple formatted print function that can be used as the print function 45** by most exception types. It prints the printcontext string, interpreting 46** occurrences of %0 through %9 as references to the argument vector. 47** If exception argument 3 is an int or long, then %3 will print the 48** argument in decimal, and %o3 or %x3 will print it in octal or hex. 49*/ 50 51void 52sm_etype_printf(exc, stream) 53 SM_EXC_T *exc; 54 SM_FILE_T *stream; 55{ 56 size_t n = strlen(exc->exc_type->etype_argformat); 57 const char *p, *s; 58 char format; 59 60 for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p) 61 { 62 if (*p != '%') 63 { 64 (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); 65 continue; 66 } 67 ++p; 68 if (*p == '\0') 69 { 70 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 71 break; 72 } 73 if (*p == '%') 74 { 75 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 76 continue; 77 } 78 format = '\0'; 79 if (isalpha(*p)) 80 { 81 format = *p++; 82 if (*p == '\0') 83 { 84 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 85 (void) sm_io_putc(stream, SM_TIME_DEFAULT, 86 format); 87 break; 88 } 89 } 90 if (isdigit(*p)) 91 { 92 size_t i = *p - '0'; 93 if (i < n) 94 { 95 switch (exc->exc_type->etype_argformat[i]) 96 { 97 case 's': 98 case 'r': 99 s = exc->exc_argv[i].v_str; 100 if (s == NULL) 101 s = "(null)"; 102 sm_io_fputs(stream, SM_TIME_DEFAULT, s); 103 continue; 104 case 'i': 105 sm_io_fprintf(stream, 106 SM_TIME_DEFAULT, 107 format == 'o' ? "%o" 108 : format == 'x' ? "%x" 109 : "%d", 110 exc->exc_argv[i].v_int); 111 continue; 112 case 'l': 113 sm_io_fprintf(stream, 114 SM_TIME_DEFAULT, 115 format == 'o' ? "%lo" 116 : format == 'x' ? "%lx" 117 : "%ld", 118 exc->exc_argv[i].v_long); 119 continue; 120 case 'e': 121 sm_exc_write(exc->exc_argv[i].v_exc, 122 stream); 123 continue; 124 } 125 } 126 } 127 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 128 if (format) 129 (void) sm_io_putc(stream, SM_TIME_DEFAULT, format); 130 (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); 131 } 132} 133 134/* 135** Standard exception types. 136*/ 137 138/* 139** SM_ETYPE_OS_PRINT -- Print OS related exception. 140** 141** Parameters: 142** exc -- exception. 143** stream -- file for output. 144** 145** Returns: 146** none. 147*/ 148 149static void 150sm_etype_os_print __P(( 151 SM_EXC_T *exc, 152 SM_FILE_T *stream)); 153 154static void 155sm_etype_os_print(exc, stream) 156 SM_EXC_T *exc; 157 SM_FILE_T *stream; 158{ 159 int err = exc->exc_argv[0].v_int; 160 char *syscall = exc->exc_argv[1].v_str; 161 char *sysargs = exc->exc_argv[2].v_str; 162 163 if (sysargs) 164 sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s", 165 sysargs, syscall, sm_errstring(err)); 166 else 167 sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall, 168 sm_errstring(err)); 169} 170 171/* 172** SmEtypeOs represents the failure of a Unix system call. 173** The three arguments are: 174** int errno (eg, ENOENT) 175** char *syscall (eg, "open") 176** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf") 177*/ 178 179const SM_EXC_TYPE_T SmEtypeOs = 180{ 181 SmExcTypeMagic, 182 "E:sm.os", 183 "isr", 184 sm_etype_os_print, 185 NULL, 186}; 187 188/* 189** SmEtypeErr is a completely generic error which should only be 190** used in applications and test programs. Libraries should use 191** more specific exception codes. 192*/ 193 194const SM_EXC_TYPE_T SmEtypeErr = 195{ 196 SmExcTypeMagic, 197 "E:sm.err", 198 "r", 199 sm_etype_printf, 200 "%0", 201}; 202 203/* 204** SM_EXC_VNEW_X -- Construct a new exception object. 205** 206** Parameters: 207** etype -- type of exception. 208** ap -- varargs. 209** 210** Returns: 211** pointer to exception object. 212*/ 213 214/* 215** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x. 216** 217** If an exception is raised, then to avoid a storage leak, we must: 218** (a) Free all storage we have allocated. 219** (b) Free all exception arguments in the varargs list. 220** Getting this right is tricky. 221** 222** To see why (b) is required, consider the code fragment 223** SM_EXCEPT(exc, "*") 224** sm_exc_raisenew_x(&MyEtype, exc); 225** SM_END_TRY 226** In the normal case, sm_exc_raisenew_x will allocate and raise a new 227** exception E that owns exc. When E is eventually freed, exc is also freed. 228** In the exceptional case, sm_exc_raisenew_x must free exc before raising 229** an out-of-memory exception so that exc is not leaked. 230*/ 231 232static SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list)); 233 234static SM_EXC_T * 235sm_exc_vnew_x(etype, ap) 236 const SM_EXC_TYPE_T *etype; 237 va_list ap; 238{ 239 /* 240 ** All variables that are modified in the SM_TRY clause and 241 ** referenced in the SM_EXCEPT clause must be declared volatile. 242 */ 243 244 /* NOTE: Type of si, i, and argc *must* match */ 245 SM_EXC_T * volatile exc = NULL; 246 int volatile si = 0; 247 SM_VAL_T * volatile argv = NULL; 248 int i, argc; 249 250 SM_REQUIRE_ISA(etype, SmExcTypeMagic); 251 argc = strlen(etype->etype_argformat); 252 SM_TRY 253 { 254 /* 255 ** Step 1. Allocate the exception structure. 256 ** On failure, scan the varargs list and free all 257 ** exception arguments. 258 */ 259 260 exc = sm_malloc_x(sizeof(SM_EXC_T)); 261 exc->sm_magic = SmExcMagic; 262 exc->exc_refcount = 1; 263 exc->exc_type = etype; 264 exc->exc_argv = NULL; 265 266 /* 267 ** Step 2. Allocate the argument vector. 268 ** On failure, free exc, scan the varargs list and free all 269 ** exception arguments. On success, scan the varargs list, 270 ** and copy the arguments into argv. 271 */ 272 273 argv = sm_malloc_x(argc * sizeof(SM_VAL_T)); 274 exc->exc_argv = argv; 275 for (i = 0; i < argc; ++i) 276 { 277 switch (etype->etype_argformat[i]) 278 { 279 case 'i': 280 argv[i].v_int = SM_VA_ARG(ap, int); 281 break; 282 case 'l': 283 argv[i].v_long = SM_VA_ARG(ap, long); 284 break; 285 case 'e': 286 argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*); 287 break; 288 case 's': 289 argv[i].v_str = SM_VA_ARG(ap, char*); 290 break; 291 case 'r': 292 SM_REQUIRE(etype->etype_argformat[i+1] == '\0'); 293 argv[i].v_str = SM_VA_ARG(ap, char*); 294 break; 295 default: 296 sm_abort("sm_exc_vnew_x: bad argformat '%c'", 297 etype->etype_argformat[i]); 298 } 299 } 300 301 /* 302 ** Step 3. Scan argv, and allocate space for all 303 ** string arguments. si is the number of elements 304 ** of argv that have been processed so far. 305 ** On failure, free exc, argv, all the exception arguments 306 ** and all of the strings that have been copied. 307 */ 308 309 for (si = 0; si < argc; ++si) 310 { 311 switch (etype->etype_argformat[si]) 312 { 313 case 's': 314 { 315 char *str = argv[si].v_str; 316 if (str != NULL) 317 argv[si].v_str = sm_strdup_x(str); 318 } 319 break; 320 case 'r': 321 { 322 char *fmt = argv[si].v_str; 323 if (fmt != NULL) 324 argv[si].v_str = sm_vstringf_x(fmt, ap); 325 } 326 break; 327 } 328 } 329 } 330 SM_EXCEPT(e, "*") 331 { 332 if (exc == NULL || argv == NULL) 333 { 334 /* 335 ** Failure in step 1 or step 2. 336 ** Scan ap and free all exception arguments. 337 */ 338 339 for (i = 0; i < argc; ++i) 340 { 341 switch (etype->etype_argformat[i]) 342 { 343 case 'i': 344 (void) SM_VA_ARG(ap, int); 345 break; 346 case 'l': 347 (void) SM_VA_ARG(ap, long); 348 break; 349 case 'e': 350 sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*)); 351 break; 352 case 's': 353 case 'r': 354 (void) SM_VA_ARG(ap, char*); 355 break; 356 } 357 } 358 } 359 else 360 { 361 /* 362 ** Failure in step 3. Scan argv and free 363 ** all exception arguments and all string 364 ** arguments that have been duplicated. 365 ** Then free argv. 366 */ 367 368 for (i = 0; i < argc; ++i) 369 { 370 switch (etype->etype_argformat[i]) 371 { 372 case 'e': 373 sm_exc_free(argv[i].v_exc); 374 break; 375 case 's': 376 case 'r': 377 if (i < si) 378 sm_free(argv[i].v_str); 379 break; 380 } 381 } 382 sm_free(argv); 383 } 384 sm_free(exc); 385 sm_exc_raise_x(e); 386 } 387 SM_END_TRY 388 389 return exc; 390} 391 392/* 393** SM_EXC_NEW_X -- Construct a new exception object. 394** 395** Parameters: 396** etype -- type of exception. 397** ... -- varargs. 398** 399** Returns: 400** pointer to exception object. 401*/ 402 403SM_EXC_T * 404#if SM_VA_STD 405sm_exc_new_x( 406 const SM_EXC_TYPE_T *etype, 407 ...) 408#else /* SM_VA_STD */ 409sm_exc_new_x(etype, va_alist) 410 const SM_EXC_TYPE_T *etype; 411 va_dcl 412#endif /* SM_VA_STD */ 413{ 414 SM_EXC_T *exc; 415 SM_VA_LOCAL_DECL 416 417 SM_VA_START(ap, etype); 418 exc = sm_exc_vnew_x(etype, ap); 419 SM_VA_END(ap); 420 return exc; 421} 422 423/* 424** SM_EXC_FREE -- Destroy a reference to an exception object. 425** 426** Parameters: 427** exc -- exception object. 428** 429** Returns: 430** none. 431*/ 432 433void 434sm_exc_free(exc) 435 SM_EXC_T *exc; 436{ 437 if (exc == NULL) 438 return; 439 SM_REQUIRE(exc->sm_magic == SmExcMagic); 440 if (exc->exc_refcount == 0) 441 return; 442 if (--exc->exc_refcount == 0) 443 { 444 int i, c; 445 446 for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0'; 447 ++i) 448 { 449 switch (c) 450 { 451 case 's': 452 case 'r': 453 sm_free(exc->exc_argv[i].v_str); 454 break; 455 case 'e': 456 sm_exc_free(exc->exc_argv[i].v_exc); 457 break; 458 } 459 } 460 exc->sm_magic = NULL; 461 sm_free(exc->exc_argv); 462 sm_free(exc); 463 } 464} 465 466/* 467** SM_EXC_MATCH -- Match exception category against a glob pattern. 468** 469** Parameters: 470** exc -- exception. 471** pattern -- glob pattern. 472** 473** Returns: 474** true iff match. 475*/ 476 477bool 478sm_exc_match(exc, pattern) 479 SM_EXC_T *exc; 480 const char *pattern; 481{ 482 if (exc == NULL) 483 return false; 484 SM_REQUIRE(exc->sm_magic == SmExcMagic); 485 return sm_match(exc->exc_type->etype_category, pattern); 486} 487 488/* 489** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline). 490** 491** Parameters: 492** exc -- exception. 493** stream -- file for output. 494** 495** Returns: 496** none. 497*/ 498 499void 500sm_exc_write(exc, stream) 501 SM_EXC_T *exc; 502 SM_FILE_T *stream; 503{ 504 SM_REQUIRE_ISA(exc, SmExcMagic); 505 exc->exc_type->etype_print(exc, stream); 506} 507 508/* 509** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline). 510** 511** Parameters: 512** exc -- exception. 513** stream -- file for output. 514** 515** Returns: 516** none. 517*/ 518 519void 520sm_exc_print(exc, stream) 521 SM_EXC_T *exc; 522 SM_FILE_T *stream; 523{ 524 SM_REQUIRE_ISA(exc, SmExcMagic); 525 exc->exc_type->etype_print(exc, stream); 526 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n'); 527} 528 529SM_EXC_HANDLER_T *SmExcHandler = NULL; 530static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL; 531 532/* 533** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread. 534** 535** Parameters: 536** h -- default exception handler. 537** 538** Returns: 539** none. 540*/ 541 542/* 543** Initialize a new process or a new thread by clearing the 544** exception handler stack and optionally setting a default 545** exception handler function. Call this at the beginning of main, 546** or in a new process after calling fork, or in a new thread. 547** 548** This function is a luxury, not a necessity. 549** If h != NULL then you can get the same effect by 550** wrapping the body of main, or the body of a forked child 551** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY. 552*/ 553 554void 555sm_exc_newthread(h) 556 SM_EXC_DEFAULT_HANDLER_T h; 557{ 558 SmExcHandler = NULL; 559 SmExcDefaultHandler = h; 560} 561 562/* 563** SM_EXC_RAISE_X -- Raise an exception. 564** 565** Parameters: 566** exc -- exception. 567** 568** Returns: 569** doesn't. 570*/ 571 572void SM_DEAD_D 573sm_exc_raise_x(exc) 574 SM_EXC_T *exc; 575{ 576 SM_REQUIRE_ISA(exc, SmExcMagic); 577 578 if (SmExcHandler == NULL) 579 { 580 if (SmExcDefaultHandler != NULL) 581 { 582 SM_EXC_DEFAULT_HANDLER_T h; 583 584 /* 585 ** If defined, the default handler is expected 586 ** to terminate the current thread of execution 587 ** using exit() or pthread_exit(). 588 ** If it instead returns normally, then we fall 589 ** through to the default case below. If it 590 ** raises an exception, then sm_exc_raise_x is 591 ** re-entered and, because we set SmExcDefaultHandler 592 ** to NULL before invoking h, we will again 593 ** end up in the default case below. 594 */ 595 596 h = SmExcDefaultHandler; 597 SmExcDefaultHandler = NULL; 598 (*h)(exc); 599 } 600 601 /* 602 ** No exception handler, so print the error and exit. 603 ** To override this behaviour on a program wide basis, 604 ** call sm_exc_newthread or put an exception handler in main(). 605 ** 606 ** XXX TODO: map the exception category to an exit code 607 ** XXX from <sysexits.h>. 608 */ 609 610 sm_exc_print(exc, smioerr); 611 exit(255); 612 } 613 614 if (SmExcHandler->eh_value == NULL) 615 SmExcHandler->eh_value = exc; 616 else 617 sm_exc_free(exc); 618 619 sm_longjmp_nosig(SmExcHandler->eh_context, 1); 620} 621 622/* 623** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...)) 624** 625** Parameters: 626** etype -- type of exception. 627** ap -- varargs. 628** 629** Returns: 630** none. 631*/ 632 633void SM_DEAD_D 634#if SM_VA_STD 635sm_exc_raisenew_x( 636 const SM_EXC_TYPE_T *etype, 637 ...) 638#else 639sm_exc_raisenew_x(etype, va_alist) 640 const SM_EXC_TYPE_T *etype; 641 va_dcl 642#endif 643{ 644 SM_EXC_T *exc; 645 SM_VA_LOCAL_DECL 646 647 SM_VA_START(ap, etype); 648 exc = sm_exc_vnew_x(etype, ap); 649 SM_VA_END(ap); 650 sm_exc_raise_x(exc); 651} 652