#include "device-monitor-private.h"
#include "device-private.h"
#include "device-util.h"
+#include "errno-list.h"
#include "event-util.h"
#include "fd-util.h"
#include "fileio.h"
/* passed from worker to main process */
typedef enum EventResult {
- EVENT_RESULT_SUCCESS,
- EVENT_RESULT_FAILED,
- EVENT_RESULT_TRY_AGAIN, /* when the block device is locked by another process. */
+ EVENT_RESULT_NERRNO_MIN = -ERRNO_MAX,
+ EVENT_RESULT_NERRNO_MAX = -1,
+ EVENT_RESULT_EXIT_STATUS_BASE = 0,
+ EVENT_RESULT_EXIT_STATUS_MAX = 255,
+ EVENT_RESULT_TRY_AGAIN = 256, /* when the block device is locked by another process. */
+ EVENT_RESULT_SIGNAL_BASE = 257,
+ EVENT_RESULT_SIGNAL_MAX = EVENT_RESULT_SIGNAL_BASE + _NSIG,
_EVENT_RESULT_MAX,
- _EVENT_RESULT_INVALID = -EINVAL,
+ _EVENT_RESULT_INVALID = -EINVAL,
} EventResult;
static Event *event_free(Event *event) {
return 1;
}
-static void device_broadcast(sd_device_monitor *monitor, sd_device *dev) {
+static void device_broadcast(sd_device_monitor *monitor, sd_device *dev, int result) {
int r;
assert(dev);
if (!monitor)
return;
+ if (result != 0) {
+ (void) device_add_property(dev, "UDEV_WORKER_FAILED", "1");
+
+ switch (result) {
+ case EVENT_RESULT_NERRNO_MIN ... EVENT_RESULT_NERRNO_MAX:
+ (void) device_add_propertyf(dev, "UDEV_WORKER_ERRNO", "%i", -result);
+ (void) device_add_propertyf(dev, "UDEV_WORKER_ERRNO_NAME", "%s", strna(errno_to_name(result)));
+ break;
+
+ case EVENT_RESULT_EXIT_STATUS_BASE ... EVENT_RESULT_EXIT_STATUS_MAX:
+ (void) device_add_propertyf(dev, "UDEV_WORKER_EXIT_STATUS", "%i", result - EVENT_RESULT_EXIT_STATUS_BASE);
+ break;
+
+ case EVENT_RESULT_TRY_AGAIN:
+ assert_not_reached();
+ break;
+
+ case EVENT_RESULT_SIGNAL_BASE ... EVENT_RESULT_SIGNAL_MAX:
+ (void) device_add_propertyf(dev, "UDEV_WORKER_SIGNAL", "%i", result - EVENT_RESULT_SIGNAL_BASE);
+ (void) device_add_propertyf(dev, "UDEV_WORKER_SIGNAL_NAME", "%s", strna(signal_to_string(result - EVENT_RESULT_SIGNAL_BASE)));
+ break;
+
+ default:
+ log_device_warning(dev, "Unknown event result \"%i\", ignoring.", result);
+ }
+ }
+
r = device_monitor_send_device(monitor, NULL, dev);
if (r < 0)
log_device_warning_errno(dev, r,
"Failed to broadcast event to libudev listeners, ignoring: %m");
}
-static int worker_send_result(Manager *manager, EventResult result) {
+static int worker_send_result(Manager *manager, int result) {
assert(manager);
assert(manager->worker_watch[WRITE_END] >= 0);
*
* The user-facing side of this: https://systemd.io/BLOCK_DEVICE_LOCKING */
r = worker_lock_block_device(dev, &fd_lock);
+ if (r == -EAGAIN)
+ return EVENT_RESULT_TRY_AGAIN;
if (r < 0)
return r;
static int worker_device_monitor_handler(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *manager = userdata;
- EventResult result;
int r;
assert(dev);
assert(manager);
r = worker_process_device(manager, dev);
- if (r == -EAGAIN) {
+ if (r == EVENT_RESULT_TRY_AGAIN)
/* if we couldn't acquire the flock(), then requeue the event */
- result = EVENT_RESULT_TRY_AGAIN;
- log_device_debug_errno(dev, r, "Block device is currently locked, requeueing the event.");
- } else if (r < 0) {
- result = EVENT_RESULT_FAILED;
- log_device_warning_errno(dev, r, "Failed to process device, ignoring: %m");
- } else
- result = EVENT_RESULT_SUCCESS;
+ log_device_debug(dev, "Block device is currently locked, requeueing the event.");
+ else {
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to process device, ignoring: %m");
- if (result != EVENT_RESULT_TRY_AGAIN)
/* send processed event back to libudev listeners */
- device_broadcast(monitor, dev);
+ device_broadcast(monitor, dev, r);
+ }
/* send udevd the result of the event execution */
- r = worker_send_result(manager, result);
+ r = worker_send_result(manager, r);
if (r < 0)
log_device_warning_errno(dev, r, "Failed to send signal to main daemon, ignoring: %m");
assert(manager);
for (;;) {
- EventResult result;
+ int result;
struct iovec iovec = IOVEC_MAKE(&result, sizeof(result));
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
struct msghdr msghdr = {
cmsg_close_all(&msghdr);
- if (size != sizeof(EventResult)) {
+ if (size != sizeof(result)) {
log_warning("Ignoring worker message with invalid size %zi bytes", size);
continue;
}
/* worker returned */
if (result == EVENT_RESULT_TRY_AGAIN &&
event_requeue(worker->event) < 0)
- device_broadcast(manager->monitor, worker->event->dev);
+ device_broadcast(manager->monitor, worker->event->dev, -ETIMEDOUT);
/* When event_requeue() succeeds, worker->event is NULL, and event_free() handles NULL gracefully. */
event_free(worker->event);
device_tag_index(worker->event->dev, NULL, false);
/* Forward kernel event to libudev listeners */
- device_broadcast(manager->monitor, worker->event->dev);
+ device_broadcast(manager->monitor, worker->event->dev,
+ WIFEXITED(status) ? EVENT_RESULT_EXIT_STATUS_BASE + WEXITSTATUS(status):
+ WIFSIGNALED(status) ? EVENT_RESULT_SIGNAL_BASE + WTERMSIG(status) : 0);
}
worker_free(worker);