]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/collect/collect.c
Rip out setting of the log level from udev_new and put it in a new function
[thirdparty/systemd.git] / src / udev / collect / collect.c
CommitLineData
c7ae0d34
HR
1/*
2 * Collect variables across events.
3 *
4 * usage: collect [--add|--remove] <checkpoint> <id> <idlist>
5 *
6 * Adds ID <id> to the list governed by <checkpoint>.
7 * <id> must be part of the ID list <idlist>.
8 * If all IDs given by <idlist> are listed (ie collect has been
9 * invoked for each ID in <idlist>) collect returns 0, the
10 * number of missing IDs otherwise.
11 * A negative number is returned on error.
12 *
13 * Copyright(C) 2007, Hannes Reinecke <hare@suse.de>
14 *
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 */
21
c7ae0d34 22#include <errno.h>
c7ae0d34 23#include <getopt.h>
07630cea
LP
24#include <stddef.h>
25#include <stdio.h>
c7ae0d34 26
b5efdb8a 27#include "alloc-util.h"
48a9b173 28#include "libudev-private.h"
d91b8841 29#include "macro.h"
d054f0a4 30#include "stdio-util.h"
07630cea 31#include "string-util.h"
b237a168 32#include "udev-util.h"
c7ae0d34 33
2852ae21
LP
34#define BUFSIZE 16
35#define UDEV_ALARM_TIMEOUT 180
c7ae0d34
HR
36
37enum collect_state {
912541b0
KS
38 STATE_NONE,
39 STATE_OLD,
40 STATE_CONFIRMED,
c7ae0d34
HR
41};
42
43struct _mate {
912541b0
KS
44 struct udev_list_node node;
45 char *name;
46 enum collect_state state;
c7ae0d34
HR
47};
48
002a9577 49static struct udev_list_node bunch;
c7ae0d34
HR
50static int debug;
51
52/* This can increase dynamically */
322fc7a6 53static size_t bufsize = BUFSIZE;
c7ae0d34 54
b27ee00b 55static inline struct _mate *node_to_mate(struct udev_list_node *node)
002a9577 56{
b27ee00b 57 return container_of(node, struct _mate, node);
002a9577
KS
58}
59
919ce0b7 60noreturn static void sig_alrm(int signo)
c7ae0d34 61{
912541b0 62 exit(4);
c7ae0d34
HR
63}
64
65static void usage(void)
66{
5ac0162c
LP
67 printf("%s [options] <checkpoint> <id> <idlist>\n\n"
68 "Collect variables across events.\n\n"
69 " -h --help Print this message\n"
70 " -a --add Add ID <id> to the list <idlist>\n"
71 " -r --remove Remove ID <id> from the list <idlist>\n"
72 " -d --debug Debug to stderr\n\n"
912541b0
KS
73 " Adds ID <id> to the list governed by <checkpoint>.\n"
74 " <id> must be part of the list <idlist>.\n"
75 " If all IDs given by <idlist> are listed (ie collect has been\n"
76 " invoked for each ID in <idlist>) collect returns 0, the\n"
77 " number of missing IDs otherwise.\n"
5ac0162c
LP
78 " On error a negative number is returned.\n\n"
79 , program_invocation_short_name);
c7ae0d34
HR
80}
81
82/*
83 * prepare
84 *
85 * Prepares the database file
86 */
87static int prepare(char *dir, char *filename)
88{
e68eedbb 89 char buf[PATH_MAX];
c9732bae 90 int r, fd;
912541b0 91
c9732bae
RC
92 r = mkdir(dir, 0700);
93 if (r < 0 && errno != EEXIST)
94 return -errno;
912541b0 95
e68eedbb 96 snprintf(buf, sizeof buf, "%s/%s", dir, filename);
912541b0 97
e68eedbb 98 fd = open(buf, O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
912541b0 99 if (fd < 0)
f5f6d0e2 100 fprintf(stderr, "Cannot open %s: %m\n", buf);
912541b0
KS
101
102 if (lockf(fd,F_TLOCK,0) < 0) {
103 if (debug)
104 fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT);
105 if (errno == EAGAIN || errno == EACCES) {
106 alarm(UDEV_ALARM_TIMEOUT);
107 lockf(fd, F_LOCK, 0);
108 if (debug)
109 fprintf(stderr, "Acquired lock on %s\n", buf);
110 } else {
111 if (debug)
f5f6d0e2 112 fprintf(stderr, "Could not get lock on %s: %m\n", buf);
912541b0
KS
113 }
114 }
115
116 return fd;
c7ae0d34
HR
117}
118
119/*
120 * Read checkpoint file
121 *
122 * Tricky reading this. We allocate a buffer twice as large
214a6c79 123 * as we're going to read. Then we read into the upper half
c7ae0d34
HR
124 * of that buffer and start parsing.
125 * Once we do _not_ find end-of-work terminator (whitespace
126 * character) we move the upper half to the lower half,
127 * adjust the read pointer and read the next bit.
128 * Quite clever methinks :-)
129 * I should become a programmer ...
130 *
131 * Yes, one could have used fgets() for this. But then we'd
132 * have to use freopen etc which I found quite tedious.
133 */
134static int checkout(int fd)
135{
912541b0
KS
136 int len;
137 char *buf, *ptr, *word = NULL;
138 struct _mate *him;
c7ae0d34
HR
139
140 restart:
912541b0 141 len = bufsize >> 1;
7d566799 142 buf = malloc(bufsize + 1);
2852ae21 143 if (!buf)
8dc8ef59 144 return log_oom();
912541b0 145 memset(buf, ' ', bufsize);
7d566799
ZJS
146 buf[bufsize] = '\0';
147
912541b0
KS
148 ptr = buf + len;
149 while ((read(fd, buf + len, len)) > 0) {
150 while (ptr && *ptr) {
151 word = ptr;
152 ptr = strpbrk(word," \n\t\r");
153 if (!ptr && word < (buf + len)) {
154 bufsize = bufsize << 1;
155 if (debug)
c4ef0548 156 fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize);
912541b0
KS
157 free(buf);
158 lseek(fd, 0, SEEK_SET);
159 goto restart;
160 }
161 if (ptr) {
162 *ptr = '\0';
163 ptr++;
164 if (!strlen(word))
165 continue;
166
167 if (debug)
168 fprintf(stderr, "Found word %s\n", word);
169 him = malloc(sizeof (struct _mate));
8dc8ef59
VP
170 if (!him) {
171 free(buf);
172 return log_oom();
173 }
912541b0 174 him->name = strdup(word);
8dc8ef59
VP
175 if (!him->name) {
176 free(buf);
177 free(him);
178 return log_oom();
179 }
912541b0
KS
180 him->state = STATE_OLD;
181 udev_list_node_append(&him->node, &bunch);
182 word = NULL;
183 }
184 }
185 memcpy(buf, buf + len, len);
186 memset(buf + len, ' ', len);
187
188 if (!ptr)
189 ptr = word;
190 if (!ptr)
191 break;
192 ptr -= len;
193 }
194
195 free(buf);
196 return 0;
c7ae0d34
HR
197}
198
199/*
200 * invite
201 *
202 * Adds a new ID 'us' to the internal list,
203 * marks it as confirmed.
204 */
205static void invite(char *us)
206{
912541b0
KS
207 struct udev_list_node *him_node;
208 struct _mate *who = NULL;
c7ae0d34 209
912541b0
KS
210 if (debug)
211 fprintf(stderr, "Adding ID '%s'\n", us);
c7ae0d34 212
912541b0
KS
213 udev_list_node_foreach(him_node, &bunch) {
214 struct _mate *him = node_to_mate(him_node);
002a9577 215
090be865 216 if (streq(him->name, us)) {
912541b0
KS
217 him->state = STATE_CONFIRMED;
218 who = him;
219 }
220 }
221 if (debug && !who)
222 fprintf(stderr, "ID '%s' not in database\n", us);
c7ae0d34
HR
223
224}
225
226/*
227 * reject
228 *
229 * Marks the ID 'us' as invalid,
230 * causing it to be removed when the
231 * list is written out.
232 */
233static void reject(char *us)
234{
912541b0
KS
235 struct udev_list_node *him_node;
236 struct _mate *who = NULL;
237
238 if (debug)
239 fprintf(stderr, "Removing ID '%s'\n", us);
240
241 udev_list_node_foreach(him_node, &bunch) {
242 struct _mate *him = node_to_mate(him_node);
243
090be865 244 if (streq(him->name, us)) {
912541b0
KS
245 him->state = STATE_NONE;
246 who = him;
247 }
248 }
249 if (debug && !who)
250 fprintf(stderr, "ID '%s' not in database\n", us);
c7ae0d34
HR
251}
252
253/*
254 * kickout
255 *
256 * Remove all IDs in the internal list which are not part
3f85ef0f 257 * of the list passed via the command line.
c7ae0d34
HR
258 */
259static void kickout(void)
260{
912541b0
KS
261 struct udev_list_node *him_node;
262 struct udev_list_node *tmp;
263
264 udev_list_node_foreach_safe(him_node, tmp, &bunch) {
265 struct _mate *him = node_to_mate(him_node);
266
267 if (him->state == STATE_OLD) {
268 udev_list_node_remove(&him->node);
269 free(him->name);
270 free(him);
271 }
272 }
c7ae0d34
HR
273}
274
275/*
276 * missing
277 *
278 * Counts all missing IDs in the internal list.
279 */
280static int missing(int fd)
281{
912541b0
KS
282 char *buf;
283 int ret = 0;
284 struct udev_list_node *him_node;
285
286 buf = malloc(bufsize);
287 if (!buf)
8dc8ef59 288 return log_oom();
912541b0
KS
289
290 udev_list_node_foreach(him_node, &bunch) {
291 struct _mate *him = node_to_mate(him_node);
292
293 if (him->state == STATE_NONE) {
294 ret++;
295 } else {
296 while (strlen(him->name)+1 >= bufsize) {
297 char *tmpbuf;
298
299 bufsize = bufsize << 1;
300 tmpbuf = realloc(buf, bufsize);
301 if (!tmpbuf) {
302 free(buf);
8dc8ef59 303 return log_oom();
912541b0
KS
304 }
305 buf = tmpbuf;
306 }
307 snprintf(buf, strlen(him->name)+2, "%s ", him->name);
cb32f014
VP
308 if (write(fd, buf, strlen(buf)) < 0) {
309 free(buf);
310 return -1;
311 }
912541b0
KS
312 }
313 }
314
315 free(buf);
316 return ret;
c7ae0d34
HR
317}
318
319/*
320 * everybody
321 *
322 * Prints out the status of the internal list.
323 */
324static void everybody(void)
325{
912541b0
KS
326 struct udev_list_node *him_node;
327 const char *state = "";
328
329 udev_list_node_foreach(him_node, &bunch) {
330 struct _mate *him = node_to_mate(him_node);
331
332 switch (him->state) {
333 case STATE_NONE:
334 state = "none";
335 break;
336 case STATE_OLD:
337 state = "old";
338 break;
339 case STATE_CONFIRMED:
340 state = "confirmed";
341 break;
342 }
343 fprintf(stderr, "ID: %s=%s\n", him->name, state);
344 }
c7ae0d34
HR
345}
346
347int main(int argc, char **argv)
348{
912541b0
KS
349 struct udev *udev;
350 static const struct option options[] = {
351 { "add", no_argument, NULL, 'a' },
352 { "remove", no_argument, NULL, 'r' },
353 { "debug", no_argument, NULL, 'd' },
354 { "help", no_argument, NULL, 'h' },
355 {}
356 };
357 int argi;
358 char *checkpoint, *us;
359 int fd;
360 int i;
361 int ret = EXIT_SUCCESS;
362 int prune = 0;
363 char tmpdir[UTIL_PATH_SIZE];
364
b237a168
ZJS
365 log_set_target(LOG_TARGET_AUTO);
366 udev_parse_config();
367 log_parse_environment();
368 log_open();
369
912541b0
KS
370 udev = udev_new();
371 if (udev == NULL) {
372 ret = EXIT_FAILURE;
373 goto exit;
374 }
375
57255510 376 for (;;) {
912541b0
KS
377 int option;
378
379 option = getopt_long(argc, argv, "ardh", options, NULL);
380 if (option == -1)
381 break;
382
383 switch (option) {
384 case 'a':
385 prune = 0;
386 break;
387 case 'r':
388 prune = 1;
389 break;
390 case 'd':
391 debug = 1;
392 break;
393 case 'h':
394 usage();
395 goto exit;
396 default:
397 ret = 1;
398 goto exit;
399 }
400 }
401
402 argi = optind;
403 if (argi + 2 > argc) {
404 printf("Missing parameter(s)\n");
405 ret = 1;
406 goto exit;
407 }
408 checkpoint = argv[argi++];
409 us = argv[argi++];
410
411 if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
f5f6d0e2 412 fprintf(stderr, "Cannot set SIGALRM: %m\n");
912541b0
KS
413 ret = 2;
414 goto exit;
415 }
416
417 udev_list_node_init(&bunch);
418
419 if (debug)
420 fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
421
d5a89d7d 422 strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL);
912541b0
KS
423 fd = prepare(tmpdir, checkpoint);
424 if (fd < 0) {
425 ret = 3;
426 goto out;
427 }
428
429 if (checkout(fd) < 0) {
430 ret = 2;
431 goto out;
432 }
433
434 for (i = argi; i < argc; i++) {
435 struct udev_list_node *him_node;
436 struct _mate *who;
437
438 who = NULL;
439 udev_list_node_foreach(him_node, &bunch) {
440 struct _mate *him = node_to_mate(him_node);
441
090be865 442 if (streq(him->name, argv[i]))
912541b0
KS
443 who = him;
444 }
445 if (!who) {
446 struct _mate *him;
447
448 if (debug)
449 fprintf(stderr, "ID %s: not in database\n", argv[i]);
ef89eef7 450 him = new(struct _mate, 1);
8dc8ef59
VP
451 if (!him) {
452 ret = ENOMEM;
453 goto out;
454 }
455
ef89eef7 456 him->name = strdup(argv[i]);
8dc8ef59 457 if (!him->name) {
ef89eef7 458 free(him);
8dc8ef59
VP
459 ret = ENOMEM;
460 goto out;
461 }
462
912541b0
KS
463 him->state = STATE_NONE;
464 udev_list_node_append(&him->node, &bunch);
465 } else {
466 if (debug)
467 fprintf(stderr, "ID %s: found in database\n", argv[i]);
468 who->state = STATE_CONFIRMED;
469 }
470 }
471
472 if (prune)
473 reject(us);
474 else
475 invite(us);
476
477 if (debug) {
478 everybody();
479 fprintf(stderr, "Prune lists\n");
480 }
481 kickout();
482
483 lseek(fd, 0, SEEK_SET);
484 ftruncate(fd, 0);
485 ret = missing(fd);
486
487 lockf(fd, F_ULOCK, 0);
488 close(fd);
4ec9c3e7 489out:
912541b0
KS
490 if (debug)
491 everybody();
492 if (ret >= 0)
493 printf("COLLECT_%s=%d\n", checkpoint, ret);
4ec9c3e7 494exit:
912541b0
KS
495 udev_unref(udev);
496 return ret;
c7ae0d34 497}