/** * builds the command line - escapes the parameters * **/ std::string scriptExec::buildCommandLine( const std::string& scriptCmd, baseEvent* pCommand ) { std::string cmdLine; cmdLine = scriptCmd; for( unsigned int i = 0; i < pCommand->scriptParamSize(); i++ ) { cmdLine += " "; cmdLine += shellEscape( pCommand->getScriptParam(i) ); } return cmdLine; } // buildCommandLine
int main(int argc, char **argv) { PQExpBuffer valueBuf = NULL; PQExpBuffer escapeBuf = createPQExpBuffer(); RestoreOptions *opts; int c; int exit_code = 0; Archive *AH; char *inputFileSpec = NULL; extern int optind; extern char *optarg; static int use_setsessauth = 0; static int disable_triggers = 0; SegmentDatabase SegDB; StatusOp *pOp; struct sigaction act; pid_t newpid; /* int i; */ PQExpBuffer pszCmdLine; int status; int rc; char *pszErrorMsg; ArchiveHandle *pAH; int postDataSchemaOnly = 0; #ifdef USE_DDBOOST char *ddp_file_name = NULL; char *dd_boost_dir = NULL; #endif struct option cmdopts[] = { {"clean", 0, NULL, 'c'}, {"create", 0, NULL, 'C'}, {"data-only", 0, NULL, 'a'}, {"dbname", 1, NULL, 'd'}, {"exit-on-error", 0, NULL, 'e'}, {"file", 1, NULL, 'f'}, {"format", 1, NULL, 'F'}, {"function", 1, NULL, 'P'}, {"host", 1, NULL, 'h'}, {"ignore-version", 0, NULL, 'i'}, {"index", 1, NULL, 'I'}, {"list", 0, NULL, 'l'}, {"no-privileges", 0, NULL, 'x'}, {"no-acl", 0, NULL, 'x'}, {"no-owner", 0, NULL, 'O'}, {"no-reconnect", 0, NULL, 'R'}, {"port", 1, NULL, 'p'}, {"password", 0, NULL, 'W'}, {"schema-only", 0, NULL, 's'}, {"superuser", 1, NULL, 'S'}, {"table", 1, NULL, 't'}, {"trigger", 1, NULL, 'T'}, {"use-list", 1, NULL, 'L'}, {"username", 1, NULL, 'U'}, {"verbose", 0, NULL, 'v'}, /* * the following options don't have an equivalent short option letter, * but are available as '-X long-name' */ {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"disable-triggers", no_argument, &disable_triggers, 1}, /* * the following are cdb specific, and don't have an equivalent short * option */ {"gp-d", required_argument, NULL, 1}, {"gp-e", no_argument, NULL, 2}, {"gp-k", required_argument, NULL, 3}, {"gp-c", required_argument, NULL, 4}, {"target-dbid", required_argument, NULL, 5}, {"target-host", required_argument, NULL, 6}, {"target-port", required_argument, NULL, 7}, {"post-data-schema-only", no_argument, &postDataSchemaOnly, 1}, {"dd_boost_file", required_argument, NULL, 8}, {"dd_boost_enabled", no_argument, NULL, 9}, {"dd_boost_dir", required_argument, NULL, 10}, {"dd_boost_buf_size", required_argument, NULL, 11}, {"gp-f", required_argument, NULL, 12}, {"prefix", required_argument, NULL, 13}, {"status", required_argument, NULL, 14}, {"netbackup-service-host", required_argument, NULL, 15}, {"netbackup-block-size", required_argument, NULL, 16}, {"change-schema-file", required_argument, NULL, 17}, {"schema-level-file", required_argument, NULL, 18}, {"ddboost-storage-unit",required_argument, NULL, 19}, {NULL, 0, NULL, 0} }; set_pglocale_pgservice(argv[0], "pg_dump"); opts = NewRestoreOptions(); /* set format default */ opts->formatName = "p"; progname = get_progname(argv[0]); 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("pg_restore (Greenplum Database) " PG_VERSION); exit(0); } } while ((c = getopt_long(argc, argv, "acCd:ef:F:h:iI:lL:Op:P:RsS:t:T:uU:vwWxX:", cmdopts, NULL)) != -1) { switch (c) { case 'a': /* Dump data only */ opts->dataOnly = 1; break; case 'c': /* clean (i.e., drop) schema prior to create */ opts->dropSchema = 1; break; case 'C': opts->createDB = 1; break; case 'd': opts->dbname = strdup(optarg); break; case 'e': opts->exit_on_error = true; break; case 'f': /* output file name */ opts->filename = strdup(optarg); break; case 'F': if (strlen(optarg) != 0) opts->formatName = strdup(optarg); break; case 'h': if (strlen(optarg) != 0) opts->pghost = strdup(optarg); break; case 'i': /* obsolete option */ break; case 'l': /* Dump the TOC summary */ opts->tocSummary = 1; break; case 'L': /* input TOC summary file name */ opts->tocFile = strdup(optarg); break; case 'O': opts->noOwner = 1; break; case 'p': if (strlen(optarg) != 0) opts->pgport = strdup(optarg); break; case 'R': /* no-op, still accepted for backwards compatibility */ break; case 'P': /* Function */ opts->selTypes = 1; opts->selFunction = 1; opts->functionNames = strdup(optarg); break; case 'I': /* Index */ opts->selTypes = 1; opts->selIndex = 1; opts->indexNames = strdup(optarg); break; case 'T': /* Trigger */ opts->selTypes = 1; opts->selTrigger = 1; opts->triggerNames = strdup(optarg); break; case 's': /* dump schema only */ opts->schemaOnly = 1; break; case 'S': /* Superuser username */ if (strlen(optarg) != 0) opts->superuser = strdup(optarg); break; case 't': /* Dump data for this table only */ opts->selTypes = 1; opts->selTable = 1; opts->tableNames = strdup(optarg); break; case 'u': opts->promptPassword = TRI_YES; opts->username = simple_prompt("User name: ", 100, true); break; case 'U': opts->username = optarg; break; case 'v': /* verbose */ opts->verbose = 1; break; case 'w': opts->promptPassword = TRI_NO; break; case 'W': opts->promptPassword = TRI_YES; break; case 'x': /* skip ACL dump */ opts->aclsSkip = 1; break; case 'X': /* -X is a deprecated alternative to long options */ if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; else if (strcmp(optarg, "disable-triggers") == 0) disable_triggers = 1; else { fprintf(stderr, _("%s: invalid -X option -- %s\n"), progname, optarg); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } break; case 0: /* This covers the long options equivalent to -X xxx. */ break; case 1: /* --gp-d MPP Output Directory */ g_pszMPPOutputDirectory = strdup(optarg); break; case 2: /* --gp-e On Error Stop for psql */ g_bOnErrorStop = opts->exit_on_error = true; break; case 3: /* --gp-k MPP Dump Info Format is * Key_s-dbid_s-role_t-dbid */ g_gpdumpInfo = strdup(optarg); if (!ParseCDBDumpInfo(progname, g_gpdumpInfo, &g_gpdumpKey, &g_role, &g_sourceDBID, &g_MPPPassThroughCredentials)) { exit(1); } break; case 4: /* gp-c */ g_compPg = strdup(optarg); break; case 5: /* target-dbid */ g_targetDBID = atoi(strdup(optarg)); break; case 6: /* target-host */ g_targetHost = strdup(optarg); break; case 7: /* target-port */ g_targetPort = strdup(optarg); break; #ifdef USE_DDBOOST case 9: dd_boost_enabled = 1; break; case 10: dd_boost_dir = strdup(optarg); break; case 11: dd_boost_buf_size = strdup(optarg); break; #endif case 12: table_filter_file = strdup(optarg); break; case 13: dump_prefix = strdup(optarg); break; /* Hack to pass in the status_file name to cdbbackup.c (gp_restore_launch) */ case 14: break; case 15: netbackup_service_host = strdup(optarg); break; case 16: netbackup_block_size = strdup(optarg); break; case 17: change_schema_file = strdup(optarg); break; case 18: schema_level_file = strdup(optarg); break; #ifdef USE_DDBOOST case 19: ddboost_storage_unit = strdup(optarg); break; #endif default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } /* Should get at most one of -d and -f, else user is confused */ if (opts->dbname) { if (opts->filename) { fprintf(stderr, _("%s: cannot specify both -d and -f output\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } opts->useDB = 1; } opts->disable_triggers = disable_triggers; opts->use_setsessauth = use_setsessauth; if (opts->formatName) { switch (opts->formatName[0]) { case 'c': case 'C': opts->format = archCustom; break; case 'f': case 'F': opts->format = archFiles; break; case 't': case 'T': opts->format = archTar; break; case 'p': case 'P': bUsePSQL = true; break; default: mpp_err_msg(logInfo, progname, "unrecognized archive format '%s'; please specify 't' or 'c'\n", opts->formatName); exit(1); } } if (g_gpdumpInfo == NULL) { mpp_err_msg(logInfo, progname, "missing required parameter gp-k (backup key)\n"); exit(1); } #ifdef USE_DDBOOST if (dd_boost_enabled) { /* remote is always false when restoring from primary DDR */ int err = DD_ERR_NONE; err = ddp_init("gp_dump"); if (err != DD_ERR_NONE) { mpp_err_msg("ERROR", "ddboost", "ddboost init failed. Err = %d\n", err); exit(1); } if (initDDSystem(&ddp_inst, &ddp_conn, &dd_client_info, &ddboost_storage_unit, false, &DEFAULT_BACKUP_DIRECTORY, false)) { mpp_err_msg(logInfo, progname, "Initializing DD system failed\n"); exit(1); } mpp_err_msg(logInfo, progname, "ddboost is initialized\n"); ddp_file_name = formDDBoostFileName(g_gpdumpKey, postDataSchemaOnly, dd_boost_dir); if (ddp_file_name == NULL) { mpp_err_msg(logInfo, progname, "Error in opening ddboost file\n"); exit(1); } } #endif SegDB.dbid = g_sourceDBID; SegDB.role = g_role; SegDB.port = opts->pgport ? atoi(opts->pgport) : 5432; SegDB.pszHost = opts->pghost ? strdup(opts->pghost) : NULL; SegDB.pszDBName = opts->dbname ? strdup(opts->dbname) : NULL; SegDB.pszDBUser = opts->username ? strdup(opts->username) : NULL; SegDB.pszDBPswd = NULL; if (g_MPPPassThroughCredentials != NULL && *g_MPPPassThroughCredentials != '\0') { unsigned int nBytes; char *pszDBPswd = Base64ToData(g_MPPPassThroughCredentials, &nBytes); if (pszDBPswd == NULL) { mpp_err_msg(logError, progname, "Invalid Greenplum DB Credentials: %s\n", g_MPPPassThroughCredentials); exit(1); } if (nBytes > 0) { SegDB.pszDBPswd = malloc(nBytes + 1); if (SegDB.pszDBPswd == NULL) { mpp_err_msg(logInfo, progname, "Cannot allocate memory for Greenplum Database Credentials\n"); exit(1); } memcpy(SegDB.pszDBPswd, pszDBPswd, nBytes); SegDB.pszDBPswd[nBytes] = '\0'; } } if (g_role == ROLE_MASTER) g_conn = MakeDBConnection(&SegDB, true); else g_conn = MakeDBConnection(&SegDB, false); if (PQstatus(g_conn) == CONNECTION_BAD) { exit_horribly(NULL, NULL, "connection to database \"%s\" failed: %s", PQdb(g_conn), PQerrorMessage(g_conn)); } if (g_gpdumpKey != NULL) { /* * Open the database again, for writing status info */ g_conn_status = MakeDBConnection(&SegDB, false); if (PQstatus(g_conn_status) == CONNECTION_BAD) { exit_horribly(NULL, NULL, "Connection on host %s failed: %s", StringNotNull(SegDB.pszHost, "localhost"), PQerrorMessage(g_conn_status)); } g_main_tid = pthread_self(); g_pStatusOpList = CreateStatusOpList(); if (g_pStatusOpList == NULL) { exit_horribly(NULL, NULL, "cannot allocate memory for gp_backup_status operation\n"); } /* * Create thread for monitoring for cancel requests. If we're running * using PSQL, the monitor is not allowed to start until the worker * process is forked. This is done to prevent the forked process from * being blocked by locks held by library routines (__tz_convert, for * example). */ if (bUsePSQL) { pthread_mutex_lock(&g_threadSyncPoint); } pthread_create(&g_monitor_tid, NULL, monitorThreadProc, NULL); /* Install Ctrl-C interrupt handler, now that we have a connection */ if (!bUsePSQL) { act.sa_handler = myHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGINT, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGINT interrupt handler\n"); } act.sa_handler = myHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGTERM, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGTERM interrupt handler\n"); } } pOp = CreateStatusOp(TASK_START, TASK_RC_SUCCESS, SUFFIX_START, TASK_MSG_SUCCESS); if (pOp == NULL) { exit_horribly(NULL, NULL, "cannot allocate memory for gp_backup_status operation\n"); } AddToStatusOpList(g_pStatusOpList, pOp); } /* end cdb additions */ if (bUsePSQL) { /* Install Ctrl-C interrupt handler, now that we have a connection */ act.sa_handler = psqlHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGINT, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGINT interrupt handler\n"); } act.sa_handler = psqlHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGTERM, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGTERM interrupt handler\n"); } /* Establish a SIGCHLD handler to catch termination the psql process */ act.sa_handler = myChildHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGCHLD, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGCHLD interrupt handler\n"); exit(1); } mpp_err_msg(logInfo, progname, "Before fork of gp_restore_agent\n"); newpid = fork(); if (newpid < 0) { mpp_err_msg(logError, progname, "Failed to fork\n"); } else if (newpid == 0) { /* TODO: use findAcceptableBackupFilePathName(...) to look for the file name * if user invoked gp_restore_agent directly without supplying a file name. * If the agent is invoked from gp_restore_launch, then we are ok. */ if (optind < argc) { char *rawInputFile = argv[optind]; valueBuf = createPQExpBuffer(); inputFileSpec = shellEscape(rawInputFile, valueBuf, false, false); } if (inputFileSpec == NULL || inputFileSpec[0] == '\0') { mpp_err_msg(logError, progname, "dump file path is empty"); exit(1); } if (postDataSchemaOnly) { if (strstr(inputFileSpec,"_post_data") == NULL) { fprintf(stderr,"Adding _post_data to the end of the file name?\n"); char * newFS = malloc(strlen(inputFileSpec) + strlen("_post_data") + 1); strcpy(newFS, inputFileSpec); strcat(newFS, "_post_data"); inputFileSpec = newFS; } } pszCmdLine = createPQExpBuffer(); #ifdef USE_DDBOOST if (dd_boost_enabled) { formDDBoostPsqlCommandLine(pszCmdLine, argv[0], (postDataSchemaOnly == 1 ? true : false), g_role, g_compPg, ddp_file_name, dd_boost_buf_size, table_filter_file, change_schema_file, schema_level_file, ddboost_storage_unit); } else { #endif formPsqlCommandLine(pszCmdLine, argv[0], (postDataSchemaOnly == 1), g_role, inputFileSpec, g_compPg, table_filter_file, netbackup_service_host, netbackup_block_size, change_schema_file, schema_level_file); #ifdef USE_DDBOOST } #endif appendPQExpBuffer(pszCmdLine, " -h %s -p %s -U %s -d ", g_targetHost, g_targetPort, SegDB.pszDBUser); shellEscape(SegDB.pszDBName, pszCmdLine, true /* quote */, false /* reset */); appendPQExpBuffer(pszCmdLine, " -a "); if (g_bOnErrorStop) appendPQExpBuffer(pszCmdLine, " -v ON_ERROR_STOP="); if (g_role == ROLE_SEGDB) putenv("PGOPTIONS=-c gp_session_role=UTILITY"); if (g_role == ROLE_MASTER) putenv("PGOPTIONS=-c gp_session_role=DISPATCH"); mpp_err_msg(logInfo, progname, "Command Line: %s\n", pszCmdLine->data); /* * Make this new process the process group leader of the children * being launched. This allows a signal to be sent to all * processes in the group simultaneously. */ setpgid(newpid, newpid); execl("/bin/sh", "sh", "-c", pszCmdLine->data, NULL); mpp_err_msg(logInfo, progname, "Error in gp_restore_agent - execl of %s with Command Line %s failed", "/bin/sh", pszCmdLine->data); _exit(127); } else { /* * Make the new child process the process group leader of the * children being launched. This allows a signal to be sent to * all processes in the group simultaneously. * * This is a redundant call to avoid a race condition suggested by * Stevens. */ setpgid(newpid, newpid); /* Allow the monitor thread to begin execution. */ pthread_mutex_unlock(&g_threadSyncPoint); /* Parent . Lets sleep and wake up until we see it's done */ while (!bPSQLDone) { sleep(5); } /* * If this process has been sent a SIGINT or SIGTERM, we need to * send a SIGINT to the psql process GROUP. */ if (bKillPsql) { mpp_err_msg(logInfo, progname, "Terminating psql due to signal.\n"); kill(-newpid, SIGINT); } waitpid(newpid, &status, 0); if (WIFEXITED(status)) { rc = WEXITSTATUS(status); if (rc == 0) { mpp_err_msg(logInfo, progname, "psql finished with rc %d.\n", rc); /* Normal completion falls to end of routine. */ } else { if (rc >= 128) { /* * If the exit code has the 128-bit set, the exit code * represents a shell exited by signal where the * signal number is exitCode - 128. */ rc -= 128; pszErrorMsg = MakeString("psql finished abnormally with signal number %d.\n", rc); } else { pszErrorMsg = MakeString("psql finished abnormally with return code %d.\n", rc); } makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg); free(pszErrorMsg); exit_code = 2; } } else if (WIFSIGNALED(status)) { pszErrorMsg = MakeString("psql finished abnormally with signal number %d.\n", WTERMSIG(status)); mpp_err_msg(logError, progname, pszErrorMsg); makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg); free(pszErrorMsg); exit_code = 2; } else { pszErrorMsg = MakeString("psql crashed or finished badly; status=%#x.\n", status); mpp_err_msg(logError, progname, pszErrorMsg); makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg); free(pszErrorMsg); exit_code = 2; } } } else { AH = OpenArchive(inputFileSpec, opts->format); /* Let the archiver know how noisy to be */ AH->verbose = opts->verbose; /* * Whether to keep submitting sql commands as "pg_restore ... | psql ... " */ AH->exit_on_error = opts->exit_on_error; if (opts->tocFile) SortTocFromFile(AH, opts); if (opts->tocSummary) PrintTOCSummary(AH, opts); else { pAH = (ArchiveHandle *) AH; if (opts->useDB) { /* check for version mismatch */ if (pAH->version < K_VERS_1_3) die_horribly(NULL, NULL, "direct database connections are not supported in pre-1.3 archives\n"); pAH->connection = g_conn; /* XXX Should get this from the archive */ AH->minRemoteVersion = 070100; AH->maxRemoteVersion = 999999; _check_database_version(pAH); } RestoreArchive(AH, opts); /* * The following is necessary when the -C option is used. A new * connection is gotten to the database within RestoreArchive */ if (pAH->connection != g_conn) g_conn = pAH->connection; } /* done, print a summary of ignored errors */ if (AH->n_errors) fprintf(stderr, _("WARNING: errors ignored on restore: %d\n"), AH->n_errors); /* AH may be freed in CloseArchive? */ exit_code = AH->n_errors ? 1 : 0; CloseArchive(AH); } #ifdef USE_DDBOOST if(dd_boost_enabled) cleanupDDSystem(); free(ddboost_storage_unit); #endif makeSureMonitorThreadEnds(TASK_RC_SUCCESS, TASK_MSG_SUCCESS); DestroyStatusOpList(g_pStatusOpList); if (change_schema_file) free(change_schema_file); if (schema_level_file) free(schema_level_file); if (SegDB.pszHost) free(SegDB.pszHost); if (SegDB.pszDBName) free(SegDB.pszDBName); if (SegDB.pszDBUser) free(SegDB.pszDBUser); if (SegDB.pszDBPswd) free(SegDB.pszDBPswd); if (valueBuf) destroyPQExpBuffer(valueBuf); if (escapeBuf) destroyPQExpBuffer(escapeBuf); PQfinish(g_conn); if (exit_code == 0) mpp_err_msg(logInfo, progname, "Finished successfully\n"); else mpp_err_msg(logError, progname, "Finished with errors\n"); return exit_code; }