1 /* 2 * Copyright (c) 2000-2002 Sendmail, 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> 12 SM_RCSID("@(#)$Id: exc.c,v 1.49 2006/12/19 19:28:09 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 29 const char SmExcMagic[] = "sm_exc"; 30 const 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 51 void 52 sm_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 149 static void 150 sm_etype_os_print __P(( 151 SM_EXC_T *exc, 152 SM_FILE_T *stream)); 153 154 static void 155 sm_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 179 const 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 194 const 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 232 static SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list SM_NONVOLATILE)); 233 234 static SM_EXC_T * 235 sm_exc_vnew_x(etype, ap) 236 const SM_EXC_TYPE_T *etype; 237 va_list SM_NONVOLATILE 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 403 SM_EXC_T * 404 #if SM_VA_STD 405 sm_exc_new_x( 406 const SM_EXC_TYPE_T *etype, 407 ...) 408 #else /* SM_VA_STD */ 409 sm_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 433 void 434 sm_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 477 bool 478 sm_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 499 void 500 sm_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 519 void 520 sm_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 529 SM_EXC_HANDLER_T *SmExcHandler = NULL; 530 static 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 554 void 555 sm_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 572 void SM_DEAD_D 573 sm_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 633 void SM_DEAD_D 634 #if SM_VA_STD 635 sm_exc_raisenew_x( 636 const SM_EXC_TYPE_T *etype, 637 ...) 638 #else 639 sm_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