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>
34 #include "../../udev/list.h"
36 #define TMPFILE UDEV_PREFIX "/dev/.udev/collect"
38 #define UDEV_ALARM_TIMEOUT 180
47 struct list_head node;
49 enum collect_state state;
52 static LIST_HEAD(bunch);
55 /* This can increase dynamically */
56 static size_t bufsize = BUFSIZE;
58 static void sig_alrm(int signo)
63 static void usage(void)
65 printf("usage: collect [--add|--remove] [--debug] <checkpoint> <id> <idlist>\n"
67 " Adds ID <id> to the list governed by <checkpoint>.\n"
68 " <id> must be part of the list <idlist>.\n"
69 " If all IDs given by <idlist> are listed (ie collect has been\n"
70 " invoked for each ID in <idlist>) collect returns 0, the\n"
71 " number of missing IDs otherwise.\n"
72 " On error a negative number is returned.\n"
79 * Prepares the database file
81 static int prepare(char *dir, char *filename)
87 if (stat(dir, &statbuf) < 0)
90 sprintf(buf, "%s/%s", dir, filename);
92 fd = open(buf,O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
94 fprintf(stderr, "Cannot open %s: %s\n", buf, strerror(errno));
96 if (lockf(fd,F_TLOCK,0) < 0) {
98 fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT);
99 if (errno == EAGAIN || errno == EACCES) {
100 alarm(UDEV_ALARM_TIMEOUT);
101 lockf(fd, F_LOCK, 0);
103 fprintf(stderr, "Acquired lock on %s\n", buf);
106 fprintf(stderr, "Could not get lock on %s: %s\n", buf, strerror(errno));
114 * Read checkpoint file
116 * Tricky reading this. We allocate a buffer twice as large
117 * as we're goint to read. Then we read into the upper half
118 * of that buffer and start parsing.
119 * Once we do _not_ find end-of-work terminator (whitespace
120 * character) we move the upper half to the lower half,
121 * adjust the read pointer and read the next bit.
122 * Quite clever methinks :-)
123 * I should become a programmer ...
125 * Yes, one could have used fgets() for this. But then we'd
126 * have to use freopen etc which I found quite tedious.
128 static int checkout(int fd)
131 char *buf, *ptr, *word = NULL;
136 buf = calloc(1,bufsize + 1);
138 fprintf(stderr, "Out of memory\n");
141 memset(buf, ' ', bufsize);
143 while ((read(fd, buf + len, len)) > 0) {
144 while (ptr && *ptr) {
146 ptr = strpbrk(word," \n\t\r");
147 if (!ptr && word < (buf + len)) {
148 bufsize = bufsize << 1;
150 fprintf(stderr, "ID overflow, restarting with size %zi\n", bufsize);
152 lseek(fd, 0, SEEK_SET);
162 fprintf(stderr, "Found word %s\n", word);
163 him = malloc(sizeof (struct _mate));
164 him->name = malloc(strlen(word) + 1);
165 strcpy(him->name, word);
166 him->state = STATE_OLD;
167 list_add_tail(&him->node, &bunch);
171 memcpy(buf, buf + len, len);
172 memset(buf + len, ' ', len);
188 * Adds a new ID 'us' to the internal list,
189 * marks it as confirmed.
191 static void invite(char *us)
193 struct _mate *him, *who;
196 fprintf(stderr, "Adding ID '%s'\n", us);
199 list_for_each_entry(him, &bunch, node) {
200 if (!strcmp(him->name, us)) {
201 him->state = STATE_CONFIRMED;
206 fprintf(stderr, "ID '%s' not in database\n", us);
213 * Marks the ID 'us' as invalid,
214 * causing it to be removed when the
215 * list is written out.
217 static void reject(char *us)
219 struct _mate *him, *who;
222 fprintf(stderr, "Removing ID '%s'\n", us);
225 list_for_each_entry(him, &bunch, node) {
226 if (!strcmp(him->name, us)) {
227 him->state = STATE_NONE;
232 fprintf(stderr, "ID '%s' not in database\n", us);
239 * Remove all IDs in the internal list which are not part
240 * of the list passed via the commandline.
242 static void kickout(void)
244 struct _mate *him, *them;
246 list_for_each_entry_safe(him, them, &bunch, node) {
247 if (him->state == STATE_OLD) {
248 list_del(&him->node);
258 * Counts all missing IDs in the internal list.
260 static int missing(int fd)
266 buf = malloc(bufsize);
270 list_for_each_entry(him, &bunch, node) {
271 if (him->state == STATE_NONE) {
274 while (strlen(him->name)+1 >= bufsize) {
277 bufsize = bufsize << 1;
278 tmpbuf = realloc(buf, bufsize);
285 snprintf(buf, strlen(him->name)+2, "%s ", him->name);
286 write(fd, buf, strlen(buf));
298 * Prints out the status of the internal list.
300 static void everybody(void)
303 const char *state = "";
305 list_for_each_entry(him, &bunch, node) {
306 switch (him->state) {
313 case STATE_CONFIRMED:
316 fprintf(stderr, "ID: %s=%s\n", him->name, state);
321 int main(int argc, char **argv)
323 static const struct option options[] = {
324 { "add", 0, NULL, 'a' },
325 { "remove", 0, NULL, 'r' },
326 { "debug", 0, NULL, 'd' },
327 { "help", 0, NULL, 'h' },
331 char *checkpoint, *us;
332 struct _mate *him, *who;
341 option = getopt_long(argc, argv, "ardh", options, NULL);
365 if (argi + 2 > argc) {
366 printf("Missing parameter(s)\n");
370 checkpoint = argv[argi++];
373 if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
374 fprintf(stderr, "Cannot set SIGALRM: %s\n", strerror(errno));
379 INIT_LIST_HEAD(&bunch);
382 fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
384 fd = prepare(TMPFILE, checkpoint);
390 if (checkout(fd) < 0) {
395 for (i = argi; i < argc; i++) {
397 list_for_each_entry(him, &bunch, node) {
398 if (!strcmp(him->name, argv[i]))
403 fprintf(stderr, "ID %s: not in database\n", argv[i]);
404 him = malloc(sizeof (struct _mate));
405 him->name = malloc(strlen(argv[i]) + 1);
406 strcpy(him->name, argv[i]);
407 him->state = STATE_NONE;
408 list_add_tail(&him->node, &bunch);
411 fprintf(stderr, "ID %s: found in database\n", argv[i]);
412 who->state = STATE_CONFIRMED;
423 fprintf(stderr, "Prune lists\n");
427 lseek(fd, 0, SEEK_SET);
431 lockf(fd, F_ULOCK, 0);
437 printf("COLLECT_%s=%d\n", checkpoint, ret);