mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
homed: add concept for "adopting" an existing homedir locally
Currently homed scans /home/ via inotify for new .home + .homedir/ popping up to register as local users. Let's also add an explicit way to request this form of "adoption": a bus call that takes a path and that makes a home dir activatable locally. (Usecase: you cross boot between two systems – let's say your traditional fedora and your ParticleOS – and want to use the same homedir from both: simply mount the /home dir from the other somewhere, and then hit "homectl adopt /somewhere/lennart.home" and you have the user locally too).
This commit is contained in:
@@ -1184,6 +1184,19 @@
|
||||
<xi:include href="version-info.xml" xpointer="v245"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>adopt</command> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>…]</term>
|
||||
|
||||
<listitem><para>Adopts one or more existing home directories on the local system. Takes one or more paths to
|
||||
<filename>*.home</filename> LUKS home directories or <filename>*.homedir/</filename> standalone home
|
||||
directories or subvolumes previously created by <filename>systemd-homed</filename> and makes them
|
||||
available locally for login. The referenced files are not moved. This is an alternative for moving
|
||||
such home directories into <filename>/home/</filename> (where they would be picked up
|
||||
automatically).</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>remove</command> <replaceable>USER</replaceable></term>
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ node /org/freedesktop/home1 {
|
||||
@org.freedesktop.systemd1.Privileged("true")
|
||||
DeactivateHome(in s user_name);
|
||||
RegisterHome(in s user_record);
|
||||
AdoptHome(in s image_path,
|
||||
in t flags);
|
||||
UnregisterHome(in s user_name);
|
||||
CreateHome(in s user_record);
|
||||
CreateHomeEx(in s user_record,
|
||||
@@ -160,6 +162,8 @@ node /org/freedesktop/home1 {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="RegisterHome()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="AdoptHome()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="UnregisterHome()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CreateHome()"/>
|
||||
@@ -274,6 +278,12 @@ node /org/freedesktop/home1 {
|
||||
is useful to register home directories locally that are not located where
|
||||
<filename>systemd-homed.service</filename> would find them automatically.</para>
|
||||
|
||||
<para><function>AdoptHome()</function> also registers a new home directory locally. It takes a path to
|
||||
a home directory itself, and will register it locally. This only works for <filename>*.home</filename>
|
||||
and <filename>*.homedir/</filename> home directories. This operation is done automatically for all such
|
||||
home areas showing up in <filename>/home/</filename>, but may be requested explicitly with this call for
|
||||
directories elsewhere. The <varname>flags</varname> must be set to zero, currently.</para>
|
||||
|
||||
<para><function>UnregisterHome()</function> unregisters an existing home directory. It takes a user
|
||||
name as argument and undoes what <function>RegisterHome()</function> does. It does not attempt to
|
||||
remove the home directory itself, it just unregisters it with the local system. Note that if the home
|
||||
@@ -633,9 +643,9 @@ node /org/freedesktop/home1/home {
|
||||
<title>The Manager Object</title>
|
||||
<para><function>ActivateHomeIfReferenced()</function>, <function>RefHomeUnrestricted()</function>,
|
||||
<function>CreateHomeEx()</function>, and <function>UpdateHomeEx()</function> were added in version 256.</para>
|
||||
<para><function>ListSigningKeys()</function>, <function>GetSigningKey()</function>,
|
||||
<function>AddSigningKey()</function>, and <function>RemoveSigningKey()</function> were added in version
|
||||
258.</para>
|
||||
<para><function>AdoptHome()</function>, <function>ListSigningKeys()</function>,
|
||||
<function>GetSigningKey()</function>, <function>AddSigningKey()</function>, and
|
||||
<function>RemoveSigningKey()</function> were added in version 258.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Home Objects</title>
|
||||
|
||||
@@ -173,7 +173,7 @@ _homectl() {
|
||||
fi
|
||||
|
||||
local -A VERBS=(
|
||||
[STANDALONE]='list lock-all'
|
||||
[STANDALONE]='list lock-all adopt'
|
||||
[CREATE]='create'
|
||||
[NAMES]='activate deactivate inspect authenticate remove lock unlock'
|
||||
[NAME]='update passwd'
|
||||
|
||||
@@ -1564,6 +1564,38 @@ static int create_home(int argc, char *argv[], void *userdata) {
|
||||
return create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ true);
|
||||
}
|
||||
|
||||
static int verb_adopt_home(int argc, char *argv[], void *userdata) {
|
||||
int r, ret = 0;
|
||||
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
STRV_FOREACH(i, strv_skip(argv, 1)) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
r = bus_message_new_method_call(bus, &m, bus_mgr, "AdoptHome");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "st", *i, UINT64_C(0));
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to adopt home: %s", bus_error_message(&error, r));
|
||||
if (ret == 0)
|
||||
ret = r;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remove_home(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int r, ret = 0;
|
||||
@@ -2808,6 +2840,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" inspect USER… Inspect a home area\n"
|
||||
" authenticate USER… Authenticate a home area\n"
|
||||
" create USER Create a home area\n"
|
||||
" adopt PATH… Add an existing home area on this system\n"
|
||||
" remove USER… Remove a home area\n"
|
||||
" update USER Update a home area\n"
|
||||
" passwd USER Change password of a home area\n"
|
||||
@@ -5242,6 +5275,7 @@ static int run(int argc, char *argv[]) {
|
||||
{ "inspect", VERB_ANY, VERB_ANY, 0, inspect_home },
|
||||
{ "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home },
|
||||
{ "create", VERB_ANY, 2, 0, create_home },
|
||||
{ "adopt", VERB_ANY, VERB_ANY, 0, verb_adopt_home },
|
||||
{ "remove", 2, VERB_ANY, 0, remove_home },
|
||||
{ "update", VERB_ANY, 2, 0, update_home },
|
||||
{ "passwd", VERB_ANY, 2, 0, passwd_home },
|
||||
|
||||
@@ -519,6 +519,47 @@ static int method_register_home(
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int method_adopt_home(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
const char *image_path = NULL;
|
||||
uint64_t flags = 0;
|
||||
r = sd_bus_message_read(message, "st", &image_path, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_is_absolute(image_path) || !path_is_safe(image_path))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path is not absolute or not valid: %s", image_path);
|
||||
if (flags != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags field must be zero.");
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
message,
|
||||
"org.freedesktop.home1.create-home",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
|
||||
r = manager_adopt_home(m, image_path);
|
||||
if (r == -EMEDIUMTYPE)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNRECOGNIZED_HOME_FORMAT, "Unrecognized format of home directory: %s", image_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int method_unregister_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return generic_home_method(userdata, message, bus_home_method_unregister, error);
|
||||
}
|
||||
@@ -1091,6 +1132,11 @@ static const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_NO_RESULT,
|
||||
method_register_home,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("AdoptHome",
|
||||
SD_BUS_ARGS("s", image_path, "t", flags),
|
||||
SD_BUS_NO_RESULT,
|
||||
method_adopt_home,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
||||
/* Remove the JSON record from homed, but don't remove actual $HOME */
|
||||
SD_BUS_METHOD_WITH_ARGS("UnregisterHome",
|
||||
|
||||
@@ -177,7 +177,7 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
|
||||
else if (FLAGS_SET(event->mask, IN_MOVED_TO))
|
||||
log_debug("%s has been moved in, having a look.", j);
|
||||
|
||||
(void) manager_assess_image(m, -1, get_home_root(), event->name);
|
||||
(void) manager_assess_image(m, /* dir_fd= */ -EBADF, get_home_root(), event->name);
|
||||
(void) bus_manager_emit_auto_login_changed(m);
|
||||
}
|
||||
|
||||
@@ -841,6 +841,10 @@ static int manager_assess_image(
|
||||
assert(dir_path);
|
||||
assert(dentry_name);
|
||||
|
||||
/* Maybe registers the specified .home or .homedir as a home we manage. Returns:
|
||||
*
|
||||
* -EMEDIUMTYPE: Not a dir with .homedir suffix or a file with .home suffix */
|
||||
|
||||
luks_suffix = endswith(dentry_name, ".home");
|
||||
if (luks_suffix)
|
||||
directory_suffix = NULL;
|
||||
@@ -849,7 +853,7 @@ static int manager_assess_image(
|
||||
|
||||
/* Early filter out: by name */
|
||||
if (!luks_suffix && !directory_suffix)
|
||||
return 0;
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
path = path_join(dir_path, dentry_name);
|
||||
if (!path)
|
||||
@@ -868,7 +872,7 @@ static int manager_assess_image(
|
||||
_cleanup_free_ char *n = NULL, *user_name = NULL, *realm = NULL;
|
||||
|
||||
if (!luks_suffix)
|
||||
return 0;
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
n = strndup(dentry_name, luks_suffix - dentry_name);
|
||||
if (!n)
|
||||
@@ -876,7 +880,7 @@ static int manager_assess_image(
|
||||
|
||||
r = split_user_name_realm(n, &user_name, &realm);
|
||||
if (r == -EINVAL) /* Not the right format: ignore */
|
||||
return 0;
|
||||
return -EMEDIUMTYPE;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to split image name into user name/realm: %m");
|
||||
|
||||
@@ -889,7 +893,7 @@ static int manager_assess_image(
|
||||
UserStorage storage;
|
||||
|
||||
if (!directory_suffix)
|
||||
return 0;
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
n = strndup(dentry_name, directory_suffix - dentry_name);
|
||||
if (!n)
|
||||
@@ -897,7 +901,7 @@ static int manager_assess_image(
|
||||
|
||||
r = split_user_name_realm(n, &user_name, &realm);
|
||||
if (r == -EINVAL) /* Not the right format: ignore */
|
||||
return 0;
|
||||
return -EMEDIUMTYPE;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to split image name into user name/realm: %m");
|
||||
|
||||
@@ -939,7 +943,26 @@ static int manager_assess_image(
|
||||
return manager_add_home_by_image(m, user_name, realm, path, NULL, storage, st.st_uid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
int manager_adopt_home(Manager *m, const char *path) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(path);
|
||||
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
r = path_extract_filename(path, &fn);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *dir = NULL;
|
||||
r = path_extract_directory(path, &dir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return manager_assess_image(m, /* dir_fd= */ -EBADF, dir, fn);
|
||||
}
|
||||
|
||||
int manager_enumerate_images(Manager *m) {
|
||||
|
||||
@@ -87,6 +87,8 @@ int manager_reschedule_rebalance(Manager *m);
|
||||
|
||||
int manager_verify_user_record(Manager *m, UserRecord *hr);
|
||||
|
||||
int manager_adopt_home(Manager *m, const char *path);
|
||||
|
||||
int manager_acquire_key_pair(Manager *m);
|
||||
int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus_error *error);
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_REBALANCE_NOT_NEEDED, EALREADY),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_REFERENCED, EBADR),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_KEY, ENOKEY),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_UNRECOGNIZED_HOME_FORMAT, EMEDIUMTYPE),
|
||||
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_UPDATE_CANDIDATE, EALREADY),
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@
|
||||
#define BUS_ERROR_REBALANCE_NOT_NEEDED "org.freedesktop.home1.RebalanceNotNeeded"
|
||||
#define BUS_ERROR_HOME_NOT_REFERENCED "org.freedesktop.home1.HomeNotReferenced"
|
||||
#define BUS_ERROR_NO_SUCH_KEY "org.freedesktop.home1.NoSuchKey"
|
||||
#define BUS_ERROR_UNRECOGNIZED_HOME_FORMAT "org.freedesktop.home1.UnrecognizedHomeFormat"
|
||||
|
||||
#define BUS_ERROR_NO_UPDATE_CANDIDATE "org.freedesktop.sysupdate1.NoCandidate"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user