diff --git a/Makefile b/Makefile index 1fe7ec6b..bbf778fd 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ HEADERS = $(wildcard *.h) repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o -repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o +repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o dirmod.o DATA = repmgr.sql uninstall_repmgr.sql diff --git a/dirmod.c b/dirmod.c new file mode 100644 index 00000000..82837f66 --- /dev/null +++ b/dirmod.c @@ -0,0 +1,194 @@ +/* + * + * dirmod.c + * directory handling functions + * + * Copyright (C) 2ndQuadrant, 2010-2016 + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "postgres_fe.h" + +/* Don't modify declarations in system headers */ + +#include +#include +#include + +/* + * pgfnames + * + * return a list of the names of objects in the argument directory. Caller + * must call pgfnames_cleanup later to free the memory allocated by this + * function. + */ +char ** +pgfnames(const char *path) +{ + DIR *dir; + struct dirent *file; + char **filenames; + int numnames = 0; + int fnsize = 200; /* enough for many small dbs */ + + dir = opendir(path); + if (dir == NULL) + { + return NULL; + } + + filenames = (char **) palloc(fnsize * sizeof(char *)); + + while (errno = 0, (file = readdir(dir)) != NULL) + { + if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0) + { + if (numnames + 1 >= fnsize) + { + fnsize *= 2; + filenames = (char **) repalloc(filenames, + fnsize * sizeof(char *)); + } + filenames[numnames++] = pstrdup(file->d_name); + } + } + + if (errno) + { + fprintf(stderr, _("could not read directory \"%s\": %s\n"), + path, strerror(errno)); + } + + filenames[numnames] = NULL; + + if (closedir(dir)) + { + fprintf(stderr, _("could not close directory \"%s\": %s\n"), + path, strerror(errno)); + } + + return filenames; +} + + +/* + * pgfnames_cleanup + * + * deallocate memory used for filenames + */ +void +pgfnames_cleanup(char **filenames) +{ + char **fn; + + for (fn = filenames; *fn; fn++) + pfree(*fn); + + pfree(filenames); +} + + +/* + * rmtree + * + * Delete a directory tree recursively. + * Assumes path points to a valid directory. + * Deletes everything under path. + * If rmtopdir is true deletes the directory too. + * Returns true if successful, false if there was any problem. + * (The details of the problem are reported already, so caller + * doesn't really have to say anything more, but most do.) + */ +bool +rmtree(const char *path, bool rmtopdir) +{ + bool result = true; + char pathbuf[MAXPGPATH]; + char **filenames; + char **filename; + struct stat statbuf; + + /* + * we copy all the names out of the directory before we start modifying + * it. + */ + filenames = pgfnames(path); + + if (filenames == NULL) + return false; + + /* now we have the names we can start removing things */ + for (filename = filenames; *filename; filename++) + { + snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename); + + /* + * It's ok if the file is not there anymore; we were just about to + * delete it anyway. + * + * This is not an academic possibility. One scenario where this + * happens is when bgwriter has a pending unlink request for a file in + * a database that's being dropped. In dropdb(), we call + * ForgetDatabaseFsyncRequests() to flush out any such pending unlink + * requests, but because that's asynchronous, it's not guaranteed that + * the bgwriter receives the message in time. + */ + if (lstat(pathbuf, &statbuf) != 0) + { + if (errno != ENOENT) + { + result = false; + } + continue; + } + + if (S_ISDIR(statbuf.st_mode)) + { + /* call ourselves recursively for a directory */ + if (!rmtree(pathbuf, true)) + { + /* we already reported the error */ + result = false; + } + } + else + { + if (unlink(pathbuf) != 0) + { + if (errno != ENOENT) + { + result = false; + } + } + } + } + + if (rmtopdir) + { + if (rmdir(path) != 0) + { + result = false; + } + } + + pgfnames_cleanup(filenames); + + return result; +} + diff --git a/dirmod.h b/dirmod.h new file mode 100644 index 00000000..47caf982 --- /dev/null +++ b/dirmod.h @@ -0,0 +1,23 @@ +/* + * dirmod.h + * Copyright (c) 2ndQuadrant, 2010-2016 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef _DIRMOD_H_ +#define _DIRMOD_H_ + +#endif diff --git a/repmgr.c b/repmgr.c index 6e7f10bb..aa340b62 100644 --- a/repmgr.c +++ b/repmgr.c @@ -1332,6 +1332,8 @@ do_standby_clone(void) char *first_wal_segment = NULL; char *last_wal_segment = NULL; + char xlog_dir[MAXLEN] = ""; + PQExpBufferData event_details; @@ -1997,15 +1999,16 @@ stop_backup: * Remove any existing WAL from the target directory, since * rsync's --exclude option doesn't do it. */ - maxlen_snprintf(script, "rm -rf %s/pg_xlog/*", - local_data_directory); - r = system(script); - if (r != 0) + + maxlen_snprintf(xlog_dir, "%s/pg_xlog/", local_data_directory); + + if (!rmtree(xlog_dir, false)) { - log_err(_("unable to empty local WAL directory %s/pg_xlog/\n"), - local_data_directory); + log_err(_("unable to empty local WAL directory %s\n"), + xlog_dir); exit(ERR_BAD_RSYNC); } + } /* diff --git a/repmgr.h b/repmgr.h index 780dccf7..4141c7fd 100644 --- a/repmgr.h +++ b/repmgr.h @@ -28,6 +28,7 @@ #include "dbutils.h" #include "errcode.h" #include "config.h" +#include "dirmod.h" #define MIN_SUPPORTED_VERSION "9.3" #define MIN_SUPPORTED_VERSION_NUM 90300