/* * 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; __attribute__ ((format(printf, 1, 2))) 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; (void) 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=systemd-udev-settle.service\n", f); fputs("Before=cryptsetup.target\n", f); } else fputs("After=lvm2-activation-early.service cryptsetup.target\n", f); fputs("Before=local-fs.target shutdown.target\n" "Wants=systemd-udev-settle.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); 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) (void) close(kmsg_fd); return r; }