Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly
[dragonfly.git] / sbin / hammer / cmd_cleanup.c
CommitLineData
6a6e350f
MD
1/*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
5e435c92 3 *
6a6e350f
MD
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
5e435c92 33 *
bb8e52c0 34 * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.6 2008/10/07 22:28:41 thomas Exp $
6a6e350f
MD
35 */
36/*
37 * Clean up a specific HAMMER filesystem or all HAMMER filesystems.
38 *
39 * Each filesystem is expected to have a <mount>/snapshots directory.
40 * No cleanup will be performed on any filesystem that does not. If
41 * no filesystems are specified the 'df' program is run and any HAMMER
42 * or null-mounted hammer PFS's are extracted.
43 *
44 * The snapshots directory may contain a config file called 'config'. If
45 * no config file is present one will be created with the following
46 * defaults:
47 *
5e435c92 48 * snapshots 1d 60d (0d 0d for /tmp, /var/tmp, /usr/obj)
6a6e350f
MD
49 * prune 1d 5m
50 * reblock 1d 5m
51 * recopy 30d 5m
52 *
53 * All hammer commands create and maintain cycle files in the snapshots
54 * directory.
55 */
56
57#include "hammer.h"
58
c453712a
MD
59struct didpfs {
60 struct didpfs *next;
61 uuid_t uuid;
62};
63
6a6e350f
MD
64static void do_cleanup(const char *path);
65static int strtosecs(char *ptr);
66static const char *dividing_slash(const char *path);
67static int check_period(const char *snapshots_path, const char *cmd, int arg1,
68 time_t *savep);
69static void save_period(const char *snapshots_path, const char *cmd,
70 time_t savet);
c6c298a7 71static int check_softlinks(const char *snapshots_path);
ff1c9800 72static void cleanup_softlinks(const char *path, const char *snapshots_path,
5e435c92 73 int arg2, char *arg3);
ff1c9800 74static int check_expired(const char *fpath, int arg2);
6a6e350f 75
cbbb4f37 76static int create_snapshot(const char *path, const char *snapshots_path,
6a6e350f
MD
77 int arg1, int arg2);
78static int cleanup_prune(const char *path, const char *snapshots_path,
c6c298a7 79 int arg1, int arg2, int snapshots_disabled);
6a6e350f
MD
80static int cleanup_reblock(const char *path, const char *snapshots_path,
81 int arg1, int arg2);
82static int cleanup_recopy(const char *path, const char *snapshots_path,
83 int arg1, int arg2);
84
85static void runcmd(int *resp, const char *ctl, ...);
86
87#define WS " \t\r\n"
6a6e350f 88
c453712a 89struct didpfs *FirstPFS;
6a6e350f
MD
90
91void
92hammer_cmd_cleanup(char **av, int ac)
93{
94 FILE *fp;
5cb5194d 95 char *fs, *ptr, *path;
6a6e350f
MD
96 char buf[256];
97
98 tzset();
99 if (ac == 0) {
6ed24421 100 fp = popen("/sbin/mount -t hammer,null", "r");
6a6e350f 101 if (fp == NULL)
5cb5194d 102 errx(1, "hammer cleanup: 'mount' failed");
6a6e350f 103 while (fgets(buf, sizeof(buf), fp) != NULL) {
5cb5194d
SW
104 fs = strtok(buf, WS);
105 if (fs == NULL)
6a6e350f 106 continue;
5cb5194d
SW
107 ptr = strtok(NULL, WS);
108 if (ptr == NULL)
109 continue;
110 path = strtok(NULL, WS);
111 if (path == NULL)
112 continue;
113 ptr = strtok(NULL, WS);
114 if (ptr == NULL)
115 continue;
116 if ((strncmp(ptr, "(hammer,", 8) == 0) ||
117 ((strncmp(ptr, "(null,", 6) == 0) &&
cb3c760c
MD
118 (strstr(fs, "/@@0x") != NULL ||
119 strstr(fs, "/@@-1") != NULL))) {
5cb5194d 120 do_cleanup(path);
574066d3 121 }
6a6e350f
MD
122 }
123 fclose(fp);
124 } else {
125 while (ac) {
126 do_cleanup(*av);
127 --ac;
128 ++av;
129 }
130 }
131}
132
133static
134void
135do_cleanup(const char *path)
136{
137 struct hammer_ioc_pseudofs_rw pfs;
138 union hammer_ioc_mrecord_any mrec_tmp;
139 char *snapshots_path;
140 char *config_path;
141 struct stat st;
142 char *cmd;
143 char *ptr;
144 int arg1;
145 int arg2;
5e435c92 146 char *arg3;
6a6e350f
MD
147 time_t savet;
148 char buf[256];
149 FILE *fp;
c453712a 150 struct didpfs *didpfs;
c6c298a7
MD
151 int snapshots_disabled = 0;
152 int prune_warning = 0;
6a6e350f
MD
153 int fd;
154 int r;
155
156 bzero(&pfs, sizeof(pfs));
157 bzero(&mrec_tmp, sizeof(mrec_tmp));
158 pfs.ondisk = &mrec_tmp.pfs.pfsd;
159 pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
160 pfs.pfs_id = -1;
161
162 printf("cleanup %-20s -", path);
163 fd = open(path, O_RDONLY);
164 if (fd < 0) {
165 printf(" unable to access directory: %s\n", strerror(errno));
166 return;
167 }
168 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
169 printf(" not a HAMMER filesystem: %s\n", strerror(errno));
170 return;
171 }
172 close(fd);
173 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
174 printf(" unrecognized HAMMER version\n");
175 return;
176 }
177
178 /*
179 * Make sure we have not already handled this PFS. Several nullfs
180 * mounts might alias the same PFS.
181 */
c453712a
MD
182 for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
183 if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
bb8e52c0 184 printf(" PFS #%d already handled\n", pfs.pfs_id);
c453712a
MD
185 return;
186 }
6a6e350f 187 }
c453712a
MD
188 didpfs = malloc(sizeof(*didpfs));
189 didpfs->next = FirstPFS;
190 FirstPFS = didpfs;
191 didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
6a6e350f 192
ff1c9800
MD
193 /*
194 * Figure out where the snapshot directory is.
195 */
196 if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
197 asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
198 } else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
199 printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
200 return;
201 } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
202 printf(" WARNING: must configure snapshot dir for PFS slave\n");
203 printf("\tWe suggest <fs>/var/slaves/<name> where "
204 "<fs> is the base HAMMER fs\n");
bb8e52c0 205 printf("\tcontaining the slave\n");
ff1c9800
MD
206 return;
207 } else {
208 asprintf(&snapshots_path,
209 "%s%ssnapshots", path, dividing_slash(path));
210 }
211
6a6e350f
MD
212 /*
213 * Create a snapshot directory if necessary, and a config file if
214 * necessary.
215 */
6a6e350f
MD
216 if (stat(snapshots_path, &st) < 0) {
217 if (mkdir(snapshots_path, 0755) != 0) {
218 free(snapshots_path);
ff1c9800
MD
219 printf(" unable to create snapshot dir \"%s\": %s\n",
220 snapshots_path, strerror(errno));
6a6e350f
MD
221 return;
222 }
223 }
224 asprintf(&config_path, "%s/config", snapshots_path);
225 if ((fp = fopen(config_path, "r")) == NULL) {
226 fp = fopen(config_path, "w");
227 if (fp == NULL) {
228 printf(" cannot create %s: %s\n",
229 config_path, strerror(errno));
230 return;
231 }
232 if (strcmp(path, "/tmp") == 0 ||
233 strcmp(path, "/var/tmp") == 0 ||
234 strcmp(path, "/usr/obj") == 0) {
5e435c92 235 fprintf(fp, "snapshots 0d 0d\n");
6a6e350f
MD
236 } else {
237 fprintf(fp, "snapshots 1d 60d\n");
238 }
239 fprintf(fp,
240 "prune 1d 5m\n"
241 "reblock 1d 5m\n"
242 "recopy 30d 10m\n");
243 fclose(fp);
244 fp = fopen(config_path, "r");
245 }
246 if (fp == NULL) {
247 printf(" cannot access %s: %s\n",
248 config_path, strerror(errno));
249 return;
250 }
251
5bd5f172
SS
252 if (flock(fileno(fp), LOCK_EX|LOCK_NB) == -1) {
253 if (errno == EWOULDBLOCK)
254 printf(" PFS #%d locked by other process\n", pfs.pfs_id);
255 else
256 printf(" can not lock %s: %s\n", config_path, strerror(errno));
257 fclose(fp);
258 return;
259 }
260
ff1c9800 261 printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
6a6e350f
MD
262
263 /*
264 * Process the config file
265 */
266 while (fgets(buf, sizeof(buf), fp) != NULL) {
267 cmd = strtok(buf, WS);
268 arg1 = 0;
269 arg2 = 0;
5e435c92 270 arg3 = NULL;
6a6e350f
MD
271 if ((ptr = strtok(NULL, WS)) != NULL) {
272 arg1 = strtosecs(ptr);
5e435c92 273 if ((ptr = strtok(NULL, WS)) != NULL) {
6a6e350f 274 arg2 = strtosecs(ptr);
5e435c92
MD
275 arg3 = strtok(NULL, WS);
276 }
6a6e350f
MD
277 }
278
279 printf("%20s - ", cmd);
280 fflush(stdout);
281
6a6e350f
MD
282 r = 1;
283 if (strcmp(cmd, "snapshots") == 0) {
5e435c92
MD
284 if (arg1 == 0) {
285 if (arg2 && check_softlinks(snapshots_path)) {
286 printf("only removing old snapshots\n");
287 prune_warning = 1;
288 cleanup_softlinks(path, snapshots_path,
289 arg2, arg3);
290 } else {
291 printf("disabled\n");
292 snapshots_disabled = 1;
293 }
294 } else
6a6e350f
MD
295 if (check_period(snapshots_path, cmd, arg1, &savet)) {
296 printf("run\n");
5e435c92
MD
297 cleanup_softlinks(path, snapshots_path,
298 arg2, arg3);
cbbb4f37 299 r = create_snapshot(path, snapshots_path,
6a6e350f
MD
300 arg1, arg2);
301 } else {
302 printf("skip\n");
303 }
5e435c92
MD
304 } else if (arg1 == 0) {
305 /*
306 * The commands following this check can't handle
307 * a period of 0, so call the feature disabled and
308 * ignore the directive.
309 */
310 printf("disabled\n");
6a6e350f
MD
311 } else if (strcmp(cmd, "prune") == 0) {
312 if (check_period(snapshots_path, cmd, arg1, &savet)) {
5e435c92
MD
313 if (prune_warning) {
314 printf("run - WARNING snapshot "
315 "softlinks present "
316 "but snapshots disabled\n");
317 } else {
c6c298a7 318 printf("run\n");
5e435c92 319 }
6a6e350f 320 r = cleanup_prune(path, snapshots_path,
c6c298a7 321 arg1, arg2, snapshots_disabled);
6a6e350f
MD
322 } else {
323 printf("skip\n");
324 }
325 } else if (strcmp(cmd, "reblock") == 0) {
326 if (check_period(snapshots_path, cmd, arg1, &savet)) {
327 printf("run");
328 fflush(stdout);
329 if (VerboseOpt)
330 printf("\n");
331 r = cleanup_reblock(path, snapshots_path,
332 arg1, arg2);
333 } else {
334 printf("skip\n");
335 }
336 } else if (strcmp(cmd, "recopy") == 0) {
337 if (check_period(snapshots_path, cmd, arg1, &savet)) {
338 printf("run");
339 fflush(stdout);
340 if (VerboseOpt)
341 printf("\n");
342 r = cleanup_recopy(path, snapshots_path,
343 arg1, arg2);
344 } else {
345 printf("skip\n");
346 }
347 } else {
348 printf("unknown directive\n");
349 r = 1;
350 }
351 if (r == 0)
352 save_period(snapshots_path, cmd, savet);
353 }
354 fclose(fp);
355 usleep(1000);
356}
357
358static
359int
360strtosecs(char *ptr)
361{
362 int val;
363
364 val = strtol(ptr, &ptr, 0);
365 switch(*ptr) {
366 case 'd':
367 val *= 24;
368 /* fall through */
369 case 'h':
370 val *= 60;
371 /* fall through */
372 case 'm':
373 val *= 60;
374 /* fall through */
375 case 's':
376 break;
377 default:
378 errx(1, "illegal suffix converting %s\n", ptr);
379 break;
380 }
381 return(val);
382}
383
384static const char *
385dividing_slash(const char *path)
386{
387 int len = strlen(path);
388 if (len && path[len-1] == '/')
389 return("");
390 else
391 return("/");
392}
393
394/*
395 * Check whether the desired period has elapsed since the last successful
396 * run. The run may take a while and cross a boundary so we remember the
397 * current time_t so we can save it later on.
398 *
399 * Periods in minutes, hours, or days are assumed to have been crossed
400 * if the local time crosses a minute, hour, or day boundary regardless
401 * of how close the last operation actually was.
402 */
403static int
404check_period(const char *snapshots_path, const char *cmd, int arg1,
405 time_t *savep)
406{
407 char *check_path;
408 struct tm tp1;
409 struct tm tp2;
410 FILE *fp;
411 time_t baset, lastt;
412 char buf[256];
413
414 time(savep);
415 localtime_r(savep, &tp1);
416
417 /*
418 * Retrieve the start time of the last successful operation.
419 */
420 asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
421 fp = fopen(check_path, "r");
422 free(check_path);
423 if (fp == NULL)
424 return(1);
425 if (fgets(buf, sizeof(buf), fp) == NULL) {
426 fclose(fp);
427 return(1);
428 }
429 fclose(fp);
430
431 lastt = strtol(buf, NULL, 0);
432 localtime_r(&lastt, &tp2);
433
434 /*
435 * Normalize the times. e.g. if asked to do something on a 1-day
436 * interval the operation will be performed as soon as the day
437 * turns over relative to the previous operation, even if the previous
438 * operation ran a few seconds ago just before midnight.
439 */
440 if (arg1 % 60 == 0) {
441 tp1.tm_sec = 0;
442 tp2.tm_sec = 0;
443 }
444 if (arg1 % (60 * 60) == 0) {
445 tp1.tm_min = 0;
446 tp2.tm_min = 0;
447 }
448 if (arg1 % (24 * 60 * 60) == 0) {
449 tp1.tm_hour = 0;
450 tp2.tm_hour = 0;
451 }
452
453 baset = mktime(&tp1);
454 lastt = mktime(&tp2);
455
456#if 0
457 printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
458#endif
459
460 if ((int)(baset - lastt) >= arg1)
461 return(1);
462 return(0);
463}
464
465/*
466 * Store the start time of the last successful operation.
467 */
468static void
469save_period(const char *snapshots_path, const char *cmd,
470 time_t savet)
471{
472 char *ocheck_path;
473 char *ncheck_path;
474 FILE *fp;
475
476 asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
477 asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
478 fp = fopen(ncheck_path, "w");
3b9fbdea
MD
479 if (fp) {
480 fprintf(fp, "0x%08llx\n", (long long)savet);
481 if (fclose(fp) == 0)
482 rename(ncheck_path, ocheck_path);
483 remove(ncheck_path);
484 } else {
485 fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
486 ncheck_path, strerror(errno));
487 }
6a6e350f
MD
488}
489
ff1c9800
MD
490/*
491 * Simply count the number of softlinks in the snapshots dir
492 */
c6c298a7
MD
493static int
494check_softlinks(const char *snapshots_path)
495{
496 struct dirent *den;
497 struct stat st;
498 DIR *dir;
499 char *fpath;
500 int res = 0;
501
c6c298a7
MD
502 if ((dir = opendir(snapshots_path)) != NULL) {
503 while ((den = readdir(dir)) != NULL) {
504 if (den->d_name[0] == '.')
505 continue;
506 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
507 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
508 ++res;
509 free(fpath);
510 }
511 closedir(dir);
512 }
513 return(res);
514}
515
ff1c9800
MD
516/*
517 * Clean up expired softlinks in the snapshots dir
518 */
519static void
5e435c92
MD
520cleanup_softlinks(const char *path __unused, const char *snapshots_path,
521 int arg2, char *arg3)
ff1c9800
MD
522{
523 struct dirent *den;
524 struct stat st;
525 DIR *dir;
526 char *fpath;
5e435c92
MD
527 int anylink = 0;
528
09e1b0d6 529 if (arg3 != NULL && strstr(arg3, "any") != NULL)
5e435c92 530 anylink = 1;
ff1c9800
MD
531
532 if ((dir = opendir(snapshots_path)) != NULL) {
533 while ((den = readdir(dir)) != NULL) {
534 if (den->d_name[0] == '.')
535 continue;
536 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
537 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
5e435c92
MD
538 (anylink || strncmp(den->d_name, "snap-", 5) == 0)
539 ) {
ff1c9800
MD
540 if (check_expired(den->d_name, arg2)) {
541 if (VerboseOpt) {
542 printf(" expire %s\n",
543 fpath);
544 }
545 remove(fpath);
546 }
547 }
548 free(fpath);
549 }
550 closedir(dir);
551 }
552}
553
554/*
555 * Take a softlink path in the form snap-yyyymmdd-hhmm and the
556 * expiration in seconds (arg2) and return non-zero if the softlink
557 * has expired.
558 */
559static int
560check_expired(const char *fpath, int arg2)
561{
562 struct tm tm;
563 time_t t;
564 int year;
565 int month;
5e435c92
MD
566 int day = 0;
567 int hour = 0;
568 int minute = 0;
ff1c9800
MD
569 int r;
570
5e435c92
MD
571 while (*fpath && *fpath != '-' && *fpath != '.')
572 ++fpath;
573 if (*fpath)
574 ++fpath;
575
576 r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
ff1c9800 577 &year, &month, &day, &hour, &minute);
5e435c92
MD
578
579 if (r >= 3) {
ff1c9800
MD
580 bzero(&tm, sizeof(tm));
581 tm.tm_isdst = -1;
582 tm.tm_min = minute;
583 tm.tm_hour = hour;
584 tm.tm_mday = day;
585 tm.tm_mon = month - 1;
586 tm.tm_year = year - 1900;
5e435c92
MD
587 t = mktime(&tm);
588 if (t == (time_t)-1)
589 return(0);
590 t = time(NULL) - t;
ff1c9800
MD
591 if ((int)t > arg2)
592 return(1);
593 }
594 return(0);
595}
596
6a6e350f
MD
597/*
598 * Issue a snapshot.
599 */
600static int
cbbb4f37 601create_snapshot(const char *path __unused, const char *snapshots_path,
6a6e350f
MD
602 int arg1 __unused, int arg2 __unused)
603{
604 int r;
605
ff1c9800 606 runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
6a6e350f
MD
607 return(r);
608}
609
610static int
611cleanup_prune(const char *path __unused, const char *snapshots_path,
c6c298a7 612 int arg1 __unused, int arg2, int snapshots_disabled)
6a6e350f 613{
c6c298a7
MD
614 /*
615 * If snapshots have been disabled run prune-everything instead
616 * of prune.
617 */
618 if (snapshots_disabled && arg2) {
619 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
620 snapshots_path, arg2, path);
621 } else if (snapshots_disabled) {
622 runcmd(NULL, "hammer prune-everything %s", path);
623 } else if (arg2) {
6a6e350f
MD
624 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
625 snapshots_path, arg2, snapshots_path);
626 } else {
627 runcmd(NULL, "hammer prune %s", snapshots_path);
628 }
629 return(0);
630}
631
632static int
633cleanup_reblock(const char *path, const char *snapshots_path,
634 int arg1 __unused, int arg2)
635{
636 if (VerboseOpt == 0) {
637 printf(".");
638 fflush(stdout);
639 }
797a0b63
MD
640
641 /*
642 * When reblocking the B-Tree always reblock everything in normal
643 * mode.
644 */
645 runcmd(NULL,
646 "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
647 snapshots_path, arg2, path);
648 if (VerboseOpt == 0) {
649 printf(".");
650 fflush(stdout);
651 }
652
653 /*
654 * When reblocking the inodes always reblock everything in normal
655 * mode.
656 */
6a6e350f 657 runcmd(NULL,
797a0b63 658 "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
6a6e350f
MD
659 snapshots_path, arg2, path);
660 if (VerboseOpt == 0) {
661 printf(".");
662 fflush(stdout);
663 }
797a0b63
MD
664
665 /*
666 * When reblocking the directories always reblock everything in normal
667 * mode.
668 */
6a6e350f 669 runcmd(NULL,
797a0b63 670 "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
6a6e350f
MD
671 snapshots_path, arg2, path);
672 if (VerboseOpt == 0) {
673 printf(".");
674 fflush(stdout);
675 }
797a0b63
MD
676
677 /*
678 * Do not reblock all the data in normal mode.
679 */
6a6e350f
MD
680 runcmd(NULL,
681 "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
682 snapshots_path, arg2, path);
683 if (VerboseOpt == 0)
684 printf("\n");
685 return(0);
686}
687
688static int
689cleanup_recopy(const char *path, const char *snapshots_path,
690 int arg1 __unused, int arg2)
691{
692 if (VerboseOpt == 0) {
693 printf(".");
694 fflush(stdout);
695 }
696 runcmd(NULL,
697 "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
698 snapshots_path, arg2, path);
699 if (VerboseOpt == 0) {
700 printf(".");
701 fflush(stdout);
702 }
703 runcmd(NULL,
704 "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
705 snapshots_path, arg2, path);
706 if (VerboseOpt == 0) {
707 printf(".");
708 fflush(stdout);
709 }
797a0b63
MD
710 runcmd(NULL,
711 "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
712 snapshots_path, arg2, path);
713 if (VerboseOpt == 0) {
714 printf(".");
715 fflush(stdout);
716 }
6a6e350f
MD
717 runcmd(NULL,
718 "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
719 snapshots_path, arg2, path);
720 if (VerboseOpt == 0)
721 printf("\n");
722 return(0);
723}
724
725static
726void
727runcmd(int *resp, const char *ctl, ...)
728{
729 va_list va;
730 char *cmd;
731 char *arg;
732 char **av;
733 int n;
734 int nmax;
735 int res;
736 pid_t pid;
737
738 /*
739 * Generate the command
740 */
741 va_start(va, ctl);
742 vasprintf(&cmd, ctl, va);
743 va_end(va);
744 if (VerboseOpt)
745 printf(" %s\n", cmd);
746
747 /*
748 * Break us down into arguments. We do not just use system() here
749 * because it blocks SIGINT and friends.
750 */
751 n = 0;
752 nmax = 16;
753 av = malloc(sizeof(char *) * nmax);
754
755 for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
c453712a 756 if (n == nmax - 1) {
6a6e350f
MD
757 nmax += 16;
758 av = realloc(av, sizeof(char *) * nmax);
759 }
760 av[n++] = arg;
761 }
c453712a 762 av[n++] = NULL;
6a6e350f
MD
763
764 /*
765 * Run the command.
766 */
445faa69 767 RunningIoctl = 1;
6a6e350f
MD
768 if ((pid = fork()) == 0) {
769 if (VerboseOpt < 2) {
770 int fd = open("/dev/null", O_RDWR);
771 dup2(fd, 1);
772 close(fd);
773 }
774 execvp(av[0], av);
775 _exit(127);
776 } else if (pid < 0) {
777 res = 127;
778 } else {
779 int status;
445faa69 780
6a6e350f
MD
781 while (waitpid(pid, &status, 0) != pid)
782 ;
783 res = WEXITSTATUS(status);
784 }
445faa69
MD
785 RunningIoctl = 0;
786 if (DidInterrupt)
787 _exit(1);
6a6e350f
MD
788
789 free(cmd);
790 free(av);
791 if (resp)
792 *resp = res;
793}
794
795