/* * On Windows make sure that we are running with a restricted token, * On other platforms do nothing. */ void get_restricted_token(const char *progname) { #ifdef WIN32 HANDLE restrictedToken; /* * Before we execute another program, make sure that we are running with a * restricted token. If not, re-execute ourselves with one. */ if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL || strcmp(restrict_env, "1") != 0) { PROCESS_INFORMATION pi; char *cmdline; ZeroMemory(&pi, sizeof(pi)); cmdline = pg_strdup(GetCommandLine()); putenv("PG_RESTRICT_EXEC=1"); if ((restrictedToken = CreateRestrictedProcess(cmdline, &pi, progname)) == 0) { fprintf(stderr, _("%s: could not re-execute with restricted token: error code %lu\n"), progname, GetLastError()); } else { /* * Successfully re-execed. Now wait for child process to capture * exitcode. */ DWORD x; CloseHandle(restrictedToken); CloseHandle(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); if (!GetExitCodeProcess(pi.hProcess, &x)) { fprintf(stderr, _("%s: could not get exit code from subprocess: error code %lu\n"), progname, GetLastError()); exit(1); } exit(x); } } #endif }
/* * start command asynchronously. */ pid_t forkexec(const char *cmd, int *outStdin) { pid_t pid; *outStdin = -1; #ifndef WIN32 { int fd[2]; if (pipe(fd) < 0 || (pid = fork()) < 0) return 0; if (pid == 0) { /* child process */ if (close(fd[1]) < 0 || dup2(fd[0], STDIN_FILENO) < 0 || execl("/bin/sh", "sh", "-c", cmd, NULL) < 0) { elog(LOG, "pg_statsinfo(): could not execute background process: %s", strerror(errno)); exit(1); } } close(fd[0]); *outStdin = fd[1]; } #else { PROCESS_INFORMATION pi; *outStdin = CreateRestrictedProcess((char *) cmd, &pi, false); if (*outStdin) pid = (pid_t) pi.dwProcessId; else { _dosmaperr(GetLastError()); pid = 0; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } #endif return pid; }
static int start_postmaster(void) { char cmd[MAXPGPATH]; #ifndef WIN32 /* * Since there might be quotes to handle here, it is easier simply to pass * everything to a shell to process them. */ if (log_file != NULL) snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &" SYSTEMQUOTE, exec_path, pgdata_opt, post_opts, DEVNULL, log_file); else snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" 2>&1 &" SYSTEMQUOTE, exec_path, pgdata_opt, post_opts, DEVNULL); return system(cmd); #else /* WIN32 */ /* * On win32 we don't use system(). So we don't need to use & (which would * be START /B on win32). However, we still call the shell (CMD.EXE) with * it to handle redirection etc. */ PROCESS_INFORMATION pi; if (log_file != NULL) snprintf(cmd, MAXPGPATH, "CMD /C " SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1" SYSTEMQUOTE, exec_path, pgdata_opt, post_opts, DEVNULL, log_file); else snprintf(cmd, MAXPGPATH, "CMD /C " SYSTEMQUOTE "\"%s\" %s%s < \"%s\" 2>&1" SYSTEMQUOTE, exec_path, pgdata_opt, post_opts, DEVNULL); if (!CreateRestrictedProcess(cmd, &pi, false)) return GetLastError(); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return 0; #endif /* WIN32 */ }
int main(int argc, char *argv[]) { /* * options with no short version return a low integer, the rest return * their short version value */ static struct option long_options[] = { {"pgdata", required_argument, NULL, 'D'}, {"help", no_argument, NULL, '?'}, {"version", no_argument, NULL, 'V'}, {"debug", no_argument, NULL, 'd'}, {"show", no_argument, NULL, 's'}, {"noclean", no_argument, NULL, 'n'}, {NULL, 0, NULL, 0} }; int c, ret; int option_index; char *effective_user; char bin_dir[MAXPGPATH]; char *pg_data_native; bool node_type_specified = false; progname = get_progname(argv[0]); set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("initgtm")); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("initgtm (Postgres-XL) " PGXC_VERSION); exit(0); } } /* process command-line options */ while ((c = getopt_long(argc, argv, "dD:nsZ:", long_options, &option_index)) != -1) { switch (c) { case 'D': pg_data = xstrdup(optarg); break; case 'd': debug = true; printf(_("Running in debug mode.\n")); break; case 'n': noclean = true; printf(_("Running in noclean mode. Mistakes will not be cleaned up.\n")); break; case 's': show_setting = true; break; case 'Z': if (strcmp(xstrdup(optarg), "gtm") == 0) is_gtm = true; else if (strcmp(xstrdup(optarg), "gtm_proxy") == 0) is_gtm = false; else { fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } node_type_specified = true; break; default: /* getopt_long already emitted a complaint */ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } /* Non-option argument specifies data directory */ if (optind < argc) { pg_data = xstrdup(argv[optind]); optind++; } if (optind < argc) { fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind + 1]); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } /* Check on definition of GTM data folder */ if (strlen(pg_data) == 0) { fprintf(stderr, _("%s: no data directory specified\n" "You must identify the directory where the data for this GTM system\n" "will reside. Do this with either the invocation option -D or the\n" "environment variable PGDATA.\n"), progname); exit(1); } if (!node_type_specified) { fprintf(stderr, _("%s: no node type specified\n" "You must identify the node type chosen for initialization.\n" "Do this with the invocation option -Z by choosing \"gtm\" or" "\"gtm_proxy\"\n"), progname); exit(1); } pg_data_native = pg_data; canonicalize_path(pg_data); #ifdef WIN32 /* * Before we execute another program, make sure that we are running with a * restricted token. If not, re-execute ourselves with one. */ if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL || strcmp(restrict_env, "1") != 0) { PROCESS_INFORMATION pi; char *cmdline; ZeroMemory(&pi, sizeof(pi)); cmdline = xstrdup(GetCommandLine()); putenv("PG_RESTRICT_EXEC=1"); if (!CreateRestrictedProcess(cmdline, &pi)) { fprintf(stderr, "Failed to re-exec with restricted token: %lu.\n", GetLastError()); } else { /* * Successfully re-execed. Now wait for child process to capture * exitcode. */ DWORD x; CloseHandle(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); if (!GetExitCodeProcess(pi.hProcess, &x)) { fprintf(stderr, "Failed to get exit code from subprocess: %lu\n", GetLastError()); exit(1); } exit(x); } } #endif /* Like for initdb, check if a valid version of Postgres is running */ if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, backend_exec)) < 0) { char full_path[MAXPGPATH]; if (find_my_exec(argv[0], full_path) < 0) strlcpy(full_path, progname, sizeof(full_path)); if (ret == -1) fprintf(stderr, _("The program \"postgres\" is needed by %s " "but was not found in the\n" "same directory as \"%s\".\n" "Check your installation.\n"), progname, full_path); else fprintf(stderr, _("The program \"postgres\" was found by \"%s\"\n" "but was not the same version as %s.\n" "Check your installation.\n"), full_path, progname); exit(1); } /* store binary directory */ strcpy(bin_path, backend_exec); *last_dir_separator(bin_path) = '\0'; canonicalize_path(bin_path); if (!share_path) { share_path = pg_malloc(MAXPGPATH); get_share_path(backend_exec, share_path); } else if (!is_absolute_path(share_path)) { fprintf(stderr, _("%s: input file location must be an absolute path\n"), progname); exit(1); } canonicalize_path(share_path); effective_user = get_id(); /* Take into account GTM and GTM-proxy cases */ if (is_gtm) set_input(&conf_file, "gtm.conf.sample"); else set_input(&conf_file, "gtm_proxy.conf.sample"); if (show_setting || debug) { fprintf(stderr, "VERSION=%s\n" "GTMDATA=%s\nshare_path=%s\nGTMPATH=%s\n" "GTM_CONF_SAMPLE=%s\n", PGXC_VERSION, pg_data, share_path, bin_path, conf_file); if (show_setting) exit(0); } check_input(conf_file); printf(_("The files belonging to this GTM system will be owned " "by user \"%s\".\n" "This user must also own the server process.\n\n"), effective_user); printf("\n"); umask(S_IRWXG | S_IRWXO); /* * now we are starting to do real work, trap signals so we can clean up */ /* some of these are not valid on Windows */ #ifdef SIGHUP pqsignal(SIGHUP, trapsig); #endif #ifdef SIGINT pqsignal(SIGINT, trapsig); #endif #ifdef SIGQUIT pqsignal(SIGQUIT, trapsig); #endif #ifdef SIGTERM pqsignal(SIGTERM, trapsig); #endif /* Ignore SIGPIPE when writing to backend, so we can clean up */ #ifdef SIGPIPE pqsignal(SIGPIPE, SIG_IGN); #endif switch (pg_check_dir(pg_data)) { case 0: /* PGDATA not there, must create it */ printf(_("creating directory %s ... "), pg_data); fflush(stdout); if (!mkdatadir(NULL)) exit_nicely(); else check_ok(); made_new_pgdata = true; break; case 1: /* Present but empty, fix permissions and use it */ printf(_("fixing permissions on existing directory %s ... "), pg_data); fflush(stdout); if (chmod(pg_data, S_IRWXU) != 0) { fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"), progname, pg_data, strerror(errno)); exit_nicely(); } else check_ok(); found_existing_pgdata = true; break; case 2: /* Present and not empty */ fprintf(stderr, _("%s: directory \"%s\" exists but is not empty\n"), progname, pg_data); fprintf(stderr, _("If you want to create a new GTM system, either remove or empty\n" "the directory \"%s\" or run %s\n" "with an argument other than \"%s\".\n"), pg_data, progname, pg_data); exit(1); /* no further message needed */ default: /* Trouble accessing directory */ fprintf(stderr, _("%s: could not access directory \"%s\": %s\n"), progname, pg_data, strerror(errno)); exit_nicely(); } /* Select suitable configuration settings */ set_null_conf(); /* Now create all the text config files */ setup_config(); /* Get directory specification used to start this executable */ strcpy(bin_dir, argv[0]); get_parent_directory(bin_dir); if (is_gtm) printf(_("\nSuccess. You can now start the GTM server using:\n\n" " %s%s%sgtm%s -D %s%s%s\n" "or\n" " %s%s%sgtm_ctl%s -Z gtm -D %s%s%s -l logfile start\n\n"), QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH, QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH); else printf(_("\nSuccess. You can now start the GTM proxy server using:\n\n" " %s%s%sgtm_proxy%s -D %s%s%s\n" "or\n" " %s%s%sgtm_ctl%s -Z gtm_proxy -D %s%s%s -l logfile start\n\n"), QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH, QUOTE_PATH, bin_dir, (strlen(bin_dir) > 0) ? DIR_SEP : "", QUOTE_PATH, QUOTE_PATH, pg_data_native, QUOTE_PATH); return 0; }
static void WINAPI pgwin32_ServiceMain(DWORD argc, LPTSTR *argv) { PROCESS_INFORMATION pi; DWORD ret; DWORD check_point_start; /* Initialize variables */ status.dwWin32ExitCode = S_OK; status.dwCheckPoint = 0; status.dwWaitHint = 60000; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE; status.dwServiceSpecificExitCode = 0; status.dwCurrentState = SERVICE_START_PENDING; memset(&pi, 0, sizeof(pi)); read_post_opts(); /* Register the control request handler */ if ((hStatus = RegisterServiceCtrlHandler(register_servicename, pgwin32_ServiceHandler)) == (SERVICE_STATUS_HANDLE) 0) return; if ((shutdownEvent = CreateEvent(NULL, true, false, NULL)) == NULL) return; /* Start the postmaster */ pgwin32_SetServiceStatus(SERVICE_START_PENDING); if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi, true)) { pgwin32_SetServiceStatus(SERVICE_STOPPED); return; } postmasterPID = pi.dwProcessId; postmasterProcess = pi.hProcess; CloseHandle(pi.hThread); if (do_wait) { write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Waiting for server startup...\n")); if (test_postmaster_connection(true) == false) { write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Timed out waiting for server startup\n")); pgwin32_SetServiceStatus(SERVICE_STOPPED); return; } write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Server started and accepting connections\n")); } /* * Save the checkpoint value as it might have been incremented in * test_postmaster_connection */ check_point_start = status.dwCheckPoint; pgwin32_SetServiceStatus(SERVICE_RUNNING); /* Wait for quit... */ ret = WaitForMultipleObjects(2, shutdownHandles, FALSE, INFINITE); pgwin32_SetServiceStatus(SERVICE_STOP_PENDING); switch (ret) { case WAIT_OBJECT_0: /* shutdown event */ kill(postmasterPID, SIGINT); /* * Increment the checkpoint and try again Abort after 12 * checkpoints as the postmaster has probably hung */ while (WaitForSingleObject(postmasterProcess, 5000) == WAIT_TIMEOUT && status.dwCheckPoint < 12) status.dwCheckPoint++; break; case (WAIT_OBJECT_0 + 1): /* postmaster went down */ break; default: /* shouldn't get here? */ break; } CloseHandle(shutdownEvent); CloseHandle(postmasterProcess); pgwin32_SetServiceStatus(SERVICE_STOPPED); }