MFp4 (imp_freebsd branch): snapshot of devd work:

o improve parsing and lexing
o create data structures based on the parsed file now.
o Still need to rewrite main loop and add regex (still uses hard coded
  devd-generic)
o minor man page updates.

# There should be one more commit before rc2

Approved by: re (blanket)
This commit is contained in:
Warner Losh 2002-12-07 08:04:36 +00:00
parent b77b2b6ed3
commit 3054f218eb
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=107665
8 changed files with 701 additions and 316 deletions

View File

@ -1,9 +1,9 @@
# $FreeBSD$
PROG= devd
SRCS= devd.c token.l parse.y y.tab.h
PROG_CXX=devd
SRCS= devd.cc token.l parse.y y.tab.h
MAN= devd.8 devd.conf.5
WARNS?= 5
#WARNS?= 5
DPADD= ${LIBL}
LDADD= -ll

View File

@ -1,223 +0,0 @@
/*-
* Copyright (c) 2002 M. Warner Losh.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* DEVD control daemon.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "devd.h"
#define CF "/etc/devd.conf"
extern FILE *yyin;
extern int lineno;
int dflag;
int romeo_must_die = 0;
struct file_list_head dirlist = TAILQ_HEAD_INITIALIZER(dirlist);
static void event_loop(void);
static void parse(void);
static void parse_config_file(const char *fn);
static void parse_files_in_dir(const char *dirname);
static void reset_config(void);
static void usage(void);
void
add_directory(const char *dir)
{
struct file_list *elm;
elm = malloc(sizeof(*elm));
elm->path = strdup(dir);
TAILQ_INSERT_TAIL(&dirlist, elm, fl_link);
}
static void
reset_config(void)
{
struct file_list *flp;
TAILQ_FOREACH(flp, &dirlist, fl_link) {
free(flp->path);
free(flp);
}
}
static void
parse_config_file(const char *fn)
{
if (dflag)
printf("Parsing %s\n", fn);
yyin = fopen(fn, "r");
if (yyin == NULL)
err(1, "Cannot open config file %s", fn);
if (yyparse() != 0)
errx(1, "Cannot parse %s at line %d", fn, lineno);
fclose(yyin);
}
static void
parse_files_in_dir(const char *dirname)
{
DIR *dirp;
struct dirent *dp;
char path[PATH_MAX];
if (dflag)
printf("Parsing files in %s\n", dirname);
dirp = opendir(dirname);
if (dirp == NULL)
return;
readdir(dirp); /* Skip . */
readdir(dirp); /* Skip .. */
while ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
snprintf(path, sizeof(path), "%s/%s",
dirname, dp->d_name);
parse_config_file(path);
}
}
}
static void
parse(void)
{
struct file_list *flp;
parse_config_file(CF);
TAILQ_FOREACH(flp, &dirlist, fl_link) {
parse_files_in_dir(flp->path);
}
}
static void
process_event(const char *buffer)
{
char type;
char cmd[1024];
char *sp;
// Ignore unknown devices for now.
if (*buffer == '?')
return;
type = *buffer++;
sp = strchr(buffer, ' ');
if (sp == NULL)
return; /* Can't happen? */
*sp = '\0';
snprintf(cmd, sizeof(cmd), "/etc/devd-generic %s %s", buffer,
type == '+' ? "start" : "stop");
if (dflag)
printf("Trying '%s'\n", cmd);
system(cmd);
}
static void
event_loop(void)
{
int rv;
int fd;
char buffer[1024 + 1]; /* XXX */
fd = open("/dev/devctl", O_RDONLY); /* XXX */
if (fd == -1)
err(1, "Can't open devctl");
if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
err(1, "Can't set close-on-exec flag");
while (1) {
if (romeo_must_die)
break;
rv = read(fd, buffer, sizeof(buffer) - 1);
if (rv > 0) {
buffer[rv] = '\0';
while (buffer[--rv] == '\n')
buffer[rv] = '\0';
process_event(buffer);
} else if (rv < 0) {
if (errno != EINTR)
break;
} else {
/* EOF */
break;
}
}
close(fd);
}
static void
gensighand(int foo __unused)
{
romeo_must_die++;
}
static void
usage()
{
fprintf(stderr, "usage: %s [-d]", getprogname());
exit(1);
}
int
main(int argc, char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "d")) != -1) {
switch (ch) {
case 'd':
dflag++;
break;
default:
usage();
}
}
reset_config();
parse();
if (!dflag)
daemon(0, 0);
event_loop();
signal(SIGHUP, gensighand);
signal(SIGINT, gensighand);
signal(SIGTERM, gensighand);
return (0);
}

