static void setup_event_handlers(void) { pqsignal(SIGHUP, handle_sighup); pqsignal(SIGINT, handle_sigint); pqsignal(SIGTERM, handle_sigint); }
static void WorkerCommon(void) { /* Establish signal handlers before unblocking signals. */ pqsignal(SIGHUP, sighupHandler); pqsignal(SIGTERM, sigtermHandler); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); }
void worker_main(Datum arg) { int ret; StringInfoData buf; uint32 segment = UInt32GetDatum(arg); /* Setup signal handlers */ pqsignal(SIGHUP, worker_sighup); pqsignal(SIGTERM, worker_sigterm); /* Allow signals */ BackgroundWorkerUnblockSignals(); initialize_worker(segment); /* Connect to the database */ BackgroundWorkerInitializeConnection(job->datname, job->rolname); elog(LOG, "%s initialized running job id %d", MyBgworkerEntry->bgw_name, job->job_id); pgstat_report_appname(MyBgworkerEntry->bgw_name); /* Initialize the query text */ initStringInfo(&buf); appendStringInfo(&buf, "SELECT * FROM %s.%s(%d, NULL)", job_run_function.schema, job_run_function.name, job->job_id); /* Initialize the SPI subsystem */ SetCurrentStatementStartTimestamp(); StartTransactionCommand(); SPI_connect(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_activity(STATE_RUNNING, buf.data); SetCurrentStatementStartTimestamp(); /* And run the query */ ret = SPI_execute(buf.data, true, 0); if (ret < 0) elog(FATAL, "errors while executing %s", buf.data); /* Commmit the transaction */ SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); proc_exit(0); }
/* * proc_exit callback to tear down the JVM */ static void _destroyJavaVM(int status, Datum dummy) { if(s_javaVM != 0) { Invocation ctx; #ifdef USE_PLJAVA_SIGHANDLERS #if PG_VERSION_NUM >= 90300 TimeoutId tid; #else pqsigfunc saveSigAlrm; #endif Invocation_pushInvocation(&ctx, false); if(sigsetjmp(recoverBuf, 1) != 0) { elog(DEBUG2, "needed to forcibly shut down the Java virtual machine"); s_javaVM = 0; return; } #if PG_VERSION_NUM >= 90300 InitializeTimeouts(); /* establishes SIGALRM handler */ tid = RegisterTimeout(USER_TIMEOUT, terminationTimeoutHandler); #else saveSigAlrm = pqsignal(SIGALRM, terminationTimeoutHandler); enable_sig_alarm(5000, false); #endif elog(DEBUG2, "shutting down the Java virtual machine"); JNI_destroyVM(s_javaVM); #if PG_VERSION_NUM >= 90300 disable_timeout(tid, false); #else disable_sig_alarm(false); pqsignal(SIGALRM, saveSigAlrm); #endif #else Invocation_pushInvocation(&ctx, false); elog(DEBUG2, "shutting down the Java virtual machine"); JNI_destroyVM(s_javaVM); #endif elog(DEBUG2, "done shutting down the Java virtual machine"); s_javaVM = 0; currentInvocation = 0; } }
/* * hello_main * * Main loop processing. */ void hello_main(Datum main_arg) { /* Set up the sigterm signal before unblocking them */ pqsignal(SIGTERM, hello_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); while (!got_sigterm) { int rc; /* Wait 10s */ rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, 10000L, PG_WAIT_EXTENSION); ResetLatch(&MyProc->procLatch); /* Emergency bailout if postmaster has died */ if (rc & WL_POSTMASTER_DEATH) proc_exit(1); elog(LOG, "Hello World!"); /* Say Hello to the world */ } proc_exit(0); }
/* * Foreign-data wrapper handler function: return a struct with pointers * to my callback routines. */ Datum jdbc_fdw_handler(PG_FUNCTION_ARGS) { FdwRoutine *fdwroutine = makeNode(FdwRoutine); #if (PG_VERSION_NUM < 90200) fdwroutine->PlanForeignScan = jdbcPlanForeignScan; #endif #if (PG_VERSION_NUM >= 90200) fdwroutine->GetForeignRelSize = jdbcGetForeignRelSize; fdwroutine->GetForeignPaths = jdbcGetForeignPaths; fdwroutine->GetForeignPlan = jdbcGetForeignPlan; #endif fdwroutine->ExplainForeignScan = jdbcExplainForeignScan; fdwroutine->BeginForeignScan = jdbcBeginForeignScan; fdwroutine->IterateForeignScan = jdbcIterateForeignScan; fdwroutine->ReScanForeignScan = jdbcReScanForeignScan; fdwroutine->EndForeignScan = jdbcEndForeignScan; pqsignal(SIGINT, SIGINTInterruptHandler); PG_RETURN_POINTER(fdwroutine); }
/* * signal handler in case we are interrupted. * * The Windows runtime docs at * http://msdn.microsoft.com/library/en-us/vclib/html/_crt_signal.asp * specifically forbid a number of things being done from a signal handler, * including IO, memory allocation and system calls, and only allow jmpbuf * if you are handling SIGFPE. * * I avoided doing the forbidden things by setting a flag instead of calling * exit_nicely() directly. * * Also note the behaviour of Windows with SIGINT, which says this: * Note SIGINT is not supported for any Win32 application, including * Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs, * Win32 operating systems generate a new thread to specifically handle * that interrupt. This can cause a single-thread application such as UNIX, * to become multithreaded, resulting in unexpected behavior. * * I have no idea how to handle this. (Strange they call UNIX an application!) * So this will need some testing on Windows. */ static void trapsig(int signum) { /* handle systems that reset the handler, like Windows (grr) */ pqsignal(signum, trapsig); caught_signal = true; }
void _PG_init(void) { coreIntHandler = pqsignal(SIGINT, handleInterrupt); #ifdef WIN32 #if POSTGIS_GEOS_VERSION >= 34 GEOS_interruptRegisterCallback(interruptCallback); #endif lwgeom_register_interrupt_callback(interruptCallback); #endif #if 0 /* Define custom GUC variables. */ DefineCustomIntVariable( "postgis.debug.level", /* name */ "Sets the debugging level of PostGIS.", /* short_desc */ "This is an experimental configuration.", /* long_desc */ &postgis_debug_level, /* valueAddr */ 0, 8, /* min-max */ 0, /* bootValue */ PGC_SUSET, /* GucContext context */ GUC_UNIT_MS, /* int flags */ #if POSTGIS_PGSQL_VERSION >= 91 NULL, /* GucStringCheckHook check_hook */ #endif NULL, /* GucStringAssignHook assign_hook */ NULL /* GucShowHook show_hook */ ); #endif #if 0 /* Define custom GUC variables. */ DefineCustomStringVariable( "postgis.greeting.string", /* name */ "Sets the greeting string used on postgis module load.", /* short_desc */ "This is an experimental configuration.", /* long_desc */ &greeting, /* valueAddr */ "Welcome to PostGIS " POSTGIS_VERSION, /* bootValue */ PGC_SUSET, /* GucContext context */ GUC_UNIT_MS, /* int flags */ #if POSTGIS_PGSQL_VERSION >= 91 NULL, /* GucStringCheckHook check_hook */ #endif NULL, /* GucStringAssignHook assign_hook */ NULL /* GucShowHook show_hook */ ); #endif /* install PostgreSQL handlers */ pg_install_lwgeom_handlers(); /* initialize geometry backend */ lwgeom_init_backend(); }
static void config_log_main(Datum main_arg) { config_log_objects *objects; pqsignal(SIGTERM, config_log_sigterm); pqsignal(SIGHUP, config_log_sighup); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* Connect to database */ BackgroundWorkerInitializeConnection(config_log_database, NULL); /* Verify expected objects exist */ objects = initialize_objects(); while (!got_sigterm) { int rc; rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, 100000L); ResetLatch(&MyProc->procLatch); /* emergency bailout if postmaster has died */ if (rc & WL_POSTMASTER_DEATH) proc_exit(1); /* * In case of a SIGHUP, just reload the configuration. */ if (got_sighup) { got_sighup = false; ProcessConfigFile(PGC_SIGHUP); execute_pg_settings_logger(objects); } } proc_exit(0); }
void hello_main(Datum main_arg) { /* Register functions for SIGTERM/SIGHUP management */ pqsignal(SIGHUP, hello_sighup); pqsignal(SIGTERM, hello_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); while (true) { /* Wait 1s */ WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 1000L, PG_WAIT_EXTENSION); ResetLatch(MyLatch); /* Process signals */ if (got_sighup) { /* Process config file */ ProcessConfigFile(PGC_SIGHUP); got_sighup = false; ereport(LOG, (errmsg("hello signal: processed SIGHUP"))); } if (got_sigterm) { /* Simply exit */ ereport(LOG, (errmsg("hello signal: processed SIGTERM"))); proc_exit(0); } } /* No problems, so clean exit */ proc_exit(0); }
int main(int argc, char *argv[]) { set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_test_fsync")); progname = get_progname(argv[0]); handle_args(argc, argv); /* Prevent leaving behind the test file */ pqsignal(SIGINT, signal_cleanup); pqsignal(SIGTERM, signal_cleanup); #ifndef WIN32 pqsignal(SIGALRM, process_alarm); #endif #ifdef SIGHUP /* Not defined on win32 */ pqsignal(SIGHUP, signal_cleanup); #endif prepare_buf(); test_open(); /* Test using 1 XLOG_BLCKSZ write */ test_sync(1); /* Test using 2 XLOG_BLCKSZ writes */ test_sync(2); test_open_syncs(); test_file_descriptor_sync(); test_non_sync(); unlink(filename); return 0; }
/* ---------------------------------------------------------------- * proc_exit * * this function calls all the callbacks registered * for it (to free resources) and then calls exit. * * This should be the only function to call exit(). * -cim 2/6/90 * * Unfortunately, we can't really guarantee that add-on code * obeys the rule of not calling exit() directly. So, while * this is the preferred way out of the system, we also register * an atexit callback that will make sure cleanup happens. * ---------------------------------------------------------------- */ void proc_exit(int code) { pqsignal(SIGALRM, SIG_IGN); /* Clean up everything that must be cleaned up */ proc_exit_prepare(code); #ifdef PROFILE_PID_DIR { /* * If we are profiling ourself then gprof's mcleanup() is about to * write out a profile to ./gmon.out. Since mcleanup() always uses a * fixed file name, each backend will overwrite earlier profiles. To * fix that, we create a separate subdirectory for each backend * (./gprof/pid) and 'cd' to that subdirectory before we exit() - that * forces mcleanup() to write each profile into its own directory. We * end up with something like: $PGDATA/gprof/8829/gmon.out * $PGDATA/gprof/8845/gmon.out ... * * To avoid undesirable disk space bloat, autovacuum workers are * discriminated against: all their gmon.out files go into the same * subdirectory. Without this, an installation that is "just sitting * there" nonetheless eats megabytes of disk space every few seconds. * * Note that we do this here instead of in an on_proc_exit() callback * because we want to ensure that this code executes last - we don't * want to interfere with any other on_proc_exit() callback. For the * same reason, we do not include it in proc_exit_prepare ... so if * you are exiting in the "wrong way" you won't drop your profile in a * nice place. */ char gprofDirName[32]; if (IsAutoVacuumWorkerProcess()) snprintf(gprofDirName, 32, "gprof/avworker"); else snprintf(gprofDirName, 32, "gprof/%d", (int) getpid()); mkdir("gprof", S_IRWXU | S_IRWXG | S_IRWXO); mkdir(gprofDirName, S_IRWXU | S_IRWXG | S_IRWXO); chdir(gprofDirName); } #endif elog(DEBUG3, "exit(%d)", code); exit(code); }
/* * setQFout * -- handler for -o command line option and \o command * * Tries to open file fname (or pipe if fname starts with '|') * and stores the file handle in pset) * Upon failure, sets stdout and returns false. */ bool setQFout(const char *fname) { bool status = true; /* Close old file/pipe */ if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr) { if (pset.queryFoutPipe) pclose(pset.queryFout); else fclose(pset.queryFout); } /* If no filename, set stdout */ if (!fname || fname[0] == '\0') { pset.queryFout = stdout; pset.queryFoutPipe = false; } else if (*fname == '|') { pset.queryFout = popen(fname + 1, "w"); pset.queryFoutPipe = true; } else { pset.queryFout = fopen(fname, "w"); pset.queryFoutPipe = false; } if (!(pset.queryFout)) { psql_error("%s: %s\n", fname, strerror(errno)); pset.queryFout = stdout; pset.queryFoutPipe = false; status = false; } /* Direct signals */ #ifndef WIN32 pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL); #endif return status; }
/* * Initialize timeout module. * * This must be called in every process that wants to use timeouts. * * If the process was forked from another one that was also using this * module, be sure to call this before re-enabling signals; else handlers * meant to run in the parent process might get invoked in this one. */ void InitializeTimeouts(void) { int i; /* Initialize, or re-initialize, all local state */ disable_alarm(); num_active_timeouts = 0; for (i = 0; i < MAX_TIMEOUTS; i++) { all_timeouts[i].index = i; all_timeouts[i].indicator = false; all_timeouts[i].timeout_handler = NULL; all_timeouts[i].start_time = 0; all_timeouts[i].fin_time = 0; } all_timeouts_initialized = true; /* Now establish the signal handler */ pqsignal(SIGALRM, handle_sig_alarm); }
/* * slashUsage * * print out help for the backslash commands */ void slashUsage(unsigned short int pager) { FILE *output; char *currdb; currdb = PQdb(pset.db); if (currdb == NULL) currdb = ""; output = PageOutput(90, pager); /* if you add/remove a line here, change the row count above */ fprintf(output, _("General\n")); fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n")); fprintf(output, _(" \\g [FILE] or ; execute query (and send results to file or |pipe)\n")); fprintf(output, _(" \\h [NAME] help on syntax of SQL commands, * for all commands\n")); fprintf(output, _(" \\q quit psql\n")); fprintf(output, "\n"); fprintf(output, _("Query Buffer\n")); fprintf(output, _(" \\e [FILE] edit the query buffer (or file) with external editor\n")); fprintf(output, _(" \\ef [FUNCNAME] edit function definition with external editor\n")); fprintf(output, _(" \\p show the contents of the query buffer\n")); fprintf(output, _(" \\r reset (clear) the query buffer\n")); #ifdef USE_READLINE fprintf(output, _(" \\s [FILE] display history or save it to file\n")); #endif fprintf(output, _(" \\w FILE write query buffer to file\n")); fprintf(output, "\n"); fprintf(output, _("Input/Output\n")); fprintf(output, _(" \\copy ... perform SQL COPY with data stream to the client host\n")); fprintf(output, _(" \\echo [STRING] write string to standard output\n")); fprintf(output, _(" \\i FILE execute commands from file\n")); fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n")); fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n")); fprintf(output, "\n"); fprintf(output, _("Informational\n")); fprintf(output, _(" (options: S = show system objects, + = additional detail)\n")); fprintf(output, _(" \\d[S+] list tables, views, and sequences\n")); fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n")); fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n")); fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n")); fprintf(output, _(" \\dc[S] [PATTERN] list conversions\n")); fprintf(output, _(" \\dC [PATTERN] list casts\n")); fprintf(output, _(" \\dd[S] [PATTERN] show comments on objects\n")); fprintf(output, _(" \\ddp [PATTERN] list default privileges\n")); fprintf(output, _(" \\dD[S] [PATTERN] list domains\n")); fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n")); fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n")); fprintf(output, _(" \\dew[+] [PATTERN] list foreign-data wrappers\n")); fprintf(output, _(" \\df[antw][S+] [PATRN] list [only agg/normal/trigger/window] functions\n")); fprintf(output, _(" \\dF[+] [PATTERN] list text search configurations\n")); fprintf(output, _(" \\dFd[+] [PATTERN] list text search dictionaries\n")); fprintf(output, _(" \\dFp[+] [PATTERN] list text search parsers\n")); fprintf(output, _(" \\dFt[+] [PATTERN] list text search templates\n")); fprintf(output, _(" \\dg[+] [PATTERN] list roles (groups)\n")); fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n")); fprintf(output, _(" \\di[S+] [PATTERN] list indexes\n")); fprintf(output, _(" \\dl list large objects, same as \\lo_list\n")); fprintf(output, _(" \\dn[+] [PATTERN] list schemas\n")); fprintf(output, _(" \\do[S] [PATTERN] list operators\n")); fprintf(output, _(" \\dp [PATTERN] list table, view, and sequence access privileges\n")); fprintf(output, _(" \\dr[S+] [PATTERN] list foreign tables\n")); /* GPDB Only */ fprintf(output, _(" \\drds [PATRN1 [PATRN2]] list per-database role settings\n")); fprintf(output, _(" \\ds[S+] [PATTERN] list sequences\n")); fprintf(output, _(" \\dt[S+] [PATTERN] list tables\n")); fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n")); fprintf(output, _(" \\du[+] [PATTERN] list roles (users)\n")); fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); fprintf(output, _(" \\dE [PATTERN] list external tables\n")); fprintf(output, _(" \\l[+] list all databases\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); fprintf(output, "\n"); fprintf(output, _("Formatting\n")); fprintf(output, _(" \\a toggle between unaligned and aligned output mode\n")); fprintf(output, _(" \\C [STRING] set table title, or unset if none\n")); fprintf(output, _(" \\f [STRING] show or set field separator for unaligned query output\n")); fprintf(output, _(" \\H toggle HTML output mode (currently %s)\n"), ON(pset.popt.topt.format == PRINT_HTML)); fprintf(output, _(" \\pset NAME [VALUE] set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" " numericlocale|recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); fprintf(output, _(" \\x [on|off] toggle expanded output (currently %s)\n"), ON(pset.popt.topt.expanded)); fprintf(output, "\n"); fprintf(output, _("Connection\n")); fprintf(output, _(" \\c[onnect] [DBNAME|- USER|- HOST|- PORT|-]\n" " connect to new database (currently \"%s\")\n"), PQdb(pset.db)); fprintf(output, _(" \\encoding [ENCODING] show or set client encoding\n")); fprintf(output, _(" \\password [USERNAME] securely change the password for a user\n")); fprintf(output, _(" \\conninfo display information about current connection\n")); fprintf(output, "\n"); fprintf(output, _("Operating System\n")); fprintf(output, _(" \\cd [DIR] change the current working directory\n")); fprintf(output, _(" \\timing [on|off] toggle timing of commands (currently %s)\n"), ON(pset.timing)); fprintf(output, _(" \\! [COMMAND] execute command in shell or start interactive shell\n")); fprintf(output, "\n"); fprintf(output, _("Variables\n")); fprintf(output, _(" \\prompt [TEXT] NAME prompt user to set internal variable\n")); fprintf(output, _(" \\set [NAME [VALUE]] set internal variable, or list all if no parameters\n")); fprintf(output, _(" \\unset NAME unset (delete) internal variable\n")); fprintf(output, "\n"); fprintf(output, _("Large Objects\n")); fprintf(output, _(" \\lo_export LOBOID FILE\n" " \\lo_import FILE [COMMENT]\n" " \\lo_list\n" " \\lo_unlink LOBOID large object operations\n")); if (output != stdout) { pclose(output); #ifndef WIN32 pqsignal(SIGPIPE, SIG_DFL); #endif } }
/* Set up signal handlers */ void WalSndSignals(void) { /* Set up signal handlers */ pqsignal(SIGHUP, WalSndSigHupHandler); /* set flag to read config * file */ pqsignal(SIGINT, SIG_IGN); /* not used */ pqsignal(SIGTERM, WalSndShutdownHandler); /* request shutdown */ pqsignal(SIGQUIT, WalSndQuickDieHandler); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, WalSndXLogSendHandler); /* request WAL sending */ pqsignal(SIGUSR2, WalSndLastCycleHandler); /* request a last cycle and * shutdown */ /* Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); }
static void FileRepSubProcess_ConfigureSignals(void) { /* Accept Signals */ /* emergency shutdown */ pqsignal(SIGQUIT, FileRepSubProcess_ImmediateShutdownHandler); /* graceful shutdown */ pqsignal(SIGUSR2, FileRepSubProcess_ShutdownHandler); /* reload configuration file */ pqsignal(SIGHUP, FileRepSubProcess_SigHupHandler); /* data or segment state changed */ pqsignal(SIGUSR1, FileRepSubProcess_FileRepStateHandler); /* Ignore Signals */ pqsignal(SIGTERM, SIG_IGN); pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); /* Use default action */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGINT, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); #ifdef SIGSEGV pqsignal(SIGSEGV, FileRepSubProcess_HandleCrash); #endif #ifdef SIGILL pqsignal(SIGILL, FileRepSubProcess_HandleCrash); #endif #ifdef SIGBUS pqsignal(SIGBUS, FileRepSubProcess_HandleCrash); #endif }
/* * PQprint() * * Format results of a query for printing. * * PQprintOpt is a typedef (structure) that contains * various flags and options. consult libpq-fe.h for * details * * This function should probably be removed sometime since psql * doesn't use it anymore. It is unclear to what extent this is used * by external clients, however. */ void PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po) { int nFields; nFields = PQnfields(res); if (nFields > 0) { /* only print rows with at least 1 field. */ int i, j; int nTups; int *fieldMax = NULL; /* in case we don't use them */ unsigned char *fieldNotNum = NULL; char *border = NULL; char **fields = NULL; const char **fieldNames; int fieldMaxLen = 0; int numFieldName; int fs_len = strlen(po->fieldSep); int total_line_length = 0; int usePipe = 0; char *pagerenv; #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) sigset_t osigset; bool sigpipe_masked = false; bool sigpipe_pending; #endif #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) pqsigfunc oldsigpipehandler = NULL; #endif #ifdef TIOCGWINSZ struct winsize screen_size; #else struct winsize { int ws_row; int ws_col; } screen_size; #endif nTups = PQntuples(res); if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *)))) { fprintf(stderr, libpq_gettext("out of memory\n")); abort(); } if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1))) { fprintf(stderr, libpq_gettext("out of memory\n")); abort(); } if (!(fieldMax = (int *) calloc(nFields, sizeof(int)))) { fprintf(stderr, libpq_gettext("out of memory\n")); abort(); } for (numFieldName = 0; po->fieldName && po->fieldName[numFieldName]; numFieldName++) ; for (j = 0; j < nFields; j++) { int len; const char *s = (j < numFieldName && po->fieldName[j][0]) ? po->fieldName[j] : PQfname(res, j); fieldNames[j] = s; len = s ? strlen(s) : 0; fieldMax[j] = len; len += fs_len; if (len > fieldMaxLen) fieldMaxLen = len; total_line_length += len; } total_line_length += nFields * strlen(po->fieldSep) + 1; if (fout == NULL) fout = stdout; if (po->pager && fout == stdout && isatty(fileno(stdin)) && isatty(fileno(stdout))) { /* * If we think there'll be more than one screen of output, try to * pipe to the pager program. */ #ifdef TIOCGWINSZ if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 || screen_size.ws_col == 0 || screen_size.ws_row == 0) { screen_size.ws_row = 24; screen_size.ws_col = 80; } #else screen_size.ws_row = 24; screen_size.ws_col = 80; #endif pagerenv = getenv("PAGER"); if (pagerenv != NULL && pagerenv[0] != '\0' && !po->html3 && ((po->expanded && nTups * (nFields + 1) >= screen_size.ws_row) || (!po->expanded && nTups * (total_line_length / screen_size.ws_col + 1) * (1 + (po->standard != 0)) >= screen_size.ws_row - (po->header != 0) * (total_line_length / screen_size.ws_col + 1) * 2 - (po->header != 0) * 2 /* row count and newline */ ))) { fout = popen(pagerenv, "w"); if (fout) { usePipe = 1; #ifndef WIN32 #ifdef ENABLE_THREAD_SAFETY if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0) sigpipe_masked = true; #else oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN); #endif /* ENABLE_THREAD_SAFETY */ #endif /* WIN32 */ } else fout = stdout; } } if (!po->expanded && (po->align || po->html3)) { if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *)))) { fprintf(stderr, libpq_gettext("out of memory\n")); abort(); } } else if (po->header && !po->html3) { if (po->expanded) { if (po->align) fprintf(fout, libpq_gettext("%-*s%s Value\n"), fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep); else fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep); } else { int len = 0; for (j = 0; j < nFields; j++) { const char *s = fieldNames[j]; fputs(s, fout); len += strlen(s) + fs_len; if ((j + 1) < nFields) fputs(po->fieldSep, fout); } fputc('\n', fout); for (len -= fs_len; len--; fputc('-', fout)); fputc('\n', fout); } } if (po->expanded && po->html3) { if (po->caption) fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption); else fprintf(fout, "<center><h2>" "Query retrieved %d rows * %d fields" "</h2></center>\n", nTups, nFields); } for (i = 0; i < nTups; i++) { if (po->expanded) { if (po->html3) fprintf(fout, "<table %s><caption align=\"top\">%d</caption>\n", po->tableOpt ? po->tableOpt : "", i); else fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i); } for (j = 0; j < nFields; j++) do_field(po, res, i, j, fs_len, fields, nFields, fieldNames, fieldNotNum, fieldMax, fieldMaxLen, fout); if (po->html3 && po->expanded) fputs("</table>\n", fout); } if (!po->expanded && (po->align || po->html3)) { if (po->html3) { if (po->header) { if (po->caption) fprintf(fout, "<table %s><caption align=\"top\">%s</caption>\n", po->tableOpt ? po->tableOpt : "", po->caption); else fprintf(fout, "<table %s><caption align=\"top\">" "Retrieved %d rows * %d fields" "</caption>\n", po->tableOpt ? po->tableOpt : "", nTups, nFields); } else fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : ""); } if (po->header) border = do_header(fout, po, nFields, fieldMax, fieldNames, fieldNotNum, fs_len, res); for (i = 0; i < nTups; i++) output_row(fout, po, nFields, fields, fieldNotNum, fieldMax, border, i); free(fields); if (border) free(border); } if (po->header && !po->html3) fprintf(fout, "(%d row%s)\n\n", PQntuples(res), (PQntuples(res) == 1) ? "" : "s"); free(fieldMax); free(fieldNotNum); free((void *) fieldNames); if (usePipe) { #ifdef WIN32 _pclose(fout); #else pclose(fout); #ifdef ENABLE_THREAD_SAFETY /* we can't easily verify if EPIPE occurred, so say it did */ if (sigpipe_masked) pq_reset_sigpipe(&osigset, sigpipe_pending, true); #else pqsignal(SIGPIPE, oldsigpipehandler); #endif /* ENABLE_THREAD_SAFETY */ #endif /* WIN32 */ } if (po->html3 && !po->expanded) fputs("</table>\n", fout); } }
void worker_spi_main(Datum main_arg) { int index = DatumGetInt32(main_arg); worktable *table; StringInfoData buf; char name[20]; table = palloc(sizeof(worktable)); sprintf(name, "schema%d", index); table->schema = pstrdup(name); table->name = pstrdup("counted"); /* Establish signal handlers before unblocking signals. */ pqsignal(SIGHUP, worker_spi_sighup); pqsignal(SIGTERM, worker_spi_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* Connect to our database */ BackgroundWorkerInitializeConnection("postgres", NULL); elog(LOG, "%s initialized with %s.%s", MyBgworkerEntry->bgw_name, table->schema, table->name); initialize_worker_spi(table); /* * Quote identifiers passed to us. Note that this must be done after * initialize_worker_spi, because that routine assumes the names are not * quoted. * * Note some memory might be leaked here. */ table->schema = quote_identifier(table->schema); table->name = quote_identifier(table->name); initStringInfo(&buf); appendStringInfo(&buf, "WITH deleted AS (DELETE " "FROM %s.%s " "WHERE type = 'delta' RETURNING value), " "total AS (SELECT coalesce(sum(value), 0) as sum " "FROM deleted) " "UPDATE %s.%s " "SET value = %s.value + total.sum " "FROM total WHERE type = 'total' " "RETURNING %s.value", table->schema, table->name, table->schema, table->name, table->name, table->name); /* * Main loop: do this until the SIGTERM handler tells us to terminate */ while (!got_sigterm) { int ret; int rc; /* * Background workers mustn't call usleep() or any direct equivalent: * instead, they may wait on their process latch, which sleeps as * necessary, but is awakened if postmaster dies. That way the * background process goes away immediately in an emergency. */ rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, worker_spi_naptime * 1000L); ResetLatch(&MyProc->procLatch); /* emergency bailout if postmaster has died */ if (rc & WL_POSTMASTER_DEATH) proc_exit(1); /* * In case of a SIGHUP, just reload the configuration. */ if (got_sighup) { got_sighup = false; ProcessConfigFile(PGC_SIGHUP); } /* * Start a transaction on which we can run queries. Note that each * StartTransactionCommand() call should be preceded by a * SetCurrentStatementStartTimestamp() call, which sets both the time * for the statement we're about the run, and also the transaction * start time. Also, each other query sent to SPI should probably be * preceded by SetCurrentStatementStartTimestamp(), so that statement * start time is always up to date. * * The SPI_connect() call lets us run queries through the SPI manager, * and the PushActiveSnapshot() call creates an "active" snapshot * which is necessary for queries to have MVCC data to work on. * * The pgstat_report_activity() call makes our activity visible * through the pgstat views. */ SetCurrentStatementStartTimestamp(); StartTransactionCommand(); SPI_connect(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_activity(STATE_RUNNING, buf.data); /* We can now execute queries via SPI */ ret = SPI_execute(buf.data, false, 0); if (ret != SPI_OK_UPDATE_RETURNING) elog(FATAL, "cannot select from table %s.%s: error code %d", table->schema, table->name, ret); if (SPI_processed > 0) { bool isnull; int32 val; val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); if (!isnull) elog(LOG, "%s: count in %s.%s is now %d", MyBgworkerEntry->bgw_name, table->schema, table->name, val); } /* * And finish our transaction. */ SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); } proc_exit(1); }
/* * kafka_consume_main * * Main function for Kafka consumers running as background workers */ void kafka_consume_main(Datum arg) { char err_msg[512]; rd_kafka_topic_conf_t *topic_conf; rd_kafka_t *kafka; rd_kafka_topic_t *topic; rd_kafka_message_t **messages; const struct rd_kafka_metadata *meta; struct rd_kafka_metadata_topic topic_meta; rd_kafka_resp_err_t err; bool found; Oid id = (Oid) arg; ListCell *lc; KafkaConsumerProc *proc = hash_search(consumer_procs, &id, HASH_FIND, &found); KafkaConsumer consumer; CopyStmt *copy; int valid_brokers = 0; int i; int my_partitions = 0; if (!found) elog(ERROR, "kafka consumer %d not found", id); pqsignal(SIGTERM, kafka_consume_main_sigterm); #define BACKTRACE_SEGFAULTS #ifdef BACKTRACE_SEGFAULTS pqsignal(SIGSEGV, debug_segfault); #endif /* we're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* give this proc access to the database */ BackgroundWorkerInitializeConnection(NameStr(proc->dbname), NULL); /* load saved consumer state */ StartTransactionCommand(); load_consumer_state(proc->consumer_id, &consumer); copy = get_copy_statement(&consumer); topic_conf = rd_kafka_topic_conf_new(); kafka = rd_kafka_new(RD_KAFKA_CONSUMER, NULL, err_msg, sizeof(err_msg)); rd_kafka_set_logger(kafka, logger); /* * Add all brokers currently in pipeline_kafka_brokers */ if (consumer.brokers == NIL) elog(ERROR, "no valid brokers were found"); foreach(lc, consumer.brokers) valid_brokers += rd_kafka_brokers_add(kafka, lfirst(lc)); if (!valid_brokers) elog(ERROR, "no valid brokers were found"); /* * Set up our topic to read from */ topic = rd_kafka_topic_new(kafka, consumer.topic, topic_conf); err = rd_kafka_metadata(kafka, false, topic, &meta, CONSUMER_TIMEOUT); if (err != RD_KAFKA_RESP_ERR_NO_ERROR) elog(ERROR, "failed to acquire metadata: %s", rd_kafka_err2str(err)); Assert(meta->topic_cnt == 1); topic_meta = meta->topics[0]; load_consumer_offsets(&consumer, &topic_meta, proc->offset); CommitTransactionCommand(); /* * Begin consuming all partitions that this process is responsible for */ for (i = 0; i < topic_meta.partition_cnt; i++) { int partition = topic_meta.partitions[i].id; Assert(partition <= consumer.num_partitions); if (partition % consumer.parallelism != proc->partition_group) continue; elog(LOG, "[kafka consumer] %s <- %s consuming partition %d from offset %ld", consumer.rel->relname, consumer.topic, partition, consumer.offsets[partition]); if (rd_kafka_consume_start(topic, partition, consumer.offsets[partition]) == -1) elog(ERROR, "failed to start consuming: %s", rd_kafka_err2str(rd_kafka_errno2err(errno))); my_partitions++; } /* * No point doing anything if we don't have any partitions assigned to us */ if (my_partitions == 0) { elog(LOG, "[kafka consumer] %s <- %s consumer %d doesn't have any partitions to read from", consumer.rel->relname, consumer.topic, MyProcPid); goto done; } messages = palloc0(sizeof(rd_kafka_message_t) * consumer.batch_size); /* * Consume messages until we are terminated */ while (!got_sigterm) { ssize_t num_consumed; int i; int messages_buffered = 0; int partition; StringInfoData buf; bool xact = false; for (partition = 0; partition < consumer.num_partitions; partition++) { if (partition % consumer.parallelism != proc->partition_group) continue; num_consumed = rd_kafka_consume_batch(topic, partition, CONSUMER_TIMEOUT, messages, consumer.batch_size); if (num_consumed <= 0) continue; if (!xact) { StartTransactionCommand(); xact = true; } initStringInfo(&buf); for (i = 0; i < num_consumed; i++) { if (messages[i]->payload != NULL) { appendBinaryStringInfo(&buf, messages[i]->payload, messages[i]->len); if (buf.len > 0 && buf.data[buf.len - 1] != '\n') appendStringInfoChar(&buf, '\n'); messages_buffered++; } consumer.offsets[partition] = messages[i]->offset; rd_kafka_message_destroy(messages[i]); } } if (!xact) { pg_usleep(1 * 1000); continue; } /* we don't want to die in the event of any errors */ PG_TRY(); { if (messages_buffered) execute_copy(copy, &buf); } PG_CATCH(); { elog(LOG, "[kafka consumer] %s <- %s failed to process batch, dropped %d message%s:", consumer.rel->relname, consumer.topic, (int) num_consumed, (num_consumed == 1 ? "" : "s")); EmitErrorReport(); FlushErrorState(); AbortCurrentTransaction(); xact = false; } PG_END_TRY(); if (!xact) StartTransactionCommand(); if (messages_buffered) save_consumer_state(&consumer, proc->partition_group); CommitTransactionCommand(); } done: hash_search(consumer_procs, &id, HASH_REMOVE, NULL); rd_kafka_topic_destroy(topic); rd_kafka_destroy(kafka); rd_kafka_wait_destroyed(CONSUMER_TIMEOUT); }
/* * AutoVacMain */ NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]) { ListCell *cell; List *dblist; autovac_dbase *db; TransactionId xidForceLimit; bool for_xid_wrap; sigjmp_buf local_sigjmp_buf; /* we are a postmaster subprocess now */ IsUnderPostmaster = true; am_autovacuum = true; /* MPP-4990: Autovacuum always runs as utility-mode */ Gp_role = GP_ROLE_UTILITY; /* reset MyProcPid */ MyProcPid = getpid(); /* record Start Time for logging */ MyStartTime = time(NULL); /* Identify myself via ps */ init_ps_display("autovacuum process", "", "", ""); SetProcessingMode(InitProcessing); /* * If possible, make this process a group leader, so that the postmaster * can signal any child processes too. (autovacuum probably never has * any child processes, but for consistency we make all postmaster * child processes do this.) */ #ifdef HAVE_SETSID if (setsid() < 0) elog(FATAL, "setsid() failed: %m"); #endif /* * Set up signal handlers. We operate on databases much like a regular * backend, so we use the same signal handling. See equivalent code in * tcop/postgres.c. * * Currently, we don't pay attention to postgresql.conf changes that * happen during a single daemon iteration, so we can ignore SIGHUP. */ pqsignal(SIGHUP, SIG_IGN); /* * SIGINT is used to signal cancelling the current table's vacuum; SIGTERM * means abort and exit cleanly, and SIGQUIT means abandon ship. */ pqsignal(SIGINT, StatementCancelHandler); pqsignal(SIGTERM, die); pqsignal(SIGQUIT, quickdie); pqsignal(SIGALRM, handle_sig_alarm); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, procsignal_sigusr1_handler); /* We don't listen for async notifies */ pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); pqsignal(SIGCHLD, SIG_DFL); /* Early initialization */ BaseInit(); /* * Create a per-backend PGPROC struct in shared memory, except in the * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do * this before we can use LWLocks (and in the EXEC_BACKEND case we already * had to do some stuff with LWLocks). */ #ifndef EXEC_BACKEND InitProcess(); #endif /* * If an exception is encountered, processing resumes here. * * See notes in postgres.c about the design of this coding. */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* Prevents interrupts while cleaning up */ HOLD_INTERRUPTS(); /* Report the error to the server log */ EmitErrorReport(); /* * We can now go away. Note that because we called InitProcess, a * callback was registered to do ProcKill, which will clean up * necessary state. */ proc_exit(0); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; PG_SETMASK(&UnBlockSig); /* * Force zero_damaged_pages OFF in the autovac process, even if it is set * in postgresql.conf. We don't really want such a dangerous option being * applied non-interactively. */ SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE); /* Get a list of databases */ dblist = autovac_get_database_list(); /* * Determine the oldest datfrozenxid/relfrozenxid that we will allow * to pass without forcing a vacuum. (This limit can be tightened for * particular tables, but not loosened.) */ recentXid = ReadNewTransactionId(); xidForceLimit = recentXid - autovacuum_freeze_max_age; /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */ if (xidForceLimit < FirstNormalTransactionId) xidForceLimit -= FirstNormalTransactionId; /* * Choose a database to connect to. We pick the database that was least * recently auto-vacuumed, or one that needs vacuuming to prevent Xid * wraparound-related data loss. If any db at risk of wraparound is * found, we pick the one with oldest datfrozenxid, * independently of autovacuum times. * * Note that a database with no stats entry is not considered, except for * Xid wraparound purposes. The theory is that if no one has ever * connected to it since the stats were last initialized, it doesn't need * vacuuming. * * XXX This could be improved if we had more info about whether it needs * vacuuming before connecting to it. Perhaps look through the pgstats * data for the database's tables? One idea is to keep track of the * number of new and dead tuples per database in pgstats. However it * isn't clear how to construct a metric that measures that and not cause * starvation for less busy databases. */ db = NULL; for_xid_wrap = false; foreach(cell, dblist) { autovac_dbase *tmp = lfirst(cell); /* Find pgstat entry if any */ tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid); /* Check to see if this one is at risk of wraparound */ if (TransactionIdPrecedes(tmp->frozenxid, xidForceLimit)) { if (db == NULL || TransactionIdPrecedes(tmp->frozenxid, db->frozenxid)) db = tmp; for_xid_wrap = true; continue; } else if (for_xid_wrap) continue; /* ignore not-at-risk DBs */ /* * Otherwise, skip a database with no pgstat entry; it means it * hasn't seen any activity. */ if (!tmp->entry) continue; /* * Remember the db with oldest autovac time. (If we are here, * both tmp->entry and db->entry must be non-null.) */ if (db == NULL || tmp->entry->last_autovac_time < db->entry->last_autovac_time) db = tmp; }
static Datum internalCallHandler(bool trusted, PG_FUNCTION_ARGS) { Invocation ctx; Datum retval = 0; #ifdef USE_PLJAVA_SIGHANDLERS /* * Substitute SIGUSR1 handler CatchupInterruptHandler with new local * handler PLCatchupInterruptHandler, which will strictly let main * thread of GPDB to process the signal rather than any other threads. * * Resolving possible lwlock releasing crash caused by SIGUSR1. * MPP-23735 */ if(!s_handlerSubstituted) { s_mainThreadIdForHandler = pthread_self(); s_oldHandlerFunc = pqsignal(SIGUSR1, PLCatchupInterruptHandler); s_handlerSubstituted = true; } #endif /* * Just in case it could be helpful in offering diagnostics later, hang * on to an Oid that is known to refer to PL/Java (because it got here). * It's cheap, and can be followed back to the right language and * handler function entries later if needed. */ *(trusted ? &pljavaTrustedOid : &pljavaUntrustedOid) = fcinfo->flinfo->fn_oid; if ( IS_COMPLETE != initstage ) { initsequencer( initstage, false); /* Force initial setting */ s_currentTrust = !trusted; } Invocation_pushInvocation(&ctx, trusted); PG_TRY(); { Function function = Function_getFunction(fcinfo); if(CALLED_AS_TRIGGER(fcinfo)) { /* Called as a trigger procedure */ retval = Function_invokeTrigger(function, fcinfo); } else { /* Called as a function */ retval = Function_invoke(function, fcinfo); } Invocation_popInvocation(false); } PG_CATCH(); { Invocation_popInvocation(true); PG_RE_THROW(); } PG_END_TRY(); return retval; }
/*------------ MAIN ----------------------------------------*/ int main(int argc, char **argv) { int c; progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_standby (PostgreSQL) " PG_VERSION); exit(0); } } #ifndef WIN32 /* * You can send SIGUSR1 to trigger failover. * * Postmaster uses SIGQUIT to request immediate shutdown. The default * action is to core dump, but we don't want that, so trap it and commit * suicide without core dump. * * We used to use SIGINT and SIGQUIT to trigger failover, but that turned * out to be a bad idea because postmaster uses SIGQUIT to request * immediate shutdown. We still trap SIGINT, but that may change in a * future release. * * There's no way to trigger failover via signal on Windows. */ (void) pqsignal(SIGUSR1, sighandler); (void) pqsignal(SIGINT, sighandler); /* deprecated, use SIGUSR1 */ (void) pqsignal(SIGQUIT, sigquit_handler); #endif while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1) { switch (c) { case 'c': /* Use copy */ restoreCommandType = RESTORE_COMMAND_COPY; break; case 'd': /* Debug mode */ debug = true; break; case 'k': /* keepfiles */ keepfiles = atoi(optarg); if (keepfiles < 0) { fprintf(stderr, "%s: -k keepfiles must be >= 0\n", progname); exit(2); } break; case 'l': /* Use link */ /* * Link feature disabled, possibly permanently. Linking causes * a problem after recovery ends that is not currently * resolved by PostgreSQL. 25 Jun 2009 */ #ifdef NOT_USED restoreCommandType = RESTORE_COMMAND_LINK; #endif break; case 'r': /* Retries */ maxretries = atoi(optarg); if (maxretries < 0) { fprintf(stderr, "%s: -r maxretries must be >= 0\n", progname); exit(2); } break; case 's': /* Sleep time */ sleeptime = atoi(optarg); if (sleeptime <= 0 || sleeptime > 60) { fprintf(stderr, "%s: -s sleeptime incorrectly set\n", progname); exit(2); } break; case 't': /* Trigger file */ triggerPath = strdup(optarg); break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg); if (maxwaittime < 0) { fprintf(stderr, "%s: -w maxwaittime incorrectly set\n", progname); exit(2); } break; default: fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); break; } } /* * Parameter checking - after checking to see if trigger file present */ if (argc == 1) { fprintf(stderr, "%s: not enough command-line arguments\n", progname); exit(2); } /* * We will go to the archiveLocation to get nextWALFileName. * nextWALFileName may not exist yet, which would not be an error, so we * separate the archiveLocation and nextWALFileName so we can check * separately whether archiveLocation exists, if not that is an error */ if (optind < argc) { archiveLocation = argv[optind]; optind++; } else { fprintf(stderr, "%s: must specify archive location\n", progname); fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); } if (optind < argc) { nextWALFileName = argv[optind]; optind++; } else { fprintf(stderr, "%s: must specify WAL file name as second non-option argument (use \"%%f\")\n", progname); fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); } if (optind < argc) { xlogFilePath = argv[optind]; optind++; } else { fprintf(stderr, "%s: must specify xlog destination as third non-option argument (use \"%%p\")\n", progname); fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); } if (optind < argc) { restartWALFileName = argv[optind]; optind++; } CustomizableInitialize(); need_cleanup = SetWALFileNameForCleanup(); if (debug) { fprintf(stderr, "Trigger file: %s\n", triggerPath ? triggerPath : "<not set>"); fprintf(stderr, "Waiting for WAL file: %s\n", nextWALFileName); fprintf(stderr, "WAL file path: %s\n", WALFilePath); fprintf(stderr, "Restoring to: %s\n", xlogFilePath); fprintf(stderr, "Sleep interval: %d second%s\n", sleeptime, (sleeptime > 1 ? "s" : " ")); fprintf(stderr, "Max wait interval: %d %s\n", maxwaittime, (maxwaittime > 0 ? "seconds" : "forever")); fprintf(stderr, "Command for restore: %s\n", restoreCommand); fprintf(stderr, "Keep archive history: "); if (need_cleanup) fprintf(stderr, "%s and later\n", exclusiveCleanupFileName); else fprintf(stderr, "no cleanup required\n"); fflush(stderr); } /* * Check for initial history file: always the first file to be requested * It's OK if the file isn't there - all other files need to wait */ if (strlen(nextWALFileName) > 8 && strspn(nextWALFileName, "0123456789ABCDEF") == 8 && strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".history"), ".history") == 0) { nextWALFileType = XLOG_HISTORY; if (RestoreWALFileForRecovery()) exit(0); else { if (debug) { fprintf(stderr, "history file not found\n"); fflush(stderr); } exit(1); } } /* * Main wait loop */ for (;;) { /* Check for trigger file or signal first */ CheckForExternalTrigger(); #ifndef WIN32 if (signaled) { Failover = FastFailover; if (debug) { fprintf(stderr, "signaled to exit: fast failover\n"); fflush(stderr); } } #endif /* * Check for fast failover immediately, before checking if the * requested WAL file is available */ if (Failover == FastFailover) exit(1); if (CustomizableNextWALFileReady()) { /* * Once we have restored this file successfully we can remove some * prior WAL files. If this restore fails we musn't remove any * file because some of them will be requested again immediately * after the failed restore, or when we restart recovery. */ if (RestoreWALFileForRecovery()) { if (need_cleanup) CustomizableCleanupPriorWALFiles(); exit(0); } else { /* Something went wrong in copying the file */ exit(1); } } /* Check for smart failover if the next WAL file was not available */ if (Failover == SmartFailover) exit(1); if (sleeptime <= 60) pg_usleep(sleeptime * 1000000L); waittime += sleeptime; if (waittime >= maxwaittime && maxwaittime > 0) { Failover = FastFailover; if (debug) { fprintf(stderr, "Timed out after %d seconds: fast failover\n", waittime); fflush(stderr); } } if (debug) { fprintf(stderr, "WAL file not present yet."); if (triggerPath) fprintf(stderr, " Checking for trigger file..."); fprintf(stderr, "\n"); fflush(stderr); } } }
/* We don't want SIGQUIT to core dump */ static void sigquit_handler(int sig) { pqsignal(SIGINT, SIG_DFL); kill(getpid(), SIGINT); }
/* * Main entry point for checkpointer process * * This is invoked from AuxiliaryProcessMain, which has already created the * basic execution environment, but not enabled signals yet. */ void CheckpointerMain(void) { sigjmp_buf local_sigjmp_buf; MemoryContext checkpointer_context; CheckpointerShmem->checkpointer_pid = MyProcPid; /* * Properly accept or ignore signals the postmaster might send us * * Note: we deliberately ignore SIGTERM, because during a standard Unix * system shutdown cycle, init will SIGTERM all processes at once. We * want to wait for the backends to exit, whereupon the postmaster will * tell us it's okay to shut down (via SIGUSR2). */ pqsignal(SIGHUP, ChkptSigHupHandler); /* set flag to read config * file */ pqsignal(SIGINT, ReqCheckpointHandler); /* request checkpoint */ pqsignal(SIGTERM, SIG_IGN); /* ignore SIGTERM */ pqsignal(SIGQUIT, chkpt_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, chkpt_sigusr1_handler); pqsignal(SIGUSR2, ReqShutdownHandler); /* request shutdown */ /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); /* We allow SIGQUIT (quickdie) at all times */ sigdelset(&BlockSig, SIGQUIT); /* * Initialize so that first time-driven event happens at the correct time. */ last_checkpoint_time = last_xlog_switch_time = (pg_time_t) time(NULL); /* * Create a resource owner to keep track of our resources (currently only * buffer pins). */ CurrentResourceOwner = ResourceOwnerCreate(NULL, "Checkpointer"); /* * Create a memory context that we will do all our work in. We do this so * that we can reset the context during error recovery and thereby avoid * possible memory leaks. Formerly this code just ran in * TopMemoryContext, but resetting that would be a really bad idea. */ checkpointer_context = AllocSetContextCreate(TopMemoryContext, "Checkpointer", ALLOCSET_DEFAULT_SIZES); MemoryContextSwitchTo(checkpointer_context); /* * If an exception is encountered, processing resumes here. * * See notes in postgres.c about the design of this coding. */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; /* Prevent interrupts while cleaning up */ HOLD_INTERRUPTS(); /* Report the error to the server log */ EmitErrorReport(); /* * These operations are really just a minimal subset of * AbortTransaction(). We don't have very many resources to worry * about in checkpointer, but we do have LWLocks, buffers, and temp * files. */ LWLockReleaseAll(); ConditionVariableCancelSleep(); pgstat_report_wait_end(); AbortBufferIO(); UnlockBuffers(); /* buffer pins are released here: */ ResourceOwnerRelease(CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); /* we needn't bother with the other ResourceOwnerRelease phases */ AtEOXact_Buffers(false); AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_HashTables(false); /* Warn any waiting backends that the checkpoint failed. */ if (ckpt_active) { SpinLockAcquire(&CheckpointerShmem->ckpt_lck); CheckpointerShmem->ckpt_failed++; CheckpointerShmem->ckpt_done = CheckpointerShmem->ckpt_started; SpinLockRelease(&CheckpointerShmem->ckpt_lck); ckpt_active = false; } /* * Now return to normal top-level context and clear ErrorContext for * next time. */ MemoryContextSwitchTo(checkpointer_context); FlushErrorState(); /* Flush any leaked data in the top-level context */ MemoryContextResetAndDeleteChildren(checkpointer_context); /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); /* * Sleep at least 1 second after any error. A write error is likely * to be repeated, and we don't want to be filling the error logs as * fast as we can. */ pg_usleep(1000000L); /* * Close all open files after any error. This is helpful on Windows, * where holding deleted files open causes various strange errors. * It's not clear we need it elsewhere, but shouldn't hurt. */ smgrcloseall(); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; /* * Unblock signals (they were blocked when the postmaster forked us) */ PG_SETMASK(&UnBlockSig); /* * Ensure all shared memory values are set correctly for the config. Doing * this here ensures no race conditions from other concurrent updaters. */ UpdateSharedMemoryConfig(); /* * Advertise our latch that backends can use to wake us up while we're * sleeping. */ ProcGlobal->checkpointerLatch = &MyProc->procLatch; /* * Loop forever */ for (;;) { bool do_checkpoint = false; int flags = 0; pg_time_t now; int elapsed_secs; int cur_timeout; int rc; /* Clear any already-pending wakeups */ ResetLatch(MyLatch); /* * Process any requests or signals received recently. */ AbsorbFsyncRequests(); if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); /* * Checkpointer is the last process to shut down, so we ask it to * hold the keys for a range of other tasks required most of which * have nothing to do with checkpointing at all. * * For various reasons, some config values can change dynamically * so the primary copy of them is held in shared memory to make * sure all backends see the same value. We make Checkpointer * responsible for updating the shared memory copy if the * parameter setting changes because of SIGHUP. */ UpdateSharedMemoryConfig(); } if (checkpoint_requested) { checkpoint_requested = false; do_checkpoint = true; BgWriterStats.m_requested_checkpoints++; } if (shutdown_requested) { /* * From here on, elog(ERROR) should end with exit(1), not send * control back to the sigsetjmp block above */ ExitOnAnyError = true; /* Close down the database */ ShutdownXLOG(0, 0); /* Normal exit from the checkpointer is here */ proc_exit(0); /* done */ } /* * Force a checkpoint if too much time has elapsed since the last one. * Note that we count a timed checkpoint in stats only when this * occurs without an external request, but we set the CAUSE_TIME flag * bit even if there is also an external request. */ now = (pg_time_t) time(NULL); elapsed_secs = now - last_checkpoint_time; if (elapsed_secs >= CheckPointTimeout) { if (!do_checkpoint) BgWriterStats.m_timed_checkpoints++; do_checkpoint = true; flags |= CHECKPOINT_CAUSE_TIME; } /* * Do a checkpoint if requested. */ if (do_checkpoint) { bool ckpt_performed = false; bool do_restartpoint; /* * Check if we should perform a checkpoint or a restartpoint. As a * side-effect, RecoveryInProgress() initializes TimeLineID if * it's not set yet. */ do_restartpoint = RecoveryInProgress(); /* * Atomically fetch the request flags to figure out what kind of a * checkpoint we should perform, and increase the started-counter * to acknowledge that we've started a new checkpoint. */ SpinLockAcquire(&CheckpointerShmem->ckpt_lck); flags |= CheckpointerShmem->ckpt_flags; CheckpointerShmem->ckpt_flags = 0; CheckpointerShmem->ckpt_started++; SpinLockRelease(&CheckpointerShmem->ckpt_lck); /* * The end-of-recovery checkpoint is a real checkpoint that's * performed while we're still in recovery. */ if (flags & CHECKPOINT_END_OF_RECOVERY) do_restartpoint = false; /* * We will warn if (a) too soon since last checkpoint (whatever * caused it) and (b) somebody set the CHECKPOINT_CAUSE_XLOG flag * since the last checkpoint start. Note in particular that this * implementation will not generate warnings caused by * CheckPointTimeout < CheckPointWarning. */ if (!do_restartpoint && (flags & CHECKPOINT_CAUSE_XLOG) && elapsed_secs < CheckPointWarning) ereport(LOG, (errmsg_plural("checkpoints are occurring too frequently (%d second apart)", "checkpoints are occurring too frequently (%d seconds apart)", elapsed_secs, elapsed_secs), errhint("Consider increasing the configuration parameter \"max_wal_size\"."))); /* * Initialize checkpointer-private variables used during * checkpoint. */ ckpt_active = true; if (do_restartpoint) ckpt_start_recptr = GetXLogReplayRecPtr(NULL); else ckpt_start_recptr = GetInsertRecPtr(); ckpt_start_time = now; ckpt_cached_elapsed = 0; /* * Do the checkpoint. */ if (!do_restartpoint) { CreateCheckPoint(flags); ckpt_performed = true; } else ckpt_performed = CreateRestartPoint(flags); /* * After any checkpoint, close all smgr files. This is so we * won't hang onto smgr references to deleted files indefinitely. */ smgrcloseall(); /* * Indicate checkpoint completion to any waiting backends. */ SpinLockAcquire(&CheckpointerShmem->ckpt_lck); CheckpointerShmem->ckpt_done = CheckpointerShmem->ckpt_started; SpinLockRelease(&CheckpointerShmem->ckpt_lck); if (ckpt_performed) { /* * Note we record the checkpoint start time not end time as * last_checkpoint_time. This is so that time-driven * checkpoints happen at a predictable spacing. */ last_checkpoint_time = now; } else { /* * We were not able to perform the restartpoint (checkpoints * throw an ERROR in case of error). Most likely because we * have not received any new checkpoint WAL records since the * last restartpoint. Try again in 15 s. */ last_checkpoint_time = now - CheckPointTimeout + 15; } ckpt_active = false; } /* Check for archive_timeout and switch xlog files if necessary. */ CheckArchiveTimeout(); /* * Send off activity statistics to the stats collector. (The reason * why we re-use bgwriter-related code for this is that the bgwriter * and checkpointer used to be just one process. It's probably not * worth the trouble to split the stats support into two independent * stats message types.) */ pgstat_send_bgwriter(); /* * Sleep until we are signaled or it's time for another checkpoint or * xlog file switch. */ now = (pg_time_t) time(NULL); elapsed_secs = now - last_checkpoint_time; if (elapsed_secs >= CheckPointTimeout) continue; /* no sleep for us ... */ cur_timeout = CheckPointTimeout - elapsed_secs; if (XLogArchiveTimeout > 0 && !RecoveryInProgress()) { elapsed_secs = now - last_xlog_switch_time; if (elapsed_secs >= XLogArchiveTimeout) continue; /* no sleep for us ... */ cur_timeout = Min(cur_timeout, XLogArchiveTimeout - elapsed_secs); } rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, cur_timeout * 1000L /* convert to ms */, WAIT_EVENT_CHECKPOINTER_MAIN); /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. */ if (rc & WL_POSTMASTER_DEATH) exit(1); } }
void setup_cancel_handler(void) { pqsignal(SIGINT, handle_sigint); }
/* * Main entry point for syslogger process * argc/argv parameters are valid only in EXEC_BACKEND case. */ NON_EXEC_STATIC void SysLoggerMain(int argc, char *argv[]) { #ifndef WIN32 char logbuffer[READ_BUF_SIZE]; int bytes_in_logbuffer = 0; #endif char *currentLogDir; char *currentLogFilename; int currentLogRotationAge; pg_time_t now; IsUnderPostmaster = true; /* we are a postmaster subprocess now */ MyProcPid = getpid(); /* reset MyProcPid */ MyStartTime = time(NULL); /* set our start time in case we call elog */ now = MyStartTime; #ifdef EXEC_BACKEND syslogger_parseArgs(argc, argv); #endif /* EXEC_BACKEND */ am_syslogger = true; init_ps_display("logger process", "", "", ""); /* * If we restarted, our stderr is already redirected into our own input * pipe. This is of course pretty useless, not to mention that it * interferes with detecting pipe EOF. Point stderr to /dev/null. This * assumes that all interesting messages generated in the syslogger will * come through elog.c and will be sent to write_syslogger_file. */ if (redirection_done) { int fd = open(DEVNULL, O_WRONLY, 0); /* * The closes might look redundant, but they are not: we want to be * darn sure the pipe gets closed even if the open failed. We can * survive running with stderr pointing nowhere, but we can't afford * to have extra pipe input descriptors hanging around. */ close(fileno(stdout)); close(fileno(stderr)); if (fd != -1) { dup2(fd, fileno(stdout)); dup2(fd, fileno(stderr)); close(fd); } } /* * Syslogger's own stderr can't be the syslogPipe, so set it back to text * mode if we didn't just close it. (It was set to binary in * SubPostmasterMain). */ #ifdef WIN32 else _setmode(_fileno(stderr), _O_TEXT); #endif /* * Also close our copy of the write end of the pipe. This is needed to * ensure we can detect pipe EOF correctly. (But note that in the restart * case, the postmaster already did this.) */ #ifndef WIN32 if (syslogPipe[1] >= 0) close(syslogPipe[1]); syslogPipe[1] = -1; #else if (syslogPipe[1]) CloseHandle(syslogPipe[1]); syslogPipe[1] = 0; #endif /* * If possible, make this process a group leader, so that the postmaster * can signal any child processes too. (syslogger probably never has any * child processes, but for consistency we make all postmaster child * processes do this.) */ #ifdef HAVE_SETSID if (setsid() < 0) elog(FATAL, "setsid() failed: %m"); #endif InitializeLatchSupport(); /* needed for latch waits */ /* Initialize private latch for use by signal handlers */ InitLatch(&sysLoggerLatch); /* * Properly accept or ignore signals the postmaster might send us * * Note: we ignore all termination signals, and instead exit only when all * upstream processes are gone, to ensure we don't miss any dying gasps of * broken backends... */ pqsignal(SIGHUP, sigHupHandler); /* set flag to read config file */ pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, SIG_IGN); pqsignal(SIGQUIT, SIG_IGN); pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, sigUsr1Handler); /* request log rotation */ pqsignal(SIGUSR2, SIG_IGN); /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); PG_SETMASK(&UnBlockSig); #ifdef WIN32 /* Fire up separate data transfer thread */ InitializeCriticalSection(&sysloggerSection); EnterCriticalSection(&sysloggerSection); threadHandle = (HANDLE) _beginthreadex(NULL, 0, pipeThread, NULL, 0, NULL); if (threadHandle == 0) elog(FATAL, "could not create syslogger data transfer thread: %m"); #endif /* WIN32 */ /* * Remember active logfile's name. We recompute this from the reference * time because passing down just the pg_time_t is a lot cheaper than * passing a whole file path in the EXEC_BACKEND case. */ last_file_name = logfile_getname(first_syslogger_file_time, NULL); /* remember active logfile parameters */ currentLogDir = pstrdup(Log_directory); currentLogFilename = pstrdup(Log_filename); currentLogRotationAge = Log_RotationAge; /* set next planned rotation time */ set_next_rotation_time(); /* main worker loop */ for (;;) { bool time_based_rotation = false; int size_rotation_for = 0; long cur_timeout; int cur_flags; #ifndef WIN32 int rc; #endif /* Clear any already-pending wakeups */ ResetLatch(&sysLoggerLatch); /* * Process any requests or signals received recently. */ if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); /* * Check if the log directory or filename pattern changed in * postgresql.conf. If so, force rotation to make sure we're * writing the logfiles in the right place. */ if (strcmp(Log_directory, currentLogDir) != 0) { pfree(currentLogDir); currentLogDir = pstrdup(Log_directory); rotation_requested = true; /* * Also, create new directory if not present; ignore errors */ mkdir(Log_directory, S_IRWXU); } if (strcmp(Log_filename, currentLogFilename) != 0) { pfree(currentLogFilename); currentLogFilename = pstrdup(Log_filename); rotation_requested = true; } /* * If rotation time parameter changed, reset next rotation time, * but don't immediately force a rotation. */ if (currentLogRotationAge != Log_RotationAge) { currentLogRotationAge = Log_RotationAge; set_next_rotation_time(); } /* * If we had a rotation-disabling failure, re-enable rotation * attempts after SIGHUP, and force one immediately. */ if (rotation_disabled) { rotation_disabled = false; rotation_requested = true; } } if (Log_RotationAge > 0 && !rotation_disabled) { /* Do a logfile rotation if it's time */ now = (pg_time_t) time(NULL); if (now >= next_rotation_time) rotation_requested = time_based_rotation = true; } if (!rotation_requested && Log_RotationSize > 0 && !rotation_disabled) { /* Do a rotation if file is too big */ if (ftell(syslogFile) >= Log_RotationSize * 1024L) { rotation_requested = true; size_rotation_for |= LOG_DESTINATION_STDERR; } if (csvlogFile != NULL && ftell(csvlogFile) >= Log_RotationSize * 1024L) { rotation_requested = true; size_rotation_for |= LOG_DESTINATION_CSVLOG; } } if (rotation_requested) { /* * Force rotation when both values are zero. It means the request * was sent by pg_rotate_logfile. */ if (!time_based_rotation && size_rotation_for == 0) size_rotation_for = LOG_DESTINATION_STDERR | LOG_DESTINATION_CSVLOG; logfile_rotate(time_based_rotation, size_rotation_for); } /* * Calculate time till next time-based rotation, so that we don't * sleep longer than that. We assume the value of "now" obtained * above is still close enough. Note we can't make this calculation * until after calling logfile_rotate(), since it will advance * next_rotation_time. * * Also note that we need to beware of overflow in calculation of the * timeout: with large settings of Log_RotationAge, next_rotation_time * could be more than INT_MAX msec in the future. In that case we'll * wait no more than INT_MAX msec, and try again. */ if (Log_RotationAge > 0 && !rotation_disabled) { pg_time_t delay; delay = next_rotation_time - now; if (delay > 0) { if (delay > INT_MAX / 1000) delay = INT_MAX / 1000; cur_timeout = delay * 1000L; /* msec */ } else cur_timeout = 0; cur_flags = WL_TIMEOUT; } else { cur_timeout = -1L; cur_flags = 0; } /* * Sleep until there's something to do */ #ifndef WIN32 rc = WaitLatchOrSocket(&sysLoggerLatch, WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags, syslogPipe[0], cur_timeout); if (rc & WL_SOCKET_READABLE) { int bytesRead; bytesRead = read(syslogPipe[0], logbuffer + bytes_in_logbuffer, sizeof(logbuffer) - bytes_in_logbuffer); if (bytesRead < 0) { if (errno != EINTR) ereport(LOG, (errcode_for_socket_access(), errmsg("could not read from logger pipe: %m"))); } else if (bytesRead > 0) { bytes_in_logbuffer += bytesRead; process_pipe_input(logbuffer, &bytes_in_logbuffer); continue; } else { /* * Zero bytes read when select() is saying read-ready means * EOF on the pipe: that is, there are no longer any processes * with the pipe write end open. Therefore, the postmaster * and all backends are shut down, and we are done. */ pipe_eof_seen = true; /* if there's any data left then force it out now */ flush_pipe_input(logbuffer, &bytes_in_logbuffer); } } #else /* WIN32 */ /* * On Windows we leave it to a separate thread to transfer data and * detect pipe EOF. The main thread just wakes up to handle SIGHUP * and rotation conditions. * * Server code isn't generally thread-safe, so we ensure that only one * of the threads is active at a time by entering the critical section * whenever we're not sleeping. */ LeaveCriticalSection(&sysloggerSection); (void) WaitLatch(&sysLoggerLatch, WL_LATCH_SET | cur_flags, cur_timeout); EnterCriticalSection(&sysloggerSection); #endif /* WIN32 */ if (pipe_eof_seen) { /* * seeing this message on the real stderr is annoying - so we make * it DEBUG1 to suppress in normal use. */ ereport(DEBUG1, (errmsg("logger shutting down"))); /* * Normal exit from the syslogger is here. Note that we * deliberately do not close syslogFile before exiting; this is to * allow for the possibility of elog messages being generated * inside proc_exit. Regular exit() will take care of flushing * and closing stdio channels. */ proc_exit(0); } } }
/* * helpSQL -- help with SQL commands * * Note: we assume caller removed any trailing spaces in "topic". */ void helpSQL(const char *topic, unsigned short int pager) { #define VALUE_OR_NULL(a) ((a) ? (a) : "") if (!topic || strlen(topic) == 0) { /* Print all the available command names */ int screen_width; int ncolumns; int nrows; FILE *output; int i; int j; #ifdef TIOCGWINSZ struct winsize screen_size; if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1) screen_width = 80; /* ioctl failed, assume 80 */ else screen_width = screen_size.ws_col; #else screen_width = 80; /* default assumption */ #endif ncolumns = (screen_width - 3) / (QL_MAX_CMD_LEN + 1); ncolumns = Max(ncolumns, 1); nrows = (QL_HELP_COUNT + (ncolumns - 1)) / ncolumns; output = PageOutput(nrows + 1, pager); fputs(_("Available help:\n"), output); for (i = 0; i < nrows; i++) { fprintf(output, " "); for (j = 0; j < ncolumns - 1; j++) fprintf(output, "%-*s", QL_MAX_CMD_LEN + 1, VALUE_OR_NULL(QL_HELP[i + j * nrows].cmd)); if (i + j * nrows < QL_HELP_COUNT) fprintf(output, "%s", VALUE_OR_NULL(QL_HELP[i + j * nrows].cmd)); fputc('\n', output); } /* Only close if we used the pager */ if (output != stdout) { pclose(output); #ifndef WIN32 pqsignal(SIGPIPE, SIG_DFL); #endif } } else { int i, j, x = 0; bool help_found = false; FILE *output; size_t len, wordlen; int nl_count = 0; /* * We first try exact match, then first + second words, then first * word only. */ len = strlen(topic); for (x = 1; x <= 3; x++) { if (x > 1) /* Nothing on first pass - try the opening * word(s) */ { wordlen = j = 1; while (topic[j] != ' ' && j++ < len) wordlen++; if (x == 2) { j++; while (topic[j] != ' ' && j++ <= len) wordlen++; } if (wordlen >= len) /* Don't try again if the same word */ { output = PageOutput(nl_count, pager); break; } len = wordlen; } /* Count newlines for pager */ for (i = 0; QL_HELP[i].cmd; i++) { if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 || strcmp(topic, "*") == 0) { nl_count += 5 + QL_HELP[i].nl_count; /* If we have an exact match, exit. Fixes \h SELECT */ if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0) break; } } output = PageOutput(nl_count, pager); for (i = 0; QL_HELP[i].cmd; i++) { if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 || strcmp(topic, "*") == 0) { PQExpBufferData buffer; initPQExpBuffer(&buffer); QL_HELP[i].syntaxfunc(&buffer); help_found = true; fprintf(output, _("Command: %s\n" "Description: %s\n" "Syntax:\n%s\n\n"), QL_HELP[i].cmd, _(QL_HELP[i].help), buffer.data); /* If we have an exact match, exit. Fixes \h SELECT */ if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0) break; } } if (help_found) /* Don't keep trying if we got a match */ break; } if (!help_found) fprintf(output, _("No help available for \"%s\".\nTry \\h with no arguments to see available help.\n"), topic); /* Only close if we used the pager */ if (output != stdout) { pclose(output); #ifndef WIN32 pqsignal(SIGPIPE, SIG_DFL); #endif } } }
/* * There are a few ways to arrive in the initsequencer. * 1. From _PG_init (called exactly once when the library is loaded for ANY * reason). * 1a. Because of the command LOAD 'libraryname'; * This case can be distinguished because _PG_init will have found the * LOAD command and saved the 'libraryname' in pljavaLoadPath. * 1b. Because of a CREATE FUNCTION naming this library. pljavaLoadPath will * be NULL. * 1c. By the first actual use of a PL/Java function, causing this library * to be loaded. pljavaLoadPath will be NULL. The called function's Oid * will be available to the call handler once we return from _PG_init, * but it isn't (easily) available here. * 2. From the call handler, if initialization isn't complete yet. That can only * mean something failed in the earlier call to _PG_init, and whatever it was * is highly likely to fail again. That may lead to the untidyness of * duplicated diagnostic messages, but for now I like the belt-and-suspenders * approach of making sure the init sequence gets as many chances as possible * to succeed. * 3. From a GUC assign hook, if the user has updated a setting that might allow * initialization to succeed. It resumes from where it left off. * * In all cases, the sequence must progress as far as starting the VM and * initializing the PL/Java classes. In all cases except 1a, that's enough, * assuming the language handlers and schema have all been set up already (or, * in case 1b, the user is intent on setting them up explicitly). * * In case 1a, we can go ahead and test for, and create, the schema, functions, * and language entries as needed, using pljavaLoadPath as the library path * if creating the language handler functions. One-stop shopping. (The presence * of pljavaLoadPath in any of the other cases, such as resumption by an assign * hook, indicates it is really a continuation of case 1a.) */ static void initsequencer(enum initstage is, bool tolerant) { JVMOptList optList; Invocation ctx; jint JNIresult; char *greeting; switch (is) { case IS_FORMLESS_VOID: initstage = IS_GUCS_REGISTERED; case IS_GUCS_REGISTERED: libjvmlocation = strdup("libjvm.so"); initstage = IS_PLJAVA_ENABLED; case IS_PLJAVA_ENABLED: libjvm_handle = pg_dlopen(libjvmlocation); if ( NULL == libjvm_handle ) { ereport(ERROR, ( errmsg("Cannot load libjvm.so library, check that it is available in LD_LIBRARY_PATH"), errdetail("%s", (char *)pg_dlerror()))); goto check_tolerant; } initstage = IS_CAND_JVMOPENED; case IS_CAND_JVMOPENED: pljava_createvm = (jint (JNICALL *)(JavaVM **, void **, void *)) pg_dlsym(libjvm_handle, "JNI_CreateJavaVM"); if ( NULL == pljava_createvm ) { /* * If it hasn't got the symbol, it can't be the right * library, so close/unload it so another can be tried. * Format the dlerror string first: dlclose may clobber it. */ char *dle = MemoryContextStrdup(ErrorContext, pg_dlerror()); pg_dlclose(libjvm_handle); initstage = IS_CAND_JVMLOCATION; ereport(ERROR, ( errmsg("Cannot start Java VM"), errdetail("%s", dle), errhint("Check that libjvm.so is available in LD_LIBRARY_PATH"))); goto check_tolerant; } initstage = IS_CREATEVM_SYM_FOUND; case IS_CREATEVM_SYM_FOUND: s_javaLogLevel = INFO; checkIntTimeType(); HashMap_initialize(); /* creates things in TopMemoryContext */ #ifdef PLJAVA_DEBUG /* Hard setting for debug. Don't forget to recompile... */ pljava_debug = 1; #endif initstage = IS_MISC_ONCE_DONE; case IS_MISC_ONCE_DONE: JVMOptList_init(&optList); /* uses CurrentMemoryContext */ seenVisualVMName = false; addUserJVMOptions(&optList); if ( ! seenVisualVMName ) JVMOptList_addVisualVMName(&optList); JVMOptList_add(&optList, "vfprintf", (void*)my_vfprintf, true); #ifndef GCJ JVMOptList_add(&optList, "-Xrs", 0, true); #endif effectiveClassPath = getClassPath("-Djava.class.path="); if(effectiveClassPath != 0) { JVMOptList_add(&optList, effectiveClassPath, 0, true); } initstage = IS_JAVAVM_OPTLIST; case IS_JAVAVM_OPTLIST: JNIresult = initializeJavaVM(&optList); /* frees the optList */ if( JNI_OK != JNIresult ) { initstage = IS_MISC_ONCE_DONE; /* optList has been freed */ StaticAssertStmt(sizeof(jint) <= sizeof(long int), "jint wider than long int?!"); ereport(WARNING, (errmsg("failed to create Java virtual machine"), errdetail("JNI_CreateJavaVM returned an error code: %ld", (long int)JNIresult), jvmStartedAtLeastOnce ? errhint("Because an earlier attempt during this session " "did start a VM before failing, this probably means your " "Java runtime environment does not support more than one " "VM creation per session. You may need to exit this " "session and start a new one.") : 0)); goto check_tolerant; } jvmStartedAtLeastOnce = true; elog(DEBUG2, "successfully created Java virtual machine"); initstage = IS_JAVAVM_STARTED; case IS_JAVAVM_STARTED: #ifdef USE_PLJAVA_SIGHANDLERS pqsignal(SIGINT, pljavaStatementCancelHandler); pqsignal(SIGTERM, pljavaDieHandler); #endif /* Register an on_proc_exit handler that destroys the VM */ on_proc_exit(_destroyJavaVM, 0); initstage = IS_SIGHANDLERS; case IS_SIGHANDLERS: Invocation_pushBootContext(&ctx); PG_TRY(); { initPLJavaClasses(); initJavaSession(); Invocation_popBootContext(); initstage = IS_PLJAVA_FOUND; } PG_CATCH(); { MemoryContextSwitchTo(ctx.upperContext); /* leave ErrorContext */ Invocation_popBootContext(); initstage = IS_MISC_ONCE_DONE; /* We can't stay here... */ if ( tolerant ) reLogWithChangedLevel(WARNING); /* so xact is not aborted */ else { EmitErrorReport(); /* no more unwinding, just log it */ /* Seeing an ERROR emitted to the log, without leaving the * transaction aborted, would violate the principle of least * astonishment. But at check_tolerant below, another ERROR will * be thrown immediately, so the transaction effect will be as * expected and this ERROR will contribute information beyond * what is in the generic one thrown down there. */ FlushErrorState(); } } PG_END_TRY(); if ( IS_PLJAVA_FOUND != initstage ) { /* JVM initialization failed for some reason. Destroy * the VM if it exists. Perhaps the user will try * fixing the pljava.classpath and make a new attempt. */ ereport(WARNING, ( errmsg("failed to load initial PL/Java classes"), errhint("The most common reason is that \"pljava_classpath\" " "needs to be set, naming the proper \"pljava.jar\" file.") )); _destroyJavaVM(0, 0); goto check_tolerant; } case IS_PLJAVA_FOUND: greeting = InstallHelper_hello(); ereport(NULL != pljavaLoadPath ? NOTICE : DEBUG1, ( errmsg("PL/Java loaded"), errdetail("versions:\n%s", greeting))); pfree(greeting); if ( NULL != pljavaLoadPath ) InstallHelper_groundwork(); /* sqlj schema, language handlers, ...*/ initstage = IS_COMPLETE; case IS_COMPLETE: pljavaLoadingAsExtension = false; if ( alteredSettingsWereNeeded ) { /* Use this StringInfoData to conditionally construct part of the * hint string suggesting ALTER DATABASE ... SET ... FROM CURRENT * provided the server is >= 9.2 where that will actually work. * In 9.3, psprintf appeared, which would make this all simpler, * but if 9.3+ were all that had to be supported, this would all * be moot anyway. Doing the initStringInfo inside the ereport * ensures the string is allocated in ErrorContext and won't leak. * Don't remove the extra parens grouping * (initStringInfo, appendStringInfo, errhint) ... with the parens, * that's a comma expression, which is sequenced; without them, they * are just function parameters with evaluation order unknown. */ StringInfoData buf; #if PG_VERSION_NUM >= 90200 #define MOREHINT \ appendStringInfo(&buf, \ "using ALTER DATABASE %s SET ... FROM CURRENT or ", \ pljavaDbName()), #else #define MOREHINT #endif ereport(NOTICE, ( errmsg("PL/Java successfully started after adjusting settings"), (initStringInfo(&buf), MOREHINT errhint("The settings that worked should be saved (%s" "in the \"%s\" file). For a reminder of what has been set, " "try: SELECT name, setting FROM pg_settings WHERE name LIKE" " 'pljava.%%' AND source = 'session'", buf.data, superuser() ? PG_GETCONFIGOPTION("config_file") : "postgresql.conf")))); #undef MOREHINT if ( loadAsExtensionFailed ) { ereport(NOTICE, (errmsg( "PL/Java load successful after failed CREATE EXTENSION"), errdetail( "PL/Java is now installed, but not as an extension."), errhint( "To correct that, either COMMIT or ROLLBACK, make sure " "the working settings are saved, exit this session, and " "in a new session, either: " "1. if committed, run " "\"CREATE EXTENSION pljava FROM unpackaged\", or 2. " "if rolled back, simply \"CREATE EXTENSION pljava\" again." ))); } } return; default: ereport(ERROR, ( errmsg("cannot set up PL/Java"), errdetail( "An unexpected stage was reached in the startup sequence."), errhint( "Please report the circumstances to the PL/Java maintainers.") )); } check_tolerant: if ( pljavaLoadingAsExtension ) { tolerant = false; loadAsExtensionFailed = true; pljavaLoadingAsExtension = false; } if ( !tolerant ) { ereport(ERROR, ( errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg( "cannot use PL/Java before successfully completing its setup"), errhint( "Check the log for messages closely preceding this one, " "detailing what step of setup failed and what will be needed, " "probably setting one of the \"pljava.\" configuration " "variables, to complete the setup. If there is not enough " "help in the log, try again with different settings for " "\"log_min_messages\" or \"log_error_verbosity\"."))); } }
/* * Main entry point for bgwriter process * * This is invoked from BootstrapMain, which has already created the basic * execution environment, but not enabled signals yet. */ void BackgroundWriterMain(void) { sigjmp_buf local_sigjmp_buf; MemoryContext bgwriter_context; am_bg_writer = true; /* * If possible, make this process a group leader, so that the postmaster * can signal any child processes too. (bgwriter probably never has any * child processes, but for consistency we make all postmaster child * processes do this.) */ #ifdef HAVE_SETSID if (setsid() < 0) elog(FATAL, "setsid() failed: %m"); #endif /* * Properly accept or ignore signals the postmaster might send us * * SIGUSR1 is presently unused; keep it spare in case someday we want this * process to participate in ProcSignal signalling. */ pqsignal(SIGHUP, BgSigHupHandler); /* set flag to read config file */ pqsignal(SIGINT, SIG_IGN); /* as of 9.2 no longer requests checkpoint */ pqsignal(SIGTERM, ReqShutdownHandler); /* shutdown */ pqsignal(SIGQUIT, bg_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, SIG_IGN); /* reserve for ProcSignal */ pqsignal(SIGUSR2, SIG_IGN); /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); /* We allow SIGQUIT (quickdie) at all times */ sigdelset(&BlockSig, SIGQUIT); /* * Create a resource owner to keep track of our resources (currently only * buffer pins). */ CurrentResourceOwner = ResourceOwnerCreate(NULL, "Background Writer"); /* * Create a memory context that we will do all our work in. We do this so * that we can reset the context during error recovery and thereby avoid * possible memory leaks. Formerly this code just ran in * TopMemoryContext, but resetting that would be a really bad idea. */ bgwriter_context = AllocSetContextCreate(TopMemoryContext, "Background Writer", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(bgwriter_context); /* * If an exception is encountered, processing resumes here. * * See notes in postgres.c about the design of this coding. */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; /* Prevent interrupts while cleaning up */ HOLD_INTERRUPTS(); /* Report the error to the server log */ EmitErrorReport(); /* * These operations are really just a minimal subset of * AbortTransaction(). We don't have very many resources to worry * about in bgwriter, but we do have LWLocks, buffers, and temp files. */ LWLockReleaseAll(); AbortBufferIO(); UnlockBuffers(); /* buffer pins are released here: */ ResourceOwnerRelease(CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); /* we needn't bother with the other ResourceOwnerRelease phases */ AtEOXact_Buffers(false); AtEOXact_Files(); AtEOXact_HashTables(false); /* * Now return to normal top-level context and clear ErrorContext for * next time. */ MemoryContextSwitchTo(bgwriter_context); FlushErrorState(); /* Flush any leaked data in the top-level context */ MemoryContextResetAndDeleteChildren(bgwriter_context); /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); /* * Sleep at least 1 second after any error. A write error is likely * to be repeated, and we don't want to be filling the error logs as * fast as we can. */ pg_usleep(1000000L); /* * Close all open files after any error. This is helpful on Windows, * where holding deleted files open causes various strange errors. * It's not clear we need it elsewhere, but shouldn't hurt. */ smgrcloseall(); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; /* * Unblock signals (they were blocked when the postmaster forked us) */ PG_SETMASK(&UnBlockSig); /* * Use the recovery target timeline ID during recovery */ if (RecoveryInProgress()) ThisTimeLineID = GetRecoveryTargetTLI(); /* * Loop forever */ for (;;) { /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. */ if (!PostmasterIsAlive()) exit(1); if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); /* update global shmem state for sync rep */ } if (shutdown_requested) { /* * From here on, elog(ERROR) should end with exit(1), not send * control back to the sigsetjmp block above */ ExitOnAnyError = true; /* Normal exit from the bgwriter is here */ proc_exit(0); /* done */ } /* * Do one cycle of dirty-buffer writing. */ BgBufferSync(); /* Nap for the configured time. */ BgWriterNap(); } }