udev_rules.c revision bd0ed2ffbec325512c3131ce0db896234e36c923
/*
*
* Userspace devfs
*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003-2005 Kay Sievers <kay.sievers@vrfy.org>
*
*
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include "libsysfs/sysfs/libsysfs.h"
#include "list.h"
#include "udev_libc_wrapper.h"
#include "udev.h"
#include "udev_utils.h"
#include "udev_version.h"
#include "logging.h"
#include "udev_rules.h"
#include "udev_db.h"
/* compare string with pattern (supports * ? [0-9] [!A-Z]) */
static int strcmp_pattern(const char *p, const char *s)
{
if (s[0] == '\0') {
while (p[0] == '*')
p++;
return (p[0] != '\0');
}
switch (p[0]) {
case '[':
{
int not = 0;
p++;
if (p[0] == '!') {
not = 1;
p++;
}
while ((p[0] != '\0') && (p[0] != ']')) {
int match = 0;
if (p[1] == '-') {
if ((s[0] >= p[0]) && (s[0] <= p[2]))
match = 1;
p += 3;
} else {
match = (p[0] == s[0]);
p++;
}
while ((p[0] != '\0') && (p[0] != ']'))
p++;
if (p[0] == ']')
}
}
}
break;
case '*':
if (strcmp_pattern(p, s+1))
return strcmp_pattern(p+1, s);
return 0;
case '\0':
if (s[0] == '\0') {
return 0;
}
break;
default:
if ((p[0] == s[0]) || (p[0] == '?'))
break;
}
return 1;
}
/* extract possible {attr} and move str behind it */
static char *get_format_attribute(char **str)
{
char *pos;
if (*str[0] == '{') {
err("missing closing brace for format");
return NULL;
}
pos[0] = '\0';
}
return attr;
}
/* extract possible format length and move str behind it*/
static int get_format_len(char **str)
{
int num;
char *tail;
if (num > 0) {
return num;
} else {
}
}
return -1;
}
{
char *linepos;
char *temp;
if (!linepos)
return -1;
dbg("escaped characters are not supported, skip");
return -1;
}
/* skip whitespace */
linepos++;
/* get the key */
while (1) {
linepos++;
if (linepos[0] == '\0')
return -1;
break;
if (linepos[0] == '=')
break;
}
/* terminate key */
linepos[0] = '\0';
linepos++;
/* skip whitespace */
linepos++;
/* get the value*/
if (linepos[0] == '\0')
return -1;
if (linepos[0] == '"') {
linepos++;
if (!temp)
return -1;
temp[0] = '\0';
} else if (linepos[0] == '\'') {
linepos++;
if (!temp)
return -1;
temp[0] = '\0';
} else {
temp++;
temp[0] = '\0';
}
return 0;
}
static int import_file_into_env(const char *filename)
{
char *bufline;
char *linepos;
char *variable;
char *value;
char *buf;
int lineno;
int retval = 0;
return -1;
}
/* loop through the whole file */
lineno = 0;
cur = 0;
lineno++;
continue;
}
/* eat the whitespace */
bufline++;
count--;
}
if (count == 0)
continue;
/* see if this is a comment */
if (bufline[0] == COMMENT_CHARACTER)
continue;
}
}
return retval;
}
/** 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.
*/
{
int num = 0;
while (1) {
return num;
}
num++;
if (num > 1000) {
return -1;
}
}
}
static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device,
{
struct sysfs_attribute *tmpattr;
if (class_dev) {
if (tmpattr)
goto attr_found;
}
if (sysfs_device) {
if (tmpattr)
goto attr_found;
}
return -1;
return 0;
}
{
int len;
int i;
unsigned int next_free_number;
struct sysfs_class_device *class_dev_parent;
enum subst_type {
};
static const struct subst_map {
char *name;
char fmt;
enum subst_type type;
} map[] = {
{}
};
enum subst_type type;
while (1) {
len = -1;
while (head[0] != '\0') {
if (head[0] == '$') {
/* substitute named variable */
break;
head++;
continue;
}
head[0] = '\0';
goto found;
}
}
}
else if (head[0] == '%') {
/* substitute format char */
break;
head++;
continue;
}
head[0] = '\0';
tail++;
goto found;
}
}
}
head++;
}
break;
dbg("format=%i, string='%s', tail='%s', class_dev=%p, sysfs_dev=%p",
switch (type) {
case SUBST_DEVPATH:
break;
case SUBST_ID:
break;
case SUBST_KERNEL_NAME:
break;
case SUBST_KERNEL_NUMBER:
break;
case SUBST_MAJOR:
break;
case SUBST_MINOR:
break;
case SUBST_RESULT:
break;
/* get part part of the result string */
i = 0;
if (i > 0) {
dbg("request part #%d of result string", i);
while (--i) {
cpos++;
cpos++;
}
if (i > 0) {
err("requested part of result string not found");
break;
}
/* %{2+}c copies the whole string from the second part on */
if (rest[0] != '+') {
if (cpos)
cpos[0] = '\0';
}
} else {
}
break;
case SUBST_SYSFS:
dbg("missing attribute");
break;
}
struct sysfs_device *parent_device;
while (parent_device) {
break;
}
if (!parent_device)
break;
}
/* strip trailing whitespace of sysfs value */
temp2[--i] = '\0';
break;
case SUBST_ENUM:
if (next_free_number > 0) {
}
break;
case SUBST_PARENT:
if (!class_dev)
break;
if (class_dev_parent != NULL) {
struct udevice udev_parent;
/* lookup the name in the udev_db with the DEVPATH of the parent */
} else
dbg("parent not found in database");
}
break;
case SUBST_TEMP_NODE:
dbg("create temporary device node for callout");
}
break;
case SUBST_ROOT:
break;
case SUBST_MODALIAS:
break;
break;
case SUBST_ENV:
dbg("missing attribute");
break;
}
break;
break;
default:
break;
}
/* possibly truncate to format-char specified length */
if (len != -1) {
}
}
}
{
int retval;
int count;
int status;
int pipefds[2];
char *pos;
int devnull;
int i;
i = 0;
if (pos[0] == '\'') {
/* don't separate if in apostrophes */
pos++;
pos++;
} else {
}
i++;
}
} else {
}
if (retval != 0) {
err("pipe failed");
return -1;
}
switch(pid) {
case 0:
/* child dup2 write side of pipe to STDOUT */
if (devnull >= 0) {
}
err("exec of program failed");
_exit(1);
case -1:
retval = -1;
break;
default:
/* parent reads from pipefds[0] */
retval = 0;
i = 0;
while (1) {
if (count < 0) {
retval = -1;
}
if (count == 0)
break;
i += count;
if (i >= len-1) {
retval = -1;
break;
}
}
value[i] = '\0';
retval = -1;
}
}
if (!retval) {
} else
value[0] = '\0';
return retval;
}
{
goto exit;
} else {
goto exit;
}
}
goto exit;
} else {
goto exit;
}
}
goto exit;
} else {
goto exit;
}
}
goto exit;
} else {
goto exit;
}
}
goto exit;
}
goto exit;
} else {
goto exit;
}
}
if (rule->env_pair_count) {
int i;
for (i = 0; i < rule->env_pair_count; i++) {
const char *value;
if (!value) {
goto exit;
}
goto exit;
} else {
goto exit;
}
}
}
/* walk up the chain of physical devices and find a match */
while (1) {
/* check for matching driver */
if (parent_device == NULL) {
dbg("device has no sysfs_device");
goto exit;
}
goto try_parent;
} else {
goto try_parent;
}
}
/* check for matching bus value */
if (parent_device == NULL) {
dbg("device has no sysfs_device");
goto exit;
}
goto try_parent;
} else {
goto try_parent;
}
}
/* check for matching bus id */
if (parent_device == NULL) {
dbg("device has no sysfs_device");
goto exit;
}
goto try_parent;
} else {
goto try_parent;
}
}
/* check for matching sysfs pairs */
if (rule->sysfs_pair_count) {
int i;
for (i = 0; i < rule->sysfs_pair_count; i++) {
char value[VALUE_SIZE];
goto try_parent;
/* strip trailing whitespace of value, if not asked to match for it */
}
goto try_parent;
} else {
goto try_parent;
}
}
}
/* found matching physical device */
break;
dbg("try parent sysfs device");
if (parent_device == NULL)
goto exit;
}
if (import_file_into_env(import) == 0) {
goto exit;
} else
goto exit;
}
/* execute external program */
goto exit;
} else {
goto exit;
}
}
/* check for matching result of external program */
goto exit;
} else {
goto exit;
}
}
/* rule matches */
return 0;
exit:
return -1;
}
{
struct sysfs_class_device *class_dev_parent;
/* Figure out where the "device"-symlink is at. For char devices this will
* always be in the class_dev->path. On block devices, only the main block
* device will have the device symlink in it's path. All partition devices
* need to look at the symlink in its parent directory.
*/
if (class_dev_parent != NULL) {
dbg("given class device has a parent, use this instead");
} else {
}
if (sysfs_device) {
dbg("found devices device: path='%s', bus_id='%s', bus='%s'",
}
/* look for a matching rule to apply */
while (1) {
rule = udev_rules_iter_next();
break;
dbg("node name already set, rule ignored");
continue;
}
dbg("process rule");
/* apply options */
if (rule->ignore_device) {
info("configured rule in '%s[%i]' applied, '%s' is ignored",
return 0;
}
if (rule->ignore_remove) {
dbg("remove event should be ignored");
}
/* apply all_partitions option only at a main block device */
dbg("creation of partition nodes requested");
}
/* apply permissions */
}
}
}
/* collect symlinks */
struct name_entry *name_loop;
struct name_entry *temp_loop;
info("reset symlink list");
}
}
info("configured rule in '%s[%i]' applied, added symlink '%s'",
/* add multiple symlinks separated by spaces */
while (next) {
next[0] = '\0';
}
}
}
/* set name, later rules with name set will be ignored */
info("configured rule in '%s[%i]' applied, node handling for '%s' supressed",
} else {
info("configured rule in '%s:%i' applied, '%s' becomes '%s'",
dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i",
}
}
struct name_entry *name_loop;
struct name_entry *temp_loop;
info("reset run list");
}
}
}
}
dbg("last rule to be applied");
break;
}
}
}
}
dbg("removing temporary device node");
}
return 0;
}
{
/* look for a matching rule to apply */
while (1) {
rule = udev_rules_iter_next();
break;
dbg("process rule");
continue;
dbg("skip rule that names a device");
continue;
}
if (rule->ignore_device) {
info("configured rule in '%s[%i]' applied, '%s' is ignored",
return 0;
}
struct name_entry *name_loop;
struct name_entry *temp_loop;
info("reset run list");
}
}
}
break;
}
dbg("last rule to be applied");
break;
}
}
}
return 0;
}