udev_rules.c revision ff94cec3d71b5f46eb54645f4bc65e2d67ed5269
/*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003-2006 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <syslog.h>
#include <dirent.h>
#include <fnmatch.h>
#include "udev.h"
#include "udev_rules.h"
#include "udev_selinux.h"
extern char **environ;
/* 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\n");
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;
return -1;
/* skip whitespace */
linepos++;
/* get the key */
return -1;
temp[0] = '\0';
/* get a quoted value */
temp[0] = '\0';
goto out;
}
}
/* get the value*/
temp[0] = '\0';
out:
return 0;
}
{
int status;
int devnull;
int i;
int retval = 0;
/* build argv from comand */
i = 0;
if (pos[0] == '\'') {
/* don't separate if in apostrophes */
pos++;
pos++;
} else {
}
i++;
}
} else {
}
/* prepare pipes from child to parent */
return -1;
}
}
if (udev_log_priority >= LOG_INFO) {
return -1;
}
}
}
switch(pid) {
case 0:
/* child closes parent ends of pipes */
/* discard child output or connect to pipe */
if (devnull > 0) {
} else
}
}
/* may be on a filesytem which is not mounted right now */
} else {
/* other problems */
}
_exit(1);
case -1:
return -1;
default:
/* read from child if requested */
/* parent closes child ends of pipes */
/* read child output */
int fdcount;
if (fdcount < 0) {
continue;
retval = -1;
break;
}
/* get stdout */
char inbuf[1024];
char *pos;
char *line;
if (count <= 0) {
if (count < 0) {
retval = -1;
}
continue;
}
/* store result for rule processing */
if (result) {
} else {
retval = -1;
}
}
}
/* get stderr */
char errbuf[1024];
char *pos;
char *line;
if (count <= 0) {
if (count < 0)
continue;
}
}
}
/* return the childs stdout string */
if (result) {
if (reslen)
}
}
if (WEXITSTATUS(status) != 0)
retval = -1;
} else {
retval = -1;
}
}
return retval;
}
{
const char *bufline;
char *linepos;
char *variable;
char *value;
int lineno;
/* loop through the whole buffer */
lineno = 0;
cur = 0;
lineno++;
/* eat the whitespace */
bufline++;
count--;
}
if (count == 0)
continue;
/* see if this is a comment */
if (bufline[0] == COMMENT_CHARACTER)
continue;
continue;
}
/* handle device, renamed by external tool, returning new path */
} else
}
}
return 0;
}
{
char *buf;
return -1;
}
return 0;
}
{
char result[2048];
return -1;
}
{
struct sysfs_device *dev_parent;
int rc = -1;
if (dev_parent != NULL) {
struct udevice *udev_parent;
struct name_entry *name_loop;
if (udev_parent == NULL)
return -1;
/* import the udev_db of the parent */
char *pos;
if (pos) {
pos[0] = '\0';
pos++;
} else
}
}
rc = 0;
} else
dbg("parent not found in database\n");
}
return rc;
}
{
int sock;
struct sockaddr_un saddr;
char buf[2048];
int i;
int retval = 0;
if (sockpath[0] == '@') {
/* abstract namespace socket requested */
/* existing socket file */
} else {
/* no socket file, assume abstract namespace socket */
}
bufpos++;
bufpos++;
}
if (count < 0)
retval = -1;
return retval;
}
{
struct name_entry *name_loop;
int retval = 0;
dbg("executing run list\n");
} else {
if (!name_loop->ignore_error)
retval = -1;
}
}
return retval;
}
#define WAIT_LOOP_PER_SECOND 50
{
char devicepath[PATH_SIZE];
while (--loop) {
/* lookup file */
return 0;
}
/* make sure, the device did not disappear in the meantime */
return -2;
}
}
return -1;
}
/* handle "[$SUBSYSTEM/$KERNEL]<attribute>" lookup */
{
char *attrib;
char *id;
int found = 0;
if (attrstr[0] != '[')
goto out;
goto out;
attrib[0] = '\0';
goto out;
id[0] = '\0';
if (attrib[0] != '\0')
else
}
found = 1;
}
out:
return found;
}
{
char *pos;
int found = 0;
continue;
found = 1;
break;
}
}
}
if (!found)
}
return found;
}
{
int len;
int i;
int count;
enum subst_type {
};
static const struct subst_map {
char *name;
char fmt;
enum subst_type type;
} map[] = {
{ NULL, '\0', 0 }
};
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;
}
}
head[0] = '$';
} else if (head[0] == '%') {
/* substitute format char */
break;
head++;
continue;
}
head[0] = '\0';
tail++;
goto found;
}
}
head[0] = '%';
}
head++;
}
break;
switch (type) {
case SUBST_DEVPATH:
break;
case SUBST_KERNEL:
break;
case SUBST_KERNEL_NUMBER:
break;
case SUBST_ID:
}
break;
case SUBST_DRIVER:
}
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\n", i);
while (--i) {
cpos++;
cpos++;
}
if (i > 0) {
err("requested part of result string not found\n");
break;
}
/* %{2+}c copies the whole string from the second part on */
if (rest[0] != '+') {
if (cpos)
cpos[0] = '\0';
}
} else {
}
break;
case SUBST_ATTR:
err("missing file parameter for attr\n");
else {
char *attrib;
else
break;
}
/* try the current device, other matches may have selected */
/* look at all devices along the chain of parents */
do {
break;
}
} while (dev_parent != NULL);
}
break;
/* strip trailing whitespace, and replace unwanted characters */
if (count > 0)
}
break;
case SUBST_PARENT:
{
struct sysfs_device *dev_parent;
if (dev_parent != NULL) {
struct udevice *udev_parent;
if (udev_parent != NULL) {
/* lookup the name in the udev_db with the DEVPATH of the parent */
} else
dbg("parent not found in database\n");
}
}
}
break;
case SUBST_TEMP_NODE:
dbg("create temporary device node for callout\n");
}
break;
case SUBST_NAME:
} else {
}
break;
case SUBST_LINKS:
struct name_entry *name_loop;
}
}
break;
case SUBST_ROOT:
break;
case SUBST_SYS:
break;
case SUBST_ENV:
dbg("missing attribute\n");
break;
}
break;
}
break;
default:
break;
}
/* possibly truncate to format-char specified length */
}
}
}
{
}
{
}
static int match_key(const char *key_name, struct udev_rule *rule, struct key *key, const char *val)
{
char *key_value;
char *pos;
int match = 0;
return 0;
/* look for a matching string, parts are separated by '|' */
while (key_value) {
if (pos) {
pos[0] = '\0';
pos++;
}
if (match)
break;
}
return 0;
}
return 0;
}
return -1;
}
/* match a single rule against a given device and possibly its parent devices */
{
int i;
goto nomatch;
goto nomatch;
goto nomatch;
goto nomatch;
goto nomatch;
/* match NAME against a value assigned by an earlier rule */
goto nomatch;
/* match against current list of symlinks */
struct name_entry *name_loop;
int match = 0;
match = 1;
break;
}
}
if (!match)
goto nomatch;
}
/* we only check for matches, assignments will be handled later */
if (!value) {
value = "";
}
goto nomatch;
}
}
char *attr;
int match;
}
} else if (filename[0] != '/') {
}
}
goto nomatch;
goto nomatch;
dbg("TEST key is true\n");
}
int found;
goto nomatch;
}
/* check for matching sysfs attribute pairs */
char *attrib;
char val[VALUE_SIZE];
else
goto nomatch;
}
goto nomatch;
/* strip trailing whitespace of value, if not asked to match for it */
}
goto nomatch;
}
}
/* walk up the chain of parent devices and find a match */
while (1) {
/* check for matching kernel device name */
goto try_parent;
/* check for matching subsystem value */
goto try_parent;
/* check for matching driver */
goto try_parent;
/* check for matching sysfs attribute pairs */
const char *value;
char val[VALUE_SIZE];
goto try_parent;
/* strip trailing whitespace of value, if not asked to match for it */
}
goto try_parent;
}
}
/* found matching device */
break;
/* move to parent device */
dbg("try parent sysfs device\n");
goto nomatch;
}
/* execute external program */
dbg("PROGRAM is false\n");
goto nomatch;
} else {
int count;
dbg("PROGRAM matches\n");
if (count > 0)
}
dbg("PROGRAM returned successful\n");
goto nomatch;
}
dbg("PROGRAM key is true\n");
}
/* check for matching result of external program */
goto nomatch;
/* import variables returned from program or or file into environment */
int rc = -1;
}
if (rc != 0) {
dbg("IMPORT failed\n");
goto nomatch;
} else
dbg("IMPORT key is true\n");
}
/* rule matches, if we have ENV assignments export it */
char temp_value[NAME_SIZE];
/* make sure we don't write to the same string we possibly read from */
if (temp_value[0] == '\0') {
} else {
struct name_entry *entry;
break;
}
}
}
/* if we have ATTR assignments, write value to sysfs file */
char *attrib;
FILE *f;
}
}
if (attr[0] == '\0') {
}
if (f != NULL) {
fclose(f);
} else
}
}
return 0;
return -1;
}
{
int name_set = 0;
/* look for a matching rule to apply */
while (1) {
break;
if (name_set &&
dbg("node name already set, rule ignored\n");
continue;
}
dbg("process rule\n");
/* apply options */
if (rule->ignore_device) {
return 0;
}
if (rule->ignore_remove) {
dbg("remove event should be ignored\n");
}
if (rule->link_priority != 0) {
}
if (rule->event_timeout >= 0) {
}
/* apply all_partitions option only at a main block device */
if (rule->partitions &&
dbg("creation of partition nodes requested\n");
}
/* apply permissions */
char buf[20];
}
}
}
/* collect symlinks */
if (!udev->symlink_final &&
int count;
info("reset symlink list\n");
}
/* allow multiple symlinks separated by spaces */
if (count > 0)
}
pos++;
while (next) {
next[0] = '\0';
next++;
}
if (pos[0] != '\0') {
}
}
/* set name, later rules with name set will be ignored */
int count;
name_set = 1;
if (count > 0)
}
dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i\n",
}
struct name_entry *entry;
info("reset run list\n");
}
if (rule->run_ignore_error)
}
dbg("last rule to be applied\n");
break;
}
}
}
}
if (!name_set) {
}
dbg("removing temporary device node\n");
}
return 0;
}
{
/* look for a matching rule to apply */
while (1) {
break;
dbg("process rule\n");
dbg("skip rule that names a device\n");
continue;
}
if (rule->ignore_device) {
return 0;
}
if (rule->ignore_remove) {
dbg("remove event should be ignored\n");
}
struct name_entry *entry;
info("reset run list\n");
}
if (rule->run_ignore_error)
break;
}
dbg("last rule to be applied\n");
break;
}
}
}
}
return 0;
}