591
sbin/devd/devd.cc Normal file
View File

@ -0,0 +1,591 @@
/*-
* Copyright (c) 2002 M. Warner Losh.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* DEVD control daemon.
*/
// TODO list:
// o rewrite the main loop:
// - expand variables
// - find best match
// - execute it.
// o need to insert the event_proc structures in order of priority.
// bigger numbers mean higher priority.
// o devd.conf and devd man pages need a lot of help:
// - devd.conf needs to lose the warning about zone files.
// - devd.conf needs more details on the supported statements.
// - devd.conf needs an example or two.
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <map>
#include <string>
#include <vector>
#include "devd.h"
#define CF "/etc/devd.conf"
using namespace std;
extern FILE *yyin;
extern int lineno;
int dflag;
int romeo_must_die = 0;
static void event_loop(void);
static void usage(void);
class config;
class var_list
{
public:
var_list() {}
virtual ~var_list() {}
void set_variable(const string &var, const string &val);
const string &get_variable(const string &var) const;
bool is_set(const string &var) const;
static const string bogus;
static const string nothing;
private:
map<string, string> _vars;
};
class eps
{
public:
eps() {}
virtual ~eps() {}
virtual bool do_match(config &) = 0;
virtual bool do_action(config &) = 0;
};
class match : public eps
{
public:
match(const char *var, const char *re);
virtual ~match();
virtual bool do_match(config &);
virtual bool do_action(config &) { return true; }
private:
string _var;
string _re;
};
class action : public eps
{
public:
action(const char *cmd);
virtual ~action();
virtual bool do_match(config &) { return true; }
virtual bool do_action(config &);
private:
string _cmd;
};
class event_proc
{
public:
event_proc();
virtual ~event_proc();
int get_priority() { return (_prio); }
void set_priority(int prio) { _prio = prio; }
void add(eps *);
bool matches(config &);
bool run(config &);
private:
int _prio;
vector<eps *> _epsvec;
};
class config
{
public:
config() : _pidfile("") { push_var_table(); }
virtual ~config() { reset(); }
void add_attach(int, event_proc *);
void add_detach(int, event_proc *);
void add_directory(const char *);
void add_nomatch(int, event_proc *);
void set_pidfile(const char *);
void reset();
void parse();
void drop_pidfile();
void push_var_table();
void pop_var_table();
void set_variable(const char *var, const char *val);
const string &get_variable(const string &var);
const string &expand_string(const string &var);
protected:
void parse_one_file(const char *fn);
void parse_files_in_dir(const char *dirname);
private:
vector<string> _dir_list;
string _pidfile;
vector<var_list *> _var_list_table;
vector<event_proc *> _attach_list;
vector<event_proc *> _detach_list;
vector<event_proc *> _nomatch_list;
};
config cfg;
event_proc::event_proc() : _prio(-1)
{
// nothing
}
event_proc::~event_proc()
{
vector<eps *>::const_iterator i;
for (i = _epsvec.begin(); i != _epsvec.end(); i++)
delete *i;
_epsvec.clear();
}
void
event_proc::add(eps *eps)
{
_epsvec.push_back(eps);
}
bool
event_proc::matches(config &c)
{
vector<eps *>::const_iterator i;
for (i = _epsvec.begin(); i != _epsvec.end(); i++)
if (!(*i)->do_match(c))
return (false);
return (true);
}
bool
event_proc::run(config &c)
{
vector<eps *>::const_iterator i;
for (i = _epsvec.begin(); i != _epsvec.end(); i++)
if (!(*i)->do_action(c))
return (false);
return (true);
}
action::action(const char *cmd)
: _cmd(cmd)
{
// nothing
}
action::~action()
{
// nothing
}
bool
action::do_action(config &)
{
// this is lame because we don't expand variables.
// xxx
::system(_cmd.c_str());
return (true);
}
match::match(const char *var, const char *re)
: _var(var), _re(re)
{
// nothing
}
match::~match()
{
// nothing
}
bool
match::do_match(config &)
{
// XXX
return false;
}
const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
const string var_list::nothing = "";
const string &
var_list::get_variable(const string &var) const
{
map<string, string>::const_iterator i;
i = _vars.find(var);
if (i == _vars.end())
return var_list::bogus;
return (i->second);
}
bool
var_list::is_set(const string &var) const
{
return (_vars.find(var) != _vars.end());
}
void
var_list::set_variable(const string &var, const string &val)
{
_vars[var] = val;
}
void
config::reset(void)
{
_dir_list.clear();
_var_list_table.clear();
// XXX need to cleanup _{attach,detach,nomatch}_list
}
void
config::parse_one_file(const char *fn)
{
if (dflag)
printf("Parsing %s\n", fn);
yyin = fopen(fn, "r");
if (yyin == NULL)
err(1, "Cannot open config file %s", fn);
if (yyparse() != 0)
errx(1, "Cannot parse %s at line %d", fn, lineno);
fclose(yyin);
}
void
config::parse_files_in_dir(const char *dirname)
{
DIR *dirp;
struct dirent *dp;
char path[PATH_MAX];
if (dflag)
printf("Parsing files in %s\n", dirname);
dirp = opendir(dirname);
if (dirp == NULL)
return;
readdir(dirp); /* Skip . */
readdir(dirp); /* Skip .. */
while ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
snprintf(path, sizeof(path), "%s/%s",
dirname, dp->d_name);
parse_one_file(path);
}
}
}
void
config::parse(void)
{
vector<string>::const_iterator i;
parse_one_file(CF);
for (i = _dir_list.begin(); i != _dir_list.end(); i++)
parse_files_in_dir((*i).c_str());
}
void
config::drop_pidfile()
{
FILE *fp;
if (_pidfile == "")
return;
fp = fopen(_pidfile.c_str(), "w");
if (fp == NULL)
return;
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
void
config::add_attach(int prio, event_proc *p)
{
p->set_priority(prio);
_attach_list.push_back(p);
}
void
config::add_detach(int prio, event_proc *p)
{
p->set_priority(prio);
_detach_list.push_back(p);
}
void
config::add_directory(const char *dir)
{
_dir_list.push_back(string(dir));
}
void
config::add_nomatch(int prio, event_proc *p)
{
p->set_priority(prio);
_nomatch_list.push_back(p);
}
void
config::set_pidfile(const char *fn)
{
_pidfile = string(fn);
}
void
config::push_var_table()
{
var_list *vl;
vl = new var_list();
_var_list_table.push_back(vl);
}
void
config::pop_var_table()
{
delete _var_list_table.back();
_var_list_table.pop_back();
}
void
config::set_variable(const char *var, const char *val)
{
_var_list_table.back()->set_variable(var, val);
}
const string &
config::get_variable(const string &var)
{
vector<var_list *>::reverse_iterator i;
for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) {
if ((*i)->is_set(var))
return (var);
}
return (var_list::nothing);
}
const string &
config::expand_string(const string &)
{
return var_list::bogus;
}
static void
process_event(const char *buffer)
{
char type;
char cmd[1024];
char *sp;
// XXX should involve config
// XXX and set some variables
// XXX run the list and so forth
// Ignore unknown devices for now.
if (*buffer == '?')
return;
type = *buffer++;
sp = strchr(buffer, ' ');
if (sp == NULL)
return; /* Can't happen? */
*sp = '\0';
snprintf(cmd, sizeof(cmd), "/etc/devd-generic %s %s", buffer,
type == '+' ? "start" : "stop");
if (dflag)
printf("Trying '%s'\n", cmd);
system(cmd);
}
static void
event_loop(void)
{
int rv;
int fd;
char buffer[DEVCTL_MAXBUF];
fd = open(PATH_DEVCTL, O_RDONLY);
if (fd == -1)
err(1, "Can't open devctl");
if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
err(1, "Can't set close-on-exec flag");
while (1) {
if (romeo_must_die)
break;
rv = read(fd, buffer, sizeof(buffer) - 1);
if (rv > 0) {
buffer[rv] = '\0';
while (buffer[--rv] == '\n')
buffer[rv] = '\0';
process_event(buffer);
} else if (rv < 0) {
if (errno != EINTR)
break;
} else {
/* EOF */
break;
}
}
close(fd);
}
/*
* functions that the parser uses.
*/
void
add_attach(int prio, event_proc *p)
{
cfg.add_attach(prio, p);
}
void
add_detach(int prio, event_proc *p)
{
cfg.add_detach(prio, p);
}
void
add_directory(const char *dir)
{
cfg.add_directory(dir);
free(const_cast<char *>(dir));
}
void
add_nomatch(int prio, event_proc *p)
{
cfg.add_nomatch(prio, p);
}
event_proc *
add_to_event_proc(event_proc *ep, eps *eps)
{
if (ep == NULL)
ep = new event_proc();
ep->add(eps);
return (ep);
}
eps *
new_action(const char *cmd)
{
eps *e = new action(cmd);
free(const_cast<char *>(cmd));
return (e);
}
eps *
new_match(const char *var, const char *re)
{
eps *e = new match(var, re);
free(const_cast<char *>(var));
free(const_cast<char *>(re));
return (e);
}
void
set_pidfile(const char *name)
{
cfg.set_pidfile(name);
free(const_cast<char *>(name));
}
void
set_variable(const char *var, const char *val)
{
cfg.set_variable(var, val);
free(const_cast<char *>(var));
free(const_cast<char *>(val));
}
static void
gensighand(int)
{
romeo_must_die++;
_exit(0);
}
static void
usage()
{
fprintf(stderr, "usage: %s [-d]", getprogname());
exit(1);
}
/*
* main
*/
int
main(int argc, char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "d")) != -1) {
switch (ch) {
case 'd':
dflag++;
break;
default:
usage();
}
}
cfg.parse();
if (!dflag)
daemon(0, 0);
cfg.drop_pidfile();
signal(SIGHUP, gensighand);
signal(SIGINT, gensighand);
signal(SIGTERM, gensighand);
event_loop();
return (0);
}

