summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/accelerometer/accelerometer.c25
-rw-r--r--src/ata_id/ata_id.c140
-rw-r--r--src/cdrom_id/cdrom_id.c33
-rw-r--r--src/collect/collect.c24
-rw-r--r--src/libudev/libudev-device-private.c9
-rw-r--r--src/libudev/libudev-device.c30
-rw-r--r--src/libudev/libudev-hwdb.c21
-rw-r--r--src/libudev/libudev-monitor.c56
-rw-r--r--src/libudev/libudev-private.h17
-rw-r--r--src/libudev/libudev-util.c14
-rw-r--r--src/libudev/libudev.c117
-rw-r--r--src/libudev/libudev.h6
-rw-r--r--src/scsi_id/scsi_id.c41
-rw-r--r--src/scsi_id/scsi_serial.c8
-rw-r--r--src/shared/Makefile.am2
-rw-r--r--src/shared/cgroup-util.c2
-rw-r--r--src/shared/hashmap.c1795
-rw-r--r--src/shared/hashmap.h385
-rw-r--r--src/shared/list.h138
-rw-r--r--src/shared/log.c136
-rw-r--r--src/shared/log.h54
-rw-r--r--src/shared/macro.h5
-rw-r--r--src/shared/mempool.c9
-rw-r--r--src/shared/mempool.h1
-rw-r--r--src/shared/missing.h33
-rw-r--r--src/shared/mkdir.c4
-rw-r--r--src/shared/path-util.c53
-rw-r--r--src/shared/selinux-util.c16
-rw-r--r--src/shared/selinux-util.h2
-rw-r--r--src/shared/set.c56
-rw-r--r--src/shared/set.h121
-rw-r--r--src/shared/smack-util.c30
-rw-r--r--src/shared/smack-util.h1
-rw-r--r--src/shared/sparse-endian.h2
-rw-r--r--src/shared/strv.c7
-rw-r--r--src/shared/strv.h2
-rw-r--r--src/shared/utf8.c106
-rw-r--r--src/shared/utf8.h13
-rw-r--r--src/shared/util.c528
-rw-r--r--src/shared/util.h71
-rw-r--r--src/udev/Makefile.am5
-rw-r--r--src/udev/udev-builtin-blkid.c15
-rw-r--r--src/udev/udev-builtin-btrfs.c5
-rw-r--r--src/udev/udev-builtin-hwdb.c2
-rw-r--r--src/udev/udev-builtin-input_id.c73
-rw-r--r--src/udev/udev-builtin-keyboard.c8
-rw-r--r--src/udev/udev-builtin-kmod.c26
-rw-r--r--src/udev/udev-builtin-net_id.c4
-rw-r--r--src/udev/udev-builtin-path_id.c110
-rw-r--r--src/udev/udev-builtin-usb_id.c2
-rw-r--r--src/udev/udev-builtin.c2
-rw-r--r--src/udev/udev-ctrl.c18
-rw-r--r--src/udev/udev-event.c64
-rw-r--r--src/udev/udev-node.c16
-rw-r--r--src/udev/udev-rules.c59
-rw-r--r--src/udev/udev-watch.c10
-rw-r--r--src/udev/udev.h16
-rw-r--r--src/udev/udevadm-control.c25
-rw-r--r--src/udev/udevadm-hwdb.c8
-rw-r--r--src/udev/udevadm-info.c78
-rw-r--r--src/udev/udevadm-monitor.c25
-rw-r--r--src/udev/udevadm-settle.c32
-rw-r--r--src/udev/udevadm-test-builtin.c12
-rw-r--r--src/udev/udevadm-test.c25
-rw-r--r--src/udev/udevadm-trigger.c115
-rw-r--r--src/udev/udevadm-util.c49
-rw-r--r--src/udev/udevadm-util.h22
-rw-r--r--src/udev/udevadm.c17
-rw-r--r--src/udev/udevd.c146
-rw-r--r--src/v4l_id/Makefile.am3
-rw-r--r--src/v4l_id/v4l_id.c23
71 files changed, 3782 insertions, 1346 deletions
diff --git a/src/accelerometer/accelerometer.c b/src/accelerometer/accelerometer.c
index 71c5b0c199..862224b7aa 100644
--- a/src/accelerometer/accelerometer.c
+++ b/src/accelerometer/accelerometer.c
@@ -73,14 +73,6 @@
#define LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
-_printf_(6,0)
-static void log_fn(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- log_metav(priority, file, line, fn, format, args);
-}
-
typedef enum {
ORIENTATION_UNDEFINED,
ORIENTATION_NORMAL,
@@ -207,11 +199,13 @@ static void test_orientation(struct udev *udev,
puts(text);
}
-static void help(void)
-{
- printf("Usage: accelerometer [options] <device path>\n"
- " --debug debug to stderr\n"
- " --help print this help text\n\n");
+static void help(void) {
+
+ printf("%s [options] <device path>\n\n"
+ "Accelerometer device identification.\n\n"
+ " -h --help Print this message\n"
+ " -d --debug Debug to stderr\n"
+ , program_invocation_short_name);
}
int main (int argc, char** argv)
@@ -236,13 +230,11 @@ int main (int argc, char** argv)
if (udev == NULL)
return 1;
- udev_set_log_fn(udev, log_fn);
-
/* CLI argument parsing */
while (1) {
int option;
- option = getopt_long(argc, argv, "dxh", options, NULL);
+ option = getopt_long(argc, argv, "dh", options, NULL);
if (option == -1)
break;
@@ -250,7 +242,6 @@ int main (int argc, char** argv)
case 'd':
log_set_target(LOG_TARGET_CONSOLE);
log_set_max_level(LOG_DEBUG);
- udev_set_log_priority(udev, LOG_DEBUG);
log_open();
break;
case 'h':
diff --git a/src/ata_id/ata_id.c b/src/ata_id/ata_id.c
index 8ead84cf27..175e619a70 100644
--- a/src/ata_id/ata_id.c
+++ b/src/ata_id/ata_id.c
@@ -47,6 +47,7 @@
#include "libudev.h"
#include "libudev-private.h"
+#include "udev-util.h"
#include "log.h"
#define COMMAND_TIMEOUT_MSEC (30 * 1000)
@@ -409,29 +410,23 @@ out:
return ret;
}
-_printf_(6,0)
-static void log_fn(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- log_metav(priority, file, line, fn, format, args);
-}
-
int main(int argc, char *argv[])
{
- struct udev *udev;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
struct hd_driveid id;
- uint8_t identify[512];
- uint16_t *identify_words;
+ union {
+ uint8_t byte[512];
+ uint16_t wyde[256];
+ uint64_t octa[64];
+ } identify;
char model[41];
char model_enc[256];
char serial[21];
char revision[9];
const char *node = NULL;
int export = 0;
- int fd;
+ _cleanup_close_ int fd = -1;
uint16_t word;
- int rc = 0;
int is_packet_device = 0;
static const struct option options[] = {
{ "export", no_argument, NULL, 'x' },
@@ -443,9 +438,7 @@ int main(int argc, char *argv[])
udev = udev_new();
if (udev == NULL)
- goto exit;
-
- udev_set_log_fn(udev, log_fn);
+ return 0;
while (1) {
int option;
@@ -460,59 +453,55 @@ int main(int argc, char *argv[])
break;
case 'h':
printf("Usage: ata_id [--export] [--help] <device>\n"
- " --export print values as environment keys\n"
- " --help print this help text\n\n");
- goto exit;
+ " -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");
- rc = 1;
- goto exit;
+ return 1;
}
fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
if (fd < 0) {
log_error("unable to open '%s'", node);
- rc = 1;
- goto exit;
+ return 1;
}
- if (disk_identify(udev, fd, identify, &is_packet_device) == 0) {
+ 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, 10, 20); /* serial */
- disk_identify_fixup_string(identify, 23, 8); /* fwrev */
- disk_identify_fixup_string(identify, 27, 40); /* model */
- disk_identify_fixup_uint16(identify, 0); /* configuration */
- disk_identify_fixup_uint16(identify, 75); /* queue depth */
- disk_identify_fixup_uint16(identify, 75); /* SATA capabilities */
- disk_identify_fixup_uint16(identify, 82); /* command set supported */
- disk_identify_fixup_uint16(identify, 83); /* command set supported */
- disk_identify_fixup_uint16(identify, 84); /* command set supported */
- disk_identify_fixup_uint16(identify, 85); /* command set supported */
- disk_identify_fixup_uint16(identify, 86); /* command set supported */
- disk_identify_fixup_uint16(identify, 87); /* command set supported */
- disk_identify_fixup_uint16(identify, 89); /* time required for SECURITY ERASE UNIT */
- disk_identify_fixup_uint16(identify, 90); /* time required for enhanced SECURITY ERASE UNIT */
- disk_identify_fixup_uint16(identify, 91); /* current APM values */
- disk_identify_fixup_uint16(identify, 94); /* current AAM value */
- disk_identify_fixup_uint16(identify, 128); /* device lock function */
- disk_identify_fixup_uint16(identify, 217); /* nominal media rotation rate */
- memcpy(&id, identify, sizeof id);
+ 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, 75); /* 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, 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("HDIO_GET_IDENTITY failed for '%s': %m", node);
- rc = 2;
- goto close;
+ log_debug_errno(errno, "HDIO_GET_IDENTITY failed for '%s': %m", node);
+ return 2;
}
}
- identify_words = (uint16_t *) identify;
memcpy (model, id.model, 40);
model[40] = '\0';
@@ -562,8 +551,8 @@ int main(int argc, char *argv[])
}
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);
+ 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");
@@ -626,7 +615,8 @@ int main(int argc, char *argv[])
* the device does not claim compliance with the Serial ATA specification and words
* 76 through 79 are not valid and shall be ignored.
*/
- word = *((uint16_t *) identify + 76);
+
+ word = identify.wyde[76];
if (word != 0x0000 && word != 0xffff) {
printf("ID_ATA_SATA=1\n");
/*
@@ -643,54 +633,34 @@ int main(int argc, char *argv[])
}
/* Word 217 indicates the nominal media rotation rate of the device */
- word = *((uint16_t *) identify + 217);
- if (word != 0x0000) {
- 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);
- }
- }
+ 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 = *((uint16_t *) identify + 108);
- if ((word & 0xf000) == 0x5000) {
- uint64_t wwwn;
-
- wwwn = *((uint16_t *) identify + 108);
- wwwn <<= 16;
- wwwn |= *((uint16_t *) identify + 109);
- wwwn <<= 16;
- wwwn |= *((uint16_t *) identify + 110);
- wwwn <<= 16;
- wwwn |= *((uint16_t *) identify + 111);
- printf("ID_WWN=0x%llx\n", (unsigned long long int) wwwn);
- /* ATA devices have no vendor extension */
- printf("ID_WWN_WITH_EXTENSION=0x%llx\n", (unsigned long long int) wwwn);
- }
+ word = identify.wyde[108];
+ if ((word & 0xf000) == 0x5000)
+ printf("ID_WWN=0x%1$"PRIu64"x\n"
+ "ID_WWN_WITH_EXTENSION=0x%1$"PRIu64"x\n",
+ identify.octa[108/4]);
/* from Linux's include/linux/ata.h */
- if (identify_words[0] == 0x848a || identify_words[0] == 0x844a) {
+ if (identify.wyde[0] == 0x848a ||
+ identify.wyde[0] == 0x844a ||
+ (identify.wyde[83] & 0xc004) == 0x4004)
printf("ID_ATA_CFA=1\n");
- } else {
- if ((identify_words[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);
}
-close:
- close(fd);
-exit:
- udev_unref(udev);
- log_close();
- return rc;
+
+ return 0;
}
diff --git a/src/cdrom_id/cdrom_id.c b/src/cdrom_id/cdrom_id.c
index 9b3ab7021e..7bafd62809 100644
--- a/src/cdrom_id/cdrom_id.c
+++ b/src/cdrom_id/cdrom_id.c
@@ -41,14 +41,6 @@
#include "libudev.h"
#include "libudev-private.h"
-_printf_(6,0)
-static void log_fn(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- log_metav(priority, file, line, fn, format, args);
-}
-
/* device info */
static unsigned int cd_cd_rom;
static unsigned int cd_cd_r;
@@ -878,8 +870,6 @@ int main(int argc, char *argv[])
if (udev == NULL)
goto exit;
- udev_set_log_fn(udev, log_fn);
-
while (1) {
int option;
@@ -900,16 +890,15 @@ int main(int argc, char *argv[])
case 'd':
log_set_target(LOG_TARGET_CONSOLE);
log_set_max_level(LOG_DEBUG);
- udev_set_log_priority(udev, LOG_DEBUG);
log_open();
break;
case 'h':
printf("Usage: cdrom_id [options] <device>\n"
- " --lock-media lock the media (to enable eject request events)\n"
- " --unlock-media unlock the media\n"
- " --eject-media eject the media\n"
- " --debug debug to stderr\n"
- " --help print this help text\n\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;
@@ -925,7 +914,7 @@ int main(int argc, char *argv[])
goto exit;
}
- srand((unsigned int)getpid());
+ initialize_srand();
for (cnt = 20; cnt > 0; cnt--) {
struct timespec duration;
@@ -1077,17 +1066,17 @@ work:
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=%d\n", cd_media_session_next);
+ printf("ID_CDROM_MEDIA_SESSION_NEXT=%u\n", cd_media_session_next);
if (cd_media_session_count > 0)
- printf("ID_CDROM_MEDIA_SESSION_COUNT=%d\n", cd_media_session_count);
+ 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=%d\n", cd_media_track_count);
+ 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=%d\n", cd_media_track_count_audio);
+ 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=%d\n", cd_media_track_count_data);
+ printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%u\n", cd_media_track_count_data);
exit:
if (fd >= 0)
close(fd);
diff --git a/src/collect/collect.c b/src/collect/collect.c
index 323d56a82d..f309f3b98e 100644
--- a/src/collect/collect.c
+++ b/src/collect/collect.c
@@ -72,15 +72,19 @@ noreturn static void sig_alrm(int signo)
static void usage(void)
{
- printf("usage: collect [--add|--remove] [--debug] <checkpoint> <id> <idlist>\n"
- "\n"
+ 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");
+ " On error a negative number is returned.\n\n"
+ , program_invocation_short_name);
}
/*
@@ -90,12 +94,12 @@ static void usage(void)
*/
static int prepare(char *dir, char *filename)
{
- struct stat statbuf;
char buf[512];
- int fd;
+ int r, fd;
- if (stat(dir, &statbuf) < 0)
- mkdir(dir, 0700);
+ r = mkdir(dir, 0700);
+ if (r < 0 && errno != EEXIST)
+ return -errno;
snprintf(buf, sizeof(buf), "%s/%s", dir, filename);
@@ -157,7 +161,7 @@ static int checkout(int fd)
if (!ptr && word < (buf + len)) {
bufsize = bufsize << 1;
if (debug)
- fprintf(stderr, "ID overflow, restarting with size %zi\n", bufsize);
+ fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize);
free(buf);
lseek(fd, 0, SEEK_SET);
goto restart;
@@ -258,7 +262,7 @@ static void reject(char *us)
* kickout
*
* Remove all IDs in the internal list which are not part
- * of the list passed via the commandline.
+ * of the list passed via the command line.
*/
static void kickout(void)
{
diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c
index 637d064819..fb4c6e2940 100644
--- a/src/libudev/libudev-device-private.c
+++ b/src/libudev/libudev-device-private.c
@@ -102,7 +102,6 @@ static bool device_has_info(struct udev_device *udev_device)
int udev_device_update_db(struct udev_device *udev_device)
{
- struct udev *udev = udev_device_get_udev(udev_device);
bool has_info;
const char *id;
char filename[UTIL_PATH_SIZE];
@@ -129,10 +128,8 @@ int udev_device_update_db(struct udev_device *udev_device)
strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL);
mkdir_parents(filename_tmp, 0755);
f = fopen(filename_tmp, "we");
- if (f == NULL) {
- udev_err(udev, "unable to create temporary db file '%s': %m\n", filename_tmp);
- return -1;
- }
+ if (f == NULL)
+ return log_debug_errno(errno, "unable to create temporary db file '%s': %m", filename_tmp);
/*
* set 'sticky' bit to indicate that we should not clean the
@@ -172,7 +169,7 @@ int udev_device_update_db(struct udev_device *udev_device)
r = rename(filename_tmp, filename);
if (r < 0)
return -1;
- udev_dbg(udev, "created %s file '%s' for '%s'\n", has_info ? "db" : "empty",
+ log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty",
filename, udev_device_get_devpath(udev_device));
return 0;
}
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 2699374072..825a0e10f4 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -139,7 +139,7 @@ static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
char num[32];
udev_device->ifindex = ifindex;
- snprintf(num, sizeof(num), "%u", ifindex);
+ snprintf(num, sizeof(num), "%d", ifindex);
udev_device_add_property(udev_device, "IFINDEX", num);
return 0;
}
@@ -534,10 +534,8 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
}
f = fopen(dbfile, "re");
- if (f == NULL) {
- udev_dbg(udev_device->udev, "no db file to read %s: %m\n", dbfile);
- return -errno;
- }
+ if (f == NULL)
+ return log_debug_errno(errno, "no db file to read %s: %m", dbfile);
/* devices with a database entry are initialized */
udev_device->is_initialized = true;
@@ -577,7 +575,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
}
fclose(f);
- udev_dbg(udev_device->udev, "device %p filled with db file data\n", udev_device);
+ log_debug("device %p filled with db file data", udev_device);
return 0;
}
@@ -642,7 +640,6 @@ void udev_device_set_info_loaded(struct udev_device *device)
struct udev_device *udev_device_new(struct udev *udev)
{
struct udev_device *udev_device;
- struct udev_list_entry *list_entry;
if (udev == NULL) {
errno = EINVAL;
@@ -662,11 +659,7 @@ struct udev_device *udev_device_new(struct udev *udev)
udev_list_init(udev, &udev_device->sysattr_list, false);
udev_list_init(udev, &udev_device->tags_list, true);
udev_device->watch_handle = -1;
- /* copy global properties */
- udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev))
- udev_device_add_property(udev_device,
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
+
return udev_device;
}
@@ -704,7 +697,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
/* path starts in sys */
if (!startswith(syspath, "/sys")) {
- udev_dbg(udev, "not in sys :%s\n", syspath);
+ log_debug("not in sys :%s", syspath);
errno = EINVAL;
return NULL;
}
@@ -730,8 +723,13 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
return NULL;
} else {
/* everything else just needs to be a directory */
- if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
+ if (stat(path, &statbuf) != 0)
+ return NULL;
+
+ if (!S_ISDIR(statbuf.st_mode)) {
+ errno = EISDIR;
return NULL;
+ }
}
udev_device = udev_device_new(udev);
@@ -739,7 +737,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
return NULL;
udev_device_set_syspath(udev_device, path);
- udev_dbg(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device));
+ log_debug("device %p has devpath '%s'", udev_device, udev_device_get_devpath(udev_device));
return udev_device;
}
@@ -971,7 +969,7 @@ _public_ struct udev_device *udev_device_new_from_environment(struct udev *udev)
udev_device_add_property_from_string_parse(udev_device, environ[i]);
if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) {
- udev_dbg(udev, "missing values, invalid device\n");
+ log_debug("missing values, invalid device");
udev_device_unref(udev_device);
udev_device = NULL;
}
diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c
index 5582f71c68..d457604a07 100644
--- a/src/libudev/libudev-hwdb.c
+++ b/src/libudev/libudev-hwdb.c
@@ -277,38 +277,38 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
hwdb->f = fopen(UDEV_HWDB_BIN, "re");
if (!hwdb->f) {
- udev_dbg(udev, "error reading " UDEV_HWDB_BIN ": %m");
+ log_debug(UDEV_HWDB_BIN "does not exist, please run udevadm hwdb --update");
udev_hwdb_unref(hwdb);
return NULL;
}
if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
(size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
- udev_dbg(udev, "error reading " UDEV_HWDB_BIN ": %m");
+ log_debug_errno(errno, "error reading " UDEV_HWDB_BIN ": %m");
udev_hwdb_unref(hwdb);
return NULL;
}
hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
if (hwdb->map == MAP_FAILED) {
- udev_dbg(udev, "error mapping " UDEV_HWDB_BIN ": %m");
+ log_debug_errno(errno, "error mapping " UDEV_HWDB_BIN ": %m");
udev_hwdb_unref(hwdb);
return NULL;
}
if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
(size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
- udev_dbg(udev, "error recognizing the format of " UDEV_HWDB_BIN);
+ log_debug("error recognizing the format of " UDEV_HWDB_BIN);
udev_hwdb_unref(hwdb);
return NULL;
}
- udev_dbg(udev, "=== trie on-disk ===\n");
- udev_dbg(udev, "tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
- udev_dbg(udev, "file size: %8"PRIu64" bytes\n", hwdb->st.st_size);
- udev_dbg(udev, "header size %8"PRIu64" bytes\n", le64toh(hwdb->head->header_size));
- udev_dbg(udev, "strings %8"PRIu64" bytes\n", le64toh(hwdb->head->strings_len));
- udev_dbg(udev, "nodes %8"PRIu64" bytes\n", le64toh(hwdb->head->nodes_len));
+ log_debug("=== trie on-disk ===");
+ log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
+ log_debug("file size: %8"PRIu64" bytes", hwdb->st.st_size);
+ log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size));
+ log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len));
+ log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len));
return hwdb;
}
@@ -360,6 +360,7 @@ bool udev_hwdb_validate(struct udev_hwdb *hwdb) {
return false;
if (stat("/etc/udev/hwdb.bin", &st) < 0)
return true;
+
if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
return true;
return false;
diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c
index 137ea148d7..ae1b95b0b3 100644
--- a/src/libudev/libudev-monitor.c
+++ b/src/libudev/libudev-monitor.c
@@ -121,7 +121,7 @@ static bool udev_has_devtmpfs(struct udev *udev) {
r = name_to_handle_at(AT_FDCWD, "/dev", &h.handle, &mount_id, 0);
if (r < 0) {
if (errno != EOPNOTSUPP)
- udev_err(udev, "name_to_handle_at on /dev: %m\n");
+ log_debug_errno(errno, "name_to_handle_at on /dev: %m");
return false;
}
@@ -174,7 +174,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
* will not receive any messages.
*/
if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) {
- udev_dbg(udev, "the udev service seems not to be active, disable the monitor\n");
+ log_debug("the udev service seems not to be active, disable the monitor");
group = UDEV_MONITOR_NONE;
} else
group = UDEV_MONITOR_UDEV;
@@ -190,7 +190,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
if (fd < 0) {
udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
if (udev_monitor->sock == -1) {
- udev_err(udev, "error getting socket: %m\n");
+ log_debug_errno(errno, "error getting socket: %m");
free(udev_monitor);
return NULL;
}
@@ -407,14 +407,14 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
if (err == 0)
udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid;
} else {
- udev_err(udev_monitor->udev, "bind failed: %m\n");
+ log_debug_errno(errno, "bind failed: %m");
return -errno;
}
/* enable receiving of sender credentials */
err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
if (err < 0)
- udev_err(udev_monitor->udev, "setting SO_PASSCRED failed: %m\n");
+ log_debug_errno(errno, "setting SO_PASSCRED failed: %m");
return 0;
}
@@ -582,7 +582,10 @@ _public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *ud
struct cmsghdr *cmsg;
union sockaddr_union snl;
struct ucred *cred;
- char buf[8192];
+ union {
+ struct udev_monitor_netlink_header nlh;
+ char raw[8192];
+ } buf;
ssize_t buflen;
ssize_t bufpos;
@@ -602,12 +605,12 @@ retry:
buflen = recvmsg(udev_monitor->sock, &smsg, 0);
if (buflen < 0) {
if (errno != EINTR)
- udev_dbg(udev_monitor->udev, "unable to receive message\n");
+ log_debug("unable to receive message");
return NULL;
}
if (buflen < 32 || (size_t)buflen >= sizeof(buf)) {
- udev_dbg(udev_monitor->udev, "invalid message length\n");
+ log_debug("invalid message length");
return NULL;
}
@@ -615,12 +618,12 @@ retry:
/* unicast message, check if we trust the sender */
if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 ||
snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) {
- udev_dbg(udev_monitor->udev, "unicast netlink message ignored\n");
+ log_debug("unicast netlink message ignored");
return NULL;
}
} else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) {
if (snl.nl.nl_pid > 0) {
- udev_dbg(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n",
+ log_debug("multicast kernel netlink message from pid %d ignored",
snl.nl.nl_pid);
return NULL;
}
@@ -628,13 +631,13 @@ retry:
cmsg = CMSG_FIRSTHDR(&smsg);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
- udev_dbg(udev_monitor->udev, "no sender credentials received, message ignored\n");
+ log_debug("no sender credentials received, message ignored");
return NULL;
}
cred = (struct ucred *)CMSG_DATA(cmsg);
if (cred->uid != 0) {
- udev_dbg(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid);
+ log_debug("sender uid=%d, message ignored", cred->uid);
return NULL;
}
@@ -642,38 +645,35 @@ retry:
if (udev_device == NULL)
return NULL;
- if (memcmp(buf, "libudev", 8) == 0) {
- struct udev_monitor_netlink_header *nlh;
-
+ if (memcmp(buf.raw, "libudev", 8) == 0) {
/* udev message needs proper version magic */
- nlh = (struct udev_monitor_netlink_header *) buf;
- if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) {
- udev_err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n",
- nlh->magic, htonl(UDEV_MONITOR_MAGIC));
+ if (buf.nlh.magic != htonl(UDEV_MONITOR_MAGIC)) {
+ log_debug("unrecognized message signature (%x != %x)",
+ buf.nlh.magic, htonl(UDEV_MONITOR_MAGIC));
udev_device_unref(udev_device);
return NULL;
}
- if (nlh->properties_off+32 > (size_t)buflen) {
+ if (buf.nlh.properties_off+32 > (size_t)buflen) {
udev_device_unref(udev_device);
return NULL;
}
- bufpos = nlh->properties_off;
+ bufpos = buf.nlh.properties_off;
/* devices received from udev are always initialized */
udev_device_set_is_initialized(udev_device);
} else {
/* kernel message with header */
- bufpos = strlen(buf) + 1;
+ bufpos = strlen(buf.raw) + 1;
if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) {
- udev_dbg(udev_monitor->udev, "invalid message length\n");
+ log_debug("invalid message length");
udev_device_unref(udev_device);
return NULL;
}
/* check message header */
- if (strstr(buf, "@/") == NULL) {
- udev_dbg(udev_monitor->udev, "unrecognized message header\n");
+ if (strstr(buf.raw, "@/") == NULL) {
+ log_debug("unrecognized message header");
udev_device_unref(udev_device);
return NULL;
}
@@ -685,7 +685,7 @@ retry:
char *key;
size_t keylen;
- key = &buf[bufpos];
+ key = &buf.raw[bufpos];
keylen = strlen(key);
if (keylen == 0)
break;
@@ -694,7 +694,7 @@ retry:
}
if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) {
- udev_dbg(udev_monitor->udev, "missing values, invalid device\n");
+ log_debug("missing values, invalid device");
udev_device_unref(udev_device);
return NULL;
}
@@ -778,7 +778,7 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
smsg.msg_name = &udev_monitor->snl_destination;
smsg.msg_namelen = sizeof(struct sockaddr_nl);
count = sendmsg(udev_monitor->sock, &smsg, 0);
- udev_dbg(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor);
+ log_debug("passed %zi bytes to netlink monitor %p", count, udev_monitor);
return count;
}
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
index 09e786c320..64f132f91d 100644
--- a/src/libudev/libudev-private.h
+++ b/src/libudev/libudev-private.h
@@ -26,7 +26,6 @@
#include "libudev.h"
#include "macro.h"
-#include "time-util.h"
#include "util.h"
#include "mkdir.h"
#include "strxcpyx.h"
@@ -34,24 +33,8 @@
#define READ_END 0
#define WRITE_END 1
-/* avoid (sometimes expensive) calculations of parameters for debug output */
-#define udev_log_cond(udev, prio, arg...) \
- do { \
- if (udev_get_log_priority(udev) >= prio) \
- udev_log(udev, prio, __FILE__, __LINE__, __FUNCTION__, ## arg); \
- } while (0)
-
-#define udev_dbg(udev, arg...) udev_log_cond(udev, LOG_DEBUG, ## arg)
-#define udev_info(udev, arg...) udev_log_cond(udev, LOG_INFO, ## arg)
-#define udev_err(udev, arg...) udev_log_cond(udev, LOG_ERR, ## arg)
-
/* libudev.c */
-void udev_log(struct udev *udev,
- int priority, const char *file, int line, const char *fn,
- const char *format, ...) _printf_(6, 7);
int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]);
-struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value);
-struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev);
/* libudev-device.c */
struct udev_device *udev_device_new(struct udev *udev);
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index 00dc6e157e..e626aad53e 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -96,7 +96,7 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
strscpy(result, maxsize, val);
else
result[0] = '\0';
- udev_dbg(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
+ log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
} else {
size_t l;
char *s;
@@ -105,7 +105,7 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
if (attr != NULL)
strpcpyl(&s, l, "/", attr, NULL);
- udev_dbg(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
+ log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
}
udev_device_unref(dev);
return 0;
@@ -162,9 +162,13 @@ int util_log_priority(const char *priority)
char *endptr;
int prio;
- prio = strtol(priority, &endptr, 10);
- if (endptr[0] == '\0' || isspace(endptr[0]))
- return prio;
+ prio = strtoul(priority, &endptr, 10);
+ if (endptr[0] == '\0' || isspace(endptr[0])) {
+ if (prio >= 0 && prio <= 7)
+ return prio;
+ else
+ return -ERANGE;
+ }
return log_level_from_string(priority);
}
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index 4373ab4d54..e656b352c6 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -1,7 +1,7 @@
/***
This file is part of systemd.
- Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2008-2014 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
@@ -50,41 +50,18 @@ struct udev {
int priority, const char *file, int line, const char *fn,
const char *format, va_list args);
void *userdata;
- struct udev_list properties_list;
- int log_priority;
};
-void udev_log(struct udev *udev,
- int priority, const char *file, int line, const char *fn,
- const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
- udev->log_fn(udev, priority, file, line, fn, format, args);
- va_end(args);
-}
-
-_printf_(6,0)
-static void log_stderr(struct udev *udev,
- int priority, const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- fprintf(stderr, "libudev: %s: ", fn);
- vfprintf(stderr, format, args);
-}
-
/**
* udev_get_userdata:
* @udev: udev library context
*
* Retrieve stored data pointer from library context. This might be useful
- * to access from callbacks like a custom logging function.
+ * to access from callbacks.
*
* Returns: stored userdata
**/
-_public_ void *udev_get_userdata(struct udev *udev)
-{
+_public_ void *udev_get_userdata(struct udev *udev) {
if (udev == NULL)
return NULL;
return udev->userdata;
@@ -97,8 +74,7 @@ _public_ void *udev_get_userdata(struct udev *udev)
*
* Store custom @userdata in the library context.
**/
-_public_ void udev_set_userdata(struct udev *udev, void *userdata)
-{
+_public_ void udev_set_userdata(struct udev *udev, void *userdata) {
if (udev == NULL)
return;
udev->userdata = userdata;
@@ -115,23 +91,19 @@ _public_ void udev_set_userdata(struct udev *udev, void *userdata)
*
* Returns: a new udev library context
**/
-_public_ struct udev *udev_new(void)
-{
+_public_ struct udev *udev_new(void) {
struct udev *udev;
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
udev = new0(struct udev, 1);
if (udev == NULL)
return NULL;
udev->refcount = 1;
- udev->log_fn = log_stderr;
- udev->log_priority = LOG_INFO;
- udev_list_init(udev, &udev->properties_list, true);
f = fopen( UDEV_CONF_FILE, "re");
if (f != NULL) {
char line[UTIL_LINE_SIZE];
- int line_nr = 0;
+ unsigned line_nr = 0;
while (fgets(line, sizeof(line), f)) {
size_t len;
@@ -152,7 +124,7 @@ _public_ struct udev *udev_new(void)
/* split key/value */
val = strchr(key, '=');
if (val == NULL) {
- udev_err(udev, "missing <key>=<value> in " UDEV_CONF_FILE "[%i]; skip line\n", line_nr);
+ log_debug(UDEV_CONF_FILE ":%u: missing assignment, skipping line.", line_nr);
continue;
}
val[0] = '\0';
@@ -184,7 +156,7 @@ _public_ struct udev *udev_new(void)
/* unquote */
if (val[0] == '"' || val[0] == '\'') {
if (val[len-1] != val[0]) {
- udev_err(udev, "inconsistent quoting in " UDEV_CONF_FILE"[%i]; skip line\n", line_nr);
+ log_debug(UDEV_CONF_FILE ":%u: inconsistent quoting, skipping line.", line_nr);
continue;
}
val[len-1] = '\0';
@@ -192,11 +164,16 @@ _public_ struct udev *udev_new(void)
}
if (streq(key, "udev_log")) {
- udev_set_log_priority(udev, util_log_priority(val));
+ int prio;
+
+ prio = util_log_priority(val);
+ if (prio < 0)
+ log_debug("/etc/udev/udev.conf:%u: invalid log level '%s', ignoring.", line_nr, val);
+ else
+ log_set_max_level(prio);
continue;
}
}
- fclose(f);
}
return udev;
@@ -210,8 +187,7 @@ _public_ struct udev *udev_new(void)
*
* Returns: the passed udev library context
**/
-_public_ struct udev *udev_ref(struct udev *udev)
-{
+_public_ struct udev *udev_ref(struct udev *udev) {
if (udev == NULL)
return NULL;
udev->refcount++;
@@ -227,14 +203,12 @@ _public_ struct udev *udev_ref(struct udev *udev)
*
* Returns: the passed udev library context if it has still an active reference, or #NULL otherwise.
**/
-_public_ struct udev *udev_unref(struct udev *udev)
-{
+_public_ struct udev *udev_unref(struct udev *udev) {
if (udev == NULL)
return NULL;
udev->refcount--;
if (udev->refcount > 0)
return udev;
- udev_list_cleanup(&udev->properties_list);
free(udev);
return NULL;
}
@@ -242,68 +216,37 @@ _public_ struct udev *udev_unref(struct udev *udev)
/**
* udev_set_log_fn:
* @udev: udev library context
- * @log_fn: function to be called for logging messages
+ * @log_fn: function to be called for log messages
*
- * The built-in logging writes to stderr. It can be
- * overridden by a custom function, to plug log messages
- * into the users' logging functionality.
+ * This function is deprecated.
*
**/
_public_ void udev_set_log_fn(struct udev *udev,
void (*log_fn)(struct udev *udev,
int priority, const char *file, int line, const char *fn,
- const char *format, va_list args))
-{
- udev->log_fn = log_fn;
- udev_dbg(udev, "custom logging function %p registered\n", log_fn);
+ const char *format, va_list args)) {
+ return;
}
/**
* udev_get_log_priority:
* @udev: udev library context
*
- * The initial logging priority is read from the udev config file
- * at startup.
+ * This function is deprecated.
*
- * Returns: the current logging priority
**/
-_public_ int udev_get_log_priority(struct udev *udev)
-{
- return udev->log_priority;
+_public_ int udev_get_log_priority(struct udev *udev) {
+ return log_get_max_level();
}
/**
* udev_set_log_priority:
* @udev: udev library context
- * @priority: the new logging priority
+ * @priority: the new log priority
+ *
+ * This function is deprecated.
*
- * Set the current logging priority. The value controls which messages
- * are logged.
**/
-_public_ void udev_set_log_priority(struct udev *udev, int priority)
-{
- char num[32];
-
- udev->log_priority = priority;
- snprintf(num, sizeof(num), "%u", udev->log_priority);
- udev_add_property(udev, "UDEV_LOG", num);
-}
-
-struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value)
-{
- if (value == NULL) {
- struct udev_list_entry *list_entry;
-
- list_entry = udev_get_properties_list_entry(udev);
- list_entry = udev_list_entry_get_by_name(list_entry, key);
- if (list_entry != NULL)
- udev_list_entry_delete(list_entry);
- return NULL;
- }
- return udev_list_entry_add(&udev->properties_list, key, value);
-}
-
-struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev)
-{
- return udev_list_get_entry(&udev->properties_list);
+_public_ void udev_set_log_priority(struct udev *udev, int priority) {
+ log_set_max_level(priority);
}
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index 4f2f11502c..a94505c09e 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -41,9 +41,9 @@ struct udev *udev_new(void);
void udev_set_log_fn(struct udev *udev,
void (*log_fn)(struct udev *udev,
int priority, const char *file, int line, const char *fn,
- const char *format, va_list args));
-int udev_get_log_priority(struct udev *udev);
-void udev_set_log_priority(struct udev *udev, int priority);
+ const char *format, va_list args)) __attribute__ ((deprecated));
+int udev_get_log_priority(struct udev *udev) __attribute__ ((deprecated));
+void udev_set_log_priority(struct udev *udev, int priority) __attribute__ ((deprecated));
void *udev_get_userdata(struct udev *udev);
void udev_set_userdata(struct udev *udev, void *userdata);
diff --git a/src/scsi_id/scsi_id.c b/src/scsi_id/scsi_id.c
index ad190552f7..8b76d87fbe 100644
--- a/src/scsi_id/scsi_id.c
+++ b/src/scsi_id/scsi_id.c
@@ -67,14 +67,6 @@ static char model_enc_str[256];
static char revision_str[16];
static char type_str[16];
-_printf_(6,0)
-static void log_fn(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- log_metav(priority, file, line, fn, format, args);
-}
-
static void set_type(const char *from, char *to, size_t len)
{
int type_num;
@@ -186,7 +178,7 @@ static int get_file_options(struct udev *udev,
if (errno == ENOENT)
return 1;
else {
- log_error("can't open %s: %m", config_file);
+ log_error_errno(errno, "can't open %s: %m", config_file);
return -1;
}
}
@@ -316,18 +308,20 @@ static int get_file_options(struct udev *udev,
}
static void help(void) {
- printf("Usage: scsi_id [OPTION...] DEVICE\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 threat device as blacklisted\n"
- " -g,--whitelisted threat device as whitelisted\n"
- " -u,--replace-whitespace replace all whitespace by underscores\n"
- " -v,--verbose verbose logging\n"
- " --version print version\n"
- " -x,--export print values as environment keys\n"
- " -h,--help print this help text\n\n");
+ 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);
}
@@ -394,7 +388,6 @@ static int set_options(struct udev *udev,
case 'v':
log_set_target(LOG_TARGET_CONSOLE);
log_set_max_level(LOG_DEBUG);
- udev_set_log_priority(udev, LOG_DEBUG);
log_open();
break;
@@ -594,8 +587,6 @@ int main(int argc, char **argv)
if (udev == NULL)
goto exit;
- udev_set_log_fn(udev, log_fn);
-
/*
* Get config file options.
*/
@@ -620,7 +611,7 @@ int main(int argc, char **argv)
exit(1);
if (!dev_specified) {
- log_error("no device specified");
+ log_error("No device specified.");
retval = 1;
goto exit;
}
diff --git a/src/scsi_id/scsi_serial.c b/src/scsi_id/scsi_serial.c
index 1806498503..37081adc1f 100644
--- a/src/scsi_id/scsi_serial.c
+++ b/src/scsi_id/scsi_serial.c
@@ -365,7 +365,7 @@ resend:
dev_scsi->use_sg = 3;
goto resend;
}
- log_debug("%s: ioctl failed: %m", dev_scsi->kernel);
+ log_debug_errno(errno, "%s: ioctl failed: %m", dev_scsi->kernel);
goto error;
}
@@ -823,12 +823,12 @@ int scsi_std_inquiry(struct udev *udev,
fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
- log_debug("scsi_id: cannot open %s: %m", devname);
+ log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname);
return 1;
}
if (fstat(fd, &statbuf) < 0) {
- log_debug("scsi_id: cannot stat %s: %m", devname);
+ log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname);
err = 2;
goto out;
}
@@ -865,7 +865,7 @@ int scsi_get_serial(struct udev *udev,
int retval;
memzero(dev_scsi->serial, len);
- srand((unsigned int)getpid());
+ initialize_srand();
for (cnt = 20; cnt > 0; cnt--) {
struct timespec duration;
diff --git a/src/shared/Makefile.am b/src/shared/Makefile.am
index 3862d31910..bb695f8720 100644
--- a/src/shared/Makefile.am
+++ b/src/shared/Makefile.am
@@ -19,7 +19,6 @@ libudev_shared_la_SOURCES=\
MurmurHash2.c \
path-util.c \
selinux-util.c \
- set.c \
siphash24.c \
smack-util.c \
strbuf.c \
@@ -41,6 +40,7 @@ noinst_HEADERS = \
hashmap.h \
ioprio.h \
label.h \
+ list.h \
log.h \
macro.h \
mempool.h \
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index 3f9018cf1f..f1dad13656 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -118,7 +118,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
if (ret >= 0 && errno != ESRCH)
ret = -errno;
} else {
- if (sigcont)
+ if (sigcont && sig != SIGKILL)
kill(pid, SIGCONT);
if (ret == 0)
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index fa4edf11c6..9cfdc60930 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -1,7 +1,10 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
- This file is part of eudev, forked from systemd.
+ This file is part of systemd.
Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
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
@@ -24,38 +27,250 @@
#include "util.h"
#include "hashmap.h"
+#include "set.h"
#include "macro.h"
#include "siphash24.h"
+#include "strv.h"
+#include "list.h"
#include "mempool.h"
-#define INITIAL_N_BUCKETS 31
-
-struct hashmap_entry {
+/*
+ * Implementation of hashmaps.
+ * Addressing: open
+ * - uses less RAM compared to closed addressing (chaining), because
+ * our entries are small (especially in Sets, which tend to contain
+ * the majority of entries in systemd).
+ * Collision resolution: Robin Hood
+ * - tends to equalize displacement of entries from their optimal buckets.
+ * Probe sequence: linear
+ * - though theoretically worse than random probing/uniform hashing/double
+ * hashing, it is good for cache locality.
+ *
+ * References:
+ * Celis, P. 1986. Robin Hood Hashing.
+ * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
+ * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
+ * - The results are derived for random probing. Suggests deletion with
+ * tombstones and two mean-centered search methods. None of that works
+ * well for linear probing.
+ *
+ * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
+ * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
+ * http://www.math.uu.se/~svante/papers/sj157.pdf
+ * - Applies to Robin Hood with linear probing. Contains remarks on
+ * the unsuitability of mean-centered search with linear probing.
+ *
+ * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
+ * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
+ * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
+ * in a successful search), and Janson writes about displacement. C = d + 1.
+ *
+ * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
+ * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
+ * - Explanation of backward shift deletion with pictures.
+ *
+ * Khuong, P. 2013. The Other Robin Hood Hashing.
+ * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
+ * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
+ */
+
+/*
+ * XXX Ideas for improvement:
+ * For unordered hashmaps, randomize iteration order, similarly to Perl:
+ * http://blog.booking.com/hardening-perls-hash-function.html
+ */
+
+/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
+ * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
+#define INV_KEEP_FREE 5U
+
+/* Fields common to entries of all hashmap/set types */
+struct hashmap_base_entry {
const void *key;
+};
+
+/* Entry types for specific hashmap/set types
+ * hashmap_base_entry must be at the beginning of each entry struct. */
+
+struct plain_hashmap_entry {
+ struct hashmap_base_entry b;
void *value;
- struct hashmap_entry *bucket_next, *bucket_previous;
- struct hashmap_entry *iterate_next, *iterate_previous;
};
-struct Hashmap {
- const struct hash_ops *hash_ops;
+struct ordered_hashmap_entry {
+ struct plain_hashmap_entry p;
+ unsigned iterate_next, iterate_previous;
+};
+
+struct set_entry {
+ struct hashmap_base_entry b;
+};
+
+/* In several functions it is advantageous to have the hash table extended
+ * virtually by a couple of additional buckets. We reserve special index values
+ * for these "swap" buckets. */
+#define _IDX_SWAP_BEGIN (UINT_MAX - 3)
+#define IDX_PUT (_IDX_SWAP_BEGIN + 0)
+#define IDX_TMP (_IDX_SWAP_BEGIN + 1)
+#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2)
+
+#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */
+#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */
+
+assert_cc(IDX_FIRST == _IDX_SWAP_END);
+assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
+
+/* Storage space for the "swap" buckets.
+ * All entry types can fit into a ordered_hashmap_entry. */
+struct swap_entries {
+ struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
+};
+
+/* Distance from Initial Bucket */
+typedef uint8_t dib_raw_t;
+#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
+#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
+#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
+#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
+
+#define DIB_FREE UINT_MAX
+
+#ifdef ENABLE_DEBUG_HASHMAP
+struct hashmap_debug_info {
+ LIST_FIELDS(struct hashmap_debug_info, debug_list);
+ unsigned max_entries; /* high watermark of n_entries */
+
+ /* who allocated this hashmap */
+ int line;
+ const char *file;
+ const char *func;
+
+ /* fields to detect modification while iterating */
+ unsigned put_count; /* counts puts into the hashmap */
+ unsigned rem_count; /* counts removals from hashmap */
+ unsigned last_rem_idx; /* remembers last removal index */
+};
- struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
+static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
+
+#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
+
+#else /* !ENABLE_DEBUG_HASHMAP */
+#define HASHMAP_DEBUG_FIELDS
+#endif /* ENABLE_DEBUG_HASHMAP */
+
+enum HashmapType {
+ HASHMAP_TYPE_PLAIN,
+ HASHMAP_TYPE_ORDERED,
+ HASHMAP_TYPE_SET,
+ _HASHMAP_TYPE_MAX
+};
+
+struct _packed_ indirect_storage {
+ char *storage; /* where buckets and DIBs are stored */
+ uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */
+
+ unsigned n_entries; /* number of stored entries */
+ unsigned n_buckets; /* number of buckets */
+
+ unsigned idx_lowest_entry; /* Index below which all buckets are free.
+ Makes "while(hashmap_steal_first())" loops
+ O(n) instead of O(n^2) for unordered hashmaps. */
+ uint8_t _pad[3]; /* padding for the whole HashmapBase */
+ /* The bitfields in HashmapBase complete the alignment of the whole thing. */
+};
+
+struct direct_storage {
+ /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
+ * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
+ * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
+ char storage[sizeof(struct indirect_storage)];
+};
+
+#define DIRECT_BUCKETS(entry_t) \
+ (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
+
+/* We should be able to store at least one entry directly. */
+assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
+
+/* We have 3 bits for n_direct_entries. */
+assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
+
+/* Hashmaps with directly stored entries all use this shared hash key.
+ * It's no big deal if the key is guessed, because there can be only
+ * a handful of directly stored entries in a hashmap. When a hashmap
+ * outgrows direct storage, it gets its own key for indirect storage. */
+static uint8_t shared_hash_key[HASH_KEY_SIZE];
+static bool shared_hash_key_initialized;
+
+/* Fields that all hashmap/set types must have */
+struct HashmapBase {
+ const struct hash_ops *hash_ops; /* hash and compare ops to use */
+
+ union _packed_ {
+ struct indirect_storage indirect; /* if has_indirect */
+ struct direct_storage direct; /* if !has_indirect */
+ };
+
+ enum HashmapType type:2; /* HASHMAP_TYPE_* */
+ bool has_indirect:1; /* whether indirect storage is used */
+ unsigned n_direct_entries:3; /* Number of entries in direct storage.
+ * Only valid if !has_indirect. */
+ bool from_pool:1; /* whether was allocated from mempool */
+ HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
+};
+
+/* Specific hash types
+ * HashmapBase must be at the beginning of each hashmap struct. */
+
+struct Hashmap {
+ struct HashmapBase b;
+};
- struct hashmap_entry ** buckets;
- unsigned n_buckets, n_entries;
+struct OrderedHashmap {
+ struct HashmapBase b;
+ unsigned iterate_list_head, iterate_list_tail;
+};
- uint8_t hash_key[HASH_KEY_SIZE];
- bool from_pool:1;
+struct Set {
+ struct HashmapBase b;
};
-struct hashmap_tile {
- Hashmap h;
- struct hashmap_entry *initial_buckets[INITIAL_N_BUCKETS];
+DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
+DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
+/* No need for a separate Set pool */
+assert_cc(sizeof(Hashmap) == sizeof(Set));
+
+struct hashmap_type_info {
+ size_t head_size;
+ size_t entry_size;
+ struct mempool *mempool;
+ unsigned n_direct_buckets;
};
-static DEFINE_MEMPOOL(hashmap_pool, struct hashmap_tile, 8);
-static DEFINE_MEMPOOL(hashmap_entry_pool, struct hashmap_entry, 64);
+static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
+ [HASHMAP_TYPE_PLAIN] = {
+ .head_size = sizeof(Hashmap),
+ .entry_size = sizeof(struct plain_hashmap_entry),
+ .mempool = &hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
+ },
+ [HASHMAP_TYPE_ORDERED] = {
+ .head_size = sizeof(OrderedHashmap),
+ .entry_size = sizeof(struct ordered_hashmap_entry),
+ .mempool = &ordered_hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
+ },
+ [HASHMAP_TYPE_SET] = {
+ .head_size = sizeof(Set),
+ .entry_size = sizeof(struct set_entry),
+ .mempool = &hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
+ },
+};
unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
uint64_t u;
@@ -87,9 +302,82 @@ const struct hash_ops trivial_hash_ops = {
.compare = trivial_compare_func
};
-static unsigned bucket_hash(Hashmap *h, const void *p) {
- return (unsigned) (h->hash_ops->hash(p, h->hash_key) % h->n_buckets);
+unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
+ return (unsigned long) u;
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+ uint64_t a, b;
+ a = *(const uint64_t*) _a;
+ b = *(const uint64_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops uint64_hash_ops = {
+ .hash = uint64_hash_func,
+ .compare = uint64_compare_func
+};
+
+#if SIZEOF_DEV_T != 8
+unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
+ return (unsigned long) u;
+}
+
+int devt_compare_func(const void *_a, const void *_b) {
+ dev_t a, b;
+ a = *(const dev_t*) _a;
+ b = *(const dev_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#endif
+
+static unsigned n_buckets(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.n_buckets
+ : hashmap_type_info[h->type].n_direct_buckets;
+}
+
+static unsigned n_entries(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.n_entries
+ : h->n_direct_entries;
+}
+
+static void n_entries_inc(HashmapBase *h) {
+ if (h->has_indirect)
+ h->indirect.n_entries++;
+ else
+ h->n_direct_entries++;
+}
+
+static void n_entries_dec(HashmapBase *h) {
+ if (h->has_indirect)
+ h->indirect.n_entries--;
+ else
+ h->n_direct_entries--;
+}
+
+static char *storage_ptr(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.storage
+ : h->direct.storage;
+}
+
+static uint8_t *hash_key(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.hash_key
+ : shared_hash_key;
+}
+
+static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
+ return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h));
}
+#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
static uint8_t current[HASH_KEY_SIZE];
@@ -110,131 +398,484 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
memcpy(hash_key, current, sizeof(current));
}
-Hashmap *hashmap_new(const struct hash_ops *hash_ops) {
- bool b;
- struct hashmap_tile *ht;
- Hashmap *h;
+static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
+ return (struct hashmap_base_entry*)
+ (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+}
+
+static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
+ return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
+ return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
+ return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
+ return &swap->e[idx - _IDX_SWAP_BEGIN];
+}
- b = is_main_thread();
+/* Returns a pointer to the bucket at index idx.
+ * Understands real indexes and swap indexes, hence "_virtual". */
+static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
+ unsigned idx) {
+ if (idx < _IDX_SWAP_BEGIN)
+ return bucket_at(h, idx);
- if (b) {
- ht = mempool_alloc_tile(&hashmap_pool);
- if (!ht)
- return NULL;
+ if (idx < _IDX_SWAP_END)
+ return &bucket_at_swap(swap, idx)->p.b;
- memzero(ht, sizeof(struct hashmap_tile));
- } else {
- ht = malloc0(sizeof(struct hashmap_tile));
+ assert_not_reached("Invalid index");
+}
+
+static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
+ return (dib_raw_t*)
+ (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
+}
+
+static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
+ return idx >= from ? idx - from
+ : n_buckets(h) + idx - from;
+}
+
+static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
+ unsigned initial_bucket;
+
+ if (raw_dib == DIB_RAW_FREE)
+ return DIB_FREE;
+
+ if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
+ return raw_dib;
+
+ /*
+ * Having an overflow DIB value is very unlikely. The hash function
+ * would have to be bad. For example, in a table of size 2^24 filled
+ * to load factor 0.9 the maximum observed DIB is only about 60.
+ * In theory (assuming I used Maxima correctly), for an infinite size
+ * hash table with load factor 0.8 the probability of a given entry
+ * having DIB > 40 is 1.9e-8.
+ * This returns the correct DIB value by recomputing the hash value in
+ * the unlikely case. XXX Hitting this case could be a hint to rehash.
+ */
+ initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
+ return bucket_distance(h, idx, initial_bucket);
+}
+
+static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
+ dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
+}
+
+static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
+ dib_raw_t *dibs;
+
+ dibs = dib_raw_ptr(h);
+
+ for ( ; idx < n_buckets(h); idx++)
+ if (dibs[idx] != DIB_RAW_FREE)
+ return idx;
+
+ return IDX_NIL;
+}
+
+static void bucket_mark_free(HashmapBase *h, unsigned idx) {
+ memset(bucket_at(h, idx), 0, hashmap_type_info[h->type].entry_size);
+ bucket_set_dib(h, idx, DIB_FREE);
+}
- if (!ht)
- return NULL;
+static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
+ unsigned from, unsigned to) {
+ struct hashmap_base_entry *e_from, *e_to;
+
+ assert(from != to);
+
+ e_from = bucket_at_virtual(h, swap, from);
+ e_to = bucket_at_virtual(h, swap, to);
+
+ memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ struct ordered_hashmap_entry *le, *le_to;
+
+ le_to = (struct ordered_hashmap_entry*) e_to;
+
+ if (le_to->iterate_next != IDX_NIL) {
+ le = (struct ordered_hashmap_entry*)
+ bucket_at_virtual(h, swap, le_to->iterate_next);
+ le->iterate_previous = to;
+ }
+
+ if (le_to->iterate_previous != IDX_NIL) {
+ le = (struct ordered_hashmap_entry*)
+ bucket_at_virtual(h, swap, le_to->iterate_previous);
+ le->iterate_next = to;
+ }
+
+ if (lh->iterate_list_head == from)
+ lh->iterate_list_head = to;
+ if (lh->iterate_list_tail == from)
+ lh->iterate_list_tail = to;
}
+}
- h = &ht->h;
- h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
+static unsigned next_idx(HashmapBase *h, unsigned idx) {
+ return (idx + 1U) % n_buckets(h);
+}
- h->n_buckets = INITIAL_N_BUCKETS;
- h->n_entries = 0;
- h->iterate_list_head = h->iterate_list_tail = NULL;
+static unsigned prev_idx(HashmapBase *h, unsigned idx) {
+ return (n_buckets(h) + idx - 1U) % n_buckets(h);
+}
- h->buckets = ht->initial_buckets;
+static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
+ switch (h->type) {
- h->from_pool = b;
+ case HASHMAP_TYPE_PLAIN:
+ case HASHMAP_TYPE_ORDERED:
+ return ((struct plain_hashmap_entry*)e)->value;
- get_hash_key(h->hash_key, true);
+ case HASHMAP_TYPE_SET:
+ return (void*) e->key;
- return h;
+ default:
+ assert_not_reached("Unknown hashmap type");
+ }
}
-static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
+static void base_remove_entry(HashmapBase *h, unsigned idx) {
+ unsigned left, right, prev, dib;
+ dib_raw_t raw_dib, *dibs;
+
+ dibs = dib_raw_ptr(h);
+ assert(dibs[idx] != DIB_RAW_FREE);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.rem_count++;
+ h->debug.last_rem_idx = idx;
+#endif
+
+ left = idx;
+ /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
+ for (right = next_idx(h, left); ; right = next_idx(h, right)) {
+ raw_dib = dibs[right];
+ if (raw_dib == 0 || raw_dib == DIB_RAW_FREE)
+ break;
+
+ /* The buckets are not supposed to be all occupied and with DIB > 0.
+ * That would mean we could make everyone better off by shifting them
+ * backward. This scenario is impossible. */
+ assert(left != right);
+ }
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
+
+ if (le->iterate_next != IDX_NIL)
+ ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
+ else
+ lh->iterate_list_tail = le->iterate_previous;
+
+ if (le->iterate_previous != IDX_NIL)
+ ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
+ else
+ lh->iterate_list_head = le->iterate_next;
+ }
+
+ /* Now shift all buckets in the interval (left, right) one step backwards */
+ for (prev = left, left = next_idx(h, left); left != right;
+ prev = left, left = next_idx(h, left)) {
+ dib = bucket_calculate_dib(h, left, dibs[left]);
+ assert(dib != 0);
+ bucket_move_entry(h, NULL, left, prev);
+ bucket_set_dib(h, prev, dib - 1);
+ }
+
+ bucket_mark_free(h, prev);
+ n_entries_dec(h);
+}
+#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
+
+static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
+ struct ordered_hashmap_entry *e;
+ unsigned idx;
+
assert(h);
- assert(e);
-
- /* Insert into hash table */
- e->bucket_next = h->buckets[hash];
- e->bucket_previous = NULL;
- if (h->buckets[hash])
- h->buckets[hash]->bucket_previous = e;
- h->buckets[hash] = e;
-
- /* Insert into iteration list */
- e->iterate_previous = h->iterate_list_tail;
- e->iterate_next = NULL;
- if (h->iterate_list_tail) {
- assert(h->iterate_list_head);
- h->iterate_list_tail->iterate_next = e;
+ assert(i);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST) {
+ idx = h->iterate_list_head;
+ e = ordered_bucket_at(h, idx);
} else {
- assert(!h->iterate_list_head);
- h->iterate_list_head = e;
+ idx = i->idx;
+ e = ordered_bucket_at(h, idx);
+ /*
+ * We allow removing the current entry while iterating, but removal may cause
+ * a backward shift. The next entry may thus move one bucket to the left.
+ * To detect when it happens, we remember the key pointer of the entry we were
+ * going to iterate next. If it does not match, there was a backward shift.
+ */
+ if (e->p.b.key != i->next_key) {
+ idx = prev_idx(HASHMAP_BASE(h), idx);
+ e = ordered_bucket_at(h, idx);
+ }
+ assert(e->p.b.key == i->next_key);
}
- h->iterate_list_tail = e;
- h->n_entries++;
- assert(h->n_entries >= 1);
+#ifdef ENABLE_DEBUG_HASHMAP
+ i->prev_idx = idx;
+#endif
+
+ if (e->iterate_next != IDX_NIL) {
+ struct ordered_hashmap_entry *n;
+ i->idx = e->iterate_next;
+ n = ordered_bucket_at(h, i->idx);
+ i->next_key = n->p.b.key;
+ } else
+ i->idx = IDX_NIL;
+
+ return idx;
+
+at_end:
+ i->idx = IDX_NIL;
+ return IDX_NIL;
}
-static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
+static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
+ unsigned idx;
+
assert(h);
- assert(e);
+ assert(i);
- /* Remove from iteration list */
- if (e->iterate_next)
- e->iterate_next->iterate_previous = e->iterate_previous;
- else
- h->iterate_list_tail = e->iterate_previous;
+ if (i->idx == IDX_NIL)
+ goto at_end;
- if (e->iterate_previous)
- e->iterate_previous->iterate_next = e->iterate_next;
- else
- h->iterate_list_head = e->iterate_next;
+ if (i->idx == IDX_FIRST) {
+ /* fast forward to the first occupied bucket */
+ if (h->has_indirect) {
+ i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
+ h->indirect.idx_lowest_entry = i->idx;
+ } else
+ i->idx = skip_free_buckets(h, 0);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+ } else {
+ struct hashmap_base_entry *e;
+
+ assert(i->idx > 0);
- /* Remove from hash table bucket list */
- if (e->bucket_next)
- e->bucket_next->bucket_previous = e->bucket_previous;
+ e = bucket_at(h, i->idx);
+ /*
+ * We allow removing the current entry while iterating, but removal may cause
+ * a backward shift. The next entry may thus move one bucket to the left.
+ * To detect when it happens, we remember the key pointer of the entry we were
+ * going to iterate next. If it does not match, there was a backward shift.
+ */
+ if (e->key != i->next_key)
+ e = bucket_at(h, --i->idx);
+
+ assert(e->key == i->next_key);
+ }
- if (e->bucket_previous)
- e->bucket_previous->bucket_next = e->bucket_next;
+ idx = i->idx;
+#ifdef ENABLE_DEBUG_HASHMAP
+ i->prev_idx = idx;
+#endif
+
+ i->idx = skip_free_buckets(h, i->idx + 1);
+ if (i->idx != IDX_NIL)
+ i->next_key = bucket_at(h, i->idx)->key;
else
- h->buckets[hash] = e->bucket_next;
+ i->idx = IDX_NIL;
+
+ return idx;
- assert(h->n_entries >= 1);
- h->n_entries--;
+at_end:
+ i->idx = IDX_NIL;
+ return IDX_NIL;
}
-static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
- unsigned hash;
+static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
+ if (!h) {
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+ }
- assert(h);
- assert(e);
+#ifdef ENABLE_DEBUG_HASHMAP
+ if (i->idx == IDX_FIRST) {
+ i->put_count = h->debug.put_count;
+ i->rem_count = h->debug.rem_count;
+ } else {
+ /* While iterating, must not add any new entries */
+ assert(i->put_count == h->debug.put_count);
+ /* ... or remove entries other than the current one */
+ assert(i->rem_count == h->debug.rem_count ||
+ (i->rem_count == h->debug.rem_count - 1 &&
+ i->prev_idx == h->debug.last_rem_idx));
+ /* Reset our removals counter */
+ i->rem_count = h->debug.rem_count;
+ }
+#endif
- hash = bucket_hash(h, e->key);
- unlink_entry(h, e, hash);
+ return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
+ : hashmap_iterate_in_internal_order(h, i);
+}
- if (h->from_pool)
- mempool_free_tile(&hashmap_entry_pool, e);
- else
- free(e);
+void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key) {
+ struct hashmap_base_entry *e;
+ void *data;
+ unsigned idx;
+
+ idx = hashmap_iterate_entry(h, i);
+ if (idx == IDX_NIL) {
+ if (key)
+ *key = NULL;
+
+ return NULL;
+ }
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ if (key)
+ *key = e->key;
+
+ return data;
}
-void hashmap_free(Hashmap*h) {
+void *set_iterate(Set *s, Iterator *i) {
+ return internal_hashmap_iterate(HASHMAP_BASE(s), i, NULL);
+}
- /* Free the hashmap, but nothing in it */
+#define HASHMAP_FOREACH_IDX(idx, h, i) \
+ for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
+ (idx != IDX_NIL); \
+ (idx) = hashmap_iterate_entry((h), &(i)))
+
+static void reset_direct_storage(HashmapBase *h) {
+ const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
+ void *p;
+
+ assert(!h->has_indirect);
+
+ p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
+ memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
+}
+
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ HashmapBase *h;
+ const struct hashmap_type_info *hi = &hashmap_type_info[type];
+ bool use_pool;
+
+ use_pool = is_main_thread();
+
+ h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
if (!h)
- return;
+ return NULL;
+
+ h->type = type;
+ h->from_pool = use_pool;
+ h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
- hashmap_clear(h);
+ if (type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*)h;
+ lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+ }
+
+ reset_direct_storage(h);
+
+ if (!shared_hash_key_initialized) {
+ random_bytes(shared_hash_key, sizeof(shared_hash_key));
+ shared_hash_key_initialized= true;
+ }
- if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
- free(h->buckets);
+#ifdef ENABLE_DEBUG_HASHMAP
+ LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
+ h->debug.func = func;
+ h->debug.file = file;
+ h->debug.line = line;
+#endif
+
+ return h;
+}
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
+ enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ HashmapBase *q;
+
+ assert(h);
+
+ if (*h)
+ return 0;
+
+ q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+ if (!q)
+ return -ENOMEM;
+
+ *h = q;
+ return 0;
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static void hashmap_free_no_clear(HashmapBase *h) {
+ assert(!h->has_indirect);
+ assert(!h->n_direct_entries);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
+#endif
if (h->from_pool)
- mempool_free_tile(&hashmap_pool, container_of(h, struct hashmap_tile, h));
+ mempool_free_tile(hashmap_type_info[h->type].mempool, h);
else
free(h);
}
-void hashmap_free_free(Hashmap *h) {
+void internal_hashmap_free(HashmapBase *h) {
+
+ /* Free the hashmap, but nothing in it */
+
+ if (!h)
+ return;
+
+ internal_hashmap_clear(h);
+ hashmap_free_no_clear(h);
+}
+
+void internal_hashmap_free_free(HashmapBase *h) {
/* Free the hashmap and all data objects in it, but not the
* keys */
@@ -242,249 +883,763 @@ void hashmap_free_free(Hashmap *h) {
if (!h)
return;
- hashmap_clear_free(h);
- hashmap_free(h);
+ internal_hashmap_clear_free(h);
+ hashmap_free_no_clear(h);
}
-void hashmap_clear(Hashmap *h) {
+void hashmap_free_free_free(Hashmap *h) {
+
+ /* Free the hashmap and all data and key objects in it */
+
if (!h)
return;
- while (h->iterate_list_head)
- remove_entry(h, h->iterate_list_head);
+ hashmap_clear_free_free(h);
+ hashmap_free_no_clear(HASHMAP_BASE(h));
}
-void hashmap_clear_free(Hashmap *h) {
- void *p;
+void internal_hashmap_clear(HashmapBase *h) {
+ if (!h)
+ return;
+
+ if (h->has_indirect) {
+ free(h->indirect.storage);
+ h->has_indirect = false;
+ }
+
+ h->n_direct_entries = 0;
+ reset_direct_storage(h);
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+ }
+}
+
+void internal_hashmap_clear_free(HashmapBase *h) {
+ unsigned idx;
if (!h)
return;
- while ((p = hashmap_steal_first(h)))
- free(p);
+ for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
+ idx = skip_free_buckets(h, idx + 1))
+ free(entry_value(h, bucket_at(h, idx)));
+
+ internal_hashmap_clear(h);
}
-static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
- struct hashmap_entry *e;
- assert(h);
- assert(hash < h->n_buckets);
+void hashmap_clear_free_free(Hashmap *h) {
+ unsigned idx;
- for (e = h->buckets[hash]; e; e = e->bucket_next)
- if (h->hash_ops->compare(e->key, key) == 0)
- return e;
+ if (!h)
+ return;
+
+ for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
+ idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
+ struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
+ free((void*)e->b.key);
+ free(e->value);
+ }
- return NULL;
+ internal_hashmap_clear(HASHMAP_BASE(h));
}
-static int resize_buckets(Hashmap *h, unsigned entries_add) {
- struct hashmap_entry **n, *i;
- unsigned m, new_n_entries, new_n_buckets;
- uint8_t nkey[HASH_KEY_SIZE];
+static int resize_buckets(HashmapBase *h, unsigned entries_add);
- assert(h);
+/*
+ * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
+ * Performs Robin Hood swaps as it goes. The entry to put must be placed
+ * by the caller into swap slot IDX_PUT.
+ * If used for in-place resizing, may leave a displaced entry in swap slot
+ * IDX_PUT. Caller must rehash it next.
+ * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
+ * false otherwise.
+ */
+static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
+ struct swap_entries *swap) {
+ dib_raw_t raw_dib, *dibs;
+ unsigned dib, distance;
- new_n_entries = h->n_entries + entries_add;
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.put_count++;
+#endif
- /* overflow? */
- if (_unlikely_(new_n_entries < entries_add || new_n_entries > UINT_MAX / 4))
- return -ENOMEM;
+ dibs = dib_raw_ptr(h);
- new_n_buckets = new_n_entries * 4 / 3;
+ for (distance = 0; ; distance++) {
+ raw_dib = dibs[idx];
+ if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) {
+ if (raw_dib == DIB_RAW_REHASH)
+ bucket_move_entry(h, swap, idx, IDX_TMP);
- if (_likely_(new_n_buckets <= h->n_buckets))
- return 0;
+ if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
+ h->indirect.idx_lowest_entry = idx;
- /* Increase by four at least */
- m = MAX((h->n_entries+1)*4-1, new_n_buckets);
+ bucket_set_dib(h, idx, distance);
+ bucket_move_entry(h, swap, IDX_PUT, idx);
+ if (raw_dib == DIB_RAW_REHASH) {
+ bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+ return true;
+ }
- /* If we hit OOM we simply risk packed hashmaps... */
- n = new0(struct hashmap_entry*, m);
- if (!n)
- return -ENOMEM;
+ return false;
+ }
- /* Let's use a different randomized hash key for the
- * extension, so that people cannot guess what we are using
- * here forever */
- get_hash_key(nkey, false);
+ dib = bucket_calculate_dib(h, idx, raw_dib);
- for (i = h->iterate_list_head; i; i = i->iterate_next) {
- unsigned long old_bucket, new_bucket;
+ if (dib < distance) {
+ /* Found a wealthier entry. Go Robin Hood! */
- old_bucket = h->hash_ops->hash(i->key, h->hash_key) % h->n_buckets;
+ bucket_set_dib(h, idx, distance);
- /* First, drop from old bucket table */
- if (i->bucket_next)
- i->bucket_next->bucket_previous = i->bucket_previous;
+ /* swap the entries */
+ bucket_move_entry(h, swap, idx, IDX_TMP);
+ bucket_move_entry(h, swap, IDX_PUT, idx);
+ bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
- if (i->bucket_previous)
- i->bucket_previous->bucket_next = i->bucket_next;
- else
- h->buckets[old_bucket] = i->bucket_next;
+ distance = dib;
+ }
- /* Then, add to new backet table */
- new_bucket = h->hash_ops->hash(i->key, nkey) % m;
+ idx = next_idx(h, idx);
+ }
+}
- i->bucket_next = n[new_bucket];
- i->bucket_previous = NULL;
- if (n[new_bucket])
- n[new_bucket]->bucket_previous = i;
- n[new_bucket] = i;
+/*
+ * Puts an entry into a hashmap, boldly - no check whether key already exists.
+ * The caller must place the entry (only its key and value, not link indexes)
+ * in swap slot IDX_PUT.
+ * Caller must ensure: the key does not exist yet in the hashmap.
+ * that resize is not needed if !may_resize.
+ * Returns: 1 if entry was put successfully.
+ * -ENOMEM if may_resize==true and resize failed with -ENOMEM.
+ * Cannot return -ENOMEM if !may_resize.
+ */
+static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
+ struct swap_entries *swap, bool may_resize) {
+ struct ordered_hashmap_entry *new_entry;
+ int r;
+
+ assert(idx < n_buckets(h));
+
+ new_entry = bucket_at_swap(swap, IDX_PUT);
+
+ if (may_resize) {
+ r = resize_buckets(h, 1);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ idx = bucket_hash(h, new_entry->p.b.key);
}
+ assert(n_entries(h) < n_buckets(h));
- if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
- free(h->buckets);
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
- h->buckets = n;
- h->n_buckets = m;
+ new_entry->iterate_next = IDX_NIL;
+ new_entry->iterate_previous = lh->iterate_list_tail;
- memcpy(h->hash_key, nkey, HASH_KEY_SIZE);
+ if (lh->iterate_list_tail != IDX_NIL) {
+ struct ordered_hashmap_entry *old_tail;
+
+ old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
+ assert(old_tail->iterate_next == IDX_NIL);
+ old_tail->iterate_next = IDX_PUT;
+ }
+
+ lh->iterate_list_tail = IDX_PUT;
+ if (lh->iterate_list_head == IDX_NIL)
+ lh->iterate_list_head = IDX_PUT;
+ }
+
+ assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
+
+ n_entries_inc(h);
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
+#endif
return 1;
}
+#define hashmap_put_boldly(h, idx, swap, may_resize) \
+ hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
+
+/*
+ * Returns 0 if resize is not needed.
+ * 1 if successfully resized.
+ * -ENOMEM on allocation failure.
+ */
+static int resize_buckets(HashmapBase *h, unsigned entries_add) {
+ struct swap_entries swap;
+ char *new_storage;
+ dib_raw_t *old_dibs, *new_dibs;
+ const struct hashmap_type_info *hi;
+ unsigned idx, optimal_idx;
+ unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
+ uint8_t new_shift;
+ bool rehash_next;
-static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash) {
- /* For when we know no such entry exists yet */
+ assert(h);
- struct hashmap_entry *e;
+ hi = &hashmap_type_info[h->type];
+ new_n_entries = n_entries(h) + entries_add;
- if (resize_buckets(h, 1) > 0)
- hash = bucket_hash(h, key);
+ /* overflow? */
+ if (_unlikely_(new_n_entries < entries_add))
+ return -ENOMEM;
- if (h->from_pool)
- e = mempool_alloc_tile(&hashmap_entry_pool);
- else
- e = new(struct hashmap_entry, 1);
+ /* For direct storage we allow 100% load, because it's tiny. */
+ if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
+ return 0;
- if (!e)
+ /*
+ * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
+ * From it follows: m = n + n/(INV_KEEP_FREE - 1)
+ */
+ new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
+ /* overflow? */
+ if (_unlikely_(new_n_buckets < new_n_entries))
return -ENOMEM;
- e->key = key;
- e->value = value;
+ if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
+ return -ENOMEM;
- link_entry(h, e, hash);
+ old_n_buckets = n_buckets(h);
+
+ if (_likely_(new_n_buckets <= old_n_buckets))
+ return 0;
+
+ new_shift = log2u_round_up(MAX(
+ new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
+ 2 * sizeof(struct direct_storage)));
+
+ /* Realloc storage (buckets and DIB array). */
+ new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
+ 1U << new_shift);
+ if (!new_storage)
+ return -ENOMEM;
+
+ /* Must upgrade direct to indirect storage. */
+ if (!h->has_indirect) {
+ memcpy(new_storage, h->direct.storage,
+ old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
+ h->indirect.n_entries = h->n_direct_entries;
+ h->indirect.idx_lowest_entry = 0;
+ h->n_direct_entries = 0;
+ }
+
+ /* Get a new hash key. If we've just upgraded to indirect storage,
+ * allow reusing a previously generated key. It's still a different key
+ * from the shared one that we used for direct storage. */
+ get_hash_key(h->indirect.hash_key, !h->has_indirect);
+
+ h->has_indirect = true;
+ h->indirect.storage = new_storage;
+ h->indirect.n_buckets = (1U << new_shift) /
+ (hi->entry_size + sizeof(dib_raw_t));
+
+ old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
+ new_dibs = dib_raw_ptr(h);
+
+ /*
+ * Move the DIB array to the new place, replacing valid DIB values with
+ * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
+ * Note: Overlap is not possible, because we have at least doubled the
+ * number of buckets and dib_raw_t is smaller than any entry type.
+ */
+ for (idx = 0; idx < old_n_buckets; idx++) {
+ assert(old_dibs[idx] != DIB_RAW_REHASH);
+ new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
+ : DIB_RAW_REHASH;
+ }
+
+ /* Zero the area of newly added entries (including the old DIB area) */
+ memset(bucket_at(h, old_n_buckets), 0,
+ (n_buckets(h) - old_n_buckets) * hi->entry_size);
+
+ /* The upper half of the new DIB array needs initialization */
+ memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
+ (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
+
+ /* Rehash entries that need it */
+ n_rehashed = 0;
+ for (idx = 0; idx < old_n_buckets; idx++) {
+ if (new_dibs[idx] != DIB_RAW_REHASH)
+ continue;
+
+ optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
+
+ /*
+ * Not much to do if by luck the entry hashes to its current
+ * location. Just set its DIB.
+ */
+ if (optimal_idx == idx) {
+ new_dibs[idx] = 0;
+ n_rehashed++;
+ continue;
+ }
+
+ new_dibs[idx] = DIB_RAW_FREE;
+ bucket_move_entry(h, &swap, idx, IDX_PUT);
+ /* bucket_move_entry does not clear the source */
+ memset(bucket_at(h, idx), 0, hi->entry_size);
+
+ do {
+ /*
+ * Find the new bucket for the current entry. This may make
+ * another entry homeless and load it into IDX_PUT.
+ */
+ rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
+ n_rehashed++;
+
+ /* Did the current entry displace another one? */
+ if (rehash_next)
+ optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
+ } while (rehash_next);
+ }
+
+ assert(n_rehashed == n_entries(h));
return 1;
}
+/*
+ * Finds an entry with a matching key
+ * Returns: index of the found entry, or IDX_NIL if not found.
+ */
+static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned dib, distance;
+ dib_raw_t *dibs = dib_raw_ptr(h);
+
+ assert(idx < n_buckets(h));
+
+ for (distance = 0; ; distance++) {
+ if (dibs[idx] == DIB_RAW_FREE)
+ return IDX_NIL;
+
+ dib = bucket_calculate_dib(h, idx, dibs[idx]);
+
+ if (dib < distance)
+ return IDX_NIL;
+ if (dib == distance) {
+ e = bucket_at(h, idx);
+ if (h->hash_ops->compare(e->key, key) == 0)
+ return idx;
+ }
+
+ idx = next_idx(h, idx);
+ }
+}
+#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
+
int hashmap_put(Hashmap *h, const void *key, void *value) {
- struct hashmap_entry *e;
- unsigned hash;
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
assert(h);
hash = bucket_hash(h, key);
- e = hash_scan(h, hash, key);
- if (e) {
+ idx = bucket_scan(h, hash, key);
+ if (idx != IDX_NIL) {
+ e = plain_bucket_at(h, idx);
if (e->value == value)
return 0;
return -EEXIST;
}
- return __hashmap_put(h, key, value, hash);
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = key;
+ e->value = value;
+ return hashmap_put_boldly(h, hash, &swap, true);
}
-void* hashmap_get(Hashmap *h, const void *key) {
- unsigned hash;
- struct hashmap_entry *e;
+int set_put(Set *s, const void *key) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+
+ assert(s);
+
+ hash = bucket_hash(s, key);
+ idx = bucket_scan(s, hash, key);
+ if (idx != IDX_NIL)
+ return 0;
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ e->key = key;
+ return hashmap_put_boldly(s, hash, &swap, true);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx != IDX_NIL) {
+ e = plain_bucket_at(h, idx);
+#ifdef ENABLE_DEBUG_HASHMAP
+ /* Although the key is equal, the key pointer may have changed,
+ * and this would break our assumption for iterating. So count
+ * this operation as incompatible with iteration. */
+ if (e->b.key != key) {
+ h->b.debug.put_count++;
+ h->b.debug.rem_count++;
+ h->b.debug.last_rem_idx = idx;
+ }
+#endif
+ e->b.key = key;
+ e->value = value;
+ return 0;
+ }
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = key;
+ e->value = value;
+ return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ e = plain_bucket_at(h, idx);
+ e->value = value;
+ return 0;
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
if (!h)
return NULL;
hash = bucket_hash(h, key);
- e = hash_scan(h, hash, key);
- if (!e)
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
return NULL;
- return e->value;
+ e = bucket_at(h, idx);
+ return entry_value(h, e);
}
-void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
- unsigned hash;
- struct hashmap_entry *e;
+void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
if (!h)
return NULL;
hash = bucket_hash(h, key);
- e = hash_scan(h, hash, key);
- if (!e)
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
return NULL;
+ e = plain_bucket_at(h, idx);
if (key2)
- *key2 = (void*) e->key;
+ *key2 = (void*) e->b.key;
return e->value;
}
-bool hashmap_contains(Hashmap *h, const void *key) {
+bool internal_hashmap_contains(HashmapBase *h, const void *key) {
unsigned hash;
if (!h)
return false;
hash = bucket_hash(h, key);
- return !!hash_scan(h, hash, key);
+ return bucket_scan(h, hash, key) != IDX_NIL;
}
-void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
- struct hashmap_entry *e;
+void *internal_hashmap_remove(HashmapBase *h, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+ void *data;
- assert(i);
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ remove_entry(h, idx);
+
+ return data;
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+ void *data;
+
+ if (!h) {
+ if (rkey)
+ *rkey = NULL;
+ return NULL;
+ }
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL) {
+ if (rkey)
+ *rkey = NULL;
+ return NULL;
+ }
+
+ e = plain_bucket_at(h, idx);
+ data = e->value;
+ if (rkey)
+ *rkey = (void*) e->b.key;
+
+ remove_entry(h, idx);
+
+ return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned old_hash, new_hash, idx;
if (!h)
- goto at_end;
+ return -ENOENT;
- if (*i == ITERATOR_LAST)
- goto at_end;
+ old_hash = bucket_hash(h, old_key);
+ idx = bucket_scan(h, old_hash, old_key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
- if (*i == ITERATOR_FIRST && !h->iterate_list_head)
- goto at_end;
+ new_hash = bucket_hash(h, new_key);
+ if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
+ return -EEXIST;
- e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
+ remove_entry(h, idx);
- if (e->iterate_next)
- *i = (Iterator) e->iterate_next;
- else
- *i = ITERATOR_LAST;
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = new_key;
+ e->value = value;
+ assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
- if (key)
- *key = e->key;
+ return 0;
+}
- return e->value;
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e;
+ unsigned old_hash, new_hash, idx;
-at_end:
- *i = ITERATOR_LAST;
+ if (!s)
+ return -ENOENT;
- if (key)
- *key = NULL;
+ old_hash = bucket_hash(s, old_key);
+ idx = bucket_scan(s, old_hash, old_key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ new_hash = bucket_hash(s, new_key);
+ if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
+ return -EEXIST;
+
+ remove_entry(s, idx);
- return NULL;
+ e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ e->key = new_key;
+ assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
+
+ return 0;
}
-void* hashmap_steal_first(Hashmap *h) {
- void *data;
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned old_hash, new_hash, idx_old, idx_new;
if (!h)
+ return -ENOENT;
+
+ old_hash = bucket_hash(h, old_key);
+ idx_old = bucket_scan(h, old_hash, old_key);
+ if (idx_old == IDX_NIL)
+ return -ENOENT;
+
+ old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
+
+ new_hash = bucket_hash(h, new_key);
+ idx_new = bucket_scan(h, new_hash, new_key);
+ if (idx_new != IDX_NIL)
+ if (idx_old != idx_new) {
+ remove_entry(h, idx_new);
+ /* Compensate for a possible backward shift. */
+ if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
+ idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
+ assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
+ }
+
+ remove_entry(h, idx_old);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = new_key;
+ e->value = value;
+ assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = plain_bucket_at(h, idx);
+ if (e->value != value)
+ return NULL;
+
+ remove_entry(h, idx);
+
+ return value;
+}
+
+static unsigned find_first_entry(HashmapBase *h) {
+ Iterator i = ITERATOR_FIRST;
+
+ if (!h || !n_entries(h))
+ return IDX_NIL;
+
+ return hashmap_iterate_entry(h, &i);
+}
+
+void *internal_hashmap_first(HashmapBase *h) {
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ return entry_value(h, bucket_at(h, idx));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
return NULL;
- if (!h->iterate_list_head)
+ e = bucket_at(h, idx);
+ return (void*) e->key;
+}
+
+void *internal_hashmap_steal_first(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ void *data;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
return NULL;
- data = h->iterate_list_head->value;
- remove_entry(h, h->iterate_list_head);
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ remove_entry(h, idx);
return data;
}
-unsigned hashmap_size(Hashmap *h) {
+void *internal_hashmap_steal_first_key(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ void *key;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ key = (void*) e->key;
+ remove_entry(h, idx);
+
+ return key;
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) {
if (!h)
return 0;
- return h->n_entries;
+ return n_entries(h);
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) {
+
+ if (!h)
+ return 0;
+
+ return n_buckets(h);
+}
+
+int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
+ Iterator i;
+ unsigned idx;
+
+ assert(h);
+
+ HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+ struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
+ int r;
+
+ r = hashmap_put(h, pe->b.key, pe->value);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ return 0;
+}
+
+int set_merge(Set *s, Set *other) {
+ Iterator i;
+ unsigned idx;
+
+ assert(s);
+
+ HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+ struct set_entry *se = set_bucket_at(other, idx);
+ int r;
+
+ r = set_put(s, se->b.key);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
}
-int hashmap_reserve(Hashmap *h, unsigned entries_add) {
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
int r;
assert(h);
@@ -496,20 +1651,200 @@ int hashmap_reserve(Hashmap *h, unsigned entries_add) {
return 0;
}
-char **hashmap_get_strv(Hashmap *h) {
+/*
+ * The same as hashmap_merge(), but every new item from other is moved to h.
+ * Keys already in h are skipped and stay in other.
+ * Returns: 0 on success.
+ * -ENOMEM on alloc failure, in which case no move has been done.
+ */
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e, *n;
+ Iterator i;
+ unsigned idx;
+ int r;
+
+ assert(h);
+
+ if (!other)
+ return 0;
+
+ assert(other->type == h->type);
+
+ /*
+ * This reserves buckets for the worst case, where none of other's
+ * entries are yet present in h. This is preferable to risking
+ * an allocation failure in the middle of the moving and having to
+ * rollback or return a partial result.
+ */
+ r = resize_buckets(h, n_entries(other));
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH_IDX(idx, other, i) {
+ unsigned h_hash;
+
+ e = bucket_at(other, idx);
+ h_hash = bucket_hash(h, e->key);
+ if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
+ continue;
+
+ n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ n->key = e->key;
+ if (h->type != HASHMAP_TYPE_SET)
+ ((struct plain_hashmap_entry*) n)->value =
+ ((struct plain_hashmap_entry*) e)->value;
+ assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
+
+ remove_entry(other, idx);
+ }
+
+ return 0;
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
+ struct swap_entries swap;
+ unsigned h_hash, other_hash, idx;
+ struct hashmap_base_entry *e, *n;
+ int r;
+
+ assert(h);
+
+ h_hash = bucket_hash(h, key);
+ if (bucket_scan(h, h_hash, key) != IDX_NIL)
+ return -EEXIST;
+
+ if (!other)
+ return -ENOENT;
+
+ assert(other->type == h->type);
+
+ other_hash = bucket_hash(other, key);
+ idx = bucket_scan(other, other_hash, key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ e = bucket_at(other, idx);
+
+ n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ n->key = e->key;
+ if (h->type != HASHMAP_TYPE_SET)
+ ((struct plain_hashmap_entry*) n)->value =
+ ((struct plain_hashmap_entry*) e)->value;
+ r = hashmap_put_boldly(h, h_hash, &swap, true);
+ if (r < 0)
+ return r;
+
+ remove_entry(other, idx);
+ return 0;
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h) {
+ HashmapBase *copy;
+ int r;
+
+ assert(h);
+
+ copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS);
+ if (!copy)
+ return NULL;
+
+ switch (h->type) {
+ case HASHMAP_TYPE_PLAIN:
+ case HASHMAP_TYPE_ORDERED:
+ r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
+ break;
+ case HASHMAP_TYPE_SET:
+ r = set_merge((Set*)copy, (Set*)h);
+ break;
+ default:
+ assert_not_reached("Unknown hashmap type");
+ }
+
+ if (r < 0) {
+ internal_hashmap_free(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+char **internal_hashmap_get_strv(HashmapBase *h) {
char **sv;
- Iterator it;
- char *item;
- int n;
+ Iterator i;
+ unsigned idx, n;
- sv = new(char*, h->n_entries+1);
+ sv = new(char*, n_entries(h)+1);
if (!sv)
return NULL;
n = 0;
- HASHMAP_FOREACH(item, h, it)
- sv[n++] = item;
+ HASHMAP_FOREACH_IDX(idx, h, i)
+ sv[n++] = entry_value(h, bucket_at(h, idx));
sv[n] = NULL;
return sv;
}
+
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
+ struct ordered_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(key);
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = ordered_bucket_at(h, idx);
+ if (e->iterate_next == IDX_NIL)
+ return NULL;
+ return ordered_bucket_at(h, e->iterate_next)->p.value;
+}
+
+int set_consume(Set *s, void *value) {
+ int r;
+
+ r = set_put(s, value);
+ if (r <= 0)
+ free(value);
+
+ return r;
+}
+
+int set_put_strdup(Set *s, const char *p) {
+ char *c;
+ int r;
+
+ assert(s);
+ assert(p);
+
+ c = strdup(p);
+ if (!c)
+ return -ENOMEM;
+
+ r = set_consume(s, c);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+int set_put_strdupv(Set *s, char **l) {
+ int n = 0, r;
+ char **i;
+
+ STRV_FOREACH(i, l) {
+ r = set_put_strdup(s, *i);
+ if (r < 0)
+ return r;
+
+ n += r;
+ }
+
+ return n;
+}
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index 22bda37437..894f67939e 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -1,7 +1,12 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
/***
- This file is part of eudev, forked from systemd.
+ This file is part of systemd.
Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
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
@@ -17,26 +22,50 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
#include <stdbool.h>
#include "macro.h"
#include "util.h"
-/* Pretty straightforward hash table implementation. As a minor
- * optimization a NULL hashmap object will be treated as empty hashmap
- * for all read operations. That way it is not necessary to
- * instantiate an object for each Hashmap use. */
+/*
+ * A hash table implementation. As a minor optimization a NULL hashmap object
+ * will be treated as empty hashmap for all read operations. That way it is not
+ * necessary to instantiate an object for each Hashmap use.
+ *
+ * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
+ * the implemention will:
+ * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
+ * - perform extra checks for invalid use of iterators
+ */
#define HASH_KEY_SIZE 16
-typedef struct Hashmap Hashmap;
-typedef struct _IteratorStruct _IteratorStruct;
-typedef _IteratorStruct* Iterator;
+/* The base type for all hashmap and set types. Many functions in the
+ * implementation take (HashmapBase*) parameters and are run-time polymorphic,
+ * though the API is not meant to be polymorphic (do not call functions
+ * internal_*() directly). */
+typedef struct HashmapBase HashmapBase;
+
+/* Specific hashmap/set types */
+typedef struct Hashmap Hashmap; /* Maps keys to values */
+typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
+typedef struct Set Set; /* Stores just keys */
-#define ITERATOR_FIRST ((Iterator) 0)
-#define ITERATOR_LAST ((Iterator) -1)
+/* Ideally the Iterator would be an opaque struct, but it is instantiated
+ * by hashmap users, so the definition has to be here. Do not use its fields
+ * directly. */
+typedef struct {
+ unsigned idx; /* index of an entry to be iterated next */
+ const void *next_key; /* expected value of that entry's key pointer */
+#ifdef ENABLE_DEBUG_HASHMAP
+ unsigned put_count; /* hashmap's put_count recorded at start of iteration */
+ unsigned rem_count; /* hashmap's rem_count in previous iteration */
+ unsigned prev_idx; /* idx in previous iteration */
+#endif
+} Iterator;
+
+#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
+#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
typedef int (*compare_func_t)(const void *a, const void *b);
@@ -57,29 +86,335 @@ unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_S
int trivial_compare_func(const void *a, const void *b) _const_;
extern const struct hash_ops trivial_hash_ops;
-Hashmap *hashmap_new(const struct hash_ops *hash_ops);
-void hashmap_free(Hashmap *h);
-void hashmap_free_free(Hashmap *h);
+/* 32bit values we can always just embedd in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
+unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+int uint64_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops uint64_hash_ops;
+
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+#if SIZEOF_DEV_T != 8
+unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+int devt_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#else
+#define devt_hash_func uint64_hash_func
+#define devt_compare_func uint64_compare_func
+#define devt_hash_ops uint64_hash_ops
+#endif
+
+/* Macros for type checking */
+#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
+ (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
+ __builtin_types_compatible_p(typeof(h), Hashmap*) || \
+ __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
+ __builtin_types_compatible_p(typeof(h), Set*))
+
+#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
+ (__builtin_types_compatible_p(typeof(h), Hashmap*) || \
+ __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
+
+#define HASHMAP_BASE(h) \
+ __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
+ (HashmapBase*)(h), \
+ (void)0)
+
+#define PLAIN_HASHMAP(h) \
+ __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
+ (Hashmap*)(h), \
+ (void)0)
+
+#ifdef ENABLE_DEBUG_HASHMAP
+# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
+# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
+# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
+#else
+# define HASHMAP_DEBUG_PARAMS
+# define HASHMAP_DEBUG_SRC_ARGS
+# define HASHMAP_DEBUG_PASS_ARGS
+#endif
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+void internal_hashmap_free(HashmapBase *h);
+static inline void hashmap_free(Hashmap *h) {
+ internal_hashmap_free(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_free(OrderedHashmap *h) {
+ internal_hashmap_free(HASHMAP_BASE(h));
+}
+
+void internal_hashmap_free_free(HashmapBase *h);
+static inline void hashmap_free_free(Hashmap *h) {
+ internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_free_free(OrderedHashmap *h) {
+ internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+
+void hashmap_free_free_free(Hashmap *h);
+static inline void ordered_hashmap_free_free_free(OrderedHashmap *h) {
+ hashmap_free_free_free(PLAIN_HASHMAP(h));
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h);
+static inline Hashmap *hashmap_copy(Hashmap *h) {
+ return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
+ return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int hashmap_put(Hashmap *h, const void *key, void *value);
-void *hashmap_get(Hashmap *h, const void *key);
+static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_put(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_update(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_replace(PLAIN_HASHMAP(h), key, value);
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key);
+static inline void *hashmap_get(Hashmap *h, const void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+
void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
-bool hashmap_contains(Hashmap *h, const void *key);
-int hashmap_reserve(Hashmap *h, unsigned entries_add);
+static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
+ return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
+}
-unsigned hashmap_size(Hashmap *h) _pure_;
+bool internal_hashmap_contains(HashmapBase *h, const void *key);
+static inline bool hashmap_contains(Hashmap *h, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
-void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
+void *internal_hashmap_remove(HashmapBase *h, const void *key);
+static inline void *hashmap_remove(Hashmap *h, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
-void hashmap_clear(Hashmap *h);
-void hashmap_clear_free(Hashmap *h);
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
+ return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
+}
-void *hashmap_steal_first(Hashmap *h);
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
+}
-char **hashmap_get_strv(Hashmap *h);
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+ return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+ return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
+ * should just work, allow this by having looser type-checking here. */
+int internal_hashmap_merge(Hashmap *h, Hashmap *other);
+#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
+#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
+static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
+/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
+static inline int hashmap_move(Hashmap *h, Hashmap *other) {
+ return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
+ return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
+static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) _pure_;
+static inline unsigned hashmap_size(Hashmap *h) {
+ return internal_hashmap_size(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
+ return internal_hashmap_size(HASHMAP_BASE(h));
+}
+
+static inline bool hashmap_isempty(Hashmap *h) {
+ return hashmap_size(h) == 0;
+}
+static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
+ return ordered_hashmap_size(h) == 0;
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
+static inline unsigned hashmap_buckets(Hashmap *h) {
+ return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
+ return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key);
+static inline void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
+ return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
+}
+static inline void *ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, const void **key) {
+ return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
+}
+
+void internal_hashmap_clear(HashmapBase *h);
+static inline void hashmap_clear(Hashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear(OrderedHashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+void internal_hashmap_clear_free(HashmapBase *h);
+static inline void hashmap_clear_free(Hashmap *h) {
+ internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
+ internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+
+void hashmap_clear_free_free(Hashmap *h);
+static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
+ hashmap_clear_free_free(PLAIN_HASHMAP(h));
+}
+
+/*
+ * Note about all *_first*() functions
+ *
+ * For plain Hashmaps and Sets the order of entries is undefined.
+ * The functions find whatever entry is first in the implementation
+ * internal order.
+ *
+ * Only for OrderedHashmaps the order is well defined and finding
+ * the first entry is O(1).
+ */
+
+void *internal_hashmap_steal_first(HashmapBase *h);
+static inline void *hashmap_steal_first(Hashmap *h) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h);
+static inline void *hashmap_steal_first_key(Hashmap *h) {
+ return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
+ return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) _pure_;
+static inline void *hashmap_first_key(Hashmap *h) {
+ return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
+ return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first(HashmapBase *h) _pure_;
+static inline void *hashmap_first(Hashmap *h) {
+ return internal_hashmap_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first(OrderedHashmap *h) {
+ return internal_hashmap_first(HASHMAP_BASE(h));
+}
+
+/* no hashmap_next */
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
+
+char **internal_hashmap_get_strv(HashmapBase *h);
+static inline char **hashmap_get_strv(Hashmap *h) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+
+/*
+ * Hashmaps are iterated in unpredictable order.
+ * OrderedHashmaps are an exception to this. They are iterated in the order
+ * the entries were inserted.
+ * It is safe to remove the current entry.
+ */
#define HASHMAP_FOREACH(e, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); \
+ (e); \
+ (e) = hashmap_iterate((h), &(i), NULL))
+
+#define ORDERED_HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), NULL); \
+ (e); \
+ (e) = ordered_hashmap_iterate((h), &(i), NULL))
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); \
+ (e); \
+ (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
+
+#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)); \
+ (e); \
+ (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)))
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
+
#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
+#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
+#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
+#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
+#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
+#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
diff --git a/src/shared/list.h b/src/shared/list.h
new file mode 100644
index 0000000000..c020f7e936
--- /dev/null
+++ b/src/shared/list.h
@@ -0,0 +1,138 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ 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/>.
+***/
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define LIST_HEAD(t,name) \
+ t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define LIST_FIELDS(t,name) \
+ t *name##_next, *name##_prev
+
+/* Initialize the list's head */
+#define LIST_HEAD_INIT(head) \
+ do { \
+ (head) = NULL; } \
+ while(false)
+
+/* Initialize a list item */
+#define LIST_INIT(name,item) \
+ do { \
+ typeof(*(item)) *_item = (item); \
+ assert(_item); \
+ _item->name##_prev = _item->name##_next = NULL; \
+ } while(false)
+
+/* Prepend an item to the list */
+#define LIST_PREPEND(name,head,item) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_item = (item); \
+ assert(_item); \
+ if ((_item->name##_next = *_head)) \
+ _item->name##_next->name##_prev = _item; \
+ _item->name##_prev = NULL; \
+ *_head = _item; \
+ } while(false)
+
+/* Remove an item from the list */
+#define LIST_REMOVE(name,head,item) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_item = (item); \
+ assert(_item); \
+ if (_item->name##_next) \
+ _item->name##_next->name##_prev = _item->name##_prev; \
+ if (_item->name##_prev) \
+ _item->name##_prev->name##_next = _item->name##_next; \
+ else { \
+ assert(*_head == _item); \
+ *_head = _item->name##_next; \
+ } \
+ _item->name##_next = _item->name##_prev = NULL; \
+ } while(false)
+
+/* Find the head of the list */
+#define LIST_FIND_HEAD(name,item,head) \
+ do { \
+ typeof(*(item)) *_item = (item); \
+ if (!_item) \
+ (head) = NULL; \
+ else { \
+ while (_item->name##_prev) \
+ _item = _item->name##_prev; \
+ (head) = _item; \
+ } \
+ } while (false)
+
+/* Find the tail of the list */
+#define LIST_FIND_TAIL(name,item,tail) \
+ do { \
+ typeof(*(item)) *_item = (item); \
+ if (!_item) \
+ (tail) = NULL; \
+ else { \
+ while (_item->name##_next) \
+ _item = _item->name##_next; \
+ (tail) = _item; \
+ } \
+ } while (false)
+
+/* Insert an item after another one (a = where, b = what) */
+#define LIST_INSERT_AFTER(name,head,a,b) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
+ assert(_b); \
+ if (!_a) { \
+ if ((_b->name##_next = *_head)) \
+ _b->name##_next->name##_prev = _b; \
+ _b->name##_prev = NULL; \
+ *_head = _b; \
+ } else { \
+ if ((_b->name##_next = _a->name##_next)) \
+ _b->name##_next->name##_prev = _b; \
+ _b->name##_prev = _a; \
+ _a->name##_next = _b; \
+ } \
+ } while(false)
+
+#define LIST_JUST_US(name,item) \
+ (!(item)->name##_prev && !(item)->name##_next) \
+
+#define LIST_FOREACH(name,i,head) \
+ for ((i) = (head); (i); (i) = (i)->name##_next)
+
+#define LIST_FOREACH_SAFE(name,i,n,head) \
+ for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
+
+#define LIST_FOREACH_BEFORE(name,i,p) \
+ for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
+
+#define LIST_FOREACH_AFTER(name,i,p) \
+ for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+
+/* Loop starting from p->next until p->prev.
+ p can be adjusted meanwhile. */
+#define LIST_LOOP_BUT_ONE(name,i,head,p) \
+ for ((i) = (p)->name##_next ? (p)->name##_next : (head); \
+ (i) != (p); \
+ (i) = (i)->name##_next ? (i)->name##_next : (head))
diff --git a/src/shared/log.c b/src/shared/log.c
index 70708cbf0e..92361b0cbc 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -102,8 +102,8 @@ void log_close_syslog(void) {
}
static int create_log_socket(int type) {
- int fd;
struct timeval tv;
+ int fd;
fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
if (fd < 0)
@@ -118,18 +118,20 @@ static int create_log_socket(int type) {
timeval_store(&tv, 10 * USEC_PER_MSEC);
else
timeval_store(&tv, 10 * USEC_PER_SEC);
- setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
return fd;
}
static int log_open_syslog(void) {
- int r;
- union sockaddr_union sa = {
+
+ static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/dev/log",
};
+ int r;
+
if (syslog_fd >= 0)
return 0;
@@ -234,10 +236,11 @@ void log_set_max_level(int level) {
static int write_to_console(
int level,
- const char*file,
+ int error,
+ const char *file,
int line,
const char *func,
- const char *object_name,
+ const char *object_field,
const char *object,
const char *buffer) {
@@ -288,15 +291,16 @@ static int write_to_console(
}
static int write_to_syslog(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *buffer) {
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
- char header_priority[16], header_time[64], header_pid[16];
+ char header_priority[1 + DECIMAL_STR_MAX(int) + 2], header_time[64], header_pid[1 + DECIMAL_STR_MAX(pid_t) + 4];
struct iovec iovec[5] = {};
struct msghdr msghdr = {
.msg_iov = iovec,
@@ -350,15 +354,16 @@ static int write_to_syslog(
}
static int write_to_kmsg(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *buffer) {
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
- char header_priority[16], header_pid[16];
+ char header_priority[1 + DECIMAL_STR_MAX(int) + 2], header_pid[1 + DECIMAL_STR_MAX(pid_t) + 4];
struct iovec iovec[5] = {};
if (kmsg_fd < 0)
@@ -382,45 +387,15 @@ static int write_to_kmsg(
return 1;
}
-static int log_do_header(char *header, size_t size,
- int level,
- const char *file, int line, const char *func,
- const char *object_name, const char *object) {
- snprintf(header, size,
- "PRIORITY=%i\n"
- "SYSLOG_FACILITY=%i\n"
- "%s%.*s%s"
- "%s%.*i%s"
- "%s%.*s%s"
- "%s%.*s%s"
- "SYSLOG_IDENTIFIER=%s\n",
- LOG_PRI(level),
- LOG_FAC(level),
- file ? "CODE_FILE=" : "",
- file ? LINE_MAX : 0, file, /* %.0s means no output */
- file ? "\n" : "",
- line ? "CODE_LINE=" : "",
- line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
- line ? "\n" : "",
- func ? "CODE_FUNCTION=" : "",
- func ? LINE_MAX : 0, func,
- func ? "\n" : "",
- object ? object_name : "",
- object ? LINE_MAX : 0, object, /* %.0s means no output */
- object ? "\n" : "",
- program_invocation_short_name);
- header[size - 1] = '\0';
- return 0;
-}
-
static int log_dispatch(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- char *buffer) {
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ char *buffer) {
int r = 0;
@@ -449,8 +424,7 @@ static int log_dispatch(
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {
- k = write_to_syslog(level, file, line, func,
- object_name, object, buffer);
+ k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_syslog();
@@ -465,8 +439,7 @@ static int log_dispatch(
log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_KMSG)) {
- k = write_to_kmsg(level, file, line, func,
- object_name, object, buffer);
+ k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
if (k < 0) {
log_close_kmsg();
log_open_console();
@@ -475,8 +448,7 @@ static int log_dispatch(
}
if (k <= 0) {
- k = write_to_console(level, file, line, func,
- object_name, object, buffer);
+ k = write_to_console(level, error, file, line, func, object_field, object, buffer);
if (k < 0)
return k;
}
@@ -487,8 +459,9 @@ static int log_dispatch(
return r;
}
-int log_metav(
+int log_internalv(
int level,
+ int error,
const char*file,
int line,
const char *func,
@@ -504,27 +477,34 @@ int log_metav(
vsnprintf(buffer, sizeof(buffer), format, ap);
char_array_0(buffer);
- return log_dispatch(level, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
}
-int log_meta(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format, ...) {
+int log_internal(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format, ...) {
int r;
va_list ap;
va_start(ap, format);
- r = log_metav(level, file, line, func, format, ap);
+ r = log_internalv(level, error, file, line, func, format, ap);
va_end(ap);
return r;
}
-static void log_assert(int level, const char *text, const char *file, int line, const char *func, const char *format) {
+static void log_assert(
+ int level,
+ const char *text,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format) {
static char buffer[LINE_MAX];
if (_likely_(LOG_PRI(level) > log_max_level))
@@ -537,7 +517,7 @@ static void log_assert(int level, const char *text, const char *file, int line,
char_array_0(buffer);
log_abort_msg = buffer;
- log_dispatch(level, file, line, func, NULL, NULL, buffer);
+ log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
}
noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
@@ -551,7 +531,7 @@ noreturn void log_assert_failed_unreachable(const char *text, const char *file,
}
int log_oom_internal(const char *file, int line, const char *func) {
- log_meta(LOG_ERR, file, line, func, "Out of memory.");
+ log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory.");
return -ENOMEM;
}
diff --git a/src/shared/log.h b/src/shared/log.h
index 30f0cacb65..485f3fa0f6 100644
--- a/src/shared/log.h
+++ b/src/shared/log.h
@@ -52,26 +52,29 @@ void log_close_journal(void);
void log_close_kmsg(void);
void log_close_console(void);
-int log_meta(
+int log_internal(
int level,
- const char*file,
+ int error,
+ const char *file,
int line,
const char *func,
- const char *format, ...) _printf_(5,6);
+ const char *format, ...) _printf_(6,7);
-int log_metav(
+int log_internalv(
int level,
- const char*file,
+ int error,
+ const char *file,
int line,
const char *func,
const char *format,
- va_list ap) _printf_(5,0);
+ va_list ap) _printf_(6,0);
int log_oom_internal(
const char *file,
int line,
const char *func);
+/* Logging for various assertions */
noreturn void log_assert_failed(
const char *text,
const char *file,
@@ -85,17 +88,32 @@ noreturn void log_assert_failed_unreachable(
const char *func);
-#define log_full(level, ...) \
-do { \
- if (log_get_max_level() >= (level)) \
- log_meta((level), __FILE__, __LINE__, __func__, __VA_ARGS__); \
-} while (0)
-
-#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
-#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
-#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
-#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
-#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
+/* Logging with level */
+#define log_full_errno(level, error, ...) \
+ ({ \
+ int _l = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_l)) \
+ ? log_internal(_l, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
+ })
+
+#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
+
+/* Normal logging */
+#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
+#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
+#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
+#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
+#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
+#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
+
+/* Logging triggered by an errno-like error */
+#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
+#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__)
+#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
+#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
+#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
@@ -103,7 +121,7 @@ do { \
# define log_trace(...) do {} while(0)
#endif
-#define log_struct(level, ...) log_struct_internal(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
+/* This modifies the buffer passed! */
#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
diff --git a/src/shared/macro.h b/src/shared/macro.h
index cea13182ec..41fe61fb68 100644
--- a/src/shared/macro.h
+++ b/src/shared/macro.h
@@ -35,6 +35,7 @@
#define _likely_(x) (__builtin_expect(!!(x),1))
#define _unlikely_(x) (__builtin_expect(!!(x),0))
#define _public_ __attribute__ ((visibility("default")))
+#define _alignas_(x) __attribute__((aligned(__alignof(x))))
#define _cleanup_(x) __attribute__((cleanup(x)))
/* Temporarily disable some warnings */
@@ -236,4 +237,8 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
#endif
#endif
+#define UID_INVALID ((uid_t) -1)
+#define GID_INVALID ((gid_t) -1)
+#define MODE_INVALID ((mode_t) -1)
+
#include "log.h"
diff --git a/src/shared/mempool.c b/src/shared/mempool.c
index b39a37f2db..d5d98d8829 100644
--- a/src/shared/mempool.c
+++ b/src/shared/mempool.c
@@ -74,6 +74,15 @@ void* mempool_alloc_tile(struct mempool *mp) {
return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
}
+void* mempool_alloc0_tile(struct mempool *mp) {
+ void *p;
+
+ p = mempool_alloc_tile(mp);
+ if (p)
+ memzero(p, mp->tile_size);
+ return p;
+}
+
void mempool_free_tile(struct mempool *mp, void *p) {
* (void**) p = mp->freelist;
mp->freelist = p;
diff --git a/src/shared/mempool.h b/src/shared/mempool.h
index 8b0bf381bf..42f473bee1 100644
--- a/src/shared/mempool.h
+++ b/src/shared/mempool.h
@@ -34,6 +34,7 @@ struct mempool {
};
void* mempool_alloc_tile(struct mempool *mp);
+void* mempool_alloc0_tile(struct mempool *mp);
void mempool_free_tile(struct mempool *mp, void *p);
#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
diff --git a/src/shared/missing.h b/src/shared/missing.h
index 572e17117a..00b5f7811c 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -42,6 +42,39 @@
/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */
#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS)
+#ifndef __NR_getrandom
+# if defined __x86_64__
+# define __NR_getrandom 318
+# elif defined(__i386__)
+# define __NR_getrandom 355
+# elif defined(__arm__) || defined(__aarch64__)
+# define __NR_getrandom 384
+# elif defined(__ia64__)
+# define __NR_getrandom 1339
+# elif defined(__m68k__)
+# define __NR_getrandom 352
+# elif defined(__s390x__)
+# define __NR_getrandom 349
+# else
+# warning "__NR_getrandom unknown for your architecture"
+# define __NR_getrandom 0xffffffff
+# endif
+#endif
+
+#if !HAVE_DECL_GETRANDOM
+static inline int getrandom(void *buffer, size_t count, unsigned flags) {
+ return syscall(__NR_getrandom, buffer, count, flags);
+}
+#endif
+
+#ifndef GRND_NONBLOCK
+#define GRND_NONBLOCK 0x0001
+#endif
+
+#ifndef GRND_RANDOM
+#define GRND_RANDOM 0x0002
+#endif
+
#ifndef BTRFS_IOCTL_MAGIC
#define BTRFS_IOCTL_MAGIC 0x94
#endif
diff --git a/src/shared/mkdir.c b/src/shared/mkdir.c
index 3bc5131e40..dc400d9b91 100644
--- a/src/shared/mkdir.c
+++ b/src/shared/mkdir.c
@@ -42,8 +42,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkd
if ((st.st_mode & 0007) > (mode & 0007) ||
(st.st_mode & 0070) > (mode & 0070) ||
(st.st_mode & 0700) > (mode & 0700) ||
- (uid != (uid_t) -1 && st.st_uid != uid) ||
- (gid != (gid_t) -1 && st.st_gid != gid) ||
+ (uid != UID_INVALID && st.st_uid != uid) ||
+ (gid != GID_INVALID && st.st_gid != gid) ||
!S_ISDIR(st.st_mode)) {
errno = EEXIST;
return -errno;
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index ccd5c7ecc6..b1f997aef3 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -112,7 +112,7 @@ char *path_make_absolute_cwd(const char *p) {
if (!cwd)
return NULL;
- return path_make_absolute(p, cwd);
+ return strjoin(cwd, "/", p, NULL);
}
char **path_strv_resolve(char **l, const char *prefix) {
@@ -183,6 +183,7 @@ char **path_strv_resolve(char **l, const char *prefix) {
} else {
/* canonicalized path goes outside of
* prefix, keep the original path instead */
+ free(u);
u = orig;
orig = NULL;
}
@@ -318,10 +319,11 @@ int path_is_mount_point(const char *t, bool allow_symlink) {
.handle.handle_bytes = MAX_HANDLE_SZ
};
- int mount_id, mount_id_parent;
+ int mount_id = -1, mount_id_parent = -1;
_cleanup_free_ char *parent = NULL;
struct stat a, b;
int r;
+ bool nosupp = false;
/* We are not actually interested in the file handles, but
* name_to_handle_at() also passes us the mount ID, hence use
@@ -332,16 +334,19 @@ int path_is_mount_point(const char *t, bool allow_symlink) {
r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
if (r < 0) {
- if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
+ if (errno == ENOSYS)
+ /* This kernel does not support name_to_handle_at()
+ * fall back to the traditional stat() logic. */
+ goto fallback;
+ else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
* name_to_handle_at(), hence fallback to the
* traditional stat() logic */
- goto fallback;
-
- if (errno == ENOENT)
+ nosupp = true;
+ else if (errno == ENOENT)
return 0;
-
- return -errno;
+ else
+ return -errno;
}
r = path_get_parent(t, &parent);
@@ -349,18 +354,23 @@ int path_is_mount_point(const char *t, bool allow_symlink) {
return r;
h.handle.handle_bytes = MAX_HANDLE_SZ;
- r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0);
- if (r < 0) {
- /* The parent can't do name_to_handle_at() but the
- * directory we are interested in can? If so, it must
- * be a mount point */
+ r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
+ if (r < 0)
if (errno == EOPNOTSUPP)
- return 1;
-
- return -errno;
- }
-
- return mount_id != mount_id_parent;
+ if (nosupp)
+ /* Neither parent nor child do name_to_handle_at()?
+ We have no choice but to fall back. */
+ goto fallback;
+ else
+ /* The parent can't do name_to_handle_at() but
+ * the directory we are interested in can?
+ * Or the other way around?
+ * If so, it must be a mount point. */
+ return 1;
+ else
+ return -errno;
+ else
+ return mount_id != mount_id_parent;
fallback:
if (allow_symlink)
@@ -375,11 +385,14 @@ fallback:
return -errno;
}
+ free(parent);
+ parent = NULL;
+
r = path_get_parent(t, &parent);
if (r < 0)
return r;
- r = lstat(parent, &b);
+ r = stat(parent, &b);
if (r < 0)
return -errno;
diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c
index e2ccef2865..7f8cc0eb76 100644
--- a/src/shared/selinux-util.c
+++ b/src/shared/selinux-util.c
@@ -231,7 +231,7 @@ int mac_selinux_get_our_label(char **label) {
return r;
}
-int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label) {
+int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
int r = -EOPNOTSUPP;
#ifdef HAVE_SELINUX
@@ -255,11 +255,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label
if (r < 0)
return -errno;
- r = getexeccon(&fcon);
- if (r < 0)
- return -errno;
-
- if (!fcon) {
+ if (!exec_label) {
/* If there is no context set for next exec let's use context
of target executable */
r = getfilecon(exe, &fcon);
@@ -330,9 +326,13 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
}
- if (r < 0 && errno != ENOENT)
+ /* No context specified by the policy? Proceed without setting it. */
+ if (r < 0 && errno == ENOENT)
+ return 0;
+
+ if (r < 0)
r = -errno;
- else if (r == 0) {
+ else {
r = setfscreatecon(filecon);
if (r < 0) {
log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h
index dffd144513..744b1bcf45 100644
--- a/src/shared/selinux-util.h
+++ b/src/shared/selinux-util.h
@@ -35,7 +35,7 @@ int mac_selinux_apply(const char *path, const char *label);
int mac_selinux_get_create_label_from_exe(const char *exe, char **label);
int mac_selinux_get_our_label(char **label);
-int mac_selinux_get_child_mls_label(int socket_fd, const char *exec, char **label);
+int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label);
void mac_selinux_free(char *label);
int mac_selinux_create_file_prepare(const char *path, mode_t mode);
diff --git a/src/shared/set.c b/src/shared/set.c
deleted file mode 100644
index 087d874598..0000000000
--- a/src/shared/set.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/***
- This file is part of eudev, forked from systemd.
-
- Copyright 2010 Lennart Poettering
-
- 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 <stdlib.h>
-
-#include "set.h"
-#include "hashmap.h"
-
-#define MAKE_SET(h) ((Set*) (h))
-#define MAKE_HASHMAP(s) ((Hashmap*) (s))
-
-/* For now this is not much more than a wrapper around a hashmap */
-
-Set *set_new(const struct hash_ops *hash_ops) {
- return MAKE_SET(hashmap_new(hash_ops));
-}
-
-void set_free(Set* s) {
- hashmap_free(MAKE_HASHMAP(s));
-}
-
-int set_put(Set *s, void *value) {
- return hashmap_put(MAKE_HASHMAP(s), value, value);
-}
-
-void *set_get(Set *s, void *value) {
- return hashmap_get(MAKE_HASHMAP(s), value);
-}
-
-bool set_contains(Set *s, void *value) {
- return hashmap_contains(MAKE_HASHMAP(s), value);
-}
-
-void *set_iterate(Set *s, Iterator *i) {
- return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
-}
-
-int set_reserve(Set *s, unsigned entries_add) {
- return hashmap_reserve(MAKE_HASHMAP(s), entries_add);
-}
diff --git a/src/shared/set.h b/src/shared/set.h
index ae3090a09f..4605ecd2c1 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -1,5 +1,9 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
/***
- This file is part of eudev, forked from systemd.
+ This file is part of systemd.
Copyright 2010 Lennart Poettering
@@ -17,29 +21,114 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
-/* Pretty straightforward set implementation. Internally based on the
- * hashmap. That means that as a minor optimization a NULL set
- * object will be treated as empty set for all read
- * operations. That way it is not necessary to instantiate an object
- * for each set use. */
-
#include "hashmap.h"
#include "util.h"
-typedef struct Set Set;
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+
+static inline void set_free(Set *s) {
+ internal_hashmap_free(HASHMAP_BASE(s));
+}
+
+static inline void set_free_free(Set *s) {
+ internal_hashmap_free_free(HASHMAP_BASE(s));
+}
+
+/* no set_free_free_free */
+
+static inline Set *set_copy(Set *s) {
+ return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+
+int set_put(Set *s, const void *key);
+/* no set_update */
+/* no set_replace */
+static inline void *set_get(Set *s, void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(s), key);
+}
+/* no set_get2 */
+
+static inline bool set_contains(Set *s, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(s), key);
+}
+
+static inline void *set_remove(Set *s, void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(s), key);
+}
-Set *set_new(const struct hash_ops *hash_ops);
-void set_free(Set* s);
+/* no set_remove2 */
+/* no set_remove_value */
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
+/* no set_remove_and_replace */
+int set_merge(Set *s, Set *other);
-int set_put(Set *s, void *value);
-void *set_get(Set *s, void *value);
-bool set_contains(Set *s, void *value);
+static inline int set_reserve(Set *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
-int set_reserve(Set *s, unsigned entries_add);
+static inline int set_move(Set *s, Set *other) {
+ return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
+}
+
+static inline int set_move_one(Set *s, Set *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
+}
+
+static inline unsigned set_size(Set *s) {
+ return internal_hashmap_size(HASHMAP_BASE(s));
+}
+
+static inline bool set_isempty(Set *s) {
+ return set_size(s) == 0;
+}
+
+static inline unsigned set_buckets(Set *s) {
+ return internal_hashmap_buckets(HASHMAP_BASE(s));
+}
void *set_iterate(Set *s, Iterator *i);
+static inline void set_clear(Set *s) {
+ internal_hashmap_clear(HASHMAP_BASE(s));
+}
+
+static inline void set_clear_free(Set *s) {
+ internal_hashmap_clear_free(HASHMAP_BASE(s));
+}
+
+/* no set_clear_free_free */
+
+static inline void *set_steal_first(Set *s) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(s));
+}
+
+/* no set_steal_first_key */
+/* no set_first_key */
+
+static inline void *set_first(Set *s) {
+ return internal_hashmap_first(HASHMAP_BASE(s));
+}
+
+/* no set_next */
+
+static inline char **set_get_strv(Set *s) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(s));
+}
+
+int set_consume(Set *s, void *value);
+int set_put_strdup(Set *s, const char *p);
+int set_put_strdupv(Set *s, char **l);
+
+#define SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
+
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
+
#define _cleanup_set_free_ _cleanup_(set_freep)
+#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c
index 407a8e1aa3..65bbb94e7c 100644
--- a/src/shared/smack-util.c
+++ b/src/shared/smack-util.c
@@ -25,6 +25,7 @@
#include "util.h"
#include "path-util.h"
+#include "fileio.h"
#include "smack-util.h"
#define SMACK_FLOOR_LABEL "_"
@@ -123,14 +124,38 @@ int mac_smack_apply_ip_in_fd(int fd, const char *label) {
return r;
}
-int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+int mac_smack_apply_pid(pid_t pid, const char *label) {
+
+#ifdef HAVE_SMACK
+ const char *p;
+#endif
int r = 0;
+ assert(label);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ p = procfs_file_alloca(pid, "attr/current");
+ r = write_string_file(p, label);
+ if (r < 0)
+ return r;
+#endif
+
+ return r;
+}
+
+int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+
#ifdef HAVE_SMACK
struct stat st;
+#endif
+ int r = 0;
assert(path);
+#ifdef HAVE_SMACK
if (!mac_smack_use())
return 0;
@@ -174,8 +199,7 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
if (ignore_erofs && errno == EROFS)
return 0;
- log_debug("Unable to fix SMACK label of %s: %m", path);
- r = -errno;
+ r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path);
}
#endif
diff --git a/src/shared/smack-util.h b/src/shared/smack-util.h
index 22aef5ac23..587c135308 100644
--- a/src/shared/smack-util.h
+++ b/src/shared/smack-util.h
@@ -29,5 +29,6 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
int mac_smack_apply(const char *path, const char *label);
int mac_smack_apply_fd(int fd, const char *label);
+int mac_smack_apply_pid(pid_t pid, const char *label);
int mac_smack_apply_ip_in_fd(int fd, const char *label);
int mac_smack_apply_ip_out_fd(int fd, const char *label);
diff --git a/src/shared/sparse-endian.h b/src/shared/sparse-endian.h
index 51694bebb7..c913fda8c5 100644
--- a/src/shared/sparse-endian.h
+++ b/src/shared/sparse-endian.h
@@ -21,9 +21,9 @@
#ifndef SPARSE_ENDIAN_H
#define SPARSE_ENDIAN_H
+#include <byteswap.h>
#include <endian.h>
#include <stdint.h>
-#include <byteswap.h>
#ifdef __CHECKER__
#define __bitwise __attribute__((bitwise))
diff --git a/src/shared/strv.c b/src/shared/strv.c
index 85ae556c16..5cfab8f23e 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -26,7 +26,7 @@
#include "util.h"
#include "strv.h"
-void strv_free(char **l) {
+void strv_clear(char **l) {
char **k;
if (!l)
@@ -35,6 +35,11 @@ void strv_free(char **l) {
for (k = l; *k; k++)
free(*k);
+ *l = NULL;
+}
+
+void strv_free(char **l) {
+ strv_clear(l);
free(l);
}
diff --git a/src/shared/strv.h b/src/shared/strv.h
index 22c8132c57..94bdbe62aa 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -28,6 +28,8 @@ void strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
+void strv_clear(char **l);
+
char **strv_copy(char * const *l);
unsigned strv_length(char * const *l) _pure_;
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
index 403166d7f9..fec2f4df34 100644
--- a/src/shared/utf8.c
+++ b/src/shared/utf8.c
@@ -148,10 +148,12 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
int encoded_len, val;
encoded_len = utf8_encoded_valid_unichar((const char *) p);
- val = utf8_encoded_to_unichar((const char*) p);
-
if (encoded_len < 0 ||
- val < 0 ||
+ (size_t) encoded_len > length)
+ return false;
+
+ val = utf8_encoded_to_unichar((const char*) p);
+ if (val < 0 ||
is_unicode_control(val) ||
(!newline && val == '\n'))
return false;
@@ -175,39 +177,91 @@ char *ascii_is_valid(const char *str) {
return (char*) str;
}
+/**
+ * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
+ * @out_utf8: output buffer of at least 4 bytes or NULL
+ * @g: UCS-4 character to encode
+ *
+ * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
+ * The length of the character is returned. It is not zero-terminated! If the
+ * output buffer is NULL, only the length is returned.
+ *
+ * Returns: The length in bytes that the UTF-8 representation does or would
+ * occupy.
+ */
+size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
+ if (g < (1 << 7)) {
+ if (out_utf8)
+ out_utf8[0] = g & 0x7f;
+ return 1;
+ } else if (g < (1 << 11)) {
+ if (out_utf8) {
+ out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
+ out_utf8[1] = 0x80 | (g & 0x3f);
+ }
+ return 2;
+ } else if (g < (1 << 16)) {
+ if (out_utf8) {
+ out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
+ out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
+ out_utf8[2] = 0x80 | (g & 0x3f);
+ }
+ return 3;
+ } else if (g < (1 << 21)) {
+ if (out_utf8) {
+ out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
+ out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
+ out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
+ out_utf8[3] = 0x80 | (g & 0x3f);
+ }
+ return 4;
+ } else {
+ return 0;
+ }
+}
+
char *utf16_to_utf8(const void *s, size_t length) {
- char *r;
const uint8_t *f;
- uint8_t *t;
+ char *r, *t;
- r = new(char, (length*3+1)/2 + 1);
+ r = new(char, (length * 4 + 1) / 2 + 1);
if (!r)
return NULL;
- t = (uint8_t*) r;
-
- for (f = s; f < (const uint8_t*) s + length; f += 2) {
- uint16_t c;
-
- c = (f[1] << 8) | f[0];
-
- if (c == 0) {
- *t = 0;
- return r;
- } else if (c < 0x80) {
- *(t++) = (uint8_t) c;
- } else if (c < 0x800) {
- *(t++) = (uint8_t) (0xc0 | (c >> 6));
- *(t++) = (uint8_t) (0x80 | (c & 0x3f));
- } else {
- *(t++) = (uint8_t) (0xe0 | (c >> 12));
- *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
- *(t++) = (uint8_t) (0x80 | (c & 0x3f));
+ f = s;
+ t = r;
+
+ while (f < (const uint8_t*) s + length) {
+ uint16_t w1, w2;
+
+ /* see RFC 2781 section 2.2 */
+
+ w1 = f[1] << 8 | f[0];
+ f += 2;
+
+ if (!utf16_is_surrogate(w1)) {
+ t += utf8_encode_unichar(t, w1);
+
+ continue;
}
+
+ if (utf16_is_trailing_surrogate(w1))
+ continue;
+ else if (f >= (const uint8_t*) s + length)
+ break;
+
+ w2 = f[1] << 8 | f[0];
+ f += 2;
+
+ if (!utf16_is_trailing_surrogate(w2)) {
+ f -= 2;
+ continue;
+ }
+
+ t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
}
*t = 0;
-
return r;
}
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
index 24232247f5..143b0ba08c 100644
--- a/src/shared/utf8.h
+++ b/src/shared/utf8.h
@@ -30,7 +30,20 @@ _pure_ static inline bool utf8_is_printable(const char* str, size_t length) {
return utf8_is_printable_newline(str, length, true);
}
+size_t utf8_encode_unichar(char *out_utf8, uint32_t g);
char *utf16_to_utf8(const void *s, size_t length);
int utf8_encoded_valid_unichar(const char *str);
int utf8_encoded_to_unichar(const char *str);
+
+static inline bool utf16_is_surrogate(uint16_t c) {
+ return (0xd800 <= c && c <= 0xdfff);
+}
+
+static inline bool utf16_is_trailing_surrogate(uint16_t c) {
+ return (0xdc00 <= c && c <= 0xdfff);
+}
+
+static inline uint32_t utf16_surrogate_pair_to_unichar(uint16_t lead, uint16_t trail) {
+ return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
+}
diff --git a/src/shared/util.c b/src/shared/util.c
index 3cad297873..6ef53b05ef 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -37,7 +37,6 @@
#include <linux/tiocl.h>
#include <termios.h>
#include <stdarg.h>
-#include <sys/inotify.h>
#include <poll.h>
#include <ctype.h>
#include <sys/prctl.h>
@@ -124,6 +123,69 @@ char* endswith(const char *s, const char *postfix) {
return (char*) s + sl - pl;
}
+static size_t cescape_char(char c, char *buf) {
+ char * buf_old = buf;
+
+ switch (c) {
+
+ case '\a':
+ *(buf++) = '\\';
+ *(buf++) = 'a';
+ break;
+ case '\b':
+ *(buf++) = '\\';
+ *(buf++) = 'b';
+ break;
+ case '\f':
+ *(buf++) = '\\';
+ *(buf++) = 'f';
+ break;
+ case '\n':
+ *(buf++) = '\\';
+ *(buf++) = 'n';
+ break;
+ case '\r':
+ *(buf++) = '\\';
+ *(buf++) = 'r';
+ break;
+ case '\t':
+ *(buf++) = '\\';
+ *(buf++) = 't';
+ break;
+ case '\v':
+ *(buf++) = '\\';
+ *(buf++) = 'v';
+ break;
+ case '\\':
+ *(buf++) = '\\';
+ *(buf++) = '\\';
+ break;
+ case '"':
+ *(buf++) = '\\';
+ *(buf++) = '"';
+ break;
+ case '\'':
+ *(buf++) = '\\';
+ *(buf++) = '\'';
+ break;
+
+ default:
+ /* For special chars we prefer octal over
+ * hexadecimal encoding, simply because glib's
+ * g_strescape() does the same */
+ if ((c < ' ') || (c >= 127)) {
+ *(buf++) = '\\';
+ *(buf++) = octchar((unsigned char) c >> 6);
+ *(buf++) = octchar((unsigned char) c >> 3);
+ *(buf++) = octchar((unsigned char) c);
+ } else
+ *(buf++) = c;
+ break;
+ }
+
+ return buf - buf_old;
+}
+
int close_nointr(int fd) {
assert(fd >= 0);
@@ -197,7 +259,7 @@ int parse_uid(const char *s, uid_t* ret_uid) {
if ((unsigned long) uid != ul)
return -ERANGE;
- /* Some libc APIs use (uid_t) -1 as special placeholder */
+ /* Some libc APIs use UID_INVALID as special placeholder */
if (uid == (uid_t) 0xFFFFFFFF)
return -ENXIO;
@@ -285,7 +347,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
static size_t strcspn_escaped(const char *s, const char *reject) {
bool escaped = false;
- size_t n;
+ int n;
for (n=0; s[n]; n++) {
if (escaped)
@@ -295,6 +357,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
else if (strchr(reject, s[n]))
break;
}
+
/* if s ends in \, return index of previous char */
return n - escaped;
}
@@ -322,7 +385,7 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
*l = strcspn_escaped(current + 1, quotechars);
if (current[*l + 1] == '\0' ||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) {
- /* right quote missing or garbage at the end*/
+ /* right quote missing or garbage at the end */
*state = current;
return NULL;
}
@@ -330,6 +393,11 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
*state = current++ + *l + 2;
} else if (quoted) {
*l = strcspn_escaped(current, separator);
+ if (current[*l] && !strchr(separator, current[*l])) {
+ /* unfinished escape */
+ *state = current;
+ return NULL;
+ }
*state = current + *l;
} else {
*l = strcspn(current, separator);
@@ -346,6 +414,39 @@ char *truncate_nl(char *s) {
return s;
}
+int get_process_environ(pid_t pid, char **env) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *outcome = NULL;
+ int c;
+ const char *p;
+ size_t allocated = 0, sz = 0;
+
+ assert(pid >= 0);
+ assert(env);
+
+ p = procfs_file_alloca(pid, "environ");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
+ return -ENOMEM;
+
+ if (c == '\0')
+ outcome[sz++] = '\n';
+ else
+ sz += cescape_char(c, outcome + sz);
+ }
+
+ outcome[sz] = '\0';
+ *env = outcome;
+ outcome = NULL;
+
+ return 0;
+}
+
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
@@ -427,6 +528,115 @@ int rmdir_parents(const char *path, const char *stop) {
return 0;
}
+int get_process_comm(pid_t pid, char **name) {
+ const char *p;
+ int r;
+
+ assert(name);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "comm");
+
+ r = read_one_line_file(p, name);
+ if (r == -ENOENT)
+ return -ESRCH;
+
+ return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *r = NULL, *k;
+ const char *p;
+ int c;
+
+ assert(line);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ if (max_length == 0) {
+ size_t len = 0, allocated = 0;
+
+ while ((c = getc(f)) != EOF) {
+
+ if (!GREEDY_REALLOC(r, allocated, len+2)) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ r[len++] = isprint(c) ? c : ' ';
+ }
+
+ if (len > 0)
+ r[len-1] = 0;
+
+ } else {
+ bool space = false;
+ size_t left;
+
+ r = new(char, max_length);
+ if (!r)
+ return -ENOMEM;
+
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
+
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
+
+ if (left <= 4)
+ break;
+
+ *(k++) = (char) c;
+ left--;
+ } else
+ space = true;
+ }
+
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
+ }
+
+ /* Kernel threads have no argv[] */
+ if (isempty(r)) {
+ _cleanup_free_ char *t = NULL;
+ int h;
+
+ free(r);
+
+ if (!comm_fallback)
+ return -ENOENT;
+
+ h = get_process_comm(pid, &t);
+ if (h < 0)
+ return h;
+
+ r = strjoin("[", t, "]", NULL);
+ if (!r)
+ return -ENOMEM;
+ }
+
+ *line = r;
+ return 0;
+}
+
char hexchar(int x) {
static const char table[16] = "0123456789abcdef";
@@ -450,63 +660,7 @@ char *cescape(const char *s) {
return NULL;
for (f = s, t = r; *f; f++)
-
- switch (*f) {
-
- case '\a':
- *(t++) = '\\';
- *(t++) = 'a';
- break;
- case '\b':
- *(t++) = '\\';
- *(t++) = 'b';
- break;
- case '\f':
- *(t++) = '\\';
- *(t++) = 'f';
- break;
- case '\n':
- *(t++) = '\\';
- *(t++) = 'n';
- break;
- case '\r':
- *(t++) = '\\';
- *(t++) = 'r';
- break;
- case '\t':
- *(t++) = '\\';
- *(t++) = 't';
- break;
- case '\v':
- *(t++) = '\\';
- *(t++) = 'v';
- break;
- case '\\':
- *(t++) = '\\';
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '\\';
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\\';
- *(t++) = '\'';
- break;
-
- default:
- /* For special chars we prefer octal over
- * hexadecimal encoding, simply because glib's
- * g_strescape() does the same */
- if ((*f < ' ') || (*f >= 127)) {
- *(t++) = '\\';
- *(t++) = octchar((unsigned char) *f >> 6);
- *(t++) = octchar((unsigned char) *f >> 3);
- *(t++) = octchar((unsigned char) *f);
- } else
- *(t++) = *f;
- break;
- }
+ t += cescape_char(*f, t);
*t = 0;
@@ -542,7 +696,7 @@ char *xescape(const char *s, const char *bad) {
return r;
}
-_pure_ static bool ignore_file_allow_backup(const char *filename) {
+_pure_ static bool hidden_file_allow_backup(const char *filename) {
assert(filename);
return
@@ -559,13 +713,13 @@ _pure_ static bool ignore_file_allow_backup(const char *filename) {
endswith(filename, ".swp");
}
-bool ignore_file(const char *filename) {
+bool hidden_file(const char *filename) {
assert(filename);
if (endswith(filename, "~"))
return true;
- return ignore_file_allow_backup(filename);
+ return hidden_file_allow_backup(filename);
}
int open_terminal(const char *name, int mode) {
@@ -660,21 +814,25 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
ssize_t k;
k = read(fd, p, nbytes);
- if (k < 0 && errno == EINTR)
- continue;
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
- if (k < 0 && errno == EAGAIN && do_poll) {
+ if (errno == EAGAIN && do_poll) {
- /* We knowingly ignore any return value here,
- * and expect that any error/EOF is reported
- * via read() */
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via read() */
- fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
- continue;
+ fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
+ continue;
+ }
+
+ return n > 0 ? n : -errno;
}
- if (k <= 0)
- return n > 0 ? n : (k < 0 ? -errno : 0);
+ if (k == 0)
+ return n;
p += k;
nbytes -= k;
@@ -685,14 +843,53 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
}
int dev_urandom(void *p, size_t n) {
- _cleanup_close_ int fd;
+ static int have_syscall = -1;
+ int r, fd;
ssize_t k;
+ /* Gathers some randomness from the kernel. This call will
+ * never block, and will always return some data from the
+ * kernel, regardless if the random pool is fully initialized
+ * or not. It thus makes no guarantee for the quality of the
+ * returned entropy, but is good enough for or usual usecases
+ * of seeding the hash functions for hashtable */
+
+ /* Use the getrandom() syscall unless we know we don't have
+ * it, or when the requested size is too large for it. */
+ if (have_syscall != 0 || (size_t) (int) n != n) {
+ r = getrandom(p, n, GRND_NONBLOCK);
+ if (r == (int) n) {
+ have_syscall = true;
+ return 0;
+ }
+
+ if (r < 0) {
+ if (errno == ENOSYS)
+ /* we lack the syscall, continue with
+ * reading from /dev/urandom */
+ have_syscall = false;
+ else if (errno == EAGAIN)
+ /* not enough entropy for now. Let's
+ * remember to use the syscall the
+ * next time, again, but also read
+ * from /dev/urandom for now, which
+ * doesn't care about the current
+ * amount of entropy. */
+ have_syscall = true;
+ else
+ return -errno;
+ } else
+ /* too short read? */
+ return -EIO;
+ }
+
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return errno == ENOENT ? -ENOSYS : -errno;
k = loop_read(fd, p, n, true);
+ safe_close(fd);
+
if (k < 0)
return (int) k;
if ((size_t) k != n)
@@ -701,8 +898,36 @@ int dev_urandom(void *p, size_t n) {
return 0;
}
-void random_bytes(void *p, size_t n) {
+void initialize_srand(void) {
static bool srand_called = false;
+ unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+ void *auxv;
+#endif
+
+ if (srand_called)
+ return;
+
+ x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+ /* The kernel provides us with a bit of entropy in auxv, so
+ * let's try to make use of that to seed the pseudo-random
+ * generator. It's better than nothing... */
+
+ auxv = (void*) getauxval(AT_RANDOM);
+ if (auxv)
+ x ^= *(unsigned*) auxv;
+#endif
+
+ x ^= (unsigned) now(CLOCK_REALTIME);
+ x ^= (unsigned) gettid();
+
+ srand(x);
+ srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
uint8_t *q;
int r;
@@ -713,28 +938,7 @@ void random_bytes(void *p, size_t n) {
/* If some idiot made /dev/urandom unavailable to us, he'll
* get a PRNG instead. */
- if (!srand_called) {
- unsigned x = 0;
-
-#ifdef HAVE_SYS_AUXV_H
- /* The kernel provides us with a bit of entropy in
- * auxv, so let's try to make use of that to seed the
- * pseudo-random generator. It's better than
- * nothing... */
-
- void *auxv;
-
- auxv = (void*) getauxval(AT_RANDOM);
- if (auxv)
- x ^= *(unsigned*) auxv;
-#endif
-
- x ^= (unsigned) now(CLOCK_REALTIME);
- x ^= (unsigned) gettid();
-
- srand(x);
- srand_called = true;
- }
+ initialize_srand();
for (q = p; q < (uint8_t*) p + n; q ++)
*q = rand();
@@ -754,11 +958,11 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
* first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
- if (mode != (mode_t) -1)
+ if (mode != MODE_INVALID)
if (chmod(path, mode) < 0)
return -errno;
- if (uid != (uid_t) -1 || gid != (gid_t) -1)
+ if (uid != UID_INVALID || gid != GID_INVALID)
if (chown(path, uid, gid) < 0)
return -errno;
@@ -807,7 +1011,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
de->d_type != DT_UNKNOWN)
return false;
- if (ignore_file_allow_backup(de->d_name))
+ if (hidden_file_allow_backup(de->d_name))
return false;
return endswith(de->d_name, suffix);
@@ -849,15 +1053,15 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
FILE *f;
char *t;
- int fd;
+ int r, fd;
assert(path);
assert(_f);
assert(_temp_path);
- t = tempfn_xxxxxx(path);
- if (!t)
- return -ENOMEM;
+ r = tempfn_xxxxxx(path, &t);
+ if (r < 0)
+ return r;
#if HAVE_DECL_MKOSTEMP
fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
@@ -1248,6 +1452,26 @@ bool in_initrd(void) {
return saved;
}
+bool filename_is_valid(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (strchr(p, '/'))
+ return false;
+
+ if (streq(p, "."))
+ return false;
+
+ if (streq(p, ".."))
+ return false;
+
+ if (strlen(p) > FILENAME_MAX)
+ return false;
+
+ return true;
+}
+
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *), void *arg) {
@@ -1271,31 +1495,39 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
return NULL;
}
-int proc_cmdline(char **ret) {
- int r;
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
+ size_t a, newalloc;
+ void *q;
- if (detect_container(NULL) > 0) {
- char *buf = NULL, *p;
- size_t sz = 0;
+ assert(p);
+ assert(allocated);
- r = read_full_file("/proc/1/cmdline", &buf, &sz);
- if (r < 0)
- return r;
+ if (*allocated >= need)
+ return *p;
- for (p = buf; p + 1 < buf + sz; p++)
- if (*p == 0)
- *p = ' ';
+ newalloc = MAX(need * 2, 64u / size);
+ a = newalloc * size;
- *p = 0;
- *ret = buf;
- return 1;
- }
+ /* check for overflows */
+ if (a < size * need)
+ return NULL;
- r = read_one_line_file("/proc/cmdline", ret);
- if (r < 0)
- return r;
+ q = realloc(*p, a);
+ if (!q)
+ return NULL;
- return 1;
+ *p = q;
+ *allocated = newalloc;
+ return q;
+}
+
+int proc_cmdline(char **ret) {
+ assert(ret);
+
+ if (detect_container(NULL) > 0)
+ return get_process_cmdline(1, 0, false, ret);
+ else
+ return read_one_line_file("/proc/cmdline", ret);
}
int getpeercred(int fd, struct ucred *ucred) {
@@ -1317,6 +1549,10 @@ int getpeercred(int fd, struct ucred *ucred) {
* to namespacing issues */
if (u.pid <= 0)
return -ENODATA;
+ if (u.uid == UID_INVALID)
+ return -ENODATA;
+ if (u.gid == GID_INVALID)
+ return -ENODATA;
*ucred = u;
return 0;
@@ -1354,35 +1590,45 @@ int mkstemp_safe(char *pattern) {
return fd;
}
-char *tempfn_xxxxxx(const char *p) {
+int tempfn_xxxxxx(const char *p, char **ret) {
const char *fn;
char *t;
- size_t k;
assert(p);
+ assert(ret);
- t = new(char, strlen(p) + 1 + 6 + 1);
- if (!t)
- return NULL;
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#waldoXXXXXX
+ */
fn = basename(p);
- k = fn - p;
+ if (!filename_is_valid(fn))
+ return -EINVAL;
- strcpy(stpcpy(stpcpy(mempcpy(t, p, k), "."), fn), "XXXXXX");
+ t = new(char, strlen(p) + 2 + 6 + 1);
+ if (!t)
+ return -ENOMEM;
- return t;
+ strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX");
+
+ *ret = path_kill_slashes(t);
+ return 0;
}
int is_dir(const char* path, bool follow) {
struct stat st;
+ int r;
- if (follow) {
- if (stat(path, &st) < 0)
- return -errno;
- } else {
- if (lstat(path, &st) < 0)
- return -errno;
- }
+ if (follow)
+ r = stat(path, &st);
+ else
+ r = lstat(path, &st);
+ if (r < 0)
+ return -errno;
return !!S_ISDIR(st.st_mode);
}
@@ -1396,7 +1642,7 @@ int execute_command(const char *command, char *const argv[]) {
return status;
if ((pid = fork()) < 0) {
- log_error("Failed to fork: %m");
+ log_error_errno(errno, "Failed to fork: %m");
return pid;
}
@@ -1404,7 +1650,7 @@ int execute_command(const char *command, char *const argv[]) {
execvp(command, argv);
- log_error("Failed to execute %s: %m", command);
+ log_error_errno(errno, "Failed to execute %s: %m", command);
_exit(EXIT_FAILURE);
}
else while (1)
diff --git a/src/shared/util.h b/src/shared/util.h
index 9550aa19f9..bce4f0fa77 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -31,6 +31,7 @@
#include <stddef.h>
#include <unistd.h>
#include <sys/socket.h>
+#include <sys/inotify.h>
#include "time-util.h"
#include "missing.h"
@@ -62,7 +63,7 @@
#if SIZEOF_TIME_T == 8
# define PRI_TIME PRIu64
-#elif SIZEOF_GID_T == 4
+#elif SIZEOF_TIME_T == 4
# define PRI_TIME PRIu32
#else
# error Unknown time_t size
@@ -98,7 +99,7 @@
#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
-size_t page_size(void);
+size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
#define streq(a,b) (strcmp((a),(b)) == 0)
@@ -113,6 +114,10 @@ bool streq_ptr(const char *a, const char *b) _pure_;
#define new0(t, n) ((t*) calloc((n), sizeof(t)))
#define malloc0(n) (calloc((n), 1))
+static inline const char* one_zero(bool b) {
+ return b ? "1" : "0";
+}
+
static inline bool isempty(const char *p) {
return !p || !p[0];
}
@@ -142,7 +147,7 @@ int safe_atollu(const char *s, unsigned long long *ret_u);
int safe_atolli(const char *s, long long int *ret_i);
-#if __WORDSIZE == 32
+#if LONG_MAX == INT_MAX
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
return safe_atou(s, (unsigned*) ret_u);
@@ -180,6 +185,11 @@ char *strnappend(const char *s, const char *suffix, size_t length);
char *truncate_nl(char *s);
int rmdir_parents(const char *path, const char *stop);
+
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_environ(pid_t pid, char **environ);
+
char hexchar(int x) _const_;
char octchar(int x) _const_;
@@ -188,10 +198,11 @@ char *xescape(const char *s, const char *bad);
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
-bool ignore_file(const char *filename) _pure_;
+bool hidden_file(const char *filename) _pure_;
int dev_urandom(void *p, size_t n);
void random_bytes(void *p, size_t n);
+void initialize_srand(void);
/* For basic lookup tables with strictly enumerated entries */
#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
@@ -348,6 +359,7 @@ _alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b)
return realloc(p, a * b);
}
+bool filename_is_valid(const char *p) _pure_;
/**
* Check if a string contains any glob patterns.
*/
@@ -368,12 +380,36 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
break; \
} else
+static inline void *mempset(void *s, int c, size_t n) {
+ memset(s, c, n);
+ return (uint8_t*)s + n;
+}
+
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
+#define GREEDY_REALLOC(array, allocated, need) \
+ greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
static inline void _reset_errno_(int *saved_errno) {
errno = *saved_errno;
}
#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno
+static inline unsigned log2u(unsigned x) {
+ assert(x > 0);
+
+ return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
+}
+
+static inline unsigned log2u_round_up(unsigned x) {
+ assert(x > 0);
+
+ if (x == 1)
+ return 0;
+
+ return log2u(x - 1) + 1;
+}
+
int unlink_noerrno(const char *path);
#define strappenda(a, ...) \
@@ -391,6 +427,19 @@ int unlink_noerrno(const char *path);
_d_; \
})
+#define procfs_file_alloca(pid, field) \
+ ({ \
+ pid_t _pid_ = (pid); \
+ const char *_r_; \
+ if (_pid_ == 0) { \
+ _r_ = ("/proc/self/" field); \
+ } else { \
+ _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+ } \
+ _r_; \
+ })
+
static inline void qsort_safe(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *)) {
if (nmemb) {
@@ -410,8 +459,20 @@ union file_handle_union {
char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
};
-char *tempfn_xxxxxx(const char *p);
+int tempfn_xxxxxx(const char *p, char **ret);
int is_dir(const char *path, bool follow);
int execute_command(const char *command, char *const argv[]);
+
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
+
+#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
+ for ((e) = &buffer.ev; \
+ (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
+ (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
+
+union inotify_event_buffer {
+ struct inotify_event ev;
+ uint8_t raw[INOTIFY_EVENT_MAX];
+};
diff --git a/src/udev/Makefile.am b/src/udev/Makefile.am
index f9af997ffe..652cb67006 100644
--- a/src/udev/Makefile.am
+++ b/src/udev/Makefile.am
@@ -34,7 +34,10 @@ udevadm_SOURCES = \
udevadm-settle.c \
udevadm-trigger.c \
udevadm-test.c \
- udevadm-test-builtin.c
+ udevadm-test-builtin.c \
+ udevadm-util.c \
+ udevadm-util.h
+
udevadm_LDADD = \
libudev-core.la
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index f97f516462..93563d867b 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -138,6 +138,7 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
blkid_probe pr;
const char *data;
const char *name;
+ const char *prtype = NULL;
int nvals;
int i;
int err = 0;
@@ -172,7 +173,8 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
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_USAGE | BLKID_SUBLKS_VERSION |
+ BLKID_SUBLKS_BADCSUM);
if (noraid)
blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID);
@@ -194,6 +196,15 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
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;
+ }
nvals = blkid_probe_numof_values(pr);
for (i = 0; i < nvals; i++) {
@@ -214,6 +225,6 @@ out:
const struct udev_builtin udev_builtin_blkid = {
.name = "blkid",
.cmd = builtin_blkid,
- .help = "filesystem and partition probing",
+ .help = "Filesystem and partition probing",
.run_once = true,
};
diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c
index 2e2112fd92..3643596a70 100644
--- a/src/udev/udev-builtin-btrfs.c
+++ b/src/udev/udev-builtin-btrfs.c
@@ -24,6 +24,7 @@
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
+
#ifdef HAVE_LINUX_BTRFS_H
#include <linux/btrfs.h>
#endif
@@ -32,7 +33,7 @@
#include "udev.h"
static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct btrfs_ioctl_vol_args args;
+ struct btrfs_ioctl_vol_args args = {};
_cleanup_close_ int fd = -1;
int err;
@@ -48,7 +49,7 @@ static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool t
if (err < 0)
return EXIT_FAILURE;
- udev_builtin_add_property(dev, test, "ID_BTRFS_READY", err == 0 ? "1" : "0");
+ udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(err == 0));
return EXIT_SUCCESS;
}
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index 695a31a12f..1eddf86b61 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -214,5 +214,5 @@ const struct udev_builtin udev_builtin_hwdb = {
.init = builtin_hwdb_init,
.exit = builtin_hwdb_exit,
.validate = builtin_hwdb_validate,
- .help = "hardware database",
+ .help = "Hardware database",
};
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index 1a1121ef94..26c723225d 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -1,9 +1,11 @@
/*
- * compose persistent device path
+ * 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
@@ -29,6 +31,7 @@
#include <linux/input.h>
#include "udev.h"
+#include "util.h"
/* we must use this kernel-compatible implementation */
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
@@ -38,6 +41,34 @@
#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;
+
+ snprintf(width, sizeof(width), "%d", abs_size_mm(&xabsinfo));
+ snprintf(height, sizeof(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
@@ -48,12 +79,17 @@ 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;
- snprintf(text, sizeof(text), "%s", udev_device_get_sysattr_value(pdev, attr));
+ v = udev_device_get_sysattr_value(pdev, attr);
+ if (!v)
+ v = "";
+
+ snprintf(text, sizeof(text), "%s", v);
log_debug("%s raw kernel attribute: %s", attr, text);
memzero(bitmask, bitmask_size);
@@ -196,6 +232,7 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
unsigned long bitmask_abs[NBITS(ABS_MAX)];
unsigned long bitmask_key[NBITS(KEY_MAX)];
unsigned long bitmask_rel[NBITS(REL_MAX)];
+ const char *sysname, *devnode;
/* walk up the parental chain until we find the real input device; the
* argument is very likely a subdevice of this, like eventN */
@@ -203,24 +240,28 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL)
pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
- /* not an "input" class device */
- if (pdev == NULL)
- return EXIT_SUCCESS;
-
- /* 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);
- test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test);
- test_key(dev, bitmask_ev, bitmask_key, test);
+ 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);
+ test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test);
+ test_key(dev, bitmask_ev, bitmask_key, test);
+ }
+
+ 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",
+ .help = "Input device properties",
};
diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c
index d6b7dbbac0..118e797c96 100644
--- a/src/udev/udev-builtin-keyboard.c
+++ b/src/udev/udev-builtin-keyboard.c
@@ -62,7 +62,7 @@ static int install_force_release(struct udev_device *dev, const unsigned int *re
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("Error writing force-release attribute: %s", strerror(-ret));
+ log_error_errno(ret, "Error writing force-release attribute: %m");
return ret;
}
@@ -140,7 +140,7 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo
fd = open(udev_device_get_devnode(dev), O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) {
- log_error("Error, opening device '%s': %m", node);
+ log_error_errno(errno, "Error, opening device '%s': %m", node);
return EXIT_FAILURE;
}
@@ -149,7 +149,7 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo
log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
map[i].scan, map[i].scan, map[i].key, map[i].key);
if (ioctl(fd, EVIOCSKEYCODE, &map[i]) < 0)
- log_error("Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", node, map[i].scan, map[i].key);
+ log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", node, map[i].scan, map[i].key);
}
/* install list of force-release codes */
@@ -165,5 +165,5 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo
const struct udev_builtin udev_builtin_keyboard = {
.name = "keyboard",
.cmd = builtin_keyboard,
- .help = "keyboard scan code to key mapping",
+ .help = "Keyboard scan code to key mapping",
};
diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c
index b83fd523d9..f39b01a452 100644
--- a/src/udev/udev-builtin-kmod.c
+++ b/src/udev/udev-builtin-kmod.c
@@ -34,7 +34,7 @@
#include "udev.h"
-static struct kmod_ctx *ctx;
+static struct kmod_ctx *ctx = NULL;
static int load_module(struct udev *udev, const char *alias) {
int err;
@@ -47,18 +47,18 @@ static int load_module(struct udev *udev, const char *alias) {
return err;
if (list == NULL)
- log_debug("no module matches '%s'", alias);
+ 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));
+ log_debug("Module '%s' is blacklisted", kmod_module_get_name(mod));
else if (err == 0)
- log_debug("inserted '%s'", kmod_module_get_name(mod));
+ log_debug("Inserted '%s'", kmod_module_get_name(mod));
else
- log_debug("failed to insert '%s'", kmod_module_get_name(mod));
+ log_debug("Failed to insert '%s'", kmod_module_get_name(mod));
kmod_module_unref(mod);
}
@@ -85,10 +85,8 @@ static int load_module(struct udev *udev, const char *alias) {
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) {
- udev_main_log(data, priority, file, line, fn, format, args);
+_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) {
@@ -106,7 +104,7 @@ static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool te
}
for (i = 2; argv[i]; i++) {
- log_debug("execute '%s' '%s'", argv[1], argv[i]);
+ log_debug("Execute '%s' '%s'", argv[1], argv[i]);
load_module(udev, argv[i]);
}
@@ -123,7 +121,7 @@ static int builtin_kmod_init(struct udev *udev) {
if (!ctx)
return -ENOMEM;
- log_debug("load module index");
+ log_debug("Load module index");
kmod_set_log_fn(ctx, udev_kmod_log, udev);
kmod_load_resources(ctx);
#endif
@@ -133,7 +131,7 @@ static int builtin_kmod_init(struct udev *udev) {
/* called on udev shutdown and reload request */
static void builtin_kmod_exit(struct udev *udev) {
#ifdef HAVE_LIBKMOD
- log_debug("unload module index");
+ log_debug("Unload module index");
ctx = kmod_unref(ctx);
#endif
}
@@ -141,7 +139,7 @@ static void builtin_kmod_exit(struct udev *udev) {
#ifdef HAVE_LIBKMOD
/* 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");
+ log_debug("Validate module index");
if (!ctx)
return false;
return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK);
@@ -156,6 +154,6 @@ const struct udev_builtin udev_builtin_kmod = {
#ifdef HAVE_LIBKMOD
.validate = builtin_kmod_validate,
#endif
- .help = "kernel module loader",
+ .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
index 4731a860b0..5f350abd68 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -307,7 +307,7 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
s[0] = '\0';
interf = s+1;
- /* prefix every port number in the chain with "u"*/
+ /* prefix every port number in the chain with "u" */
s = ports;
while ((s = strchr(s, '.')))
s[0] = 'u';
@@ -562,5 +562,5 @@ out:
const struct udev_builtin udev_builtin_net_id = {
.name = "net_id",
.cmd = builtin_net_id,
- .help = "network device properties",
+ .help = "Network device properties",
};
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 0d247f6b5a..b6749aab76 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -118,7 +118,7 @@ out:
return parent;
}
-static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) {
+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;
@@ -154,6 +154,100 @@ out:
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;
+
+ 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);
+
+ if (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;
@@ -548,9 +642,9 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
}
/*
- * Do return devices with have an unknown type of parent device, they
- * might produce conflicting IDs below multiple independent parent
- * devices.
+ * 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) {
free(path);
@@ -558,9 +652,9 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
}
/*
- * Do not return a have-only a single-parent block devices, some
- * have entire hidden buses behind it, and not create predictable
- * IDs that way.
+ * 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(udev_device_get_subsystem(dev), "block") && !supported_transport) {
free(path);
@@ -609,6 +703,6 @@ out:
const struct udev_builtin udev_builtin_path_id = {
.name = "path_id",
.cmd = builtin_path_id,
- .help = "compose persistent device path",
+ .help = "Compose persistent device path",
.run_once = true,
};
diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
index 06d4191584..ab0d96e377 100644
--- a/src/udev/udev-builtin-usb_id.c
+++ b/src/udev/udev-builtin-usb_id.c
@@ -471,6 +471,6 @@ fallback:
const struct udev_builtin udev_builtin_usb_id = {
.name = "usb_id",
.cmd = builtin_usb_id,
- .help = "usb device properties",
+ .help = "USB device properties",
.run_once = true,
};
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 70b7ab4332..e4440a6e99 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -86,7 +86,7 @@ void udev_builtin_list(struct udev *udev) {
unsigned int i;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- fprintf(stderr, " %-12s %s\n", builtins[i]->name, builtins[i]->help);
+ fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
}
const char *udev_builtin_name(enum udev_builtin_cmd cmd) {
diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c
index 2fdf4561a9..898ef54514 100644
--- a/src/udev/udev-ctrl.c
+++ b/src/udev/udev-ctrl.c
@@ -85,7 +85,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) {
if (fd < 0) {
uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
if (uctrl->sock < 0) {
- log_error("error getting socket: %m");
+ log_error_errno(errno, "error getting socket: %m");
udev_ctrl_unref(uctrl);
return NULL;
}
@@ -95,7 +95,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) {
}
r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
if (r < 0)
- log_warning("could not set SO_PASSCRED: %m");
+ log_warning_errno(errno, "could not set SO_PASSCRED: %m");
uctrl->saddr.sun_family = AF_LOCAL;
strscpy(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), "/run/udev/control");
@@ -119,14 +119,14 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
if (err < 0) {
err = -errno;
- log_error("bind failed: %m");
+ log_error_errno(errno, "bind failed: %m");
return err;
}
err = listen(uctrl->sock, 0);
if (err < 0) {
err = -errno;
- log_error("listen failed: %m");
+ log_error_errno(errno, "listen failed: %m");
return err;
}
@@ -209,14 +209,14 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
if (conn->sock < 0) {
if (errno != EINTR)
- log_error("unable to receive ctrl connection: %m");
+ 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("unable to receive credentials of ctrl connection: %s", strerror(-r));
+ log_error_errno(r, "unable to receive credentials of ctrl connection: %m");
goto err;
}
if (ucred.uid > 0) {
@@ -227,7 +227,7 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
/* 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("could not set SO_PASSCRED: %m");
+ log_warning_errno(errno, "could not set SO_PASSCRED: %m");
udev_ctrl_ref(uctrl);
return conn;
@@ -383,7 +383,7 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
goto err;
} else {
if (!(pfd[0].revents & POLLIN)) {
- log_error("ctrl connection error: %m");
+ log_error_errno(errno, "ctrl connection error: %m");
goto err;
}
}
@@ -396,7 +396,7 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
size = recvmsg(conn->sock, &smsg, 0);
if (size < 0) {
- log_error("unable to receive ctrl message: %m");
+ log_error_errno(errno, "unable to receive ctrl message: %m");
goto err;
}
cmsg = CMSG_FIRSTHDR(&smsg);
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index e86f9cb059..5c1352b1b9 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -388,7 +388,7 @@ static int spawn_exec(struct udev_event *event,
if (fd_stderr < 0)
dup2(fd, STDERR_FILENO);
} else
- log_error("open /dev/null failed: %m");
+ log_error_errno(errno, "open /dev/null failed: %m");
/* connect pipes to std{out,err} */
if (fd_stdout >= 0) {
@@ -410,7 +410,7 @@ static int spawn_exec(struct udev_event *event,
execve(argv[0], argv, envp);
/* exec failed */
- log_error("failed to execute '%s' '%s': %m", argv[0], cmd);
+ log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd);
return -errno;
}
@@ -438,14 +438,14 @@ static void spawn_read(struct udev_event *event,
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error("error creating epoll fd: %m");
+ 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("fail to add stdout fd to epoll: %m");
+ log_error_errno(errno, "fail to add stdout fd to epoll: %m");
return;
}
}
@@ -453,7 +453,7 @@ static void spawn_read(struct udev_event *event,
if (fd_stderr >= 0) {
r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe);
if (r < 0) {
- log_error("fail to add stderr fd to epoll: %m");
+ log_error_errno(errno, "fail to add stderr fd to epoll: %m");
return;
}
}
@@ -482,7 +482,7 @@ static void spawn_read(struct udev_event *event,
if (fdcount < 0) {
if (errno == EINTR)
continue;
- log_error("failed to poll: %m");
+ log_error_errno(errno, "failed to poll: %m");
return;
} else if (fdcount == 0) {
log_error("timeout '%s'", cmd);
@@ -528,7 +528,7 @@ static void spawn_read(struct udev_event *event,
} else if (ev[i].events & EPOLLHUP) {
r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL);
if (r < 0) {
- log_error("failed to remove fd from epoll: %m");
+ log_error_errno(errno, "failed to remove fd from epoll: %m");
return;
}
*fd = -1;
@@ -577,7 +577,7 @@ static int spawn_wait(struct udev_event *event,
if (errno == EINTR)
continue;
err = -errno;
- log_error("failed to poll: %m");
+ log_error_errno(errno, "failed to poll: %m");
goto out;
}
if (fdcount == 0) {
@@ -588,7 +588,7 @@ static int spawn_wait(struct udev_event *event,
if (errno == EINTR)
continue;
err = -errno;
- log_error("failed to poll: %m");
+ log_error_errno(errno, "failed to poll: %m");
goto out;
}
if (fdcount == 0) {
@@ -677,7 +677,6 @@ int udev_event_spawn(struct udev_event *event,
usec_t timeout_warn_usec,
const char *cmd, char **envp, const sigset_t *sigmask,
char *result, size_t ressize) {
- struct udev *udev = event->udev;
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
pid_t pid;
@@ -690,17 +689,17 @@ int udev_event_spawn(struct udev_event *event,
udev_build_argv(event->udev, arg, NULL, argv);
/* pipes from child to parent */
- if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
+ if (result != NULL || log_get_max_level() >= LOG_INFO) {
if (pipe2(outpipe, O_NONBLOCK) != 0) {
err = -errno;
- log_error("pipe failed: %m");
+ log_error_errno(errno, "pipe failed: %m");
goto out;
}
}
- if (udev_get_log_priority(udev) >= LOG_INFO) {
+ if (log_get_max_level() >= LOG_INFO) {
if (pipe2(errpipe, O_NONBLOCK) != 0) {
err = -errno;
- log_error("pipe failed: %m");
+ log_error_errno(errno, "pipe failed: %m");
goto out;
}
}
@@ -737,7 +736,7 @@ int udev_event_spawn(struct udev_event *event,
_exit(2 );
case -1:
- log_error("fork of '%s' failed: %m", cmd);
+ log_error_errno(errno, "fork of '%s' failed: %m", cmd);
err = -1;
goto out;
default:
@@ -785,7 +784,7 @@ static int rename_netif_dev_fromname_toname(struct udev_device *dev,const char *
sk = socket(PF_INET, SOCK_DGRAM, 0);
if (sk < 0) {
err = -errno;
- log_error("error opening socket: %m\n");
+ log_error("error opening socket: %m");
return err;
}
@@ -799,11 +798,11 @@ static int rename_netif_dev_fromname_toname(struct udev_device *dev,const char *
int loop;
if (err == 0) {
- log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname);
+ log_info("renamed network interface %s to %s", ifr.ifr_name, ifr.ifr_newname);
goto out;
}
/* keep trying if the destination interface name already exists */
- log_debug("collision on rename of network interface %s to %s , retrying until timeout\n",
+ log_debug("collision on rename of network interface %s to %s , retrying until timeout",
ifr.ifr_name, ifr.ifr_newname);
err = -errno;
@@ -819,7 +818,7 @@ static int rename_netif_dev_fromname_toname(struct udev_device *dev,const char *
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err == 0) {
- log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname);
+ log_info("renamed network interface %s to %s", ifr.ifr_name, ifr.ifr_newname);
break;
}
err = -errno;
@@ -829,13 +828,13 @@ static int rename_netif_dev_fromname_toname(struct udev_device *dev,const char *
out:
if (err < 0)
- log_error("error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname);
+ log_error("error changing net interface name %s to %s: %m", ifr.ifr_name, ifr.ifr_newname);
#else
if (err >= 0) {
- log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname);
+ log_info("renamed network interface %s to %s", ifr.ifr_name, ifr.ifr_newname);
} else {
err = -errno;
- log_error("error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname);
+ log_error("error changing net interface name %s to %s: %m", ifr.ifr_name, ifr.ifr_newname);
}
#endif
@@ -848,9 +847,10 @@ static int rename_netif(struct udev_event *event) {
}
void udev_event_execute_rules(struct udev_event *event,
- usec_t timeout_usec,
- usec_t timeout_warn_usec,
- struct udev_rules *rules, const sigset_t *sigmask) {
+ usec_t timeout_usec, usec_t timeout_warn_usec,
+ struct udev_list *properties_list,
+ struct udev_rules *rules,
+ const sigset_t *sigmask) {
struct udev_device *dev = event->dev;
if (udev_device_get_subsystem(dev) == NULL)
@@ -864,7 +864,10 @@ void udev_event_execute_rules(struct udev_event *event,
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, sigmask);
+ udev_rules_apply_to_event(rules, event,
+ timeout_usec, timeout_warn_usec,
+ properties_list,
+ sigmask);
if (major(udev_device_get_devnum(dev)) != 0)
udev_node_remove(dev);
@@ -898,7 +901,10 @@ void udev_event_execute_rules(struct udev_event *event,
}
}
- udev_rules_apply_to_event(rules, event, timeout_usec, timeout_warn_usec, sigmask);
+ udev_rules_apply_to_event(rules, event,
+ timeout_usec, timeout_warn_usec,
+ properties_list,
+ sigmask);
/* rename a new network interface, if needed */
@@ -955,9 +961,9 @@ void udev_event_execute_rules(struct udev_event *event,
r = rename_netif_dev_fromname_toname(dev,udev_device_get_sysname(dev),newifname);
if (r == 0) {
finalifname = newifname;
- log_debug("renamed netif to '%s' for collision avoidance\n", newifname);
+ log_debug("renamed netif to '%s' for collision avoidance", newifname);
} else {
- log_error("could not rename netif to '%s' for collision avoidance\n",newifname);
+ log_error("could not rename netif to '%s' for collision avoidance",newifname);
}
}
/* rename it now to its final target if its not already there */
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 4ac6f71490..c30a428ea1 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -112,12 +112,12 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s
mac_selinux_create_file_clear();
} while (err == -ENOENT);
if (err != 0) {
- log_error("symlink '%s' '%s' failed: %m", target, slink_tmp);
+ log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp);
goto exit;
}
err = rename(slink_tmp, slink);
if (err != 0) {
- log_error("rename '%s' '%s' failed: %m", slink_tmp, slink);
+ log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink);
unlink(slink_tmp);
}
exit:
@@ -264,7 +264,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
if (lstat(devnode, &stats) != 0) {
err = -errno;
- log_debug("can not stat() node '%s' (%m)", devnode);
+ log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode);
goto out;
}
@@ -283,10 +283,10 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
err = chmod(devnode, mode);
if (err < 0)
- log_warning("setting mode of %s to %#o failed: %m", devnode, mode);
+ log_warning_errno(errno, "setting mode of %s to %#o failed: %m", devnode, mode);
err = chown(devnode, uid, gid);
if (err < 0)
- log_warning("setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid);
+ 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);
}
@@ -304,7 +304,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
r = mac_selinux_apply(devnode, label);
if (r < 0)
- log_error("SECLABEL: failed to set SELinux label '%s': %s", label, strerror(-r));
+ log_error_errno(r, "SECLABEL: failed to set SELinux label '%s': %m", label);
else
log_debug("SECLABEL: set SELinux label '%s'", label);
@@ -313,7 +313,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
r = mac_smack_apply(devnode, label);
if (r < 0)
- log_error("SECLABEL: failed to set SMACK label '%s': %s", label, strerror(-r));
+ log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label);
else
log_debug("SECLABEL: set SMACK label '%s'", label);
@@ -323,7 +323,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
/* set the defaults */
if (!selinux)
- label_fix(devnode, true, false);
+ mac_selinux_fix(devnode, true, false);
if (!smack)
mac_smack_apply(devnode, NULL);
}
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index b8fc35e11a..f51691143b 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -479,9 +479,9 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
r = get_user_creds(&owner, &uid, NULL, NULL, NULL);
if (r < 0) {
if (r == -ENOENT || r == -ESRCH)
- udev_err(rules->udev, "specified user '%s' unknown\n", owner);
+ log_error("specified user '%s' unknown", owner);
else
- udev_err(rules->udev, "error resolving user '%s': %s\n", owner, strerror(-r));
+ log_error_errno(r, "error resolving user '%s': %m", owner);
}
/* grow buffer if needed */
@@ -526,9 +526,9 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
r = get_group_creds(&group, &gid);
if (r < 0) {
if (r == -ENOENT || r == -ESRCH)
- udev_err(rules->udev, "specified group '%s' unknown\n", group);
+ log_error("specified group '%s' unknown", group);
else
- udev_err(rules->udev, "error resolving group '%s': %s\n", group, strerror(-r));
+ log_error_errno(r, "error resolving group '%s': %m", group);
}
/* grow buffer if needed */
@@ -1047,11 +1047,11 @@ static int 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;
+ struct rule_tmp rule_tmp = {
+ .rules = rules,
+ .rule.type = TK_RULE,
+ };
- memzero(&rule_tmp, sizeof(struct rule_tmp));
- rule_tmp.rules = rules;
- rule_tmp.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;
@@ -1072,14 +1072,14 @@ static int add_rule(struct udev_rules *rules, char *line,
/* 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 (*linepos != '\n') {
- char buf[2] = {linepos[1]};
+ 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')\n",
+ 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[1] == '#')
+ if (*linepos == '#')
log_error("hint: comments can only start at beginning of line");
}
break;
@@ -1420,12 +1420,12 @@ static int add_rule(struct udev_rules *rules, char *line,
} else {
if (streq(value, "%k")) {
log_error("NAME=\"%%k\" is ignored, because it breaks kernel supplied names, "
- "please remove it from %s:%u\n", filename, lineno);
+ "please remove it from %s:%u", filename, lineno);
continue;
}
if (value[0] == '\0') {
log_debug("NAME=\"\" is ignored, because udev will not delete any device nodes, "
- "please remove it from %s:%u\n", filename, lineno);
+ "please remove it from %s:%u", filename, lineno);
continue;
}
rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
@@ -1690,7 +1690,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs);
if (r < 0) {
- log_error("failed to enumerate rules files: %s", strerror(-r));
+ log_error_errno(r, "failed to enumerate rules files: %m");
return udev_rules_unref(rules);
}
@@ -1916,9 +1916,9 @@ int udev_rules_assigning_name_to(struct udev_rules *rules, const char *match_nam
strscpy(name_str,UTIL_PATH_SIZE,name);
count = util_replace_chars(name_str, "/");
if (count > 0)
- log_debug("%i character(s) replaced\n", count);
+ log_debug("%i character(s) replaced", count);
if (streq(name_str,match_name)) {
- log_debug("found a match! NAME assigns %s in: %s:%u\n",
+ log_debug("found a match! NAME assigns %s in: %s:%u",
name,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
@@ -1945,6 +1945,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
+ struct udev_list *properties_list,
const sigset_t *sigmask) {
struct token *cur;
struct token *rule;
@@ -2334,9 +2335,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
if (r < 0) {
if (r == -ENOENT || r == -ESRCH)
- udev_err(event->udev, "specified user '%s' unknown\n", owner);
+ log_error("specified user '%s' unknown", owner);
else
- udev_err(event->udev, "error resolving user '%s': %s\n", owner, strerror(-r));
+ log_error_errno(r, "error resolving user '%s': %m", owner);
event->uid = 0;
}
@@ -2360,9 +2361,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
r = get_group_creds(&gr, &event->gid);
if (r < 0) {
if (r == -ENOENT || r == -ESRCH)
- udev_err(event->udev, "specified group '%s' unknown\n", group);
+ log_error("specified group '%s' unknown", group);
else
- udev_err(event->udev, "error resolving group '%s': %s\n", group, strerror(-r));
+ log_error_errno(r, "error resolving group '%s': %m", group);
event->gid = 0;
}
@@ -2517,7 +2518,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
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 "
- "can not be renamed; please fix it in %s:%u\n", name,
+ "can not be renamed; please fix it in %s:%u", name,
rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
break;
}
@@ -2592,10 +2593,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
f = fopen(attr, "we");
if (f != NULL) {
if (fprintf(f, "%s", value) <= 0)
- log_error("error writing ATTR{%s}: %m", attr);
+ log_error_errno(errno, "error writing ATTR{%s}: %m", attr);
fclose(f);
} else {
- log_error("error opening ATTR{%s} for writing: %m", attr);
+ log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr);
}
break;
}
@@ -2710,7 +2711,7 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
r = mkdir_p(tags_dir, 0755);
if (r < 0) {
- log_error("failed to create %s: %s", tags_dir, strerror(-r));
+ log_error_errno(r, "failed to create %s: %m", tags_dir);
return r;
}
@@ -2718,10 +2719,10 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
r = symlink(device_node, tag_symlink);
- if (r < 0 && errno != EEXIST) {
- log_error("failed to create symlink %s -> %s: %m", tag_symlink, device_node);
- return -errno;
- } else
+ if (r < 0 && errno != EEXIST)
+ return log_error_errno(errno, "failed to create symlink %s -> %s: %m",
+ tag_symlink, device_node);
+ else
r = 0;
}
}
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 061bd05875..6ba8674d77 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -39,7 +39,7 @@ static int inotify_fd = -1;
int udev_watch_init(struct udev *udev) {
inotify_fd = inotify_init1(IN_CLOEXEC);
if (inotify_fd < 0)
- log_error("inotify_init failed: %m");
+ log_error_errno(errno, "inotify_init failed: %m");
return inotify_fd;
}
@@ -56,7 +56,7 @@ void udev_watch_restore(struct udev *udev) {
dir = opendir("/run/udev/watch.old");
if (dir == NULL) {
- log_error("unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m");
+ log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m");
return;
}
@@ -88,7 +88,7 @@ unlink:
rmdir("/run/udev/watch.old");
} else if (errno != ENOENT) {
- log_error("unable to move watches dir /run/udev/watch; old watches will not be restored: %m");
+ log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m");
}
}
@@ -103,7 +103,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) {
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("inotify_add_watch(%d, %s, %o) failed: %m",
+ log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m",
inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
return;
}
@@ -113,7 +113,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) {
unlink(filename);
r = symlink(udev_device_get_id_filename(dev), filename);
if (r < 0)
- log_error("Failed to create symlink %s: %m", filename);
+ log_error_errno(errno, "Failed to create symlink %s: %m", filename);
udev_device_set_watch_handle(dev, wd);
}
diff --git a/src/udev/udev.h b/src/udev/udev.h
index 5a1f5488cb..fae0408672 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -71,7 +71,9 @@ 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);
-int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec,
+int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
+ usec_t timeout_usec, usec_t timeout_warn_usec,
+ struct udev_list *properties_list,
const sigset_t *sigmask);
int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
@@ -86,8 +88,11 @@ int udev_event_spawn(struct udev_event *event,
usec_t timeout_warn_usec,
const char *cmd, char **envp, const sigset_t *sigmask,
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_rules *rules, const sigset_t *sigset);
+void udev_event_execute_rules(struct udev_event *event,
+ usec_t timeout_usec, usec_t timeout_warn_usec,
+ struct udev_list *properties_list,
+ struct udev_rules *rules,
+ const sigset_t *sigset);
void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigset);
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]);
@@ -193,11 +198,6 @@ int udev_builtin_add_property(struct udev_device *dev, bool test, const char *ke
int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias,
const char *filter, bool test);
-/* udev logging */
-void udev_main_log(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args) _printf_(6, 0);
-
/* udevadm commands */
struct udevadm_cmd {
const char *name;
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 6e3bc2ae98..6af7163d47 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -29,16 +29,19 @@
#include "udev-util.h"
static void print_help(void) {
- printf("Usage: udevadm control COMMAND\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"
- " -h,--help print this help text\n\n");
+ 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[]) {
@@ -161,5 +164,5 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
const struct udevadm_cmd udevadm_control = {
.name = "control",
.cmd = adm_control,
- .help = "control the udev daemon",
+ .help = "Control the udev daemon",
};
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index 4a3f35c2af..a3f93ba343 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -435,6 +435,10 @@ static int insert_data(struct trie *trie, struct udev_list *match_list,
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;
@@ -615,7 +619,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs);
if (err < 0) {
- log_error("failed to enumerate hwdb files: %s", strerror(-err));
+ log_error_errno(err, "failed to enumerate hwdb files: %m");
rc = EXIT_FAILURE;
goto out;
}
@@ -648,7 +652,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
mkdir_parents(hwdb_bin, 0755);
err = trie_store(trie, hwdb_bin);
if (err < 0) {
- log_error("Failure writing database %s: %s", hwdb_bin, strerror(-err));
+ log_error_errno(err, "Failure writing database %s: %m", hwdb_bin);
rc = EXIT_FAILURE;
}
}
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 3cb503fc1f..c845df7a23 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -31,6 +31,7 @@
#include "udev.h"
#include "udev-util.h"
+#include "udevadm-util.h"
#include "util.h"
static bool skip_attribute(const char *name) {
@@ -258,33 +259,29 @@ static void cleanup_db(struct udev *udev) {
}
}
-static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix) {
- char name[UTIL_PATH_SIZE];
-
- if (prefix && !startswith(id, prefix)) {
- strscpyl(name, sizeof(name), prefix, id, NULL);
- id = name;
- }
-
- 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;
+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[]) {
@@ -312,27 +309,6 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) {
{}
};
- static const char *usage =
- "Usage: udevadm info [OPTIONS] [DEVPATH|FILE]\n"
- " -q,--query=TYPE query device information:\n"
- " name name of device node\n"
- " symlink pointing to node\n"
- " path sys device path\n"
- " property the device properties\n"
- " all all values\n"
- " -p,--path=SYSPATH sys 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 cleanup the udev database\n"
- " --version print version of the program\n"
- " -h,--help print this message\n";
-
enum action_type {
ACTION_QUERY,
ACTION_ATTRIBUTE_WALK,
@@ -417,7 +393,7 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) {
printf("%s\n", UDEV_VERSION);
return 0;
case 'h':
- printf("%s\n", usage);
+ help();
return 0;
default:
return 1;
@@ -427,7 +403,7 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) {
case ACTION_QUERY:
if (!device) {
if (!argv[optind]) {
- fprintf(stderr, "%s\n", usage);
+ help();
return 2;
}
device = find_device(udev, argv[optind], NULL);
@@ -518,5 +494,5 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) {
const struct udevadm_cmd udevadm_info = {
.name = "info",
.cmd = uinfo,
- .help = "query sysfs or the udev database",
+ .help = "Query sysfs or the udev database",
};
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index e776fb99dc..15ded09339 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -64,13 +64,16 @@ static void print_device(struct udev_device *device, const char *source, int pro
}
static void help(void) {
- printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\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"
- " -h,--help\n\n");
+ 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[]) {
@@ -155,7 +158,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error("error creating epoll fd: %m");
+ log_error_errno(errno, "error creating epoll fd: %m");
return 1;
}
@@ -195,7 +198,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
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("fail to add fd to epoll: %m");
+ log_error_errno(errno, "fail to add fd to epoll: %m");
return 2;
}
@@ -229,7 +232,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
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("fail to add fd to epoll: %m");
+ log_error_errno(errno, "fail to add fd to epoll: %m");
return 5;
}
@@ -276,5 +279,5 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
const struct udevadm_cmd udevadm_monitor = {
.name = "monitor",
.cmd = adm_monitor,
- .help = "listen to kernel and udev events",
+ .help = "Listen to kernel and udev events",
};
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index 0a8f535c4a..fff5de7a8b 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -37,20 +37,23 @@
#include "util.h"
static void help(void) {
- printf("Usage: udevadm settle OPTIONS\n"
- " -t,--timeout=<seconds> maximum time to wait for events\n"
- " -E,--exit-if-exists=<file> stop waiting if file exists\n"
- " -h,--help\n\n");
+ 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[] = {
- { "seq-start", required_argument, NULL, '\0' }, /* removed */
- { "seq-end", required_argument, NULL, '\0' }, /* removed */
{ "timeout", required_argument, NULL, 't' },
{ "exit-if-exists", required_argument, NULL, 'E' },
- { "quiet", no_argument, NULL, 'q' }, /* removed */
{ "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 */
{}
};
const char *exists = NULL;
@@ -60,8 +63,9 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
struct udev_queue *queue;
int rc = EXIT_FAILURE;
- while ((c = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "t:E:hs:e:q", options, NULL)) >= 0) {
switch (c) {
+
case 't': {
int r;
@@ -73,14 +77,24 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
};
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");
}
@@ -145,5 +159,5 @@ out:
const struct udevadm_cmd udevadm_settle = {
.name = "settle",
.cmd = adm_settle,
- .help = "wait for pending udev events",
+ .help = "Wait for pending udev events",
};
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index f25608c486..baaeca9352 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -34,10 +34,14 @@
#include "udev.h"
static void help(struct udev *udev) {
- fprintf(stderr, "\n");
- fprintf(stderr, "Usage: udevadm builtin [--help] COMMAND SYSPATH\n");
+ 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);
- fprintf(stderr, "\n");
}
static int adm_builtin(struct udev *udev, int argc, char *argv[]) {
@@ -112,6 +116,6 @@ out:
const struct udevadm_cmd udevadm_test_builtin = {
.name = "test-builtin",
.cmd = adm_builtin,
- .help = "test a built-in command",
+ .help = "Test a built-in command",
.debug = true,
};
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index 4738b611c3..4922b5b6ac 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -31,6 +31,17 @@
#include "udev.h"
#include "udev-util.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];
@@ -71,11 +82,7 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
}
break;
case 'h':
- printf("Usage: udevadm test OPTIONS <syspath>\n"
- " -a,--action=ACTION set action string\n"
- " -N,--resolve-names=early|late|never when to resolve names\n"
- " -h,--help print this help string\n"
- "\n");
+ help();
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
@@ -136,7 +143,11 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
goto out;
}
- udev_event_execute_rules(event, 60 * USEC_PER_SEC, 20 * USEC_PER_SEC, rules, &sigmask_orig);
+ udev_event_execute_rules(event,
+ 60 * USEC_PER_SEC, 20 * USEC_PER_SEC,
+ NULL,
+ rules,
+ &sigmask_orig);
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));
@@ -157,6 +168,6 @@ out:
const struct udevadm_cmd udevadm_test = {
.name = "test",
.cmd = adm_test,
- .help = "test an event run",
+ .help = "Test an event run",
.debug = true,
};
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index a52d1b5ad5..4dc756a28b 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -32,6 +32,7 @@
#include "udev.h"
#include "udev-util.h"
+#include "udevadm-util.h"
#include "util.h"
static int verbose;
@@ -53,7 +54,7 @@ static void exec_list(struct udev_enumerate *udev_enumerate, const char *action)
if (fd < 0)
continue;
if (write(fd, action, strlen(action)) < 0)
- log_debug("error writing '%s' to '%s': %m", action, filename);
+ log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename);
close(fd);
}
}
@@ -72,39 +73,48 @@ static const char *keyval(const char *str, const char **val, char *buf, size_t s
}
static void help(void) {
- printf("Usage: udevadm trigger OPTIONS\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 sys devices (default)\n"
- " subsystems sys 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 a matching name\n"
- " -b,--parent-match=<name> trigger devices with that parent device\n"
- " -h,--help\n\n");
+ 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' },
- { "parent-match", required_argument, NULL, 'b' },
- { "help", no_argument, NULL, 'h' },
+ { "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 {
@@ -174,25 +184,31 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
udev_enumerate_add_match_sysname(udev_enumerate, optarg);
break;
case 'b': {
- char path[UTIL_PATH_SIZE];
- struct udev_device *dev;
-
- /* add sys dir if needed */
- if (!startswith(optarg, "/sys"))
- strscpyl(path, sizeof(path), "/sys", optarg, NULL);
- else
- strscpy(path, sizeof(path), optarg);
- util_remove_trailing_chars(path, '/');
- dev = udev_device_new_from_syspath(udev, path);
+ _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;
+ }
+
+ udev_enumerate_add_match_parent(udev_enumerate, dev);
+ 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;
}
+
udev_enumerate_add_match_parent(udev_enumerate, dev);
- /* drop reference immediately, enumerate pins the device as long as needed */
- udev_device_unref(dev);
break;
}
+
case 'h':
help();
return 0;
@@ -203,9 +219,16 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
}
}
- if (optind < argc) {
- fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]);
- return 1;
+ 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;
+ }
+
+ udev_enumerate_add_match_parent(udev_enumerate, dev);
}
switch (device_type) {
@@ -225,5 +248,5 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
const struct udevadm_cmd udevadm_trigger = {
.name = "trigger",
.cmd = adm_trigger,
- .help = "request events from the kernel",
+ .help = "Request events from the kernel",
};
diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c
new file mode 100644
index 0000000000..37e80c31df
--- /dev/null
+++ b/src/udev/udevadm-util.c
@@ -0,0 +1,49 @@
+/*
+ * 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 "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 = strappenda(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
new file mode 100644
index 0000000000..dba651fddb
--- /dev/null
+++ b/src/udev/udevadm-util.h
@@ -0,0 +1,22 @@
+/*
+ * 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
index fda5aecd83..3e57cf6dee 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -26,12 +26,6 @@
#include "udev.h"
-void udev_main_log(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args) {
- log_metav(priority, file, line, fn, format, args);
-}
-
static int adm_version(struct udev *udev, int argc, char *argv[]) {
printf("%s\n", UDEV_VERSION);
return 0;
@@ -65,11 +59,14 @@ static const struct udevadm_cmd *udevadm_cmds[] = {
static int adm_help(struct udev *udev, int argc, char *argv[]) {
unsigned int i;
- fprintf(stderr, "Usage: udevadm [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n");
+ 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);
- fprintf(stderr, "\n");
+ printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help);
return 0;
}
@@ -97,7 +94,6 @@ int main(int argc, char *argv[]) {
goto out;
log_open();
- udev_set_log_fn(udev, udev_main_log);
mac_selinux_init("/dev");
while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0)
@@ -105,7 +101,6 @@ int main(int argc, char *argv[]) {
case 'd':
log_set_max_level(LOG_DEBUG);
- udev_set_log_priority(udev, LOG_DEBUG);
break;
case 'h':
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index a74906aa62..e835bef8df 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -53,12 +53,6 @@
#include "dev-setup.h"
#include "fileio.h"
-void udev_main_log(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args) {
- log_metav(priority, file, line, fn, format, args);
-}
-
static struct udev_rules *rules;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *monitor;
@@ -80,6 +74,7 @@ static sigset_t sigmask_orig;
static UDEV_LIST(event_list);
static UDEV_LIST(worker_list);
static char *udev_cgroup;
+static struct udev_list properties_list;
static bool udev_exit;
enum event_state {
@@ -223,14 +218,14 @@ static void worker_new(struct event *event) {
sigfillset(&mask);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
- log_error("error creating signalfd %m");
+ log_error_errno(errno, "error creating signalfd %m");
rc = 2;
goto out;
}
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error("error creating epoll fd: %m");
+ log_error_errno(errno, "error creating epoll fd: %m");
rc = 3;
goto out;
}
@@ -246,7 +241,7 @@ static void worker_new(struct event *event) {
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
- log_error("fail to add fds to epoll: %m");
+ log_error_errno(errno, "fail to add fds to epoll: %m");
rc = 4;
goto out;
}
@@ -296,7 +291,7 @@ static void worker_new(struct event *event) {
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("Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
+ log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
err = -EWOULDBLOCK;
fd_lock = safe_close(fd_lock);
goto skip;
@@ -305,9 +300,15 @@ static void worker_new(struct event *event) {
}
/* apply rules, create node, symlinks */
- udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_event_timeout_warn_usec, rules, &sigmask_orig);
+ udev_event_execute_rules(udev_event,
+ arg_event_timeout_usec, arg_event_timeout_warn_usec,
+ &properties_list,
+ rules,
+ &sigmask_orig);
- udev_event_execute_run(udev_event, arg_event_timeout_usec, arg_event_timeout_warn_usec, &sigmask_orig);
+ udev_event_execute_run(udev_event,
+ arg_event_timeout_usec, arg_event_timeout_warn_usec,
+ &sigmask_orig);
/* apply/restore inotify watch */
if (udev_event->inotify_watch) {
@@ -349,7 +350,7 @@ skip:
if (fdcount < 0) {
if (errno == EINTR)
continue;
- log_error("failed to poll: %m");
+ log_error_errno(errno, "failed to poll: %m");
goto out;
}
@@ -389,7 +390,7 @@ out:
udev_monitor_unref(worker_monitor);
event->state = EVENT_QUEUED;
free(worker);
- log_error("fork of child failed: %m");
+ log_error_errno(errno, "fork of child failed: %m");
break;
default:
/* close monitor, but keep address around */
@@ -420,7 +421,7 @@ static void event_run(struct event *event) {
count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
if (count < 0) {
- log_error("worker [%u] did not accept message %zi (%m), kill it", worker->pid, count);
+ log_error_errno(errno, "worker [%u] did not accept message %zi (%m), kill it", worker->pid, count);
kill(worker->pid, SIGKILL);
worker->state = WORKER_KILLED;
continue;
@@ -636,7 +637,6 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
if (i >= 0) {
log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
log_set_max_level(i);
- udev_set_log_priority(udev, i);
worker_kill(udev);
}
@@ -669,10 +669,10 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
val = &val[1];
if (val[0] == '\0') {
log_debug("udevd message (ENV) received, unset '%s'", key);
- udev_add_property(udev, key, NULL);
+ udev_list_entry_add(&properties_list, key, NULL);
} else {
log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
- udev_add_property(udev, key, val);
+ udev_list_entry_add(&properties_list, key, val);
}
} else {
log_error("wrong key format '%s'", key);
@@ -807,41 +807,34 @@ static int synthesize_change(struct udev_device *dev) {
}
static int handle_inotify(struct udev *udev) {
- int nbytes, pos;
- char *buf;
- struct inotify_event *ev;
- int r;
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
- r = ioctl(fd_inotify, FIONREAD, &nbytes);
- if (r < 0 || nbytes <= 0)
- return -errno;
+ l = read(fd_inotify, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
- buf = malloc(nbytes);
- if (!buf) {
- log_error("error getting buffer for inotify");
- return -ENOMEM;
+ return log_error_errno(errno, "Failed to read inotify fd: %m");
}
- nbytes = read(fd_inotify, buf, nbytes);
-
- for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
struct udev_device *dev;
- ev = (struct inotify_event *)(buf + pos);
- dev = udev_watch_lookup(udev, ev->wd);
+ dev = udev_watch_lookup(udev, e->wd);
if (!dev)
continue;
- log_debug("inotify event: %x for %s", ev->mask, udev_device_get_devnode(dev));
- if (ev->mask & IN_CLOSE_WRITE)
+ log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
+ if (e->mask & IN_CLOSE_WRITE)
synthesize_change(dev);
- else if (ev->mask & IN_IGNORED)
+ else if (e->mask & IN_IGNORED)
udev_watch_end(udev, dev);
udev_device_unref(dev);
}
- free(buf);
return 0;
}
@@ -966,7 +959,7 @@ static void static_dev_create_from_modules(struct udev *udev) {
strscpyl(filename, sizeof(filename), "/dev/", devname, NULL);
mkdir_parents_label(filename, 0755);
mac_selinux_create_file_prepare(filename, mode);
- log_debug("mknod '%s' %c%u:%u\n", filename, type, maj, min);
+ log_debug("mknod '%s' %c%u:%u", filename, type, maj, min);
if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST)
utimensat(AT_FDCWD, filename, NULL, 0);
mac_selinux_create_file_clear();
@@ -976,7 +969,7 @@ static void static_dev_create_from_modules(struct udev *udev) {
}
/*
- * read the kernel commandline, in case we need to get into debug mode
+ * 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
@@ -988,13 +981,13 @@ static void kernel_cmdline_options(struct udev *udev) {
int r;
r = proc_cmdline(&line);
- if (r < 0)
- log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
- if (r <= 0)
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
return;
+ }
FOREACH_WORD_QUOTED(word, l, line, state) {
- char *s, *opt;
+ char *s, *opt, *value;
s = strndup(word, l);
if (!s)
@@ -1006,24 +999,23 @@ static void kernel_cmdline_options(struct udev *udev) {
else
opt = s;
- if (startswith(opt, "udev.log-priority=")) {
+ if ((value = startswith(opt, "udev.log-priority="))) {
int prio;
- prio = util_log_priority(opt + 18);
+ prio = util_log_priority(value);
log_set_max_level(prio);
- udev_set_log_priority(udev, prio);
- } else if (startswith(opt, "udev.children-max=")) {
- r = safe_atoi(opt + 18, &arg_children_max);
+ } else if ((value = startswith(opt, "udev.children-max="))) {
+ r = safe_atoi(value, &arg_children_max);
if (r < 0)
- log_warning("Invalid udev.children-max ignored: %s", opt + 18);
- } else if (startswith(opt, "udev.exec-delay=")) {
- r = safe_atoi(opt + 16, &arg_exec_delay);
+ log_warning("Invalid udev.children-max ignored: %s", value);
+ } else if ((value = startswith(opt, "udev.exec-delay="))) {
+ r = safe_atoi(value, &arg_exec_delay);
if (r < 0)
- log_warning("Invalid udev.exec-delay ignored: %s", opt + 16);
- } else if (startswith(opt, "udev.event-timeout=")) {
- r = safe_atou64(opt + 16, &arg_event_timeout_usec);
+ log_warning("Invalid udev.exec-delay ignored: %s", value);
+ } else if ((value = startswith(opt, "udev.event-timeout="))) {
+ r = safe_atou64(value, &arg_event_timeout_usec);
if (r < 0) {
- log_warning("Invalid udev.event-timeout ignored: %s", opt + 16);
+ log_warning("Invalid udev.event-timeout ignored: %s", value);
break;
}
arg_event_timeout_usec *= USEC_PER_SEC;
@@ -1037,14 +1029,15 @@ static void kernel_cmdline_options(struct udev *udev) {
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Manages devices.\n\n"
- " --daemon\n"
- " --debug\n"
- " --children-max=<maximum number of workers>\n"
- " --exec-delay=<seconds to wait before executing RUN=>\n"
- " --event-timeout=<seconds to wait before terminating an event>\n"
- " --resolve-names=early|late|never\n"
- " --version\n"
- " --help\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);
}
@@ -1146,19 +1139,14 @@ int main(int argc, char *argv[]) {
log_set_target(LOG_TARGET_AUTO);
log_open();
- udev_set_log_fn(udev, udev_main_log);
- log_set_max_level(udev_get_log_priority(udev));
-
r = parse_argv(argc, argv);
if (r <= 0)
goto exit;
kernel_cmdline_options(udev);
- if (arg_debug) {
+ if (arg_debug)
log_set_max_level(LOG_DEBUG);
- udev_set_log_priority(udev, LOG_DEBUG);
- }
if (getuid() != 0) {
log_error("root privileges required");
@@ -1167,22 +1155,24 @@ int main(int argc, char *argv[]) {
r = mac_selinux_init("/dev");
if (r < 0) {
- log_error("could not initialize labelling: %s", strerror(-r));
+ log_error_errno(r, "could not initialize labelling: %m");
goto exit;
}
/* set umask before creating any file/directory */
r = chdir("/");
if (r < 0) {
- log_error("could not change dir to /: %m");
+ log_error_errno(errno, "could not change dir to /: %m");
goto exit;
}
umask(022);
+ udev_list_init(udev, &properties_list, true);
+
r = mkdir("/run/udev", 0755);
if (r < 0 && errno != EEXIST) {
- log_error("could not create /run/udev: %m");
+ log_error_errno(errno, "could not create /run/udev: %m");
goto exit;
}
@@ -1237,7 +1227,7 @@ int main(int argc, char *argv[]) {
udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
- log_info("starting version " VERSION "\n");
+ log_info("starting version " VERSION);
udev_builtin_init(udev);
@@ -1249,7 +1239,7 @@ int main(int argc, char *argv[]) {
rc = udev_rules_apply_static_dev_perms(rules);
if (rc < 0)
- log_error("failed to apply permissions on static device nodes - %s", strerror(-rc));
+ log_error_errno(rc, "failed to apply permissions on static device nodes - %m");
if (arg_daemonize) {
pid_t pid;
@@ -1259,7 +1249,7 @@ int main(int argc, char *argv[]) {
case 0:
break;
case -1:
- log_error("fork of daemon failed: %m");
+ log_error_errno(errno, "fork of daemon failed: %m");
rc = 4;
goto exit;
default:
@@ -1332,7 +1322,7 @@ int main(int argc, char *argv[]) {
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error("error creating epoll fd: %m");
+ log_error_errno(errno, "error creating epoll fd: %m");
goto exit;
}
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 ||
@@ -1340,7 +1330,7 @@ int main(int argc, char *argv[]) {
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 ||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) {
- log_error("fail to add fds to epoll: %m");
+ log_error_errno(errno, "fail to add fds to epoll: %m");
goto exit;
}
diff --git a/src/v4l_id/Makefile.am b/src/v4l_id/Makefile.am
index 074417f909..6ca3da1146 100644
--- a/src/v4l_id/Makefile.am
+++ b/src/v4l_id/Makefile.am
@@ -1,5 +1,8 @@
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+AM_CPPFLAGS = \
+ -I $(top_srcdir)/src/shared
+
udevlibexec_PROGRAMS = \
v4l_id
diff --git a/src/v4l_id/v4l_id.c b/src/v4l_id/v4l_id.c
index d5463b27e5..0e3d3003cb 100644
--- a/src/v4l_id/v4l_id.c
+++ b/src/v4l_id/v4l_id.c
@@ -13,6 +13,10 @@
* General Public License for more details:
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
#include <stdio.h>
#include <errno.h>
#include <string.h>
@@ -26,17 +30,18 @@
#include <sys/ioctl.h>
#include <linux/videodev2.h>
-int main (int argc, char *argv[])
-{
+#include "util.h"
+
+int main(int argc, char *argv[]) {
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{}
};
- int fd;
+ _cleanup_close_ int fd = -1;
char *device;
struct v4l2_capability v2cap;
- while (1) {
+ for (;;) {
int option;
option = getopt_long(argc, argv, "h", options, NULL);
@@ -45,7 +50,10 @@ int main (int argc, char *argv[])
switch (option) {
case 'h':
- printf("Usage: v4l_id [--help] <device file>\n\n");
+ printf("%s [-h,--help] <device file>\n\n"
+ "Video4Linux device identification.\n\n"
+ " -h Print this message\n"
+ , program_invocation_short_name);
return 0;
default:
return 1;
@@ -55,11 +63,11 @@ int main (int argc, char *argv[])
if (device == NULL)
return 2;
- fd = open (device, O_RDONLY);
+ fd = open(device, O_RDONLY);
if (fd < 0)
return 3;
- if (ioctl (fd, VIDIOC_QUERYCAP, &v2cap) == 0) {
+ 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=:");
@@ -78,6 +86,5 @@ int main (int argc, char *argv[])
printf("\n");
}
- close (fd);
return 0;
}