static pgFile * pgFileNew(const char *path, bool omit_symlink) { struct stat st; pgFile *file; /* stat the file */ if ((omit_symlink ? stat(path, &st) : lstat(path, &st)) == -1) { /* file not found is not an error case */ if (errno == ENOENT) return NULL; ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not stat file \"%s\": %s", path, strerror(errno)))); } file = (pgFile *) pgut_malloc(offsetof(pgFile, path) + strlen(path) + 1); file->mtime = st.st_mtime; file->size = st.st_size; file->read_size = 0; file->write_size = 0; file->mode = st.st_mode; file->crc = 0; file->is_datafile = false; file->linked = NULL; strcpy(file->path, path); /* enough buffer size guaranteed */ return file; }
static List * ParseControlFile(const char *path) { #define LINEBUF 1024 char buf[LINEBUF]; int lineno; FILE *file; List *items = NIL; file = pgut_fopen(path, "rt"); for (lineno = 1; fgets(buf, LINEBUF, file); lineno++) { char *keyword; char *value; int i; if (!ParseControlFileLine(buf, &keyword, &value)) continue; /* PATH_OPTIONS */ for (i = 0; i < NUM_PATH_OPTIONS; i++) { pgut_option *opt = &options[i]; if (pgut_keyeq(keyword, opt->lname)) { pgut_setopt(opt, value, SOURCE_FILE); break; } } /* Other options */ if (i >= NUM_PATH_OPTIONS) { size_t len; char *item; len = strlen(keyword) + strlen(value) + 2; item = pgut_malloc(len); snprintf(item, len, "%s=%s", keyword, value); items = lappend(items, item); if (pg_strcasecmp(item, "TYPE=FUNCTION") == 0) type_function = true; if (pg_strcasecmp(item, "TYPE=BINARY") == 0 || pg_strcasecmp(item, "TYPE=FIXED") == 0) type_binary = true; if (pg_strcasecmp(item, "WRITER=BINARY") == 0) writer_binary = true; } } fclose(file); return items; }
char * strdup_with_len(const char *str, size_t len) { char *r; if (str == NULL) return NULL; r = pgut_malloc(len + 1); memcpy(r, str, len); r[len] = '\0'; return r; }
/* * Convert ISO-8601 format string to time_t value. */ bool parse_time(const char *value, time_t *time) { size_t len; char *tmp; int i; struct tm tm; char junk[2]; /* tmp = replace( value, !isalnum, ' ' ) */ tmp = pgut_malloc(strlen(value) + + 1); len = 0; for (i = 0; value[i]; i++) tmp[len++] = (IsAlnum(value[i]) ? value[i] : ' '); tmp[len] = '\0'; /* parse for "YYYY-MM-DD HH:MI:SS" */ memset(&tm, 0, sizeof(tm)); tm.tm_year = 0; /* tm_year is year - 1900 */ tm.tm_mon = 0; /* tm_mon is 0 - 11 */ tm.tm_mday = 1; /* tm_mday is 1 - 31 */ tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; i = sscanf(tmp, "%04d %02d %02d %02d %02d %02d%1s", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, junk); free(tmp); if (i < 1 || 6 < i) return false; /* adjust year */ if (tm.tm_year < 100) tm.tm_year += 2000 - 1900; else if (tm.tm_year >= 1900) tm.tm_year -= 1900; /* adjust month */ if (i > 1) tm.tm_mon -= 1; /* determine whether Daylight Saving Time is in effect */ tm.tm_isdst = -1; *time = mktime(&tm); return true; }
/* Append the given string `val` to the `list` */ void simple_string_list_append(SimpleStringList *list, const char *val) { SimpleStringListCell *cell; /* this calculation correctly accounts for the null trailing byte */ cell = (SimpleStringListCell *) pgut_malloc(sizeof(SimpleStringListCell) + strlen(val)); cell->next = NULL; strcpy(cell->val, val); if (list->tail) list->tail->next = cell; else list->head = cell; list->tail = cell; }
static char * longopts_to_optstring(const struct option opts[]) { size_t len; char *result; char *s; for (len = 0; opts[len].name; len++) { } result = pgut_malloc(len * 2 + 1); s = result; for (len = 0; opts[len].name; len++) { if (!isprint(opts[len].val)) continue; *s++ = opts[len].val; if (opts[len].has_arg != no_argument) *s++ = ':'; } *s = '\0'; return result; }
/* * Construct parray of pgFile from the file list. * If root is not NULL, path will be absolute path. */ parray * dir_read_file_list(const char *root, const char *file_txt) { FILE *fp; parray *files; char buf[MAXPGPATH * 2]; fp = fopen(file_txt, "rt"); if (fp == NULL) ereport(ERROR, ((errno == ENOENT ? errcode(ERROR_CORRUPTED) : errcode(ERROR_SYSTEM)), errmsg("could not open \"%s\": %s", file_txt, strerror(errno)))); files = parray_new(); while (fgets(buf, lengthof(buf), fp)) { char path[MAXPGPATH]; char type; unsigned long write_size; pg_crc32c crc; unsigned int mode; /* bit length of mode_t depends on platforms */ struct tm tm; pgFile *file; memset(&tm, 0, sizeof(tm)); if (sscanf(buf, "%s %c %lu %u %o %d-%d-%d %d:%d:%d", path, &type, &write_size, &crc, &mode, &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 11) { ereport(ERROR, (errcode(ERROR_CORRUPTED), errmsg("invalid format found in \"%s\"", file_txt))); } if (type != 'f' && type != 'F' && type != 'd' && type != 'l') { ereport(ERROR, (errcode(ERROR_CORRUPTED), errmsg("invalid type '%c' found in \"%s\"", type, file_txt))); } tm.tm_isdst = -1; file = (pgFile *) pgut_malloc(offsetof(pgFile, path) + (root ? strlen(root) + 1 : 0) + strlen(path) + 1); tm.tm_year -= 1900; tm.tm_mon -= 1; file->mtime = mktime(&tm); file->mode = mode | ((type == 'f' || type == 'F') ? S_IFREG : type == 'd' ? S_IFDIR : type == 'l' ? S_IFLNK : 0); file->size = 0; file->read_size = 0; file->write_size = write_size; file->crc = crc; file->is_datafile = (type == 'F' ? true : false); file->linked = NULL; if (root) sprintf(file->path, "%s/%s", root, path); else strcpy(file->path, path); parray_append(files, file); } fclose(fp); /* file.txt is sorted, so this qsort is redundant */ parray_qsort(files, pgFileComparePath); return files; }
/* * Initialize backup catalog. */ int do_init(void) { char path[MAXPGPATH]; char *log_directory = NULL; char *archive_command = NULL; FILE *fp; struct dirent **dp; int results; if (access(backup_path, F_OK) == 0){ results = scandir(backup_path, &dp, selects, NULL); if(results != 0){ elog(ERROR, _("backup catalog already exist. and it's not empty.")); } } /* create backup catalog root directory */ dir_create_dir(backup_path, DIR_PERMISSION); /* create directories for backup of online files */ join_path_components(path, backup_path, RESTORE_WORK_DIR); dir_create_dir(path, DIR_PERMISSION); snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR, PG_XLOG_DIR); dir_create_dir(path, DIR_PERMISSION); snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR, SRVLOG_DIR); dir_create_dir(path, DIR_PERMISSION); /* create directory for timeline history files */ join_path_components(path, backup_path, TIMELINE_HISTORY_DIR); dir_create_dir(path, DIR_PERMISSION); /* read postgresql.conf */ if (pgdata) { join_path_components(path, pgdata, "postgresql.conf"); parse_postgresql_conf(path, &log_directory, &archive_command); } /* create pg_rman.ini */ join_path_components(path, backup_path, PG_RMAN_INI_FILE); fp = fopen(path, "wt"); if (fp == NULL) elog(ERROR_SYSTEM, _("can't create pg_rman.ini: %s"), strerror(errno)); /* set ARCLOG_PATH refered with log_directory */ if (arclog_path == NULL && archive_command && archive_command[0]) { char *command = pgut_strdup(archive_command); char *begin; char *end; char *fname; /* example: 'cp "%p" /path/to/arclog/"%f"' */ for (begin = command; *begin;) { begin = begin + strspn(begin, " \n\r\t\v"); end = begin + strcspn(begin, " \n\r\t\v"); *end = '\0'; if ((fname = strstr(begin, "%f")) != NULL) { while (strchr(" \n\r\t\v\"'", *begin)) begin++; fname--; while (fname > begin && strchr(" \n\r\t\v\"'/", fname[-1])) fname--; *fname = '\0'; if (is_absolute_path(begin)) arclog_path = pgut_strdup(begin); break; } begin = end + 1; } free(command); } if (arclog_path) { fprintf(fp, "ARCLOG_PATH='%s'\n", arclog_path); elog(INFO, "ARCLOG_PATH is set to '%s'", arclog_path); } else if (archive_command && archive_command[0]) elog(WARNING, "ARCLOG_PATH is not set because failed to parse archive_command '%s'." "Please set ARCLOG_PATH in pg_rman.ini or environmental variable", archive_command); else elog(WARNING, "ARCLOG_PATH is not set because archive_command is empty." "Please set ARCLOG_PATH in pg_rman.ini or environmental variable"); /* set SRVLOG_PATH refered with log_directory */ if (srvlog_path == NULL) { if (log_directory) { if (is_absolute_path(log_directory)) srvlog_path = pgut_strdup(log_directory); else { srvlog_path = pgut_malloc(MAXPGPATH); join_path_components(srvlog_path, pgdata, log_directory); } } else if (pgdata) { /* default: log_directory = 'pg_log' */ srvlog_path = pgut_malloc(MAXPGPATH); join_path_components(srvlog_path, pgdata, "pg_log"); } } if (srvlog_path) { fprintf(fp, "SRVLOG_PATH='%s'\n", srvlog_path); elog(INFO, "SRVLOG_PATH is set to '%s'", srvlog_path); } fprintf(fp, "\n"); fclose(fp); free(archive_command); free(log_directory); return 0; }
/* * Create range object from one or two arguments. * All not-digit characters in the argument(s) are ignored. * Both arg1 and arg2 must be valid pointer. */ static void parse_range(pgBackupRange *range, const char *arg1, const char *arg2) { size_t len = strlen(arg1) + strlen(arg2) + 1; char *tmp; int num; struct tm tm; tmp = pgut_malloc(len); tmp[0] = '\0'; if (arg1 != NULL) remove_not_digit(tmp, len, arg1); if (arg2 != NULL) remove_not_digit(tmp + strlen(tmp), len - strlen(tmp), arg2); memset(&tm, 0, sizeof(tm)); tm.tm_year = 0; /* tm_year is year - 1900 */ tm.tm_mon = 0; /* tm_mon is 0 - 11 */ tm.tm_mday = 1; /* tm_mday is 1 - 31 */ tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; num = sscanf(tmp, "%04d %02d %02d %02d %02d %02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if (num < 1) { if (strcmp(tmp,"") != 0) elog(ERROR, "supplied id(%s) is invalid", tmp); else elog(ERROR, "arguments are invalid. near \"%s\"", arg1); } free(tmp); /* adjust year and month to convert to time_t */ tm.tm_year -= 1900; if (num > 1) tm.tm_mon -= 1; tm.tm_isdst = -1; if (!IsValidTime(tm)) elog(ERROR, "supplied time(%s) is invalid.", arg1); range->begin = mktime(&tm); switch (num) { case 1: tm.tm_year++; break; case 2: tm.tm_mon++; break; case 3: tm.tm_mday++; break; case 4: tm.tm_hour++; break; case 5: tm.tm_min++; break; case 6: tm.tm_sec++; break; } range->end = mktime(&tm); range->end--; }