From 4d8c4ea7e66889dc49e456feacf14ad59ed5fb6e Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Fri, 31 Jul 2009 15:53:11 +0000 Subject: [PATCH] Add libdevmapper functions to support synchronisation with udev. --- WHATS_NEW | 1 + libdm/.exported_symbols | 6 + libdm/ioctl/libdm-iface.c | 7 + libdm/libdevmapper.h | 12 ++ libdm/libdm-common.c | 289 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 315 insertions(+) diff --git a/WHATS_NEW b/WHATS_NEW index 68c74974c..4cc70bb98 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ Version 2.02.51 - ================================ + Add libdevmapper functions to support synchronisation with udev. Added configure --enable-udev_rules --enable-udev_sync. Added configure --with-udev-prefix --with-udevdir. Added udev dir to hold udev rules. diff --git a/libdm/.exported_symbols b/libdm/.exported_symbols index bea4a55b8..c829fa764 100644 --- a/libdm/.exported_symbols +++ b/libdm/.exported_symbols @@ -24,6 +24,7 @@ dm_task_get_uuid dm_task_get_read_ahead dm_task_set_ro dm_task_set_newname +dm_task_set_cookie dm_task_set_event_nr dm_task_set_major dm_task_set_minor @@ -154,3 +155,8 @@ dm_list_last dm_list_prev dm_list_next dm_list_size +dm_udev_set_sync_support +dm_udev_get_sync_support +dm_udev_notify +dm_udev_wait +dm_udev_cleanup diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c index 2f8a93064..bd9bce026 100644 --- a/libdm/ioctl/libdm-iface.c +++ b/libdm/ioctl/libdm-iface.c @@ -864,6 +864,13 @@ int dm_check_version(void) return 0; } +int dm_cookie_supported(void) +{ + return (dm_check_version() && + _dm_version >= 4 && + _dm_version_minor >= 15); +} + void *dm_get_next_target(struct dm_task *dmt, void *next, uint64_t *start, uint64_t *length, char **target_type, char **params) diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index 69faa80e6..6bf9dab60 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -164,6 +164,7 @@ int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor, int allow int dm_task_set_uid(struct dm_task *dmt, uid_t uid); int dm_task_set_gid(struct dm_task *dmt, gid_t gid); int dm_task_set_mode(struct dm_task *dmt, mode_t mode); +int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie); int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr); int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start); int dm_task_set_message(struct dm_task *dmt, const char *message); @@ -1010,4 +1011,15 @@ int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field, void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue); +int dm_cookie_supported(void); + +/* + * Udev notification functions. + */ +void dm_udev_set_sync_support(int sync_with_udev); +int dm_udev_get_sync_support(void); +int dm_udev_notify(uint32_t cookie); +int dm_udev_wait(uint32_t cookie); +int dm_udev_cleanup(uint32_t cookie); + #endif /* LIB_DEVICE_MAPPER_H */ diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c index c44b794c1..940cf5ad4 100644 --- a/libdm/libdm-common.c +++ b/libdm/libdm-common.c @@ -24,6 +24,12 @@ #include #include +#ifdef UDEV_SYNC_SUPPORT +# include +# include +# include +#endif + #ifdef linux # include #endif @@ -33,11 +39,16 @@ #endif #define DEV_DIR "/dev/" +#define COOKIE_MAGIC 0x0D4D static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR; static int _verbose = 0; +#ifdef UDEV_SYNC_SUPPORT +static int _sync_with_udev = 1; +#endif + /* * Library users can provide their own logging * function. @@ -761,3 +772,281 @@ out: return r; } +#ifndef UDEV_SYNC_SUPPORT +void dm_udev_set_sync_support(int sync_with_udev) +{ +} + +int dm_udev_get_sync_support(void) +{ + return 0; +} + +int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie) +{ + *cookie = 0; + + return 1; +} + +int dm_udev_notify(uint32_t cookie) +{ + return 1; +} + +int dm_udev_wait(uint32_t cookie) +{ + return 1; +} + +int dm_udev_cleanup(uint32_t cookie) +{ + return 1; +} + +#else /* UDEV_SYNC_SUPPORT */ + +void dm_udev_set_sync_support(int sync_with_udev) +{ + _sync_with_udev = sync_with_udev; +} + +int dm_udev_get_sync_support(void) +{ + return _sync_with_udev; +} + +static int _get_cookie_sem(uint32_t cookie, int *semid) +{ + if ((*semid = semget((key_t) cookie, 1, 0)) >= 0) + return 1; + + switch (errno) { + case ENOENT: + log_error("Could not find notification " + "semaphore identified by cookie " + "value %" PRIu32 " (0x%x)", + cookie, cookie); + break; + case EACCES: + log_error("No permission to access " + "notificaton semaphore identified " + "by cookie value %" PRIu32 " (0x%x)", + cookie, cookie); + break; + default: + /* FIXME errno use missing */ + log_error("Failed to access notification " + "semaphore identified by cookie " + "value %" PRIu32 " (0x%x)", + cookie, cookie); + break; + } + + return 0; +} + +static int _udev_notify_sem_inc(int semid) +{ + struct sembuf sb = {0, 1, 0}; + + /* FIXME errno use missing */ + return semop(semid, &sb, 1) == 0; +} + +static int _udev_notify_sem_dec(int semid) +{ + /* FIXME Think we should have IPC_NOWAIT here in case something went wrong and it's already 0 */ + struct sembuf sb = {0, -1, 0}; + + /* FIXME errno use missing */ + return semop(semid, &sb, 1) == 0; +} + +static int _udev_notify_sem_destroy(int semid, uint32_t cookie) +{ + /* FIXME errno use missing */ + if (semctl(semid, 0, IPC_RMID, 0) < 0) { + log_error("Could not cleanup notification semaphore " + "identified by cookie value %" PRIu32 " (0x%x)", + cookie, cookie); + return 0; + } + + return 1; +} + +static int _udev_notify_sem_create(uint32_t *cookie, int *semid) +{ + int fd; + int gen_semid; + uint16_t base_cookie; + uint32_t gen_cookie; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { + log_error("Failed to open /dev/urandom " + "to create random cookie value"); + *cookie = 0; + return 0; + } + + /* Generate random cookie value. Be sure it is unique and non-zero. */ + do { + /* FIXME Handle non-error returns from read(). Move _io() into libdm? */ + if (read(fd, &base_cookie, sizeof(base_cookie)) != sizeof(base_cookie)) { + log_error("Failed to initialize notification cookie"); + goto bad; + } + + gen_cookie = COOKIE_MAGIC << 16 | base_cookie; + + if (base_cookie && (gen_semid = semget((key_t) gen_cookie, + 1, 0600 | IPC_CREAT | IPC_EXCL)) < 0) { + switch (errno) { + case EEXIST: + /* if the semaphore key exists, we + * simply generate another random one */ + base_cookie = 0; + break; + case ENOMEM: + log_error("Not enough memory to create " + "notification semaphore"); + goto bad; + case ENOSPC: + /* FIXME Suggest what to check & do */ + log_error("Limit for the maximum number " + "of semaphores reached"); + goto bad; + default: + /* FIXME Use errno */ + log_error("Failed to create " + "notification semaphore"); + goto bad; + } + } + } while (!base_cookie); + + if (semctl(gen_semid, 0, SETVAL, 1) < 0) { + /* FIXME Use errno and give gen_semid */ + log_error("Failed to initialize notification semaphore"); + /* We have to destroy just created semaphore + * so it won't stay in the system. */ + _udev_notify_sem_destroy(gen_semid, gen_cookie); + goto bad; + } + + if (close(fd)) + stack; + + *semid = gen_semid; + *cookie = gen_cookie; + + return 1; + +bad: + if (close(fd)) + stack; + + *cookie = 0; + + return 0; +} + +int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie) +{ + int semid; + + if (!dm_udev_get_sync_support() || !dm_cookie_supported()) { + dmt->event_nr = *cookie = 0; + return 1; + } + + if (*cookie) { + if (!_get_cookie_sem(*cookie, &semid)) + goto_bad; + } else if (!_udev_notify_sem_create(cookie, &semid)) + goto_bad; + + if (!_udev_notify_sem_inc(semid)) { + log_error("Could not set notification semaphore " + "identified by cookie value %" PRIu32 " (0x%x)", + *cookie, *cookie); + goto bad; + } + + dmt->event_nr = *cookie; + return 1; + +bad: + dmt->event_nr = 0; + return 0; +} + +int dm_udev_notify(uint32_t cookie) +{ + int semid; + + if (!cookie || !dm_udev_get_sync_support() || !dm_cookie_supported()) + return 1; + + if (!_get_cookie_sem(cookie, &semid)) + return_0; + + if (!_udev_notify_sem_dec(semid)) { + log_error("Could not signal waiting process using notification " + "semaphore identified by cookie value %" PRIu32 " (0x%x)", + cookie, cookie); + return 0; + } + + return 1; +} + +int dm_udev_wait(uint32_t cookie) +{ + int semid; + struct sembuf sb = {0, 0, 0}; + + if (!cookie || !dm_udev_get_sync_support() || !dm_cookie_supported()) + return 1; + + if (!_get_cookie_sem(cookie, &semid)) + return_0; + + if (!_udev_notify_sem_dec(semid)) { + log_error("Failed to set a proper state for notification " + "semaphore identified by cookie value %" PRIu32 " (0x%x) " + "to initialize waiting for incoming notifications.", + cookie, cookie); + _udev_notify_sem_destroy(semid, cookie); + return 0; + } + +repeat_wait: + if (semop(semid, &sb, 1) < 0) { + if (errno == EINTR) + goto repeat_wait; + /* FIXME missing errno use */ + log_error("Could not set wait state for notification semaphore " + "identified by cookie value %" PRIu32 " (0x%x)", + cookie, cookie); + _udev_notify_sem_destroy(semid, cookie); + return 0; + } + + return _udev_notify_sem_destroy(semid, cookie); +} + +int dm_udev_cleanup(uint32_t cookie) +{ + int semid; + + if (!cookie || !dm_udev_get_sync_support() || !dm_cookie_supported()) + return 1; + + if (!_get_cookie_sem(cookie, &semid)) + return 0; + + return _udev_notify_sem_destroy(semid, cookie); +} +#endif /* UDEV_SYNC_SUPPORT */