summaryrefslogtreecommitdiff
path: root/src/udev
diff options
context:
space:
mode:
Diffstat (limited to 'src/udev')
-rw-r--r--src/udev/.gitignore2
-rw-r--r--src/udev/accelerometer/accelerometer.c6
-rw-r--r--src/udev/ata_id/ata_id.c2
-rw-r--r--src/udev/cdrom_id/cdrom_id.c28
-rw-r--r--src/udev/collect/collect.c8
l---------src/udev/mtd_probe/Makefile1
-rw-r--r--src/udev/mtd_probe/mtd_probe.c4
-rw-r--r--src/udev/mtd_probe/mtd_probe.h6
-rw-r--r--src/udev/net/ethtool-util.c3
-rw-r--r--src/udev/net/link-config.c30
-rw-r--r--src/udev/net/link-config.h5
-rw-r--r--src/udev/scsi_id/scsi_serial.c1
-rw-r--r--src/udev/udev-builtin-blkid.c4
-rw-r--r--src/udev/udev-builtin-btrfs.c4
-rw-r--r--src/udev/udev-builtin-hwdb.c6
-rw-r--r--src/udev/udev-builtin-input_id.c175
-rw-r--r--src/udev/udev-builtin-keyboard.c251
-rw-r--r--src/udev/udev-builtin-kmod.c5
-rw-r--r--src/udev/udev-builtin-net_id.c58
-rw-r--r--src/udev/udev-builtin-path_id.c30
-rw-r--r--src/udev/udev-builtin-uaccess.c7
-rw-r--r--src/udev/udev-builtin-usb_id.c68
-rw-r--r--src/udev/udev-builtin.c11
-rw-r--r--src/udev/udev-ctrl.c75
-rw-r--r--src/udev/udev-event.c266
-rw-r--r--src/udev/udev-node.c5
-rw-r--r--src/udev/udev-rules.c111
-rw-r--r--src/udev/udev-watch.c4
-rw-r--r--src/udev/udev.h13
-rw-r--r--src/udev/udevadm-control.c5
-rw-r--r--src/udev/udevadm-hwdb.c1
-rw-r--r--src/udev/udevadm-info.c17
-rw-r--r--src/udev/udevadm-monitor.c8
-rw-r--r--src/udev/udevadm-settle.c15
-rw-r--r--src/udev/udevadm-test-builtin.c10
-rw-r--r--src/udev/udevadm-test.c20
-rw-r--r--src/udev/udevadm-trigger.c7
-rw-r--r--src/udev/udevadm.c3
-rw-r--r--src/udev/udevd.c1623
-rw-r--r--src/udev/v4l_id/v4l_id.c19
40 files changed, 1632 insertions, 1285 deletions
diff --git a/src/udev/.gitignore b/src/udev/.gitignore
index a229430e36..ba112ce218 100644
--- a/src/udev/.gitignore
+++ b/src/udev/.gitignore
@@ -2,4 +2,4 @@
/keyboard-keys-from-name.gperf
/keyboard-keys-from-name.h
/keyboard-keys-to-name.h
-/keyboard-keys.txt
+/keyboard-keys-list.txt
diff --git a/src/udev/accelerometer/accelerometer.c b/src/udev/accelerometer/accelerometer.c
index 0f1b3c6ec3..9e2c590c15 100644
--- a/src/udev/accelerometer/accelerometer.c
+++ b/src/udev/accelerometer/accelerometer.c
@@ -46,16 +46,10 @@
#include <stdio.h>
#include <string.h>
-#include <stdbool.h>
#include <math.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
#include <stdlib.h>
-#include <unistd.h>
#include <getopt.h>
#include <limits.h>
-#include <linux/limits.h>
#include <linux/input.h>
#include "libudev.h"
diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
index 9e4f674a9e..cc1bf45ae9 100644
--- a/src/udev/ata_id/ata_id.c
+++ b/src/udev/ata_id/ata_id.c
@@ -496,7 +496,7 @@ int main(int argc, char *argv[])
}
}
- memcpy (model, id.model, 40);
+ memcpy(model, id.model, 40);
model[40] = '\0';
udev_util_encode_string(model, model_enc, sizeof(model_enc));
util_replace_whitespace((char *) id.model, model, 40);
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 6052f6abd8..3d74ae50f1 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -36,6 +36,7 @@
#include "libudev.h"
#include "libudev-private.h"
+#include "random-util.h"
/* device info */
static unsigned int cd_cd_rom;
@@ -650,8 +651,8 @@ static int cd_media_info(struct udev *udev, int fd)
* write protected; we need to check the contents if it is blank */
if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) {
unsigned char buffer[32 * 2048];
- unsigned char result, len;
- int block, offset;
+ unsigned char len;
+ int offset;
if (cd_media_dvd_ram) {
/* a write protected dvd-ram may report "complete" status */
@@ -732,22 +733,23 @@ static int cd_media_info(struct udev *udev, int fd)
/* if any non-zero data is found in sector 16 (iso and udf) or
* eventually 0 (fat32 boot sector, ext2 superblock, etc), disc
* is assumed non-blank */
- result = 0;
- for (block = 32768; block >= 0 && !result; block -= 32768) {
- offset = block;
- while (offset < (block + 2048) && !result) {
- result = buffer [offset];
- offset++;
+ for (offset = 32768; offset < (32768 + 2048); offset++) {
+ if (buffer [offset]) {
+ log_debug("data in block 16, assuming complete");
+ goto determined;
}
}
- if (!result) {
- cd_media_state = media_status[0];
- log_debug("no data in blocks 0 or 16, assuming blank");
- } else {
- log_debug("data in blocks 0 or 16, assuming complete");
+ for (offset = 0; offset < 2048; offset++) {
+ if (buffer [offset]) {
+ log_debug("data in block 0, assuming complete");
+ goto determined;
+ }
}
+
+ cd_media_state = media_status[0];
+ log_debug("no data in blocks 0 or 16, assuming blank");
}
determined:
diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c
index 4bb6edbef1..6cf41c67bb 100644
--- a/src/udev/collect/collect.c
+++ b/src/udev/collect/collect.c
@@ -20,18 +20,10 @@
*/
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "libudev.h"
#include "libudev-private.h"
#include "macro.h"
diff --git a/src/udev/mtd_probe/Makefile b/src/udev/mtd_probe/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/udev/mtd_probe/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/udev/mtd_probe/mtd_probe.c b/src/udev/mtd_probe/mtd_probe.c
index 13c757bd1c..67b750c4b3 100644
--- a/src/udev/mtd_probe/mtd_probe.c
+++ b/src/udev/mtd_probe/mtd_probe.c
@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
-#include "mtd_probe.h"
+
#include <stdio.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
@@ -26,6 +26,8 @@
#include <unistd.h>
#include <stdlib.h>
+#include "mtd_probe.h"
+
int main(int argc, char** argv)
{
int mtd_fd;
diff --git a/src/udev/mtd_probe/mtd_probe.h b/src/udev/mtd_probe/mtd_probe.h
index d99be9addc..caea5c2693 100644
--- a/src/udev/mtd_probe/mtd_probe.h
+++ b/src/udev/mtd_probe/mtd_probe.h
@@ -21,6 +21,8 @@
#include <mtd/mtd-user.h>
+#include "macro.h"
+
/* Full oob structure as written on the flash */
struct sm_oob {
uint32_t reserved;
@@ -30,8 +32,7 @@ struct sm_oob {
uint8_t ecc2[3];
uint8_t lba_copy2[2];
uint8_t ecc1[3];
-} __attribute__((packed));
-
+} _packed_;
/* one sector is always 512 bytes, but it can consist of two nand pages */
#define SM_SECTOR_SIZE 512
@@ -47,5 +48,4 @@ struct sm_oob {
#define SM_SMALL_PAGE 256
#define SM_SMALL_OOB_SIZE 8
-
void probe_smart_media(int mtd_fd, mtd_info_t *info);
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index ec67126b21..927b8abc64 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -93,8 +93,7 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
return 0;
}
-int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex)
-{
+int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) {
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_GSET
};
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 8b3dc45d4e..5610b2808e 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -22,7 +22,6 @@
#include <netinet/ether.h>
#include <linux/netdevice.h>
-#include "sd-id128.h"
#include "missing.h"
#include "link-config.h"
@@ -36,11 +35,9 @@
#include "path-util.h"
#include "conf-parser.h"
#include "conf-files.h"
-#include "fileio.h"
-#include "hashmap.h"
#include "rtnl-util.h"
#include "network-internal.h"
-#include "siphash24.h"
+#include "random-util.h"
struct link_config_ctx {
LIST_HEAD(link_config, links);
@@ -70,9 +67,9 @@ static void link_config_free(link_config *link) {
free(link->filename);
free(link->match_mac);
- free(link->match_path);
- free(link->match_driver);
- free(link->match_type);
+ strv_free(link->match_path);
+ strv_free(link->match_driver);
+ strv_free(link->match_type);
free(link->match_name);
free(link->match_host);
free(link->match_virt);
@@ -177,6 +174,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
else
log_debug("Parsed configuration file %s", filename);
+ if (link->mtu > UINT_MAX || link->speed > UINT_MAX)
+ return -ERANGE;
+
link->filename = strdup(filename);
LIST_PREPEND(links, ctx->links, link);
@@ -240,6 +240,10 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
link_config **ret) {
link_config *link;
+ assert(ctx);
+ assert(device);
+ assert(ret);
+
LIST_FOREACH(links, link, ctx->links) {
const char* attr_value;
@@ -259,7 +263,7 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
attr_value = udev_device_get_sysattr_value(device, "name_assign_type");
if (attr_value)
- (void)safe_atou8(attr_value, &name_assign_type);
+ (void) safe_atou8(attr_value, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM) {
log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'",
@@ -379,10 +383,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (!old_name)
return -EINVAL;
- r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024,
- config->duplex);
+ r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
if (r < 0)
- log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
+ log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m",
old_name, config->speed / 1024,
duplex_to_string(config->duplex));
@@ -461,8 +464,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
mac = config->mac;
}
- r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac,
- config->mtu);
+ r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu);
if (r < 0)
return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name);
@@ -473,7 +475,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
const char *name;
- char *driver;
+ char *driver = NULL;
int r;
name = udev_device_get_sysname(device);
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index cb434d1aee..9875057e84 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -23,7 +23,6 @@
#include "ethtool-util.h"
#include "condition.h"
-#include "util.h"
#include "list.h"
#include "libudev.h"
@@ -67,8 +66,8 @@ struct link_config {
NamePolicy *name_policy;
char *name;
char *alias;
- unsigned int mtu;
- unsigned int speed;
+ size_t mtu;
+ size_t speed;
Duplex duplex;
WakeOnLan wol;
diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c
index dcfff1d4ea..3691a69d48 100644
--- a/src/udev/scsi_id/scsi_serial.c
+++ b/src/udev/scsi_id/scsi_serial.c
@@ -37,6 +37,7 @@
#include "libudev-private.h"
#include "scsi.h"
#include "scsi_id.h"
+#include "random-util.h"
/*
* A priority based list of id, naa, and binary/ascii for the identifier
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index 03e3dc2867..1dad4476f3 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -20,8 +20,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
@@ -265,7 +263,7 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC);
if (fd < 0) {
- fprintf(stderr, "error: %s: %m\n", udev_device_get_devnode(dev));
+ err = log_debug_errno(errno, "Failure opening block device %s: %m", udev_device_get_devnode(dev));
goto out;
}
diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c
index 3643596a70..3352821567 100644
--- a/src/udev/udev-builtin-btrfs.c
+++ b/src/udev/udev-builtin-btrfs.c
@@ -17,12 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
#include <fcntl.h>
-#include <errno.h>
#include <sys/ioctl.h>
#ifdef HAVE_LINUX_BTRFS_H
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index 95476648f9..7dfc74e6fa 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -18,10 +18,6 @@
***/
#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <inttypes.h>
-#include <ctype.h>
#include <stdlib.h>
#include <fnmatch.h>
#include <getopt.h>
@@ -89,6 +85,8 @@ static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device
bool last = false;
int r = 0;
+ assert(dev);
+
for (d = srcdev; d && !last; d = udev_device_get_parent(d)) {
const char *dsubsys;
const char *modalias = NULL;
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index 46f1c539d2..e3fa4bc162 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -126,77 +126,122 @@ static void get_cap_mask(struct udev_device *dev,
}
/* pointer devices */
-static void test_pointers (struct udev_device *dev,
- const unsigned long* bitmask_ev,
- const unsigned long* bitmask_abs,
- const unsigned long* bitmask_key,
- const unsigned long* bitmask_rel,
- bool test) {
- int is_mouse = 0;
- int is_touchpad = 0;
-
- if (!test_bit (EV_KEY, bitmask_ev)) {
- if (test_bit (EV_ABS, bitmask_ev) &&
- test_bit (ABS_X, bitmask_abs) &&
- test_bit (ABS_Y, bitmask_abs) &&
- test_bit (ABS_Z, bitmask_abs))
- udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1");
- return;
+static bool test_pointers(struct udev_device *dev,
+ const unsigned long* bitmask_ev,
+ const unsigned long* bitmask_abs,
+ const unsigned long* bitmask_key,
+ const unsigned long* bitmask_rel,
+ const unsigned long* bitmask_props,
+ bool test) {
+ bool has_abs_coordinates = false;
+ bool has_rel_coordinates = false;
+ bool has_mt_coordinates = false;
+ bool has_joystick_axes_or_buttons = false;
+ bool is_direct = false;
+ bool has_touch = false;
+ bool has_3d_coordinates = false;
+ bool has_keys = false;
+ bool stylus_or_pen = false;
+ bool finger_but_no_pen = false;
+ bool has_mouse_button = false;
+ bool is_mouse = false;
+ bool is_touchpad = false;
+ bool is_touchscreen = false;
+ bool is_tablet = false;
+ bool is_joystick = false;
+ bool is_accelerometer = false;
+ bool is_pointing_stick= false;
+
+ has_keys = test_bit(EV_KEY, bitmask_ev);
+ has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs);
+ has_3d_coordinates = has_abs_coordinates && test_bit(ABS_Z, bitmask_abs);
+ is_accelerometer = test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props);
+
+ if (!has_keys && has_3d_coordinates)
+ is_accelerometer = true;
+
+ if (is_accelerometer) {
+ udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1");
+ return true;
}
- if (test_bit (EV_ABS, bitmask_ev) &&
- test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) {
- if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key))
- udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1");
- else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key))
- is_touchpad = 1;
- else if (test_bit (BTN_MOUSE, bitmask_key))
+ is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props);
+ stylus_or_pen = test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key);
+ finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key);
+ has_mouse_button = test_bit(BTN_LEFT, bitmask_key);
+ has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel);
+ has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs);
+
+ /* unset has_mt_coordinates if devices claims to have all abs axis */
+ if(has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs))
+ has_mt_coordinates = false;
+ is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props);
+ has_touch = test_bit(BTN_TOUCH, bitmask_key);
+ /* joysticks don't necessarily have buttons; e. g.
+ * rudders/pedals are joystick-like, but buttonless; they have
+ * other fancy axes */
+ has_joystick_axes_or_buttons = test_bit(BTN_TRIGGER, bitmask_key) ||
+ test_bit(BTN_A, bitmask_key) ||
+ test_bit(BTN_1, bitmask_key) ||
+ test_bit(ABS_RX, bitmask_abs) ||
+ test_bit(ABS_RY, bitmask_abs) ||
+ test_bit(ABS_RZ, bitmask_abs) ||
+ test_bit(ABS_THROTTLE, bitmask_abs) ||
+ test_bit(ABS_RUDDER, bitmask_abs) ||
+ test_bit(ABS_WHEEL, bitmask_abs) ||
+ test_bit(ABS_GAS, bitmask_abs) ||
+ test_bit(ABS_BRAKE, bitmask_abs);
+
+ if (has_abs_coordinates) {
+ if (stylus_or_pen)
+ is_tablet = true;
+ else if (finger_but_no_pen && !is_direct)
+ is_touchpad = true;
+ else if (has_mouse_button)
/* This path is taken by VMware's USB mouse, which has
* absolute axes, but no touch/pressure button. */
- is_mouse = 1;
- else if (test_bit (BTN_TOUCH, bitmask_key))
- udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1");
- /* joysticks don't necessarily have to have buttons; e. g.
- * rudders/pedals are joystick-like, but buttonless; they have
- * other fancy axes */
- else if (test_bit (BTN_TRIGGER, bitmask_key) ||
- test_bit (BTN_A, bitmask_key) ||
- test_bit (BTN_1, bitmask_key) ||
- test_bit (ABS_RX, bitmask_abs) ||
- test_bit (ABS_RY, bitmask_abs) ||
- test_bit (ABS_RZ, bitmask_abs) ||
- test_bit (ABS_THROTTLE, bitmask_abs) ||
- test_bit (ABS_RUDDER, bitmask_abs) ||
- test_bit (ABS_WHEEL, bitmask_abs) ||
- test_bit (ABS_GAS, bitmask_abs) ||
- test_bit (ABS_BRAKE, bitmask_abs))
- udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1");
+ is_mouse = true;
+ else if (has_touch)
+ is_touchscreen = true;
+ else if (has_joystick_axes_or_buttons)
+ is_joystick = true;
}
+ if (has_mt_coordinates && is_direct)
+ is_touchscreen = true;
- if (test_bit (EV_REL, bitmask_ev) &&
- test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) &&
- test_bit (BTN_MOUSE, bitmask_key))
- is_mouse = 1;
+ if (has_rel_coordinates && has_mouse_button)
+ is_mouse = true;
+ if (is_pointing_stick)
+ udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
if (is_mouse)
udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1");
if (is_touchpad)
udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1");
+ if (is_touchscreen)
+ udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1");
+ if (is_joystick)
+ udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1");
+ if (is_tablet)
+ udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1");
+
+ return is_tablet || is_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick;
}
/* key like devices */
-static void test_key (struct udev_device *dev,
- const unsigned long* bitmask_ev,
- const unsigned long* bitmask_key,
- bool test) {
+static bool test_key(struct udev_device *dev,
+ const unsigned long* bitmask_ev,
+ const unsigned long* bitmask_key,
+ bool test) {
unsigned i;
unsigned long found;
unsigned long mask;
+ bool ret = false;
/* do we have any KEY_* capability? */
- if (!test_bit (EV_KEY, bitmask_ev)) {
+ if (!test_bit(EV_KEY, bitmask_ev)) {
log_debug("test_key: no EV_KEY capability");
- return;
+ return false;
}
/* only consider KEY_* here, not BTN_* */
@@ -208,7 +253,7 @@ static void test_key (struct udev_device *dev,
/* If there are no keys in the lower block, check the higher block */
if (!found) {
for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) {
- if (test_bit (i, bitmask_key)) {
+ if (test_bit(i, bitmask_key)) {
log_debug("test_key: Found key %x in high block", i);
found = 1;
break;
@@ -216,14 +261,20 @@ static void test_key (struct udev_device *dev,
}
}
- if (found > 0)
+ if (found > 0) {
udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+ ret = true;
+ }
/* the first 32 bits are ESC, numbers, and Q to D; if we have all of
* those, consider it a full keyboard; do not test KEY_RESERVED, though */
mask = 0xFFFFFFFE;
- if ((bitmask_key[0] & mask) == mask)
+ if ((bitmask_key[0] & mask) == mask) {
udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1");
+ ret = true;
+ }
+
+ return ret;
}
static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) {
@@ -232,7 +283,12 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
unsigned long bitmask_abs[NBITS(ABS_MAX)];
unsigned long bitmask_key[NBITS(KEY_MAX)];
unsigned long bitmask_rel[NBITS(REL_MAX)];
+ unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)];
const char *sysname, *devnode;
+ bool is_pointer;
+ bool is_key;
+
+ assert(dev);
/* walk up the parental chain until we find the real input device; the
* argument is very likely a subdevice of this, like eventN */
@@ -248,8 +304,15 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test);
get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
- test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test);
- test_key(dev, bitmask_ev, bitmask_key, test);
+ get_cap_mask(dev, pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
+ is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs,
+ bitmask_key, bitmask_rel,
+ bitmask_props, test);
+ is_key = test_key(dev, bitmask_ev, bitmask_key, test);
+ /* Some evdev nodes have only a scrollwheel */
+ if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) &&
+ (test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel)))
+ udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
}
devnode = udev_device_get_devnode(dev);
diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c
index d8ee4cbb61..01f3879f37 100644
--- a/src/udev/udev-builtin-keyboard.c
+++ b/src/udev/udev-builtin-keyboard.c
@@ -18,19 +18,15 @@
***/
#include <stdio.h>
-#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include <fcntl.h>
#include <sys/ioctl.h>
-#include <linux/limits.h>
#include <linux/input.h>
#include "udev.h"
static const struct key *keyboard_lookup_key(const char *str, unsigned len);
#include "keyboard-keys-from-name.h"
-#include "keyboard-keys-to-name.h"
static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) {
struct udev_device *atkbd;
@@ -41,6 +37,9 @@ static int install_force_release(struct udev_device *dev, const unsigned *releas
unsigned i;
int ret;
+ assert(dev);
+ assert(release);
+
atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
if (!atkbd)
return -ENODEV;
@@ -66,99 +65,205 @@ static int install_force_release(struct udev_device *dev, const unsigned *releas
return ret;
}
-static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct udev_list_entry *entry;
+static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode)
+{
struct {
unsigned scan;
unsigned key;
- } map[1024];
- unsigned map_count = 0;
+ } map;
+ char *endptr;
+ const struct key *k;
+ unsigned keycode_num;
+
+ /* translate identifier to key code */
+ k = keyboard_lookup_key(keycode, strlen(keycode));
+ if (k) {
+ keycode_num = k->id;
+ } else {
+ /* check if it's a numeric code already */
+ keycode_num = strtoul(keycode, &endptr, 0);
+ if (endptr[0] !='\0') {
+ log_error("Unknown key identifier '%s'", keycode);
+ return;
+ }
+ }
+
+ map.scan = scancode;
+ map.key = keycode_num;
+
+ log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
+ map.scan, map.scan, map.key, map.key);
+
+ if (ioctl(fd, EVIOCSKEYCODE, &map) < 0)
+ log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key);
+}
+
+static inline char* parse_token(const char *current, int32_t *val_out) {
+ char *next;
+ int32_t val;
+
+ if (!current)
+ return NULL;
+
+ val = strtol(current, &next, 0);
+ if (*next && *next != ':')
+ return NULL;
+
+ if (next != current)
+ *val_out = val;
+
+ if (*next)
+ next++;
+
+ return next;
+}
+
+static void override_abs(int fd, const char *devnode,
+ unsigned evcode, const char *value) {
+ struct input_absinfo absinfo;
+ int rc;
+ char *next;
+
+ rc = ioctl(fd, EVIOCGABS(evcode), &absinfo);
+ if (rc < 0) {
+ log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode);
+ return;
+ }
+
+ next = parse_token(value, &absinfo.minimum);
+ next = parse_token(next, &absinfo.maximum);
+ next = parse_token(next, &absinfo.resolution);
+ next = parse_token(next, &absinfo.fuzz);
+ next = parse_token(next, &absinfo.flat);
+ if (!next) {
+ log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode);
+ return;
+ }
+
+ log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"",
+ evcode,
+ absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat,
+ devnode);
+ rc = ioctl(fd, EVIOCSABS(evcode), &absinfo);
+ if (rc < 0)
+ log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode);
+}
+
+static void set_trackpoint_sensitivity(struct udev_device *dev, const char *value)
+{
+ struct udev_device *pdev;
+ char val_s[DECIMAL_STR_MAX(int)];
+ int r, val_i;
+
+ assert(dev);
+ assert(value);
+
+ /* The sensitivity sysfs attr belongs to the serio parent device */
+ pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
+ if (!pdev) {
+ log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev));
+ return;
+ }
+
+ r = safe_atoi(value, &val_i);
+ if (r < 0) {
+ log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev));
+ return;
+ }
+
+ xsprintf(val_s, "%d", val_i);
+
+ r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s);
+ if (r < 0)
+ log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev));
+}
+
+static int open_device(const char *devnode) {
+ int fd;
+
+ fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ if (fd < 0)
+ return log_error_errno(errno, "Error opening device \"%s\": %m", devnode);
+
+ return fd;
+}
+
+static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) {
+ struct udev_list_entry *entry;
unsigned release[1024];
unsigned release_count = 0;
+ _cleanup_close_ int fd = -1;
+ const char *node;
+
+ node = udev_device_get_devnode(dev);
+ if (!node) {
+ log_error("No device node for \"%s\"", udev_device_get_syspath(dev));
+ return EXIT_FAILURE;
+ }
udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) {
const char *key;
- unsigned scancode, keycode_num;
char *endptr;
- const char *keycode;
- const struct key *k;
key = udev_list_entry_get_name(entry);
- if (!startswith(key, "KEYBOARD_KEY_"))
- continue;
-
- /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
- scancode = strtoul(key + 13, &endptr, 16);
- if (endptr[0] != '\0') {
- log_error("Error, unable to parse scan code from '%s'", key);
- continue;
- }
+ if (startswith(key, "KEYBOARD_KEY_")) {
+ const char *keycode;
+ unsigned scancode;
+
+ /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
+ scancode = strtoul(key + 13, &endptr, 16);
+ if (endptr[0] != '\0') {
+ log_warning("Unable to parse scan code from \"%s\"", key);
+ continue;
+ }
- keycode = udev_list_entry_get_value(entry);
+ keycode = udev_list_entry_get_value(entry);
- /* a leading '!' needs a force-release entry */
- if (keycode[0] == '!') {
- keycode++;
+ /* a leading '!' needs a force-release entry */
+ if (keycode[0] == '!') {
+ keycode++;
- release[release_count] = scancode;
- if (release_count < ELEMENTSOF(release)-1)
- release_count++;
+ release[release_count] = scancode;
+ if (release_count < ELEMENTSOF(release)-1)
+ release_count++;
- if (keycode[0] == '\0')
- continue;
- }
-
- /* translate identifier to key code */
- k = keyboard_lookup_key(keycode, strlen(keycode));
- if (k) {
- keycode_num = k->id;
- } else {
- /* check if it's a numeric code already */
- keycode_num = strtoul(keycode, &endptr, 0);
- if (endptr[0] !='\0') {
- log_error("Error, unknown key identifier '%s'", keycode);
- continue;
+ if (keycode[0] == '\0')
+ continue;
}
- }
- map[map_count].scan = scancode;
- map[map_count].key = keycode_num;
- if (map_count < ELEMENTSOF(map)-1)
- map_count++;
- }
+ if (fd == -1) {
+ fd = open_device(node);
+ if (fd < 0)
+ return EXIT_FAILURE;
+ }
- if (map_count > 0 || release_count > 0) {
- const char *node;
- int fd;
- unsigned i;
+ map_keycode(fd, node, scancode, keycode);
+ } else if (startswith(key, "EVDEV_ABS_")) {
+ unsigned evcode;
- node = udev_device_get_devnode(dev);
- if (!node) {
- log_error("Error, no device node for '%s'", udev_device_get_syspath(dev));
- return EXIT_FAILURE;
- }
+ /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
+ evcode = strtoul(key + 10, &endptr, 16);
+ if (endptr[0] != '\0') {
+ log_warning("Unable to parse EV_ABS code from \"%s\"", key);
+ continue;
+ }
- fd = open(udev_device_get_devnode(dev), O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (fd < 0) {
- log_error_errno(errno, "Error, opening device '%s': %m", node);
- return EXIT_FAILURE;
- }
+ if (fd == -1) {
+ fd = open_device(node);
+ if (fd < 0)
+ return EXIT_FAILURE;
+ }
- /* install list of map codes */
- for (i = 0; i < map_count; i++) {
- log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
- map[i].scan, map[i].scan, map[i].key, map[i].key);
- if (ioctl(fd, EVIOCSKEYCODE, &map[i]) < 0)
- log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", node, map[i].scan, map[i].key);
+ override_abs(fd, node, evcode, udev_list_entry_get_value(entry));
+ } else if (streq(key, "POINTINGSTICK_SENSITIVITY")) {
+ set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry));
}
-
- /* install list of force-release codes */
- if (release_count > 0)
- install_force_release(dev, release, release_count);
-
- close(fd);
}
+ /* install list of force-release codes */
+ if (release_count > 0)
+ install_force_release(dev, release, release_count);
+
return EXIT_SUCCESS;
}
diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c
index ad2829e500..81e78a8aa3 100644
--- a/src/udev/udev-builtin-kmod.c
+++ b/src/udev/udev-builtin-kmod.c
@@ -21,12 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
#include <libkmod.h>
#include "udev.h"
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 37ff1b8008..6e7e1271fb 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -35,7 +35,7 @@
* Type of names:
* b<number> -- BCMA bus core number
* ccw<name> -- CCW bus group name
- * o<index> -- on-board device index number
+ * o<index>[d<dev_port>] -- on-board device index number
* s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
* x<MAC> -- MAC address
* [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
@@ -91,6 +91,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
+#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
@@ -128,36 +129,53 @@ struct netnames {
/* retrieve on-board index number and label from firmware */
static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
- const char *index;
+ unsigned dev_port = 0;
+ size_t l;
+ char *s;
+ const char *attr;
int idx;
/* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
- index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
+ attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
/* SMBIOS type 41 -- Onboard Devices Extended Information */
- if (!index)
- index = udev_device_get_sysattr_value(names->pcidev, "index");
- if (!index)
+ if (!attr)
+ attr = udev_device_get_sysattr_value(names->pcidev, "index");
+ if (!attr)
return -ENOENT;
- idx = strtoul(index, NULL, 0);
+
+ idx = strtoul(attr, NULL, 0);
if (idx <= 0)
return -EINVAL;
- snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
+
+ /* kernel provided port index for multiple ports on a single PCI function */
+ attr = udev_device_get_sysattr_value(dev, "dev_port");
+ if (attr)
+ dev_port = strtol(attr, NULL, 10);
+
+ s = names->pci_onboard;
+ l = sizeof(names->pci_onboard);
+ l = strpcpyf(&s, l, "o%d", idx);
+ if (dev_port > 0)
+ l = strpcpyf(&s, l, "d%d", dev_port);
+ if (l == 0)
+ names->pci_onboard[0] = '\0';
names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
+
return 0;
}
/* read the 256 bytes PCI configuration space to check the multi-function bit */
static bool is_pci_multifunction(struct udev_device *dev) {
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int fd = -1;
const char *filename;
uint8_t config[64];
filename = strjoina(udev_device_get_syspath(dev), "/config");
- f = fopen(filename, "re");
- if (!f)
+ fd = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
return false;
- if (fread(&config, sizeof(config), 1, f) != 1)
+ if (read(fd, &config, sizeof(config)) != sizeof(config))
return false;
/* bit 0-6 header type, bit 7 multi/single function device */
@@ -182,7 +200,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
return -ENOENT;
- /* kernel provided multi-device index */
+ /* kernel provided port index for multiple ports on a single PCI function */
attr = udev_device_get_sysattr_value(dev, "dev_port");
if (attr)
dev_port = strtol(attr, NULL, 10);
@@ -248,7 +266,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (dev_port > 0)
l = strpcpyf(&s, l, "d%d", dev_port);
if (l == 0)
- names->pci_path[0] = '\0';
+ names->pci_slot[0] = '\0';
}
out:
udev_device_unref(pci);
@@ -258,6 +276,9 @@ out:
static int names_pci(struct udev_device *dev, struct netnames *names) {
struct udev_device *parent;
+ assert(dev);
+ assert(names);
+
parent = udev_device_get_parent(dev);
if (!parent)
return -ENOENT;
@@ -284,6 +305,9 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
size_t l;
char *s;
+ assert(dev);
+ assert(names);
+
usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
if (!usbdev)
return -ENOENT;
@@ -332,6 +356,9 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) {
struct udev_device *bcmadev;
unsigned int core;
+ assert(dev);
+ assert(names);
+
bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
if (!bcmadev)
return -ENOENT;
@@ -353,6 +380,9 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
size_t bus_id_len;
int rc;
+ assert(dev);
+ assert(names);
+
/* Retrieve the associated CCW device */
cdev = udev_device_get_parent(dev);
if (!cdev)
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index b6749aab76..4ca0a69d7d 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -77,6 +77,9 @@ static int format_lun_number(struct udev_device *dev, char **path) {
static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
struct udev_device *parent = dev;
+ assert(dev);
+ assert(subsys);
+
while (parent != NULL) {
const char *subsystem;
@@ -96,6 +99,9 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
const char *port;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
return NULL;
@@ -126,6 +132,9 @@ static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent,
const char *sas_address;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
return NULL;
@@ -169,6 +178,9 @@ static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **pa
const char *phy_count;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
return NULL;
@@ -259,6 +271,9 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
const char *port;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
/* find iscsi session */
transportdev = parent;
for (;;) {
@@ -316,6 +331,9 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
struct dirent *dent;
int basenum;
+ assert(parent);
+ assert(path);
+
hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
if (hostdev == NULL)
return NULL;
@@ -398,6 +416,9 @@ static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char *
char guid[38];
size_t i, k;
+ assert(parent);
+ assert(path);
+
hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
if (!hostdev)
return NULL;
@@ -555,6 +576,10 @@ static struct udev_device *handle_bcma(struct udev_device *parent, char **path)
static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) {
struct udev_device *scsi_dev;
+ assert(parent);
+ assert(dev);
+ assert(path);
+
scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device");
if (scsi_dev != NULL) {
const char *wwpn;
@@ -582,6 +607,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
bool supported_transport = false;
bool supported_parent = false;
+ assert(dev);
+
/* S390 ccw bus */
parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
if (parent != NULL) {
@@ -638,7 +665,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
supported_parent = true;
}
- parent = udev_device_get_parent(parent);
+ if (parent)
+ parent = udev_device_get_parent(parent);
}
/*
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index b78c09b910..99bb91ae57 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -20,14 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
#include <errno.h>
-#include <dirent.h>
-#include <getopt.h>
#include "systemd/sd-login.h"
#include "logind-acl.h"
diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
index ab0d96e377..d309dc31cb 100644
--- a/src/udev/udev-builtin-usb_id.c
+++ b/src/udev/udev-builtin-usb_id.c
@@ -150,35 +150,35 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
_cleanup_close_ int fd = -1;
ssize_t size;
unsigned char buf[18 + 65535];
- int pos = 0;
+ size_t pos = 0;
unsigned strpos = 0;
struct usb_interface_descriptor {
- u_int8_t bLength;
- u_int8_t bDescriptorType;
- u_int8_t bInterfaceNumber;
- u_int8_t bAlternateSetting;
- u_int8_t bNumEndpoints;
- u_int8_t bInterfaceClass;
- u_int8_t bInterfaceSubClass;
- u_int8_t bInterfaceProtocol;
- u_int8_t iInterface;
- } __attribute__((packed));
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+ } _packed_;
if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0)
return log_oom();
fd = open(filename, O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- fprintf(stderr, "error opening USB device 'descriptors' file\n");
- return -errno;
- }
+ if (fd < 0)
+ return log_debug_errno(errno, "Error opening USB device 'descriptors' file: %m");
size = read(fd, buf, sizeof(buf));
if (size < 18 || size == sizeof(buf))
return -EIO;
ifs_str[0] = '\0';
- while (pos < size && strpos+7 < len-2) {
+ while (pos + sizeof(struct usb_interface_descriptor) < (size_t) size &&
+ strpos + 7 < len - 2) {
+
struct usb_interface_descriptor *desc;
char if_str[8];
@@ -229,17 +229,17 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
* is concatenated with the identification with an underscore '_'.
*/
static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) {
- char vendor_str[64];
+ char vendor_str[64] = "";
char vendor_str_enc[256];
const char *vendor_id;
- char model_str[64];
+ char model_str[64] = "";
char model_str_enc[256];
const char *product_id;
- char serial_str[UTIL_NAME_SIZE];
- char packed_if_str[UTIL_NAME_SIZE];
- char revision_str[64];
- char type_str[64];
- char instance_str[64];
+ char serial_str[UTIL_NAME_SIZE] = "";
+ char packed_if_str[UTIL_NAME_SIZE] = "";
+ char revision_str[64] = "";
+ char type_str[64] = "";
+ char instance_str[64] = "";
const char *ifnum = NULL;
const char *driver = NULL;
char serial[256];
@@ -252,13 +252,7 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
size_t l;
char *s;
- vendor_str[0] = '\0';
- model_str[0] = '\0';
- serial_str[0] = '\0';
- packed_if_str[0] = '\0';
- revision_str[0] = '\0';
- type_str[0] = '\0';
- instance_str[0] = '\0';
+ assert(dev);
/* shortcut, if we are called directly for a "usb_device" type */
if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) {
@@ -310,7 +304,7 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str));
/* mass storage : SCSI or ATAPI */
- if ((protocol == 6 || protocol == 2)) {
+ if (protocol == 6 || protocol == 2) {
struct udev_device *dev_scsi;
const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
int host, bus, target, lun;
@@ -438,10 +432,10 @@ fallback:
s = serial;
l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL);
- if (serial_str[0] != '\0')
+ if (!isempty(serial_str))
l = strpcpyl(&s, l, "_", serial_str, NULL);
- if (instance_str[0] != '\0')
+ if (!isempty(instance_str))
strpcpyl(&s, l, "-", instance_str, NULL);
udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
@@ -452,14 +446,14 @@ fallback:
udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
- if (serial_str[0] != '\0')
+ if (!isempty(serial_str))
udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
- if (type_str[0] != '\0')
+ if (!isempty(type_str))
udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
- if (instance_str[0] != '\0')
+ if (!isempty(instance_str))
udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
udev_builtin_add_property(dev, test, "ID_BUS", "usb");
- if (packed_if_str[0] != '\0')
+ if (!isempty(packed_if_str))
udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
if (ifnum != NULL)
udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum);
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 1950ec23a1..fabc653800 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -17,12 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
#include <string.h>
-#include <errno.h>
#include <getopt.h>
#include "udev.h"
@@ -127,12 +123,7 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c
}
int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(dev, key, val);
if (test)
printf("%s=%s\n", key, val);
diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c
index 7b5ef6b2a8..b0ad277f73 100644
--- a/src/udev/udev-ctrl.c
+++ b/src/udev/udev-ctrl.c
@@ -10,16 +10,16 @@
*/
#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
-#include <sys/types.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include "socket-util.h"
+#include "formats-util.h"
#include "udev.h"
/* wire protocol magic must match */
@@ -57,7 +57,7 @@ struct udev_ctrl {
int refcount;
struct udev *udev;
int sock;
- struct sockaddr_un saddr;
+ union sockaddr_union saddr;
socklen_t addrlen;
bool bound;
bool cleanup_socket;
@@ -96,9 +96,9 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) {
if (r < 0)
log_warning_errno(errno, "could not set SO_PASSCRED: %m");
- uctrl->saddr.sun_family = AF_LOCAL;
- strscpy(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), "/run/udev/control");
- uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path);
+ uctrl->saddr.un.sun_family = AF_LOCAL;
+ strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control");
+ uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.un.sun_path);
return uctrl;
}
@@ -110,10 +110,10 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
int err;
if (!uctrl->bound) {
- err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
+ err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
if (err < 0 && errno == EADDRINUSE) {
- unlink(uctrl->saddr.sun_path);
- err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
+ unlink(uctrl->saddr.un.sun_path);
+ err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
}
if (err < 0) {
@@ -140,21 +140,19 @@ struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) {
}
static struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) {
- if (uctrl == NULL)
- return NULL;
- uctrl->refcount++;
+ if (uctrl)
+ uctrl->refcount++;
+
return uctrl;
}
struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) {
- if (uctrl == NULL)
- return NULL;
- uctrl->refcount--;
- if (uctrl->refcount > 0)
- return uctrl;
- if (uctrl->sock >= 0)
- close(uctrl->sock);
- free(uctrl);
+ if (uctrl && -- uctrl->refcount == 0) {
+ if (uctrl->sock >= 0)
+ close(uctrl->sock);
+ free(uctrl);
+ }
+
return NULL;
}
@@ -162,7 +160,7 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
if (uctrl == NULL)
return 0;
if (uctrl->cleanup_socket)
- unlink(uctrl->saddr.sun_path);
+ unlink(uctrl->saddr.un.sun_path);
return 0;
}
@@ -224,15 +222,15 @@ struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connectio
}
struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) {
- if (conn == NULL)
- return NULL;
- conn->refcount--;
- if (conn->refcount > 0)
- return conn;
- if (conn->sock >= 0)
- close(conn->sock);
- udev_ctrl_unref(conn->uctrl);
- free(conn);
+ if (conn && -- conn->refcount == 0) {
+ if (conn->sock >= 0)
+ close(conn->sock);
+
+ udev_ctrl_unref(conn->uctrl);
+
+ free(conn);
+ }
+
return NULL;
}
@@ -251,7 +249,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
ctrl_msg_wire.intval = intval;
if (!uctrl->connected) {
- if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) {
+ if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) {
err = -errno;
goto out;
}
@@ -377,6 +375,9 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
log_error_errno(errno, "unable to receive ctrl message: %m");
goto err;
}
+
+ cmsg_close_all(&smsg);
+
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
@@ -402,13 +403,11 @@ err:
}
struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) {
- if (ctrl_msg == NULL)
- return NULL;
- ctrl_msg->refcount--;
- if (ctrl_msg->refcount > 0)
- return ctrl_msg;
- udev_ctrl_connection_unref(ctrl_msg->conn);
- free(ctrl_msg);
+ if (ctrl_msg && -- ctrl_msg->refcount == 0) {
+ udev_ctrl_connection_unref(ctrl_msg->conn);
+ free(ctrl_msg);
+ }
+
return NULL;
}
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index bc115f112d..4dcf8f2e1c 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -23,7 +23,6 @@
#include <errno.h>
#include <ctype.h>
#include <string.h>
-#include <time.h>
#include <net/if.h>
#include <sys/prctl.h>
#include <poll.h>
@@ -31,8 +30,19 @@
#include <sys/wait.h>
#include <sys/signalfd.h>
-#include "udev.h"
#include "rtnl-util.h"
+#include "event-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "udev.h"
+
+typedef struct Spawn {
+ const char *cmd;
+ pid_t pid;
+ usec_t timeout_warn;
+ usec_t timeout;
+} Spawn;
struct udev_event *udev_event_new(struct udev_device *dev) {
struct udev *udev = udev_device_get_udev(dev);
@@ -45,8 +55,7 @@ struct udev_event *udev_event_new(struct udev_device *dev) {
event->udev = udev;
udev_list_init(udev, &event->run_list, false);
udev_list_init(udev, &event->seclabel_list, false);
- event->fd_signal = -1;
- event->birth_usec = now(CLOCK_MONOTONIC);
+ event->birth_usec = clock_boottime_or_monotonic();
return event;
}
@@ -110,6 +119,8 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
char *s;
size_t l;
+ assert(dev);
+
from = src;
s = dest;
l = size;
@@ -374,7 +385,7 @@ out:
}
static int spawn_exec(struct udev_event *event,
- const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
+ const char *cmd, char *const argv[], char **envp,
int fd_stdout, int fd_stderr) {
_cleanup_close_ int fd = -1;
@@ -402,9 +413,8 @@ static int spawn_exec(struct udev_event *event,
/* terminate child in case parent goes away */
prctl(PR_SET_PDEATHSIG, SIGTERM);
- /* restore original udev sigmask before exec */
- if (sigmask)
- sigprocmask(SIG_SETMASK, sigmask, NULL);
+ /* restore sigmask before exec */
+ (void) reset_signal_mask();
execve(argv[0], argv, envp);
@@ -467,7 +477,7 @@ static void spawn_read(struct udev_event *event,
if (timeout_usec > 0) {
usec_t age_usec;
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
+ age_usec = clock_boottime_or_monotonic() - event->birth_usec;
if (age_usec >= timeout_usec) {
log_error("timeout '%s'", cmd);
return;
@@ -540,102 +550,116 @@ static void spawn_read(struct udev_event *event,
result[respos] = '\0';
}
+static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Spawn *spawn = userdata;
+ char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+
+ assert(spawn);
+
+ kill_and_sigcont(spawn->pid, SIGKILL);
+
+ log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
+ format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+
+ return 1;
+}
+
+static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+ Spawn *spawn = userdata;
+ char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+
+ assert(spawn);
+
+ log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
+ format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+
+ return 1;
+}
+
+static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ Spawn *spawn = userdata;
+
+ assert(spawn);
+
+ switch (si->si_code) {
+ case CLD_EXITED:
+ if (si->si_status != 0)
+ log_warning("process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+ else {
+ log_debug("process '%s' succeeded.", spawn->cmd);
+ sd_event_exit(sd_event_source_get_event(s), 0);
+
+ return 1;
+ }
+
+ break;
+ case CLD_KILLED:
+ case CLD_DUMPED:
+ log_warning("process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
+
+ break;
+ default:
+ log_error("process '%s' failed due to unknown reason.", spawn->cmd);
+ }
+
+ sd_event_exit(sd_event_source_get_event(s), -EIO);
+
+ return 1;
+}
+
static int spawn_wait(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
const char *cmd, pid_t pid) {
- struct pollfd pfd[1];
- int err = 0;
-
- pfd[0].events = POLLIN;
- pfd[0].fd = event->fd_signal;
+ Spawn spawn = {
+ .cmd = cmd,
+ .pid = pid,
+ };
+ _cleanup_event_unref_ sd_event *e = NULL;
+ int r, ret;
- while (pid > 0) {
- int timeout;
- int timeout_warn = 0;
- int fdcount;
+ r = sd_event_new(&e);
+ if (r < 0)
+ return r;
- if (timeout_usec > 0) {
- usec_t age_usec;
+ if (timeout_usec > 0) {
+ usec_t usec, age_usec;
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
- if (age_usec >= timeout_usec)
- timeout = 1000;
- else {
- if (timeout_warn_usec > 0)
- timeout_warn = ((timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
+ usec = now(clock_boottime_or_monotonic());
+ age_usec = usec - event->birth_usec;
+ if (age_usec < timeout_usec) {
+ if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
+ spawn.timeout_warn = timeout_warn_usec - age_usec;
- timeout = ((timeout_usec - timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout_warn, USEC_PER_SEC,
+ on_spawn_timeout_warning, &spawn);
+ if (r < 0)
+ return r;
}
- } else {
- timeout = -1;
- }
- fdcount = poll(pfd, 1, timeout_warn);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -errno;
- log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
- if (fdcount == 0) {
- log_warning("slow: '%s' ["PID_FMT"]", cmd, pid);
+ spawn.timeout = timeout_usec - age_usec;
- fdcount = poll(pfd, 1, timeout);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -errno;
- log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
- if (fdcount == 0) {
- log_error("timeout: killing '%s' ["PID_FMT"]", cmd, pid);
- kill(pid, SIGKILL);
- }
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
+ if (r < 0)
+ return r;
}
+ }
- if (pfd[0].revents & POLLIN) {
- struct signalfd_siginfo fdsi;
- int status;
- ssize_t size;
+ r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn);
+ if (r < 0)
+ return r;
- size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size != sizeof(struct signalfd_siginfo))
- continue;
+ r = sd_event_loop(e);
+ if (r < 0)
+ return r;
- switch (fdsi.ssi_signo) {
- case SIGTERM:
- event->sigterm = true;
- break;
- case SIGCHLD:
- if (waitpid(pid, &status, WNOHANG) < 0)
- break;
- if (WIFEXITED(status)) {
- log_debug("'%s' ["PID_FMT"] exit with return code %i", cmd, pid, WEXITSTATUS(status));
- if (WEXITSTATUS(status) != 0)
- err = -1;
- } else if (WIFSIGNALED(status)) {
- log_error("'%s' ["PID_FMT"] terminated by signal %i (%s)", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
- err = -1;
- } else if (WIFSTOPPED(status)) {
- log_error("'%s' ["PID_FMT"] stopped", cmd, pid);
- err = -1;
- } else if (WIFCONTINUED(status)) {
- log_error("'%s' ["PID_FMT"] continued", cmd, pid);
- err = -1;
- } else {
- log_error("'%s' ["PID_FMT"] exit with status 0x%04x", cmd, pid, status);
- err = -1;
- }
- pid = 0;
- break;
- }
- }
- }
-out:
- return err;
+ r = sd_event_get_exit_code(e, &ret);
+ if (r < 0)
+ return r;
+
+ return ret;
}
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {
@@ -674,7 +698,7 @@ out:
int udev_event_spawn(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *cmd, char **envp, const sigset_t *sigmask,
+ const char *cmd, char **envp,
char *result, size_t ressize) {
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
@@ -724,7 +748,7 @@ int udev_event_spawn(struct udev_event *event,
log_debug("starting '%s'", cmd);
- spawn_exec(event, cmd, argv, envp, sigmask,
+ spawn_exec(event, cmd, argv, envp,
outpipe[WRITE_END], errpipe[WRITE_END]);
_exit(2 );
@@ -786,59 +810,41 @@ static int rename_netif(struct udev_event *event) {
void udev_event_execute_rules(struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
struct udev_list *properties_list,
- struct udev_rules *rules,
- const sigset_t *sigmask) {
+ struct udev_rules *rules) {
struct udev_device *dev = event->dev;
if (udev_device_get_subsystem(dev) == NULL)
return;
if (streq(udev_device_get_action(dev), "remove")) {
- udev_device_read_db(dev, NULL);
- udev_device_delete_db(dev);
+ udev_device_read_db(dev);
udev_device_tag_index(dev, NULL, false);
+ udev_device_delete_db(dev);
if (major(udev_device_get_devnum(dev)) != 0)
udev_watch_end(event->udev, dev);
udev_rules_apply_to_event(rules, event,
timeout_usec, timeout_warn_usec,
- properties_list,
- sigmask);
+ properties_list);
if (major(udev_device_get_devnum(dev)) != 0)
udev_node_remove(dev);
} else {
- event->dev_db = udev_device_shallow_clone(dev);
+ event->dev_db = udev_device_clone_with_db(dev);
if (event->dev_db != NULL) {
- udev_device_read_db(event->dev_db, NULL);
- udev_device_set_info_loaded(event->dev_db);
-
/* disable watch during event processing */
if (major(udev_device_get_devnum(dev)) != 0)
udev_watch_end(event->udev, event->dev_db);
}
if (major(udev_device_get_devnum(dev)) == 0 &&
- streq(udev_device_get_action(dev), "move")) {
- struct udev_list_entry *entry;
-
- for ((entry = udev_device_get_properties_list_entry(event->dev_db)); entry; entry = udev_list_entry_get_next(entry)) {
- const char *key, *value;
- struct udev_list_entry *property;
-
- key = udev_list_entry_get_name(entry);
- value = udev_list_entry_get_value(entry);
-
- property = udev_device_add_property(dev, key, value);
- udev_list_entry_set_num(property, true);
- }
- }
+ streq(udev_device_get_action(dev), "move"))
+ udev_device_copy_properties(dev, event->dev_db);
udev_rules_apply_to_event(rules, event,
timeout_usec, timeout_warn_usec,
- properties_list,
- sigmask);
+ properties_list);
/* rename a new network interface, if needed */
if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
@@ -850,20 +856,12 @@ void udev_event_execute_rules(struct udev_event *event,
log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev),
udev_device_get_sysname(dev), event->name);
else {
- const char *interface_old;
-
- /* remember old name */
- interface_old = udev_device_get_sysname(dev);
-
r = udev_device_rename(dev, event->name);
if (r < 0)
log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m",
udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name);
- else {
- udev_device_add_property(dev, "INTERFACE_OLD", interface_old);
- udev_device_add_property(dev, "INTERFACE", event->name);
+ else
log_debug("changed devpath to '%s'", udev_device_get_devpath(dev));
- }
}
}
@@ -898,22 +896,18 @@ void udev_event_execute_rules(struct udev_event *event,
}
/* preserve old, or get new initialization timestamp */
- if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0)
- udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db));
- else if (udev_device_get_usec_initialized(event->dev) == 0)
- udev_device_set_usec_initialized(event->dev, now(CLOCK_MONOTONIC));
+ udev_device_ensure_usec_initialized(event->dev, event->dev_db);
/* (re)write database file */
- udev_device_update_db(dev);
udev_device_tag_index(dev, event->dev_db, true);
+ udev_device_update_db(dev);
udev_device_set_is_initialized(dev);
- udev_device_unref(event->dev_db);
- event->dev_db = NULL;
+ event->dev_db = udev_device_unref(event->dev_db);
}
}
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigmask) {
+void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) {
struct udev_list_entry *list_entry;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) {
@@ -936,7 +930,7 @@ void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_
udev_event_apply_format(event, cmd, program, sizeof(program));
envp = udev_device_get_properties_envp(event->dev);
- udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, NULL, 0);
+ udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, NULL, 0);
}
}
}
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 793b48469d..d824172b89 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -15,7 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
@@ -23,15 +22,13 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
-#include <grp.h>
#include <dirent.h>
-#include <sys/time.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
#include "smack-util.h"
#include "selinux-util.h"
+#include "formats-util.h"
static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
struct stat stats;
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index c9a0197534..915371525f 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -35,6 +35,7 @@
#include "strbuf.h"
#include "strv.h"
#include "util.h"
+#include "sysctl-util.h"
#define PREALLOC_TOKEN 2048
@@ -128,6 +129,7 @@ enum token_type {
TK_M_DRIVER, /* val */
TK_M_WAITFOR, /* val */
TK_M_ATTR, /* val, attr */
+ TK_M_SYSCTL, /* val, attr */
TK_M_PARENTS_MIN,
TK_M_KERNELS, /* val */
@@ -166,6 +168,7 @@ enum token_type {
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_ATTR, /* val, attr */
+ TK_A_SYSCTL, /* val, attr */
TK_A_RUN_BUILTIN, /* val, bool */
TK_A_RUN_PROGRAM, /* val, bool */
TK_A_GOTO, /* size_t */
@@ -262,6 +265,7 @@ static const char *token_str(enum token_type type) {
[TK_M_DRIVER] = "M DRIVER",
[TK_M_WAITFOR] = "M WAITFOR",
[TK_M_ATTR] = "M ATTR",
+ [TK_M_SYSCTL] = "M SYSCTL",
[TK_M_PARENTS_MIN] = "M PARENTS_MIN",
[TK_M_KERNELS] = "M KERNELS",
@@ -300,6 +304,7 @@ static const char *token_str(enum token_type type) {
[TK_A_NAME] = "A NAME",
[TK_A_DEVLINK] = "A DEVLINK",
[TK_A_ATTR] = "A ATTR",
+ [TK_A_SYSCTL] = "A SYSCTL",
[TK_A_RUN_BUILTIN] = "A RUN_BUILTIN",
[TK_A_RUN_PROGRAM] = "A RUN_PROGRAM",
[TK_A_GOTO] = "A GOTO",
@@ -363,9 +368,11 @@ static void dump_token(struct udev_rules *rules, struct token *token) {
log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
break;
case TK_M_ATTR:
+ case TK_M_SYSCTL:
case TK_M_ATTRS:
case TK_M_ENV:
case TK_A_ATTR:
+ case TK_A_SYSCTL:
case TK_A_ENV:
log_debug("%s %s '%s' '%s'(%s)",
token_str(type), operation_str(op), attr, value, string_glob_str(glob));
@@ -555,7 +562,6 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
char *key;
char *val;
size_t len;
- struct udev_list_entry *entry;
/* find key */
key = line;
@@ -606,10 +612,7 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
val++;
}
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(dev, key, val);
return 0;
}
@@ -630,7 +633,7 @@ static int import_file_into_properties(struct udev_device *dev, const char *file
static int import_program_into_properties(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *program, const sigset_t *sigmask) {
+ const char *program) {
struct udev_device *dev = event->dev;
char **envp;
char result[UTIL_LINE_SIZE];
@@ -638,7 +641,7 @@ static int import_program_into_properties(struct udev_event *event,
int err;
envp = udev_device_get_properties_envp(dev);
- err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result));
+ err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, result, sizeof(result));
if (err < 0)
return err;
@@ -661,6 +664,9 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
struct udev_device *dev_parent;
struct udev_list_entry *list_entry;
+ assert(dev);
+ assert(filter);
+
dev_parent = udev_device_get_parent(dev);
if (dev_parent == NULL)
return -1;
@@ -670,12 +676,7 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
const char *val = udev_list_entry_get_value(list_entry);
if (fnmatch(filter, key, 0) == 0) {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(dev, key, val);
}
}
return 0;
@@ -906,8 +907,10 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
break;
case TK_M_ENV:
case TK_M_ATTR:
+ case TK_M_SYSCTL:
case TK_M_ATTRS:
case TK_A_ATTR:
+ case TK_A_SYSCTL:
case TK_A_ENV:
case TK_A_SECLABEL:
attr = data;
@@ -1144,11 +1147,27 @@ static int add_rule(struct udev_rules *rules, char *line,
log_error("invalid ATTR operation");
goto invalid;
}
- if (op < OP_MATCH_MAX) {
+ if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
- } else {
+ else
rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
+ continue;
+ }
+
+ if (startswith(key, "SYSCTL{")) {
+ attr = get_key_attribute(rules->udev, key + strlen("SYSCTL"));
+ if (attr == NULL) {
+ log_error("error parsing SYSCTL attribute");
+ goto invalid;
+ }
+ if (op == OP_REMOVE) {
+ log_error("invalid SYSCTL operation");
+ goto invalid;
}
+ if (op < OP_MATCH_MAX)
+ rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
+ else
+ rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
continue;
}
@@ -1876,8 +1895,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- struct udev_list *properties_list,
- const sigset_t *sigmask) {
+ struct udev_list *properties_list) {
struct token *cur;
struct token *rule;
enum escape_type esc = ESCAPE_UNSET;
@@ -1995,6 +2013,23 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
if (match_attr(rules, event->dev, event, cur) != 0)
goto nomatch;
break;
+ case TK_M_SYSCTL: {
+ char filename[UTIL_PATH_SIZE];
+ _cleanup_free_ char *value = NULL;
+ size_t len;
+
+ udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+ sysctl_normalize(filename);
+ if (sysctl_read(filename, &value) < 0)
+ goto nomatch;
+
+ len = strlen(value);
+ while (len > 0 && isspace(value[--len]))
+ value[len] = '\0';
+ if (match_key(rules, cur, value) != 0)
+ goto nomatch;
+ break;
+ }
case TK_M_KERNELS:
case TK_M_SUBSYSTEMS:
case TK_M_DRIVERS:
@@ -2096,7 +2131,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
- if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result)) < 0) {
+ if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, result, sizeof(result)) < 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
@@ -2132,7 +2167,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
- if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import, sigmask) != 0)
+ if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -2178,12 +2213,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
const char *value;
value = udev_device_get_property_value(event->dev_db, key);
- if (value != NULL) {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_num(entry, true);
- } else {
+ if (value != NULL)
+ udev_device_add_property(event->dev, key, value);
+ else {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
}
@@ -2203,13 +2235,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
pos = strstr(cmdline, key);
if (pos != NULL) {
- struct udev_list_entry *entry;
-
pos += strlen(key);
if (pos[0] == '\0' || isspace(pos[0])) {
/* we import simple flags as 'FLAG=1' */
- entry = udev_device_add_property(event->dev, key, "1");
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(event->dev, key, "1");
imported = true;
} else if (pos[0] == '=') {
const char *value;
@@ -2219,8 +2248,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
while (pos[0] != '\0' && !isspace(pos[0]))
pos++;
pos[0] = '\0';
- entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(event->dev, key, value);
imported = true;
}
}
@@ -2393,7 +2421,6 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
char *value = rules_str(rules, cur->key.value_off);
char value_new[UTIL_NAME_SIZE];
const char *value_old = NULL;
- struct udev_list_entry *entry;
if (value[0] == '\0') {
if (cur->key.op == OP_ADD)
@@ -2413,10 +2440,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
} else
udev_event_apply_format(event, value, value_new, sizeof(value_new));
- entry = udev_device_add_property(event->dev, name, value_new);
- /* store in db, skip private keys */
- if (name[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(event->dev, name, value_new);
break;
}
case TK_A_TAG: {
@@ -2542,6 +2566,21 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
}
break;
}
+ case TK_A_SYSCTL: {
+ char filename[UTIL_PATH_SIZE];
+ char value[UTIL_NAME_SIZE];
+ int r;
+
+ udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+ sysctl_normalize(filename);
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
+ log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
+ rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+ r = sysctl_write(filename, value);
+ if (r < 0)
+ log_error("error writing SYSCTL{%s}='%s': %s", filename, value, strerror(-r));
+ break;
+ }
case TK_A_RUN_BUILTIN:
case TK_A_RUN_PROGRAM: {
struct udev_list_entry *entry;
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 6ba8674d77..15b76dd6ab 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -17,14 +17,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <sys/types.h>
#include <errno.h>
-#include <fcntl.h>
#include <stdio.h>
#include <dirent.h>
#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
#include <sys/inotify.h>
diff --git a/src/udev/udev.h b/src/udev/udev.h
index dece6eccab..fd8504c424 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -20,7 +20,6 @@
#include <sys/types.h>
#include <sys/param.h>
-#include <signal.h>
#include "macro.h"
#include "sd-rtnl.h"
@@ -44,11 +43,9 @@ struct udev_event {
struct udev_list run_list;
int exec_delay;
usec_t birth_usec;
- int fd_signal;
sd_rtnl *rtnl;
unsigned int builtin_run;
unsigned int builtin_ret;
- bool sigterm;
bool inotify_watch;
bool inotify_watch_final;
bool group_set;
@@ -75,8 +72,7 @@ struct udev_rules *udev_rules_unref(struct udev_rules *rules);
bool udev_rules_check_timestamp(struct udev_rules *rules);
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
- struct udev_list *properties_list,
- const sigset_t *sigmask);
+ struct udev_list *properties_list);
int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
@@ -88,14 +84,13 @@ int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
int udev_event_spawn(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *cmd, char **envp, const sigset_t *sigmask,
+ const char *cmd, char **envp,
char *result, size_t ressize);
void udev_event_execute_rules(struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
struct udev_list *properties_list,
- struct udev_rules *rules,
- const sigset_t *sigset);
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigset);
+ struct udev_rules *rules);
+void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec);
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]);
/* udev-watch.c */
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 6af7163d47..78170463b6 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -12,7 +12,6 @@
* GNU General Public License for more details.
*/
-#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -20,10 +19,6 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/un.h>
#include "udev.h"
#include "udev-util.h"
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index d65e40c011..00609e31b5 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -18,7 +18,6 @@
***/
#include <stdlib.h>
-#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <ctype.h>
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 0bab01a234..b3d5565c48 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -15,19 +15,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <ctype.h>
-#include <stdarg.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
#include "udev-util.h"
@@ -208,17 +205,15 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
if ((stats.st_mode & mask) != 0)
continue;
if (S_ISDIR(stats.st_mode)) {
- DIR *dir2;
+ _cleanup_closedir_ DIR *dir2;
dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
- if (dir2 != NULL) {
+ if (dir2 != NULL)
cleanup_dir(dir2, mask, depth-1);
- closedir(dir2);
- }
- unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
- } else {
- unlinkat(dirfd(dir), dent->d_name, 0);
- }
+
+ (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+ } else
+ (void) unlinkat(dirfd(dir), dent->d_name, 0);
}
}
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index 15ded09339..5e93955186 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -15,25 +15,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <unistd.h>
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
#include <string.h>
-#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <time.h>
#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/epoll.h>
-#include <linux/types.h>
-#include <linux/netlink.h>
#include "udev.h"
#include "udev-util.h"
+#include "formats-util.h"
static bool udev_exit;
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index fff5de7a8b..79f45610db 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -23,17 +23,10 @@
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <signal.h>
-#include <time.h>
#include <poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
-#include "udev-util.h"
#include "util.h"
static void help(void) {
@@ -56,6 +49,7 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
{ "quiet", no_argument, NULL, 'q' }, /* removed */
{}
};
+ usec_t deadline;
const char *exists = NULL;
unsigned int timeout = 120;
struct pollfd pfd[1] = { {.fd = -1}, };
@@ -105,13 +99,15 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
return EXIT_FAILURE;
}
+ deadline = now(CLOCK_MONOTONIC) + timeout * USEC_PER_SEC;
+
/* guarantee that the udev daemon isn't pre-processing */
if (getuid() == 0) {
struct udev_ctrl *uctrl;
uctrl = udev_ctrl_new(udev);
if (uctrl != NULL) {
- if (udev_ctrl_send_ping(uctrl, timeout) < 0) {
+ if (udev_ctrl_send_ping(uctrl, MAX(5U, timeout)) < 0) {
log_debug("no connection to daemon");
udev_ctrl_unref(uctrl);
return EXIT_SUCCESS;
@@ -146,6 +142,9 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
break;
}
+ if (now(CLOCK_MONOTONIC) >= deadline)
+ break;
+
/* wake up when queue is empty */
if (poll(pfd, 1, MSEC_PER_SEC) > 0 && pfd[0].revents & POLLIN)
udev_queue_flush(queue);
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index baaeca9352..35a7349439 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -17,19 +17,9 @@
#include <stdlib.h>
#include <stddef.h>
-#include <string.h>
#include <stdio.h>
-#include <unistd.h>
#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/inotify.h>
-#include <poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index 4922b5b6ac..d04e618d0d 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -17,13 +17,10 @@
*/
#include <stdlib.h>
-#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
-#include <ctype.h>
-#include <fcntl.h>
#include <signal.h>
#include <getopt.h>
#include <sys/signalfd.h>
@@ -120,34 +117,25 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
strscpy(filename, sizeof(filename), syspath);
util_remove_trailing_chars(filename, '/');
- dev = udev_device_new_from_syspath(udev, filename);
+ dev = udev_device_new_from_synthetic_event(udev, filename, action);
if (dev == NULL) {
fprintf(stderr, "unable to open device '%s'\n", filename);
rc = 4;
goto out;
}
- /* skip reading of db, but read kernel parameters */
+ /* don't read info from the db */
udev_device_set_info_loaded(dev);
- udev_device_read_uevent_file(dev);
- udev_device_set_action(dev, action);
event = udev_event_new(dev);
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (event->fd_signal < 0) {
- fprintf(stderr, "error creating signalfd\n");
- rc = 5;
- goto out;
- }
udev_event_execute_rules(event,
60 * USEC_PER_SEC, 20 * USEC_PER_SEC,
NULL,
- rules,
- &sigmask_orig);
+ rules);
udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
@@ -159,8 +147,6 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
printf("run: '%s'\n", program);
}
out:
- if (event != NULL && event->fd_signal >= 0)
- close(event->fd_signal);
udev_builtin_exit(udev);
return rc;
}
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 4dc756a28b..11e83f355f 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -15,20 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
-#include <dirent.h>
#include <fcntl.h>
-#include <fnmatch.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
#include "udev.h"
#include "udev-util.h"
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 56cd0cd4ec..b86d8921f3 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -16,11 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <unistd.h>
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
-#include <string.h>
#include <errno.h>
#include <getopt.h>
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 99d4c8983a..eb43091190 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -26,58 +26,70 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
-#include <ctype.h>
#include <fcntl.h>
-#include <time.h>
#include <getopt.h>
-#include <dirent.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/prctl.h>
#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/signalfd.h>
#include <sys/epoll.h>
#include <sys/mount.h>
-#include <poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/inotify.h>
-#include <sys/utsname.h>
#include "sd-daemon.h"
+#include "sd-event.h"
+
+#include "signal-util.h"
+#include "event-util.h"
#include "rtnl-util.h"
#include "cgroup-util.h"
+#include "process-util.h"
#include "dev-setup.h"
#include "fileio.h"
#include "selinux-util.h"
#include "udev.h"
#include "udev-util.h"
+#include "formats-util.h"
+#include "hashmap.h"
-static struct udev_rules *rules;
-static struct udev_ctrl *udev_ctrl;
-static struct udev_monitor *monitor;
-static int worker_watch[2] = { -1, -1 };
-static int fd_signal = -1;
-static int fd_ep = -1;
-static int fd_inotify = -1;
-static bool stop_exec_queue;
-static bool reload;
-static int children;
static bool arg_debug = false;
static int arg_daemonize = false;
static int arg_resolve_names = 1;
-static int arg_children_max;
+static unsigned arg_children_max;
static int arg_exec_delay;
static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
-static sigset_t sigmask_orig;
-static UDEV_LIST(event_list);
-static UDEV_LIST(worker_list);
-static char *udev_cgroup;
-static struct udev_list properties_list;
-static bool udev_exit;
+
+typedef struct Manager {
+ struct udev *udev;
+ sd_event *event;
+ Hashmap *workers;
+ struct udev_list_node events;
+ const char *cgroup;
+ pid_t pid; /* the process that originally allocated the manager object */
+
+ struct udev_rules *rules;
+ struct udev_list properties;
+
+ struct udev_monitor *monitor;
+ struct udev_ctrl *ctrl;
+ struct udev_ctrl_connection *ctrl_conn_blocking;
+ int fd_inotify;
+ int worker_watch[2];
+
+ sd_event_source *ctrl_event;
+ sd_event_source *uevent_event;
+ sd_event_source *inotify_event;
+
+ usec_t last_usec;
+
+ bool stop_exec_queue:1;
+ bool exit:1;
+} Manager;
enum event_state {
EVENT_UNDEF,
@@ -87,10 +99,12 @@ enum event_state {
struct event {
struct udev_list_node node;
+ Manager *manager;
struct udev *udev;
struct udev_device *dev;
+ struct udev_device *dev_kernel;
+ struct worker *worker;
enum event_state state;
- int exitcode;
unsigned long long int delaying_seqnum;
unsigned long long int seqnum;
const char *devpath;
@@ -99,13 +113,15 @@ struct event {
dev_t devnum;
int ifindex;
bool is_block;
+ sd_event_source *timeout_warning;
+ sd_event_source *timeout;
};
static inline struct event *node_to_event(struct udev_list_node *node) {
return container_of(node, struct event, node);
}
-static void event_queue_cleanup(struct udev *udev, enum event_state type);
+static void event_queue_cleanup(Manager *manager, enum event_state type);
enum worker_state {
WORKER_UNDEF,
@@ -115,67 +131,200 @@ enum worker_state {
};
struct worker {
+ Manager *manager;
struct udev_list_node node;
- struct udev *udev;
int refcount;
pid_t pid;
struct udev_monitor *monitor;
enum worker_state state;
struct event *event;
- usec_t event_start_usec;
- bool event_warned;
};
/* passed from worker to main process */
struct worker_message {
- pid_t pid;
- int exitcode;
};
-static inline struct worker *node_to_worker(struct udev_list_node *node) {
- return container_of(node, struct worker, node);
-}
+static void event_free(struct event *event) {
+ int r;
+
+ if (!event)
+ return;
-static void event_queue_delete(struct event *event) {
udev_list_node_remove(&event->node);
udev_device_unref(event->dev);
+ udev_device_unref(event->dev_kernel);
+
+ sd_event_source_unref(event->timeout_warning);
+ sd_event_source_unref(event->timeout);
+
+ if (event->worker)
+ event->worker->event = NULL;
+
+ assert(event->manager);
+
+ if (udev_list_node_is_empty(&event->manager->events)) {
+ /* only clean up the queue from the process that created it */
+ if (event->manager->pid == getpid()) {
+ r = unlink("/run/udev/queue");
+ if (r < 0)
+ log_warning_errno(errno, "could not unlink /run/udev/queue: %m");
+ }
+ }
+
free(event);
}
-static struct worker *worker_ref(struct worker *worker) {
- worker->refcount++;
- return worker;
-}
+static void worker_free(struct worker *worker) {
+ if (!worker)
+ return;
-static void worker_cleanup(struct worker *worker) {
- udev_list_node_remove(&worker->node);
+ assert(worker->manager);
+
+ hashmap_remove(worker->manager->workers, UINT_TO_PTR(worker->pid));
udev_monitor_unref(worker->monitor);
- children--;
+ event_free(worker->event);
+
free(worker);
}
-static void worker_unref(struct worker *worker) {
- worker->refcount--;
- if (worker->refcount > 0)
+static void manager_workers_free(Manager *manager) {
+ struct worker *worker;
+ Iterator i;
+
+ assert(manager);
+
+ HASHMAP_FOREACH(worker, manager->workers, i)
+ worker_free(worker);
+
+ manager->workers = hashmap_free(manager->workers);
+}
+
+static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) {
+ _cleanup_free_ struct worker *worker = NULL;
+ int r;
+
+ assert(ret);
+ assert(manager);
+ assert(worker_monitor);
+ assert(pid > 1);
+
+ worker = new0(struct worker, 1);
+ if (!worker)
+ return -ENOMEM;
+
+ worker->refcount = 1;
+ worker->manager = manager;
+ /* close monitor, but keep address around */
+ udev_monitor_disconnect(worker_monitor);
+ worker->monitor = udev_monitor_ref(worker_monitor);
+ worker->pid = pid;
+
+ r = hashmap_ensure_allocated(&manager->workers, NULL);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(manager->workers, UINT_TO_PTR(pid), worker);
+ if (r < 0)
+ return r;
+
+ *ret = worker;
+ worker = NULL;
+
+ return 0;
+}
+
+static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ struct event *event = userdata;
+
+ assert(event);
+ assert(event->worker);
+
+ kill_and_sigcont(event->worker->pid, SIGKILL);
+ event->worker->state = WORKER_KILLED;
+
+ log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath);
+
+ return 1;
+}
+
+static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+ struct event *event = userdata;
+
+ assert(event);
+
+ log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath);
+
+ return 1;
+}
+
+static void worker_attach_event(struct worker *worker, struct event *event) {
+ sd_event *e;
+ uint64_t usec;
+ int r;
+
+ assert(worker);
+ assert(worker->manager);
+ assert(event);
+ assert(!event->worker);
+ assert(!worker->event);
+
+ worker->state = WORKER_RUNNING;
+ worker->event = event;
+ event->state = EVENT_RUNNING;
+ event->worker = worker;
+
+ e = worker->manager->event;
+
+ r = sd_event_now(e, clock_boottime_or_monotonic(), &usec);
+ if (r < 0)
return;
- log_debug("worker ["PID_FMT"] cleaned up", worker->pid);
- worker_cleanup(worker);
+
+ (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(),
+ usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
+
+ (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(),
+ usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event);
}
-static void worker_list_cleanup(struct udev *udev) {
- struct udev_list_node *loop, *tmp;
+static void manager_free(Manager *manager) {
+ if (!manager)
+ return;
- udev_list_node_foreach_safe(loop, tmp, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ udev_builtin_exit(manager->udev);
- worker_cleanup(worker);
- }
+ sd_event_source_unref(manager->ctrl_event);
+ sd_event_source_unref(manager->uevent_event);
+ sd_event_source_unref(manager->inotify_event);
+
+ udev_unref(manager->udev);
+ sd_event_unref(manager->event);
+ manager_workers_free(manager);
+ event_queue_cleanup(manager, EVENT_UNDEF);
+
+ udev_monitor_unref(manager->monitor);
+ udev_ctrl_unref(manager->ctrl);
+ udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+
+ udev_list_cleanup(&manager->properties);
+ udev_rules_unref(manager->rules);
+
+ safe_close(manager->fd_inotify);
+ safe_close_pair(manager->worker_watch);
+
+ free(manager);
}
-static void worker_new(struct event *event) {
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+static int worker_send_message(int fd) {
+ struct worker_message message = {};
+
+ return loop_write(fd, &message, sizeof(message), false);
+}
+
+static void worker_spawn(Manager *manager, struct event *event) {
struct udev *udev = event->udev;
- struct worker *worker;
- struct udev_monitor *worker_monitor;
+ _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL;
pid_t pid;
/* listen for new events */
@@ -183,69 +332,62 @@ static void worker_new(struct event *event) {
if (worker_monitor == NULL)
return;
/* allow the main daemon netlink address to send devices to the worker */
- udev_monitor_allow_unicast_sender(worker_monitor, monitor);
+ udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
udev_monitor_enable_receiving(worker_monitor);
- worker = new0(struct worker, 1);
- if (worker == NULL) {
- udev_monitor_unref(worker_monitor);
- return;
- }
- /* worker + event reference */
- worker->refcount = 2;
- worker->udev = udev;
-
pid = fork();
switch (pid) {
case 0: {
struct udev_device *dev = NULL;
- int fd_monitor;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
- struct epoll_event ep_signal, ep_monitor;
+ int fd_monitor;
+ _cleanup_close_ int fd_signal = -1, fd_ep = -1;
+ struct epoll_event ep_signal = { .events = EPOLLIN };
+ struct epoll_event ep_monitor = { .events = EPOLLIN };
sigset_t mask;
- int rc = EXIT_SUCCESS;
+ int r = 0;
/* take initial device from queue */
dev = event->dev;
event->dev = NULL;
- free(worker);
- worker_list_cleanup(udev);
- event_queue_cleanup(udev, EVENT_UNDEF);
- udev_monitor_unref(monitor);
- udev_ctrl_unref(udev_ctrl);
- close(fd_signal);
- close(fd_ep);
- close(worker_watch[READ_END]);
+ unsetenv("NOTIFY_SOCKET");
+
+ manager_workers_free(manager);
+ event_queue_cleanup(manager, EVENT_UNDEF);
+
+ manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+ manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+
+ manager->event = sd_event_unref(manager->event);
sigfillset(&mask);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
- log_error_errno(errno, "error creating signalfd %m");
- rc = 2;
+ r = log_error_errno(errno, "error creating signalfd %m");
goto out;
}
+ ep_signal.data.fd = fd_signal;
+
+ fd_monitor = udev_monitor_get_fd(worker_monitor);
+ ep_monitor.data.fd = fd_monitor;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- rc = 3;
+ r = log_error_errno(errno, "error creating epoll fd: %m");
goto out;
}
- memzero(&ep_signal, sizeof(struct epoll_event));
- ep_signal.events = EPOLLIN;
- ep_signal.data.fd = fd_signal;
-
- fd_monitor = udev_monitor_get_fd(worker_monitor);
- memzero(&ep_monitor, sizeof(struct epoll_event));
- ep_monitor.events = EPOLLIN;
- ep_monitor.data.fd = fd_monitor;
-
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
- log_error_errno(errno, "fail to add fds to epoll: %m");
- rc = 4;
+ r = log_error_errno(errno, "fail to add fds to epoll: %m");
goto out;
}
@@ -257,20 +399,17 @@ static void worker_new(struct event *event) {
for (;;) {
struct udev_event *udev_event;
- struct worker_message msg;
int fd_lock = -1;
- int err = 0;
+
+ assert(dev);
log_debug("seq %llu running", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
if (udev_event == NULL) {
- rc = 5;
+ r = -ENOMEM;
goto out;
}
- /* needed for SIGCHLD/SIGTERM in spawn() */
- udev_event->fd_signal = fd_signal;
-
if (arg_exec_delay > 0)
udev_event->exec_delay = arg_exec_delay;
@@ -295,8 +434,8 @@ static void worker_new(struct event *event) {
fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) {
log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
- err = -EWOULDBLOCK;
fd_lock = safe_close(fd_lock);
+ r = -EAGAIN;
goto skip;
}
}
@@ -308,13 +447,11 @@ static void worker_new(struct event *event) {
/* apply rules, create node, symlinks */
udev_event_execute_rules(udev_event,
arg_event_timeout_usec, arg_event_timeout_warn_usec,
- &properties_list,
- rules,
- &sigmask_orig);
+ &manager->properties,
+ manager->rules);
udev_event_execute_run(udev_event,
- arg_event_timeout_usec, arg_event_timeout_warn_usec,
- &sigmask_orig);
+ arg_event_timeout_usec, arg_event_timeout_warn_usec);
if (udev_event->rtnl)
/* in case rtnl was initialized */
@@ -332,22 +469,17 @@ static void worker_new(struct event *event) {
udev_monitor_send_device(worker_monitor, NULL, dev);
skip:
- /* send udevd the result of the event execution */
- memzero(&msg, sizeof(struct worker_message));
- msg.exitcode = err;
- msg.pid = getpid();
- send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
+ log_debug("seq %llu processed", udev_device_get_seqnum(dev));
- log_debug("seq %llu processed with %i", udev_device_get_seqnum(dev), err);
+ /* send udevd the result of the event execution */
+ r = worker_send_message(manager->worker_watch[WRITE_END]);
+ if (r < 0)
+ log_error_errno(r, "failed to send result of seq %llu to main daemon: %m",
+ udev_device_get_seqnum(dev));
udev_device_unref(dev);
dev = NULL;
- if (udev_event->sigterm) {
- udev_event_unref(udev_event);
- goto out;
- }
-
udev_event_unref(udev_event);
/* wait for more device messages from main udevd, or term signal */
@@ -360,7 +492,7 @@ skip:
if (fdcount < 0) {
if (errno == EINTR)
continue;
- log_error_errno(errno, "failed to poll: %m");
+ r = log_error_errno(errno, "failed to poll: %m");
goto out;
}
@@ -385,51 +517,45 @@ skip:
}
out:
udev_device_unref(dev);
- safe_close(fd_signal);
- safe_close(fd_ep);
- close(fd_inotify);
- close(worker_watch[WRITE_END]);
- udev_rules_unref(rules);
- udev_builtin_exit(udev);
- udev_monitor_unref(worker_monitor);
- udev_unref(udev);
+ manager_free(manager);
log_close();
- exit(rc);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
case -1:
- udev_monitor_unref(worker_monitor);
event->state = EVENT_QUEUED;
- free(worker);
log_error_errno(errno, "fork of child failed: %m");
break;
default:
- /* close monitor, but keep address around */
- udev_monitor_disconnect(worker_monitor);
- worker->monitor = worker_monitor;
- worker->pid = pid;
- worker->state = WORKER_RUNNING;
- worker->event_start_usec = now(CLOCK_MONOTONIC);
- worker->event_warned = false;
- worker->event = event;
- event->state = EVENT_RUNNING;
- udev_list_node_append(&worker->node, &worker_list);
- children++;
+ {
+ struct worker *worker;
+ int r;
+
+ r = worker_new(&worker, manager, worker_monitor, pid);
+ if (r < 0)
+ return;
+
+ worker_attach_event(worker, event);
+
log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid);
break;
}
+ }
}
-static void event_run(struct event *event) {
- struct udev_list_node *loop;
+static void event_run(Manager *manager, struct event *event) {
+ struct worker *worker;
+ Iterator i;
+
+ assert(manager);
+ assert(event);
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ HASHMAP_FOREACH(worker, manager->workers, i) {
ssize_t count;
if (worker->state != WORKER_IDLE)
continue;
- count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
+ count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev);
if (count < 0) {
log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it",
worker->pid, count);
@@ -437,34 +563,42 @@ static void event_run(struct event *event) {
worker->state = WORKER_KILLED;
continue;
}
- worker_ref(worker);
- worker->event = event;
- worker->state = WORKER_RUNNING;
- worker->event_start_usec = now(CLOCK_MONOTONIC);
- worker->event_warned = false;
- event->state = EVENT_RUNNING;
+ worker_attach_event(worker, event);
return;
}
- if (children >= arg_children_max) {
+ if (hashmap_size(manager->workers) >= arg_children_max) {
if (arg_children_max > 1)
- log_debug("maximum number (%i) of children reached", children);
+ log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers));
return;
}
/* start new worker and pass initial device */
- worker_new(event);
+ worker_spawn(manager, event);
}
-static int event_queue_insert(struct udev_device *dev) {
+static int event_queue_insert(Manager *manager, struct udev_device *dev) {
struct event *event;
+ int r;
+
+ assert(manager);
+ assert(dev);
+
+ /* only one process can add events to the queue */
+ if (manager->pid == 0)
+ manager->pid = getpid();
+
+ assert(manager->pid == getpid());
event = new0(struct event, 1);
- if (event == NULL)
- return -1;
+ if (!event)
+ return -ENOMEM;
event->udev = udev_device_get_udev(dev);
+ event->manager = manager;
event->dev = dev;
+ event->dev_kernel = udev_device_shallow_clone(dev);
+ udev_device_copy_properties(event->dev_kernel, dev);
event->seqnum = udev_device_get_seqnum(dev);
event->devpath = udev_device_get_devpath(dev);
event->devpath_len = strlen(event->devpath);
@@ -477,16 +611,25 @@ static int event_queue_insert(struct udev_device *dev) {
udev_device_get_action(dev), udev_device_get_subsystem(dev));
event->state = EVENT_QUEUED;
- udev_list_node_append(&event->node, &event_list);
+
+ if (udev_list_node_is_empty(&manager->events)) {
+ r = touch("/run/udev/queue");
+ if (r < 0)
+ log_warning_errno(r, "could not touch /run/udev/queue: %m");
+ }
+
+ udev_list_node_append(&event->node, &manager->events);
+
return 0;
}
-static void worker_kill(struct udev *udev) {
- struct udev_list_node *loop;
+static void manager_kill_workers(Manager *manager) {
+ struct worker *worker;
+ Iterator i;
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ assert(manager);
+ HASHMAP_FOREACH(worker, manager->workers, i) {
if (worker->state == WORKER_KILLED)
continue;
@@ -496,12 +639,12 @@ static void worker_kill(struct udev *udev) {
}
/* lookup event for identical, parent, child device */
-static bool is_devpath_busy(struct event *event) {
+static bool is_devpath_busy(Manager *manager, struct event *event) {
struct udev_list_node *loop;
size_t common;
/* check if queue contains events we depend on */
- udev_list_node_foreach(loop, &event_list) {
+ udev_list_node_foreach(loop, &manager->events) {
struct event *loop_event = node_to_event(loop);
/* we already found a later event, earlier can not block us, no need to check again */
@@ -567,111 +710,271 @@ static bool is_devpath_busy(struct event *event) {
return false;
}
-static void event_queue_start(struct udev *udev) {
+static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish");
+
+ sd_event_exit(manager->event, -ETIMEDOUT);
+
+ return 1;
+}
+
+static void manager_exit(Manager *manager) {
+ uint64_t usec;
+ int r;
+
+ assert(manager);
+
+ manager->exit = true;
+
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Starting shutdown...");
+
+ /* close sources of new events and discard buffered events */
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+
+ manager->fd_inotify = safe_close(manager->fd_inotify);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+
+ manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+
+ /* discard queued events and kill workers */
+ event_queue_cleanup(manager, EVENT_QUEUED);
+ manager_kill_workers(manager);
+
+ r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
+ if (r < 0)
+ return;
+
+ r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(),
+ usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
+ if (r < 0)
+ return;
+}
+
+/* reload requested, HUP signal received, rules changed, builtin changed */
+static void manager_reload(Manager *manager) {
+
+ assert(manager);
+
+ sd_notify(false,
+ "RELOADING=1\n"
+ "STATUS=Flushing configuration...");
+
+ manager_kill_workers(manager);
+ manager->rules = udev_rules_unref(manager->rules);
+ udev_builtin_exit(manager->udev);
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing...");
+}
+
+static void event_queue_start(Manager *manager) {
struct udev_list_node *loop;
+ usec_t usec;
+ int r;
+
+ assert(manager);
+
+ if (udev_list_node_is_empty(&manager->events) ||
+ manager->exit || manager->stop_exec_queue)
+ return;
- udev_list_node_foreach(loop, &event_list) {
+ r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
+ if (r >= 0) {
+ /* check for changed config, every 3 seconds at most */
+ if (manager->last_usec == 0 ||
+ (usec - manager->last_usec) > 3 * USEC_PER_SEC) {
+ if (udev_rules_check_timestamp(manager->rules) ||
+ udev_builtin_validate(manager->udev))
+ manager_reload(manager);
+
+ manager->last_usec = usec;
+ }
+ }
+
+ udev_builtin_init(manager->udev);
+
+ if (!manager->rules) {
+ manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
+ if (!manager->rules)
+ return;
+ }
+
+ udev_list_node_foreach(loop, &manager->events) {
struct event *event = node_to_event(loop);
if (event->state != EVENT_QUEUED)
continue;
/* do not start event if parent or child event is still running */
- if (is_devpath_busy(event))
+ if (is_devpath_busy(manager, event))
continue;
- event_run(event);
+ event_run(manager, event);
}
}
-static void event_queue_cleanup(struct udev *udev, enum event_state match_type) {
+static void event_queue_cleanup(Manager *manager, enum event_state match_type) {
struct udev_list_node *loop, *tmp;
- udev_list_node_foreach_safe(loop, tmp, &event_list) {
+ udev_list_node_foreach_safe(loop, tmp, &manager->events) {
struct event *event = node_to_event(loop);
if (match_type != EVENT_UNDEF && match_type != event->state)
continue;
- event_queue_delete(event);
+ event_free(event);
}
}
-static void worker_returned(int fd_worker) {
+static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
for (;;) {
struct worker_message msg;
+ struct iovec iovec = {
+ .iov_base = &msg,
+ .iov_len = sizeof(msg),
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+ } control = {};
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
ssize_t size;
- struct udev_list_node *loop;
+ struct ucred *ucred = NULL;
+ struct worker *worker;
- size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
- if (size != sizeof(struct worker_message))
- break;
+ size = recvmsg(fd, &msghdr, MSG_DONTWAIT);
+ if (size < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (errno == EAGAIN)
+ /* nothing more to read */
+ break;
- /* lookup worker who sent the signal */
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ return log_error_errno(errno, "failed to receive message: %m");
+ } else if (size != sizeof(struct worker_message)) {
+ log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size);
+ continue;
+ }
- if (worker->pid != msg.pid)
- continue;
+ for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ }
- /* worker returned */
- if (worker->event) {
- worker->event->exitcode = msg.exitcode;
- event_queue_delete(worker->event);
- worker->event = NULL;
- }
- if (worker->state != WORKER_KILLED)
- worker->state = WORKER_IDLE;
- worker_unref(worker);
- break;
+ if (!ucred || ucred->pid <= 0) {
+ log_warning_errno(EIO, "ignoring worker message without valid PID");
+ continue;
}
+
+ /* lookup worker who sent the signal */
+ worker = hashmap_get(manager->workers, UINT_TO_PTR(ucred->pid));
+ if (!worker) {
+ log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
+ continue;
+ }
+
+ if (worker->state != WORKER_KILLED)
+ worker->state = WORKER_IDLE;
+
+ /* worker returned */
+ event_free(worker->event);
}
+
+ /* we have free workers, try to schedule events */
+ event_queue_start(manager);
+
+ return 1;
+}
+
+static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
+ struct udev_device *dev;
+ int r;
+
+ assert(manager);
+
+ dev = udev_monitor_receive_device(manager->monitor);
+ if (dev) {
+ udev_device_ensure_usec_initialized(dev, NULL);
+ r = event_queue_insert(manager, dev);
+ if (r < 0)
+ udev_device_unref(dev);
+ else
+ /* we have fresh events, try to schedule them */
+ event_queue_start(manager);
+ }
+
+ return 1;
}
/* receive the udevd message from userspace */
-static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
- struct udev *udev = udev_ctrl_get_udev(uctrl);
- struct udev_ctrl_connection *ctrl_conn;
- struct udev_ctrl_msg *ctrl_msg = NULL;
+static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
+ _cleanup_udev_ctrl_connection_unref_ struct udev_ctrl_connection *ctrl_conn = NULL;
+ _cleanup_udev_ctrl_msg_unref_ struct udev_ctrl_msg *ctrl_msg = NULL;
const char *str;
int i;
- ctrl_conn = udev_ctrl_get_connection(uctrl);
- if (ctrl_conn == NULL)
- goto out;
+ assert(manager);
+
+ ctrl_conn = udev_ctrl_get_connection(manager->ctrl);
+ if (!ctrl_conn)
+ return 1;
ctrl_msg = udev_ctrl_receive_msg(ctrl_conn);
- if (ctrl_msg == NULL)
- goto out;
+ if (!ctrl_msg)
+ return 1;
i = udev_ctrl_get_set_log_level(ctrl_msg);
if (i >= 0) {
log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
log_set_max_level(i);
- worker_kill(udev);
+ manager_kill_workers(manager);
}
if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (STOP_EXEC_QUEUE) received");
- stop_exec_queue = true;
+ manager->stop_exec_queue = true;
}
if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (START_EXEC_QUEUE) received");
- stop_exec_queue = false;
+ manager->stop_exec_queue = false;
+ event_queue_start(manager);
}
if (udev_ctrl_get_reload(ctrl_msg) > 0) {
log_debug("udevd message (RELOAD) received");
- reload = true;
+ manager_reload(manager);
}
str = udev_ctrl_get_set_env(ctrl_msg);
if (str != NULL) {
- char *key;
+ _cleanup_free_ char *key = NULL;
key = strdup(str);
- if (key != NULL) {
+ if (key) {
char *val;
val = strchr(key, '=');
@@ -680,17 +983,15 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
val = &val[1];
if (val[0] == '\0') {
log_debug("udevd message (ENV) received, unset '%s'", key);
- udev_list_entry_add(&properties_list, key, NULL);
+ udev_list_entry_add(&manager->properties, key, NULL);
} else {
log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
- udev_list_entry_add(&properties_list, key, val);
+ udev_list_entry_add(&manager->properties, key, val);
}
- } else {
+ } else
log_error("wrong key format '%s'", key);
- }
- free(key);
}
- worker_kill(udev);
+ manager_kill_workers(manager);
}
i = udev_ctrl_get_set_children_max(ctrl_msg);
@@ -704,13 +1005,13 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
if (udev_ctrl_get_exit(ctrl_msg) > 0) {
log_debug("udevd message (EXIT) received");
- udev_exit = true;
- /* keep reference to block the client until we exit */
- udev_ctrl_connection_ref(ctrl_conn);
+ manager_exit(manager);
+ /* keep reference to block the client until we exit
+ TODO: deal with several blocking exit requests */
+ manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn);
}
-out:
- udev_ctrl_msg_unref(ctrl_msg);
- return udev_ctrl_connection_unref(ctrl_conn);
+
+ return 1;
}
static int synthesize_change(struct udev_device *dev) {
@@ -817,189 +1118,284 @@ static int synthesize_change(struct udev_device *dev) {
return 0;
}
-static int handle_inotify(struct udev *udev) {
+static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
union inotify_event_buffer buffer;
struct inotify_event *e;
ssize_t l;
- l = read(fd_inotify, &buffer, sizeof(buffer));
+ assert(manager);
+
+ l = read(fd, &buffer, sizeof(buffer));
if (l < 0) {
if (errno == EAGAIN || errno == EINTR)
- return 0;
+ return 1;
return log_error_errno(errno, "Failed to read inotify fd: %m");
}
FOREACH_INOTIFY_EVENT(e, buffer, l) {
- struct udev_device *dev;
+ _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
- dev = udev_watch_lookup(udev, e->wd);
+ dev = udev_watch_lookup(manager->udev, e->wd);
if (!dev)
continue;
log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
- if (e->mask & IN_CLOSE_WRITE)
+ if (e->mask & IN_CLOSE_WRITE) {
synthesize_change(dev);
- else if (e->mask & IN_IGNORED)
- udev_watch_end(udev, dev);
- udev_device_unref(dev);
+ /* settle might be waiting on us to determine the queue
+ * state. If we just handled an inotify event, we might have
+ * generated a "change" event, but we won't have queued up
+ * the resultant uevent yet. Do that.
+ */
+ on_uevent(NULL, -1, 0, manager);
+ } else if (e->mask & IN_IGNORED)
+ udev_watch_end(manager->udev, dev);
}
- return 0;
+ return 1;
}
-static void handle_signal(struct udev *udev, int signo) {
- switch (signo) {
- case SIGINT:
- case SIGTERM:
- udev_exit = true;
- break;
- case SIGCHLD:
- for (;;) {
- pid_t pid;
- int status;
- struct udev_list_node *loop, *tmp;
+static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *manager = userdata;
- pid = waitpid(-1, &status, WNOHANG);
- if (pid <= 0)
- break;
+ assert(manager);
- udev_list_node_foreach_safe(loop, tmp, &worker_list) {
- struct worker *worker = node_to_worker(loop);
-
- if (worker->pid != pid)
- continue;
- log_debug("worker ["PID_FMT"] exit", pid);
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0)
- log_error("worker ["PID_FMT"] exit with return code %i",
- pid, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- log_error("worker ["PID_FMT"] terminated by signal %i (%s)",
- pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
- } else if (WIFSTOPPED(status)) {
- log_error("worker ["PID_FMT"] stopped", pid);
- } else if (WIFCONTINUED(status)) {
- log_error("worker ["PID_FMT"] continued", pid);
- } else {
- log_error("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
- }
+ manager_exit(manager);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- if (worker->event) {
- log_error("worker ["PID_FMT"] failed while handling '%s'",
- pid, worker->event->devpath);
- worker->event->exitcode = -32;
- event_queue_delete(worker->event);
+ return 1;
+}
- /* drop reference taken for state 'running' */
- worker_unref(worker);
- }
- }
- worker_unref(worker);
- break;
+static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ manager_reload(manager);
+
+ return 1;
+}
+
+static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ for (;;) {
+ pid_t pid;
+ int status;
+ struct worker *worker;
+
+ pid = waitpid(-1, &status, WNOHANG);
+ if (pid <= 0)
+ break;
+
+ worker = hashmap_get(manager->workers, UINT_TO_PTR(pid));
+ if (!worker) {
+ log_warning("worker ["PID_FMT"] is unknown, ignoring", pid);
+ continue;
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 0)
+ log_debug("worker ["PID_FMT"] exited", pid);
+ else
+ log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (WIFSTOPPED(status)) {
+ log_info("worker ["PID_FMT"] stopped", pid);
+ continue;
+ } else if (WIFCONTINUED(status)) {
+ log_info("worker ["PID_FMT"] continued", pid);
+ continue;
+ } else
+ log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ if (worker->event) {
+ log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath);
+ /* delete state from disk */
+ udev_device_delete_db(worker->event->dev);
+ udev_device_tag_index(worker->event->dev, NULL, false);
+ /* forward kernel event without amending it */
+ udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
}
}
- break;
- case SIGHUP:
- reload = true;
- break;
+
+ worker_free(worker);
}
+
+ /* we can start new workers, try to schedule events */
+ event_queue_start(manager);
+
+ return 1;
+}
+
+static int on_post(sd_event_source *s, void *userdata) {
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ if (udev_list_node_is_empty(&manager->events)) {
+ /* no pending events */
+ if (!hashmap_isempty(manager->workers)) {
+ /* there are idle workers */
+ log_debug("cleanup idle workers");
+ manager_kill_workers(manager);
+ } else {
+ /* we are idle */
+ if (manager->exit) {
+ r = sd_event_exit(manager->event, 0);
+ if (r < 0)
+ return r;
+ } else if (manager->cgroup)
+ /* cleanup possible left-over processes in our cgroup */
+ cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
+ }
+ }
+
+ return 1;
}
-static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) {
- int ctrl = -1, netlink = -1;
- int fd, n;
+static int listen_fds(int *rctrl, int *rnetlink) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ int ctrl_fd = -1, netlink_fd = -1;
+ int fd, n, r;
+
+ assert(rctrl);
+ assert(rnetlink);
n = sd_listen_fds(true);
- if (n <= 0)
- return -1;
+ if (n < 0)
+ return n;
for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
- if (ctrl >= 0)
- return -1;
- ctrl = fd;
+ if (ctrl_fd >= 0)
+ return -EINVAL;
+ ctrl_fd = fd;
continue;
}
if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
- if (netlink >= 0)
- return -1;
- netlink = fd;
+ if (netlink_fd >= 0)
+ return -EINVAL;
+ netlink_fd = fd;
continue;
}
- return -1;
+ return -EINVAL;
}
- if (ctrl < 0 || netlink < 0)
- return -1;
+ if (ctrl_fd < 0) {
+ _cleanup_udev_ctrl_unref_ struct udev_ctrl *ctrl = NULL;
+
+ udev = udev_new();
+ if (!udev)
+ return -ENOMEM;
+
+ ctrl = udev_ctrl_new(udev);
+ if (!ctrl)
+ return log_error_errno(EINVAL, "error initializing udev control socket");
+
+ r = udev_ctrl_enable_receiving(ctrl);
+ if (r < 0)
+ return log_error_errno(EINVAL, "error binding udev control socket");
+
+ fd = udev_ctrl_get_fd(ctrl);
+ if (fd < 0)
+ return log_error_errno(EIO, "could not get ctrl fd");
+
+ ctrl_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (ctrl_fd < 0)
+ return log_error_errno(errno, "could not dup ctrl fd: %m");
+ }
+
+ if (netlink_fd < 0) {
+ _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
+
+ if (!udev) {
+ udev = udev_new();
+ if (!udev)
+ return -ENOMEM;
+ }
+
+ monitor = udev_monitor_new_from_netlink(udev, "kernel");
+ if (!monitor)
+ return log_error_errno(EINVAL, "error initializing netlink socket");
+
+ (void) udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
+
+ r = udev_monitor_enable_receiving(monitor);
+ if (r < 0)
+ return log_error_errno(EINVAL, "error binding netlink socket");
+
+ fd = udev_monitor_get_fd(monitor);
+ if (fd < 0)
+ return log_error_errno(netlink_fd, "could not get uevent fd: %m");
+
+ netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (ctrl_fd < 0)
+ return log_error_errno(errno, "could not dup netlink fd: %m");
+ }
+
+ *rctrl = ctrl_fd;
+ *rnetlink = netlink_fd;
- log_debug("ctrl=%i netlink=%i", ctrl, netlink);
- *rctrl = ctrl;
- *rnetlink = netlink;
return 0;
}
/*
* read the kernel command line, in case we need to get into debug mode
- * udev.log-priority=<level> syslog priority
- * udev.children-max=<number of workers> events are fully serialized if set to 1
- * udev.exec-delay=<number of seconds> delay execution of every executed program
+ * udev.log-priority=<level> syslog priority
+ * udev.children-max=<number of workers> events are fully serialized if set to 1
+ * udev.exec-delay=<number of seconds> delay execution of every executed program
+ * udev.event-timeout=<number of seconds> seconds to wait before terminating an event
*/
-static void kernel_cmdline_options(struct udev *udev) {
- _cleanup_free_ char *line = NULL;
- const char *word, *state;
- size_t l;
+static int parse_proc_cmdline_item(const char *key, const char *value) {
int r;
- r = proc_cmdline(&line);
- if (r < 0) {
- log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
- return;
- }
+ assert(key);
- FOREACH_WORD_QUOTED(word, l, line, state) {
- char *s, *opt, *value;
+ if (!value)
+ return 0;
- s = strndup(word, l);
- if (!s)
- break;
+ if (startswith(key, "rd."))
+ key += strlen("rd.");
- /* accept the same options for the initrd, prefixed with "rd." */
- if (in_initrd() && startswith(s, "rd."))
- opt = s + 3;
- else
- opt = s;
+ if (startswith(key, "udev."))
+ key += strlen("udev.");
+ else
+ return 0;
- if ((value = startswith(opt, "udev.log-priority="))) {
- int prio;
+ if (streq(key, "log-priority")) {
+ int prio;
- prio = util_log_priority(value);
- log_set_max_level(prio);
- } else if ((value = startswith(opt, "udev.children-max="))) {
- r = safe_atoi(value, &arg_children_max);
- if (r < 0)
- log_warning("Invalid udev.children-max ignored: %s", value);
- } else if ((value = startswith(opt, "udev.exec-delay="))) {
- r = safe_atoi(value, &arg_exec_delay);
- if (r < 0)
- log_warning("Invalid udev.exec-delay ignored: %s", value);
- } else if ((value = startswith(opt, "udev.event-timeout="))) {
- r = safe_atou64(value, &arg_event_timeout_usec);
- if (r < 0) {
- log_warning("Invalid udev.event-timeout ignored: %s", value);
- break;
- }
+ prio = util_log_priority(value);
+ log_set_max_level(prio);
+ } else if (streq(key, "children-max")) {
+ r = safe_atou(value, &arg_children_max);
+ if (r < 0)
+ log_warning("invalid udev.children-max ignored: %s", value);
+ } else if (streq(key, "exec-delay")) {
+ r = safe_atoi(value, &arg_exec_delay);
+ if (r < 0)
+ log_warning("invalid udev.exec-delay ignored: %s", value);
+ } else if (streq(key, "event-timeout")) {
+ r = safe_atou64(value, &arg_event_timeout_usec);
+ if (r < 0)
+ log_warning("invalid udev.event-timeout ignored: %s", value);
+ else {
arg_event_timeout_usec *= USEC_PER_SEC;
arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
}
-
- free(s);
}
+
+ return 0;
}
static void help(void) {
@@ -1044,7 +1440,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_daemonize = true;
break;
case 'c':
- r = safe_atoi(optarg, &arg_children_max);
+ r = safe_atou(optarg, &arg_children_max);
if (r < 0)
log_warning("Invalid --children-max ignored: %s", optarg);
break;
@@ -1094,23 +1490,127 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
+static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) {
+ _cleanup_(manager_freep) Manager *manager = NULL;
+ int r, fd_worker, one = 1;
+
+ assert(ret);
+ assert(fd_ctrl >= 0);
+ assert(fd_uevent >= 0);
+
+ manager = new0(Manager, 1);
+ if (!manager)
+ return log_oom();
+
+ manager->fd_inotify = -1;
+ manager->worker_watch[WRITE_END] = -1;
+ manager->worker_watch[READ_END] = -1;
+
+ manager->udev = udev_new();
+ if (!manager->udev)
+ return log_error_errno(errno, "could not allocate udev context: %m");
+
+ udev_builtin_init(manager->udev);
+
+ manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
+ if (!manager->rules)
+ return log_error_errno(ENOMEM, "error reading rules");
+
+ udev_list_node_init(&manager->events);
+ udev_list_init(manager->udev, &manager->properties, true);
+
+ manager->cgroup = cgroup;
+
+ manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl);
+ if (!manager->ctrl)
+ return log_error_errno(EINVAL, "error taking over udev control socket");
+
+ manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent);
+ if (!manager->monitor)
+ return log_error_errno(EINVAL, "error taking over netlink socket");
+
+ /* unnamed socket from workers to the main daemon */
+ r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
+ if (r < 0)
+ return log_error_errno(errno, "error creating socketpair: %m");
+
+ fd_worker = manager->worker_watch[READ_END];
+
+ r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ if (r < 0)
+ return log_error_errno(errno, "could not enable SO_PASSCRED: %m");
+
+ manager->fd_inotify = udev_watch_init(manager->udev);
+ if (manager->fd_inotify < 0)
+ return log_error_errno(ENOMEM, "error initializing inotify");
+
+ udev_watch_restore(manager->udev);
+
+ /* block and listen to all signals on signalfd */
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) == 0);
+
+ r = sd_event_default(&manager->event);
+ if (r < 0)
+ return log_error_errno(errno, "could not allocate event loop: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigint event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigterm event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sighup event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigchld event source: %m");
+
+ r = sd_event_set_watchdog(manager->event, true);
+ if (r < 0)
+ return log_error_errno(r, "error creating watchdog event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating ctrl event source: %m");
+
+ /* This needs to be after the inotify and uevent handling, to make sure
+ * that the ping is send back after fully processing the pending uevents
+ * (including the synthetic ones we may create due to inotify events).
+ */
+ r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating inotify event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating uevent event source: %m");
+
+ r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating worker event source: %m");
+
+ r = sd_event_add_post(manager->event, NULL, on_post, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating post event source: %m");
+
+ *ret = manager;
+ manager = NULL;
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
- struct udev *udev;
- sigset_t mask;
- int fd_ctrl = -1;
- int fd_netlink = -1;
- int fd_worker = -1;
- struct epoll_event ep_ctrl = { .events = EPOLLIN };
- struct epoll_event ep_inotify = { .events = EPOLLIN };
- struct epoll_event ep_signal = { .events = EPOLLIN };
- struct epoll_event ep_netlink = { .events = EPOLLIN };
- struct epoll_event ep_worker = { .events = EPOLLIN };
- struct udev_ctrl_connection *ctrl_conn = NULL;
- int rc = 1, r;
-
- udev = udev_new();
- if (udev == NULL)
- goto exit;
+ _cleanup_(manager_freep) Manager *manager = NULL;
+ _cleanup_free_ char *cgroup = NULL;
+ int r, fd_ctrl, fd_uevent;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
@@ -1120,424 +1620,121 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto exit;
- kernel_cmdline_options(udev);
+ r = parse_proc_cmdline(parse_proc_cmdline_item);
+ if (r < 0)
+ log_warning_errno(r, "failed to parse kernel command line, ignoring: %m");
if (arg_debug)
log_set_max_level(LOG_DEBUG);
if (getuid() != 0) {
- log_error("root privileges required");
+ r = log_error_errno(EPERM, "root privileges required");
goto exit;
}
- r = mac_selinux_init("/dev");
- if (r < 0) {
- log_error_errno(r, "could not initialize labelling: %m");
- goto exit;
+ if (arg_children_max == 0) {
+ cpu_set_t cpu_set;
+
+ arg_children_max = 8;
+
+ if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
+ arg_children_max += CPU_COUNT(&cpu_set) * 2;
+ }
+
+ log_debug("set children_max to %u", arg_children_max);
}
/* set umask before creating any file/directory */
r = chdir("/");
if (r < 0) {
- log_error_errno(errno, "could not change dir to /: %m");
+ r = log_error_errno(errno, "could not change dir to /: %m");
goto exit;
}
umask(022);
- udev_list_init(udev, &properties_list, true);
+ r = mac_selinux_init("/dev");
+ if (r < 0) {
+ log_error_errno(r, "could not initialize labelling: %m");
+ goto exit;
+ }
r = mkdir("/run/udev", 0755);
if (r < 0 && errno != EEXIST) {
- log_error_errno(errno, "could not create /run/udev: %m");
+ r = log_error_errno(errno, "could not create /run/udev: %m");
goto exit;
}
- dev_setup(NULL);
-
- /* before opening new files, make sure std{in,out,err} fds are in a sane state */
- if (arg_daemonize) {
- int fd;
-
- fd = open("/dev/null", O_RDWR);
- if (fd >= 0) {
- if (write(STDOUT_FILENO, 0, 0) < 0)
- dup2(fd, STDOUT_FILENO);
- if (write(STDERR_FILENO, 0, 0) < 0)
- dup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- close(fd);
- } else {
- log_error("cannot open /dev/null");
- }
- }
-
- if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
- /* get control and netlink socket from systemd */
- udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl);
- if (udev_ctrl == NULL) {
- log_error("error taking over udev control socket");
- rc = 1;
- goto exit;
- }
-
- monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink);
- if (monitor == NULL) {
- log_error("error taking over netlink socket");
- rc = 3;
- goto exit;
- }
-
- /* get our own cgroup, we regularly kill everything udev has left behind */
- if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &udev_cgroup) < 0)
- udev_cgroup = NULL;
- } else {
- /* open control and netlink socket */
- udev_ctrl = udev_ctrl_new(udev);
- if (udev_ctrl == NULL) {
- log_error("error initializing udev control socket");
- rc = 1;
- goto exit;
- }
- fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
-
- monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (monitor == NULL) {
- log_error("error initializing netlink socket");
- rc = 3;
- goto exit;
- }
- fd_netlink = udev_monitor_get_fd(monitor);
-
- udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
- }
-
- if (udev_monitor_enable_receiving(monitor) < 0) {
- log_error("error binding netlink socket");
- rc = 3;
- goto exit;
- }
+ dev_setup(NULL, UID_INVALID, GID_INVALID);
- if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
- log_error("error binding udev control socket");
- rc = 1;
- goto exit;
+ if (getppid() == 1) {
+ /* get our own cgroup, we regularly kill everything udev has left behind
+ we only do this on systemd systems, and only if we are directly spawned
+ by PID1. otherwise we are not guaranteed to have a dedicated cgroup */
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
+ if (r < 0)
+ log_warning_errno(r, "failed to get cgroup: %m");
}
- log_info("starting version " VERSION);
-
- udev_builtin_init(udev);
-
- rules = udev_rules_new(udev, arg_resolve_names);
- if (rules == NULL) {
- log_error("error reading rules");
+ r = listen_fds(&fd_ctrl, &fd_uevent);
+ if (r < 0) {
+ r = log_error_errno(r, "could not listen on fds: %m");
goto exit;
}
- rc = udev_rules_apply_static_dev_perms(rules);
- if (rc < 0)
- log_error_errno(rc, "failed to apply permissions on static device nodes - %m");
-
if (arg_daemonize) {
pid_t pid;
+ log_info("starting version " VERSION);
+
pid = fork();
switch (pid) {
case 0:
break;
case -1:
- log_error_errno(errno, "fork of daemon failed: %m");
- rc = 4;
+ r = log_error_errno(errno, "fork of daemon failed: %m");
goto exit;
default:
- rc = EXIT_SUCCESS;
- goto exit_daemonize;
+ mac_selinux_finish();
+ log_close();
+ _exit(EXIT_SUCCESS);
}
setsid();
write_string_file("/proc/self/oom_score_adj", "-1000");
- } else {
- sd_notify(1, "READY=1");
}
- if (arg_children_max <= 0) {
- cpu_set_t cpu_set;
-
- arg_children_max = 8;
-
- if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
- arg_children_max += CPU_COUNT(&cpu_set) * 2;
- }
- }
- log_debug("set children_max to %u", arg_children_max);
-
- udev_list_node_init(&event_list);
- udev_list_node_init(&worker_list);
-
- fd_inotify = udev_watch_init(udev);
- if (fd_inotify < 0) {
- log_error("error initializing inotify");
- rc = 4;
- goto exit;
- }
- udev_watch_restore(udev);
-
- /* block and listen to all signals on signalfd */
- sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (fd_signal < 0) {
- log_error("error creating signalfd");
- rc = 5;
+ r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup);
+ if (r < 0) {
+ r = log_error_errno(r, "failed to allocate manager object: %m");
goto exit;
}
- /* unnamed socket from workers to the main daemon */
- if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
- log_error("error creating socketpair");
- rc = 6;
- goto exit;
- }
- fd_worker = worker_watch[READ_END];
+ r = udev_rules_apply_static_dev_perms(manager->rules);
+ if (r < 0)
+ log_error_errno(r, "failed to apply permissions on static device nodes: %m");
- ep_ctrl.data.fd = fd_ctrl;
- ep_inotify.data.fd = fd_inotify;
- ep_signal.data.fd = fd_signal;
- ep_netlink.data.fd = fd_netlink;
- ep_worker.data.fd = fd_worker;
+ (void) sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing...");
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- goto exit;
- }
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) {
- log_error_errno(errno, "fail to add fds to epoll: %m");
+ r = sd_event_loop(manager->event);
+ if (r < 0) {
+ log_error_errno(r, "event loop failed: %m");
goto exit;
}
- for (;;) {
- static usec_t last_usec;
- struct epoll_event ev[8];
- int fdcount;
- int timeout;
- bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl;
- int i;
-
- if (udev_exit) {
- /* close sources of new events and discard buffered events */
- if (fd_ctrl >= 0) {
- epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL);
- fd_ctrl = -1;
- }
- if (monitor != NULL) {
- epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL);
- udev_monitor_unref(monitor);
- monitor = NULL;
- }
- if (fd_inotify >= 0) {
- epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL);
- close(fd_inotify);
- fd_inotify = -1;
- }
-
- /* discard queued events and kill workers */
- event_queue_cleanup(udev, EVENT_QUEUED);
- worker_kill(udev);
-
- /* exit after all has cleaned up */
- if (udev_list_node_is_empty(&event_list) && children == 0)
- break;
-
- /* timeout at exit for workers to finish */
- timeout = 30 * MSEC_PER_SEC;
- } else if (udev_list_node_is_empty(&event_list) && children == 0) {
- /* we are idle */
- timeout = -1;
-
- /* cleanup possible left-over processes in our cgroup */
- if (udev_cgroup)
- cg_kill(SYSTEMD_CGROUP_CONTROLLER, udev_cgroup, SIGKILL, false, true, NULL);
- } else {
- /* kill idle or hanging workers */
- timeout = 3 * MSEC_PER_SEC;
- }
-
- /* tell settle that we are busy or idle */
- if (!udev_list_node_is_empty(&event_list)) {
- int fd;
-
- fd = open("/run/udev/queue", O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
- if (fd >= 0)
- close(fd);
- } else {
- unlink("/run/udev/queue");
- }
-
- fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
- if (fdcount < 0)
- continue;
-
- if (fdcount == 0) {
- struct udev_list_node *loop;
-
- /* timeout */
- if (udev_exit) {
- log_error("timeout, giving up waiting for workers to finish");
- break;
- }
-
- /* kill idle workers */
- if (udev_list_node_is_empty(&event_list)) {
- log_debug("cleanup idle workers");
- worker_kill(udev);
- }
-
- /* check for hanging events */
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
- usec_t ts;
-
- if (worker->state != WORKER_RUNNING)
- continue;
-
- ts = now(CLOCK_MONOTONIC);
-
- if ((ts - worker->event_start_usec) > arg_event_timeout_warn_usec) {
- if ((ts - worker->event_start_usec) > arg_event_timeout_usec) {
- log_error("worker ["PID_FMT"] %s timeout; kill it", worker->pid, worker->event->devpath);
- kill(worker->pid, SIGKILL);
- worker->state = WORKER_KILLED;
-
- /* drop reference taken for state 'running' */
- worker_unref(worker);
- log_error("seq %llu '%s' killed", udev_device_get_seqnum(worker->event->dev), worker->event->devpath);
- worker->event->exitcode = -64;
- event_queue_delete(worker->event);
- worker->event = NULL;
- } else if (!worker->event_warned) {
- log_warning("worker ["PID_FMT"] %s is taking a long time", worker->pid, worker->event->devpath);
- worker->event_warned = true;
- }
- }
- }
+ sd_event_get_exit_code(manager->event, &r);
- }
-
- is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false;
- for (i = 0; i < fdcount; i++) {
- if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN)
- is_worker = true;
- else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN)
- is_netlink = true;
- else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN)
- is_signal = true;
- else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN)
- is_inotify = true;
- else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN)
- is_ctrl = true;
- }
-
- /* check for changed config, every 3 seconds at most */
- if ((now(CLOCK_MONOTONIC) - last_usec) > 3 * USEC_PER_SEC) {
- if (udev_rules_check_timestamp(rules))
- reload = true;
- if (udev_builtin_validate(udev))
- reload = true;
-
- last_usec = now(CLOCK_MONOTONIC);
- }
-
- /* reload requested, HUP signal received, rules changed, builtin changed */
- if (reload) {
- worker_kill(udev);
- rules = udev_rules_unref(rules);
- udev_builtin_exit(udev);
- reload = false;
- }
-
- /* event has finished */
- if (is_worker)
- worker_returned(fd_worker);
-
- if (is_netlink) {
- struct udev_device *dev;
-
- dev = udev_monitor_receive_device(monitor);
- if (dev != NULL) {
- udev_device_set_usec_initialized(dev, now(CLOCK_MONOTONIC));
- if (event_queue_insert(dev) < 0)
- udev_device_unref(dev);
- }
- }
-
- /* start new events */
- if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) {
- udev_builtin_init(udev);
- if (rules == NULL)
- rules = udev_rules_new(udev, arg_resolve_names);
- if (rules != NULL)
- event_queue_start(udev);
- }
-
- if (is_signal) {
- struct signalfd_siginfo fdsi;
- ssize_t size;
-
- size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size == sizeof(struct signalfd_siginfo))
- handle_signal(udev, fdsi.ssi_signo);
- }
-
- /* we are shutting down, the events below are not handled anymore */
- if (udev_exit)
- continue;
-
- /* device node watch */
- if (is_inotify)
- handle_inotify(udev);
-
- /*
- * This needs to be after the inotify handling, to make sure,
- * that the ping is send back after the possibly generated
- * "change" events by the inotify device node watch.
- *
- * A single time we may receive a client connection which we need to
- * keep open to block the client. It will be closed right before we
- * exit.
- */
- if (is_ctrl)
- ctrl_conn = handle_ctrl_msg(udev_ctrl);
- }
-
- rc = EXIT_SUCCESS;
exit:
- udev_ctrl_cleanup(udev_ctrl);
- unlink("/run/udev/queue");
-exit_daemonize:
- if (fd_ep >= 0)
- close(fd_ep);
- worker_list_cleanup(udev);
- event_queue_cleanup(udev, EVENT_UNDEF);
- udev_rules_unref(rules);
- udev_builtin_exit(udev);
- if (fd_signal >= 0)
- close(fd_signal);
- if (worker_watch[READ_END] >= 0)
- close(worker_watch[READ_END]);
- if (worker_watch[WRITE_END] >= 0)
- close(worker_watch[WRITE_END]);
- udev_monitor_unref(monitor);
- udev_ctrl_connection_unref(ctrl_conn);
- udev_ctrl_unref(udev_ctrl);
- udev_list_cleanup(&properties_list);
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Shutting down...");
+
+ if (manager)
+ udev_ctrl_cleanup(manager->ctrl);
mac_selinux_finish();
- udev_unref(udev);
log_close();
- return rc;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c
index 0ebe434639..5c57db44c1 100644
--- a/src/udev/v4l_id/v4l_id.c
+++ b/src/udev/v4l_id/v4l_id.c
@@ -36,29 +36,28 @@ int main(int argc, char *argv[]) {
_cleanup_close_ int fd = -1;
char *device;
struct v4l2_capability v2cap;
+ int c;
- for (;;) {
- int option;
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- option = getopt_long(argc, argv, "h", options, NULL);
- if (option == -1)
- break;
-
- switch (option) {
+ switch (c) {
case 'h':
printf("%s [-h,--help] <device file>\n\n"
"Video4Linux device identification.\n\n"
" -h Print this message\n"
, program_invocation_short_name);
return 0;
+ case '?':
+ return -EINVAL;
+
default:
- return 1;
+ assert_not_reached("Unhandled option");
}
- }
- device = argv[optind];
+ device = argv[optind];
if (device == NULL)
return 2;
+
fd = open(device, O_RDONLY);
if (fd < 0)
return 3;