sin: fix segfault; improve D-Bus service error handling
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I35ea89fb5ffecd42f644eac4c87ef9af6a6a6964
This commit is contained in:
parent
fa767ddef9
commit
813bb6ecfd
2 changed files with 407 additions and 0 deletions
231
src/dbus-common.c
Normal file
231
src/dbus-common.c
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
#include "dbus-common.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int dbus_service_init(DBusService* svc, const char* bus_name, const char* object_path,
|
||||
const char* interface_name) {
|
||||
if (!svc || !bus_name || !object_path || !interface_name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
svc->bus_name = strdup(bus_name);
|
||||
svc->object_path = strdup(object_path);
|
||||
svc->interface_name = strdup(interface_name);
|
||||
svc->mode = DBUS_MODE_NONE;
|
||||
|
||||
if (!svc->bus_name || !svc->object_path || !svc->interface_name) {
|
||||
free(svc->bus_name);
|
||||
free(svc->object_path);
|
||||
free(svc->interface_name);
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbus_service_cleanup(DBusService* svc) {
|
||||
if (!svc)
|
||||
return;
|
||||
|
||||
if (svc->slot) {
|
||||
sd_bus_slot_unref(svc->slot);
|
||||
svc->slot = NULL;
|
||||
}
|
||||
|
||||
if (svc->bus) {
|
||||
sd_bus_flush_close_unref(svc->bus);
|
||||
svc->bus = NULL;
|
||||
}
|
||||
|
||||
free(svc->bus_name);
|
||||
free(svc->object_path);
|
||||
free(svc->interface_name);
|
||||
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
}
|
||||
|
||||
int dbus_service_detect_mode(DBusService* svc) {
|
||||
if (!svc || !svc->bus)
|
||||
return -1;
|
||||
|
||||
sd_bus_message* reply = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
int r = sd_bus_call_method(svc->bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus", "ListNames", &error, &reply, "");
|
||||
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to list D-Bus names: %s\n", error.message);
|
||||
sd_bus_error_free(&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
|
||||
char** names = NULL;
|
||||
r = sd_bus_message_read_strv(reply, &names);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to read name list: %s\n", strerror(-r));
|
||||
sd_bus_message_unref(reply);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int name_owned = 0;
|
||||
if (names) {
|
||||
for (int i = 0; names[i]; i++) {
|
||||
if (strcmp(names[i], svc->bus_name) == 0) {
|
||||
name_owned = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(names);
|
||||
}
|
||||
|
||||
sd_bus_message_unref(reply);
|
||||
|
||||
svc->mode = name_owned ? DBUS_MODE_MONITOR : DBUS_MODE_IMPLEMENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbus_service_claim_name(DBusService* svc) {
|
||||
if (!svc || !svc->bus)
|
||||
return -1;
|
||||
|
||||
int r = sd_bus_request_name(svc->bus, svc->bus_name,
|
||||
SD_BUS_NAME_ALLOW_REPLACEMENT | SD_BUS_NAME_REPLACE_EXISTING);
|
||||
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to claim D-Bus name %s: %s\n", svc->bus_name, strerror(-r));
|
||||
return -1;
|
||||
}
|
||||
|
||||
svc->mode = DBUS_MODE_IMPLEMENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbus_service_become_monitor(DBusService* svc, const char** match_rules, int num_rules) {
|
||||
if (!svc || !svc->bus)
|
||||
return -1;
|
||||
|
||||
sd_bus_message* reply = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
sd_bus_message* m = NULL;
|
||||
int r =
|
||||
sd_bus_message_new_method_call(svc->bus, &m, "org.freedesktop.DBus", "/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus.Monitoring", "BecomeMonitor");
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to create BecomeMonitor message: %s\n", strerror(-r));
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "s");
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
for (int i = 0; i < num_rules; i++) {
|
||||
r = sd_bus_message_append_basic(m, 's', match_rules[i]);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
uint32_t flags = 0;
|
||||
r = sd_bus_message_append_basic(m, 'u', &flags);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_bus_call(svc->bus, m, 0, &error, &reply);
|
||||
sd_bus_message_unref(m);
|
||||
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "BecomeMonitor failed: %s\n", error.message);
|
||||
sd_bus_error_free(&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(reply);
|
||||
|
||||
svc->mode = DBUS_MODE_MONITOR;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
sd_bus_message_unref(m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dbus_service_poll(DBusService* svc) {
|
||||
if (!svc || !svc->bus)
|
||||
return -1;
|
||||
|
||||
int r = sd_bus_process(svc->bus, NULL);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "D-Bus process error: %s\n", strerror(-r));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
r = sd_bus_wait(svc->bus, 0);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "D-Bus wait error: %s\n", strerror(-r));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbus_parse_inhibit_request(sd_bus_message* m, char** app_name, char** reason) {
|
||||
if (!m || !app_name || !reason)
|
||||
return -1;
|
||||
|
||||
*app_name = NULL;
|
||||
*reason = NULL;
|
||||
|
||||
int r = sd_bus_message_read(m, "ss", app_name, reason);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*app_name)
|
||||
*app_name = strdup(*app_name);
|
||||
if (*reason)
|
||||
*reason = strdup(*reason);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbus_send_signal(DBusService* svc, const char* signal_name, const char* signature, ...) {
|
||||
if (!svc || !svc->bus)
|
||||
return -1;
|
||||
|
||||
sd_bus_message* m = NULL;
|
||||
int r =
|
||||
sd_bus_message_new_signal(svc->bus, &m, svc->object_path, svc->interface_name, signal_name);
|
||||
if (r < 0)
|
||||
return -1;
|
||||
|
||||
if (signature && signature[0]) {
|
||||
va_list args;
|
||||
va_start(args, signature);
|
||||
r = sd_bus_message_appendv(m, signature, args);
|
||||
va_end(args);
|
||||
if (r < 0) {
|
||||
sd_bus_message_unref(m);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_send(svc->bus, m, NULL);
|
||||
sd_bus_message_unref(m);
|
||||
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
176
src/dbus-systemd-login1.c
Normal file
176
src/dbus-systemd-login1.c
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#include "dbus-systemd-login1.h"
|
||||
#include "dbus-common.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
typedef struct {
|
||||
DBusService service;
|
||||
InhibitEventCallback callback;
|
||||
uint32_t next_cookie;
|
||||
} SystemdLogin1Data;
|
||||
|
||||
static int login1_method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) {
|
||||
SystemdLogin1Data* data = userdata;
|
||||
|
||||
char *what = NULL, *who = NULL, *why = NULL, *mode = NULL;
|
||||
int r = sd_bus_message_read(m, "ssss", &what, &who, &why, &mode);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
sd_bus_message* reply = NULL;
|
||||
r = sd_bus_call_method(data->service.bus, "org.freedesktop.login1", "/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager", "Inhibit", ret_error, &reply, "ssss",
|
||||
what ? what : "", who ? who : "sin", why ? why : "inhibited",
|
||||
mode ? mode : "block");
|
||||
if (r < 0) {
|
||||
sd_bus_message_unref(reply);
|
||||
return sd_bus_reply_method_error(m, ret_error);
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
r = sd_bus_message_read(reply, "h", &fd);
|
||||
if (r < 0) {
|
||||
fd = -1;
|
||||
}
|
||||
sd_bus_message_unref(reply);
|
||||
|
||||
uint32_t cookie = data->next_cookie++;
|
||||
|
||||
InhibitType type = INHIBIT_TYPE_SUSPEND;
|
||||
if (what && strstr(what, "idle")) {
|
||||
type = INHIBIT_TYPE_IDLE;
|
||||
} else if (what && strstr(what, "sleep")) {
|
||||
type = INHIBIT_TYPE_SUSPEND;
|
||||
}
|
||||
|
||||
InhibitEntry entry = {.type = type, .cookie = cookie};
|
||||
snprintf(entry.id, sizeof(entry.id), "login1_%u", cookie);
|
||||
strncpy(entry.app_name, who ? who : "unknown", sizeof(entry.app_name) - 1);
|
||||
strncpy(entry.reason, why ? why : "", sizeof(entry.reason) - 1);
|
||||
|
||||
if (data->callback) {
|
||||
data->callback((InhibitInterface*) data->service.parent, &entry, 1);
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(m, "h", fd);
|
||||
}
|
||||
|
||||
static const sd_bus_vtable login1_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Inhibit", "ssss", "h", login1_method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END};
|
||||
|
||||
static int login1_init(InhibitInterface* iface) {
|
||||
if (!iface || !iface->private_data)
|
||||
return -1;
|
||||
|
||||
SystemdLogin1Data* data = iface->private_data;
|
||||
|
||||
int r = dbus_service_init(&data->service, "org.freedesktop.login1", "/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager");
|
||||
if (r < 0)
|
||||
return -1;
|
||||
|
||||
r = sd_bus_open_system(&data->service.bus);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to open system bus for login1 (may need root): %s\n", strerror(-r));
|
||||
dbus_service_cleanup(&data->service);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data->service.parent = iface;
|
||||
|
||||
r = dbus_service_detect_mode(&data->service);
|
||||
if (r < 0) {
|
||||
dbus_service_cleanup(&data->service);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->service.mode == DBUS_MODE_IMPLEMENT) {
|
||||
r = dbus_service_claim_name(&data->service);
|
||||
if (r < 0) {
|
||||
dbus_service_cleanup(&data->service);
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path,
|
||||
data->service.interface_name, login1_vtable, data);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to add login1 vtable: %s\n", strerror(-r));
|
||||
dbus_service_cleanup(&data->service);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
const char* rules[] = {"interface='org.freedesktop.login1.Manager'"};
|
||||
r = dbus_service_become_monitor(&data->service, rules, 1);
|
||||
if (r < 0) {
|
||||
dbus_service_cleanup(&data->service);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void login1_cleanup(InhibitInterface* iface) {
|
||||
if (!iface || !iface->private_data)
|
||||
return;
|
||||
SystemdLogin1Data* data = iface->private_data;
|
||||
dbus_service_cleanup(&data->service);
|
||||
}
|
||||
|
||||
static int login1_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name,
|
||||
const char* reason, InhibitEntry* out_entry) {
|
||||
(void) iface;
|
||||
(void) type;
|
||||
(void) app_name;
|
||||
(void) reason;
|
||||
(void) out_entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int login1_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) {
|
||||
(void) iface;
|
||||
(void) entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int login1_poll(InhibitInterface* iface) {
|
||||
if (!iface || !iface->private_data)
|
||||
return -1;
|
||||
SystemdLogin1Data* data = iface->private_data;
|
||||
return dbus_service_poll(&data->service);
|
||||
}
|
||||
|
||||
static void login1_set_callback(InhibitInterface* iface, InhibitEventCallback cb) {
|
||||
if (!iface || !iface->private_data)
|
||||
return;
|
||||
SystemdLogin1Data* data = iface->private_data;
|
||||
data->callback = cb;
|
||||
}
|
||||
|
||||
InhibitInterface* dbus_systemd_login1_create(void) {
|
||||
InhibitInterface* iface = calloc(1, sizeof(InhibitInterface));
|
||||
if (!iface)
|
||||
return NULL;
|
||||
|
||||
SystemdLogin1Data* data = calloc(1, sizeof(SystemdLogin1Data));
|
||||
if (!data) {
|
||||
free(iface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iface->name = "systemd-login1";
|
||||
iface->init = login1_init;
|
||||
iface->cleanup = login1_cleanup;
|
||||
iface->inhibit = login1_inhibit;
|
||||
iface->uninhibit = login1_uninhibit;
|
||||
iface->poll = login1_poll;
|
||||
iface->set_callback = login1_set_callback;
|
||||
iface->private_data = data;
|
||||
|
||||
return iface;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue