HAMMER Utilities: MFC all changes as of 20080924 - through 'hammer cleanup'.
authorMatthew Dillon <dillon@dragonflybsd.org>
Thu, 25 Sep 2008 01:39:33 +0000 (25 01:39 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Thu, 25 Sep 2008 01:39:33 +0000 (25 01:39 +0000)
sbin/hammer/cmd_cleanup.c [new file with mode: 0644]
sbin/hammer/cmd_pseudofs.c
sbin/hammer/hammer.8
sbin/hammer/hammer.c
sbin/hammer/hammer.h

diff --git a/sbin/hammer/cmd_cleanup.c b/sbin/hammer/cmd_cleanup.c
new file mode 100644 (file)
index 0000000..8fc1e57
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.4.2.2 2008/09/25 01:39:33 dillon Exp $
+ */
+/*
+ * Clean up a specific HAMMER filesystem or all HAMMER filesystems.
+ *
+ * Each filesystem is expected to have a <mount>/snapshots directory.
+ * No cleanup will be performed on any filesystem that does not.  If
+ * no filesystems are specified the 'df' program is run and any HAMMER
+ * or null-mounted hammer PFS's are extracted.
+ *
+ * The snapshots directory may contain a config file called 'config'.  If
+ * no config file is present one will be created with the following
+ * defaults:
+ *
+ *     snapshots 1d 60d        (0d 60d for /tmp, /var/tmp, /usr/obj)
+ *     prune     1d 5m
+ *     reblock   1d 5m
+ *     recopy    30d 5m
+ *
+ * All hammer commands create and maintain cycle files in the snapshots
+ * directory.
+ */
+
+#include "hammer.h"
+
+struct didpfs {
+       struct didpfs *next;
+       uuid_t          uuid;
+};
+
+static void do_cleanup(const char *path);
+static int strtosecs(char *ptr);
+static const char *dividing_slash(const char *path);
+static int check_period(const char *snapshots_path, const char *cmd, int arg1,
+                       time_t *savep);
+static void save_period(const char *snapshots_path, const char *cmd,
+                       time_t savet);
+static int check_softlinks(const char *snapshots_path);
+static void cleanup_softlinks(const char *path, const char *snapshots_path,
+                       int arg2);
+static int check_expired(const char *fpath, int arg2);
+
+static int cleanup_snapshots(const char *path, const char *snapshots_path,
+                             int arg1, int arg2);
+static int cleanup_prune(const char *path, const char *snapshots_path,
+                             int arg1, int arg2, int snapshots_disabled);
+static int cleanup_reblock(const char *path, const char *snapshots_path,
+                             int arg1, int arg2);
+static int cleanup_recopy(const char *path, const char *snapshots_path,
+                             int arg1, int arg2);
+
+static void runcmd(int *resp, const char *ctl, ...);
+
+#define WS     " \t\r\n"
+
+struct didpfs *FirstPFS;
+
+void
+hammer_cmd_cleanup(char **av, int ac)
+{
+       FILE *fp;
+       char *ptr;
+       char *path;
+       char buf[256];
+
+       tzset();
+       if (ac == 0) {
+               fp = popen("df -t hammer,null", "r");
+               if (fp == NULL)
+                       errx(1, "hammer cleanup: 'df' failed");
+               while (fgets(buf, sizeof(buf), fp) != NULL) {
+                       ptr = strtok(buf, WS);
+                       if (ptr && strcmp(ptr, "Filesystem") == 0)
+                               continue;
+                       if (ptr)
+                               ptr = strtok(NULL, WS);
+                       if (ptr)
+                               ptr = strtok(NULL, WS);
+                       if (ptr)
+                               ptr = strtok(NULL, WS);
+                       if (ptr)
+                               ptr = strtok(NULL, WS);
+                       if (ptr) {
+                               path = strtok(NULL, WS);
+                               if (path)
+                                       do_cleanup(path);
+                       }
+               }
+               fclose(fp);
+       } else {
+               while (ac) {
+                       do_cleanup(*av);
+                       --ac;
+                       ++av;
+               }
+       }
+}
+
+static
+void
+do_cleanup(const char *path)
+{
+       struct hammer_ioc_pseudofs_rw pfs;
+       union hammer_ioc_mrecord_any mrec_tmp;
+       char *snapshots_path;
+       char *config_path;
+       struct stat st;
+       char *cmd;
+       char *ptr;
+       int arg1;
+       int arg2;
+       time_t savet;
+       char buf[256];
+       FILE *fp;
+       struct didpfs *didpfs;
+       int snapshots_disabled = 0;
+       int prune_warning = 0;
+       int fd;
+       int r;
+
+       bzero(&pfs, sizeof(pfs));
+       bzero(&mrec_tmp, sizeof(mrec_tmp));
+       pfs.ondisk = &mrec_tmp.pfs.pfsd;
+       pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
+       pfs.pfs_id = -1;
+
+       printf("cleanup %-20s -", path);
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               printf(" unable to access directory: %s\n", strerror(errno));
+               return;
+       }
+       if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
+               printf(" not a HAMMER filesystem: %s\n", strerror(errno));
+               return;
+       }
+       close(fd);
+       if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
+               printf(" unrecognized HAMMER version\n");
+               return;
+       }
+
+       /*
+        * Make sure we have not already handled this PFS.  Several nullfs
+        * mounts might alias the same PFS.
+        */
+       for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
+               if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
+                       printf(" pfs_id %d already handled\n", pfs.pfs_id);
+                       return;
+               }
+       }
+       didpfs = malloc(sizeof(*didpfs));
+       didpfs->next = FirstPFS;
+       FirstPFS = didpfs;
+       didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
+
+       /*
+        * Figure out where the snapshot directory is.
+        */
+       if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
+               asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
+       } else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
+               printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
+               return;
+       } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
+               printf(" WARNING: must configure snapshot dir for PFS slave\n");
+               printf("\tWe suggest <fs>/var/slaves/<name> where "
+                      "<fs> is the base HAMMER fs\n");
+               printf("\tContaining the slave\n");
+               return;
+       } else {
+               asprintf(&snapshots_path,
+                        "%s%ssnapshots", path, dividing_slash(path));
+       }
+
+       /*
+        * Create a snapshot directory if necessary, and a config file if
+        * necessary.
+        */
+       if (stat(snapshots_path, &st) < 0) {
+               if (mkdir(snapshots_path, 0755) != 0) {
+                       free(snapshots_path);
+                       printf(" unable to create snapshot dir \"%s\": %s\n",
+                               snapshots_path, strerror(errno));
+                       return;
+               }
+       }
+       asprintf(&config_path, "%s/config", snapshots_path);
+       if ((fp = fopen(config_path, "r")) == NULL) {
+               fp = fopen(config_path, "w");
+               if (fp == NULL) {
+                       printf(" cannot create %s: %s\n",
+                               config_path, strerror(errno));
+                       return;
+               }
+               if (strcmp(path, "/tmp") == 0 ||
+                   strcmp(path, "/var/tmp") == 0 ||
+                   strcmp(path, "/usr/obj") == 0) {
+                       fprintf(fp, "snapshots 0d 60d\n");
+               } else {
+                       fprintf(fp, "snapshots 1d 60d\n");
+               }
+               fprintf(fp, 
+                       "prune     1d 5m\n"
+                       "reblock   1d 5m\n"
+                       "recopy    30d 10m\n");
+               fclose(fp);
+               fp = fopen(config_path, "r");
+       }
+       if (fp == NULL) {
+               printf(" cannot access %s: %s\n",
+                      config_path, strerror(errno));
+               return;
+       }
+
+       printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
+
+       /*
+        * Process the config file
+        */
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               cmd = strtok(buf, WS);
+               arg1 = 0;
+               arg2 = 0;
+               if ((ptr = strtok(NULL, WS)) != NULL) {
+                       arg1 = strtosecs(ptr);
+                       if ((ptr = strtok(NULL, WS)) != NULL)
+                               arg2 = strtosecs(ptr);
+               }
+
+               printf("%20s - ", cmd);
+               fflush(stdout);
+
+               if (arg1 == 0) {
+                       printf("disabled\n");
+                       if (strcmp(cmd, "snapshots") == 0) {
+                               if (check_softlinks(snapshots_path))
+                                       prune_warning = 1;
+                               else
+                                       snapshots_disabled = 1;
+                       }
+                       continue;
+               }
+
+               r = 1;
+               if (strcmp(cmd, "snapshots") == 0) {
+                       if (check_period(snapshots_path, cmd, arg1, &savet)) {
+                               printf("run\n");
+                               cleanup_softlinks(path, snapshots_path, arg2);
+                               r = cleanup_snapshots(path, snapshots_path,
+                                                 arg1, arg2);
+                       } else {
+                               printf("skip\n");
+                       }
+               } else if (strcmp(cmd, "prune") == 0) {
+                       if (check_period(snapshots_path, cmd, arg1, &savet)) {
+                               if (prune_warning)
+                                       printf("run - WARNING snapshot softlinks present but snapshots disabled\n");
+                               else
+                                       printf("run\n");
+                               r = cleanup_prune(path, snapshots_path,
+                                             arg1, arg2, snapshots_disabled);
+                       } else {
+                               printf("skip\n");
+                       }
+               } else if (strcmp(cmd, "reblock") == 0) {
+                       if (check_period(snapshots_path, cmd, arg1, &savet)) {
+                               printf("run");
+                               fflush(stdout);
+                               if (VerboseOpt)
+                                       printf("\n");
+                               r = cleanup_reblock(path, snapshots_path,
+                                               arg1, arg2);
+                       } else {
+                               printf("skip\n");
+                       }
+               } else if (strcmp(cmd, "recopy") == 0) {
+                       if (check_period(snapshots_path, cmd, arg1, &savet)) {
+                               printf("run");
+                               fflush(stdout);
+                               if (VerboseOpt)
+                                       printf("\n");
+                               r = cleanup_recopy(path, snapshots_path,
+                                              arg1, arg2);
+                       } else {
+                               printf("skip\n");
+                       }
+               } else {
+                       printf("unknown directive\n");
+                       r = 1;
+               }
+               if (r == 0)
+                       save_period(snapshots_path, cmd, savet);
+       }
+       fclose(fp);
+       usleep(1000);
+}
+
+static
+int
+strtosecs(char *ptr)
+{
+       int val;
+
+       val = strtol(ptr, &ptr, 0);
+       switch(*ptr) {
+       case 'd':
+               val *= 24;
+               /* fall through */
+       case 'h':
+               val *= 60;
+               /* fall through */
+       case 'm':
+               val *= 60;
+               /* fall through */
+       case 's':
+               break;
+       default:
+               errx(1, "illegal suffix converting %s\n", ptr);
+               break;
+       }
+       return(val);
+}
+
+static const char *
+dividing_slash(const char *path)
+{
+       int len = strlen(path);
+       if (len && path[len-1] == '/')
+               return("");
+       else
+               return("/");
+}
+
+/*
+ * Check whether the desired period has elapsed since the last successful
+ * run.  The run may take a while and cross a boundary so we remember the
+ * current time_t so we can save it later on.
+ *
+ * Periods in minutes, hours, or days are assumed to have been crossed
+ * if the local time crosses a minute, hour, or day boundary regardless
+ * of how close the last operation actually was.
+ */
+static int
+check_period(const char *snapshots_path, const char *cmd, int arg1,
+       time_t *savep)
+{
+       char *check_path;
+       struct tm tp1;
+       struct tm tp2;
+       FILE *fp;
+       time_t baset, lastt;
+       char buf[256];
+
+       time(savep);
+       localtime_r(savep, &tp1);
+
+       /*
+        * Retrieve the start time of the last successful operation.
+        */
+       asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
+       fp = fopen(check_path, "r");
+       free(check_path);
+       if (fp == NULL)
+               return(1);
+       if (fgets(buf, sizeof(buf), fp) == NULL) {
+               fclose(fp);
+               return(1);
+       }
+       fclose(fp);
+
+       lastt = strtol(buf, NULL, 0);
+       localtime_r(&lastt, &tp2);
+
+       /*
+        * Normalize the times.  e.g. if asked to do something on a 1-day
+        * interval the operation will be performed as soon as the day
+        * turns over relative to the previous operation, even if the previous
+        * operation ran a few seconds ago just before midnight.
+        */
+       if (arg1 % 60 == 0) {
+               tp1.tm_sec = 0;
+               tp2.tm_sec = 0;
+       }
+       if (arg1 % (60 * 60) == 0) {
+               tp1.tm_min = 0;
+               tp2.tm_min = 0;
+       }
+       if (arg1 % (24 * 60 * 60) == 0) {
+               tp1.tm_hour = 0;
+               tp2.tm_hour = 0;
+       }
+
+       baset = mktime(&tp1);
+       lastt = mktime(&tp2);
+
+#if 0
+       printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
+#endif
+
+       if ((int)(baset - lastt) >= arg1)
+               return(1);
+       return(0);
+}
+
+/*
+ * Store the start time of the last successful operation.
+ */
+static void
+save_period(const char *snapshots_path, const char *cmd,
+                       time_t savet)
+{
+       char *ocheck_path;
+       char *ncheck_path;
+       FILE *fp;
+
+       asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
+       asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
+       fp = fopen(ncheck_path, "w");
+       fprintf(fp, "0x%08llx\n", (long long)savet);
+       if (fclose(fp) == 0)
+               rename(ncheck_path, ocheck_path);
+       remove(ncheck_path);
+}
+
+/*
+ * Simply count the number of softlinks in the snapshots dir
+ */
+static int
+check_softlinks(const char *snapshots_path)
+{
+       struct dirent *den;
+       struct stat st;
+       DIR *dir;
+       char *fpath;
+       int res = 0;
+
+       if ((dir = opendir(snapshots_path)) != NULL) {
+               while ((den = readdir(dir)) != NULL) {
+                       if (den->d_name[0] == '.')
+                               continue;
+                       asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
+                       if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
+                               ++res;
+                       free(fpath);
+               }
+               closedir(dir);
+       }
+       return(res);
+}
+
+/*
+ * Clean up expired softlinks in the snapshots dir
+ */
+static void
+cleanup_softlinks(const char *path __unused, const char *snapshots_path, int arg2)
+{
+       struct dirent *den;
+       struct stat st;
+       DIR *dir;
+       char *fpath;
+
+       if ((dir = opendir(snapshots_path)) != NULL) {
+               while ((den = readdir(dir)) != NULL) {
+                       if (den->d_name[0] == '.')
+                               continue;
+                       asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
+                       if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
+                           strncmp(den->d_name, "snap-", 5) == 0) {
+                               if (check_expired(den->d_name, arg2)) {
+                                       if (VerboseOpt) {
+                                               printf("    expire %s\n",
+                                                       fpath);
+                                       }
+                                       remove(fpath);
+                               }
+                       }
+                       free(fpath);
+               }
+               closedir(dir);
+       }
+}
+
+/*
+ * Take a softlink path in the form snap-yyyymmdd-hhmm and the
+ * expiration in seconds (arg2) and return non-zero if the softlink
+ * has expired.
+ */
+static int
+check_expired(const char *fpath, int arg2)
+{
+       struct tm tm;
+       time_t t;
+       int year;
+       int month;
+       int day;
+       int hour;
+       int minute;
+       int r;
+
+       r = sscanf(fpath, "snap-%4d%2d%2d-%2d%2d",
+                  &year, &month, &day, &hour, &minute);
+       if (r == 5) {
+               bzero(&tm, sizeof(tm));
+               tm.tm_isdst = -1;
+               tm.tm_min = minute;
+               tm.tm_hour = hour;
+               tm.tm_mday = day;
+               tm.tm_mon = month - 1;
+               tm.tm_year = year - 1900;
+               t = time(NULL) - mktime(&tm);
+               if ((int)t > arg2)
+                       return(1);
+       }
+       return(0);
+}
+
+/*
+ * Issue a snapshot.
+ */
+static int
+cleanup_snapshots(const char *path __unused, const char *snapshots_path,
+                 int arg1 __unused, int arg2 __unused)
+{
+       int r;
+
+       runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
+       return(r);
+}
+
+static int
+cleanup_prune(const char *path __unused, const char *snapshots_path,
+                 int arg1 __unused, int arg2, int snapshots_disabled)
+{
+       /*
+        * If snapshots have been disabled run prune-everything instead
+        * of prune.
+        */
+       if (snapshots_disabled && arg2) {
+               runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
+                       snapshots_path, arg2, path);
+       } else if (snapshots_disabled) {
+               runcmd(NULL, "hammer prune-everything %s", path);
+       } else if (arg2) {
+               runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
+                       snapshots_path, arg2, snapshots_path);
+       } else {
+               runcmd(NULL, "hammer prune %s", snapshots_path);
+       }
+       return(0);
+}
+
+static int
+cleanup_reblock(const char *path, const char *snapshots_path,
+                 int arg1 __unused, int arg2)
+{
+       if (VerboseOpt == 0) {
+               printf(".");
+               fflush(stdout);
+       }
+       runcmd(NULL,
+              "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s 95",
+              snapshots_path, arg2, path);
+       if (VerboseOpt == 0) {
+               printf(".");
+               fflush(stdout);
+       }
+       runcmd(NULL,
+              "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s 95",
+              snapshots_path, arg2, path);
+       if (VerboseOpt == 0) {
+               printf(".");
+               fflush(stdout);
+       }
+       runcmd(NULL,
+              "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
+              snapshots_path, arg2, path);
+       if (VerboseOpt == 0)
+               printf("\n");
+       return(0);
+}
+
+static int
+cleanup_recopy(const char *path, const char *snapshots_path,
+                 int arg1 __unused, int arg2)
+{
+       if (VerboseOpt == 0) {
+               printf(".");
+               fflush(stdout);
+       }
+       runcmd(NULL,
+              "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
+              snapshots_path, arg2, path);
+       if (VerboseOpt == 0) {
+               printf(".");
+               fflush(stdout);
+       }
+       runcmd(NULL,
+              "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
+              snapshots_path, arg2, path);
+       if (VerboseOpt == 0) {
+               printf(".");
+               fflush(stdout);
+       }
+       runcmd(NULL,
+              "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
+              snapshots_path, arg2, path);
+       if (VerboseOpt == 0)
+               printf("\n");
+       return(0);
+}
+
+static
+void
+runcmd(int *resp, const char *ctl, ...)
+{
+       va_list va;
+       char *cmd;
+       char *arg;
+       char **av;
+       int n;
+       int nmax;
+       int res;
+       pid_t pid;
+
+       /*
+        * Generate the command
+        */
+       va_start(va, ctl);
+       vasprintf(&cmd, ctl, va);
+       va_end(va);
+       if (VerboseOpt)
+               printf("    %s\n", cmd);
+
+       /*
+        * Break us down into arguments.  We do not just use system() here
+        * because it blocks SIGINT and friends.
+        */
+       n = 0;
+       nmax = 16;
+       av = malloc(sizeof(char *) * nmax);
+
+       for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
+               if (n == nmax - 1) {
+                       nmax += 16;
+                       av = realloc(av, sizeof(char *) * nmax);
+               }
+               av[n++] = arg;
+       }
+       av[n++] = NULL;
+
+       /*
+        * Run the command.
+        */
+       if ((pid = fork()) == 0) {
+               if (VerboseOpt < 2) {
+                       int fd = open("/dev/null", O_RDWR);
+                       dup2(fd, 1);
+                       close(fd);
+               }
+               execvp(av[0], av);
+               _exit(127);
+       } else if (pid < 0) {
+               res = 127;
+       } else {
+               int status;
+               while (waitpid(pid, &status, 0) != pid)
+                       ;
+               res = WEXITSTATUS(status);
+       }
+
+       free(cmd);
+       free(av);
+       if (resp)
+               *resp = res;
+}
+
+
index 2503956..f06a272 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.6.2.3 2008/09/09 23:41:13 dillon Exp $
+ * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.6.2.4 2008/09/25 01:39:33 dillon Exp $
  */
 
 #include "hammer.h"
