<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><command>register</command> <replaceable>FILE</replaceable> [<replaceable>FILE</replaceable>…]</term>
+
+ <listitem><para>Registers one or more users, without creating their home directories. Takes one or
+ more paths to JSON user record files. If the path is specified as <literal>-</literal> reads the
+ JSON user record from standard input.</para>
+
+ <para>Registering a user makes it accessible on the local system without creating a new home
+ directory. This is particularly useful for making a user accessible on a system it was not originally
+ created on.</para>
+
+ <para>Here's an example how to make a local user account with its home directory accessible on a
+ remote system, using SMB/CIFS file sharing. With Samba installed in its default configuration invoke
+ as <literal>root</literal>:</para>
+
+ <programlisting># smbpasswd -a lennart</programlisting>
+
+ <para>Continue as regular user <literal>lennart</literal>:</para>
+
+<programlisting>$ homectl update lennart --ssh-authorized-keys=… -N --storage=cifs --cifs-service="//$HOSTNAME/lennart"
+$ homectl get-signing-key | ssh targetsystem homectl add-signing-key --key-name="$HOSTNAME".public
+$ homectl inspect -E lennart | ssh targetsystem homectl register -
+$ ssh lennart@targetsystem</programlisting>
+
+ <para>This first ensures the user account <literal>lennart</literal> is known to and accessible by
+ Samba. It then registers a local SSH access that shall be used for accessing this user, and
+ configures CIFS as default storage for non-local systems on the account. It then adds the local
+ system's account signing key to the target system. Then it registers the local user account with the
+ target system. Finally it logs into the account on the target system. The target system will then
+ connect back via SMB/CIFS to access the home directory.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>unregister</command> <replaceable>USER</replaceable>…</term>
+
+ <listitem><para>Unregisters one or more user accounts. This only removes the user record from the
+ local system, it does not delete the home directory. The home directory can be readded via the
+ <command>register</command> or <command>adopt</command> command later, on this or another
+ system. Note that unregistering a user whose home directory is placed in <filename>/home/</filename>
+ will not make the user disappear from the local user database, as all supported home directories
+ placed there will show up in the user database. However, the user record will become "unfixated",
+ i.e. lose its binding to the local system. When logged into it will automatically regain the binding,
+ and acquire a local UID/GID pair.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><command>remove</command> <replaceable>USER</replaceable></term>
return ret;
}
+static int register_home_one(sd_bus *bus, FILE *f, const char *path) {
+ int r;
+
+ assert(bus);
+ assert(path);
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ unsigned line = 0, column = 0;
+ r = sd_json_parse_file(f, path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "[%s:%u:%u] Failed to parse user record: %m", path, line, column);
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ r = bus_message_new_method_call(bus, &m, bus_mgr, "RegisterHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ _cleanup_free_ char *formatted = NULL;
+ r = sd_json_variant_format(v, /* flags= */ 0, &formatted);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format JSON record: %m");
+
+ r = sd_bus_message_append(m, "s", formatted);
+ 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)
+ return log_error_errno(r, "Failed to register home: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
+static int verb_register_home(int argc, char *argv[], void *userdata) {
+ int r;
+
+ _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);
+
+ if (arg_identity) {
+ if (argc > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not accepting an arguments if --identity= is specified, refusing.");
+
+ return register_home_one(bus, /* f= */ NULL, arg_identity);
+ }
+
+ if (argc == 1 || (argc == 2 && streq(argv[1], "-")))
+ return register_home_one(bus, /* f= */ stdin, "<stdio>");
+
+ r = 0;
+ STRV_FOREACH(i, strv_skip(argv, 1)) {
+ if (streq(*i, "-"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing reading from standard input if multiple user records are specified.");
+
+ RET_GATHER(r, register_home_one(bus, /* f= */ NULL, *i));
+ }
+
+ return r;
+}
+
+static int verb_unregister_home(int argc, char *argv[], void *userdata) {
+ int r;
+
+ _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);
+
+ int ret = 0;
+ 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, "UnregisterHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ 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, /* ret_reply= */ NULL);
+ if (r < 0)
+ RET_GATHER(ret, log_error_errno(r, "Failed to unregister home: %s", bus_error_message(&error, 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;
" 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"
+ " register PATH… Register a user record locally\n"
+ " unregister USER… Unregister a user record locally\n"
" remove USER… Remove a home area\n"
" update USER Update a home area\n"
" passwd USER Change password of a home area\n"
{ "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home },
{ "create", VERB_ANY, 2, 0, create_home },
{ "adopt", VERB_ANY, VERB_ANY, 0, verb_adopt_home },
+ { "register", VERB_ANY, VERB_ANY, 0, verb_register_home },
+ { "unregister", 2, VERB_ANY, 0, verb_unregister_home },
{ "remove", 2, VERB_ANY, 0, remove_home },
{ "update", VERB_ANY, 2, 0, update_home },
{ "passwd", VERB_ANY, 2, 0, passwd_home },