]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/collect/collect.c
use "Out of memory." consistantly (or with "\n")
[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
22#include <stdio.h>
23#include <stdlib.h>
24#include <stddef.h>
25#include <unistd.h>
26#include <signal.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <string.h>
30#include <getopt.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33
48a9b173
KS
34#include "libudev.h"
35#include "libudev-private.h"
d91b8841 36#include "macro.h"
c7ae0d34 37
912541b0
KS
38#define BUFSIZE 16
39#define UDEV_ALARM_TIMEOUT 180
c7ae0d34
HR
40
41enum collect_state {
912541b0
KS
42 STATE_NONE,
43 STATE_OLD,
44 STATE_CONFIRMED,
c7ae0d34
HR
45};
46
47struct _mate {
912541b0
KS
48 struct udev_list_node node;
49 char *name;
50 enum collect_state state;
c7ae0d34
HR
51};
52
002a9577 53static struct udev_list_node bunch;
c7ae0d34
HR
54static int debug;
55
56/* This can increase dynamically */
322fc7a6 57static size_t bufsize = BUFSIZE;
c7ae0d34 58
b27ee00b 59static inline struct _mate *node_to_mate(struct udev_list_node *node)
002a9577 60{
b27ee00b 61 return container_of(node, struct _mate, node);
002a9577
KS
62}
63
d91b8841 64_noreturn_ static void sig_alrm(int signo)
c7ae0d34 65{
912541b0 66 exit(4);
c7ae0d34
HR
67}
68
69static void usage(void)
70{
912541b0
KS
71 printf("usage: collect [--add|--remove] [--debug] <checkpoint> <id> <idlist>\n"
72 "\n"
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"
78 " On error a negative number is returned.\n"
79 "\n");
c7ae0d34
HR
80}
81
82/*
83 * prepare
84 *
85 * Prepares the database file
86 */
87static int prepare(char *dir, char *filename)
88{
912541b0
KS
89 struct stat statbuf;
90 char buf[512];
91 int fd;
92
93 if (stat(dir, &statbuf) < 0)
94 mkdir(dir, 0700);
95
96 sprintf(buf, "%s/%s", dir, filename);
97
98 fd = open(buf,O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
99 if (fd < 0)
100 fprintf(stderr, "Cannot open %s: %s\n", buf, strerror(errno));
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)
112 fprintf(stderr, "Could not get lock on %s: %s\n", buf, strerror(errno));
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
KS
141 len = bufsize >> 1;
142 buf = calloc(1,bufsize + 1);
143 if (!buf) {
669241a0 144 fprintf(stderr, "Out of memory.\n");
912541b0
KS
145 return -1;
146 }
147 memset(buf, ' ', bufsize);
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)
156 fprintf(stderr, "ID overflow, restarting with size %zi\n", bufsize);
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));
170 him->name = strdup(word);
171 him->state = STATE_OLD;
172 udev_list_node_append(&him->node, &bunch);
173 word = NULL;
174 }
175 }
176 memcpy(buf, buf + len, len);
177 memset(buf + len, ' ', len);
178
179 if (!ptr)
180 ptr = word;
181 if (!ptr)
182 break;
183 ptr -= len;
184 }
185
186 free(buf);
187 return 0;
c7ae0d34
HR
188}
189
190/*
191 * invite
192 *
193 * Adds a new ID 'us' to the internal list,
194 * marks it as confirmed.
195 */
196static void invite(char *us)
197{
912541b0
KS
198 struct udev_list_node *him_node;
199 struct _mate *who = NULL;
c7ae0d34 200
912541b0
KS
201 if (debug)
202 fprintf(stderr, "Adding ID '%s'\n", us);
c7ae0d34 203
912541b0
KS
204 udev_list_node_foreach(him_node, &bunch) {
205 struct _mate *him = node_to_mate(him_node);
002a9577 206
912541b0
KS
207 if (!strcmp(him->name, us)) {
208 him->state = STATE_CONFIRMED;
209 who = him;
210 }
211 }
212 if (debug && !who)
213 fprintf(stderr, "ID '%s' not in database\n", us);
c7ae0d34
HR
214
215}
216
217/*
218 * reject
219 *
220 * Marks the ID 'us' as invalid,
221 * causing it to be removed when the
222 * list is written out.
223 */
224static void reject(char *us)
225{
912541b0
KS
226 struct udev_list_node *him_node;
227 struct _mate *who = NULL;
228
229 if (debug)
230 fprintf(stderr, "Removing ID '%s'\n", us);
231
232 udev_list_node_foreach(him_node, &bunch) {
233 struct _mate *him = node_to_mate(him_node);
234
235 if (!strcmp(him->name, us)) {
236 him->state = STATE_NONE;
237 who = him;
238 }
239 }
240 if (debug && !who)
241 fprintf(stderr, "ID '%s' not in database\n", us);
c7ae0d34
HR
242}
243
244/*
245 * kickout
246 *
247 * Remove all IDs in the internal list which are not part
248 * of the list passed via the commandline.
249 */
250static void kickout(void)
251{
912541b0
KS
252 struct udev_list_node *him_node;
253 struct udev_list_node *tmp;
254
255 udev_list_node_foreach_safe(him_node, tmp, &bunch) {
256 struct _mate *him = node_to_mate(him_node);
257
258 if (him->state == STATE_OLD) {
259 udev_list_node_remove(&him->node);
260 free(him->name);
261 free(him);
262 }
263 }
c7ae0d34
HR
264}
265
266/*
267 * missing
268 *
269 * Counts all missing IDs in the internal list.
270 */
271static int missing(int fd)
272{
912541b0
KS
273 char *buf;
274 int ret = 0;
275 struct udev_list_node *him_node;
276
277 buf = malloc(bufsize);
278 if (!buf)
279 return -1;
280
281 udev_list_node_foreach(him_node, &bunch) {
282 struct _mate *him = node_to_mate(him_node);
283
284 if (him->state == STATE_NONE) {
285 ret++;
286 } else {
287 while (strlen(him->name)+1 >= bufsize) {
288 char *tmpbuf;
289
290 bufsize = bufsize << 1;
291 tmpbuf = realloc(buf, bufsize);
292 if (!tmpbuf) {
293 free(buf);
294 return -1;
295 }
296 buf = tmpbuf;
297 }
298 snprintf(buf, strlen(him->name)+2, "%s ", him->name);
299 write(fd, buf, strlen(buf));
300 }
301 }
302
303 free(buf);
304 return ret;
c7ae0d34
HR
305}
306
307/*
308 * everybody
309 *
310 * Prints out the status of the internal list.
311 */
312static void everybody(void)
313{
912541b0
KS
314 struct udev_list_node *him_node;
315 const char *state = "";
316
317 udev_list_node_foreach(him_node, &bunch) {
318 struct _mate *him = node_to_mate(him_node);
319
320 switch (him->state) {
321 case STATE_NONE:
322 state = "none";
323 break;
324 case STATE_OLD:
325 state = "old";
326 break;
327 case STATE_CONFIRMED:
328 state = "confirmed";
329 break;
330 }
331 fprintf(stderr, "ID: %s=%s\n", him->name, state);
332 }
c7ae0d34
HR
333}
334
335int main(int argc, char **argv)
336{
912541b0
KS
337 struct udev *udev;
338 static const struct option options[] = {
339 { "add", no_argument, NULL, 'a' },
340 { "remove", no_argument, NULL, 'r' },
341 { "debug", no_argument, NULL, 'd' },
342 { "help", no_argument, NULL, 'h' },
343 {}
344 };
345 int argi;
346 char *checkpoint, *us;
347 int fd;
348 int i;
349 int ret = EXIT_SUCCESS;
350 int prune = 0;
351 char tmpdir[UTIL_PATH_SIZE];
352
353 udev = udev_new();
354 if (udev == NULL) {
355 ret = EXIT_FAILURE;
356 goto exit;
357 }
358
359 while (1) {
360 int option;
361
362 option = getopt_long(argc, argv, "ardh", options, NULL);
363 if (option == -1)
364 break;
365
366 switch (option) {
367 case 'a':
368 prune = 0;
369 break;
370 case 'r':
371 prune = 1;
372 break;
373 case 'd':
374 debug = 1;
375 break;
376 case 'h':
377 usage();
378 goto exit;
379 default:
380 ret = 1;
381 goto exit;
382 }
383 }
384
385 argi = optind;
386 if (argi + 2 > argc) {
387 printf("Missing parameter(s)\n");
388 ret = 1;
389 goto exit;
390 }
391 checkpoint = argv[argi++];
392 us = argv[argi++];
393
394 if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
395 fprintf(stderr, "Cannot set SIGALRM: %s\n", strerror(errno));
396 ret = 2;
397 goto exit;
398 }
399
400 udev_list_node_init(&bunch);
401
402 if (debug)
403 fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
404
6ada823a 405 util_strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL);
912541b0
KS
406 fd = prepare(tmpdir, checkpoint);
407 if (fd < 0) {
408 ret = 3;
409 goto out;
410 }
411
412 if (checkout(fd) < 0) {
413 ret = 2;
414 goto out;
415 }
416
417 for (i = argi; i < argc; i++) {
418 struct udev_list_node *him_node;
419 struct _mate *who;
420
421 who = NULL;
422 udev_list_node_foreach(him_node, &bunch) {
423 struct _mate *him = node_to_mate(him_node);
424
425 if (!strcmp(him->name, argv[i]))
426 who = him;
427 }
428 if (!who) {
429 struct _mate *him;
430
431 if (debug)
432 fprintf(stderr, "ID %s: not in database\n", argv[i]);
433 him = malloc(sizeof (struct _mate));
434 him->name = malloc(strlen(argv[i]) + 1);
435 strcpy(him->name, argv[i]);
436 him->state = STATE_NONE;
437 udev_list_node_append(&him->node, &bunch);
438 } else {
439 if (debug)
440 fprintf(stderr, "ID %s: found in database\n", argv[i]);
441 who->state = STATE_CONFIRMED;
442 }
443 }
444
445 if (prune)
446 reject(us);
447 else
448 invite(us);
449
450 if (debug) {
451 everybody();
452 fprintf(stderr, "Prune lists\n");
453 }
454 kickout();
455
456 lseek(fd, 0, SEEK_SET);
457 ftruncate(fd, 0);
458 ret = missing(fd);
459
460 lockf(fd, F_ULOCK, 0);
461 close(fd);
4ec9c3e7 462out:
912541b0
KS
463 if (debug)
464 everybody();
465 if (ret >= 0)
466 printf("COLLECT_%s=%d\n", checkpoint, ret);
4ec9c3e7 467exit:
912541b0
KS
468 udev_unref(udev);
469 return ret;
c7ae0d34 470}