16a6e350fSMatthew Dillon /* 26a6e350fSMatthew Dillon * Copyright (c) 2008 The DragonFly Project. All rights reserved. 36a6e350fSMatthew Dillon * 46a6e350fSMatthew Dillon * This code is derived from software contributed to The DragonFly Project 56a6e350fSMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 66a6e350fSMatthew Dillon * 76a6e350fSMatthew Dillon * Redistribution and use in source and binary forms, with or without 86a6e350fSMatthew Dillon * modification, are permitted provided that the following conditions 96a6e350fSMatthew Dillon * are met: 106a6e350fSMatthew Dillon * 116a6e350fSMatthew Dillon * 1. Redistributions of source code must retain the above copyright 126a6e350fSMatthew Dillon * notice, this list of conditions and the following disclaimer. 136a6e350fSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 146a6e350fSMatthew Dillon * notice, this list of conditions and the following disclaimer in 156a6e350fSMatthew Dillon * the documentation and/or other materials provided with the 166a6e350fSMatthew Dillon * distribution. 176a6e350fSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 186a6e350fSMatthew Dillon * contributors may be used to endorse or promote products derived 196a6e350fSMatthew Dillon * from this software without specific, prior written permission. 206a6e350fSMatthew Dillon * 216a6e350fSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 226a6e350fSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 236a6e350fSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 246a6e350fSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 256a6e350fSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 266a6e350fSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 276a6e350fSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 286a6e350fSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 296a6e350fSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 306a6e350fSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 316a6e350fSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 326a6e350fSMatthew Dillon * SUCH DAMAGE. 336a6e350fSMatthew Dillon * 34bb8e52c0SThomas Nikolajsen * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.6 2008/10/07 22:28:41 thomas Exp $ 356a6e350fSMatthew Dillon */ 366a6e350fSMatthew Dillon /* 376a6e350fSMatthew Dillon * Clean up a specific HAMMER filesystem or all HAMMER filesystems. 386a6e350fSMatthew Dillon * 396a6e350fSMatthew Dillon * Each filesystem is expected to have a <mount>/snapshots directory. 406a6e350fSMatthew Dillon * No cleanup will be performed on any filesystem that does not. If 416a6e350fSMatthew Dillon * no filesystems are specified the 'df' program is run and any HAMMER 426a6e350fSMatthew Dillon * or null-mounted hammer PFS's are extracted. 436a6e350fSMatthew Dillon * 446a6e350fSMatthew Dillon * The snapshots directory may contain a config file called 'config'. If 456a6e350fSMatthew Dillon * no config file is present one will be created with the following 466a6e350fSMatthew Dillon * defaults: 476a6e350fSMatthew Dillon * 485e435c92SMatthew Dillon * snapshots 1d 60d (0d 0d for /tmp, /var/tmp, /usr/obj) 496a6e350fSMatthew Dillon * prune 1d 5m 50*f13fea8bSSascha Wildner * rebalance 1d 5m 516a6e350fSMatthew Dillon * reblock 1d 5m 526a6e350fSMatthew Dillon * recopy 30d 5m 536a6e350fSMatthew Dillon * 546a6e350fSMatthew Dillon * All hammer commands create and maintain cycle files in the snapshots 556a6e350fSMatthew Dillon * directory. 566a6e350fSMatthew Dillon */ 576a6e350fSMatthew Dillon 586a6e350fSMatthew Dillon #include "hammer.h" 596a6e350fSMatthew Dillon 60c453712aSMatthew Dillon struct didpfs { 61c453712aSMatthew Dillon struct didpfs *next; 62c453712aSMatthew Dillon uuid_t uuid; 63c453712aSMatthew Dillon }; 64c453712aSMatthew Dillon 656a6e350fSMatthew Dillon static void do_cleanup(const char *path); 666a6e350fSMatthew Dillon static int strtosecs(char *ptr); 676a6e350fSMatthew Dillon static const char *dividing_slash(const char *path); 686a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1, 696a6e350fSMatthew Dillon time_t *savep); 706a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd, 716a6e350fSMatthew Dillon time_t savet); 72c6c298a7SMatthew Dillon static int check_softlinks(const char *snapshots_path); 73ff1c9800SMatthew Dillon static void cleanup_softlinks(const char *path, const char *snapshots_path, 745e435c92SMatthew Dillon int arg2, char *arg3); 75ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2); 766a6e350fSMatthew Dillon 77cbbb4f37SSimon Schubert static int create_snapshot(const char *path, const char *snapshots_path, 786a6e350fSMatthew Dillon int arg1, int arg2); 790b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path, 800b8bd7daSMatthew Dillon int arg1, int arg2); 816a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path, 82c6c298a7SMatthew Dillon int arg1, int arg2, int snapshots_disabled); 836a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path, 846a6e350fSMatthew Dillon int arg1, int arg2); 856a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path, 866a6e350fSMatthew Dillon int arg1, int arg2); 876a6e350fSMatthew Dillon 886a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...); 896a6e350fSMatthew Dillon 906a6e350fSMatthew Dillon #define WS " \t\r\n" 916a6e350fSMatthew Dillon 92c453712aSMatthew Dillon struct didpfs *FirstPFS; 936a6e350fSMatthew Dillon 946a6e350fSMatthew Dillon void 956a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac) 966a6e350fSMatthew Dillon { 97eac446c5SMatthew Dillon char *fstype, *fs, *path; 98eac446c5SMatthew Dillon struct statfs *stfsbuf; 99eac446c5SMatthew Dillon int mntsize, i; 1006a6e350fSMatthew Dillon 1016a6e350fSMatthew Dillon tzset(); 1026a6e350fSMatthew Dillon if (ac == 0) { 103eac446c5SMatthew Dillon mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT); 104eac446c5SMatthew Dillon if (mntsize > 0) { 105eac446c5SMatthew Dillon for (i=0; i < mntsize; i++) { 106eac446c5SMatthew Dillon /* 107eac446c5SMatthew Dillon * We will cleanup in the case fstype is hammer. 108eac446c5SMatthew Dillon * If we have null-mounted PFS, we check the 109eac446c5SMatthew Dillon * mount source. If it looks like a PFS, we 110eac446c5SMatthew Dillon * proceed to cleanup also. 111eac446c5SMatthew Dillon */ 112eac446c5SMatthew Dillon fstype = stfsbuf[i].f_fstypename; 113eac446c5SMatthew Dillon fs = stfsbuf[i].f_mntfromname; 114eac446c5SMatthew Dillon if ((strcmp(fstype, "hammer") == 0) || 115eac446c5SMatthew Dillon ((strcmp(fstype, "null") == 0) && 116cb3c760cSMatthew Dillon (strstr(fs, "/@@0x") != NULL || 117cb3c760cSMatthew Dillon strstr(fs, "/@@-1") != NULL))) { 118eac446c5SMatthew Dillon path = stfsbuf[i].f_mntonname; 1196a6e350fSMatthew Dillon do_cleanup(path); 1206a6e350fSMatthew Dillon } 121574066d3SMatthew Dillon } 122eac446c5SMatthew Dillon } 123eac446c5SMatthew Dillon 1246a6e350fSMatthew Dillon } else { 1256a6e350fSMatthew Dillon while (ac) { 1266a6e350fSMatthew Dillon do_cleanup(*av); 1276a6e350fSMatthew Dillon --ac; 1286a6e350fSMatthew Dillon ++av; 1296a6e350fSMatthew Dillon } 1306a6e350fSMatthew Dillon } 1316a6e350fSMatthew Dillon } 1326a6e350fSMatthew Dillon 1336a6e350fSMatthew Dillon static 1346a6e350fSMatthew Dillon void 1356a6e350fSMatthew Dillon do_cleanup(const char *path) 1366a6e350fSMatthew Dillon { 1376a6e350fSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1386a6e350fSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 1396a6e350fSMatthew Dillon char *snapshots_path; 1406a6e350fSMatthew Dillon char *config_path; 1416a6e350fSMatthew Dillon struct stat st; 1426a6e350fSMatthew Dillon char *cmd; 1436a6e350fSMatthew Dillon char *ptr; 1446a6e350fSMatthew Dillon int arg1; 1456a6e350fSMatthew Dillon int arg2; 1465e435c92SMatthew Dillon char *arg3; 1476a6e350fSMatthew Dillon time_t savet; 1486a6e350fSMatthew Dillon char buf[256]; 1496a6e350fSMatthew Dillon FILE *fp; 150c453712aSMatthew Dillon struct didpfs *didpfs; 151c6c298a7SMatthew Dillon int snapshots_disabled = 0; 152c6c298a7SMatthew Dillon int prune_warning = 0; 1536a6e350fSMatthew Dillon int fd; 1546a6e350fSMatthew Dillon int r; 1550b8bd7daSMatthew Dillon int found_rebal = 0; 1566a6e350fSMatthew Dillon 1576a6e350fSMatthew Dillon bzero(&pfs, sizeof(pfs)); 1586a6e350fSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 1596a6e350fSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 1606a6e350fSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 1616a6e350fSMatthew Dillon pfs.pfs_id = -1; 1626a6e350fSMatthew Dillon 1636a6e350fSMatthew Dillon printf("cleanup %-20s -", path); 1646a6e350fSMatthew Dillon fd = open(path, O_RDONLY); 1656a6e350fSMatthew Dillon if (fd < 0) { 1666a6e350fSMatthew Dillon printf(" unable to access directory: %s\n", strerror(errno)); 1676a6e350fSMatthew Dillon return; 1686a6e350fSMatthew Dillon } 1696a6e350fSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1706a6e350fSMatthew Dillon printf(" not a HAMMER filesystem: %s\n", strerror(errno)); 1716a6e350fSMatthew Dillon return; 1726a6e350fSMatthew Dillon } 1736a6e350fSMatthew Dillon close(fd); 1746a6e350fSMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 1756a6e350fSMatthew Dillon printf(" unrecognized HAMMER version\n"); 1766a6e350fSMatthew Dillon return; 1776a6e350fSMatthew Dillon } 1786a6e350fSMatthew Dillon 1796a6e350fSMatthew Dillon /* 1806a6e350fSMatthew Dillon * Make sure we have not already handled this PFS. Several nullfs 1816a6e350fSMatthew Dillon * mounts might alias the same PFS. 1826a6e350fSMatthew Dillon */ 183c453712aSMatthew Dillon for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) { 184c453712aSMatthew Dillon if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) { 185bb8e52c0SThomas Nikolajsen printf(" PFS #%d already handled\n", pfs.pfs_id); 1866a6e350fSMatthew Dillon return; 1876a6e350fSMatthew Dillon } 188c453712aSMatthew Dillon } 189c453712aSMatthew Dillon didpfs = malloc(sizeof(*didpfs)); 190c453712aSMatthew Dillon didpfs->next = FirstPFS; 191c453712aSMatthew Dillon FirstPFS = didpfs; 192c453712aSMatthew Dillon didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid; 1936a6e350fSMatthew Dillon 1946a6e350fSMatthew Dillon /* 195ff1c9800SMatthew Dillon * Figure out where the snapshot directory is. 196ff1c9800SMatthew Dillon */ 197ff1c9800SMatthew Dillon if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') { 198ff1c9800SMatthew Dillon asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots); 199ff1c9800SMatthew Dillon } else if (mrec_tmp.pfs.pfsd.snapshots[0]) { 200ff1c9800SMatthew Dillon printf(" WARNING: pfs-slave's snapshots dir is not absolute\n"); 201ff1c9800SMatthew Dillon return; 202ff1c9800SMatthew Dillon } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) { 203ff1c9800SMatthew Dillon printf(" WARNING: must configure snapshot dir for PFS slave\n"); 204ff1c9800SMatthew Dillon printf("\tWe suggest <fs>/var/slaves/<name> where " 205ff1c9800SMatthew Dillon "<fs> is the base HAMMER fs\n"); 206bb8e52c0SThomas Nikolajsen printf("\tcontaining the slave\n"); 207ff1c9800SMatthew Dillon return; 208ff1c9800SMatthew Dillon } else { 209ff1c9800SMatthew Dillon asprintf(&snapshots_path, 210ff1c9800SMatthew Dillon "%s%ssnapshots", path, dividing_slash(path)); 211ff1c9800SMatthew Dillon } 212ff1c9800SMatthew Dillon 213ff1c9800SMatthew Dillon /* 2146a6e350fSMatthew Dillon * Create a snapshot directory if necessary, and a config file if 2156a6e350fSMatthew Dillon * necessary. 2166a6e350fSMatthew Dillon */ 2176a6e350fSMatthew Dillon if (stat(snapshots_path, &st) < 0) { 2186a6e350fSMatthew Dillon if (mkdir(snapshots_path, 0755) != 0) { 2196a6e350fSMatthew Dillon free(snapshots_path); 220ff1c9800SMatthew Dillon printf(" unable to create snapshot dir \"%s\": %s\n", 221ff1c9800SMatthew Dillon snapshots_path, strerror(errno)); 2226a6e350fSMatthew Dillon return; 2236a6e350fSMatthew Dillon } 2246a6e350fSMatthew Dillon } 2256a6e350fSMatthew Dillon asprintf(&config_path, "%s/config", snapshots_path); 2266a6e350fSMatthew Dillon if ((fp = fopen(config_path, "r")) == NULL) { 2276a6e350fSMatthew Dillon fp = fopen(config_path, "w"); 2286a6e350fSMatthew Dillon if (fp == NULL) { 2296a6e350fSMatthew Dillon printf(" cannot create %s: %s\n", 2306a6e350fSMatthew Dillon config_path, strerror(errno)); 2316a6e350fSMatthew Dillon return; 2326a6e350fSMatthew Dillon } 2336a6e350fSMatthew Dillon if (strcmp(path, "/tmp") == 0 || 2346a6e350fSMatthew Dillon strcmp(path, "/var/tmp") == 0 || 2356a6e350fSMatthew Dillon strcmp(path, "/usr/obj") == 0) { 2365e435c92SMatthew Dillon fprintf(fp, "snapshots 0d 0d\n"); 2376a6e350fSMatthew Dillon } else { 2386a6e350fSMatthew Dillon fprintf(fp, "snapshots 1d 60d\n"); 2396a6e350fSMatthew Dillon } 2406a6e350fSMatthew Dillon fprintf(fp, 2416a6e350fSMatthew Dillon "prune 1d 5m\n" 2420b8bd7daSMatthew Dillon "rebalance 1d 5m\n" 2436a6e350fSMatthew Dillon "reblock 1d 5m\n" 2446a6e350fSMatthew Dillon "recopy 30d 10m\n"); 2456a6e350fSMatthew Dillon fclose(fp); 2466a6e350fSMatthew Dillon fp = fopen(config_path, "r"); 2476a6e350fSMatthew Dillon } 2486a6e350fSMatthew Dillon if (fp == NULL) { 2496a6e350fSMatthew Dillon printf(" cannot access %s: %s\n", 2506a6e350fSMatthew Dillon config_path, strerror(errno)); 2516a6e350fSMatthew Dillon return; 2526a6e350fSMatthew Dillon } 2536a6e350fSMatthew Dillon 2545bd5f172SSimon Schubert if (flock(fileno(fp), LOCK_EX|LOCK_NB) == -1) { 2555bd5f172SSimon Schubert if (errno == EWOULDBLOCK) 2565bd5f172SSimon Schubert printf(" PFS #%d locked by other process\n", pfs.pfs_id); 2575bd5f172SSimon Schubert else 2585bd5f172SSimon Schubert printf(" can not lock %s: %s\n", config_path, strerror(errno)); 2595bd5f172SSimon Schubert fclose(fp); 2605bd5f172SSimon Schubert return; 2615bd5f172SSimon Schubert } 2625bd5f172SSimon Schubert 263ff1c9800SMatthew Dillon printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path); 2646a6e350fSMatthew Dillon 2656a6e350fSMatthew Dillon /* 2666a6e350fSMatthew Dillon * Process the config file 2676a6e350fSMatthew Dillon */ 2686a6e350fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 2696a6e350fSMatthew Dillon cmd = strtok(buf, WS); 2706a6e350fSMatthew Dillon arg1 = 0; 2716a6e350fSMatthew Dillon arg2 = 0; 2725e435c92SMatthew Dillon arg3 = NULL; 2736a6e350fSMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 2746a6e350fSMatthew Dillon arg1 = strtosecs(ptr); 2755e435c92SMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 2766a6e350fSMatthew Dillon arg2 = strtosecs(ptr); 2775e435c92SMatthew Dillon arg3 = strtok(NULL, WS); 2785e435c92SMatthew Dillon } 2796a6e350fSMatthew Dillon } 2806a6e350fSMatthew Dillon 2816a6e350fSMatthew Dillon printf("%20s - ", cmd); 2826a6e350fSMatthew Dillon fflush(stdout); 2836a6e350fSMatthew Dillon 2846a6e350fSMatthew Dillon r = 1; 2856a6e350fSMatthew Dillon if (strcmp(cmd, "snapshots") == 0) { 2865e435c92SMatthew Dillon if (arg1 == 0) { 2875e435c92SMatthew Dillon if (arg2 && check_softlinks(snapshots_path)) { 2885e435c92SMatthew Dillon printf("only removing old snapshots\n"); 2895e435c92SMatthew Dillon prune_warning = 1; 2905e435c92SMatthew Dillon cleanup_softlinks(path, snapshots_path, 2915e435c92SMatthew Dillon arg2, arg3); 2925e435c92SMatthew Dillon } else { 2935e435c92SMatthew Dillon printf("disabled\n"); 2945e435c92SMatthew Dillon snapshots_disabled = 1; 2955e435c92SMatthew Dillon } 2965e435c92SMatthew Dillon } else 2976a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 2986a6e350fSMatthew Dillon printf("run\n"); 2995e435c92SMatthew Dillon cleanup_softlinks(path, snapshots_path, 3005e435c92SMatthew Dillon arg2, arg3); 301cbbb4f37SSimon Schubert r = create_snapshot(path, snapshots_path, 3026a6e350fSMatthew Dillon arg1, arg2); 3036a6e350fSMatthew Dillon } else { 3046a6e350fSMatthew Dillon printf("skip\n"); 3056a6e350fSMatthew Dillon } 3065e435c92SMatthew Dillon } else if (arg1 == 0) { 3075e435c92SMatthew Dillon /* 3085e435c92SMatthew Dillon * The commands following this check can't handle 3095e435c92SMatthew Dillon * a period of 0, so call the feature disabled and 3105e435c92SMatthew Dillon * ignore the directive. 3115e435c92SMatthew Dillon */ 3125e435c92SMatthew Dillon printf("disabled\n"); 3136a6e350fSMatthew Dillon } else if (strcmp(cmd, "prune") == 0) { 3146a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 3155e435c92SMatthew Dillon if (prune_warning) { 3165e435c92SMatthew Dillon printf("run - WARNING snapshot " 3175e435c92SMatthew Dillon "softlinks present " 318226f3799SThomas Nikolajsen "but snapshots disabled\n"); 3195e435c92SMatthew Dillon } else { 320c6c298a7SMatthew Dillon printf("run\n"); 3215e435c92SMatthew Dillon } 3226a6e350fSMatthew Dillon r = cleanup_prune(path, snapshots_path, 323c6c298a7SMatthew Dillon arg1, arg2, snapshots_disabled); 3246a6e350fSMatthew Dillon } else { 3256a6e350fSMatthew Dillon printf("skip\n"); 3266a6e350fSMatthew Dillon } 3270b8bd7daSMatthew Dillon } else if (strcmp(cmd, "rebalance") == 0) { 3280b8bd7daSMatthew Dillon found_rebal = 1; 3290b8bd7daSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 3300b8bd7daSMatthew Dillon printf("run"); 3310b8bd7daSMatthew Dillon fflush(stdout); 3320b8bd7daSMatthew Dillon if (VerboseOpt) 3330b8bd7daSMatthew Dillon printf("\n"); 3340b8bd7daSMatthew Dillon r = cleanup_rebalance(path, snapshots_path, 3350b8bd7daSMatthew Dillon arg1, arg2); 3360b8bd7daSMatthew Dillon } else { 3370b8bd7daSMatthew Dillon printf("skip\n"); 3380b8bd7daSMatthew Dillon } 3396a6e350fSMatthew Dillon } else if (strcmp(cmd, "reblock") == 0) { 3406a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 3416a6e350fSMatthew Dillon printf("run"); 3426a6e350fSMatthew Dillon fflush(stdout); 3436a6e350fSMatthew Dillon if (VerboseOpt) 3446a6e350fSMatthew Dillon printf("\n"); 3456a6e350fSMatthew Dillon r = cleanup_reblock(path, snapshots_path, 3466a6e350fSMatthew Dillon arg1, arg2); 3476a6e350fSMatthew Dillon } else { 3486a6e350fSMatthew Dillon printf("skip\n"); 3496a6e350fSMatthew Dillon } 3506a6e350fSMatthew Dillon } else if (strcmp(cmd, "recopy") == 0) { 3516a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 3526a6e350fSMatthew Dillon printf("run"); 3536a6e350fSMatthew Dillon fflush(stdout); 3546a6e350fSMatthew Dillon if (VerboseOpt) 3556a6e350fSMatthew Dillon printf("\n"); 3566a6e350fSMatthew Dillon r = cleanup_recopy(path, snapshots_path, 3576a6e350fSMatthew Dillon arg1, arg2); 3586a6e350fSMatthew Dillon } else { 3596a6e350fSMatthew Dillon printf("skip\n"); 3606a6e350fSMatthew Dillon } 3616a6e350fSMatthew Dillon } else { 3626a6e350fSMatthew Dillon printf("unknown directive\n"); 3636a6e350fSMatthew Dillon r = 1; 3646a6e350fSMatthew Dillon } 3656a6e350fSMatthew Dillon if (r == 0) 3666a6e350fSMatthew Dillon save_period(snapshots_path, cmd, savet); 3676a6e350fSMatthew Dillon } 3686a6e350fSMatthew Dillon fclose(fp); 3690b8bd7daSMatthew Dillon 3700b8bd7daSMatthew Dillon /* 3710b8bd7daSMatthew Dillon * Add new rebalance feature if the config doesn't have it 3720b8bd7daSMatthew Dillon */ 3730b8bd7daSMatthew Dillon if (found_rebal == 0) { 3740b8bd7daSMatthew Dillon if ((fp = fopen(config_path, "r+")) != NULL) { 3750b8bd7daSMatthew Dillon fseek(fp, 0L, 2); 3760b8bd7daSMatthew Dillon fprintf(fp, "rebalance 1d 5m\n"); 3770b8bd7daSMatthew Dillon fclose(fp); 3780b8bd7daSMatthew Dillon } 3790b8bd7daSMatthew Dillon } 3806a6e350fSMatthew Dillon usleep(1000); 3816a6e350fSMatthew Dillon } 3826a6e350fSMatthew Dillon 3836a6e350fSMatthew Dillon static 3846a6e350fSMatthew Dillon int 3856a6e350fSMatthew Dillon strtosecs(char *ptr) 3866a6e350fSMatthew Dillon { 3876a6e350fSMatthew Dillon int val; 3886a6e350fSMatthew Dillon 3896a6e350fSMatthew Dillon val = strtol(ptr, &ptr, 0); 3906a6e350fSMatthew Dillon switch(*ptr) { 3916a6e350fSMatthew Dillon case 'd': 3926a6e350fSMatthew Dillon val *= 24; 3936a6e350fSMatthew Dillon /* fall through */ 3946a6e350fSMatthew Dillon case 'h': 3956a6e350fSMatthew Dillon val *= 60; 3966a6e350fSMatthew Dillon /* fall through */ 3976a6e350fSMatthew Dillon case 'm': 3986a6e350fSMatthew Dillon val *= 60; 3996a6e350fSMatthew Dillon /* fall through */ 4006a6e350fSMatthew Dillon case 's': 4016a6e350fSMatthew Dillon break; 4026a6e350fSMatthew Dillon default: 4036a6e350fSMatthew Dillon errx(1, "illegal suffix converting %s\n", ptr); 4046a6e350fSMatthew Dillon break; 4056a6e350fSMatthew Dillon } 4066a6e350fSMatthew Dillon return(val); 4076a6e350fSMatthew Dillon } 4086a6e350fSMatthew Dillon 4096a6e350fSMatthew Dillon static const char * 4106a6e350fSMatthew Dillon dividing_slash(const char *path) 4116a6e350fSMatthew Dillon { 4126a6e350fSMatthew Dillon int len = strlen(path); 4136a6e350fSMatthew Dillon if (len && path[len-1] == '/') 4146a6e350fSMatthew Dillon return(""); 4156a6e350fSMatthew Dillon else 4166a6e350fSMatthew Dillon return("/"); 4176a6e350fSMatthew Dillon } 4186a6e350fSMatthew Dillon 4196a6e350fSMatthew Dillon /* 4206a6e350fSMatthew Dillon * Check whether the desired period has elapsed since the last successful 4216a6e350fSMatthew Dillon * run. The run may take a while and cross a boundary so we remember the 4226a6e350fSMatthew Dillon * current time_t so we can save it later on. 4236a6e350fSMatthew Dillon * 4246a6e350fSMatthew Dillon * Periods in minutes, hours, or days are assumed to have been crossed 4256a6e350fSMatthew Dillon * if the local time crosses a minute, hour, or day boundary regardless 4266a6e350fSMatthew Dillon * of how close the last operation actually was. 4276a6e350fSMatthew Dillon */ 4286a6e350fSMatthew Dillon static int 4296a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1, 4306a6e350fSMatthew Dillon time_t *savep) 4316a6e350fSMatthew Dillon { 4326a6e350fSMatthew Dillon char *check_path; 4336a6e350fSMatthew Dillon struct tm tp1; 4346a6e350fSMatthew Dillon struct tm tp2; 4356a6e350fSMatthew Dillon FILE *fp; 4366a6e350fSMatthew Dillon time_t baset, lastt; 4376a6e350fSMatthew Dillon char buf[256]; 4386a6e350fSMatthew Dillon 4396a6e350fSMatthew Dillon time(savep); 4406a6e350fSMatthew Dillon localtime_r(savep, &tp1); 4416a6e350fSMatthew Dillon 4426a6e350fSMatthew Dillon /* 4436a6e350fSMatthew Dillon * Retrieve the start time of the last successful operation. 4446a6e350fSMatthew Dillon */ 4456a6e350fSMatthew Dillon asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd); 4466a6e350fSMatthew Dillon fp = fopen(check_path, "r"); 4476a6e350fSMatthew Dillon free(check_path); 4486a6e350fSMatthew Dillon if (fp == NULL) 4496a6e350fSMatthew Dillon return(1); 4506a6e350fSMatthew Dillon if (fgets(buf, sizeof(buf), fp) == NULL) { 4516a6e350fSMatthew Dillon fclose(fp); 4526a6e350fSMatthew Dillon return(1); 4536a6e350fSMatthew Dillon } 4546a6e350fSMatthew Dillon fclose(fp); 4556a6e350fSMatthew Dillon 4566a6e350fSMatthew Dillon lastt = strtol(buf, NULL, 0); 4576a6e350fSMatthew Dillon localtime_r(&lastt, &tp2); 4586a6e350fSMatthew Dillon 4596a6e350fSMatthew Dillon /* 4606a6e350fSMatthew Dillon * Normalize the times. e.g. if asked to do something on a 1-day 4616a6e350fSMatthew Dillon * interval the operation will be performed as soon as the day 4626a6e350fSMatthew Dillon * turns over relative to the previous operation, even if the previous 4636a6e350fSMatthew Dillon * operation ran a few seconds ago just before midnight. 4646a6e350fSMatthew Dillon */ 4656a6e350fSMatthew Dillon if (arg1 % 60 == 0) { 4666a6e350fSMatthew Dillon tp1.tm_sec = 0; 4676a6e350fSMatthew Dillon tp2.tm_sec = 0; 4686a6e350fSMatthew Dillon } 4696a6e350fSMatthew Dillon if (arg1 % (60 * 60) == 0) { 4706a6e350fSMatthew Dillon tp1.tm_min = 0; 4716a6e350fSMatthew Dillon tp2.tm_min = 0; 4726a6e350fSMatthew Dillon } 4736a6e350fSMatthew Dillon if (arg1 % (24 * 60 * 60) == 0) { 4746a6e350fSMatthew Dillon tp1.tm_hour = 0; 4756a6e350fSMatthew Dillon tp2.tm_hour = 0; 4766a6e350fSMatthew Dillon } 4776a6e350fSMatthew Dillon 4786a6e350fSMatthew Dillon baset = mktime(&tp1); 4796a6e350fSMatthew Dillon lastt = mktime(&tp2); 4806a6e350fSMatthew Dillon 4816a6e350fSMatthew Dillon #if 0 4826a6e350fSMatthew Dillon printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1); 4836a6e350fSMatthew Dillon #endif 4846a6e350fSMatthew Dillon 4856a6e350fSMatthew Dillon if ((int)(baset - lastt) >= arg1) 4866a6e350fSMatthew Dillon return(1); 4876a6e350fSMatthew Dillon return(0); 4886a6e350fSMatthew Dillon } 4896a6e350fSMatthew Dillon 4906a6e350fSMatthew Dillon /* 4916a6e350fSMatthew Dillon * Store the start time of the last successful operation. 4926a6e350fSMatthew Dillon */ 4936a6e350fSMatthew Dillon static void 4946a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd, 4956a6e350fSMatthew Dillon time_t savet) 4966a6e350fSMatthew Dillon { 4976a6e350fSMatthew Dillon char *ocheck_path; 4986a6e350fSMatthew Dillon char *ncheck_path; 4996a6e350fSMatthew Dillon FILE *fp; 5006a6e350fSMatthew Dillon 5016a6e350fSMatthew Dillon asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd); 5026a6e350fSMatthew Dillon asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd); 5036a6e350fSMatthew Dillon fp = fopen(ncheck_path, "w"); 5043b9fbdeaSMatthew Dillon if (fp) { 5056a6e350fSMatthew Dillon fprintf(fp, "0x%08llx\n", (long long)savet); 5066a6e350fSMatthew Dillon if (fclose(fp) == 0) 5076a6e350fSMatthew Dillon rename(ncheck_path, ocheck_path); 5086a6e350fSMatthew Dillon remove(ncheck_path); 5093b9fbdeaSMatthew Dillon } else { 5103b9fbdeaSMatthew Dillon fprintf(stderr, "hammer: Unable to create period-file %s: %s\n", 5113b9fbdeaSMatthew Dillon ncheck_path, strerror(errno)); 5123b9fbdeaSMatthew Dillon } 5136a6e350fSMatthew Dillon } 5146a6e350fSMatthew Dillon 515ff1c9800SMatthew Dillon /* 516ff1c9800SMatthew Dillon * Simply count the number of softlinks in the snapshots dir 517ff1c9800SMatthew Dillon */ 518c6c298a7SMatthew Dillon static int 519c6c298a7SMatthew Dillon check_softlinks(const char *snapshots_path) 520c6c298a7SMatthew Dillon { 521c6c298a7SMatthew Dillon struct dirent *den; 522c6c298a7SMatthew Dillon struct stat st; 523c6c298a7SMatthew Dillon DIR *dir; 524c6c298a7SMatthew Dillon char *fpath; 525c6c298a7SMatthew Dillon int res = 0; 526c6c298a7SMatthew Dillon 527c6c298a7SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 528c6c298a7SMatthew Dillon while ((den = readdir(dir)) != NULL) { 529c6c298a7SMatthew Dillon if (den->d_name[0] == '.') 530c6c298a7SMatthew Dillon continue; 531c6c298a7SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 532c6c298a7SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) 533c6c298a7SMatthew Dillon ++res; 534c6c298a7SMatthew Dillon free(fpath); 535c6c298a7SMatthew Dillon } 536c6c298a7SMatthew Dillon closedir(dir); 537c6c298a7SMatthew Dillon } 538c6c298a7SMatthew Dillon return(res); 539c6c298a7SMatthew Dillon } 540c6c298a7SMatthew Dillon 5416a6e350fSMatthew Dillon /* 542ff1c9800SMatthew Dillon * Clean up expired softlinks in the snapshots dir 543ff1c9800SMatthew Dillon */ 544ff1c9800SMatthew Dillon static void 5455e435c92SMatthew Dillon cleanup_softlinks(const char *path __unused, const char *snapshots_path, 5465e435c92SMatthew Dillon int arg2, char *arg3) 547ff1c9800SMatthew Dillon { 548ff1c9800SMatthew Dillon struct dirent *den; 549ff1c9800SMatthew Dillon struct stat st; 550ff1c9800SMatthew Dillon DIR *dir; 551ff1c9800SMatthew Dillon char *fpath; 5525e435c92SMatthew Dillon int anylink = 0; 5535e435c92SMatthew Dillon 55409e1b0d6SMatthew Dillon if (arg3 != NULL && strstr(arg3, "any") != NULL) 5555e435c92SMatthew Dillon anylink = 1; 556ff1c9800SMatthew Dillon 557ff1c9800SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 558ff1c9800SMatthew Dillon while ((den = readdir(dir)) != NULL) { 559ff1c9800SMatthew Dillon if (den->d_name[0] == '.') 560ff1c9800SMatthew Dillon continue; 561ff1c9800SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 562ff1c9800SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) && 5635e435c92SMatthew Dillon (anylink || strncmp(den->d_name, "snap-", 5) == 0) 5645e435c92SMatthew Dillon ) { 565ff1c9800SMatthew Dillon if (check_expired(den->d_name, arg2)) { 566ff1c9800SMatthew Dillon if (VerboseOpt) { 567ff1c9800SMatthew Dillon printf(" expire %s\n", 568ff1c9800SMatthew Dillon fpath); 569ff1c9800SMatthew Dillon } 570ff1c9800SMatthew Dillon remove(fpath); 571ff1c9800SMatthew Dillon } 572ff1c9800SMatthew Dillon } 573ff1c9800SMatthew Dillon free(fpath); 574ff1c9800SMatthew Dillon } 575ff1c9800SMatthew Dillon closedir(dir); 576ff1c9800SMatthew Dillon } 577ff1c9800SMatthew Dillon } 578ff1c9800SMatthew Dillon 579ff1c9800SMatthew Dillon /* 580ff1c9800SMatthew Dillon * Take a softlink path in the form snap-yyyymmdd-hhmm and the 581ff1c9800SMatthew Dillon * expiration in seconds (arg2) and return non-zero if the softlink 582ff1c9800SMatthew Dillon * has expired. 583ff1c9800SMatthew Dillon */ 584ff1c9800SMatthew Dillon static int 585ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2) 586ff1c9800SMatthew Dillon { 587ff1c9800SMatthew Dillon struct tm tm; 588ff1c9800SMatthew Dillon time_t t; 589ff1c9800SMatthew Dillon int year; 590ff1c9800SMatthew Dillon int month; 5915e435c92SMatthew Dillon int day = 0; 5925e435c92SMatthew Dillon int hour = 0; 5935e435c92SMatthew Dillon int minute = 0; 594ff1c9800SMatthew Dillon int r; 595ff1c9800SMatthew Dillon 5965e435c92SMatthew Dillon while (*fpath && *fpath != '-' && *fpath != '.') 5975e435c92SMatthew Dillon ++fpath; 5985e435c92SMatthew Dillon if (*fpath) 5995e435c92SMatthew Dillon ++fpath; 6005e435c92SMatthew Dillon 6015e435c92SMatthew Dillon r = sscanf(fpath, "%4d%2d%2d-%2d%2d", 602ff1c9800SMatthew Dillon &year, &month, &day, &hour, &minute); 6035e435c92SMatthew Dillon 6045e435c92SMatthew Dillon if (r >= 3) { 605ff1c9800SMatthew Dillon bzero(&tm, sizeof(tm)); 606ff1c9800SMatthew Dillon tm.tm_isdst = -1; 607ff1c9800SMatthew Dillon tm.tm_min = minute; 608ff1c9800SMatthew Dillon tm.tm_hour = hour; 609ff1c9800SMatthew Dillon tm.tm_mday = day; 610ff1c9800SMatthew Dillon tm.tm_mon = month - 1; 611ff1c9800SMatthew Dillon tm.tm_year = year - 1900; 6125e435c92SMatthew Dillon t = mktime(&tm); 6135e435c92SMatthew Dillon if (t == (time_t)-1) 6145e435c92SMatthew Dillon return(0); 6155e435c92SMatthew Dillon t = time(NULL) - t; 616ff1c9800SMatthew Dillon if ((int)t > arg2) 617ff1c9800SMatthew Dillon return(1); 618ff1c9800SMatthew Dillon } 619ff1c9800SMatthew Dillon return(0); 620ff1c9800SMatthew Dillon } 621ff1c9800SMatthew Dillon 622ff1c9800SMatthew Dillon /* 6236a6e350fSMatthew Dillon * Issue a snapshot. 6246a6e350fSMatthew Dillon */ 6256a6e350fSMatthew Dillon static int 626cbbb4f37SSimon Schubert create_snapshot(const char *path __unused, const char *snapshots_path, 6276a6e350fSMatthew Dillon int arg1 __unused, int arg2 __unused) 6286a6e350fSMatthew Dillon { 6296a6e350fSMatthew Dillon int r; 6306a6e350fSMatthew Dillon 631ff1c9800SMatthew Dillon runcmd(&r, "hammer snapshot %s %s", path, snapshots_path); 6326a6e350fSMatthew Dillon return(r); 6336a6e350fSMatthew Dillon } 6346a6e350fSMatthew Dillon 6356a6e350fSMatthew Dillon static int 6366a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path, 637c6c298a7SMatthew Dillon int arg1 __unused, int arg2, int snapshots_disabled) 6386a6e350fSMatthew Dillon { 639c6c298a7SMatthew Dillon /* 640c6c298a7SMatthew Dillon * If snapshots have been disabled run prune-everything instead 641c6c298a7SMatthew Dillon * of prune. 642c6c298a7SMatthew Dillon */ 643c6c298a7SMatthew Dillon if (snapshots_disabled && arg2) { 644c6c298a7SMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s", 645c6c298a7SMatthew Dillon snapshots_path, arg2, path); 646c6c298a7SMatthew Dillon } else if (snapshots_disabled) { 647c6c298a7SMatthew Dillon runcmd(NULL, "hammer prune-everything %s", path); 648c6c298a7SMatthew Dillon } else if (arg2) { 6496a6e350fSMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s", 6506a6e350fSMatthew Dillon snapshots_path, arg2, snapshots_path); 6516a6e350fSMatthew Dillon } else { 6526a6e350fSMatthew Dillon runcmd(NULL, "hammer prune %s", snapshots_path); 6536a6e350fSMatthew Dillon } 6546a6e350fSMatthew Dillon return(0); 6556a6e350fSMatthew Dillon } 6566a6e350fSMatthew Dillon 6576a6e350fSMatthew Dillon static int 6580b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path, 6590b8bd7daSMatthew Dillon int arg1 __unused, int arg2) 6600b8bd7daSMatthew Dillon { 6610b8bd7daSMatthew Dillon if (VerboseOpt == 0) { 6620b8bd7daSMatthew Dillon printf("."); 6630b8bd7daSMatthew Dillon fflush(stdout); 6640b8bd7daSMatthew Dillon } 6650b8bd7daSMatthew Dillon 6660b8bd7daSMatthew Dillon runcmd(NULL, 6670b8bd7daSMatthew Dillon "hammer -c %s/.rebalance.cycle -t %d rebalance %s", 6680b8bd7daSMatthew Dillon snapshots_path, arg2, path); 6690b8bd7daSMatthew Dillon if (VerboseOpt == 0) { 6700b8bd7daSMatthew Dillon printf("."); 6710b8bd7daSMatthew Dillon fflush(stdout); 6720b8bd7daSMatthew Dillon } 6730b8bd7daSMatthew Dillon if (VerboseOpt == 0) 6740b8bd7daSMatthew Dillon printf("\n"); 6750b8bd7daSMatthew Dillon return(0); 6760b8bd7daSMatthew Dillon } 6770b8bd7daSMatthew Dillon 6780b8bd7daSMatthew Dillon static int 6796a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path, 6806a6e350fSMatthew Dillon int arg1 __unused, int arg2) 6816a6e350fSMatthew Dillon { 6826a6e350fSMatthew Dillon if (VerboseOpt == 0) { 6836a6e350fSMatthew Dillon printf("."); 6846a6e350fSMatthew Dillon fflush(stdout); 6856a6e350fSMatthew Dillon } 686797a0b63SMatthew Dillon 687797a0b63SMatthew Dillon /* 688797a0b63SMatthew Dillon * When reblocking the B-Tree always reblock everything in normal 689797a0b63SMatthew Dillon * mode. 690797a0b63SMatthew Dillon */ 6916a6e350fSMatthew Dillon runcmd(NULL, 692797a0b63SMatthew Dillon "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s", 6936a6e350fSMatthew Dillon snapshots_path, arg2, path); 6946a6e350fSMatthew Dillon if (VerboseOpt == 0) { 6956a6e350fSMatthew Dillon printf("."); 6966a6e350fSMatthew Dillon fflush(stdout); 6976a6e350fSMatthew Dillon } 698797a0b63SMatthew Dillon 699797a0b63SMatthew Dillon /* 700797a0b63SMatthew Dillon * When reblocking the inodes always reblock everything in normal 701797a0b63SMatthew Dillon * mode. 702797a0b63SMatthew Dillon */ 7036a6e350fSMatthew Dillon runcmd(NULL, 704797a0b63SMatthew Dillon "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s", 7056a6e350fSMatthew Dillon snapshots_path, arg2, path); 7066a6e350fSMatthew Dillon if (VerboseOpt == 0) { 7076a6e350fSMatthew Dillon printf("."); 7086a6e350fSMatthew Dillon fflush(stdout); 7096a6e350fSMatthew Dillon } 710797a0b63SMatthew Dillon 711797a0b63SMatthew Dillon /* 712797a0b63SMatthew Dillon * When reblocking the directories always reblock everything in normal 713797a0b63SMatthew Dillon * mode. 714797a0b63SMatthew Dillon */ 715797a0b63SMatthew Dillon runcmd(NULL, 716797a0b63SMatthew Dillon "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s", 717797a0b63SMatthew Dillon snapshots_path, arg2, path); 718797a0b63SMatthew Dillon if (VerboseOpt == 0) { 719797a0b63SMatthew Dillon printf("."); 720797a0b63SMatthew Dillon fflush(stdout); 721797a0b63SMatthew Dillon } 722797a0b63SMatthew Dillon 723797a0b63SMatthew Dillon /* 724797a0b63SMatthew Dillon * Do not reblock all the data in normal mode. 725797a0b63SMatthew Dillon */ 7266a6e350fSMatthew Dillon runcmd(NULL, 7276a6e350fSMatthew Dillon "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95", 7286a6e350fSMatthew Dillon snapshots_path, arg2, path); 7296a6e350fSMatthew Dillon if (VerboseOpt == 0) 7306a6e350fSMatthew Dillon printf("\n"); 7316a6e350fSMatthew Dillon return(0); 7326a6e350fSMatthew Dillon } 7336a6e350fSMatthew Dillon 7346a6e350fSMatthew Dillon static int 7356a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path, 7366a6e350fSMatthew Dillon int arg1 __unused, int arg2) 7376a6e350fSMatthew Dillon { 7386a6e350fSMatthew Dillon if (VerboseOpt == 0) { 7396a6e350fSMatthew Dillon printf("."); 7406a6e350fSMatthew Dillon fflush(stdout); 7416a6e350fSMatthew Dillon } 7426a6e350fSMatthew Dillon runcmd(NULL, 7436a6e350fSMatthew Dillon "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s", 7446a6e350fSMatthew Dillon snapshots_path, arg2, path); 7456a6e350fSMatthew Dillon if (VerboseOpt == 0) { 7466a6e350fSMatthew Dillon printf("."); 7476a6e350fSMatthew Dillon fflush(stdout); 7486a6e350fSMatthew Dillon } 7496a6e350fSMatthew Dillon runcmd(NULL, 7506a6e350fSMatthew Dillon "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s", 7516a6e350fSMatthew Dillon snapshots_path, arg2, path); 7526a6e350fSMatthew Dillon if (VerboseOpt == 0) { 7536a6e350fSMatthew Dillon printf("."); 7546a6e350fSMatthew Dillon fflush(stdout); 7556a6e350fSMatthew Dillon } 7566a6e350fSMatthew Dillon runcmd(NULL, 757797a0b63SMatthew Dillon "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s", 758797a0b63SMatthew Dillon snapshots_path, arg2, path); 759797a0b63SMatthew Dillon if (VerboseOpt == 0) { 760797a0b63SMatthew Dillon printf("."); 761797a0b63SMatthew Dillon fflush(stdout); 762797a0b63SMatthew Dillon } 763797a0b63SMatthew Dillon runcmd(NULL, 7646a6e350fSMatthew Dillon "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s", 7656a6e350fSMatthew Dillon snapshots_path, arg2, path); 7666a6e350fSMatthew Dillon if (VerboseOpt == 0) 7676a6e350fSMatthew Dillon printf("\n"); 7686a6e350fSMatthew Dillon return(0); 7696a6e350fSMatthew Dillon } 7706a6e350fSMatthew Dillon 7716a6e350fSMatthew Dillon static 7726a6e350fSMatthew Dillon void 7736a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...) 7746a6e350fSMatthew Dillon { 7756a6e350fSMatthew Dillon va_list va; 7766a6e350fSMatthew Dillon char *cmd; 7776a6e350fSMatthew Dillon char *arg; 7786a6e350fSMatthew Dillon char **av; 7796a6e350fSMatthew Dillon int n; 7806a6e350fSMatthew Dillon int nmax; 7816a6e350fSMatthew Dillon int res; 7826a6e350fSMatthew Dillon pid_t pid; 7836a6e350fSMatthew Dillon 7846a6e350fSMatthew Dillon /* 7856a6e350fSMatthew Dillon * Generate the command 7866a6e350fSMatthew Dillon */ 7876a6e350fSMatthew Dillon va_start(va, ctl); 7886a6e350fSMatthew Dillon vasprintf(&cmd, ctl, va); 7896a6e350fSMatthew Dillon va_end(va); 7906a6e350fSMatthew Dillon if (VerboseOpt) 7916a6e350fSMatthew Dillon printf(" %s\n", cmd); 7926a6e350fSMatthew Dillon 7936a6e350fSMatthew Dillon /* 7946a6e350fSMatthew Dillon * Break us down into arguments. We do not just use system() here 7956a6e350fSMatthew Dillon * because it blocks SIGINT and friends. 7966a6e350fSMatthew Dillon */ 7976a6e350fSMatthew Dillon n = 0; 7986a6e350fSMatthew Dillon nmax = 16; 7996a6e350fSMatthew Dillon av = malloc(sizeof(char *) * nmax); 8006a6e350fSMatthew Dillon 8016a6e350fSMatthew Dillon for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) { 802c453712aSMatthew Dillon if (n == nmax - 1) { 8036a6e350fSMatthew Dillon nmax += 16; 8046a6e350fSMatthew Dillon av = realloc(av, sizeof(char *) * nmax); 8056a6e350fSMatthew Dillon } 8066a6e350fSMatthew Dillon av[n++] = arg; 8076a6e350fSMatthew Dillon } 808c453712aSMatthew Dillon av[n++] = NULL; 8096a6e350fSMatthew Dillon 8106a6e350fSMatthew Dillon /* 8116a6e350fSMatthew Dillon * Run the command. 8126a6e350fSMatthew Dillon */ 813445faa69SMatthew Dillon RunningIoctl = 1; 8146a6e350fSMatthew Dillon if ((pid = fork()) == 0) { 8156a6e350fSMatthew Dillon if (VerboseOpt < 2) { 8166a6e350fSMatthew Dillon int fd = open("/dev/null", O_RDWR); 8176a6e350fSMatthew Dillon dup2(fd, 1); 8186a6e350fSMatthew Dillon close(fd); 8196a6e350fSMatthew Dillon } 8206a6e350fSMatthew Dillon execvp(av[0], av); 8216a6e350fSMatthew Dillon _exit(127); 8226a6e350fSMatthew Dillon } else if (pid < 0) { 8236a6e350fSMatthew Dillon res = 127; 8246a6e350fSMatthew Dillon } else { 8256a6e350fSMatthew Dillon int status; 826445faa69SMatthew Dillon 8276a6e350fSMatthew Dillon while (waitpid(pid, &status, 0) != pid) 8286a6e350fSMatthew Dillon ; 8296a6e350fSMatthew Dillon res = WEXITSTATUS(status); 8306a6e350fSMatthew Dillon } 831445faa69SMatthew Dillon RunningIoctl = 0; 832445faa69SMatthew Dillon if (DidInterrupt) 833445faa69SMatthew Dillon _exit(1); 8346a6e350fSMatthew Dillon 8356a6e350fSMatthew Dillon free(cmd); 8366a6e350fSMatthew Dillon free(av); 8376a6e350fSMatthew Dillon if (resp) 8386a6e350fSMatthew Dillon *resp = res; 8396a6e350fSMatthew Dillon } 840