#include <time.h>
#include <sys/wait.h>
#include <sys/stat.h>
+#include <sys/sysinfo.h>
#include "libsysfs/sysfs/libsysfs.h"
#include "list.h"
#include "logging.h"
#include "namedev.h"
#include "klibc_fixups.h"
+#include "udevdb.h"
static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr);
return -1;
}
+/** Finds the lowest positive N such that <name>N isn't present in
+ * $(udevroot) either as a file or a symlink.
+ *
+ * @param name Name to check for
+ * @return 0 if <name> didn't exist and N otherwise.
+ */
+static unsigned int find_free_number (struct udevice *udev, char *name)
+{
+ char temp[NAME_SIZE];
+ char path[NAME_SIZE];
+ struct udevice dev;
+ int result;
+
+ /* have to sweep the database for each lookup */
+ result = 0;
+ strncpy(temp, name, sizeof (temp));
+ while (1) {
+ if (udevdb_get_dev_byname(temp, path, &dev) != 0)
+ goto found;
+ /* symlink might be stale if $(udevroot) isn't cleaned; check
+ * on major/minor to see if it's the same device
+ */
+ if (dev.major == udev->major && dev.minor == udev->minor)
+ goto found;
+ snprintf (temp, sizeof(temp), "%s%d", name, ++result);
+ }
+
+found:
+ return result;
+}
+
static void apply_format(struct udevice *udev, char *string, size_t maxsize,
struct sysfs_class_device *class_dev,
struct sysfs_device *sysfs_device)
char *rest;
int slen;
struct sysfs_attribute *tmpattr;
+ unsigned int next_free_number;
pos = string;
-
while (1) {
- pos = strchr(string, '%');
- if (pos != NULL) {
- pos[0] = '\0';
- tail = pos+1;
- len = get_format_len(&tail);
- c = tail[0];
- strfieldcpy(temp, tail+1);
- tail = temp;
- } else {
+ pos = strchr(pos, '%');
+ if (pos == NULL)
break;
- }
- dbg("format=%c, string='%s', tail='%s'",c , string, tail);
+ pos[0] = '\0';
+ tail = pos+1;
+ len = get_format_len(&tail);
+ c = tail[0];
+ strfieldcpy(temp, tail+1);
+ tail = temp;
+ dbg("format=%c, string='%s', tail='%s'",c , string, tail);
attr = get_format_attribute(&tail);
+
switch (c) {
case 'b':
if (strlen(udev->bus_id) == 0)
break;
case '%':
strfieldcatmax(string, "%", maxsize);
+ pos++;
+ break;
+ case 'e':
+ next_free_number = find_free_number(udev, string);
+ if (next_free_number > 0) {
+ snprintf(temp2, sizeof(temp2), "%d", next_free_number);
+ strfieldcatmax(string, temp2, maxsize);
+ }
break;
default:
dbg("unknown substitution type '%%%c'", c);
/* don't separate if in apostrophes */
pos++;
argv[i] = strsep(&pos, "\'");
- while (pos[0] == ' ')
+ while (pos && pos[0] == ' ')
pos++;
- } else {
+ } else {
argv[i] = strsep(&pos, " ");
}
dbg("arg[%i] '%s'", i, argv[i]);
retval = -1;
}
- if (i > 0 && value[i] == '\n')
+ if (i > 0 && value[i-1] == '\n')
i--;
value[i] = '\0';
dbg("result is '%s'", value);
return 0;
}
+static int whitelist_search(struct sysfs_class_device *class_dev)
+{
+ char *sysblock = "/sys/block";
+ int i;
+
+ static char *list[] = {
+ "nb",
+ "ram",
+ "loop",
+ "fd",
+ "md",
+ "dos_cd",
+ "double",
+ "flash",
+ "msd",
+ "rflash",
+ "rom",
+ "rrom",
+ "sbpcd",
+ "pcd",
+ "pf",
+ "scd",
+ "ubd",
+ NULL,
+ };
+
+ if (strncmp(class_dev->path, sysblock, strlen(sysblock)))
+ return 0;
+
+ for (i=0; list[i] != NULL; i++)
+ if (!strncmp(class_dev->name, list[i], strlen(list[i])))
+ return 1;
+
+ return 0;
+}
+
static struct sysfs_device *get_sysfs_device(struct sysfs_class_device *class_dev)
{
struct sysfs_device *sysfs_device;
* all partitions have the symlink in its parent directory.
* But we need to watch out for block devices that do not have parents, yet
* look like a partition (fd0, loop0, etc.) They all do not have a device
- * symlink yet. We do sit and spin on waiting for them right now, we should
- * possibly have a whitelist for these devices here...
+ * symlink yet. We do sit and spin on waiting for them right now unless
+ * they happen to be in the whitelist in which case we exit.
*/
class_dev_parent = sysfs_get_classdev_parent(class_dev);
- if (class_dev_parent)
- dbg("Really a partition");
+ if (class_dev_parent != NULL)
+ dbg("given class device has a parent, use this instead");
tspec.tv_sec = 0;
tspec.tv_nsec = 10000000; /* sleep 10 millisec */
loop = 10;
while (loop--) {
- if (udev_sleep)
+ if (udev_sleep) {
+ if (whitelist_search(class_dev)) {
+ sysfs_device = NULL;
+ goto exit;
+ }
nanosleep(&tspec, NULL);
+ }
+
if (class_dev_parent)
sysfs_device = sysfs_get_classdev_device(class_dev_parent);
else
sysfs_device = sysfs_get_classdev_device(class_dev);
-
if (sysfs_device != NULL)
goto device_found;
}
dbg("timed out waiting for device symlink, continuing on anyway...");
-
+
device_found:
/* We have another issue with just the wait above - the sysfs part of
* the kernel may not be quick enough to have created the link to the
* device under the "bus" subsystem. Due to this, the sysfs_device->bus
* will not contain the actual bus name :(
- *
- * Libsysfs now provides a new API sysfs_get_device_bus(), so use it
- * if needed
*/
if (sysfs_device) {
if (sysfs_device->bus[0] != '\0')
/* execute external program */
if (dev->program[0] != '\0') {
+ char program[PROGRAM_SIZE];
+
dbg("check " FIELD_PROGRAM);
- apply_format(udev, dev->program, sizeof(dev->program),
- class_dev, sysfs_device);
- if (execute_program(dev->program, udev->program_result, NAME_SIZE) != 0) {
+ strfieldcpy(program, dev->program);
+ apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
+ if (execute_program(program, udev->program_result, NAME_SIZE) != 0) {
dbg(FIELD_PROGRAM " returned nonzero");
goto try_parent;
} else {
struct sysfs_device *sysfs_device = NULL;
struct config_device *dev;
struct perm_device *perm;
+ struct sysinfo info;
char *pos;
udev->mode = 0;
}
if (dev->name[0] != '\0') {
+ /* apply all_partitions flag only at a main block device */
+ if (dev->partitions > 0 &&
+ (udev->type != 'b' || udev->kernel_number[0] != '\0'))
+ continue;
+
info("configured rule in '%s' at line %i applied, '%s' becomes '%s'",
dev->config_file, dev->config_line, udev->kernel_name, dev->name);
strfieldcpy(udev->name, dev->name);
}
}
}
-
- /* no rule was found for the net device */
- if (udev->type == 'n') {
- dbg("no name for net device '%s' configured", udev->kernel_name);
- return -1;
- }
-
/* no rule was found so we use the kernel name */
strfieldcpy(udev->name, udev->kernel_name);
- goto done;
+ if (udev->type == 'n')
+ goto done;
+ else
+ goto perms;
found:
apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device);
+ strfieldcpy(udev->config_file, dev->config_file);
+ udev->config_line = dev->config_line;
if (udev->type == 'n')
- return 0;
+ goto done;
udev->partitions = dev->partitions;
- strfieldcpy(udev->config_file, dev->config_file);
- udev->config_line = dev->config_line;
/* get permissions given in rule */
set_empty_perms(udev, dev->mode,
dev->owner,
dev->group);
-done:
+perms:
/* get permissions given in config file or set defaults */
perm = find_perm(udev->name);
if (perm != NULL) {
dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o",
udev->name, udev->owner, udev->group, udev->mode);
+done:
/* store time of action */
- udev->config_time = time(NULL);
+ sysinfo(&info);
+ udev->config_uptime = info.uptime;
return 0;
}