+
+static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int prepare_callback(sd_event_source *s, void *userdata) {
+ sd_bus *bus = userdata;
+ int r, e;
+ usec_t until;
+
+ assert(s);
+ assert(bus);
+
+ e = sd_bus_get_events(bus);
+ if (e < 0)
+ return e;
+
+ if (bus->output_fd != bus->input_fd) {
+
+ r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_event_source_set_io_events(bus->input_io_event_source, e);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_get_timeout(bus, &until);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ int j;
+
+ j = sd_event_source_set_time(bus->time_event_source, until);
+ if (j < 0)
+ return j;
+ }
+
+ r = sd_event_source_set_enabled(bus->time_event_source, r > 0);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int quit_callback(sd_event_source *event, void *userdata) {
+ sd_bus *bus = userdata;
+
+ assert(event);
+
+ sd_bus_flush(bus);
+
+ return 1;
+}
+
+_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(!bus->event, -EBUSY);
+
+ assert(!bus->input_io_event_source);
+ assert(!bus->output_io_event_source);
+ assert(!bus->time_event_source);
+
+ if (event)
+ bus->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&bus->event);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_event_add_io(bus->event, bus->input_fd, 0, io_callback, bus, &bus->input_io_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(bus->input_io_event_source, priority);
+ if (r < 0)
+ goto fail;
+
+ if (bus->output_fd != bus->input_fd) {
+ r = sd_event_add_io(bus->event, bus->output_fd, 0, io_callback, bus, &bus->output_io_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(bus->output_io_event_source, priority);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = sd_event_source_set_prepare(bus->input_io_event_source, prepare_callback);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_add_monotonic(bus->event, 0, 0, time_callback, bus, &bus->time_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(bus->time_event_source, priority);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_add_quit(bus->event, quit_callback, bus, &bus->quit_event_source);
+ if (r < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ sd_bus_detach_event(bus);
+ return r;
+}
+
+_public_ int sd_bus_detach_event(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+
+ if (!bus->event)
+ return 0;
+
+ if (bus->input_io_event_source) {
+ sd_event_source_set_enabled(bus->input_io_event_source, SD_EVENT_OFF);
+ bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source);
+ }
+
+ if (bus->output_io_event_source) {
+ sd_event_source_set_enabled(bus->output_io_event_source, SD_EVENT_OFF);
+ bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source);
+ }
+
+ if (bus->time_event_source) {
+ sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF);
+ bus->time_event_source = sd_event_source_unref(bus->time_event_source);
+ }
+
+ if (bus->quit_event_source) {
+ sd_event_source_set_enabled(bus->quit_event_source, SD_EVENT_OFF);
+ bus->quit_event_source = sd_event_source_unref(bus->quit_event_source);
+ }
+
+ if (bus->event)
+ bus->event = sd_event_unref(bus->event);
+
+ return 1;
+}
+
+_public_ sd_event* sd_bus_get_event(sd_bus *bus) {
+ assert_return(bus, NULL);
+
+ return bus->event;
+}
+
+_public_ sd_bus_message* sd_bus_get_current(sd_bus *bus) {
+ assert_return(bus, NULL);
+
+ return bus->current;
+}
+
+static int bus_default(int (*bus_open)(sd_bus **), sd_bus **default_bus, sd_bus **ret) {
+ sd_bus *b = NULL;
+ int r;
+
+ assert(bus_open);
+ assert(default_bus);
+
+ if (!ret)
+ return !!*default_bus;
+
+ if (*default_bus) {
+ *ret = sd_bus_ref(*default_bus);
+ return 0;
+ }
+
+ r = bus_open(&b);
+ if (r < 0)
+ return r;
+
+ b->default_bus_ptr = default_bus;
+ b->tid = gettid();
+ *default_bus = b;
+
+ *ret = b;
+ return 1;
+}
+
+_public_ int sd_bus_default_system(sd_bus **ret) {
+ static __thread sd_bus *default_system_bus = NULL;
+
+ return bus_default(sd_bus_open_system, &default_system_bus, ret);
+}
+
+_public_ int sd_bus_default_user(sd_bus **ret) {
+ static __thread sd_bus *default_user_bus = NULL;
+
+ return bus_default(sd_bus_open_user, &default_user_bus, ret);
+}
+
+_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) {
+ assert_return(b, -EINVAL);
+ assert_return(tid, -EINVAL);
+ assert_return(!bus_pid_changed(b), -ECHILD);
+
+ if (b->tid != 0) {
+ *tid = b->tid;
+ return 0;
+ }
+
+ if (b->event)
+ return sd_event_get_tid(b->event, tid);
+
+ return -ENXIO;
+}
+
+_public_ char *sd_bus_label_escape(const char *s) {
+ char *r, *t;
+ const char *f;
+
+ assert_return(s, NULL);
+
+ /* Escapes all chars that D-Bus' object path cannot deal
+ * with. Can be reversed with bus_path_unescape(). We special
+ * case the empty string. */
+
+ if (*s == 0)
+ return strdup("_");
+
+ r = new(char, strlen(s)*3 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ /* Escape everything that is not a-zA-Z0-9. We also
+ * escape 0-9 if it's the first character */
+
+ if (!(*f >= 'A' && *f <= 'Z') &&
+ !(*f >= 'a' && *f <= 'z') &&
+ !(f > s && *f >= '0' && *f <= '9')) {
+ *(t++) = '_';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+_public_ char *sd_bus_label_unescape(const char *f) {
+ char *r, *t;
+
+ assert_return(f, NULL);
+
+ /* Special case for the empty string */
+ if (streq(f, "_"))
+ return strdup("");
+
+ r = new(char, strlen(f) + 1);
+ if (!r)
+ return NULL;
+
+ for (t = r; *f; f++) {
+
+ if (*f == '_') {
+ int a, b;
+
+ if ((a = unhexchar(f[1])) < 0 ||
+ (b = unhexchar(f[2])) < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '_';
+ } else {
+ *(t++) = (char) ((a << 4) | b);
+ f += 2;
+ }
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+_public_ int sd_bus_get_peer_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
+ sd_bus_creds *c;
+ pid_t pid = 0;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return(ret, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!bus->is_kernel, -ENOTSUP);
+
+ if (!bus->ucred_valid && !isempty(bus->label))
+ return -ENODATA;
+
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ if (bus->ucred_valid) {
+ pid = c->pid = bus->ucred.pid;
+ c->uid = bus->ucred.uid;
+ c->gid = bus->ucred.gid;
+
+ c->mask |= (SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID) & mask;
+ }
+
+ if (!isempty(bus->label) && (mask & SD_BUS_CREDS_SELINUX_CONTEXT)) {
+ c->label = strdup(bus->label);
+ if (!c->label) {
+ sd_bus_creds_unref(c);
+ return -ENOMEM;
+ }
+
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ r = bus_creds_add_more(c, mask, pid, 0);
+ if (r < 0)
+ return r;
+
+ *ret = c;
+ return 0;
+}