chiark / gitweb /
libudev: add missing 'global' to symbol export
[elogind.git] / src / libudev / libudev-queue-private.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
5   Copyright 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 /*
22  * DISCLAIMER - The file format mentioned here is private to udev/libudev,
23  *              and may be changed without notice.
24  *
25  * The udev event queue is exported as a binary log file.
26  * Each log record consists of a sequence number followed by the device path.
27  *
28  * When a new event is queued, its details are appended to the log.
29  * When the event finishes, a second record is appended to the log
30  * with the same sequence number but a devpath len of 0.
31  *
32  * Example:
33  *        { 0x0000000000000001 }
34  *        { 0x0000000000000001, 0x0019, "/devices/virtual/mem/null" },
35  *        { 0x0000000000000002, 0x001b, "/devices/virtual/mem/random" },
36  *        { 0x0000000000000001, 0x0000 },
37  *        { 0x0000000000000003, 0x0019, "/devices/virtual/mem/zero" },
38  *
39  * Events 2 and 3 are still queued, but event 1 has finished.
40  *
41  * The queue does not grow indefinitely. It is periodically re-created
42  * to remove finished events. Atomic rename() makes this transparent to readers.
43  *
44  * The queue file starts with a single sequence number which specifies the
45  * minimum sequence number in the log that follows. Any events prior to this
46  * sequence number have already finished.
47  */
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <dirent.h>
55 #include <limits.h>
56 #include <errno.h>
57 #include <sys/stat.h>
58 #include <sys/types.h>
59
60 #include "libudev.h"
61 #include "libudev-private.h"
62
63 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
64
65 struct udev_queue_export {
66         struct udev *udev;
67         int queued_count;        /* number of unfinished events exported in queue file */
68         FILE *queue_file;
69         unsigned long long int seqnum_max;        /* earliest sequence number in queue file */
70         unsigned long long int seqnum_min;        /* latest sequence number in queue file */
71         int waste_bytes;                        /* queue file bytes wasted on finished events */
72 };
73
74 struct udev_queue_export *udev_queue_export_new(struct udev *udev)
75 {
76         struct udev_queue_export *udev_queue_export;
77         unsigned long long int initial_seqnum;
78
79         if (udev == NULL)
80                 return NULL;
81
82         udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
83         if (udev_queue_export == NULL)
84                 return NULL;
85         udev_queue_export->udev = udev;
86
87         initial_seqnum = udev_get_kernel_seqnum(udev);
88         udev_queue_export->seqnum_min = initial_seqnum;
89         udev_queue_export->seqnum_max = initial_seqnum;
90
91         udev_queue_export_cleanup(udev_queue_export);
92         if (rebuild_queue_file(udev_queue_export) != 0) {
93                 free(udev_queue_export);
94                 return NULL;
95         }
96
97         return udev_queue_export;
98 }
99
100 struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
101 {
102         if (udev_queue_export == NULL)
103                 return NULL;
104         if (udev_queue_export->queue_file != NULL)
105                 fclose(udev_queue_export->queue_file);
106         free(udev_queue_export);
107         return NULL;
108 }
109
110 void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
111 {
112         if (udev_queue_export == NULL)
113                 return;
114         unlink("/run/udev/queue.tmp");
115         unlink("/run/udev/queue.bin");
116 }
117
118 static int skip_to(FILE *file, long offset)
119 {
120         long old_offset;
121
122         /* fseek may drop buffered data, avoid it for small seeks */
123         old_offset = ftell(file);
124         if (offset > old_offset && offset - old_offset <= BUFSIZ) {
125                 size_t skip_bytes = offset - old_offset;
126                 char *buf = alloca(skip_bytes);
127
128                 if (fread(buf, skip_bytes, 1, file) != skip_bytes)
129                         return -1;
130         }
131
132         return fseek(file, offset, SEEK_SET);
133 }
134
135 struct queue_devpaths {
136         unsigned int devpaths_first;        /* index of first queued event */
137         unsigned int devpaths_size;
138         long devpaths[];                /* seqnum -> offset of devpath in queue file (or 0) */
139 };
140
141 /*
142  * Returns a table mapping seqnum to devpath file offset for currently queued events.
143  * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
144  */
145 static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
146 {
147         struct queue_devpaths *devpaths;
148         unsigned long long int range;
149         long devpath_offset;
150         ssize_t devpath_len;
151         unsigned long long int seqnum;
152         unsigned long long int n;
153         unsigned int i;
154
155         /* seek to the first event in the file */
156         rewind(udev_queue_export->queue_file);
157         udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
158
159         /* allocate the table */
160         range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
161         if (range - 1 > INT_MAX) {
162                 udev_err(udev_queue_export->udev, "queue file overflow\n");
163                 return NULL;
164         }
165         devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
166         if (devpaths == NULL)
167                 return NULL;
168         devpaths->devpaths_size = range + 1;
169
170         /* read all records and populate the table */
171         for (;;) {
172                 if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
173                         break;
174                 n = seqnum - udev_queue_export->seqnum_max;
175                 if (n >= devpaths->devpaths_size)
176                         goto read_error;
177
178                 devpath_offset = ftell(udev_queue_export->queue_file);
179                 devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
180                 if (devpath_len < 0)
181                         goto read_error;
182
183                 if (devpath_len > 0)
184                         devpaths->devpaths[n] = devpath_offset;
185                 else
186                         devpaths->devpaths[n] = 0;
187         }
188
189         /* find first queued event */
190         for (i = 0; i < devpaths->devpaths_size; i++) {
191                 if (devpaths->devpaths[i] != 0)
192                         break;
193         }
194         devpaths->devpaths_first = i;
195
196         return devpaths;
197
198 read_error:
199         udev_err(udev_queue_export->udev, "queue file corrupted\n");
200         free(devpaths);
201         return NULL;
202 }
203
204 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
205 {
206         unsigned long long int seqnum;
207         struct queue_devpaths *devpaths = NULL;
208         FILE *new_queue_file = NULL;
209         unsigned int i;
210
211         /* read old queue file */
212         if (udev_queue_export->queue_file != NULL) {
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                 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         new_queue_file = fopen("/run/udev/queue.tmp", "w+e");
224         if (new_queue_file == NULL)
225                 goto error;
226         seqnum = udev_queue_export->seqnum_max;
227         fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
228
229         /* copy unfinished events only to the new file */
230         if (devpaths != NULL) {
231                 for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
232                         char devpath[UTIL_PATH_SIZE];
233                         int err;
234                         unsigned short devpath_len;
235
236                         if (devpaths->devpaths[i] != 0)
237                         {
238                                 skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
239                                 err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
240                                 devpath_len = err;
241
242                                 fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
243                                 fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
244                                 fwrite(devpath, 1, devpath_len, new_queue_file);
245                         }
246                         seqnum++;
247                 }
248                 free(devpaths);
249                 devpaths = NULL;
250         }
251         fflush(new_queue_file);
252         if (ferror(new_queue_file))
253                 goto error;
254
255         /* rename the new file on top of the old one */
256         if (rename("/run/udev/queue.tmp", "/run/udev/queue.bin") != 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         udev_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                 return -1;
293
294         if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
295                 goto write_error;
296
297         len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
298         if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
299                 goto write_error;
300         if (len > 0) {
301                 if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
302                         goto write_error;
303         }
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         udev_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 };
325
326 static inline size_t queue_record_size(size_t devpath_len)
327 {
328         return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
329 }
330
331 static int update_queue(struct udev_queue_export *udev_queue_export,
332                          struct udev_device *udev_device, enum device_state state)
333 {
334         unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
335         const char *devpath = NULL;
336         size_t devpath_len = 0;
337         int bytes;
338         int err;
339
340         /* FINISHED records have a zero length devpath */
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         /* if we're removing the last event from the queue, that's the best time to rebuild it */
353         if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1) {
354                 /* we don't need to read the old queue file */
355                 fclose(udev_queue_export->queue_file);
356                 udev_queue_export->queue_file = NULL;
357                 rebuild_queue_file(udev_queue_export);
358                 return 0;
359         }
360
361         /* try to rebuild the queue files before they grow larger than one page. */
362         bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
363         if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
364                 rebuild_queue_file(udev_queue_export);
365
366         /* don't record a finished event, if we already dropped the event in a failed rebuild */
367         if (seqnum < udev_queue_export->seqnum_max)
368                 return 0;
369
370         /* now write to the queue */
371         if (state == DEVICE_QUEUED) {
372                 udev_queue_export->queued_count++;
373                 udev_queue_export->seqnum_min = seqnum;
374         } else {
375                 udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
376                 udev_queue_export->queued_count--;
377         }
378         err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
379
380         /* try to handle ENOSPC */
381         if (err != 0 && udev_queue_export->queued_count == 0) {
382                 udev_queue_export_cleanup(udev_queue_export);
383                 err = rebuild_queue_file(udev_queue_export);
384         }
385
386         return err;
387 }
388
389 static int update(struct udev_queue_export *udev_queue_export,
390                   struct udev_device *udev_device, enum device_state state)
391 {
392         if (update_queue(udev_queue_export, udev_device, state) != 0)
393                 return -1;
394
395         return 0;
396 }
397
398 int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
399 {
400         return update(udev_queue_export, udev_device, DEVICE_QUEUED);
401 }
402
403 int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
404 {
405         return update(udev_queue_export, udev_device, DEVICE_FINISHED);
406 }