chiark / gitweb /
cryptsetup-generator: Split main() into more functions and use hasmaps
authorJan Janssen <medhefgo@web.de>
Tue, 2 Dec 2014 17:49:28 +0000 (18:49 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 5 Dec 2014 00:27:00 +0000 (01:27 +0100)
man/systemd-cryptsetup-generator.xml
src/cryptsetup/cryptsetup-generator.c

index 3abb39d550b44f4bd72daf5cdd84f8c58714a95d..ff94e88f996c994f2a69c4079f3fcafa06ba8031 100644 (file)
                                 activate the specified device as part
                                 of the boot process as if it was
                                 listed in
                                 activate the specified device as part
                                 of the boot process as if it was
                                 listed in
-                                <filename>/etc/fstab</filename>. This
+                                <filename>/etc/crypttab</filename>. This
                                 option may be specified more than once
                                 in order to set up multiple
                                 devices. <varname>rd.luks.uuid=</varname>
                                 option may be specified more than once
                                 in order to set up multiple
                                 devices. <varname>rd.luks.uuid=</varname>
                                 honored by both the main system and
                                 the initrd.</para>
                                 <para>If /etc/crypttab contains entries with
                                 honored by both the main system and
                                 the initrd.</para>
                                 <para>If /etc/crypttab contains entries with
-                                the same UUID, then the options for this entry
-                                will be used.</para>
-                                <para>If /etc/crypttab exists, only those UUID
+                                the same UUID, then the name, keyfile and options
+                                specified there will be used. Otherwise the device
+                                will have the name <literal>luks-UUID</literal>.</para>
+                                <para>If /etc/crypttab exists, only those UUIDs
                                 specified on the kernel command line
                                 will be activated in the initrd or the real root.</para>
                                 </listitem>
                                 specified on the kernel command line
                                 will be activated in the initrd or the real root.</para>
                                 </listitem>
index 45c23bb323072aee38644463ecb4d2d498416fdd..c1581ef9c8a7be55a18044eed0c2356803780631 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <string.h>
 #include <errno.h>
 #include <errno.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <unistd.h>
 
+#include "dropin.h"
+#include "fileio.h"
+#include "generator.h"
+#include "hashmap.h"
 #include "log.h"
 #include "log.h"
-#include "util.h"
-#include "unit-name.h"
 #include "mkdir.h"
 #include "mkdir.h"
-#include "strv.h"
-#include "fileio.h"
 #include "path-util.h"
 #include "path-util.h"
-#include "dropin.h"
-#include "generator.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "util.h"
+
+typedef struct crypto_device {
+        char *uuid;
+        char *options;
+        bool create;
+} crypto_device;
 
 static const char *arg_dest = "/tmp";
 static bool arg_enabled = true;
 static bool arg_read_crypttab = true;
 
 static const char *arg_dest = "/tmp";
 static bool arg_enabled = true;
 static bool arg_read_crypttab = true;
-static char **arg_disks = NULL;
-static char **arg_options = NULL;
-static char *arg_keyfile = NULL;
+static bool arg_whitelist = false;
+static Hashmap *arg_disks = NULL;
+static char *arg_default_options = NULL;
+static char *arg_default_keyfile = NULL;
 
 static bool has_option(const char *haystack, const char *needle) {
         const char *f = haystack;
 
 static bool has_option(const char *haystack, const char *needle) {
         const char *f = haystack;
@@ -251,8 +259,54 @@ static int create_disk(
         return 0;
 }
 
         return 0;
 }
 
+static void free_arg_disks(void) {
+        crypto_device *d;
+
+        while ((d = hashmap_steal_first(arg_disks))) {
+                free(d->uuid);
+                free(d->options);
+                free(d);
+        }
+
+        hashmap_free(arg_disks);
+}
+
+static crypto_device *get_crypto_device(const char *uuid) {
+        int r;
+        crypto_device *d;
+
+        assert(uuid);
+
+        d = hashmap_get(arg_disks, uuid);
+        if (!d) {
+                d = new0(struct crypto_device, 1);
+                if (!d)
+                        return NULL;
+
+                d->create = false;
+                d->options = NULL;
+
+                d->uuid = strdup(uuid);
+                if (!d->uuid) {
+                        free(d);
+                        return NULL;
+                }
+
+                r = hashmap_put(arg_disks, d->uuid, d);
+                if (r < 0) {
+                        free(d->uuid);
+                        free(d);
+                        return NULL;
+                }
+        }
+
+        return d;
+}
+
 static int parse_proc_cmdline_item(const char *key, const char *value) {
         int r;
 static int parse_proc_cmdline_item(const char *key, const char *value) {
         int r;
+        crypto_device *d;
+        _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
 
         if (STR_IN_SET(key, "luks", "rd.luks") && value) {
 
 
         if (STR_IN_SET(key, "luks", "rd.luks") && value) {
 
@@ -272,19 +326,29 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
 
         } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
 
 
         } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
 
-                if (strv_extend(&arg_disks, value) < 0)
+                d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
+                if (!d)
                         return log_oom();
 
                         return log_oom();
 
+                d->create = arg_whitelist = true;
+
         } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
 
         } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
 
-                if (strv_extend(&arg_options, value) < 0)
+                r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
+                if (r == 2) {
+                        d = get_crypto_device(uuid);
+                        if (!d)
+                                return log_oom();
+
+                        free(d->options);
+                        d->options = uuid_value;
+                        uuid_value = NULL;
+                } else if (free_and_strdup(&arg_default_options, value) < 0)
                         return log_oom();
 
         } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
 
                         return log_oom();
 
         } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
 
