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 * 34*c6c298a7SMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.2 2008/09/20 06:46:22 dillon 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 * 486a6e350fSMatthew Dillon * snapshots 1d 60d (0d 60d for /tmp, /var/tmp, /usr/obj) 496a6e350fSMatthew Dillon * prune 1d 5m 506a6e350fSMatthew Dillon * reblock 1d 5m 516a6e350fSMatthew Dillon * recopy 30d 5m 526a6e350fSMatthew Dillon * 536a6e350fSMatthew Dillon * All hammer commands create and maintain cycle files in the snapshots 546a6e350fSMatthew Dillon * directory. 556a6e350fSMatthew Dillon */ 566a6e350fSMatthew Dillon 576a6e350fSMatthew Dillon #include "hammer.h" 586a6e350fSMatthew Dillon 596a6e350fSMatthew Dillon static void do_cleanup(const char *path); 606a6e350fSMatthew Dillon static int strtosecs(char *ptr); 616a6e350fSMatthew Dillon static const char *dividing_slash(const char *path); 626a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1, 636a6e350fSMatthew Dillon time_t *savep); 646a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd, 656a6e350fSMatthew Dillon time_t savet); 66*c6c298a7SMatthew Dillon static int check_softlinks(const char *snapshots_path); 676a6e350fSMatthew Dillon 686a6e350fSMatthew Dillon static int cleanup_snapshots(const char *path, const char *snapshots_path, 696a6e350fSMatthew Dillon int arg1, int arg2); 706a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path, 71*c6c298a7SMatthew Dillon int arg1, int arg2, int snapshots_disabled); 726a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path, 736a6e350fSMatthew Dillon int arg1, int arg2); 746a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path, 756a6e350fSMatthew Dillon int arg1, int arg2); 766a6e350fSMatthew Dillon 776a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...); 786a6e350fSMatthew Dillon 796a6e350fSMatthew Dillon #define WS " \t\r\n" 806a6e350fSMatthew Dillon #define MAXPFS 65536 816a6e350fSMatthew Dillon #define DIDBITS (sizeof(int) * 8) 826a6e350fSMatthew Dillon 836a6e350fSMatthew Dillon static int DidPFS[MAXPFS/DIDBITS]; 846a6e350fSMatthew Dillon 856a6e350fSMatthew Dillon void 866a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac) 876a6e350fSMatthew Dillon { 886a6e350fSMatthew Dillon FILE *fp; 896a6e350fSMatthew Dillon char *ptr; 906a6e350fSMatthew Dillon char *path; 916a6e350fSMatthew Dillon char buf[256]; 926a6e350fSMatthew Dillon 936a6e350fSMatthew Dillon tzset(); 946a6e350fSMatthew Dillon if (ac == 0) { 956a6e350fSMatthew Dillon fp = popen("df -t hammer,null", "r"); 966a6e350fSMatthew Dillon if (fp == NULL) 976a6e350fSMatthew Dillon errx(1, "hammer cleanup: 'df' failed"); 986a6e350fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 996a6e350fSMatthew Dillon ptr = strtok(buf, WS); 1006a6e350fSMatthew Dillon if (ptr && strcmp(ptr, "Filesystem") == 0) 1016a6e350fSMatthew Dillon continue; 1026a6e350fSMatthew Dillon if (ptr) 1036a6e350fSMatthew Dillon ptr = strtok(NULL, WS); 1046a6e350fSMatthew Dillon if (ptr) 1056a6e350fSMatthew Dillon ptr = strtok(NULL, WS); 1066a6e350fSMatthew Dillon if (ptr) 1076a6e350fSMatthew Dillon ptr = strtok(NULL, WS); 1086a6e350fSMatthew Dillon if (ptr) 1096a6e350fSMatthew Dillon ptr = strtok(NULL, WS); 1106a6e350fSMatthew Dillon if (ptr) { 1116a6e350fSMatthew Dillon path = strtok(NULL, WS); 1126a6e350fSMatthew Dillon if (path) 1136a6e350fSMatthew Dillon do_cleanup(path); 1146a6e350fSMatthew Dillon } 1156a6e350fSMatthew Dillon } 1166a6e350fSMatthew Dillon fclose(fp); 1176a6e350fSMatthew Dillon } else { 1186a6e350fSMatthew Dillon while (ac) { 1196a6e350fSMatthew Dillon do_cleanup(*av); 1206a6e350fSMatthew Dillon --ac; 1216a6e350fSMatthew Dillon ++av; 1226a6e350fSMatthew Dillon } 1236a6e350fSMatthew Dillon } 1246a6e350fSMatthew Dillon } 1256a6e350fSMatthew Dillon 1266a6e350fSMatthew Dillon static 1276a6e350fSMatthew Dillon void 1286a6e350fSMatthew Dillon do_cleanup(const char *path) 1296a6e350fSMatthew Dillon { 1306a6e350fSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1316a6e350fSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 1326a6e350fSMatthew Dillon char *snapshots_path; 1336a6e350fSMatthew Dillon char *config_path; 1346a6e350fSMatthew Dillon struct stat st; 1356a6e350fSMatthew Dillon char *cmd; 1366a6e350fSMatthew Dillon char *ptr; 1376a6e350fSMatthew Dillon int arg1; 1386a6e350fSMatthew Dillon int arg2; 1396a6e350fSMatthew Dillon time_t savet; 1406a6e350fSMatthew Dillon char buf[256]; 1416a6e350fSMatthew Dillon FILE *fp; 142*c6c298a7SMatthew Dillon int snapshots_disabled = 0; 143*c6c298a7SMatthew Dillon int prune_warning = 0; 1446a6e350fSMatthew Dillon int fd; 1456a6e350fSMatthew Dillon int r; 1466a6e350fSMatthew Dillon 1476a6e350fSMatthew Dillon bzero(&pfs, sizeof(pfs)); 1486a6e350fSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 1496a6e350fSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 1506a6e350fSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 1516a6e350fSMatthew Dillon pfs.pfs_id = -1; 1526a6e350fSMatthew Dillon 1536a6e350fSMatthew Dillon printf("cleanup %-20s -", path); 1546a6e350fSMatthew Dillon fd = open(path, O_RDONLY); 1556a6e350fSMatthew Dillon if (fd < 0) { 1566a6e350fSMatthew Dillon printf(" unable to access directory: %s\n", strerror(errno)); 1576a6e350fSMatthew Dillon return; 1586a6e350fSMatthew Dillon } 1596a6e350fSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1606a6e350fSMatthew Dillon printf(" not a HAMMER filesystem: %s\n", strerror(errno)); 1616a6e350fSMatthew Dillon return; 1626a6e350fSMatthew Dillon } 1636a6e350fSMatthew Dillon close(fd); 1646a6e350fSMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 1656a6e350fSMatthew Dillon printf(" unrecognized HAMMER version\n"); 1666a6e350fSMatthew Dillon return; 1676a6e350fSMatthew Dillon } 1686a6e350fSMatthew Dillon 1696a6e350fSMatthew Dillon /* 1706a6e350fSMatthew Dillon * Make sure we have not already handled this PFS. Several nullfs 1716a6e350fSMatthew Dillon * mounts might alias the same PFS. 1726a6e350fSMatthew Dillon */ 1736a6e350fSMatthew Dillon if (pfs.pfs_id < 0 || pfs.pfs_id >= MAXPFS) { 1746a6e350fSMatthew Dillon printf(" pfs_id %d illegal\n", pfs.pfs_id); 1756a6e350fSMatthew Dillon return; 1766a6e350fSMatthew Dillon } 1776a6e350fSMatthew Dillon 1786a6e350fSMatthew Dillon if (DidPFS[pfs.pfs_id / DIDBITS] & (1 << (pfs.pfs_id % DIDBITS))) { 1796a6e350fSMatthew Dillon printf(" pfs_id %d already handled\n", pfs.pfs_id); 1806a6e350fSMatthew Dillon return; 1816a6e350fSMatthew Dillon } 1826a6e350fSMatthew Dillon DidPFS[pfs.pfs_id / DIDBITS] |= (1 << (pfs.pfs_id % DIDBITS)); 1836a6e350fSMatthew Dillon 1846a6e350fSMatthew Dillon /* 1856a6e350fSMatthew Dillon * Create a snapshot directory if necessary, and a config file if 1866a6e350fSMatthew Dillon * necessary. 1876a6e350fSMatthew Dillon */ 1886a6e350fSMatthew Dillon asprintf(&snapshots_path, "%s%ssnapshots", path, dividing_slash(path)); 1896a6e350fSMatthew Dillon if (stat(snapshots_path, &st) < 0) { 1906a6e350fSMatthew Dillon if (mkdir(snapshots_path, 0755) != 0) { 1916a6e350fSMatthew Dillon free(snapshots_path); 1926a6e350fSMatthew Dillon printf(" unable to create snapshot dir: %s\n", 1936a6e350fSMatthew Dillon strerror(errno)); 1946a6e350fSMatthew Dillon return; 1956a6e350fSMatthew Dillon } 1966a6e350fSMatthew Dillon } 1976a6e350fSMatthew Dillon asprintf(&config_path, "%s/config", snapshots_path); 1986a6e350fSMatthew Dillon if ((fp = fopen(config_path, "r")) == NULL) { 1996a6e350fSMatthew Dillon fp = fopen(config_path, "w"); 2006a6e350fSMatthew Dillon if (fp == NULL) { 2016a6e350fSMatthew Dillon printf(" cannot create %s: %s\n", 2026a6e350fSMatthew Dillon config_path, strerror(errno)); 2036a6e350fSMatthew Dillon return; 2046a6e350fSMatthew Dillon } 2056a6e350fSMatthew Dillon if (strcmp(path, "/tmp") == 0 || 2066a6e350fSMatthew Dillon strcmp(path, "/var/tmp") == 0 || 2076a6e350fSMatthew Dillon strcmp(path, "/usr/obj") == 0) { 2086a6e350fSMatthew Dillon fprintf(fp, "snapshots 0d 60d\n"); 2096a6e350fSMatthew Dillon } else { 2106a6e350fSMatthew Dillon fprintf(fp, "snapshots 1d 60d\n"); 2116a6e350fSMatthew Dillon } 2126a6e350fSMatthew Dillon fprintf(fp, 2136a6e350fSMatthew Dillon "prune 1d 5m\n" 2146a6e350fSMatthew Dillon "reblock 1d 5m\n" 2156a6e350fSMatthew Dillon "recopy 30d 10m\n"); 2166a6e350fSMatthew Dillon fclose(fp); 2176a6e350fSMatthew Dillon fp = fopen(config_path, "r"); 2186a6e350fSMatthew Dillon } 2196a6e350fSMatthew Dillon if (fp == NULL) { 2206a6e350fSMatthew Dillon printf(" cannot access %s: %s\n", 2216a6e350fSMatthew Dillon config_path, strerror(errno)); 2226a6e350fSMatthew Dillon return; 2236a6e350fSMatthew Dillon } 2246a6e350fSMatthew Dillon 2256a6e350fSMatthew Dillon printf(" processing PFS #%d\n", pfs.pfs_id); 2266a6e350fSMatthew Dillon 2276a6e350fSMatthew Dillon /* 2286a6e350fSMatthew Dillon * Process the config file 2296a6e350fSMatthew Dillon */ 2306a6e350fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 2316a6e350fSMatthew Dillon cmd = strtok(buf, WS); 2326a6e350fSMatthew Dillon arg1 = 0; 2336a6e350fSMatthew Dillon arg2 = 0; 2346a6e350fSMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 2356a6e350fSMatthew Dillon arg1 = strtosecs(ptr); 2366a6e350fSMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) 2376a6e350fSMatthew Dillon arg2 = strtosecs(ptr); 2386a6e350fSMatthew Dillon } 2396a6e350fSMatthew Dillon 2406a6e350fSMatthew Dillon printf("%20s - ", cmd); 2416a6e350fSMatthew Dillon fflush(stdout); 2426a6e350fSMatthew Dillon 2436a6e350fSMatthew Dillon if (arg1 == 0) { 2446a6e350fSMatthew Dillon printf("disabled\n"); 245*c6c298a7SMatthew Dillon if (strcmp(cmd, "snapshots") == 0) { 246*c6c298a7SMatthew Dillon if (check_softlinks(snapshots_path)) 247*c6c298a7SMatthew Dillon prune_warning = 1; 248*c6c298a7SMatthew Dillon else 249*c6c298a7SMatthew Dillon snapshots_disabled = 1; 250*c6c298a7SMatthew Dillon } 2516a6e350fSMatthew Dillon continue; 2526a6e350fSMatthew Dillon } 2536a6e350fSMatthew Dillon 2546a6e350fSMatthew Dillon r = 1; 2556a6e350fSMatthew Dillon if (strcmp(cmd, "snapshots") == 0) { 2566a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 2576a6e350fSMatthew Dillon printf("run\n"); 2586a6e350fSMatthew Dillon r = cleanup_snapshots(path, snapshots_path, 2596a6e350fSMatthew Dillon arg1, arg2); 2606a6e350fSMatthew Dillon } else { 2616a6e350fSMatthew Dillon printf("skip\n"); 2626a6e350fSMatthew Dillon } 2636a6e350fSMatthew Dillon } else if (strcmp(cmd, "prune") == 0) { 2646a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 265*c6c298a7SMatthew Dillon if (prune_warning) 266*c6c298a7SMatthew Dillon printf("run - WARNING snapshot softlinks present but snapshots disabled\n"); 267*c6c298a7SMatthew Dillon else 268*c6c298a7SMatthew Dillon printf("run\n"); 2696a6e350fSMatthew Dillon r = cleanup_prune(path, snapshots_path, 270*c6c298a7SMatthew Dillon arg1, arg2, snapshots_disabled); 2716a6e350fSMatthew Dillon } else { 2726a6e350fSMatthew Dillon printf("skip\n"); 2736a6e350fSMatthew Dillon } 2746a6e350fSMatthew Dillon } else if (strcmp(cmd, "reblock") == 0) { 2756a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 2766a6e350fSMatthew Dillon printf("run"); 2776a6e350fSMatthew Dillon fflush(stdout); 2786a6e350fSMatthew Dillon if (VerboseOpt) 2796a6e350fSMatthew Dillon printf("\n"); 2806a6e350fSMatthew Dillon r = cleanup_reblock(path, snapshots_path, 2816a6e350fSMatthew Dillon arg1, arg2); 2826a6e350fSMatthew Dillon } else { 2836a6e350fSMatthew Dillon printf("skip\n"); 2846a6e350fSMatthew Dillon } 2856a6e350fSMatthew Dillon } else if (strcmp(cmd, "recopy") == 0) { 2866a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 2876a6e350fSMatthew Dillon printf("run"); 2886a6e350fSMatthew Dillon fflush(stdout); 2896a6e350fSMatthew Dillon if (VerboseOpt) 2906a6e350fSMatthew Dillon printf("\n"); 2916a6e350fSMatthew Dillon r = cleanup_recopy(path, snapshots_path, 2926a6e350fSMatthew Dillon arg1, arg2); 2936a6e350fSMatthew Dillon } else { 2946a6e350fSMatthew Dillon printf("skip\n"); 2956a6e350fSMatthew Dillon } 2966a6e350fSMatthew Dillon } else { 2976a6e350fSMatthew Dillon printf("unknown directive\n"); 2986a6e350fSMatthew Dillon r = 1; 2996a6e350fSMatthew Dillon } 3006a6e350fSMatthew Dillon if (r == 0) 3016a6e350fSMatthew Dillon save_period(snapshots_path, cmd, savet); 3026a6e350fSMatthew Dillon } 3036a6e350fSMatthew Dillon fclose(fp); 3046a6e350fSMatthew Dillon usleep(1000); 3056a6e350fSMatthew Dillon } 3066a6e350fSMatthew Dillon 3076a6e350fSMatthew Dillon static 3086a6e350fSMatthew Dillon int 3096a6e350fSMatthew Dillon strtosecs(char *ptr) 3106a6e350fSMatthew Dillon { 3116a6e350fSMatthew Dillon int val; 3126a6e350fSMatthew Dillon 3136a6e350fSMatthew Dillon val = strtol(ptr, &ptr, 0); 3146a6e350fSMatthew Dillon switch(*ptr) { 3156a6e350fSMatthew Dillon case 'd': 3166a6e350fSMatthew Dillon val *= 24; 3176a6e350fSMatthew Dillon /* fall through */ 3186a6e350fSMatthew Dillon case 'h': 3196a6e350fSMatthew Dillon val *= 60; 3206a6e350fSMatthew Dillon /* fall through */ 3216a6e350fSMatthew Dillon case 'm': 3226a6e350fSMatthew Dillon val *= 60; 3236a6e350fSMatthew Dillon /* fall through */ 3246a6e350fSMatthew Dillon case 's': 3256a6e350fSMatthew Dillon break; 3266a6e350fSMatthew Dillon default: 3276a6e350fSMatthew Dillon errx(1, "illegal suffix converting %s\n", ptr); 3286a6e350fSMatthew Dillon break; 3296a6e350fSMatthew Dillon } 3306a6e350fSMatthew Dillon return(val); 3316a6e350fSMatthew Dillon } 3326a6e350fSMatthew Dillon 3336a6e350fSMatthew Dillon static const char * 3346a6e350fSMatthew Dillon dividing_slash(const char *path) 3356a6e350fSMatthew Dillon { 3366a6e350fSMatthew Dillon int len = strlen(path); 3376a6e350fSMatthew Dillon if (len && path[len-1] == '/') 3386a6e350fSMatthew Dillon return(""); 3396a6e350fSMatthew Dillon else 3406a6e350fSMatthew Dillon return("/"); 3416a6e350fSMatthew Dillon } 3426a6e350fSMatthew Dillon 3436a6e350fSMatthew Dillon /* 3446a6e350fSMatthew Dillon * Check whether the desired period has elapsed since the last successful 3456a6e350fSMatthew Dillon * run. The run may take a while and cross a boundary so we remember the 3466a6e350fSMatthew Dillon * current time_t so we can save it later on. 3476a6e350fSMatthew Dillon * 3486a6e350fSMatthew Dillon * Periods in minutes, hours, or days are assumed to have been crossed 3496a6e350fSMatthew Dillon * if the local time crosses a minute, hour, or day boundary regardless 3506a6e350fSMatthew Dillon * of how close the last operation actually was. 3516a6e350fSMatthew Dillon */ 3526a6e350fSMatthew Dillon static int 3536a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1, 3546a6e350fSMatthew Dillon time_t *savep) 3556a6e350fSMatthew Dillon { 3566a6e350fSMatthew Dillon char *check_path; 3576a6e350fSMatthew Dillon struct tm tp1; 3586a6e350fSMatthew Dillon struct tm tp2; 3596a6e350fSMatthew Dillon FILE *fp; 3606a6e350fSMatthew Dillon time_t baset, lastt; 3616a6e350fSMatthew Dillon char buf[256]; 3626a6e350fSMatthew Dillon 3636a6e350fSMatthew Dillon time(savep); 3646a6e350fSMatthew Dillon localtime_r(savep, &tp1); 3656a6e350fSMatthew Dillon 3666a6e350fSMatthew Dillon /* 3676a6e350fSMatthew Dillon * Retrieve the start time of the last successful operation. 3686a6e350fSMatthew Dillon */ 3696a6e350fSMatthew Dillon asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd); 3706a6e350fSMatthew Dillon fp = fopen(check_path, "r"); 3716a6e350fSMatthew Dillon free(check_path); 3726a6e350fSMatthew Dillon if (fp == NULL) 3736a6e350fSMatthew Dillon return(1); 3746a6e350fSMatthew Dillon if (fgets(buf, sizeof(buf), fp) == NULL) { 3756a6e350fSMatthew Dillon fclose(fp); 3766a6e350fSMatthew Dillon return(1); 3776a6e350fSMatthew Dillon } 3786a6e350fSMatthew Dillon fclose(fp); 3796a6e350fSMatthew Dillon 3806a6e350fSMatthew Dillon lastt = strtol(buf, NULL, 0); 3816a6e350fSMatthew Dillon localtime_r(&lastt, &tp2); 3826a6e350fSMatthew Dillon 3836a6e350fSMatthew Dillon /* 3846a6e350fSMatthew Dillon * Normalize the times. e.g. if asked to do something on a 1-day 3856a6e350fSMatthew Dillon * interval the operation will be performed as soon as the day 3866a6e350fSMatthew Dillon * turns over relative to the previous operation, even if the previous 3876a6e350fSMatthew Dillon * operation ran a few seconds ago just before midnight. 3886a6e350fSMatthew Dillon */ 3896a6e350fSMatthew Dillon if (arg1 % 60 == 0) { 3906a6e350fSMatthew Dillon tp1.tm_sec = 0; 3916a6e350fSMatthew Dillon tp2.tm_sec = 0; 3926a6e350fSMatthew Dillon } 3936a6e350fSMatthew Dillon if (arg1 % (60 * 60) == 0) { 3946a6e350fSMatthew Dillon tp1.tm_min = 0; 3956a6e350fSMatthew Dillon tp2.tm_min = 0; 3966a6e350fSMatthew Dillon } 3976a6e350fSMatthew Dillon if (arg1 % (24 * 60 * 60) == 0) { 3986a6e350fSMatthew Dillon tp1.tm_hour = 0; 3996a6e350fSMatthew Dillon tp2.tm_hour = 0; 4006a6e350fSMatthew Dillon } 4016a6e350fSMatthew Dillon 4026a6e350fSMatthew Dillon baset = mktime(&tp1); 4036a6e350fSMatthew Dillon lastt = mktime(&tp2); 4046a6e350fSMatthew Dillon 4056a6e350fSMatthew Dillon #if 0 4066a6e350fSMatthew Dillon printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1); 4076a6e350fSMatthew Dillon #endif 4086a6e350fSMatthew Dillon 4096a6e350fSMatthew Dillon if ((int)(baset - lastt) >= arg1) 4106a6e350fSMatthew Dillon return(1); 4116a6e350fSMatthew Dillon return(0); 4126a6e350fSMatthew Dillon } 4136a6e350fSMatthew Dillon 4146a6e350fSMatthew Dillon /* 4156a6e350fSMatthew Dillon * Store the start time of the last successful operation. 4166a6e350fSMatthew Dillon */ 4176a6e350fSMatthew Dillon static void 4186a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd, 4196a6e350fSMatthew Dillon time_t savet) 4206a6e350fSMatthew Dillon { 4216a6e350fSMatthew Dillon char *ocheck_path; 4226a6e350fSMatthew Dillon char *ncheck_path; 4236a6e350fSMatthew Dillon FILE *fp; 4246a6e350fSMatthew Dillon 4256a6e350fSMatthew Dillon asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd); 4266a6e350fSMatthew Dillon asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd); 4276a6e350fSMatthew Dillon fp = fopen(ncheck_path, "w"); 4286a6e350fSMatthew Dillon fprintf(fp, "0x%08llx\n", (long long)savet); 4296a6e350fSMatthew Dillon if (fclose(fp) == 0) 4306a6e350fSMatthew Dillon rename(ncheck_path, ocheck_path); 4316a6e350fSMatthew Dillon remove(ncheck_path); 4326a6e350fSMatthew Dillon } 4336a6e350fSMatthew Dillon 434*c6c298a7SMatthew Dillon static int 435*c6c298a7SMatthew Dillon check_softlinks(const char *snapshots_path) 436*c6c298a7SMatthew Dillon { 437*c6c298a7SMatthew Dillon struct dirent *den; 438*c6c298a7SMatthew Dillon struct stat st; 439*c6c298a7SMatthew Dillon DIR *dir; 440*c6c298a7SMatthew Dillon char *fpath; 441*c6c298a7SMatthew Dillon int res = 0; 442*c6c298a7SMatthew Dillon 443*c6c298a7SMatthew Dillon /* 444*c6c298a7SMatthew Dillon * Force snapshots_disabled to 0 if the snapshots directory 445*c6c298a7SMatthew Dillon * contains softlinks. 446*c6c298a7SMatthew Dillon */ 447*c6c298a7SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 448*c6c298a7SMatthew Dillon while ((den = readdir(dir)) != NULL) { 449*c6c298a7SMatthew Dillon if (den->d_name[0] == '.') 450*c6c298a7SMatthew Dillon continue; 451*c6c298a7SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 452*c6c298a7SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) 453*c6c298a7SMatthew Dillon ++res; 454*c6c298a7SMatthew Dillon free(fpath); 455*c6c298a7SMatthew Dillon } 456*c6c298a7SMatthew Dillon closedir(dir); 457*c6c298a7SMatthew Dillon } 458*c6c298a7SMatthew Dillon return(res); 459*c6c298a7SMatthew Dillon } 460*c6c298a7SMatthew Dillon 4616a6e350fSMatthew Dillon /* 4626a6e350fSMatthew Dillon * Issue a snapshot. 4636a6e350fSMatthew Dillon */ 4646a6e350fSMatthew Dillon static int 4656a6e350fSMatthew Dillon cleanup_snapshots(const char *path __unused, const char *snapshots_path, 4666a6e350fSMatthew Dillon int arg1 __unused, int arg2 __unused) 4676a6e350fSMatthew Dillon { 4686a6e350fSMatthew Dillon int r; 4696a6e350fSMatthew Dillon 4706a6e350fSMatthew Dillon runcmd(&r, "hammer snapshot %s", snapshots_path); 4716a6e350fSMatthew Dillon return(r); 4726a6e350fSMatthew Dillon } 4736a6e350fSMatthew Dillon 4746a6e350fSMatthew Dillon static int 4756a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path, 476*c6c298a7SMatthew Dillon int arg1 __unused, int arg2, int snapshots_disabled) 4776a6e350fSMatthew Dillon { 478*c6c298a7SMatthew Dillon /* 479*c6c298a7SMatthew Dillon * If snapshots have been disabled run prune-everything instead 480*c6c298a7SMatthew Dillon * of prune. 481*c6c298a7SMatthew Dillon */ 482*c6c298a7SMatthew Dillon if (snapshots_disabled && arg2) { 483*c6c298a7SMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s", 484*c6c298a7SMatthew Dillon snapshots_path, arg2, path); 485*c6c298a7SMatthew Dillon } else if (snapshots_disabled) { 486*c6c298a7SMatthew Dillon runcmd(NULL, "hammer prune-everything %s", path); 487*c6c298a7SMatthew Dillon } else if (arg2) { 4886a6e350fSMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s", 4896a6e350fSMatthew Dillon snapshots_path, arg2, snapshots_path); 4906a6e350fSMatthew Dillon } else { 4916a6e350fSMatthew Dillon runcmd(NULL, "hammer prune %s", snapshots_path); 4926a6e350fSMatthew Dillon } 4936a6e350fSMatthew Dillon return(0); 4946a6e350fSMatthew Dillon } 4956a6e350fSMatthew Dillon 4966a6e350fSMatthew Dillon static int 4976a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path, 4986a6e350fSMatthew Dillon int arg1 __unused, int arg2) 4996a6e350fSMatthew Dillon { 5006a6e350fSMatthew Dillon if (VerboseOpt == 0) { 5016a6e350fSMatthew Dillon printf("."); 5026a6e350fSMatthew Dillon fflush(stdout); 5036a6e350fSMatthew Dillon } 5046a6e350fSMatthew Dillon runcmd(NULL, 5056a6e350fSMatthew Dillon "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s 95", 5066a6e350fSMatthew Dillon snapshots_path, arg2, path); 5076a6e350fSMatthew Dillon if (VerboseOpt == 0) { 5086a6e350fSMatthew Dillon printf("."); 5096a6e350fSMatthew Dillon fflush(stdout); 5106a6e350fSMatthew Dillon } 5116a6e350fSMatthew Dillon runcmd(NULL, 5126a6e350fSMatthew Dillon "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s 95", 5136a6e350fSMatthew Dillon snapshots_path, arg2, path); 5146a6e350fSMatthew Dillon if (VerboseOpt == 0) { 5156a6e350fSMatthew Dillon printf("."); 5166a6e350fSMatthew Dillon fflush(stdout); 5176a6e350fSMatthew Dillon } 5186a6e350fSMatthew Dillon runcmd(NULL, 5196a6e350fSMatthew Dillon "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95", 5206a6e350fSMatthew Dillon snapshots_path, arg2, path); 5216a6e350fSMatthew Dillon if (VerboseOpt == 0) 5226a6e350fSMatthew Dillon printf("\n"); 5236a6e350fSMatthew Dillon return(0); 5246a6e350fSMatthew Dillon } 5256a6e350fSMatthew Dillon 5266a6e350fSMatthew Dillon static int 5276a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path, 5286a6e350fSMatthew Dillon int arg1 __unused, int arg2) 5296a6e350fSMatthew Dillon { 5306a6e350fSMatthew Dillon if (VerboseOpt == 0) { 5316a6e350fSMatthew Dillon printf("."); 5326a6e350fSMatthew Dillon fflush(stdout); 5336a6e350fSMatthew Dillon } 5346a6e350fSMatthew Dillon runcmd(NULL, 5356a6e350fSMatthew Dillon "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s", 5366a6e350fSMatthew Dillon snapshots_path, arg2, path); 5376a6e350fSMatthew Dillon if (VerboseOpt == 0) { 5386a6e350fSMatthew Dillon printf("."); 5396a6e350fSMatthew Dillon fflush(stdout); 5406a6e350fSMatthew Dillon } 5416a6e350fSMatthew Dillon runcmd(NULL, 5426a6e350fSMatthew Dillon "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s", 5436a6e350fSMatthew Dillon snapshots_path, arg2, path); 5446a6e350fSMatthew Dillon if (VerboseOpt == 0) { 5456a6e350fSMatthew Dillon printf("."); 5466a6e350fSMatthew Dillon fflush(stdout); 5476a6e350fSMatthew Dillon } 5486a6e350fSMatthew Dillon runcmd(NULL, 5496a6e350fSMatthew Dillon "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s", 5506a6e350fSMatthew Dillon snapshots_path, arg2, path); 5516a6e350fSMatthew Dillon if (VerboseOpt == 0) 5526a6e350fSMatthew Dillon printf("\n"); 5536a6e350fSMatthew Dillon return(0); 5546a6e350fSMatthew Dillon } 5556a6e350fSMatthew Dillon 5566a6e350fSMatthew Dillon static 5576a6e350fSMatthew Dillon void 5586a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...) 5596a6e350fSMatthew Dillon { 5606a6e350fSMatthew Dillon va_list va; 5616a6e350fSMatthew Dillon char *cmd; 5626a6e350fSMatthew Dillon char *arg; 5636a6e350fSMatthew Dillon char **av; 5646a6e350fSMatthew Dillon int n; 5656a6e350fSMatthew Dillon int nmax; 5666a6e350fSMatthew Dillon int res; 5676a6e350fSMatthew Dillon pid_t pid; 5686a6e350fSMatthew Dillon 5696a6e350fSMatthew Dillon /* 5706a6e350fSMatthew Dillon * Generate the command 5716a6e350fSMatthew Dillon */ 5726a6e350fSMatthew Dillon va_start(va, ctl); 5736a6e350fSMatthew Dillon vasprintf(&cmd, ctl, va); 5746a6e350fSMatthew Dillon va_end(va); 5756a6e350fSMatthew Dillon if (VerboseOpt) 5766a6e350fSMatthew Dillon printf(" %s\n", cmd); 5776a6e350fSMatthew Dillon 5786a6e350fSMatthew Dillon /* 5796a6e350fSMatthew Dillon * Break us down into arguments. We do not just use system() here 5806a6e350fSMatthew Dillon * because it blocks SIGINT and friends. 5816a6e350fSMatthew Dillon */ 5826a6e350fSMatthew Dillon n = 0; 5836a6e350fSMatthew Dillon nmax = 16; 5846a6e350fSMatthew Dillon av = malloc(sizeof(char *) * nmax); 5856a6e350fSMatthew Dillon 5866a6e350fSMatthew Dillon for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) { 5876a6e350fSMatthew Dillon if (n == nmax) { 5886a6e350fSMatthew Dillon nmax += 16; 5896a6e350fSMatthew Dillon av = realloc(av, sizeof(char *) * nmax); 5906a6e350fSMatthew Dillon } 5916a6e350fSMatthew Dillon av[n++] = arg; 5926a6e350fSMatthew Dillon } 5936a6e350fSMatthew Dillon 5946a6e350fSMatthew Dillon /* 5956a6e350fSMatthew Dillon * Run the command. 5966a6e350fSMatthew Dillon */ 5976a6e350fSMatthew Dillon if ((pid = fork()) == 0) { 5986a6e350fSMatthew Dillon if (VerboseOpt < 2) { 5996a6e350fSMatthew Dillon int fd = open("/dev/null", O_RDWR); 6006a6e350fSMatthew Dillon dup2(fd, 1); 6016a6e350fSMatthew Dillon close(fd); 6026a6e350fSMatthew Dillon } 6036a6e350fSMatthew Dillon execvp(av[0], av); 6046a6e350fSMatthew Dillon _exit(127); 6056a6e350fSMatthew Dillon } else if (pid < 0) { 6066a6e350fSMatthew Dillon res = 127; 6076a6e350fSMatthew Dillon } else { 6086a6e350fSMatthew Dillon int status; 6096a6e350fSMatthew Dillon while (waitpid(pid, &status, 0) != pid) 6106a6e350fSMatthew Dillon ; 6116a6e350fSMatthew Dillon res = WEXITSTATUS(status); 6126a6e350fSMatthew Dillon } 6136a6e350fSMatthew Dillon 6146a6e350fSMatthew Dillon free(cmd); 6156a6e350fSMatthew Dillon free(av); 6166a6e350fSMatthew Dillon if (resp) 6176a6e350fSMatthew Dillon *resp = res; 6186a6e350fSMatthew Dillon } 6196a6e350fSMatthew Dillon 6206a6e350fSMatthew Dillon 621