2 * Collect variables across events.
4 * usage: collect [--add|--remove] <checkpoint> <id> <idlist>
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.
13 * Copyright(C) 2007, Hannes Reinecke <hare@suse.de>
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.
31 #include <sys/types.h>
35 #include "libudev-private.h"
39 #define UDEV_ALARM_TIMEOUT 180
48 struct udev_list_node node;
50 enum collect_state state;
53 static struct udev_list_node bunch;
56 /* This can increase dynamically */
57 static size_t bufsize = BUFSIZE;
59 static inline struct _mate *node_to_mate(struct udev_list_node *node)
61 return container_of(node, struct _mate, node);
64 noreturn static void sig_alrm(int signo)
69 static void usage(void)
71 printf("%s [options] <checkpoint> <id> <idlist>\n\n"
72 "Collect variables across events.\n\n"
73 " -h --help Print this message\n"
74 " -a --add Add ID <id> to the list <idlist>\n"
75 " -r --remove Remove ID <id> from the list <idlist>\n"
76 " -d --debug Debug to stderr\n\n"
77 " Adds ID <id> to the list governed by <checkpoint>.\n"
78 " <id> must be part of the list <idlist>.\n"
79 " If all IDs given by <idlist> are listed (ie collect has been\n"
80 " invoked for each ID in <idlist>) collect returns 0, the\n"
81 " number of missing IDs otherwise.\n"
82 " On error a negative number is returned.\n\n"
83 , program_invocation_short_name);
89 * Prepares the database file
91 static int prepare(char *dir, char *filename)
97 if (r < 0 && errno != EEXIST)
100 snprintf(buf, sizeof(buf), "%s/%s", dir, filename);
102 fd = open(buf,O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
104 fprintf(stderr, "Cannot open %s: %m\n", buf);
106 if (lockf(fd,F_TLOCK,0) < 0) {
108 fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT);
109 if (errno == EAGAIN || errno == EACCES) {
110 alarm(UDEV_ALARM_TIMEOUT);
111 lockf(fd, F_LOCK, 0);
113 fprintf(stderr, "Acquired lock on %s\n", buf);
116 fprintf(stderr, "Could not get lock on %s: %m\n", buf);
124 * Read checkpoint file
126 * Tricky reading this. We allocate a buffer twice as large
127 * as we're going to read. Then we read into the upper half
128 * of that buffer and start parsing.
129 * Once we do _not_ find end-of-work terminator (whitespace
130 * character) we move the upper half to the lower half,
131 * adjust the read pointer and read the next bit.
132 * Quite clever methinks :-)
133 * I should become a programmer ...
135 * Yes, one could have used fgets() for this. But then we'd
136 * have to use freopen etc which I found quite tedious.
138 static int checkout(int fd)
141 char *buf, *ptr, *word = NULL;
146 buf = malloc(bufsize + 1);
149 memset(buf, ' ', bufsize);
153 while ((read(fd, buf + len, len)) > 0) {
154 while (ptr && *ptr) {
156 ptr = strpbrk(word," \n\t\r");
157 if (!ptr && word < (buf + len)) {
158 bufsize = bufsize << 1;
160 fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize);
162 lseek(fd, 0, SEEK_SET);
172 fprintf(stderr, "Found word %s\n", word);
173 him = malloc(sizeof (struct _mate));
178 him->name = strdup(word);
184 him->state = STATE_OLD;
185 udev_list_node_append(&him->node, &bunch);
189 memcpy(buf, buf + len, len);
190 memset(buf + len, ' ', len);
206 * Adds a new ID 'us' to the internal list,
207 * marks it as confirmed.
209 static void invite(char *us)
211 struct udev_list_node *him_node;
212 struct _mate *who = NULL;
215 fprintf(stderr, "Adding ID '%s'\n", us);
217 udev_list_node_foreach(him_node, &bunch) {
218 struct _mate *him = node_to_mate(him_node);
220 if (streq(him->name, us)) {
221 him->state = STATE_CONFIRMED;
226 fprintf(stderr, "ID '%s' not in database\n", us);
233 * Marks the ID 'us' as invalid,
234 * causing it to be removed when the
235 * list is written out.
237 static void reject(char *us)
239 struct udev_list_node *him_node;
240 struct _mate *who = NULL;
243 fprintf(stderr, "Removing ID '%s'\n", us);
245 udev_list_node_foreach(him_node, &bunch) {
246 struct _mate *him = node_to_mate(him_node);
248 if (streq(him->name, us)) {
249 him->state = STATE_NONE;
254 fprintf(stderr, "ID '%s' not in database\n", us);
260 * Remove all IDs in the internal list which are not part
261 * of the list passed via the command line.
263 static void kickout(void)
265 struct udev_list_node *him_node;
266 struct udev_list_node *tmp;
268 udev_list_node_foreach_safe(him_node, tmp, &bunch) {
269 struct _mate *him = node_to_mate(him_node);
271 if (him->state == STATE_OLD) {
272 udev_list_node_remove(&him->node);
282 * Counts all missing IDs in the internal list.
284 static int missing(int fd)
288 struct udev_list_node *him_node;
290 buf = malloc(bufsize);
294 udev_list_node_foreach(him_node, &bunch) {
295 struct _mate *him = node_to_mate(him_node);
297 if (him->state == STATE_NONE) {
300 while (strlen(him->name)+1 >= bufsize) {
303 bufsize = bufsize << 1;
304 tmpbuf = realloc(buf, bufsize);
311 snprintf(buf, strlen(him->name)+2, "%s ", him->name);
312 if (write(fd, buf, strlen(buf)) < 0) {
326 * Prints out the status of the internal list.
328 static void everybody(void)
330 struct udev_list_node *him_node;
331 const char *state = "";
333 udev_list_node_foreach(him_node, &bunch) {
334 struct _mate *him = node_to_mate(him_node);
336 switch (him->state) {
343 case STATE_CONFIRMED:
347 fprintf(stderr, "ID: %s=%s\n", him->name, state);
351 int main(int argc, char **argv)
354 static const struct option options[] = {
355 { "add", no_argument, NULL, 'a' },
356 { "remove", no_argument, NULL, 'r' },
357 { "debug", no_argument, NULL, 'd' },
358 { "help", no_argument, NULL, 'h' },
362 char *checkpoint, *us;
365 int ret = EXIT_SUCCESS;
367 char tmpdir[UTIL_PATH_SIZE];
378 option = getopt_long(argc, argv, "ardh", options, NULL);
402 if (argi + 2 > argc) {
403 printf("Missing parameter(s)\n");
407 checkpoint = argv[argi++];
410 if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
411 fprintf(stderr, "Cannot set SIGALRM: %m\n");
416 udev_list_node_init(&bunch);
419 fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
421 strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL);
422 fd = prepare(tmpdir, checkpoint);
428 if (checkout(fd) < 0) {
433 for (i = argi; i < argc; i++) {
434 struct udev_list_node *him_node;
438 udev_list_node_foreach(him_node, &bunch) {
439 struct _mate *him = node_to_mate(him_node);
441 if (streq(him->name, argv[i]))
448 fprintf(stderr, "ID %s: not in database\n", argv[i]);
449 him = new(struct _mate, 1);
455 him->name = strdup(argv[i]);
462 him->state = STATE_NONE;
463 udev_list_node_append(&him->node, &bunch);
466 fprintf(stderr, "ID %s: found in database\n", argv[i]);
467 who->state = STATE_CONFIRMED;
478 fprintf(stderr, "Prune lists\n");
482 lseek(fd, 0, SEEK_SET);
486 lockf(fd, F_ULOCK, 0);
492 printf("COLLECT_%s=%d\n", checkpoint, ret);