chiark / gitweb /
udevd: queue-export - remove retry loop
[elogind.git] / libudev / libudev-queue-private.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  */
12
13 /*
14  * DISCLAIMER - The file format mentioned here is private to udev/libudev,
15  *              and may be changed without notice.
16  *
17  * The udev event queue is exported as a binary log file.
18  * Each log record consists of a sequence number followed by the device path.
19  *
20  * When a new event is queued, its details are appended to the log.
21  * When the event finishes, a second record is appended to the log
22  * with the same sequence number but a devpath len of 0.
23  *
24  * Example:
25  *      { 0x0000000000000001 }
26  *      { 0x0000000000000001, 0x0019, "/devices/virtual/mem/null" },
27  *      { 0x0000000000000002, 0x001b, "/devices/virtual/mem/random" },
28  *      { 0x0000000000000001, 0x0000 },
29  *      { 0x0000000000000003, 0x0019, "/devices/virtual/mem/zero" },
30  *
31  * Events 2 and 3 are still queued, but event 1 has finished.
32  *
33  * The queue does not grow indefinitely. It is periodically re-created
34  * to remove finished events. Atomic rename() makes this transparent to readers.
35  *
36  * The queue file starts with a single sequence number which specifies the
37  * minimum sequence number in the log that follows. Any events prior to this
38  * sequence number have already finished.
39  */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <dirent.h>
47 #include <limits.h>
48 #include <errno.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51
52 #include "libudev.h"
53 #include "libudev-private.h"
54
55 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
56
57 struct udev_queue_export {
58         struct udev *udev;
59         int failed_count;       /* number of failed events exported */
60         int queued_count;       /* number of unfinished events exported in queue file */
61         FILE *queue_file;
62         unsigned long long int seqnum_max;      /* earliest sequence number in queue file */
63         unsigned long long int seqnum_min;      /* latest sequence number in queue file */
64         int waste_bytes;                        /* queue file bytes wasted on finished events */
65 };
66
67 struct udev_queue_export *udev_queue_export_new(struct udev *udev)
68 {
69         struct udev_queue_export *udev_queue_export;
70         unsigned long long int initial_seqnum;
71
72         if (udev == NULL)
73                 return NULL;
74
75         udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
76         if (udev_queue_export == NULL)
77                 return NULL;
78         udev_queue_export->udev = udev;
79
80         initial_seqnum = udev_get_kernel_seqnum(udev);
81         udev_queue_export->seqnum_min = initial_seqnum;
82         udev_queue_export->seqnum_max = initial_seqnum;
83
84         udev_queue_export_cleanup(udev_queue_export);
85         if (rebuild_queue_file(udev_queue_export) != 0) {
86                 free(udev_queue_export);
87                 return NULL;
88         }
89
90         return udev_queue_export;
91 }
92
93 void udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
94 {
95         if (udev_queue_export == NULL)
96                 return;
97         if (udev_queue_export->queue_file != NULL)
98                 fclose(udev_queue_export->queue_file);
99         free(udev_queue_export);
100 }
101
102 void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
103 {
104         char filename[UTIL_PATH_SIZE];
105
106         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
107         unlink(filename);
108
109         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
110         unlink(filename);
111 }
112
113 static int skip_to(FILE *file, long offset)
114 {
115         long old_offset;
116
117         /* fseek may drop buffered data, avoid it for small seeks */
118         old_offset = ftell(file);
119         if (offset > old_offset && offset - old_offset <= BUFSIZ) {
120                 size_t skip_bytes = offset - old_offset;
121                 char buf[skip_bytes];
122
123                 if (fread(buf, skip_bytes, 1, file) != skip_bytes)
124                         return -1;
125         }
126
127         return fseek(file, offset, SEEK_SET);
128 }
129
130 struct queue_devpaths {
131         unsigned int devpaths_first;    /* index of first queued event */
132         unsigned int devpaths_size;
133         long devpaths[];                /* seqnum -> offset of devpath in queue file (or 0) */
134 };
135
136 /*
137  * Returns a table mapping seqnum to devpath file offset for currently queued events.
138  * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
139  */
140 static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
141 {
142         struct queue_devpaths *devpaths;
143         unsigned long long int range;
144         long devpath_offset;
145         ssize_t devpath_len;
146         unsigned long long int seqnum;
147         unsigned long long int n;
148         unsigned int i;
149
150         /* seek to the first event in the file */
151         rewind(udev_queue_export->queue_file);
152         udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
153
154         /* allocate the table */
155         range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
156         if (range - 1 > INT_MAX) {
157                 err(udev_queue_export->udev, "queue file overflow\n");
158                 return NULL;
159         }
160         devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
161         if (devpaths == NULL)
162                 return NULL;
163         devpaths->devpaths_size = range + 1;
164
165         /* read all records and populate the table */
166         while(1) {
167                 if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
168                         break;
169                 n = seqnum - udev_queue_export->seqnum_max;
170                 if (n >= devpaths->devpaths_size)
171                         goto read_error;
172
173                 devpath_offset = ftell(udev_queue_export->queue_file);
174                 devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
175                 if (devpath_len < 0)
176                         goto read_error;
177
178                 if (devpath_len > 0)
179                         devpaths->devpaths[n] = devpath_offset;
180                 else
181                         devpaths->devpaths[n] = 0;
182         }
183
184         /* find first queued event */
185         for (i = 0; i < devpaths->devpaths_size; i++) {
186                 if (devpaths->devpaths[i] != 0)
187                         break;
188         }
189         devpaths->devpaths_first = i;
190
191         return devpaths;
192
193 read_error:
194         err(udev_queue_export->udev, "queue file corrupted\n");
195         free(devpaths);
196         return NULL;
197 }
198
199 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
200 {
201         unsigned long long int seqnum;
202         struct queue_devpaths *devpaths = NULL;
203         char filename[UTIL_PATH_SIZE];
204         char filename_tmp[UTIL_PATH_SIZE];
205         FILE *new_queue_file = NULL;
206         unsigned int i;
207
208         /* read old queue file */
209         if (udev_queue_export->queue_file != NULL) {
210                 dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n",
211                                                 udev_queue_export->waste_bytes);
212
213                 devpaths = build_index(udev_queue_export);
214                 if (devpaths != NULL)
215                         udev_queue_export->seqnum_max += devpaths->devpaths_first;
216         }
217         if (devpaths == NULL) {
218                 dbg(udev_queue_export->udev, "creating empty queue file\n");
219                 udev_queue_export->queued_count = 0;
220                 udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
221         }
222
223         /* create new queue file */
224         util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
225         new_queue_file = fopen(filename_tmp, "w+");
226         if (new_queue_file == NULL)
227                 goto error;
228         seqnum = udev_queue_export->seqnum_max;
229         fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
230
231         /* copy unfinished events only to the new file */
232         if (devpaths != NULL) {
233                 for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
234                         char devpath[UTIL_PATH_SIZE];
235                         int err;
236                         unsigned short devpath_len;
237
238                         if (devpaths->devpaths[i] != 0)
239                         {
240                                 skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
241                                 err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
242                                 devpath_len = err;
243
244                                 fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
245                                 fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
246                                 fwrite(devpath, 1, devpath_len, new_queue_file);
247                         }
248                         seqnum++;
249                 }
250                 free(devpaths);
251                 devpaths = NULL;
252         }
253         fflush(new_queue_file);
254         if (ferror(new_queue_file))
255                 goto error;
256
257         /* rename the new file on top of the old one */
258         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
259         if (rename(filename_tmp, filename) != 0)
260                 goto error;
261
262         if (udev_queue_export->queue_file != NULL)
263                 fclose(udev_queue_export->queue_file);
264         udev_queue_export->queue_file = new_queue_file;
265         udev_queue_export->waste_bytes = 0;
266
267         return 0;
268
269 error:
270         err(udev_queue_export->udev, "failed to create queue file: %m\n");
271         udev_queue_export_cleanup(udev_queue_export);
272
273         if (udev_queue_export->queue_file != NULL) {
274                 fclose(udev_queue_export->queue_file);
275                 udev_queue_export->queue_file = NULL;
276         }
277         if (new_queue_file != NULL)
278                 fclose(new_queue_file);
279
280         if (devpaths != NULL)
281                 free(devpaths);
282         udev_queue_export->queued_count = 0;
283         udev_queue_export->waste_bytes = 0;
284         udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
285
286         return -1;
287 }
288
289 static int write_queue_record(struct udev_queue_export *udev_queue_export,
290                               unsigned long long int seqnum, const char *devpath, size_t devpath_len)
291 {
292         unsigned short len;
293
294         if (udev_queue_export->queue_file == NULL) {
295                 dbg(udev_queue_export->udev, "can't record event: queue file not available\n");
296                 return -1;
297         }
298
299         if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
300                 goto write_error;
301
302         len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
303         if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
304                 goto write_error;
305         if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
306                 goto write_error;
307
308         /* *must* flush output; caller may fork */
309         if (fflush(udev_queue_export->queue_file) != 0)
310                 goto write_error;
311
312         return 0;
313
314 write_error:
315         /* if we failed half way through writing a record to a file,
316            we should not try to write any further records to it. */
317         err(udev_queue_export->udev, "error writing to queue file: %m\n");
318         fclose(udev_queue_export->queue_file);
319         udev_queue_export->queue_file = NULL;
320
321         return -1;
322 }
323
324 enum device_state {
325         DEVICE_QUEUED,
326         DEVICE_FINISHED,
327         DEVICE_FAILED,
328 };
329
330 static inline size_t queue_record_size(size_t devpath_len)
331 {
332         return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
333 }
334
335 static int update_queue(struct udev_queue_export *udev_queue_export,
336                          struct udev_device *udev_device, enum device_state state)
337 {
338         unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
339         const char *devpath = NULL;
340         size_t devpath_len = 0;
341         int bytes;
342         int err;
343
344         if (state == DEVICE_QUEUED) {
345                 devpath = udev_device_get_devpath(udev_device);
346                 devpath_len = strlen(devpath);
347         }
348
349         /* recover from an earlier failed rebuild */
350         if (udev_queue_export->queue_file == NULL) {
351                 if (rebuild_queue_file(udev_queue_export) != 0)
352                         return -1;
353         }
354
355         /* when the queue file grows too large, garbage-collect and rebuild it */
356         bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
357
358         /* if we're removing the last event from the queue, that's the best time to rebuild it */
359         if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1 && bytes > 2048) {
360                 /* because we don't need to read the old queue file */
361                 fclose(udev_queue_export->queue_file);
362                 udev_queue_export->queue_file = NULL;
363                 rebuild_queue_file(udev_queue_export);
364                 return 0;
365         }
366
367         /* try to rebuild the queue files before they grow larger than one page. */
368         if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
369                 rebuild_queue_file(udev_queue_export);
370
371         /* don't record a finished event, if we already dropped the event in a failed rebuild */
372         if (seqnum < udev_queue_export->seqnum_max)
373                 return 0;
374
375         /* now write to the queue */
376         if (state == DEVICE_QUEUED) {
377                 udev_queue_export->queued_count++;
378                 udev_queue_export->seqnum_min = seqnum;
379         } else {
380                 udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
381                 udev_queue_export->queued_count--;
382         }
383         err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
384
385         /* try to handle ENOSPC */
386         if (err != 0 && udev_queue_export->queued_count == 0) {
387                 udev_queue_export_cleanup(udev_queue_export);
388                 err = rebuild_queue_file(udev_queue_export);
389         }
390
391         return err;
392 }
393
394 static void update_failed(struct udev_queue_export *udev_queue_export,
395                           struct udev_device *udev_device, enum device_state state)
396 {
397         struct udev *udev = udev_device_get_udev(udev_device);
398         char filename[UTIL_PATH_SIZE];
399
400         if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0)
401                 return;
402
403         /* location of failed file */
404         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/failed/",
405                       udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
406
407         switch (state) {
408         case DEVICE_FAILED:
409                 /* record event in the failed directory */
410                 udev_queue_export->failed_count++;
411                 util_create_path(udev, filename);
412                 udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
413                 symlink(udev_device_get_devpath(udev_device), filename);
414                 udev_selinux_resetfscreatecon(udev);
415                 break;
416
417         case DEVICE_QUEUED:
418                 /* delete failed file */
419                 if (unlink(filename) == 0) {
420                         util_delete_path(udev, filename);
421                         udev_queue_export->failed_count--;
422                 }
423                 break;
424
425         case DEVICE_FINISHED:
426                 if (udev_device_get_sysname_old(udev_device) != NULL &&
427                     strcmp(udev_device_get_sysname_old(udev_device), udev_device_get_sysname(udev_device)) != 0) {
428                         /* "move" event - rename failed file to current name, do not delete failed */
429                         char filename_old[UTIL_PATH_SIZE];
430
431                         util_strscpyl(filename_old, sizeof(filename_old), udev_get_dev_path(udev), "/.udev/failed/",
432                                       udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname_old(udev_device), NULL);
433                         if (rename(filename_old, filename) == 0)
434                                 info(udev, "renamed devpath, moved failed state of '%s' to %s'\n",
435                                      udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device));
436                 }
437                 break;
438         }
439
440         return;
441 }
442
443 static int update(struct udev_queue_export *udev_queue_export,
444                   struct udev_device *udev_device, enum device_state state)
445 {
446         update_failed(udev_queue_export, udev_device, state);
447
448         if (update_queue(udev_queue_export, udev_device, state) != 0)
449                 return -1;
450
451         return 0;
452 }
453
454 int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
455 {
456         return update(udev_queue_export, udev_device, DEVICE_QUEUED);
457 }
458
459 int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
460 {
461         return update(udev_queue_export, udev_device, DEVICE_FINISHED);
462 }
463
464 int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
465 {
466         return update(udev_queue_export, udev_device, DEVICE_FAILED);
467 }