/* * resolve_symlinks - resolve symlinks to the underlying file * * Replace "path" by the absolute path to the referenced file. * * Returns 0 if OK, -1 if error. * * Note: we are not particularly tense about producing nice error messages * because we are not really expecting error here; we just determined that * the symlink does point to a valid executable. */ static int resolve_symlinks(char *path) { #ifdef HAVE_READLINK struct stat buf; char orig_wd[MAXPGPATH], link_buf[MAXPGPATH]; char *fname; /* * To resolve a symlink properly, we have to chdir into its directory and * then chdir to where the symlink points; otherwise we may fail to * resolve relative links correctly (consider cases involving mount * points, for example). After following the final symlink, we use * getcwd() to figure out where the heck we're at. * * One might think we could skip all this if path doesn't point to a * symlink to start with, but that's wrong. We also want to get rid of * any directory symlinks that are present in the given path. We expect * getcwd() to give us an accurate, symlink-free path. */ if (!getcwd(orig_wd, MAXPGPATH)) { log_error(_("could not identify current directory: %s"), strerror(errno)); return -1; } for (;;) { char *lsep; int rllen; lsep = last_dir_separator(path); if (lsep) { *lsep = '\0'; if (chdir(path) == -1) { log_error(_("could not change directory to \"%s\""), path); return -1; } fname = lsep + 1; } else fname = path; if (lstat(fname, &buf) < 0 || !S_ISLNK(buf.st_mode)) break; rllen = readlink(fname, link_buf, sizeof(link_buf)); if (rllen < 0 || rllen >= sizeof(link_buf)) { log_error(_("could not read symbolic link \"%s\""), fname); return -1; } link_buf[rllen] = '\0'; strcpy(path, link_buf); } /* must copy final component out of 'path' temporarily */ strcpy(link_buf, fname); if (!getcwd(path, MAXPGPATH)) { log_error(_("could not identify current directory: %s"), strerror(errno)); return -1; } join_path_components(path, path, link_buf); canonicalize_path(path); if (chdir(orig_wd) == -1) { log_error(_("could not change directory to \"%s\""), orig_wd); return -1; } #endif /* HAVE_READLINK */ return 0; }
/* * find_my_exec -- find an absolute path to a valid executable * * argv0 is the name passed on the command line * retpath is the output area (must be of size MAXPGPATH) * Returns 0 if OK, -1 if error. * * The reason we have to work so hard to find an absolute path is that * on some platforms we can't do dynamic loading unless we know the * executable's location. Also, we need a full path not a relative * path because we will later change working directory. Finally, we want * a true path not a symlink location, so that we can locate other files * that are part of our installation relative to the executable. * * This function is not thread-safe because it calls validate_exec(), * which calls getgrgid(). This function should be used only in * non-threaded binaries, not in library routines. */ int find_my_exec(const char *argv0, char *retpath) { char cwd[MAXPGPATH], test_path[MAXPGPATH]; char *path; if (!getcwd(cwd, MAXPGPATH)) { log_error(_("could not identify current directory: %s"), strerror(errno)); return -1; } /* * If argv0 contains a separator, then PATH wasn't used. */ if (first_dir_separator(argv0) != NULL) { if (is_absolute_path(argv0)) StrNCpy(retpath, argv0, MAXPGPATH); else join_path_components(retpath, cwd, argv0); canonicalize_path(retpath); if (validate_exec(retpath) == 0) return resolve_symlinks(retpath); log_error(_("invalid binary \"%s\""), retpath); return -1; } #ifdef WIN32 /* Win32 checks the current directory first for names without slashes */ join_path_components(retpath, cwd, argv0); if (validate_exec(retpath) == 0) return resolve_symlinks(retpath); #endif /* * Since no explicit path was supplied, the user must have been relying on * PATH. We'll search the same PATH. */ if ((path = getenv("PATH")) && *path) { char *startp = NULL, *endp = NULL; do { if (!startp) startp = path; else startp = endp + 1; endp = first_path_separator(startp); if (!endp) endp = startp + strlen(startp); /* point to end */ StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH)); if (is_absolute_path(test_path)) join_path_components(retpath, test_path, argv0); else { join_path_components(retpath, cwd, test_path); join_path_components(retpath, retpath, argv0); } canonicalize_path(retpath); switch (validate_exec(retpath)) { case 0: /* found ok */ return resolve_symlinks(retpath); case -1: /* wasn't even a candidate, keep looking */ break; case -2: /* found but disqualified */ log_error(_("could not read binary \"%s\""), retpath); break; } } while (*endp); } log_error(_("could not find a \"%s\" to execute"), argv0); return -1; }
/* * Entry point of pg_arman command. */ int main(int argc, char *argv[]) { const char *cmd = NULL; const char *range1 = NULL; const char *range2 = NULL; pgBackupRange range; int i; /* do not buffer progress messages */ setvbuf(stdout, 0, _IONBF, 0); /* TODO: remove this */ /* initialize configuration */ catalog_init_config(¤t); /* overwrite configuration with command line arguments */ i = pgut_getopt(argc, argv, options); for (; i < argc; i++) { if (cmd == NULL) cmd = argv[i]; else if (range1 == NULL) range1 = argv[i]; else if (range2 == NULL) range2 = argv[i]; else elog(ERROR, "too many arguments"); } /* command argument (backup/restore/show/...) is required. */ if (cmd == NULL) { help(false); return 1; } /* get object range argument if any */ if (range1 && range2) parse_range(&range, range1, range2); else if (range1) parse_range(&range, range1, ""); else range.begin = range.end = 0; /* Read default configuration from file. */ if (backup_path) { char path[MAXPGPATH]; /* Check if backup_path is directory. */ struct stat stat_buf; int rc = stat(backup_path, &stat_buf); /* If rc == -1, there is no file or directory. So it's OK. */ if (rc != -1 && !S_ISDIR(stat_buf.st_mode)) elog(ERROR, "-B, --backup-path must be a path to directory"); join_path_components(path, backup_path, PG_RMAN_INI_FILE); pgut_readopt(path, options, ERROR); } /* BACKUP_PATH is always required */ if (backup_path == NULL) elog(ERROR, "required parameter not specified: BACKUP_PATH (-B, --backup-path)"); /* path must be absolute */ if (backup_path != NULL && !is_absolute_path(backup_path)) elog(ERROR, "-B, --backup-path must be an absolute path"); if (pgdata != NULL && !is_absolute_path(pgdata)) elog(ERROR, "-D, --pgdata must be an absolute path"); if (arclog_path != NULL && !is_absolute_path(arclog_path)) elog(ERROR, "-A, --arclog-path must be an absolute path"); /* Sanity checks with commands */ if (pg_strcasecmp(cmd, "delete") == 0 && arclog_path == NULL) elog(ERROR, "delete command needs ARCLOG_PATH (-A, --arclog-path) to be set"); /* setup exclusion list for file search */ for (i = 0; pgdata_exclude[i]; i++) /* find first empty slot */ ; if (arclog_path) pgdata_exclude[i++] = arclog_path; /* do actual operation */ if (pg_strcasecmp(cmd, "init") == 0) return do_init(); else if (pg_strcasecmp(cmd, "backup") == 0) { pgBackupOption bkupopt; int res; bkupopt.smooth_checkpoint = smooth_checkpoint; bkupopt.keep_data_generations = keep_data_generations; bkupopt.keep_data_days = keep_data_days; /* Do the backup */ res = do_backup(bkupopt); if (res != 0) return res; /* If validation has been requested, do it */ range.begin = current.start_time; range.end = current.start_time + 1; if (backup_validate) do_validate(&range); } else if (pg_strcasecmp(cmd, "restore") == 0) return do_restore(target_time, target_xid, target_inclusive, target_tli); else if (pg_strcasecmp(cmd, "show") == 0) return do_show(&range, show_all); else if (pg_strcasecmp(cmd, "validate") == 0) return do_validate(&range); else if (pg_strcasecmp(cmd, "delete") == 0) return do_delete(&range); else elog(ERROR, "invalid command \"%s\"", cmd); return 0; }