|
12 | 12 |
|
13 | 13 | #include "postgres.h"
|
14 | 14 |
|
| 15 | +#include <unistd.h> |
| 16 | +#include <time.h> |
| 17 | + |
15 | 18 | #include "miscadmin.h"
|
| 19 | +#include "libpq/pqsignal.h" |
16 | 20 | #include "postmaster/bgworker_internals.h"
|
17 | 21 | #include "storage/barrier.h"
|
| 22 | +#include "storage/ipc.h" |
| 23 | +#include "storage/latch.h" |
18 | 24 | #include "storage/lwlock.h"
|
19 | 25 | #include "storage/pmsignal.h"
|
| 26 | +#include "storage/proc.h" |
| 27 | +#include "storage/procsignal.h" |
20 | 28 | #include "storage/shmem.h"
|
| 29 | +#include "tcop/tcopprot.h" |
21 | 30 | #include "utils/ascii.h"
|
| 31 | +#include "utils/ps_status.h" |
| 32 | +#include "utils/timeout.h" |
22 | 33 |
|
23 | 34 | /*
|
24 | 35 | * The postmaster's list of registered background workers, in private memory.
|
@@ -354,6 +365,214 @@ SanityCheckBackgroundWorker(BackgroundWorker *worker, int elevel)
|
354 | 365 | return true;
|
355 | 366 | }
|
356 | 367 |
|
| 368 | +static void |
| 369 | +bgworker_quickdie(SIGNAL_ARGS) |
| 370 | +{ |
| 371 | + sigaddset(&BlockSig, SIGQUIT); /* prevent nested calls */ |
| 372 | + PG_SETMASK(&BlockSig); |
| 373 | + |
| 374 | + /* |
| 375 | + * We DO NOT want to run proc_exit() callbacks -- we're here because |
| 376 | + * shared memory may be corrupted, so we don't want to try to clean up our |
| 377 | + * transaction. Just nail the windows shut and get out of town. Now that |
| 378 | + * there's an atexit callback to prevent third-party code from breaking |
| 379 | + * things by calling exit() directly, we have to reset the callbacks |
| 380 | + * explicitly to make this work as intended. |
| 381 | + */ |
| 382 | + on_exit_reset(); |
| 383 | + |
| 384 | + /* |
| 385 | + * Note we do exit(0) here, not exit(2) like quickdie. The reason is that |
| 386 | + * we don't want to be seen this worker as independently crashed, because |
| 387 | + * then postmaster would delay restarting it again afterwards. If some |
| 388 | + * idiot DBA manually sends SIGQUIT to a random bgworker, the "dead man |
| 389 | + * switch" will ensure that postmaster sees this as a crash. |
| 390 | + */ |
| 391 | + exit(0); |
| 392 | +} |
| 393 | + |
| 394 | +/* |
| 395 | + * Standard SIGTERM handler for background workers |
| 396 | + */ |
| 397 | +static void |
| 398 | +bgworker_die(SIGNAL_ARGS) |
| 399 | +{ |
| 400 | + PG_SETMASK(&BlockSig); |
| 401 | + |
| 402 | + ereport(FATAL, |
| 403 | + (errcode(ERRCODE_ADMIN_SHUTDOWN), |
| 404 | + errmsg("terminating background worker \"%s\" due to administrator command", |
| 405 | + MyBgworkerEntry->bgw_name))); |
| 406 | +} |
| 407 | + |
| 408 | +/* |
| 409 | + * Standard SIGUSR1 handler for unconnected workers |
| 410 | + * |
| 411 | + * Here, we want to make sure an unconnected worker will at least heed |
| 412 | + * latch activity. |
| 413 | + */ |
| 414 | +static void |
| 415 | +bgworker_sigusr1_handler(SIGNAL_ARGS) |
| 416 | +{ |
| 417 | + int save_errno = errno; |
| 418 | + |
| 419 | + latch_sigusr1_handler(); |
| 420 | + |
| 421 | + errno = save_errno; |
| 422 | +} |
| 423 | + |
| 424 | +/* |
| 425 | + * Start a new background worker |
| 426 | + * |
| 427 | + * This is the main entry point for background worker, to be called from |
| 428 | + * postmaster. |
| 429 | + */ |
| 430 | +void |
| 431 | +StartBackgroundWorker(void) |
| 432 | +{ |
| 433 | + sigjmp_buf local_sigjmp_buf; |
| 434 | + char buf[MAXPGPATH]; |
| 435 | + BackgroundWorker *worker = MyBgworkerEntry; |
| 436 | + bgworker_main_type entrypt; |
| 437 | + |
| 438 | + if (worker == NULL) |
| 439 | + elog(FATAL, "unable to find bgworker entry"); |
| 440 | + |
| 441 | + /* we are a postmaster subprocess now */ |
| 442 | + IsUnderPostmaster = true; |
| 443 | + IsBackgroundWorker = true; |
| 444 | + |
| 445 | + /* reset MyProcPid */ |
| 446 | + MyProcPid = getpid(); |
| 447 | + |
| 448 | + /* record Start Time for logging */ |
| 449 | + MyStartTime = time(NULL); |
| 450 | + |
| 451 | + /* Identify myself via ps */ |
| 452 | + snprintf(buf, MAXPGPATH, "bgworker: %s", worker->bgw_name); |
| 453 | + init_ps_display(buf, "", "", ""); |
| 454 | + |
| 455 | + SetProcessingMode(InitProcessing); |
| 456 | + |
| 457 | + /* Apply PostAuthDelay */ |
| 458 | + if (PostAuthDelay > 0) |
| 459 | + pg_usleep(PostAuthDelay * 1000000L); |
| 460 | + |
| 461 | + /* |
| 462 | + * If possible, make this process a group leader, so that the postmaster |
| 463 | + * can signal any child processes too. |
| 464 | + */ |
| 465 | +#ifdef HAVE_SETSID |
| 466 | + if (setsid() < 0) |
| 467 | + elog(FATAL, "setsid() failed: %m"); |
| 468 | +#endif |
| 469 | + |
| 470 | + /* |
| 471 | + * Set up signal handlers. |
| 472 | + */ |
| 473 | + if (worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION) |
| 474 | + { |
| 475 | + /* |
| 476 | + * SIGINT is used to signal canceling the current action |
| 477 | + */ |
| 478 | + pqsignal(SIGINT, StatementCancelHandler); |
| 479 | + pqsignal(SIGUSR1, procsignal_sigusr1_handler); |
| 480 | + pqsignal(SIGFPE, FloatExceptionHandler); |
| 481 | + |
| 482 | + /* XXX Any other handlers needed here? */ |
| 483 | + } |
| 484 | + else |
| 485 | + { |
| 486 | + pqsignal(SIGINT, SIG_IGN); |
| 487 | + pqsignal(SIGUSR1, bgworker_sigusr1_handler); |
| 488 | + pqsignal(SIGFPE, SIG_IGN); |
| 489 | + } |
| 490 | + pqsignal(SIGTERM, bgworker_die); |
| 491 | + pqsignal(SIGHUP, SIG_IGN); |
| 492 | + |
| 493 | + pqsignal(SIGQUIT, bgworker_quickdie); |
| 494 | + InitializeTimeouts(); /* establishes SIGALRM handler */ |
| 495 | + |
| 496 | + pqsignal(SIGPIPE, SIG_IGN); |
| 497 | + pqsignal(SIGUSR2, SIG_IGN); |
| 498 | + pqsignal(SIGCHLD, SIG_DFL); |
| 499 | + |
| 500 | + /* |
| 501 | + * If an exception is encountered, processing resumes here. |
| 502 | + * |
| 503 | + * See notes in postgres.c about the design of this coding. |
| 504 | + */ |
| 505 | + if (sigsetjmp(local_sigjmp_buf, 1) != 0) |
| 506 | + { |
| 507 | + /* Since not using PG_TRY, must reset error stack by hand */ |
| 508 | + error_context_stack = NULL; |
| 509 | + |
| 510 | + /* Prevent interrupts while cleaning up */ |
| 511 | + HOLD_INTERRUPTS(); |
| 512 | + |
| 513 | + /* Report the error to the server log */ |
| 514 | + EmitErrorReport(); |
| 515 | + |
| 516 | + /* |
| 517 | + * Do we need more cleanup here? For shmem-connected bgworkers, we |
| 518 | + * will call InitProcess below, which will install ProcKill as exit |
| 519 | + * callback. That will take care of releasing locks, etc. |
| 520 | + */ |
| 521 | + |
| 522 | + /* and go away */ |
| 523 | + proc_exit(1); |
| 524 | + } |
| 525 | + |
| 526 | + /* We can now handle ereport(ERROR) */ |
| 527 | + PG_exception_stack = &local_sigjmp_buf; |
| 528 | + |
| 529 | + /* Early initialization */ |
| 530 | + BaseInit(); |
| 531 | + |
| 532 | + /* |
| 533 | + * If necessary, create a per-backend PGPROC struct in shared memory, |
| 534 | + * except in the EXEC_BACKEND case where this was done in |
| 535 | + * SubPostmasterMain. We must do this before we can use LWLocks (and in |
| 536 | + * the EXEC_BACKEND case we already had to do some stuff with LWLocks). |
| 537 | + */ |
| 538 | +#ifndef EXEC_BACKEND |
| 539 | + if (worker->bgw_flags & BGWORKER_SHMEM_ACCESS) |
| 540 | + InitProcess(); |
| 541 | +#endif |
| 542 | + |
| 543 | + /* |
| 544 | + * If bgw_main is set, we use that value as the initial entrypoint. |
| 545 | + * However, if the library containing the entrypoint wasn't loaded at |
| 546 | + * postmaster startup time, passing it as a direct function pointer is |
| 547 | + * not possible. To work around that, we allow callers for whom a |
| 548 | + * function pointer is not available to pass a library name (which will |
| 549 | + * be loaded, if necessary) and a function name (which will be looked up |
| 550 | + * in the named library). |
| 551 | + */ |
| 552 | + if (worker->bgw_main != NULL) |
| 553 | + entrypt = worker->bgw_main; |
| 554 | + else |
| 555 | + entrypt = (bgworker_main_type) |
| 556 | + load_external_function(worker->bgw_library_name, |
| 557 | + worker->bgw_function_name, |
| 558 | + true, NULL); |
| 559 | + |
| 560 | + /* |
| 561 | + * Note that in normal processes, we would call InitPostgres here. For a |
| 562 | + * worker, however, we don't know what database to connect to, yet; so we |
| 563 | + * need to wait until the user code does it via |
| 564 | + * BackgroundWorkerInitializeConnection(). |
| 565 | + */ |
| 566 | + |
| 567 | + /* |
| 568 | + * Now invoke the user-defined worker code |
| 569 | + */ |
| 570 | + entrypt(worker->bgw_main_arg); |
| 571 | + |
| 572 | + /* ... and if it returns, we're done */ |
| 573 | + proc_exit(0); |
| 574 | +} |
| 575 | + |
357 | 576 | /*
|
358 | 577 | * Register a new background worker while processing shared_preload_libraries.
|
359 | 578 | *
|
|
0 commit comments