diff options
Diffstat (limited to 'src/libsystemd')
73 files changed, 5655 insertions, 697 deletions
diff --git a/src/libsystemd/libsystemd.sym.m4 b/src/libsystemd/libsystemd.sym.m4 index 76a8c921c6..0b94a87dd6 100644 --- a/src/libsystemd/libsystemd.sym.m4 +++ b/src/libsystemd/libsystemd.sym.m4 @@ -63,7 +63,7 @@ global: sd_journal_open_files; sd_journal_open_container; - /* sd-dameon */ + /* sd-daemon */ sd_booted; sd_is_fifo; sd_is_mq; @@ -163,8 +163,13 @@ global: sd_pid_notify_with_fds; } LIBSYSTEMD_217; -m4_ifdef(`ENABLE_KDBUS', -LIBSYSTEMD_FUTURE { +LIBSYSTEMD_220 { +global: + sd_pid_get_user_slice; + sd_peer_get_user_slice; +} LIBSYSTEMD_219; + +LIBSYSTEMD_221 { global: /* sd-bus */ sd_bus_default; @@ -179,25 +184,36 @@ global: sd_bus_set_address; sd_bus_set_fd; sd_bus_set_exec; + sd_bus_get_address; sd_bus_set_bus_client; + sd_bus_is_bus_client; sd_bus_set_server; + sd_bus_is_server; sd_bus_set_anonymous; + sd_bus_is_anonymous; sd_bus_set_trusted; + sd_bus_is_trusted; sd_bus_set_monitor; + sd_bus_is_monitor; sd_bus_set_description; - sd_bus_negotiate_fds; - sd_bus_negotiate_timestamp; + sd_bus_get_description; sd_bus_negotiate_creds; + sd_bus_negotiate_timestamp; + sd_bus_negotiate_fds; + sd_bus_can_send; + sd_bus_get_creds_mask; + sd_bus_set_allow_interactive_authorization; + sd_bus_get_allow_interactive_authorization; sd_bus_start; sd_bus_close; sd_bus_try_close; sd_bus_ref; sd_bus_unref; sd_bus_is_open; - sd_bus_can_send; sd_bus_get_bus_id; + sd_bus_get_scope; + sd_bus_get_tid; sd_bus_get_owner_creds; - sd_bus_get_description; sd_bus_send; sd_bus_send_to; sd_bus_call; @@ -209,9 +225,10 @@ global: sd_bus_process_priority; sd_bus_wait; sd_bus_flush; - sd_bus_get_current_message; sd_bus_get_current_slot; - sd_bus_get_tid; + sd_bus_get_current_message; + sd_bus_get_current_handler; + sd_bus_get_current_userdata; sd_bus_attach_event; sd_bus_detach_event; sd_bus_get_event; @@ -231,6 +248,8 @@ global: sd_bus_slot_get_description; sd_bus_slot_set_description; sd_bus_slot_get_current_message; + sd_bus_slot_get_current_handler; + sd_bus_slot_get_current_userdata; sd_bus_message_new_signal; sd_bus_message_new_method_call; sd_bus_message_new_method_return; @@ -240,13 +259,13 @@ global: sd_bus_message_new_method_errnof; sd_bus_message_ref; sd_bus_message_unref; - sd_bus_message_get_bus; sd_bus_message_get_type; sd_bus_message_get_cookie; sd_bus_message_get_reply_cookie; + sd_bus_message_get_priority; sd_bus_message_get_expect_reply; sd_bus_message_get_auto_start; - sd_bus_message_get_priority; + sd_bus_message_get_allow_interactive_authorization; sd_bus_message_get_signature; sd_bus_message_get_path; sd_bus_message_get_interface; @@ -258,13 +277,16 @@ global: sd_bus_message_get_monotonic_usec; sd_bus_message_get_realtime_usec; sd_bus_message_get_seqnum; + sd_bus_message_get_bus; sd_bus_message_get_creds; - sd_bus_message_is_empty; sd_bus_message_is_signal; sd_bus_message_is_method_call; sd_bus_message_is_method_error; + sd_bus_message_is_empty; + sd_bus_message_has_signature; sd_bus_message_set_expect_reply; sd_bus_message_set_auto_start; + sd_bus_message_set_allow_interactive_authorization; sd_bus_message_set_destination; sd_bus_message_set_priority; sd_bus_message_append; @@ -316,22 +338,33 @@ global: sd_bus_emit_interfaces_removed_strv; sd_bus_emit_interfaces_removed; sd_bus_query_sender_creds; + sd_bus_query_sender_privilege; sd_bus_creds_new_from_pid; sd_bus_creds_ref; sd_bus_creds_unref; sd_bus_creds_get_mask; - sd_bus_creds_get_uid; - sd_bus_creds_get_gid; + sd_bus_creds_get_augmented_mask; sd_bus_creds_get_pid; + sd_bus_creds_get_ppid; sd_bus_creds_get_tid; + sd_bus_creds_get_uid; + sd_bus_creds_get_euid; + sd_bus_creds_get_suid; + sd_bus_creds_get_fsuid; + sd_bus_creds_get_gid; + sd_bus_creds_get_egid; + sd_bus_creds_get_sgid; + sd_bus_creds_get_fsgid; + sd_bus_creds_get_supplementary_gids; sd_bus_creds_get_comm; sd_bus_creds_get_tid_comm; sd_bus_creds_get_exe; sd_bus_creds_get_cmdline; sd_bus_creds_get_cgroup; sd_bus_creds_get_unit; - sd_bus_creds_get_user_unit; sd_bus_creds_get_slice; + sd_bus_creds_get_user_unit; + sd_bus_creds_get_user_slice; sd_bus_creds_get_session; sd_bus_creds_get_owner_uid; sd_bus_creds_has_effective_cap; @@ -341,6 +374,7 @@ global: sd_bus_creds_get_selinux_context; sd_bus_creds_get_audit_session_id; sd_bus_creds_get_audit_login_uid; + sd_bus_creds_get_tty; sd_bus_creds_get_unique_name; sd_bus_creds_get_well_known_names; sd_bus_creds_get_description; @@ -355,6 +389,7 @@ global: sd_bus_error_copy; sd_bus_error_is_set; sd_bus_error_has_name; + sd_bus_error_add_map; sd_bus_path_encode; sd_bus_path_decode; sd_bus_track_new; @@ -382,9 +417,10 @@ global: sd_event_add_signal; sd_event_add_child; sd_event_add_defer; + sd_event_add_post; sd_event_add_exit; - sd_event_wait; sd_event_prepare; + sd_event_wait; sd_event_dispatch; sd_event_run; sd_event_loop; @@ -398,6 +434,9 @@ global: sd_event_get_watchdog; sd_event_source_ref; sd_event_source_unref; + sd_event_source_get_event; + sd_event_source_get_userdata; + sd_event_source_set_userdata; sd_event_source_set_description; sd_event_source_get_description; sd_event_source_set_prepare; @@ -406,8 +445,6 @@ global: sd_event_source_set_priority; sd_event_source_get_enabled; sd_event_source_set_enabled; - sd_event_source_get_userdata; - sd_event_source_set_userdata; sd_event_source_get_io_fd; sd_event_source_set_io_fd; sd_event_source_get_io_events; @@ -420,8 +457,11 @@ global: sd_event_source_get_time_clock; sd_event_source_get_signal; sd_event_source_get_child_pid; - sd_event_source_get_event; +} LIBSYSTEMD_220; +m4_ifdef(`ENABLE_KDBUS', +LIBSYSTEMD_FUTURE { +global: /* sd-utf8 */ sd_utf8_is_valid; sd_ascii_is_valid; @@ -454,5 +494,5 @@ global: /* sd-path */ sd_path_home; sd_path_search; -} LIBSYSTEMD_217; +} LIBSYSTEMD_220; ) diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h index 96c82d7e8f..a9350d7f51 100644 --- a/src/libsystemd/sd-bus/bus-bloom.h +++ b/src/libsystemd/sd-bus/bus-bloom.h @@ -23,7 +23,6 @@ #include <stdbool.h> #include <stdint.h> -#include <sys/types.h> /* * Our default bloom filter has the following parameters: diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index eb60ca3628..52f8dfd3be 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -35,9 +35,9 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLOCK), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLOCK), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLOCK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, EBADR), SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), @@ -60,7 +60,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), - SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, ENOTSUP), + SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), @@ -70,7 +70,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT), SD_BUS_ERROR_MAP(BUS_ERROR_NO_RESOURCES, ENOMEM), - SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLOCK), + SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index e935833bc3..b17b62ac93 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -21,7 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "sd-bus.h" #include "bus-error.h" #define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index d29b98a269..f157c25bba 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -23,7 +23,7 @@ #include <fcntl.h> #include "util.h" -#include "fileio.h" +#include "process-util.h" #include "bus-internal.h" #include "bus-socket.h" #include "bus-container.h" diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 06e5b4fd9a..43ddfc651d 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -34,7 +34,6 @@ #include "bus-bloom.h" #include "bus-util.h" #include "capability.h" -#include "cgroup-util.h" _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { int r; @@ -43,6 +42,9 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { assert_return(unique, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); + if (!bus->bus_client) + return -EINVAL; + r = bus_ensure_running(bus); if (r < 0) return r; @@ -77,7 +79,7 @@ static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags if (r < 0) return -errno; - if (n->flags & KDBUS_NAME_IN_QUEUE) + if (n->return_flags & KDBUS_NAME_IN_QUEUE) return 0; return 1; @@ -131,12 +133,14 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) _public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { assert_return(bus, -EINVAL); assert_return(name, -EINVAL); - assert_return(bus->bus_client, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); assert_return(service_name_is_valid(name), -EINVAL); assert_return(name[0] != ':', -EINVAL); + if (!bus->bus_client) + return -EINVAL; + /* Don't allow requesting the special driver and local names */ if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) return -EINVAL; @@ -214,11 +218,13 @@ static int bus_release_name_dbus1(sd_bus *bus, const char *name) { _public_ int sd_bus_release_name(sd_bus *bus, const char *name) { assert_return(bus, -EINVAL); assert_return(name, -EINVAL); - assert_return(bus->bus_client, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(service_name_is_valid(name), -EINVAL); assert_return(name[0] != ':', -EINVAL); + if (!bus->bus_client) + return -EINVAL; + /* Don't allow releasing the special driver and local names */ if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) return -EINVAL; @@ -375,6 +381,9 @@ _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatabl assert_return(acquired || activatable, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); + if (!bus->bus_client) + return -EINVAL; + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; @@ -414,6 +423,26 @@ static int bus_populate_creds_from_items( c->mask |= SD_BUS_CREDS_TID; } + if (mask & SD_BUS_CREDS_PPID) { + if (item->pids.ppid > 0) { + c->ppid = (pid_t) item->pids.ppid; + c->mask |= SD_BUS_CREDS_PPID; + } else if (item->pids.pid == 1) { + /* The structure doesn't + * really distinguish the case + * where a process has no + * parent and where we don't + * know it because it could + * not be translated due to + * namespaces. However, we + * know that PID 1 has no + * parent process, hence let's + * patch that in, manually. */ + c->ppid = 0; + c->mask |= SD_BUS_CREDS_PPID; + } + } + break; case KDBUS_ITEM_CREDS: @@ -551,12 +580,12 @@ static int bus_populate_creds_from_items( break; case KDBUS_ITEM_AUDIT: - if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID && (uint32_t) item->audit.sessionid != (uint32_t) -1) { + if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { c->audit_session_id = (uint32_t) item->audit.sessionid; c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; } - if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID && (uid_t) item->audit.loginuid != UID_INVALID) { + if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { c->audit_login_uid = (uid_t) item->audit.loginuid; c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; } @@ -584,16 +613,17 @@ static int bus_populate_creds_from_items( case KDBUS_ITEM_AUXGROUPS: if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - size_t n; + size_t i, n; uid_t *g; - assert_cc(sizeof(gid_t) == sizeof(uint32_t)); - - n = (item->size - offsetof(struct kdbus_item, data32)) / sizeof(uint32_t); - g = newdup(gid_t, item->data32, n); + n = (item->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); + g = new(gid_t, n); if (!g) return -ENOMEM; + for (i = 0; i < n; i++) + g[i] = item->data64[i]; + free(c->supplementary_gids); c->supplementary_gids = g; c->n_supplementary_gids = n; @@ -622,7 +652,7 @@ int bus_get_name_creds_kdbus( int r; if (streq(name, "org.freedesktop.DBus")) - return -ENOTSUP; + return -EOPNOTSUPP; r = bus_kernel_parse_unique_name(name, &id); if (r < 0) @@ -644,7 +674,8 @@ int bus_get_name_creds_kdbus( * the bits we want, then ask for the PID/TID so that we * can read the rest from /proc. */ if ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + (mask & (SD_BUS_CREDS_PPID| + SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| @@ -654,7 +685,7 @@ int bus_get_name_creds_kdbus( mask |= SD_BUS_CREDS_PID; cmd->size = size; - cmd->flags = attach_flags_to_kdbus(mask); + cmd->attach_flags = attach_flags_to_kdbus(mask); r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd); if (r < 0) @@ -875,11 +906,13 @@ _public_ int sd_bus_get_name_creds( assert_return(bus, -EINVAL); assert_return(name, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -ENOTSUP); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); assert_return(mask == 0 || creds, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(service_name_is_valid(name), -EINVAL); - assert_return(bus->bus_client, -ENODATA); + + if (!bus->bus_client) + return -EINVAL; if (streq(name, "org.freedesktop.DBus.Local")) return -EINVAL; @@ -910,7 +943,8 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds ** * to get the bits we want, then ask for the PID/TID so that we * can read the rest from /proc. */ if ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + (mask & (SD_BUS_CREDS_PPID| + SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| @@ -919,7 +953,7 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds ** SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) mask |= SD_BUS_CREDS_PID; - cmd.flags = attach_flags_to_kdbus(mask); + cmd.attach_flags = attach_flags_to_kdbus(mask); r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); if (r < 0) @@ -989,7 +1023,7 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { assert_return(bus, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -ENOTSUP); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); assert_return(ret, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -1393,6 +1427,9 @@ int bus_add_match_internal( assert(bus); + if (!bus->bus_client) + return -EINVAL; + if (bus->is_kernel) return bus_add_match_internal_kernel(bus, components, n_components, cookie); else @@ -1452,6 +1489,9 @@ int bus_remove_match_internal( assert(bus); + if (!bus->bus_client) + return -EINVAL; + if (bus->is_kernel) return bus_remove_match_internal_kernel(bus, cookie); else @@ -1469,6 +1509,9 @@ _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_ assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(service_name_is_valid(name), -EINVAL); + if (!bus->bus_client) + return -EINVAL; + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index a6317e9785..28bc8d2818 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -462,11 +462,22 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b /* No data passed? Or not enough data passed to retrieve the missing bits? */ if (!c || !(c->mask & SD_BUS_CREDS_PID)) { /* We couldn't read anything from the call, let's try - * to get it from the sender or peer */ + * to get it from the sender or peer. */ if (call->sender) + /* There's a sender, but the creds are + * missing. This means we are talking via + * dbus1, or are getting a message that was + * sent to us via kdbus, but was converted + * from a dbus1 message by the bus-proxy and + * thus also lacks the creds. */ return sd_bus_get_name_creds(call->bus, call->sender, mask, creds); else + /* There's no sender, hence we are on a dbus1 + * direct connection. For direct connections + * the credentials of the AF_UNIX peer matter, + * which may be queried via + * sd_bus_get_owner_creds(). */ return sd_bus_get_owner_creds(call->bus, mask, creds); } @@ -488,10 +499,18 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) return -ENOTCONN; if (capability >= 0) { + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); if (r < 0) return r; + /* We cannot use augmented caps for authorization, + * since then data is acquired raceful from + * /proc. This can never actually happen, but let's + * better be safe than sorry, and do an extra check + * here. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM); + /* Note that not even on kdbus we might have the caps * field, due to faked identities, or namespace * translation issues. */ @@ -512,6 +531,13 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) if (our_uid != 0 || !know_caps || capability < 0) { uid_t sender_uid; + /* We cannot use augmented uid/euid for authorization, + * since then data is acquired raceful from + * /proc. This can never actually happen, but let's + * better be safe than sorry, and do an extra check + * here. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM); + /* Try to use the EUID, if we have it. */ r = sd_bus_creds_get_euid(creds, &sender_uid); if (r < 0) diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index ea8a619c5a..4d67619cf8 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -23,13 +23,15 @@ #include <linux/capability.h> #include "util.h" +#include "formats-util.h" +#include "process-util.h" +#include "terminal-util.h" #include "capability.h" #include "cgroup-util.h" #include "fileio.h" #include "audit.h" #include "bus-message.h" #include "bus-util.h" -#include "time-util.h" #include "strv.h" #include "bus-creds.h" #include "bus-label.h" @@ -51,7 +53,10 @@ void bus_creds_done(sd_bus_creds *c) { free(c->unit); free(c->user_unit); free(c->slice); + free(c->user_slice); free(c->unescaped_description); + free(c->supplementary_gids); + free(c->tty); free(c->well_known_names); /* note that this is an strv, but * we only free the array, not the @@ -101,7 +106,9 @@ _public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) { free(c->unique_name); free(c->cgroup_root); free(c->description); + free(c->supplementary_gids); + c->supplementary_gids = NULL; strv_free(c->well_known_names); c->well_known_names = NULL; @@ -127,6 +134,12 @@ _public_ uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c) { return c->mask; } +_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) { + assert_return(c, 0); + + return c->augmented; +} + sd_bus_creds* bus_creds_new(void) { sd_bus_creds *c; @@ -144,7 +157,7 @@ _public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t m int r; assert_return(pid >= 0, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP); + assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); assert_return(ret, -EINVAL); if (pid == 0) @@ -227,7 +240,6 @@ _public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) { return 0; } - _public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) { assert_return(c, -EINVAL); assert_return(egid, -EINVAL); @@ -284,6 +296,23 @@ _public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) { return 0; } +_public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) { + assert_return(c, -EINVAL); + assert_return(ppid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_PPID)) + return -ENODATA; + + /* PID 1 has no parent process. Let's distinguish the case of + * not knowing and not having a parent process by the returned + * error code. */ + if (c->ppid == 0) + return -ENXIO; + + *ppid = c->ppid; + return 0; +} + _public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) { assert_return(c, -EINVAL); assert_return(tid, -EINVAL); @@ -338,7 +367,9 @@ _public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) { if (!(c->mask & SD_BUS_CREDS_EXE)) return -ENODATA; - assert(c->exe); + if (!c->exe) + return -ENXIO; + *ret = c->exe; return 0; } @@ -436,6 +467,33 @@ _public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) { return 0; } +_public_ int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_USER_SLICE)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->user_slice) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_user_slice(shifted, (char**) &c->user_slice); + if (r < 0) + return r; + } + + *ret = c->user_slice; + return 0; +} + _public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) { int r; @@ -488,8 +546,8 @@ _public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) { if (!(c->mask & SD_BUS_CREDS_CMDLINE)) return -ENODATA; - assert_return(c->cmdline, -ESRCH); - assert(c->cmdline); + if (!c->cmdline) + return -ENXIO; if (!c->cmdline_array) { c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size); @@ -508,6 +566,9 @@ _public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessio if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID)) return -ENODATA; + if (c->audit_session_id == AUDIT_SESSION_INVALID) + return -ENXIO; + *sessionid = c->audit_session_id; return 0; } @@ -519,10 +580,27 @@ _public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) { if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID)) return -ENODATA; + if (c->audit_login_uid == UID_INVALID) + return -ENXIO; + *uid = c->audit_login_uid; return 0; } +_public_ int sd_bus_creds_get_tty(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_TTY)) + return -ENODATA; + + if (!c->tty) + return -ENXIO; + + *ret = c->tty; + return 0; +} + _public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) { assert_return(c, -EINVAL); assert_return(unique_name, -EINVAL); @@ -593,10 +671,11 @@ static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { assert(capability >= 0); assert(c->capability); - sz = DIV_ROUND_UP(cap_last_cap(), 32U); - if ((unsigned)capability > cap_last_cap()) + if ((unsigned) capability > cap_last_cap()) return 0; + sz = DIV_ROUND_UP(cap_last_cap(), 32U); + return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability)); } @@ -693,32 +772,33 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (!(mask & SD_BUS_CREDS_AUGMENT)) return 0; - missing = mask & ~c->mask; - if (missing == 0) - return 0; - /* Try to retrieve PID from creds if it wasn't passed to us */ if (pid <= 0 && (c->mask & SD_BUS_CREDS_PID)) pid = c->pid; - if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID)) - tid = c->pid; - /* Without pid we cannot do much... */ if (pid <= 0) return 0; - if (pid > 0) { - c->pid = pid; - c->mask |= SD_BUS_CREDS_PID; - } + /* Try to retrieve TID from creds if it wasn't passed to us */ + if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID)) + tid = c->tid; + + /* Calculate what we shall and can add */ + missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT); + if (missing == 0) + return 0; + + c->pid = pid; + c->mask |= SD_BUS_CREDS_PID; if (tid > 0) { c->tid = tid; c->mask |= SD_BUS_CREDS_TID; } - if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | + if (missing & (SD_BUS_CREDS_PPID | + SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS | @@ -741,6 +821,25 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { FOREACH_LINE(line, f, return -errno) { truncate_nl(line); + if (missing & SD_BUS_CREDS_PPID) { + p = startswith(line, "PPid:"); + if (p) { + p += strspn(p, WHITESPACE); + + /* Explicitly check for PPID 0 (which is the case for PID 1) */ + if (!streq(p, "0")) { + r = parse_pid(p, &c->ppid); + if (r < 0) + return r; + + } else + c->ppid = 0; + + c->mask |= SD_BUS_CREDS_PPID; + continue; + } + } + if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) { p = startswith(line, "Uid:"); if (p) { @@ -750,10 +849,15 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4) return -EIO; - c->uid = (uid_t) uid; - c->euid = (uid_t) euid; - c->suid = (uid_t) suid; - c->fsuid = (uid_t) fsuid; + if (missing & SD_BUS_CREDS_UID) + c->uid = (uid_t) uid; + if (missing & SD_BUS_CREDS_EUID) + c->euid = (uid_t) euid; + if (missing & SD_BUS_CREDS_SUID) + c->suid = (uid_t) suid; + if (missing & SD_BUS_CREDS_FSUID) + c->fsuid = (uid_t) fsuid; + c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID); continue; } @@ -768,10 +872,15 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4) return -EIO; - c->gid = (gid_t) gid; - c->egid = (gid_t) egid; - c->sgid = (gid_t) sgid; - c->fsgid = (gid_t) fsgid; + if (missing & SD_BUS_CREDS_GID) + c->gid = (gid_t) gid; + if (missing & SD_BUS_CREDS_EGID) + c->egid = (gid_t) egid; + if (missing & SD_BUS_CREDS_SGID) + c->sgid = (gid_t) sgid; + if (missing & SD_BUS_CREDS_FSGID) + c->fsgid = (gid_t) fsgid; + c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID); continue; } @@ -879,7 +988,17 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & SD_BUS_CREDS_EXE) { r = get_process_exe(pid, &c->exe); - if (r < 0) { + if (r == -ESRCH) { + /* Unfortunately we cannot really distinguish + * the case here where the process does not + * exist, and /proc/$PID/exe being unreadable + * because $PID is a kernel thread. Hence, + * assume it is a kernel thread, and rely on + * that this case is caught with a later + * call. */ + c->exe = NULL; + c->mask |= SD_BUS_CREDS_EXE; + } else if (r < 0) { if (r != -EPERM && r != -EACCES) return r; } else @@ -891,17 +1010,18 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { p = procfs_file_alloca(pid, "cmdline"); r = read_full_file(p, &c->cmdline, &c->cmdline_size); + if (r == -ENOENT) + return -ESRCH; if (r < 0) { - if (r == -ENOENT) - return -ESRCH; if (r != -EPERM && r != -EACCES) return r; } else { if (c->cmdline_size == 0) { free(c->cmdline); c->cmdline = NULL; - } else - c->mask |= SD_BUS_CREDS_CMDLINE; + } + + c->mask |= SD_BUS_CREDS_CMDLINE; } } @@ -912,34 +1032,43 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { return -ENOMEM; r = read_one_line_file(p, &c->tid_comm); + if (r == -ENOENT) + return -ESRCH; if (r < 0) { - if (r == -ENOENT) - return -ESRCH; if (r != -EPERM && r != -EACCES) return r; } else c->mask |= SD_BUS_CREDS_TID_COMM; } - if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { + if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { - r = cg_pid_get_path(NULL, pid, &c->cgroup); - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else { + if (!c->cgroup) { + r = cg_pid_get_path(NULL, pid, &c->cgroup); + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } + } + + if (!c->cgroup_root) { r = cg_get_root_path(&c->cgroup_root); if (r < 0) return r; - - c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID); } + + if (c->cgroup) + c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID); } if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) { r = audit_session_from_pid(pid, &c->audit_session_id); - if (r < 0) { - if (r != -ENOTSUP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES) + if (r == -ENXIO) { + /* ENXIO means: no audit session id assigned */ + c->audit_session_id = AUDIT_SESSION_INVALID; + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } else if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) return r; } else c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; @@ -947,13 +1076,43 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) { r = audit_loginuid_from_pid(pid, &c->audit_login_uid); - if (r < 0) { - if (r != -ENOTSUP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES) + if (r == -ENXIO) { + /* ENXIO means: no audit login uid assigned */ + c->audit_login_uid = UID_INVALID; + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } else if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) return r; } else c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; } + if (missing & SD_BUS_CREDS_TTY) { + r = get_ctty(pid, NULL, &c->tty); + if (r == -ENXIO) { + /* ENXIO means: process has no controlling TTY */ + c->tty = NULL; + c->mask |= SD_BUS_CREDS_TTY; + } else if (r < 0) { + if (r != -EPERM && r != -EACCES && r != -ENOENT) + return r; + } else + c->mask |= SD_BUS_CREDS_TTY; + } + + /* In case only the exe path was to be read we cannot + * distinguish the case where the exe path was unreadable + * because the process was a kernel thread, or when the + * process didn't exist at all. Hence, let's do a final check, + * to be sure. */ + if (!pid_is_alive(pid)) + return -ESRCH; + + if (tid > 0 && tid != pid && !pid_is_unwaited(tid)) + return -ESRCH; + + c->augmented = missing & c->mask; + return 0; } @@ -978,6 +1137,21 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) /* Copy the original data over */ + if (c->mask & mask & SD_BUS_CREDS_PID) { + n->pid = c->pid; + n->mask |= SD_BUS_CREDS_PID; + } + + if (c->mask & mask & SD_BUS_CREDS_TID) { + n->tid = c->tid; + n->mask |= SD_BUS_CREDS_TID; + } + + if (c->mask & mask & SD_BUS_CREDS_PPID) { + n->ppid = c->ppid; + n->mask |= SD_BUS_CREDS_PPID; + } + if (c->mask & mask & SD_BUS_CREDS_UID) { n->uid = c->uid; n->mask |= SD_BUS_CREDS_UID; @@ -1019,24 +1193,22 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) } if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); - if (!n->supplementary_gids) - return -ENOMEM; - n->n_supplementary_gids = c->n_supplementary_gids; - n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - - if (c->mask & mask & SD_BUS_CREDS_PID) { - n->pid = c->pid; - n->mask |= SD_BUS_CREDS_PID; - } + if (c->supplementary_gids) { + n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); + if (!n->supplementary_gids) + return -ENOMEM; + n->n_supplementary_gids = c->n_supplementary_gids; + } else { + n->supplementary_gids = NULL; + n->n_supplementary_gids = 0; + } - if (c->mask & mask & SD_BUS_CREDS_TID) { - n->tid = c->tid; - n->mask |= SD_BUS_CREDS_TID; + n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; } if (c->mask & mask & SD_BUS_CREDS_COMM) { + assert(c->comm); + n->comm = strdup(c->comm); if (!n->comm) return -ENOMEM; @@ -1045,6 +1217,8 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) } if (c->mask & mask & SD_BUS_CREDS_TID_COMM) { + assert(c->tid_comm); + n->tid_comm = strdup(c->tid_comm); if (!n->tid_comm) return -ENOMEM; @@ -1053,23 +1227,34 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) } if (c->mask & mask & SD_BUS_CREDS_EXE) { - n->exe = strdup(c->exe); - if (!n->exe) - return -ENOMEM; + if (c->exe) { + n->exe = strdup(c->exe); + if (!n->exe) + return -ENOMEM; + } else + n->exe = NULL; n->mask |= SD_BUS_CREDS_EXE; } if (c->mask & mask & SD_BUS_CREDS_CMDLINE) { - n->cmdline = memdup(c->cmdline, c->cmdline_size); - if (!n->cmdline) - return -ENOMEM; + if (c->cmdline) { + n->cmdline = memdup(c->cmdline, c->cmdline_size); + if (!n->cmdline) + return -ENOMEM; + + n->cmdline_size = c->cmdline_size; + } else { + n->cmdline = NULL; + n->cmdline_size = 0; + } - n->cmdline_size = c->cmdline_size; n->mask |= SD_BUS_CREDS_CMDLINE; } - if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID)) { + if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID)) { + assert(c->cgroup); + n->cgroup = strdup(c->cgroup); if (!n->cgroup) return -ENOMEM; @@ -1078,10 +1263,12 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) if (!n->cgroup_root) return -ENOMEM; - n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID); + n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID); } if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) { + assert(c->capability); + n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4); if (!n->capability) return -ENOMEM; @@ -1090,6 +1277,8 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) } if (c->mask & mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + assert(c->label); + n->label = strdup(c->label); if (!n->label) return -ENOMEM; @@ -1105,7 +1294,19 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; } + if (c->mask & mask & SD_BUS_CREDS_TTY) { + if (c->tty) { + n->tty = strdup(c->tty); + if (!n->tty) + return -ENOMEM; + } else + n->tty = NULL; + n->mask |= SD_BUS_CREDS_TTY; + } + if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) { + assert(c->unique_name); + n->unique_name = strdup(c->unique_name); if (!n->unique_name) return -ENOMEM; @@ -1113,24 +1314,31 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) } if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - n->well_known_names = strv_copy(c->well_known_names); - if (!n->well_known_names) - return -ENOMEM; + if (strv_isempty(c->well_known_names)) + n->well_known_names = NULL; + else { + n->well_known_names = strv_copy(c->well_known_names); + if (!n->well_known_names) + return -ENOMEM; + } + n->well_known_names_driver = c->well_known_names_driver; + n->well_known_names_local = c->well_known_names_local; n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; } if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) { + assert(c->description); n->description = strdup(c->description); if (!n->description) return -ENOMEM; n->mask |= SD_BUS_CREDS_DESCRIPTION; } + n->augmented = c->augmented & n->mask; + /* Get more data */ - r = bus_creds_add_more(n, mask, - c->mask & SD_BUS_CREDS_PID ? c->pid : 0, - c->mask & SD_BUS_CREDS_TID ? c->tid : 0); + r = bus_creds_add_more(n, mask, 0, 0); if (r < 0) return r; diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h index 3b337efa32..209d216123 100644 --- a/src/libsystemd/sd-bus/bus-creds.h +++ b/src/libsystemd/sd-bus/bus-creds.h @@ -24,12 +24,13 @@ #include <stdbool.h> #include "sd-bus.h" -#include "time-util.h" struct sd_bus_creds { bool allocated; unsigned n_ref; + uint64_t mask; + uint64_t augmented; uid_t uid; uid_t euid; @@ -43,6 +44,7 @@ struct sd_bus_creds { gid_t *supplementary_gids; unsigned n_supplementary_gids; + pid_t ppid; pid_t pid; pid_t tid; @@ -59,6 +61,9 @@ struct sd_bus_creds { char *unit; char *user_unit; char *slice; + char *user_slice; + + char *tty; uint32_t *capability; diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 7181639645..9db86adb7f 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -22,9 +22,10 @@ #include "util.h" #include "capability.h" #include "strv.h" -#include "audit.h" #include "macro.h" #include "cap-list.h" +#include "formats-util.h" +#include "terminal-util.h" #include "bus-message.h" #include "bus-internal.h" @@ -330,13 +331,11 @@ static void dump_capabilities( } int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { - bool audit_sessionid_is_set = false, audit_loginuid_is_set = false; - const char *u = NULL, *uu = NULL, *s = NULL, *sl = NULL; uid_t owner, audit_loginuid; uint32_t audit_sessionid; char **cmdline = NULL, **well_known = NULL; - const char *prefix, *color, *suffix; - int r; + const char *prefix, *color, *suffix, *s; + int r, q, v, w, z; assert(c); @@ -361,8 +360,16 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); if (c->mask & SD_BUS_CREDS_TID) fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); + if (c->mask & SD_BUS_CREDS_PPID) { + if (c->ppid == 0) + fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix); + else + fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix); + } + if (c->mask & SD_BUS_CREDS_TTY) + fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix); - if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID)))) + if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY)))) fputs("\n", f); if (c->mask & SD_BUS_CREDS_UID) @@ -404,12 +411,13 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { if (c->mask & SD_BUS_CREDS_TID_COMM) fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix); if (c->mask & SD_BUS_CREDS_EXE) - fprintf(f, "%sExe=%s%s%s", prefix, color, c->exe, suffix); + fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix); if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))) fputs("\n", f); - if (sd_bus_creds_get_cmdline(c, &cmdline) >= 0) { + r = sd_bus_creds_get_cmdline(c, &cmdline); + if (r >= 0) { char **i; fprintf(f, "%sCommandLine=%s", prefix, color); @@ -421,7 +429,8 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { } fprintf(f, "%s", suffix); - } + } else if (r != -ENODATA) + fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix); if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix); @@ -433,32 +442,42 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { if (c->mask & SD_BUS_CREDS_CGROUP) fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix); - (void) sd_bus_creds_get_unit(c, &u); - if (u) - fprintf(f, "%sUnit=%s%s%s", prefix, color, u, suffix); - (void) sd_bus_creds_get_user_unit(c, &uu); - if (uu) - fprintf(f, "%sUserUnit=%s%s%s", prefix, color, uu, suffix); - (void) sd_bus_creds_get_slice(c, &sl); - if (sl) - fprintf(f, "%sSlice=%s%s%s", prefix, color, sl, suffix); - (void) sd_bus_creds_get_session(c, &s); - if (s) - fprintf(f, "%sSession=%s%s%s", prefix, color, s, suffix); - - if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s)) + s = NULL; + r = sd_bus_creds_get_unit(c, &s); + if (r != -ENODATA) + fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + v = sd_bus_creds_get_slice(c, &s); + if (v != -ENODATA) + fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + q = sd_bus_creds_get_user_unit(c, &s); + if (q != -ENODATA) + fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + w = sd_bus_creds_get_user_slice(c, &s); + if (w != -ENODATA) + fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + z = sd_bus_creds_get_session(c, &s); + if (z != -ENODATA) + fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix); + + if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA)) fputs("\n", f); - if (sd_bus_creds_get_audit_login_uid(c, &audit_loginuid) >= 0) { - audit_loginuid_is_set = true; + r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid); + if (r >= 0) fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix); - } - if (sd_bus_creds_get_audit_session_id(c, &audit_sessionid) >= 0) { - audit_sessionid_is_set = true; + else if (r != -ENODATA) + fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix); + q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid); + if (q >= 0) fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix); - } + else if (q != -ENODATA) + fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix); - if (terse && (audit_loginuid_is_set || audit_sessionid_is_set)) + if (terse && (r != -ENODATA || q != -ENODATA)) fputs("\n", f); if (c->mask & SD_BUS_CREDS_UNIQUE_NAME) diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index 3bf0c5d3e4..dac157be16 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -40,7 +40,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = { SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", ENOTSUP), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP), SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), @@ -168,7 +168,7 @@ static sd_bus_error errno_to_bus_error_const(int error) { case ECONNRESET: return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); - case ENOTSUP: + case EOPNOTSUPP: return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); case EADDRNOTAVAIL: diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c index 02b95cd136..2d18a4e6c1 100644 --- a/src/libsystemd/sd-bus/bus-gvariant.c +++ b/src/libsystemd/sd-bus/bus-gvariant.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "bus-type.h" #include "bus-gvariant.h" #include "bus-signature.h" diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index e9f1a816aa..1351938c80 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -22,8 +22,6 @@ ***/ #include <sys/socket.h> -#include <sys/un.h> -#include <netinet/in.h> #include <pthread.h> #include "hashmap.h" @@ -211,6 +209,7 @@ struct sd_bus { bool manual_peer_interface:1; bool is_system:1; bool is_user:1; + bool allow_interactive_authorization:1; int use_memfd; diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c index d528ab2a04..e2f4550c7e 100644 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ b/src/libsystemd/sd-bus/bus-introspect.c @@ -20,7 +20,6 @@ ***/ #include "util.h" -#include "sd-bus-protocol.h" #include "bus-introspect.h" #include "bus-signature.h" #include "bus-internal.h" diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h index 98312d123b..1914e6cb8b 100644 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ b/src/libsystemd/sd-bus/bus-introspect.h @@ -21,7 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <stdio.h> #include "sd-bus.h" diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index e90ee449d9..417e4d5903 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -38,8 +38,8 @@ #include "strv.h" #include "memfd-util.h" #include "capability.h" -#include "cgroup-util.h" #include "fileio.h" +#include "formats-util.h" #include "bus-internal.h" #include "bus-message.h" @@ -595,6 +595,14 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; } + if (d->pids.ppid > 0) { + m->creds.ppid = (pid_t) d->pids.ppid; + m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; + } else if (d->pids.pid == 1) { + m->creds.ppid = 0; + m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; + } + break; case KDBUS_ITEM_CREDS: @@ -681,15 +689,11 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { break; case KDBUS_ITEM_AUDIT: - if ((uint32_t) d->audit.sessionid != (uint32_t) -1) { - m->creds.audit_session_id = (uint32_t) d->audit.sessionid; - m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; - } + m->creds.audit_session_id = (uint32_t) d->audit.sessionid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; - if ((uid_t) d->audit.loginuid != UID_INVALID) { - m->creds.audit_login_uid = (uid_t) d->audit.loginuid; - m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; - } + m->creds.audit_login_uid = (uid_t) d->audit.loginuid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; break; case KDBUS_ITEM_CAPS: @@ -749,10 +753,21 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { case KDBUS_ITEM_AUXGROUPS: if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - assert_cc(sizeof(gid_t) == sizeof(uint32_t)); + size_t i, n; + gid_t *g; + + n = (d->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); + g = new(gid_t, n); + if (!g) { + r = -ENOMEM; + goto fail; + } - m->creds.n_supplementary_gids = (d->size - offsetof(struct kdbus_item, data32)) / sizeof(uint32_t); - m->creds.supplementary_gids = (gid_t*) d->data32; + for (i = 0; i < n; i++) + g[i] = d->data64[i]; + + m->creds.supplementary_gids = g; + m->creds.n_supplementary_gids = n; m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; } @@ -841,9 +856,8 @@ fail: int bus_kernel_take_fd(sd_bus *b) { struct kdbus_bloom_parameter *bloom = NULL; + struct kdbus_item *items, *item; struct kdbus_cmd_hello *hello; - struct kdbus_item_list *items; - struct kdbus_item *item; _cleanup_free_ char *g = NULL; const char *name; size_t l = 0, m = 0, sz; @@ -948,8 +962,16 @@ int bus_kernel_take_fd(sd_bus *b) { } r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); - if (r < 0) + if (r < 0) { + if (errno == ENOTTY) + /* If the ioctl is not supported we assume that the + * API version changed in a major incompatible way, + * let's indicate an API incompatibility in this + * case. */ + return -ESOCKTNOSUPPORT; + return -errno; + } if (!b->kdbus_buffer) { b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0); @@ -963,13 +985,13 @@ int bus_kernel_take_fd(sd_bus *b) { /* The higher 32bit of the bus_flags fields are considered * 'incompatible flags'. Refuse them all for now. */ if (hello->bus_flags > 0xFFFFFFFFULL) { - r = -ENOTSUP; + r = -ESOCKTNOSUPPORT; goto fail; } /* extract bloom parameters from items */ items = (void*)((uint8_t*)b->kdbus_buffer + hello->offset); - KDBUS_ITEM_FOREACH(item, items, items) { + KDBUS_FOREACH(item, items, hello->items_size) { switch (item->type) { case KDBUS_ITEM_BLOOM_PARAMETER: bloom = &item->bloom_parameter; @@ -978,7 +1000,7 @@ int bus_kernel_take_fd(sd_bus *b) { } if (!bloom || !bloom_validate_parameters((size_t) bloom->size, (unsigned) bloom->n_hash)) { - r = -ENOTSUP; + r = -EOPNOTSUPP; goto fail; } @@ -1345,15 +1367,12 @@ int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { } r = ioctl(bus->input_fd, KDBUS_CMD_RECV, &recv); + if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS) + log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); if (r < 0) { if (errno == EAGAIN) return 0; - if (errno == EOVERFLOW) { - log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); - return 0; - } - return -errno; } @@ -1389,7 +1408,7 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al assert(allocated); if (!bus || !bus->is_kernel) - return -ENOTSUP; + return -EOPNOTSUPP; assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0); @@ -1499,7 +1518,7 @@ uint64_t attach_flags_to_kdbus(uint64_t mask) { SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) m |= KDBUS_ATTACH_CREDS; - if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID)) + if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) m |= KDBUS_ATTACH_PIDS; if (mask & SD_BUS_CREDS_COMM) @@ -1574,7 +1593,7 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { make->size += ALIGN8(n->size); - /* The busses we create make no restrictions on what metadata + /* The buses we create make no restrictions on what metadata * peers can read from incoming messages. */ n = KDBUS_ITEM_NEXT(n); n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; @@ -1600,6 +1619,11 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { safe_close(fd); + + /* Major API change? then the ioctls got shuffled around. */ + if (errno == ENOTTY) + return -ESOCKTNOSUPPORT; + return -errno; } @@ -1746,32 +1770,6 @@ int bus_kernel_realize_attach_flags(sd_bus *bus) { return 0; } -int bus_kernel_fix_attach_mask(void) { - _cleanup_free_ char *mask = NULL; - uint64_t m = (uint64_t) -1; - char buf[2+16+2]; - int r; - - /* By default we don't want any kdbus metadata fields to be - * suppressed, hence we reset the kernel mask for it to - * (uint64_t) -1. If the module argument was overwritten by - * the kernel cmdline, we leave it as is. */ - - r = get_proc_cmdline_key("kdbus.attach_flags_mask=", &mask); - if (r < 0) - return log_warning_errno(r, "Failed to read kernel command line: %m"); - - if (r == 0) { - sprintf(buf, "0x%" PRIx64 "\n", m); - r = write_string_file("/sys/module/kdbus/parameters/attach_flags_mask", buf); - if (r < 0) - return log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to write kdbus attach mask: %m"); - } - - return 0; -} - int bus_kernel_get_bus_name(sd_bus *bus, char **name) { struct kdbus_cmd_info cmd = { .size = sizeof(struct kdbus_cmd_info), diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h index b9f31ba790..bb4dff6d82 100644 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ b/src/libsystemd/sd-bus/bus-kernel.h @@ -90,8 +90,6 @@ int bus_kernel_drop_one(int fd); int bus_kernel_realize_attach_flags(sd_bus *bus); -int bus_kernel_fix_attach_mask(void); - int bus_kernel_get_bus_name(sd_bus *bus, char **name); int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset); diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 162f0ab608..7c5264fad4 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -22,7 +22,6 @@ #include "bus-internal.h" #include "bus-message.h" #include "bus-match.h" -#include "bus-error.h" #include "bus-util.h" #include "strv.h" @@ -329,7 +328,7 @@ int bus_match_run( bus->current_handler = node->leaf.callback->callback; bus->current_userdata = slot->userdata; } - r = node->leaf.callback->callback(bus, m, slot->userdata, &error_buffer); + r = node->leaf.callback->callback(m, slot->userdata, &error_buffer); if (bus) { bus->current_userdata = NULL; bus->current_handler = NULL; diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 2959303033..6ee209dd1b 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -27,7 +27,6 @@ #include "utf8.h" #include "strv.h" #include "time-util.h" -#include "cgroup-util.h" #include "memfd-util.h" #include "sd-bus.h" @@ -421,7 +420,7 @@ static int message_append_reply_cookie(sd_bus_message *m, uint64_t cookie) { else { /* 64bit cookies are not supported on dbus1 */ if (cookie > 0xffffffffUL) - return -ENOTSUP; + return -EOPNOTSUPP; return message_append_field_uint32(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, (uint32_t) cookie); } @@ -441,7 +440,7 @@ int bus_message_from_header( size_t extra, sd_bus_message **ret) { - sd_bus_message *m; + _cleanup_free_ sd_bus_message *m = NULL; struct bus_header *h; size_t a, label_sz; @@ -460,15 +459,13 @@ int bus_message_from_header( return -EBADMSG; h = header; - if (h->version != 1 && - h->version != 2) + if (!IN_SET(h->version, 1, 2)) return -EBADMSG; if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID) return -EBADMSG; - if (h->endian != BUS_LITTLE_ENDIAN && - h->endian != BUS_BIG_ENDIAN) + if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN)) return -EBADMSG; /* Note that we are happy with unknown flags in the flags header! */ @@ -557,6 +554,7 @@ int bus_message_from_header( m->bus = sd_bus_ref(bus); *ret = m; + m = NULL; return 0; } @@ -634,6 +632,9 @@ static sd_bus_message *message_new(sd_bus *bus, uint8_t type) { m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m); m->bus = sd_bus_ref(bus); + if (bus->allow_interactive_authorization) + m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; + return m; } @@ -749,7 +750,7 @@ static int message_new_reply( t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; t->reply_cookie = BUS_MESSAGE_COOKIE(call); if (t->reply_cookie == 0) - return -ENOTSUP; + return -EOPNOTSUPP; r = message_append_reply_cookie(t, t->reply_cookie); if (r < 0) @@ -1462,7 +1463,7 @@ static int message_push_fd(sd_bus_message *m, int fd) { return -EINVAL; if (!m->allow_fds) - return -ENOTSUP; + return -EOPNOTSUPP; copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); if (copy < 0) @@ -2941,7 +2942,7 @@ int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { if (cookie > 0xffffffffULL && !BUS_MESSAGE_IS_GVARIANT(m)) - return -ENOTSUP; + return -EOPNOTSUPP; /* In vtables the return signature of method calls is listed, * let's check if they match if this is a response */ @@ -3488,8 +3489,6 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { return r; } else { - rindex = m->rindex; - if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH)) { uint32_t l; bool ok; @@ -4797,7 +4796,7 @@ _public_ int sd_bus_message_read_array( assert_return(bus_type_is_trivial(type), -EINVAL); assert_return(ptr, -EINVAL); assert_return(size, -EINVAL); - assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -ENOTSUP); + assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -EOPNOTSUPP); r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); if (r <= 0) @@ -5508,7 +5507,7 @@ int bus_message_parse_fields(sd_bus_message *m) { /* Try to read the error message, but if we can't it's a non-issue */ if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR) - sd_bus_message_read(m, "s", &m->error.message); + (void) sd_bus_message_read(m, "s", &m->error.message); return 0; } @@ -5551,6 +5550,7 @@ int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) { } int bus_message_read_strv_extend(sd_bus_message *m, char ***l) { + const char *s; int r; assert(m); @@ -5560,19 +5560,13 @@ int bus_message_read_strv_extend(sd_bus_message *m, char ***l) { if (r <= 0) return r; - for (;;) { - const char *s; - - r = sd_bus_message_read_basic(m, 's', &s); - if (r < 0) - return r; - if (r == 0) - break; - + while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) { r = strv_extend(l, s); if (r < 0) return r; } + if (r < 0) + return r; r = sd_bus_message_exit_container(m); if (r < 0) diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index 32955329b4..d784e603dd 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -27,7 +27,6 @@ #include "macro.h" #include "sd-bus.h" -#include "kdbus.h" #include "time-util.h" #include "bus-creds.h" #include "bus-protocol.h" diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index fc6c223283..e4bbd880e5 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -277,7 +277,7 @@ static int node_callbacks_run( bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; bus->current_userdata = slot->userdata; - r = c->callback(bus, m, slot->userdata, &error_buffer); + r = c->callback(m, slot->userdata, &error_buffer); bus->current_userdata = NULL; bus->current_handler = NULL; bus->current_slot = sd_bus_slot_unref(slot); @@ -395,7 +395,7 @@ static int method_callbacks_run( bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->vtable->x.method.handler; bus->current_userdata = u; - r = c->vtable->x.method.handler(bus, m, u, &error); + r = c->vtable->x.method.handler(m, u, &error); bus->current_userdata = NULL; bus->current_handler = NULL; bus->current_slot = sd_bus_slot_unref(slot); diff --git a/src/libsystemd/sd-bus/bus-signature.h b/src/libsystemd/sd-bus/bus-signature.h index 2e06e30548..c4fed0b53d 100644 --- a/src/libsystemd/sd-bus/bus-signature.h +++ b/src/libsystemd/sd-bus/bus-signature.h @@ -22,7 +22,6 @@ ***/ #include <stdbool.h> -#include <sys/types.h> bool signature_is_single(const char *s, bool allow_dict_entry); bool signature_is_pair(const char *s); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 52883fa8cd..4fffc6581d 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -20,18 +20,17 @@ ***/ #include <endian.h> -#include <assert.h> #include <stdlib.h> #include <unistd.h> #include <poll.h> -#include <byteswap.h> +#include "sd-daemon.h" #include "util.h" #include "macro.h" #include "missing.h" -#include "strv.h" #include "utf8.h" -#include "sd-daemon.h" +#include "formats-util.h" +#include "signal-util.h" #include "sd-bus.h" #include "bus-socket.h" @@ -179,7 +178,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) { /* We expect two response lines: "OK" and possibly * "AGREE_UNIX_FD" */ - e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2); + e = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2); if (!e) return 0; @@ -494,7 +493,7 @@ static int bus_socket_auth_verify(sd_bus *b) { static int bus_socket_read_auth(sd_bus *b) { struct msghdr mh; - struct iovec iov; + struct iovec iov = {}; size_t n; ssize_t k; int r; @@ -529,7 +528,6 @@ static int bus_socket_read_auth(sd_bus *b) { b->rbuffer = p; - zero(iov); iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size; iov.iov_len = n - b->rbuffer_size; @@ -609,10 +607,10 @@ void bus_socket_setup(sd_bus *b) { /* Enable SO_PASSCRED + SO_PASSEC. We try this on any * socket, just in case. */ enable = !b->bus_client; - (void)setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)); + (void) setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)); enable = !b->bus_client && (b->attach_flags & KDBUS_ATTACH_SECLABEL); - (void)setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable)); + (void) setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable)); /* Increase the buffers to 8 MB */ fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); @@ -810,23 +808,21 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { if (bus->prefer_writev) k = writev(bus->output_fd, iov, m->n_iovec); else { - struct msghdr mh; - zero(mh); + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = m->n_iovec, + }; if (m->n_fds > 0) { struct cmsghdr *control; - control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds)); - mh.msg_control = control; + mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds)); + mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds); control->cmsg_level = SOL_SOCKET; control->cmsg_type = SCM_RIGHTS; - mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds); memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds); } - mh.msg_iov = iov; - mh.msg_iovlen = m->n_iovec; - k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); if (k < 0 && errno == ENOTSOCK) { bus->prefer_writev = true; @@ -917,8 +913,8 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) { r = bus_message_from_malloc(bus, bus->rbuffer, size, bus->fds, bus->n_fds, - !bus->bus_client && bus->ucred_valid ? &bus->ucred : NULL, - !bus->bus_client && bus->label[0] ? bus->label : NULL, + NULL, + NULL, &t); if (r < 0) { free(b); @@ -938,7 +934,7 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) { int bus_socket_read_message(sd_bus *bus) { struct msghdr mh; - struct iovec iov; + struct iovec iov = {}; ssize_t k; size_t need; int r; @@ -968,7 +964,6 @@ int bus_socket_read_message(sd_bus *bus) { bus->rbuffer = b; - zero(iov); iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size; iov.iov_len = need - bus->rbuffer_size; diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index 6be8310bbe..ec9340f8e1 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -20,7 +20,6 @@ ***/ #include "sd-bus.h" -#include "set.h" #include "bus-util.h" #include "bus-internal.h" #include "bus-track.h" @@ -91,6 +90,9 @@ _public_ int sd_bus_track_new( assert_return(bus, -EINVAL); assert_return(track, -EINVAL); + if (!bus->bus_client) + return -EINVAL; + t = new0(sd_bus_track, 1); if (!t) return -ENOMEM; @@ -140,12 +142,11 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { return NULL; } -static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { +static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { sd_bus_track *track = userdata; const char *name, *old, *new; int r; - assert(bus); assert(message); assert(track); diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c index b7914d15d3..6bc7b880a6 100644 --- a/src/libsystemd/sd-bus/bus-type.c +++ b/src/libsystemd/sd-bus/bus-type.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "bus-type.h" bool bus_type_is_valid(char c) { diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h index 0e507839ca..581574ab73 100644 --- a/src/libsystemd/sd-bus/bus-type.h +++ b/src/libsystemd/sd-bus/bus-type.h @@ -25,7 +25,6 @@ #include "macro.h" #include "sd-bus.h" -#include "sd-bus-protocol.h" bool bus_type_is_valid(char c) _const_; bool bus_type_is_valid_in_signature(char c) _const_; diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c index 52d4ebe611..99937799b3 100644 --- a/src/libsystemd/sd-bus/bus-util.c +++ b/src/libsystemd/sd-bus/bus-util.c @@ -30,21 +30,23 @@ #include "path-util.h" #include "missing.h" #include "set.h" +#include "signal-util.h" +#include "unit-name.h" #include "sd-bus.h" #include "bus-error.h" +#include "bus-label.h" #include "bus-message.h" #include "bus-util.h" #include "bus-internal.h" -static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { +static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; - assert(bus); assert(m); assert(e); - sd_bus_close(bus); + sd_bus_close(sd_bus_message_get_bus(m)); sd_event_exit(e, 0); return 1; @@ -132,7 +134,7 @@ int bus_event_loop_with_idle( /* Fallback for dbus1 connections: we * unregister the name and wait for the * response to come through for it */ - if (r == -ENOTSUP) { + if (r == -EOPNOTSUPP) { /* Inform the service manager that we * are going down, so that it will @@ -190,11 +192,35 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) { return has_owner; } -int bus_verify_polkit( +static int check_good_user(sd_bus_message *m, uid_t good_user) { + _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + uid_t sender_uid; + int r; + + assert(m); + + if (good_user == UID_INVALID) + return 0; + + r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + /* Don't trust augmented credentials for authorization */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); + + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0) + return r; + + return sender_uid == good_user; +} + +int bus_test_polkit( sd_bus_message *call, int capability, const char *action, - bool interactive, + uid_t good_user, bool *_challenge, sd_bus_error *e) { @@ -203,6 +229,12 @@ int bus_verify_polkit( assert(call); assert(action); + /* Tests non-interactively! */ + + r = check_good_user(call, good_user); + if (r != 0) + return r; + r = sd_bus_query_sender_privilege(call, capability); if (r < 0) return r; @@ -211,19 +243,13 @@ int bus_verify_polkit( #ifdef ENABLE_POLKIT else { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - int authorized = false, challenge = false, c; + int authorized = false, challenge = false; const char *sender; sender = sd_bus_message_get_sender(call); if (!sender) return -EBADMSG; - c = sd_bus_message_get_allow_interactive_authorization(call); - if (c < 0) - return c; - if (c > 0) - interactive = true; - r = sd_bus_call_method( call->bus, "org.freedesktop.PolicyKit1", @@ -236,7 +262,7 @@ int bus_verify_polkit( "system-bus-name", 1, "name", "s", sender, action, 0, - !!interactive, + 0, ""); if (r < 0) { @@ -296,12 +322,11 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) { free(q); } -static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) { +static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL; AsyncPolkitQuery *q = userdata; int r; - assert(bus); assert(reply); assert(q); @@ -314,7 +339,7 @@ static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userd goto finish; } - r = q->callback(bus, q->request, q->userdata, &error_buffer); + r = q->callback(q->request, q->userdata, &error_buffer); r = bus_maybe_reply_error(q->request, r, &error_buffer); finish: @@ -330,6 +355,7 @@ int bus_verify_polkit_async( int capability, const char *action, bool interactive, + uid_t good_user, Hashmap **registry, sd_bus_error *error) { @@ -347,6 +373,10 @@ int bus_verify_polkit_async( assert(action); assert(registry); + r = check_good_user(call, good_user); + if (r != 0) + return r; + #ifdef ENABLE_POLKIT q = hashmap_get(*registry, call); if (q) { @@ -691,6 +721,18 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { return 1; } + case SD_BUS_TYPE_INT64: { + int64_t i; + + r = sd_bus_message_read_basic(property, type, &i); + if (r < 0) + return r; + + printf("%s=%lld\n", name, (long long) i); + + return 1; + } + case SD_BUS_TYPE_UINT32: { uint32_t u; @@ -920,7 +962,6 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ switch (type) { case SD_BUS_TYPE_STRING: { const char *s; - char *str; char **p = userdata; r = sd_bus_message_read_basic(m, type, &s); @@ -930,14 +971,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ if (isempty(s)) break; - str = strdup(s); - if (!str) { - r = -ENOMEM; - break; - } - free(*p); - *p = str; - + r = free_and_strdup(p, s); break; } @@ -1002,14 +1036,14 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ return r; } -int bus_message_map_all_properties(sd_bus *bus, - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata) { +int bus_message_map_all_properties( + sd_bus_message *m, + const struct bus_properties_map *map, + void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; - assert(bus); assert(m); assert(map); @@ -1045,9 +1079,9 @@ int bus_message_map_all_properties(sd_bus *bus, v = (uint8_t *)userdata + prop->offset; if (map[i].set) - r = prop->set(bus, member, m, &error, v); + r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v); else - r = map_basic(bus, member, m, &error, v); + r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v); if (r < 0) return r; @@ -1064,22 +1098,24 @@ int bus_message_map_all_properties(sd_bus *bus, if (r < 0) return r; } + if (r < 0) + return r; return sd_bus_message_exit_container(m); } -int bus_message_map_properties_changed(sd_bus *bus, - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata) { +int bus_message_map_properties_changed( + sd_bus_message *m, + const struct bus_properties_map *map, + void *userdata) { + const char *member; int r, invalidated, i; - assert(bus); assert(m); assert(map); - r = bus_message_map_all_properties(bus, m, map, userdata); + r = bus_message_map_all_properties(m, map, userdata); if (r < 0) return r; @@ -1094,6 +1130,8 @@ int bus_message_map_properties_changed(sd_bus *bus, ++invalidated; break; } + if (r < 0) + return r; r = sd_bus_message_exit_container(m); if (r < 0) @@ -1102,11 +1140,13 @@ int bus_message_map_properties_changed(sd_bus *bus, return invalidated; } -int bus_map_all_properties(sd_bus *bus, - const char *destination, - const char *path, - const struct bus_properties_map *map, - void *userdata) { +int bus_map_all_properties( + sd_bus *bus, + const char *destination, + const char *path, + const struct bus_properties_map *map, + void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -1128,7 +1168,7 @@ int bus_map_all_properties(sd_bus *bus, if (r < 0) return r; - return bus_message_map_all_properties(bus, m, map, userdata); + return bus_message_map_all_properties(m, map, userdata); } int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) { @@ -1139,7 +1179,7 @@ int bus_open_transport(BusTransport transport, const char *host, bool user, sd_b assert(bus); assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); - assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP); + assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); switch (transport) { @@ -1174,7 +1214,7 @@ int bus_open_transport_systemd(BusTransport transport, const char *host, bool us assert(bus); assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); - assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP); + assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); switch (transport) { @@ -1565,24 +1605,22 @@ typedef struct BusWaitForJobs { sd_bus_slot *slot_disconnected; } BusWaitForJobs; -static int match_disconnected(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { - assert(bus); +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { assert(m); log_error("Warning! D-Bus connection terminated."); - sd_bus_close(bus); + sd_bus_close(sd_bus_message_get_bus(m)); return 0; } -static int match_job_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { const char *path, *unit, *result; BusWaitForJobs *d = userdata; uint32_t id; char *found; int r; - assert(bus); assert(m); assert(d); @@ -1690,6 +1728,73 @@ static int bus_process_wait(sd_bus *bus) { } } +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { + _cleanup_free_ char *dbus_path = NULL; + + assert(d); + assert(d->name); + assert(result); + + dbus_path = unit_dbus_path_from_name(d->name); + if (!dbus_path) + return -ENOMEM; + + return sd_bus_get_property_string(d->bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "Result", + NULL, + result); +} + +static const struct { + const char *result, *explanation; +} explanations [] = { + { "resources", "a configured resource limit was exceeded" }, + { "timeout", "a timeout was exceeded" }, + { "exit-code", "the control process exited with error code" }, + { "signal", "a fatal signal was delivered to the control process" }, + { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result) { + _cleanup_free_ char *service_shell_quoted = NULL; + + assert(service); + + service_shell_quoted = shell_maybe_quote(service); + + if (!isempty(result)) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(explanations); ++i) + if (streq(result, explanations[i].result)) + break; + + if (i < ELEMENTSOF(explanations)) { + log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + service, + explanations[i].explanation, + strna(service_shell_quoted)); + + goto finish; + } + } + + log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + service, + strna(service_shell_quoted)); + +finish: + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.", + strna(service_shell_quoted)); +} + static int check_wait_response(BusWaitForJobs *d, bool quiet) { int r = 0; @@ -1710,13 +1815,14 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) { log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { if (d->name) { - bool quotes; + int q; + _cleanup_free_ char *result = NULL; - quotes = chars_intersect(d->name, SHELL_NEED_QUOTES); + q = bus_job_get_service_result(d, &result); + if (q < 0) + log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.", - d->name, - quotes ? "'" : "", d->name, quotes ? "'" : ""); + log_job_error_with_service_result(d->name, result); } else log_error("Job failed. See \"journalctl -xe\" for details."); } @@ -1733,7 +1839,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) { else if (streq(d->result, "assert")) r = -EPROTO; else if (streq(d->result, "unsupported")) - r = -ENOTSUP; + r = -EOPNOTSUPP; else if (!streq(d->result, "done") && !streq(d->result, "skipped")) r = -EIO; @@ -1759,7 +1865,6 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) { if (q < 0 && r == 0) r = q; - errno = 0; log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); } @@ -1785,7 +1890,17 @@ int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { return set_put_strdup(d->jobs, path); } -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) { +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { + int r; + + r = bus_wait_for_jobs_add(d, path); + if (r < 0) + return log_oom(); + + return bus_wait_for_jobs(d, quiet); +} + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { const char *type, *path, *source; int r; @@ -1800,6 +1915,10 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) { else log_info("Removed symlink %s.", path); } + + r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source); + if (r < 0) + return r; } if (r < 0) return bus_log_parse_error(r); @@ -1810,3 +1929,157 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) { return 0; } + +/** + * bus_path_encode_unique() - encode unique object path + * @b: bus connection or NULL + * @prefix: object path prefix + * @sender_id: unique-name of client, or NULL + * @external_id: external ID to be chosen by client, or NULL + * @ret_path: storage for encoded object path pointer + * + * Whenever we provide a bus API that allows clients to create and manage + * server-side objects, we need to provide a unique name for these objects. If + * we let the server choose the name, we suffer from a race condition: If a + * client creates an object asynchronously, it cannot destroy that object until + * it received the method reply. It cannot know the name of the new object, + * thus, it cannot destroy it. Furthermore, it enforces a round-trip. + * + * Therefore, many APIs allow the client to choose the unique name for newly + * created objects. There're two problems to solve, though: + * 1) Object names are usually defined via dbus object paths, which are + * usually globally namespaced. Therefore, multiple clients must be able + * to choose unique object names without interference. + * 2) If multiple libraries share the same bus connection, they must be + * able to choose unique object names without interference. + * The first problem is solved easily by prefixing a name with the + * unique-bus-name of a connection. The server side must enforce this and + * reject any other name. The second problem is solved by providing unique + * suffixes from within sd-bus. + * + * This helper allows clients to create unique object-paths. It uses the + * template '/prefix/sender_id/external_id' and returns the new path in + * @ret_path (must be freed by the caller). + * If @sender_id is NULL, the unique-name of @b is used. If @external_id is + * NULL, this function allocates a unique suffix via @b (by requesting a new + * cookie). If both @sender_id and @external_id are given, @b can be passed as + * NULL. + * + * Returns: 0 on success, negative error code on failure. + */ +int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) { + _cleanup_free_ char *sender_label = NULL, *external_label = NULL; + char external_buf[DECIMAL_STR_MAX(uint64_t)], *p; + int r; + + assert_return(b || (sender_id && external_id), -EINVAL); + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(ret_path, -EINVAL); + + if (!sender_id) { + r = sd_bus_get_unique_name(b, &sender_id); + if (r < 0) + return r; + } + + if (!external_id) { + xsprintf(external_buf, "%"PRIu64, ++b->cookie); + external_id = external_buf; + } + + sender_label = bus_label_escape(sender_id); + if (!sender_label) + return -ENOMEM; + + external_label = bus_label_escape(external_id); + if (!external_label) + return -ENOMEM; + + p = strjoin(prefix, "/", sender_label, "/", external_label, NULL); + if (!p) + return -ENOMEM; + + *ret_path = p; + return 0; +} + +/** + * bus_path_decode_unique() - decode unique object path + * @path: object path to decode + * @prefix: object path prefix + * @ret_sender: output parameter for sender-id label + * @ret_external: output parameter for external-id label + * + * This does the reverse of bus_path_encode_unique() (see its description for + * details). Both trailing labels, sender-id and external-id, are unescaped and + * returned in the given output parameters (the caller must free them). + * + * Note that this function returns 0 if the path does not match the template + * (see bus_path_encode_unique()), 1 if it matched. + * + * Returns: Negative error code on failure, 0 if the given object path does not + * match the template (return parameters are set to NULL), 1 if it was + * parsed successfully (return parameters contain allocated labels). + */ +int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) { + const char *p, *q; + char *sender, *external; + + assert(object_path_is_valid(path)); + assert(object_path_is_valid(prefix)); + assert(ret_sender); + assert(ret_external); + + p = object_path_startswith(path, prefix); + if (!p) { + *ret_sender = NULL; + *ret_external = NULL; + return 0; + } + + q = strchr(p, '/'); + if (!q) { + *ret_sender = NULL; + *ret_external = NULL; + return 0; + } + + sender = bus_label_unescape_n(p, q - p); + external = bus_label_unescape(q + 1); + if (!sender || !external) { + free(sender); + free(external); + return -ENOMEM; + } + + *ret_sender = sender; + *ret_external = external; + return 1; +} + +bool is_kdbus_wanted(void) { + _cleanup_free_ char *value = NULL; + int r; + + if (get_proc_cmdline_key("kdbus", NULL) <= 0) { + r = get_proc_cmdline_key("kdbus=", &value); + if (r <= 0 || parse_boolean(value) != 1) + return false; + } + + return true; +} + +bool is_kdbus_available(void) { + _cleanup_close_ int fd = -1; + struct kdbus_cmd cmd = { .size = sizeof(cmd), .flags = KDBUS_FLAG_NEGOTIATE }; + + if (!is_kdbus_wanted()) + return false; + + fd = open("/sys/fs/kdbus/control", O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); + if (fd < 0) + return false; + + return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0; +} diff --git a/src/libsystemd/sd-bus/bus-util.h b/src/libsystemd/sd-bus/bus-util.h index e8a97cef9e..999a372cdd 100644 --- a/src/libsystemd/sd-bus/bus-util.h +++ b/src/libsystemd/sd-bus/bus-util.h @@ -24,8 +24,8 @@ #include "sd-event.h" #include "sd-bus.h" #include "hashmap.h" +#include "install.h" #include "time-util.h" -#include "util.h" typedef enum BusTransport { BUS_TRANSPORT_LOCAL, @@ -46,19 +46,9 @@ struct bus_properties_map { int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); -int bus_message_map_all_properties(sd_bus *bus, - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata); -int bus_message_map_properties_changed(sd_bus *bus, - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata); -int bus_map_all_properties(sd_bus *bus, - const char *destination, - const char *path, - const struct bus_properties_map *map, - void *userdata); +int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); +int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); +int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata); int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name); @@ -70,9 +60,9 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error); int bus_check_peercred(sd_bus *c); -int bus_verify_polkit(sd_bus_message *call, int capability, const char *action, bool interactive, bool *_challenge, sd_bus_error *e); +int bus_test_polkit(sd_bus_message *call, int capability, const char *action, uid_t good_user, bool *_challenge, sd_bus_error *e); -int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, Hashmap **registry, sd_bus_error *error); +int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error); void bus_verify_polkit_async_registry_free(Hashmap *registry); int bus_open_system_systemd(sd_bus **_bus); @@ -208,7 +198,14 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); void bus_wait_for_jobs_free(BusWaitForJobs *d); int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet); +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet); +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); + +int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); +int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); + +bool is_kdbus_wanted(void); +bool is_kdbus_available(void); diff --git a/src/libsystemd/sd-bus/busctl-introspect.h b/src/libsystemd/sd-bus/busctl-introspect.h index d6b4cf05a7..ea807d5973 100644 --- a/src/libsystemd/sd-bus/busctl-introspect.h +++ b/src/libsystemd/sd-bus/busctl-introspect.h @@ -21,7 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <inttypes.h> #include <stdbool.h> typedef struct XMLIntrospectOps { diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index d3c1772019..39caa4e7d6 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -26,18 +26,17 @@ #include "log.h" #include "build.h" #include "pager.h" -#include "xml.h" #include "path-util.h" #include "set.h" #include "sd-bus.h" -#include "bus-message.h" #include "bus-internal.h" #include "bus-util.h" #include "bus-dump.h" #include "bus-signature.h" #include "bus-type.h" #include "busctl-introspect.h" +#include "terminal-util.h" static bool arg_no_pager = false; static bool arg_legend = true; diff --git a/src/libsystemd/sd-bus/kdbus.h b/src/libsystemd/sd-bus/kdbus.h index 1cc475eb1d..fc1d77dd7c 100644 --- a/src/libsystemd/sd-bus/kdbus.h +++ b/src/libsystemd/sd-bus/kdbus.h @@ -70,14 +70,14 @@ struct kdbus_notify_name_change { * KDBUS_ITEM_CREDS */ struct kdbus_creds { - __u32 uid; - __u32 euid; - __u32 suid; - __u32 fsuid; - __u32 gid; - __u32 egid; - __u32 sgid; - __u32 fsgid; + __u64 uid; + __u64 euid; + __u64 suid; + __u64 fsuid; + __u64 gid; + __u64 egid; + __u64 sgid; + __u64 fsgid; } __attribute__((__aligned__(8))); /** @@ -457,24 +457,16 @@ struct kdbus_item { } __attribute__((__aligned__(8))); /** - * struct kdbus_item_list - A list of items - * @size: The total size of the structure - * @items: Array of items - */ -struct kdbus_item_list { - __u64 size; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** * enum kdbus_msg_flags - type of message * @KDBUS_MSG_EXPECT_REPLY: Expect a reply message, used for * method calls. The userspace-supplied * cookie identifies the message and the * respective reply carries the cookie * in cookie_reply - * @KDBUS_MSG_NO_AUTO_START: Do not start a service, if the addressed - * name is not currently active + * @KDBUS_MSG_NO_AUTO_START: Do not start a service if the addressed + * name is not currently active. This flag is + * not looked at by the kernel but only + * serves as hint for userspace implementations. * @KDBUS_MSG_SIGNAL: Treat this message as signal */ enum kdbus_msg_flags { @@ -507,9 +499,12 @@ enum kdbus_payload_type { * @cookie: Userspace-supplied cookie, for the connection * to identify its messages * @timeout_ns: The time to wait for a message reply from the peer. - * If there is no reply, a kernel-generated message + * If there is no reply, and the send command is + * executed asynchronously, a kernel-generated message * with an attached KDBUS_ITEM_REPLY_TIMEOUT item - * is sent to @src_id. The timeout is expected in + * is sent to @src_id. For synchronously executed send + * command, the value denotes the maximum time the call + * blocks to wait for a reply. The timeout is expected in * nanoseconds and as absolute CLOCK_MONOTONIC value. * @cookie_reply: A reply to the requesting message with the same * cookie. The requesting connection can match its @@ -602,9 +597,15 @@ enum kdbus_recv_flags { * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not * be installed. These descriptors in * KDBUS_ITEM_FDS will carry the value -1. + * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since + * the last time a message was received. + * The 'dropped_msgs' counter contains the + * number of messages dropped pool + * overflows or other missed broadcasts. */ enum kdbus_recv_return_flags { KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0, + KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1, }; /** @@ -614,10 +615,12 @@ enum kdbus_recv_return_flags { * @return_flags: Command return flags, kernel → userspace * @priority: Minimum priority of the messages to de-queue. Lowest * values have the highest priority. - * @dropped_msgs: In case the KDBUS_CMD_RECV ioctl returns - * -EOVERFLOW, this field will contain the number of - * broadcast messages that have been lost since the - * last call. + * @dropped_msgs: In case there were any dropped messages since the last + * time a message was received, this will be set to the + * number of lost messages and + * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in + * 'return_flags'. This can only happen if the ioctl + * returns 0 or EAGAIN. * @msg: Return storage for received message. * @items: Additional items for this command. * @@ -691,10 +694,10 @@ enum kdbus_hello_flags { * @id: The ID of this connection (kernel → userspace) * @pool_size: Size of the connection's buffer where the received * messages are placed - * @offset: Pool offset where additional items of type - * kdbus_item_list are stored. They contain information - * about the bus and the newly created connection. - * @items_size: Copy of item_list.size stored in @offset. + * @offset: Pool offset where items are returned to report + * additional information about the bus and the newly + * created connection. + * @items_size: Size of buffer returned in the pool slice at @offset. * @id128: Unique 128-bit ID of the bus (kernel → userspace) * @items: A list of items * @@ -772,11 +775,13 @@ struct kdbus_cmd_list { /** * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl * @size: The total size of the struct - * @flags: KDBUS_ATTACH_* flags, userspace → kernel + * @flags: Flags for this ioctl, userspace → kernel * @return_flags: Command return flags, kernel → userspace * @id: The 64-bit ID of the connection. If set to zero, passing * @name is required. kdbus will look up the name to * determine the ID in this case. + * @attach_flags: Set of attach flags to specify the set of information + * to receive, userspace → kernel * @offset: Returned offset in the caller's pool buffer where the * kdbus_info struct result is stored. The user must * use KDBUS_CMD_FREE to free the allocated memory. @@ -794,6 +799,7 @@ struct kdbus_cmd_info { __u64 flags; __u64 return_flags; __u64 id; + __u64 attach_flags; __u64 offset; __u64 info_size; struct kdbus_item items[0]; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index cac9b65601..edc27aef87 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -20,19 +20,16 @@ ***/ #include <endian.h> -#include <assert.h> #include <stdlib.h> #include <unistd.h> #include <netdb.h> #include <poll.h> -#include <byteswap.h> #include <sys/mman.h> #include <pthread.h> #include "util.h" #include "macro.h" #include "strv.h" -#include "set.h" #include "missing.h" #include "def.h" #include "cgroup-util.h" @@ -45,8 +42,6 @@ #include "bus-socket.h" #include "bus-kernel.h" #include "bus-control.h" -#include "bus-introspect.h" -#include "bus-signature.h" #include "bus-objects.h" #include "bus-util.h" #include "bus-container.h" @@ -357,13 +352,30 @@ _public_ int sd_bus_set_description(sd_bus *bus, const char *description) { return free_and_strdup(&bus->description, description); } -static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) { +_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->allow_interactive_authorization = !!b; + return 0; +} + +_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->allow_interactive_authorization; +} + +static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { const char *s; + sd_bus *bus; int r; + assert(reply); + bus = reply->bus; assert(bus); assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING); - assert(reply); r = sd_bus_message_get_errno(reply); if (r > 0) @@ -1513,15 +1525,27 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { } static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) { + bool remarshal = false; + assert(b); - /* Do packet version and endianness already match? */ - if ((b->message_version == 0 || b->message_version == (*m)->header->version) && - (b->message_endian == 0 || b->message_endian == (*m)->header->endian)) - return 0; + /* wrong packet version */ + if (b->message_version != 0 && b->message_version != (*m)->header->version) + remarshal = true; + + /* wrong packet endianness */ + if (b->message_endian != 0 && b->message_endian != (*m)->header->endian) + remarshal = true; - /* No? Then remarshal! */ - return bus_message_remarshal(b, m); + /* TODO: kdbus-messages received from the kernel contain data which is + * not allowed to be passed to KDBUS_CMD_SEND. Therefore, we have to + * force remarshaling of the message. Technically, we could just + * recreate the kdbus message, but that is non-trivial as other parts of + * the message refer to m->kdbus already. This should be fixed! */ + if ((*m)->kdbus && (*m)->release_kdbus) + remarshal = true; + + return remarshal ? bus_message_remarshal(b, m) : 0; } int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { @@ -1671,8 +1695,11 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m); int r; - assert_return(bus, -EINVAL); assert_return(m, -EINVAL); + + if (!bus) + bus = m->bus; + assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); @@ -1684,7 +1711,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, if (r < 0) return r; if (r == 0) - return -ENOTSUP; + return -EOPNOTSUPP; } /* If the cookie number isn't kept, then we know that no reply @@ -1757,8 +1784,11 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { int r; - assert_return(bus, -EINVAL); assert_return(m, -EINVAL); + + if (!bus) + bus = m->bus; + assert_return(!bus_pid_changed(bus), -ECHILD); if (!BUS_IS_OPEN(bus->state)) @@ -1814,11 +1844,14 @@ _public_ int sd_bus_call_async( _cleanup_bus_slot_unref_ sd_bus_slot *s = NULL; int r; - assert_return(bus, -EINVAL); assert_return(m, -EINVAL); assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); assert_return(callback, -EINVAL); + + if (!bus) + bus = m->bus; + assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); @@ -1912,11 +1945,14 @@ _public_ int sd_bus_call( unsigned i; int r; - assert_return(bus, -EINVAL); assert_return(m, -EINVAL); assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); assert_return(!bus_error_is_dirty(error), -EINVAL); + + if (!bus) + bus = m->bus; + assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); @@ -2171,7 +2207,7 @@ static int process_timeout(sd_bus *bus) { bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; bus->current_userdata = slot->userdata; - r = c->callback(bus, m, slot->userdata, &error_buffer); + r = c->callback(m, slot->userdata, &error_buffer); bus->current_userdata = NULL; bus->current_handler = NULL; bus->current_slot = NULL; @@ -2274,7 +2310,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; bus->current_userdata = slot->userdata; - r = c->callback(bus, m, slot->userdata, &error_buffer); + r = c->callback(m, slot->userdata, &error_buffer); bus->current_userdata = NULL; bus->current_handler = NULL; bus->current_slot = NULL; @@ -2321,7 +2357,7 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) { bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = l->callback; bus->current_userdata = slot->userdata; - r = l->callback(bus, m, slot->userdata, &error_buffer); + r = l->callback(m, slot->userdata, &error_buffer); bus->current_userdata = NULL; bus->current_handler = NULL; bus->current_slot = sd_bus_slot_unref(slot); @@ -2602,7 +2638,7 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) { bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; bus->current_userdata = slot->userdata; - r = c->callback(bus, m, slot->userdata, &error_buffer); + r = c->callback(m, slot->userdata, &error_buffer); bus->current_userdata = NULL; bus->current_handler = NULL; bus->current_slot = NULL; @@ -3376,7 +3412,7 @@ _public_ int sd_bus_try_close(sd_bus *bus) { assert_return(!bus_pid_changed(bus), -ECHILD); if (!bus->is_kernel) - return -ENOTSUP; + return -EOPNOTSUPP; if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; @@ -3477,7 +3513,7 @@ _public_ int sd_bus_get_address(sd_bus *bus, const char **address) { return -ENODATA; } -int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { +_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { assert_return(bus, -EINVAL); assert_return(mask, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -3486,35 +3522,35 @@ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { return 0; } -int sd_bus_is_bus_client(sd_bus *bus) { +_public_ int sd_bus_is_bus_client(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->bus_client; } -int sd_bus_is_server(sd_bus *bus) { +_public_ int sd_bus_is_server(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->is_server; } -int sd_bus_is_anonymous(sd_bus *bus) { +_public_ int sd_bus_is_anonymous(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->anonymous_auth; } -int sd_bus_is_trusted(sd_bus *bus) { +_public_ int sd_bus_is_trusted(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->trusted; } -int sd_bus_is_monitor(sd_bus *bus) { +_public_ int sd_bus_is_monitor(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); diff --git a/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c index 35f87e91bd..d14110aa04 100644 --- a/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c +++ b/src/libsystemd/sd-bus/test-bus-benchmark.c @@ -19,16 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <ctype.h> #include <sys/wait.h> +#include "def.h" #include "util.h" -#include "log.h" #include "time-util.h" #include "sd-bus.h" -#include "bus-message.h" -#include "bus-error.h" #include "bus-kernel.h" #include "bus-internal.h" #include "bus-util.h" @@ -37,6 +34,12 @@ static usec_t arg_loop_usec = 100 * USEC_PER_MSEC; +typedef enum Type { + TYPE_KDBUS, + TYPE_LEGACY, + TYPE_DIRECT, +} Type; + static void server(sd_bus *b, size_t *result) { int r; @@ -60,7 +63,8 @@ static void server(sd_bus *b, size_t *result) { /* Make sure the mmap is mapped */ assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0); - assert_se(sd_bus_reply_method_return(m, NULL) >= 0); + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) { uint64_t res; assert_se(sd_bus_message_read(m, "t", &res) > 0); @@ -68,16 +72,16 @@ static void server(sd_bus *b, size_t *result) { *result = res; return; - } else + } else if (!sd_bus_message_is_signal(m, NULL, NULL)) assert_not_reached("Unknown method"); } } -static void transaction(sd_bus *b, size_t sz) { +static void transaction(sd_bus *b, size_t sz, const char *server_name) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; uint8_t *p; - assert_se(sd_bus_message_new_method_call(b, &m, ":1.1", "/", "benchmark.server", "Work") >= 0); + assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0); assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0); memset(p, 0x80, sz); @@ -85,7 +89,7 @@ static void transaction(sd_bus *b, size_t sz) { assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0); } -static void client_bisect(const char *address) { +static void client_bisect(const char *address, const char *server_name) { _cleanup_bus_message_unref_ sd_bus_message *x = NULL; size_t lsize, rsize, csize; sd_bus *b; @@ -100,7 +104,8 @@ static void client_bisect(const char *address) { r = sd_bus_start(b); assert_se(r >= 0); - assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0); + r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); + assert_se(r >= 0); lsize = 1; rsize = MAX_SIZE; @@ -125,7 +130,7 @@ static void client_bisect(const char *address) { t = now(CLOCK_MONOTONIC); for (n_copying = 0;; n_copying++) { - transaction(b, csize); + transaction(b, csize, server_name); if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) break; } @@ -135,7 +140,7 @@ static void client_bisect(const char *address) { t = now(CLOCK_MONOTONIC); for (n_memfd = 0;; n_memfd++) { - transaction(b, csize); + transaction(b, csize, server_name); if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) break; } @@ -151,14 +156,14 @@ static void client_bisect(const char *address) { } b->use_memfd = 1; - assert_se(sd_bus_message_new_method_call(b, &x, ":1.1", "/", "benchmark.server", "Exit") >= 0); + assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0); assert_se(sd_bus_message_append(x, "t", csize) >= 0); assert_se(sd_bus_send(b, x, NULL) >= 0); sd_bus_unref(b); } -static void client_chart(const char *address) { +static void client_chart(Type type, const char *address, const char *server_name, int fd) { _cleanup_bus_message_unref_ sd_bus_message *x = NULL; size_t csize; sd_bus *b; @@ -167,15 +172,34 @@ static void client_chart(const char *address) { r = sd_bus_new(&b); assert_se(r >= 0); - r = sd_bus_set_address(b, address); - assert_se(r >= 0); + if (type == TYPE_DIRECT) { + r = sd_bus_set_fd(b, fd, fd); + assert_se(r >= 0); + } else { + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_set_bus_client(b, true); + assert_se(r >= 0); + } r = sd_bus_start(b); assert_se(r >= 0); - assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0); + r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); + assert_se(r >= 0); - printf("SIZE\tCOPY\tMEMFD\n"); + switch (type) { + case TYPE_KDBUS: + printf("SIZE\tCOPY\tMEMFD\n"); + break; + case TYPE_LEGACY: + printf("SIZE\tLEGACY\n"); + break; + case TYPE_DIRECT: + printf("SIZE\tDIRECT\n"); + break; + } for (csize = 1; csize <= MAX_SIZE; csize *= 2) { usec_t t; @@ -183,22 +207,24 @@ static void client_chart(const char *address) { printf("%zu\t", csize); - b->use_memfd = 0; + if (type == TYPE_KDBUS) { + b->use_memfd = 0; - t = now(CLOCK_MONOTONIC); - for (n_copying = 0;; n_copying++) { - transaction(b, csize); - if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) - break; - } + t = now(CLOCK_MONOTONIC); + for (n_copying = 0;; n_copying++) { + transaction(b, csize, server_name); + if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) + break; + } - printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec)); + printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec)); - b->use_memfd = -1; + b->use_memfd = -1; + } t = now(CLOCK_MONOTONIC); for (n_memfd = 0;; n_memfd++) { - transaction(b, csize); + transaction(b, csize, server_name); if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) break; } @@ -207,7 +233,7 @@ static void client_chart(const char *address) { } b->use_memfd = 1; - assert_se(sd_bus_message_new_method_call(b, &x, ":1.1", "/", "benchmark.server", "Exit") >= 0); + assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0); assert_se(sd_bus_message_append(x, "t", csize) >= 0); assert_se(sd_bus_send(b, x, NULL) >= 0); @@ -219,9 +245,11 @@ int main(int argc, char *argv[]) { MODE_BISECT, MODE_CHART, } mode = MODE_BISECT; - int i; - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; + Type type = TYPE_KDBUS; + int i, pair[2] = { -1, -1 }; + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL; _cleanup_close_ int bus_ref = -1; + const char *unique; cpu_set_t cpuset; size_t result; sd_bus *b; @@ -232,33 +260,72 @@ int main(int argc, char *argv[]) { if (streq(argv[i], "chart")) { mode = MODE_CHART; continue; + } else if (streq(argv[i], "legacy")) { + type = TYPE_LEGACY; + continue; + } else if (streq(argv[i], "direct")) { + type = TYPE_DIRECT; + continue; } assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0); } + assert_se(!MODE_BISECT || TYPE_KDBUS); + assert_se(arg_loop_usec > 0); - assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + if (type == TYPE_KDBUS) { + assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + + bus_ref = bus_kernel_create_bus(name, false, &bus_name); + if (bus_ref == -ENOENT) + exit(EXIT_TEST_SKIP); - bus_ref = bus_kernel_create_bus(name, false, &bus_name); - if (bus_ref == -ENOENT) - exit(EXIT_TEST_SKIP); + assert_se(bus_ref >= 0); - assert_se(bus_ref >= 0); + address = strappend("kernel:path=", bus_name); + assert_se(address); + } else if (type == TYPE_LEGACY) { + const char *e; - address = strappend("kernel:path=", bus_name); - assert_se(address); + e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); + assert_se(e); + + address = strdup(e); + assert_se(address); + } r = sd_bus_new(&b); assert_se(r >= 0); - r = sd_bus_set_address(b, address); - assert_se(r >= 0); + if (type == TYPE_DIRECT) { + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); + + r = sd_bus_set_fd(b, pair[0], pair[0]); + assert_se(r >= 0); + + r = sd_bus_set_server(b, true, SD_ID128_NULL); + assert_se(r >= 0); + } else { + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_set_bus_client(b, true); + assert_se(r >= 0); + } r = sd_bus_start(b); assert_se(r >= 0); + if (type != TYPE_DIRECT) { + r = sd_bus_get_unique_name(b, &unique); + assert_se(r >= 0); + + server_name = strdup(unique); + assert_se(server_name); + } + sync(); setpriority(PRIO_PROCESS, 0, -19); @@ -275,11 +342,11 @@ int main(int argc, char *argv[]) { switch (mode) { case MODE_BISECT: - client_bisect(address); + client_bisect(address, server_name); break; case MODE_CHART: - client_chart(address); + client_chart(type, address, server_name, pair[1]); break; } @@ -297,6 +364,7 @@ int main(int argc, char *argv[]) { assert_se(waitpid(pid, NULL, 0) == pid); + safe_close(pair[1]); sd_bus_unref(b); return 0; diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index 8625ee6d89..046e999008 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -27,24 +27,22 @@ #include "log.h" #include "util.h" #include "macro.h" +#include "formats-util.h" #include "sd-bus.h" -#include "bus-message.h" #include "bus-error.h" #include "bus-match.h" #include "bus-internal.h" #include "bus-util.h" -static int match_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { +static int match_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m))); return 0; } -static int object_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { +static int object_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { int r; - assert_se(bus); - if (sd_bus_message_is_method_error(m, NULL)) return 0; @@ -264,11 +262,11 @@ fail: static void* client1(void*p) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - sd_bus *bus = NULL; - sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *hello; int r; - int pp[2] = { -1, -1 }; + _cleanup_close_pair_ int pp[2] = { -1, -1 }; char x; r = sd_bus_open_user(&bus); @@ -347,18 +345,12 @@ finish: else sd_bus_send(bus, q, NULL); - sd_bus_flush(bus); - sd_bus_unref(bus); } - sd_bus_error_free(&error); - - safe_close_pair(pp); - return INT_TO_PTR(r); } -static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { +static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { bool *x = userdata; log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m))); @@ -369,8 +361,8 @@ static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_er static void* client2(void*p) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; - sd_bus *bus = NULL; - sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; bool quit = false; const char *mid; int r; @@ -399,8 +391,7 @@ static void* client2(void*p) { goto finish; } - sd_bus_message_unref(m); - m = NULL; + m = sd_bus_message_unref(m); r = sd_bus_message_new_signal( bus, @@ -419,8 +410,7 @@ static void* client2(void*p) { goto finish; } - sd_bus_message_unref(m); - m = NULL; + m = sd_bus_message_unref(m); r = sd_bus_message_new_method_call( bus, @@ -448,8 +438,7 @@ static void* client2(void*p) { log_info("Machine ID is %s.", mid); - sd_bus_message_unref(m); - m = NULL; + m = sd_bus_message_unref(m); r = sd_bus_message_new_method_call( bus, @@ -463,8 +452,7 @@ static void* client2(void*p) { goto finish; } - sd_bus_message_unref(reply); - reply = NULL; + reply = sd_bus_message_unref(reply); r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply); if (r < 0) @@ -472,8 +460,7 @@ static void* client2(void*p) { else log_info("Slow call succeed."); - sd_bus_message_unref(m); - m = NULL; + m = sd_bus_message_unref(m); r = sd_bus_message_new_method_call( bus, @@ -526,12 +513,9 @@ finish: goto finish; } - sd_bus_send(bus, q, NULL); - sd_bus_flush(bus); - sd_bus_unref(bus); + (void) sd_bus_send(bus, q, NULL); } - sd_bus_error_free(&error); return INT_TO_PTR(r); } diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index ff2602ba34..edd5033db2 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -22,7 +22,6 @@ #include "sd-bus.h" #include "bus-dump.h" #include "bus-util.h" -#include "util.h" int main(int argc, char *argv[]) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index 463fc81c75..5753c04b0e 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -35,11 +35,11 @@ static void test_error(void) { }; assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -ENOTSUP); + assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -EOPNOTSUPP); assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED)); assert_se(streq(error.message, "xxx")); assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED)); - assert_se(sd_bus_error_get_errno(&error) == ENOTSUP); + assert_se(sd_bus_error_get_errno(&error) == EOPNOTSUPP); assert_se(sd_bus_error_is_set(&error)); sd_bus_error_free(&error); diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c index 67b6461f30..b2caa02870 100644 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ b/src/libsystemd/sd-bus/test-bus-introspect.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "log.h" #include "bus-introspect.h" diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c index 071b7e0cf9..b11c43bd7b 100644 --- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c +++ b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c @@ -23,8 +23,6 @@ #include "log.h" #include "sd-bus.h" -#include "bus-message.h" -#include "bus-error.h" #include "bus-kernel.h" #include "bus-util.h" diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c index 3aec568229..6506eaab2e 100644 --- a/src/libsystemd/sd-bus/test-bus-kernel.c +++ b/src/libsystemd/sd-bus/test-bus-kernel.c @@ -25,8 +25,6 @@ #include "log.h" #include "sd-bus.h" -#include "bus-message.h" -#include "bus-error.h" #include "bus-kernel.h" #include "bus-util.h" #include "bus-dump.h" @@ -45,8 +43,6 @@ int main(int argc, char *argv[]) { assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); - bus_kernel_fix_attach_mask(); - bus_ref = bus_kernel_create_bus(name, false, &bus_name); if (bus_ref == -ENOENT) return EXIT_TEST_SKIP; @@ -119,7 +115,7 @@ int main(int argc, char *argv[]) { assert_se(r == -EBUSY); r = sd_bus_process_priority(b, -10, &m); - assert_se(r == -ENOMSG); + assert_se(r == 0); r = sd_bus_process(b, &m); assert_se(r > 0); diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index d95a03c221..f8ecadf499 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -19,9 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <stdlib.h> -#include <byteswap.h> #include <math.h> #ifdef HAVE_GLIB @@ -41,6 +39,16 @@ #include "bus-dump.h" #include "bus-label.h" +static void test_bus_path_encode_unique(void) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL; + + assert_se(bus_path_encode_unique(NULL, "/foo/bar", "some.sender", "a.suffix", &a) >= 0 && streq_ptr(a, "/foo/bar/some_2esender/a_2esuffix")); + assert_se(bus_path_decode_unique(a, "/foo/bar", &b, &c) > 0 && streq_ptr(b, "some.sender") && streq_ptr(c, "a.suffix")); + assert_se(bus_path_decode_unique(a, "/bar/foo", &d, &d) == 0 && !d); + assert_se(bus_path_decode_unique("/foo/bar/onlyOneSuffix", "/foo/bar", &d, &d) == 0 && !d); + assert_se(bus_path_decode_unique("/foo/bar/_/_", "/foo/bar", &d, &e) > 0 && streq_ptr(d, "") && streq_ptr(e, "")); +} + static void test_bus_path_encode(void) { _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; @@ -359,6 +367,7 @@ int main(int argc, char *argv[]) { test_bus_label_escape(); test_bus_path_encode(); + test_bus_path_encode_unique(); return 0; } diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c index 76ca0b6bf3..40c67046da 100644 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ b/src/libsystemd/sd-bus/test-bus-match.c @@ -20,7 +20,6 @@ ***/ #include "log.h" -#include "util.h" #include "macro.h" #include "bus-match.h" @@ -30,7 +29,7 @@ static bool mask[32]; -static int filter(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { +static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { log_info("Ran %u", PTR_TO_UINT(userdata)); assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask)); mask[PTR_TO_UINT(userdata)] = true; diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index acf67a52b3..52952603e4 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -19,11 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <stdlib.h> #include <pthread.h> -#include <unistd.h> -#include <fcntl.h> #include "log.h" #include "util.h" @@ -44,7 +41,7 @@ struct context { uint32_t automatic_integer_property; }; -static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int something_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { struct context *c = userdata; const char *s; char *n = NULL; @@ -70,7 +67,7 @@ static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_ return 1; } -static int exit_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int exit_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { struct context *c = userdata; int r; @@ -132,10 +129,10 @@ static int value_handler(sd_bus *bus, const char *path, const char *interface, c return 1; } -static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int notify_test(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_properties_changed(bus, m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0); + assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -143,10 +140,10 @@ static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_er return 1; } -static int notify_test2(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_properties_changed_strv(bus, m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0); + assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -154,10 +151,10 @@ static int notify_test2(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_e return 1; } -static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0); + assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -165,10 +162,10 @@ static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata, return 1; } -static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_interfaces_removed(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0); + assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -176,10 +173,10 @@ static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdat return 1; } -static int emit_object_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_object_added(bus, m->path) >= 0); + assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), m->path) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); @@ -187,10 +184,10 @@ static int emit_object_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_ return 1; } -static int emit_object_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; - assert_se(sd_bus_emit_object_removed(bus, m->path) >= 0); + assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), m->path) >= 0); r = sd_bus_reply_method_return(m, NULL); assert_se(r >= 0); diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c index 5f807c3b1e..080d8eddb7 100644 --- a/src/libsystemd/sd-bus/test-bus-server.c +++ b/src/libsystemd/sd-bus/test-bus-server.c @@ -19,11 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <stdlib.h> #include <pthread.h> -#include <unistd.h> -#include <fcntl.h> #include "log.h" #include "util.h" @@ -31,7 +28,6 @@ #include "sd-bus.h" #include "bus-internal.h" -#include "bus-message.h" #include "bus-util.h" struct context { diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c index 3fc565c620..4165c9273a 100644 --- a/src/libsystemd/sd-bus/test-bus-signature.c +++ b/src/libsystemd/sd-bus/test-bus-signature.c @@ -19,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> -#include <stdlib.h> #include "log.h" #include "bus-signature.h" diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c index a054f74bf3..2d062fc9b5 100644 --- a/src/libsystemd/sd-bus/test-bus-zero-copy.c +++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <fcntl.h> #include <sys/mman.h> #include "util.h" @@ -28,7 +27,6 @@ #include "sd-bus.h" #include "bus-message.h" -#include "bus-error.h" #include "bus-kernel.h" #include "bus-dump.h" @@ -39,6 +37,7 @@ int main(int argc, char *argv[]) { _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; + const char *unique; uint8_t *p; sd_bus *a, *b; int r, bus_ref; @@ -81,7 +80,10 @@ int main(int argc, char *argv[]) { r = sd_bus_start(b); assert_se(r >= 0); - r = sd_bus_message_new_method_call(b, &m, ":1.1", "/a/path", "an.inter.face", "AMethod"); + r = sd_bus_get_unique_name(a, &unique); + assert_se(r >= 0); + + r = sd_bus_message_new_method_call(b, &m, unique, "/a/path", "an.inter.face", "AMethod"); assert_se(r >= 0); r = sd_bus_message_open_container(m, 'r', "aysay"); diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 028c2a7a5b..82ac72c72a 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -19,11 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> -#include <fcntl.h> #include <netinet/in.h> #include <stdlib.h> #include <errno.h> @@ -352,16 +350,10 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char .msg_iovlen = 1, .msg_name = &sockaddr, }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(int) * n_fds)]; - } control; _cleanup_close_ int fd = -1; struct cmsghdr *cmsg = NULL; const char *e; - size_t controllen_without_ucred = 0; - bool try_without_ucred = false; + bool have_pid; int r; if (!state) { @@ -400,40 +392,37 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) msghdr.msg_namelen = sizeof(struct sockaddr_un); - if (n_fds > 0) { - msghdr.msg_control = &control; - msghdr.msg_controllen = CMSG_LEN(sizeof(int) * n_fds); + have_pid = pid != 0 && pid != getpid(); - cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); - - memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); - } + if (n_fds > 0 || have_pid) { + msghdr.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds) + + CMSG_SPACE(sizeof(struct ucred) * have_pid); + msghdr.msg_control = alloca(msghdr.msg_controllen); - if (pid != 0 && pid != getpid()) { - struct ucred *ucred; + cmsg = CMSG_FIRSTHDR(&msghdr); + if (n_fds > 0) { + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); - try_without_ucred = true; - controllen_without_ucred = msghdr.msg_controllen; + memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); - msghdr.msg_control = &control; - msghdr.msg_controllen += CMSG_LEN(sizeof(struct ucred)); + if (have_pid) + assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg)); + } - if (cmsg) - cmsg = CMSG_NXTHDR(&msghdr, cmsg); - else - cmsg = CMSG_FIRSTHDR(&msghdr); + if (have_pid) { + struct ucred *ucred; - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - ucred = (struct ucred*) CMSG_DATA(cmsg); - ucred->pid = pid; - ucred->uid = getuid(); - ucred->gid = getgid(); + ucred = (struct ucred*) CMSG_DATA(cmsg); + ucred->pid = pid; + ucred->uid = getuid(); + ucred->gid = getgid(); + } } /* First try with fake ucred data, as requested */ @@ -443,10 +432,10 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char } /* If that failed, try with our own ucred instead */ - if (try_without_ucred) { - if (controllen_without_ucred <= 0) + if (have_pid) { + msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); + if (msghdr.msg_controllen == 0) msghdr.msg_control = NULL; - msghdr.msg_controllen = controllen_without_ucred; if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { r = 1; diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h new file mode 100644 index 0000000000..8d04640dc7 --- /dev/null +++ b/src/libsystemd/sd-device/device-enumerator-private.h @@ -0,0 +1,36 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-device.h" + +int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); +int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); +int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); +sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); +sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); + +#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \ + for (device = device_enumerator_get_first(enumerator); \ + device; \ + device = device_enumerator_get_next(enumerator)) diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c new file mode 100644 index 0000000000..7fd77e9480 --- /dev/null +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -0,0 +1,983 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers <kay@vrfy.org> + Copyright 2014-2015 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "util.h" +#include "prioq.h" +#include "strv.h" +#include "set.h" + +#include "sd-device.h" + +#include "device-util.h" +#include "device-enumerator-private.h" + +#define DEVICE_ENUMERATE_MAX_DEPTH 256 + +typedef enum DeviceEnumerationType { + DEVICE_ENUMERATION_TYPE_DEVICES, + DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, + _DEVICE_ENUMERATION_TYPE_MAX, + _DEVICE_ENUMERATION_TYPE_INVALID = -1, +} DeviceEnumerationType; + +struct sd_device_enumerator { + unsigned n_ref; + + DeviceEnumerationType type; + Prioq *devices; + bool scan_uptodate; + + Set *match_subsystem; + Set *nomatch_subsystem; + Hashmap *match_sysattr; + Hashmap *nomatch_sysattr; + Hashmap *match_property; + Set *match_sysname; + Set *match_tag; + sd_device *match_parent; + bool match_allow_uninitialized; +}; + +_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { + _cleanup_device_enumerator_unref_ sd_device_enumerator *enumerator = NULL; + + assert(ret); + + enumerator = new0(sd_device_enumerator, 1); + if (!enumerator) + return -ENOMEM; + + enumerator->n_ref = 1; + enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID; + + *ret = enumerator; + enumerator = NULL; + + return 0; +} + +_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + assert_se((++ enumerator->n_ref) >= 2); + + return enumerator; +} + +_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) { + if (enumerator && (-- enumerator->n_ref) == 0) { + sd_device *device; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + prioq_free(enumerator->devices); + + set_free_free(enumerator->match_subsystem); + set_free_free(enumerator->nomatch_subsystem); + hashmap_free_free_free(enumerator->match_sysattr); + hashmap_free_free_free(enumerator->nomatch_sysattr); + hashmap_free_free_free(enumerator->match_property); + set_free_free(enumerator->match_sysname); + set_free_free(enumerator->match_tag); + sd_device_unref(enumerator->match_parent); + + free(enumerator); + } + + return NULL; +} + +_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { + Set **set; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(subsystem, -EINVAL); + + if (match) + set = &enumerator->match_subsystem; + else + set = &enumerator->nomatch_subsystem; + + r = set_ensure_allocated(set, NULL); + if (r < 0) + return r; + + r = set_put_strdup(*set, subsystem); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) { + _cleanup_free_ char *sysattr = NULL, *value = NULL; + Hashmap **hashmap; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(_sysattr, -EINVAL); + + if (match) + hashmap = &enumerator->match_sysattr; + else + hashmap = &enumerator->nomatch_sysattr; + + r = hashmap_ensure_allocated(hashmap, NULL); + if (r < 0) + return r; + + sysattr = strdup(_sysattr); + if (!sysattr) + return -ENOMEM; + + if (_value) { + value = strdup(_value); + if (!value) + return -ENOMEM; + } + + r = hashmap_put(*hashmap, sysattr, value); + if (r < 0) + return r; + + sysattr = NULL; + value = NULL; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) { + _cleanup_free_ char *property = NULL, *value = NULL; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(_property, -EINVAL); + + r = hashmap_ensure_allocated(&enumerator->match_property, NULL); + if (r < 0) + return r; + + property = strdup(_property); + if (!property) + return -ENOMEM; + + if (_value) { + value = strdup(_value); + if (!value) + return -ENOMEM; + } + + r = hashmap_put(enumerator->match_property, property, value); + if (r < 0) + return r; + + property = NULL; + value = NULL; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(sysname, -EINVAL); + + r = set_ensure_allocated(&enumerator->match_sysname, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_sysname, sysname); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(tag, -EINVAL); + + r = set_ensure_allocated(&enumerator->match_tag, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_tag, tag); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { + assert_return(enumerator, -EINVAL); + assert_return(parent, -EINVAL); + + sd_device_unref(enumerator->match_parent); + enumerator->match_parent = sd_device_ref(parent); + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { + assert_return(enumerator, -EINVAL); + + enumerator->match_allow_uninitialized = true; + + enumerator->scan_uptodate = false; + + return 0; +} + +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { + assert_return(enumerator, -EINVAL); + + enumerator->match_allow_uninitialized = false; + + enumerator->scan_uptodate = false; + + return 0; +} + +static int device_compare(const void *_a, const void *_b) { + sd_device *a = (sd_device *)_a, *b = (sd_device *)_b; + const char *devpath_a, *devpath_b, *sound_a; + bool delay_a, delay_b; + + assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); + + sound_a = strstr(devpath_a, "/sound/card"); + if (sound_a) { + /* For sound cards the control device must be enumerated last to + * make sure it's the final device node that gets ACLs applied. + * Applications rely on this fact and use ACL changes on the + * control node as an indicator that the ACL change of the + * entire sound card completed. The kernel makes this guarantee + * when creating those devices, and hence we should too when + * enumerating them. */ + sound_a += strlen("/sound/card"); + sound_a = strchr(sound_a, '/'); + + if (sound_a) { + unsigned prefix_len; + + prefix_len = sound_a - devpath_a; + + if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { + const char *sound_b; + + sound_b = devpath_b + prefix_len; + + if (startswith(sound_a, "/controlC") && + !startswith(sound_b, "/contolC")) + return 1; + + if (!startswith(sound_a, "/controlC") && + startswith(sound_b, "/controlC")) + return -1; + } + } + } + + /* md and dm devices are enumerated after all other devices */ + delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); + delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); + if (delay_a != delay_b) + return delay_a - delay_b; + + return strcmp(devpath_a, devpath_b); +} + +int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(device, -EINVAL); + + r = prioq_ensure_allocated(&enumerator->devices, device_compare); + if (r < 0) + return r; + + r = prioq_put(enumerator->devices, device, NULL); + if (r < 0) + return r; + + sd_device_ref(device); + + return 0; +} + +static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) { + const char *value; + int r; + + assert(device); + assert(sysattr); + + r = sd_device_get_sysattr_value(device, sysattr, &value); + if (r < 0) + return false; + + if (!match_value) + return true; + + if (fnmatch(match_value, value, 0) == 0) + return true; + + return false; +} + +static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) { + const char *sysattr; + const char *value; + Iterator i; + + assert(enumerator); + assert(device); + + HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i) + if (match_sysattr_value(device, sysattr, value)) + return false; + + HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i) + if (!match_sysattr_value(device, sysattr, value)) + return false; + + return true; +} + +static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { + const char *property; + const char *value; + Iterator i; + + assert(enumerator); + assert(device); + + if (hashmap_isempty(enumerator->match_property)) + return true; + + HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) { + const char *property_dev, *value_dev; + + FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) { + if (fnmatch(property, property_dev, 0) != 0) + continue; + + if (!value && !value_dev) + return true; + + if (!value || !value_dev) + continue; + + if (fnmatch(value, value_dev, 0) == 0) + return true; + } + } + + return false; +} + +static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) { + const char *tag; + Iterator i; + + assert(enumerator); + assert(device); + + SET_FOREACH(tag, enumerator->match_tag, i) + if (!sd_device_has_tag(device, tag)) + return false; + + return true; +} + +static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) { + const char *devpath, *devpath_dev; + int r; + + assert(enumerator); + assert(device); + + if (!enumerator->match_parent) + return true; + + r = sd_device_get_devpath(enumerator->match_parent, &devpath); + assert(r >= 0); + + r = sd_device_get_devpath(device, &devpath_dev); + assert(r >= 0); + + return startswith(devpath_dev, devpath); +} + +static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) { + const char *sysname_match; + Iterator i; + + assert(enumerator); + assert(sysname); + + if (set_isempty(enumerator->match_sysname)) + return true; + + SET_FOREACH(sysname_match, enumerator->match_sysname, i) + if (fnmatch(sysname_match, sysname, 0) == 0) + return true; + + return false; +} + +static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + assert(enumerator); + assert(basedir); + + path = strjoina("/sys/", basedir, "/"); + + if (subdir1) + path = strjoina(path, subdir1, "/"); + + if (subdir2) + path = strjoina(path, subdir2, "/"); + + dir = opendir(path); + if (!dir) + return -errno; + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_device_unref_ sd_device *device = NULL; + char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1]; + dev_t devnum; + int ifindex, initialized, k; + + if (dent->d_name[0] == '.') + continue; + + if (!match_sysname(enumerator, dent->d_name)) + continue; + + (void)sprintf(syspath, "%s%s", path, dent->d_name); + + k = sd_device_new_from_syspath(&device, syspath); + if (k < 0) { + if (k != -ENODEV) + /* this is necessarily racey, so ignore missing devices */ + r = k; + + continue; + } + + k = sd_device_get_devnum(device, &devnum); + if (k < 0) { + r = k; + continue; + } + + k = sd_device_get_ifindex(device, &ifindex); + if (k < 0) { + r = k; + continue; + } + + k = sd_device_get_is_initialized(device, &initialized); + if (k < 0) { + r = k; + continue; + } + + /* + * All devices with a device node or network interfaces + * possibly need udev to adjust the device node permission + * or context, or rename the interface before it can be + * reliably used from other processes. + * + * For now, we can only check these types of devices, we + * might not store a database, and have no way to find out + * for all other types of devices. + */ + if (!enumerator->match_allow_uninitialized && + !initialized && + (major(devnum) > 0 || ifindex > 0)) + continue; + + if (!match_parent(enumerator, device)) + continue; + + if (!match_tag(enumerator, device)) + continue; + + if (!match_property(enumerator, device)) + continue; + + if (!match_sysattr(enumerator, device)) + continue; + + k = device_enumerator_add_device(enumerator, device); + if (k < 0) + r = k; + } + + return r; +} + +static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { + const char *subsystem_match; + Iterator i; + + assert(enumerator); + + if (!subsystem) + return false; + + SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i) + if (fnmatch(subsystem_match, subsystem, 0) == 0) + return false; + + if (set_isempty(enumerator->match_subsystem)) + return true; + + SET_FOREACH(subsystem_match, enumerator->match_subsystem, i) + if (fnmatch(subsystem_match, subsystem, 0) == 0) + return true; + + return false; +} + +static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + path = strjoina("/sys/", basedir); + + dir = opendir(path); + if (!dir) + return -errno; + + log_debug(" device-enumerator: scanning %s", path); + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + int k; + + if (dent->d_name[0] == '.') + continue; + + if (!match_subsystem(enumerator, subsystem ? : dent->d_name)) + continue; + + k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir); + if (k < 0) + r = k; + } + + return r; +} + +static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + assert(enumerator); + assert(tag); + + path = strjoina("/run/udev/tags/", tag); + + dir = opendir(path); + if (!dir) { + if (errno == ENOENT) + return 0; + else { + log_error("sd-device-enumerator: could not open tags directory %s: %m", path); + return -errno; + } + } + + /* TODO: filter away subsystems? */ + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_device_unref_ sd_device *device = NULL; + const char *subsystem, *sysname; + int k; + + if (dent->d_name[0] == '.') + continue; + + k = sd_device_new_from_device_id(&device, dent->d_name); + if (k < 0) { + if (k != -ENODEV) + /* this is necessarily racy, so ignore missing devices */ + r = k; + + continue; + } + + k = sd_device_get_subsystem(device, &subsystem); + if (k < 0) { + r = k; + continue; + } + + if (!match_subsystem(enumerator, subsystem)) + continue; + + k = sd_device_get_sysname(device, &sysname); + if (k < 0) { + r = k; + continue; + } + + if (!match_sysname(enumerator, sysname)) + continue; + + if (!match_parent(enumerator, device)) + continue; + + if (!match_property(enumerator, device)) + continue; + + if (!match_sysattr(enumerator, device)) + continue; + + k = device_enumerator_add_device(enumerator, device); + if (k < 0) { + r = k; + continue; + } + } + + return r; +} + +static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) { + const char *tag; + Iterator i; + int r; + + assert(enumerator); + + SET_FOREACH(tag, enumerator->match_tag, i) { + r = enumerator_scan_devices_tag(enumerator, tag); + if (r < 0) + return r; + } + + return 0; +} + +static int parent_add_child(sd_device_enumerator *enumerator, const char *path) { + _cleanup_device_unref_ sd_device *device = NULL; + const char *subsystem, *sysname; + int r; + + r = sd_device_new_from_syspath(&device, path); + if (r == -ENODEV) + /* this is necessarily racy, so ignore missing devices */ + return 0; + else if (r < 0) + return r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r < 0) + return r; + + if (!match_subsystem(enumerator, subsystem)) + return 0; + + r = sd_device_get_sysname(device, &sysname); + if (r < 0) + return r; + + if (!match_sysname(enumerator, sysname)) + return 0; + + if (!match_property(enumerator, device)) + return 0; + + if (!match_sysattr(enumerator, device)) + return 0; + + r = device_enumerator_add_device(enumerator, device); + if (r < 0) + return r; + + return 1; +} + +static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + int r = 0; + + dir = opendir(path); + if (!dir) { + log_debug("sd-device-enumerate: could not open parent directory %s: %m", path); + return -errno; + } + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_free_ char *child = NULL; + int k; + + if (dent->d_name[0] == '.') + continue; + + if (dent->d_type != DT_DIR) + continue; + + child = strjoin(path, "/", dent->d_name, NULL); + if (!child) + return -ENOMEM; + + k = parent_add_child(enumerator, child); + if (k < 0) + r = k; + + if (maxdepth > 0) + parent_crawl_children(enumerator, child, maxdepth - 1); + else + log_debug("device-enumerate: max depth reached, %s: ignoring devices", child); + } + + return r; +} + +static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { + const char *path; + int r = 0, k; + + r = sd_device_get_syspath(enumerator->match_parent, &path); + if (r < 0) + return r; + + k = parent_add_child(enumerator, path); + if (k < 0) + r = k; + + k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); + if (k < 0) + r = k; + + return r; +} + +static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { + int r = 0; + + log_debug("device-enumerator: scan all dirs"); + + if (access("/sys/subsystem", F_OK) >= 0) { + /* we have /subsystem/, forget all the old stuff */ + r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); + if (r < 0) { + log_debug("device-enumerator: failed to scan /sys/subsystem: %s", strerror(-r)); + return r; + } + } else { + int k; + + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m"); + r = k; + } + + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m"); + r = k; + } + } + + return r; +} + +int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { + sd_device *device; + int r; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) + return 0; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + if (!set_isempty(enumerator->match_tag)) { + r = enumerator_scan_devices_tags(enumerator); + if (r < 0) + return r; + } else if (enumerator->match_parent) { + r = enumerator_scan_devices_children(enumerator); + if (r < 0) + return r; + } else { + r = enumerator_scan_devices_all(enumerator); + if (r < 0) + return r; + } + + enumerator->scan_uptodate = true; + + return 0; +} + +_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { + int r; + + assert_return(enumerator, NULL); + + r = device_enumerator_scan_devices(enumerator); + if (r < 0) + return NULL; + + enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (!enumerator->scan_uptodate || + enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} + +int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { + sd_device *device; + const char *subsysdir; + int r = 0, k; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) + return 0; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + /* modules */ + if (match_subsystem(enumerator, "module")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan modules: %m"); + r = k; + } + } + + if (access("/sys/subsystem", F_OK) >= 0) + subsysdir = "subsystem"; + else + subsysdir = "bus"; + + /* subsystems (only buses support coldplug) */ + if (match_subsystem(enumerator, "subsystem")) { + k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m"); + r = k; + } + } + + /* subsystem drivers */ + if (match_subsystem(enumerator, "drivers")) { + k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan drivers: %m"); + r = k; + } + } + + enumerator->scan_uptodate = true; + + return r; +} + +_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { + int r; + + assert_return(enumerator, NULL); + + r = device_enumerator_scan_subsystems(enumerator); + if (r < 0) + return NULL; + + enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (enumerator->scan_uptodate || + enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} + +sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + return prioq_peek(enumerator->devices); +} + +sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h new file mode 100644 index 0000000000..b96441de56 --- /dev/null +++ b/src/libsystemd/sd-device/device-internal.h @@ -0,0 +1,126 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers <kay@vrfy.org> + Copyright 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#pragma once + +#include "hashmap.h" +#include "set.h" + +struct sd_device { + uint64_t n_ref; + + sd_device *parent; + bool parent_set; /* no need to try to reload parent */ + + OrderedHashmap *properties; + Iterator properties_iterator; + uint64_t properties_generation; /* changes whenever the properties are changed */ + uint64_t properties_iterator_generation; /* generation when iteration was started */ + + /* the subset of the properties that should be written to the db*/ + OrderedHashmap *properties_db; + + Hashmap *sysattr_values; /* cached sysattr values */ + + Set *sysattrs; /* names of sysattrs */ + Iterator sysattrs_iterator; + bool sysattrs_read; /* don't try to re-read sysattrs once read */ + + Set *tags; + Iterator tags_iterator; + uint64_t tags_generation; /* changes whenever the tags are changed */ + uint64_t tags_iterator_generation; /* generation when iteration was started */ + bool property_tags_outdated; /* need to update TAGS= property */ + + Set *devlinks; + Iterator devlinks_iterator; + uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ + uint64_t devlinks_iterator_generation; /* generation when iteration was started */ + bool property_devlinks_outdated; /* need to update DEVLINKS= property */ + int devlink_priority; + + char **properties_strv; /* the properties hashmap as a strv */ + uint8_t *properties_nulstr; /* the same as a nulstr */ + size_t properties_nulstr_len; + bool properties_buf_outdated; /* need to reread hashmap */ + + int watch_handle; + + char *syspath; + const char *devpath; + const char *sysnum; + char *sysname; + bool sysname_set; /* don't reread sysname */ + + char *devtype; + int ifindex; + char *devname; + dev_t devnum; + + char *subsystem; + bool subsystem_set; /* don't reread subsystem */ + char *driver; + bool driver_set; /* don't reread driver */ + + char *id_filename; + + bool is_initialized; + uint64_t usec_initialized; + + mode_t devmode; + uid_t devuid; + gid_t devgid; + + bool uevent_loaded; /* don't reread uevent */ + bool db_loaded; /* don't reread db */ + + bool sealed; /* don't read more information from uevent/db */ + bool db_persist; /* don't clean up the db when switching from initrd to real root */ +}; + +typedef enum DeviceAction { + DEVICE_ACTION_ADD, + DEVICE_ACTION_REMOVE, + DEVICE_ACTION_CHANGE, + DEVICE_ACTION_MOVE, + DEVICE_ACTION_ONLINE, + DEVICE_ACTION_OFFLINE, + _DEVICE_ACTION_MAX, + _DEVICE_ACTION_INVALID = -1, +} DeviceAction; + +int device_new_aux(sd_device **ret); +int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); +int device_add_property_internal(sd_device *device, const char *key, const char *value); +int device_read_uevent_file(sd_device *device); +int device_read_db_aux(sd_device *device, bool force); + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify); +int device_set_ifindex(sd_device *device, const char *ifindex); +int device_set_devmode(sd_device *device, const char *devmode); +int device_set_devname(sd_device *device, const char *_devname); +int device_set_devtype(sd_device *device, const char *_devtype); +int device_set_devnum(sd_device *device, const char *major, const char *minor); +int device_set_subsystem(sd_device *device, const char *_subsystem); +int device_set_driver(sd_device *device, const char *_driver); +int device_set_usec_initialized(sd_device *device, const char *initialized); + +DeviceAction device_action_from_string(const char *s) _pure_; +const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c new file mode 100644 index 0000000000..2e60433246 --- /dev/null +++ b/src/libsystemd/sd-device/device-private.c @@ -0,0 +1,1117 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers <kay@vrfy.org> + Copyright 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <ctype.h> +#include <sys/types.h> +#include <net/if.h> + +#include "util.h" +#include "macro.h" +#include "refcnt.h" +#include "path-util.h" +#include "strxcpyx.h" +#include "fileio.h" +#include "hashmap.h" +#include "set.h" +#include "strv.h" +#include "mkdir.h" + +#include "sd-device.h" + +#include "device-util.h" +#include "device-internal.h" +#include "device-private.h" + +int device_add_property(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + + r = device_add_property_aux(device, key, value, false); + if (r < 0) + return r; + + if (key[0] != '.') { + r = device_add_property_aux(device, key, value, true); + if (r < 0) + return r; + } + + return 0; +} + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + return device_add_property_internal(device, key, value); +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + char *path; + int r; + + assert(device); + assert(value); + + switch (key) { + case 'S': + path = strjoina("/dev/", value); + r = device_add_devlink(device, path); + if (r < 0) + return r; + + break; + case 'L': + r = safe_atoi(value, &device->devlink_priority); + if (r < 0) + return r; + + break; + case 'E': + r = device_add_property_internal_from_string(device, value); + if (r < 0) + return r; + + break; + case 'G': + r = device_add_tag(device, value); + if (r < 0) + return r; + + break; + case 'W': + r = safe_atoi(value, &device->watch_handle); + if (r < 0) + return r; + + break; + case 'I': + r = device_set_usec_initialized(device, value); + if (r < 0) + return r; + + break; + default: + log_debug("device db: unknown key '%c'", key); + } + + return 0; +} + +void device_set_devlink_priority(sd_device *device, int priority) { + assert(device); + + device->devlink_priority = priority; +} + +void device_set_is_initialized(sd_device *device) { + assert(device); + + device->is_initialized = true; +} + +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { + char num[DECIMAL_STR_MAX(usec_t)]; + usec_t usec_initialized; + int r; + + assert(device); + + if (device_old && device_old->usec_initialized > 0) + usec_initialized = device_old->usec_initialized; + else + usec_initialized = now(CLOCK_MONOTONIC); + + r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); + if (r < 0) + return -errno; + + r = device_set_usec_initialized(device, num); + if (r < 0) + return r; + + return 0; +} + +static int device_read_db(sd_device *device) { + _cleanup_free_ char *db = NULL; + char *path; + const char *id, *value; + char key; + size_t db_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->db_loaded || device->sealed) + return 0; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = read_full_file(path, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + else { + log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r)); + return r; + } + } + + /* devices with a database entry are initialized */ + device_set_is_initialized(device); + + for (i = 0; i < db_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_debug("sd-device: ignoring invalid db entry with key '%c'", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_debug("sd-device: failed to handle db entry '%c:%s': %s", key, value, strerror(-r)); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing db"); + } + } + + device->db_loaded = true; + + return 0; +} + +uint64_t device_get_properties_generation(sd_device *device) { + assert(device); + + return device->properties_generation; +} + +uint64_t device_get_tags_generation(sd_device *device) { + assert(device); + + return device->tags_generation; +} + +uint64_t device_get_devlinks_generation(sd_device *device) { + assert(device); + + return device->devlinks_generation; +} + +int device_get_devnode_mode(sd_device *device, mode_t *mode) { + int r; + + assert(device); + assert(mode); + + r = device_read_db(device); + if (r < 0) + return r; + + *mode = device->devmode; + + return 0; +} + +int device_get_devnode_uid(sd_device *device, uid_t *uid) { + int r; + + assert(device); + assert(uid); + + r = device_read_db(device); + if (r < 0) + return r; + + *uid = device->devuid; + + return 0; +} + +static int device_set_devuid(sd_device *device, const char *uid) { + unsigned u; + int r; + + assert(device); + assert(uid); + + r = safe_atou(uid, &u); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVUID", uid); + if (r < 0) + return r; + + device->devuid = u; + + return 0; +} + +int device_get_devnode_gid(sd_device *device, gid_t *gid) { + int r; + + assert(device); + assert(gid); + + r = device_read_db(device); + if (r < 0) + return r; + + *gid = device->devgid; + + return 0; +} + +static int device_set_devgid(sd_device *device, const char *gid) { + unsigned g; + int r; + + assert(device); + assert(gid); + + r = safe_atou(gid, &g); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVGID", gid); + if (r < 0) + return r; + + device->devgid = g; + + return 0; +} + +static int device_amend(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + assert(value); + + if (streq(key, "DEVPATH")) { + char *path; + + path = strjoina("/sys", value); + + /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ + r = device_set_syspath(device, path, false); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path); + } else if (streq(key, "SUBSYSTEM")) { + r = device_set_subsystem(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value); + } else if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value); + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value); + } else if (streq(key, "USEC_INITIALIZED")) { + r = device_set_usec_initialized(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value); + } else if (streq(key, "DRIVER")) { + r = device_set_driver(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value); + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value); + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value); + } else if (streq(key, "DEVUID")) { + r = device_set_devuid(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value); + } else if (streq(key, "DEVGID")) { + r = device_set_devgid(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value); + } else if (streq(key, "DEVLINKS")) { + const char *word, *state; + size_t l; + + FOREACH_WORD(word, l, value, state) { + char devlink[l + 1]; + + strncpy(devlink, word, l); + devlink[l] = '\0'; + + r = device_add_devlink(device, devlink); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink); + } + } else if (streq(key, "TAGS")) { + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { + char tag[l + 1]; + + (void)strncpy(tag, word, l); + tag[l] = '\0'; + + r = device_add_tag(device, tag); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag); + } + } else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value); + } + + return 0; +} + +static const char* const device_action_table[_DEVICE_ACTION_MAX] = { + [DEVICE_ACTION_ADD] = "add", + [DEVICE_ACTION_REMOVE] = "remove", + [DEVICE_ACTION_CHANGE] = "change", + [DEVICE_ACTION_MOVE] = "move", + [DEVICE_ACTION_ONLINE] = "online", + [DEVICE_ACTION_OFFLINE] = "offline", +}; + +DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); + +static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, + DeviceAction *_action) { + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum = 0; + const char *major = NULL, *minor = NULL; + char *value; + int r; + + assert(device); + assert(key); + assert(_major); + assert(_minor); + assert(_seqnum); + assert(_action); + + value = strchr(key, '='); + if (!value) { + log_debug("sd-device: not a key-value pair: '%s'", key); + return -EINVAL; + } + + *value = '\0'; + + value++; + + if (streq(key, "MAJOR")) + major = value; + else if (streq(key, "MINOR")) + minor = value; + else { + if (streq(key, "ACTION")) { + action = device_action_from_string(value); + if (action == _DEVICE_ACTION_INVALID) + return -EINVAL; + } else if (streq(key, "SEQNUM")) { + r = safe_atou64(value, &seqnum); + if (r < 0) + return r; + else if (seqnum == 0) + /* kernel only sends seqnum > 0 */ + return -EINVAL; + } + + r = device_amend(device, key, value); + if (r < 0) + return r; + } + + if (major != 0) + *_major = major; + + if (minor != 0) + *_minor = minor; + + if (action != _DEVICE_ACTION_INVALID) + *_action = action; + + if (seqnum > 0) + *_seqnum = seqnum; + + return 0; +} + +void device_seal(sd_device *device) { + assert(device); + + device->sealed = true; +} + +static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { + assert(device); + + if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { + log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum"); + return -EINVAL; + } + + device->sealed = true; + + return 0; +} + +int device_new_from_strv(sd_device **ret, char **strv) { + _cleanup_device_unref_ sd_device *device = NULL; + char **key; + const char *major = NULL, *minor = NULL; + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum; + int r; + + assert(ret); + assert(strv); + + r = device_new_aux(&device); + if (r < 0) + return r; + + STRV_FOREACH(key, strv) { + r = device_append(device, *key, &major, &minor, &seqnum, &action); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device, action, seqnum); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { + _cleanup_device_unref_ sd_device *device = NULL; + const char *major = NULL, *minor = NULL; + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum; + unsigned i = 0; + int r; + + assert(ret); + assert(nulstr); + assert(len); + + r = device_new_aux(&device); + if (r < 0) + return r; + + while (i < len) { + char *key; + const char *end; + + key = (char*)&nulstr[i]; + end = memchr(key, '\0', len - i); + if (!end) { + log_debug("sd-device: failed to parse nulstr"); + return -EINVAL; + } + i += end - key + 1; + + r = device_append(device, key, &major, &minor, &seqnum, &action); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device, action, seqnum); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +static int device_update_properties_bufs(sd_device *device) { + const char *val, *prop; + _cleanup_free_ char **buf_strv = NULL; + _cleanup_free_ uint8_t *buf_nulstr = NULL; + size_t allocated_nulstr = 0; + size_t nulstr_len = 0, num = 0, i = 0; + + assert(device); + + if (!device->properties_buf_outdated) + return 0; + + FOREACH_DEVICE_PROPERTY(device, prop, val) { + size_t len = 0; + + len = strlen(prop) + 1 + strlen(val); + + buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2); + if (!buf_nulstr) + return -ENOMEM; + + strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); + nulstr_len += len + 1; + ++num; + } + + /* build buf_strv from buf_nulstr */ + buf_strv = new0(char *, num + 1); + if (!buf_strv) + return -ENOMEM; + + NULSTR_FOREACH(val, (char*) buf_nulstr) { + buf_strv[i] = (char *) val; + assert(i < num); + i++; + } + + free(device->properties_nulstr); + device->properties_nulstr = buf_nulstr; + buf_nulstr = NULL; + device->properties_nulstr_len = nulstr_len; + free(device->properties_strv); + device->properties_strv = buf_strv; + buf_strv = NULL; + + device->properties_buf_outdated = false; + + return 0; +} + +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) { + int r; + + assert(device); + assert(nulstr); + assert(len); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + *nulstr = device->properties_nulstr; + *len = device->properties_nulstr_len; + + return 0; +} + +int device_get_properties_strv(sd_device *device, char ***strv) { + int r; + + assert(device); + assert(strv); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + *strv = device->properties_strv; + + return 0; +} + +int device_get_devlink_priority(sd_device *device, int *priority) { + int r; + + assert(device); + assert(priority); + + r = device_read_db(device); + if (r < 0) + return r; + + *priority = device->devlink_priority; + + return 0; +} + +int device_get_watch_handle(sd_device *device, int *handle) { + int r; + + assert(device); + assert(handle); + + r = device_read_db(device); + if (r < 0) + return r; + + *handle = device->watch_handle; + + return 0; +} + +void device_set_watch_handle(sd_device *device, int handle) { + assert(device); + + device->watch_handle = handle; +} + +int device_rename(sd_device *device, const char *name) { + _cleanup_free_ char *dirname = NULL; + char *new_syspath; + const char *interface; + int r; + + assert(device); + assert(name); + + dirname = dirname_malloc(device->syspath); + if (!dirname) + return -ENOMEM; + + new_syspath = strjoina(dirname, "/", name); + + /* the user must trust that the new name is correct */ + r = device_set_syspath(device, new_syspath, false); + if (r < 0) + return r; + + r = sd_device_get_property_value(device, "INTERFACE", &interface); + if (r >= 0) { + r = device_add_property_internal(device, "INTERFACE", name); + if (r < 0) + return r; + + /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ + r = device_add_property_internal(device, "INTERFACE_OLD", interface); + if (r < 0) + return r; + } else if (r != -ENOENT) + return r; + + return 0; +} + +int device_shallow_clone(sd_device *old_device, sd_device **new_device) { + _cleanup_device_unref_ sd_device *ret = NULL; + int r; + + assert(old_device); + assert(new_device); + + r = device_new_aux(&ret); + if (r < 0) + return r; + + r = device_set_syspath(ret, old_device->syspath, false); + if (r < 0) + return r; + + r = device_set_subsystem(ret, old_device->subsystem); + if (r < 0) + return r; + + ret->devnum = old_device->devnum; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_clone_with_db(sd_device *old_device, sd_device **new_device) { + _cleanup_device_unref_ sd_device *ret = NULL; + int r; + + assert(old_device); + assert(new_device); + + r = device_shallow_clone(old_device, &ret); + if (r < 0) + return r; + + r = device_read_db(ret); + if (r < 0) + return r; + + ret->sealed = true; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { + _cleanup_device_unref_ sd_device *ret = NULL; + int r; + + assert(new_device); + assert(syspath); + assert(action); + + r = sd_device_new_from_syspath(&ret, syspath); + if (r < 0) + return r; + + r = device_read_uevent_file(ret); + if (r < 0) + return r; + + r = device_add_property_internal(ret, "ACTION", action); + if (r < 0) + return r; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_copy_properties(sd_device *device_dst, sd_device *device_src) { + const char *property, *value; + int r; + + assert(device_dst); + assert(device_src); + + FOREACH_DEVICE_PROPERTY(device_src, property, value) { + r = device_add_property(device_dst, property, value); + if (r < 0) + return r; + } + + return 0; +} + +void device_cleanup_tags(sd_device *device) { + assert(device); + + set_free_free(device->tags); + device->tags = NULL; + device->property_tags_outdated = true; + device->tags_generation ++; +} + +void device_cleanup_devlinks(sd_device *device) { + assert(device); + + set_free_free(device->devlinks); + device->devlinks = NULL; + device->property_devlinks_outdated = true; + device->devlinks_generation ++; +} + +void device_remove_tag(sd_device *device, const char *tag) { + assert(device); + assert(tag); + + free(set_remove(device->tags, tag)); + device->property_tags_outdated = true; + device->tags_generation ++; +} + +static int device_tag(sd_device *device, const char *tag, bool add) { + const char *id; + char *path; + int r; + + assert(device); + assert(tag); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/tags/", tag, "/", id); + + if (add) { + r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); + if (r < 0) + return r; + } else { + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + } + + return 0; +} + +int device_tag_index(sd_device *device, sd_device *device_old, bool add) { + const char *tag; + int r = 0, k; + + if (add && device_old) { + /* delete possible left-over tags */ + FOREACH_DEVICE_TAG(device_old, tag) { + if (!sd_device_has_tag(device, tag)) { + k = device_tag(device_old, tag, false); + if (r >= 0 && k < 0) + r = k; + } + } + } + + FOREACH_DEVICE_TAG(device, tag) { + k = device_tag(device, tag, add); + if (r >= 0 && k < 0) + r = k; + } + + return r; +} + +static bool device_has_info(sd_device *device) { + assert(device); + + if (!set_isempty(device->devlinks)) + return true; + + if (device->devlink_priority != 0) + return true; + + if (!ordered_hashmap_isempty(device->properties_db)) + return true; + + if (!set_isempty(device->tags)) + return true; + + if (device->watch_handle >= 0) + return true; + + return false; +} + +void device_set_db_persist(sd_device *device) { + assert(device); + + device->db_persist = true; +} + +int device_update_db(sd_device *device) { + const char *id; + char *path; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *path_tmp = NULL; + bool has_info; + int r; + + assert(device); + + has_info = device_has_info(device); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + /* do not store anything for otherwise empty devices */ + if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) { + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; + } + + /* write a database file */ + r = mkdir_parents(path, 0755); + if (r < 0) + return r; + + r = fopen_temporary(path, &f, &path_tmp); + if (r < 0) + return r; + + /* + * set 'sticky' bit to indicate that we should not clean the + * database when we transition from initramfs to the real root + */ + if (device->db_persist) { + r = fchmod(fileno(f), 01644); + if (r < 0) { + r = -errno; + goto fail; + } + } else { + r = fchmod(fileno(f), 0644); + if (r < 0) { + r = -errno; + goto fail; + } + } + + if (has_info) { + const char *property, *value, *tag; + Iterator i; + + if (major(device->devnum) > 0) { + const char *devlink; + + FOREACH_DEVICE_DEVLINK(device, devlink) + fprintf(f, "S:%s\n", devlink + strlen("/dev/")); + + if (device->devlink_priority != 0) + fprintf(f, "L:%i\n", device->devlink_priority); + + if (device->watch_handle >= 0) + fprintf(f, "W:%i\n", device->watch_handle); + } + + if (device->usec_initialized > 0) + fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); + + ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i) + fprintf(f, "E:%s=%s\n", property, value); + + FOREACH_DEVICE_TAG(device, tag) + fprintf(f, "G:%s\n", tag); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + r = rename(path_tmp, path); + if (r < 0) { + r = -errno; + goto fail; + } + + log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", + path, device->devpath); + + return 0; + +fail: + log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", + path, device->devpath); + unlink(path); + unlink(path_tmp); + + return r; +} + +int device_delete_db(sd_device *device) { + const char *id; + char *path; + int r; + + assert(device); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; +} + +int device_read_db_force(sd_device *device) { + assert(device); + + return device_read_db_aux(device, true); +} diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h new file mode 100644 index 0000000000..49a7b66a2b --- /dev/null +++ b/src/libsystemd/sd-device/device-private.h @@ -0,0 +1,64 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len); +int device_new_from_strv(sd_device **ret, char **strv); + +int device_get_id_filename(sd_device *device, const char **ret); + +int device_get_devlink_priority(sd_device *device, int *priority); +int device_get_watch_handle(sd_device *device, int *handle); +int device_get_devnode_mode(sd_device *device, mode_t *mode); +int device_get_devnode_uid(sd_device *device, uid_t *uid); +int device_get_devnode_gid(sd_device *device, gid_t *gid); + +void device_seal(sd_device *device); +void device_set_is_initialized(sd_device *device); +void device_set_watch_handle(sd_device *device, int fd); +void device_set_db_persist(sd_device *device); +void device_set_devlink_priority(sd_device *device, int priority); +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); +int device_add_devlink(sd_device *device, const char *devlink); +int device_add_property(sd_device *device, const char *property, const char *value); +int device_add_tag(sd_device *device, const char *tag); +void device_remove_tag(sd_device *device, const char *tag); +void device_cleanup_tags(sd_device *device); +void device_cleanup_devlinks(sd_device *device); + +uint64_t device_get_properties_generation(sd_device *device); +uint64_t device_get_tags_generation(sd_device *device); +uint64_t device_get_devlinks_generation(sd_device *device); + +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len); +int device_get_properties_strv(sd_device *device, char ***strv); + +int device_rename(sd_device *device, const char *name); +int device_shallow_clone(sd_device *old_device, sd_device **new_device); +int device_clone_with_db(sd_device *old_device, sd_device **new_device); +int device_copy_properties(sd_device *device_dst, sd_device *device_src); +int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action); + +int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); +int device_update_db(sd_device *device); +int device_delete_db(sd_device *device); +int device_read_db_force(sd_device *device); diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h new file mode 100644 index 0000000000..9b05a2498d --- /dev/null +++ b/src/libsystemd/sd-device/device-util.h @@ -0,0 +1,60 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014-2015 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "util.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device*, sd_device_unref); +#define _cleanup_device_unref_ _cleanup_(sd_device_unrefp) + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device_enumerator*, sd_device_enumerator_unref); +#define _cleanup_device_enumerator_unref_ _cleanup_(sd_device_enumerator_unrefp) + +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ + for (key = sd_device_get_property_first(device, &(value)); \ + key; \ + key = sd_device_get_property_next(device, &(value))) + +#define FOREACH_DEVICE_TAG(device, tag) \ + for (tag = sd_device_get_tag_first(device); \ + tag; \ + tag = sd_device_get_tag_next(device)) + +#define FOREACH_DEVICE_SYSATTR(device, attr) \ + for (attr = sd_device_get_sysattr_first(device); \ + attr; \ + attr = sd_device_get_sysattr_next(device)) + +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ + for (devlink = sd_device_get_devlink_first(device); \ + devlink; \ + devlink = sd_device_get_devlink_next(device)) + +#define FOREACH_DEVICE(enumerator, device) \ + for (device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_device_next(enumerator)) + +#define FOREACH_SUBSYSTEM(enumerator, device) \ + for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_subsystem_next(enumerator)) diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c new file mode 100644 index 0000000000..8e63b9ef56 --- /dev/null +++ b/src/libsystemd/sd-device/sd-device.c @@ -0,0 +1,1839 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers <kay@vrfy.org> + Copyright 2014 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <ctype.h> +#include <sys/types.h> +#include <net/if.h> + +#include "util.h" +#include "macro.h" +#include "path-util.h" +#include "strxcpyx.h" +#include "fileio.h" +#include "hashmap.h" +#include "set.h" +#include "strv.h" + +#include "sd-device.h" + +#include "device-util.h" +#include "device-private.h" +#include "device-internal.h" + +int device_new_aux(sd_device **ret) { + _cleanup_device_unref_ sd_device *device = NULL; + + assert(ret); + + device = new0(sd_device, 1); + if (!device) + return -ENOMEM; + + device->n_ref = 1; + device->watch_handle = -1; + + *ret = device; + device = NULL; + + return 0; +} + +_public_ sd_device *sd_device_ref(sd_device *device) { + if (device) + assert_se(++ device->n_ref >= 2); + + return device; +} + +_public_ sd_device *sd_device_unref(sd_device *device) { + if (device && -- device->n_ref == 0) { + sd_device_unref(device->parent); + free(device->syspath); + free(device->sysname); + free(device->devtype); + free(device->devname); + free(device->subsystem); + free(device->driver); + free(device->id_filename); + free(device->properties_strv); + free(device->properties_nulstr); + + ordered_hashmap_free_free_free(device->properties); + ordered_hashmap_free_free_free(device->properties_db); + hashmap_free_free_free(device->sysattr_values); + set_free_free(device->sysattrs); + set_free_free(device->tags); + set_free_free(device->devlinks); + + free(device); + } + + return NULL; +} + +int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) { + OrderedHashmap **properties; + + assert(device); + assert(_key); + + if (db) + properties = &device->properties_db; + else + properties = &device->properties; + + if (_value) { + _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL; + int r; + + r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops); + if (r < 0) + return r; + + key = strdup(_key); + if (!key) + return -ENOMEM; + + value = strdup(_value); + if (!value) + return -ENOMEM; + + old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key); + + r = ordered_hashmap_replace(*properties, key, value); + if (r < 0) + return r; + + key = NULL; + value = NULL; + } else { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value = NULL; + + value = ordered_hashmap_remove2(*properties, _key, (void**) &key); + } + + if (!db) { + device->properties_generation ++; + device->properties_buf_outdated = true; + } + + return 0; +} + +int device_add_property_internal(sd_device *device, const char *key, const char *value) { + return device_add_property_aux(device, key, value, false); +} + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { + _cleanup_free_ char *syspath = NULL; + const char *devpath; + int r; + + assert(device); + assert(_syspath); + + /* must be a subdirectory of /sys */ + if (!path_startswith(_syspath, "/sys/")) { + log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath); + return -EINVAL; + } + + if (verify) { + r = readlink_and_canonicalize(_syspath, &syspath); + if (r == -ENOENT) + /* the device does not exist (any more?) */ + return -ENODEV; + else if (r == -EINVAL) { + /* not a symlink */ + syspath = canonicalize_file_name(_syspath); + if (!syspath) { + if (errno == ENOENT) + /* the device does not exist (any more?) */ + return -ENODEV; + + log_debug("sd-device: could not canonicalize '%s': %m", _syspath); + return -errno; + } + } else if (r < 0) { + log_debug("sd-device: could not get target of '%s': %s", _syspath, strerror(-r)); + return r; + } + + if (path_startswith(syspath, "/sys/devices/")) { + char *path; + + /* all 'devices' require an 'uevent' file */ + path = strjoina(syspath, "/uevent"); + r = access(path, F_OK); + if (r < 0) { + if (errno == ENOENT) + /* this is not a valid device */ + return -ENODEV; + + log_debug("sd-device: %s does not have an uevent file: %m", syspath); + return -errno; + } + } else { + /* everything else just just needs to be a directory */ + if (!is_dir(syspath, false)) + return -ENODEV; + } + } else { + syspath = strdup(_syspath); + if (!syspath) + return -ENOMEM; + } + + devpath = syspath + strlen("/sys"); + + r = device_add_property_internal(device, "DEVPATH", devpath); + if (r < 0) + return r; + + free(device->syspath); + device->syspath = syspath; + syspath = NULL; + + device->devpath = devpath; + + return 0; +} + +_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { + _cleanup_device_unref_ sd_device *device = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(syspath, -EINVAL); + + r = device_new_aux(&device); + if (r < 0) + return r; + + r = device_set_syspath(device, syspath, true); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) { + char *syspath; + char id[DECIMAL_STR_MAX(unsigned) * 2 + 1]; + + assert_return(ret, -EINVAL); + assert_return(type == 'b' || type == 'c', -EINVAL); + + /* use /sys/dev/{block,char}/<maj>:<min> link */ + snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); + + syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id); + + return sd_device_new_from_syspath(ret, syspath); +} + +_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) { + char *syspath; + + assert_return(ret, -EINVAL); + assert_return(subsystem, -EINVAL); + assert_return(sysname, -EINVAL); + + if (streq(subsystem, "subsystem")) { + syspath = strjoina("/sys/subsystem/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/class/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else if (streq(subsystem, "module")) { + syspath = strjoina("/sys/module/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else if (streq(subsystem, "drivers")) { + char subsys[PATH_MAX]; + char *driver; + + strscpy(subsys, sizeof(subsys), sysname); + driver = strchr(subsys, ':'); + if (driver) { + driver[0] = '\0'; + driver++; + + syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else + return -EINVAL; + } else { + syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", subsystem, "/devices/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/class/", subsystem, "/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } + + return -ENODEV; +} + +int device_set_devtype(sd_device *device, const char *_devtype) { + _cleanup_free_ char *devtype = NULL; + int r; + + assert(device); + assert(_devtype); + + devtype = strdup(_devtype); + if (!devtype) + return -ENOMEM; + + r = device_add_property_internal(device, "DEVTYPE", devtype); + if (r < 0) + return r; + + free(device->devtype); + device->devtype = devtype; + devtype = NULL; + + return 0; +} + +int device_set_ifindex(sd_device *device, const char *_ifindex) { + int ifindex, r; + + assert(device); + assert(_ifindex); + + r = safe_atoi(_ifindex, &ifindex); + if (r < 0) + return r; + + if (ifindex <= 0) + return -EINVAL; + + r = device_add_property_internal(device, "IFINDEX", _ifindex); + if (r < 0) + return r; + + device->ifindex = ifindex; + + return 0; +} + +int device_set_devname(sd_device *device, const char *_devname) { + _cleanup_free_ char *devname = NULL; + int r; + + assert(device); + assert(_devname); + + if (_devname[0] != '/') { + r = asprintf(&devname, "/dev/%s", _devname); + if (r < 0) + return -ENOMEM; + } else { + devname = strdup(_devname); + if (!devname) + return -ENOMEM; + } + + r = device_add_property_internal(device, "DEVNAME", devname); + if (r < 0) + return r; + + free(device->devname); + device->devname = devname; + devname = NULL; + + return 0; +} + +int device_set_devmode(sd_device *device, const char *_devmode) { + unsigned devmode; + int r; + + assert(device); + assert(_devmode); + + r = safe_atou(_devmode, &devmode); + if (r < 0) + return r; + + if (devmode > 07777) + return -EINVAL; + + r = device_add_property_internal(device, "DEVMODE", _devmode); + if (r < 0) + return r; + + device->devmode = devmode; + + return 0; +} + +int device_set_devnum(sd_device *device, const char *major, const char *minor) { + unsigned maj = 0, min = 0; + int r; + + assert(device); + assert(major); + + r = safe_atou(major, &maj); + if (r < 0) + return r; + if (!maj) + return 0; + + if (minor) { + r = safe_atou(minor, &min); + if (r < 0) + return r; + } + + r = device_add_property_internal(device, "MAJOR", major); + if (r < 0) + return r; + + if (minor) { + r = device_add_property_internal(device, "MINOR", minor); + if (r < 0) + return r; + } + + device->devnum = makedev(maj, min); + + return 0; +} + +static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) { + int r; + + assert(device); + assert(key); + assert(value); + assert(major); + assert(minor); + + if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return r; + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return r; + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return r; + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return r; + } else if (streq(key, "MAJOR")) + *major = value; + else if (streq(key, "MINOR")) + *minor = value; + else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return r; + } + + return 0; +} + +int device_read_uevent_file(sd_device *device) { + _cleanup_free_ char *uevent = NULL; + const char *syspath, *key, *value, *major = NULL, *minor = NULL; + char *path; + size_t uevent_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->uevent_loaded || device->sealed) + return 0; + + device->uevent_loaded = true; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/uevent"); + + r = read_full_file(path, &uevent, &uevent_len); + if (r == -EACCES) + /* empty uevent files may be write-only */ + return 0; + else if (r == -ENOENT) + /* some devices may not have uevent files, see set_syspath() */ + return 0; + else if (r < 0) { + log_debug("sd-device: failed to read uevent file '%s': %s", path, strerror(-r)); + return r; + } + + for (i = 0; i < uevent_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, uevent[i])) { + key = &uevent[i]; + + state = KEY; + } + + break; + case KEY: + if (uevent[i] == '=') { + uevent[i] = '\0'; + + state = PRE_VALUE; + } else if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + log_debug("sd-device: ignoring invalid uevent line '%s'", key); + + state = PRE_KEY; + } + + break; + case PRE_VALUE: + value = &uevent[i]; + + state = VALUE; + + break; + case VALUE: + if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + + r = handle_uevent_line(device, key, value, &major, &minor); + if (r < 0) + log_debug("sd-device: failed to handle uevent entry '%s=%s': %s", key, value, strerror(-r)); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing uevent file"); + } + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + log_debug("sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %s", major, minor, path, strerror(-r)); + } + + return 0; +} + +_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { + int r; + + assert_return(device, -EINVAL); + assert_return(ifindex, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *ifindex = device->ifindex; + + return 0; +} + +_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { + int r; + + assert_return(ret, -EINVAL); + assert_return(id, -EINVAL); + + switch (id[0]) { + case 'b': + case 'c': + { + char type; + int maj, min; + + r = sscanf(id, "%c%i:%i", &type, &maj, &min); + if (r != 3) + return -EINVAL; + + return sd_device_new_from_devnum(ret, type, makedev(maj, min)); + } + case 'n': + { + _cleanup_device_unref_ sd_device *device = NULL; + _cleanup_close_ int sk = -1; + struct ifreq ifr = {}; + int ifindex; + + r = safe_atoi(&id[1], &ifr.ifr_ifindex); + if (r < 0) + return r; + else if (ifr.ifr_ifindex <= 0) + return -EINVAL; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) + return -errno; + + r = ioctl(sk, SIOCGIFNAME, &ifr); + if (r < 0) + return -errno; + + r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name); + if (r < 0) + return r; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) + return r; + + /* this is racey, so we might end up with the wrong device */ + if (ifr.ifr_ifindex != ifindex) + return -ENODEV; + + *ret = device; + device = NULL; + + return 0; + } + case '+': + { + char subsys[PATH_MAX]; + char *sysname; + + (void)strscpy(subsys, sizeof(subsys), id + 1); + sysname = strchr(subsys, ':'); + if (!sysname) + return -EINVAL; + + sysname[0] = '\0'; + sysname ++; + + return sd_device_new_from_subsystem_sysname(ret, subsys, sysname); + } + default: + return -EINVAL; + } +} + +_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + assert(path_startswith(device->syspath, "/sys/")); + + *ret = device->syspath; + + return 0; +} + +static int device_new_from_child(sd_device **ret, sd_device *child) { + _cleanup_free_ char *path = NULL; + const char *subdir, *syspath; + int r; + + assert(ret); + assert(child); + + r = sd_device_get_syspath(child, &syspath); + if (r < 0) + return r; + + path = strdup(syspath); + if (!path) + return -ENOMEM; + subdir = path + strlen("/sys"); + + for (;;) { + char *pos; + + pos = strrchr(subdir, '/'); + if (!pos || pos < subdir + 2) + break; + + *pos = '\0'; + + r = sd_device_new_from_syspath(ret, path); + if (r < 0) + continue; + + return 0; + } + + return -ENODEV; +} + +_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { + + assert_return(ret, -EINVAL); + assert_return(child, -EINVAL); + + if (!child->parent_set) { + child->parent_set = true; + + (void)device_new_from_child(&child->parent, child); + } + + if (!child->parent) + return -ENOENT; + + *ret = child->parent; + + return 0; +} + +int device_set_subsystem(sd_device *device, const char *_subsystem) { + _cleanup_free_ char *subsystem = NULL; + int r; + + assert(device); + assert(_subsystem); + + subsystem = strdup(_subsystem); + if (!subsystem) + return -ENOMEM; + + r = device_add_property_internal(device, "SUBSYSTEM", subsystem); + if (r < 0) + return r; + + free(device->subsystem); + device->subsystem = subsystem; + subsystem = NULL; + + device->subsystem_set = true; + + return 0; +} + +_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { + assert_return(ret, -EINVAL); + assert_return(device, -EINVAL); + + if (!device->subsystem_set) { + _cleanup_free_ char *subsystem = NULL; + const char *syspath; + char *path; + int r; + + /* read 'subsystem' link */ + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/subsystem"); + r = readlink_value(path, &subsystem); + if (r >= 0) + r = device_set_subsystem(device, subsystem); + /* use implicit names */ + else if (path_startswith(device->devpath, "/module/")) + r = device_set_subsystem(device, "module"); + else if (strstr(device->devpath, "/drivers/")) + r = device_set_subsystem(device, "drivers"); + else if (path_startswith(device->devpath, "/subsystem/") || + path_startswith(device->devpath, "/class/") || + path_startswith(device->devpath, "/bus/")) + r = device_set_subsystem(device, "subsystem"); + if (r < 0 && r != -ENOENT) + return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath); + + device->subsystem_set = true; + } + + *ret = device->subsystem; + + return 0; +} + +_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { + int r; + + assert(devtype); + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *devtype = device->devtype; + + return 0; +} + +_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) { + sd_device *parent = NULL; + int r; + + assert_return(child, -EINVAL); + assert_return(subsystem, -EINVAL); + + r = sd_device_get_parent(child, &parent); + while (r >= 0) { + const char *parent_subsystem = NULL; + const char *parent_devtype = NULL; + + (void)sd_device_get_subsystem(parent, &parent_subsystem); + if (streq_ptr(parent_subsystem, subsystem)) { + if (!devtype) + break; + + (void)sd_device_get_devtype(parent, &parent_devtype); + if (streq_ptr(parent_devtype, devtype)) + break; + } + r = sd_device_get_parent(parent, &parent); + } + + if (r < 0) + return r; + + *ret = parent; + + return 0; +} + +_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { + int r; + + assert_return(device, -EINVAL); + assert_return(devnum, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *devnum = device->devnum; + + return 0; +} + +int device_set_driver(sd_device *device, const char *_driver) { + _cleanup_free_ char *driver = NULL; + int r; + + assert(device); + assert(_driver); + + driver = strdup(_driver); + if (!driver) + return -ENOMEM; + + r = device_add_property_internal(device, "DRIVER", driver); + if (r < 0) + return r; + + free(device->driver); + device->driver = driver; + driver = NULL; + + device->driver_set = true; + + return 0; +} + +_public_ int sd_device_get_driver(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->driver_set) { + _cleanup_free_ char *driver = NULL; + const char *syspath; + char *path; + int r; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/driver"); + r = readlink_value(path, &driver); + if (r >= 0) { + r = device_set_driver(device, driver); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); + } else if (r == -ENOENT) + device->driver_set = true; + else + return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); + } + + *ret = device->driver; + + return 0; +} + +_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { + assert_return(device, -EINVAL); + assert_return(devpath, -EINVAL); + + assert(device->devpath); + assert(device->devpath[0] == '/'); + + *devpath = device->devpath; + + return 0; +} + +_public_ int sd_device_get_devname(sd_device *device, const char **devname) { + int r; + + assert_return(device, -EINVAL); + assert_return(devname, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (!device->devname) + return -ENOENT; + + assert(path_startswith(device->devname, "/dev/")); + + *devname = device->devname; + + return 0; +} + +static int device_set_sysname(sd_device *device) { + _cleanup_free_ char *sysname = NULL; + const char *sysnum = NULL; + const char *pos; + size_t len = 0; + + pos = strrchr(device->devpath, '/'); + if (!pos) + return -EINVAL; + pos ++; + + /* devpath is not a root directory */ + if (*pos == '\0' || pos <= device->devpath) + return -EINVAL; + + sysname = strdup(pos); + if (!sysname) + return -ENOMEM; + + /* some devices have '!' in their name, change that to '/' */ + while (sysname[len] != '\0') { + if (sysname[len] == '!') + sysname[len] = '/'; + + len ++; + } + + /* trailing number */ + while (len > 0 && isdigit(sysname[--len])) + sysnum = &sysname[len]; + + if (len == 0) + sysnum = NULL; + + free(device->sysname); + device->sysname = sysname; + sysname = NULL; + + device->sysnum = sysnum; + + device->sysname_set = true; + + return 0; +} + +_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->sysname_set) { + r = device_set_sysname(device); + if (r < 0) + return r; + } + + *ret = device->sysname; + + return 0; +} + +_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->sysname_set) { + r = device_set_sysname(device); + if (r < 0) + return r; + } + + *ret = device->sysnum; + + return 0; +} + +static bool is_valid_tag(const char *tag) { + assert(tag); + + return !strchr(tag, ':') && !strchr(tag, ' '); +} + +int device_add_tag(sd_device *device, const char *tag) { + int r; + + assert(device); + assert(tag); + + if (!is_valid_tag(tag)) + return -EINVAL; + + r = set_ensure_allocated(&device->tags, &string_hash_ops); + if (r < 0) + return r; + + r = set_put_strdup(device->tags, tag); + if (r < 0) + return r; + + device->tags_generation ++; + device->property_tags_outdated = true; + + return 0; +} + +int device_add_devlink(sd_device *device, const char *devlink) { + int r; + + assert(device); + assert(devlink); + + r = set_ensure_allocated(&device->devlinks, &string_hash_ops); + if (r < 0) + return r; + + r = set_put_strdup(device->devlinks, devlink); + if (r < 0) + return r; + + device->devlinks_generation ++; + device->property_devlinks_outdated = true; + + return 0; +} + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + return device_add_property_internal(device, key, value); +} + +int device_set_usec_initialized(sd_device *device, const char *initialized) { + uint64_t usec_initialized; + int r; + + assert(device); + assert(initialized); + + r = safe_atou64(initialized, &usec_initialized); + if (r < 0) + return r; + + r = device_add_property_internal(device, "USEC_INITIALIZED", initialized); + if (r < 0) + return r; + + device->usec_initialized = usec_initialized; + + return 0; +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + char *path; + int r; + + assert(device); + assert(value); + + switch (key) { + case 'G': + r = device_add_tag(device, value); + if (r < 0) + return r; + + break; + case 'S': + path = strjoina("/dev/", value); + r = device_add_devlink(device, path); + if (r < 0) + return r; + + break; + case 'E': + r = device_add_property_internal_from_string(device, value); + if (r < 0) + return r; + + break; + case 'I': + r = device_set_usec_initialized(device, value); + if (r < 0) + return r; + + break; + case 'L': + r = safe_atoi(value, &device->devlink_priority); + if (r < 0) + return r; + + break; + case 'W': + r = safe_atoi(value, &device->watch_handle); + if (r < 0) + return r; + + break; + default: + log_debug("device db: unknown key '%c'", key); + } + + return 0; +} + +int device_get_id_filename(sd_device *device, const char **ret) { + assert(device); + assert(ret); + + if (!device->id_filename) { + _cleanup_free_ char *id = NULL; + const char *subsystem; + dev_t devnum; + int ifindex, r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r < 0) + return r; + + r = sd_device_get_devnum(device, &devnum); + if (r < 0) + return r; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) + return r; + + if (major(devnum) > 0) { + assert(subsystem); + + /* use dev_t -- b259:131072, c254:0 */ + r = asprintf(&id, "%c%u:%u", + streq(subsystem, "block") ? 'b' : 'c', + major(devnum), minor(devnum)); + if (r < 0) + return -ENOMEM; + } else if (ifindex > 0) { + /* use netdev ifindex -- n3 */ + r = asprintf(&id, "n%u", ifindex); + if (r < 0) + return -ENOMEM; + } else { + /* use $subsys:$sysname -- pci:0000:00:1f.2 + * sysname() has '!' translated, get it from devpath + */ + const char *sysname; + + sysname = basename(device->devpath); + if (!sysname) + return -EINVAL; + + if (!subsystem) + return -EINVAL; + + r = asprintf(&id, "+%s:%s", subsystem, sysname); + if (r < 0) + return -ENOMEM; + } + + device->id_filename = id; + id = NULL; + } + + *ret = device->id_filename; + + return 0; +} + +int device_read_db_aux(sd_device *device, bool force) { + _cleanup_free_ char *db = NULL; + char *path; + const char *id, *value; + char key; + size_t db_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + if (device->db_loaded || (!force && device->sealed)) + return 0; + + device->db_loaded = true; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = read_full_file(path, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + else { + log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r)); + return r; + } + } + + /* devices with a database entry are initialized */ + device->is_initialized = true; + + for (i = 0; i < db_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_debug("sd-device: ignoring invalid db entry with key '%c'", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_debug("sd-device: failed to handle db entry '%c:%s': %s", key, value, strerror(-r)); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing db"); + } + } + + return 0; +} + +static int device_read_db(sd_device *device) { + return device_read_db_aux(device, false); +} + +_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) { + int r; + + assert_return(device, -EINVAL); + assert_return(initialized, -EINVAL); + + r = device_read_db(device); + if (r < 0) + return r; + + *initialized = device->is_initialized; + + return 0; +} + +_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) { + usec_t now_ts; + int r; + + assert_return(device, -EINVAL); + assert_return(usec, -EINVAL); + + r = device_read_db(device); + if (r < 0) + return r; + + if (!device->is_initialized) + return -EBUSY; + + if (!device->usec_initialized) + return -ENODATA; + + now_ts = now(clock_boottime_or_monotonic()); + + if (now_ts < device->usec_initialized) + return -EIO; + + *usec = now_ts - device->usec_initialized; + + return 0; +} + +_public_ const char *sd_device_get_tag_first(sd_device *device) { + assert_return(device, NULL); + + (void) device_read_db(device); + + device->tags_iterator_generation = device->tags_generation; + device->tags_iterator = ITERATOR_FIRST; + + return set_iterate(device->tags, &device->tags_iterator); +} + +_public_ const char *sd_device_get_tag_next(sd_device *device) { + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->tags_iterator_generation != device->tags_generation) + return NULL; + + return set_iterate(device->tags, &device->tags_iterator); +} + +_public_ const char *sd_device_get_devlink_first(sd_device *device) { + assert_return(device, NULL); + + (void) device_read_db(device); + + device->devlinks_iterator_generation = device->devlinks_generation; + device->devlinks_iterator = ITERATOR_FIRST; + + return set_iterate(device->devlinks, &device->devlinks_iterator); +} + +_public_ const char *sd_device_get_devlink_next(sd_device *device) { + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->devlinks_iterator_generation != device->devlinks_generation) + return NULL; + + return set_iterate(device->devlinks, &device->devlinks_iterator); +} + +static int device_properties_prepare(sd_device *device) { + int r; + + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->property_devlinks_outdated) { + char *devlinks = NULL; + const char *devlink; + + devlink = sd_device_get_devlink_first(device); + if (devlink) + devlinks = strdupa(devlink); + + while ((devlink = sd_device_get_devlink_next(device))) + devlinks = strjoina(devlinks, " ", devlink); + + r = device_add_property_internal(device, "DEVLINKS", devlinks); + if (r < 0) + return r; + + device->property_devlinks_outdated = false; + } + + if (device->property_tags_outdated) { + char *tags = NULL; + const char *tag; + + tag = sd_device_get_tag_first(device); + if (tag) + tags = strjoina(":", tag); + + while ((tag = sd_device_get_tag_next(device))) + tags = strjoina(tags, ":", tag); + + tags = strjoina(tags, ":"); + + r = device_add_property_internal(device, "TAGS", tags); + if (r < 0) + return r; + + device->property_tags_outdated = false; + } + + return 0; +} + +_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) { + const char *key; + const char *value; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + device->properties_iterator_generation = device->properties_generation; + device->properties_iterator = ITERATOR_FIRST; + + value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key); + + if (_value) + *_value = value; + + return key; +} + +_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) { + const char *key; + const char *value; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + if (device->properties_iterator_generation != device->properties_generation) + return NULL; + + value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key); + + if (_value) + *_value = value; + + return key; +} + +static int device_sysattrs_read_all(sd_device *device) { + _cleanup_closedir_ DIR *dir = NULL; + const char *syspath; + struct dirent *dent; + int r; + + assert(device); + + if (device->sysattrs_read) + return 0; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + dir = opendir(syspath); + if (!dir) + return -errno; + + r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); + if (r < 0) + return r; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *path; + struct stat statbuf; + + /* only handle symlinks and regular files */ + if (dent->d_type != DT_LNK && dent->d_type != DT_REG) + continue; + + path = strjoina(syspath, "/", dent->d_name); + + if (lstat(path, &statbuf) != 0) + continue; + + if (!(statbuf.st_mode & S_IRUSR)) + continue; + + r = set_put_strdup(device->sysattrs, dent->d_name); + if (r < 0) + return r; + } + + device->sysattrs_read = true; + + return 0; +} + +_public_ const char *sd_device_get_sysattr_first(sd_device *device) { + int r; + + assert_return(device, NULL); + + if (!device->sysattrs_read) { + r = device_sysattrs_read_all(device); + if (r < 0) { + errno = -r; + return NULL; + } + } + + device->sysattrs_iterator = ITERATOR_FIRST; + + return set_iterate(device->sysattrs, &device->sysattrs_iterator); +} + +_public_ const char *sd_device_get_sysattr_next(sd_device *device) { + assert_return(device, NULL); + + if (!device->sysattrs_read) + return NULL; + + return set_iterate(device->sysattrs, &device->sysattrs_iterator); +} + +_public_ int sd_device_has_tag(sd_device *device, const char *tag) { + assert_return(device, -EINVAL); + assert_return(tag, -EINVAL); + + (void) device_read_db(device); + + return !!set_contains(device->tags, tag); +} + +_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) { + char *value; + int r; + + assert_return(device, -EINVAL); + assert_return(key, -EINVAL); + assert_return(_value, -EINVAL); + + r = device_properties_prepare(device); + if (r < 0) + return r; + + value = ordered_hashmap_get(device->properties, key); + if (!value) + return -ENOENT; + + *_value = value; + + return 0; +} + +/* replaces the value if it already exists */ +static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value_old = NULL; + int r; + + assert(device); + assert(_key); + + r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops); + if (r < 0) + return r; + + value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key); + if (!key) { + key = strdup(_key); + if (!key) + return -ENOMEM; + } + + r = hashmap_put(device->sysattr_values, key, value); + if (r < 0) + return r; + + key = NULL; + + return 0; +} + +static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) { + const char *key = NULL, *value; + + assert(device); + assert(_key); + + value = hashmap_get2(device->sysattr_values, _key, (void **) &key); + if (!key) + return -ENOENT; + + if (_value) + *_value = value; + + return 0; +} + +/* We cache all sysattr lookups. If an attribute does not exist, it is stored + * with a NULL value in the cache, otherwise the returned string is stored */ +_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) { + _cleanup_free_ char *value = NULL; + const char *syspath, *cached_value = NULL; + char *path; + struct stat statbuf; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + /* look for possibly already cached result */ + r = device_get_sysattr_value(device, sysattr, &cached_value); + if (r != -ENOENT) { + if (r < 0) + return r; + + if (!cached_value) + /* we looked up the sysattr before and it did not exist */ + return -ENOENT; + + if (_value) + *_value = cached_value; + + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/", sysattr); + r = lstat(path, &statbuf); + if (r < 0) { + /* remember that we could not access the sysattr */ + r = device_add_sysattr_value(device, sysattr, NULL); + if (r < 0) + return r; + + return -ENOENT; + } else if (S_ISLNK(statbuf.st_mode)) { + /* Some core links return only the last element of the target path, + * these are just values, the paths should not be exposed. */ + if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { + r = readlink_value(path, &value); + if (r < 0) + return r; + } else + return -EINVAL; + } else if (S_ISDIR(statbuf.st_mode)) { + /* skip directories */ + return -EINVAL; + } else if (!(statbuf.st_mode & S_IRUSR)) { + /* skip non-readable files */ + return -EPERM; + } else { + size_t size; + + /* read attribute value */ + r = read_full_file(path, &value, &size); + if (r < 0) + return r; + + /* drop trailing newlines */ + while (size > 0 && value[--size] == '\n') + value[size] = '\0'; + } + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + *_value = value; + value = NULL; + + return 0; +} + +static void device_remove_sysattr_value(sd_device *device, const char *_key) { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value = NULL; + + assert(device); + assert(_key); + + value = hashmap_remove2(device->sysattr_values, _key, (void **) &key); + + return; +} + +/* set the attribute and save it in the cache. If a NULL value is passed the + * attribute is cleared from the cache */ +_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *value = NULL; + const char *syspath; + char *path; + struct stat statbuf; + size_t value_len = 0; + ssize_t size; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + if (!_value) { + device_remove_sysattr_value(device, sysattr); + + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/", sysattr); + r = lstat(path, &statbuf); + if (r < 0) { + value = strdup(""); + if (!value) + return -ENOMEM; + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + return -ENXIO; + } + + if (S_ISLNK(statbuf.st_mode)) + return -EINVAL; + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + return -EISDIR; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + return -EACCES; + + value_len = strlen(_value); + + /* drop trailing newlines */ + while (value_len > 0 && _value[value_len - 1] == '\n') + _value[--value_len] = '\0'; + + /* value length is limited to 4k */ + if (value_len > 4096) + return -EINVAL; + + fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) + return -errno; + + value = strdup(_value); + if (!value) + return -ENOMEM; + + size = write(fd, value, value_len); + if (size < 0) + return -errno; + + if ((size_t)size != value_len) + return -EIO; + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + value = NULL; + + return 0; +} diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 25089a0335..cc8bc50c04 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -22,7 +22,6 @@ #include <sys/epoll.h> #include <sys/timerfd.h> #include <sys/wait.h> -#include <pthread.h> #include "sd-id128.h" #include "sd-daemon.h" @@ -34,10 +33,10 @@ #include "missing.h" #include "set.h" #include "list.h" +#include "signal-util.h" #include "sd-event.h" -#define EPOLL_QUEUE_MAX 512U #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) typedef enum EventSourceType { @@ -463,7 +462,7 @@ _public_ sd_event* sd_event_unref(sd_event *e) { static bool event_pid_changed(sd_event *e) { assert(e); - /* We don't support people creating am event loop and keeping + /* We don't support people creating an event loop and keeping * it around over a fork(). Let's complain. */ return e->original_pid != getpid(); @@ -921,7 +920,7 @@ _public_ int sd_event_add_time( callback = time_exit_callback; type = clock_to_event_source_type(clock); - assert_return(type >= 0, -ENOTSUP); + assert_return(type >= 0, -EOPNOTSUPP); d = event_get_clock_data(e, type); assert(d); @@ -2236,7 +2235,7 @@ static int dispatch_exit(sd_event *e) { r = source_dispatch(p); - e->state = SD_EVENT_PASSIVE; + e->state = SD_EVENT_INITIAL; sd_event_unref(e); return r; @@ -2305,7 +2304,7 @@ _public_ int sd_event_prepare(sd_event *e) { assert_return(e, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); if (e->exit_requested) goto pending; @@ -2339,15 +2338,15 @@ _public_ int sd_event_prepare(sd_event *e) { if (event_next_pending(e) || e->need_process_child) goto pending; - e->state = SD_EVENT_PREPARED; + e->state = SD_EVENT_ARMED; return 0; pending: - e->state = SD_EVENT_PREPARED; + e->state = SD_EVENT_ARMED; r = sd_event_wait(e, 0); if (r == 0) - e->state = SD_EVENT_PREPARED; + e->state = SD_EVENT_ARMED; return r; } @@ -2360,14 +2359,14 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { assert_return(e, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_PREPARED, -EBUSY); + assert_return(e->state == SD_EVENT_ARMED, -EBUSY); if (e->exit_requested) { e->state = SD_EVENT_PENDING; return 1; } - ev_queue_max = CLAMP(e->n_sources, 1U, EPOLL_QUEUE_MAX); + ev_queue_max = MAX(e->n_sources, 1u); ev_queue = newa(struct epoll_event, ev_queue_max); m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max, @@ -2448,7 +2447,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { r = 0; finish: - e->state = SD_EVENT_PASSIVE; + e->state = SD_EVENT_INITIAL; return r; } @@ -2471,14 +2470,14 @@ _public_ int sd_event_dispatch(sd_event *e) { e->state = SD_EVENT_RUNNING; r = source_dispatch(p); - e->state = SD_EVENT_PASSIVE; + e->state = SD_EVENT_INITIAL; sd_event_unref(e); return r; } - e->state = SD_EVENT_PASSIVE; + e->state = SD_EVENT_INITIAL; return 1; } @@ -2489,19 +2488,23 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { assert_return(e, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); r = sd_event_prepare(e); - if (r > 0) - return sd_event_dispatch(e); - else if (r < 0) - return r; + if (r == 0) + /* There was nothing? Then wait... */ + r = sd_event_wait(e, timeout); - r = sd_event_wait(e, timeout); - if (r > 0) - return sd_event_dispatch(e); - else - return r; + if (r > 0) { + /* There's something now, then let's dispatch it */ + r = sd_event_dispatch(e); + if (r < 0) + return r; + + return 1; + } + + return r; } _public_ int sd_event_loop(sd_event *e) { @@ -2509,7 +2512,7 @@ _public_ int sd_event_loop(sd_event *e) { assert_return(e, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); sd_event_ref(e); diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index 721700be7b..94e98e0077 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -23,6 +23,7 @@ #include "log.h" #include "util.h" #include "macro.h" +#include "signal-util.h" static int prepare_handler(sd_event_source *s, void *userdata) { log_info("preparing %c", PTR_TO_INT(userdata)); diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c index 61c7b446b3..2a0e00f7d2 100644 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ b/src/libsystemd/sd-hwdb/sd-hwdb.c @@ -23,10 +23,8 @@ #include <errno.h> #include <string.h> #include <inttypes.h> -#include <ctype.h> #include <stdlib.h> #include <fnmatch.h> -#include <getopt.h> #include <sys/mman.h> #include "sd-hwdb.h" @@ -319,7 +317,7 @@ _public_ int sd_hwdb_new(sd_hwdb **ret) { if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 || (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) { log_debug("error recognizing the format of %s", hwdb_bin_path); - return -EINVAL;; + return -EINVAL; } log_debug("=== trie on-disk ==="); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index c876f6e381..46f2181ea8 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -26,6 +26,7 @@ #include "util.h" #include "macro.h" #include "sd-id128.h" +#include "random-util.h" _public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) { unsigned n; @@ -108,9 +109,9 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { static thread_local bool saved_machine_id_valid = false; _cleanup_close_ int fd = -1; char buf[33]; - ssize_t k; unsigned j; sd_id128_t t; + int r; assert_return(ret, -EINVAL); @@ -123,13 +124,9 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { if (fd < 0) return -errno; - k = loop_read(fd, buf, 33, false); - if (k < 0) - return (int) k; - - if (k != 33) - return -EIO; - + r = loop_read_exact(fd, buf, 33, false); + if (r < 0) + return r; if (buf[32] !='\n') return -EIO; @@ -157,10 +154,10 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) { static thread_local bool saved_boot_id_valid = false; _cleanup_close_ int fd = -1; char buf[36]; - ssize_t k; unsigned j; sd_id128_t t; char *p; + int r; assert_return(ret, -EINVAL); @@ -173,22 +170,19 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) { if (fd < 0) return -errno; - k = loop_read(fd, buf, 36, false); - if (k < 0) - return (int) k; - - if (k != 36) - return -EIO; + r = loop_read_exact(fd, buf, 36, false); + if (r < 0) + return r; for (j = 0, p = buf; j < 16; j++) { int a, b; - if (p >= buf + k - 1) + if (p >= buf + 35) return -EIO; if (*p == '-') { p++; - if (p >= buf + k - 1) + if (p >= buf + 35) return -EIO; } diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index f71749f72d..ed8aa0952a 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -31,6 +31,7 @@ #include "strv.h" #include "fileio.h" #include "login-shared.h" +#include "formats-util.h" #include "sd-login.h" _public_ int sd_pid_get_session(pid_t pid, char **session) { @@ -73,6 +74,14 @@ _public_ int sd_pid_get_slice(pid_t pid, char **slice) { return cg_pid_get_slice(pid, slice); } +_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) { + + assert_return(pid >= 0, -EINVAL); + assert_return(slice, -EINVAL); + + return cg_pid_get_user_slice(pid, slice); +} + _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { assert_return(pid >= 0, -EINVAL); @@ -82,7 +91,7 @@ _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { } _public_ int sd_peer_get_session(int fd, char **session) { - struct ucred ucred; + struct ucred ucred = {}; int r; assert_return(fd >= 0, -EINVAL); @@ -165,6 +174,20 @@ _public_ int sd_peer_get_slice(int fd, char **slice) { return cg_pid_get_slice(ucred.pid, slice); } +_public_ int sd_peer_get_user_slice(int fd, char **slice) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EINVAL); + assert_return(slice, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_user_slice(ucred.pid, slice); +} + static int file_of_uid(uid_t uid, char **p) { assert(p); @@ -496,9 +519,9 @@ _public_ int sd_session_get_desktop(const char *session, char **desktop) { if (r < 0) return r; - t = cunescape(escaped); - if (!t) - return -ENOMEM; + r = cunescape(escaped, 0, &t); + if (r < 0) + return r; *desktop = t; return 0; diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 2802e8246d..05affa442d 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -26,6 +26,7 @@ #include "util.h" #include "strv.h" +#include "formats-util.h" static void test_login(void) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h index 02bd545526..11a0012348 100644 --- a/src/libsystemd/sd-network/network-util.h +++ b/src/libsystemd/sd-network/network-util.h @@ -21,7 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "sd-network.h" DEFINE_TRIVIAL_CLEANUP_FUNC(sd_network_monitor*, sd_network_monitor_unref); diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index c4713feb5d..db1f6997cb 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -20,19 +20,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <string.h> #include <errno.h> #include <sys/inotify.h> #include <poll.h> -#include <net/if.h> #include "util.h" #include "macro.h" #include "strv.h" #include "fileio.h" #include "sd-network.h" -#include "network-internal.h" _public_ int sd_network_get_operational_state(char **state) { _cleanup_free_ char *s = NULL; @@ -264,6 +261,14 @@ _public_ int sd_network_link_get_domains(int ifindex, char ***ret) { return network_get_link_strv("DOMAINS", ifindex, ret); } +_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) { + return network_get_link_strv("CARRIER_BOUND_TO", ifindex, ret); +} + +_public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) { + return network_get_link_strv("CARRIER_BOUND_BY", ifindex, ret); +} + _public_ int sd_network_link_get_wildcard_domain(int ifindex) { int r; _cleanup_free_ char *p = NULL, *s = NULL; diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index 651fceb79d..7363be2794 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -323,7 +323,7 @@ static int get_path(uint64_t type, char **buffer, const char **ret) { return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); } - return -ENOTSUP; + return -EOPNOTSUPP; } _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { @@ -552,7 +552,7 @@ static int get_search(uint64_t type, char ***list) { NULL); } - return -ENOTSUP; + return -EOPNOTSUPP; } _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 6448280c4f..b0dc822591 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -19,24 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> -#include <fcntl.h> #include <signal.h> #include <unistd.h> -#include <sys/select.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> -#include <sys/wait.h> -#include <sys/types.h> -#include <pwd.h> -#include <netinet/in.h> -#include <arpa/nameser.h> #include <resolv.h> -#include <dirent.h> -#include <sys/time.h> -#include <sys/resource.h> #include <stdint.h> #include <pthread.h> #include <sys/prctl.h> @@ -461,7 +450,7 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { assert(length >= sizeof(ResRequest)); assert(length == sizeof(ResRequest) + res_req->dname_len); - dname = (const char *) req + sizeof(ResRequest); + dname = (const char *) res_req + sizeof(ResRequest); if (req->type == REQUEST_RES_QUERY) ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE); @@ -664,7 +653,7 @@ static void resolve_free(sd_resolve *resolve) { /* Send one termination packet for each worker */ for (i = 0; i < resolve->n_valid_workers; i++) - send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); + (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); } /* Now terminate them and wait until they are gone. */ diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c index d08e1b5a05..354a4071b7 100644 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ b/src/libsystemd/sd-resolve/test-resolve.c @@ -21,14 +21,11 @@ ***/ #include <string.h> -#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <netinet/in.h> -#include <arpa/nameser.h> #include <resolv.h> -#include <signal.h> #include <errno.h> #include "socket-util.h" @@ -49,7 +46,7 @@ static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrin for (i = ai; i; i = i->ai_next) { _cleanup_free_ char *addr = NULL; - assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, &addr) == 0); + assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, true, &addr) == 0); puts(addr); } diff --git a/src/libsystemd/sd-rtnl/local-addresses.h b/src/libsystemd/sd-rtnl/local-addresses.h index ef7def530d..bdc28d3510 100644 --- a/src/libsystemd/sd-rtnl/local-addresses.h +++ b/src/libsystemd/sd-rtnl/local-addresses.h @@ -21,10 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <inttypes.h> -#include <sys/types.h> -#include <assert.h> -#include <sys/socket.h> #include "sd-rtnl.h" #include "in-addr-util.h" diff --git a/src/libsystemd/sd-rtnl/rtnl-internal.h b/src/libsystemd/sd-rtnl/rtnl-internal.h index a192198419..05b88b1ad8 100644 --- a/src/libsystemd/sd-rtnl/rtnl-internal.h +++ b/src/libsystemd/sd-rtnl/rtnl-internal.h @@ -109,6 +109,7 @@ struct sd_rtnl_message { size_t *rta_offset_tb[RTNL_CONTAINER_DEPTH]; unsigned short rta_tb_size[RTNL_CONTAINER_DEPTH]; bool sealed:1; + bool broadcast:1; sd_rtnl_message *next; /* next in a chain of multi-part messages */ }; diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index 276591f31b..bab2a4ff08 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -20,11 +20,11 @@ ***/ #include <netinet/in.h> -#include <netinet/ether.h> #include <stdbool.h> #include <unistd.h> #include "util.h" +#include "formats-util.h" #include "refcnt.h" #include "missing.h" @@ -45,7 +45,7 @@ static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret) { /* Note that 'rtnl' is currently unused, if we start using it internally we must take care to avoid problems due to mutual references between - busses and their queued messages. See sd-bus. + buses and their queued messages. See sd-bus. */ m = new0(sd_rtnl_message, 1); @@ -649,13 +649,13 @@ int sd_rtnl_message_get_family(sd_rtnl_message *m, int *family) { return 0; } - return -ENOTSUP; + return -EOPNOTSUPP; } int sd_rtnl_message_is_broadcast(sd_rtnl_message *m) { assert_return(m, -EINVAL); - return !m->hdr->nlmsg_pid; + return m->broadcast; } int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) { @@ -1475,7 +1475,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool return 0; } - if (group) + if (_group) *_group = group; return r; @@ -1501,7 +1501,7 @@ int socket_read_message(sd_rtnl *rtnl) { assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); /* read nothing, just get the pending message size */ - r = socket_recv_message(rtnl->fd, &iov, &group, true); + r = socket_recv_message(rtnl->fd, &iov, NULL, true); if (r <= 0) return r; else @@ -1555,13 +1555,15 @@ int socket_read_message(sd_rtnl *rtnl) { /* finished reading multi-part message */ done = true; - continue; + /* if first is not defined, put NLMSG_DONE into the receive queue. */ + if (first) + continue; } /* check that we support this message type */ r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type); if (r < 0) { - if (r == -ENOTSUP) + if (r == -EOPNOTSUPP) log_debug("sd-rtnl: ignored message with unknown type: %i", new_msg->nlmsg_type); @@ -1578,6 +1580,8 @@ int socket_read_message(sd_rtnl *rtnl) { if (r < 0) return r; + m->broadcast = !!group; + m->hdr = memdup(new_msg, new_msg->nlmsg_len); if (!m->hdr) return -ENOMEM; diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c index a4c71f3785..d211684ff1 100644 --- a/src/libsystemd/sd-rtnl/rtnl-types.c +++ b/src/libsystemd/sd-rtnl/rtnl-types.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stddef.h> #include <stdint.h> #include <sys/socket.h> #include <linux/netlink.h> @@ -91,6 +90,30 @@ static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = { [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, }; +static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = { + [BOND_ARP_TARGETS_0] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_1] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_2] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_3] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_4] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_5] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_6] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_7] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_8] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_9] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_10] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_11] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_12] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_13] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_14] = { .type = NLA_U32 }, + [BOND_ARP_TARGETS_MAX] = { .type = NLA_U32 }, +}; + +static const NLTypeSystem rtnl_bond_arp_type_system = { + .max = ELEMENTSOF(rtnl_bond_arp_target_types) - 1, + .types = rtnl_bond_arp_target_types, +}; + static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { [IFLA_BOND_MODE] = { .type = NLA_U8 }, [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 }, @@ -99,7 +122,7 @@ static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { [IFLA_BOND_DOWNDELAY] = { .type = NLA_U32 }, [IFLA_BOND_USE_CARRIER] = { .type = NLA_U8 }, [IFLA_BOND_ARP_INTERVAL] = { .type = NLA_U32 }, - [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED }, + [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED, .type_system = &rtnl_bond_arp_type_system }, [IFLA_BOND_ARP_VALIDATE] = { .type = NLA_U32 }, [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NLA_U32 }, [IFLA_BOND_PRIMARY] = { .type = NLA_U32 }, @@ -181,6 +204,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_ [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap", [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit", [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti", + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6", [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", }; @@ -215,6 +239,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_D .types = rtnl_link_info_data_iptun_types }, [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .max = ELEMENTSOF(rtnl_link_info_data_ipvti_types) - 1, .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .max = ELEMENTSOF(rtnl_link_info_data_ipvti_types) - 1, + .types = rtnl_link_info_data_ipvti_types }, [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .max = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types) - 1, .types = rtnl_link_info_data_ip6tnl_types }, @@ -349,7 +375,9 @@ static const NLTypeSystem rtnl_link_type_system = { .types = rtnl_link_types, }; -static const NLType rtnl_address_types[IFA_MAX + 1] = { +/* IFA_FLAGS was defined in kernel 3.14, but we still support older + * kernels where IFA_MAX is lower. */ +static const NLType rtnl_address_types[CONST_MAX(IFA_MAX, IFA_FLAGS) + 1] = { [IFA_ADDRESS] = { .type = NLA_IN_ADDR }, [IFA_LOCAL] = { .type = NLA_IN_ADDR }, [IFA_LABEL] = { .type = NLA_STRING, .size = IFNAMSIZ - 1 }, @@ -359,9 +387,7 @@ static const NLType rtnl_address_types[IFA_MAX + 1] = { [IFA_ANYCAST], [IFA_MULTICAST], */ -#ifdef IFA_FLAGS [IFA_FLAGS] = { .type = NLA_U32 }, -#endif }; static const NLTypeSystem rtnl_address_type_system = { @@ -412,6 +438,7 @@ static const NLTypeSystem rtnl_neigh_type_system = { }; static const NLType rtnl_types[RTM_MAX + 1] = { + [NLMSG_DONE] = { .type = NLA_META, .size = 0 }, [NLMSG_ERROR] = { .type = NLA_META, .size = sizeof(struct nlmsgerr) }, [RTM_NEWLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, [RTM_DELLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, @@ -444,12 +471,12 @@ int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, ui assert(type_system->types); if (type > type_system->max) - return -ENOTSUP; + return -EOPNOTSUPP; nl_type = &type_system->types[type]; if (nl_type->type == NLA_UNSPEC) - return -ENOTSUP; + return -EOPNOTSUPP; *ret = nl_type; @@ -466,8 +493,7 @@ int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSys if (r < 0) return r; - assert_return(nl_type->type == NLA_NESTED, -EINVAL); - + assert(nl_type->type == NLA_NESTED); assert(nl_type->type_system); *ret = nl_type->type_system; @@ -485,8 +511,7 @@ int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLT if (r < 0) return r; - assert_return(nl_type->type == NLA_UNION, -EINVAL); - + assert(nl_type->type == NLA_UNION); assert(nl_type->type_system_union); *ret = nl_type->type_system_union; @@ -498,7 +523,7 @@ int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union int type; assert(type_system_union); - assert_return(type_system_union->match_type == NL_MATCH_SIBLING, -EINVAL); + assert(type_system_union->match_type == NL_MATCH_SIBLING); assert(type_system_union->lookup); assert(type_system_union->type_systems); assert(ret); @@ -506,7 +531,7 @@ int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union type = type_system_union->lookup(key); if (type < 0) - return -ENOTSUP; + return -EOPNOTSUPP; assert(type < type_system_union->num); @@ -520,17 +545,15 @@ int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_sys assert(type_system_union); assert(type_system_union->type_systems); + assert(type_system_union->match_type == NL_MATCH_PROTOCOL); assert(ret); - assert_return(type_system_union->match_type == NL_MATCH_PROTOCOL, -EINVAL); - assert_return(protocol < type_system_union->num, -EINVAL); if (protocol >= type_system_union->num) - return -ENOTSUP; + return -EOPNOTSUPP; type_system = &type_system_union->type_systems[protocol]; - - if (!type_system) - return -ENOTSUP; + if (type_system->max == 0) + return -EOPNOTSUPP; *ret = type_system; diff --git a/src/libsystemd/sd-rtnl/rtnl-types.h b/src/libsystemd/sd-rtnl/rtnl-types.h index 1ab9444987..de1544bf36 100644 --- a/src/libsystemd/sd-rtnl/rtnl-types.h +++ b/src/libsystemd/sd-rtnl/rtnl-types.h @@ -87,6 +87,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL, NL_UNION_LINK_INFO_DATA_SIT_TUNNEL, NL_UNION_LINK_INFO_DATA_VTI_TUNNEL, + NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL, NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, _NL_UNION_LINK_INFO_DATA_MAX, _NL_UNION_LINK_INFO_DATA_INVALID = -1 @@ -94,3 +95,25 @@ typedef enum NLUnionLinkInfoData { const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; + +/* Maximum ARP IP target defined in kernel */ +#define BOND_MAX_ARP_TARGETS 16 + +typedef enum BondArpTargets { + BOND_ARP_TARGETS_0, + BOND_ARP_TARGETS_1, + BOND_ARP_TARGETS_2, + BOND_ARP_TARGETS_3, + BOND_ARP_TARGETS_4, + BOND_ARP_TARGETS_5, + BOND_ARP_TARGETS_6, + BOND_ARP_TARGETS_7, + BOND_ARP_TARGETS_8, + BOND_ARP_TARGETS_9, + BOND_ARP_TARGETS_10, + BOND_ARP_TARGETS_11, + BOND_ARP_TARGETS_12, + BOND_ARP_TARGETS_13, + BOND_ARP_TARGETS_14, + BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS, +} BondArpTargets; diff --git a/src/libsystemd/sd-rtnl/rtnl-util.c b/src/libsystemd/sd-rtnl/rtnl-util.c index 5666ea44cb..9ddf074c24 100644 --- a/src/libsystemd/sd-rtnl/rtnl-util.c +++ b/src/libsystemd/sd-rtnl/rtnl-util.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/ether.h> #include "sd-rtnl.h" diff --git a/src/libsystemd/sd-rtnl/rtnl-util.h b/src/libsystemd/sd-rtnl/rtnl-util.h index ca9fbd4f41..9e4bdb867e 100644 --- a/src/libsystemd/sd-rtnl/rtnl-util.h +++ b/src/libsystemd/sd-rtnl/rtnl-util.h @@ -21,7 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/ether.h> #include "util.h" #include "sd-rtnl.h" diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index ae49c77e01..40dea1252f 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -61,6 +61,11 @@ static int sd_rtnl_new(sd_rtnl **ret) { sizeof(struct nlmsghdr), sizeof(uint8_t))) return -ENOMEM; + /* Change notification responses have sequence 0, so we must + * start our request sequence numbers at 1, or we may confuse our + * responses with notifications from the kernel */ + rtnl->serial = 1; + *ret = rtnl; rtnl = NULL; @@ -257,7 +262,9 @@ static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) { assert(m); assert(m->hdr); - m->hdr->nlmsg_seq = rtnl->serial++; + /* don't use seq == 0, as that is used for broadcasts, so we + would get confused by replies to such messages */ + m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++; rtnl_message_seal(m); @@ -414,16 +421,14 @@ static int process_timeout(sd_rtnl *rtnl) { } static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { - struct reply_callback *c; + _cleanup_free_ struct reply_callback *c = NULL; uint64_t serial; + uint16_t type; int r; assert(rtnl); assert(m); - if (sd_rtnl_message_is_broadcast(m)) - return 0; - serial = rtnl_message_get_serial(m); c = hashmap_remove(rtnl->reply_callbacks, &serial); if (!c) @@ -432,12 +437,17 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { if (c->timeout != 0) prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx); + r = sd_rtnl_message_get_type(m, &type); + if (r < 0) + return 0; + + if (type == NLMSG_DONE) + m = NULL; + r = c->callback(rtnl, m, c->userdata); if (r < 0) log_debug_errno(r, "sd-rtnl: callback failed: %m"); - free(c); - return 1; } @@ -488,13 +498,15 @@ static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) { if (!m) goto null_message; - r = process_reply(rtnl, m); - if (r != 0) - goto null_message; - - r = process_match(rtnl, m); - if (r != 0) - goto null_message; + if (sd_rtnl_message_is_broadcast(m)) { + r = process_match(rtnl, m); + if (r != 0) + goto null_message; + } else { + r = process_reply(rtnl, m); + if (r != 0) + goto null_message; + } if (ret) { *ret = m; @@ -696,7 +708,6 @@ int sd_rtnl_call(sd_rtnl *rtnl, sd_rtnl_message **ret) { usec_t timeout; uint32_t serial; - unsigned i = 0; int r; assert_return(rtnl, -EINVAL); @@ -711,36 +722,44 @@ int sd_rtnl_call(sd_rtnl *rtnl, for (;;) { usec_t left; + unsigned i; - while (i < rtnl->rqueue_size) { - sd_rtnl_message *incoming; + for (i = 0; i < rtnl->rqueue_size; i++) { uint32_t received_serial; - incoming = rtnl->rqueue[i]; - received_serial = rtnl_message_get_serial(incoming); + received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); if (received_serial == serial) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL; + uint16_t type; + + incoming = rtnl->rqueue[i]; + /* found a match, remove from rqueue and return it */ memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1, sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1)); rtnl->rqueue_size--; r = sd_rtnl_message_get_errno(incoming); - if (r < 0) { - sd_rtnl_message_unref(incoming); + if (r < 0) + return r; + + r = sd_rtnl_message_get_type(incoming, &type); + if (r < 0) return r; + + if (type == NLMSG_DONE) { + *ret = NULL; + return 0; } if (ret) { *ret = incoming; - } else - sd_rtnl_message_unref(incoming); + incoming = NULL; + } return 1; } - - /* Try to read more, right away */ - i ++; } r = socket_read_message(rtnl); @@ -993,7 +1012,7 @@ int sd_rtnl_add_match(sd_rtnl *rtnl, assert_return(!rtnl_pid_changed(rtnl), -ECHILD); assert_return(rtnl_message_type_is_link(type) || rtnl_message_type_is_addr(type) || - rtnl_message_type_is_route(type), -ENOTSUP); + rtnl_message_type_is_route(type), -EOPNOTSUPP); c = new0(struct match_callback, 1); if (!c) diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c index 02f7a8e38a..47cce64816 100644 --- a/src/libsystemd/sd-rtnl/test-rtnl.c +++ b/src/libsystemd/sd-rtnl/test-rtnl.c @@ -29,7 +29,6 @@ #include "rtnl-util.h" #include "event-util.h" #include "missing.h" -#include "rtnl-internal.h" static void test_message_link_bridge(sd_rtnl *rtnl) { _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL; |