libudev-queue-export.c revision f503f6b22fa54d1a65156a51d8b3311190c73ae5
/*
* libudev - interface to udev device information
*
* Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*/
/*
* and may be changed without notice.
*
*
* The udev event queue is exported as a binary log file.
* Each log record consists of a sequence number followed by the device path.
*
* When a new event is queued, its details are appended to the log.
* When the event finishes, a second record is appended to the log
* with the same sequence number but a null devpath.
*
* Example:
* {1, "" },
* Event 2 is still queued, but event 1 has been finished
*
* The queue does not grow indefinitely. It is periodically re-created
* to remove finished events. Atomic rename() makes this transparent to readers.
*
*
* The queue file starts with a single sequence number which specifies the
* minimum sequence number in the log that follows. Any events prior to this
* sequence number have already finished.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <limits.h>
#include <assert.h>
#include "udev.h"
struct udev_queue_export {
int failed_count; /* number of failed events exported */
int queued_count; /* number of unfinished events exported in queue file */
unsigned long long int seqnum_max; /* earliest sequence number in queue file */
unsigned long long int seqnum_min; /* latest sequence number in queue file */
int waste_bytes; /* queue file bytes wasted on finished events */
};
{
struct udev_queue_export *udev_queue_export;
unsigned long long int initial_seqnum;
return NULL;
if (udev_queue_export == NULL)
return NULL;
if (rebuild_queue_file(udev_queue_export) != 0) {
return NULL;
}
return udev_queue_export;
}
{
if (udev_queue_export == NULL)
return;
}
{
char filename[UTIL_PATH_SIZE];
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
}
{
long old_offset;
/* fseek may drop buffered data, avoid it for small seeks */
char buf[skip_bytes];
return -1;
}
}
struct queue_devpaths {
unsigned int devpaths_first; /* index of first queued event */
unsigned int devpaths_size;
long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */
};
/*
* Returns a table mapping seqnum to devpath file offset for currently queued events.
* devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
*/
{
struct queue_devpaths *devpaths;
unsigned long long int range;
long devpath_offset;
unsigned long long int seqnum;
unsigned long long int n;
unsigned int i;
/* seek to the first event in the file */
/* allocate the table */
return NULL;
}
return NULL;
/* read all records and populate the table */
while(1) {
break;
if (n >= devpaths->devpaths_size)
goto read_error;
if (devpath_len < 0)
goto read_error;
if (devpath_len > 0)
else
}
/* find first queued event */
for (i = 0; i < devpaths->devpaths_size; i++) {
break;
}
devpaths->devpaths_first = i;
return devpaths;
return NULL;
}
{
unsigned long long int seqnum;
char filename[UTIL_PATH_SIZE];
char filename_tmp[UTIL_PATH_SIZE];
unsigned int i;
/* read old queue file */
}
}
/* create new queue file */
util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
if (new_queue_file == NULL)
goto error;
/* copy unfinished events only to the new file */
char devpath[UTIL_PATH_SIZE];
int err;
unsigned short devpath_len;
{
devpath_len = err;
}
seqnum++;
}
}
if (ferror(new_queue_file))
goto error;
/* rename the new file on top of the old one */
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
goto error;
udev_queue_export->waste_bytes = 0;
return 0;
}
if (new_queue_file != NULL)
udev_queue_export->waste_bytes = 0;
return -1;
}
{
unsigned short len;
return -1;
}
goto write_error;
goto write_error;
goto write_error;
/* *must* flush output; caller may fork */
goto write_error;
return 0;
/* if we failed half way through writing a record to a file,
we should not try to write any further records to it. */
return -1;
}
enum device_state {
};
{
return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
}
{
size_t devpath_len = 0;
int bytes;
int err;
if (state == DEVICE_QUEUED) {
}
/* recover from an earlier failed rebuild */
if (rebuild_queue_file(udev_queue_export) != 0)
return -1;
}
/* when the queue files grow too large, they must be garbage collected and rebuilt */
/* if we're removing the last event from the queue, that's the best time to rebuild it */
/* because we don't need to read the old queue file */
return 0;
}
/* try to rebuild the queue files before they grow larger than one page. */
/* don't record a finished event, if we already dropped the event in a failed rebuild */
return 0;
/* now write to the queue */
if (state == DEVICE_QUEUED) {
} else {
}
/* try to handle ENOSPC */
}
return err;
}
{
char filename[UTIL_PATH_SIZE];
char *s;
size_t l;
return;
/* location of failed file */
s = filename;
l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
switch (state) {
case DEVICE_FAILED:
/* record event in the failed directory */
if (udev_queue_export->failed_count == 0)
break;
case DEVICE_QUEUED:
/* delete failed file */
}
break;
case DEVICE_FINISHED:
/* "move" event - rename failed file to current name, do not delete failed */
char filename_old[UTIL_PATH_SIZE];
s = filename_old;
l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
}
break;
}
return;
}
{
return -1;
return 0;
}
int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
{
}
int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
{
}
int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
{
}