@@ -474,10 +474,19 @@ dump_pfsd(hammer_pseudofs_data_t pfsd)
                printf("    slave\n");
        }
        printf("    label=\"%s\"\n", pfsd->label);
-       if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE)
+       if (pfsd->snapshots[0])
+               printf("    snapshots=\"%s\"\n", pfsd->snapshots);
+       if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) {
                printf("    operating as a SLAVE\n");
-       else
+               if (pfsd->snapshots[0] == 0)
+                       printf("    snapshots directory not set for slave\n");
+       } else {
                printf("    operating as a MASTER\n");
+               if (pfsd->snapshots[0] == 0) {
+                       printf("    snapshots dir for master "
+                              "defaults to <fs>/snapshots\n");
+               }
+       }
 }
 
 static void
@@ -524,6 +533,29 @@ parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd)
                                exit(1);
                        }
                        snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr);
+               } else if (strcmp(cmd, "snapshots") == 0) {
+                       len = strlen(ptr);
+                       if (ptr[0] != '/') {
+                               fprintf(stderr,
+                                       "option %s: '%s' must be an "
+                                       "absolute path\n", cmd, ptr);
+                               if (ptr[0] == 0) {
+                                       fprintf(stderr, 
+                                               "use 'snapshots-clear' "
+                                               "to unset snapshots dir\n");
+                               }
+                               exit(1);
+                       }
+                       if (len >= (int)sizeof(pfsd->snapshots)) {
+                               fprintf(stderr,
+                                       "option %s: path too long, %d "
+                                       "character limit\n", cmd, len);
+                               exit(1);
+                       }
+                       snprintf(pfsd->snapshots, sizeof(pfsd->snapshots),
+                                "%s", ptr);
+               } else if (strcmp(cmd, "snapshots-clear") == 0) {
+                       pfsd->snapshots[0] = 0;
                } else {
                        fprintf(stderr, "invalid option: %s\n", cmd);
                        exit(1);
@@ -557,6 +589,8 @@ pseudofs_usage(int code)
                "    shared-uuid=0x16llx\n"
                "    unique-uuid=0x16llx\n"
                "    label=\"string\"\n"
+               "    snapshots=\"/path\"\n"
+               "    snapshots-clear\n"
        );
        exit(code);
 }
