954 lines
27 KiB
C
954 lines
27 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#include <linux/capability.h>
|
|
|
|
#include "bus-common-errors.h"
|
|
#include "bus-polkit.h"
|
|
#include "fd-util.h"
|
|
#include "homed-bus.h"
|
|
#include "homed-home-bus.h"
|
|
#include "homed-home.h"
|
|
#include "strv.h"
|
|
#include "user-record-util.h"
|
|
#include "user-util.h"
|
|
|
|
static int property_get_unix_record(
|
|
sd_bus *bus,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *property,
|
|
sd_bus_message *reply,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
Home *h = userdata;
|
|
|
|
assert(bus);
|
|
assert(reply);
|
|
assert(h);
|
|
|
|
return sd_bus_message_append(
|
|
reply, "(suusss)",
|
|
h->user_name,
|
|
(uint32_t) h->uid,
|
|
h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
|
|
h->record ? user_record_real_name(h->record) : NULL,
|
|
h->record ? user_record_home_directory(h->record) : NULL,
|
|
h->record ? user_record_shell(h->record) : NULL);
|
|
}
|
|
|
|
static int property_get_state(
|
|
sd_bus *bus,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *property,
|
|
sd_bus_message *reply,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
Home *h = userdata;
|
|
|
|
assert(bus);
|
|
assert(reply);
|
|
assert(h);
|
|
|
|
return sd_bus_message_append(reply, "s", home_state_to_string(home_get_state(h)));
|
|
}
|
|
|
|
int bus_home_client_is_trusted(Home *h, sd_bus_message *message) {
|
|
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
|
uid_t euid;
|
|
int r;
|
|
|
|
assert(h);
|
|
|
|
if (!message)
|
|
return -EINVAL;
|
|
|
|
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_creds_get_euid(creds, &euid);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return euid == 0 || h->uid == euid;
|
|
}
|
|
|
|
int bus_home_get_record_json(
|
|
Home *h,
|
|
sd_bus_message *message,
|
|
char **ret,
|
|
bool *ret_incomplete) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
|
|
UserRecordLoadFlags flags;
|
|
int r, trusted;
|
|
|
|
assert(h);
|
|
assert(ret);
|
|
|
|
trusted = bus_home_client_is_trusted(h, message);
|
|
if (trusted < 0) {
|
|
log_warning_errno(trusted, "Failed to determine whether client is trusted, assuming untrusted.");
|
|
trusted = false;
|
|
}
|
|
|
|
flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
|
|
if (trusted)
|
|
flags |= USER_RECORD_ALLOW_PRIVILEGED;
|
|
else
|
|
flags |= USER_RECORD_STRIP_PRIVILEGED;
|
|
|
|
r = home_augment_status(h, flags, &augmented);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = json_variant_format(augmented->json, 0, ret);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (ret_incomplete)
|
|
*ret_incomplete = augmented->incomplete;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int property_get_user_record(
|
|
sd_bus *bus,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *property,
|
|
sd_bus_message *reply,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_free_ char *json = NULL;
|
|
Home *h = userdata;
|
|
bool incomplete;
|
|
int r;
|
|
|
|
assert(bus);
|
|
assert(reply);
|
|
assert(h);
|
|
|
|
r = bus_home_get_record_json(h, sd_bus_get_current_message(bus), &json, &incomplete);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return sd_bus_message_append(reply, "(sb)", json, incomplete);
|
|
}
|
|
|
|
int bus_home_method_activate(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = home_activate(h, secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
/* The operation is now in process, keep track of this message so that we can later reply to it. */
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_deactivate(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = home_deactivate(h, false, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_unregister(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.remove-home",
|
|
NULL,
|
|
true,
|
|
UID_INVALID,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_unregister(h, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r > 0);
|
|
|
|
/* Note that home_unregister() destroyed 'h' here, so no more accesses */
|
|
|
|
return sd_bus_reply_method_return(message, NULL);
|
|
}
|
|
|
|
int bus_home_method_realize(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.create-home",
|
|
NULL,
|
|
true,
|
|
UID_INVALID,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_create(h, secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
h->unregister_on_failure = false;
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_remove(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.remove-home",
|
|
NULL,
|
|
true,
|
|
UID_INVALID,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_remove(h, error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r > 0) /* Done already. Note that home_remove() destroyed 'h' here, so no more accesses */
|
|
return sd_bus_reply_method_return(message, NULL);
|
|
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_fixate(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = home_fixate(h, secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_authenticate(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.authenticate-home",
|
|
NULL,
|
|
true,
|
|
h->uid,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_authenticate(h, secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_update_record(Home *h, sd_bus_message *message, UserRecord *hr, sd_bus_error *error) {
|
|
int r;
|
|
|
|
assert(h);
|
|
assert(message);
|
|
assert(hr);
|
|
|
|
r = user_record_is_supported(hr, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.update-home",
|
|
NULL,
|
|
true,
|
|
UID_INVALID,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_update(h, hr, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_update(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_REQUIRE_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return bus_home_method_update_record(h, message, hr, error);
|
|
}
|
|
|
|
int bus_home_method_resize(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
Home *h = userdata;
|
|
uint64_t sz;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = sd_bus_message_read(message, "t", &sz);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.resize-home",
|
|
NULL,
|
|
true,
|
|
UID_INVALID,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_resize(h, sz, secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_change_password(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *new_secret = NULL, *old_secret = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &new_secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_message_read_secret(message, &old_secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_verify_polkit_async(
|
|
message,
|
|
CAP_SYS_ADMIN,
|
|
"org.freedesktop.home1.passwd-home",
|
|
NULL,
|
|
true,
|
|
h->uid,
|
|
&h->manager->polkit_registry,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return 1; /* Will call us back */
|
|
|
|
r = home_passwd(h, new_secret, old_secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_lock(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = home_lock(h, error);
|
|
if (r < 0)
|
|
return r;
|
|
if (r > 0) /* Done */
|
|
return sd_bus_reply_method_return(message, NULL);
|
|
|
|
/* The operation is now in process, keep track of this message so that we can later reply to it. */
|
|
assert(!h->current_operation);
|
|
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_unlock(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = home_unlock(h, secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(r == 0);
|
|
assert(!h->current_operation);
|
|
|
|
/* The operation is now in process, keep track of this message so that we can later reply to it. */
|
|
r = home_set_current_message(h, message);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_acquire(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
|
_cleanup_(operation_unrefp) Operation *o = NULL;
|
|
_cleanup_close_ int fd = -1;
|
|
int r, please_suspend;
|
|
Home *h = userdata;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = bus_message_read_secret(message, &secret, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_message_read(message, "b", &please_suspend);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* This operation might not be something we can executed immediately, hence queue it */
|
|
fd = home_create_fifo(h, please_suspend);
|
|
if (fd < 0)
|
|
return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name);
|
|
|
|
o = operation_new(OPERATION_ACQUIRE, message);
|
|
if (!o)
|
|
return -ENOMEM;
|
|
|
|
o->secret = TAKE_PTR(secret);
|
|
o->send_fd = TAKE_FD(fd);
|
|
|
|
r = home_schedule_operation(h, o, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_method_ref(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_close_ int fd = -1;
|
|
Home *h = userdata;
|
|
HomeState state;
|
|
int please_suspend, r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
r = sd_bus_message_read(message, "b", &please_suspend);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
state = home_get_state(h);
|
|
switch (state) {
|
|
case HOME_ABSENT:
|
|
return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
|
|
case HOME_UNFIXATED:
|
|
case HOME_INACTIVE:
|
|
case HOME_DIRTY:
|
|
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
|
|
case HOME_LOCKED:
|
|
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
|
|
default:
|
|
if (HOME_STATE_IS_ACTIVE(state))
|
|
break;
|
|
|
|
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
|
}
|
|
|
|
fd = home_create_fifo(h, please_suspend);
|
|
if (fd < 0)
|
|
return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name);
|
|
|
|
return sd_bus_reply_method_return(message, "h", fd);
|
|
}
|
|
|
|
int bus_home_method_release(
|
|
sd_bus_message *message,
|
|
void *userdata,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_(operation_unrefp) Operation *o = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(message);
|
|
assert(h);
|
|
|
|
o = operation_new(OPERATION_RELEASE, message);
|
|
if (!o)
|
|
return -ENOMEM;
|
|
|
|
r = home_schedule_operation(h, o, error);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* We map a uid_t as uint32_t bus property, let's ensure this is safe. */
|
|
assert_cc(sizeof(uid_t) == sizeof(uint32_t));
|
|
|
|
int bus_home_path(Home *h, char **ret) {
|
|
assert(ret);
|
|
|
|
return sd_bus_path_encode("/org/freedesktop/home1/home", h->user_name, ret);
|
|
}
|
|
|
|
static int bus_home_object_find(
|
|
sd_bus *bus,
|
|
const char *path,
|
|
const char *interface,
|
|
void *userdata,
|
|
void **found,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_free_ char *e = NULL;
|
|
Manager *m = userdata;
|
|
uid_t uid;
|
|
Home *h;
|
|
int r;
|
|
|
|
r = sd_bus_path_decode(path, "/org/freedesktop/home1/home", &e);
|
|
if (r <= 0)
|
|
return 0;
|
|
|
|
if (parse_uid(e, &uid) >= 0)
|
|
h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
|
|
else
|
|
h = hashmap_get(m->homes_by_name, e);
|
|
if (!h)
|
|
return 0;
|
|
|
|
*found = h;
|
|
return 1;
|
|
}
|
|
|
|
static int bus_home_node_enumerator(
|
|
sd_bus *bus,
|
|
const char *path,
|
|
void *userdata,
|
|
char ***nodes,
|
|
sd_bus_error *error) {
|
|
|
|
_cleanup_strv_free_ char **l = NULL;
|
|
Manager *m = userdata;
|
|
size_t k = 0;
|
|
Home *h;
|
|
int r;
|
|
|
|
assert(nodes);
|
|
|
|
l = new0(char*, hashmap_size(m->homes_by_uid) + 1);
|
|
if (!l)
|
|
return -ENOMEM;
|
|
|
|
HASHMAP_FOREACH(h, m->homes_by_uid) {
|
|
r = bus_home_path(h, l + k);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
*nodes = TAKE_PTR(l);
|
|
return 1;
|
|
}
|
|
|
|
const sd_bus_vtable home_vtable[] = {
|
|
SD_BUS_VTABLE_START(0),
|
|
|
|
SD_BUS_PROPERTY("UserName", "s",
|
|
NULL, offsetof(Home, user_name),
|
|
SD_BUS_VTABLE_PROPERTY_CONST),
|
|
SD_BUS_PROPERTY("UID", "u",
|
|
NULL, offsetof(Home, uid),
|
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
|
SD_BUS_PROPERTY("UnixRecord", "(suusss)",
|
|
property_get_unix_record, 0,
|
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
|
SD_BUS_PROPERTY("State", "s",
|
|
property_get_state, 0,
|
|
0),
|
|
SD_BUS_PROPERTY("UserRecord", "(sb)",
|
|
property_get_user_record, 0,
|
|
SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE),
|
|
|
|
SD_BUS_METHOD_WITH_NAMES("Activate",
|
|
"s",
|
|
SD_BUS_PARAM(secret),
|
|
NULL,,
|
|
bus_home_method_activate,
|
|
SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0),
|
|
SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED),
|
|
SD_BUS_METHOD_WITH_NAMES("Realize",
|
|
"s",
|
|
SD_BUS_PARAM(secret),
|
|
NULL,,
|
|
bus_home_method_realize,
|
|
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
|
|
|
|
SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
|
|
SD_BUS_METHOD_WITH_NAMES("Fixate",
|
|
"s",
|
|
SD_BUS_PARAM(secret),
|
|
NULL,,
|
|
bus_home_method_fixate,
|
|
SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD_WITH_NAMES("Authenticate",
|
|
"s",
|
|
SD_BUS_PARAM(secret),
|
|
NULL,,
|
|
bus_home_method_authenticate,
|
|
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD_WITH_NAMES("Update",
|
|
"s",
|
|
SD_BUS_PARAM(user_record),
|
|
NULL,,
|
|
bus_home_method_update,
|
|
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD_WITH_NAMES("Resize",
|
|
"ts",
|
|
SD_BUS_PARAM(size)
|
|
SD_BUS_PARAM(secret),
|
|
NULL,,
|
|
bus_home_method_resize,
|
|
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD_WITH_NAMES("ChangePassword",
|
|
"ss",
|
|
SD_BUS_PARAM(new_secret)
|
|
SD_BUS_PARAM(old_secret),
|
|
NULL,,
|
|
bus_home_method_change_password,
|
|
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0),
|
|
SD_BUS_METHOD_WITH_NAMES("Unlock",
|
|
"s",
|
|
SD_BUS_PARAM(secret),
|
|
NULL,,
|
|
bus_home_method_unlock,
|
|
SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD_WITH_NAMES("Acquire",
|
|
"sb",
|
|
SD_BUS_PARAM(secret)
|
|
SD_BUS_PARAM(please_suspend),
|
|
"h",
|
|
SD_BUS_PARAM(send_fd),
|
|
bus_home_method_acquire,
|
|
SD_BUS_VTABLE_SENSITIVE),
|
|
SD_BUS_METHOD_WITH_NAMES("Ref",
|
|
"b",
|
|
SD_BUS_PARAM(please_suspend),
|
|
"h",
|
|
SD_BUS_PARAM(send_fd),
|
|
bus_home_method_ref,
|
|
0),
|
|
SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0),
|
|
SD_BUS_VTABLE_END
|
|
};
|
|
|
|
const BusObjectImplementation home_object = {
|
|
"/org/freedesktop/home1/home",
|
|
"org.freedesktop.home1.Home",
|
|
.fallback_vtables = BUS_FALLBACK_VTABLES({home_vtable, bus_home_object_find}),
|
|
.node_enumerator = bus_home_node_enumerator,
|
|
.manager = true,
|
|
};
|
|
|
|
static int on_deferred_change(sd_event_source *s, void *userdata) {
|
|
_cleanup_free_ char *path = NULL;
|
|
Home *h = userdata;
|
|
int r;
|
|
|
|
assert(h);
|
|
|
|
h->deferred_change_event_source = sd_event_source_unref(h->deferred_change_event_source);
|
|
|
|
r = bus_home_path(h, &path);
|
|
if (r < 0) {
|
|
log_warning_errno(r, "Failed to generate home bus path, ignoring: %m");
|
|
return 0;
|
|
}
|
|
|
|
if (h->announced)
|
|
r = sd_bus_emit_properties_changed_strv(h->manager->bus, path, "org.freedesktop.home1.Home", NULL);
|
|
else
|
|
r = sd_bus_emit_object_added(h->manager->bus, path);
|
|
if (r < 0)
|
|
log_warning_errno(r, "Failed to send home change event, ignoring: %m");
|
|
else
|
|
h->announced = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bus_home_emit_change(Home *h) {
|
|
int r;
|
|
|
|
assert(h);
|
|
|
|
if (h->deferred_change_event_source)
|
|
return 1;
|
|
|
|
if (!h->manager->event)
|
|
return 0;
|
|
|
|
if (IN_SET(sd_event_get_state(h->manager->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
|
|
return 0;
|
|
|
|
r = sd_event_add_defer(h->manager->event, &h->deferred_change_event_source, on_deferred_change, h);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to allocate deferred change event source: %m");
|
|
|
|
r = sd_event_source_set_priority(h->deferred_change_event_source, SD_EVENT_PRIORITY_IDLE+5);
|
|
if (r < 0)
|
|
log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
|
|
|
|
(void) sd_event_source_set_description(h->deferred_change_event_source, "deferred-change-event");
|
|
return 1;
|
|
}
|
|
|
|
int bus_home_emit_remove(Home *h) {
|
|
_cleanup_free_ char *path = NULL;
|
|
int r;
|
|
|
|
assert(h);
|
|
|
|
if (!h->announced)
|
|
return 0;
|
|
|
|
r = bus_home_path(h, &path);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_emit_object_removed(h->manager->bus, path);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
h->announced = false;
|
|
return 1;
|
|
}
|