]> git.tuebingen.mpg.de Git - misma.git/blob - util.c
Fix seconds_to_human().
[misma.git] / util.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2 #include "misma.h"
3
4 #include <sys/ipc.h>
5 #include <sys/sem.h>
6 #include <fcntl.h>
7 #include <ctype.h>
8
9 void *xrealloc(void *p, size_t size)
10 {
11         assert(size > 0);
12         assert((p = realloc(p, size)));
13         return p;
14 }
15
16 void *xmalloc(size_t size)
17 {
18         return xrealloc(NULL, size);
19 }
20
21 void *xzmalloc(size_t size)
22 {
23         void *p = xrealloc(NULL, size);
24         memset(p, 0, size);
25         return p;
26 }
27
28 void *xstrdup(const char *s)
29 {
30         char *ret = strdup(s? s: "");
31
32         assert(ret);
33         return ret;
34 }
35
36 char *msg(const char *fmt, ...)
37 {
38         char *m;
39         size_t size = 100;
40
41         m = xmalloc(size);
42         while (1) {
43                 int n;
44                 va_list ap;
45
46                 /* Try to print in the allocated space. */
47                 va_start(ap, fmt);
48                 n = vsnprintf(m, size, fmt, ap);
49                 va_end(ap);
50                 /* If that worked, return the string. */
51                 if (n < size)
52                         return m;
53                 /* Else try again with more space. */
54                 size = n + 1; /* precisely what is needed */
55                 m = xrealloc(m, size);
56         }
57 }
58
59 bool fd2buf(int fd, char **buf)
60 {
61         ssize_t ret, nread = 0, sz = 100;
62
63         *buf = xmalloc(sz);
64         for (;;) {
65                 ret = read(fd, *buf + nread, sz - nread - 1);
66                 if (ret < 0) {
67                         if (errno == EAGAIN || errno == EINTR)
68                                 continue;
69                         ERROR_LOG("read error: %s\n", strerror(errno));
70                         return false;
71                 }
72                 if (ret == 0) {
73                         (*buf)[nread] = '\0';
74                         return true;
75                 }
76                 nread += ret;
77                 if (nread >= sz - 1) {
78                         sz *= 2;
79                         *buf = xrealloc(*buf, sz);
80                 }
81         }
82 }
83
84 bool xexec(char * const argv[], char **buf)
85 {
86         pid_t pid;
87         int pipefd[2] = {-1, -1};
88         unsigned n;
89
90         for (n = 0; argv[n]; n++)
91                 DEBUG_LOG("argv[%u]=%s\n", n, argv[n]);
92         if (buf) {
93                 if (pipe(pipefd) < 0)
94                         die_errno("pipe");
95         }
96         if ((pid = fork()) < 0)
97                 die_errno("fork");
98         if (pid > 0) { /* parent */
99                 int wstatus;
100                 bool success = true;
101                 if (buf) {
102                         close(pipefd[1]);
103                         success = fd2buf(pipefd[0], buf);
104                         close(pipefd[0]);
105                 }
106                 if (waitpid(pid, &wstatus, 0) < 0)
107                         die_errno("waitp");
108                 if (!success)
109                         return false;
110                 if (!WIFEXITED(wstatus))
111                         return false;
112                 if (WEXITSTATUS(wstatus) != EXIT_SUCCESS)
113                         return false;
114                 return true;
115         }
116         if (pipefd[0] >= 0)
117                 close(pipefd[0]);
118         if (pipefd[1] >= 0 && pipefd[1] != STDOUT_FILENO) {
119                 if (dup2(pipefd[1], STDOUT_FILENO) < 0)
120                         die_errno("dup2()");
121                 close(pipefd[1]);
122         }
123         execvp(argv[0], argv);
124         EMERG_LOG("execvp error: %s\n", strerror(errno));
125         _exit(EXIT_FAILURE);
126 }
127
128 void die_empty_arg(const char *opt)
129 {
130         die("argument to --%s must not be empty", opt);
131 }
132
133 void die_range(const char *opt)
134 {
135         die("argument to --%s is out of range", opt);
136 }
137
138 void check_range(uint32_t val, uint32_t min, uint32_t max, const char *opt)
139 {
140         if (val < min || val > max)
141                 die_range(opt);
142 }
143
144 static uint32_t atou32(const char *str, const char *opt)
145 {
146         char *endptr;
147         long long tmp;
148
149         errno = 0; /* To distinguish success/failure after call */
150         tmp = strtoll(str, &endptr, 10);
151         if (errno == ERANGE && (tmp == LLONG_MAX || tmp == LLONG_MIN))
152                 die_range(opt);
153         if (tmp < 0 || tmp > (uint32_t)-1)
154                 die_range(opt);
155         /*
156          * If there were no digits at all, strtoll() stores the original value
157          * of str in *endptr.
158          */
159         if (endptr == str)
160                 die_empty_arg(opt);
161         /*
162          * The implementation may also set errno and return 0 in case no
163          * conversion was performed.
164          */
165         if (errno != 0 && tmp == 0)
166                 die_empty_arg(opt);
167         if (*endptr != '\0') /* Further characters after number */
168                 die("--%s: trailing characters after number", opt);
169         return tmp;
170 }
171
172 static void split_arg(const char *arg, const char *context,
173                 char **prefix, char **suffix)
174 {
175         char *colon;
176         char *tmp = xstrdup(arg);
177
178         if (!tmp[0])
179                 die_empty_arg(context);
180         colon = strchr(tmp, ':');
181         if (!colon) {
182                 *prefix = NULL;
183                 *suffix = tmp;
184                 return;
185         }
186         *colon = '\0';
187         if (colon == tmp || !colon[1])
188                 die("%s: invalid argument", context);
189         *prefix = xstrdup(tmp);
190         *suffix = xstrdup(colon + 1);
191         free(tmp);
192 }
193
194 void parse_lvmspec(const char *arg, const char *context,
195                 struct lvmspec *result)
196 {
197         char *slash, *pipe;
198         char *tmp = xstrdup(arg);
199
200         slash = strchr(tmp, '/');
201         if (slash) {
202                 if (slash == tmp || !slash[1])
203                         die("%s: invalid argument", context);
204                 *slash = '\0';
205                 result->scope = LS_ORIGIN;
206                 result->tlv = xstrdup(slash + 1);
207                 goto free_tmp;
208         }
209         pipe = strchr(tmp, '|');
210         if (pipe) {
211                 if (pipe == tmp || !pipe[1])
212                         die("%s: invalid argument", context);
213                 *pipe = '\0';
214                 result->scope = LS_POOL;
215                 result->pool = xstrdup(pipe + 1);
216                 goto free_tmp;
217         }
218         result->scope = LS_VG;
219 free_tmp:
220         result->vg = xstrdup(tmp);
221         free(tmp);
222 }
223
224 void free_lvmspec(struct lvmspec *spec)
225 {
226         if (spec->scope == LS_GLOBAL)
227                 return;
228         free(spec->vg);
229         if (spec->scope == LS_POOL)
230                 free(spec->pool);
231         else if (spec->scope == LS_ORIGIN)
232                 free(spec->tlv);
233 }
234
235 void parse_threshold_arg(const char *arg, const char *context,
236                 struct threshold_arg *result)
237 {
238         char *prefix, *suffix, *comma;
239         uint32_t val;
240
241         split_arg(arg, context, &prefix, &suffix);
242         if (prefix) {
243                 parse_lvmspec(prefix, context, &result->lvmspec);
244                 if (result->lvmspec.scope == LS_ORIGIN)
245                         die("invalid scope for threshold lvmspec");
246         } else
247                 result->lvmspec.scope = LS_GLOBAL;
248         free(prefix);
249         comma = strchr(suffix, ',');
250         if (!comma)
251                 die("%s: invalid argument", context);
252         *comma = '\0';
253         val = atou32(suffix, context);
254         check_range(val, 1, 99, context);
255         result->threshold.data = val;
256         val = atou32(comma + 1, context);
257         check_range(val, 1, 99, context);
258         result->threshold.meta = val;
259         free(suffix);
260 }
261
262 unsigned parse_timespec(const char *spec, const char *context)
263 {
264         char *p, *tmp = xstrdup(spec);
265         uint64_t val, multiplier;
266
267         for (p = tmp; isdigit(*p); p++)
268                 ;
269         if (*p == '\0')
270                 die("%s: timepec lacks trailing time unit", context);
271         switch (*p) {
272         case 's': multiplier = 1; break;
273         case 'm': multiplier = 60; break;
274         case 'h': multiplier = 3600; break;
275         case 'd': multiplier = 86400; break;
276         case 'y': multiplier = 365 * 86400; break;
277         default:
278                 die("%s: invalid time unit in timepec argument", context);
279         }
280         *p = '\0';
281         if (p[1])
282                 die("%s: trailing characters after time unit", context);
283         val = atou32(tmp, context) * multiplier;
284         free(tmp);
285         if (val > (uint32_t)-1)
286                 die_range(context);
287         return val;
288 }
289
290 void parse_time_arg(const char *arg, const char *context,
291                  struct time_arg *result)
292 {
293         char *prefix, *suffix;
294
295         split_arg(arg, context, &prefix, &suffix);
296         if (prefix)
297                 parse_lvmspec(prefix, context, &result->lvmspec);
298         else
299                 result->lvmspec.scope = LS_GLOBAL;
300         free(prefix);
301         result->seconds = parse_timespec(suffix, context);
302         free(suffix);
303 }
304
305 void line_iter_init(struct line_iter *liter, char *text)
306 {
307         liter->line = liter->base = text;
308 }
309
310 char *line_iter_get(struct line_iter *liter)
311 {
312         char *cr, *line;
313
314         if (!liter->line || !liter->line[0])
315                 return NULL;
316         line = liter->line;
317         cr = strchr(liter->line, '\n');
318         if (cr) {
319                 *cr = '\0';
320                 liter->line = cr + 1;
321         } else
322                 liter->line = NULL;
323         return line;
324 }
325
326 void valid_fd012(void)
327 {
328         /* Ensure that file descriptors 0, 1, and 2 are valid. */
329         while (1) {
330                 int fd = open("/dev/null", O_RDWR);
331                 if (fd < 0)
332                         die_errno("open");
333                 if (fd > 2) {
334                         close(fd);
335                         break;
336                 }
337         }
338 }
339
340 int daemonize(const char *logfile)
341 {
342         pid_t pid;
343         int nullfd, logfd, pipefd[2];
344
345         if (pipe(pipefd) < 0)
346                 die_errno("pipe");
347         if ((pid = fork()) < 0)
348                 die_errno("fork");
349         if (pid) { /* parent exits after reading from the pipe */
350                 char c;
351                 close(pipefd[1]);
352                 if (read(pipefd[0], &c, 1) <= 0)
353                         die("child terminated unsuccessfully");
354                 exit(EXIT_SUCCESS);
355         }
356         close(pipefd[0]);
357         /* become session leader */
358         if (setsid() < 0)
359                 die_errno("setsid");
360         if ((nullfd = open("/dev/null", O_RDWR)) < 0)
361                 die_errno("open /dev/null");
362         logfile = logfile? logfile : "/dev/null";
363         if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0666)) < 0)
364                 die_errno("open %s", logfile);
365         INFO_LOG("subsequent log messages go to %s\n", logfile);
366         if (dup2(nullfd, STDIN_FILENO) < 0)
367                 die_errno("dup2");
368         close(nullfd);
369         if (dup2(logfd, STDOUT_FILENO) < 0)
370                 die_errno("dup2");
371         if (dup2(logfd, STDERR_FILENO) < 0)
372                 die_errno("dup2");
373         close(logfd);
374         valid_fd012();
375         if (chdir("/") < 0)
376                 die_errno("chdir");
377         return pipefd[1];
378 }
379
380 static int super_dull_hash(const char *input)
381 {
382         const uint8_t *x = (typeof(x))input;
383         const unsigned p1 = 16777619, p2 = 2971215073;
384         unsigned n, m, h, result = 0;
385
386         for (n = 0; n < 4; n++) {
387                 h = p1 * (x[0] + n);
388                 for (m = 1; x[m] != 0; m++)
389                         h = p2 * (h ^ x[m]);
390                 result = (result << 8) | (h % 256);
391         }
392         return result >> 1;
393 }
394
395 /**
396  * We use a semaphore set with two semaphores. The first semaphore is modified
397  * in both misma_lock() and get_misma_pid() while the second one is modified
398  * only in misma_lock(). This allows us to obtain the PID of the running misma
399  * process by querying the PID that last performed an operation on the second
400  * semaphore. This is achieved by passing GETPID as the control operation to
401  * semctl().
402  */
403
404 bool misma_lock(const char *string)
405 {
406         int ret, semid;
407         struct sembuf sops[4];
408         key_t key = super_dull_hash(string);
409
410         ret = semget(key, 2, IPC_CREAT | 0600);
411         if (ret < 0)
412                 return false;
413         semid = ret;
414         DEBUG_LOG("key: 0x%0x, semid: %d\n", (unsigned)key, semid);
415         sops[0].sem_num = 0;
416         sops[0].sem_op = 0;
417         sops[0].sem_flg = SEM_UNDO | IPC_NOWAIT;
418
419         sops[1].sem_num = 0;
420         sops[1].sem_op = 1;
421         sops[1].sem_flg = SEM_UNDO | IPC_NOWAIT;
422
423         sops[2].sem_num = 1;
424         sops[2].sem_op = 0;
425         sops[2].sem_flg = SEM_UNDO | IPC_NOWAIT;
426
427         sops[3].sem_num = 1;
428         sops[3].sem_op = 1;
429         sops[3].sem_flg = SEM_UNDO | IPC_NOWAIT;
430
431         return semop(semid, sops, 4) >= 0;
432 }
433
434 /* returns zero if misma is not running */
435 pid_t get_misma_pid(const char *string)
436 {
437         int ret, semid;
438         struct sembuf sops = {
439                 .sem_num = 0,
440                 .sem_op = 0,
441                 .sem_flg = SEM_UNDO | IPC_NOWAIT
442         };
443         key_t key = super_dull_hash(string);
444
445         ret = semget(key, 2, 0);
446         if (ret < 0)
447                 return 0;
448         semid = ret;
449         DEBUG_LOG("key: 0x%0x, semid: %d\n", (unsigned)key, semid);
450         if (semop(semid, &sops, 1) >= 0)
451                 return 0;
452         ret = semctl(semid, 1, GETPID);
453         if (ret < 0)
454                 return 0;
455         return ret;
456 }
457
458 /* Simplistic min-heap implementation (see e.g. Cormen et al. Chapter 6) */
459 struct heap {
460         void ***aa; /* array address */
461         unsigned n; /* num elements */
462         int (*compare)(const void *data1, const void *data2);
463 };
464
465 static unsigned heap_parent(unsigned idx)
466 {
467         return (idx + 1) / 2 - 1;
468 }
469
470 static unsigned heap_left(unsigned idx)
471 {
472         return (idx + 1) * 2 - 1;
473 }
474
475 static unsigned heap_right(unsigned idx)
476 {
477         return (idx + 1) * 2;
478 }
479
480 static void heapify(struct heap *h, unsigned idx)
481 {
482         unsigned l = heap_left(idx), r = heap_right(idx), smallest;
483         void **array = *(h->aa);
484
485         assert(idx < h->n);
486         if (l < h->n && h->compare(array[l], array[idx]) > 0)
487                 smallest = l;
488         else
489                 smallest = idx;
490         if (r < h->n && h->compare(array[r], array[smallest]) > 0)
491                 smallest = r;
492         if (smallest != idx) { /* exchange idx and smallest */
493                 void *tmp = array[idx];
494                 array[idx] = array[smallest];
495                 array[smallest] = tmp;
496                 heapify(h, smallest);
497         }
498 }
499
500 struct heap *heap_init(void *aa, unsigned num_elements,
501         int (*compare)(const void *data1, const void *data2))
502 {
503         struct heap *h = xmalloc(sizeof(*h));
504
505         INFO_LOG("creating heap with %u elements\n", num_elements);
506         h->aa = aa;
507         h->n = num_elements;
508         h->compare = compare;
509         for (unsigned j = h->n / 2 - 1; j != ~0U; j--)
510                 heapify(h, j);
511         return h;
512 }
513
514 void *heap_min(const struct heap *h)
515 {
516         assert(h->n > 0);
517         return (*(h->aa))[0];
518 }
519
520 unsigned heap_num_elements(const struct heap *h)
521 {
522         return h->n;
523 }
524
525 void *heap_extract_min(struct heap *h)
526 {
527         void *smallest = heap_min(h);
528         void **array = *(h->aa);
529
530         array[0] = array[h->n - 1];
531         h->n--;
532         *(h->aa) = xrealloc((*h->aa), h->n * sizeof(void *));
533         heapify(h, 0);
534         return smallest;
535 }
536
537 void heap_insert(void *new_element, struct heap *h)
538 {
539         unsigned parent;
540         void **array;
541
542         h->n++;
543         *(h->aa) = xrealloc((*h->aa), h->n * sizeof(void *));
544         array = *(h->aa);
545         array[h->n - 1] = new_element;
546         for (unsigned j = h->n - 1; j > 0; j = parent) {
547                 void *tmp;
548                 parent = heap_parent(j);
549                 if (h->compare(array[j], array[parent]) <= 0)
550                         break;
551                 tmp = array[j];
552                 array[j] = array[parent];
553                 array[parent] = tmp;
554         }
555 }
556
557 void heap_dump(const struct heap *h, void (*dumper)(const void *))
558 {
559         void **array = *(h->aa);
560         for (unsigned j = 0; j < h->n; j++)
561                 dumper(array[j]);
562 }