index 6443658..c5887bb 100644 (file)
@@ -30,7 +30,7 @@
 .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\" 
-.\" $DragonFly: src/sbin/hammer/hammer.8,v 1.38.2.5 2008/09/09 23:41:13 dillon Exp $
+.\" $DragonFly: src/sbin/hammer/hammer.8,v 1.38.2.6 2008/09/25 01:39:33 dillon Exp $
 .Dd July 27, 2008
 .Dt HAMMER 8
 .Os
@@ -181,6 +181,42 @@ Generate the top 32 bits of a
 .Nm HAMMER
 64 bit directory hash for the specified
 file name.
+.\" ==== cleanup ====
+.It Ar cleanup Op Ar filesystem
+This is a meta-command which executes snapshot, pruning, and reblocking
+commands on the specified HAMMER filesystem.
+If no filesystem is specified this command will scan all HAMMER and nullfs
+mounts, extract PFS id's, and clean-up each PFS found.
+.Pp
+This command will access a
+.Ar snapshots
+subdirectory and a
+.Ar snapshots/config
+file for each filesystem, creating them if necessary.
+The default configuration file will create a daily snapshot, do a daily
+pruning and reblocking run with a fragmentation level of 95%, and
+a monthly unconditional reblocking run.
+.Pp
+All operations are limited to 5 minutes per function by default.
+Reblocking runs are broken down into three separate functions and
+take 15 minutes.  
+Also note that this directive will disable snapshots on
+.Pa /tmp ,
+.Pa /var/tmp ,
+and
+.Pa /usr/obj
+by default.
+.Pp
+The defaults may be adjusted by modifying the config file.
+The pruning and reblocking commands automatically maintain a cyclefile
+for incremental operation.
+If you ^C the program the cyclefile will be updated, but a sub-command
+may continue to run in the background for a few seconds until the HAMMER
+ioctl detects the interrupt.
+.Pp
+Work on this command is still in progress.
+Expected additions:  An ability to remove snapshots dynamically as the
+filesystem becomes full.
 .\" ==== prune ====
 .It Ar prune Ar softlink-dir
 Prune the file system based on previously created snapshot softlinks.