-                free(arg_keyfile);
-                arg_keyfile = strdup(value);
-                if (!arg_keyfile)
+                if (free_and_strdup(&arg_default_keyfile, value))
                         return log_oom();
 
         }
                         return log_oom();
 
         }
@@ -292,213 +356,157 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
         return 0;
 }
 
         return 0;
 }
 
-int main(int argc, char *argv[]) {
-        _cleanup_strv_free_ char **disks_done = NULL;
+static int add_crypttab_devices(void) {
+        struct stat st;
+        unsigned crypttab_line = 0;
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        unsigned n = 0;
-        int r = EXIT_FAILURE, r2 = EXIT_FAILURE, z;
-        char **i;
 
 
-        if (argc > 1 && argc != 4) {
-                log_error("This program takes three or no arguments.");
-                return EXIT_FAILURE;
+        if (!arg_read_crypttab)
+                return 0;
+
+        f = fopen("/etc/crypttab", "re");
+        if (!f) {
+                if (errno != ENOENT)
+                        log_error_errno(errno, "Failed to open /etc/crypttab: %m");
+                return 0;
         }
 
         }
 
-        if (argc > 1)
-                arg_dest = argv[1];
+        if (fstat(fileno(f), &st) < 0) {
+                log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
+                return 0;
+        }
 
 
-        log_set_target(LOG_TARGET_SAFE);
-        log_parse_environment();
-        log_open();
+        /* If we readd support for specifying passphrases
+         * directly in crypttab we should upgrade the warning
+         * below, though possibly only if a passphrase is
+         * specified directly. */
+        if (st.st_mode & 0005)
+                log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
 
 
-        umask(0022);
+        for (;;) {
+                int r, k;
+                char line[LINE_MAX], *l, *uuid;
+                crypto_device *d = NULL;
+                _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
 
 
-        z = parse_proc_cmdline(parse_proc_cmdline_item);
-        if (z < 0)
-                log_warning_errno(z, "Failed to parse kernel command line, ignoring: %m");
+                if (!fgets(line, sizeof(line), f))
+                        break;
 
 
-        if (!arg_enabled) {
-                r = r2 = EXIT_SUCCESS;
-                goto cleanup;
-        }
+                crypttab_line++;
 
 
-        strv_uniq(arg_disks);
+                l = strstrip(line);
+                if (*l == '#' || *l == 0)
+                        continue;
 
 
-        if (arg_read_crypttab) {
-                struct stat st;
+                k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
+                if (k < 2 || k > 4) {
+                        log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
+                        continue;
+                }
 
 
-                f = fopen("/etc/crypttab", "re");
-                if (!f) {
-                        if (errno == ENOENT)
-                                r = EXIT_SUCCESS;
-                        else
-                                log_error_errno(errno, "Failed to open /etc/crypttab: %m");
+                uuid = startswith(device, "UUID=");
+                if (!uuid)
+                        uuid = path_startswith(device, "/dev/disk/by-uuid/");
+                if (!uuid)
+                        uuid = startswith(name, "luks-");
+                if (uuid)
+                        d = hashmap_get(arg_disks, uuid);
 
 
-                        goto next;
+                if (arg_whitelist && !d) {
+                        log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
+                        continue;
                 }
 
                 }
 
-                if (fstat(fileno(f), &st) < 0) {
-                        log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
-                        goto next;
-                }
+                r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
+                if (r < 0)
+                        return r;
 
 
-                /* If we readd support for specifying passphrases
-                 * directly in crypttabe we should upgrade the warning
-                 * below, though possibly only if a passphrase is
-                 * specified directly. */
-                if (st.st_mode & 0005)
-                        log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
+                if (d)
+                        d->create = false;
+        }
 
 
-                for (;;) {
-                        char line[LINE_MAX], *l;
-                        _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
-                        int k;
+        return 0;
+}
 
 
-                        if (!fgets(line, sizeof(line), f))
-                                break;
+static int add_proc_cmdline_devices(void) {
+        int r;
+        Iterator i;
+        crypto_device *d;
 
 
-                        n++;
+        HASHMAP_FOREACH(d, arg_disks, i) {
+                const char *options;
+                _cleanup_free_ char *name = NULL, *device = NULL;
 
 
-                        l = strstrip(line);
-                        if (*l == '#' || *l == 0)
-                                continue;
+                if (!d->create)
+                        continue;
 
 
-                        k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
-                        if (k < 2 || k > 4) {
-                                log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
-                                continue;
-                        }
+                name = strappend("luks-", d->uuid);
+                if (!name)
+                        return log_oom();
 
 
-                        /*
-                          If options are specified on the kernel command line, let them override
-                          the ones from crypttab.
-                        */
-                        STRV_FOREACH(i, arg_options) {
-                                _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
-                                const char *p = *i;
-
-                                k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
-                                if (k == 2 && streq(proc_uuid, device + 5)) {
-                                        free(options);
-                                        options = strdup(p);
-                                        if (!options) {
-                                                log_oom();
-                                                goto cleanup;
-                                        }
-                                }
-                        }
+                device = strappend("UUID=", d->uuid);
+                if (!device)
+                        return log_oom();
 
 
-                        if (arg_disks) {
-                                /*
-                                  If luks UUIDs are specified on the kernel command line, use them as a filter
-                                  for /etc/crypttab and only generate units for those.
-                                */
-                                STRV_FOREACH(i, arg_disks) {
-                                        _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
-                                        const char *p = *i;
-
-                                        if (startswith(p, "luks-"))
-                                                p += 5;
-
-                                        proc_name = strappend("luks-", p);
-                                        proc_device = strappend("UUID=", p);
-
-                                        if (!proc_name || !proc_device) {
-                                                log_oom();
-                                                goto cleanup;
-                                        }
-
-                                        if (streq(proc_device, device) || streq(proc_name, name)) {
-                                                if (create_disk(name, device, password, options) < 0)
-                                                        goto cleanup;
-
-                                                if (strv_extend(&disks_done, p) < 0) {
-                                                        log_oom();
-                                                        goto cleanup;
-                                                }
-                                        }
-                                }
-                        } else if (create_disk(name, device, password, options) < 0)
-                                goto cleanup;
+                if (d->options)
+                        options = d->options;
+                else if (arg_default_options)
+                        options = arg_default_options;
+                else
+                        options = "timeout=0";
 
 
-                }
+                r = create_disk(name, device, arg_default_keyfile, options);
+                if (r < 0)
+                        return r;
         }
 
         }
 
-        r = EXIT_SUCCESS;
-
-next:
-        STRV_FOREACH(i, arg_disks) {
-                /*
-                  Generate units for those UUIDs, which were specified
-                  on the kernel command line and not yet written.
-                */
+        return 0;
+}
 
 
-                _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
-                const char *p = *i;
+int main(int argc, char *argv[]) {
+        int r = EXIT_FAILURE;
 
 
-                if (startswith(p, "luks-"))
-                        p += 5;
+        if (argc > 1 && argc != 4) {
+                log_error("This program takes three or no arguments.");
+                return EXIT_FAILURE;
+        }
 
 
-                if (strv_contains(disks_done, p))
-                        continue;
+        if (argc > 1)
+                arg_dest = argv[1];
 
 
-                name = strappend("luks-", p);
-                device = strappend("UUID=", p);
+        log_set_target(LOG_TARGET_SAFE);
+        log_parse_environment();
+        log_open();
 
 
-                if (!name || !device) {
-                        log_oom();
-                        goto cleanup;
-                }
+        umask(0022);
 
 
-                if (arg_options) {
-                        /*
-                          If options are specified on the kernel command line, use them.
-                        */
-                        char **j;
-
-                        STRV_FOREACH(j, arg_options) {
-                                _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
-                                const char *s = *j;
-                                int k;
-
-                                k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
-                                if (k == 2) {
-                                        if (streq(proc_uuid, device + 5)) {
-                                                free(options);
-                                                options = proc_options;
-                                                proc_options = NULL;
-                                        }
-                                } else if (!options) {
-                                        /*
-                                          Fall back to options without a specified UUID
-                                        */
-                                        options = strdup(s);
-                                        if (!options) {
-                                                log_oom();
-                                                goto cleanup;
-                                        };
-                                }
-                        }
-                }
+        arg_disks = hashmap_new(&string_hash_ops);
+        if (!arg_disks)
+                goto cleanup;
 
 
-                if (!options) {
-                        options = strdup("timeout=0");
-                        if (!options) {
-                                log_oom();
-                                goto cleanup;
-                        }
-                }
+        r = parse_proc_cmdline(parse_proc_cmdline_item);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+                r = EXIT_FAILURE;
+        }
 
 
-                if (create_disk(name, device, arg_keyfile, options) < 0)
-                        goto cleanup;
+        if (!arg_enabled) {
+                r = EXIT_SUCCESS;
+                goto cleanup;
         }
 
         }
 
-        r2 = EXIT_SUCCESS;
+        if (add_crypttab_devices() < 0)
+                goto cleanup;
+
+        if (add_proc_cmdline_devices() < 0)
+                goto cleanup;
+
+        r = EXIT_SUCCESS;
 
 cleanup:
 
 cleanup:
-        strv_free(arg_disks);
-        strv_free(arg_options);
-        free(arg_keyfile);
+        free_arg_disks();
+        free(arg_default_options);
+        free(arg_default_keyfile);
 
 
-        return r != EXIT_SUCCESS ? r : r2;
+        return r;
 }
 }