diff options
Diffstat (limited to 'src/udev')
61 files changed, 0 insertions, 18004 deletions
diff --git a/src/udev/.gitignore b/src/udev/.gitignore deleted file mode 100644 index f5d8be3dc1..0000000000 --- a/src/udev/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/udev.pc -/keyboard-keys-from-name.gperf -/keyboard-keys-from-name.h -/keyboard-keys-list.txt diff --git a/src/udev/.vimrc b/src/udev/.vimrc deleted file mode 100644 index 366fbdca4b..0000000000 --- a/src/udev/.vimrc +++ /dev/null @@ -1,4 +0,0 @@ -" 'set exrc' in ~/.vimrc will read .vimrc from the current directory -set tabstop=8 -set shiftwidth=8 -set expandtab diff --git a/src/udev/Makefile b/src/udev/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/udev/ata_id/Makefile b/src/udev/ata_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/ata_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c deleted file mode 100644 index 1e414664ce..0000000000 --- a/src/udev/ata_id/ata_id.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * ata_id - reads product/serial number from ATA drives - * - * Copyright (C) 2005-2008 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net> - * Copyright (C) 2009-2010 David Zeuthen <zeuthen@gmail.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <linux/bsg.h> -#include <linux/hdreg.h> -#include <scsi/scsi.h> -#include <scsi/scsi_ioctl.h> -#include <scsi/sg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "libudev.h" - -#include "fd-util.h" -#include "libudev-private.h" -#include "log.h" -#include "udev-util.h" - -#define COMMAND_TIMEOUT_MSEC (30 * 1000) - -static int disk_scsi_inquiry_command(int fd, - void *buf, - size_t buf_len) -{ - uint8_t cdb[6] = { - /* - * INQUIRY, see SPC-4 section 6.4 - */ - [0] = 0x12, /* OPERATION CODE: INQUIRY */ - [3] = (buf_len >> 8), /* ALLOCATION LENGTH */ - [4] = (buf_len & 0xff), - }; - uint8_t sense[32] = {}; - struct sg_io_v4 io_v4 = { - .guard = 'Q', - .protocol = BSG_PROTOCOL_SCSI, - .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, - .request_len = sizeof(cdb), - .request = (uintptr_t) cdb, - .max_response_len = sizeof(sense), - .response = (uintptr_t) sense, - .din_xfer_len = buf_len, - .din_xferp = (uintptr_t) buf, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - int ret; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr = { - .interface_id = 'S', - .cmdp = (unsigned char*) cdb, - .cmd_len = sizeof (cdb), - .dxferp = buf, - .dxfer_len = buf_len, - .sbp = sense, - .mx_sb_len = sizeof(sense), - .dxfer_direction = SG_DXFER_FROM_DEV, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - return ret; - - /* even if the ioctl succeeds, we need to check the return value */ - if (!(io_hdr.status == 0 && - io_hdr.host_status == 0 && - io_hdr.driver_status == 0)) { - errno = EIO; - return -1; - } - } else - return ret; - } - - /* even if the ioctl succeeds, we need to check the return value */ - if (!(io_v4.device_status == 0 && - io_v4.transport_status == 0 && - io_v4.driver_status == 0)) { - errno = EIO; - return -1; - } - - return 0; -} - -static int disk_identify_command(int fd, - void *buf, - size_t buf_len) -{ - uint8_t cdb[12] = { - /* - * ATA Pass-Through 12 byte command, as described in - * - * T10 04-262r8 ATA Command Pass-Through - * - * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf - */ - [0] = 0xa1, /* OPERATION CODE: 12 byte pass through */ - [1] = 4 << 1, /* PROTOCOL: PIO Data-in */ - [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - [3] = 0, /* FEATURES */ - [4] = 1, /* SECTORS */ - [5] = 0, /* LBA LOW */ - [6] = 0, /* LBA MID */ - [7] = 0, /* LBA HIGH */ - [8] = 0 & 0x4F, /* SELECT */ - [9] = 0xEC, /* Command: ATA IDENTIFY DEVICE */ - }; - uint8_t sense[32] = {}; - uint8_t *desc = sense + 8; - struct sg_io_v4 io_v4 = { - .guard = 'Q', - .protocol = BSG_PROTOCOL_SCSI, - .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, - .request_len = sizeof(cdb), - .request = (uintptr_t) cdb, - .max_response_len = sizeof(sense), - .response = (uintptr_t) sense, - .din_xfer_len = buf_len, - .din_xferp = (uintptr_t) buf, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - int ret; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr = { - .interface_id = 'S', - .cmdp = (unsigned char*) cdb, - .cmd_len = sizeof (cdb), - .dxferp = buf, - .dxfer_len = buf_len, - .sbp = sense, - .mx_sb_len = sizeof (sense), - .dxfer_direction = SG_DXFER_FROM_DEV, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - return ret; - } else - return ret; - } - - if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { - errno = EIO; - return -1; - } - - return 0; -} - -static int disk_identify_packet_device_command(int fd, - void *buf, - size_t buf_len) -{ - uint8_t cdb[16] = { - /* - * ATA Pass-Through 16 byte command, as described in - * - * T10 04-262r8 ATA Command Pass-Through - * - * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf - */ - [0] = 0x85, /* OPERATION CODE: 16 byte pass through */ - [1] = 4 << 1, /* PROTOCOL: PIO Data-in */ - [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - [3] = 0, /* FEATURES */ - [4] = 0, /* FEATURES */ - [5] = 0, /* SECTORS */ - [6] = 1, /* SECTORS */ - [7] = 0, /* LBA LOW */ - [8] = 0, /* LBA LOW */ - [9] = 0, /* LBA MID */ - [10] = 0, /* LBA MID */ - [11] = 0, /* LBA HIGH */ - [12] = 0, /* LBA HIGH */ - [13] = 0, /* DEVICE */ - [14] = 0xA1, /* Command: ATA IDENTIFY PACKET DEVICE */ - [15] = 0, /* CONTROL */ - }; - uint8_t sense[32] = {}; - uint8_t *desc = sense + 8; - struct sg_io_v4 io_v4 = { - .guard = 'Q', - .protocol = BSG_PROTOCOL_SCSI, - .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, - .request_len = sizeof (cdb), - .request = (uintptr_t) cdb, - .max_response_len = sizeof (sense), - .response = (uintptr_t) sense, - .din_xfer_len = buf_len, - .din_xferp = (uintptr_t) buf, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - int ret; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr = { - .interface_id = 'S', - .cmdp = (unsigned char*) cdb, - .cmd_len = sizeof (cdb), - .dxferp = buf, - .dxfer_len = buf_len, - .sbp = sense, - .mx_sb_len = sizeof (sense), - .dxfer_direction = SG_DXFER_FROM_DEV, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - return ret; - } else - return ret; - } - - if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { - errno = EIO; - return -1; - } - - return 0; -} - -/** - * disk_identify_get_string: - * @identify: A block of IDENTIFY data - * @offset_words: Offset of the string to get, in words. - * @dest: Destination buffer for the string. - * @dest_len: Length of destination buffer, in bytes. - * - * Copies the ATA string from @identify located at @offset_words into @dest. - */ -static void disk_identify_get_string(uint8_t identify[512], - unsigned int offset_words, - char *dest, - size_t dest_len) -{ - unsigned int c1; - unsigned int c2; - - while (dest_len > 0) { - c1 = identify[offset_words * 2 + 1]; - c2 = identify[offset_words * 2]; - *dest = c1; - dest++; - *dest = c2; - dest++; - offset_words++; - dest_len -= 2; - } -} - -static void disk_identify_fixup_string(uint8_t identify[512], - unsigned int offset_words, - size_t len) -{ - disk_identify_get_string(identify, offset_words, - (char *) identify + offset_words * 2, len); -} - -static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offset_words) -{ - uint16_t *p; - - p = (uint16_t *) identify; - p[offset_words] = le16toh (p[offset_words]); -} - -/** - * disk_identify: - * @udev: The libudev context. - * @fd: File descriptor for the block device. - * @out_identify: Return location for IDENTIFY data. - * @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE. - * - * Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the - * device represented by @fd. If successful, then the result will be - * copied into @out_identify and @out_is_packet_device. - * - * This routine is based on code from libatasmart, Copyright 2008 - * Lennart Poettering, LGPL v2.1. - * - * Returns: 0 if the data was successfully obtained, otherwise - * non-zero with errno set. - */ -static int disk_identify(struct udev *udev, - int fd, - uint8_t out_identify[512], - int *out_is_packet_device) -{ - int ret; - uint8_t inquiry_buf[36]; - int peripheral_device_type; - int all_nul_bytes; - int n; - int is_packet_device = 0; - - /* init results */ - memzero(out_identify, 512); - - /* If we were to use ATA PASS_THROUGH (12) on an ATAPI device - * we could accidentally blank media. This is because MMC's BLANK - * command has the same op-code (0x61). - * - * To prevent this from happening we bail out if the device - * isn't a Direct Access Block Device, e.g. SCSI type 0x00 - * (CD/DVD devices are type 0x05). So we send a SCSI INQUIRY - * command first... libata is handling this via its SCSI - * emulation layer. - * - * This also ensures that we're actually dealing with a device - * that understands SCSI commands. - * - * (Yes, it is a bit perverse that we're tunneling the ATA - * command through SCSI and relying on the ATA driver - * emulating SCSI well-enough...) - * - * (See commit 160b069c25690bfb0c785994c7c3710289179107 for - * the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635 - * for the original bug-report.) - */ - ret = disk_scsi_inquiry_command (fd, inquiry_buf, sizeof (inquiry_buf)); - if (ret != 0) - goto out; - - /* SPC-4, section 6.4.2: Standard INQUIRY data */ - peripheral_device_type = inquiry_buf[0] & 0x1f; - if (peripheral_device_type == 0x05) - { - is_packet_device = 1; - ret = disk_identify_packet_device_command(fd, out_identify, 512); - goto check_nul_bytes; - } - if (peripheral_device_type != 0x00) { - ret = -1; - errno = EIO; - goto out; - } - - /* OK, now issue the IDENTIFY DEVICE command */ - ret = disk_identify_command(fd, out_identify, 512); - if (ret != 0) - goto out; - - check_nul_bytes: - /* Check if IDENTIFY data is all NUL bytes - if so, bail */ - all_nul_bytes = 1; - for (n = 0; n < 512; n++) { - if (out_identify[n] != '\0') { - all_nul_bytes = 0; - break; - } - } - - if (all_nul_bytes) { - ret = -1; - errno = EIO; - goto out; - } - -out: - if (out_is_packet_device != NULL) - *out_is_packet_device = is_packet_device; - return ret; -} - -int main(int argc, char *argv[]) -{ - _cleanup_udev_unref_ struct udev *udev = NULL; - struct hd_driveid id; - union { - uint8_t byte[512]; - uint16_t wyde[256]; - } identify; - char model[41]; - char model_enc[256]; - char serial[21]; - char revision[9]; - const char *node = NULL; - int export = 0; - _cleanup_close_ int fd = -1; - uint16_t word; - int is_packet_device = 0; - static const struct option options[] = { - { "export", no_argument, NULL, 'x' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - log_parse_environment(); - log_open(); - - udev = udev_new(); - if (udev == NULL) - return 0; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "xh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'x': - export = 1; - break; - case 'h': - printf("Usage: ata_id [--export] [--help] <device>\n" - " -x,--export print values as environment keys\n" - " -h,--help print this help text\n\n"); - return 0; - } - } - - node = argv[optind]; - if (node == NULL) { - log_error("no node specified"); - return 1; - } - - fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC); - if (fd < 0) { - log_error("unable to open '%s'", node); - return 1; - } - - if (disk_identify(udev, fd, identify.byte, &is_packet_device) == 0) { - /* - * fix up only the fields from the IDENTIFY data that we are going to - * use and copy it into the hd_driveid struct for convenience - */ - disk_identify_fixup_string(identify.byte, 10, 20); /* serial */ - disk_identify_fixup_string(identify.byte, 23, 8); /* fwrev */ - disk_identify_fixup_string(identify.byte, 27, 40); /* model */ - disk_identify_fixup_uint16(identify.byte, 0); /* configuration */ - disk_identify_fixup_uint16(identify.byte, 75); /* queue depth */ - disk_identify_fixup_uint16(identify.byte, 76); /* SATA capabilities */ - disk_identify_fixup_uint16(identify.byte, 82); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 83); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 84); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 85); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 86); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 87); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 89); /* time required for SECURITY ERASE UNIT */ - disk_identify_fixup_uint16(identify.byte, 90); /* time required for enhanced SECURITY ERASE UNIT */ - disk_identify_fixup_uint16(identify.byte, 91); /* current APM values */ - disk_identify_fixup_uint16(identify.byte, 94); /* current AAM value */ - disk_identify_fixup_uint16(identify.byte, 108); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 109); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 110); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 111); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 128); /* device lock function */ - disk_identify_fixup_uint16(identify.byte, 217); /* nominal media rotation rate */ - memcpy(&id, identify.byte, sizeof id); - } else { - /* If this fails, then try HDIO_GET_IDENTITY */ - if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) { - log_debug_errno(errno, "HDIO_GET_IDENTITY failed for '%s': %m", node); - return 2; - } - } - - 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); - util_replace_chars(model, NULL); - util_replace_whitespace((char *) id.serial_no, serial, 20); - util_replace_chars(serial, NULL); - util_replace_whitespace((char *) id.fw_rev, revision, 8); - util_replace_chars(revision, NULL); - - if (export) { - /* Set this to convey the disk speaks the ATA protocol */ - printf("ID_ATA=1\n"); - - if ((id.config >> 8) & 0x80) { - /* This is an ATAPI device */ - switch ((id.config >> 8) & 0x1f) { - case 0: - printf("ID_TYPE=cd\n"); - break; - case 1: - printf("ID_TYPE=tape\n"); - break; - case 5: - printf("ID_TYPE=cd\n"); - break; - case 7: - printf("ID_TYPE=optical\n"); - break; - default: - printf("ID_TYPE=generic\n"); - break; - } - } else { - printf("ID_TYPE=disk\n"); - } - printf("ID_BUS=ata\n"); - printf("ID_MODEL=%s\n", model); - printf("ID_MODEL_ENC=%s\n", model_enc); - printf("ID_REVISION=%s\n", revision); - if (serial[0] != '\0') { - printf("ID_SERIAL=%s_%s\n", model, serial); - printf("ID_SERIAL_SHORT=%s\n", serial); - } else { - printf("ID_SERIAL=%s\n", model); - } - - if (id.command_set_1 & (1<<5)) { - printf("ID_ATA_WRITE_CACHE=1\n"); - printf("ID_ATA_WRITE_CACHE_ENABLED=%d\n", (id.cfs_enable_1 & (1<<5)) ? 1 : 0); - } - if (id.command_set_1 & (1<<10)) { - printf("ID_ATA_FEATURE_SET_HPA=1\n"); - printf("ID_ATA_FEATURE_SET_HPA_ENABLED=%d\n", (id.cfs_enable_1 & (1<<10)) ? 1 : 0); - - /* - * TODO: use the READ NATIVE MAX ADDRESS command to get the native max address - * so it is easy to check whether the protected area is in use. - */ - } - if (id.command_set_1 & (1<<3)) { - printf("ID_ATA_FEATURE_SET_PM=1\n"); - printf("ID_ATA_FEATURE_SET_PM_ENABLED=%d\n", (id.cfs_enable_1 & (1<<3)) ? 1 : 0); - } - if (id.command_set_1 & (1<<1)) { - printf("ID_ATA_FEATURE_SET_SECURITY=1\n"); - printf("ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d\n", (id.cfs_enable_1 & (1<<1)) ? 1 : 0); - printf("ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d\n", id.trseuc * 2); - if ((id.cfs_enable_1 & (1<<1))) /* enabled */ { - if (id.dlf & (1<<8)) - printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n"); - else - printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n"); - } - if (id.dlf & (1<<5)) - printf("ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d\n", id.trsEuc * 2); - if (id.dlf & (1<<4)) - printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n"); - if (id.dlf & (1<<3)) - printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n"); - if (id.dlf & (1<<2)) - printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n"); - } - if (id.command_set_1 & (1<<0)) { - printf("ID_ATA_FEATURE_SET_SMART=1\n"); - printf("ID_ATA_FEATURE_SET_SMART_ENABLED=%d\n", (id.cfs_enable_1 & (1<<0)) ? 1 : 0); - } - if (id.command_set_2 & (1<<9)) { - printf("ID_ATA_FEATURE_SET_AAM=1\n"); - printf("ID_ATA_FEATURE_SET_AAM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<9)) ? 1 : 0); - printf("ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d\n", id.acoustic >> 8); - printf("ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d\n", id.acoustic & 0xff); - } - if (id.command_set_2 & (1<<5)) { - printf("ID_ATA_FEATURE_SET_PUIS=1\n"); - printf("ID_ATA_FEATURE_SET_PUIS_ENABLED=%d\n", (id.cfs_enable_2 & (1<<5)) ? 1 : 0); - } - if (id.command_set_2 & (1<<3)) { - printf("ID_ATA_FEATURE_SET_APM=1\n"); - printf("ID_ATA_FEATURE_SET_APM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<3)) ? 1 : 0); - if ((id.cfs_enable_2 & (1<<3))) - printf("ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d\n", id.CurAPMvalues & 0xff); - } - if (id.command_set_2 & (1<<0)) - printf("ID_ATA_DOWNLOAD_MICROCODE=1\n"); - - /* - * Word 76 indicates the capabilities of a SATA device. A PATA device shall set - * word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then - * the device does not claim compliance with the Serial ATA specification and words - * 76 through 79 are not valid and shall be ignored. - */ - - word = identify.wyde[76]; - if (word != 0x0000 && word != 0xffff) { - printf("ID_ATA_SATA=1\n"); - /* - * If bit 2 of word 76 is set to one, then the device supports the Gen2 - * signaling rate of 3.0 Gb/s (see SATA 2.6). - * - * If bit 1 of word 76 is set to one, then the device supports the Gen1 - * signaling rate of 1.5 Gb/s (see SATA 2.6). - */ - if (word & (1<<2)) - printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n"); - if (word & (1<<1)) - printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n"); - } - - /* Word 217 indicates the nominal media rotation rate of the device */ - word = identify.wyde[217]; - if (word == 0x0001) - printf ("ID_ATA_ROTATION_RATE_RPM=0\n"); /* non-rotating e.g. SSD */ - else if (word >= 0x0401 && word <= 0xfffe) - printf ("ID_ATA_ROTATION_RATE_RPM=%d\n", word); - - /* - * Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier - * format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE. - * All other values are reserved. - */ - word = identify.wyde[108]; - if ((word & 0xf000) == 0x5000) { - uint64_t wwwn; - - wwwn = identify.wyde[108]; - wwwn <<= 16; - wwwn |= identify.wyde[109]; - wwwn <<= 16; - wwwn |= identify.wyde[110]; - wwwn <<= 16; - wwwn |= identify.wyde[111]; - printf("ID_WWN=0x%1$" PRIx64 "\n" - "ID_WWN_WITH_EXTENSION=0x%1$" PRIx64 "\n", - wwwn); - } - - /* from Linux's include/linux/ata.h */ - if (identify.wyde[0] == 0x848a || - identify.wyde[0] == 0x844a || - (identify.wyde[83] & 0xc004) == 0x4004) - printf("ID_ATA_CFA=1\n"); - } else { - if (serial[0] != '\0') - printf("%s_%s\n", model, serial); - else - printf("%s\n", model); - } - - return 0; -} diff --git a/src/udev/cdrom_id/Makefile b/src/udev/cdrom_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/cdrom_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c deleted file mode 100644 index 72f284f710..0000000000 --- a/src/udev/cdrom_id/cdrom_id.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * cdrom_id - optical drive and media information prober - * - * Copyright (C) 2008-2010 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <limits.h> -#include <linux/cdrom.h> -#include <scsi/sg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "libudev.h" - -#include "libudev-private.h" -#include "random-util.h" - -/* device info */ -static unsigned int cd_cd_rom; -static unsigned int cd_cd_r; -static unsigned int cd_cd_rw; -static unsigned int cd_dvd_rom; -static unsigned int cd_dvd_r; -static unsigned int cd_dvd_rw; -static unsigned int cd_dvd_ram; -static unsigned int cd_dvd_plus_r; -static unsigned int cd_dvd_plus_rw; -static unsigned int cd_dvd_plus_r_dl; -static unsigned int cd_dvd_plus_rw_dl; -static unsigned int cd_bd; -static unsigned int cd_bd_r; -static unsigned int cd_bd_re; -static unsigned int cd_hddvd; -static unsigned int cd_hddvd_r; -static unsigned int cd_hddvd_rw; -static unsigned int cd_mo; -static unsigned int cd_mrw; -static unsigned int cd_mrw_w; - -/* media info */ -static unsigned int cd_media; -static unsigned int cd_media_cd_rom; -static unsigned int cd_media_cd_r; -static unsigned int cd_media_cd_rw; -static unsigned int cd_media_dvd_rom; -static unsigned int cd_media_dvd_r; -static unsigned int cd_media_dvd_rw; -static unsigned int cd_media_dvd_rw_ro; /* restricted overwrite mode */ -static unsigned int cd_media_dvd_rw_seq; /* sequential mode */ -static unsigned int cd_media_dvd_ram; -static unsigned int cd_media_dvd_plus_r; -static unsigned int cd_media_dvd_plus_rw; -static unsigned int cd_media_dvd_plus_r_dl; -static unsigned int cd_media_dvd_plus_rw_dl; -static unsigned int cd_media_bd; -static unsigned int cd_media_bd_r; -static unsigned int cd_media_bd_re; -static unsigned int cd_media_hddvd; -static unsigned int cd_media_hddvd_r; -static unsigned int cd_media_hddvd_rw; -static unsigned int cd_media_mo; -static unsigned int cd_media_mrw; -static unsigned int cd_media_mrw_w; - -static const char *cd_media_state = NULL; -static unsigned int cd_media_session_next; -static unsigned int cd_media_session_count; -static unsigned int cd_media_track_count; -static unsigned int cd_media_track_count_data; -static unsigned int cd_media_track_count_audio; -static unsigned long long int cd_media_session_last_offset; - -#define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13])) -#define SK(errcode) (((errcode) >> 16) & 0xF) -#define ASC(errcode) (((errcode) >> 8) & 0xFF) -#define ASCQ(errcode) ((errcode) & 0xFF) - -static bool is_mounted(const char *device) -{ - struct stat statbuf; - FILE *fp; - int maj, min; - bool mounted = false; - - if (stat(device, &statbuf) < 0) - return false; - - fp = fopen("/proc/self/mountinfo", "re"); - if (fp == NULL) - return false; - while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) { - if (makedev(maj, min) == statbuf.st_rdev) { - mounted = true; - break; - } - } - fclose(fp); - return mounted; -} - -static void info_scsi_cmd_err(struct udev *udev, const char *cmd, int err) -{ - if (err == -1) { - log_debug("%s failed", cmd); - return; - } - log_debug("%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh", cmd, SK(err), ASC(err), ASCQ(err)); -} - -struct scsi_cmd { - struct cdrom_generic_command cgc; - union { - struct request_sense s; - unsigned char u[18]; - } _sense; - struct sg_io_hdr sg_io; -}; - -static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd) -{ - memzero(cmd, sizeof(struct scsi_cmd)); - cmd->cgc.quiet = 1; - cmd->cgc.sense = &cmd->_sense.s; - cmd->sg_io.interface_id = 'S'; - cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); - cmd->sg_io.cmdp = cmd->cgc.cmd; - cmd->sg_io.sbp = cmd->_sense.u; - cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO; -} - -static void scsi_cmd_set(struct udev *udev, struct scsi_cmd *cmd, size_t i, unsigned char arg) -{ - cmd->sg_io.cmd_len = i + 1; - cmd->cgc.cmd[i] = arg; -} - -#define CHECK_CONDITION 0x01 - -static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) -{ - int ret = 0; - - if (bufsize > 0) { - cmd->sg_io.dxferp = buf; - cmd->sg_io.dxfer_len = bufsize; - cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; - } else { - cmd->sg_io.dxfer_direction = SG_DXFER_NONE; - } - if (ioctl(fd, SG_IO, &cmd->sg_io)) - return -1; - - if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) { - errno = EIO; - ret = -1; - if (cmd->sg_io.masked_status & CHECK_CONDITION) { - ret = ERRCODE(cmd->_sense.u); - if (ret == 0) - ret = -1; - } - } - return ret; -} - -static int media_lock(struct udev *udev, int fd, bool lock) -{ - int err; - - /* disable the kernel's lock logic */ - err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK); - if (err < 0) - log_debug("CDROM_CLEAR_OPTIONS, CDO_LOCK failed"); - - err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0); - if (err < 0) - log_debug("CDROM_LOCKDOOR failed"); - - return err; -} - -static int media_eject(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x1b); - scsi_cmd_set(udev, &sc, 4, 0x02); - scsi_cmd_set(udev, &sc, 5, 0); - err = scsi_cmd_run(udev, &sc, fd, NULL, 0); - if ((err != 0)) { - info_scsi_cmd_err(udev, "START_STOP_UNIT", err); - return -1; - } - return 0; -} - -static int cd_capability_compat(struct udev *udev, int fd) -{ - int capability; - - capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL); - if (capability < 0) { - log_debug("CDROM_GET_CAPABILITY failed"); - return -1; - } - - if (capability & CDC_CD_R) - cd_cd_r = 1; - if (capability & CDC_CD_RW) - cd_cd_rw = 1; - if (capability & CDC_DVD) - cd_dvd_rom = 1; - if (capability & CDC_DVD_R) - cd_dvd_r = 1; - if (capability & CDC_DVD_RAM) - cd_dvd_ram = 1; - if (capability & CDC_MRW) - cd_mrw = 1; - if (capability & CDC_MRW_W) - cd_mrw_w = 1; - return 0; -} - -static int cd_media_compat(struct udev *udev, int fd) -{ - if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) { - log_debug("CDROM_DRIVE_STATUS != CDS_DISC_OK"); - return -1; - } - cd_media = 1; - return 0; -} - -static int cd_inquiry(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char inq[128]; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x12); - scsi_cmd_set(udev, &sc, 4, 36); - scsi_cmd_set(udev, &sc, 5, 0); - err = scsi_cmd_run(udev, &sc, fd, inq, 36); - if ((err != 0)) { - info_scsi_cmd_err(udev, "INQUIRY", err); - return -1; - } - - if ((inq[0] & 0x1F) != 5) { - log_debug("not an MMC unit"); - return -1; - } - - log_debug("INQUIRY: [%.8s][%.16s][%.4s]", inq + 8, inq + 16, inq + 32); - return 0; -} - -static void feature_profile_media(struct udev *udev, int cur_profile) -{ - switch (cur_profile) { - case 0x03: - case 0x04: - case 0x05: - log_debug("profile 0x%02x ", cur_profile); - cd_media = 1; - cd_media_mo = 1; - break; - case 0x08: - log_debug("profile 0x%02x media_cd_rom", cur_profile); - cd_media = 1; - cd_media_cd_rom = 1; - break; - case 0x09: - log_debug("profile 0x%02x media_cd_r", cur_profile); - cd_media = 1; - cd_media_cd_r = 1; - break; - case 0x0a: - log_debug("profile 0x%02x media_cd_rw", cur_profile); - cd_media = 1; - cd_media_cd_rw = 1; - break; - case 0x10: - log_debug("profile 0x%02x media_dvd_ro", cur_profile); - cd_media = 1; - cd_media_dvd_rom = 1; - break; - case 0x11: - log_debug("profile 0x%02x media_dvd_r", cur_profile); - cd_media = 1; - cd_media_dvd_r = 1; - break; - case 0x12: - log_debug("profile 0x%02x media_dvd_ram", cur_profile); - cd_media = 1; - cd_media_dvd_ram = 1; - break; - case 0x13: - log_debug("profile 0x%02x media_dvd_rw_ro", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - cd_media_dvd_rw_ro = 1; - break; - case 0x14: - log_debug("profile 0x%02x media_dvd_rw_seq", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - cd_media_dvd_rw_seq = 1; - break; - case 0x1B: - log_debug("profile 0x%02x media_dvd_plus_r", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r = 1; - break; - case 0x1A: - log_debug("profile 0x%02x media_dvd_plus_rw", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw = 1; - break; - case 0x2A: - log_debug("profile 0x%02x media_dvd_plus_rw_dl", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw_dl = 1; - break; - case 0x2B: - log_debug("profile 0x%02x media_dvd_plus_r_dl", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r_dl = 1; - break; - case 0x40: - log_debug("profile 0x%02x media_bd", cur_profile); - cd_media = 1; - cd_media_bd = 1; - break; - case 0x41: - case 0x42: - log_debug("profile 0x%02x media_bd_r", cur_profile); - cd_media = 1; - cd_media_bd_r = 1; - break; - case 0x43: - log_debug("profile 0x%02x media_bd_re", cur_profile); - cd_media = 1; - cd_media_bd_re = 1; - break; - case 0x50: - log_debug("profile 0x%02x media_hddvd", cur_profile); - cd_media = 1; - cd_media_hddvd = 1; - break; - case 0x51: - log_debug("profile 0x%02x media_hddvd_r", cur_profile); - cd_media = 1; - cd_media_hddvd_r = 1; - break; - case 0x52: - log_debug("profile 0x%02x media_hddvd_rw", cur_profile); - cd_media = 1; - cd_media_hddvd_rw = 1; - break; - default: - log_debug("profile 0x%02x <ignored>", cur_profile); - break; - } -} - -static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) -{ - unsigned int i; - - for (i = 0; i+4 <= size; i += 4) { - int profile; - - profile = profiles[i] << 8 | profiles[i+1]; - switch (profile) { - case 0x03: - case 0x04: - case 0x05: - log_debug("profile 0x%02x mo", profile); - cd_mo = 1; - break; - case 0x08: - log_debug("profile 0x%02x cd_rom", profile); - cd_cd_rom = 1; - break; - case 0x09: - log_debug("profile 0x%02x cd_r", profile); - cd_cd_r = 1; - break; - case 0x0A: - log_debug("profile 0x%02x cd_rw", profile); - cd_cd_rw = 1; - break; - case 0x10: - log_debug("profile 0x%02x dvd_rom", profile); - cd_dvd_rom = 1; - break; - case 0x12: - log_debug("profile 0x%02x dvd_ram", profile); - cd_dvd_ram = 1; - break; - case 0x13: - case 0x14: - log_debug("profile 0x%02x dvd_rw", profile); - cd_dvd_rw = 1; - break; - case 0x1B: - log_debug("profile 0x%02x dvd_plus_r", profile); - cd_dvd_plus_r = 1; - break; - case 0x1A: - log_debug("profile 0x%02x dvd_plus_rw", profile); - cd_dvd_plus_rw = 1; - break; - case 0x2A: - log_debug("profile 0x%02x dvd_plus_rw_dl", profile); - cd_dvd_plus_rw_dl = 1; - break; - case 0x2B: - log_debug("profile 0x%02x dvd_plus_r_dl", profile); - cd_dvd_plus_r_dl = 1; - break; - case 0x40: - cd_bd = 1; - log_debug("profile 0x%02x bd", profile); - break; - case 0x41: - case 0x42: - cd_bd_r = 1; - log_debug("profile 0x%02x bd_r", profile); - break; - case 0x43: - cd_bd_re = 1; - log_debug("profile 0x%02x bd_re", profile); - break; - case 0x50: - cd_hddvd = 1; - log_debug("profile 0x%02x hddvd", profile); - break; - case 0x51: - cd_hddvd_r = 1; - log_debug("profile 0x%02x hddvd_r", profile); - break; - case 0x52: - cd_hddvd_rw = 1; - log_debug("profile 0x%02x hddvd_rw", profile); - break; - default: - log_debug("profile 0x%02x <ignored>", profile); - break; - } - } - return 0; -} - -/* returns 0 if media was detected */ -static int cd_profiles_old_mmc(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - int err; - - unsigned char header[32]; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x51); - scsi_cmd_set(udev, &sc, 8, sizeof(header)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - if (cd_media == 1) { - log_debug("no current profile, but disc is present; assuming CD-ROM"); - cd_media_cd_rom = 1; - cd_media_track_count = 1; - cd_media_track_count_data = 1; - return 0; - } else { - log_debug("no current profile, assuming no media"); - return -1; - } - }; - - cd_media = 1; - - if (header[2] & 16) { - cd_media_cd_rw = 1; - log_debug("profile 0x0a media_cd_rw"); - } else if ((header[2] & 3) < 2 && cd_cd_r) { - cd_media_cd_r = 1; - log_debug("profile 0x09 media_cd_r"); - } else { - cd_media_cd_rom = 1; - log_debug("profile 0x08 media_cd_rom"); - } - return 0; -} - -/* returns 0 if media was detected */ -static int cd_profiles(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char features[65530]; - unsigned int cur_profile = 0; - unsigned int len; - unsigned int i; - int err; - int ret; - - ret = -1; - - /* First query the current profile */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 8, 8); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, 8); - if ((err != 0)) { - info_scsi_cmd_err(udev, "GET CONFIGURATION", err); - /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ - if (SK(err) == 0x5 && (ASC(err) == 0x20 || ASC(err) == 0x24)) { - log_debug("drive is pre-MMC2 and does not support 46h get configuration command"); - log_debug("trying to work around the problem"); - ret = cd_profiles_old_mmc(udev, fd); - } - goto out; - } - - cur_profile = features[6] << 8 | features[7]; - if (cur_profile > 0) { - log_debug("current profile 0x%02x", cur_profile); - feature_profile_media (udev, cur_profile); - ret = 0; /* we have media */ - } else { - log_debug("no current profile, assuming no media"); - } - - len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; - log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); - - if (len > sizeof(features)) { - log_debug("can not get features in a single query, truncating"); - len = sizeof(features); - } else if (len <= 8) - len = sizeof(features); - - /* Now get the full feature buffer */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); - scsi_cmd_set(udev, &sc, 8, len & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, len); - if ((err != 0)) { - info_scsi_cmd_err(udev, "GET CONFIGURATION", err); - return -1; - } - - /* parse the length once more, in case the drive decided to have other features suddenly :) */ - len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; - log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); - - if (len > sizeof(features)) { - log_debug("can not get features in a single query, truncating"); - len = sizeof(features); - } - - /* device features */ - for (i = 8; i+4 < len; i += (4 + features[i+3])) { - unsigned int feature; - - feature = features[i] << 8 | features[i+1]; - - switch (feature) { - case 0x00: - log_debug("GET CONFIGURATION: feature 'profiles', with %i entries", features[i+3] / 4); - feature_profiles(udev, &features[i]+4, MIN(features[i+3], len - i - 4)); - break; - default: - log_debug("GET CONFIGURATION: feature 0x%04x <ignored>, with 0x%02x bytes", feature, features[i+3]); - break; - } - } -out: - return ret; -} - -static int cd_media_info(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char header[32]; - static const char *media_status[] = { - "blank", - "appendable", - "complete", - "other" - }; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x51); - scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - return -1; - }; - - cd_media = 1; - log_debug("disk type %02x", header[8]); - log_debug("hardware reported media status: %s", media_status[header[2] & 3]); - - /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */ - if (!cd_media_cd_rom) - cd_media_state = media_status[header[2] & 3]; - - /* fresh DVD-RW in restricted overwite mode reports itself as - * "appendable"; change it to "blank" to make it consistent with what - * gets reported after blanking, and what userspace expects */ - if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) - cd_media_state = media_status[0]; - - /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are - * always "complete", DVD-RAM are "other" or "complete" if the disc is - * 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 len; - int offset; - - if (cd_media_dvd_ram) { - /* a write protected dvd-ram may report "complete" status */ - - unsigned char dvdstruct[8]; - unsigned char format[12]; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0xAD); - scsi_cmd_set(udev, &sc, 7, 0xC0); - scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); - scsi_cmd_set(udev, &sc, 11, 0); - err = scsi_cmd_run(udev, &sc, fd, dvdstruct, sizeof(dvdstruct)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DVD STRUCTURE", err); - return -1; - } - if (dvdstruct[4] & 0x02) { - cd_media_state = media_status[2]; - log_debug("write-protected DVD-RAM media inserted"); - goto determined; - } - - /* let's make sure we don't try to read unformatted media */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x23); - scsi_cmd_set(udev, &sc, 8, sizeof(format)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, format, sizeof(format)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DVD FORMAT CAPACITIES", err); - return -1; - } - - len = format[3]; - if (len & 7 || len < 16) { - log_debug("invalid format capacities length"); - return -1; - } - - switch(format[8] & 3) { - case 1: - log_debug("unformatted DVD-RAM media inserted"); - /* This means that last format was interrupted - * or failed, blank dvd-ram discs are factory - * formatted. Take no action here as it takes - * quite a while to reformat a dvd-ram and it's - * not automatically started */ - goto determined; - - case 2: - log_debug("formatted DVD-RAM media inserted"); - break; - - case 3: - cd_media = 0; //return no media - log_debug("format capacities returned no media"); - return -1; - } - } - - /* Take a closer look at formatted media (unformatted DVD+RW - * has "blank" status", DVD-RAM was examined earlier) and check - * for ISO and UDF PVDs or a fs superblock presence and do it - * in one ioctl (we need just sectors 0 and 16) */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x28); - scsi_cmd_set(udev, &sc, 5, 0); - scsi_cmd_set(udev, &sc, 8, 32); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); - if ((err != 0)) { - cd_media = 0; - info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); - return -1; - } - - /* 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 */ - - for (offset = 32768; offset < (32768 + 2048); offset++) { - if (buffer [offset]) { - log_debug("data in block 16, assuming complete"); - goto determined; - } - } - - 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: - /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in - * restricted overwrite mode can never append, only in sequential mode */ - if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) - cd_media_session_next = header[10] << 8 | header[5]; - cd_media_session_count = header[9] << 8 | header[4]; - cd_media_track_count = header[11] << 8 | header[6]; - - return 0; -} - -static int cd_media_toc(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char header[12]; - unsigned char toc[65536]; - unsigned int len, i, num_tracks; - unsigned char *p; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 6, 1); - scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC", err); - return -1; - } - - len = (header[0] << 8 | header[1]) + 2; - log_debug("READ TOC: len: %d, start track: %d, end track: %d", len, header[2], header[3]); - if (len > sizeof(toc)) - return -1; - if (len < 2) - return -1; - /* 2: first track, 3: last track */ - num_tracks = header[3] - header[2] + 1; - - /* empty media has no tracks */ - if (len < 8) - return 0; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ - scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); - scsi_cmd_set(udev, &sc, 8, len & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, toc, len); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC (tracks)", err); - return -1; - } - - /* Take care to not iterate beyond the last valid track as specified in - * the TOC, but also avoid going beyond the TOC length, just in case - * the last track number is invalidly large */ - for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { - unsigned int block; - unsigned int is_data_track; - - is_data_track = (p[1] & 0x04) != 0; - - block = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; - log_debug("track=%u info=0x%x(%s) start_block=%u", - p[2], p[1] & 0x0f, is_data_track ? "data":"audio", block); - - if (is_data_track) - cd_media_track_count_data++; - else - cd_media_track_count_audio++; - } - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ - scsi_cmd_set(udev, &sc, 8, sizeof(header)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC (multi session)", err); - return -1; - } - len = header[4+4] << 24 | header[4+5] << 16 | header[4+6] << 8 | header[4+7]; - log_debug("last track %u starts at block %u", header[4+2], len); - cd_media_session_last_offset = (unsigned long long int)len * 2048; - return 0; -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - static const struct option options[] = { - { "lock-media", no_argument, NULL, 'l' }, - { "unlock-media", no_argument, NULL, 'u' }, - { "eject-media", no_argument, NULL, 'e' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - bool eject = false; - bool lock = false; - bool unlock = false; - const char *node = NULL; - int fd = -1; - int cnt; - int rc = 0; - - log_parse_environment(); - log_open(); - - udev = udev_new(); - if (udev == NULL) - goto exit; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "deluh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'l': - lock = true; - break; - case 'u': - unlock = true; - break; - case 'e': - eject = true; - break; - case 'd': - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - log_open(); - break; - case 'h': - printf("Usage: cdrom_id [options] <device>\n" - " -l,--lock-media lock the media (to enable eject request events)\n" - " -u,--unlock-media unlock the media\n" - " -e,--eject-media eject the media\n" - " -d,--debug debug to stderr\n" - " -h,--help print this help text\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - node = argv[optind]; - if (!node) { - log_error("no device"); - fprintf(stderr, "no device\n"); - rc = 1; - goto exit; - } - - initialize_srand(); - for (cnt = 20; cnt > 0; cnt--) { - struct timespec duration; - - fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|(is_mounted(node) ? 0 : O_EXCL)); - if (fd >= 0 || errno != EBUSY) - break; - duration.tv_sec = 0; - duration.tv_nsec = (100 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); - nanosleep(&duration, NULL); - } - if (fd < 0) { - log_debug("unable to open '%s'", node); - fprintf(stderr, "unable to open '%s'\n", node); - rc = 1; - goto exit; - } - log_debug("probing: '%s'", node); - - /* same data as original cdrom_id */ - if (cd_capability_compat(udev, fd) < 0) { - rc = 1; - goto exit; - } - - /* check for media - don't bail if there's no media as we still need to - * to read profiles */ - cd_media_compat(udev, fd); - - /* check if drive talks MMC */ - if (cd_inquiry(udev, fd) < 0) - goto work; - - /* read drive and possibly current profile */ - if (cd_profiles(udev, fd) != 0) - goto work; - - /* at this point we are guaranteed to have media in the drive - find out more about it */ - - /* get session/track info */ - cd_media_toc(udev, fd); - - /* get writable media state */ - cd_media_info(udev, fd); - -work: - /* lock the media, so we enable eject button events */ - if (lock && cd_media) { - log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (lock)"); - media_lock(udev, fd, true); - } - - if (unlock && cd_media) { - log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)"); - media_lock(udev, fd, false); - } - - if (eject) { - log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)"); - media_lock(udev, fd, false); - log_debug("START_STOP_UNIT (eject)"); - media_eject(udev, fd); - } - - printf("ID_CDROM=1\n"); - if (cd_cd_rom) - printf("ID_CDROM_CD=1\n"); - if (cd_cd_r) - printf("ID_CDROM_CD_R=1\n"); - if (cd_cd_rw) - printf("ID_CDROM_CD_RW=1\n"); - if (cd_dvd_rom) - printf("ID_CDROM_DVD=1\n"); - if (cd_dvd_r) - printf("ID_CDROM_DVD_R=1\n"); - if (cd_dvd_rw) - printf("ID_CDROM_DVD_RW=1\n"); - if (cd_dvd_ram) - printf("ID_CDROM_DVD_RAM=1\n"); - if (cd_dvd_plus_r) - printf("ID_CDROM_DVD_PLUS_R=1\n"); - if (cd_dvd_plus_rw) - printf("ID_CDROM_DVD_PLUS_RW=1\n"); - if (cd_dvd_plus_r_dl) - printf("ID_CDROM_DVD_PLUS_R_DL=1\n"); - if (cd_dvd_plus_rw_dl) - printf("ID_CDROM_DVD_PLUS_RW_DL=1\n"); - if (cd_bd) - printf("ID_CDROM_BD=1\n"); - if (cd_bd_r) - printf("ID_CDROM_BD_R=1\n"); - if (cd_bd_re) - printf("ID_CDROM_BD_RE=1\n"); - if (cd_hddvd) - printf("ID_CDROM_HDDVD=1\n"); - if (cd_hddvd_r) - printf("ID_CDROM_HDDVD_R=1\n"); - if (cd_hddvd_rw) - printf("ID_CDROM_HDDVD_RW=1\n"); - if (cd_mo) - printf("ID_CDROM_MO=1\n"); - if (cd_mrw) - printf("ID_CDROM_MRW=1\n"); - if (cd_mrw_w) - printf("ID_CDROM_MRW_W=1\n"); - - if (cd_media) - printf("ID_CDROM_MEDIA=1\n"); - if (cd_media_mo) - printf("ID_CDROM_MEDIA_MO=1\n"); - if (cd_media_mrw) - printf("ID_CDROM_MEDIA_MRW=1\n"); - if (cd_media_mrw_w) - printf("ID_CDROM_MEDIA_MRW_W=1\n"); - if (cd_media_cd_rom) - printf("ID_CDROM_MEDIA_CD=1\n"); - if (cd_media_cd_r) - printf("ID_CDROM_MEDIA_CD_R=1\n"); - if (cd_media_cd_rw) - printf("ID_CDROM_MEDIA_CD_RW=1\n"); - if (cd_media_dvd_rom) - printf("ID_CDROM_MEDIA_DVD=1\n"); - if (cd_media_dvd_r) - printf("ID_CDROM_MEDIA_DVD_R=1\n"); - if (cd_media_dvd_ram) - printf("ID_CDROM_MEDIA_DVD_RAM=1\n"); - if (cd_media_dvd_rw) - printf("ID_CDROM_MEDIA_DVD_RW=1\n"); - if (cd_media_dvd_plus_r) - printf("ID_CDROM_MEDIA_DVD_PLUS_R=1\n"); - if (cd_media_dvd_plus_rw) - printf("ID_CDROM_MEDIA_DVD_PLUS_RW=1\n"); - if (cd_media_dvd_plus_rw_dl) - printf("ID_CDROM_MEDIA_DVD_PLUS_RW_DL=1\n"); - if (cd_media_dvd_plus_r_dl) - printf("ID_CDROM_MEDIA_DVD_PLUS_R_DL=1\n"); - if (cd_media_bd) - printf("ID_CDROM_MEDIA_BD=1\n"); - if (cd_media_bd_r) - printf("ID_CDROM_MEDIA_BD_R=1\n"); - if (cd_media_bd_re) - printf("ID_CDROM_MEDIA_BD_RE=1\n"); - if (cd_media_hddvd) - printf("ID_CDROM_MEDIA_HDDVD=1\n"); - if (cd_media_hddvd_r) - printf("ID_CDROM_MEDIA_HDDVD_R=1\n"); - if (cd_media_hddvd_rw) - printf("ID_CDROM_MEDIA_HDDVD_RW=1\n"); - - if (cd_media_state != NULL) - printf("ID_CDROM_MEDIA_STATE=%s\n", cd_media_state); - if (cd_media_session_next > 0) - printf("ID_CDROM_MEDIA_SESSION_NEXT=%u\n", cd_media_session_next); - if (cd_media_session_count > 0) - printf("ID_CDROM_MEDIA_SESSION_COUNT=%u\n", cd_media_session_count); - if (cd_media_session_count > 1 && cd_media_session_last_offset > 0) - printf("ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%llu\n", cd_media_session_last_offset); - if (cd_media_track_count > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT=%u\n", cd_media_track_count); - if (cd_media_track_count_audio > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%u\n", cd_media_track_count_audio); - if (cd_media_track_count_data > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%u\n", cd_media_track_count_data); -exit: - if (fd >= 0) - close(fd); - udev_unref(udev); - log_close(); - return rc; -} diff --git a/src/udev/collect/Makefile b/src/udev/collect/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/collect/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c deleted file mode 100644 index 0e973cd521..0000000000 --- a/src/udev/collect/collect.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Collect variables across events. - * - * usage: collect [--add|--remove] <checkpoint> <id> <idlist> - * - * Adds ID <id> to the list governed by <checkpoint>. - * <id> must be part of the ID list <idlist>. - * If all IDs given by <idlist> are listed (ie collect has been - * invoked for each ID in <idlist>) collect returns 0, the - * number of missing IDs otherwise. - * A negative number is returned on error. - * - * Copyright(C) 2007, Hannes Reinecke <hare@suse.de> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include <errno.h> -#include <getopt.h> -#include <stddef.h> -#include <stdio.h> - -#include "alloc-util.h" -#include "libudev-private.h" -#include "macro.h" -#include "stdio-util.h" -#include "string-util.h" - -#define BUFSIZE 16 -#define UDEV_ALARM_TIMEOUT 180 - -enum collect_state { - STATE_NONE, - STATE_OLD, - STATE_CONFIRMED, -}; - -struct _mate { - struct udev_list_node node; - char *name; - enum collect_state state; -}; - -static struct udev_list_node bunch; -static int debug; - -/* This can increase dynamically */ -static size_t bufsize = BUFSIZE; - -static inline struct _mate *node_to_mate(struct udev_list_node *node) -{ - return container_of(node, struct _mate, node); -} - -noreturn static void sig_alrm(int signo) -{ - exit(4); -} - -static void usage(void) -{ - printf("%s [options] <checkpoint> <id> <idlist>\n\n" - "Collect variables across events.\n\n" - " -h --help Print this message\n" - " -a --add Add ID <id> to the list <idlist>\n" - " -r --remove Remove ID <id> from the list <idlist>\n" - " -d --debug Debug to stderr\n\n" - " Adds ID <id> to the list governed by <checkpoint>.\n" - " <id> must be part of the list <idlist>.\n" - " If all IDs given by <idlist> are listed (ie collect has been\n" - " invoked for each ID in <idlist>) collect returns 0, the\n" - " number of missing IDs otherwise.\n" - " On error a negative number is returned.\n\n" - , program_invocation_short_name); -} - -/* - * prepare - * - * Prepares the database file - */ -static int prepare(char *dir, char *filename) -{ - char buf[PATH_MAX]; - int r, fd; - - r = mkdir(dir, 0700); - if (r < 0 && errno != EEXIST) - return -errno; - - snprintf(buf, sizeof buf, "%s/%s", dir, filename); - - fd = open(buf, O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR); - if (fd < 0) - fprintf(stderr, "Cannot open %s: %m\n", buf); - - if (lockf(fd,F_TLOCK,0) < 0) { - if (debug) - fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT); - if (errno == EAGAIN || errno == EACCES) { - alarm(UDEV_ALARM_TIMEOUT); - lockf(fd, F_LOCK, 0); - if (debug) - fprintf(stderr, "Acquired lock on %s\n", buf); - } else { - if (debug) - fprintf(stderr, "Could not get lock on %s: %m\n", buf); - } - } - - return fd; -} - -/* - * Read checkpoint file - * - * Tricky reading this. We allocate a buffer twice as large - * as we're going to read. Then we read into the upper half - * of that buffer and start parsing. - * Once we do _not_ find end-of-work terminator (whitespace - * character) we move the upper half to the lower half, - * adjust the read pointer and read the next bit. - * Quite clever methinks :-) - * I should become a programmer ... - * - * Yes, one could have used fgets() for this. But then we'd - * have to use freopen etc which I found quite tedious. - */ -static int checkout(int fd) -{ - int len; - char *buf, *ptr, *word = NULL; - struct _mate *him; - - restart: - len = bufsize >> 1; - buf = malloc(bufsize + 1); - if (!buf) - return log_oom(); - memset(buf, ' ', bufsize); - buf[bufsize] = '\0'; - - ptr = buf + len; - while ((read(fd, buf + len, len)) > 0) { - while (ptr && *ptr) { - word = ptr; - ptr = strpbrk(word," \n\t\r"); - if (!ptr && word < (buf + len)) { - bufsize = bufsize << 1; - if (debug) - fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize); - free(buf); - lseek(fd, 0, SEEK_SET); - goto restart; - } - if (ptr) { - *ptr = '\0'; - ptr++; - if (!strlen(word)) - continue; - - if (debug) - fprintf(stderr, "Found word %s\n", word); - him = malloc(sizeof (struct _mate)); - if (!him) { - free(buf); - return log_oom(); - } - him->name = strdup(word); - if (!him->name) { - free(buf); - free(him); - return log_oom(); - } - him->state = STATE_OLD; - udev_list_node_append(&him->node, &bunch); - word = NULL; - } - } - memcpy(buf, buf + len, len); - memset(buf + len, ' ', len); - - if (!ptr) - ptr = word; - if (!ptr) - break; - ptr -= len; - } - - free(buf); - return 0; -} - -/* - * invite - * - * Adds a new ID 'us' to the internal list, - * marks it as confirmed. - */ -static void invite(char *us) -{ - struct udev_list_node *him_node; - struct _mate *who = NULL; - - if (debug) - fprintf(stderr, "Adding ID '%s'\n", us); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (streq(him->name, us)) { - him->state = STATE_CONFIRMED; - who = him; - } - } - if (debug && !who) - fprintf(stderr, "ID '%s' not in database\n", us); - -} - -/* - * reject - * - * Marks the ID 'us' as invalid, - * causing it to be removed when the - * list is written out. - */ -static void reject(char *us) -{ - struct udev_list_node *him_node; - struct _mate *who = NULL; - - if (debug) - fprintf(stderr, "Removing ID '%s'\n", us); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (streq(him->name, us)) { - him->state = STATE_NONE; - who = him; - } - } - if (debug && !who) - fprintf(stderr, "ID '%s' not in database\n", us); -} - -/* - * kickout - * - * Remove all IDs in the internal list which are not part - * of the list passed via the command line. - */ -static void kickout(void) -{ - struct udev_list_node *him_node; - struct udev_list_node *tmp; - - udev_list_node_foreach_safe(him_node, tmp, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (him->state == STATE_OLD) { - udev_list_node_remove(&him->node); - free(him->name); - free(him); - } - } -} - -/* - * missing - * - * Counts all missing IDs in the internal list. - */ -static int missing(int fd) -{ - char *buf; - int ret = 0; - struct udev_list_node *him_node; - - buf = malloc(bufsize); - if (!buf) - return log_oom(); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (him->state == STATE_NONE) { - ret++; - } else { - while (strlen(him->name)+1 >= bufsize) { - char *tmpbuf; - - bufsize = bufsize << 1; - tmpbuf = realloc(buf, bufsize); - if (!tmpbuf) { - free(buf); - return log_oom(); - } - buf = tmpbuf; - } - snprintf(buf, strlen(him->name)+2, "%s ", him->name); - if (write(fd, buf, strlen(buf)) < 0) { - free(buf); - return -1; - } - } - } - - free(buf); - return ret; -} - -/* - * everybody - * - * Prints out the status of the internal list. - */ -static void everybody(void) -{ - struct udev_list_node *him_node; - const char *state = ""; - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - switch (him->state) { - case STATE_NONE: - state = "none"; - break; - case STATE_OLD: - state = "old"; - break; - case STATE_CONFIRMED: - state = "confirmed"; - break; - } - fprintf(stderr, "ID: %s=%s\n", him->name, state); - } -} - -int main(int argc, char **argv) -{ - struct udev *udev; - static const struct option options[] = { - { "add", no_argument, NULL, 'a' }, - { "remove", no_argument, NULL, 'r' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - int argi; - char *checkpoint, *us; - int fd; - int i; - int ret = EXIT_SUCCESS; - int prune = 0; - char tmpdir[UTIL_PATH_SIZE]; - - udev = udev_new(); - if (udev == NULL) { - ret = EXIT_FAILURE; - goto exit; - } - - for (;;) { - int option; - - option = getopt_long(argc, argv, "ardh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'a': - prune = 0; - break; - case 'r': - prune = 1; - break; - case 'd': - debug = 1; - break; - case 'h': - usage(); - goto exit; - default: - ret = 1; - goto exit; - } - } - - argi = optind; - if (argi + 2 > argc) { - printf("Missing parameter(s)\n"); - ret = 1; - goto exit; - } - checkpoint = argv[argi++]; - us = argv[argi++]; - - if (signal(SIGALRM, sig_alrm) == SIG_ERR) { - fprintf(stderr, "Cannot set SIGALRM: %m\n"); - ret = 2; - goto exit; - } - - udev_list_node_init(&bunch); - - if (debug) - fprintf(stderr, "Using checkpoint '%s'\n", checkpoint); - - strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL); - fd = prepare(tmpdir, checkpoint); - if (fd < 0) { - ret = 3; - goto out; - } - - if (checkout(fd) < 0) { - ret = 2; - goto out; - } - - for (i = argi; i < argc; i++) { - struct udev_list_node *him_node; - struct _mate *who; - - who = NULL; - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (streq(him->name, argv[i])) - who = him; - } - if (!who) { - struct _mate *him; - - if (debug) - fprintf(stderr, "ID %s: not in database\n", argv[i]); - him = new(struct _mate, 1); - if (!him) { - ret = ENOMEM; - goto out; - } - - him->name = strdup(argv[i]); - if (!him->name) { - free(him); - ret = ENOMEM; - goto out; - } - - him->state = STATE_NONE; - udev_list_node_append(&him->node, &bunch); - } else { - if (debug) - fprintf(stderr, "ID %s: found in database\n", argv[i]); - who->state = STATE_CONFIRMED; - } - } - - if (prune) - reject(us); - else - invite(us); - - if (debug) { - everybody(); - fprintf(stderr, "Prune lists\n"); - } - kickout(); - - lseek(fd, 0, SEEK_SET); - ftruncate(fd, 0); - ret = missing(fd); - - lockf(fd, F_ULOCK, 0); - close(fd); -out: - if (debug) - everybody(); - if (ret >= 0) - printf("COLLECT_%s=%d\n", checkpoint, ret); -exit: - udev_unref(udev); - return ret; -} diff --git a/src/udev/mtd_probe/Makefile b/src/udev/mtd_probe/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/mtd_probe/Makefile +++ /dev/null @@ -1 +0,0 @@ -../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 deleted file mode 100644 index 462fab7623..0000000000 --- a/src/udev/mtd_probe/mtd_probe.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include <fcntl.h> -#include <mtd/mtd-user.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "mtd_probe.h" - -int main(int argc, char** argv) -{ - int mtd_fd; - int error; - mtd_info_t mtd_info; - - if (argc != 2) { - printf("usage: mtd_probe /dev/mtd[n]\n"); - return 1; - } - - mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC); - if (mtd_fd == -1) { - perror("open"); - exit(-1); - } - - error = ioctl(mtd_fd, MEMGETINFO, &mtd_info); - if (error == -1) { - perror("ioctl"); - exit(-1); - } - - probe_smart_media(mtd_fd, &mtd_info); - return -1; -} diff --git a/src/udev/mtd_probe/mtd_probe.h b/src/udev/mtd_probe/mtd_probe.h deleted file mode 100644 index 68e4954537..0000000000 --- a/src/udev/mtd_probe/mtd_probe.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include <mtd/mtd-user.h> - -#include "macro.h" - -/* Full oob structure as written on the flash */ -struct sm_oob { - uint32_t reserved; - uint8_t data_status; - uint8_t block_status; - uint8_t lba_copy1[2]; - uint8_t ecc2[3]; - uint8_t lba_copy2[2]; - uint8_t ecc1[3]; -} _packed_; - -/* one sector is always 512 bytes, but it can consist of two nand pages */ -#define SM_SECTOR_SIZE 512 - -/* oob area is also 16 bytes, but might be from two pages */ -#define SM_OOB_SIZE 16 - -/* This is maximum zone size, and all devices that have more that one zone - have this size */ -#define SM_MAX_ZONE_SIZE 1024 - -/* support for small page nand */ -#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/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c deleted file mode 100644 index 2a7ba17637..0000000000 --- a/src/udev/mtd_probe/probe_smartmedia.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include <fcntl.h> -#include <mtd/mtd-user.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "mtd_probe.h" - -static const uint8_t cis_signature[] = { - 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 -}; - - -void probe_smart_media(int mtd_fd, mtd_info_t* info) -{ - int sector_size; - int block_size; - int size_in_megs; - int spare_count; - char* cis_buffer = malloc(SM_SECTOR_SIZE); - int offset; - int cis_found = 0; - - if (!cis_buffer) - return; - - if (info->type != MTD_NANDFLASH) - goto exit; - - sector_size = info->writesize; - block_size = info->erasesize; - size_in_megs = info->size / (1024 * 1024); - - if (sector_size != SM_SECTOR_SIZE && sector_size != SM_SMALL_PAGE) - goto exit; - - switch(size_in_megs) { - case 1: - case 2: - spare_count = 6; - break; - case 4: - spare_count = 12; - break; - default: - spare_count = 24; - break; - } - - for (offset = 0 ; offset < block_size * spare_count ; - offset += sector_size) { - lseek(mtd_fd, SEEK_SET, offset); - if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE) { - cis_found = 1; - break; - } - } - - if (!cis_found) - goto exit; - - if (memcmp(cis_buffer, cis_signature, sizeof(cis_signature)) != 0 && - (memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, - sizeof(cis_signature)) != 0)) - goto exit; - - printf("MTD_FTL=smartmedia\n"); - free(cis_buffer); - exit(0); -exit: - free(cis_buffer); - return; -} diff --git a/src/udev/net/.gitignore b/src/udev/net/.gitignore deleted file mode 100644 index 9ca85bacc9..0000000000 --- a/src/udev/net/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/link-config-gperf.c diff --git a/src/udev/net/Makefile b/src/udev/net/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/udev/net/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile
\ No newline at end of file diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c deleted file mode 100644 index 708a665576..0000000000 --- a/src/udev/net/ethtool-util.c +++ /dev/null @@ -1,325 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <net/if.h> -#include <sys/ioctl.h> -#include <linux/ethtool.h> -#include <linux/sockios.h> - -#include "conf-parser.h" -#include "ethtool-util.h" -#include "log.h" -#include "socket-util.h" -#include "string-table.h" -#include "strxcpyx.h" -#include "util.h" - -static const char* const duplex_table[_DUP_MAX] = { - [DUP_FULL] = "full", - [DUP_HALF] = "half" -}; - -DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex); -DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting"); - -static const char* const wol_table[_WOL_MAX] = { - [WOL_PHY] = "phy", - [WOL_MAGIC] = "magic", - [WOL_OFF] = "off" -}; - -DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan); -DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); - -static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { - [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", - [NET_DEV_FEAT_GRO] = "rx-gro", - [NET_DEV_FEAT_LRO] = "rx-lro", - [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", - [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation", -}; - -int ethtool_connect(int *ret) { - int fd; - - assert_return(ret, -EINVAL); - - fd = socket_ioctl_fd(); - if (fd < 0) - return fd; - *ret = fd; - - return 0; -} - -int ethtool_get_driver(int *fd, const char *ifname, char **ret) { - struct ethtool_drvinfo ecmd = { - .cmd = ETHTOOL_GDRVINFO - }; - struct ifreq ifr = { - .ifr_data = (void*) &ecmd - }; - char *d; - int r; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - - d = strdup(ecmd.driver); - if (!d) - return -ENOMEM; - - *ret = d; - return 0; -} - -int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { - struct ethtool_cmd ecmd = { - .cmd = ETHTOOL_GSET - }; - struct ifreq ifr = { - .ifr_data = (void*) &ecmd - }; - bool need_update = false; - int r; - - if (speed == 0 && duplex == _DUP_INVALID) - return 0; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - - if (ethtool_cmd_speed(&ecmd) != speed) { - ethtool_cmd_speed_set(&ecmd, speed); - need_update = true; - } - - switch (duplex) { - case DUP_HALF: - if (ecmd.duplex != DUPLEX_HALF) { - ecmd.duplex = DUPLEX_HALF; - need_update = true; - } - break; - case DUP_FULL: - if (ecmd.duplex != DUPLEX_FULL) { - ecmd.duplex = DUPLEX_FULL; - need_update = true; - } - break; - default: - break; - } - - if (need_update) { - ecmd.cmd = ETHTOOL_SSET; - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - } - - return 0; -} - -int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { - struct ethtool_wolinfo ecmd = { - .cmd = ETHTOOL_GWOL - }; - struct ifreq ifr = { - .ifr_data = (void*) &ecmd - }; - bool need_update = false; - int r; - - if (wol == _WOL_INVALID) - return 0; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - - switch (wol) { - case WOL_PHY: - if (ecmd.wolopts != WAKE_PHY) { - ecmd.wolopts = WAKE_PHY; - need_update = true; - } - break; - case WOL_MAGIC: - if (ecmd.wolopts != WAKE_MAGIC) { - ecmd.wolopts = WAKE_MAGIC; - need_update = true; - } - break; - case WOL_OFF: - if (ecmd.wolopts != 0) { - ecmd.wolopts = 0; - need_update = true; - } - break; - default: - break; - } - - if (need_update) { - ecmd.cmd = ETHTOOL_SWOL; - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - } - - return 0; -} - -static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { - _cleanup_free_ struct ethtool_gstrings *strings = NULL; - struct { - struct ethtool_sset_info info; - uint32_t space; - } buffer = { - .info = { - .cmd = ETHTOOL_GSSET_INFO, - .sset_mask = UINT64_C(1) << stringset_id, - }, - }; - unsigned len; - int r; - - ifr->ifr_data = (void *) &buffer.info; - - r = ioctl(*fd, SIOCETHTOOL, ifr); - if (r < 0) - return -errno; - - if (!buffer.info.sset_mask) - return -EINVAL; - - len = buffer.info.data[0]; - - strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN); - if (!strings) - return -ENOMEM; - - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = stringset_id; - strings->len = len; - - ifr->ifr_data = (void *) strings; - - r = ioctl(*fd, SIOCETHTOOL, ifr); - if (r < 0) - return -errno; - - *gstrings = strings; - strings = NULL; - - return 0; -} - -static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) { - unsigned i; - - for (i = 0; i < strings->len; i++) { - if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature)) - return i; - } - - return -1; -} - -int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { - _cleanup_free_ struct ethtool_gstrings *strings = NULL; - struct ethtool_sfeatures *sfeatures; - int block, bit, i, r; - struct ifreq ifr = {}; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings); - if (r < 0) - return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname); - - sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0])); - sfeatures->cmd = ETHTOOL_SFEATURES; - sfeatures->size = DIV_ROUND_UP(strings->len, 32U); - - for (i = 0; i < _NET_DEV_FEAT_MAX; i++) { - - if (features[i] != -1) { - - r = find_feature_index(strings, netdev_feature_table[i]); - if (r < 0) { - log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]); - continue; - } - - block = r / 32; - bit = r % 32; - - sfeatures->features[block].valid |= 1 << bit; - - if (features[i]) - sfeatures->features[block].requested |= 1 << bit; - else - sfeatures->features[block].requested &= ~(1 << bit); - } - } - - ifr.ifr_data = (void *) sfeatures; - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname); - - return 0; -} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h deleted file mode 100644 index 0744164653..0000000000 --- a/src/udev/net/ethtool-util.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <macro.h> - -/* we can't use DUPLEX_ prefix, as it - * clashes with <linux/ethtool.h> */ -typedef enum Duplex { - DUP_FULL, - DUP_HALF, - _DUP_MAX, - _DUP_INVALID = -1 -} Duplex; - -typedef enum WakeOnLan { - WOL_PHY, - WOL_MAGIC, - WOL_OFF, - _WOL_MAX, - _WOL_INVALID = -1 -} WakeOnLan; - -typedef enum NetDevFeature { - NET_DEV_FEAT_GSO, - NET_DEV_FEAT_GRO, - NET_DEV_FEAT_LRO, - NET_DEV_FEAT_TSO, - NET_DEV_FEAT_UFO, - _NET_DEV_FEAT_MAX, - _NET_DEV_FEAT_INVALID = -1 -} NetDevFeature; - -int ethtool_connect(int *ret); - -int ethtool_get_driver(int *fd, const char *ifname, char **ret); -int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); -int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); -int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features); - -const char *duplex_to_string(Duplex d) _const_; -Duplex duplex_from_string(const char *d) _pure_; - -const char *wol_to_string(WakeOnLan wol) _const_; -WakeOnLan wol_from_string(const char *wol) _pure_; - -int config_parse_duplex(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_wol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf deleted file mode 100644 index f8b85cbd13..0000000000 --- a/src/udev/net/link-config-gperf.gperf +++ /dev/null @@ -1,42 +0,0 @@ -%{ -#include <stddef.h> -#include "conf-parser.h" -#include "network-internal.h" -#include "link-config.h" -#include "ethtool-util.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name link_config_gperf_hash -%define lookup-function-name link_config_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac) -Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) -Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) -Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) -Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch) -Link.Description, config_parse_string, 0, offsetof(link_config, description) -Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) -Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) -Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) -Link.Name, config_parse_ifname, 0, offsetof(link_config, name) -Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) -Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu) -Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) -Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) -Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) -Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) -Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) -Link.UDPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_UFO]) -Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO]) -Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO]) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c deleted file mode 100644 index ece9248c2a..0000000000 --- a/src/udev/net/link-config.c +++ /dev/null @@ -1,517 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <netinet/ether.h> - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "conf-files.h" -#include "conf-parser.h" -#include "ethtool-util.h" -#include "fd-util.h" -#include "libudev-private.h" -#include "link-config.h" -#include "log.h" -#include "missing.h" -#include "netlink-util.h" -#include "network-internal.h" -#include "parse-util.h" -#include "path-util.h" -#include "proc-cmdline.h" -#include "random-util.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -struct link_config_ctx { - LIST_HEAD(link_config, links); - - int ethtool_fd; - - bool enable_name_policy; - - sd_netlink *rtnl; - - usec_t link_dirs_ts_usec; -}; - -static const char* const link_dirs[] = { - "/etc/systemd/network", - "/run/systemd/network", - "/usr/lib/systemd/network", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/network", -#endif - NULL}; - -static void link_config_free(link_config *link) { - if (!link) - return; - - free(link->filename); - - free(link->match_mac); - 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); - free(link->match_kernel); - free(link->match_arch); - - free(link->description); - free(link->mac); - free(link->name_policy); - free(link->name); - free(link->alias); - - free(link); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(link_config*, link_config_free); - -static void link_configs_free(link_config_ctx *ctx) { - link_config *link, *link_next; - - if (!ctx) - return; - - LIST_FOREACH_SAFE(links, link, link_next, ctx->links) - link_config_free(link); -} - -void link_config_ctx_free(link_config_ctx *ctx) { - if (!ctx) - return; - - safe_close(ctx->ethtool_fd); - - sd_netlink_unref(ctx->rtnl); - - link_configs_free(ctx); - - free(ctx); - - return; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free); - -int link_config_ctx_new(link_config_ctx **ret) { - _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL; - - if (!ret) - return -EINVAL; - - ctx = new0(link_config_ctx, 1); - if (!ctx) - return -ENOMEM; - - LIST_HEAD_INIT(ctx->links); - - ctx->ethtool_fd = -1; - - ctx->enable_name_policy = true; - - *ret = ctx; - ctx = NULL; - - return 0; -} - -static int load_link(link_config_ctx *ctx, const char *filename) { - _cleanup_(link_config_freep) link_config *link = NULL; - _cleanup_fclose_ FILE *file = NULL; - int r; - - assert(ctx); - assert(filename); - - file = fopen(filename, "re"); - if (!file) { - if (errno == ENOENT) - return 0; - else - return -errno; - } - - if (null_or_empty_fd(fileno(file))) { - log_debug("Skipping empty file: %s", filename); - return 0; - } - - link = new0(link_config, 1); - if (!link) - return log_oom(); - - link->mac_policy = _MACPOLICY_INVALID; - link->wol = _WOL_INVALID; - link->duplex = _DUP_INVALID; - - memset(&link->features, -1, _NET_DEV_FEAT_MAX); - - r = config_parse(NULL, filename, file, - "Match\0Link\0Ethernet\0", - config_item_perf_lookup, link_config_gperf_lookup, - false, false, true, link); - if (r < 0) - return r; - 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); - link = NULL; - - return 0; -} - -static bool enable_name_policy(void) { - _cleanup_free_ char *value = NULL; - int r; - - r = get_proc_cmdline_key("net.ifnames=", &value); - if (r > 0 && streq(value, "0")) - return false; - - return true; -} - -int link_config_load(link_config_ctx *ctx) { - int r; - _cleanup_strv_free_ char **files; - char **f; - - link_configs_free(ctx); - - if (!enable_name_policy()) { - ctx->enable_name_policy = false; - log_info("Network interface NamePolicy= disabled on kernel command line, ignoring."); - } - - /* update timestamp */ - paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); - - r = conf_files_list_strv(&files, ".link", NULL, link_dirs); - if (r < 0) - return log_error_errno(r, "failed to enumerate link files: %m"); - - STRV_FOREACH_BACKWARDS(f, files) { - r = load_link(ctx, *f); - if (r < 0) - return r; - } - - return 0; -} - -bool link_config_should_reload(link_config_ctx *ctx) { - return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false); -} - -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; - - attr_value = udev_device_get_sysattr_value(device, "address"); - - if (net_match_config(link->match_mac, link->match_path, link->match_driver, - link->match_type, link->match_name, link->match_host, - link->match_virt, link->match_kernel, link->match_arch, - attr_value ? ether_aton(attr_value) : NULL, - udev_device_get_property_value(device, "ID_PATH"), - udev_device_get_driver(udev_device_get_parent(device)), - udev_device_get_property_value(device, "ID_NET_DRIVER"), - udev_device_get_devtype(device), - udev_device_get_sysname(device))) { - if (link->match_name) { - unsigned char name_assign_type = NET_NAME_UNKNOWN; - - attr_value = udev_device_get_sysattr_value(device, "name_assign_type"); - if (attr_value) - (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'", - link->filename, udev_device_get_sysname(device)); - *ret = link; - - return 0; - } else if (name_assign_type == NET_NAME_RENAMED) { - log_warning("Config file %s matches device based on renamed interface name '%s', ignoring", - link->filename, udev_device_get_sysname(device)); - - continue; - } - } - - log_debug("Config file %s applies to device %s", - link->filename, udev_device_get_sysname(device)); - - *ret = link; - - return 0; - } - } - - *ret = NULL; - - return -ENOENT; -} - -static bool mac_is_random(struct udev_device *device) { - const char *s; - unsigned type; - int r; - - /* if we can't get the assign type, assume it is not random */ - s = udev_device_get_sysattr_value(device, "addr_assign_type"); - if (!s) - return false; - - r = safe_atou(s, &type); - if (r < 0) - return false; - - return type == NET_ADDR_RANDOM; -} - -static bool should_rename(struct udev_device *device, bool respect_predictable) { - const char *s; - unsigned type; - int r; - - /* if we can't get the assgin type, assume we should rename */ - s = udev_device_get_sysattr_value(device, "name_assign_type"); - if (!s) - return true; - - r = safe_atou(s, &type); - if (r < 0) - return true; - - switch (type) { - case NET_NAME_USER: - case NET_NAME_RENAMED: - /* these were already named by userspace, do not touch again */ - return false; - case NET_NAME_PREDICTABLE: - /* the kernel claims to have given a predictable name */ - if (respect_predictable) - return false; - /* fall through */ - case NET_NAME_ENUM: - default: - /* the name is known to be bad, or of an unknown type */ - return true; - } -} - -static int get_mac(struct udev_device *device, bool want_random, - struct ether_addr *mac) { - int r; - - if (want_random) - random_bytes(mac->ether_addr_octet, ETH_ALEN); - else { - uint64_t result; - - r = net_get_unique_predictable_data(device, &result); - if (r < 0) - return r; - - assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, &result, ETH_ALEN); - } - - /* see eth_random_addr in the kernel */ - mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ - mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - - return 0; -} - -int link_config_apply(link_config_ctx *ctx, link_config *config, - struct udev_device *device, const char **name) { - const char *old_name; - const char *new_name = NULL; - struct ether_addr generated_mac; - struct ether_addr *mac = NULL; - bool respect_predictable = false; - int r, ifindex; - - assert(ctx); - assert(config); - assert(device); - assert(name); - - old_name = udev_device_get_sysname(device); - if (!old_name) - return -EINVAL; - - 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 %zu Mbps (%s): %m", - old_name, config->speed / 1024, - duplex_to_string(config->duplex)); - - r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); - if (r < 0) - log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m", - old_name, wol_to_string(config->wol)); - - r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features); - if (r < 0) - log_warning_errno(r, "Could not set offload features of %s: %m", old_name); - - ifindex = udev_device_get_ifindex(device); - if (ifindex <= 0) { - log_warning("Could not find ifindex"); - return -ENODEV; - } - - if (ctx->enable_name_policy && config->name_policy) { - NamePolicy *policy; - - for (policy = config->name_policy; - !new_name && *policy != _NAMEPOLICY_INVALID; policy++) { - switch (*policy) { - case NAMEPOLICY_KERNEL: - respect_predictable = true; - break; - case NAMEPOLICY_DATABASE: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE"); - break; - case NAMEPOLICY_ONBOARD: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"); - break; - case NAMEPOLICY_SLOT: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT"); - break; - case NAMEPOLICY_PATH: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH"); - break; - case NAMEPOLICY_MAC: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC"); - break; - default: - break; - } - } - } - - if (should_rename(device, respect_predictable)) { - /* if not set by policy, fall back manually set name */ - if (!new_name) - new_name = config->name; - } else - new_name = NULL; - - switch (config->mac_policy) { - case MACPOLICY_PERSISTENT: - if (mac_is_random(device)) { - r = get_mac(device, false, &generated_mac); - if (r == -ENOENT) { - log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name); - break; - } else if (r < 0) - return r; - mac = &generated_mac; - } - break; - case MACPOLICY_RANDOM: - if (!mac_is_random(device)) { - r = get_mac(device, true, &generated_mac); - if (r == -ENOENT) { - log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name); - break; - } else if (r < 0) - return r; - mac = &generated_mac; - } - break; - case MACPOLICY_NONE: - default: - mac = config->mac; - } - - 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); - - *name = new_name; - - return 0; -} - -int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) { - const char *name; - char *driver = NULL; - int r; - - name = udev_device_get_sysname(device); - if (!name) - return -EINVAL; - - r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver); - if (r < 0) - return r; - - *ret = driver; - return 0; -} - -static const char* const mac_policy_table[_MACPOLICY_MAX] = { - [MACPOLICY_PERSISTENT] = "persistent", - [MACPOLICY_RANDOM] = "random", - [MACPOLICY_NONE] = "none" -}; - -DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); -DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, - "Failed to parse MAC address policy"); - -static const char* const name_policy_table[_NAMEPOLICY_MAX] = { - [NAMEPOLICY_KERNEL] = "kernel", - [NAMEPOLICY_DATABASE] = "database", - [NAMEPOLICY_ONBOARD] = "onboard", - [NAMEPOLICY_SLOT] = "slot", - [NAMEPOLICY_PATH] = "path", - [NAMEPOLICY_MAC] = "mac" -}; - -DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy); -DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, - _NAMEPOLICY_INVALID, - "Failed to parse interface name policy"); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h deleted file mode 100644 index b0d8ceb76a..0000000000 --- a/src/udev/net/link-config.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "libudev.h" - -#include "condition.h" -#include "ethtool-util.h" -#include "list.h" - -typedef struct link_config_ctx link_config_ctx; -typedef struct link_config link_config; - -typedef enum MACPolicy { - MACPOLICY_PERSISTENT, - MACPOLICY_RANDOM, - MACPOLICY_NONE, - _MACPOLICY_MAX, - _MACPOLICY_INVALID = -1 -} MACPolicy; - -typedef enum NamePolicy { - NAMEPOLICY_KERNEL, - NAMEPOLICY_DATABASE, - NAMEPOLICY_ONBOARD, - NAMEPOLICY_SLOT, - NAMEPOLICY_PATH, - NAMEPOLICY_MAC, - _NAMEPOLICY_MAX, - _NAMEPOLICY_INVALID = -1 -} NamePolicy; - -struct link_config { - char *filename; - - struct ether_addr *match_mac; - char **match_path; - char **match_driver; - char **match_type; - char **match_name; - Condition *match_host; - Condition *match_virt; - Condition *match_kernel; - Condition *match_arch; - - char *description; - struct ether_addr *mac; - MACPolicy mac_policy; - NamePolicy *name_policy; - char *name; - char *alias; - size_t mtu; - size_t speed; - Duplex duplex; - WakeOnLan wol; - NetDevFeature features[_NET_DEV_FEAT_MAX]; - - LIST_FIELDS(link_config, links); -}; - -int link_config_ctx_new(link_config_ctx **ret); -void link_config_ctx_free(link_config_ctx *ctx); - -int link_config_load(link_config_ctx *ctx); -bool link_config_should_reload(link_config_ctx *ctx); - -int link_config_get(link_config_ctx *ctx, struct udev_device *device, struct link_config **ret); -int link_config_apply(link_config_ctx *ctx, struct link_config *config, struct udev_device *device, const char **name); - -int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret); - -const char *name_policy_to_string(NamePolicy p) _const_; -NamePolicy name_policy_from_string(const char *p) _pure_; - -const char *mac_policy_to_string(MACPolicy p) _const_; -MACPolicy mac_policy_from_string(const char *p) _pure_; - -/* gperf lookup function */ -const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length); - -int config_parse_mac_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_name_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/udev/scsi_id/.gitignore b/src/udev/scsi_id/.gitignore deleted file mode 100644 index 6aebddd809..0000000000 --- a/src/udev/scsi_id/.gitignore +++ /dev/null @@ -1 +0,0 @@ -scsi_id_version.h diff --git a/src/udev/scsi_id/Makefile b/src/udev/scsi_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/scsi_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/udev/scsi_id/README b/src/udev/scsi_id/README deleted file mode 100644 index 9cfe73991c..0000000000 --- a/src/udev/scsi_id/README +++ /dev/null @@ -1,4 +0,0 @@ -scsi_id - generate a SCSI unique identifier for a given SCSI device - -Please send questions, comments or patches to <patmans@us.ibm.com> or -<linux-hotplug-devel@lists.sourceforge.net>. diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h deleted file mode 100644 index a27a84a40a..0000000000 --- a/src/udev/scsi_id/scsi.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -/* - * scsi.h - * - * General scsi and linux scsi specific defines and structs. - * - * Copyright (C) IBM Corp. 2003 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2 of the License. - */ - -#include <scsi/scsi.h> - -struct scsi_ioctl_command { - unsigned int inlen; /* excluding scsi command length */ - unsigned int outlen; - unsigned char data[1]; - /* on input, scsi command starts here then opt. data */ -}; - -/* - * Default 5 second timeout - */ -#define DEF_TIMEOUT 5000 - -#define SENSE_BUFF_LEN 32 - -/* - * The request buffer size passed to the SCSI INQUIRY commands, use 254, - * as this is a nice value for some devices, especially some of the usb - * mass storage devices. - */ -#define SCSI_INQ_BUFF_LEN 254 - -/* - * SCSI INQUIRY vendor and model (really product) lengths. - */ -#define VENDOR_LENGTH 8 -#define MODEL_LENGTH 16 - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 - -/* - * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the - * SCSI Primary Commands specification for details. - */ - -/* - * id type values of id descriptors. These are assumed to fit in 4 bits. - */ -#define SCSI_ID_VENDOR_SPECIFIC 0 -#define SCSI_ID_T10_VENDOR 1 -#define SCSI_ID_EUI_64 2 -#define SCSI_ID_NAA 3 -#define SCSI_ID_RELPORT 4 -#define SCSI_ID_TGTGROUP 5 -#define SCSI_ID_LUNGROUP 6 -#define SCSI_ID_MD5 7 -#define SCSI_ID_NAME 8 - -/* - * Supported NAA values. These fit in 4 bits, so the "don't care" value - * cannot conflict with real values. - */ -#define SCSI_ID_NAA_DONT_CARE 0xff -#define SCSI_ID_NAA_IEEE_REG 0x05 -#define SCSI_ID_NAA_IEEE_REG_EXTENDED 0x06 - -/* - * Supported Code Set values. - */ -#define SCSI_ID_BINARY 1 -#define SCSI_ID_ASCII 2 - -struct scsi_id_search_values { - u_char id_type; - u_char naa_type; - u_char code_set; -}; - -/* - * Following are the "true" SCSI status codes. Linux has traditionally - * used a 1 bit right and masked version of these. So now CHECK_CONDITION - * and friends (in <scsi/scsi.h>) are deprecated. - */ -#define SCSI_CHECK_CONDITION 0x02 -#define SCSI_CONDITION_MET 0x04 -#define SCSI_BUSY 0x08 -#define SCSI_IMMEDIATE 0x10 -#define SCSI_IMMEDIATE_CONDITION_MET 0x14 -#define SCSI_RESERVATION_CONFLICT 0x18 -#define SCSI_COMMAND_TERMINATED 0x22 -#define SCSI_TASK_SET_FULL 0x28 -#define SCSI_ACA_ACTIVE 0x30 -#define SCSI_TASK_ABORTED 0x40 diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c deleted file mode 100644 index 4655691642..0000000000 --- a/src/udev/scsi_id/scsi_id.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * Copyright (C) SUSE Linux Products GmbH, 2006 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <signal.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "libudev.h" - -#include "fd-util.h" -#include "libudev-private.h" -#include "scsi_id.h" -#include "string-util.h" -#include "udev-util.h" - -static const struct option options[] = { - { "device", required_argument, NULL, 'd' }, - { "config", required_argument, NULL, 'f' }, - { "page", required_argument, NULL, 'p' }, - { "blacklisted", no_argument, NULL, 'b' }, - { "whitelisted", no_argument, NULL, 'g' }, - { "replace-whitespace", no_argument, NULL, 'u' }, - { "sg-version", required_argument, NULL, 's' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, /* don't advertise -V */ - { "export", no_argument, NULL, 'x' }, - { "help", no_argument, NULL, 'h' }, - {} -}; - -static bool all_good = false; -static bool dev_specified = false; -static char config_file[MAX_PATH_LEN] = "/etc/scsi_id.config"; -static enum page_code default_page_code = PAGE_UNSPECIFIED; -static int sg_version = 4; -static bool reformat_serial = false; -static bool export = false; -static char vendor_str[64]; -static char model_str[64]; -static char vendor_enc_str[256]; -static char model_enc_str[256]; -static char revision_str[16]; -static char type_str[16]; - -static void set_type(const char *from, char *to, size_t len) -{ - int type_num; - char *eptr; - const char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - type = "optical"; - break; - case 5: - type = "cd"; - break; - case 7: - type = "optical"; - break; - case 0xe: - type = "disk"; - break; - case 0xf: - type = "optical"; - break; - default: - break; - } - } - strscpy(to, len, type); -} - -/* - * get_value: - * - * buf points to an '=' followed by a quoted string ("foo") or a string ending - * with a space or ','. - * - * Return a pointer to the NUL terminated string, returns NULL if no - * matches. - */ -static char *get_value(char **buffer) -{ - static const char *quote_string = "\"\n"; - static const char *comma_string = ",\n"; - char *val; - const char *end; - - if (**buffer == '"') { - /* - * skip leading quote, terminate when quote seen - */ - (*buffer)++; - end = quote_string; - } else { - end = comma_string; - } - val = strsep(buffer, end); - if (val && end == quote_string) - /* - * skip trailing quote - */ - (*buffer)++; - - while (isspace(**buffer)) - (*buffer)++; - - return val; -} - -static int argc_count(char *opts) -{ - int i = 0; - while (*opts != '\0') - if (*opts++ == ' ') - i++; - return i; -} - -/* - * get_file_options: - * - * If vendor == NULL, find a line in the config file with only "OPTIONS="; - * if vendor and model are set find the first OPTIONS line in the config - * file that matches. Set argc and argv to match the OPTIONS string. - * - * vendor and model can end in '\n'. - */ -static int get_file_options(struct udev *udev, - const char *vendor, const char *model, - int *argc, char ***newargv) -{ - char *buffer; - _cleanup_fclose_ FILE *f; - char *buf; - char *str1; - char *vendor_in, *model_in, *options_in; /* read in from file */ - int lineno; - int c; - int retval = 0; - - f = fopen(config_file, "re"); - if (f == NULL) { - if (errno == ENOENT) - return 1; - else { - log_error_errno(errno, "can't open %s: %m", config_file); - return -1; - } - } - - /* - * Allocate a buffer rather than put it on the stack so we can - * keep it around to parse any options (any allocated newargv - * points into this buffer for its strings). - */ - buffer = malloc(MAX_BUFFER_LEN); - if (!buffer) - return log_oom(); - - *newargv = NULL; - lineno = 0; - for (;;) { - vendor_in = model_in = options_in = NULL; - - buf = fgets(buffer, MAX_BUFFER_LEN, f); - if (buf == NULL) - break; - lineno++; - if (buf[strlen(buffer) - 1] != '\n') { - log_error("Config file line %d too long", lineno); - break; - } - - while (isspace(*buf)) - buf++; - - /* blank or all whitespace line */ - if (*buf == '\0') - continue; - - /* comment line */ - if (*buf == '#') - continue; - - str1 = strsep(&buf, "="); - if (str1 && strcaseeq(str1, "VENDOR")) { - str1 = get_value(&buf); - if (!str1) { - retval = log_oom(); - break; - } - vendor_in = str1; - - str1 = strsep(&buf, "="); - if (str1 && strcaseeq(str1, "MODEL")) { - str1 = get_value(&buf); - if (!str1) { - retval = log_oom(); - break; - } - model_in = str1; - str1 = strsep(&buf, "="); - } - } - - if (str1 && strcaseeq(str1, "OPTIONS")) { - str1 = get_value(&buf); - if (!str1) { - retval = log_oom(); - break; - } - options_in = str1; - } - - /* - * Only allow: [vendor=foo[,model=bar]]options=stuff - */ - if (!options_in || (!vendor_in && model_in)) { - log_error("Error parsing config file line %d '%s'", lineno, buffer); - retval = -1; - break; - } - if (vendor == NULL) { - if (vendor_in == NULL) - break; - } else if (vendor_in && - strneq(vendor, vendor_in, strlen(vendor_in)) && - (!model_in || - (strneq(model, model_in, strlen(model_in))))) { - /* - * Matched vendor and optionally model. - * - * Note: a short vendor_in or model_in can - * give a partial match (that is FOO - * matches FOOBAR). - */ - break; - } - } - - if (retval == 0) { - if (vendor_in != NULL || model_in != NULL || - options_in != NULL) { - /* - * Something matched. Allocate newargv, and store - * values found in options_in. - */ - strcpy(buffer, options_in); - c = argc_count(buffer) + 2; - *newargv = calloc(c, sizeof(**newargv)); - if (!*newargv) - retval = log_oom(); - else { - *argc = c; - c = 0; - /* - * argv[0] at 0 is skipped by getopt, but - * store the buffer address there for - * later freeing - */ - (*newargv)[c] = buffer; - for (c = 1; c < *argc; c++) - (*newargv)[c] = strsep(&buffer, " \t"); - } - } else { - /* No matches */ - retval = 1; - } - } - if (retval != 0) - free(buffer); - return retval; -} - -static void help(void) { - printf("Usage: %s [OPTION...] DEVICE\n\n" - "SCSI device identification.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n\n" - " -d --device= Device node for SG_IO commands\n" - " -f --config= Location of config file\n" - " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" - " -s --sg-version=3|4 Use SGv3 or SGv4\n" - " -b --blacklisted Treat device as blacklisted\n" - " -g --whitelisted Treat device as whitelisted\n" - " -u --replace-whitespace Replace all whitespace by underscores\n" - " -v --verbose Verbose logging\n" - " -x --export Print values as environment keys\n" - , program_invocation_short_name); - -} - -static int set_options(struct udev *udev, - int argc, char **argv, - char *maj_min_dev) -{ - int option; - - /* - * optind is a global extern used by getopt. Since we can call - * set_options twice (once for command line, and once for config - * file) we have to reset this back to 1. - */ - optind = 1; - while ((option = getopt_long(argc, argv, "d:f:gp:uvVxh", options, NULL)) >= 0) - switch (option) { - case 'b': - all_good = false; - break; - - case 'd': - dev_specified = true; - strscpy(maj_min_dev, MAX_PATH_LEN, optarg); - break; - - case 'f': - strscpy(config_file, MAX_PATH_LEN, optarg); - break; - - case 'g': - all_good = true; - break; - - case 'h': - help(); - exit(0); - - case 'p': - if (streq(optarg, "0x80")) - default_page_code = PAGE_80; - else if (streq(optarg, "0x83")) - default_page_code = PAGE_83; - else if (streq(optarg, "pre-spc3-83")) - default_page_code = PAGE_83_PRE_SPC3; - else { - log_error("Unknown page code '%s'", optarg); - return -1; - } - break; - - case 's': - sg_version = atoi(optarg); - if (sg_version < 3 || sg_version > 4) { - log_error("Unknown SG version '%s'", optarg); - return -1; - } - break; - - case 'u': - reformat_serial = true; - break; - - case 'v': - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - log_open(); - break; - - case 'V': - printf("%s\n", VERSION); - exit(0); - - case 'x': - export = true; - break; - - case '?': - return -1; - - default: - assert_not_reached("Unknown option"); - } - - if (optind < argc && !dev_specified) { - dev_specified = true; - strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]); - } - - return 0; -} - -static int per_dev_options(struct udev *udev, - struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) -{ - int retval; - int newargc; - char **newargv = NULL; - int option; - - *good_bad = all_good; - *page_code = default_page_code; - - retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); - - optind = 1; /* reset this global extern */ - while (retval == 0) { - option = getopt_long(newargc, newargv, "bgp:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'b': - *good_bad = 0; - break; - - case 'g': - *good_bad = 1; - break; - - case 'p': - if (streq(optarg, "0x80")) { - *page_code = PAGE_80; - } else if (streq(optarg, "0x83")) { - *page_code = PAGE_83; - } else if (streq(optarg, "pre-spc3-83")) { - *page_code = PAGE_83_PRE_SPC3; - } else { - log_error("Unknown page code '%s'", optarg); - retval = -1; - } - break; - - default: - log_error("Unknown or bad option '%c' (0x%x)", option, option); - retval = -1; - break; - } - } - - if (newargv) { - free(newargv[0]); - free(newargv); - } - return retval; -} - -static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) -{ - int retval; - - dev_scsi->use_sg = sg_version; - - retval = scsi_std_inquiry(udev, dev_scsi, path); - if (retval) - return retval; - - udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); - udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); - - util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); - util_replace_chars(vendor_str, NULL); - util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); - util_replace_chars(model_str, NULL); - set_type(dev_scsi->type, type_str, sizeof(type_str)); - util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); - util_replace_chars(revision_str, NULL); - return 0; -} - -/* - * scsi_id: try to get an id, if one is found, printf it to stdout. - * returns a value passed to exit() - 0 if printed an id, else 1. - */ -static int scsi_id(struct udev *udev, char *maj_min_dev) -{ - struct scsi_id_device dev_scsi = {}; - int good_dev; - int page_code; - int retval = 0; - - if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { - retval = 1; - goto out; - } - - /* get per device (vendor + model) options from the config file */ - per_dev_options(udev, &dev_scsi, &good_dev, &page_code); - if (!good_dev) { - retval = 1; - goto out; - } - - /* read serial number from mode pages (no values for optical drives) */ - scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); - - if (export) { - char serial_str[MAX_SERIAL_LEN]; - - printf("ID_SCSI=1\n"); - printf("ID_VENDOR=%s\n", vendor_str); - printf("ID_VENDOR_ENC=%s\n", vendor_enc_str); - printf("ID_MODEL=%s\n", model_str); - printf("ID_MODEL_ENC=%s\n", model_enc_str); - printf("ID_REVISION=%s\n", revision_str); - printf("ID_TYPE=%s\n", type_str); - if (dev_scsi.serial[0] != '\0') { - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("ID_SERIAL=%s\n", serial_str); - util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("ID_SERIAL_SHORT=%s\n", serial_str); - } - if (dev_scsi.wwn[0] != '\0') { - printf("ID_WWN=0x%s\n", dev_scsi.wwn); - if (dev_scsi.wwn_vendor_extension[0] != '\0') { - printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); - printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); - } else - printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); - } - if (dev_scsi.tgpt_group[0] != '\0') - printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); - if (dev_scsi.unit_serial_number[0] != '\0') - printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); - goto out; - } - - if (dev_scsi.serial[0] == '\0') { - retval = 1; - goto out; - } - - if (reformat_serial) { - char serial_str[MAX_SERIAL_LEN]; - - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("%s\n", serial_str); - goto out; - } - - printf("%s\n", dev_scsi.serial); -out: - return retval; -} - -int main(int argc, char **argv) -{ - _cleanup_udev_unref_ struct udev *udev; - int retval = 0; - char maj_min_dev[MAX_PATH_LEN]; - int newargc; - char **newargv = NULL; - - log_parse_environment(); - log_open(); - - udev = udev_new(); - if (udev == NULL) - goto exit; - - /* - * Get config file options. - */ - retval = get_file_options(udev, NULL, NULL, &newargc, &newargv); - if (retval < 0) { - retval = 1; - goto exit; - } - if (retval == 0) { - assert(newargv); - - if (set_options(udev, newargc, newargv, maj_min_dev) < 0) { - retval = 2; - goto exit; - } - } - - /* - * Get command line options (overriding any config file settings). - */ - if (set_options(udev, argc, argv, maj_min_dev) < 0) - exit(1); - - if (!dev_specified) { - log_error("No device specified."); - retval = 1; - goto exit; - } - - retval = scsi_id(udev, maj_min_dev); - -exit: - if (newargv) { - free(newargv[0]); - free(newargv); - } - log_close(); - return retval; -} diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h deleted file mode 100644 index 5c2e1c28ee..0000000000 --- a/src/udev/scsi_id/scsi_id.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -/* - * Copyright (C) IBM Corp. 2003 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define MAX_PATH_LEN 512 - -/* - * MAX_ATTR_LEN: maximum length of the result of reading a sysfs - * attribute. - */ -#define MAX_ATTR_LEN 256 - -/* - * MAX_SERIAL_LEN: the maximum length of the serial number, including - * added prefixes such as vendor and product (model) strings. - */ -#define MAX_SERIAL_LEN 256 - -/* - * MAX_BUFFER_LEN: maximum buffer size and line length used while reading - * the config file. - */ -#define MAX_BUFFER_LEN 256 - -struct scsi_id_device { - char vendor[9]; - char model[17]; - char revision[5]; - char type[33]; - char kernel[64]; - char serial[MAX_SERIAL_LEN]; - char serial_short[MAX_SERIAL_LEN]; - int use_sg; - - /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */ - char unit_serial_number[MAX_SERIAL_LEN]; - - /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */ - char wwn[17]; - - /* NULs if not set - otherwise hex encoding using lower-case e.g. '0xe00000d80000' */ - char wwn_vendor_extension[17]; - - /* NULs if not set - otherwise decimal number */ - char tgpt_group[8]; -}; - -int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname); -int scsi_get_serial(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len); - -/* - * Page code values. - */ -enum page_code { - PAGE_83_PRE_SPC3 = -0x83, - PAGE_UNSPECIFIED = 0x00, - PAGE_80 = 0x80, - PAGE_83 = 0x83, -}; diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c deleted file mode 100644 index e079e28698..0000000000 --- a/src/udev/scsi_id/scsi_serial.c +++ /dev/null @@ -1,964 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * - * Author: Patrick Mansfield<patmans@us.ibm.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <linux/bsg.h> -#include <linux/types.h> -#include <scsi/scsi.h> -#include <scsi/sg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "libudev.h" - -#include "libudev-private.h" -#include "random-util.h" -#include "scsi.h" -#include "scsi_id.h" -#include "string-util.h" - -/* - * A priority based list of id, naa, and binary/ascii for the identifier - * descriptor in VPD page 0x83. - * - * Brute force search for a match starting with the first value in the - * following id_search_list. This is not a performance issue, since there - * is normally one or some small number of descriptors. - */ -static const struct scsi_id_search_values id_search_list[] = { - { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, - /* - * Devices already exist using NAA values that are now marked - * reserved. These should not conflict with other values, or it is - * a bug in the device. As long as we find the IEEE extended one - * first, we really don't care what other ones are used. Using - * don't care here means that a device that returns multiple - * non-IEEE descriptors in a random order will get different - * names. - */ - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, -}; - -static const char hex_str[]="0123456789abcdef"; - -/* - * Values returned in the result/status, only the ones used by the code - * are used here. - */ - -#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ -#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ -#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ -#define DRIVER_TIMEOUT 0x06 -#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ - -/* The following "category" function returns one of the following */ -#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ -#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ -#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ -#define SG_ERR_CAT_TIMEOUT 3 -#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ -#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */ -#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */ -#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */ - -static int do_scsi_page80_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len); - -static int sg_err_category_new(struct udev *udev, - int scsi_status, int msg_status, int - host_status, int driver_status, const - unsigned char *sense_buffer, int sb_len) -{ - scsi_status &= 0x7e; - - /* - * XXX change to return only two values - failed or OK. - */ - - if (!scsi_status && !host_status && !driver_status) - return SG_ERR_CAT_CLEAN; - - if ((scsi_status == SCSI_CHECK_CONDITION) || - (scsi_status == SCSI_COMMAND_TERMINATED) || - ((driver_status & 0xf) == DRIVER_SENSE)) { - if (sense_buffer && (sb_len > 2)) { - int sense_key; - unsigned char asc; - - if (sense_buffer[0] & 0x2) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - } else { - sense_key = sense_buffer[2] & 0xf; - asc = (sb_len > 12) ? sense_buffer[12] : 0; - } - - if (sense_key == RECOVERED_ERROR) - return SG_ERR_CAT_RECOVERED; - else if (sense_key == UNIT_ATTENTION) { - if (0x28 == asc) - return SG_ERR_CAT_MEDIA_CHANGED; - if (0x29 == asc) - return SG_ERR_CAT_RESET; - } else if (sense_key == ILLEGAL_REQUEST) - return SG_ERR_CAT_NOTSUPPORTED; - } - return SG_ERR_CAT_SENSE; - } - if (host_status) { - if ((host_status == DID_NO_CONNECT) || - (host_status == DID_BUS_BUSY) || - (host_status == DID_TIME_OUT)) - return SG_ERR_CAT_TIMEOUT; - } - if (driver_status) { - if (driver_status == DRIVER_TIMEOUT) - return SG_ERR_CAT_TIMEOUT; - } - return SG_ERR_CAT_OTHER; -} - -static int sg_err_category3(struct udev *udev, struct sg_io_hdr *hp) -{ - return sg_err_category_new(udev, - hp->status, hp->msg_status, - hp->host_status, hp->driver_status, - hp->sbp, hp->sb_len_wr); -} - -static int sg_err_category4(struct udev *udev, struct sg_io_v4 *hp) -{ - return sg_err_category_new(udev, hp->device_status, 0, - hp->transport_status, hp->driver_status, - (unsigned char *)(uintptr_t)hp->response, - hp->response_len); -} - -static int scsi_dump_sense(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *sense_buffer, int sb_len) -{ - int s; - int code; - int sense_class; - int sense_key; - int asc, ascq; -#ifdef DUMP_SENSE - char out_buffer[256]; - int i, j; -#endif - - /* - * Figure out and print the sense key, asc and ascq. - * - * If you want to suppress these for a particular drive model, add - * a black list entry in the scsi_id config file. - * - * XXX We probably need to: lookup the sense/asc/ascq in a retry - * table, and if found return 1 (after dumping the sense, asc, and - * ascq). So, if/when we get something like a power on/reset, - * we'll retry the command. - */ - - if (sb_len < 1) { - log_debug("%s: sense buffer empty", dev_scsi->kernel); - return -1; - } - - sense_class = (sense_buffer[0] >> 4) & 0x07; - code = sense_buffer[0] & 0xf; - - if (sense_class == 7) { - /* - * extended sense data. - */ - s = sense_buffer[7] + 8; - if (sb_len < s) { - log_debug("%s: sense buffer too small %d bytes, %d bytes too short", - dev_scsi->kernel, sb_len, s - sb_len); - return -1; - } - if ((code == 0x0) || (code == 0x1)) { - sense_key = sense_buffer[2] & 0xf; - if (s < 14) { - /* - * Possible? - */ - log_debug("%s: sense result too" " small %d bytes", - dev_scsi->kernel, s); - return -1; - } - asc = sense_buffer[12]; - ascq = sense_buffer[13]; - } else if ((code == 0x2) || (code == 0x3)) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - ascq = sense_buffer[3]; - } else { - log_debug("%s: invalid sense code 0x%x", - dev_scsi->kernel, code); - return -1; - } - log_debug("%s: sense key 0x%x ASC 0x%x ASCQ 0x%x", - dev_scsi->kernel, sense_key, asc, ascq); - } else { - if (sb_len < 4) { - log_debug("%s: sense buffer too small %d bytes, %d bytes too short", - dev_scsi->kernel, sb_len, 4 - sb_len); - return -1; - } - - if (sense_buffer[0] < 15) - log_debug("%s: old sense key: 0x%x", dev_scsi->kernel, sense_buffer[0] & 0x0f); - else - log_debug("%s: sense = %2x %2x", - dev_scsi->kernel, sense_buffer[0], sense_buffer[2]); - log_debug("%s: non-extended sense class %d code 0x%0x", - dev_scsi->kernel, sense_class, code); - - } - -#ifdef DUMP_SENSE - for (i = 0, j = 0; (i < s) && (j < 254); i++) { - out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4]; - out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f]; - out_buffer[j++] = ' '; - } - out_buffer[j] = '\0'; - log_debug("%s: sense dump:", dev_scsi->kernel); - log_debug("%s: %s", dev_scsi->kernel, out_buffer); - -#endif - return -1; -} - -static int scsi_dump(struct udev *udev, - struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) -{ - if (!io->status && !io->host_status && !io->msg_status && - !io->driver_status) { - /* - * Impossible, should not be called. - */ - log_debug("%s: called with no error", __FUNCTION__); - return -1; - } - - log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x", - dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status); - if (io->status == SCSI_CHECK_CONDITION) - return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr); - else - return -1; -} - -static int scsi_dump_v4(struct udev *udev, - struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) -{ - if (!io->device_status && !io->transport_status && - !io->driver_status) { - /* - * Impossible, should not be called. - */ - log_debug("%s: called with no error", __FUNCTION__); - return -1; - } - - log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x", - dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status); - if (io->device_status == SCSI_CHECK_CONDITION) - return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response, - io->response_len); - else - return -1; -} - -static int scsi_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - unsigned char evpd, unsigned char page, - unsigned char *buf, unsigned int buflen) -{ - unsigned char inq_cmd[INQUIRY_CMDLEN] = - { INQUIRY_CMD, evpd, page, 0, buflen, 0 }; - unsigned char sense[SENSE_BUFF_LEN]; - void *io_buf; - struct sg_io_v4 io_v4; - struct sg_io_hdr io_hdr; - int retry = 3; /* rather random */ - int retval; - - if (buflen > SCSI_INQ_BUFF_LEN) { - log_debug("buflen %d too long", buflen); - return -1; - } - -resend: - if (dev_scsi->use_sg == 4) { - memzero(&io_v4, sizeof(struct sg_io_v4)); - io_v4.guard = 'Q'; - io_v4.protocol = BSG_PROTOCOL_SCSI; - io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; - io_v4.request_len = sizeof(inq_cmd); - io_v4.request = (uintptr_t)inq_cmd; - io_v4.max_response_len = sizeof(sense); - io_v4.response = (uintptr_t)sense; - io_v4.din_xfer_len = buflen; - io_v4.din_xferp = (uintptr_t)buf; - io_buf = (void *)&io_v4; - } else { - memzero(&io_hdr, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inq_cmd); - io_hdr.mx_sb_len = sizeof(sense); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = buflen; - io_hdr.dxferp = buf; - io_hdr.cmdp = inq_cmd; - io_hdr.sbp = sense; - io_hdr.timeout = DEF_TIMEOUT; - io_buf = (void *)&io_hdr; - } - - retval = ioctl(fd, SG_IO, io_buf); - if (retval < 0) { - if ((errno == EINVAL || errno == ENOSYS) && dev_scsi->use_sg == 4) { - dev_scsi->use_sg = 3; - goto resend; - } - log_debug_errno(errno, "%s: ioctl failed: %m", dev_scsi->kernel); - goto error; - } - - if (dev_scsi->use_sg == 4) - retval = sg_err_category4(udev, io_buf); - else - retval = sg_err_category3(udev, io_buf); - - switch (retval) { - case SG_ERR_CAT_NOTSUPPORTED: - buf[1] = 0; - /* Fallthrough */ - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - retval = 0; - break; - - default: - if (dev_scsi->use_sg == 4) - retval = scsi_dump_v4(udev, dev_scsi, io_buf); - else - retval = scsi_dump(udev, dev_scsi, io_buf); - } - - if (!retval) { - retval = buflen; - } else if (retval > 0) { - if (--retry > 0) - goto resend; - retval = -1; - } - -error: - if (retval < 0) - log_debug("%s: Unable to get INQUIRY vpd %d page 0x%x.", - dev_scsi->kernel, evpd, page); - - return retval; -} - -/* Get list of supported EVPD pages */ -static int do_scsi_page0_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - unsigned char *buffer, unsigned int len) -{ - int retval; - - memzero(buffer, len); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, 0x0, buffer, len); - if (retval < 0) - return 1; - - if (buffer[1] != 0) { - log_debug("%s: page 0 not available.", dev_scsi->kernel); - return 1; - } - if (buffer[3] > len) { - log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]); - return 1; - } - - /* - * Following check is based on code once included in the 2.5.x - * kernel. - * - * Some ill behaved devices return the standard inquiry here - * rather than the evpd data, snoop the data to verify. - */ - if (buffer[3] > MODEL_LENGTH) { - /* - * If the vendor id appears in the page assume the page is - * invalid. - */ - if (strneq((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) { - log_debug("%s: invalid page0 data", dev_scsi->kernel); - return 1; - } - } - return 0; -} - -/* - * The caller checks that serial is long enough to include the vendor + - * model. - */ -static int prepend_vendor_model(struct udev *udev, - struct scsi_id_device *dev_scsi, char *serial) -{ - int ind; - - strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH); - strncat(serial, dev_scsi->model, MODEL_LENGTH); - ind = strlen(serial); - - /* - * This is not a complete check, since we are using strncat/cpy - * above, ind will never be too large. - */ - if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { - log_debug("%s: expected length %d, got length %d", - dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); - return -1; - } - return ind; -} - -/* - * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill - * serial number. - */ -static int check_fill_0x83_id(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *page_83, - const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, - int max_len, char *wwn, - char *wwn_vendor_extension, char *tgpt_group) -{ - int i, j, s, len; - - /* - * ASSOCIATION must be with the device (value 0) - * or with the target port for SCSI_ID_TGTPORT - */ - if ((page_83[1] & 0x30) == 0x10) { - if (id_search->id_type != SCSI_ID_TGTGROUP) - return 1; - } else if ((page_83[1] & 0x30) != 0) - return 1; - - if ((page_83[1] & 0x0f) != id_search->id_type) - return 1; - - /* - * Possibly check NAA sub-type. - */ - if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && - (id_search->naa_type != (page_83[4] & 0xf0) >> 4)) - return 1; - - /* - * Check for matching code set - ASCII or BINARY. - */ - if ((page_83[0] & 0x0f) != id_search->code_set) - return 1; - - /* - * page_83[3]: identifier length - */ - len = page_83[3]; - if ((page_83[0] & 0x0f) != SCSI_ID_ASCII) - /* - * If not ASCII, use two bytes for each binary value. - */ - len *= 2; - - /* - * Add one byte for the NUL termination, and one for the id_type. - */ - len += 2; - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - len += VENDOR_LENGTH + MODEL_LENGTH; - - if (max_len < len) { - log_debug("%s: length %d too short - need %d", - dev_scsi->kernel, max_len, len); - return 1; - } - - if (id_search->id_type == SCSI_ID_TGTGROUP && tgpt_group != NULL) { - unsigned int group; - - group = ((unsigned int)page_83[6] << 8) | page_83[7]; - sprintf(tgpt_group,"%x", group); - return 1; - } - - serial[0] = hex_str[id_search->id_type]; - - /* - * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before - * the id since it is not unique across all vendors and models, - * this differs from SCSI_ID_T10_VENDOR, where the vendor is - * included in the identifier. - */ - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - if (prepend_vendor_model(udev, dev_scsi, &serial[1]) < 0) - return 1; - - i = 4; /* offset to the start of the identifier */ - s = j = strlen(serial); - if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) { - /* - * ASCII descriptor. - */ - while (i < (4 + page_83[3])) - serial[j++] = page_83[i++]; - } else { - /* - * Binary descriptor, convert to ASCII, using two bytes of - * ASCII for each byte in the page_83. - */ - while (i < (4 + page_83[3])) { - serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; - serial[j++] = hex_str[page_83[i] & 0x0f]; - i++; - } - } - - strcpy(serial_short, &serial[s]); - - if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) { - strncpy(wwn, &serial[s], 16); - if (wwn_vendor_extension != NULL) - strncpy(wwn_vendor_extension, &serial[s + 16], 16); - } - - return 0; -} - -/* Extract the raw binary from VPD 0x83 pre-SPC devices */ -static int check_fill_0x83_prespc3(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *page_83, - const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, int max_len) -{ - int i, j; - - serial[0] = hex_str[id_search->id_type]; - /* serial has been memset to zero before */ - j = strlen(serial); /* j = 1; */ - - for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) { - serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4]; - serial[j++] = hex_str[ page_83[4+i] & 0x0f]; - } - serial[max_len-1] = 0; - strncpy(serial_short, serial, max_len-1); - return 0; -} - - -/* Get device identification VPD page */ -static int do_scsi_page83_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len, - char *unit_serial_number, char *wwn, - char *wwn_vendor_extension, char *tgpt_group) -{ - int retval; - unsigned int id_ind, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; - - /* also pick up the page 80 serial number */ - do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN); - - memzero(page_83, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, - SCSI_INQ_BUFF_LEN); - if (retval < 0) - return 1; - - if (page_83[1] != PAGE_83) { - log_debug("%s: Invalid page 0x83", dev_scsi->kernel); - return 1; - } - - /* - * XXX Some devices (IBM 3542) return all spaces for an identifier if - * the LUN is not actually configured. This leads to identifiers of - * the form: "1 ". - */ - - /* - * Model 4, 5, and (some) model 6 EMC Symmetrix devices return - * a page 83 reply according to SCSI-2 format instead of SPC-2/3. - * - * The SCSI-2 page 83 format returns an IEEE WWN in binary - * encoded hexi-decimal in the 16 bytes following the initial - * 4-byte page 83 reply header. - * - * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part - * of an Identification descriptor. The 3rd byte of the first - * Identification descriptor is a reserved (BSZ) byte field. - * - * Reference the 7th byte of the page 83 reply to determine - * whether the reply is compliant with SCSI-2 or SPC-2/3 - * specifications. A zero value in the 7th byte indicates - * an SPC-2/3 conformant reply, (i.e., the reserved field of the - * first Identification descriptor). This byte will be non-zero - * for a SCSI-2 conformant page 83 reply from these EMC - * Symmetrix models since the 7th byte of the reply corresponds - * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, - * 0x006048. - */ - - if (page_83[6] != 0) - return check_fill_0x83_prespc3(udev, - dev_scsi, page_83, id_search_list, - serial, serial_short, len); - - /* - * Search for a match in the prioritized id_search_list - since WWN ids - * come first we can pick up the WWN in check_fill_0x83_id(). - */ - for (id_ind = 0; - id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]); - id_ind++) { - /* - * Examine each descriptor returned. There is normally only - * one or a small number of descriptors. - */ - for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) { - retval = check_fill_0x83_id(udev, - dev_scsi, &page_83[j], - &id_search_list[id_ind], - serial, serial_short, len, - wwn, wwn_vendor_extension, - tgpt_group); - if (!retval) - return retval; - else if (retval < 0) - return retval; - } - } - return 1; -} - -/* - * Get device identification VPD page for older SCSI-2 device which is not - * compliant with either SPC-2 or SPC-3 format. - * - * Return the hard coded error code value 2 if the page 83 reply is not - * conformant to the SCSI-2 format. - */ -static int do_scsi_page83_prespc3_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len) -{ - int retval; - int i, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; - - memzero(page_83, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN); - if (retval < 0) - return 1; - - if (page_83[1] != PAGE_83) { - log_debug("%s: Invalid page 0x83", dev_scsi->kernel); - return 1; - } - /* - * Model 4, 5, and (some) model 6 EMC Symmetrix devices return - * a page 83 reply according to SCSI-2 format instead of SPC-2/3. - * - * The SCSI-2 page 83 format returns an IEEE WWN in binary - * encoded hexi-decimal in the 16 bytes following the initial - * 4-byte page 83 reply header. - * - * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part - * of an Identification descriptor. The 3rd byte of the first - * Identification descriptor is a reserved (BSZ) byte field. - * - * Reference the 7th byte of the page 83 reply to determine - * whether the reply is compliant with SCSI-2 or SPC-2/3 - * specifications. A zero value in the 7th byte indicates - * an SPC-2/3 conformant reply, (i.e., the reserved field of the - * first Identification descriptor). This byte will be non-zero - * for a SCSI-2 conformant page 83 reply from these EMC - * Symmetrix models since the 7th byte of the reply corresponds - * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, - * 0x006048. - */ - if (page_83[6] == 0) - return 2; - - serial[0] = hex_str[id_search_list[0].id_type]; - /* - * The first four bytes contain data, not a descriptor. - */ - i = 4; - j = strlen(serial); - /* - * Binary descriptor, convert to ASCII, - * using two bytes of ASCII for each byte - * in the page_83. - */ - while (i < (page_83[3]+4)) { - serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; - serial[j++] = hex_str[page_83[i] & 0x0f]; - i++; - } - return 0; -} - -/* Get unit serial number VPD page */ -static int do_scsi_page80_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len) -{ - int retval; - int ser_ind; - int i; - int len; - unsigned char buf[SCSI_INQ_BUFF_LEN]; - - memzero(buf, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN); - if (retval < 0) - return retval; - - if (buf[1] != PAGE_80) { - log_debug("%s: Invalid page 0x80", dev_scsi->kernel); - return 1; - } - - len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3]; - if (max_len < len) { - log_debug("%s: length %d too short - need %d", - dev_scsi->kernel, max_len, len); - return 1; - } - /* - * Prepend 'S' to avoid unlikely collision with page 0x83 vendor - * specific type where we prepend '0' + vendor + model. - */ - len = buf[3]; - if (serial != NULL) { - serial[0] = 'S'; - ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]); - if (ser_ind < 0) - return 1; - ser_ind++; /* for the leading 'S' */ - for (i = 4; i < len + 4; i++, ser_ind++) - serial[ser_ind] = buf[i]; - } - if (serial_short != NULL) { - memcpy(serial_short, &buf[4], len); - serial_short[len] = '\0'; - } - return 0; -} - -int scsi_std_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, const char *devname) -{ - int fd; - unsigned char buf[SCSI_INQ_BUFF_LEN]; - struct stat statbuf; - int err = 0; - - fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); - if (fd < 0) { - log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname); - return 1; - } - - if (fstat(fd, &statbuf) < 0) { - log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname); - err = 2; - goto out; - } - sprintf(dev_scsi->kernel,"%d:%d", major(statbuf.st_rdev), - minor(statbuf.st_rdev)); - - memzero(buf, SCSI_INQ_BUFF_LEN); - err = scsi_inquiry(udev, dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN); - if (err < 0) - goto out; - - err = 0; - memcpy(dev_scsi->vendor, buf + 8, 8); - dev_scsi->vendor[8] = '\0'; - memcpy(dev_scsi->model, buf + 16, 16); - dev_scsi->model[16] = '\0'; - memcpy(dev_scsi->revision, buf + 32, 4); - dev_scsi->revision[4] = '\0'; - sprintf(dev_scsi->type,"%x", buf[0] & 0x1f); - -out: - close(fd); - return err; -} - -int scsi_get_serial(struct udev *udev, - struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len) -{ - unsigned char page0[SCSI_INQ_BUFF_LEN]; - int fd = -1; - int cnt; - int ind; - int retval; - - memzero(dev_scsi->serial, len); - initialize_srand(); - for (cnt = 20; cnt > 0; cnt--) { - struct timespec duration; - - fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); - if (fd >= 0 || errno != EBUSY) - break; - duration.tv_sec = 0; - duration.tv_nsec = (200 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); - nanosleep(&duration, NULL); - } - if (fd < 0) - return 1; - - if (page_code == PAGE_80) { - if (do_scsi_page80_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } else if (page_code == PAGE_83) { - if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } else if (page_code == PAGE_83_PRE_SPC3) { - retval = do_scsi_page83_prespc3_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len); - if (retval) { - /* - * Fallback to servicing a SPC-2/3 compliant page 83 - * inquiry if the page 83 reply format does not - * conform to pre-SPC3 expectations. - */ - if (retval == 2) { - if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } - else { - retval = 1; - goto completed; - } - } else { - retval = 0; - goto completed; - } - } else if (page_code != 0x00) { - log_debug("%s: unsupported page code 0x%d", dev_scsi->kernel, page_code); - retval = 1; - goto completed; - } - - /* - * Get page 0, the page of the pages. By default, try from best to - * worst of supported pages: 0x83 then 0x80. - */ - if (do_scsi_page0_inquiry(udev, dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) { - /* - * Don't try anything else. Black list if a specific page - * should be used for this vendor+model, or maybe have an - * optional fall-back to page 0x80 or page 0x83. - */ - retval = 1; - goto completed; - } - - for (ind = 4; ind <= page0[3] + 3; ind++) - if (page0[ind] == PAGE_83) - if (!do_scsi_page83_inquiry(udev, dev_scsi, fd, - dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - /* - * Success - */ - retval = 0; - goto completed; - } - - for (ind = 4; ind <= page0[3] + 3; ind++) - if (page0[ind] == PAGE_80) - if (!do_scsi_page80_inquiry(udev, dev_scsi, fd, - dev_scsi->serial, dev_scsi->serial_short, len)) { - /* - * Success - */ - retval = 0; - goto completed; - } - retval = 1; - -completed: - close(fd); - return retval; -} diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c deleted file mode 100644 index 3c58445836..0000000000 --- a/src/udev/udev-builtin-blkid.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * probe disks for filesystems and partitions - * - * Copyright (C) 2011 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2011 Karel Zak <kzak@redhat.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <blkid/blkid.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -#include "sd-id128.h" - -#include "alloc-util.h" -#include "efivars.h" -#include "fd-util.h" -#include "gpt.h" -#include "string-util.h" -#include "udev.h" - -static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) { - char s[256]; - - s[0] = '\0'; - - if (streq(name, "TYPE")) { - udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); - - } else if (streq(name, "USAGE")) { - udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); - - } else if (streq(name, "VERSION")) { - udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); - - } else if (streq(name, "UUID")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); - - } else if (streq(name, "UUID_SUB")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); - - } else if (streq(name, "LABEL")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); - - } else if (streq(name, "PTTYPE")) { - udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); - - } else if (streq(name, "PTUUID")) { - udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value); - - } else if (streq(name, "PART_ENTRY_NAME")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); - - } else if (streq(name, "PART_ENTRY_TYPE")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); - - } else if (startswith(name, "PART_ENTRY_")) { - strscpyl(s, sizeof(s), "ID_", name, NULL); - udev_builtin_add_property(dev, test, s, value); - - } else if (streq(name, "SYSTEM_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s); - - } else if (streq(name, "PUBLISHER_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s); - - } else if (streq(name, "APPLICATION_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s); - - } else if (streq(name, "BOOT_SYSTEM_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s); - } -} - -static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) { - -#if defined(GPT_ROOT_NATIVE) && defined(ENABLE_EFI) - - _cleanup_free_ char *root_id = NULL; - bool found_esp = false; - blkid_partlist pl; - int i, nvals, r; - - assert(pr); - - /* Iterate through the partitions on this disk, and see if the - * EFI ESP we booted from is on it. If so, find the first root - * disk, and add a property indicating its partition UUID. */ - - errno = 0; - pl = blkid_probe_get_partitions(pr); - if (!pl) - return errno > 0 ? -errno : -ENOMEM; - - nvals = blkid_partlist_numof_partitions(pl); - for (i = 0; i < nvals; i++) { - blkid_partition pp; - const char *stype, *sid; - sd_id128_t type; - - pp = blkid_partlist_get_partition(pl, i); - if (!pp) - continue; - - sid = blkid_partition_get_uuid(pp); - if (!sid) - continue; - - stype = blkid_partition_get_type_string(pp); - if (!stype) - continue; - - if (sd_id128_from_string(stype, &type) < 0) - continue; - - if (sd_id128_equal(type, GPT_ESP)) { - sd_id128_t id, esp; - - /* We found an ESP, let's see if it matches - * the ESP we booted from. */ - - if (sd_id128_from_string(sid, &id) < 0) - continue; - - r = efi_loader_get_device_part_uuid(&esp); - if (r < 0) - return r; - - if (sd_id128_equal(id, esp)) - found_esp = true; - - } else if (sd_id128_equal(type, GPT_ROOT_NATIVE)) { - unsigned long long flags; - - flags = blkid_partition_get_flags(pp); - if (flags & GPT_FLAG_NO_AUTO) - continue; - - /* We found a suitable root partition, let's - * remember the first one. */ - - if (!root_id) { - root_id = strdup(sid); - if (!root_id) - return -ENOMEM; - } - } - } - - /* We found the ESP on this disk, and also found a root - * partition, nice! Let's export its UUID */ - if (found_esp && root_id) - udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", root_id); -#endif - - return 0; -} - -static int probe_superblocks(blkid_probe pr) { - struct stat st; - int rc; - - if (fstat(blkid_probe_get_fd(pr), &st)) - return -1; - - blkid_probe_enable_partitions(pr, 1); - - if (!S_ISCHR(st.st_mode) && - blkid_probe_get_size(pr) <= 1024 * 1440 && - blkid_probe_is_wholedisk(pr)) { - /* - * check if the small disk is partitioned, if yes then - * don't probe for filesystems. - */ - blkid_probe_enable_superblocks(pr, 0); - - rc = blkid_do_fullprobe(pr); - if (rc < 0) - return rc; /* -1 = error, 1 = nothing, 0 = success */ - - if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) - return 0; /* partition table detected */ - } - - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); - blkid_probe_enable_superblocks(pr, 1); - - return blkid_do_safeprobe(pr); -} - -static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) { - const char *root_partition; - int64_t offset = 0; - bool noraid = false; - _cleanup_close_ int fd = -1; - blkid_probe pr; - const char *data; - const char *name; - const char *prtype = NULL; - int nvals; - int i; - int err = 0; - bool is_gpt = false; - - static const struct option options[] = { - { "offset", optional_argument, NULL, 'o' }, - { "noraid", no_argument, NULL, 'R' }, - {} - }; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "oR", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'o': - offset = strtoull(optarg, NULL, 0); - break; - case 'R': - noraid = true; - break; - } - } - - pr = blkid_new_probe(); - if (!pr) - return EXIT_FAILURE; - - blkid_probe_set_superblocks_flags(pr, - BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | - BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | - BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION | - BLKID_SUBLKS_BADCSUM); - - if (noraid) - blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); - - fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); - if (fd < 0) { - err = log_debug_errno(errno, "Failure opening block device %s: %m", udev_device_get_devnode(dev)); - goto out; - } - - err = blkid_probe_set_device(pr, fd, offset, 0); - if (err < 0) - goto out; - - log_debug("probe %s %sraid offset=%"PRIi64, - udev_device_get_devnode(dev), - noraid ? "no" : "", offset); - - err = probe_superblocks(pr); - if (err < 0) - goto out; - if (blkid_probe_has_value(pr, "SBBADCSUM")) { - if (!blkid_probe_lookup_value(pr, "TYPE", &prtype, NULL)) - log_warning("incorrect %s checksum on %s", - prtype, udev_device_get_devnode(dev)); - else - log_warning("incorrect checksum on %s", - udev_device_get_devnode(dev)); - goto out; - } - - /* If we are a partition then our parent passed on the root - * partition UUID to us */ - root_partition = udev_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID"); - - nvals = blkid_probe_numof_values(pr); - for (i = 0; i < nvals; i++) { - if (blkid_probe_get_value(pr, i, &name, &data, NULL)) - continue; - - print_property(dev, test, name, data); - - /* Is this a disk with GPT partition table? */ - if (streq(name, "PTTYPE") && streq(data, "gpt")) - is_gpt = true; - - /* Is this a partition that matches the root partition - * property we inherited from our parent? */ - if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition)) - udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1"); - } - - if (is_gpt) - find_gpt_root(dev, pr, test); - - blkid_free_probe(pr); -out: - if (err < 0) - return EXIT_FAILURE; - - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_blkid = { - .name = "blkid", - .cmd = builtin_blkid, - .help = "Filesystem and partition probing", - .run_once = true, -}; diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c deleted file mode 100644 index cfaa463804..0000000000 --- a/src/udev/udev-builtin-btrfs.c +++ /dev/null @@ -1,58 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers <kay@vrfy.org> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <fcntl.h> -#include <stdlib.h> -#include <sys/ioctl.h> - -#ifdef HAVE_LINUX_BTRFS_H -#include <linux/btrfs.h> -#endif - -#include "fd-util.h" -#include "missing.h" -#include "string-util.h" -#include "udev.h" - -static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool test) { - struct btrfs_ioctl_vol_args args = {}; - _cleanup_close_ int fd = -1; - int err; - - if (argc != 3 || !streq(argv[1], "ready")) - return EXIT_FAILURE; - - fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC); - if (fd < 0) - return EXIT_FAILURE; - - strscpy(args.name, sizeof(args.name), argv[2]); - err = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args); - if (err < 0) - return EXIT_FAILURE; - - udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(err == 0)); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_btrfs = { - .name = "btrfs", - .cmd = builtin_btrfs, - .help = "btrfs volume management", -}; diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c deleted file mode 100644 index f4a065a97d..0000000000 --- a/src/udev/udev-builtin-hwdb.c +++ /dev/null @@ -1,223 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers <kay@vrfy.org> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <fnmatch.h> -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> - -#include "sd-hwdb.h" - -#include "alloc-util.h" -#include "hwdb-util.h" -#include "string-util.h" -#include "udev-util.h" -#include "udev.h" - -static sd_hwdb *hwdb; - -int udev_builtin_hwdb_lookup(struct udev_device *dev, - const char *prefix, const char *modalias, - const char *filter, bool test) { - _cleanup_free_ char *lookup = NULL; - const char *key, *value; - int n = 0; - - if (!hwdb) - return -ENOENT; - - if (prefix) { - lookup = strjoin(prefix, modalias, NULL); - if (!lookup) - return -ENOMEM; - modalias = lookup; - } - - SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) { - if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0) - continue; - - if (udev_builtin_add_property(dev, test, key, value) < 0) - return -ENOMEM; - n++; - } - return n; -} - -static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) { - const char *v, *p; - int vn, pn; - - v = udev_device_get_sysattr_value(dev, "idVendor"); - if (!v) - return NULL; - p = udev_device_get_sysattr_value(dev, "idProduct"); - if (!p) - return NULL; - vn = strtol(v, NULL, 16); - if (vn <= 0) - return NULL; - pn = strtol(p, NULL, 16); - if (pn <= 0) - return NULL; - snprintf(s, size, "usb:v%04Xp%04X*", vn, pn); - return s; -} - -static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev, - const char *subsystem, const char *prefix, - const char *filter, bool test) { - struct udev_device *d; - char s[16]; - bool last = false; - int r = 0; - - assert(dev); - - if (!srcdev) - srcdev = dev; - - for (d = srcdev; d && !last; d = udev_device_get_parent(d)) { - const char *dsubsys; - const char *modalias = NULL; - - dsubsys = udev_device_get_subsystem(d); - if (!dsubsys) - continue; - - /* look only at devices of a specific subsystem */ - if (subsystem && !streq(dsubsys, subsystem)) - continue; - - modalias = udev_device_get_property_value(d, "MODALIAS"); - - if (streq(dsubsys, "usb") && streq_ptr(udev_device_get_devtype(d), "usb_device")) { - /* if the usb_device does not have a modalias, compose one */ - if (!modalias) - modalias = modalias_usb(d, s, sizeof(s)); - - /* avoid looking at any parent device, they are usually just a USB hub */ - last = true; - } - - if (!modalias) - continue; - - r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test); - if (r > 0) - break; - } - - return r; -} - -static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) { - static const struct option options[] = { - { "filter", required_argument, NULL, 'f' }, - { "device", required_argument, NULL, 'd' }, - { "subsystem", required_argument, NULL, 's' }, - { "lookup-prefix", required_argument, NULL, 'p' }, - {} - }; - const char *filter = NULL; - const char *device = NULL; - const char *subsystem = NULL; - const char *prefix = NULL; - _cleanup_udev_device_unref_ struct udev_device *srcdev = NULL; - - if (!hwdb) - return EXIT_FAILURE; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "f:d:s:p:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'f': - filter = optarg; - break; - - case 'd': - device = optarg; - break; - - case 's': - subsystem = optarg; - break; - - case 'p': - prefix = optarg; - break; - } - } - - /* query a specific key given as argument */ - if (argv[optind]) { - if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0) - return EXIT_SUCCESS; - return EXIT_FAILURE; - } - - /* read data from another device than the device we will store the data */ - if (device) { - srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device); - if (!srcdev) - return EXIT_FAILURE; - } - - if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0) - return EXIT_SUCCESS; - return EXIT_FAILURE; -} - -/* called at udev startup and reload */ -static int builtin_hwdb_init(struct udev *udev) { - int r; - - if (hwdb) - return 0; - - r = sd_hwdb_new(&hwdb); - if (r < 0) - return r; - - return 0; -} - -/* called on udev shutdown and reload request */ -static void builtin_hwdb_exit(struct udev *udev) { - hwdb = sd_hwdb_unref(hwdb); -} - -/* called every couple of seconds during event activity; 'true' if config has changed */ -static bool builtin_hwdb_validate(struct udev *udev) { - return hwdb_validate(hwdb); -} - -const struct udev_builtin udev_builtin_hwdb = { - .name = "hwdb", - .cmd = builtin_hwdb, - .init = builtin_hwdb_init, - .exit = builtin_hwdb_exit, - .validate = builtin_hwdb_validate, - .help = "Hardware database", -}; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c deleted file mode 100644 index 59b9804dc4..0000000000 --- a/src/udev/udev-builtin-input_id.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * expose input properties via udev - * - * Copyright (C) 2009 Martin Pitt <martin.pitt@ubuntu.com> - * Portions Copyright (C) 2004 David Zeuthen, <david@fubar.dk> - * Copyright (C) 2011 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2014 Carlos Garnacho <carlosg@gnome.org> - * Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <linux/limits.h> -#include <linux/input.h> - -#include "fd-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev.h" -#include "util.h" - -/* we must use this kernel-compatible implementation */ -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<<OFF(x)) -#define LONG(x) ((x)/BITS_PER_LONG) -#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) - -static inline int abs_size_mm(const struct input_absinfo *absinfo) { - /* Resolution is defined to be in units/mm for ABS_X/Y */ - return (absinfo->maximum - absinfo->minimum) / absinfo->resolution; -} - -static void extract_info(struct udev_device *dev, const char *devpath, bool test) { - char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)]; - struct input_absinfo xabsinfo = {}, yabsinfo = {}; - _cleanup_close_ int fd = -1; - - fd = open(devpath, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return; - - if (ioctl(fd, EVIOCGABS(ABS_X), &xabsinfo) < 0 || - ioctl(fd, EVIOCGABS(ABS_Y), &yabsinfo) < 0) - return; - - if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0) - return; - - xsprintf(width, "%d", abs_size_mm(&xabsinfo)); - xsprintf(height, "%d", abs_size_mm(&yabsinfo)); - - udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width); - udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height); -} - -/* - * Read a capability attribute and return bitmask. - * @param dev udev_device - * @param attr sysfs attribute name (e. g. "capabilities/key") - * @param bitmask: Output array which has a sizeof of bitmask_size - */ -static void get_cap_mask(struct udev_device *dev, - struct udev_device *pdev, const char* attr, - unsigned long *bitmask, size_t bitmask_size, - bool test) { - const char *v; - char text[4096]; - unsigned i; - char* word; - unsigned long val; - - v = udev_device_get_sysattr_value(pdev, attr); - if (!v) - v = ""; - - xsprintf(text, "%s", v); - log_debug("%s raw kernel attribute: %s", attr, text); - - memzero(bitmask, bitmask_size); - i = 0; - while ((word = strrchr(text, ' ')) != NULL) { - val = strtoul (word+1, NULL, 16); - if (i < bitmask_size/sizeof(unsigned long)) - bitmask[i] = val; - else - log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); - *word = '\0'; - ++i; - } - val = strtoul (text, NULL, 16); - if (i < bitmask_size / sizeof(unsigned long)) - bitmask[i] = val; - else - log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); - - if (test) { - /* printf pattern with the right unsigned long number of hex chars */ - xsprintf(text, " bit %%4u: %%0%zulX\n", - 2 * sizeof(unsigned long)); - log_debug("%s decoded bit map:", attr); - val = bitmask_size / sizeof (unsigned long); - /* skip over leading zeros */ - while (bitmask[val-1] == 0 && val > 0) - --val; - for (i = 0; i < val; ++i) { - DISABLE_WARNING_FORMAT_NONLITERAL; - log_debug(text, i * BITS_PER_LONG, bitmask[i]); - REENABLE_WARNING; - } - } -} - -/* pointer devices */ -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; - } - - 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 = true; - else if (has_touch || is_direct) - is_touchscreen = true; - else if (has_joystick_axes_or_buttons) - is_joystick = true; - } - if (has_mt_coordinates) { - if (stylus_or_pen) - is_tablet = true; - else if (finger_but_no_pen && !is_direct) - is_touchpad = true; - else if (has_touch || is_direct) - is_touchscreen = true; - } - - 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 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)) { - log_debug("test_key: no EV_KEY capability"); - return false; - } - - /* only consider KEY_* here, not BTN_* */ - found = 0; - for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { - found |= bitmask_key[i]; - log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0); - } - /* 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)) { - log_debug("test_key: Found key %x in high block", i); - found = 1; - break; - } - } - } - - 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) { - 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) { - struct udev_device *pdev; - unsigned long bitmask_ev[NBITS(EV_MAX)]; - 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 */ - pdev = dev; - while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) - pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); - - if (pdev) { - /* Use this as a flag that input devices were detected, so that this - * program doesn't need to be called more than once per device */ - udev_builtin_add_property(dev, test, "ID_INPUT", "1"); - get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); - 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); - 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); - sysname = udev_device_get_sysname(dev); - if (devnode && sysname && startswith(sysname, "event")) - extract_info(dev, devnode, test); - - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_input_id = { - .name = "input_id", - .cmd = builtin_input_id, - .help = "Input device properties", -}; diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c deleted file mode 100644 index 09024116f2..0000000000 --- a/src/udev/udev-builtin-keyboard.c +++ /dev/null @@ -1,277 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers <kay@vrfy.org> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <linux/input.h> - -#include "fd-util.h" -#include "parse-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev.h" - -static const struct key *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len); -#include "keyboard-keys-from-name.h" - -static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) { - struct udev_device *atkbd; - const char *cur; - char codes[4096]; - char *s; - size_t l; - unsigned i; - int ret; - - assert(dev); - assert(release); - - atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); - if (!atkbd) - return -ENODEV; - - cur = udev_device_get_sysattr_value(atkbd, "force_release"); - if (!cur) - return -ENODEV; - - s = codes; - l = sizeof(codes); - - /* copy current content */ - l = strpcpy(&s, l, cur); - - /* append new codes */ - for (i = 0; i < release_count; i++) - l = strpcpyf(&s, l, ",%u", release[i]); - - log_debug("keyboard: updating force-release list with '%s'", codes); - ret = udev_device_set_sysattr_value(atkbd, "force_release", codes); - if (ret < 0) - log_error_errno(ret, "Error writing force-release attribute: %m"); - return ret; -} - -static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode) -{ - struct { - unsigned scan; - unsigned key; - } 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; - char *endptr; - - key = udev_list_entry_get_name(entry); - 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); - - /* a leading '!' needs a force-release entry */ - if (keycode[0] == '!') { - keycode++; - - release[release_count] = scancode; - if (release_count < ELEMENTSOF(release)-1) - release_count++; - - if (keycode[0] == '\0') - continue; - } - - if (fd == -1) { - fd = open_device(node); - if (fd < 0) - return EXIT_FAILURE; - } - - map_keycode(fd, node, scancode, keycode); - } else if (startswith(key, "EVDEV_ABS_")) { - unsigned evcode; - - /* 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; - } - - if (fd == -1) { - fd = open_device(node); - if (fd < 0) - return EXIT_FAILURE; - } - - 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); - - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_keyboard = { - .name = "keyboard", - .cmd = builtin_keyboard, - .help = "Keyboard scan code to key mapping", -}; diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c deleted file mode 100644 index 9665f678fd..0000000000 --- a/src/udev/udev-builtin-kmod.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * load kernel modules - * - * Copyright (C) 2011-2012 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2011 ProFUSION embedded systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <libkmod.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> - -#include "string-util.h" -#include "udev.h" - -static struct kmod_ctx *ctx = NULL; - -static int load_module(struct udev *udev, const char *alias) { - struct kmod_list *list = NULL; - struct kmod_list *l; - int err; - - err = kmod_module_new_from_lookup(ctx, alias, &list); - if (err < 0) - return err; - - if (list == NULL) - log_debug("No module matches '%s'", alias); - - kmod_list_foreach(l, list) { - struct kmod_module *mod = kmod_module_get_module(l); - - err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); - if (err == KMOD_PROBE_APPLY_BLACKLIST) - log_debug("Module '%s' is blacklisted", kmod_module_get_name(mod)); - else if (err == 0) - log_debug("Inserted '%s'", kmod_module_get_name(mod)); - else - log_debug("Failed to insert '%s'", kmod_module_get_name(mod)); - - kmod_module_unref(mod); - } - - kmod_module_unref_list(list); - return err; -} - -_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { - log_internalv(priority, 0, file, line, fn, format, args); -} - -static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev *udev = udev_device_get_udev(dev); - int i; - - if (!ctx) - return 0; - - if (argc < 3 || !streq(argv[1], "load")) { - log_error("expect: %s load <module>", argv[0]); - return EXIT_FAILURE; - } - - for (i = 2; argv[i]; i++) { - log_debug("Execute '%s' '%s'", argv[1], argv[i]); - load_module(udev, argv[i]); - } - - return EXIT_SUCCESS; -} - -/* called at udev startup and reload */ -static int builtin_kmod_init(struct udev *udev) { - if (ctx) - return 0; - - ctx = kmod_new(NULL, NULL); - if (!ctx) - return -ENOMEM; - - log_debug("Load module index"); - kmod_set_log_fn(ctx, udev_kmod_log, udev); - kmod_load_resources(ctx); - return 0; -} - -/* called on udev shutdown and reload request */ -static void builtin_kmod_exit(struct udev *udev) { - log_debug("Unload module index"); - ctx = kmod_unref(ctx); -} - -/* called every couple of seconds during event activity; 'true' if config has changed */ -static bool builtin_kmod_validate(struct udev *udev) { - log_debug("Validate module index"); - if (!ctx) - return false; - return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK); -} - -const struct udev_builtin udev_builtin_kmod = { - .name = "kmod", - .cmd = builtin_kmod, - .init = builtin_kmod_init, - .exit = builtin_kmod_exit, - .validate = builtin_kmod_validate, - .help = "Kernel module loader", - .run_once = false, -}; diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c deleted file mode 100644 index fe9d6f4482..0000000000 --- a/src/udev/udev-builtin-net_id.c +++ /dev/null @@ -1,638 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers <kay@vrfy.org> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -/* - * Predictable network interface device names based on: - * - firmware/bios-provided index numbers for on-board devices - * - firmware-provided pci-express hotplug slot index number - * - physical/geographical location of the hardware - * - the interface's MAC address - * - * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames - * - * Two character prefixes based on the type of interface: - * en — Ethernet - * sl — serial line IP (slip) - * wl — wlan - * ww — wwan - * - * Type of names: - * b<number> — BCMA bus core number - * c<bus_id> — CCW bus group name, without leading zeros [s390] - * o<index>[n<phys_port_name>|d<dev_port>] - * — on-board device index number - * s<slot>[f<function>][n<phys_port_name>|d<dev_port>] - * — hotplug slot index number - * x<MAC> — MAC address - * [P<domain>]p<bus>s<slot>[f<function>][n<phys_port_name>|d<dev_port>] - * — PCI geographical location - * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>] - * — USB port number chain - * - * All multi-function PCI devices will carry the [f<function>] number in the - * device name, including the function 0 device. - * - * When using PCI geography, The PCI domain is only prepended when it is not 0. - * - * For USB devices the full chain of port numbers of hubs is composed. If the - * name gets longer than the maximum number of 15 characters, the name is not - * exported. - * The usual USB configuration == 1 and interface == 0 values are suppressed. - * - * PCI Ethernet card with firmware index "1": - * ID_NET_NAME_ONBOARD=eno1 - * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1 - * - * PCI Ethernet card in hotplug slot with firmware index number: - * /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1 - * ID_NET_NAME_MAC=enx000000000466 - * ID_NET_NAME_PATH=enp5s0 - * ID_NET_NAME_SLOT=ens1 - * - * PCI Ethernet multi-function card with 2 ports: - * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 - * ID_NET_NAME_MAC=enx78e7d1ea46da - * ID_NET_NAME_PATH=enp2s0f0 - * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 - * ID_NET_NAME_MAC=enx78e7d1ea46dc - * ID_NET_NAME_PATH=enp2s0f1 - * - * PCI wlan card: - * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 - * ID_NET_NAME_MAC=wlx0024d7e31130 - * ID_NET_NAME_PATH=wlp3s0 - * - * USB built-in 3G modem: - * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 - * ID_NET_NAME_MAC=wwx028037ec0200 - * ID_NET_NAME_PATH=wwp0s29u1u4i6 - * - * USB Android phone: - * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 - * ID_NET_NAME_MAC=enxd626b3450fb5 - * ID_NET_NAME_PATH=enp0s29u1u2 - */ - -#include <errno.h> -#include <fcntl.h> -#include <net/if.h> -#include <net/if_arp.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <linux/pci_regs.h> - -#include "fd-util.h" -#include "fileio.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev.h" - -#define ONBOARD_INDEX_MAX (16*1024-1) - -enum netname_type{ - NET_UNDEF, - NET_PCI, - NET_USB, - NET_BCMA, - NET_VIRTIO, - NET_CCWGROUP, -}; - -struct netnames { - enum netname_type type; - - uint8_t mac[6]; - bool mac_valid; - - struct udev_device *pcidev; - char pci_slot[IFNAMSIZ]; - char pci_path[IFNAMSIZ]; - char pci_onboard[IFNAMSIZ]; - const char *pci_onboard_label; - - char usb_ports[IFNAMSIZ]; - char bcma_core[IFNAMSIZ]; - char ccw_group[IFNAMSIZ]; -}; - -/* retrieve on-board index number and label from firmware */ -static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { - unsigned dev_port = 0; - size_t l; - char *s; - const char *attr, *port_name; - int idx; - - /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ - attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index"); - /* SMBIOS type 41 — Onboard Devices Extended Information */ - if (!attr) - attr = udev_device_get_sysattr_value(names->pcidev, "index"); - if (!attr) - return -ENOENT; - - idx = strtoul(attr, NULL, 0); - if (idx <= 0) - return -EINVAL; - - /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for - * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary - * cut-off, which is somewhere beyond the realistic number of physical network interface a system might - * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */ - if (idx > ONBOARD_INDEX_MAX) - return -ENOENT; - - /* 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); - - /* kernel provided front panel port name for multiple port PCI device */ - port_name = udev_device_get_sysattr_value(dev, "phys_port_name"); - - s = names->pci_onboard; - l = sizeof(names->pci_onboard); - l = strpcpyf(&s, l, "o%d", idx); - if (port_name) - l = strpcpyf(&s, l, "n%s", port_name); - else 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_close_ int fd = -1; - const char *filename; - uint8_t config[64]; - - filename = strjoina(udev_device_get_syspath(dev), "/config"); - fd = open(filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) - return false; - if (read(fd, &config, sizeof(config)) != sizeof(config)) - return false; - - /* bit 0-6 header type, bit 7 multi/single function device */ - if ((config[PCI_HEADER_TYPE] & 0x80) != 0) - return true; - - return false; -} - -static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { - struct udev *udev = udev_device_get_udev(names->pcidev); - unsigned domain, bus, slot, func, dev_port = 0; - size_t l; - char *s; - const char *attr, *port_name; - struct udev_device *pci = NULL; - char slots[PATH_MAX]; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - int hotplug_slot = 0, err = 0; - - if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4) - return -ENOENT; - - /* 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); - - /* kernel provided front panel port name for multiple port PCI device */ - port_name = udev_device_get_sysattr_value(dev, "phys_port_name"); - - /* compose a name based on the raw kernel's PCI bus, slot numbers */ - s = names->pci_path; - l = sizeof(names->pci_path); - if (domain > 0) - l = strpcpyf(&s, l, "P%u", domain); - l = strpcpyf(&s, l, "p%us%u", bus, slot); - if (func > 0 || is_pci_multifunction(names->pcidev)) - l = strpcpyf(&s, l, "f%u", func); - if (port_name) - l = strpcpyf(&s, l, "n%s", port_name); - else if (dev_port > 0) - l = strpcpyf(&s, l, "d%u", dev_port); - if (l == 0) - names->pci_path[0] = '\0'; - - /* ACPI _SUN — slot user number */ - pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); - if (!pci) { - err = -ENOENT; - goto out; - } - - snprintf(slots, sizeof slots, "%s/slots", udev_device_get_syspath(pci)); - dir = opendir(slots); - if (!dir) { - err = -errno; - goto out; - } - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - int i; - char *rest, *address, str[PATH_MAX]; - - if (dent->d_name[0] == '.') - continue; - i = strtol(dent->d_name, &rest, 10); - if (rest[0] != '\0') - continue; - if (i < 1) - continue; - - snprintf(str, sizeof str, "%s/%s/address", slots, dent->d_name); - if (read_one_line_file(str, &address) >= 0) { - /* match slot address with device by stripping the function */ - if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address))) - hotplug_slot = i; - free(address); - } - - if (hotplug_slot > 0) - break; - } - - if (hotplug_slot > 0) { - s = names->pci_slot; - l = sizeof(names->pci_slot); - if (domain > 0) - l = strpcpyf(&s, l, "P%d", domain); - l = strpcpyf(&s, l, "s%d", hotplug_slot); - if (func > 0 || is_pci_multifunction(names->pcidev)) - l = strpcpyf(&s, l, "f%d", func); - if (port_name) - l = strpcpyf(&s, l, "n%s", port_name); - else if (dev_port > 0) - l = strpcpyf(&s, l, "d%d", dev_port); - if (l == 0) - names->pci_slot[0] = '\0'; - } -out: - udev_device_unref(pci); - return err; -} - -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); - - /* there can only ever be one virtio bus per parent device, so we can - safely ignore any virtio buses. see - <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */ - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); - - if (!parent) - return -ENOENT; - - /* check if our direct parent is a PCI device with no other bus in-between */ - if (streq_ptr("pci", udev_device_get_subsystem(parent))) { - names->type = NET_PCI; - names->pcidev = parent; - } else { - names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); - if (!names->pcidev) - return -ENOENT; - } - dev_pci_onboard(dev, names); - dev_pci_slot(dev, names); - return 0; -} - -static int names_usb(struct udev_device *dev, struct netnames *names) { - struct udev_device *usbdev; - char name[256]; - char *ports; - char *config; - char *interf; - 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; - - /* get USB port number chain, configuration, interface */ - strscpy(name, sizeof(name), udev_device_get_sysname(usbdev)); - s = strchr(name, '-'); - if (!s) - return -EINVAL; - ports = s+1; - - s = strchr(ports, ':'); - if (!s) - return -EINVAL; - s[0] = '\0'; - config = s+1; - - s = strchr(config, '.'); - if (!s) - return -EINVAL; - s[0] = '\0'; - interf = s+1; - - /* prefix every port number in the chain with "u" */ - s = ports; - while ((s = strchr(s, '.'))) - s[0] = 'u'; - s = names->usb_ports; - l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL); - - /* append USB config number, suppress the common config == 1 */ - if (!streq(config, "1")) - l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL); - - /* append USB interface number, suppress the interface == 0 */ - if (!streq(interf, "0")) - l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL); - if (l == 0) - return -ENAMETOOLONG; - - names->type = NET_USB; - return 0; -} - -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; - - /* bus num:core num */ - if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1) - return -EINVAL; - /* suppress the common core == 0 */ - if (core > 0) - xsprintf(names->bcma_core, "b%u", core); - - names->type = NET_BCMA; - return 0; -} - -static int names_ccw(struct udev_device *dev, struct netnames *names) { - struct udev_device *cdev; - const char *bus_id; - size_t bus_id_len; - int rc; - - assert(dev); - assert(names); - - /* Retrieve the associated CCW device */ - cdev = udev_device_get_parent(dev); - if (!cdev) - return -ENOENT; - - /* Network devices are always grouped CCW devices */ - if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev))) - return -ENOENT; - - /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely - * identifies the network device on the Linux on System z channel - * subsystem. Note that the bus-ID contains lowercase characters. - */ - bus_id = udev_device_get_sysname(cdev); - if (!bus_id) - return -ENOENT; - - /* Check the length of the bus-ID. Rely on that the kernel provides - * a correct bus-ID; alternatively, improve this check and parse and - * verify each bus-ID part... - */ - bus_id_len = strlen(bus_id); - if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9) - return -EINVAL; - - /* Strip leading zeros from the bus id for aesthetic purposes. This - * keeps the ccw names stable, yet much shorter in general case of - * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is - * not prepended when it is zero. - */ - bus_id += strspn(bus_id, ".0"); - - /* Store the CCW bus-ID for use as network device name */ - rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id); - if (rc >= 0 && rc < (int)sizeof(names->ccw_group)) - names->type = NET_CCWGROUP; - return 0; -} - -static int names_mac(struct udev_device *dev, struct netnames *names) { - const char *s; - unsigned int i; - unsigned int a1, a2, a3, a4, a5, a6; - - /* check for NET_ADDR_PERM, skip random MAC addresses */ - s = udev_device_get_sysattr_value(dev, "addr_assign_type"); - if (!s) - return EXIT_FAILURE; - i = strtoul(s, NULL, 0); - if (i != 0) - return 0; - - s = udev_device_get_sysattr_value(dev, "address"); - if (!s) - return -ENOENT; - if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) - return -EINVAL; - - /* skip empty MAC addresses */ - if (a1 + a2 + a3 + a4 + a5 + a6 == 0) - return -EINVAL; - - names->mac[0] = a1; - names->mac[1] = a2; - names->mac[2] = a3; - names->mac[3] = a4; - names->mac[4] = a5; - names->mac[5] = a6; - names->mac_valid = true; - return 0; -} - -/* IEEE Organizationally Unique Identifier vendor string */ -static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) { - char str[32]; - - if (!names->mac_valid) - return -ENOENT; - /* skip commonly misused 00:00:00 (Xerox) prefix */ - if (memcmp(names->mac, "\0\0\0", 3) == 0) - return -EINVAL; - xsprintf(str, "OUI:%02X%02X%02X%02X%02X%02X", names->mac[0], - names->mac[1], names->mac[2], names->mac[3], names->mac[4], - names->mac[5]); - udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test); - return 0; -} - -static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) { - const char *s; - const char *p; - unsigned int i; - const char *devtype; - const char *prefix = "en"; - struct netnames names = {}; - int err; - - /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */ - s = udev_device_get_sysattr_value(dev, "type"); - if (!s) - return EXIT_FAILURE; - i = strtoul(s, NULL, 0); - switch (i) { - case ARPHRD_ETHER: - prefix = "en"; - break; - case ARPHRD_SLIP: - prefix = "sl"; - break; - default: - return 0; - } - - /* skip stacked devices, like VLANs, ... */ - s = udev_device_get_sysattr_value(dev, "ifindex"); - if (!s) - return EXIT_FAILURE; - p = udev_device_get_sysattr_value(dev, "iflink"); - if (!p) - return EXIT_FAILURE; - if (!streq(s, p)) - return 0; - - devtype = udev_device_get_devtype(dev); - if (devtype) { - if (streq("wlan", devtype)) - prefix = "wl"; - else if (streq("wwan", devtype)) - prefix = "ww"; - } - - err = names_mac(dev, &names); - if (err >= 0 && names.mac_valid) { - char str[IFNAMSIZ]; - - xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix, - names.mac[0], names.mac[1], names.mac[2], - names.mac[3], names.mac[4], names.mac[5]); - udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str); - - ieee_oui(dev, &names, test); - } - - /* get path names for Linux on System z network devices */ - err = names_ccw(dev, &names); - if (err >= 0 && names.type == NET_CCWGROUP) { - char str[IFNAMSIZ]; - - if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - goto out; - } - - /* get PCI based path names, we compose only PCI based paths */ - err = names_pci(dev, &names); - if (err < 0) - goto out; - - /* plain PCI device */ - if (names.type == NET_PCI) { - char str[IFNAMSIZ]; - - if (names.pci_onboard[0]) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str); - - if (names.pci_onboard_label) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str); - - if (names.pci_path[0]) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - - if (names.pci_slot[0]) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); - goto out; - } - - /* USB device */ - err = names_usb(dev, &names); - if (err >= 0 && names.type == NET_USB) { - char str[IFNAMSIZ]; - - if (names.pci_path[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - - if (names.pci_slot[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); - goto out; - } - - /* Broadcom bus */ - err = names_bcma(dev, &names); - if (err >= 0 && names.type == NET_BCMA) { - char str[IFNAMSIZ]; - - if (names.pci_path[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - - if (names.pci_slot[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); - goto out; - } -out: - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_net_id = { - .name = "net_id", - .cmd = builtin_net_id, - .help = "Network device properties", -}; diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c deleted file mode 100644 index 8e47775135..0000000000 --- a/src/udev/udev-builtin-net_setup_link.c +++ /dev/null @@ -1,107 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "alloc-util.h" -#include "link-config.h" -#include "log.h" -#include "udev.h" - -static link_config_ctx *ctx = NULL; - -static int builtin_net_setup_link(struct udev_device *dev, int argc, char **argv, bool test) { - _cleanup_free_ char *driver = NULL; - const char *name = NULL; - link_config *link; - int r; - - if (argc > 1) { - log_error("This program takes no arguments."); - return EXIT_FAILURE; - } - - r = link_get_driver(ctx, dev, &driver); - if (r >= 0) - udev_builtin_add_property(dev, test, "ID_NET_DRIVER", driver); - - r = link_config_get(ctx, dev, &link); - if (r < 0) { - if (r == -ENOENT) { - log_debug("No matching link configuration found."); - return EXIT_SUCCESS; - } else { - log_error_errno(r, "Could not get link config: %m"); - return EXIT_FAILURE; - } - } - - r = link_config_apply(ctx, link, dev, &name); - if (r < 0) { - log_error_errno(r, "Could not apply link config to %s: %m", udev_device_get_sysname(dev)); - return EXIT_FAILURE; - } - - udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE", link->filename); - - if (name) - udev_builtin_add_property(dev, test, "ID_NET_NAME", name); - - return EXIT_SUCCESS; -} - -static int builtin_net_setup_link_init(struct udev *udev) { - int r; - - if (ctx) - return 0; - - r = link_config_ctx_new(&ctx); - if (r < 0) - return r; - - r = link_config_load(ctx); - if (r < 0) - return r; - - log_debug("Created link configuration context."); - return 0; -} - -static void builtin_net_setup_link_exit(struct udev *udev) { - link_config_ctx_free(ctx); - ctx = NULL; - log_debug("Unloaded link configuration context."); -} - -static bool builtin_net_setup_link_validate(struct udev *udev) { - log_debug("Check if link configuration needs reloading."); - if (!ctx) - return false; - - return link_config_should_reload(ctx); -} - -const struct udev_builtin udev_builtin_net_setup_link = { - .name = "net_setup_link", - .cmd = builtin_net_setup_link, - .init = builtin_net_setup_link_init, - .exit = builtin_net_setup_link_exit, - .validate = builtin_net_setup_link_validate, - .help = "Configure network link", - .run_once = false, -}; diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c deleted file mode 100644 index 1825ee75a7..0000000000 --- a/src/udev/udev-builtin-path_id.c +++ /dev/null @@ -1,770 +0,0 @@ -/* - * compose persistent device path - * - * Copyright (C) 2009-2011 Kay Sievers <kay@vrfy.org> - * - * Logic based on Hannes Reinecke's shell script. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "string-util.h" -#include "udev.h" - -_printf_(2,3) -static int path_prepend(char **path, const char *fmt, ...) { - va_list va; - char *pre; - int err = 0; - - va_start(va, fmt); - err = vasprintf(&pre, fmt, va); - va_end(va); - if (err < 0) - goto out; - - if (*path != NULL) { - char *new; - - err = asprintf(&new, "%s-%s", pre, *path); - free(pre); - if (err < 0) - goto out; - free(*path); - *path = new; - } else { - *path = pre; - } -out: - return err; -} - -/* -** Linux only supports 32 bit luns. -** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. -*/ -static int format_lun_number(struct udev_device *dev, char **path) { - unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); - - /* address method 0, peripheral device addressing with bus id of zero */ - if (lun < 256) - return path_prepend(path, "lun-%lu", lun); - /* handle all other lun addressing methods by using a variant of the original lun format */ - return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff); -} - -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; - - subsystem = udev_device_get_subsystem(parent); - if (subsystem == NULL || !streq(subsystem, subsys)) - break; - dev = parent; - parent = udev_device_get_parent(parent); - } - return dev; -} - -static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *fcdev = NULL; - 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; - - fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); - if (fcdev == NULL) - return NULL; - port = udev_device_get_sysattr_value(fcdev, "port_name"); - if (port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "fc-%s-%s", port, lun); - free(lun); -out: - udev_device_unref(fcdev); - return parent; -} - -static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *sasdev; - 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; - - target_parent = udev_device_get_parent(targetdev); - if (target_parent == NULL) - return NULL; - - sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", - udev_device_get_sysname(target_parent)); - if (sasdev == NULL) - return NULL; - - sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); - if (sas_address == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "sas-%s-%s", sas_address, lun); - free(lun); -out: - udev_device_unref(sasdev); - return parent; -} - -static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *port; - struct udev_device *expander; - struct udev_device *target_sasdev = NULL; - struct udev_device *expander_sasdev = NULL; - struct udev_device *port_sasdev = NULL; - const char *sas_address = NULL; - const char *phy_id; - 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; - - target_parent = udev_device_get_parent(targetdev); - if (target_parent == NULL) - return NULL; - - /* Get sas device */ - target_sasdev = udev_device_new_from_subsystem_sysname(udev, - "sas_device", udev_device_get_sysname(target_parent)); - if (target_sasdev == NULL) - return NULL; - - /* The next parent is sas port */ - port = udev_device_get_parent(target_parent); - if (port == NULL) { - parent = NULL; - goto out; - } - - /* Get port device */ - port_sasdev = udev_device_new_from_subsystem_sysname(udev, - "sas_port", udev_device_get_sysname(port)); - - phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys"); - if (phy_count == NULL) { - parent = NULL; - goto out; - } - - /* Check if we are simple disk */ - if (strncmp(phy_count, "1", 2) != 0) { - parent = handle_scsi_sas_wide_port(parent, path); - goto out; - } - - /* Get connected phy */ - phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier"); - if (phy_id == NULL) { - parent = NULL; - goto out; - } - - /* The port's parent is either hba or expander */ - expander = udev_device_get_parent(port); - if (expander == NULL) { - parent = NULL; - goto out; - } - - /* Get expander device */ - expander_sasdev = udev_device_new_from_subsystem_sysname(udev, - "sas_device", udev_device_get_sysname(expander)); - if (expander_sasdev != NULL) { - /* Get expander's address */ - sas_address = udev_device_get_sysattr_value(expander_sasdev, - "sas_address"); - if (sas_address == NULL) { - parent = NULL; - goto out; - } - } - - format_lun_number(parent, &lun); - if (sas_address) - path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun); - else - path_prepend(path, "sas-phy%s-%s", phy_id, lun); - - free(lun); -out: - udev_device_unref(target_sasdev); - udev_device_unref(expander_sasdev); - udev_device_unref(port_sasdev); - return parent; -} - -static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *transportdev; - struct udev_device *sessiondev = NULL; - const char *target; - char *connname; - struct udev_device *conndev = NULL; - const char *addr; - const char *port; - char *lun = NULL; - - assert(parent); - assert(path); - - /* find iscsi session */ - transportdev = parent; - for (;;) { - transportdev = udev_device_get_parent(transportdev); - if (transportdev == NULL) - return NULL; - if (startswith(udev_device_get_sysname(transportdev), "session")) - break; - } - - /* find iscsi session device */ - sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); - if (sessiondev == NULL) - return NULL; - target = udev_device_get_sysattr_value(sessiondev, "targetname"); - if (target == NULL) { - parent = NULL; - goto out; - } - - if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { - parent = NULL; - goto out; - } - conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); - free(connname); - if (conndev == NULL) { - parent = NULL; - goto out; - } - addr = udev_device_get_sysattr_value(conndev, "persistent_address"); - port = udev_device_get_sysattr_value(conndev, "persistent_port"); - if (addr == NULL || port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); - free(lun); -out: - udev_device_unref(sessiondev); - udev_device_unref(conndev); - return parent; -} - -static struct udev_device *handle_scsi_ata(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *atadev; - const char *port_no; - - assert(parent); - assert(path); - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); - if (!targetdev) - return NULL; - - target_parent = udev_device_get_parent(targetdev); - if (!target_parent) - return NULL; - - atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent)); - if (!atadev) - return NULL; - - port_no = udev_device_get_sysattr_value(atadev, "port_no"); - if (!port_no) { - parent = NULL; - goto out; - } - path_prepend(path, "ata-%s", port_no); -out: - udev_device_unref(atadev); - return parent; -} - -static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) { - struct udev_device *hostdev; - int host, bus, target, lun; - const char *name; - char *base; - char *pos; - DIR *dir; - 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; - - name = udev_device_get_sysname(parent); - if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) - return NULL; - - /* - * Rebase host offset to get the local relative number - * - * Note: This is by definition racy, unreliable and too simple. - * Please do not copy this model anywhere. It's just a left-over - * from the time we had no idea how things should look like in - * the end. - * - * Making assumptions about a global in-kernel counter and use - * that to calculate a local offset is a very broken concept. It - * can only work as long as things are in strict order. - * - * The kernel needs to export the instance/port number of a - * controller directly, without the need for rebase magic like - * this. Manual driver unbind/bind, parallel hotplug/unplug will - * get into the way of this "I hope it works" logic. - */ - basenum = -1; - base = strdup(udev_device_get_syspath(hostdev)); - if (base == NULL) - return NULL; - pos = strrchr(base, '/'); - if (pos == NULL) { - parent = NULL; - goto out; - } - pos[0] = '\0'; - dir = opendir(base); - if (dir == NULL) { - parent = NULL; - goto out; - } - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char *rest; - int i; - - if (dent->d_name[0] == '.') - continue; - if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) - continue; - if (!startswith(dent->d_name, "host")) - continue; - i = strtoul(&dent->d_name[4], &rest, 10); - if (rest[0] != '\0') - continue; - /* - * find the smallest number; the host really needs to export its - * own instance number per parent device; relying on the global host - * enumeration and plainly rebasing the numbers sounds unreliable - */ - if (basenum == -1 || i < basenum) - basenum = i; - } - closedir(dir); - if (basenum == -1) { - parent = NULL; - goto out; - } - host -= basenum; - - path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); -out: - free(base); - return hostdev; -} - -static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path) { - struct udev_device *hostdev; - struct udev_device *vmbusdev; - const char *guid_str; - char *lun = NULL; - 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; - - vmbusdev = udev_device_get_parent(hostdev); - if (!vmbusdev) - return NULL; - - guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id"); - if (!guid_str) - return NULL; - - if (strlen(guid_str) < 37 || guid_str[0] != '{' || guid_str[36] != '}') - return NULL; - - for (i = 1, k = 0; i < 36; i++) { - if (guid_str[i] == '-') - continue; - guid[k++] = guid_str[i]; - } - guid[k] = '\0'; - - format_lun_number(parent, &lun); - path_prepend(path, "vmbus-%s-%s", guid, lun); - free(lun); - return parent; -} - -static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) { - const char *devtype; - const char *name; - const char *id; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL || !streq(devtype, "scsi_device")) - return parent; - - /* firewire */ - id = udev_device_get_sysattr_value(parent, "ieee1394_id"); - if (id != NULL) { - parent = skip_subsystem(parent, "scsi"); - path_prepend(path, "ieee1394-0x%s", id); - *supported_parent = true; - goto out; - } - - /* scsi sysfs does not have a "subsystem" for the transport */ - name = udev_device_get_syspath(parent); - - if (strstr(name, "/rport-") != NULL) { - parent = handle_scsi_fibre_channel(parent, path); - *supported_parent = true; - goto out; - } - - if (strstr(name, "/end_device-") != NULL) { - parent = handle_scsi_sas(parent, path); - *supported_parent = true; - goto out; - } - - if (strstr(name, "/session") != NULL) { - parent = handle_scsi_iscsi(parent, path); - *supported_parent = true; - goto out; - } - - if (strstr(name, "/ata") != NULL) { - parent = handle_scsi_ata(parent, path); - goto out; - } - - if (strstr(name, "/vmbus_") != NULL) { - parent = handle_scsi_hyperv(parent, path); - goto out; - } - - parent = handle_scsi_default(parent, path); -out: - return parent; -} - -static struct udev_device *handle_cciss(struct udev_device *parent, char **path) { - const char *str; - unsigned int controller, disk; - - str = udev_device_get_sysname(parent); - if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2) - return NULL; - - path_prepend(path, "cciss-disk%u", disk); - parent = skip_subsystem(parent, "cciss"); - return parent; -} - -static void handle_scsi_tape(struct udev_device *dev, char **path) { - const char *name; - - /* must be the last device in the syspath */ - if (*path != NULL) - return; - - name = udev_device_get_sysname(dev); - if (startswith(name, "nst") && strchr("lma", name[3]) != NULL) - path_prepend(path, "nst%c", name[3]); - else if (startswith(name, "st") && strchr("lma", name[2]) != NULL) - path_prepend(path, "st%c", name[2]); -} - -static struct udev_device *handle_usb(struct udev_device *parent, char **path) { - const char *devtype; - const char *str; - const char *port; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL) - return parent; - if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device")) - return parent; - - str = udev_device_get_sysname(parent); - port = strchr(str, '-'); - if (port == NULL) - return parent; - port++; - - parent = skip_subsystem(parent, "usb"); - path_prepend(path, "usb-0:%s", port); - return parent; -} - -static struct udev_device *handle_bcma(struct udev_device *parent, char **path) { - const char *sysname; - unsigned int core; - - sysname = udev_device_get_sysname(parent); - if (sscanf(sysname, "bcma%*u:%u", &core) != 1) - return NULL; - - path_prepend(path, "bcma-%u", core); - return parent; -} - -/* Handle devices of AP bus in System z platform. */ -static struct udev_device *handle_ap(struct udev_device *parent, char **path) { - const char *type, *func; - - assert(parent); - assert(path); - - type = udev_device_get_sysattr_value(parent, "type"); - func = udev_device_get_sysattr_value(parent, "ap_functions"); - - if (type != NULL && func != NULL) { - path_prepend(path, "ap-%s-%s", type, func); - goto out; - } - path_prepend(path, "ap-%s", udev_device_get_sysname(parent)); -out: - parent = skip_subsystem(parent, "ap"); - return parent; -} - -static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev_device *parent; - char *path = NULL; - bool supported_transport = false; - bool supported_parent = false; - - assert(dev); - - /* walk up the chain of devices and compose path */ - parent = dev; - while (parent != NULL) { - const char *subsys; - - subsys = udev_device_get_subsystem(parent); - if (subsys == NULL) { - ; - } else if (streq(subsys, "scsi_tape")) { - handle_scsi_tape(parent, &path); - } else if (streq(subsys, "scsi")) { - parent = handle_scsi(parent, &path, &supported_parent); - supported_transport = true; - } else if (streq(subsys, "cciss")) { - parent = handle_cciss(parent, &path); - supported_transport = true; - } else if (streq(subsys, "usb")) { - parent = handle_usb(parent, &path); - supported_transport = true; - } else if (streq(subsys, "bcma")) { - parent = handle_bcma(parent, &path); - supported_transport = true; - } else if (streq(subsys, "serio")) { - path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); - parent = skip_subsystem(parent, "serio"); - } else if (streq(subsys, "pci")) { - path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "pci"); - supported_parent = true; - } else if (streq(subsys, "platform")) { - path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "platform"); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "acpi")) { - path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "acpi"); - supported_parent = true; - } else if (streq(subsys, "xen")) { - path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "xen"); - supported_parent = true; - } else if (streq(subsys, "virtio")) { - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); - path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "scm")) { - path_prepend(&path, "scm-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "scm"); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "ccw")) { - path_prepend(&path, "ccw-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "ccw"); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "ccwgroup")) { - path_prepend(&path, "ccwgroup-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "ccwgroup"); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "ap")) { - parent = handle_ap(parent, &path); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "iucv")) { - path_prepend(&path, "iucv-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "iucv"); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "nvme")) { - const char *nsid = udev_device_get_sysattr_value(dev, "nsid"); - - if (nsid) { - path_prepend(&path, "nvme-%s", nsid); - parent = skip_subsystem(parent, "nvme"); - supported_parent = true; - supported_transport = true; - } - } - - if (parent) - parent = udev_device_get_parent(parent); - } - - /* - * Do not return devices with an unknown parent device type. They - * might produce conflicting IDs if the parent does not provide a - * unique and predictable name. - */ - if (!supported_parent) - path = mfree(path); - - /* - * Do not return block devices without a well-known transport. Some - * devices do not expose their buses and do not provide a unique - * and predictable name that way. - */ - if (streq_ptr(udev_device_get_subsystem(dev), "block") && !supported_transport) - path = mfree(path); - - if (path != NULL) { - char tag[UTIL_NAME_SIZE]; - size_t i; - const char *p; - - /* compose valid udev tag name */ - for (p = path, i = 0; *p; p++) { - if ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'Z') || - (*p >= 'a' && *p <= 'z') || - *p == '-') { - tag[i++] = *p; - continue; - } - - /* skip all leading '_' */ - if (i == 0) - continue; - - /* avoid second '_' */ - if (tag[i-1] == '_') - continue; - - tag[i++] = '_'; - } - /* strip trailing '_' */ - while (i > 0 && tag[i-1] == '_') - i--; - tag[i] = '\0'; - - udev_builtin_add_property(dev, test, "ID_PATH", path); - udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); - free(path); - return EXIT_SUCCESS; - } - return EXIT_FAILURE; -} - -const struct udev_builtin udev_builtin_path_id = { - .name = "path_id", - .cmd = builtin_path_id, - .help = "Compose persistent device path", - .run_once = true, -}; diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c deleted file mode 100644 index 3ebe36f043..0000000000 --- a/src/udev/udev-builtin-uaccess.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * manage device node user ACL - * - * Copyright 2010-2012 Kay Sievers <kay@vrfy.org> - * Copyright 2010 Lennart Poettering - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> - -#include "sd-login.h" - -#include "login-util.h" -#include "logind-acl.h" -#include "udev.h" -#include "util.h" - -static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool test) { - int r; - const char *path = NULL, *seat; - bool changed_acl = false; - uid_t uid; - - umask(0022); - - /* don't muck around with ACLs when the system is not running systemd */ - if (!logind_running()) - return 0; - - path = udev_device_get_devnode(dev); - seat = udev_device_get_property_value(dev, "ID_SEAT"); - if (!seat) - seat = "seat0"; - - r = sd_seat_get_active(seat, NULL, &uid); - if (r == -ENXIO || r == -ENODATA) { - /* No active session on this seat */ - r = 0; - goto finish; - } else if (r < 0) { - log_error("Failed to determine active user on seat %s.", seat); - goto finish; - } - - r = devnode_acl(path, true, false, 0, true, uid); - if (r < 0) { - log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path); - goto finish; - } - - changed_acl = true; - r = 0; - -finish: - if (path && !changed_acl) { - int k; - - /* Better be safe than sorry and reset ACL */ - k = devnode_acl(path, true, false, 0, false, 0); - if (k < 0) { - log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL on %s: %m", path); - if (r >= 0) - r = k; - } - } - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_uaccess = { - .name = "uaccess", - .cmd = builtin_uaccess, - .help = "Manage device node user ACL", -}; diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c deleted file mode 100644 index 587649eff0..0000000000 --- a/src/udev/udev-builtin-usb_id.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * USB device properties and persistent device path - * - * Copyright (c) 2005 SUSE Linux Products GmbH, Germany - * Author: Hannes Reinecke <hare@suse.de> - * - * Copyright (C) 2005-2011 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "string-util.h" -#include "udev.h" - -static void set_usb_iftype(char *to, int if_class_num, size_t len) { - const char *type = "generic"; - - switch (if_class_num) { - case 1: - type = "audio"; - break; - case 2: /* CDC-Control */ - break; - case 3: - type = "hid"; - break; - case 5: /* Physical */ - break; - case 6: - type = "media"; - break; - case 7: - type = "printer"; - break; - case 8: - type = "storage"; - break; - case 9: - type = "hub"; - break; - case 0x0a: /* CDC-Data */ - break; - case 0x0b: /* Chip/Smart Card */ - break; - case 0x0d: /* Content Security */ - break; - case 0x0e: - type = "video"; - break; - case 0xdc: /* Diagnostic Device */ - break; - case 0xe0: /* Wireless Controller */ - break; - case 0xfe: /* Application-specific */ - break; - case 0xff: /* Vendor-specific */ - break; - default: - break; - } - strncpy(to, type, len); - to[len-1] = '\0'; -} - -static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) { - int type_num = 0; - char *eptr; - const char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 1: /* RBC devices */ - type = "rbc"; - break; - case 2: - type = "atapi"; - break; - case 3: - type = "tape"; - break; - case 4: /* UFI */ - type = "floppy"; - break; - case 6: /* Transparent SPC-2 devices */ - type = "scsi"; - break; - default: - break; - } - } - strscpy(to, len, type); - return type_num; -} - -static void set_scsi_type(char *to, const char *from, size_t len) { - int type_num; - char *eptr; - const char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - case 0xe: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - case 7: - case 0xf: - type = "optical"; - break; - case 5: - type = "cd"; - break; - default: - break; - } - } - strscpy(to, len, type); -} - -#define USB_DT_DEVICE 0x01 -#define USB_DT_INTERFACE 0x04 - -static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) { - _cleanup_free_ char *filename = NULL; - _cleanup_close_ int fd = -1; - ssize_t size; - unsigned char buf[18 + 65535]; - size_t pos = 0; - unsigned strpos = 0; - struct usb_interface_descriptor { - 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) - 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 + sizeof(struct usb_interface_descriptor) < (size_t) size && - strpos + 7 < len - 2) { - - struct usb_interface_descriptor *desc; - char if_str[8]; - - desc = (struct usb_interface_descriptor *) &buf[pos]; - if (desc->bLength < 3) - break; - pos += desc->bLength; - - if (desc->bDescriptorType != USB_DT_INTERFACE) - continue; - - if (snprintf(if_str, 8, ":%02x%02x%02x", - desc->bInterfaceClass, - desc->bInterfaceSubClass, - desc->bInterfaceProtocol) != 7) - continue; - - if (strstr(ifs_str, if_str) != NULL) - continue; - - memcpy(&ifs_str[strpos], if_str, 8), - strpos += 7; - } - - if (strpos > 0) { - ifs_str[strpos++] = ':'; - ifs_str[strpos++] = '\0'; - } - - return 0; -} - -/* - * A unique USB identification is generated like this: - * - * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass - * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC', - * use the SCSI vendor and model as USB-Vendor and USB-model. - * 3.) Otherwise, use the USB manufacturer and product as - * USB-Vendor and USB-model. Any non-printable characters - * in those strings will be skipped; a slash '/' will be converted - * into a full stop '.'. - * 4.) If that fails, too, we will use idVendor and idProduct - * as USB-Vendor and USB-model. - * 5.) The USB identification is the USB-vendor and USB-model - * string concatenated with an underscore '_'. - * 6.) If the device supplies a serial number, this number - * 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_enc[256]; - const char *vendor_id; - 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] = ""; - const char *ifnum = NULL; - const char *driver = NULL; - char serial[256]; - - struct udev_device *dev_interface = NULL; - struct udev_device *dev_usb = NULL; - const char *if_class, *if_subclass; - int if_class_num; - int protocol = 0; - size_t l; - char *s; - - 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")) { - dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); - dev_usb = dev; - goto fallback; - } - - /* usb interface directory */ - dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); - if (dev_interface == NULL) { - log_debug("unable to access usb_interface device of '%s'", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); - driver = udev_device_get_sysattr_value(dev_interface, "driver"); - - if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); - if (!if_class) { - log_debug("%s: cannot get bInterfaceClass attribute", - udev_device_get_sysname(dev)); - return EXIT_FAILURE; - } - - if_class_num = strtoul(if_class, NULL, 16); - if (if_class_num == 8) { - /* mass storage */ - if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); - if (if_subclass != NULL) - protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); - } else { - set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); - } - - log_debug("%s: if_class %d protocol %d", - udev_device_get_syspath(dev_interface), if_class_num, protocol); - - /* usb device directory */ - dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); - if (!dev_usb) { - log_debug("unable to find parent 'usb' device of '%s'", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - /* all interfaces of the device in a single string */ - dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); - - /* mass storage : SCSI or ATAPI */ - 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; - - /* get scsi device */ - dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (dev_scsi == NULL) { - log_debug("unable to find parent 'scsi' device of '%s'", - udev_device_get_syspath(dev)); - goto fallback; - } - if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { - log_debug("invalid scsi device '%s'", udev_device_get_sysname(dev_scsi)); - goto fallback; - } - - /* Generic SPC-2 device */ - scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); - if (!scsi_vendor) { - log_debug("%s: cannot get SCSI vendor attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - - scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); - if (!scsi_model) { - log_debug("%s: cannot get SCSI model attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - - scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); - if (!scsi_type) { - log_debug("%s: cannot get SCSI type attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); - - scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); - if (!scsi_rev) { - log_debug("%s: cannot get SCSI revision attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - - /* - * some broken devices have the same identifiers - * for all luns, export the target:lun number - */ - sprintf(instance_str, "%d:%d", target, lun); - } - -fallback: - vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); - product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); - - /* fallback to USB vendor & device */ - if (vendor_str[0] == '\0') { - const char *usb_vendor = NULL; - - usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); - if (!usb_vendor) - usb_vendor = vendor_id; - if (!usb_vendor) { - log_debug("No USB vendor information available"); - return EXIT_FAILURE; - } - udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - } - - if (model_str[0] == '\0') { - const char *usb_model = NULL; - - usb_model = udev_device_get_sysattr_value(dev_usb, "product"); - if (!usb_model) - usb_model = product_id; - if (!usb_model) - return EXIT_FAILURE; - udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - } - - if (revision_str[0] == '\0') { - const char *usb_rev; - - usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); - if (usb_rev) { - util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - } - } - - if (serial_str[0] == '\0') { - const char *usb_serial; - - usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); - if (usb_serial) { - const unsigned char *p; - - /* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */ - for (p = (unsigned char *)usb_serial; *p != '\0'; p++) - if (*p < 0x20 || *p > 0x7f || *p == ',') { - usb_serial = NULL; - break; - } - } - - if (usb_serial) { - util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); - util_replace_chars(serial_str, NULL); - } - } - - s = serial; - l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); - if (!isempty(serial_str)) - l = strpcpyl(&s, l, "_", serial_str, NULL); - - if (!isempty(instance_str)) - strpcpyl(&s, l, "-", instance_str, NULL); - - udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); - udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); - udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); - udev_builtin_add_property(dev, test, "ID_MODEL", model_str); - udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); - 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 (!isempty(serial_str)) - udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); - if (!isempty(type_str)) - udev_builtin_add_property(dev, test, "ID_TYPE", type_str); - if (!isempty(instance_str)) - udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); - udev_builtin_add_property(dev, test, "ID_BUS", "usb"); - 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); - if (driver != NULL) - udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_usb_id = { - .name = "usb_id", - .cmd = builtin_usb_id, - .help = "USB device properties", - .run_once = true, -}; diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c deleted file mode 100644 index e6b36f124f..0000000000 --- a/src/udev/udev-builtin.c +++ /dev/null @@ -1,142 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2007-2012 Kay Sievers <kay@vrfy.org> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <getopt.h> -#include <stdio.h> -#include <string.h> - -#include "string-util.h" -#include "udev.h" - -static bool initialized; - -static const struct udev_builtin *builtins[] = { -#ifdef HAVE_BLKID - [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, -#endif - [UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs, - [UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb, - [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, - [UDEV_BUILTIN_KEYBOARD] = &udev_builtin_keyboard, -#ifdef HAVE_KMOD - [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, -#endif - [UDEV_BUILTIN_NET_ID] = &udev_builtin_net_id, - [UDEV_BUILTIN_NET_LINK] = &udev_builtin_net_setup_link, - [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, - [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, -#ifdef HAVE_ACL - [UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess, -#endif -}; - -void udev_builtin_init(struct udev *udev) { - unsigned int i; - - if (initialized) - return; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && builtins[i]->init) - builtins[i]->init(udev); - - initialized = true; -} - -void udev_builtin_exit(struct udev *udev) { - unsigned int i; - - if (!initialized) - return; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && builtins[i]->exit) - builtins[i]->exit(udev); - - initialized = false; -} - -bool udev_builtin_validate(struct udev *udev) { - unsigned int i; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev)) - return true; - return false; -} - -void udev_builtin_list(struct udev *udev) { - unsigned int i; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i]) - fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help); -} - -const char *udev_builtin_name(enum udev_builtin_cmd cmd) { - if (!builtins[cmd]) - return NULL; - - return builtins[cmd]->name; -} - -bool udev_builtin_run_once(enum udev_builtin_cmd cmd) { - if (!builtins[cmd]) - return false; - - return builtins[cmd]->run_once; -} - -enum udev_builtin_cmd udev_builtin_lookup(const char *command) { - char name[UTIL_PATH_SIZE]; - enum udev_builtin_cmd i; - char *pos; - - strscpy(name, sizeof(name), command); - pos = strchr(name, ' '); - if (pos) - pos[0] = '\0'; - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && streq(builtins[i]->name, name)) - return i; - return UDEV_BUILTIN_MAX; -} - -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) { - char arg[UTIL_PATH_SIZE]; - int argc; - char *argv[128]; - - if (!builtins[cmd]) - return -EOPNOTSUPP; - - /* we need '0' here to reset the internal state */ - optind = 0; - strscpy(arg, sizeof(arg), command); - udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); - return builtins[cmd]->cmd(dev, argc, argv, test); -} - -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) { - udev_device_add_property(dev, key, val); - - if (test) - printf("%s=%s\n", key, val); - return 0; -} diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c deleted file mode 100644 index 7717ac7924..0000000000 --- a/src/udev/udev-ctrl.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers <kay@vrfy.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include <errno.h> -#include <poll.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "socket-util.h" -#include "udev.h" - -/* wire protocol magic must match */ -#define UDEV_CTRL_MAGIC 0xdead1dea - -enum udev_ctrl_msg_type { - UDEV_CTRL_UNKNOWN, - UDEV_CTRL_SET_LOG_LEVEL, - UDEV_CTRL_STOP_EXEC_QUEUE, - UDEV_CTRL_START_EXEC_QUEUE, - UDEV_CTRL_RELOAD, - UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_CHILDREN_MAX, - UDEV_CTRL_PING, - UDEV_CTRL_EXIT, -}; - -struct udev_ctrl_msg_wire { - char version[16]; - unsigned int magic; - enum udev_ctrl_msg_type type; - union { - int intval; - char buf[256]; - }; -}; - -struct udev_ctrl_msg { - int refcount; - struct udev_ctrl_connection *conn; - struct udev_ctrl_msg_wire ctrl_msg_wire; -}; - -struct udev_ctrl { - int refcount; - struct udev *udev; - int sock; - union sockaddr_union saddr; - socklen_t addrlen; - bool bound; - bool cleanup_socket; - bool connected; -}; - -struct udev_ctrl_connection { - int refcount; - struct udev_ctrl *uctrl; - int sock; -}; - -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { - struct udev_ctrl *uctrl; - const int on = 1; - int r; - - uctrl = new0(struct udev_ctrl, 1); - if (uctrl == NULL) - return NULL; - uctrl->refcount = 1; - uctrl->udev = udev; - - if (fd < 0) { - uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (uctrl->sock < 0) { - log_error_errno(errno, "error getting socket: %m"); - udev_ctrl_unref(uctrl); - return NULL; - } - } else { - uctrl->bound = true; - uctrl->sock = fd; - } - - /* - * FIXME: remove it as soon as we can depend on this: - * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949 - */ - r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if (r < 0) - log_warning_errno(errno, "could not set SO_PASSCRED: %m"); - - uctrl->saddr.un.sun_family = AF_LOCAL; - strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control"); - uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); - return uctrl; -} - -struct udev_ctrl *udev_ctrl_new(struct udev *udev) { - return udev_ctrl_new_from_fd(udev, -1); -} - -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { - int err; - - if (!uctrl->bound) { - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - if (err < 0 && errno == EADDRINUSE) { - unlink(uctrl->saddr.un.sun_path); - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - } - - if (err < 0) - return log_error_errno(errno, "bind failed: %m"); - - err = listen(uctrl->sock, 0); - if (err < 0) - return log_error_errno(errno, "listen failed: %m"); - - uctrl->bound = true; - uctrl->cleanup_socket = true; - } - return 0; -} - -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) { - return uctrl->udev; -} - -static struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) { - if (uctrl) - uctrl->refcount++; - - return uctrl; -} - -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) { - if (uctrl && -- uctrl->refcount == 0) { - if (uctrl->sock >= 0) - close(uctrl->sock); - free(uctrl); - } - - return NULL; -} - -int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { - if (uctrl == NULL) - return 0; - if (uctrl->cleanup_socket) - unlink(uctrl->saddr.un.sun_path); - return 0; -} - -int udev_ctrl_get_fd(struct udev_ctrl *uctrl) { - if (uctrl == NULL) - return -EINVAL; - return uctrl->sock; -} - -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { - struct udev_ctrl_connection *conn; - struct ucred ucred = {}; - const int on = 1; - int r; - - conn = new(struct udev_ctrl_connection, 1); - if (conn == NULL) - return NULL; - conn->refcount = 1; - conn->uctrl = uctrl; - - conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); - if (conn->sock < 0) { - if (errno != EINTR) - log_error_errno(errno, "unable to receive ctrl connection: %m"); - goto err; - } - - /* check peer credential of connection */ - r = getpeercred(conn->sock, &ucred); - if (r < 0) { - log_error_errno(r, "unable to receive credentials of ctrl connection: %m"); - goto err; - } - if (ucred.uid > 0) { - log_error("sender uid="UID_FMT", message ignored", ucred.uid); - goto err; - } - - /* enable receiving of the sender credentials in the messages */ - r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if (r < 0) - log_warning_errno(errno, "could not set SO_PASSCRED: %m"); - - udev_ctrl_ref(uctrl); - return conn; -err: - if (conn->sock >= 0) - close(conn->sock); - return mfree(conn); -} - -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) { - if (conn == NULL) - return NULL; - conn->refcount++; - return conn; -} - -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) { - if (conn && -- conn->refcount == 0) { - if (conn->sock >= 0) - close(conn->sock); - - udev_ctrl_unref(conn->uctrl); - - free(conn); - } - - return NULL; -} - -static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) { - struct udev_ctrl_msg_wire ctrl_msg_wire; - int err = 0; - - memzero(&ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); - strcpy(ctrl_msg_wire.version, "udev-" VERSION); - ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; - ctrl_msg_wire.type = type; - - if (buf != NULL) - strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); - else - ctrl_msg_wire.intval = intval; - - if (!uctrl->connected) { - if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) { - err = -errno; - goto out; - } - uctrl->connected = true; - } - if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { - err = -errno; - goto out; - } - - /* wait for peer message handling or disconnect */ - for (;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = uctrl->sock; - pfd[0].events = POLLIN; - r = poll(pfd, 1, timeout * MSEC_PER_SEC); - if (r < 0) { - if (errno == EINTR) - continue; - err = -errno; - break; - } - - if (r > 0 && pfd[0].revents & POLLERR) { - err = -EIO; - break; - } - - if (r == 0) - err = -ETIMEDOUT; - break; - } -out: - return err; -} - -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); -} - -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); -} - -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); -} - -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); -} - -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); -} - -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); -} - -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { - struct udev_ctrl_msg *uctrl_msg; - ssize_t size; - struct cmsghdr *cmsg; - struct iovec iov; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - struct msghdr smsg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cred_msg, - .msg_controllen = sizeof(cred_msg), - }; - struct ucred *cred; - - uctrl_msg = new0(struct udev_ctrl_msg, 1); - if (uctrl_msg == NULL) - return NULL; - uctrl_msg->refcount = 1; - uctrl_msg->conn = conn; - udev_ctrl_connection_ref(conn); - - /* wait for the incoming message */ - for (;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = conn->sock; - pfd[0].events = POLLIN; - - r = poll(pfd, 1, 10000); - if (r < 0) { - if (errno == EINTR) - continue; - goto err; - } else if (r == 0) { - log_error("timeout waiting for ctrl message"); - goto err; - } else { - if (!(pfd[0].revents & POLLIN)) { - log_error_errno(errno, "ctrl connection error: %m"); - goto err; - } - } - - break; - } - - iov.iov_base = &uctrl_msg->ctrl_msg_wire; - iov.iov_len = sizeof(struct udev_ctrl_msg_wire); - - size = recvmsg(conn->sock, &smsg, 0); - if (size < 0) { - log_error_errno(errno, "unable to receive ctrl message: %m"); - goto err; - } - - cmsg_close_all(&smsg); - - cmsg = CMSG_FIRSTHDR(&smsg); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - log_error("no sender credentials received, message ignored"); - goto err; - } - - cred = (struct ucred *) CMSG_DATA(cmsg); - - if (cred->uid != 0) { - log_error("sender uid="UID_FMT", message ignored", cred->uid); - goto err; - } - - if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { - log_error("message magic 0x%08x doesn't match, ignore it", uctrl_msg->ctrl_msg_wire.magic); - goto err; - } - - return uctrl_msg; -err: - udev_ctrl_msg_unref(uctrl_msg); - return NULL; -} - -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg && -- ctrl_msg->refcount == 0) { - udev_ctrl_connection_unref(ctrl_msg->conn); - free(ctrl_msg); - } - - return NULL; -} - -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) - return 1; - return -1; -} - -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) - return ctrl_msg->ctrl_msg_wire.buf; - return NULL; -} - -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) - return 1; - return -1; -} - -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) - return 1; - return -1; -} diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c deleted file mode 100644 index 54cd741bb1..0000000000 --- a/src/udev/udev-event.c +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <net/if.h> -#include <poll.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/prctl.h> -#include <sys/signalfd.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "netlink-util.h" -#include "process-util.h" -#include "signal-util.h" -#include "string-util.h" -#include "udev.h" - -typedef struct Spawn { - const char *cmd; - pid_t pid; - usec_t timeout_warn; - usec_t timeout; - bool accept_failure; -} Spawn; - -struct udev_event *udev_event_new(struct udev_device *dev) { - struct udev *udev = udev_device_get_udev(dev); - struct udev_event *event; - - event = new0(struct udev_event, 1); - if (event == NULL) - return NULL; - event->dev = dev; - event->udev = udev; - udev_list_init(udev, &event->run_list, false); - udev_list_init(udev, &event->seclabel_list, false); - event->birth_usec = clock_boottime_or_monotonic(); - return event; -} - -void udev_event_unref(struct udev_event *event) { - if (event == NULL) - return; - sd_netlink_unref(event->rtnl); - udev_list_cleanup(&event->run_list); - udev_list_cleanup(&event->seclabel_list); - free(event->program_result); - free(event->name); - free(event); -} - -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) { - struct udev_device *dev = event->dev; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVNODE, - SUBST_ATTR, - SUBST_ENV, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_DRIVER, - SUBST_DEVPATH, - SUBST_ID, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_PARENT, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - }; - static const struct subst_map { - const char *name; - const char fmt; - enum subst_type type; - } map[] = { - { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, - { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, - { .name = "env", .fmt = 'E', .type = SUBST_ENV }, - { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, - { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, - { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, - { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, - { .name = "id", .fmt = 'b', .type = SUBST_ID }, - { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, - { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, - { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, - { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, - { .name = "name", .fmt = 'D', .type = SUBST_NAME }, - { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, - { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, - { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, - }; - const char *from; - char *s; - size_t l; - - assert(dev); - - from = src; - s = dest; - l = size; - - for (;;) { - enum subst_type type = SUBST_UNKNOWN; - char attrbuf[UTIL_PATH_SIZE]; - char *attr = NULL; - - while (from[0] != '\0') { - if (from[0] == '$') { - /* substitute named variable */ - unsigned int i; - - if (from[1] == '$') { - from++; - goto copy; - } - - for (i = 0; i < ELEMENTSOF(map); i++) { - if (startswith(&from[1], map[i].name)) { - type = map[i].type; - from += strlen(map[i].name)+1; - goto subst; - } - } - } else if (from[0] == '%') { - /* substitute format char */ - unsigned int i; - - if (from[1] == '%') { - from++; - goto copy; - } - - for (i = 0; i < ELEMENTSOF(map); i++) { - if (from[1] == map[i].fmt) { - type = map[i].type; - from += 2; - goto subst; - } - } - } -copy: - /* copy char */ - if (l == 0) - goto out; - s[0] = from[0]; - from++; - s++; - l--; - } - - goto out; -subst: - /* extract possible $format{attr} */ - if (from[0] == '{') { - unsigned int i; - - from++; - for (i = 0; from[i] != '}'; i++) { - if (from[i] == '\0') { - log_error("missing closing brace for format '%s'", src); - goto out; - } - } - if (i >= sizeof(attrbuf)) - goto out; - memcpy(attrbuf, from, i); - attrbuf[i] = '\0'; - from += i+1; - attr = attrbuf; - } else { - attr = NULL; - } - - switch (type) { - case SUBST_DEVPATH: - l = strpcpy(&s, l, udev_device_get_devpath(dev)); - break; - case SUBST_KERNEL: - l = strpcpy(&s, l, udev_device_get_sysname(dev)); - break; - case SUBST_KERNEL_NUMBER: - if (udev_device_get_sysnum(dev) == NULL) - break; - l = strpcpy(&s, l, udev_device_get_sysnum(dev)); - break; - case SUBST_ID: - if (event->dev_parent == NULL) - break; - l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); - break; - case SUBST_DRIVER: { - const char *driver; - - if (event->dev_parent == NULL) - break; - - driver = udev_device_get_driver(event->dev_parent); - if (driver == NULL) - break; - l = strpcpy(&s, l, driver); - break; - } - case SUBST_MAJOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%u", major(udev_device_get_devnum(dev))); - l = strpcpy(&s, l, num); - break; - } - case SUBST_MINOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%u", minor(udev_device_get_devnum(dev))); - l = strpcpy(&s, l, num); - break; - } - case SUBST_RESULT: { - char *rest; - int i; - - if (event->program_result == NULL) - break; - /* get part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - char result[UTIL_PATH_SIZE]; - char tmp[UTIL_PATH_SIZE]; - char *cpos; - - strscpy(result, sizeof(result), event->program_result); - cpos = result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - if (cpos[0] == '\0') - break; - } - if (i > 0) { - log_error("requested part of result string not found"); - break; - } - strscpy(tmp, sizeof(tmp), cpos); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(tmp, ' '); - if (cpos) - cpos[0] = '\0'; - } - l = strpcpy(&s, l, tmp); - } else { - l = strpcpy(&s, l, event->program_result); - } - break; - } - case SUBST_ATTR: { - const char *value = NULL; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - int count; - - if (attr == NULL) { - log_error("missing file parameter for attr"); - break; - } - - /* try to read the value specified by "[dmi/id]product_name" */ - if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) - value = vbuf; - - /* try to read the attribute the device */ - if (value == NULL) - value = udev_device_get_sysattr_value(event->dev, attr); - - /* try to read the attribute of the parent device, other matches have selected */ - if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) - value = udev_device_get_sysattr_value(event->dev_parent, attr); - - if (value == NULL) - break; - - /* strip trailing whitespace, and replace unwanted characters */ - if (value != vbuf) - strscpy(vbuf, sizeof(vbuf), value); - len = strlen(vbuf); - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - log_debug("%i character(s) replaced" , count); - l = strpcpy(&s, l, vbuf); - break; - } - case SUBST_PARENT: { - struct udev_device *dev_parent; - const char *devnode; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - break; - devnode = udev_device_get_devnode(dev_parent); - if (devnode != NULL) - l = strpcpy(&s, l, devnode + strlen("/dev/")); - break; - } - case SUBST_DEVNODE: - if (udev_device_get_devnode(dev) != NULL) - l = strpcpy(&s, l, udev_device_get_devnode(dev)); - break; - case SUBST_NAME: - if (event->name != NULL) - l = strpcpy(&s, l, event->name); - else if (udev_device_get_devnode(dev) != NULL) - l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/")); - else - l = strpcpy(&s, l, udev_device_get_sysname(dev)); - break; - case SUBST_LINKS: { - struct udev_list_entry *list_entry; - - list_entry = udev_device_get_devlinks_list_entry(dev); - if (list_entry == NULL) - break; - l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/")); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) - l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL); - break; - } - case SUBST_ROOT: - l = strpcpy(&s, l, "/dev"); - break; - case SUBST_SYS: - l = strpcpy(&s, l, "/sys"); - break; - case SUBST_ENV: - if (attr == NULL) { - break; - } else { - const char *value; - - value = udev_device_get_property_value(event->dev, attr); - if (value == NULL) - break; - l = strpcpy(&s, l, value); - break; - } - default: - log_error("unknown substitution type=%i", type); - break; - } - } - -out: - s[0] = '\0'; - return l; -} - -static int spawn_exec(struct udev_event *event, - const char *cmd, char *const argv[], char **envp, - int fd_stdout, int fd_stderr) { - _cleanup_close_ int fd = -1; - int r; - - /* discard child output or connect to pipe */ - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - r = dup2(fd, STDIN_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdin failed: %m"); - - if (fd_stdout < 0) { - r = dup2(fd, STDOUT_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdout failed: %m"); - } - - if (fd_stderr < 0) { - r = dup2(fd, STDERR_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stderr failed: %m"); - } - } else - log_warning_errno(errno, "open /dev/null failed: %m"); - - /* connect pipes to std{out,err} */ - if (fd_stdout >= 0) { - r = dup2(fd_stdout, STDOUT_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdout failed: %m"); - - fd_stdout = safe_close(fd_stdout); - } - - if (fd_stderr >= 0) { - r = dup2(fd_stderr, STDERR_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdout failed: %m"); - - fd_stderr = safe_close(fd_stderr); - } - - /* terminate child in case parent goes away */ - prctl(PR_SET_PDEATHSIG, SIGTERM); - - /* restore sigmask before exec */ - (void) reset_signal_mask(); - - execve(argv[0], argv, envp); - - /* exec failed */ - return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd); -} - -static void spawn_read(struct udev_event *event, - usec_t timeout_usec, - const char *cmd, - int fd_stdout, int fd_stderr, - char *result, size_t ressize) { - _cleanup_close_ int fd_ep = -1; - struct epoll_event ep_outpipe = { - .events = EPOLLIN, - .data.ptr = &fd_stdout, - }; - struct epoll_event ep_errpipe = { - .events = EPOLLIN, - .data.ptr = &fd_stderr, - }; - size_t respos = 0; - int r; - - /* read from child if requested */ - if (fd_stdout < 0 && fd_stderr < 0) - return; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - log_error_errno(errno, "error creating epoll fd: %m"); - return; - } - - if (fd_stdout >= 0) { - r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe); - if (r < 0) { - log_error_errno(errno, "fail to add stdout fd to epoll: %m"); - return; - } - } - - if (fd_stderr >= 0) { - r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe); - if (r < 0) { - log_error_errno(errno, "fail to add stderr fd to epoll: %m"); - return; - } - } - - /* read child output */ - while (fd_stdout >= 0 || fd_stderr >= 0) { - int timeout; - int fdcount; - struct epoll_event ev[4]; - int i; - - if (timeout_usec > 0) { - usec_t age_usec; - - age_usec = clock_boottime_or_monotonic() - event->birth_usec; - if (age_usec >= timeout_usec) { - log_error("timeout '%s'", cmd); - return; - } - timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC; - } else { - timeout = -1; - } - - fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - log_error_errno(errno, "failed to poll: %m"); - return; - } else if (fdcount == 0) { - log_error("timeout '%s'", cmd); - return; - } - - for (i = 0; i < fdcount; i++) { - int *fd = (int *)ev[i].data.ptr; - - if (*fd < 0) - continue; - - if (ev[i].events & EPOLLIN) { - ssize_t count; - char buf[4096]; - - count = read(*fd, buf, sizeof(buf)-1); - if (count <= 0) - continue; - buf[count] = '\0'; - - /* store stdout result */ - if (result != NULL && *fd == fd_stdout) { - if (respos + count < ressize) { - memcpy(&result[respos], buf, count); - respos += count; - } else { - log_error("'%s' ressize %zu too short", cmd, ressize); - } - } - - /* log debug output only if we watch stderr */ - if (fd_stderr >= 0) { - char *pos; - char *line; - - pos = buf; - while ((line = strsep(&pos, "\n"))) { - if (pos != NULL || line[0] != '\0') - log_debug("'%s'(%s) '%s'", cmd, *fd == fd_stdout ? "out" : "err" , line); - } - } - } else if (ev[i].events & EPOLLHUP) { - r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL); - if (r < 0) { - log_error_errno(errno, "failed to remove fd from epoll: %m"); - return; - } - *fd = -1; - } - } - } - - /* return the child's stdout string */ - if (result != NULL) - 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_debug("Process '%s' succeeded.", spawn->cmd); - sd_event_exit(sd_event_source_get_event(s), 0); - - return 1; - } else if (spawn->accept_failure) - log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); - else - log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); - - 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, - bool accept_failure) { - Spawn spawn = { - .cmd = cmd, - .pid = pid, - .accept_failure = accept_failure, - }; - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - int r, ret; - - r = sd_event_new(&e); - if (r < 0) - return r; - - if (timeout_usec > 0) { - usec_t usec, age_usec; - - 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; - - 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; - } - - spawn.timeout = timeout_usec - age_usec; - - 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; - } - } - - r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn); - if (r < 0) - return r; - - r = sd_event_loop(e); - if (r < 0) - return r; - - 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[]) { - int i = 0; - char *pos; - - if (strchr(cmd, ' ') == NULL) { - argv[i++] = cmd; - goto out; - } - - pos = cmd; - while (pos != NULL && pos[0] != '\0') { - if (pos[0] == '\'') { - /* do not separate quotes */ - pos++; - argv[i] = strsep(&pos, "\'"); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } - i++; - } -out: - argv[i] = NULL; - if (argc) - *argc = i; - return 0; -} - -int udev_event_spawn(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - bool accept_failure, - const char *cmd, - char *result, size_t ressize) { - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - pid_t pid; - int err = 0; - - /* pipes from child to parent */ - if (result != NULL || log_get_max_level() >= LOG_INFO) { - if (pipe2(outpipe, O_NONBLOCK) != 0) { - err = log_error_errno(errno, "pipe failed: %m"); - goto out; - } - } - if (log_get_max_level() >= LOG_INFO) { - if (pipe2(errpipe, O_NONBLOCK) != 0) { - err = log_error_errno(errno, "pipe failed: %m"); - goto out; - } - } - - pid = fork(); - switch(pid) { - case 0: - { - char arg[UTIL_PATH_SIZE]; - char *argv[128]; - char program[UTIL_PATH_SIZE]; - - /* child closes parent's ends of pipes */ - outpipe[READ_END] = safe_close(outpipe[READ_END]); - errpipe[READ_END] = safe_close(errpipe[READ_END]); - - strscpy(arg, sizeof(arg), cmd); - udev_build_argv(event->udev, arg, NULL, argv); - - /* allow programs in /usr/lib/udev/ to be called without the path */ - if (argv[0][0] != '/') { - strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL); - argv[0] = program; - } - - log_debug("starting '%s'", cmd); - - spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev), - outpipe[WRITE_END], errpipe[WRITE_END]); - - _exit(2); - } - case -1: - log_error_errno(errno, "fork of '%s' failed: %m", cmd); - err = -1; - goto out; - default: - /* parent closed child's ends of pipes */ - outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]); - errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]); - - spawn_read(event, - timeout_usec, - cmd, - outpipe[READ_END], errpipe[READ_END], - result, ressize); - - err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); - } - -out: - if (outpipe[READ_END] >= 0) - close(outpipe[READ_END]); - if (outpipe[WRITE_END] >= 0) - close(outpipe[WRITE_END]); - if (errpipe[READ_END] >= 0) - close(errpipe[READ_END]); - if (errpipe[WRITE_END] >= 0) - close(errpipe[WRITE_END]); - return err; -} - -static int rename_netif(struct udev_event *event) { - struct udev_device *dev = event->dev; - char name[IFNAMSIZ]; - const char *oldname; - int r; - - oldname = udev_device_get_sysname(dev); - - strscpy(name, IFNAMSIZ, event->name); - - r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name); - if (r < 0) - return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name); - - log_debug("renamed network interface '%s' to '%s'", oldname, name); - - return 0; -} - -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) { - 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); - 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); - - if (major(udev_device_get_devnum(dev)) != 0) - udev_node_remove(dev); - } else { - event->dev_db = udev_device_clone_with_db(dev); - if (event->dev_db != NULL) { - /* 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")) - udev_device_copy_properties(dev, event->dev_db); - } - - udev_rules_apply_to_event(rules, event, - timeout_usec, timeout_warn_usec, - properties_list); - - /* rename a new network interface, if needed */ - if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && - event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) { - int r; - - r = rename_netif(event); - if (r < 0) - 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 { - 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 - log_debug("changed devpath to '%s'", udev_device_get_devpath(dev)); - } - } - - if (major(udev_device_get_devnum(dev)) > 0) { - bool apply; - - /* remove/update possible left-over symlinks from old database entry */ - if (event->dev_db != NULL) - udev_node_update_old_links(dev, event->dev_db); - - if (!event->owner_set) - event->uid = udev_device_get_devnode_uid(dev); - - if (!event->group_set) - event->gid = udev_device_get_devnode_gid(dev); - - if (!event->mode_set) { - if (udev_device_get_devnode_mode(dev) > 0) { - /* kernel supplied value */ - event->mode = udev_device_get_devnode_mode(dev); - } else if (event->gid > 0) { - /* default 0660 if a group is assigned */ - event->mode = 0660; - } else { - /* default 0600 */ - event->mode = 0600; - } - } - - apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; - udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); - } - - /* preserve old, or get new initialization timestamp */ - udev_device_ensure_usec_initialized(event->dev, event->dev_db); - - /* (re)write database file */ - udev_device_tag_index(dev, event->dev_db, true); - udev_device_update_db(dev); - udev_device_set_is_initialized(dev); - - 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) { - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { - char command[UTIL_PATH_SIZE]; - const char *cmd = udev_list_entry_get_name(list_entry); - enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry); - - udev_event_apply_format(event, cmd, command, sizeof(command)); - - if (builtin_cmd < UDEV_BUILTIN_MAX) - udev_builtin_run(event->dev, builtin_cmd, command, false); - else { - if (event->exec_delay > 0) { - log_debug("delay execution of '%s'", command); - sleep(event->exec_delay); - } - - udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0); - } - } -} diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c deleted file mode 100644 index 43004bc0bc..0000000000 --- a/src/udev/udev-node.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "formats-util.h" -#include "fs-util.h" -#include "selinux-util.h" -#include "smack-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev.h" - -static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { - struct stat stats; - char target[UTIL_PATH_SIZE]; - char *s; - size_t l; - char slink_tmp[UTIL_PATH_SIZE + 32]; - int i = 0; - int tail = 0; - int err = 0; - - /* use relative link */ - target[0] = '\0'; - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - s = target; - l = sizeof(target); - while (slink[i] != '\0') { - if (slink[i] == '/') - l = strpcpy(&s, l, "../"); - i++; - } - l = strscpy(s, l, &node[tail]); - if (l == 0) { - err = -EINVAL; - goto exit; - } - - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - log_error("conflicting device node '%s' found, link to '%s' will not be created", slink, node); - goto exit; - } else if (S_ISLNK(stats.st_mode)) { - char buf[UTIL_PATH_SIZE]; - int len; - - len = readlink(slink, buf, sizeof(buf)); - if (len > 0 && len < (int)sizeof(buf)) { - buf[len] = '\0'; - if (streq(target, buf)) { - log_debug("preserve already existing symlink '%s' to '%s'", slink, target); - label_fix(slink, true, false); - utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); - goto exit; - } - } - } - } else { - log_debug("creating symlink '%s' to '%s'", slink, target); - do { - err = mkdir_parents_label(slink, 0755); - if (err != 0 && err != -ENOENT) - break; - mac_selinux_create_file_prepare(slink, S_IFLNK); - err = symlink(target, slink); - if (err != 0) - err = -errno; - mac_selinux_create_file_clear(); - } while (err == -ENOENT); - if (err == 0) - goto exit; - } - - log_debug("atomically replace '%s'", slink); - strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); - unlink(slink_tmp); - do { - err = mkdir_parents_label(slink_tmp, 0755); - if (err != 0 && err != -ENOENT) - break; - mac_selinux_create_file_prepare(slink_tmp, S_IFLNK); - err = symlink(target, slink_tmp); - if (err != 0) - err = -errno; - mac_selinux_create_file_clear(); - } while (err == -ENOENT); - if (err != 0) { - log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp); - goto exit; - } - err = rename(slink_tmp, slink); - if (err != 0) { - log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); - unlink(slink_tmp); - } -exit: - return err; -} - -/* find device node of device with highest priority */ -static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) { - struct udev *udev = udev_device_get_udev(dev); - DIR *dir; - int priority = 0; - const char *target = NULL; - - if (add) { - priority = udev_device_get_devlink_priority(dev); - strscpy(buf, bufsize, udev_device_get_devnode(dev)); - target = buf; - } - - dir = opendir(stackdir); - if (dir == NULL) - return target; - for (;;) { - struct udev_device *dev_db; - struct dirent *dent; - - dent = readdir(dir); - if (dent == NULL || dent->d_name[0] == '\0') - break; - if (dent->d_name[0] == '.') - continue; - - log_debug("found '%s' claiming '%s'", dent->d_name, stackdir); - - /* did we find ourself? */ - if (streq(dent->d_name, udev_device_get_id_filename(dev))) - continue; - - dev_db = udev_device_new_from_device_id(udev, dent->d_name); - if (dev_db != NULL) { - const char *devnode; - - devnode = udev_device_get_devnode(dev_db); - if (devnode != NULL) { - if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { - log_debug("'%s' claims priority %i for '%s'", - udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); - priority = udev_device_get_devlink_priority(dev_db); - strscpy(buf, bufsize, devnode); - target = buf; - } - } - udev_device_unref(dev_db); - } - } - closedir(dir); - return target; -} - -/* manage "stack of names" with possibly specified device priorities */ -static void link_update(struct udev_device *dev, const char *slink, bool add) { - char name_enc[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE * 2]; - char dirname[UTIL_PATH_SIZE]; - const char *target; - char buf[UTIL_PATH_SIZE]; - - util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc)); - strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); - strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); - - if (!add && unlink(filename) == 0) - rmdir(dirname); - - target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); - if (target == NULL) { - log_debug("no reference left, remove '%s'", slink); - if (unlink(slink) == 0) - rmdir_parents(slink, "/"); - } else { - log_debug("creating link '%s' to '%s'", slink, target); - node_symlink(dev, target, slink); - } - - if (add) { - int err; - - do { - int fd; - - err = mkdir_parents(filename, 0755); - if (err != 0 && err != -ENOENT) - break; - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) - close(fd); - else - err = -errno; - } while (err == -ENOENT); - } -} - -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { - struct udev_list_entry *list_entry; - - /* update possible left-over symlinks */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { - const char *name = udev_list_entry_get_name(list_entry); - struct udev_list_entry *list_entry_current; - int found; - - /* check if old link name still belongs to this device */ - found = 0; - udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { - const char *name_current = udev_list_entry_get_name(list_entry_current); - - if (streq(name, name_current)) { - found = 1; - break; - } - } - if (found) - continue; - - log_debug("update old name, '%s' no longer belonging to '%s'", - name, udev_device_get_devpath(dev)); - link_update(dev, name, false); - } -} - -static int node_permissions_apply(struct udev_device *dev, bool apply, - mode_t mode, uid_t uid, gid_t gid, - struct udev_list *seclabel_list) { - const char *devnode = udev_device_get_devnode(dev); - dev_t devnum = udev_device_get_devnum(dev); - struct stat stats; - struct udev_list_entry *entry; - int err = 0; - - if (streq(udev_device_get_subsystem(dev), "block")) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (lstat(devnode, &stats) != 0) { - err = log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode); - goto out; - } - - if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { - err = -EEXIST; - log_debug("found node '%s' with non-matching devnum %s, skip handling", - udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); - goto out; - } - - if (apply) { - bool selinux = false; - bool smack = false; - - if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { - log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); - err = chmod(devnode, mode); - if (err < 0) - log_warning_errno(errno, "setting mode of %s to %#o failed: %m", devnode, mode); - err = chown(devnode, uid, gid); - if (err < 0) - log_warning_errno(errno, "setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid); - } else { - log_debug("preserve permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); - } - - /* apply SECLABEL{$module}=$label */ - udev_list_entry_foreach(entry, udev_list_get_entry(seclabel_list)) { - const char *name, *label; - int r; - - name = udev_list_entry_get_name(entry); - label = udev_list_entry_get_value(entry); - - if (streq(name, "selinux")) { - selinux = true; - - r = mac_selinux_apply(devnode, label); - if (r < 0) - log_error_errno(r, "SECLABEL: failed to set SELinux label '%s': %m", label); - else - log_debug("SECLABEL: set SELinux label '%s'", label); - - } else if (streq(name, "smack")) { - smack = true; - - r = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label); - if (r < 0) - log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label); - else - log_debug("SECLABEL: set SMACK label '%s'", label); - - } else - log_error("SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label); - } - - /* set the defaults */ - if (!selinux) - mac_selinux_fix(devnode, true, false); - if (!smack) - mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL); - } - - /* always update timestamp when we re-use the node, like on media change events */ - utimensat(AT_FDCWD, devnode, NULL, 0); -out: - return err; -} - -void udev_node_add(struct udev_device *dev, bool apply, - mode_t mode, uid_t uid, gid_t gid, - struct udev_list *seclabel_list) { - char filename[sizeof("/dev/block/:") + 2*DECIMAL_STR_MAX(unsigned)]; - struct udev_list_entry *list_entry; - - log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT, - udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); - - if (node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list) < 0) - return; - - /* always add /dev/{block,char}/$major:$minor */ - xsprintf(filename, "/dev/%s/%u:%u", - streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", - major(udev_device_get_devnum(dev)), - minor(udev_device_get_devnum(dev))); - node_symlink(dev, udev_device_get_devnode(dev), filename); - - /* create/update symlinks, add symlinks to name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) - link_update(dev, udev_list_entry_get_name(list_entry), true); -} - -void udev_node_remove(struct udev_device *dev) { - struct udev_list_entry *list_entry; - char filename[sizeof("/dev/block/:") + 2*DECIMAL_STR_MAX(unsigned)]; - - /* remove/update symlinks, remove symlinks from name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) - link_update(dev, udev_list_entry_get_name(list_entry), false); - - /* remove /dev/{block,char}/$major:$minor */ - xsprintf(filename, "/dev/%s/%u:%u", - streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", - major(udev_device_get_devnum(dev)), - minor(udev_device_get_devnum(dev))); - unlink(filename); -} diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c deleted file mode 100644 index f6c416bf70..0000000000 --- a/src/udev/udev-rules.c +++ /dev/null @@ -1,2582 +0,0 @@ -/* - * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <fnmatch.h> -#include <limits.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "conf-files.h" -#include "escape.h" -#include "fd-util.h" -#include "fs-util.h" -#include "glob-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "strbuf.h" -#include "string-util.h" -#include "strv.h" -#include "sysctl-util.h" -#include "udev.h" -#include "user-util.h" -#include "util.h" - -#define PREALLOC_TOKEN 2048 - -struct uid_gid { - unsigned int name_off; - union { - uid_t uid; - gid_t gid; - }; -}; - -static const char* const rules_dirs[] = { - "/etc/udev/rules.d", - "/run/udev/rules.d", - UDEVLIBEXECDIR "/rules.d", - NULL -}; - -struct udev_rules { - struct udev *udev; - usec_t dirs_ts_usec; - int resolve_names; - - /* every key in the rules file becomes a token */ - struct token *tokens; - unsigned int token_cur; - unsigned int token_max; - - /* all key strings are copied and de-duplicated in a single continuous string buffer */ - struct strbuf *strbuf; - - /* during rule parsing, uid/gid lookup results are cached */ - struct uid_gid *uids; - unsigned int uids_cur; - unsigned int uids_max; - struct uid_gid *gids; - unsigned int gids_cur; - unsigned int gids_max; -}; - -static char *rules_str(struct udev_rules *rules, unsigned int off) { - return rules->strbuf->buf + off; -} - -static unsigned int rules_add_string(struct udev_rules *rules, const char *s) { - return strbuf_add_string(rules->strbuf, s, strlen(s)); -} - -/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */ -enum operation_type { - OP_UNSET, - - OP_MATCH, - OP_NOMATCH, - OP_MATCH_MAX, - - OP_ADD, - OP_REMOVE, - OP_ASSIGN, - OP_ASSIGN_FINAL, -}; - -enum string_glob_type { - GL_UNSET, - GL_PLAIN, /* no special chars */ - GL_GLOB, /* shell globs ?,*,[] */ - GL_SPLIT, /* multi-value A|B */ - GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ - GL_SOMETHING, /* commonly used "?*" */ -}; - -enum string_subst_type { - SB_UNSET, - SB_NONE, - SB_FORMAT, - SB_SUBSYS, -}; - -/* tokens of a rule are sorted/handled in this order */ -enum token_type { - TK_UNSET, - TK_RULE, - - TK_M_ACTION, /* val */ - TK_M_DEVPATH, /* val */ - TK_M_KERNEL, /* val */ - TK_M_DEVLINK, /* val */ - TK_M_NAME, /* val */ - TK_M_ENV, /* val, attr */ - TK_M_TAG, /* val */ - TK_M_SUBSYSTEM, /* val */ - 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 */ - TK_M_SUBSYSTEMS, /* val */ - TK_M_DRIVERS, /* val */ - TK_M_ATTRS, /* val, attr */ - TK_M_TAGS, /* val */ - TK_M_PARENTS_MAX, - - TK_M_TEST, /* val, mode_t */ - TK_M_PROGRAM, /* val */ - TK_M_IMPORT_FILE, /* val */ - TK_M_IMPORT_PROG, /* val */ - TK_M_IMPORT_BUILTIN, /* val */ - TK_M_IMPORT_DB, /* val */ - TK_M_IMPORT_CMDLINE, /* val */ - TK_M_IMPORT_PARENT, /* val */ - TK_M_RESULT, /* val */ - TK_M_MAX, - - TK_A_STRING_ESCAPE_NONE, - TK_A_STRING_ESCAPE_REPLACE, - TK_A_DB_PERSIST, - TK_A_INOTIFY_WATCH, /* int */ - TK_A_DEVLINK_PRIO, /* int */ - TK_A_OWNER, /* val */ - TK_A_GROUP, /* val */ - TK_A_MODE, /* val */ - TK_A_OWNER_ID, /* uid_t */ - TK_A_GROUP_ID, /* gid_t */ - TK_A_MODE_ID, /* mode_t */ - TK_A_TAG, /* val */ - TK_A_STATIC_NODE, /* val */ - TK_A_SECLABEL, /* val, attr */ - TK_A_ENV, /* val, attr */ - 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 */ - - TK_END, -}; - -/* we try to pack stuff in a way that we take only 12 bytes per token */ -struct token { - union { - unsigned char type; /* same in rule and key */ - struct { - enum token_type type:8; - bool can_set_name:1; - bool has_static_node:1; - unsigned int unused:6; - unsigned short token_count; - unsigned int label_off; - unsigned short filename_off; - unsigned short filename_line; - } rule; - struct { - enum token_type type:8; - enum operation_type op:8; - enum string_glob_type glob:8; - enum string_subst_type subst:4; - enum string_subst_type attrsubst:4; - unsigned int value_off; - union { - unsigned int attr_off; - unsigned int rule_goto; - mode_t mode; - uid_t uid; - gid_t gid; - int devlink_prio; - int watch; - enum udev_builtin_cmd builtin_cmd; - }; - } key; - }; -}; - -#define MAX_TK 64 -struct rule_tmp { - struct udev_rules *rules; - struct token rule; - struct token token[MAX_TK]; - unsigned int token_cur; -}; - -#ifdef DEBUG -static const char *operation_str(enum operation_type type) { - static const char *operation_strs[] = { - [OP_UNSET] = "UNSET", - [OP_MATCH] = "match", - [OP_NOMATCH] = "nomatch", - [OP_MATCH_MAX] = "MATCH_MAX", - - [OP_ADD] = "add", - [OP_REMOVE] = "remove", - [OP_ASSIGN] = "assign", - [OP_ASSIGN_FINAL] = "assign-final", -} ; - - return operation_strs[type]; -} - -static const char *string_glob_str(enum string_glob_type type) { - static const char *string_glob_strs[] = { - [GL_UNSET] = "UNSET", - [GL_PLAIN] = "plain", - [GL_GLOB] = "glob", - [GL_SPLIT] = "split", - [GL_SPLIT_GLOB] = "split-glob", - [GL_SOMETHING] = "split-glob", - }; - - return string_glob_strs[type]; -} - -static const char *token_str(enum token_type type) { - static const char *token_strs[] = { - [TK_UNSET] = "UNSET", - [TK_RULE] = "RULE", - - [TK_M_ACTION] = "M ACTION", - [TK_M_DEVPATH] = "M DEVPATH", - [TK_M_KERNEL] = "M KERNEL", - [TK_M_DEVLINK] = "M DEVLINK", - [TK_M_NAME] = "M NAME", - [TK_M_ENV] = "M ENV", - [TK_M_TAG] = "M TAG", - [TK_M_SUBSYSTEM] = "M SUBSYSTEM", - [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", - [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", - [TK_M_DRIVERS] = "M DRIVERS", - [TK_M_ATTRS] = "M ATTRS", - [TK_M_TAGS] = "M TAGS", - [TK_M_PARENTS_MAX] = "M PARENTS_MAX", - - [TK_M_TEST] = "M TEST", - [TK_M_PROGRAM] = "M PROGRAM", - [TK_M_IMPORT_FILE] = "M IMPORT_FILE", - [TK_M_IMPORT_PROG] = "M IMPORT_PROG", - [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", - [TK_M_IMPORT_DB] = "M IMPORT_DB", - [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", - [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", - [TK_M_RESULT] = "M RESULT", - [TK_M_MAX] = "M MAX", - - [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", - [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", - [TK_A_DB_PERSIST] = "A DB_PERSIST", - [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", - [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", - [TK_A_OWNER] = "A OWNER", - [TK_A_GROUP] = "A GROUP", - [TK_A_MODE] = "A MODE", - [TK_A_OWNER_ID] = "A OWNER_ID", - [TK_A_GROUP_ID] = "A GROUP_ID", - [TK_A_STATIC_NODE] = "A STATIC_NODE", - [TK_A_SECLABEL] = "A SECLABEL", - [TK_A_MODE_ID] = "A MODE_ID", - [TK_A_ENV] = "A ENV", - [TK_A_TAG] = "A ENV", - [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", - - [TK_END] = "END", - }; - - return token_strs[type]; -} - -static void dump_token(struct udev_rules *rules, struct token *token) { - enum token_type type = token->type; - enum operation_type op = token->key.op; - enum string_glob_type glob = token->key.glob; - const char *value = rules_str(rules, token->key.value_off); - const char *attr = &rules->strbuf->buf[token->key.attr_off]; - - switch (type) { - case TK_RULE: - { - const char *tks_ptr = (char *)rules->tokens; - const char *tk_ptr = (char *)token; - unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); - - log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'", - &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line, - idx, token->rule.token_count, - &rules->strbuf->buf[token->rule.label_off]); - break; - } - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_NAME: - case TK_A_DEVLINK: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_RUN_BUILTIN: - case TK_A_RUN_PROGRAM: - log_debug("%s %s '%s'(%s)", - token_str(type), operation_str(op), value, string_glob_str(glob)); - break; - case TK_M_IMPORT_BUILTIN: - 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)); - break; - case TK_M_TAG: - case TK_A_TAG: - log_debug("%s %s '%s'", token_str(type), operation_str(op), value); - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - log_debug("%s", token_str(type)); - break; - case TK_M_TEST: - log_debug("%s %s '%s'(%s) %#o", - token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); - break; - case TK_A_INOTIFY_WATCH: - log_debug("%s %u", token_str(type), token->key.watch); - break; - case TK_A_DEVLINK_PRIO: - log_debug("%s %u", token_str(type), token->key.devlink_prio); - break; - case TK_A_OWNER_ID: - log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid); - break; - case TK_A_GROUP_ID: - log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid); - break; - case TK_A_MODE_ID: - log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode); - break; - case TK_A_STATIC_NODE: - log_debug("%s '%s'", token_str(type), value); - break; - case TK_A_SECLABEL: - log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value); - break; - case TK_A_GOTO: - log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto); - break; - case TK_END: - log_debug("* %s", token_str(type)); - break; - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - log_debug("unknown type %u", type); - break; - } -} - -static void dump_rules(struct udev_rules *rules) { - unsigned int i; - - log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings", - rules->token_cur, - rules->token_cur * sizeof(struct token), - rules->strbuf->nodes_count, - rules->strbuf->len); - for (i = 0; i < rules->token_cur; i++) - dump_token(rules, &rules->tokens[i]); -} -#else -static inline void dump_token(struct udev_rules *rules, struct token *token) {} -static inline void dump_rules(struct udev_rules *rules) {} -#endif /* DEBUG */ - -static int add_token(struct udev_rules *rules, struct token *token) { - /* grow buffer if needed */ - if (rules->token_cur+1 >= rules->token_max) { - struct token *tokens; - unsigned int add; - - /* double the buffer size */ - add = rules->token_max; - if (add < 8) - add = 8; - - tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); - if (tokens == NULL) - return -1; - rules->tokens = tokens; - rules->token_max += add; - } - memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); - rules->token_cur++; - return 0; -} - -static uid_t add_uid(struct udev_rules *rules, const char *owner) { - unsigned int i; - uid_t uid = 0; - unsigned int off; - int r; - - /* lookup, if we know it already */ - for (i = 0; i < rules->uids_cur; i++) { - off = rules->uids[i].name_off; - if (streq(rules_str(rules, off), owner)) { - uid = rules->uids[i].uid; - return uid; - } - } - r = get_user_creds(&owner, &uid, NULL, NULL, NULL); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified user '%s' unknown", owner); - else - log_error_errno(r, "error resolving user '%s': %m", owner); - } - - /* grow buffer if needed */ - if (rules->uids_cur+1 >= rules->uids_max) { - struct uid_gid *uids; - unsigned int add; - - /* double the buffer size */ - add = rules->uids_max; - if (add < 1) - add = 8; - - uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); - if (uids == NULL) - return uid; - rules->uids = uids; - rules->uids_max += add; - } - rules->uids[rules->uids_cur].uid = uid; - off = rules_add_string(rules, owner); - if (off <= 0) - return uid; - rules->uids[rules->uids_cur].name_off = off; - rules->uids_cur++; - return uid; -} - -static gid_t add_gid(struct udev_rules *rules, const char *group) { - unsigned int i; - gid_t gid = 0; - unsigned int off; - int r; - - /* lookup, if we know it already */ - for (i = 0; i < rules->gids_cur; i++) { - off = rules->gids[i].name_off; - if (streq(rules_str(rules, off), group)) { - gid = rules->gids[i].gid; - return gid; - } - } - r = get_group_creds(&group, &gid); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified group '%s' unknown", group); - else - log_error_errno(r, "error resolving group '%s': %m", group); - } - - /* grow buffer if needed */ - if (rules->gids_cur+1 >= rules->gids_max) { - struct uid_gid *gids; - unsigned int add; - - /* double the buffer size */ - add = rules->gids_max; - if (add < 1) - add = 8; - - gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); - if (gids == NULL) - return gid; - rules->gids = gids; - rules->gids_max += add; - } - rules->gids[rules->gids_cur].gid = gid; - off = rules_add_string(rules, group); - if (off <= 0) - return gid; - rules->gids[rules->gids_cur].name_off = off; - rules->gids_cur++; - return gid; -} - -static int import_property_from_string(struct udev_device *dev, char *line) { - char *key; - char *val; - size_t len; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - return -1; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) - return -1; - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - return -1; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - return -1; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - return -1; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (val[len-1] != val[0]) { - log_debug("inconsistent quoting: '%s', skip", line); - return -1; - } - val[len-1] = '\0'; - val++; - } - - udev_device_add_property(dev, key, val); - - return 0; -} - -static int import_file_into_properties(struct udev_device *dev, const char *filename) { - FILE *f; - char line[UTIL_LINE_SIZE]; - - f = fopen(filename, "re"); - if (f == NULL) - return -1; - while (fgets(line, sizeof(line), f) != NULL) - import_property_from_string(dev, line); - fclose(f); - return 0; -} - -static int import_program_into_properties(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - const char *program) { - char result[UTIL_LINE_SIZE]; - char *line; - int err; - - err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)); - if (err < 0) - return err; - - line = result; - while (line != NULL) { - char *pos; - - pos = strchr(line, '\n'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - import_property_from_string(event->dev, line); - line = pos; - } - return 0; -} - -static int import_parent_into_properties(struct udev_device *dev, const char *filter) { - 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; - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { - const char *key = udev_list_entry_get_name(list_entry); - const char *val = udev_list_entry_get_value(list_entry); - - if (fnmatch(filter, key, 0) == 0) - udev_device_add_property(dev, key, val); - } - return 0; -} - -static void attr_subst_subdir(char *attr, size_t len) { - const char *pos, *tail, *path; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - - pos = strstr(attr, "/*/"); - if (!pos) - return; - - tail = pos + 2; - path = strndupa(attr, pos - attr + 1); /* include slash at end */ - dir = opendir(path); - if (dir == NULL) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) - if (dent->d_name[0] != '.') { - char n[strlen(dent->d_name) + strlen(tail) + 1]; - - strscpyl(n, sizeof n, dent->d_name, tail, NULL); - if (faccessat(dirfd(dir), n, F_OK, 0) == 0) { - strscpyl(attr, len, path, n, NULL); - break; - } - } -} - -static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) { - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL || linepos[0] == '\0') - return -1; - - /* skip whitespace */ - while (isspace(linepos[0]) || linepos[0] == ',') - linepos++; - - /* get the key */ - if (linepos[0] == '\0') - return -1; - *key = linepos; - - for (;;) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - if ((linepos[0] == '+') || (linepos[0] == '-') || (linepos[0] == '!') || (linepos[0] == ':')) - if (linepos[1] == '=') - break; - } - - /* remember end of key */ - temp = linepos; - - /* skip whitespace after key */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get operation type */ - if (linepos[0] == '=' && linepos[1] == '=') { - *op = OP_MATCH; - linepos += 2; - } else if (linepos[0] == '!' && linepos[1] == '=') { - *op = OP_NOMATCH; - linepos += 2; - } else if (linepos[0] == '+' && linepos[1] == '=') { - *op = OP_ADD; - linepos += 2; - } else if (linepos[0] == '-' && linepos[1] == '=') { - *op = OP_REMOVE; - linepos += 2; - } else if (linepos[0] == '=') { - *op = OP_ASSIGN; - linepos++; - } else if (linepos[0] == ':' && linepos[1] == '=') { - *op = OP_ASSIGN_FINAL; - linepos += 2; - } else - return -1; - - /* terminate key */ - temp[0] = '\0'; - - /* skip whitespace after operator */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get the value */ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - /* terminate */ - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - temp++; - - /* move line to next key */ - *line = temp; - return 0; -} - -/* extract possible KEY{attr} */ -static const char *get_key_attribute(struct udev *udev, char *str) { - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - log_error("missing closing brace for format"); - return NULL; - } - pos[0] = '\0'; - return attr; - } - return NULL; -} - -static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, - enum operation_type op, - const char *value, const void *data) { - struct token *token = rule_tmp->token + rule_tmp->token_cur; - const char *attr = NULL; - - assert(rule_tmp->token_cur < ELEMENTSOF(rule_tmp->token)); - memzero(token, sizeof(struct token)); - - switch (type) { - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_DEVLINK: - case TK_A_NAME: - case TK_A_GOTO: - case TK_M_TAG: - case TK_A_TAG: - case TK_A_STATIC_NODE: - token->key.value_off = rules_add_string(rule_tmp->rules, value); - break; - case TK_M_IMPORT_BUILTIN: - token->key.value_off = rules_add_string(rule_tmp->rules, value); - token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; - 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; - token->key.value_off = rules_add_string(rule_tmp->rules, value); - token->key.attr_off = rules_add_string(rule_tmp->rules, attr); - break; - case TK_M_TEST: - token->key.value_off = rules_add_string(rule_tmp->rules, value); - if (data != NULL) - token->key.mode = *(mode_t *)data; - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - break; - case TK_A_RUN_BUILTIN: - case TK_A_RUN_PROGRAM: - token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; - token->key.value_off = rules_add_string(rule_tmp->rules, value); - break; - case TK_A_INOTIFY_WATCH: - case TK_A_DEVLINK_PRIO: - token->key.devlink_prio = *(int *)data; - break; - case TK_A_OWNER_ID: - token->key.uid = *(uid_t *)data; - break; - case TK_A_GROUP_ID: - token->key.gid = *(gid_t *)data; - break; - case TK_A_MODE_ID: - token->key.mode = *(mode_t *)data; - break; - case TK_RULE: - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_END: - case TK_UNSET: - assert_not_reached("wrong type"); - } - - if (value != NULL && type < TK_M_MAX) { - /* check if we need to split or call fnmatch() while matching rules */ - enum string_glob_type glob; - int has_split; - int has_glob; - - has_split = (strchr(value, '|') != NULL); - has_glob = string_is_glob(value); - if (has_split && has_glob) { - glob = GL_SPLIT_GLOB; - } else if (has_split) { - glob = GL_SPLIT; - } else if (has_glob) { - if (streq(value, "?*")) - glob = GL_SOMETHING; - else - glob = GL_GLOB; - } else { - glob = GL_PLAIN; - } - token->key.glob = glob; - } - - if (value != NULL && type > TK_M_MAX) { - /* check if assigned value has substitution chars */ - if (value[0] == '[') - token->key.subst = SB_SUBSYS; - else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) - token->key.subst = SB_FORMAT; - else - token->key.subst = SB_NONE; - } - - if (attr != NULL) { - /* check if property/attribute name has substitution chars */ - if (attr[0] == '[') - token->key.attrsubst = SB_SUBSYS; - else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) - token->key.attrsubst = SB_FORMAT; - else - token->key.attrsubst = SB_NONE; - } - - token->key.type = type; - token->key.op = op; - rule_tmp->token_cur++; -} - -static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) { - unsigned int i; - unsigned int start = 0; - unsigned int end = rule_tmp->token_cur; - - for (i = 0; i < rule_tmp->token_cur; i++) { - enum token_type next_val = TK_UNSET; - unsigned int next_idx = 0; - unsigned int j; - - /* find smallest value */ - for (j = start; j < end; j++) { - if (rule_tmp->token[j].type == TK_UNSET) - continue; - if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { - next_val = rule_tmp->token[j].type; - next_idx = j; - } - } - - /* add token and mark done */ - if (add_token(rules, &rule_tmp->token[next_idx]) != 0) - return -1; - rule_tmp->token[next_idx].type = TK_UNSET; - - /* shrink range */ - if (next_idx == start) - start++; - if (next_idx+1 == end) - end--; - } - return 0; -} - -#define LOG_RULE_ERROR(fmt, ...) log_error("Invalid rule %s:%u: " fmt, filename, lineno, ##__VA_ARGS__) -#define LOG_RULE_WARNING(fmt, ...) log_warning("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) -#define LOG_RULE_DEBUG(fmt, ...) log_debug("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) -#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; } - -static void add_rule(struct udev_rules *rules, char *line, - const char *filename, unsigned int filename_off, unsigned int lineno) { - char *linepos; - const char *attr; - struct rule_tmp rule_tmp = { - .rules = rules, - .rule.type = TK_RULE, - }; - - /* the offset in the rule is limited to unsigned short */ - if (filename_off < USHRT_MAX) - rule_tmp.rule.rule.filename_off = filename_off; - rule_tmp.rule.rule.filename_line = lineno; - - linepos = line; - for (;;) { - char *key; - char *value; - enum operation_type op; - - if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) { - /* Avoid erroring on trailing whitespace. This is probably rare - * so save the work for the error case instead of always trying - * to strip the trailing whitespace with strstrip(). */ - while (isblank(*linepos)) - linepos++; - - /* If we aren't at the end of the line, this is a parsing error. - * Make a best effort to describe where the problem is. */ - if (!strchr(NEWLINE, *linepos)) { - char buf[2] = {*linepos}; - _cleanup_free_ char *tmp; - - tmp = cescape(buf); - log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')", - filename, lineno, linepos - line + 1, tmp); - if (*linepos == '#') - log_error("hint: comments can only start at beginning of line"); - } - break; - } - - if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token)) - LOG_AND_RETURN("temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur); - - if (streq(key, "ACTION")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); - - } else if (streq(key, "DEVPATH")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); - - } else if (streq(key, "KERNEL")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); - - } else if (streq(key, "SUBSYSTEM")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - /* bus, class, subsystem events should all be the same */ - if (STR_IN_SET(value, "subsystem", "bus", "class")) { - if (!streq(value, "subsystem")) - LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value); - - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); - } else - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); - - } else if (streq(key, "DRIVER")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); - - } else if (startswith(key, "ATTR{")) { - attr = get_key_attribute(rules->udev, key + strlen("ATTR")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ATTR"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "ATTR"); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); - else - rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); - - } else if (startswith(key, "SYSCTL{")) { - attr = get_key_attribute(rules->udev, key + strlen("SYSCTL")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ATTR"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "ATTR"); - - 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); - - } else if (startswith(key, "SECLABEL{")) { - attr = get_key_attribute(rules->udev, key + strlen("SECLABEL")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "SECLABEL"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "SECLABEL"); - - rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr); - - } else if (streq(key, "KERNELS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); - - } else if (streq(key, "SUBSYSTEMS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); - - } else if (streq(key, "DRIVERS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); - - } else if (startswith(key, "ATTRS{")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", "ATTRS"); - - attr = get_key_attribute(rules->udev, key + strlen("ATTRS")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ATTRS"); - - if (startswith(attr, "device/")) - LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix"); - if (strstr(attr, "../") != NULL) - LOG_RULE_WARNING("direct reference to parent sysfs directory, may break in future kernels; please fix"); - rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); - - } else if (streq(key, "TAGS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); - - } else if (startswith(key, "ENV{")) { - attr = get_key_attribute(rules->udev, key + strlen("ENV")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ENV"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "ENV"); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr); - else { - if (STR_IN_SET(attr, - "ACTION", - "SUBSYSTEM", - "DEVTYPE", - "MAJOR", - "MINOR", - "DRIVER", - "IFINDEX", - "DEVNAME", - "DEVLINKS", - "DEVPATH", - "TAGS")) - LOG_AND_RETURN("invalid ENV attribute, '%s' cannot be set", attr); - - rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr); - } - - } else if (streq(key, "TAG")) { - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); - else - rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); - - } else if (streq(key, "PROGRAM")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); - - } else if (streq(key, "RESULT")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); - - } else if (startswith(key, "IMPORT")) { - attr = get_key_attribute(rules->udev, key + strlen("IMPORT")); - if (attr == NULL) { - LOG_RULE_WARNING("ignoring IMPORT{} with missing type"); - continue; - } - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "IMPORT"); - - if (streq(attr, "program")) { - /* find known built-in command */ - if (value[0] != '/') { - const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - if (cmd < UDEV_BUILTIN_MAX) { - LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value); - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - continue; - } - } - rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); - } else if (streq(attr, "builtin")) { - const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - if (cmd >= UDEV_BUILTIN_MAX) - LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value); - else - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - } else if (streq(attr, "file")) - rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); - else if (streq(attr, "db")) - rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); - else if (streq(attr, "cmdline")) - rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); - else if (streq(attr, "parent")) - rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); - else - LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "IMPORT", attr); - - } else if (startswith(key, "TEST")) { - mode_t mode = 0; - - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", "TEST"); - - attr = get_key_attribute(rules->udev, key + strlen("TEST")); - if (attr != NULL) { - mode = strtol(attr, NULL, 8); - rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); - } else - rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); - - } else if (startswith(key, "RUN")) { - attr = get_key_attribute(rules->udev, key + strlen("RUN")); - if (attr == NULL) - attr = "program"; - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "RUN"); - - if (streq(attr, "builtin")) { - const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - if (cmd < UDEV_BUILTIN_MAX) - rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd); - else - LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value); - } else if (streq(attr, "program")) { - const enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX; - - rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd); - } else - LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr); - - } else if (streq(key, "LABEL")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - rule_tmp.rule.rule.label_off = rules_add_string(rules, value); - - } else if (streq(key, "GOTO")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); - - } else if (startswith(key, "NAME")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); - else { - if (streq(value, "%k")) { - LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove"); - continue; - } - if (isempty(value)) { - LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove"); - continue; - } - rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "SYMLINK")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); - else - rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL); - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "OWNER")) { - uid_t uid; - char *endptr; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - uid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - else if (rules->resolve_names > 0 && strchr("$%", value[0]) == NULL) { - uid = add_uid(rules, value); - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if (rules->resolve_names >= 0) - rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); - - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "GROUP")) { - gid_t gid; - char *endptr; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - gid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { - gid = add_gid(rules, value); - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if (rules->resolve_names >= 0) - rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); - - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "MODE")) { - mode_t mode; - char *endptr; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - mode = strtol(value, &endptr, 8); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); - else - rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "OPTIONS")) { - const char *pos; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - pos = strstr(value, "link_priority="); - if (pos != NULL) { - int prio = atoi(pos + strlen("link_priority=")); - - rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); - } - - pos = strstr(value, "string_escape="); - if (pos != NULL) { - pos += strlen("string_escape="); - if (startswith(pos, "none")) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); - else if (startswith(pos, "replace")) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); - } - - pos = strstr(value, "db_persist"); - if (pos != NULL) - rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); - - pos = strstr(value, "nowatch"); - if (pos != NULL) { - const int off = 0; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); - } else { - pos = strstr(value, "watch"); - if (pos != NULL) { - const int on = 1; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); - } - } - - pos = strstr(value, "static_node="); - if (pos != NULL) { - pos += strlen("static_node="); - rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL); - rule_tmp.rule.rule.has_static_node = true; - } - - } else - LOG_AND_RETURN("unknown key '%s'", key); - } - - /* add rule token and sort tokens */ - rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; - if (add_token(rules, &rule_tmp.rule) != 0 || sort_token(rules, &rule_tmp) != 0) - LOG_RULE_ERROR("failed to add rule token"); -} - -static int parse_file(struct udev_rules *rules, const char *filename) { - _cleanup_fclose_ FILE *f = NULL; - unsigned int first_token; - unsigned int filename_off; - char line[UTIL_LINE_SIZE]; - int line_nr = 0; - unsigned int i; - - f = fopen(filename, "re"); - if (!f) { - if (errno == ENOENT) - return 0; - else - return -errno; - } - - if (null_or_empty_fd(fileno(f))) { - log_debug("Skipping empty file: %s", filename); - return 0; - } else - log_debug("Reading rules file: %s", filename); - - first_token = rules->token_cur; - filename_off = rules_add_string(rules, filename); - - while (fgets(line, sizeof(line), f) != NULL) { - char *key; - size_t len; - - /* skip whitespace */ - line_nr++; - key = line; - while (isspace(key[0])) - key++; - - /* comment */ - if (key[0] == '#') - continue; - - len = strlen(line); - if (len < 3) - continue; - - /* continue reading if backslash+newline is found */ - while (line[len-2] == '\\') { - if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) - break; - if (strlen(&line[len-2]) < 2) - break; - line_nr++; - len = strlen(line); - } - - if (len+1 >= sizeof(line)) { - log_error("line too long '%s':%u, ignored", filename, line_nr); - continue; - } - add_rule(rules, key, filename, filename_off, line_nr); - } - - /* link GOTOs to LABEL rules in this file to be able to fast-forward */ - for (i = first_token+1; i < rules->token_cur; i++) { - if (rules->tokens[i].type == TK_A_GOTO) { - char *label = rules_str(rules, rules->tokens[i].key.value_off); - unsigned int j; - - for (j = i+1; j < rules->token_cur; j++) { - if (rules->tokens[j].type != TK_RULE) - continue; - if (rules->tokens[j].rule.label_off == 0) - continue; - if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off))) - continue; - rules->tokens[i].key.rule_goto = j; - break; - } - if (rules->tokens[i].key.rule_goto == 0) - log_error("GOTO '%s' has no matching label in: '%s'", label, filename); - } - } - return 0; -} - -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { - struct udev_rules *rules; - struct udev_list file_list; - struct token end_token; - char **files, **f; - int r; - - rules = new0(struct udev_rules, 1); - if (rules == NULL) - return NULL; - rules->udev = udev; - rules->resolve_names = resolve_names; - udev_list_init(udev, &file_list, true); - - /* init token array and string buffer */ - rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); - if (rules->tokens == NULL) - return udev_rules_unref(rules); - rules->token_max = PREALLOC_TOKEN; - - rules->strbuf = strbuf_new(); - if (!rules->strbuf) - return udev_rules_unref(rules); - - udev_rules_check_timestamp(rules); - - r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs); - if (r < 0) { - log_error_errno(r, "failed to enumerate rules files: %m"); - return udev_rules_unref(rules); - } - - /* - * The offset value in the rules strct is limited; add all - * rules file names to the beginning of the string buffer. - */ - STRV_FOREACH(f, files) - rules_add_string(rules, *f); - - STRV_FOREACH(f, files) - parse_file(rules, *f); - - strv_free(files); - - memzero(&end_token, sizeof(struct token)); - end_token.type = TK_END; - add_token(rules, &end_token); - log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings", - rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len); - - /* cleanup temporary strbuf data */ - log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used", - rules->strbuf->in_count, rules->strbuf->in_len, - rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count); - strbuf_complete(rules->strbuf); - - /* cleanup uid/gid cache */ - rules->uids = mfree(rules->uids); - rules->uids_cur = 0; - rules->uids_max = 0; - rules->gids = mfree(rules->gids); - rules->gids_cur = 0; - rules->gids_max = 0; - - dump_rules(rules); - return rules; -} - -struct udev_rules *udev_rules_unref(struct udev_rules *rules) { - if (rules == NULL) - return NULL; - free(rules->tokens); - strbuf_cleanup(rules->strbuf); - free(rules->uids); - free(rules->gids); - return mfree(rules); -} - -bool udev_rules_check_timestamp(struct udev_rules *rules) { - if (!rules) - return false; - - return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true); -} - -static int match_key(struct udev_rules *rules, struct token *token, const char *val) { - char *key_value = rules_str(rules, token->key.value_off); - char *pos; - bool match = false; - - if (val == NULL) - val = ""; - - switch (token->key.glob) { - case GL_PLAIN: - match = (streq(key_value, val)); - break; - case GL_GLOB: - match = (fnmatch(key_value, val, 0) == 0); - break; - case GL_SPLIT: - { - const char *s; - size_t len; - - s = rules_str(rules, token->key.value_off); - len = strlen(val); - for (;;) { - const char *next; - - next = strchr(s, '|'); - if (next != NULL) { - size_t matchlen = (size_t)(next - s); - - match = (matchlen == len && strneq(s, val, matchlen)); - if (match) - break; - } else { - match = (streq(s, val)); - break; - } - s = &next[1]; - } - break; - } - case GL_SPLIT_GLOB: - { - char value[UTIL_PATH_SIZE]; - - strscpy(value, sizeof(value), rules_str(rules, token->key.value_off)); - key_value = value; - while (key_value != NULL) { - pos = strchr(key_value, '|'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - match = (fnmatch(key_value, val, 0) == 0); - if (match) - break; - key_value = pos; - } - break; - } - case GL_SOMETHING: - match = (val[0] != '\0'); - break; - case GL_UNSET: - return -1; - } - - if (match && (token->key.op == OP_MATCH)) - return 0; - if (!match && (token->key.op == OP_NOMATCH)) - return 0; - return -1; -} - -static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) { - const char *name; - char nbuf[UTIL_NAME_SIZE]; - const char *value; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - - name = rules_str(rules, cur->key.attr_off); - switch (cur->key.attrsubst) { - case SB_FORMAT: - udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); - name = nbuf; - /* fall through */ - case SB_NONE: - value = udev_device_get_sysattr_value(dev, name); - if (value == NULL) - return -1; - break; - case SB_SUBSYS: - if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) - return -1; - value = vbuf; - break; - default: - return -1; - } - - /* remove trailing whitespace, if not asked to match for it */ - len = strlen(value); - if (len > 0 && isspace(value[len-1])) { - const char *key_value; - size_t klen; - - key_value = rules_str(rules, cur->key.value_off); - klen = strlen(key_value); - if (klen > 0 && !isspace(key_value[klen-1])) { - if (value != vbuf) { - strscpy(vbuf, sizeof(vbuf), value); - value = vbuf; - } - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - } - } - - return match_key(rules, cur, value); -} - -enum escape_type { - ESCAPE_UNSET, - ESCAPE_NONE, - ESCAPE_REPLACE, -}; - -void 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) { - struct token *cur; - struct token *rule; - enum escape_type esc = ESCAPE_UNSET; - bool can_set_name; - - if (rules->tokens == NULL) - return; - - can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) && - (major(udev_device_get_devnum(event->dev)) > 0 || - udev_device_get_ifindex(event->dev) > 0)); - - /* loop through token list, match, run actions or forward to next rule */ - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - dump_token(rules, cur); - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ - if (!can_set_name && rule->rule.can_set_name) - goto nomatch; - esc = ESCAPE_UNSET; - break; - case TK_M_ACTION: - if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVPATH: - if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) - goto nomatch; - break; - case TK_M_KERNEL: - if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVLINK: { - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { - const char *devlink; - - devlink = udev_list_entry_get_name(list_entry) + strlen("/dev/"); - if (match_key(rules, cur, devlink) == 0) { - match = true; - break; - } - } - if (!match) - goto nomatch; - break; - } - case TK_M_NAME: - if (match_key(rules, cur, event->name) != 0) - goto nomatch; - break; - case TK_M_ENV: { - const char *key_name = rules_str(rules, cur->key.attr_off); - const char *value; - - value = udev_device_get_property_value(event->dev, key_name); - - /* check global properties */ - if (!value && properties_list) { - struct udev_list_entry *list_entry; - - list_entry = udev_list_get_entry(properties_list); - list_entry = udev_list_entry_get_by_name(list_entry, key_name); - if (list_entry != NULL) - value = udev_list_entry_get_value(list_entry); - } - - if (!value) - value = ""; - if (match_key(rules, cur, value)) - goto nomatch; - break; - } - case TK_M_TAG: { - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { - if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) { - match = true; - break; - } - } - if ((!match && (cur->key.op != OP_NOMATCH)) || - (match && (cur->key.op == OP_NOMATCH))) - goto nomatch; - break; - } - case TK_M_SUBSYSTEM: - if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DRIVER: - if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) - goto nomatch; - break; - case TK_M_ATTR: - 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: - case TK_M_ATTRS: - case TK_M_TAGS: { - struct token *next; - - /* get whole sequence of parent matches */ - next = cur; - while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) - next++; - - /* loop over parents */ - event->dev_parent = event->dev; - for (;;) { - struct token *key; - - /* loop over sequence of parent match keys */ - for (key = cur; key < next; key++ ) { - dump_token(rules, key); - switch(key->type) { - case TK_M_KERNELS: - if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_SUBSYSTEMS: - if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_DRIVERS: - if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_ATTRS: - if (match_attr(rules, event->dev_parent, event, key) != 0) - goto try_parent; - break; - case TK_M_TAGS: { - bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off)); - - if (match && key->key.op == OP_NOMATCH) - goto try_parent; - if (!match && key->key.op == OP_MATCH) - goto try_parent; - break; - } - default: - goto nomatch; - } - } - break; - - try_parent: - event->dev_parent = udev_device_get_parent(event->dev_parent); - if (event->dev_parent == NULL) - goto nomatch; - } - /* move behind our sequence of parent match keys */ - cur = next; - continue; - } - case TK_M_TEST: { - char filename[UTIL_PATH_SIZE]; - struct stat statbuf; - int match; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename)); - if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { - if (filename[0] != '/') { - char tmp[UTIL_PATH_SIZE]; - - strscpy(tmp, sizeof(tmp), filename); - strscpyl(filename, sizeof(filename), - udev_device_get_syspath(event->dev), "/", tmp, NULL); - } - } - attr_subst_subdir(filename, sizeof(filename)); - - match = (stat(filename, &statbuf) == 0); - if (match && cur->key.mode > 0) - match = ((statbuf.st_mode & cur->key.mode) > 0); - if (match && cur->key.op == OP_NOMATCH) - goto nomatch; - if (!match && cur->key.op == OP_MATCH) - goto nomatch; - break; - } - case TK_M_PROGRAM: { - char program[UTIL_PATH_SIZE]; - char result[UTIL_LINE_SIZE]; - - event->program_result = mfree(event->program_result); - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program)); - log_debug("PROGRAM '%s' %s:%u", - program, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - - if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)) < 0) { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } else { - int count; - - util_remove_trailing_chars(result, '\n'); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - log_debug("%i character(s) replaced" , count); - } - event->program_result = strdup(result); - if (cur->key.op == OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_FILE: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); - if (import_file_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PROG: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); - log_debug("IMPORT '%s' %s:%u", - import, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - - if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_BUILTIN: { - char command[UTIL_PATH_SIZE]; - - if (udev_builtin_run_once(cur->key.builtin_cmd)) { - /* check if we ran already */ - if (event->builtin_run & (1 << cur->key.builtin_cmd)) { - log_debug("IMPORT builtin skip '%s' %s:%u", - udev_builtin_name(cur->key.builtin_cmd), - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - /* return the result from earlier run */ - if (event->builtin_ret & (1 << cur->key.builtin_cmd)) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - /* mark as ran */ - event->builtin_run |= (1 << cur->key.builtin_cmd); - } - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command)); - log_debug("IMPORT builtin '%s' %s:%u", - udev_builtin_name(cur->key.builtin_cmd), - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - - if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { - /* remember failure */ - log_debug("IMPORT builtin '%s' returned non-zero", - udev_builtin_name(cur->key.builtin_cmd)); - event->builtin_ret |= (1 << cur->key.builtin_cmd); - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_DB: { - const char *key = rules_str(rules, cur->key.value_off); - const char *value; - - value = udev_device_get_property_value(event->dev_db, key); - if (value != NULL) - udev_device_add_property(event->dev, key, value); - else { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_CMDLINE: { - _cleanup_fclose_ FILE *f = NULL; - bool imported = false; - - f = fopen("/proc/cmdline", "re"); - if (f != NULL) { - char cmdline[4096]; - - if (fgets(cmdline, sizeof(cmdline), f) != NULL) { - const char *key = rules_str(rules, cur->key.value_off); - char *pos; - - pos = strstr(cmdline, key); - if (pos != NULL) { - imported = true; - pos += strlen(key); - if (pos[0] == '\0' || isspace(pos[0])) - /* we import simple flags as 'FLAG=1' */ - udev_device_add_property(event->dev, key, "1"); - else if (pos[0] == '=') { - const char *value; - - pos++; - value = pos; - while (pos[0] != '\0' && !isspace(pos[0])) - pos++; - pos[0] = '\0'; - udev_device_add_property(event->dev, key, value); - } - } - } - } - if (!imported && cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PARENT: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); - if (import_parent_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_RESULT: - if (match_key(rules, cur, event->program_result) != 0) - goto nomatch; - break; - case TK_A_STRING_ESCAPE_NONE: - esc = ESCAPE_NONE; - break; - case TK_A_STRING_ESCAPE_REPLACE: - esc = ESCAPE_REPLACE; - break; - case TK_A_DB_PERSIST: - udev_device_set_db_persist(event->dev); - break; - case TK_A_INOTIFY_WATCH: - if (event->inotify_watch_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->inotify_watch_final = true; - event->inotify_watch = cur->key.watch; - break; - case TK_A_DEVLINK_PRIO: - udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); - break; - case TK_A_OWNER: { - char owner[UTIL_NAME_SIZE]; - const char *ow = owner; - int r; - - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner)); - event->owner_set = true; - r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified user '%s' unknown", owner); - else - log_error_errno(r, "error resolving user '%s': %m", owner); - - event->uid = 0; - } - log_debug("OWNER %u %s:%u", - event->uid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_GROUP: { - char group[UTIL_NAME_SIZE]; - const char *gr = group; - int r; - - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group)); - event->group_set = true; - r = get_group_creds(&gr, &event->gid); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified group '%s' unknown", group); - else - log_error_errno(r, "error resolving group '%s': %m", group); - - event->gid = 0; - } - log_debug("GROUP %u %s:%u", - event->gid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_MODE: { - char mode_str[UTIL_NAME_SIZE]; - mode_t mode; - char *endptr; - - if (event->mode_final) - break; - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str)); - mode = strtol(mode_str, &endptr, 8); - if (endptr[0] != '\0') { - log_error("ignoring invalid mode '%s'", mode_str); - break; - } - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = mode; - log_debug("MODE %#o %s:%u", - event->mode, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_OWNER_ID: - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - event->owner_set = true; - event->uid = cur->key.uid; - log_debug("OWNER %u %s:%u", - event->uid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - case TK_A_GROUP_ID: - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - event->group_set = true; - event->gid = cur->key.gid; - log_debug("GROUP %u %s:%u", - event->gid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - case TK_A_MODE_ID: - if (event->mode_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = cur->key.mode; - log_debug("MODE %#o %s:%u", - event->mode, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - case TK_A_SECLABEL: { - char label_str[UTIL_LINE_SIZE] = {}; - const char *name, *label; - - name = rules_str(rules, cur->key.attr_off); - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str)); - if (label_str[0] != '\0') - label = label_str; - else - label = rules_str(rules, cur->key.value_off); - - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_list_cleanup(&event->seclabel_list); - udev_list_entry_add(&event->seclabel_list, name, label); - log_debug("SECLABEL{%s}='%s' %s:%u", - name, label, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_ENV: { - const char *name = rules_str(rules, cur->key.attr_off); - char *value = rules_str(rules, cur->key.value_off); - char value_new[UTIL_NAME_SIZE]; - const char *value_old = NULL; - - if (value[0] == '\0') { - if (cur->key.op == OP_ADD) - break; - udev_device_add_property(event->dev, name, NULL); - break; - } - - if (cur->key.op == OP_ADD) - value_old = udev_device_get_property_value(event->dev, name); - if (value_old) { - char temp[UTIL_NAME_SIZE]; - - /* append value separated by space */ - udev_event_apply_format(event, value, temp, sizeof(temp)); - strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL); - } else - udev_event_apply_format(event, value, value_new, sizeof(value_new)); - - udev_device_add_property(event->dev, name, value_new); - break; - } - case TK_A_TAG: { - char tag[UTIL_PATH_SIZE]; - const char *p; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag)); - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_tags_list(event->dev); - for (p = tag; *p != '\0'; p++) { - if ((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '-' || *p == '_') - continue; - log_error("ignoring invalid tag name '%s'", tag); - break; - } - if (cur->key.op == OP_REMOVE) - udev_device_remove_tag(event->dev, tag); - else - udev_device_add_tag(event->dev, tag); - break; - } - case TK_A_NAME: { - const char *name = rules_str(rules, cur->key.value_off); - - char name_str[UTIL_PATH_SIZE]; - int count; - - if (event->name_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->name_final = true; - udev_event_apply_format(event, name, name_str, sizeof(name_str)); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(name_str, "/"); - if (count > 0) - log_debug("%i character(s) replaced", count); - } - if (major(udev_device_get_devnum(event->dev)) && - !streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/"))) { - log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n", - name, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - if (free_and_strdup(&event->name, name_str) < 0) { - log_oom(); - return; - } - log_debug("NAME '%s' %s:%u", - event->name, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_DEVLINK: { - char temp[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE]; - char *pos, *next; - int count = 0; - - if (event->devlink_final) - break; - if (major(udev_device_get_devnum(event->dev)) == 0) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->devlink_final = true; - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_devlinks_list(event->dev); - - /* allow multiple symlinks separated by spaces */ - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp)); - if (esc == ESCAPE_UNSET) - count = util_replace_chars(temp, "/ "); - else if (esc == ESCAPE_REPLACE) - count = util_replace_chars(temp, "/"); - if (count > 0) - log_debug("%i character(s) replaced" , count); - pos = temp; - while (isspace(pos[0])) - pos++; - next = strchr(pos, ' '); - while (next != NULL) { - next[0] = '\0'; - log_debug("LINK '%s' %s:%u", pos, - rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); - strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); - udev_device_add_devlink(event->dev, filename); - while (isspace(next[1])) - next++; - pos = &next[1]; - next = strchr(pos, ' '); - } - if (pos[0] != '\0') { - log_debug("LINK '%s' %s:%u", pos, - rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); - strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); - udev_device_add_devlink(event->dev, filename); - } - break; - } - case TK_A_ATTR: { - const char *key_name = rules_str(rules, cur->key.attr_off); - char attr[UTIL_PATH_SIZE]; - char value[UTIL_NAME_SIZE]; - _cleanup_fclose_ FILE *f = NULL; - - if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) - strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); - attr_subst_subdir(attr, sizeof(attr)); - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value)); - log_debug("ATTR '%s' writing '%s' %s:%u", attr, value, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - f = fopen(attr, "we"); - if (f == NULL) - log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr); - else if (fprintf(f, "%s", value) <= 0) - log_error_errno(errno, "error writing ATTR{%s}: %m", attr); - 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_errno(r, "error writing SYSCTL{%s}='%s': %m", filename, value); - break; - } - case TK_A_RUN_BUILTIN: - case TK_A_RUN_PROGRAM: { - struct udev_list_entry *entry; - - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_list_cleanup(&event->run_list); - log_debug("RUN '%s' %s:%u", - rules_str(rules, cur->key.value_off), - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL); - udev_list_entry_set_num(entry, cur->key.builtin_cmd); - break; - } - case TK_A_GOTO: - if (cur->key.rule_goto == 0) - break; - cur = &rules->tokens[cur->key.rule_goto]; - continue; - case TK_END: - return; - - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - log_error("wrong type %u", cur->type); - goto nomatch; - } - - cur++; - continue; - nomatch: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - } -} - -int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { - struct token *cur; - struct token *rule; - uid_t uid = 0; - gid_t gid = 0; - mode_t mode = 0; - _cleanup_strv_free_ char **tags = NULL; - char **t; - FILE *f = NULL; - _cleanup_free_ char *path = NULL; - int r; - - if (rules->tokens == NULL) - return 0; - - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - - /* skip rules without a static_node tag */ - if (!rule->rule.has_static_node) - goto next; - - uid = 0; - gid = 0; - mode = 0; - tags = strv_free(tags); - break; - case TK_A_OWNER_ID: - uid = cur->key.uid; - break; - case TK_A_GROUP_ID: - gid = cur->key.gid; - break; - case TK_A_MODE_ID: - mode = cur->key.mode; - break; - case TK_A_TAG: - r = strv_extend(&tags, rules_str(rules, cur->key.value_off)); - if (r < 0) - goto finish; - - break; - case TK_A_STATIC_NODE: { - char device_node[UTIL_PATH_SIZE]; - char tags_dir[UTIL_PATH_SIZE]; - char tag_symlink[UTIL_PATH_SIZE]; - struct stat stats; - - /* we assure, that the permissions tokens are sorted before the static token */ - - if (mode == 0 && uid == 0 && gid == 0 && tags == NULL) - goto next; - - strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL); - if (stat(device_node, &stats) != 0) - break; - if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) - break; - - /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */ - if (tags) { - STRV_FOREACH(t, tags) { - _cleanup_free_ char *unescaped_filename = NULL; - - strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL); - r = mkdir_p(tags_dir, 0755); - if (r < 0) - return log_error_errno(r, "failed to create %s: %m", tags_dir); - - unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/."); - - strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL); - r = symlink(device_node, tag_symlink); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "failed to create symlink %s -> %s: %m", - tag_symlink, device_node); - } - } - - /* don't touch the permissions if only the tags were set */ - if (mode == 0 && uid == 0 && gid == 0) - break; - - if (mode == 0) { - if (gid > 0) - mode = 0660; - else - mode = 0600; - } - if (mode != (stats.st_mode & 01777)) { - r = chmod(device_node, mode); - if (r < 0) { - log_error("failed to chmod '%s' %#o", device_node, mode); - return -errno; - } else - log_debug("chmod '%s' %#o", device_node, mode); - } - - if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { - r = chown(device_node, uid, gid); - if (r < 0) { - log_error("failed to chown '%s' %u %u ", device_node, uid, gid); - return -errno; - } else - log_debug("chown '%s' %u %u", device_node, uid, gid); - } - - utimensat(AT_FDCWD, device_node, NULL, 0); - break; - } - case TK_END: - goto finish; - } - - cur++; - continue; -next: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - continue; - } - -finish: - if (f) { - fflush(f); - fchmod(fileno(f), 0644); - if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) { - unlink_noerrno("/run/udev/static_node-tags"); - unlink_noerrno(path); - return -errno; - } - } - - return 0; -} diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c deleted file mode 100644 index bc9096ed0c..0000000000 --- a/src/udev/udev-watch.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <dirent.h> -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/inotify.h> -#include <unistd.h> - -#include "stdio-util.h" -#include "udev.h" - -static int inotify_fd = -1; - -/* inotify descriptor, will be shared with rules directory; - * set to cloexec since we need our children to be able to add - * watches for us - */ -int udev_watch_init(struct udev *udev) { - inotify_fd = inotify_init1(IN_CLOEXEC); - if (inotify_fd < 0) - log_error_errno(errno, "inotify_init failed: %m"); - return inotify_fd; -} - -/* move any old watches directory out of the way, and then restore - * the watches - */ -void udev_watch_restore(struct udev *udev) { - if (inotify_fd < 0) - return; - - if (rename("/run/udev/watch", "/run/udev/watch.old") == 0) { - DIR *dir; - struct dirent *ent; - - dir = opendir("/run/udev/watch.old"); - if (dir == NULL) { - log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m"); - return; - } - - for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { - char device[UTIL_PATH_SIZE]; - ssize_t len; - struct udev_device *dev; - - if (ent->d_name[0] == '.') - continue; - - len = readlinkat(dirfd(dir), ent->d_name, device, sizeof(device)); - if (len <= 0 || len == (ssize_t)sizeof(device)) - goto unlink; - device[len] = '\0'; - - dev = udev_device_new_from_device_id(udev, device); - if (dev == NULL) - goto unlink; - - log_debug("restoring old watch on '%s'", udev_device_get_devnode(dev)); - udev_watch_begin(udev, dev); - udev_device_unref(dev); -unlink: - unlinkat(dirfd(dir), ent->d_name, 0); - } - - closedir(dir); - rmdir("/run/udev/watch.old"); - - } else if (errno != ENOENT) - log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m"); -} - -void udev_watch_begin(struct udev *udev, struct udev_device *dev) { - char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)]; - int wd; - int r; - - if (inotify_fd < 0) - return; - - log_debug("adding watch on '%s'", udev_device_get_devnode(dev)); - wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - if (wd < 0) { - log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m", - inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - return; - } - - xsprintf(filename, "/run/udev/watch/%d", wd); - mkdir_parents(filename, 0755); - unlink(filename); - r = symlink(udev_device_get_id_filename(dev), filename); - if (r < 0) - log_error_errno(errno, "Failed to create symlink %s: %m", filename); - - udev_device_set_watch_handle(dev, wd); -} - -void udev_watch_end(struct udev *udev, struct udev_device *dev) { - int wd; - char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)]; - - if (inotify_fd < 0) - return; - - wd = udev_device_get_watch_handle(dev); - if (wd < 0) - return; - - log_debug("removing watch on '%s'", udev_device_get_devnode(dev)); - inotify_rm_watch(inotify_fd, wd); - - xsprintf(filename, "/run/udev/watch/%d", wd); - unlink(filename); - - udev_device_set_watch_handle(dev, -1); -} - -struct udev_device *udev_watch_lookup(struct udev *udev, int wd) { - char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)]; - char device[UTIL_NAME_SIZE]; - ssize_t len; - - if (inotify_fd < 0 || wd < 0) - return NULL; - - xsprintf(filename, "/run/udev/watch/%d", wd); - len = readlink(filename, device, sizeof(device)); - if (len <= 0 || (size_t)len == sizeof(device)) - return NULL; - device[len] = '\0'; - - return udev_device_new_from_device_id(udev, device); -} diff --git a/src/udev/udev.conf b/src/udev/udev.conf deleted file mode 100644 index 47d1433002..0000000000 --- a/src/udev/udev.conf +++ /dev/null @@ -1,3 +0,0 @@ -# see udev.conf(5) for details - -#udev_log="info" diff --git a/src/udev/udev.h b/src/udev/udev.h deleted file mode 100644 index 8433e8d9f2..0000000000 --- a/src/udev/udev.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - -/* - * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <sys/param.h> -#include <sys/sysmacros.h> -#include <sys/types.h> - -#include "libudev.h" -#include "sd-netlink.h" - -#include "label.h" -#include "libudev-private.h" -#include "macro.h" -#include "strv.h" -#include "util.h" - -struct udev_event { - struct udev *udev; - struct udev_device *dev; - struct udev_device *dev_parent; - struct udev_device *dev_db; - char *name; - char *program_result; - mode_t mode; - uid_t uid; - gid_t gid; - struct udev_list seclabel_list; - struct udev_list run_list; - int exec_delay; - usec_t birth_usec; - sd_netlink *rtnl; - unsigned int builtin_run; - unsigned int builtin_ret; - bool inotify_watch; - bool inotify_watch_final; - bool group_set; - bool group_final; - bool owner_set; - bool owner_final; - bool mode_set; - bool mode_final; - bool name_final; - bool devlink_final; - bool run_final; -}; - -struct udev_watch { - struct udev_list_node node; - int handle; - char *name; -}; - -/* udev-rules.c */ -struct udev_rules; -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); -struct udev_rules *udev_rules_unref(struct udev_rules *rules); -bool udev_rules_check_timestamp(struct udev_rules *rules); -void 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); -int udev_rules_apply_static_dev_perms(struct udev_rules *rules); - -/* udev-event.c */ -struct udev_event *udev_event_new(struct udev_device *dev); -void udev_event_unref(struct udev_event *event); -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); -int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, - char *result, size_t maxsize, int read_value); -int udev_event_spawn(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - bool accept_failure, - const char *cmd, 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); -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 */ -int udev_watch_init(struct udev *udev); -void udev_watch_restore(struct udev *udev); -void udev_watch_begin(struct udev *udev, struct udev_device *dev); -void udev_watch_end(struct udev *udev, struct udev_device *dev); -struct udev_device *udev_watch_lookup(struct udev *udev, int wd); - -/* udev-node.c */ -void udev_node_add(struct udev_device *dev, bool apply, - mode_t mode, uid_t uid, gid_t gid, - struct udev_list *seclabel_list); -void udev_node_remove(struct udev_device *dev); -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); - -/* udev-ctrl.c */ -struct udev_ctrl; -struct udev_ctrl *udev_ctrl_new(struct udev *udev); -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); -int udev_ctrl_cleanup(struct udev_ctrl *uctrl); -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); -int udev_ctrl_get_fd(struct udev_ctrl *uctrl); -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); -struct udev_ctrl_connection; -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg; -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); - -/* built-in commands */ -enum udev_builtin_cmd { -#ifdef HAVE_BLKID - UDEV_BUILTIN_BLKID, -#endif - UDEV_BUILTIN_BTRFS, - UDEV_BUILTIN_HWDB, - UDEV_BUILTIN_INPUT_ID, - UDEV_BUILTIN_KEYBOARD, -#ifdef HAVE_KMOD - UDEV_BUILTIN_KMOD, -#endif - UDEV_BUILTIN_NET_ID, - UDEV_BUILTIN_NET_LINK, - UDEV_BUILTIN_PATH_ID, - UDEV_BUILTIN_USB_ID, -#ifdef HAVE_ACL - UDEV_BUILTIN_UACCESS, -#endif - UDEV_BUILTIN_MAX -}; -struct udev_builtin { - const char *name; - int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); - const char *help; - int (*init)(struct udev *udev); - void (*exit)(struct udev *udev); - bool (*validate)(struct udev *udev); - bool run_once; -}; -#ifdef HAVE_BLKID -extern const struct udev_builtin udev_builtin_blkid; -#endif -extern const struct udev_builtin udev_builtin_btrfs; -extern const struct udev_builtin udev_builtin_hwdb; -extern const struct udev_builtin udev_builtin_input_id; -extern const struct udev_builtin udev_builtin_keyboard; -#ifdef HAVE_KMOD -extern const struct udev_builtin udev_builtin_kmod; -#endif -extern const struct udev_builtin udev_builtin_net_id; -extern const struct udev_builtin udev_builtin_net_setup_link; -extern const struct udev_builtin udev_builtin_path_id; -extern const struct udev_builtin udev_builtin_usb_id; -extern const struct udev_builtin udev_builtin_uaccess; -void udev_builtin_init(struct udev *udev); -void udev_builtin_exit(struct udev *udev); -enum udev_builtin_cmd udev_builtin_lookup(const char *command); -const char *udev_builtin_name(enum udev_builtin_cmd cmd); -bool udev_builtin_run_once(enum udev_builtin_cmd cmd); -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); -void udev_builtin_list(struct udev *udev); -bool udev_builtin_validate(struct udev *udev); -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); -int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias, - const char *filter, bool test); - -/* udevadm commands */ -struct udevadm_cmd { - const char *name; - int (*cmd)(struct udev *udev, int argc, char *argv[]); - const char *help; - int debug; -}; -extern const struct udevadm_cmd udevadm_info; -extern const struct udevadm_cmd udevadm_trigger; -extern const struct udevadm_cmd udevadm_settle; -extern const struct udevadm_cmd udevadm_control; -extern const struct udevadm_cmd udevadm_monitor; -extern const struct udevadm_cmd udevadm_hwdb; -extern const struct udevadm_cmd udevadm_test; -extern const struct udevadm_cmd udevadm_test_builtin; diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in deleted file mode 100644 index a0c2e82d47..0000000000 --- a/src/udev/udev.pc.in +++ /dev/null @@ -1,5 +0,0 @@ -Name: udev -Description: udev -Version: @VERSION@ - -udevdir=@udevlibexecdir@ diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c deleted file mode 100644 index 6f8e96a123..0000000000 --- a/src/udev/udevadm-control.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2005-2011 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <errno.h> -#include <getopt.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "time-util.h" -#include "udev-util.h" -#include "udev.h" - -static void print_help(void) { - printf("%s control COMMAND\n\n" - "Control the udev daemon.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -e --exit Instruct the daemon to cleanup and exit\n" - " -l --log-priority=LEVEL Set the udev log level for the daemon\n" - " -s --stop-exec-queue Do not execute events, queue only\n" - " -S --start-exec-queue Execute events, flush queue\n" - " -R --reload Reload rules and databases\n" - " -p --property=KEY=VALUE Set a global property for all events\n" - " -m --children-max=N Maximum number of children\n" - " --timeout=SECONDS Maximum time to block for a reply\n" - , program_invocation_short_name); -} - -static int adm_control(struct udev *udev, int argc, char *argv[]) { - _cleanup_udev_ctrl_unref_ struct udev_ctrl *uctrl = NULL; - int timeout = 60; - int rc = 1, c; - - static const struct option options[] = { - { "exit", no_argument, NULL, 'e' }, - { "log-priority", required_argument, NULL, 'l' }, - { "stop-exec-queue", no_argument, NULL, 's' }, - { "start-exec-queue", no_argument, NULL, 'S' }, - { "reload", no_argument, NULL, 'R' }, - { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */ - { "property", required_argument, NULL, 'p' }, - { "env", required_argument, NULL, 'p' }, /* alias for -p */ - { "children-max", required_argument, NULL, 'm' }, - { "timeout", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - if (getuid() != 0) { - log_error("root privileges required"); - return 1; - } - - uctrl = udev_ctrl_new(udev); - if (uctrl == NULL) - return 2; - - while ((c = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL)) >= 0) - switch (c) { - case 'e': - if (udev_ctrl_send_exit(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'l': { - int i; - - i = util_log_priority(optarg); - if (i < 0) { - log_error("invalid number '%s'", optarg); - return rc; - } - if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 's': - if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'S': - if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'R': - if (udev_ctrl_send_reload(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'p': - if (strchr(optarg, '=') == NULL) { - log_error("expect <KEY>=<value> instead of '%s'", optarg); - return rc; - } - if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'm': { - char *endp; - int i; - - i = strtoul(optarg, &endp, 0); - if (endp[0] != '\0' || i < 1) { - log_error("invalid number '%s'", optarg); - return rc; - } - if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 't': { - usec_t s; - int seconds; - int r; - - r = parse_sec(optarg, &s); - if (r < 0) - return log_error_errno(r, "Failed to parse timeout value '%s'.", optarg); - - if (((s + USEC_PER_SEC - 1) / USEC_PER_SEC) > INT_MAX) - log_error("Timeout value is out of range."); - else { - seconds = s != USEC_INFINITY ? (int) ((s + USEC_PER_SEC - 1) / USEC_PER_SEC) : INT_MAX; - timeout = seconds; - rc = 0; - } - break; - } - case 'h': - print_help(); - rc = 0; - break; - } - - if (optind < argc) - log_error("Extraneous argument: %s", argv[optind]); - else if (optind == 1) - log_error("Option missing"); - return rc; -} - -const struct udevadm_cmd udevadm_control = { - .name = "control", - .cmd = adm_control, - .help = "Control the udev daemon", -}; diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c deleted file mode 100644 index 1bffe8e8ab..0000000000 --- a/src/udev/udevadm-hwdb.c +++ /dev/null @@ -1,698 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers <kay@vrfy.org> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <ctype.h> -#include <getopt.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "conf-files.h" -#include "fileio.h" -#include "fs-util.h" -#include "hwdb-internal.h" -#include "hwdb-util.h" -#include "label.h" -#include "mkdir.h" -#include "strbuf.h" -#include "string-util.h" -#include "udev.h" -#include "util.h" - -/* - * Generic udev properties, key/value database based on modalias strings. - * Uses a Patricia/radix trie to index all matches for efficient lookup. - */ - -static const char * const conf_file_dirs[] = { - "/etc/udev/hwdb.d", - UDEVLIBEXECDIR "/hwdb.d", - NULL -}; - -/* in-memory trie objects */ -struct trie { - struct trie_node *root; - struct strbuf *strings; - - size_t nodes_count; - size_t children_count; - size_t values_count; -}; - -struct trie_node { - /* prefix, common part for all children of this node */ - size_t prefix_off; - - /* sorted array of pointers to children nodes */ - struct trie_child_entry *children; - uint8_t children_count; - - /* sorted array of key/value pairs */ - struct trie_value_entry *values; - size_t values_count; -}; - -/* children array item with char (0-255) index */ -struct trie_child_entry { - uint8_t c; - struct trie_node *child; -}; - -/* value array item with key/value pairs */ -struct trie_value_entry { - size_t key_off; - size_t value_off; -}; - -static int trie_children_cmp(const void *v1, const void *v2) { - const struct trie_child_entry *n1 = v1; - const struct trie_child_entry *n2 = v2; - - return n1->c - n2->c; -} - -static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) { - struct trie_child_entry *child; - - /* extend array, add new entry, sort for bisection */ - child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry)); - if (!child) - return -ENOMEM; - - node->children = child; - trie->children_count++; - node->children[node->children_count].c = c; - node->children[node->children_count].child = node_child; - node->children_count++; - qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); - trie->nodes_count++; - - return 0; -} - -static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) { - struct trie_child_entry *child; - struct trie_child_entry search; - - search.c = c; - child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); - if (child) - return child->child; - return NULL; -} - -static void trie_node_cleanup(struct trie_node *node) { - size_t i; - - for (i = 0; i < node->children_count; i++) - trie_node_cleanup(node->children[i].child); - free(node->children); - free(node->values); - free(node); -} - -static int trie_values_cmp(const void *v1, const void *v2, void *arg) { - const struct trie_value_entry *val1 = v1; - const struct trie_value_entry *val2 = v2; - struct trie *trie = arg; - - return strcmp(trie->strings->buf + val1->key_off, - trie->strings->buf + val2->key_off); -} - -static int trie_node_add_value(struct trie *trie, struct trie_node *node, - const char *key, const char *value) { - ssize_t k, v; - struct trie_value_entry *val; - - k = strbuf_add_string(trie->strings, key, strlen(key)); - if (k < 0) - return k; - v = strbuf_add_string(trie->strings, value, strlen(value)); - if (v < 0) - return v; - - if (node->values_count) { - struct trie_value_entry search = { - .key_off = k, - .value_off = v, - }; - - val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); - if (val) { - /* replace existing earlier key with new value */ - val->value_off = v; - return 0; - } - } - - /* extend array, add new entry, sort for bisection */ - val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry)); - if (!val) - return -ENOMEM; - trie->values_count++; - node->values = val; - node->values[node->values_count].key_off = k; - node->values[node->values_count].value_off = v; - node->values_count++; - qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); - return 0; -} - -static int trie_insert(struct trie *trie, struct trie_node *node, const char *search, - const char *key, const char *value) { - size_t i = 0; - int err = 0; - - for (;;) { - size_t p; - uint8_t c; - struct trie_node *child; - - for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) { - _cleanup_free_ char *s = NULL; - ssize_t off; - _cleanup_free_ struct trie_node *new_child = NULL; - - if (c == search[i + p]) - continue; - - /* split node */ - new_child = new0(struct trie_node, 1); - if (!new_child) - return -ENOMEM; - - /* move values from parent to child */ - new_child->prefix_off = node->prefix_off + p+1; - new_child->children = node->children; - new_child->children_count = node->children_count; - new_child->values = node->values; - new_child->values_count = node->values_count; - - /* update parent; use strdup() because the source gets realloc()d */ - s = strndup(trie->strings->buf + node->prefix_off, p); - if (!s) - return -ENOMEM; - - off = strbuf_add_string(trie->strings, s, p); - if (off < 0) - return off; - - node->prefix_off = off; - node->children = NULL; - node->children_count = 0; - node->values = NULL; - node->values_count = 0; - err = node_add_child(trie, node, new_child, c); - if (err) - return err; - - new_child = NULL; /* avoid cleanup */ - break; - } - i += p; - - c = search[i]; - if (c == '\0') - return trie_node_add_value(trie, node, key, value); - - child = node_lookup(node, c); - if (!child) { - ssize_t off; - - /* new child */ - child = new0(struct trie_node, 1); - if (!child) - return -ENOMEM; - - off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1)); - if (off < 0) { - free(child); - return off; - } - - child->prefix_off = off; - err = node_add_child(trie, node, child, c); - if (err) { - free(child); - return err; - } - - return trie_node_add_value(trie, child, key, value); - } - - node = child; - i++; - } -} - -struct trie_f { - FILE *f; - struct trie *trie; - uint64_t strings_off; - - uint64_t nodes_count; - uint64_t children_count; - uint64_t values_count; -}; - -/* calculate the storage space for the nodes, children arrays, value arrays */ -static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { - uint64_t i; - - for (i = 0; i < node->children_count; i++) - trie_store_nodes_size(trie, node->children[i].child); - - trie->strings_off += sizeof(struct trie_node_f); - for (i = 0; i < node->children_count; i++) - trie->strings_off += sizeof(struct trie_child_entry_f); - for (i = 0; i < node->values_count; i++) - trie->strings_off += sizeof(struct trie_value_entry_f); -} - -static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { - uint64_t i; - struct trie_node_f n = { - .prefix_off = htole64(trie->strings_off + node->prefix_off), - .children_count = node->children_count, - .values_count = htole64(node->values_count), - }; - struct trie_child_entry_f *children = NULL; - int64_t node_off; - - if (node->children_count) { - children = new0(struct trie_child_entry_f, node->children_count); - if (!children) - return -ENOMEM; - } - - /* post-order recursion */ - for (i = 0; i < node->children_count; i++) { - int64_t child_off; - - child_off = trie_store_nodes(trie, node->children[i].child); - if (child_off < 0) { - free(children); - return child_off; - } - children[i].c = node->children[i].c; - children[i].child_off = htole64(child_off); - } - - /* write node */ - node_off = ftello(trie->f); - fwrite(&n, sizeof(struct trie_node_f), 1, trie->f); - trie->nodes_count++; - - /* append children array */ - if (node->children_count) { - fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f); - trie->children_count += node->children_count; - free(children); - } - - /* append values array */ - for (i = 0; i < node->values_count; i++) { - struct trie_value_entry_f v = { - .key_off = htole64(trie->strings_off + node->values[i].key_off), - .value_off = htole64(trie->strings_off + node->values[i].value_off), - }; - - fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); - trie->values_count++; - } - - return node_off; -} - -static int trie_store(struct trie *trie, const char *filename) { - struct trie_f t = { - .trie = trie, - }; - _cleanup_free_ char *filename_tmp = NULL; - int64_t pos; - int64_t root_off; - int64_t size; - struct trie_header_f h = { - .signature = HWDB_SIG, - .tool_version = htole64(atoi(VERSION)), - .header_size = htole64(sizeof(struct trie_header_f)), - .node_size = htole64(sizeof(struct trie_node_f)), - .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), - .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), - }; - int err; - - /* calculate size of header, nodes, children entries, value entries */ - t.strings_off = sizeof(struct trie_header_f); - trie_store_nodes_size(&t, trie->root); - - err = fopen_temporary(filename , &t.f, &filename_tmp); - if (err < 0) - return err; - fchmod(fileno(t.f), 0444); - - /* write nodes */ - err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); - if (err < 0) { - fclose(t.f); - unlink_noerrno(filename_tmp); - return -errno; - } - root_off = trie_store_nodes(&t, trie->root); - h.nodes_root_off = htole64(root_off); - pos = ftello(t.f); - h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); - - /* write string buffer */ - fwrite(trie->strings->buf, trie->strings->len, 1, t.f); - h.strings_len = htole64(trie->strings->len); - - /* write header */ - size = ftello(t.f); - h.file_size = htole64(size); - err = fseeko(t.f, 0, SEEK_SET); - if (err < 0) { - fclose(t.f); - unlink_noerrno(filename_tmp); - return -errno; - } - fwrite(&h, sizeof(struct trie_header_f), 1, t.f); - err = ferror(t.f); - if (err) - err = -errno; - fclose(t.f); - if (err < 0 || rename(filename_tmp, filename) < 0) { - unlink_noerrno(filename_tmp); - return err < 0 ? err : -errno; - } - - log_debug("=== trie on-disk ==="); - log_debug("size: %8"PRIi64" bytes", size); - log_debug("header: %8zu bytes", sizeof(struct trie_header_f)); - log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")", - t.nodes_count * sizeof(struct trie_node_f), t.nodes_count); - log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.children_count * sizeof(struct trie_child_entry_f), t.children_count); - log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.values_count * sizeof(struct trie_value_entry_f), t.values_count); - log_debug("string store: %8zu bytes", trie->strings->len); - log_debug("strings start: %8"PRIu64, t.strings_off); - - return 0; -} - -static int insert_data(struct trie *trie, struct udev_list *match_list, - char *line, const char *filename) { - char *value; - struct udev_list_entry *entry; - - value = strchr(line, '='); - if (!value) { - log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename); - return -EINVAL; - } - - value[0] = '\0'; - value++; - - /* libudev requires properties to start with a space */ - while (isblank(line[0]) && isblank(line[1])) - line++; - - if (line[0] == '\0' || value[0] == '\0') { - log_error("Error, empty key or value '%s' in '%s':", line, filename); - return -EINVAL; - } - - udev_list_entry_foreach(entry, udev_list_get_entry(match_list)) - trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value); - - return 0; -} - -static int import_file(struct udev *udev, struct trie *trie, const char *filename) { - enum { - HW_MATCH, - HW_DATA, - HW_NONE, - } state = HW_NONE; - FILE *f; - char line[LINE_MAX]; - struct udev_list match_list; - - udev_list_init(udev, &match_list, false); - - f = fopen(filename, "re"); - if (f == NULL) - return -errno; - - while (fgets(line, sizeof(line), f)) { - size_t len; - char *pos; - - /* comment line */ - if (line[0] == '#') - continue; - - /* strip trailing comment */ - pos = strchr(line, '#'); - if (pos) - pos[0] = '\0'; - - /* strip trailing whitespace */ - len = strlen(line); - while (len > 0 && isspace(line[len-1])) - len--; - line[len] = '\0'; - - switch (state) { - case HW_NONE: - if (len == 0) - break; - - if (line[0] == ' ') { - log_error("Error, MATCH expected but got '%s' in '%s':", line, filename); - break; - } - - /* start of record, first match */ - state = HW_MATCH; - udev_list_entry_add(&match_list, line, NULL); - break; - - case HW_MATCH: - if (len == 0) { - log_error("Error, DATA expected but got empty line in '%s':", filename); - state = HW_NONE; - udev_list_cleanup(&match_list); - break; - } - - /* another match */ - if (line[0] != ' ') { - udev_list_entry_add(&match_list, line, NULL); - break; - } - - /* first data */ - state = HW_DATA; - insert_data(trie, &match_list, line, filename); - break; - - case HW_DATA: - /* end of record */ - if (len == 0) { - state = HW_NONE; - udev_list_cleanup(&match_list); - break; - } - - if (line[0] != ' ') { - log_error("Error, DATA expected but got '%s' in '%s':", line, filename); - state = HW_NONE; - udev_list_cleanup(&match_list); - break; - } - - insert_data(trie, &match_list, line, filename); - break; - }; - } - - fclose(f); - udev_list_cleanup(&match_list); - return 0; -} - -static void help(void) { - printf("Usage: udevadm hwdb OPTIONS\n" - " -u,--update update the hardware database\n" - " --usr generate in " UDEVLIBEXECDIR " instead of /etc/udev\n" - " -t,--test=MODALIAS query database and print result\n" - " -r,--root=PATH alternative root path in the filesystem\n" - " -h,--help\n\n"); -} - -static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { - enum { - ARG_USR = 0x100, - }; - - static const struct option options[] = { - { "update", no_argument, NULL, 'u' }, - { "usr", no_argument, NULL, ARG_USR }, - { "test", required_argument, NULL, 't' }, - { "root", required_argument, NULL, 'r' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - const char *test = NULL; - const char *root = ""; - const char *hwdb_bin_dir = "/etc/udev"; - bool update = false; - struct trie *trie = NULL; - int err, c; - int rc = EXIT_SUCCESS; - - while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0) - switch(c) { - case 'u': - update = true; - break; - case ARG_USR: - hwdb_bin_dir = UDEVLIBEXECDIR; - break; - case 't': - test = optarg; - break; - case 'r': - root = optarg; - break; - case 'h': - help(); - return EXIT_SUCCESS; - case '?': - return EXIT_FAILURE; - default: - assert_not_reached("Unknown option"); - } - - if (!update && !test) { - log_error("Either --update or --test must be used"); - return EXIT_FAILURE; - } - - if (update) { - char **files, **f; - _cleanup_free_ char *hwdb_bin = NULL; - - trie = new0(struct trie, 1); - if (!trie) { - rc = EXIT_FAILURE; - goto out; - } - - /* string store */ - trie->strings = strbuf_new(); - if (!trie->strings) { - rc = EXIT_FAILURE; - goto out; - } - - /* index */ - trie->root = new0(struct trie_node, 1); - if (!trie->root) { - rc = EXIT_FAILURE; - goto out; - } - trie->nodes_count++; - - err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs); - if (err < 0) { - log_error_errno(err, "failed to enumerate hwdb files: %m"); - rc = EXIT_FAILURE; - goto out; - } - STRV_FOREACH(f, files) { - log_debug("reading file '%s'", *f); - import_file(udev, trie, *f); - } - strv_free(files); - - strbuf_complete(trie->strings); - - log_debug("=== trie in-memory ==="); - log_debug("nodes: %8zu bytes (%8zu)", - trie->nodes_count * sizeof(struct trie_node), trie->nodes_count); - log_debug("children arrays: %8zu bytes (%8zu)", - trie->children_count * sizeof(struct trie_child_entry), trie->children_count); - log_debug("values arrays: %8zu bytes (%8zu)", - trie->values_count * sizeof(struct trie_value_entry), trie->values_count); - log_debug("strings: %8zu bytes", - trie->strings->len); - log_debug("strings incoming: %8zu bytes (%8zu)", - trie->strings->in_len, trie->strings->in_count); - log_debug("strings dedup'ed: %8zu bytes (%8zu)", - trie->strings->dedup_len, trie->strings->dedup_count); - - hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin", NULL); - if (!hwdb_bin) { - rc = EXIT_FAILURE; - goto out; - } - - mkdir_parents_label(hwdb_bin, 0755); - - err = trie_store(trie, hwdb_bin); - if (err < 0) { - log_error_errno(err, "Failure writing database %s: %m", hwdb_bin); - rc = EXIT_FAILURE; - } - - label_fix(hwdb_bin, false, false); - } - - if (test) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - int r; - - r = sd_hwdb_new(&hwdb); - if (r >= 0) { - const char *key, *value; - - SD_HWDB_FOREACH_PROPERTY(hwdb, test, key, value) - printf("%s=%s\n", key, value); - } - } -out: - if (trie) { - if (trie->root) - trie_node_cleanup(trie->root); - strbuf_cleanup(trie->strings); - free(trie); - } - return rc; -} - -const struct udevadm_cmd udevadm_hwdb = { - .name = "hwdb", - .cmd = adm_hwdb, -}; diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c deleted file mode 100644 index 6753c52005..0000000000 --- a/src/udev/udevadm-info.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ctype.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "fd-util.h" -#include "string-util.h" -#include "udev-util.h" -#include "udev.h" -#include "udevadm-util.h" - -static bool skip_attribute(const char *name) { - static const char* const skip[] = { - "uevent", - "dev", - "modalias", - "resource", - "driver", - "subsystem", - "module", - }; - unsigned int i; - - for (i = 0; i < ELEMENTSOF(skip); i++) - if (streq(name, skip[i])) - return true; - return false; -} - -static void print_all_attributes(struct udev_device *device, const char *key) { - struct udev_list_entry *sysattr; - - udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { - const char *name; - const char *value; - size_t len; - - name = udev_list_entry_get_name(sysattr); - if (skip_attribute(name)) - continue; - - value = udev_device_get_sysattr_value(device, name); - if (value == NULL) - continue; - - /* skip any values that look like a path */ - if (value[0] == '/') - continue; - - /* skip nonprintable attributes */ - len = strlen(value); - while (len > 0 && isprint(value[len-1])) - len--; - if (len > 0) - continue; - - printf(" %s{%s}==\"%s\"\n", key, name, value); - } - printf("\n"); -} - -static int print_device_chain(struct udev_device *device) { - struct udev_device *device_parent; - const char *str; - - printf("\n" - "Udevadm info starts with the device specified by the devpath and then\n" - "walks up the chain of parent devices. It prints for every device\n" - "found, all possible attributes in the udev rules key format.\n" - "A rule to match, can be composed by the attributes of the device\n" - "and the attributes from one single parent device.\n" - "\n"); - - printf(" looking at device '%s':\n", udev_device_get_devpath(device)); - printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); - str = udev_device_get_subsystem(device); - if (str == NULL) - str = ""; - printf(" SUBSYSTEM==\"%s\"\n", str); - str = udev_device_get_driver(device); - if (str == NULL) - str = ""; - printf(" DRIVER==\"%s\"\n", str); - print_all_attributes(device, "ATTR"); - - device_parent = device; - do { - device_parent = udev_device_get_parent(device_parent); - if (device_parent == NULL) - break; - printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); - printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); - str = udev_device_get_subsystem(device_parent); - if (str == NULL) - str = ""; - printf(" SUBSYSTEMS==\"%s\"\n", str); - str = udev_device_get_driver(device_parent); - if (str == NULL) - str = ""; - printf(" DRIVERS==\"%s\"\n", str); - print_all_attributes(device_parent, "ATTRS"); - } while (device_parent != NULL); - - return 0; -} - -static void print_record(struct udev_device *device) { - const char *str; - int i; - struct udev_list_entry *list_entry; - - printf("P: %s\n", udev_device_get_devpath(device)); - - str = udev_device_get_devnode(device); - if (str != NULL) - printf("N: %s\n", str + strlen("/dev/")); - - i = udev_device_get_devlink_priority(device); - if (i != 0) - printf("L: %i\n", i); - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) - printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/")); - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("E: %s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); -} - -static int stat_device(const char *name, bool export, const char *prefix) { - struct stat statbuf; - - if (stat(name, &statbuf) != 0) - return -errno; - - if (export) { - if (prefix == NULL) - prefix = "INFO_"; - printf("%sMAJOR=%u\n" - "%sMINOR=%u\n", - prefix, major(statbuf.st_dev), - prefix, minor(statbuf.st_dev)); - } else - printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev)); - return 0; -} - -static int export_devices(struct udev *udev) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate; - struct udev_list_entry *list_entry; - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -ENOMEM; - - udev_enumerate_scan_devices(udev_enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { - _cleanup_udev_device_unref_ struct udev_device *device; - - device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); - if (device != NULL) - print_record(device); - } - - return 0; -} - -static void cleanup_dir(DIR *dir, mode_t mask, int depth) { - struct dirent *dent; - - if (depth <= 0) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) - continue; - if ((stats.st_mode & mask) != 0) - continue; - if (S_ISDIR(stats.st_mode)) { - _cleanup_closedir_ DIR *dir2; - - dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2 != NULL) - cleanup_dir(dir2, mask, depth-1); - - (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); - } else - (void) unlinkat(dirfd(dir), dent->d_name, 0); - } -} - -static void cleanup_db(struct udev *udev) { - _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL; - - (void) unlink("/run/udev/queue.bin"); - - dir1 = opendir("/run/udev/data"); - if (dir1 != NULL) - cleanup_dir(dir1, S_ISVTX, 1); - - dir2 = opendir("/run/udev/links"); - if (dir2 != NULL) - cleanup_dir(dir2, 0, 2); - - dir3 = opendir("/run/udev/tags"); - if (dir3 != NULL) - cleanup_dir(dir3, 0, 2); - - dir4 = opendir("/run/udev/static_node-tags"); - if (dir4 != NULL) - cleanup_dir(dir4, 0, 2); - - dir5 = opendir("/run/udev/watch"); - if (dir5 != NULL) - cleanup_dir(dir5, 0, 1); -} - -static void help(void) { - - printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n" - "Query sysfs or the udev database.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n" - " -q --query=TYPE Query device information:\n" - " name Name of device node\n" - " symlink Pointing to node\n" - " path sysfs device path\n" - " property The device properties\n" - " all All values\n" - " -p --path=SYSPATH sysfs device path used for query or attribute walk\n" - " -n --name=NAME Node or symlink name used for query or attribute walk\n" - " -r --root Prepend dev directory to path names\n" - " -a --attribute-walk Print all key matches walking along the chain\n" - " of parent devices\n" - " -d --device-id-of-file=FILE Print major:minor of device containing this file\n" - " -x --export Export key/value pairs\n" - " -P --export-prefix Export the key name with a prefix\n" - " -e --export-db Export the content of the udev database\n" - " -c --cleanup-db Clean up the udev database\n" - , program_invocation_short_name); -} - -static int uinfo(struct udev *udev, int argc, char *argv[]) { - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - bool root = 0; - bool export = 0; - const char *export_prefix = NULL; - char name[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - int c; - - static const struct option options[] = { - { "name", required_argument, NULL, 'n' }, - { "path", required_argument, NULL, 'p' }, - { "query", required_argument, NULL, 'q' }, - { "attribute-walk", no_argument, NULL, 'a' }, - { "cleanup-db", no_argument, NULL, 'c' }, - { "export-db", no_argument, NULL, 'e' }, - { "root", no_argument, NULL, 'r' }, - { "device-id-of-file", required_argument, NULL, 'd' }, - { "export", no_argument, NULL, 'x' }, - { "export-prefix", required_argument, NULL, 'P' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - enum action_type { - ACTION_QUERY, - ACTION_ATTRIBUTE_WALK, - ACTION_DEVICE_ID_FILE, - } action = ACTION_QUERY; - - enum query_type { - QUERY_NAME, - QUERY_PATH, - QUERY_SYMLINK, - QUERY_PROPERTY, - QUERY_ALL, - } query = QUERY_ALL; - - while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0) - switch (c) { - case 'n': { - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - return 2; - } - - device = find_device(udev, optarg, "/dev/"); - if (device == NULL) { - fprintf(stderr, "device node not found\n"); - return 2; - } - break; - } - case 'p': - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - return 2; - } - - device = find_device(udev, optarg, "/sys"); - if (device == NULL) { - fprintf(stderr, "syspath not found\n"); - return 2; - } - break; - case 'q': - action = ACTION_QUERY; - if (streq(optarg, "property") || streq(optarg, "env")) - query = QUERY_PROPERTY; - else if (streq(optarg, "name")) - query = QUERY_NAME; - else if (streq(optarg, "symlink")) - query = QUERY_SYMLINK; - else if (streq(optarg, "path")) - query = QUERY_PATH; - else if (streq(optarg, "all")) - query = QUERY_ALL; - else { - fprintf(stderr, "unknown query type\n"); - return 3; - } - break; - case 'r': - root = true; - break; - case 'd': - action = ACTION_DEVICE_ID_FILE; - strscpy(name, sizeof(name), optarg); - break; - case 'a': - action = ACTION_ATTRIBUTE_WALK; - break; - case 'e': - if (export_devices(udev) < 0) - return 1; - return 0; - case 'c': - cleanup_db(udev); - return 0; - case 'x': - export = true; - break; - case 'P': - export_prefix = optarg; - break; - case 'V': - printf("%s\n", VERSION); - return 0; - case 'h': - help(); - return 0; - default: - return 1; - } - - switch (action) { - case ACTION_QUERY: - if (!device) { - if (!argv[optind]) { - help(); - return 2; - } - device = find_device(udev, argv[optind], NULL); - if (!device) { - fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n"); - return 4; - } - } - - switch(query) { - case QUERY_NAME: { - const char *node = udev_device_get_devnode(device); - - if (node == NULL) { - fprintf(stderr, "no device node found\n"); - return 5; - } - - if (root) - printf("%s\n", udev_device_get_devnode(device)); - else - printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/")); - break; - } - case QUERY_SYMLINK: - list_entry = udev_device_get_devlinks_list_entry(device); - while (list_entry != NULL) { - if (root) - printf("%s", udev_list_entry_get_name(list_entry)); - else - printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/")); - list_entry = udev_list_entry_get_next(list_entry); - if (list_entry != NULL) - printf(" "); - } - printf("\n"); - break; - case QUERY_PATH: - printf("%s\n", udev_device_get_devpath(device)); - return 0; - case QUERY_PROPERTY: - list_entry = udev_device_get_properties_list_entry(device); - while (list_entry != NULL) { - if (export) - printf("%s%s='%s'\n", strempty(export_prefix), - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - else - printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); - - list_entry = udev_list_entry_get_next(list_entry); - } - break; - case QUERY_ALL: - print_record(device); - break; - default: - assert_not_reached("unknown query type"); - } - break; - case ACTION_ATTRIBUTE_WALK: - if (!device && argv[optind]) { - device = find_device(udev, argv[optind], NULL); - if (!device) { - fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n"); - return 4; - } - } - if (!device) { - fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n"); - return 4; - } - print_device_chain(device); - break; - case ACTION_DEVICE_ID_FILE: - if (stat_device(name, export, export_prefix) != 0) - return 1; - break; - } - - return 0; -} - -const struct udevadm_cmd udevadm_info = { - .name = "info", - .cmd = uinfo, - .help = "Query sysfs or the udev database", -}; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c deleted file mode 100644 index f656c2198e..0000000000 --- a/src/udev/udevadm-monitor.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2004-2010 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <getopt.h> -#include <signal.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/time.h> -#include <time.h> - -#include "fd-util.h" -#include "formats-util.h" -#include "udev-util.h" -#include "udev.h" - -static bool udev_exit; - -static void sig_handler(int signum) { - if (signum == SIGINT || signum == SIGTERM) - udev_exit = true; -} - -static void print_device(struct udev_device *device, const char *source, int prop) { - struct timespec ts; - - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n", - source, - ts.tv_sec, ts.tv_nsec/1000, - udev_device_get_action(device), - udev_device_get_devpath(device), - udev_device_get_subsystem(device)); - if (prop) { - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("%s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); - } -} - -static void help(void) { - printf("%s monitor [--property] [--kernel] [--udev] [--help]\n\n" - "Listen to kernel and udev events.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -p --property Print the event properties\n" - " -k --kernel Print kernel uevents\n" - " -u --udev Print udev events\n" - " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n" - " -t --tag-match=TAG Filter events by tag\n" - , program_invocation_short_name); -} - -static int adm_monitor(struct udev *udev, int argc, char *argv[]) { - struct sigaction act = {}; - sigset_t mask; - bool prop = false; - bool print_kernel = false; - bool print_udev = false; - _cleanup_udev_list_cleanup_ struct udev_list subsystem_match_list; - _cleanup_udev_list_cleanup_ struct udev_list tag_match_list; - _cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor = NULL; - _cleanup_udev_monitor_unref_ struct udev_monitor *kernel_monitor = NULL; - _cleanup_close_ int fd_ep = -1; - int fd_kernel = -1, fd_udev = -1; - struct epoll_event ep_kernel, ep_udev; - int c; - - static const struct option options[] = { - { "property", no_argument, NULL, 'p' }, - { "environment", no_argument, NULL, 'e' }, /* alias for -p */ - { "kernel", no_argument, NULL, 'k' }, - { "udev", no_argument, NULL, 'u' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "tag-match", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - udev_list_init(udev, &subsystem_match_list, true); - udev_list_init(udev, &tag_match_list, true); - - while ((c = getopt_long(argc, argv, "pekus:t:h", options, NULL)) >= 0) - switch (c) { - case 'p': - case 'e': - prop = true; - break; - case 'k': - print_kernel = true; - break; - case 'u': - print_udev = true; - break; - case 's': - { - char subsys[UTIL_NAME_SIZE]; - char *devtype; - - strscpy(subsys, sizeof(subsys), optarg); - devtype = strchr(subsys, '/'); - if (devtype != NULL) { - devtype[0] = '\0'; - devtype++; - } - udev_list_entry_add(&subsystem_match_list, subsys, devtype); - break; - } - case 't': - udev_list_entry_add(&tag_match_list, optarg, NULL); - break; - case 'h': - help(); - return 0; - default: - return 1; - } - - if (!print_kernel && !print_udev) { - print_kernel = true; - print_udev = true; - } - - /* set signal handlers */ - act.sa_handler = sig_handler; - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigprocmask(SIG_UNBLOCK, &mask, NULL); - - /* Callers are expecting to see events as they happen: Line buffering */ - setlinebuf(stdout); - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - log_error_errno(errno, "error creating epoll fd: %m"); - return 1; - } - - printf("monitor will print the received events for:\n"); - if (print_udev) { - struct udev_list_entry *entry; - - udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (udev_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - return 1; - } - udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); - fd_udev = udev_monitor_get_fd(udev_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - const char *devtype = udev_list_entry_get_value(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { - const char *tag = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) - fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); - } - - if (udev_monitor_enable_receiving(udev_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to udev events\n"); - return 2; - } - - memzero(&ep_udev, sizeof(struct epoll_event)); - ep_udev.events = EPOLLIN; - ep_udev.data.fd = fd_udev; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - log_error_errno(errno, "fail to add fd to epoll: %m"); - return 2; - } - - printf("UDEV - the event which udev sends out after rule processing\n"); - } - - if (print_kernel) { - struct udev_list_entry *entry; - - kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (kernel_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - return 3; - } - udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - fd_kernel = udev_monitor_get_fd(kernel_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - if (udev_monitor_enable_receiving(kernel_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to kernel events\n"); - return 4; - } - - memzero(&ep_kernel, sizeof(struct epoll_event)); - ep_kernel.events = EPOLLIN; - ep_kernel.data.fd = fd_kernel; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { - log_error_errno(errno, "fail to add fd to epoll: %m"); - return 5; - } - - printf("KERNEL - the kernel uevent\n"); - } - printf("\n"); - - while (!udev_exit) { - int fdcount; - struct epoll_event ev[4]; - int i; - - fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); - if (fdcount < 0) { - if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %m\n"); - continue; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(kernel_monitor); - if (device == NULL) - continue; - print_device(device, "KERNEL", prop); - udev_device_unref(device); - } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(udev_monitor); - if (device == NULL) - continue; - print_device(device, "UDEV", prop); - udev_device_unref(device); - } - } - } - - return 0; -} - -const struct udevadm_cmd udevadm_monitor = { - .name = "monitor", - .cmd = adm_monitor, - .help = "Listen to kernel and udev events", -}; diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c deleted file mode 100644 index 6a5dc6e9e4..0000000000 --- a/src/udev/udevadm-settle.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <getopt.h> -#include <poll.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "parse-util.h" -#include "udev.h" -#include "util.h" - -static void help(void) { - printf("%s settle OPTIONS\n\n" - "Wait for pending udev events.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -t --timeout=SECONDS Maximum time to wait for events\n" - " -E --exit-if-exists=FILE Stop waiting if file exists\n" - , program_invocation_short_name); -} - -static int adm_settle(struct udev *udev, int argc, char *argv[]) { - static const struct option options[] = { - { "timeout", required_argument, NULL, 't' }, - { "exit-if-exists", required_argument, NULL, 'E' }, - { "help", no_argument, NULL, 'h' }, - { "seq-start", required_argument, NULL, 's' }, /* removed */ - { "seq-end", required_argument, NULL, 'e' }, /* removed */ - { "quiet", no_argument, NULL, 'q' }, /* removed */ - {} - }; - usec_t deadline; - const char *exists = NULL; - unsigned int timeout = 120; - struct pollfd pfd[1] = { {.fd = -1}, }; - int c; - struct udev_queue *queue; - int rc = EXIT_FAILURE; - - while ((c = getopt_long(argc, argv, "t:E:hs:e:q", options, NULL)) >= 0) { - switch (c) { - - case 't': { - int r; - - r = safe_atou(optarg, &timeout); - if (r < 0) { - log_error_errno(r, "Invalid timeout value '%s': %m", optarg); - return EXIT_FAILURE; - } - break; - } - - case 'E': - exists = optarg; - break; - - case 'h': - help(); - return EXIT_SUCCESS; - - case 's': - case 'e': - case 'q': - log_info("Option -%c no longer supported.", c); - return EXIT_FAILURE; - - case '?': - return EXIT_FAILURE; - - default: - assert_not_reached("Unknown argument"); - } - } - - if (optind < argc) { - fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]); - 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, MAX(5U, timeout)) < 0) { - log_debug("no connection to daemon"); - udev_ctrl_unref(uctrl); - return EXIT_SUCCESS; - } - udev_ctrl_unref(uctrl); - } - } - - queue = udev_queue_new(udev); - if (!queue) { - log_error("unable to get udev queue"); - return EXIT_FAILURE; - } - - pfd[0].events = POLLIN; - pfd[0].fd = udev_queue_get_fd(queue); - if (pfd[0].fd < 0) { - log_debug("queue is empty, nothing to watch"); - rc = EXIT_SUCCESS; - goto out; - } - - for (;;) { - if (exists && access(exists, F_OK) >= 0) { - rc = EXIT_SUCCESS; - break; - } - - /* exit if queue is empty */ - if (udev_queue_get_queue_is_empty(queue)) { - rc = EXIT_SUCCESS; - 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); - } - -out: - udev_queue_unref(queue); - return rc; -} - -const struct udevadm_cmd udevadm_settle = { - .name = "settle", - .cmd = adm_settle, - .help = "Wait for pending udev events", -}; diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c deleted file mode 100644 index 0b180d03eb..0000000000 --- a/src/udev/udevadm-test-builtin.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2011 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <getopt.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> - -#include "string-util.h" -#include "udev.h" - -static void help(struct udev *udev) { - printf("%s builtin [--help] COMMAND SYSPATH\n\n" - "Test a built-in command.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n\n" - "Commands:\n" - , program_invocation_short_name); - - udev_builtin_list(udev); -} - -static int adm_builtin(struct udev *udev, int argc, char *argv[]) { - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - char *command = NULL; - char *syspath = NULL; - char filename[UTIL_PATH_SIZE]; - struct udev_device *dev = NULL; - enum udev_builtin_cmd cmd; - int rc = EXIT_SUCCESS, c; - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - switch (c) { - case 'h': - help(udev); - goto out; - } - - command = argv[optind++]; - if (command == NULL) { - fprintf(stderr, "command missing\n"); - help(udev); - rc = 2; - goto out; - } - - syspath = argv[optind++]; - if (syspath == NULL) { - fprintf(stderr, "syspath missing\n"); - rc = 3; - goto out; - } - - udev_builtin_init(udev); - - cmd = udev_builtin_lookup(command); - if (cmd >= UDEV_BUILTIN_MAX) { - fprintf(stderr, "unknown command '%s'\n", command); - help(udev); - rc = 5; - goto out; - } - - /* add /sys if needed */ - if (!startswith(syspath, "/sys")) - strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); - else - strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_syspath(udev, filename); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n\n", filename); - rc = 4; - goto out; - } - - rc = udev_builtin_run(dev, cmd, command, true); - if (rc < 0) { - fprintf(stderr, "error executing '%s', exit code %i\n\n", command, rc); - rc = 6; - } -out: - udev_device_unref(dev); - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test_builtin = { - .name = "test-builtin", - .cmd = adm_builtin, - .help = "Test a built-in command", - .debug = true, -}; diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c deleted file mode 100644 index 702dbe5282..0000000000 --- a/src/udev/udevadm-test.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (C) 2004-2008 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <getopt.h> -#include <signal.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/signalfd.h> -#include <unistd.h> - -#include "string-util.h" -#include "udev-util.h" -#include "udev.h" - -static void help(void) { - - printf("%s test OPTIONS <syspath>\n\n" - "Test an event run.\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -a --action=ACTION Set action string\n" - " -N --resolve-names=early|late|never When to resolve names\n" - , program_invocation_short_name); -} - -static int adm_test(struct udev *udev, int argc, char *argv[]) { - int resolve_names = 1; - char filename[UTIL_PATH_SIZE]; - const char *action = "add"; - const char *syspath = NULL; - struct udev_list_entry *entry; - _cleanup_udev_rules_unref_ struct udev_rules *rules = NULL; - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - _cleanup_udev_event_unref_ struct udev_event *event = NULL; - sigset_t mask, sigmask_orig; - int rc = 0, c; - - static const struct option options[] = { - { "action", required_argument, NULL, 'a' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - log_debug("version %s", VERSION); - - while ((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0) - switch (c) { - case 'a': - action = optarg; - break; - case 'N': - if (streq (optarg, "early")) { - resolve_names = 1; - } else if (streq (optarg, "late")) { - resolve_names = 0; - } else if (streq (optarg, "never")) { - resolve_names = -1; - } else { - fprintf(stderr, "resolve-names must be early, late or never\n"); - log_error("resolve-names must be early, late or never"); - exit(EXIT_FAILURE); - } - break; - case 'h': - help(); - exit(EXIT_SUCCESS); - case '?': - exit(EXIT_FAILURE); - default: - assert_not_reached("Unknown option"); - } - - syspath = argv[optind]; - if (syspath == NULL) { - fprintf(stderr, "syspath parameter missing\n"); - rc = 2; - goto out; - } - - printf("This program is for debugging only, it does not run any program\n" - "specified by a RUN key. It may show incorrect results, because\n" - "some values may be different, or not available at a simulation run.\n" - "\n"); - - sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); - - udev_builtin_init(udev); - - rules = udev_rules_new(udev, resolve_names); - if (rules == NULL) { - fprintf(stderr, "error reading rules\n"); - rc = 3; - goto out; - } - - /* add /sys if needed */ - if (!startswith(syspath, "/sys")) - strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); - else - strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(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; - } - - /* don't read info from the db */ - udev_device_set_info_loaded(dev); - - event = udev_event_new(dev); - - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - - udev_event_execute_rules(event, - 60 * USEC_PER_SEC, 20 * USEC_PER_SEC, - NULL, - 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)); - - udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { - char program[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); - printf("run: '%s'\n", program); - } -out: - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test = { - .name = "test", - .cmd = adm_test, - .help = "Test an event run", - .debug = true, -}; diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c deleted file mode 100644 index 9d52345d92..0000000000 --- a/src/udev/udevadm-trigger.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include "string-util.h" -#include "udev-util.h" -#include "udev.h" -#include "udevadm-util.h" -#include "util.h" - -static int verbose; -static int dry_run; - -static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) { - struct udev_list_entry *entry; - - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { - char filename[UTIL_PATH_SIZE]; - int fd; - - if (verbose) - printf("%s\n", udev_list_entry_get_name(entry)); - if (dry_run) - continue; - strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); - fd = open(filename, O_WRONLY|O_CLOEXEC); - if (fd < 0) - continue; - if (write(fd, action, strlen(action)) < 0) - log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); - close(fd); - } -} - -static const char *keyval(const char *str, const char **val, char *buf, size_t size) { - char *pos; - - strscpy(buf, size,str); - pos = strchr(buf, '='); - if (pos != NULL) { - pos[0] = 0; - pos++; - } - *val = pos; - return buf; -} - -static void help(void) { - printf("%s trigger OPTIONS\n\n" - "Request events from the kernel.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -v --verbose Print the list of devices while running\n" - " -n --dry-run Do not actually trigger the events\n" - " -t --type= Type of events to trigger\n" - " devices sysfs devices (default)\n" - " subsystems sysfs subsystems and drivers\n" - " -c --action=ACTION Event action value, default is \"change\"\n" - " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" - " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" - " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" - " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" - " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" - " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" - " -y --sysname-match=NAME Trigger devices with this /sys path\n" - " --name-match=NAME Trigger devices with this /dev name\n" - " -b --parent-match=NAME Trigger devices with that parent device\n" - , program_invocation_short_name); -} - -static int adm_trigger(struct udev *udev, int argc, char *argv[]) { - enum { - ARG_NAME = 0x100, - }; - - static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "name-match", required_argument, NULL, ARG_NAME }, - { "parent-match", required_argument, NULL, 'b' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - enum { - TYPE_DEVICES, - TYPE_SUBSYSTEMS, - } device_type = TYPE_DEVICES; - const char *action = "change"; - _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL; - int c, r; - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return 1; - - while ((c = getopt_long(argc, argv, "vno:t:c:s:S:a:A:p:g:y:b:h", options, NULL)) >= 0) { - const char *key; - const char *val; - char buf[UTIL_PATH_SIZE]; - - switch (c) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 't': - if (streq(optarg, "devices")) - device_type = TYPE_DEVICES; - else if (streq(optarg, "subsystems")) - device_type = TYPE_SUBSYSTEMS; - else { - log_error("unknown type --type=%s", optarg); - return 2; - } - break; - case 'c': - if (!nulstr_contains("add\0" "remove\0" "change\0", optarg)) { - log_error("unknown action '%s'", optarg); - return 2; - } else - action = optarg; - - break; - case 's': - r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add subsystem match '%s': %m", optarg); - return 2; - } - break; - case 'S': - r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg); - return 2; - } - break; - case 'a': - key = keyval(optarg, &val, buf, sizeof(buf)); - r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val); - if (r < 0) { - log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val); - return 2; - } - break; - case 'A': - key = keyval(optarg, &val, buf, sizeof(buf)); - r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); - if (r < 0) { - log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val); - return 2; - } - break; - case 'p': - key = keyval(optarg, &val, buf, sizeof(buf)); - r = udev_enumerate_add_match_property(udev_enumerate, key, val); - if (r < 0) { - log_error_errno(r, "could not add property match '%s=%s': %m", key, val); - return 2; - } - break; - case 'g': - r = udev_enumerate_add_match_tag(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add tag match '%s': %m", optarg); - return 2; - } - break; - case 'y': - r = udev_enumerate_add_match_sysname(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add sysname match '%s': %m", optarg); - return 2; - } - break; - case 'b': { - _cleanup_udev_device_unref_ struct udev_device *dev; - - dev = find_device(udev, optarg, "/sys"); - if (dev == NULL) { - log_error("unable to open the device '%s'", optarg); - return 2; - } - - r = udev_enumerate_add_match_parent(udev_enumerate, dev); - if (r < 0) { - log_error_errno(r, "could not add parent match '%s': %m", optarg); - return 2; - } - break; - } - - case ARG_NAME: { - _cleanup_udev_device_unref_ struct udev_device *dev; - - dev = find_device(udev, optarg, "/dev/"); - if (dev == NULL) { - log_error("unable to open the device '%s'", optarg); - return 2; - } - - r = udev_enumerate_add_match_parent(udev_enumerate, dev); - if (r < 0) { - log_error_errno(r, "could not add parent match '%s': %m", optarg); - return 2; - } - break; - } - - case 'h': - help(); - return 0; - case '?': - return 1; - default: - assert_not_reached("Unknown option"); - } - } - - for (; optind < argc; optind++) { - _cleanup_udev_device_unref_ struct udev_device *dev; - - dev = find_device(udev, argv[optind], NULL); - if (dev == NULL) { - log_error("unable to open the device '%s'", argv[optind]); - return 2; - } - - r = udev_enumerate_add_match_parent(udev_enumerate, dev); - if (r < 0) { - log_error_errno(r, "could not add tag match '%s': %m", optarg); - return 2; - } - } - - switch (device_type) { - case TYPE_SUBSYSTEMS: - udev_enumerate_scan_subsystems(udev_enumerate); - exec_list(udev_enumerate, action); - return 0; - case TYPE_DEVICES: - udev_enumerate_scan_devices(udev_enumerate); - exec_list(udev_enumerate, action); - return 0; - default: - assert_not_reached("device_type"); - } -} - -const struct udevadm_cmd udevadm_trigger = { - .name = "trigger", - .cmd = adm_trigger, - .help = "Request events from the kernel", -}; diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c deleted file mode 100644 index 3539c1d6ab..0000000000 --- a/src/udev/udevadm-util.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "string-util.h" -#include "udevadm-util.h" - -struct udev_device *find_device(struct udev *udev, - const char *id, - const char *prefix) { - - assert(udev); - assert(id); - - if (prefix && !startswith(id, prefix)) - id = strjoina(prefix, id); - - if (startswith(id, "/dev/")) { - struct stat statbuf; - char type; - - if (stat(id, &statbuf) < 0) - return NULL; - - if (S_ISBLK(statbuf.st_mode)) - type = 'b'; - else if (S_ISCHR(statbuf.st_mode)) - type = 'c'; - else - return NULL; - - return udev_device_new_from_devnum(udev, type, statbuf.st_rdev); - } else if (startswith(id, "/sys/")) - return udev_device_new_from_syspath(udev, id); - else - return NULL; -} diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h deleted file mode 100644 index dc712b0d93..0000000000 --- a/src/udev/udevadm-util.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/* - * Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "udev.h" - -struct udev_device *find_device(struct udev *udev, - const char *id, - const char *prefix); diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c deleted file mode 100644 index a6a873e5de..0000000000 --- a/src/udev/udevadm.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2007-2012 Kay Sievers <kay@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <getopt.h> -#include <stddef.h> -#include <stdio.h> - -#include "selinux-util.h" -#include "string-util.h" -#include "udev.h" - -static int adm_version(struct udev *udev, int argc, char *argv[]) { - printf("%s\n", VERSION); - return 0; -} - -static const struct udevadm_cmd udevadm_version = { - .name = "version", - .cmd = adm_version, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]); - -static const struct udevadm_cmd udevadm_help = { - .name = "help", - .cmd = adm_help, -}; - -static const struct udevadm_cmd *udevadm_cmds[] = { - &udevadm_info, - &udevadm_trigger, - &udevadm_settle, - &udevadm_control, - &udevadm_monitor, - &udevadm_hwdb, - &udevadm_test, - &udevadm_test_builtin, - &udevadm_version, - &udevadm_help, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]) { - unsigned int i; - - printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n" - "Send control commands or test the device manager.\n\n" - "Commands:\n" - , program_invocation_short_name); - - for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) - if (udevadm_cmds[i]->help != NULL) - printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); - return 0; -} - -static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) { - if (cmd->debug) - log_set_max_level(LOG_DEBUG); - log_debug("calling: %s", cmd->name); - return cmd->cmd(udev, argc, argv); -} - -int main(int argc, char *argv[]) { - struct udev *udev; - static const struct option options[] = { - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - const char *command; - unsigned int i; - int rc = 1, c; - - udev = udev_new(); - if (udev == NULL) - goto out; - - log_parse_environment(); - log_open(); - mac_selinux_init(); - - while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0) - switch (c) { - - case 'd': - log_set_max_level(LOG_DEBUG); - break; - - case 'h': - rc = adm_help(udev, argc, argv); - goto out; - - case 'V': - rc = adm_version(udev, argc, argv); - goto out; - - default: - goto out; - } - - command = argv[optind]; - - if (command != NULL) - for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) - if (streq(udevadm_cmds[i]->name, command)) { - argc -= optind; - argv += optind; - /* we need '0' here to reset the internal state */ - optind = 0; - rc = run_command(udev, udevadm_cmds[i], argc, argv); - goto out; - } - - fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name); - rc = 2; -out: - mac_selinux_finish(); - udev_unref(udev); - log_close(); - return rc; -} diff --git a/src/udev/udevd.c b/src/udev/udevd.c deleted file mode 100644 index badbab6205..0000000000 --- a/src/udev/udevd.c +++ /dev/null @@ -1,1757 +0,0 @@ -/* - * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <signal.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/file.h> -#include <sys/inotify.h> -#include <sys/ioctl.h> -#include <sys/mount.h> -#include <sys/prctl.h> -#include <sys/signalfd.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "cgroup-util.h" -#include "cpu-set-util.h" -#include "dev-setup.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "fs-util.h" -#include "hashmap.h" -#include "io-util.h" -#include "netlink-util.h" -#include "parse-util.h" -#include "proc-cmdline.h" -#include "process-util.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "socket-util.h" -#include "string-util.h" -#include "terminal-util.h" -#include "udev-util.h" -#include "udev.h" -#include "user-util.h" - -static bool arg_debug = false; -static int arg_daemonize = false; -static int arg_resolve_names = 1; -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; - -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, - EVENT_QUEUED, - EVENT_RUNNING, -}; - -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; - unsigned long long int delaying_seqnum; - unsigned long long int seqnum; - const char *devpath; - size_t devpath_len; - const char *devpath_old; - 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(Manager *manager, enum event_state type); - -enum worker_state { - WORKER_UNDEF, - WORKER_RUNNING, - WORKER_IDLE, - WORKER_KILLED, -}; - -struct worker { - Manager *manager; - struct udev_list_node node; - int refcount; - pid_t pid; - struct udev_monitor *monitor; - enum worker_state state; - struct event *event; -}; - -/* passed from worker to main process */ -struct worker_message { -}; - -static void event_free(struct event *event) { - int r; - - if (!event) - return; - - 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 void worker_free(struct worker *worker) { - if (!worker) - return; - - assert(worker->manager); - - hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid)); - udev_monitor_unref(worker->monitor); - event_free(worker->event); - - free(worker); -} - -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, PID_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; - - 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; - - assert_se(sd_event_now(e, clock_boottime_or_monotonic(), &usec) >= 0); - - (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 manager_free(Manager *manager) { - if (!manager) - return; - - udev_builtin_exit(manager->udev); - - 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); -} - -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; - _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL; - pid_t pid; - int r = 0; - - /* listen for new events */ - worker_monitor = udev_monitor_new_from_netlink(udev, NULL); - if (worker_monitor == NULL) - return; - /* allow the main daemon netlink address to send devices to the worker */ - udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor); - r = udev_monitor_enable_receiving(worker_monitor); - if (r < 0) - log_error_errno(r, "worker: could not enable receiving of device: %m"); - - pid = fork(); - switch (pid) { - case 0: { - struct udev_device *dev = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - 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; - - /* take initial device from queue */ - dev = event->dev; - event->dev = NULL; - - 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->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) { - 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) { - r = log_error_errno(errno, "error creating epoll fd: %m"); - goto out; - } - - 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) { - r = log_error_errno(errno, "fail to add fds to epoll: %m"); - goto out; - } - - /* Request TERM signal if parent exits. - Ignore error, not much we can do in that case. */ - (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - - /* Reset OOM score, we only protect the main daemon. */ - write_string_file("/proc/self/oom_score_adj", "0", 0); - - for (;;) { - struct udev_event *udev_event; - int fd_lock = -1; - - assert(dev); - - log_debug("seq %llu running", udev_device_get_seqnum(dev)); - udev_event = udev_event_new(dev); - if (udev_event == NULL) { - r = -ENOMEM; - goto out; - } - - if (arg_exec_delay > 0) - udev_event->exec_delay = arg_exec_delay; - - /* - * Take a shared lock on the device node; this establishes - * a concept of device "ownership" to serialize device - * access. External processes holding an exclusive lock will - * cause udev to skip the event handling; in the case udev - * acquired the lock, the external process can block until - * udev has finished its event handling. - */ - if (!streq_ptr(udev_device_get_action(dev), "remove") && - streq_ptr("block", udev_device_get_subsystem(dev)) && - !startswith(udev_device_get_sysname(dev), "dm-") && - !startswith(udev_device_get_sysname(dev), "md")) { - struct udev_device *d = dev; - - if (streq_ptr("partition", udev_device_get_devtype(d))) - d = udev_device_get_parent(d); - - if (d) { - 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)); - fd_lock = safe_close(fd_lock); - goto skip; - } - } - } - - /* needed for renaming netifs */ - udev_event->rtnl = rtnl; - - /* apply rules, create node, symlinks */ - udev_event_execute_rules(udev_event, - arg_event_timeout_usec, arg_event_timeout_warn_usec, - &manager->properties, - manager->rules); - - udev_event_execute_run(udev_event, - arg_event_timeout_usec, arg_event_timeout_warn_usec); - - if (udev_event->rtnl) - /* in case rtnl was initialized */ - rtnl = sd_netlink_ref(udev_event->rtnl); - - /* apply/restore inotify watch */ - if (udev_event->inotify_watch) { - udev_watch_begin(udev, dev); - udev_device_update_db(dev); - } - - safe_close(fd_lock); - - /* send processed event back to libudev listeners */ - udev_monitor_send_device(worker_monitor, NULL, dev); - -skip: - log_debug("seq %llu processed", udev_device_get_seqnum(dev)); - - /* 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; - - udev_event_unref(udev_event); - - /* wait for more device messages from main udevd, or term signal */ - while (dev == NULL) { - struct epoll_event ev[4]; - int fdcount; - int i; - - fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); - if (fdcount < 0) { - if (errno == EINTR) - continue; - r = log_error_errno(errno, "failed to poll: %m"); - goto out; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { - dev = udev_monitor_receive_device(worker_monitor); - break; - } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { - struct signalfd_siginfo fdsi; - ssize_t size; - - size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; - switch (fdsi.ssi_signo) { - case SIGTERM: - goto out; - } - } - } - } - } -out: - udev_device_unref(dev); - manager_free(manager); - log_close(); - _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); - } - case -1: - event->state = EVENT_QUEUED; - log_error_errno(errno, "fork of child failed: %m"); - break; - default: - { - struct worker *worker; - - 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(Manager *manager, struct event *event) { - struct worker *worker; - Iterator i; - - assert(manager); - assert(event); - - HASHMAP_FOREACH(worker, manager->workers, i) { - ssize_t count; - - if (worker->state != WORKER_IDLE) - continue; - - 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); - kill(worker->pid, SIGKILL); - worker->state = WORKER_KILLED; - continue; - } - worker_attach_event(worker, event); - return; - } - - if (hashmap_size(manager->workers) >= arg_children_max) { - if (arg_children_max > 1) - log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers)); - return; - } - - /* start new worker and pass initial device */ - worker_spawn(manager, event); -} - -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) - 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); - event->devpath_old = udev_device_get_devpath_old(dev); - event->devnum = udev_device_get_devnum(dev); - event->is_block = streq("block", udev_device_get_subsystem(dev)); - event->ifindex = udev_device_get_ifindex(dev); - - log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev), - udev_device_get_action(dev), udev_device_get_subsystem(dev)); - - event->state = EVENT_QUEUED; - - 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 manager_kill_workers(Manager *manager) { - struct worker *worker; - Iterator i; - - assert(manager); - - HASHMAP_FOREACH(worker, manager->workers, i) { - if (worker->state == WORKER_KILLED) - continue; - - worker->state = WORKER_KILLED; - kill(worker->pid, SIGTERM); - } -} - -/* lookup event for identical, parent, child device */ -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, &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 */ - if (loop_event->seqnum < event->delaying_seqnum) - continue; - - /* event we checked earlier still exists, no need to check again */ - if (loop_event->seqnum == event->delaying_seqnum) - return true; - - /* found ourself, no later event can block us */ - if (loop_event->seqnum >= event->seqnum) - break; - - /* check major/minor */ - if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) - return true; - - /* check network device ifindex */ - if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) - return true; - - /* check our old name */ - if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* compare devpath */ - common = MIN(loop_event->devpath_len, event->devpath_len); - - /* one devpath is contained in the other? */ - if (memcmp(loop_event->devpath, event->devpath, common) != 0) - continue; - - /* identical device event found */ - if (loop_event->devpath_len == event->devpath_len) { - /* devices names might have changed/swapped in the meantime */ - if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) - continue; - if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) - continue; - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* parent device event found */ - if (event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* child device event found */ - if (loop_event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* no matching device */ - continue; - } - - return false; -} - -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_event = sd_event_source_unref(manager->ctrl_event); - manager->ctrl = udev_ctrl_unref(manager->ctrl); - - manager->inotify_event = sd_event_source_unref(manager->inotify_event); - manager->fd_inotify = safe_close(manager->fd_inotify); - - manager->uevent_event = sd_event_source_unref(manager->uevent_event); - manager->monitor = udev_monitor_unref(manager->monitor); - - /* discard queued events and kill workers */ - event_queue_cleanup(manager, EVENT_QUEUED); - manager_kill_workers(manager); - - assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); - - 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_notifyf(false, - "READY=1\n" - "STATUS=Processing with %u children at max", arg_children_max); -} - -static void event_queue_start(Manager *manager) { - struct udev_list_node *loop; - usec_t usec; - - assert(manager); - - if (udev_list_node_is_empty(&manager->events) || - manager->exit || manager->stop_exec_queue) - return; - - assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 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(manager, event)) - continue; - - event_run(manager, event); - } -} - -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, &manager->events) { - struct event *event = node_to_event(loop); - - if (match_type != EVENT_UNDEF && match_type != event->state) - continue; - - event_free(event); - } -} - -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 ucred *ucred = NULL; - struct worker *worker; - - size = recvmsg(fd, &msghdr, MSG_DONTWAIT); - if (size < 0) { - if (errno == EINTR) - continue; - else if (errno == EAGAIN) - /* nothing more to read */ - break; - - 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; - } - - CMSG_FOREACH(cmsg, &msghdr) { - 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); - } - - 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, PID_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 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; - - 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) - 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); - manager_kill_workers(manager); - } - - if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { - log_debug("udevd message (STOP_EXEC_QUEUE) received"); - manager->stop_exec_queue = true; - } - - if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { - log_debug("udevd message (START_EXEC_QUEUE) received"); - manager->stop_exec_queue = false; - event_queue_start(manager); - } - - if (udev_ctrl_get_reload(ctrl_msg) > 0) { - log_debug("udevd message (RELOAD) received"); - manager_reload(manager); - } - - str = udev_ctrl_get_set_env(ctrl_msg); - if (str != NULL) { - _cleanup_free_ char *key = NULL; - - key = strdup(str); - if (key) { - char *val; - - val = strchr(key, '='); - if (val != NULL) { - val[0] = '\0'; - val = &val[1]; - if (val[0] == '\0') { - log_debug("udevd message (ENV) received, unset '%s'", key); - udev_list_entry_add(&manager->properties, key, NULL); - } else { - log_debug("udevd message (ENV) received, set '%s=%s'", key, val); - udev_list_entry_add(&manager->properties, key, val); - } - } else - log_error("wrong key format '%s'", key); - } - manager_kill_workers(manager); - } - - i = udev_ctrl_get_set_children_max(ctrl_msg); - if (i >= 0) { - log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i); - arg_children_max = i; - - (void) sd_notifyf(false, - "READY=1\n" - "STATUS=Processing with %u children at max", arg_children_max); - } - - if (udev_ctrl_get_ping(ctrl_msg) > 0) - log_debug("udevd message (SYNC) received"); - - if (udev_ctrl_get_exit(ctrl_msg) > 0) { - log_debug("udevd message (EXIT) received"); - 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); - } - - return 1; -} - -static int synthesize_change(struct udev_device *dev) { - char filename[UTIL_PATH_SIZE]; - int r; - - if (streq_ptr("block", udev_device_get_subsystem(dev)) && - streq_ptr("disk", udev_device_get_devtype(dev)) && - !startswith(udev_device_get_sysname(dev), "dm-")) { - bool part_table_read = false; - bool has_partitions = false; - int fd; - struct udev *udev = udev_device_get_udev(dev); - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item; - - /* - * Try to re-read the partition table. This only succeeds if - * none of the devices is busy. The kernel returns 0 if no - * partition table is found, and we will not get an event for - * the disk. - */ - fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); - if (fd >= 0) { - r = flock(fd, LOCK_EX|LOCK_NB); - if (r >= 0) - r = ioctl(fd, BLKRRPART, 0); - - close(fd); - if (r >= 0) - part_table_read = true; - } - - /* search for partitions */ - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_parent(e, dev); - if (r < 0) - return r; - - r = udev_enumerate_add_match_subsystem(e, "block"); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - continue; - - if (!streq_ptr("partition", udev_device_get_devtype(d))) - continue; - - has_partitions = true; - break; - } - - /* - * We have partitions and re-read the table, the kernel already sent - * out a "change" event for the disk, and "remove/add" for all - * partitions. - */ - if (part_table_read && has_partitions) - return 0; - - /* - * We have partitions but re-reading the partition table did not - * work, synthesize "change" for the disk and all partitions. - */ - log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); - - udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - continue; - - if (!streq_ptr("partition", udev_device_get_devtype(d))) - continue; - - log_debug("device %s closed, synthesising partition '%s' 'change'", - udev_device_get_devnode(dev), udev_device_get_devnode(d)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL); - write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); - } - - return 0; - } - - log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); - - return 0; -} - -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; - - assert(manager); - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 1; - - return log_error_errno(errno, "Failed to read inotify fd: %m"); - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - - 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) { - synthesize_change(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 1; -} - -static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *manager = userdata; - - assert(manager); - - manager_exit(manager); - - return 1; -} - -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, PID_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); - } - } - - 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, CGROUP_IGNORE_SELF, NULL, NULL, NULL); - } - } - - return 1; -} - -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 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_fd >= 0) - return -EINVAL; - ctrl_fd = fd; - continue; - } - - if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { - if (netlink_fd >= 0) - return -EINVAL; - netlink_fd = fd; - continue; - } - - return -EINVAL; - } - - 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; - - 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.event-timeout=<number of seconds> seconds to wait before terminating an event - */ -static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - int r = 0; - - assert(key); - - if (!value) - return 0; - - if (streq(key, "udev.log-priority") && value) { - r = util_log_priority(value); - if (r >= 0) - log_set_max_level(r); - } else if (streq(key, "udev.event-timeout") && value) { - r = safe_atou64(value, &arg_event_timeout_usec); - if (r >= 0) { - arg_event_timeout_usec *= USEC_PER_SEC; - arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; - } - } else if (streq(key, "udev.children-max") && value) - r = safe_atou(value, &arg_children_max); - else if (streq(key, "udev.exec-delay") && value) - r = safe_atoi(value, &arg_exec_delay); - else if (startswith(key, "udev.")) - log_warning("Unknown udev kernel command line option \"%s\"", key); - - if (r < 0) - log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Manages devices.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n" - " --daemon Detach and run in the background\n" - " --debug Enable debug output\n" - " --children-max=INT Set maximum number of workers\n" - " --exec-delay=SECONDS Seconds to wait before executing RUN=\n" - " --event-timeout=SECONDS Seconds to wait before terminating an event\n" - " --resolve-names=early|late|never\n" - " When to resolve users and groups\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "event-timeout", required_argument, NULL, 't' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) { - int r; - - switch (c) { - - case 'd': - arg_daemonize = true; - break; - case 'c': - r = safe_atou(optarg, &arg_children_max); - if (r < 0) - log_warning("Invalid --children-max ignored: %s", optarg); - break; - case 'e': - r = safe_atoi(optarg, &arg_exec_delay); - if (r < 0) - log_warning("Invalid --exec-delay ignored: %s", optarg); - break; - case 't': - r = safe_atou64(optarg, &arg_event_timeout_usec); - if (r < 0) - log_warning("Invalid --event-timeout ignored: %s", optarg); - else { - arg_event_timeout_usec *= USEC_PER_SEC; - arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; - } - break; - case 'D': - arg_debug = true; - break; - case 'N': - if (streq(optarg, "early")) { - arg_resolve_names = 1; - } else if (streq(optarg, "late")) { - arg_resolve_names = 0; - } else if (streq(optarg, "never")) { - arg_resolve_names = -1; - } else { - log_error("resolve-names must be early, late or never"); - return 0; - } - break; - case 'h': - help(); - return 0; - case 'V': - printf("%s\n", VERSION); - return 0; - case '?': - return -EINVAL; - default: - assert_not_reached("Unhandled option"); - - } - } - - 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, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0); - - r = sd_event_default(&manager->event); - if (r < 0) - return log_error_errno(r, "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; -} - -static int run(int fd_ctrl, int fd_uevent, const char *cgroup) { - _cleanup_(manager_freep) Manager *manager = NULL; - int r; - - 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; - } - - 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"); - - (void) sd_notifyf(false, - "READY=1\n" - "STATUS=Processing with %u children at max", arg_children_max); - - r = sd_event_loop(manager->event); - if (r < 0) { - log_error_errno(r, "event loop failed: %m"); - goto exit; - } - - sd_event_get_exit_code(manager->event, &r); - -exit: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - if (manager) - udev_ctrl_cleanup(manager->ctrl); - return r; -} - -int main(int argc, char *argv[]) { - _cleanup_free_ char *cgroup = NULL; - int fd_ctrl = -1, fd_uevent = -1; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto exit; - - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); - if (r < 0) - log_warning_errno(r, "failed to parse kernel command line, ignoring: %m"); - - if (arg_debug) { - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - } - - if (getuid() != 0) { - r = log_error_errno(EPERM, "root privileges required"); - 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) { - r = log_error_errno(errno, "could not change dir to /: %m"); - goto exit; - } - - umask(022); - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "could not initialize labelling: %m"); - goto exit; - } - - r = mkdir("/run/udev", 0755); - if (r < 0 && errno != EEXIST) { - r = log_error_errno(errno, "could not create /run/udev: %m"); - goto exit; - } - - dev_setup(NULL, UID_INVALID, GID_INVALID); - - 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) { - if (r == -ENOENT || r == -ENOMEDIUM) - log_debug_errno(r, "did not find dedicated cgroup: %m"); - else - log_warning_errno(r, "failed to get cgroup: %m"); - } - } - - r = listen_fds(&fd_ctrl, &fd_uevent); - if (r < 0) { - r = log_error_errno(r, "could not listen on fds: %m"); - goto exit; - } - - if (arg_daemonize) { - pid_t pid; - - log_info("starting version " VERSION); - - /* connect /dev/null to stdin, stdout, stderr */ - if (log_get_max_level() < LOG_DEBUG) { - r = make_null_stdio(); - if (r < 0) - log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m"); - } - - - - pid = fork(); - switch (pid) { - case 0: - break; - case -1: - r = log_error_errno(errno, "fork of daemon failed: %m"); - goto exit; - default: - mac_selinux_finish(); - log_close(); - _exit(EXIT_SUCCESS); - } - - setsid(); - - write_string_file("/proc/self/oom_score_adj", "-1000", 0); - } - - r = run(fd_ctrl, fd_uevent, cgroup); - -exit: - mac_selinux_finish(); - log_close(); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/udev/v4l_id/Makefile b/src/udev/v4l_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/v4l_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c deleted file mode 100644 index aec6676a33..0000000000 --- a/src/udev/v4l_id/v4l_id.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2009 Kay Sievers <kay@vrfy.org> - * Copyright (c) 2009 Filippo Argiolas <filippo.argiolas@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details: - */ - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> -#include <linux/videodev2.h> - -#include "fd-util.h" -#include "util.h" - -int main(int argc, char *argv[]) { - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - _cleanup_close_ int fd = -1; - char *device; - struct v4l2_capability v2cap; - int c; - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - 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: - assert_not_reached("Unhandled option"); - } - - device = argv[optind]; - if (device == NULL) - return 2; - - fd = open(device, O_RDONLY); - if (fd < 0) - return 3; - - if (ioctl(fd, VIDIOC_QUERYCAP, &v2cap) == 0) { - printf("ID_V4L_VERSION=2\n"); - printf("ID_V4L_PRODUCT=%s\n", v2cap.card); - printf("ID_V4L_CAPABILITIES=:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0) - printf("capture:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0) - printf("video_output:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) - printf("video_overlay:"); - if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) - printf("audio:"); - if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) - printf("tuner:"); - if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) - printf("radio:"); - printf("\n"); - } - - return 0; -} |