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