chiark / gitweb /
udevd: queue-export - fix crash
[elogind.git] / udev / lib / libudev-queue-export.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  *
18  * The udev event queue is exported as a binary log file.
19  * Each log record consists of a sequence number followed by the device path.
20  *
21  * When a new event is queued, its details are appended to the log.
22  * When the event finishes, a second record is appended to the log
23  * with the same sequence number but a null devpath.
24  *
25  * Example:
26  *      {1, "/devices/virtual/mem/null" },
27  *      {2, "/devices/virtual/mem/zero" },
28  *      {1, "" },
29  * Event 2 is still queued, but event 1 has been finished
30  *
31  * The queue does not grow indefinitely.  It is periodically re-created
32  * to remove finished events.  Atomic rename() makes this transparent to readers.
33  *
34  *
35  * The queue file starts with a single sequence number which specifies the
36  * minimum sequence number in the log that follows.  Any events prior to this
37  * sequence number have already finished.
38  */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <dirent.h>
46 #include <limits.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 #include <assert.h>
50
51 #include "udev.h"
52
53 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
54
55 struct udev_queue_export {
56         struct udev *udev;
57         int failed_count;       /* number of failed events exported */
58         int queued_count;       /* number of unfinished events exported in queue file */
59         FILE *queue_file;
60         unsigned long long int seqnum_max;      /* earliest sequence number in queue file */
61         unsigned long long int seqnum_min;      /* latest sequence number in queue file */
62         int waste_bytes;                        /* queue file bytes wasted on finished events */
63 };
64
65 struct udev_queue_export *udev_queue_export_new(struct udev *udev)
66 {
67         struct udev_queue_export *udev_queue_export;
68         unsigned long long int initial_seqnum;
69
70         if (udev == NULL)
71                 return NULL;
72
73         udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
74         if (udev_queue_export == NULL)
75                 return NULL;
76         udev_queue_export->udev = udev;
77
78         initial_seqnum = udev_get_kernel_seqnum(udev);
79         udev_queue_export->seqnum_min = initial_seqnum;
80         udev_queue_export->seqnum_max = initial_seqnum;
81
82         udev_queue_export_cleanup(udev_queue_export);
83         if (rebuild_queue_file(udev_queue_export) != 0) {
84                 free(udev_queue_export);
85                 return NULL;
86         }
87
88         return udev_queue_export;
89 }
90
91 void udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
92 {
93         if (udev_queue_export == NULL)
94                 return;
95         if (udev_queue_export->queue_file != NULL)
96                 fclose(udev_queue_export->queue_file);
97         free(udev_queue_export);
98 }
99
100 void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
101 {
102         char filename[UTIL_PATH_SIZE];
103
104         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
105         unlink(filename);
106
107         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
108         unlink(filename);
109 }
110
111
112 static int skip_to(FILE *file, long offset)
113 {
114         long old_offset;
115
116         /* fseek may drop buffered data, avoid it for small seeks */
117         old_offset = ftell(file);
118         if (offset > old_offset && offset - old_offset <= BUFSIZ) {
119                 size_t skip_bytes = offset - old_offset;
120                 char buf[skip_bytes];
121
122                 if (fread(buf, skip_bytes, 1, file) != skip_bytes)
123                         return -1;
124         }
125
126         return fseek(file, offset, SEEK_SET);
127 }
128
129 struct queue_devpaths {
130         unsigned int devpaths_first;    /* index of first queued event */
131         unsigned int devpaths_size;
132         long devpaths[];                /* seqnum -> offset of devpath in queue file (or 0) */
133 };
134
135 /*
136  * Returns a table mapping seqnum to devpath file offset for currently queued events.
137  * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
138  */
139 static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
140 {
141         struct queue_devpaths *devpaths;
142         unsigned long long int range;
143         long devpath_offset;
144         ssize_t devpath_len;
145         unsigned long long int seqnum;
146         unsigned long long int n;
147         unsigned int i;
148
149         /* seek to the first event in the file */
150         rewind(udev_queue_export->queue_file);
151         udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
152
153         /* allocate the table */
154         range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
155         if (range - 1 > INT_MAX) {
156                 err(udev_queue_export->udev, "queue file overflow\n");
157                 return NULL;
158         }
159         devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
160         if (index == NULL)
161                 return NULL;
162         devpaths->devpaths_size = range + 1;
163
164         /* read all records and populate the table */
165         while(1) {
166                 if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
167                         break;
168                 n = seqnum - udev_queue_export->seqnum_max;
169                 if (n >= devpaths->devpaths_size)
170                         goto read_error;
171
172                 devpath_offset = ftell(udev_queue_export->queue_file);
173                 devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
174                 if (devpath_len < 0)
175                         goto read_error;
176
177                 if (devpath_len > 0)
178                         devpaths->devpaths[n] = devpath_offset;
179                 else
180                         devpaths->devpaths[n] = 0;
181         }
182
183         /* find first queued event */
184         for (i = 0; i < devpaths->devpaths_size; i++) {
185                 if (devpaths->devpaths[i] != 0)
186                         break;
187         }
188         devpaths->devpaths_first = i;
189
190         return devpaths;
191
192 read_error:
193         err(udev_queue_export->udev, "queue file corrupted\n");
194         free(devpaths);
195         return NULL;
196 }
197
198 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
199 {
200         unsigned long long int seqnum;
201         struct queue_devpaths *devpaths = NULL;
202         char filename[UTIL_PATH_SIZE];
203         char filename_tmp[UTIL_PATH_SIZE];
204         FILE *new_queue_file = NULL;
205         unsigned int i;
206
207         /* read old queue file */
208         if (udev_queue_export->queue_file != NULL) {
209                 dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n",
210                                                 udev_queue_export->waste_bytes);
211
212                 devpaths = build_index(udev_queue_export);
213                 if (devpaths != NULL)
214                         udev_queue_export->seqnum_max += devpaths->devpaths_first;
215         }
216         if (devpaths == NULL) {
217                 dbg(udev_queue_export->udev, "creating empty queue file\n");
218                 udev_queue_export->queued_count = 0;
219                 udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
220         }
221
222         /* create new queue file */
223         util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
224         new_queue_file = fopen(filename_tmp, "w+");
225         if (new_queue_file == NULL)
226                 goto error;
227         seqnum = udev_queue_export->seqnum_max;
228         fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
229
230         /* copy unfinished events only to the new file */
231         if (devpaths != NULL) {
232                 for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
233                         char devpath[UTIL_PATH_SIZE];
234                         int err;
235                         unsigned short devpath_len;
236
237                         if (devpaths->devpaths[i] != 0)
238                         {
239                                 skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
240                                 err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
241                                 devpath_len = err;
242
243                                 fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
244                                 fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
245                                 fwrite(devpath, 1, devpath_len, new_queue_file);
246                         }
247                         seqnum++;
248                 }
249                 free(devpaths);
250                 devpaths = NULL;
251         }
252         fflush(new_queue_file);
253         if (ferror(new_queue_file))
254                 goto error;
255
256         /* rename the new file on top of the old one */
257         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
258         if (rename(filename_tmp, filename) != 0)
259                 goto error;
260
261         if (udev_queue_export->queue_file != NULL)
262                 fclose(udev_queue_export->queue_file);
263         udev_queue_export->queue_file = new_queue_file;
264         udev_queue_export->waste_bytes = 0;
265
266         return 0;
267
268 error:
269         err(udev_queue_export->udev, "failed to create queue file: %m\n");
270         udev_queue_export_cleanup(udev_queue_export);
271
272         if (udev_queue_export->queue_file != NULL) {
273                 fclose(udev_queue_export->queue_file);
274                 udev_queue_export->queue_file = NULL;
275         }
276         if (new_queue_file != NULL)
277                 fclose(new_queue_file);
278
279         if (devpaths != NULL)
280                 free(devpaths);
281         udev_queue_export->queued_count = 0;
282         udev_queue_export->waste_bytes = 0;
283         udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
284
285         return -1;
286 }
287
288 static int write_queue_record(struct udev_queue_export *udev_queue_export,
289                               unsigned long long int seqnum, const char *devpath, size_t devpath_len)
290 {
291         unsigned short len;
292
293         if (udev_queue_export->queue_file == NULL) {
294                 dbg(udev_queue_export->udev, "can't record event: queue file not available\n");
295                 return -1;
296         }
297
298         if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
299                 goto write_error;
300
301         len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
302         if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
303                 goto write_error;
304         if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
305                 goto write_error;
306
307         /* *must* flush output; caller may fork */
308         if (fflush(udev_queue_export->queue_file) != 0)
309                 goto write_error;
310
311         return 0;
312
313 write_error:
314         /* if we failed half way through writing a record to a file,
315            we should not try to write any further records to it. */
316         err(udev_queue_export->udev, "error writing to queue file: %m\n");
317         fclose(udev_queue_export->queue_file);
318         udev_queue_export->queue_file = NULL;
319
320         return -1;
321 }
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 files grow too large, they must be garbage collected and rebuilt */
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         char *s;
400         size_t l;
401
402         if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0)
403                 return;
404
405         /* location of failed file */
406         s = filename;
407         l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
408         util_path_encode(udev_device_get_devpath(udev_device), s, l);
409
410         switch (state) {
411         case DEVICE_FAILED:
412                 /* record event in the failed directory */
413                 if (udev_queue_export->failed_count == 0)
414                         util_create_path(udev, filename);
415                 udev_queue_export->failed_count++;
416
417                 udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
418                 symlink(udev_device_get_devpath(udev_device), filename);
419                 udev_selinux_resetfscreatecon(udev);
420                 break;
421
422         case DEVICE_QUEUED:
423                 /* delete failed file */
424                 if (unlink(filename) == 0) {
425                         util_delete_path(udev, filename);
426                         udev_queue_export->failed_count--;
427                 }
428                 break;
429
430         case DEVICE_FINISHED:
431                 if (udev_device_get_devpath_old(udev_device) != NULL) {
432                         /* "move" event - rename failed file to current name, do not delete failed */
433                         char filename_old[UTIL_PATH_SIZE];
434
435                         s = filename_old;
436                         l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
437                         util_path_encode(udev_device_get_devpath_old(udev_device), s, l);
438
439                         if (rename(filename_old, filename) == 0)
440                                 info(udev, "renamed devpath, moved failed state of '%s' to %s'\n",
441                                      udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device));
442                 }
443                 break;
444         }
445
446         return;
447 }
448
449 static int update(struct udev_queue_export *udev_queue_export,
450                   struct udev_device *udev_device, enum device_state state)
451 {
452         update_failed(udev_queue_export, udev_device, state);
453
454         if (update_queue(udev_queue_export, udev_device, state) != 0)
455                 return -1;
456
457         return 0;
458 }
459
460 int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
461 {
462         return update(udev_queue_export, udev_device, DEVICE_QUEUED);
463 }
464
465 int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
466 {
467         return update(udev_queue_export, udev_device, DEVICE_FINISHED);
468 }
469
470 int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
471 {
472         return update(udev_queue_export, udev_device, DEVICE_FAILED);
473 }