View File

@ -1,21 +1,22 @@
// $FreeBSD$
//
// Refer to devd.conf(5) and devd(8) man pages for the details on how to
// run and configure devd.
//
# $FreeBSD$
#
# Refer to devd.conf(5) and devd(8) man pages for the details on how to
# run and configure devd.
#
// NB: All regular expressions have an implicit ^$ around them.
# NB: All regular expressions have an implicit ^$ around them.
# NB: device-name is shorthand for 'match device-name'
options {
// Each directory directive adds a directory the list of directories
// that we scan for files. Files are read-in in the order that they
// are returned from readdir(3). The rule-sets are combined to
// create a DFA that's used to match events to actions.
# Each directory directive adds a directory the list of directories
# that we scan for files. Files are read-in in the order that they
# are returned from readdir(3). The rule-sets are combined to
# create a DFA that's used to match events to actions.
directory "/etc/devd";
directory "/usr/local/etc/devd";
pid-file "/var/run/devd.pid";
// Setup some shorthand for regex that we use later in the file.
# Setup some shorthand for regex that we use later in the file.
set ethernet-nic-regex
"(an|ar|aue|awi|bge|cm|cnw|cs|cue|dc|de|ed|el|em|ep|ex|\
fe|fxp|gem|gx|hme|ie|kue|lge|lnc|my|nge|pcn|ray|rl|\
@ -25,15 +26,13 @@ options {
stg|sym|wds)[0-9]+";
};
// Note that the attach/detach with the highest value wins, so that one can
// override these general rules.
# Note that the attach/detach with the highest value wins, so that one can
# override these general rules.
// NB: device-name is shorthand for 'match device-name'
//
// For ethernet like devices, the default is to run dhclient. Due to
// a historical accident, the name of this script it called pccard_ether
//
#
# For ethernet like devices, the default is to run dhclient. Due to
# a historical accident, the name of this script it called pccard_ether
#
attach 0 {
device-name "$ethernet-nic-regex";
action "/etc/pccard_ether $device-name start";
@ -44,10 +43,10 @@ detach 0 {
action "/etc/pccard_ether $device-name stop";
};
// An entry like this might be in a different file, but is included here
// as an example of how to override things. Normally 'ed20' would match
// the above attach/detach stuff, but the value of 100 makes it
// ed20 is hard wired to 1.2.3.4
# An entry like this might be in a different file, but is included here
# as an example of how to override things. Normally 'ed20' would match
# the above attach/detach stuff, but the value of 100 makes it
# ed20 is hard wired to 1.2.3.4
attach 100 {
device-name "ed20";
action "ifconfig $device-name inet 1.2.3.4 netmask 0xffff0000";
@ -56,26 +55,26 @@ detach 100 {
device-name "ed20";
};
//
// Rescan scsi device-names on attach, but not detach.
//
#
# Rescan scsi device-names on attach, but not detach.
#
attach 0 {
device-name "$scsi-controller-regex";
action "camcontrol rescan all";
};
// Don't even try to second guess what to do about drivers that don't
// match here. Instead, pass it off to a smart script to deal.
# Don't even try to second guess what to do about drivers that don't
# match here. Instead, pass it off to a smart script to deal.
nomatch 0 {
action "/usr/local/bin/smart-loader $pnpinfo $location $bus";
};
// The following might be an example of something that a vendor might
// install if you were to add their device. This might reside in
// /usr/local/etc/devd/deqna.conf. A deqna is, in this hypothetical
// example, a pccard ethernet-like device. Students of history may
// know other devices by this name, and will get the in-jokes in this
// entry.
# The following might be an example of something that a vendor might
# install if you were to add their device. This might reside in
# /usr/local/etc/devd/deqna.conf. A deqna is, in this hypothetical
# example, a pccard ethernet-like device. Students of history may
# know other devices by this name, and will get the in-jokes in this
# entry.
nomatch 10 {
match "bus" "pccard[0-9]+";
match "manufacturer" "0x1234";
@ -90,3 +89,12 @@ detach 10 {
device-name "deqna[0-9]+";
action "/etc/pccard_ether $device-name stop";
};
// Single line comment, alternate style
/*
* This is a multiline comment
* 96
* 97
* 98
* 99 */
# error uncomment this line to test, should be line 100.

View File

@ -79,7 +79,7 @@ repated as often as required.
Further details on the syntax and meaning of each statement, and their
substatements is explained below.
.Pp
Comments may appear anywhere that whitespace may appear in a BIND
Comments may appear anywhere that whitespace may appear in a
configuration file. To appeal to programmers of all kinds, they can
be written in C, C++, or shell/perl constructs.
.Pp
@ -114,14 +114,6 @@ pair. For example:
// is a new comment, even though it is logically
// part of the previous comment.
.Ed
.Pp
.Em WARNING :
you cannot use the
.Li ;
(semicolon) character to start a comment such as you would in a zone
file. The semicolon indicates the end of a configuration statement,
so whatever follows it will be interpreted as the start of the next
statement.
.Sh FILES
.Bl -tag -width /etc/devd.conf -compact
.It Pa /etc/devd.conf

View File

@ -28,21 +28,22 @@
* $FreeBSD$
*/
#include <sys/queue.h>
int yylex(void);
void yyerror(const char *s);
int yyparse(void);
struct event_proc;
struct eps;
__BEGIN_DECLS
void add_attach(int, struct event_proc *);
void add_detach(int, struct event_proc *);
void add_directory(const char *);
void add_nomatch(int, struct event_proc *);
struct event_proc *add_to_event_proc(struct event_proc *, struct eps *);
struct eps *new_match(const char *, const char *);
struct eps *new_action(const char *);
void set_pidfile(const char *);
void set_variable(const char *, const char *);
void yyerror(const char *s);
int yylex(void);
int yyparse(void);
__END_DECLS
struct file_list
{
char *path;
TAILQ_ENTRY(file_list) fl_link;
};
TAILQ_HEAD(file_list_head, file_list);
extern struct file_list_head dirlist;
#define PATH_DEVCTL "/dev/devctl"
#define DEVCTL_MAXBUF 1025

View File

@ -31,12 +31,15 @@
#include "devd.h"
#include <stdio.h>
#include <string.h>
%}
%union {
char *str;
int i;
struct eps *eps; /* EventProcStatement */
struct event_proc *eventproc;
}
%token SEMICOLON BEGINBLOCK ENDBLOCK COMMA
@ -46,14 +49,14 @@
%token OPTIONS SET DIRECTORY PID_FILE DEVICE_NAME ACTION MATCH
%token ATTACH DETACH NOMATCH
%type <str> id
%type <i> number
%type <str> string
%type <eventproc> match_or_action_list
%type <eps> match_or_action match action
%%
config_file
: config_list
|
;
config_list
@ -83,32 +86,36 @@ option
;
directory_option
: DIRECTORY string SEMICOLON { add_directory($2); }
: DIRECTORY STRING SEMICOLON { add_directory($2); }
;
pid_file_option
: PID_FILE string SEMICOLON
: PID_FILE STRING SEMICOLON { set_pidfile($2); }
;
set_option
: SET id string SEMICOLON
: SET ID STRING SEMICOLON { set_variable($2, $3); }
;
attach_block
: ATTACH number BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
: ATTACH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
{ add_attach($2, $4); }
;
detach_block
: DETACH number BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
: DETACH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
{ add_detach($2, $4); }
;
nomatch_block
: NOMATCH number BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
: NOMATCH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
{ add_nomatch($2, $4); }
;
match_or_action_list
: match_or_action
: match_or_action { $$ = add_to_event_proc( NULL, $1); }
| match_or_action_list match_or_action
{ $$ = add_to_event_proc($1, $2); }
;
match_or_action
@ -117,21 +124,13 @@ match_or_action
;
match
: MATCH string string SEMICOLON
| DEVICE_NAME string SEMICOLON
: MATCH STRING STRING SEMICOLON { $$ = new_match($2, $3); }
| DEVICE_NAME STRING SEMICOLON
{ $$ = new_match(strdup("device-name"), $2); }
;
action
: ACTION string SEMICOLON
: ACTION STRING SEMICOLON { $$ = new_action($2); }
;
number
: NUMBER { $$ = $1; }
string
: STRING { $$ = $1; }
id
: ID { $$ = $1; }
%%

View File

@ -29,6 +29,7 @@
* $FreeBSD$
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
@ -38,6 +39,14 @@
int lineno = 1;
#define YY_NO_UNPUT
static void
update_lineno(const char *cp)
{
while (*cp)
if (*cp++ == '\n')
lineno++;
}
%}
%%
@ -45,17 +54,30 @@ int lineno = 1;
[ \t]+ ;
\n lineno++;
; { return SEMICOLON; }
#.*$ ;
\/\/.*$ ;
\/\*(.|\n)*\*\/ ;
\/\*(.|\n)*\*\/ { update_lineno(yytext); }
\{ { return BEGINBLOCK; }
\} { return ENDBLOCK; }
[0-9]+ { yylval.i = atoi(yytext); return NUMBER; }
\"[^"]+\" {
update_lineno(yytext);
int len = strlen(yytext) - 2;
char *walker;
int i;
if ((yylval.str = (char *) malloc(len + 1)) == NULL)
goto out;
memcpy(yylval.str, yytext + 1, len);
yylval.str[len] = '\0';
walker = yylval.str;
for (i = 1; i <= len; i++) {
if (yytext[i] == '\\' &&
yytext[i + 1] == '\n') {
i += 2;
while(isspace(yytext[i]))
i++;
}
*walker++ = yytext[i];
}
*walker++ = '\0';
out:;
return STRING;
}
@ -72,12 +94,7 @@ action { return ACTION; }
match { return MATCH; }
nomatch { return NOMATCH; }
[A-Za-z][A-Za-z0-9-]* {
int len = strlen(yytext);
if ((yylval.str = (char *) malloc(len + 1)) == NULL)
goto out2;
memcpy(yylval.str, yytext + 1, len);
yylval.str[len] = '\0';
out2:;
yylval.str = strdup(yytext);
return ID;
}
%%