@@ -427,6 +463,23 @@ Set the unique UUID for this file system.  This UUID should not be used
 anywhere else, even on exact copies of the file system.
 .It label=<string>
 Set a descriptive label for this file system.
+.It snapshots=<string>
+Specify the snapshots directory which 'hammer cleanup' will use to manage
+this PFS.  The snapshots directory does not need to be configured for 
+PFS masters and will default to "<fs>/snapshots".
+.Pp
+PFS slaves are mirroring slaves so you cannot configure a snapshots
+directory on the slave itself to be managed by the slave's machine.
+In fact, the slave will likely have a 'snapshots' sub-directory mirrored
+from the master, but that directory contains the configuration the master
+is using for its copy of the filesystem, not the configuration that we
+want to use for our slave.
+.Pp
+It is recommended that "/var/slaves/<name>" be configured for a PFS
+slave, where <name> is an appropriate label.  You can control snapshot
+retention on your slave independant of the master. 
+.It snapshots-clear
+Zero out the snapshots directory path for this PFS.
 .El
 .\" ==== pfs-upgrade ====
 .It Ar pfs-upgrade Ar dirpath
index 23d557b..62d38f3 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/hammer.c,v 1.33.2.3 2008/09/09 23:41:13 dillon Exp $
+ * $DragonFly: src/sbin/hammer/hammer.c,v 1.33.2.4 2008/09/25 01:39:33 dillon Exp $
  */
 
 #include "hammer.h"
