]> git.tuebingen.mpg.de Git - misma.git/blob - misma.c
Fix seconds_to_human().
[misma.git] / misma.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2 #include "misma.h"
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <lopsub.h>
6 #include <sys/mman.h>
7 #include <math.h>
8 #include <signal.h>
9 #include <sys/stat.h>
10 #include <sys/sysmacros.h>
11 #include <sys/ioctl.h>
12
13 #include "misma.lsg.h"
14
15 enum interval_type {
16         IT_CREATE,
17         IT_TRIM,
18         IT_MAX_AGE,
19         NUM_INTERVAL_TYPES
20 };
21
22 struct snapshot_config {
23         struct percentage_pair thresholds;
24         unsigned interval[NUM_INTERVAL_TYPES];
25 };
26 static struct snapshot_config global_config = {
27         .thresholds = {.data = 95, .meta = 95},
28         .interval = {
29                 [IT_CREATE] = 6 * 3600,
30                 [IT_TRIM] = 0,
31                 [IT_MAX_AGE] = 86400 * 365
32         }
33 };
34
35 enum event_type {ET_CREATE, ET_CHECK, ET_TRIM, NUM_EVENT_TYPES};
36
37 struct volume_group {
38         char *name;
39         struct snapshot_config config;
40 };
41 static unsigned num_vgs;
42 static struct volume_group *volume_group; /* num_vgs elements */
43
44 static const char *vgname(unsigned vgid)
45 {
46         return volume_group[vgid].name;
47 }
48
49 /* sequential search is good enough */
50 static unsigned get_vgid(const char *name)
51 {
52         for (unsigned n = 0; n < num_vgs; n++)
53                 if (!strcmp(name, volume_group[n].name))
54                         return n;
55         return ~0U;
56 }
57
58 /* insert only if it not exists already */
59 static unsigned insert_vg(const char *name)
60 {
61         struct volume_group *vg;
62         unsigned vgid = get_vgid(name);
63
64         if (vgid != ~0U)
65                 return vgid;
66         INFO_LOG("vg #%u: %s\n", num_vgs, name);
67         num_vgs++;
68         volume_group = xrealloc(volume_group, num_vgs
69                 * sizeof(struct volume_group));
70         vg = volume_group + num_vgs - 1;
71         memset(vg, 0, sizeof(struct volume_group));
72         vg->name = xstrdup(name);
73         return num_vgs - 1;
74 }
75
76 struct thin_pool {
77         char *name;
78         unsigned vgid;
79         struct snapshot_config config;
80         struct percentage_pair utilization;
81         enum lvm_scope threshold_scope;
82 };
83 static unsigned num_pools;
84 static struct thin_pool *thin_pool; /* num_pools elements */
85
86 static unsigned get_poolid(const char *name, const char *vg_name)
87 {
88         for (unsigned n = 0; n < num_pools; n++) {
89                 struct thin_pool *pool = thin_pool + n;
90                 if (!strcmp(name, pool->name) && !strcmp(vg_name,
91                                 vgname(pool->vgid)))
92                         return n;
93         }
94         return ~0U;
95 }
96
97 /* vg of pool must have been inserted already */
98 static unsigned insert_pool(const char *name, const char *vgname)
99 {
100         struct thin_pool *pool;
101         unsigned poolid = get_poolid(name, vgname);
102
103         if (poolid != ~0U)
104                 return poolid;
105         INFO_LOG("pool #%u: %s/%s\n", num_pools, vgname, name);
106         num_pools++;
107         thin_pool = xrealloc(thin_pool, num_pools * sizeof(struct thin_pool));
108         pool = thin_pool + num_pools - 1;
109         memset(pool, 0, sizeof(struct thin_pool));
110         pool->name = xstrdup(name);
111         pool->vgid = get_vgid(vgname);
112         if (pool->vgid == ~0U)
113                 die("invalid vg: %s", vgname);
114         return num_pools - 1;
115 }
116
117 struct snapshot {
118         unsigned seq;
119         uint64_t epoch;
120 };
121
122 struct origin {
123         char *name;
124         unsigned vgid;
125         unsigned poolid;
126         struct snapshot_config config;
127         enum lvm_scope iscope[NUM_INTERVAL_TYPES]; /* interval scopes */
128         uint64_t last_event[NUM_EVENT_TYPES]; /* epochs */
129         unsigned last_seq;
130         unsigned num_slots;
131         struct snapshot *snapshot;
132 };
133 static unsigned num_origins;
134 static struct origin *origin;
135 #define FOR_EACH_ORIGIN(_n) for (_n = 0; _n < num_origins; _n++)
136
137 static unsigned check_seconds = 60;
138
139 static unsigned interval_length(enum interval_type it, const struct origin *o)
140 {
141         switch (o->iscope[it]) {
142                 case LS_GLOBAL: return global_config.interval[it];
143                 case LS_VG: return volume_group[o->vgid].config.interval[it];
144                 case LS_POOL: return thin_pool[o->poolid].config.interval[it];
145                 case LS_ORIGIN: return o->config.interval[it];
146                 default: assert(0);
147         }
148 }
149
150 static unsigned get_oid(const char *name, const char *vg_name)
151 {
152         unsigned n;
153         FOR_EACH_ORIGIN(n) {
154                 struct origin *o = origin + n;
155                 if (!strcmp(name, o->name) && !strcmp(vg_name, vgname(o->vgid)))
156                         return n;
157         }
158         return ~0U;
159 }
160
161 /* vg must have been inserted already */
162 static unsigned insert_origin(const char *name, const char *vgname,
163                 const char *poolname)
164 {
165         struct origin *o;
166         unsigned oid = get_oid(name, vgname);
167
168         assert(oid == ~0U);
169         INFO_LOG("origin #%u: %s/%s, pool: %s\n", num_origins, vgname, name,
170                 poolname);
171         num_origins++;
172         origin = xrealloc(origin, num_origins * sizeof(struct origin));
173         o = origin + num_origins - 1;
174         memset(o, 0, sizeof(struct origin));
175         o->name = xstrdup(name);
176         o->vgid = get_vgid(vgname);
177         assert(o->vgid != ~0U);
178         o->poolid = get_poolid(poolname, vgname);
179         assert(o->poolid != ~0U);
180         return num_origins - 1;
181 }
182
183 struct event {
184         enum event_type type;
185         uint64_t epoch;
186         struct origin *origin;
187 };
188
189 static int event_compare(const void *d1, const void *d2)
190 {
191         const struct event *a = d1, *b = d2;
192
193         if (a->epoch < b->epoch)
194                 return 1;
195         if (a->epoch > b->epoch)
196                 return -1;
197         return 0;
198 }
199
200 static char *config_file;
201
202 #define FOR_EACH_SLOT_REVERSE(_j, _o) for ( \
203         unsigned _j = _o->num_slots - 1; _j != -1U; _j--)
204
205 static unsigned loglevel_arg_val = LL_WARNING;
206
207 /* lopsub */
208 static const struct lls_command *subcmd;
209 static struct lls_parse_result *lpr, *sublpr;
210 #define CMD_PTR(_cname) lls_cmd(LSG_MISMA_CMD_ ## _cname, misma_suite)
211 #define OPT_RESULT(_cname, _oname) (lls_opt_result(\
212         LSG_MISMA_ ## _cname ## _OPT_ ## _oname, \
213         (CMD_PTR(_cname) == CMD_PTR(MISMA))? lpr : sublpr))
214 #define OPT_GIVEN(_cname, _oname) (lls_opt_given(OPT_RESULT(_cname, _oname)))
215 #define OPT_UINT32_VAL(_cname, _oname) (lls_uint32_val(0, \
216                 OPT_RESULT(_cname, _oname)))
217 #define OPT_STRING_VAL_N(_n, _cname, _oname) (lls_string_val(_n, \
218         OPT_RESULT(_cname, _oname)))
219 #define OPT_STRING_VAL(_cname, _oname) (OPT_STRING_VAL_N(0, _cname, _oname))
220
221 struct misma_user_data {bool (*handler)(void);};
222 #define EXPORT_CMD_HANDLER(_cmd) const struct misma_user_data \
223         lsg_misma_com_ ## _cmd ## _user_data = { \
224                 .handler = com_ ## _cmd \
225         };
226
227 /* does not allocate memory */
228 void misma_log(int ll, const char* fmt,...)
229 {
230         va_list argp;
231         time_t t1;
232         struct tm *tm;
233         char str[255] = "";
234
235         if (ll < loglevel_arg_val)
236                 return;
237         if (subcmd == CMD_PTR(RUN)) {
238                 time(&t1);
239                 tm = localtime(&t1);
240                 strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
241                 fprintf(stderr, "%s ", str);
242         }
243         va_start(argp, fmt);
244         vfprintf(stderr, fmt, argp);
245         va_end(argp);
246 }
247 static const char *exit_hook;
248
249 __attribute__ ((noreturn))
250 static void run_exit_hook_and_die(const char *str)
251 {
252         char *arg;
253         char *argv[] = {"/bin/sh", "-c", NULL, NULL};
254         const char *tmp;
255
256         if (exit_hook) {
257                 /*
258                  * Prevent helpers from calling us again via die() or
259                  * die_errno(), which would result in a crash due to an endless
260                  * call stack.
261                  */
262                 tmp = exit_hook;
263                 exit_hook = NULL;
264                 arg = msg("%s '%s'", tmp, str);
265                 argv[2] = arg;
266                 xexec(argv, NULL);
267         }
268         exit(EXIT_FAILURE);
269 }
270
271 void die(const char *fmt, ...)
272 {
273         char *str;
274         va_list argp;
275         int ret;
276
277         va_start(argp, fmt);
278         ret = vasprintf(&str, fmt, argp);
279         va_end(argp);
280         if (ret < 0) { /* give up */
281                 EMERG_LOG("OOM\n");
282                 exit(EXIT_FAILURE);
283         }
284         misma_log(LL_EMERG, "%s\n", str);
285         run_exit_hook_and_die(str);
286 }
287
288 void die_errno(const char *fmt, ...)
289 {
290         char *str;
291         va_list argp;
292         int ret, save_errno = errno;
293
294         va_start(argp, fmt);
295         ret = vasprintf(&str, fmt, argp);
296         va_end(argp);
297         if (ret < 0) {
298                 EMERG_LOG("OOM\n");
299                 exit(EXIT_FAILURE);
300         }
301         misma_log(LL_EMERG, "%s: %s\n", str, strerror(save_errno));
302         run_exit_hook_and_die(str);
303 }
304
305 __attribute__ ((const))
306 static uint32_t ffz(uint32_t v)
307 {
308         uint32_t ret = 0;
309
310         assert(v != (uint32_t)-1);
311         if ((v & 0xffff) == 0xffff) {
312                 ret += 16;
313                 v >>= 16;
314         }
315         if ((v & 0xff) == 0xff) {
316                 ret += 8;
317                 v >>= 8;
318         }
319         if ((v & 0xf) == 0xf) {
320                 ret += 4;
321                 v >>= 4;
322         }
323         if ((v & 0x3) == 0x3) {
324                 ret += 2;
325                 v >>= 2;
326         }
327         if ((v & 0x1) == 0x1)
328                 ret += 1;
329         return ret;
330 }
331
332 static bool slot_is_used(unsigned slot, const struct origin *o)
333 {
334         return o->snapshot[slot].seq != 0;
335 }
336
337 static void mark_slot_unused(unsigned slot, struct origin *o)
338 {
339         o->snapshot[slot].seq = 0;
340 }
341
342 /* Use highest numbered unused slot, or default if all slots are used. */
343 static unsigned get_slot(unsigned seq, const struct origin *o)
344 {
345         unsigned mod;
346         FOR_EACH_SLOT_REVERSE(sl, o)
347                 if (!slot_is_used(sl, o))
348                         return sl;
349         /* all slots used */
350         mod = (1 << o->num_slots) - 1;
351         return ffz(seq % mod);
352 }
353
354 /*
355  * We specify --autobackup n to avoid filling up /etc/lvm/archive with tons of
356  * useless backup configurations.
357  */
358 static bool remove_snapshot(unsigned sl, struct origin *o, bool dry_run)
359 {
360         struct snapshot *snap = o->snapshot + sl;
361         bool success;
362         char *arg = msg("%s/misma-%s.%u", vgname(o->vgid), o->name, snap->seq);
363         char *argv[] = {
364                 "lvremove",
365                 "--yes",
366                 "--quiet",
367                 "--quiet",
368                 "--autobackup",
369                 "n",
370                 arg,
371                 NULL
372         };
373         if (dry_run) {
374                 printf("dry-run: would remove snapshot %s\n", arg);
375                 free(arg);
376                 return true;
377         }
378         NOTICE_LOG("removing snapshot %s\n", arg);
379         success = xexec(argv, NULL);
380         free(arg);
381         if (success)
382                 mark_slot_unused(sl, o);
383         return success;
384 }
385
386 static int slot_compare(const void *a, const void *b, void *data)
387 {
388         const struct snapshot *s1 = a, *s2 = b;
389         struct origin *o = data;
390
391         if (!slot_is_used(s1 - o->snapshot, o))
392                 return -1;
393         if (!slot_is_used(s2 - o->snapshot, o))
394                 return 1;
395         if (s1->seq < s2->seq)
396                 return 1;
397         if (s1->seq > s2->seq)
398                 return -1;
399         return 0;
400 }
401
402 static void sort_slots(struct origin *o)
403 {
404         qsort_r(o->snapshot, o->num_slots, sizeof(struct snapshot),
405                 slot_compare, o);
406 }
407
408 /*
409  * sleazy (adj.): 1640s, "downy, fuzzy," later "flimsy, unsubstantial" (1660s).
410  *
411  * A sleazy snapshot is one whose distance (with respect to creation time) to
412  * its sibling snapshots is minimal.
413  */
414 static bool remove_sleazy_snapshot(struct origin *o, bool dry_run)
415 {
416         unsigned sl, victim = 0;
417         uint64_t score = 0;
418         bool have_victim = false;
419         struct snapshot *prev = NULL, *next = NULL;
420
421         sort_slots(o);
422         for (sl = 0; sl < o->num_slots; sl++)
423                 if (slot_is_used(sl, o))
424                         break;
425         for (; sl < o->num_slots; prev = o->snapshot + sl, sl++) {
426                 uint64_t dist;
427                 struct snapshot *s = o->snapshot + sl;
428
429                 assert(slot_is_used(sl, o));
430                 next = sl == o->num_slots - 1? NULL : s + 1;
431                 if (!prev && !next)
432                         dist = 1;
433                 else if (!prev)
434                         dist = 10 * (s->epoch - next->epoch);
435                 else if (!next)
436                         dist = 10 * (prev->epoch - s->epoch);
437                 else
438                         dist = prev->epoch - next->epoch;
439                 DEBUG_LOG("seq %u, slot %u, epoch %" PRIu64 ", score %" PRIu64"\n",
440                         s->seq, sl, s->epoch, dist);
441                 if (!have_victim || dist < score) {
442                         have_victim = true;
443                         victim = sl;
444                         score = dist;
445                 }
446         }
447         if (!have_victim) {
448                 INFO_LOG("no snapshots\n");
449                 return false;
450         }
451         NOTICE_LOG("victim: seq %u, slot %u, score %" PRIu64 "\n",
452                 o->snapshot[victim].seq, victim, score);
453         if (!remove_snapshot(victim, o, dry_run))
454                 return false;
455         sort_slots(o);
456         return true;
457 }
458
459 static void set_interval(enum interval_type it, const struct time_arg *ta)
460 {
461         enum lvm_scope scope = ta->lvmspec.scope;
462         unsigned vgid, poolid, oid, n;
463
464         if (scope == LS_GLOBAL) {
465                 NOTICE_LOG("default interval #%u: %u seconds\n", it,
466                         ta->seconds);
467                 global_config.interval[it] = ta->seconds;
468                 return;
469         }
470         vgid = get_vgid(ta->lvmspec.vg);
471         if (vgid == ~0U)
472                 die("invalid vg in lvmspec: %s", ta->lvmspec.vg);
473         switch (scope) {
474         case LS_VG:
475                 volume_group[vgid].config.interval[it] = ta->seconds;
476                 break;
477         case LS_POOL:
478                 poolid = get_poolid(ta->lvmspec.pool, vgname(vgid));
479                 if (poolid == ~0U)
480                         die("invalid pool in lvmspec: %s", ta->lvmspec.pool);
481                 thin_pool[poolid].config.interval[it] = ta->seconds;
482                 break;
483         case LS_ORIGIN:
484                 oid = get_oid(ta->lvmspec.tlv, vgname(vgid));
485                 if (oid == ~0U)
486                         die("invalid tlv in lvmspec: %s", ta->lvmspec.tlv);
487                 origin[oid].config.interval[it] = ta->seconds;
488                 break;
489         default:
490                 assert(0);
491         }
492         /*
493          * Narrow the scope of all matching origins for which it is currently
494          * set to a wider scope.
495          */
496         FOR_EACH_ORIGIN(n) {
497                 struct origin *o = origin + n;
498                 if (o->iscope[it] >= scope)
499                         continue; /* already set to more narrow scope */
500                 switch (scope) {
501                 case LS_ORIGIN:
502                         if (n != oid)
503                                 continue;
504                         break;
505                 case LS_POOL:
506                         if (poolid != o->poolid || vgid != o->vgid)
507                                 continue;
508                         break;
509                 case LS_VG:
510                         if (vgid != o->vgid)
511                                 continue;
512                         break;
513                 default:
514                         assert(0);
515                 }
516                 NOTICE_LOG("interval #%u for %s/%s: %u seconds\n", it,
517                         vgname(o->vgid), o->name, ta->seconds);
518                 o->iscope[it] = scope;
519         }
520 }
521
522 struct lv_info {
523         char *vg, *lv, *pool, *origin;
524         uint64_t time;
525 };
526
527 static void free_lv_info(struct lv_info *lv)
528 {
529         free(lv->vg);
530         free(lv->lv);
531         free(lv->pool);
532         free(lv->origin);
533 }
534
535 static void parse_lvs_line(const char *line, struct lv_info *result)
536 {
537         char *tmp = xstrdup(line), *p = tmp + 2, *comma;
538
539         comma = strchr(p, ',');
540         assert(comma && comma != p);
541         *comma = '\0';
542         result->vg = xstrdup(p);
543         p = comma + 1;
544         comma = strchr(p, ',');
545         assert(comma);
546         *comma = '\0';
547         result->lv = xstrdup(p);
548         p = comma + 1;
549         comma = strchr(p, ',');
550         assert(comma);
551         *comma = '\0';
552         result->pool = xstrdup(p);
553         p = comma + 1;
554         comma = strchr(p, ',');
555         assert(comma);
556         *comma = '\0';
557         result->origin = xstrdup(p);
558         p = comma + 1;
559         assert(sscanf(p, "%" PRIu64, &result->time) == 1);
560         free(tmp);
561 }
562
563 static void init_origins(void)
564 {
565         unsigned n, oid;
566         char *argv[] = {
567                 "lvs",
568                 "--select", NULL,
569                 "--noheading",
570                 "--separator", ",",
571                 "--readonly",
572                 "--unquoted",
573                 "-o", "vgname,lvname,pool_lv,origin,lvtime",
574                 "-O", "-lv_time",
575                 "--config", "report/time_format=%s",
576                 NULL
577         };
578         char *buf, *tmp, *line, *select_string = NULL;
579         struct line_iter liter;
580         struct lv_info lv;
581
582         if (OPT_GIVEN(MISMA, ORIGIN) == 0)
583                 die("--origin not given");
584
585         /* create argument to --select */
586         for (n = 0; n < OPT_GIVEN(MISMA, ORIGIN); n++) {
587                 char *tmp2, *slash;
588                 const char *arg = OPT_STRING_VAL_N(n, MISMA, ORIGIN);
589
590                 tmp = xstrdup(arg),
591                 slash = strchr(tmp, '/');
592                 if (!slash || slash == tmp || !slash[1])
593                         die("--origin arg must be of the form vg/tlv");
594                 *slash = '\0';
595                 tmp2 = msg("%s%s (vg_name=%s && (lv_name=%s ||"
596                         "(origin=%s && lv_name =~ misma-%s.[0-9]+)))",
597                         select_string? select_string : "",
598                         select_string? " || " : "" ,
599                         tmp, slash + 1, slash + 1, slash + 1
600                 );
601                 free(tmp);
602                 free(select_string);
603                 select_string = tmp2;
604         }
605         argv[2] = select_string;
606         if (!xexec(argv, &buf))
607                 die("lvs failure");
608         tmp = xstrdup(buf);
609         line_iter_init(&liter, tmp);
610         /* insert vgs and pools */
611         while ((line = line_iter_get(&liter))) {
612                 parse_lvs_line(line, &lv);
613                 DEBUG_LOG("vg: %s, lv: %s, pool: %s, origin: %s, "
614                         "time: %" PRIu64"\n",
615                         lv.vg, lv.lv, lv.pool, lv.origin, lv.time);
616                 if (lv.origin[0] == '\0') { /* origin */
617                         insert_vg(lv.vg);
618                         if (lv.pool[0] == '\0')
619                                 die("%s/%s is no thin LV", lv.vg, lv.lv);
620                         insert_pool(lv.pool, lv.vg);
621                 }
622                 free_lv_info(&lv);
623         }
624         free(tmp);
625         tmp = xstrdup(buf);
626         line_iter_init(&liter, tmp);
627         /* insert origins */
628         while ((line = line_iter_get(&liter))) {
629                 parse_lvs_line(line, &lv);
630                 if (lv.origin[0] == '\0')
631                         insert_origin(lv.lv, lv.vg, lv.pool);
632                 free_lv_info(&lv);
633         }
634         free(tmp);
635         /* check that all given origins exist */
636         for (n = 0; n < OPT_GIVEN(MISMA, ORIGIN); n++) {
637                 const char *arg = OPT_STRING_VAL_N(n, MISMA, ORIGIN);
638                 char *slash;
639
640                 tmp = xstrdup(arg),
641                 slash = strchr(tmp, '/');
642                 *slash = '\0';
643                 oid = get_oid(slash + 1, tmp);
644                 free(tmp);
645                 if (oid == ~0U)
646                         die("origin %s does not exist", arg);
647         }
648         tmp = xstrdup(buf);
649         line_iter_init(&liter, tmp);
650         /* allocate and init snapshot arrays */
651         while ((line = line_iter_get(&liter))) {
652                 char *fmt;
653                 struct snapshot *s;
654                 struct origin *o;
655
656                 parse_lvs_line(line, &lv);
657                 if (lv.origin[0] == '\0') { /* no snapshot */
658                         free_lv_info(&lv);
659                         continue;
660                 }
661                 oid = get_oid(lv.origin, lv.vg);
662                 assert(oid != ~0U);
663                 o = origin + oid;
664                 o->num_slots++;
665                 o->snapshot = xrealloc(o->snapshot, o->num_slots
666                         * sizeof(struct snapshot));
667                 s = o->snapshot + o->num_slots - 1;
668                 fmt = msg("misma-%s.%%u", lv.origin);
669                 if (sscanf(lv.lv, fmt, &s->seq) != 1)
670                         die("parse error: %s", lv.lv);
671                 free(fmt);
672                 s->epoch = lv.time;
673                 if (s->seq > o->last_seq)
674                         o->last_seq = s->seq;
675                 if (s->epoch > o->last_event[ET_CREATE])
676                         o->last_event[ET_CREATE] = s->epoch;
677                 free_lv_info(&lv);
678         }
679         free(tmp);
680 }
681
682 static void die_lopsub(int lopsub_ret, char **errctx)
683 {
684         const char *m = lls_strerror(-lopsub_ret);
685         if (*errctx)
686                 ERROR_LOG("%s: %s\n", *errctx, m);
687         else
688                 ERROR_LOG("%s\n", m);
689         free(*errctx);
690         *errctx = NULL;
691         die("lopsub error");
692 }
693
694 static void parse_options(int argc, char **argv, const struct lls_command *cmd,
695                 struct lls_parse_result **lprp)
696 {
697         int ret, fd = -1;
698         struct stat statbuf;
699         void *map;
700         size_t sz;
701         int cf_argc;
702         char **cf_argv, *errctx = NULL;
703         const char *subcmd_name;
704         struct lls_parse_result *merged_lpr, *cf_lpr;
705
706         ret = lls_parse(argc, argv, cmd, lprp, &errctx);
707         if (ret < 0)
708                 die_lopsub(ret, &errctx);
709         if (!config_file) {
710                 if (OPT_GIVEN(MISMA, CONFIG_FILE))
711                         config_file = xstrdup(OPT_STRING_VAL(MISMA,
712                                 CONFIG_FILE));
713                 else {
714                         const char *home = getenv("HOME");
715                         if (!home || !*home)
716                                 die("fatal: HOME is unset or empty");
717                         config_file = msg("%s/.mismarc", home);
718                 }
719         }
720         ret = open(config_file, O_RDONLY);
721         if (ret < 0) {
722                 if (errno != ENOENT || OPT_GIVEN(MISMA, CONFIG_FILE))
723                         die_errno("can not open config file %s", config_file);
724                 /* no config file -- nothing to do */
725                 ret = 0;
726                 goto success;
727         }
728         fd = ret;
729         ret = fstat(fd, &statbuf);
730         if (ret < 0)
731                 die_errno("failed to stat config file %s", config_file);
732         sz = statbuf.st_size;
733         if (sz == 0) { /* config file is empty -- nothing to do */
734                 ret = 0;
735                 goto success;
736         }
737         map = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0);
738         if (map == MAP_FAILED)
739                 die_errno("failed to mmap config file %s", config_file);
740         subcmd_name = (cmd == CMD_PTR(MISMA))? NULL : lls_command_name(cmd);
741         ret = lls_convert_config(map, sz, subcmd_name, &cf_argv,
742                 &errctx);
743         munmap(map, sz);
744         if (ret < 0) {
745                 ERROR_LOG("failed to convert config file %s\n", config_file);
746                 die_lopsub(ret, &errctx);
747         }
748         cf_argc = ret;
749         ret = lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx);
750         lls_free_argv(cf_argv);
751         if (ret < 0)
752                 die_lopsub(ret, &errctx);
753         /* command line options override config file options */
754         ret = lls_merge(*lprp, cf_lpr, cmd, &merged_lpr, &errctx);
755         if (ret < 0)
756                 die_lopsub(ret, &errctx);
757         lls_free_parse_result(cf_lpr, cmd);
758         lls_free_parse_result(*lprp, cmd);
759         *lprp = merged_lpr;
760 success:
761         if (fd >= 0)
762                 close(fd);
763 }
764
765 static void get_utilization(void)
766 {
767         char *select_string = NULL, *buf, *line;
768         unsigned n;
769         char *argv[] = {
770                 "lvs",
771                 "--select", NULL,
772                 "--noheading", "--unquoted",
773                 "-o", "vgname,lvname,data_percent,metadata_percent",
774                 NULL
775         };
776         struct line_iter liter;
777
778         for (n = 0; n < num_pools; n++) {
779                 const struct thin_pool *pool = thin_pool + n;
780                 char *tmp = msg("%s%s (vg_name = %s && lv_name = %s)",
781                         (n == 0)? "" : select_string, (n == 0)? "" : "||",
782                         vgname(pool->vgid), pool->name);
783                 free(select_string);
784                 select_string = tmp;
785         }
786         argv[2] = select_string;
787         if (!xexec(argv, &buf))
788                 die("lvs failure");
789         free(select_string);
790         line_iter_init(&liter, buf);
791         while ((line = line_iter_get(&liter))) {
792                 struct percentage_pair *u;
793                 struct thin_pool *pool;
794                 unsigned poolid;
795                 float data, meta;
796                 size_t len = strlen(line);
797                 char *vg = xmalloc(len), *lv = xmalloc(len);
798                 if (sscanf(line, "%s %s %f %f", vg, lv, &data, &meta) != 4)
799                         die("cannot parse lvs line: %s", line);
800                 poolid = get_poolid(lv, vg);
801                 free(vg);
802                 free(lv);
803                 assert(poolid != ~0U);
804                 pool = thin_pool + poolid;
805                 u = &pool->utilization;
806                 u->data = data + 0.5;
807                 u->meta = meta + 0.5;
808                 INFO_LOG("pool %s/%s utilization: %u/%u\n",
809                         vgname(pool->vgid), pool->name, u->data, u->meta);
810         }
811         free(buf);
812 }
813
814 static bool pool_is_full(const struct thin_pool *pool)
815 {
816         bool ret;
817         struct percentage_pair t, u = pool->utilization;
818
819         if (pool->threshold_scope == LS_GLOBAL)
820                 t = global_config.thresholds;
821         else if (pool->threshold_scope == LS_VG)
822                 t = volume_group[pool->vgid].config.thresholds;
823         else
824                 t = pool->config.thresholds;
825         ret = u.data > t.data || u.meta > t.meta;
826         if (ret) {
827                 NOTICE_LOG("pool %s/%s utilization: %u/%u, threshold: %u/%u\n",
828                         vgname(pool->vgid), pool->name,
829                         u.data, u.meta, t.data, t.meta);
830                 WARNING_LOG("pool %s/%s exceeds utilization thresholds\n",
831                         vgname(pool->vgid), pool->name);
832         }
833         return ret;
834 }
835
836 static void check_utilization(void)
837 {
838         bool found_full_pool, removed_snapshot;
839
840 again:
841         found_full_pool = false;
842         removed_snapshot = false;
843         get_utilization();
844         for (unsigned n = 0; n < num_pools; n++) {
845                 unsigned m;
846                 const struct thin_pool *pool = thin_pool + n;
847                 if (!pool_is_full(pool))
848                         continue;
849                 found_full_pool = true;
850                 FOR_EACH_ORIGIN(m) {
851                         struct origin *o = origin + m;
852                         if (o->poolid != n)
853                                 continue;
854                         if (remove_sleazy_snapshot(o, false))
855                                 removed_snapshot = true;
856                 }
857         }
858         if (!found_full_pool)
859                 return;
860         if (removed_snapshot)
861                 goto again;
862         INFO_LOG("full pool found, but nothing to remove\n");
863 }
864
865 static bool create_snapshot(struct origin *o, bool dry_run)
866 {
867         unsigned seq = o->last_seq + 1;
868         char *name = msg("misma-%s.%u", o->name, seq);
869         char *vg_origin = msg("%s/%s", vgname(o->vgid), o->name);
870         char *argv[] = {
871                 "lvcreate",
872                 "--type",
873                 "thin",
874                 "--quiet",
875                 "--quiet",
876                 "-s",
877                 "--autobackup",
878                 "n",
879                 "-n",
880                 name,
881                 vg_origin,
882                 NULL
883         };
884         if (dry_run) {
885                 printf("dry-run: would create snapshot #%u of origin %s\n",
886                         seq, vg_origin);
887                 free(name);
888                 free(vg_origin);
889                 return true;
890         }
891         NOTICE_LOG("creating snapshot %s/%s\n", vgname(o->vgid), name);
892         if (!xexec(argv, NULL))
893                 die("could not create snapshot");
894         free(name);
895         free(vg_origin);
896         return true;
897 }
898
899 static void signal_handler(int signo)
900 {
901         die("caught signal %d, terminating", signo);
902 }
903
904 #ifndef FITRIM
905 struct fstrim_range {uint64_t start; uint64_t len; uint64_t minlen;};
906 #define FITRIM _IOWR('X', 121, struct fstrim_range)
907 #endif
908 static bool trim_filesystem(struct origin *o, bool dry_run)
909 {
910         struct stat sb;
911         char *dev;
912         unsigned majo, mino;
913         int fd;
914         char *buf;
915         struct line_iter liter;
916         char *line, *mp = NULL;
917         struct fstrim_range range = {.len = ULLONG_MAX};
918
919         dev = msg("/dev/%s/%s", vgname(o->vgid), o->name);
920         if (stat(dev, &sb) < 0) {
921                 WARNING_LOG("stat(%s): %m\n", dev);
922                 free(dev);
923                 return false;
924         }
925         if ((sb.st_mode & S_IFMT) != S_IFBLK) {
926                 WARNING_LOG("not a block device: %s\n", dev);
927                 free(dev);
928                 return false;
929         }
930         free(dev);
931         majo = major(sb.st_rdev);
932         mino = minor(sb.st_rdev);
933         fd = open("/proc/self/mountinfo", O_RDONLY);
934         if (fd < 0) {
935                 WARNING_LOG("open(/proc/self/mountinfo): %m\n");
936                 return false;
937         }
938         if (!fd2buf(fd, &buf)) {
939                 WARNING_LOG("fd2buf error\n");
940                 close(fd);
941                 return false;
942         }
943         close(fd);
944         line_iter_init(&liter, buf);
945         /* 13 15 0:5 / /proc */
946         while ((line = line_iter_get(&liter))) {
947                 unsigned id, parent, mmajo, mmino;
948                 size_t len = strlen(line);
949                 char *mountroot = xmalloc(len), *target = xmalloc(len);
950
951                 if (sscanf(line, "%u %u %u:%u %s %s", &id, &parent, &mmajo,
952                                 &mmino, mountroot, target) != 6) {
953                         WARNING_LOG("parse mountinfo line: %s\n", line);
954                         free(mountroot);
955                         free(target);
956                         return false;
957                 }
958                 free(mountroot);
959                 if (mmajo == majo && mmino == mino) {
960                         mp = target;
961                         break;
962                 }
963                 free(target);
964         }
965         free(buf);
966         if (!mp) {
967                 WARNING_LOG("unable to find mountpoint of origin\n");
968                 return false;
969         }
970         if (dry_run) {
971                 printf("%s\n", mp);
972                 free(mp);
973                 return true;
974         }
975         fd = open(mp, O_RDONLY);
976         if (fd < 0) {
977                 WARNING_LOG("open(%s): %m\n", mp);
978                 free(mp);
979                 return false;
980         }
981         if (ioctl(fd, FITRIM, &range)) {
982                 WARNING_LOG("ioctl(FITRIM, %s): %m\n", mp);
983                 close(fd);
984                 free(mp);
985                 return false;
986         }
987         close(fd);
988         NOTICE_LOG("trimmed %s\n", mp);
989         free(mp);
990         return true;
991 }
992
993 static void set_threshold(const struct threshold_arg *ta)
994 {
995         enum lvm_scope scope = ta->lvmspec.scope;
996         unsigned poolid = 0, vgid;
997
998         if (scope == LS_GLOBAL) {
999                 global_config.thresholds = ta->threshold;
1000                 return;
1001         }
1002         vgid = get_vgid(ta->lvmspec.vg);
1003         if (vgid == ~0U)
1004                 die("invalid vg in lvmspec: %s", ta->lvmspec.vg);
1005         if (scope == LS_VG) {
1006                 volume_group[vgid].config.thresholds = ta->threshold;
1007         } else {
1008                 assert(scope == LS_POOL);
1009                 poolid = get_poolid(ta->lvmspec.pool, vgname(vgid));
1010                 if (poolid == ~0U)
1011                         die("invalid pool in lvmspec: %s", ta->lvmspec.pool);
1012                 thin_pool[poolid].config.thresholds = ta->threshold;
1013         }
1014         /*
1015          * Narrow the scope of all matching pools for which it is currently
1016          * set to a wider scope.
1017          */
1018         for (unsigned n = 0; n < num_pools; n++) {
1019                 struct thin_pool *p = thin_pool + n;
1020                 if (p->threshold_scope >= scope)
1021                         continue; /* already set to more narrow scope */
1022                 if (vgid != p->vgid)
1023                         continue;
1024                 if (scope == LS_POOL && poolid != n)
1025                         continue;
1026                 NOTICE_LOG("threshold for pool %s/%s: %u/%u\n",
1027                         vgname(vgid), p->name, ta->threshold.data,
1028                         ta->threshold.meta);
1029                 p->threshold_scope = scope;
1030         }
1031 }
1032
1033 static void log_event(const void *d)
1034 {
1035         const struct event *e = d;
1036
1037         if (e->origin)
1038                 DEBUG_LOG("(%s,%u): %" PRIu64 "\n", e->origin->name,
1039                         e->type, e->epoch);
1040         else
1041                 DEBUG_LOG("(utilization): %" PRIu64 "\n", e->epoch);
1042 }
1043
1044 static unsigned check_run_options(void)
1045 {
1046         struct time_arg ta;
1047         const char *arg;
1048         unsigned n, num_events = 0;
1049
1050         for (n = 0; n < OPT_GIVEN(RUN, THRESHOLD); n++) {
1051                 struct threshold_arg tha;
1052                 arg = OPT_STRING_VAL_N(n, RUN, THRESHOLD);
1053                 parse_threshold_arg(arg,"--threshold", &tha);
1054                 set_threshold(&tha);
1055                 free_lvmspec(&tha.lvmspec);
1056         }
1057         if (OPT_GIVEN(RUN, CHECK_INTERVAL)) {
1058                 arg = OPT_STRING_VAL(RUN, CHECK_INTERVAL);
1059                 check_seconds = parse_timespec(arg, "check-interval");
1060                 check_range(check_seconds, 10, 86400, "check-interval");
1061         }
1062         for (n = 0; n < OPT_GIVEN(RUN, TRIM_INTERVAL); n++) {
1063                 arg = OPT_STRING_VAL_N(n, RUN, TRIM_INTERVAL);
1064                 parse_time_arg(arg, "--trim-interval", &ta);
1065                 if (ta.seconds > 0)
1066                         check_range(ta.seconds, 60, ~0U, "trim-interval");
1067                 set_interval(IT_TRIM, &ta);
1068                 free_lvmspec(&ta.lvmspec);
1069         }
1070         for (n = 0; n < OPT_GIVEN(RUN, CREATE_INTERVAL); n++) {
1071                 arg = OPT_STRING_VAL_N(n, RUN, CREATE_INTERVAL);
1072                 parse_time_arg(arg, "--create-interval", &ta);
1073                 check_range(ta.seconds, 60, 86400 * 365, "create-interval");
1074                 set_interval(IT_CREATE, &ta);
1075                 free_lvmspec(&ta.lvmspec);
1076         }
1077         for (n = 0; n < OPT_GIVEN(RUN, MAX_AGE); n++) {
1078                 arg = OPT_STRING_VAL_N(n, RUN, MAX_AGE);
1079                 parse_time_arg(arg, "--max-age", &ta);
1080                 check_range(ta.seconds, 86400, 86400 * 20 * 365, "max-age");
1081                 set_interval(IT_MAX_AGE, &ta);
1082                 free_lvmspec(&ta.lvmspec);
1083         }
1084         FOR_EACH_ORIGIN(n) {
1085                 struct origin *o = origin + n;
1086                 uint32_t ma, cr, max_slots; /* max age, create interval */
1087
1088                 INFO_LOG("found %u snapshots of origin %s/%s\n",
1089                         o->num_slots, vgname(o->vgid), o->name);
1090                 /* set number of slots */
1091                 ma = interval_length(IT_MAX_AGE, o);
1092                 cr = interval_length(IT_CREATE, o);
1093                 if (ma / 3 < cr)
1094                         die("%s/%s: max-age/create ratio too small",
1095                                 vgname(o->vgid), o->name);
1096                 max_slots = 1 + ceil(log2((double)ma / cr + 1));
1097                 assert(max_slots > 2);
1098                 assert(max_slots < 30);
1099                 if (o->num_slots > max_slots)
1100                         die("%s/%s: too many snapshots", vgname(o->vgid),
1101                                 o->name);
1102                 if (o->num_slots < max_slots) {
1103                         unsigned diff = max_slots - o->num_slots;
1104                         o->snapshot = xrealloc(o->snapshot, max_slots
1105                                 * sizeof(struct snapshot));
1106                         memset(o->snapshot + o->num_slots, 0,
1107                                 diff * sizeof(struct snapshot));
1108                         o->num_slots = max_slots;
1109                 }
1110                 INFO_LOG("%s/%s: using %u slots\n", vgname(o->vgid), o->name,
1111                         o->num_slots);
1112                 if (interval_length(IT_TRIM, o) > 0)
1113                         num_events++;
1114         }
1115         return num_events + 1 + num_origins;
1116 }
1117
1118 static void dispatch_create_event(struct origin *o)
1119 {
1120         unsigned seq, sl;
1121         const struct thin_pool *pool;
1122         uint64_t now;
1123
1124         pool = thin_pool + o->poolid;
1125         if (pool_is_full(pool)) {
1126                 WARNING_LOG("%s/%s: creation suspended\n", vgname(o->vgid),
1127                         o->name);
1128                 return;
1129         }
1130         seq = o->last_seq + 1, sl = get_slot(seq, o);
1131         if (slot_is_used(sl, o) && !remove_snapshot(sl, o, false))
1132                 die("%s/%s: unable to free slot\n", vgname(o->vgid), o->name);
1133         now = time(NULL);
1134         create_snapshot(o, false);
1135         o->snapshot[sl].seq = seq;
1136         o->snapshot[sl].epoch = now;
1137         o->last_seq = seq;
1138         o->last_event[ET_CREATE] = now;
1139 }
1140
1141 /* We leak the fd but that's OK as long as we're only called once. */
1142 static int silence_lvm(void)
1143 {
1144         char *val;
1145         int fd = open("/dev/null", O_RDWR);
1146
1147         if (fd < 0)
1148                 die_errno("open(/dev/null)");
1149         val = msg("%d", fd);
1150         setenv("LVM_ERR_FD", val, true /* overwrite */);
1151         free(val);
1152         return fd;
1153 }
1154
1155 __attribute__ ((noreturn))
1156 static bool com_run(void)
1157 {
1158         int fd = -1;
1159         unsigned n, num_events;
1160         struct event **ep;
1161         struct event **event; /* At most 2 * num_origins + 1 */
1162         struct heap *event_heap;
1163         uint64_t now = time(NULL);
1164
1165         num_events = check_run_options();
1166         event = xmalloc(num_events * sizeof(struct event *));
1167         ep = event;
1168         (*ep) = xmalloc(sizeof(struct event));
1169         (*ep)->type = ET_CHECK;
1170         (*ep)->origin = NULL;
1171         (*ep)->epoch = 0;
1172         log_event(*ep);
1173         ep++;
1174         FOR_EACH_ORIGIN(n) {
1175                 struct origin *o = origin + n;
1176                 (*ep) = xmalloc(sizeof(struct event));
1177                 (*ep)->type = ET_CREATE;
1178                 (*ep)->origin = o;
1179                 (*ep)->epoch = o->last_event[ET_CREATE]
1180                         + interval_length(IT_CREATE, o);
1181                 log_event(*ep);
1182                 ep++;
1183                 if (interval_length(IT_TRIM, o) == 0)
1184                         continue;
1185                 (*ep) = xmalloc(sizeof(struct event));
1186                 (*ep)->type = ET_TRIM;
1187                 (*ep)->origin = o;
1188                 (*ep)->epoch = now + interval_length(IT_TRIM, o);
1189                 log_event(*ep);
1190                 ep++;
1191         }
1192         event_heap = heap_init(&event, num_events, event_compare);
1193         if (get_misma_pid(config_file) > 0)
1194                 die("already running");
1195         if (OPT_GIVEN(RUN, DAEMON))
1196                 fd = daemonize(OPT_STRING_VAL(RUN, LOGFILE));
1197         if (!misma_lock(config_file))
1198                 die("already running");
1199         if (signal(SIGINT, &signal_handler) == SIG_ERR)
1200                 die_errno("signal handler for SIGINT");
1201         if (signal(SIGTERM, &signal_handler) == SIG_ERR)
1202                 die_errno("signal handler for SIGTERM");
1203         if (signal(SIGHUP, &signal_handler) == SIG_ERR)
1204                 die_errno("signal handler for SIGHUP");
1205         if (fd >= 0) {
1206                 if (write(fd, "\0", 1) < 0)
1207                         die_errno("write");
1208                 close(fd);
1209         }
1210         exit_hook = OPT_STRING_VAL(RUN, EXIT_HOOK);
1211         if (OPT_GIVEN(RUN, SUPPRESS_LVM_WARNINGS))
1212                 silence_lvm();
1213         for (;;) {
1214                 struct event *e = heap_min(event_heap);
1215                 struct origin *o;
1216
1217                 now = time(NULL);
1218                 if (e->epoch > now) {
1219                         INFO_LOG("sleeping %" PRIu64 " seconds\n",
1220                                 e->epoch - now);
1221                         sleep(e->epoch - now);
1222                         continue;
1223                 }
1224                 e = heap_extract_min(event_heap);
1225                 o = e->origin;
1226                 switch (e->type) {
1227                 case ET_CHECK:
1228                         INFO_LOG("next event: check\n");
1229                         check_utilization();
1230                         now = time(NULL);
1231                         e->epoch = now + check_seconds;
1232                         break;
1233                 case ET_TRIM:
1234                         INFO_LOG("next event: trim %s/%s\n",
1235                                 vgname(o->vgid), o->name);
1236                         trim_filesystem(o, false /* dry-run */);
1237                         e->origin->last_event[ET_TRIM] = now;
1238                         e->epoch = now + interval_length(IT_TRIM, o);
1239                         break;
1240                 case ET_CREATE:
1241                         INFO_LOG("next event: create %s/%s\n", vgname(o->vgid),
1242                                 o->name);
1243                         dispatch_create_event(o);
1244                         e->epoch = now + interval_length(IT_CREATE, o);
1245                         break;
1246                 default: assert(0);
1247                 }
1248                 heap_insert(e, event_heap);
1249                 heap_dump(event_heap, log_event);
1250                 sleep(3);
1251         }
1252 }
1253 EXPORT_CMD_HANDLER(run);
1254
1255 static void seconds_to_human(int64_t diff, char *buf)
1256 {
1257         if (diff > 2 * 86400 * 365)
1258                 sprintf(buf, "%3" PRId64 " years  ", diff / (86400 * 365));
1259         else if (diff > 2 * 86400 * 30)
1260                 sprintf(buf, "%3" PRId64 " months ", diff / (86400 * 30));
1261         else if (diff > 2 * 86400 * 7)
1262                 sprintf(buf, "%3" PRId64 " weeks  ", diff / (86400 * 7));
1263         else if (diff > 2 * 86400)
1264                 sprintf(buf, "%3" PRId64 " days   ", diff / 86400);
1265         else if (diff > 2 * 3600)
1266                 sprintf(buf, "%3" PRId64 " hours  ", diff / 3600);
1267         else if (diff > 2 * 60)
1268                 sprintf(buf, "%3" PRId64 " minutes", diff / 60);
1269         else
1270                 sprintf(buf, "%3" PRId64 " second%s", diff, diff == 1? "" : "s");
1271 }
1272
1273 static bool origin_matches_lvmspec(const struct origin *o,
1274                 const struct lvmspec *spec)
1275 {
1276         if (spec->scope == LS_GLOBAL)
1277                 return true;
1278         if (strcmp(spec->vg, vgname(o->vgid)))
1279                 return false;
1280         if (spec->scope == LS_VG)
1281                 return true;
1282         if (spec->scope == LS_ORIGIN)
1283                 return !strcmp(spec->tlv, o->name);
1284         return !strcmp(spec->pool, thin_pool[o->poolid].name);
1285 }
1286
1287 static bool for_each_matching_origin(bool (*func)(struct origin *, bool),
1288                 bool dry_run)
1289 {
1290         unsigned k, n, num_args = lls_num_inputs(sublpr);
1291         struct lvmspec *spec = NULL; /* STFU gcc-12.3.0 */
1292         bool match = false;
1293
1294         if (num_args > 0)
1295                 spec = xmalloc(num_args * sizeof(*spec));
1296         for (k = 0; k < num_args; k++)
1297                 parse_lvmspec(lls_input(k, sublpr), "create/rm", spec + k);
1298         FOR_EACH_ORIGIN(n) {
1299                 struct origin *o = origin + n;
1300                 for (k = 0; k < num_args; k++)
1301                         if (origin_matches_lvmspec(o, spec + k))
1302                                 break;
1303                 if (num_args == 0 || k < num_args) {
1304                         func(o, dry_run);
1305                         match = true;
1306                 }
1307         }
1308         free(spec);
1309         if (!match && num_args > 0)
1310                 printf("no matches\n");
1311         return match;
1312 }
1313
1314 static bool list_snapshots(struct origin *o, bool l_given)
1315 {
1316         if (!l_given)
1317                 printf("%s/%s:\n", vgname(o->vgid), o->name);
1318         FOR_EACH_SLOT_REVERSE(sl, o) {
1319                 char buf[32];
1320                 struct tm *tm;
1321                 struct snapshot *s = o->snapshot + sl;
1322                 time_t t;
1323
1324                 assert(slot_is_used(sl, o));
1325                 if (l_given) {
1326                         printf("/dev/%s/misma-%s.%u\t", vgname(o->vgid),
1327                                 o->name, s->seq);
1328                         t = s->epoch;
1329                         tm = localtime(&t);
1330                         strftime(buf, sizeof(buf), "%F %R", tm);
1331                         printf("%s", buf);
1332                 } else
1333                         printf("%8u ", s->seq);
1334                 t = time(NULL);
1335                 seconds_to_human(t - s->epoch, buf);
1336                 printf("  %s\n", buf);
1337         }
1338         return true;
1339 }
1340
1341 static bool com_ls(void)
1342 {
1343         return for_each_matching_origin(list_snapshots,
1344                 OPT_GIVEN(LS, LONG));
1345 }
1346 EXPORT_CMD_HANDLER(ls);
1347
1348 static bool com_create(void)
1349 {
1350         if (!misma_lock(config_file))
1351                 die("already running");
1352         return for_each_matching_origin(create_snapshot,
1353                 OPT_GIVEN(CREATE, DRY_RUN));
1354 }
1355 EXPORT_CMD_HANDLER(create);
1356
1357 static bool com_rm(void)
1358 {
1359         if (!misma_lock(config_file))
1360                 die("already running");
1361         return for_each_matching_origin(remove_sleazy_snapshot,
1362                 OPT_GIVEN(RM, DRY_RUN));
1363 }
1364 EXPORT_CMD_HANDLER(rm);
1365
1366 static bool com_kill(void)
1367 {
1368         pid_t pid;
1369         unsigned sig = OPT_UINT32_VAL(KILL, SIGNAL);
1370         unsigned ms = 32;
1371
1372         pid = get_misma_pid(config_file);
1373         if (pid == 0)
1374                 die("no misma run process to send signal to");
1375         NOTICE_LOG("sending signal %u to pid %d\n", sig, pid);
1376         if (kill(pid, sig) < 0)
1377                 die_errno("kill");
1378         if (!OPT_GIVEN(KILL, WAIT))
1379                 return true;
1380         while (ms < 5000) {
1381                 struct timespec ts = {
1382                         .tv_sec = ms / 1000,
1383                         .tv_nsec = (ms % 1000) * 1000 * 1000
1384                 };
1385                 if (nanosleep(&ts, NULL) < 0)
1386                         return false;
1387                 if (kill(pid, 0) < 0)
1388                         return errno == ESRCH;
1389                 ms *= 2;
1390         }
1391         return false;
1392 }
1393 EXPORT_CMD_HANDLER(kill);
1394
1395 #define LSG_MISMA_CMD(_name) #_name
1396 static const char * const subcommand_names[] = {LSG_MISMA_SUBCOMMANDS NULL};
1397 #undef LSG_MISMA_CMD
1398
1399 static void show_subcommand_summary(bool verbose)
1400 {
1401         int i;
1402
1403         printf("Available subcommands:\n");
1404         if (verbose) {
1405                 const struct lls_command *cmd;
1406                 for (i = 1; (cmd = lls_cmd(i, misma_suite)); i++) {
1407                         const char *purpose = lls_purpose(cmd);
1408                         const char *name = lls_command_name(cmd);
1409                         printf("%-12s%s\n", name, purpose);
1410                 }
1411         } else {
1412                 unsigned n = 8;
1413                 printf("\t");
1414                 for (i = 0; i < LSG_NUM_MISMA_SUBCOMMANDS; i++) {
1415                         if (i > 0)
1416                                 n += printf(", ");
1417                         if (n > 70) {
1418                                 printf("\n\t");
1419                                 n = 8;
1420                         }
1421                         n += printf("%s", subcommand_names[i]);
1422                 }
1423                 printf("\n");
1424         }
1425 }
1426
1427 static bool com_trim(void)
1428 {
1429         if (!misma_lock(config_file))
1430                 die("already running");
1431         return for_each_matching_origin(trim_filesystem,
1432                 OPT_GIVEN(TRIM, DRY_RUN));
1433 }
1434 EXPORT_CMD_HANDLER(trim);
1435
1436 static bool com_help(void)
1437 {
1438         int ret;
1439         char *errctx, *help;
1440         const char *arg;
1441         const struct lls_command *cmd;
1442
1443         ret = lls_check_arg_count(sublpr, 0, 1, &errctx);
1444         if (ret < 0)
1445                 die_lopsub(ret, &errctx);
1446         if (lls_num_inputs(sublpr) == 0) {
1447                 show_subcommand_summary(OPT_GIVEN(HELP, LONG));
1448                 return true;
1449         }
1450         arg = lls_input(0, sublpr);
1451         ret = lls_lookup_subcmd(arg, misma_suite, &errctx);
1452         if (ret < 0)
1453                 die_lopsub(ret, &errctx);
1454         cmd = lls_cmd(ret, misma_suite);
1455         if (OPT_GIVEN(HELP, LONG))
1456                 help = lls_long_help(cmd);
1457         else
1458                 help = lls_short_help(cmd);
1459         printf("%s\n", help);
1460         free(help);
1461         return true;
1462 }
1463 EXPORT_CMD_HANDLER(help);
1464
1465 static bool com_configtest(void)
1466 {
1467         printf("Syntax Ok\n");
1468         return true;
1469 }
1470 EXPORT_CMD_HANDLER(configtest);
1471
1472 static bool com_utilization(void)
1473 {
1474         get_utilization();
1475         for (unsigned n = 0; n < num_pools; n++) {
1476                 struct thin_pool *p = thin_pool + n;
1477                 printf("%s/%s: %u%%/%u%%\n",
1478                         vgname(p->vgid), p->name, p->utilization.data,
1479                         p->utilization.meta);
1480         }
1481         return true;
1482 }
1483 EXPORT_CMD_HANDLER(utilization);
1484
1485 const char *GET_VERSION(void);
1486 static void handle_version_and_help(void)
1487 {
1488         char *help;
1489
1490         if (OPT_GIVEN(MISMA, VERSION)) {
1491                 printf(PACKAGE " %s\n"
1492                         "Copyright (C) " COPYRIGHT_YEAR " " AUTHOR ".\n"
1493                         "License: " LICENSE ": <" LICENSE_URL ">.\n"
1494                         "This is free software: you are free to change and redistribute it.\n"
1495                         "There is NO WARRANTY, to the extent permitted by law.\n"
1496                         "\n"
1497                         "Web page: " URL "\n"
1498                         "Clone URL: " CLONE_URL "\n"
1499                         "Gitweb: " GITWEB_URL "\n"
1500                         "Author's Home Page: " HOME_URL "\n"
1501                         "Send feedback to: " AUTHOR " <" EMAIL ">\n"
1502                         ,
1503                         GET_VERSION()
1504                 );
1505                 exit(EXIT_SUCCESS);
1506         }
1507         if (OPT_GIVEN(MISMA, DETAILED_HELP))
1508                 help = lls_long_help(CMD_PTR(MISMA));
1509         else if (OPT_GIVEN(MISMA, HELP))
1510                 help = lls_short_help(CMD_PTR(MISMA));
1511         else
1512                 return;
1513         printf("%s\n", help);
1514         free(help);
1515         exit(EXIT_SUCCESS);
1516 }
1517
1518 int main(int argc, char **argv)
1519 {
1520         unsigned num_inputs;
1521         int ret;
1522         char *errctx;
1523         const struct misma_user_data *ud;
1524
1525         valid_fd012();
1526         parse_options(argc, argv, CMD_PTR(MISMA), &lpr);
1527         loglevel_arg_val = OPT_UINT32_VAL(MISMA, LOGLEVEL);
1528         handle_version_and_help();
1529         num_inputs = lls_num_inputs(lpr);
1530         if (num_inputs == 0) {
1531                 show_subcommand_summary(true /* verbose */);
1532                 exit(EXIT_SUCCESS);
1533         }
1534         ret = lls_lookup_subcmd(argv[argc - num_inputs], misma_suite, &errctx);
1535         if (ret < 0)
1536                 die_lopsub(ret, &errctx);
1537         subcmd = lls_cmd(ret, misma_suite);
1538         parse_options(num_inputs, argv + argc - num_inputs, subcmd, &sublpr);
1539         if (subcmd != CMD_PTR(HELP))
1540                 init_origins();
1541         ud = lls_user_data(subcmd);
1542         exit(ud->handler()? EXIT_SUCCESS : EXIT_FAILURE);
1543 }