libudev-queue-private.c revision 4ec9c3e79771aa95586390cecff4218cc8938160
/*
* 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 devpath len of 0.
*
* Example:
* { 0x0000000000000001 }
* { 0x0000000000000001, 0x0000 },
*
* Events 2 and 3 are still queued, but event 1 has 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 <errno.h>
#include "libudev.h"
#include "libudev-private.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_run_path(udev_queue_export->udev), "/queue.tmp", NULL);
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->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_run_path(udev_queue_export->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_run_path(udev_queue_export->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 file grows too large, garbage-collect and rebuild it */
/* 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];
return;
/* location of failed file */
switch (state) {
case DEVICE_FAILED:
/* record event in the failed directory */
break;
case DEVICE_QUEUED:
/* delete failed file */
}
break;
case DEVICE_FINISHED:
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)
{
}