@@ -187,6 +187,10 @@ main(int ac, char **av)
                hammer_cmd_softprune(av + 1, ac - 1, 0);
                exit(0);
        }
+       if (strcmp(av[0], "cleanup") == 0) {
+               hammer_cmd_cleanup(av + 1, ac - 1);
+               exit(0);
+       }
        if (strcmp(av[0], "prune-everything") == 0) {
                hammer_cmd_softprune(av + 1, ac - 1, 1);
                exit(0);
@@ -209,6 +213,7 @@ main(int ac, char **av)
                exit(0);
        }
        if (strncmp(av[0], "reblock", 7) == 0) {
+               signal(SIGINT, sigalrm);
                if (strcmp(av[0], "reblock") == 0)
                        hammer_cmd_reblock(av + 1, ac - 1, -1);
                else if (strcmp(av[0], "reblock-btree") == 0)
@@ -300,6 +305,7 @@ usage(int exit_code)
        fprintf(stderr, 
                "hammer -h\n"
                "hammer [-v] [-t seconds] [-c cyclefile] command [argument ...]\n"
+               "hammer cleanup [<filesystem>]\n"
                "hammer synctid <filesystem> [quick]\n"
                "hammer namekey[32] <path>\n"
                "hammer prune <softlink-dir>\n"
index 39a280e..539b32a 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/hammer.h,v 1.23.2.1 2008/08/04 19:41:25 dillon Exp $
+ * $DragonFly: src/sbin/hammer/hammer.h,v 1.23.2.2 2008/09/25 01:39:33 dillon Exp $
  */
 
 #include <sys/types.h>
@@ -91,6 +91,7 @@ void hammer_cmd_pseudofs_upgrade(char **av, int ac);
 void hammer_cmd_pseudofs_downgrade(char **av, int ac);
 void hammer_cmd_status(char **av, int ac);
 void hammer_cmd_snapshot(char **av, int ac);
+void hammer_cmd_cleanup(char **av, int ac);
 
 void hammer_get_cycle(hammer_base_elm_t base, hammer_tid_t *tidp);
 void hammer_set_cycle(hammer_base_elm_t base, hammer_tid_t tid);