chiark / gitweb /
libudev: import hwdb and export lookup interface
[elogind.git] / src / 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 queued_count;        /* number of unfinished events exported in queue file */
60         FILE *queue_file;
61         unsigned long long int seqnum_max;        /* earliest sequence number in queue file */
62         unsigned long long int seqnum_min;        /* latest sequence number in queue file */
63         int waste_bytes;                        /* queue file bytes wasted on finished events */
64 };
65
66 struct udev_queue_export *udev_queue_export_new(struct udev *udev)
67 {
68         struct udev_queue_export *udev_queue_export;
69         unsigned long long int initial_seqnum;
70
71         if (udev == NULL)
72                 return NULL;
73
74         udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
75         if (udev_queue_export == NULL)
76                 return NULL;
77         udev_queue_export->udev = udev;
78
79         initial_seqnum = udev_get_kernel_seqnum(udev);
80         udev_queue_export->seqnum_min = initial_seqnum;
81         udev_queue_export->seqnum_max = initial_seqnum;
82
83         udev_queue_export_cleanup(udev_queue_export);
84         if (rebuild_queue_file(udev_queue_export) != 0) {
85                 free(udev_queue_export);
86                 return NULL;
87         }
88
89         return udev_queue_export;
90 }
91
92 struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
93 {
94         if (udev_queue_export == NULL)
95                 return NULL;
96         if (udev_queue_export->queue_file != NULL)
97                 fclose(udev_queue_export->queue_file);
98         free(udev_queue_export);
99         return NULL;
100 }
101
102 void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
103 {
104         if (udev_queue_export == NULL)
105                 return;
106         unlink("/run/udev/queue.tmp");
107         unlink("/run/udev/queue.bin");
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 = alloca(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                 udev_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 (devpaths == NULL)
159                 return NULL;
160         devpaths->devpaths_size = range + 1;
161
162         /* read all records and populate the table */
163         for (;;) {
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         udev_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         FILE *new_queue_file = NULL;
201         unsigned int i;
202
203         /* read old queue file */
204         if (udev_queue_export->queue_file != NULL) {
205                 devpaths = build_index(udev_queue_export);
206                 if (devpaths != NULL)
207                         udev_queue_export->seqnum_max += devpaths->devpaths_first;
208         }
209         if (devpaths == NULL) {
210                 udev_queue_export->queued_count = 0;
211                 udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
212         }
213
214         /* create new queue file */
215         new_queue_file = fopen("/run/udev/queue.tmp", "w+e");
216         if (new_queue_file == NULL)
217                 goto error;
218         seqnum = udev_queue_export->seqnum_max;
219         fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
220
221         /* copy unfinished events only to the new file */
222         if (devpaths != NULL) {
223                 for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
224                         char devpath[UTIL_PATH_SIZE];
225                         int err;
226                         unsigned short devpath_len;
227
228                         if (devpaths->devpaths[i] != 0)
229                         {
230                                 skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
231                                 err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
232                                 devpath_len = err;
233
234                                 fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
235                                 fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
236                                 fwrite(devpath, 1, devpath_len, new_queue_file);
237                         }
238                         seqnum++;
239                 }
240                 free(devpaths);
241                 devpaths = NULL;
242         }
243         fflush(new_queue_file);
244         if (ferror(new_queue_file))
245                 goto error;
246
247         /* rename the new file on top of the old one */
248         if (rename("/run/udev/queue.tmp", "/run/udev/queue.bin") != 0)
249                 goto error;
250
251         if (udev_queue_export->queue_file != NULL)
252                 fclose(udev_queue_export->queue_file);
253         udev_queue_export->queue_file = new_queue_file;
254         udev_queue_export->waste_bytes = 0;
255
256         return 0;
257
258 error:
259         udev_err(udev_queue_export->udev, "failed to create queue file: %m\n");
260         udev_queue_export_cleanup(udev_queue_export);
261
262         if (udev_queue_export->queue_file != NULL) {
263                 fclose(udev_queue_export->queue_file);
264                 udev_queue_export->queue_file = NULL;
265         }
266         if (new_queue_file != NULL)
267                 fclose(new_queue_file);
268
269         if (devpaths != NULL)
270                 free(devpaths);
271         udev_queue_export->queued_count = 0;
272         udev_queue_export->waste_bytes = 0;
273         udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
274
275         return -1;
276 }
277
278 static int write_queue_record(struct udev_queue_export *udev_queue_export,
279                               unsigned long long int seqnum, const char *devpath, size_t devpath_len)
280 {
281         unsigned short len;
282
283         if (udev_queue_export->queue_file == NULL)
284                 return -1;
285
286         if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
287                 goto write_error;
288
289         len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
290         if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
291                 goto write_error;
292         if (len > 0) {
293                 if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
294                         goto write_error;
295         }
296
297         /* *must* flush output; caller may fork */
298         if (fflush(udev_queue_export->queue_file) != 0)
299                 goto write_error;
300
301         return 0;
302
303 write_error:
304         /* if we failed half way through writing a record to a file,
305            we should not try to write any further records to it. */
306         udev_err(udev_queue_export->udev, "error writing to queue file: %m\n");
307         fclose(udev_queue_export->queue_file);
308         udev_queue_export->queue_file = NULL;
309
310         return -1;
311 }
312
313 enum device_state {
314         DEVICE_QUEUED,
315         DEVICE_FINISHED,
316 };
317
318 static inline size_t queue_record_size(size_t devpath_len)
319 {
320         return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
321 }
322
323 static int update_queue(struct udev_queue_export *udev_queue_export,
324                          struct udev_device *udev_device, enum device_state state)
325 {
326         unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
327         const char *devpath = NULL;
328         size_t devpath_len = 0;
329         int bytes;
330         int err;
331
332         /* FINISHED records have a zero length devpath */
333         if (state == DEVICE_QUEUED) {
334                 devpath = udev_device_get_devpath(udev_device);
335                 devpath_len = strlen(devpath);
336         }
337
338         /* recover from an earlier failed rebuild */
339         if (udev_queue_export->queue_file == NULL) {
340                 if (rebuild_queue_file(udev_queue_export) != 0)
341                         return -1;
342         }
343
344         /* if we're removing the last event from the queue, that's the best time to rebuild it */
345         if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1) {
346                 /* we don't need to read the old queue file */
347                 fclose(udev_queue_export->queue_file);
348                 udev_queue_export->queue_file = NULL;
349                 rebuild_queue_file(udev_queue_export);
350                 return 0;
351         }
352
353         /* try to rebuild the queue files before they grow larger than one page. */
354         bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
355         if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
356                 rebuild_queue_file(udev_queue_export);
357
358         /* don't record a finished event, if we already dropped the event in a failed rebuild */
359         if (seqnum < udev_queue_export->seqnum_max)
360                 return 0;
361
362         /* now write to the queue */
363         if (state == DEVICE_QUEUED) {
364                 udev_queue_export->queued_count++;
365                 udev_queue_export->seqnum_min = seqnum;
366         } else {
367                 udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
368                 udev_queue_export->queued_count--;
369         }
370         err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
371
372         /* try to handle ENOSPC */
373         if (err != 0 && udev_queue_export->queued_count == 0) {
374                 udev_queue_export_cleanup(udev_queue_export);
375                 err = rebuild_queue_file(udev_queue_export);
376         }
377
378         return err;
379 }
380
381 static int update(struct udev_queue_export *udev_queue_export,
382                   struct udev_device *udev_device, enum device_state state)
383 {
384         if (update_queue(udev_queue_export, udev_device, state) != 0)
385                 return -1;
386
387         return 0;
388 }
389
390 int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
391 {
392         return update(udev_queue_export, udev_device, DEVICE_QUEUED);
393 }
394
395 int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
396 {
397         return update(udev_queue_export, udev_device, DEVICE_FINISHED);
398 }