diff --git a/Makefile b/Makefile index 4e40c341..37ebb109 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ PROGRAM = pg_probackup OBJS = backup.o \ catalog.o \ + configure.o \ data.o \ delete.o \ dir.o \ diff --git a/configure.c b/configure.c new file mode 100644 index 00000000..7f829e10 --- /dev/null +++ b/configure.c @@ -0,0 +1,125 @@ +/*------------------------------------------------------------------------- + * + * configure.c: - manage backup catalog. + * + * Portions Copyright (c) 2017-2017, Postgres Professional + * + *------------------------------------------------------------------------- + */ + +#include "pg_probackup.h" + +/* Set configure options */ +int +do_configure(bool show_only) +{ + pgBackupConfig *config = readBackupCatalogConfigFile(); + if (pgdata) + config->pgdata = pgdata; + if (pgut_dbname) + config->pgdatabase = pgut_dbname; + if (host) + config->pghost = host; + if (port) + config->pgport = port; + if (username) + config->pguser = username; + + if (retention_redundancy) + config->retention_redundancy = retention_redundancy; + if (retention_window) + config->retention_window = retention_window; + + if (show_only) + writeBackupCatalogConfig(stderr, config); + else + writeBackupCatalogConfigFile(config); + + return 0; +} + +void +pgBackupConfigInit(pgBackupConfig *config) +{ + config->system_identifier = 0; + config->pgdata = NULL; + config->pgdatabase = NULL; + config->pghost = NULL; + config->pgport = NULL; + config->pguser = NULL; + + config->retention_redundancy = 0; + config->retention_window = 0; +} + +void +writeBackupCatalogConfig(FILE *out, pgBackupConfig *config) +{ + fprintf(out, "#Backup instance info\n"); + fprintf(out, "PGDATA = %s\n", config->pgdata); + fprintf(out, "system-identifier = %li\n", config->system_identifier); + + fprintf(out, "#Connection parameters:\n"); + if (config->pgdatabase) + fprintf(out, "PGDATABASE = %s\n", config->pgdatabase); + if (config->pghost) + fprintf(out, "PGHOST = %s\n", config->pghost); + if (config->pgport) + fprintf(out, "PGPORT = %s\n", config->pgport); + if (config->pguser) + fprintf(out, "PGUSER = %s\n", config->pguser); + fprintf(out, "#Retention parameters:\n"); + if (config->retention_redundancy) + fprintf(out, "retention-redundancy = %u\n", config->retention_redundancy); + if (config->retention_window) + fprintf(out, "retention-window = %u\n", config->retention_window); + +} + +void +writeBackupCatalogConfigFile(pgBackupConfig *config) +{ + char path[MAXPGPATH]; + FILE *fp; + + join_path_components(path, backup_path, BACKUPS_DIR); + join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE); + fp = fopen(path, "wt"); + if (fp == NULL) + elog(ERROR, "cannot create %s: %s", + BACKUP_CATALOG_CONF_FILE, strerror(errno)); + + writeBackupCatalogConfig(fp, config); + fclose(fp); +} + + +pgBackupConfig* +readBackupCatalogConfigFile(void) +{ + pgBackupConfig *config = pgut_new(pgBackupConfig); + char path[MAXPGPATH]; + + pgut_option options[] = + { + /* configure options */ + { 'U', 0, "system-identifier", &(config->system_identifier), SOURCE_FILE_STRICT }, + { 's', 0, "pgdata", &(config->pgdata), SOURCE_FILE_STRICT }, + { 's', 0, "pgdatabase", &(config->pgdatabase), SOURCE_FILE_STRICT }, + { 's', 0, "pghost", &(config->pghost), SOURCE_FILE_STRICT }, + { 's', 0, "pgport", &(config->pgport), SOURCE_FILE_STRICT }, + { 's', 0, "pguser", &(config->pguser), SOURCE_FILE_STRICT }, + { 'u', 0, "retention-redundancy", &(config->retention_redundancy),SOURCE_FILE_STRICT }, + { 'u', 0, "retention-window", &(config->retention_window), SOURCE_FILE_STRICT }, + {0} + }; + + join_path_components(path, backup_path, BACKUPS_DIR); + join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE); + + pgBackupConfigInit(config); + pgut_readopt(path, options, ERROR); + + return config; + +} diff --git a/init.c b/init.c index c7cb6581..9a85022d 100644 --- a/init.c +++ b/init.c @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------- * - * init.c: manage backup catalog. + * init.c: - initialize backup catalog. * * Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION * Portions Copyright (c) 2015-2017, Postgres Professional @@ -30,11 +30,10 @@ do_init(void) { char path[MAXPGPATH]; char arclog_path_dir[MAXPGPATH]; - FILE *fp; - uint64 id; struct dirent **dp; int results; + pgBackupConfig *config = pgut_new(pgBackupConfig); /* PGDATA is always required */ if (pgdata == NULL) @@ -49,7 +48,7 @@ do_init(void) } /* Read system_identifier from PGDATA */ - id = get_system_identifier(); + system_identifier = get_system_identifier(); /* create backup catalog root directory */ dir_create_dir(backup_path, DIR_PERMISSION); @@ -62,16 +61,14 @@ do_init(void) join_path_components(arclog_path_dir, backup_path, "wal"); dir_create_dir(arclog_path_dir, DIR_PERMISSION); - /* create BACKUP_CATALOG_CONF_FILE */ - join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE); - fp = fopen(path, "wt"); - if (fp == NULL) - elog(ERROR, "cannot create %s: %s", - BACKUP_CATALOG_CONF_FILE, strerror(errno)); - - fprintf(fp, "system-identifier = %li\n", id); - fprintf(fp, "\n"); - fclose(fp); + /* + * Wite initial config. system-identifier and pgdata are set in + * init subcommand and will never be updated. + */ + pgBackupConfigInit(config); + config->system_identifier = system_identifier; + config->pgdata = pgdata; + writeBackupCatalogConfigFile(config); return 0; } diff --git a/pg_probackup.c b/pg_probackup.c index d023f67a..f9768e88 100644 --- a/pg_probackup.c +++ b/pg_probackup.c @@ -90,10 +90,10 @@ static pgut_option options[] = /* other */ { 'U', 15, "system-identifier", &system_identifier, SOURCE_FILE_STRICT }, - { 's', 'd', "dbname" , &pgut_dbname, SOURCE_CMDLINE }, - { 's', 'h', "host" , &host, SOURCE_CMDLINE }, - { 's', 'p', "port" , &port, SOURCE_CMDLINE }, - { 's', 'U', "username" , &username, SOURCE_CMDLINE }, + { 's', 'd', "pgdatabase" , &pgut_dbname, SOURCE_CMDLINE }, + { 's', 'h', "pghost" , &host, SOURCE_CMDLINE }, + { 's', 'p', "pgport" , &port, SOURCE_CMDLINE }, + { 's', 'U', "pguser" , &username, SOURCE_CMDLINE }, { 'b', 'q', "quiet" , &quiet, SOURCE_CMDLINE }, { 'b', 'v', "verbose" , &verbose, SOURCE_CMDLINE }, { 'B', 'w', "no-password" , &prompt_password, SOURCE_CMDLINE }, @@ -148,9 +148,13 @@ main(int argc, char *argv[]) /* Parse command line arguments */ i = pgut_getopt(argc, argv, options); - /* BACKUP_PATH is always required */ if (backup_path == NULL) - elog(ERROR, "required parameter not specified: BACKUP_PATH (-B, --backup-path)"); + { + /* Try to read BACKUP_PATH from environment variable */ + backup_path = getenv("BACKUP_PATH"); + if (backup_path == NULL) + elog(ERROR, "required parameter not specified: BACKUP_PATH (-B, --backup-path)"); + } else { char path[MAXPGPATH]; @@ -162,8 +166,16 @@ main(int argc, char *argv[]) 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, BACKUP_CATALOG_CONF_FILE); - pgut_readopt(path, options, ERROR); + /* Do not read options from file or env if we're going to set them */ + if (backup_subcmd != CONFIGURE) + { + /* Read options from configuration file */ + join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE); + pgut_readopt(path, options, ERROR); + + /* Read environment variables */ + pgut_getopt_env(options); + } } if (backup_id_string_param != NULL) @@ -225,19 +237,13 @@ main(int argc, char *argv[]) case DELETE: return do_delete(current.backup_id); case CONFIGURE: - elog(ERROR, "not implemented yet"); + /* TODO fixit */ + if (argc == 4) + return do_configure(true); + else + return do_configure(false); } -// if (pg_strcasecmp(cmd, "retention") == 0) -// { -// if (subcmd == NULL) -// elog(ERROR, "you must specify retention command"); -// else if (pg_strcasecmp(subcmd, "show") == 0) -// return do_retention_show(); -// else if (pg_strcasecmp(subcmd, "purge") == 0) -// return do_retention_purge(); -// } - return 0; } diff --git a/pg_probackup.h b/pg_probackup.h index 29b21945..418f074d 100644 --- a/pg_probackup.h +++ b/pg_probackup.h @@ -114,6 +114,19 @@ typedef enum ProbackupSubcmd #define INVALID_BACKUP_ID 0 #define BYTES_INVALID (-1) +typedef struct pgBackupConfig +{ + uint64 system_identifier; + char *pgdata; + const char *pgdatabase; + const char *pghost; + const char *pgport; + const char *pguser; + + uint32 retention_redundancy; + uint32 retention_window; +} pgBackupConfig; + /* Information about single backup stored in backup.conf */ typedef struct pgBackup { @@ -262,6 +275,13 @@ extern void opt_tablespace_map(pgut_option *opt, const char *arg); /* in init.c */ extern int do_init(void); +/* in configure.c */ +extern int do_configure(bool show_only); +extern void pgBackupConfigInit(pgBackupConfig *config); +extern void writeBackupCatalogConfig(FILE *out, pgBackupConfig *config); +extern void writeBackupCatalogConfigFile(pgBackupConfig *config); +extern pgBackupConfig* readBackupCatalogConfigFile(void); + /* in show.c */ extern int do_show(time_t requested_backup_id); diff --git a/pgut/pgut.c b/pgut/pgut.c index 4bc7f7cc..e961c1d1 100644 --- a/pgut/pgut.c +++ b/pgut/pgut.c @@ -505,12 +505,8 @@ longopts_to_optstring(const struct option opts[]) return result; } -/* - * Read options from environmental variables. - * Do not overwrite in option was already set via command line option. - */ -static void -option_from_env(pgut_option options[]) +void +pgut_getopt_env(pgut_option options[]) { size_t i; @@ -523,9 +519,6 @@ option_from_env(pgut_option options[]) if (opt->source > SOURCE_ENV || opt->allowed < SOURCE_ENV) continue; - if (strcmp(opt->lname, "backup-path") == 0) - value = getenv("BACKUP_PATH"); - if (strcmp(opt->lname, "pgdata") == 0) value = getenv("PGDATA"); if (strcmp(opt->lname, "port") == 0) @@ -534,7 +527,7 @@ option_from_env(pgut_option options[]) value = getenv("PGHOST"); if (strcmp(opt->lname, "username") == 0) value = getenv("PGUSER"); - if (strcmp(opt->lname, "dbname") == 0) + if (strcmp(opt->lname, "pgdatabase") == 0) { value = getenv("PGDATABASE"); if (value == NULL) @@ -575,9 +568,6 @@ pgut_getopt(int argc, char **argv, pgut_option options[]) assign_option(opt, optarg, SOURCE_CMDLINE); } - /* Read environment variables */ - option_from_env(options); - init_cancel_handler(); atexit(on_cleanup); diff --git a/pgut/pgut.h b/pgut/pgut.h index 889467a7..5afbd0ff 100644 --- a/pgut/pgut.h +++ b/pgut/pgut.h @@ -92,6 +92,7 @@ extern bool interrupted; extern void help(bool details); extern int pgut_getopt(int argc, char **argv, pgut_option options[]); extern void pgut_readopt(const char *path, pgut_option options[], int elevel); +extern void pgut_getopt_env(pgut_option options[]); extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata); extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata);