diff --git a/scripts/lvm2_activation_generator_systemd_red_hat.c b/scripts/lvm2_activation_generator_systemd_red_hat.c new file mode 100644 index 000000000..992100499 --- /dev/null +++ b/scripts/lvm2_activation_generator_systemd_red_hat.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "lvm2app.h" + +#define KMSG_DEV_PATH "/dev/kmsg" +#define LVM_CONF_USE_LVMETAD "global/use_lvmetad" + +#define DEFAULT_UNIT_DIR "/tmp" +#define UNIT_NAME_EARLY "lvm2-activation-early.service" +#define UNIT_NAME "lvm2-activation.service" +#define UNIT_TARGET "local-fs.target" + +static char unit_path[PATH_MAX]; +static char target_path[PATH_MAX]; +static char message[PATH_MAX]; +static int kmsg_fd = -1; + +static void kmsg(const char *format, ...) +{ + va_list ap; + int n; + + va_start(ap, format); + n = vsnprintf(message, sizeof(message), format, ap); + va_end(ap); + + if (kmsg_fd < 0 || (n < 0 || ((unsigned) n + 1 > sizeof(message)))) + return; + + write(kmsg_fd, message, n + 1); +} + +static int lvm_uses_lvmetad(void) +{ + lvm_t lvm; + int r; + + if (!(lvm = lvm_init(NULL))) { + kmsg("LVM: Failed to initialize library context for activation generator.\n"); + return 0; + } + r = lvm_config_find_bool(lvm, LVM_CONF_USE_LVMETAD, 0); + lvm_quit(lvm); + + return r; +} + +static int register_unit_with_target(const char *dir, const char *unit, const char *target) +{ + int r = 1; + + if (dm_snprintf(target_path, PATH_MAX, "%s/%s.wants", dir, target) < 0) { + r = 0; goto out; + } + (void) dm_prepare_selinux_context(target_path, S_IFDIR); + if (mkdir(target_path, 0755) < 0 && errno != EEXIST) { + kmsg("LVM: Failed to create target directory %s: %m.\n", target_path); + r = 0; goto out; + } + + if (dm_snprintf(target_path, PATH_MAX, "%s/%s.wants/%s", dir, target, unit) < 0) { + r = 0; goto out; + } + (void) dm_prepare_selinux_context(target_path, S_IFLNK); + if (symlink(unit_path, target_path) < 0) { + kmsg("LVM: Failed to create symlink for unit %s: %m.\n", unit); + r = 0; + } +out: + dm_prepare_selinux_context(NULL, 0); + return r; +} + +static int generate_unit(const char *dir, int early) +{ + FILE *f; + const char *unit = early ? UNIT_NAME_EARLY : UNIT_NAME; + + if (dm_snprintf(unit_path, PATH_MAX, "%s/%s", dir, unit) < 0) + return 0; + + if (!(f = fopen(unit_path, "wxe"))) { + kmsg("LVM: Failed to create unit file %s: %m.\n", unit); + return 0; + } + + fputs("# Automatically generated by lvm2-activation-generator.\n" + "#\n" + "# This unit is responsible for direct activation of LVM2 logical volumes\n" + "# if lvmetad daemon is not used (global/use_lvmetad=0 lvm.conf setting),\n" + "# hence volume autoactivation is not applicable.\n" + "# Direct LVM2 activation requires udev to be settled!\n\n" + "[Unit]\n" + "Description=Activation of LVM2 logical volumes\n" + "Documentation=man:lvm(8) man:vgchange(8)\n" + "SourcePath=/etc/lvm/lvm.conf\n" + "DefaultDependencies=no\n", f); + + if (early) + fputs("After=fedora-wait-storage.service\n", f); + else + fputs("After=lvm2-activation-early.service cryptsetup.target\n", f); + + fputs("Before=local-fs.target shutdown.target\n" + "Wants=fedora-wait-storage.service\n\n" + "[Service]\n" + "ExecStart=/usr/sbin/lvm vgchange -aay --sysinit\n" + "Type=oneshot\n", f); + + if (fclose(f) < 0) { + kmsg("LVM: Failed to write unit file %s: %m.\n", unit); + return 0; + } + + if (!register_unit_with_target(dir, unit, UNIT_TARGET)) { + kmsg("LVM: Failed to register unit %s with target %s.\n", unit, UNIT_TARGET); + return 0; + } + + return 1; +} + +int main(int argc, char *argv[]) +{ + const char *dir; + int r = EXIT_SUCCESS; + + kmsg_fd = open(KMSG_DEV_PATH, O_WRONLY|O_NOCTTY|O_CLOEXEC); + + if (argc > 1 && argc != 4) { + kmsg("LVM: Activation generator takes three or no arguments.\n"); + r = EXIT_FAILURE; goto out; + } + + /* If lvmetad used, rely on autoactivation instead of direct activation. */ + if (lvm_uses_lvmetad()) { + kmsg("LVM: Logical Volume autoactivation enabled.\n"); + goto out; + } + + dir = argc > 1 ? argv[1] : DEFAULT_UNIT_DIR; + + if (!generate_unit(dir, 1) || !generate_unit(dir, 0)) + r = EXIT_FAILURE; +out: + kmsg("LVM: Activation generator %s.\n", r ? "failed" : "successfully completed"); + if (kmsg_fd != -1) + close(kmsg_fd); + return r; +}