From 71fc104fb0504b44de4e94b7b6f27d1082fead67 Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Fri, 3 Oct 2008 00:26:21 +0000 Subject: [PATCH] Welcome devctl(4) and devd(8). Obtained-from: FreeBSD --- etc/defaults/rc.conf | 4 +- etc/devd.conf | 282 ++++++++ etc/rc.d/Makefile | 4 +- etc/rc.d/devd | 23 + sbin/Makefile | 3 +- sbin/devd/Makefile | 21 + sbin/devd/devd.8 | 148 +++++ sbin/devd/devd.cc | 941 +++++++++++++++++++++++++++ sbin/devd/devd.conf.5 | 432 ++++++++++++ sbin/devd/devd.h | 59 ++ sbin/devd/devd.hh | 184 ++++++ sbin/devd/parse.y | 153 +++++ sbin/devd/token.l | 111 ++++ share/man/man4/Makefile | 3 +- share/man/man4/devctl.4 | 129 ++++ sys/dev/acpica5/acpi.c | 4 +- sys/dev/powermng/coretemp/coretemp.c | 4 +- sys/kern/subr_bus.c | 432 +++++++++++- sys/net/if.c | 9 +- sys/sys/bus.h | 12 +- 20 files changed, 2944 insertions(+), 14 deletions(-) create mode 100644 etc/devd.conf create mode 100644 etc/rc.d/devd create mode 100644 sbin/devd/Makefile create mode 100644 sbin/devd/devd.8 create mode 100644 sbin/devd/devd.cc create mode 100644 sbin/devd/devd.conf.5 create mode 100644 sbin/devd/devd.h create mode 100644 sbin/devd/devd.hh create mode 100644 sbin/devd/parse.y create mode 100644 sbin/devd/token.l create mode 100644 share/man/man4/devctl.4 diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 2b7f00f8b5..9e72ffeb1e 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -14,7 +14,7 @@ # All arguments must be in double or single quotes. # # $FreeBSD: src/etc/defaults/rc.conf,v 1.180 2003/06/26 09:50:50 smkelly Exp $ -# $DragonFly: src/etc/defaults/rc.conf,v 1.51 2008/08/30 16:40:17 hasso Exp $ +# $DragonFly: src/etc/defaults/rc.conf,v 1.52 2008/10/03 00:26:21 hasso Exp $ ############################################################## ### Important initial Boot-time options #################### @@ -405,6 +405,8 @@ lpd_program="/usr/sbin/lpd" # path to lpd, if you want a different one. lpd_flags="" # Flags to lpd (if enabled). usbd_enable="NO" # Run the usbd daemon. usbd_flags="" # Flags to usbd (if enabled). +devd_enable="NO" # Rund devd(8) daemon. +devd_flags="" # Flags to devd(8) (if enabled). dumpdev="NO" # Device name to crashdump to (or NO). dumpdir="/var/crash" # Directory where crash dumps are to be stored savecore_flags="" # Used if dumpdev is enabled above, and present. diff --git a/etc/devd.conf b/etc/devd.conf new file mode 100644 index 0000000000..adbcfc797e --- /dev/null +++ b/etc/devd.conf @@ -0,0 +1,282 @@ +# $FreeBSD: src/etc/devd.conf,v 1.42 2008/06/27 12:04:36 rpaulo Exp $ +# $DragonFly: src/etc/devd.conf,v 1.1 2008/10/03 00:26:20 hasso Exp $ +# +# 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: 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. + directory "/etc/devd"; + directory "/usr/local/etc/devd"; + + # Setup some shorthand for regex that we use later in the file. + #XXX Yes, these are gross -- imp + set scsi-controller-regex + "(aac|adv|adw|aha|ahb|ahc|ahd|aic|amd|amr|asr|bt|ciss|ct|dpt|\ + esp|ida|iir|ips|isp|mlx|mly|mpt|ncr|ncv|nsp|stg|sym|trm|wds)\ + [0-9]+"; +}; + +# Note that the attach/detach with the highest value wins, so that one can +# override these general rules. + +# +# Configure the interface on attach. Due to a historical accident, this +# script is called pccard_ether. +# +# notify 0 { +# match "system" "IFNET"; +# match "type" "ATTACH"; +# action "/etc/pccard_ether $subsystem start"; +# }; +# +# notify 0 { +# match "system" "IFNET"; +# match "type" "DETACH"; +# action "/etc/pccard_ether $subsystem stop"; +# }; + +# +# Try to start dhclient on Ethernet like interfaces when the link comes +# up. Only devices that are configured to support DHCP will actually +# run it. No link down rule exists because dhclient automaticly exits +# when the link goes down. +# +# notify 0 { +# match "system" "IFNET"; +# match "type" "LINK_UP"; +# media-type "ethernet"; +# action "/etc/rc.d/dhclient start $subsystem"; +# }; + +# +# Like Ethernet devices, but separate because +# they have a different media type. We may want +# to exploit this later. +# +# detach 0 { +# media-type "802.11"; +# action "/etc/pccard_ether $device-name stop"; +# }; +# attach 0 { +# media-type "802.11"; +# action "/etc/pccard_ether $device-name start"; +# }; +# notify 0 { +# match "system" "IFNET"; +# match "type" "LINK_UP"; +# media-type "802.11"; +# action "/etc/rc.d/dhclient start $subsystem"; +# }; + +# +# An entry like this might be in a different file, but is included here +# as an example of how to override things. Normally 'ed50' would match +# the above attach/detach stuff, but the value of 100 makes it +# hard wired to 1.2.3.4. +# attach 100 { +# device-name "ed50"; +# action "ifconfig $device-name inet 1.2.3.4 netmask 0xffff0000"; +# }; +# detach 100 { +# device-name "ed50"; +# }; + +# +# When a USB Bluetooth dongle appears activate it. +# XXX FIX for DragonFly XXX +# attach 100 { +# device-name "ubt[0-9]+"; +# action "/etc/rc.d/bluetooth start $device-name"; +# }; +# detach 100 { +# device-name "ubt[0-9]+"; +# action "/etc/rc.d/bluetooth stop $device-name"; +# }; + +# +# When a USB keyboard arrives, attach it as the console keyboard. +# XXX Fix for DragonFly XXX +# attach 100 { +# device-name "ukbd0"; +# action "/etc/rc.d/syscons setkeyboard /dev/ukbd0"; +# }; +# detach 100 { +# device-name "ukbd0"; +# action "/etc/rc.d/syscons setkeyboard /dev/kbd0"; +# }; +# +# attach 100 { +# device-name "ums[0-9]+"; +# action "/etc/rc.d/moused start $device-name"; +# }; +# +# detach 100 { +# device-name "ums[0-9]+"; +# action "/etc/rc.d/moused stop $device-name"; +# }; + +# +# Rescan scsi device-names on attach, but not detach. However, it is +# disabled by default due to reports of problems. +# +# 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 syslog. Commented out for the +# moment, as the pnpinfo variable isn't set in devd yet. Individual +# variables within the bus supplied pnpinfo are set. +# nomatch 0 { +# action "logger Unknown device: $pnpinfo $location $bus"; +#}; + +# +# Various logging of unknown devices. +# nomatch 10 { +# match "bus" "uhub[0-9]+"; +# action "logger Unknown USB device: vendor $vendor product $product \ +# bus $bus"; +# }; + +# +# Some PC-CARDs don't offer numerical manufacturer/product IDs, just +# show the CIS info there. +# nomatch 20 { +# match "bus" "pccard[0-9]+"; +# match "manufacturer" "0xffffffff"; +# match "product" "0xffffffff"; +# action "logger Unknown PCCARD device: CISproduct $cisproduct \ +# CIS-vendor $cisvendor bus $bus"; +# }; +# +# nomatch 10 { +# match "bus" "pccard[0-9]+"; +# action "logger Unknown PCCARD device: manufacturer $manufacturer \ +# product $product CISproduct $cisproduct CIS-vendor \ +# $cisvendor bus $bus"; +# }; +# +# nomatch 10 { +# match "bus" "cardbus[0-9]+"; +# action "logger Unknown Cardbus device: device $device class $class \ +# vendor $vendor bus $bus"; +# }; + +# +# Switch power profiles when the AC line state changes. +# notify 10 { +# match "system" "ACPI"; +# match "subsystem" "ACAD"; +# action "/etc/rc.d/power_profile $notify"; +# }; + +# +# Notify all users before beginning emergency shutdown when we get +# a _CRT or _HOT thermal event and we're going to power down the system +# very soon. +# notify 10 { +# match "system" "ACPI"; +# match "subsystem" "Thermal"; +# match "notify" "0xcc"; +# action "logger -p kern.emerg \ +# 'WARNING: system temperature too high, shutting down soon!'"; +#}; + +# +# User requested suspend, so perform preparation steps and then execute +# the actual suspend process. +# notify 10 { +# match "system" "ACPI"; +# match "subsystem" "Suspend"; +# action "/etc/rc.suspend acpi $notify"; +# }; +# notify 10 { +# match "system" "ACPI"; +# match "subsystem" "Resume"; +# action "/etc/rc.resume acpi $notify"; +# }; + +# +# The next blocks enable volume hotkeys that can be found on the Asus EeePC +# XXX ASUS-Eee subsystem isn't available in DragonFly +# notify 0 { +# match "system" "ACPI"; +# match "subsystem" "ASUS-Eee"; +# match "notify" "0x13"; +# action "mixer 0"; +# }; +# +# notify 0 { +# match "system" "ACPI"; +# match "subsystem" "ASUS-Eee"; +# match "notify" "0x14"; +# action "mixer vol -10"; +# }; +# +# notify 0 { +# match "system" "ACPI"; +# match "subsystem" "ASUS-Eee"; +# match "notify" "0x15"; +# action "mixer vol +10"; +# }; + +# +# 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"; +# match "product" "0x2323"; +# action "kldload if_deqna"; +# }; +# attach 10 { +# device-name "deqna[0-9]+"; +# action "/etc/pccard_ether $device-name start"; +# }; +# detach 10 { +# device-name "deqna[0-9]+"; +# action "/etc/pccard_ether $device-name stop"; +# }; + +# +# Examples of notify hooks. A notify is a generic way for a kernel +# subsystem to send event notification to userland. +# +# Here are some examples of ACPI notify handlers. ACPI subsystems that +# generate notifies include the AC adapter, power/sleep buttons, +# control method batteries, lid switch, and thermal zones. +# +# Information returned is not always the same as the ACPI notify +# events. See the ACPI specification for more information about +# notifies. Here is the information returned for each subsystem: +# +# ACAD: AC line state (0 is offline, 1 is online) +# Button: Button pressed (0 for power, 1 for sleep) +# CMBAT: ACPI battery events +# Lid: Lid state (0 is closed, 1 is open) +# Suspend, Resume: Suspend and resume notification +# Thermal: ACPI thermal zone events +# +# This example calls a script when the AC state changes, passing the +# notify value as the first argument. If the state is 0x00, it might +# call some sysctls to implement economy mode. If 0x01, it might set +# the mode to performance. +# notify 10 { +# match "system" "ACPI"; +# match "subsystem" "ACAD"; +# action "/etc/acpi_ac $notify"; +# }; diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index 934c470610..dd66bb4b37 100644 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -1,6 +1,6 @@ # $NetBSD: Makefile,v 1.16 2001/01/14 15:37:22 minoura Exp $ # $FreeBSD: src/etc/rc.d/Makefile,v 1.20 2003/06/29 05:15:57 mtm Exp $ -# $DragonFly: src/etc/rc.d/Makefile,v 1.29 2008/08/30 16:40:17 hasso Exp $ +# $DragonFly: src/etc/rc.d/Makefile,v 1.30 2008/10/03 00:26:21 hasso Exp $ .include @@ -9,7 +9,7 @@ FILES= DAEMON LOGIN NETWORKING SERVERS abi accounting addswap adjkerntz \ amd apm apmd atm1 atm2.sh atm3.sh \ battd bootconf bootparams btconfig bthcid ccd cleanvar \ - cleartmp cron dhclient diskless dmesg dumpon \ + cleartmp cron devd dhclient diskless dmesg dumpon \ fsck ftpd hostapd hostname \ inetd initdiskless initrandom ip6fw ipfilter ipfs ipfw ipmon \ ipnat ipsec ipxrouted isdnd jail \ diff --git a/etc/rc.d/devd b/etc/rc.d/devd new file mode 100644 index 0000000000..abecadd829 --- /dev/null +++ b/etc/rc.d/devd @@ -0,0 +1,23 @@ +#!/bin/sh +# +# $FreeBSD: src/etc/rc.d/devd,v 1.11 2008/07/16 19:50:29 dougb Exp $ +# $DragonFly: src/etc/rc.d/devd,v 1.1 2008/10/03 00:26:21 hasso Exp $ + +# PROVIDE: devd +# REQUIRE: netif network_ipv6 +# BEFORE: NETWORKING mountcritremote +# KEYWORD: nojail shutdown + +. /etc/rc.subr + +name="devd" +rcvar=`set_rcvar` +command="/sbin/${name}" + +load_rc_config $name +run_rc_command "$1" + +# If devd is disabled, turn it off in the kernel to avoid memory leaks. +if ! checkyesno ${rcvar}; then + sysctl hw.bus.devctl_disable=1 +fi diff --git a/sbin/Makefile b/sbin/Makefile index f1d000df5e..27fb6d9aed 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.5 (Berkeley) 3/31/94 # $FreeBSD: src/sbin/Makefile,v 1.77.2.9 2002/08/08 09:03:46 ru Exp $ -# $DragonFly: src/sbin/Makefile,v 1.19 2008/06/22 16:14:44 swildner Exp $ +# $DragonFly: src/sbin/Makefile,v 1.20 2008/10/03 00:26:21 hasso Exp $ # # XXX MISSING: icheck ncheck @@ -12,6 +12,7 @@ SUBDIR= adjkerntz \ ccdconfig \ clri \ comcontrol \ + devd \ dhclient \ disklabel \ disklabel64 \ diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile new file mode 100644 index 0000000000..d46eb7d3c9 --- /dev/null +++ b/sbin/devd/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD: src/sbin/devd/Makefile,v 1.9 2007/11/19 00:19:01 jb Exp $ +# $DragonFly: src/sbin/devd/Makefile,v 1.1 2008/10/03 00:26:21 hasso Exp $ + +PROG_CXX=devd +SRCS= devd.cc token.l parse.y y.tab.h +MAN= devd.8 devd.conf.5 + +WARNS= 0 +#WARNS?= 4 + +NO_SHARED?=YES + +DPADD= ${LIBL} ${LIBUTIL} +LDADD= -ll -lutil + +YFLAGS+=-v +CFLAGS+=-I. -I${.CURDIR} + +CLEANFILES= y.output + +.include diff --git a/sbin/devd/devd.8 b/sbin/devd/devd.8 new file mode 100644 index 0000000000..29b88a6481 --- /dev/null +++ b/sbin/devd/devd.8 @@ -0,0 +1,148 @@ +.\" +.\" 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. +.\" +.\" $FreeBSD: src/sbin/devd/devd.8,v 1.17 2006/09/17 22:49:26 ru Exp $ +.\" $DragonFly: src/sbin/devd/devd.8,v 1.1 2008/10/03 00:26:21 hasso Exp $ +.\" +.Dd November 24, 2005 +.Dt DEVD 8 +.Os +.Sh NAME +.Nm devd +.Nd "device state change daemon" +.Sh SYNOPSIS +.Nm +.Op Fl Ddn +.Op Fl f Ar file +.Sh DESCRIPTION +The +.Nm +daemon provides a way to have userland programs run when certain +kernel events happen. +.Pp +The following options are accepted. +.Bl -tag -width ".Fl f Ar file" +.It Fl D +Enable debugging messages. +.It Fl d +Run in the foreground instead of becoming a daemon. +.It Fl f Ar file +Use configuration file +.Ar file +instead of the default +.Pa /etc/devd.conf . +If option +.Fl f +is specified more than once, the last file specified is used. +.It Fl n +Do not process all pending events before becoming a daemon. +Instead, call daemon right away. +.El +.Sh IMPLEMENTATION NOTES +The +.Nm +utility +is a system daemon that runs in the background all the time. +Whenever a device is added to or removed from the device tree, +.Nm +will execute actions specified in +.Xr devd.conf 5 . +For example, +.Nm +might execute +.Xr dhclient 8 +when an Ethernet adapter is added to the system, and kill the +.Xr dhclient 8 +instance when the same adapter is removed. +Another example would be for +.Nm +to use a table to locate and load via +.Xr kldload 8 +the proper driver for an unrecognized device that is added to the system. +.Pp +The +.Nm +utility +hooks into the +.Xr devctl 4 +device driver. +This device driver has hooks into the device configuration system. +When nodes are added or deleted from the tree, this device will +deliver information about the event to +.Nm . +Once +.Nm +has parsed the message, it will search its action list for that kind +of event and perform the action with the highest matching value. +For most mundane uses, the default handlers are adequate. +However, for more advanced users, the power is present to tweak every +aspect of what happens. +.Pp +The +.Nm +utility +reads +.Pa /etc/devd.conf +or the alternate configuration file specified with a +.Fl f +option and uses that file to drive the rest of the process. +While the format of this file is described in +.Xr devd.conf 5 , +some basics are covered here. +In the +.Ic options +section, one can define multiple directories to search +for config files. +All files in these directories whose names match the pattern +.Pa *.conf +are parsed. +These files are intended to be installed by third party vendors that +wish to hook into the +.Nm +system without modifying the user's other +config files. +.Pp +All messages that +.Nm +receives are forwarded to the +.Ux +domain socket at +.Pa /var/run/devd.pipe . +.Sh FILES +.Bl -tag -width ".Pa /var/run/devd.pipe" -compact +.It Pa /etc/devd.conf +The default +.Nm +configuration file. +.It Pa /var/run/devd.pipe +The socket used by +.Nm +to communicate with its clients. +.El +.Sh SEE ALSO +.Xr devctl 4 , +.Xr devd.conf 5 +.Sh AUTHORS +.An M. Warner Losh diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc new file mode 100644 index 0000000000..4da7b6bbaf --- /dev/null +++ b/sbin/devd/devd.cc @@ -0,0 +1,941 @@ +/*- + * Copyright (c) 2002-2003 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. + * + * $FreeBSD: src/sbin/devd/devd.cc,v 1.33 2006/09/17 22:49:26 ru Exp $ + * $DragonFly: src/sbin/devd/devd.cc,v 1.1 2008/10/03 00:26:21 hasso Exp $ + */ + +/* + * DEVD control daemon. + */ + +// TODO list: +// o devd.conf and devd man pages need a lot of help: +// - devd needs to document the unix domain socket +// - devd.conf needs more details on the supported statements. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "devd.h" /* C compatible definitions */ +#include "devd.hh" /* C++ class definitions */ + +#define PIPE "/var/run/devd.pipe" +#define CF "/etc/devd.conf" +#define SYSCTL "hw.bus.devctl_disable" + +using namespace std; + +extern FILE *yyin; +extern int lineno; + +static const char notify = '!'; +static const char nomatch = '?'; +static const char attach = '+'; +static const char detach = '-'; + +static struct pidfh *pfh; + +int Dflag; +int dflag; +int nflag; +int romeo_must_die = 0; + +static const char *configfile = CF; + +static void event_loop(void); +static void usage(void); + +template void +delete_and_clear(vector &v) +{ + typename vector::const_iterator i; + + for (i = v.begin(); i != v.end(); i++) + delete *i; + v.clear(); +} + +config cfg; + +event_proc::event_proc() : _prio(-1) +{ + // nothing +} + +event_proc::~event_proc() +{ + delete_and_clear(_epsvec); +} + +void +event_proc::add(eps *eps) +{ + _epsvec.push_back(eps); +} + +bool +event_proc::matches(config &c) +{ + vector::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::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 &c) +{ + string s = c.expand_string(_cmd); + if (Dflag) + fprintf(stderr, "Executing '%s'\n", s.c_str()); + ::system(s.c_str()); + return (true); +} + +match::match(config &c, const char *var, const char *re) + : _var(var) +{ + string pattern = re; + _re = "^"; + _re.append(c.expand_string(string(re))); + _re.append("$"); + regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); +} + +match::~match() +{ + regfree(&_regex); +} + +bool +match::do_match(config &c) +{ + string value = c.get_variable(_var); + bool retval; + + if (Dflag) + fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(), + value.c_str(), _re.c_str()); + + retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); + return retval; +} + +#include +#include +#include + +media::media(config &, const char *var, const char *type) + : _var(var), _type(-1) +{ + static struct ifmedia_description media_types[] = { + { IFM_ETHER, "Ethernet" }, + { IFM_IEEE80211, "802.11" }, + { IFM_ATM, "ATM" }, + { IFM_CARP, "CARP" }, + { -1, "unknown" }, + { 0, NULL }, + }; + for (int i = 0; media_types[i].ifmt_string != NULL; i++) + if (strcasecmp(type, media_types[i].ifmt_string) == 0) { + _type = media_types[i].ifmt_word; + break; + } +} + +media::~media() +{ +} + +bool +media::do_match(config &c) +{ + string value; + struct ifmediareq ifmr; + bool retval; + int s; + + // Since we can be called from both a device attach/detach + // context where device-name is defined and what we want, + // as well as from a link status context, where subsystem is + // the name of interest, first try device-name and fall back + // to subsystem if none exists. + value = c.get_variable("device-name"); + if (value.length() == 0) + value = c.get_variable("subsystem"); + if (Dflag) + fprintf(stderr, "Testing media type of %s against 0x%x\n", + value.c_str(), _type); + + retval = false; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s >= 0) { + memset(&ifmr, 0, sizeof(ifmr)); + strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && + ifmr.ifm_status & IFM_AVALID) { + if (Dflag) + fprintf(stderr, "%s has media type 0x%x\n", + value.c_str(), IFM_TYPE(ifmr.ifm_active)); + retval = (IFM_TYPE(ifmr.ifm_active) == _type); + } else if (_type == -1) { + if (Dflag) + fprintf(stderr, "%s has unknown media type\n", + value.c_str()); + retval = true; + } + close(s); + } + + return retval; +} + +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::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) +{ + if (Dflag) + fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); + _vars[var] = val; +} + +void +config::reset(void) +{ + _dir_list.clear(); + delete_and_clear(_var_list_table); + delete_and_clear(_attach_list); + delete_and_clear(_detach_list); + delete_and_clear(_nomatch_list); + delete_and_clear(_notify_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); + lineno = 1; + 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); + } + } +} + +class epv_greater { +public: + int operator()(event_proc *const&l1, event_proc *const&l2) + { + return (l1->get_priority() > l2->get_priority()); + } +}; + +void +config::sort_vector(vector &v) +{ + sort(v.begin(), v.end(), epv_greater()); +} + +void +config::parse(void) +{ + vector::const_iterator i; + + parse_one_file(configfile); + for (i = _dir_list.begin(); i != _dir_list.end(); i++) + parse_files_in_dir((*i).c_str()); + sort_vector(_attach_list); + sort_vector(_detach_list); + sort_vector(_nomatch_list); + sort_vector(_notify_list); +} + +void +config::open_pidfile() +{ + if (pidfile(NULL)) + errx(1, "devd already running"); +} + +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::add_notify(int prio, event_proc *p) +{ + p->set_priority(prio); + _notify_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); + if (Dflag) + fprintf(stderr, "Pushing table\n"); +} + +void +config::pop_var_table() +{ + delete _var_list_table.back(); + _var_list_table.pop_back(); + if (Dflag) + fprintf(stderr, "Popping table\n"); +} + +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::reverse_iterator i; + + for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { + if ((*i)->is_set(var)) + return ((*i)->get_variable(var)); + } + return (var_list::nothing); +} + +bool +config::is_id_char(char ch) +{ + return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || + ch == '-')); +} + +void +config::expand_one(const char *&src, string &dst) +{ + int count; + string buffer, varstr; + + src++; + // $$ -> $ + if (*src == '$') { + dst.append(src++, 1); + return; + } + + // $(foo) -> $(foo) + // Not sure if I want to support this or not, so for now we just pass + // it through. + if (*src == '(') { + dst.append("$"); + count = 1; + /* If the string ends before ) is matched , return. */ + while (count > 0 && *src) { + if (*src == ')') + count--; + else if (*src == '(') + count++; + dst.append(src++, 1); + } + return; + } + + // ${^A-Za-z] -> $\1 + if (!isalpha(*src)) { + dst.append("$"); + dst.append(src++, 1); + return; + } + + // $var -> replace with value + do { + buffer.append(src++, 1); + } while (is_id_char(*src)); + buffer.append("", 1); + varstr = get_variable(buffer.c_str()); + dst.append(varstr); +} + +const string +config::expand_string(const string &s) +{ + const char *src; + string dst; + + src = s.c_str(); + while (*src) { + if (*src == '$') + expand_one(src, dst); + else + dst.append(src++, 1); + } + dst.append("", 1); + + return (dst); +} + +bool +config::chop_var(char *&buffer, char *&lhs, char *&rhs) +{ + char *walker; + + if (*buffer == '\0') + return (false); + walker = lhs = buffer; + while (is_id_char(*walker)) + walker++; + if (*walker != '=') + return (false); + walker++; // skip = + if (*walker == '"') { + walker++; // skip " + rhs = walker; + while (*walker && *walker != '"') + walker++; + if (*walker != '"') + return (false); + rhs[-2] = '\0'; + *walker++ = '\0'; + } else { + rhs = walker; + while (*walker && !isspace(*walker)) + walker++; + if (*walker != '\0') + *walker++ = '\0'; + rhs[-1] = '\0'; + } + while (isspace(*walker)) + walker++; + buffer = walker; + return (true); +} + + +char * +config::set_vars(char *buffer) +{ + char *lhs; + char *rhs; + + while (1) { + if (!chop_var(buffer, lhs, rhs)) + break; + set_variable(lhs, rhs); + } + return (buffer); +} + +void +config::find_and_execute(char type) +{ + vector *l; + vector::const_iterator i; + const char *s; + + switch (type) { + default: + return; + case notify: + l = &_notify_list; + s = "notify"; + break; + case nomatch: + l = &_nomatch_list; + s = "nomatch"; + break; + case attach: + l = &_attach_list; + s = "attach"; + break; + case detach: + l = &_detach_list; + s = "detach"; + break; + } + if (Dflag) + fprintf(stderr, "Processing %s event\n", s); + for (i = l->begin(); i != l->end(); i++) { + if ((*i)->matches(*this)) { + (*i)->run(*this); + break; + } + } + +} + + +static void +process_event(char *buffer) +{ + char type; + char *sp; + + sp = buffer + 1; + if (Dflag) + fprintf(stderr, "Processing event '%s'\n", buffer); + type = *buffer++; + cfg.push_var_table(); + // No match doesn't have a device, and the format is a little + // different, so handle it separately. + switch (type) { + case notify: + sp = cfg.set_vars(sp); + break; + case nomatch: + //? at location pnp-info on bus + sp = strchr(sp, ' '); + if (sp == NULL) + return; /* Can't happen? */ + *sp++ = '\0'; + if (strncmp(sp, "at ", 3) == 0) + sp += 3; + sp = cfg.set_vars(sp); + if (strncmp(sp, "on ", 3) == 0) + cfg.set_variable("bus", sp + 3); + break; + case attach: /*FALLTHROUGH*/ + case detach: + sp = strchr(sp, ' '); + if (sp == NULL) + return; /* Can't happen? */ + *sp++ = '\0'; + cfg.set_variable("device-name", buffer); + if (strncmp(sp, "at ", 3) == 0) + sp += 3; + sp = cfg.set_vars(sp); + if (strncmp(sp, "on ", 3) == 0) + cfg.set_variable("bus", sp + 3); + break; + } + + cfg.find_and_execute(type); + cfg.pop_var_table(); +} + +int +create_socket(const char *name) +{ + int fd, slen; + struct sockaddr_un sun; + + if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) + err(1, "socket"); + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); + slen = SUN_LEN(&sun); + unlink(name); + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + err(1, "fcntl"); + if (bind(fd, (struct sockaddr *) & sun, slen) < 0) + err(1, "bind"); + listen(fd, 4); + chown(name, 0, 0); /* XXX - root.wheel */ + chmod(name, 0666); + return (fd); +} + +list clients; + +void +notify_clients(const char *data, int len) +{ + list bad; + list::const_iterator i; + + for (i = clients.begin(); i != clients.end(); i++) { + if (write(*i, data, len) <= 0) { + bad.push_back(*i); + close(*i); + } + } + + for (i = bad.begin(); i != bad.end(); i++) + clients.erase(find(clients.begin(), clients.end(), *i)); +} + +void +new_client(int fd) +{ + int s; + + s = accept(fd, NULL, NULL); + if (s != -1) + clients.push_back(s); +} + +static void +event_loop(void) +{ + int rv; + int fd; + char buffer[DEVCTL_MAXBUF]; + int once = 0; + int server_fd, max_fd; + timeval tv; + fd_set fds; + + fd = open(PATH_DEVCTL, O_RDONLY); + if (fd == -1) + err(1, "Can't open devctl device %s", PATH_DEVCTL); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) + err(1, "Can't set close-on-exec flag on devctl"); + server_fd = create_socket(PIPE); + max_fd = max(fd, server_fd) + 1; + while (1) { + if (romeo_must_die) + break; + if (!once && !dflag && !nflag) { + // Check to see if we have any events pending. + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(fd, &fds); + rv = select(fd + 1, &fds, &fds, &fds, &tv); + // No events -> we've processed all pending events + if (rv == 0) { + if (Dflag) + fprintf(stderr, "Calling daemon\n"); + daemon(0, 0); + cfg.open_pidfile(); + once++; + } + } + FD_ZERO(&fds); + FD_SET(fd, &fds); + FD_SET(server_fd, &fds); + rv = select(max_fd, &fds, NULL, NULL, NULL); + if (rv == -1) { + if (errno == EINTR) + continue; + err(1, "select"); + } + if (FD_ISSET(fd, &fds)) { + rv = read(fd, buffer, sizeof(buffer) - 1); + if (rv > 0) { + notify_clients(buffer, rv); + buffer[rv] = '\0'; + while (buffer[--rv] == '\n') + buffer[rv] = '\0'; + process_event(buffer); + } else if (rv < 0) { + if (errno != EINTR) + break; + } else { + /* EOF */ + break; + } + } + if (FD_ISSET(server_fd, &fds)) + new_client(server_fd); + } + 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(dir)); +} + +void +add_nomatch(int prio, event_proc *p) +{ + cfg.add_nomatch(prio, p); +} + +void +add_notify(int prio, event_proc *p) +{ + cfg.add_notify(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(cmd)); + return (e); +} + +eps * +new_match(const char *var, const char *re) +{ + eps *e = new match(cfg, var, re); + free(const_cast(var)); + free(const_cast(re)); + return (e); +} + +eps * +new_media(const char *var, const char *re) +{ + eps *e = new media(cfg, var, re); + free(const_cast(var)); + free(const_cast(re)); + return (e); +} + +void +set_pidfile(const char *name) +{ + cfg.set_pidfile(name); + free(const_cast(name)); +} + +void +set_variable(const char *var, const char *val) +{ + cfg.set_variable(var, val); + free(const_cast(var)); + free(const_cast(val)); +} + + + +static void +gensighand(int) +{ + romeo_must_die++; + unlink("/var/run/devd.pid"); /* XXX */ + _exit(0); +} + +static void +usage() +{ + fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); + exit(1); +} + +static void +check_devd_enabled() +{ + int val = 0; + size_t len; + + len = sizeof(val); + if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) + errx(1, "devctl sysctl missing from kernel!"); + if (val) { + warnx("Setting " SYSCTL " to 0"); + val = 0; + sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); + } +} + +/* + * main + */ +int +main(int argc, char **argv) +{ + int ch; + + check_devd_enabled(); + while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { + switch (ch) { + case 'D': + Dflag++; + break; + case 'd': + dflag++; + break; + case 'f': + configfile = optarg; + break; + case 'n': + nflag++; + break; + default: + usage(); + } + } + + cfg.parse(); + if (!dflag && nflag) { + if (Dflag) + fprintf(stderr, "Calling daemon\n"); + daemon(0, 0); + cfg.open_pidfile(); + } + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, gensighand); + signal(SIGINT, gensighand); + signal(SIGTERM, gensighand); + event_loop(); + return (0); +} diff --git a/sbin/devd/devd.conf.5 b/sbin/devd/devd.conf.5 new file mode 100644 index 0000000000..4d06ff3103 --- /dev/null +++ b/sbin/devd/devd.conf.5 @@ -0,0 +1,432 @@ +.\" +.\" 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. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" 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. +.\" +.\" $FreeBSD: src/sbin/devd/devd.conf.5,v 1.11 2006/10/24 20:20:41 ru Exp $ +.\" $DragonFly: src/sbin/devd/devd.conf.5,v 1.1 2008/10/03 00:26:21 hasso Exp $ +.\" +.\" The section on comments was taken from named.conf.5, which has the +.\" following copyright: +.\" Copyright (c) 1999-2000 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +.\" ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +.\" CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +.\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +.\" SOFTWARE. +.\" +.Dd October 25, 2006 +.Dt DEVD.CONF 5 +.Os +.Sh NAME +.Nm devd.conf +.Nd configuration file for +.Xr devd 8 +.Sh DESCRIPTION +.Ss General Syntax +A +.Xr devd 8 +configuration consists of two general features, statements +and comments. +All statements end with a semicolon. +Many statements can contain substatements, which are also +terminated with a semicolon. +.Pp +The following statements are supported: +.Bl -tag -width ".Ic options" +.It Ic attach +Specifies various matching criteria and actions to perform when +a newly attached device matches said criteria. +.It Ic detach +Specifies various matching criteria and actions to perform when +a newly detached device matches said criteria. +.It Ic nomatch +Specifies various matching criteria and actions to perform when +no device driver currently loaded in the kernel claims a (new) +device. +.It Ic notify +Specifies various matching criteria and actions to perform when the kernel +sends an event notification to userland. +.It Ic options +Specifies various options and parameters for the operation of +.Xr devd 8 . +.El +.Pp +Statements may occur in any order in the configuration file, and may be +repeated as often as required. +Further details on the syntax and meaning of each statement and their +substatements are explained below. +.Pp +Each statement, except +.Ic options +has a priority (an arbitrary number) associated with it, where +.Ql 0 +is defined as the lowest priority. +If two statements match the same event, only the action of the statement with +highest priority will be executed. +In this way generic statements can be overridden for devices or +notifications that require special attention. +.Pp +The general syntax of a statement is: +.Pp +.Bd -literal -offset indent +statement priority { + substatement "value"; + ... + substatement "value"; +}; +.Ed +.Ss Sub-statements +The following sub-statements are supported within the +.Ic options +statement. +.Bl -tag -width ".Ic directory" +.It Ic directory Qq Ar /some/path ; +Adds the given directory to the list of directories from which +.Xr devd 8 +will read +configuration files. +Any number of +.Ic directory +statements can be used. +.\" .It Ic pid-file Qq Pa /var/run/devd.pid ; +.\" Specifies PID file. +.It Ic set Ar regexp-name Qq Ar (some|regexp) ; +Creates a regular expression and assigns it to the variable +.Ar regexp-name . +The variable is avaiable throughout the rest of +the configuration file. +All regular expressions have an implicit +.Ql ^$ +around them. +.El +.Pp +The following sub-statements are supported within the +.Ic attach +and +.Ic detach +statements. +.Bl -tag -width ".Ic directory" +.It Ic action Qq Ar command ; +Command to execute upon a successful match. +Example +.Dq Li "/etc/pccard_ether $device-name start" . +.It Ic class Qq Ar string ; +This is shorthand for +.Dq Ic match Qo Li class Qc Qq Ar string . +.It Ic device-name Qq string ; +This is shorthand for +.Dq Ic match Qo Li device-name Qc Qq Ar string . +This matches a device named +.Ar string , +which is allowed to be a regular expression or a variable previously created +containing a regular expression. +The +.Dq Li device-name +variable +is available for later use with the +.Ic action +statement. +.It Ic match Qo Ar variable Qc Qq Ar value ; +Matches the content of +.Ar value +against +.Ar variable ; +the content of +.Ar value +may be a regular expression. +Not required during +.Ic attach +nor +.Ic detach +events since the +.Ic device-name +statement takes care of all device matching. +For a partial list of variables, see below. +.It Ic media-type Qq Ar string ; +For network devices, +.Ic media-type +will match devices that have the given media type. +Valid media types are: +.Dq Li Ethernet , +.Dq Li 802.11 , +.Dq Li ATM , +and +.Dq Li CARP . +.It Ic subdevice Qq Ar string ; +This is shorthand for +.Dq Ic match Qo Li subdevice Qc Qq Ar string . +.El +.Pp +The following sub-statements are supported within the +.Ic nomatch +statement. +.Bl -tag -width ".Ic directory" +.It Ic action Qq Ar command ; +Same as above. +.It Ic match Qo Ar variable Qc Qq Ar value ; +Matches the content of +.Ar value +against +.Ar variable ; +the content of +.Ar value +may be a regular expression. +For a partial list of variables, see below. +.El +.Pp +The following sub-statements are supported within the +.Ic notify +statement. +The +.Dq Li notify +variable is avaiable inside this statement and contains, a value, depending +on which system and subsystem that delivered the event. +.Bl -tag -width ".Ic directory" +.It Ic action Qq Ar command ; +Command to execute upon a successful match. +Example +.Dq Li "/etc/rc.d/power_profile $notify" . +.It Ic match Qo Ar system | subsystem | type | notify Qc Qq Ar value ; +Any number of +.Ic match +statements can exist within a +.Ic notify +statement; +.Ar value +can be either a fixed string or a regular expression. +Below is a list of avaiable systems, subsystems, and types. +.It Ic media-type Qq Ar string ; +See above. +.El +.Ss Variables that can be used with the match statement +A partial list of variables and their possible values that can be used together +with the +.Ic match +statement. +.Pp +.Bl -tag -width ".Li manufacturer" -compact +.It Ic Variable +.Ic Description +.It Li bus +Device name of parent bus. +.It Li cisproduct +CIS-product. +.It Li cisvendor +CIS-vendor. +.It Li class +Device class. +.It Li device +Device ID. +.It Li device-name +Name of attached/detached device. +.It Li function +Card functions. +.It Li manufacturer +Manufacturer ID (pccard). +.It Li notify +Match the value of the +.Dq Li notify +variable. +.It Li product +Product ID (pccard). +.It Li serial +Serial Number (USB). +.It Li slot +Card slot. +.It Li subvendor +Sub-vendor ID. +.It Li subdevice +Sub-device ID. +.It Li subsystem +Matches a subsystem of a system, see below. +.It Li system +Matches a system type, see below. +.It Li type +Type of notification, see below. +.It Li vendor +Vendor ID. +.El +.Ss Notify matching +A partial list of systems, subsystems, and types used within the +.Ic notify +mechanism. +.Pp +.Bl -tag -width ".Li IFNET" -compact +.It Sy System +.It Li ACPI +Events related to the ACPI subsystem. +.Bl -tag -width ".Sy Subsystem" -compact +.It Sy Subsystem +.It Li ACAD +AC line state ($notify=0x00 is offline, 0x01 is online). +.It Li Button +Button state ($notify=0x00 is power, 0x01 is sleep). +.It Li CMBAT +Battery events. +.It Li Lid +Lid state ($notify=0x00 is closed, 0x01 is open). +.It Li Thermal +Thermal zone events. +.El +.Pp +.It Li IFNET +Events related to the network subsystem. +.Bl -tag -width ".Sy Subsystem" -compact +.It Sy Subsystem +.It Ar interface +The +.Dq subsystem +is the actual name of the network interface on which the event +took place. +.Bl -tag -width ".Li LINK_DOWN" -compact +.It Sy Type +.It Li LINK_UP +Carrier status changed to UP. +.It Li LINK_DOWN +Carrier status changed to DOWN. +.El +.El +.El +.Pp +A link state change to UP on the interface +.Dq Li fxp0 +would result in the following notify event: +.Bd -literal -offset indent +system=IFNET, subsystem=fxp0, type=LINK_UP +.Ed +.Pp +An AC line state change to +.Dq offline +would result in the following event: +.Bd -literal -offset indent +system=ACPI, subsystem=ACAD, notify=0x00 +.Ed +.Ss Comments +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 +C-style comments start with the two characters +.Ql /* +(slash, star) and end with +.Ql */ +(star, slash). +Because they are completely delimited with these characters, +they can be used to comment only a portion of a line or to span +multiple lines. +.Pp +C-style comments cannot be nested. +For example, the following is +not valid because the entire comment ends with the first +.Ql */ : +.Bd -literal -offset indent +/* This is the start of a comment. + This is still part of the comment. +/* This is an incorrect attempt at nesting a comment. */ + This is no longer in any comment. */ +.Ed +.Pp +C++-style comments start with the two characters +.Ql // +(slash, slash) and continue to the end of the physical line. +They cannot be continued across multiple physical lines; to have +one logical comment span multiple lines, each line must use the +.Ql // +pair. +For example: +.Bd -literal -offset indent +// This is the start of a comment. The next line +// is a new comment, even though it is logically +// part of the previous comment. +.Ed +.Sh FILES +.Bl -tag -width ".Pa /etc/devd.conf" -compact +.It Pa /etc/devd.conf +The +.Xr devd 8 +configuration file. +.El +.Sh EXAMPLES +.Bd -literal +# +# This will catch link down events on the interfaces fxp0 and ath0 +# +notify 0 { + match "system" "IFNET"; + match "subsystem" "(fxp0|ath0)"; + match "type" "LINK_DOWN"; + action "logger $subsystem is DOWN"; +}; + +# +# Match lid open/close events +# These can be combined to a single event, by passing the +# value of $notify to the external script. +# +notify 0 { + match "system" "ACPI"; + match "subsystem" "Lid"; + match "notify" "0x00"; + action "logger Lid closed, we can sleep now!"; +}; + +notify 0 { + match "system" "ACPI"; + match "subsystem" "Lid"; + match "notify" "0x01"; + action "logger Lid opened, the sleeper must awaken!"; +}; + +# +# Try to configure ath and wi devices with pccard_ether +# as they are attached. +# +attach 0 { + device-name "(ath|wi)[0-9]+"; + action "/etc/pccard_ether $device-name start"; +}; + +# +# Stop ath and wi devices as they are detached from +# the system. +# +detach 0 { + device-name "(ath|wi)[0-9]+"; + action "/etc/pccard_ether $device-name stop"; +}; +.Ed +.Pp +The installed +.Pa /etc/devd.conf +has many additional examples. +.Sh SEE ALSO +.Xr devd 8 diff --git a/sbin/devd/devd.h b/sbin/devd/devd.h new file mode 100644 index 0000000000..82395eb405 --- /dev/null +++ b/sbin/devd/devd.h @@ -0,0 +1,59 @@ +/*- + * DEVD (Device action daemon) + * + * 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. + * + * $FreeBSD: src/sbin/devd/devd.h,v 1.5 2005/07/10 03:37:15 imp Exp $ + * $DragonFly: src/sbin/devd/devd.h,v 1.1 2008/10/03 00:26:21 hasso Exp $ + */ + +#ifndef DEVD_H +#define DEVD_H + +/** @warning This file needs to be purely 'C' compatible. + */ +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 *); +void add_notify(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_media(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 + +#define PATH_DEVCTL "/dev/devctl" +#define DEVCTL_MAXBUF 1025 + +#endif /* DEVD_H */ diff --git a/sbin/devd/devd.hh b/sbin/devd/devd.hh new file mode 100644 index 0000000000..338b5d3d95 --- /dev/null +++ b/sbin/devd/devd.hh @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 2002-2003 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. + * + * $FreeBSD: src/sbin/devd/devd.hh,v 1.5 2007/12/21 01:00:04 imp Exp $ + * $DragonFly: src/sbin/devd/devd.hh,v 1.1 2008/10/03 00:26:21 hasso Exp $ + */ + +#ifndef DEVD_HH +#define DEVD_HH + +class config; + +/** + * var_list is a collection of variables. These collections of variables + * are stacked up and popped down for each event that we have to process. + * We have multiple levels so that we can push variables that are unique + * to the event in question, in addition to having global variables. This + * allows for future flexibility. + */ +class var_list +{ +public: + var_list() {} + virtual ~var_list() {} + /** Set a variable in this var list. + */ + void set_variable(const std::string &var, const std::string &val); + /** Get the variable out of this, and no other, var_list. If + * no variable of %var is set, then %bogus will be returned. + */ + const std::string &get_variable(const std::string &var) const; + /** Is there a variable of %var set in thi stable? + */ + bool is_set(const std::string &var) const; + /** A completely bogus string. + */ + static const std::string bogus; + static const std::string nothing; +private: + std::map _vars; +}; + +/** + * eps is short for event_proc_single. It is a single entry in an + * event_proc. Each keyword needs its own subclass from eps. + */ +class eps +{ +public: + eps() {} + virtual ~eps() {} + /** Does this eps match the current config? + */ + virtual bool do_match(config &) = 0; + /** Perform some action for this eps. + */ + virtual bool do_action(config &) = 0; +}; + +/** + * match is the subclass used to match an individual variable. Its + * actions are nops. + */ +class match : public eps +{ +public: + match(config &, const char *var, const char *re); + virtual ~match(); + virtual bool do_match(config &); + virtual bool do_action(config &) { return true; } +private: + std::string _var; + std::string _re; + regex_t _regex; +}; + +/** + * media is the subclass used to match an individual variable. Its + * actions are nops. + */ +class media : public eps +{ +public: + media(config &, const char *var, const char *type); + virtual ~media(); + virtual bool do_match(config &); + virtual bool do_action(config &) { return true; } +private: + std::string _var; + int _type; +}; + +/** + * action is used to fork a process. It matches everything. + */ +class action : public eps +{ +public: + action(const char *cmd); + virtual ~action(); + virtual bool do_match(config &) { return true; } + virtual bool do_action(config &); +private: + std::string _cmd; +}; + +class event_proc +{ +public: + event_proc(); + virtual ~event_proc(); + int get_priority() const { return (_prio); } + void set_priority(int prio) { _prio = prio; } + void add(eps *); + bool matches(config &); + bool run(config &); +private: + int _prio; + std::vector _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 add_notify(int, event_proc *); + void set_pidfile(const char *); + void reset(); + void parse(); + void open_pidfile(); + void write_pidfile(); + void remove_pidfile(); + void push_var_table(); + void pop_var_table(); + void set_variable(const char *var, const char *val); + const std::string &get_variable(const std::string &var); + const std::string expand_string(const std::string &var); + char *set_vars(char *); + void find_and_execute(char); +protected: + void sort_vector(std::vector &); + void parse_one_file(const char *fn); + void parse_files_in_dir(const char *dirname); + void expand_one(const char *&src, std::string &dst); + bool is_id_char(char); + bool chop_var(char *&buffer, char *&lhs, char *&rhs); +private: + std::vector _dir_list; + std::string _pidfile; + std::vector _var_list_table; + std::vector _attach_list; + std::vector _detach_list; + std::vector _nomatch_list; + std::vector _notify_list; +}; + +#endif /* DEVD_HH */ diff --git a/sbin/devd/parse.y b/sbin/devd/parse.y new file mode 100644 index 0000000000..2b32c9dd4b --- /dev/null +++ b/sbin/devd/parse.y @@ -0,0 +1,153 @@ +%{ +/*- + * DEVD (Device action daemon) + * + * 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. + * + * $FreeBSD: src/sbin/devd/parse.y,v 1.5 2005/07/10 03:37:15 imp Exp $ + * $DragonFly: src/sbin/devd/parse.y,v 1.1 2008/10/03 00:26:21 hasso Exp $ + */ + +#include "devd.h" +#include +#include + +%} + +%union { + char *str; + int i; + struct eps *eps; /* EventProcStatement */ + struct event_proc *eventproc; +} + +%token SEMICOLON BEGINBLOCK ENDBLOCK COMMA +%token NUMBER +%token STRING +%token ID +%token OPTIONS SET DIRECTORY PID_FILE DEVICE_NAME ACTION MATCH +%token ATTACH DETACH NOMATCH NOTIFY MEDIA_TYPE CLASS SUBDEVICE + +%type match_or_action_list +%type match_or_action match action + +%% + +config_file + : config_list + | + ; + +config_list + : config + | config_list config + ; + +config + : option_block + | attach_block + | detach_block + | nomatch_block + | notify_block + ; + +option_block + : OPTIONS BEGINBLOCK options ENDBLOCK SEMICOLON + ; + +options + : option + | options option + +option + : directory_option + | pid_file_option + | set_option + ; + +directory_option + : DIRECTORY STRING SEMICOLON { add_directory($2); } + ; + +pid_file_option + : PID_FILE STRING SEMICOLON { set_pidfile($2); } + ; + +set_option + : SET ID STRING SEMICOLON { set_variable($2, $3); } + ; + +attach_block + : ATTACH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON + { add_attach($2, $4); } + | ATTACH NUMBER BEGINBLOCK ENDBLOCK SEMICOLON + ; + +detach_block + : DETACH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON + { add_detach($2, $4); } + | DETACH NUMBER BEGINBLOCK ENDBLOCK SEMICOLON + ; + +nomatch_block + : NOMATCH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON + { add_nomatch($2, $4); } + | NOMATCH NUMBER BEGINBLOCK ENDBLOCK SEMICOLON + ; + +notify_block + : NOTIFY NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON + { add_notify($2, $4); } + | NOTIFY NUMBER BEGINBLOCK ENDBLOCK SEMICOLON + ; + +match_or_action_list + : 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 + : match + | action + ; + +match + : MATCH STRING STRING SEMICOLON { $$ = new_match($2, $3); } + | DEVICE_NAME STRING SEMICOLON + { $$ = new_match(strdup("device-name"), $2); } + | MEDIA_TYPE STRING SEMICOLON + { $$ = new_media(strdup("media-type"), $2); } + | CLASS STRING SEMICOLON + { $$ = new_match(strdup("class"), $2); } + | SUBDEVICE STRING SEMICOLON + { $$ = new_match(strdup("subdevice"), $2); } + ; + +action + : ACTION STRING SEMICOLON { $$ = new_action($2); } + ; + +%% diff --git a/sbin/devd/token.l b/sbin/devd/token.l new file mode 100644 index 0000000000..83cc337f1f --- /dev/null +++ b/sbin/devd/token.l @@ -0,0 +1,111 @@ +%{ +/*- + * DEVD (Device action daemon) + * + * 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. + * + * $FreeBSD: src/sbin/devd/token.l,v 1.7 2008/03/21 20:38:28 imp Exp $ + * $DragonFly: src/sbin/devd/token.l,v 1.1 2008/10/03 00:26:21 hasso Exp $ + */ + +#include +#include +#include +#include +#include "devd.h" +#include "y.tab.h" + +int lineno = 1; +#define YY_NO_UNPUT + +static void +update_lineno(const char *cp) +{ + while (*cp) + if (*cp++ == '\n') + lineno++; +} + +%} + +%% + +[ \t]+ ; +\n lineno++; +; { return SEMICOLON; } +#.*$ ; +\/\/.*$ ; +\/\*([^*]|(\*+([^*\/])))*\*+\/ { update_lineno(yytext); } +\{ { return BEGINBLOCK; } +\} { return ENDBLOCK; } +[0-9]+ { yylval.i = atoi(yytext); return NUMBER; } +\"[^"]+\" { + int len = strlen(yytext) - 2; + char *walker; + int i; + update_lineno(yytext); + if ((yylval.str = (char *) malloc(len + 1)) == NULL) + goto out; + 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; + } + + +options { return OPTIONS; } +set { return SET; } +directory { return DIRECTORY; } +pid-file { return PID_FILE; } +attach { return ATTACH; } +detach { return DETACH; } +device-name { return DEVICE_NAME; } +media-type { return MEDIA_TYPE; } +class { return CLASS; } +subdevice { return SUBDEVICE; } +action { return ACTION; } +match { return MATCH; } +nomatch { return NOMATCH; } +notify { return NOTIFY; } +[A-Za-z][A-Za-z0-9_-]* { + yylval.str = strdup(yytext); + return ID; + } +%% + +void +yyerror(const char *s) +{ + syslog(LOG_ERR, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s); +} diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 99ee3ed6d2..77cf212bbf 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/share/man/man4/Makefile,v 1.87 2008/08/28 10:32:27 hasso Exp $ +# $DragonFly: src/share/man/man4/Makefile,v 1.88 2008/10/03 00:26:21 hasso Exp $ MAN= aac.4 \ acpi.4 \ @@ -55,6 +55,7 @@ MAN= aac.4 \ dcons_crom.4 \ ddb.4 \ de.4 \ + devctl.4 \ disc.4 \ divert.4 \ dpt.4 \ diff --git a/share/man/man4/devctl.4 b/share/man/man4/devctl.4 new file mode 100644 index 0000000000..5b9ace3829 --- /dev/null +++ b/share/man/man4/devctl.4 @@ -0,0 +1,129 @@ +.\" +.\" 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. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" 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. +.\" +.\" $FreeBSD: src/share/man/man4/devctl.4,v 1.5 2006/08/04 07:56:32 yar Exp $ +.\" $DragonFly: src/share/man/man4/devctl.4,v 1.1 2008/10/03 00:26:21 hasso Exp $ +.\" +.Dd February 11, 2003 +.Dt DEVCTL 4 +.Os +.Sh NAME +.Nm devctl +.Nd "device event reporting and device control interface" +.Sh DESCRIPTION +The +.Nm +device is used to report device events from the kernel. +Future versions will allow for some device control as well. +.Sh IMPLEMENTATION NOTES +This design allows only one reader for +.Pa /dev/devctl . +This is not desirable +in the long run, but will get a lot of hair out of this implementation. +Maybe we should make this device a clonable device. +.Pp +Also note: we specifically do not attach a device to the +.Vt device_t +tree +to avoid potential chicken and egg problems. +One could argue that all of this belongs to the root node. +One could also further argue that the +.Xr sysctl 3 +interface that we have now might more properly be an +.Xr ioctl 2 +interface. +.Pp +.Dv SIGIO +support is included in the driver. +However, the author is not sure that the +.Dv SIGIO +support is done correctly. +It was copied from a driver that had +.Dv SIGIO +support that likely has not been +tested since +.Fx 3.4 +or +.Fx 2.2.8 ! +.Pp +The read channel for this device is used to report changes to +userland in realtime. +We return one record at a time. +If you try to read this device a character at a time, you will lose +the rest of the data. +Listening programs are expected to cope. +.Pp +The sysctl and boot parameter +.Va hw.bus.devctl_disable +is used to disable +.Nm +when no +.Xr devd 8 +is running. +.Sh PROTOCOL +The +.Nm +device +uses an +.Tn ASCII +protocol. +The driver returns one record at a time to its readers. +Each record is terminated with a newline. +The first character of the record is the event type. +.Pp +.Bl -column -compact "Type" "Description" +.Em "Type Description" +! A notify event, such as a link state change. ++ Device node in tree attached. +- Device node in tree detached. +? Unknown device detected. +.El +.Ss Message Formats +Except for the first character in the record, attach and detach +messages have the same format. +.Pp +.D1 Ar T Ns Ar dev Li at Ar parent Li on Ar location +.Pp +.Bl -column -compact "location" "Description" +.Em "Part Description" +.It Ar T Ta "+ or -" +.It Ar dev Ta "The device name that was attached/detached." +.It Ar parent Ta "The device name of the parent bus that attached the device." +.It Ar location Ta "Bus specific location information." +.El +.Pp +The nomatch messages can be used to load devices driver. +If you load a device driver, then one of two things can happen. +If the device driver attaches to something, you will get a device +attached message. +If it does not, then nothing will happen. +.Pp +The attach and detach messages arrive after the event. +This means one cannot use the attach message to load an alternate +driver. +The attach message driver has already claimed this device. +One cannot use the detach messages to flush data to the device. +The device is already gone. +.Sh SEE ALSO +.Xr devd 8 diff --git a/sys/dev/acpica5/acpi.c b/sys/dev/acpica5/acpi.c index 92893c17f2..16bb416b47 100644 --- a/sys/dev/acpica5/acpi.c +++ b/sys/dev/acpica5/acpi.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpi.c,v 1.160 2004/06/14 03:52:19 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.36 2008/09/05 10:28:35 hasso Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.37 2008/10/03 00:26:21 hasso Exp $ */ #include "opt_acpi.h" @@ -2758,9 +2758,7 @@ acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify) if (ACPI_FAILURE(status)) return; ksnprintf(notify_buf, sizeof(notify_buf), "notify=0x%02x", notify); -#if 0 devctl_notify("ACPI", subsystem, handle_buf.Pointer, notify_buf); -#endif AcpiOsFree(handle_buf.Pointer); } diff --git a/sys/dev/powermng/coretemp/coretemp.c b/sys/dev/powermng/coretemp/coretemp.c index abc3429b83..55d54e5a05 100644 --- a/sys/dev/powermng/coretemp/coretemp.c +++ b/sys/dev/powermng/coretemp/coretemp.c @@ -24,7 +24,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.2 2007/08/23 10:53:03 des Exp $ - * $DragonFly: src/sys/dev/powermng/coretemp/coretemp.c,v 1.2 2008/09/02 18:12:48 hasso Exp $ + * $DragonFly: src/sys/dev/powermng/coretemp/coretemp.c,v 1.3 2008/10/03 00:26:21 hasso Exp $ */ /* @@ -269,9 +269,7 @@ coretemp_get_temp(device_t dev) device_printf(dev, "critical temperature detected, " "suggest system shutdown\n"); ksnprintf(stemp, sizeof(stemp), "%d", temp); -#if 0 devctl_notify("coretemp", "Thermal", stemp, "notify=0xcc"); -#endif } return (temp); diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index cc308362cb..709f2b6cf5 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/kern/subr_bus.c,v 1.54.2.9 2002/10/10 15:13:32 jhb Exp $ - * $DragonFly: src/sys/kern/subr_bus.c,v 1.45 2008/09/30 12:20:29 hasso Exp $ + * $DragonFly: src/sys/kern/subr_bus.c,v 1.46 2008/10/03 00:26:21 hasso Exp $ */ #include "opt_bus.h" @@ -40,6 +40,14 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include /* for device_printf() */ @@ -100,6 +108,424 @@ static int do_async_attach = 0; static int numasyncthreads; TUNABLE_INT("kern.do_async_attach", &do_async_attach); +/* + * /dev/devctl implementation + */ + +/* + * This design allows only one reader for /dev/devctl. This is not desirable + * in the long run, but will get a lot of hair out of this implementation. + * Maybe we should make this device a clonable device. + * + * Also note: we specifically do not attach a device to the device_t tree + * to avoid potential chicken and egg problems. One could argue that all + * of this belongs to the root node. One could also further argue that the + * sysctl interface that we have not might more properly be an ioctl + * interface, but at this stage of the game, I'm not inclined to rock that + * boat. + * + * I'm also not sure that the SIGIO support is done correctly or not, as + * I copied it from a driver that had SIGIO support that likely hasn't been + * tested since 3.4 or 2.2.8! + */ + +static int sysctl_devctl_disable(SYSCTL_HANDLER_ARGS); +static int devctl_disable = 0; +TUNABLE_INT("hw.bus.devctl_disable", &devctl_disable); +SYSCTL_PROC(_hw_bus, OID_AUTO, devctl_disable, CTLTYPE_INT | CTLFLAG_RW, 0, 0, + sysctl_devctl_disable, "I", "devctl disable"); + +#define CDEV_MAJOR 188 + +static d_open_t devopen; +static d_close_t devclose; +static d_read_t devread; +static d_ioctl_t devioctl; +static d_poll_t devpoll; + +static struct dev_ops devctl_ops = { + { "devctl", CDEV_MAJOR, 0 }, + .d_open = devopen, + .d_close = devclose, + .d_read = devread, + .d_ioctl = devioctl, + .d_poll = devpoll, +}; + +struct dev_event_info +{ + char *dei_data; + TAILQ_ENTRY(dev_event_info) dei_link; +}; + +TAILQ_HEAD(devq, dev_event_info); + +static struct dev_softc +{ + int inuse; + int nonblock; + struct lock lock; + struct selinfo sel; + struct devq devq; + struct proc *async_proc; +} devsoftc; + +static void +devinit(void) +{ + dev_ops_add(&devctl_ops, -1, 0); + make_dev(&devctl_ops, 0, UID_ROOT, GID_WHEEL, 0600, "devctl"); + lockinit(&devsoftc.lock, "dev mtx", 0, 0); + TAILQ_INIT(&devsoftc.devq); +} + +static int +devopen(struct dev_open_args *ap) +{ + if (devsoftc.inuse) + return (EBUSY); + /* move to init */ + devsoftc.inuse = 1; + devsoftc.nonblock = 0; + devsoftc.async_proc = NULL; + return (0); +} + +static int +devclose(struct dev_close_args *ap) +{ + devsoftc.inuse = 0; + lockmgr(&devsoftc.lock, LK_EXCLUSIVE); + wakeup(&devsoftc); + lockmgr(&devsoftc.lock, LK_RELEASE); + + return (0); +} + +/* + * The read channel for this device is used to report changes to + * userland in realtime. We are required to free the data as well as + * the n1 object because we allocate them separately. Also note that + * we return one record at a time. If you try to read this device a + * character at a time, you will lose the rest of the data. Listening + * programs are expected to cope. + */ +static int +devread(struct dev_read_args *ap) +{ + struct uio *uio = ap->a_uio; + struct dev_event_info *n1; + int rv; + + lockmgr(&devsoftc.lock, LK_EXCLUSIVE); + while (TAILQ_EMPTY(&devsoftc.devq)) { + if (devsoftc.nonblock) { + lockmgr(&devsoftc.lock, LK_RELEASE); + return (EAGAIN); + } + crit_enter(); + tsleep_interlock(&devsoftc); + lockmgr(&devsoftc.lock, LK_RELEASE); + rv = tsleep(&devsoftc, PCATCH, "devctl", 0); + crit_exit(); + lockmgr(&devsoftc.lock, LK_EXCLUSIVE); + if (rv) { + /* + * Need to translate ERESTART to EINTR here? -- jake + */ + lockmgr(&devsoftc.lock, LK_RELEASE); + return (rv); + } + } + n1 = TAILQ_FIRST(&devsoftc.devq); + TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); + lockmgr(&devsoftc.lock, LK_RELEASE); + rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); + kfree(n1->dei_data, M_BUS); + kfree(n1, M_BUS); + return (rv); +} + +static int +devioctl(struct dev_ioctl_args *ap) +{ + switch (ap->a_cmd) { + + case FIONBIO: + if (*(int*)ap->a_data) + devsoftc.nonblock = 1; + else + devsoftc.nonblock = 0; + return (0); + case FIOASYNC: + if (*(int*)ap->a_data) + devsoftc.async_proc = curproc; + else + devsoftc.async_proc = NULL; + return (0); + + /* (un)Support for other fcntl() calls. */ + case FIOCLEX: + case FIONCLEX: + case FIONREAD: + case FIOSETOWN: + case FIOGETOWN: + default: + break; + } + return (ENOTTY); +} + +static int +devpoll(struct dev_poll_args *ap) +{ + int revents = 0; + + lockmgr(&devsoftc.lock, LK_EXCLUSIVE); + if (ap->a_events & (POLLIN | POLLRDNORM)) { + if (!TAILQ_EMPTY(&devsoftc.devq)) + revents = ap->a_events & (POLLIN | POLLRDNORM); + else + selrecord(curthread, &devsoftc.sel); + } + lockmgr(&devsoftc.lock, LK_RELEASE); + + ap->a_events = revents; + return (0); +} + +/** + * @brief Return whether the userland process is running + */ +boolean_t +devctl_process_running(void) +{ + return (devsoftc.inuse == 1); +} + +/** + * @brief Queue data to be read from the devctl device + * + * Generic interface to queue data to the devctl device. It is + * assumed that @p data is properly formatted. It is further assumed + * that @p data is allocated using the M_BUS malloc type. + */ +void +devctl_queue_data(char *data) +{ + struct dev_event_info *n1 = NULL; + struct proc *p; + + n1 = kmalloc(sizeof(*n1), M_BUS, M_NOWAIT); + if (n1 == NULL) + return; + n1->dei_data = data; + lockmgr(&devsoftc.lock, LK_EXCLUSIVE); + TAILQ_INSERT_TAIL(&devsoftc.devq, n1, dei_link); + wakeup(&devsoftc); + lockmgr(&devsoftc.lock, LK_RELEASE); + get_mplock(); /* XXX */ + selwakeup(&devsoftc.sel); + rel_mplock(); /* XXX */ + p = devsoftc.async_proc; + if (p != NULL) + ksignal(p, SIGIO); +} + +/** + * @brief Send a 'notification' to userland, using standard ways + */ +void +devctl_notify(const char *system, const char *subsystem, const char *type, + const char *data) +{ + int len = 0; + char *msg; + + if (system == NULL) + return; /* BOGUS! Must specify system. */ + if (subsystem == NULL) + return; /* BOGUS! Must specify subsystem. */ + if (type == NULL) + return; /* BOGUS! Must specify type. */ + len += strlen(" system=") + strlen(system); + len += strlen(" subsystem=") + strlen(subsystem); + len += strlen(" type=") + strlen(type); + /* add in the data message plus newline. */ + if (data != NULL) + len += strlen(data); + len += 3; /* '!', '\n', and NUL */ + msg = kmalloc(len, M_BUS, M_NOWAIT); + if (msg == NULL) + return; /* Drop it on the floor */ + if (data != NULL) + ksnprintf(msg, len, "!system=%s subsystem=%s type=%s %s\n", + system, subsystem, type, data); + else + ksnprintf(msg, len, "!system=%s subsystem=%s type=%s\n", + system, subsystem, type); + devctl_queue_data(msg); +} + +/* + * Common routine that tries to make sending messages as easy as possible. + * We allocate memory for the data, copy strings into that, but do not + * free it unless there's an error. The dequeue part of the driver should + * free the data. We don't send data when the device is disabled. We do + * send data, even when we have no listeners, because we wish to avoid + * races relating to startup and restart of listening applications. + * + * devaddq is designed to string together the type of event, with the + * object of that event, plus the plug and play info and location info + * for that event. This is likely most useful for devices, but less + * useful for other consumers of this interface. Those should use + * the devctl_queue_data() interface instead. + */ +static void +devaddq(const char *type, const char *what, device_t dev) +{ + char *data = NULL; + char *loc = NULL; + char *pnp = NULL; + const char *parstr; + + if (devctl_disable) + return; + data = kmalloc(1024, M_BUS, M_NOWAIT); + if (data == NULL) + goto bad; + + /* get the bus specific location of this device */ + loc = kmalloc(1024, M_BUS, M_NOWAIT); + if (loc == NULL) + goto bad; + *loc = '\0'; + bus_child_location_str(dev, loc, 1024); + + /* Get the bus specific pnp info of this device */ + pnp = kmalloc(1024, M_BUS, M_NOWAIT); + if (pnp == NULL) + goto bad; + *pnp = '\0'; + bus_child_pnpinfo_str(dev, pnp, 1024); + + /* Get the parent of this device, or / if high enough in the tree. */ + if (device_get_parent(dev) == NULL) + parstr = "."; /* Or '/' ? */ + else + parstr = device_get_nameunit(device_get_parent(dev)); + /* String it all together. */ + ksnprintf(data, 1024, "%s%s at %s %s on %s\n", type, what, loc, pnp, + parstr); + kfree(loc, M_BUS); + kfree(pnp, M_BUS); + devctl_queue_data(data); + return; +bad: + kfree(pnp, M_BUS); + kfree(loc, M_BUS); + kfree(data, M_BUS); + return; +} + +/* + * A device was added to the tree. We are called just after it successfully + * attaches (that is, probe and attach success for this device). No call + * is made if a device is merely parented into the tree. See devnomatch + * if probe fails. If attach fails, no notification is sent (but maybe + * we should have a different message for this). + */ +static void +devadded(device_t dev) +{ + char *pnp = NULL; + char *tmp = NULL; + + pnp = kmalloc(1024, M_BUS, M_NOWAIT); + if (pnp == NULL) + goto fail; + tmp = kmalloc(1024, M_BUS, M_NOWAIT); + if (tmp == NULL) + goto fail; + *pnp = '\0'; + bus_child_pnpinfo_str(dev, pnp, 1024); + ksnprintf(tmp, 1024, "%s %s", device_get_nameunit(dev), pnp); + devaddq("+", tmp, dev); +fail: + if (pnp != NULL) + kfree(pnp, M_BUS); + if (tmp != NULL) + kfree(tmp, M_BUS); + return; +} + +/* + * A device was removed from the tree. We are called just before this + * happens. + */ +static void +devremoved(device_t dev) +{ + char *pnp = NULL; + char *tmp = NULL; + + pnp = kmalloc(1024, M_BUS, M_NOWAIT); + if (pnp == NULL) + goto fail; + tmp = kmalloc(1024, M_BUS, M_NOWAIT); + if (tmp == NULL) + goto fail; + *pnp = '\0'; + bus_child_pnpinfo_str(dev, pnp, 1024); + ksnprintf(tmp, 1024, "%s %s", device_get_nameunit(dev), pnp); + devaddq("-", tmp, dev); +fail: + if (pnp != NULL) + kfree(pnp, M_BUS); + if (tmp != NULL) + kfree(tmp, M_BUS); + return; +} + +/* + * Called when there's no match for this device. This is only called + * the first time that no match happens, so we don't keep getitng this + * message. Should that prove to be undesirable, we can change it. + * This is called when all drivers that can attach to a given bus + * decline to accept this device. Other errrors may not be detected. + */ +static void +devnomatch(device_t dev) +{ + devaddq("?", "", dev); +} + +static int +sysctl_devctl_disable(SYSCTL_HANDLER_ARGS) +{ + struct dev_event_info *n1; + int dis, error; + + dis = devctl_disable; + error = sysctl_handle_int(oidp, &dis, 0, req); + if (error || !req->newptr) + return (error); + lockmgr(&devsoftc.lock, LK_EXCLUSIVE); + devctl_disable = dis; + if (dis) { + while (!TAILQ_EMPTY(&devsoftc.devq)) { + n1 = TAILQ_FIRST(&devsoftc.devq); + TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); + kfree(n1->dei_data, M_BUS); + kfree(n1, M_BUS); + } + } + lockmgr(&devsoftc.lock, LK_RELEASE); + return (0); +} + +/* End of /dev/devctl code */ + TAILQ_HEAD(,device) bus_data_devices; static int bus_data_generation = 1; @@ -1167,6 +1593,7 @@ device_probe_and_attach(device_t dev) if (error) { if (!(dev->flags & DF_DONENOMATCH)) { BUS_PROBE_NOMATCH(bus, dev); + devnomatch(dev); dev->flags |= DF_DONENOMATCH; } return(error); @@ -1239,6 +1666,7 @@ device_doattach(device_t dev) dev->state = DS_ATTACHED; if (bootverbose && !device_is_quiet(dev)) device_print_child(bus, dev); + devadded(dev); } else { kprintf("device_probe_and_attach: %s%d attach returned %d\n", dev->driver->name, dev->unit, error); @@ -1264,6 +1692,7 @@ device_detach(device_t dev) if ((error = DEVICE_DETACH(dev)) != 0) return(error); + devremoved(dev); device_printf(dev, "detached\n"); if (dev->parent) BUS_CHILD_DETACHED(dev->parent, dev); @@ -2541,6 +2970,7 @@ root_bus_module_handler(module_t mod, int what, void* arg) root_bus->driver = &root_driver; root_bus->state = DS_ALIVE; root_devclass = devclass_find_internal("root", NULL, FALSE); + devinit(); return(0); case MOD_SHUTDOWN: diff --git a/sys/net/if.c b/sys/net/if.c index 502d3475db..808be96624 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -32,7 +32,7 @@ * * @(#)if.c 8.3 (Berkeley) 1/4/94 * $FreeBSD: src/sys/net/if.c,v 1.185 2004/03/13 02:35:03 brooks Exp $ - * $DragonFly: src/sys/net/if.c,v 1.80 2008/09/23 11:28:49 sephe Exp $ + * $DragonFly: src/sys/net/if.c,v 1.81 2008/10/03 00:26:21 hasso Exp $ */ #include "opt_compat.h" @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -523,6 +524,7 @@ if_attach(struct ifnet *ifp, lwkt_serialize_t serializer) ifa_iflink(ifa, ifp, 0 /* Insert head */); EVENTHANDLER_INVOKE(ifnet_attach_event, ifp); + devctl_notify("IFNET", ifp->if_xname, "ATTACH", NULL); ifq = &ifp->if_snd; ifq->altq_type = 0; @@ -713,6 +715,7 @@ if_detach(struct ifnet *ifp) /* Announce that the interface is gone. */ rt_ifannouncemsg(ifp, IFAN_DEPARTURE); + devctl_notify("IFNET", ifp->if_xname, "DETACH", NULL); SLIST_FOREACH(dp, &domains, dom_next) if (dp->dom_ifdetach && ifp->if_afdata[dp->dom_family]) @@ -1077,7 +1080,11 @@ if_up(struct ifnet *ifp) void if_link_state_change(struct ifnet *ifp) { + int link_state = ifp->if_link_state; + rt_ifmsg(ifp); + devctl_notify("IFNET", ifp->if_xname, + (link_state == LINK_STATE_UP) ? "LINK_UP" : "LINK_DOWN", NULL); } /* diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 01b0eae61d..40d230bd61 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/sys/bus.h,v 1.30.2.5 2004/03/17 17:54:25 njl Exp $ - * $DragonFly: src/sys/sys/bus.h,v 1.30 2008/09/30 12:20:29 hasso Exp $ + * $DragonFly: src/sys/sys/bus.h,v 1.31 2008/10/03 00:26:21 hasso Exp $ */ #ifndef _SYS_BUS_H_ @@ -143,6 +143,16 @@ SLIST_HEAD(resource_list, resource_list_entry); #endif /* _KERNEL || _KERNEL_STRUCTURES */ #ifdef _KERNEL +/** + * devctl hooks. Typically one should use the devctl_notify + * hook to send the message. However, devctl_queue_data is also + * included in case devctl_notify isn't sufficiently general. + */ +boolean_t devctl_process_running(void); +void devctl_notify(const char *__system, const char *__subsystem, + const char *__type, const char *__data); +void devctl_queue_data(char *__data); + /* * Initialise a resource list. */ -- 2.41.0