|
Line 0
Link Here
|
|
|
1 |
/* |
| 2 |
* Copyright (C) 2012 Red Hat, Inc. All rights reserved. |
| 3 |
* |
| 4 |
* This file is part of the device-mapper userspace tools. |
| 5 |
* |
| 6 |
* This copyrighted material is made available to anyone wishing to use, |
| 7 |
* modify, copy, or redistribute it subject to the terms and conditions |
| 8 |
* of the GNU General Public License v.2. |
| 9 |
* |
| 10 |
* You should have received a copy of the GNU General Public License |
| 11 |
* along with this program; if not, write to the Free Software Foundation, |
| 12 |
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 13 |
*/ |
| 14 |
|
| 15 |
#include <stdio.h> |
| 16 |
#include <unistd.h> |
| 17 |
#include <errno.h> |
| 18 |
#include <stdarg.h> |
| 19 |
#include <sys/types.h> |
| 20 |
#include <sys/stat.h> |
| 21 |
#include <fcntl.h> |
| 22 |
#include "lvm2app.h" |
| 23 |
|
| 24 |
#define KMSG_DEV_PATH "/dev/kmsg" |
| 25 |
#define LVM_CONF_USE_LVMETAD "global/use_lvmetad" |
| 26 |
|
| 27 |
#define DEFAULT_UNIT_DIR "/tmp" |
| 28 |
#define UNIT_NAME_EARLY "lvm2-activation-early.service" |
| 29 |
#define UNIT_NAME "lvm2-activation.service" |
| 30 |
#define UNIT_TARGET "local-fs.target" |
| 31 |
|
| 32 |
static char unit_path[PATH_MAX]; |
| 33 |
static char target_path[PATH_MAX]; |
| 34 |
static char message[PATH_MAX]; |
| 35 |
static int kmsg_fd = -1; |
| 36 |
|
| 37 |
__attribute__ ((format(printf, 1, 2))) |
| 38 |
static void kmsg(const char *format, ...) |
| 39 |
{ |
| 40 |
va_list ap; |
| 41 |
int n; |
| 42 |
|
| 43 |
va_start(ap, format); |
| 44 |
n = vsnprintf(message, sizeof(message), format, ap); |
| 45 |
va_end(ap); |
| 46 |
|
| 47 |
if (kmsg_fd < 0 || (n < 0 || ((unsigned) n + 1 > sizeof(message)))) |
| 48 |
return; |
| 49 |
|
| 50 |
(void) write(kmsg_fd, message, n + 1); |
| 51 |
} |
| 52 |
|
| 53 |
static int lvm_uses_lvmetad(void) |
| 54 |
{ |
| 55 |
lvm_t lvm; |
| 56 |
int r; |
| 57 |
|
| 58 |
if (!(lvm = lvm_init(NULL))) { |
| 59 |
kmsg("LVM: Failed to initialize library context for activation generator.\n"); |
| 60 |
return 0; |
| 61 |
} |
| 62 |
r = lvm_config_find_bool(lvm, LVM_CONF_USE_LVMETAD, 0); |
| 63 |
lvm_quit(lvm); |
| 64 |
|
| 65 |
return r; |
| 66 |
} |
| 67 |
|
| 68 |
static int register_unit_with_target(const char *dir, const char *unit, const char *target) |
| 69 |
{ |
| 70 |
int r = 1; |
| 71 |
|
| 72 |
if (dm_snprintf(target_path, PATH_MAX, "%s/%s.wants", dir, target) < 0) { |
| 73 |
r = 0; goto out; |
| 74 |
} |
| 75 |
(void) dm_prepare_selinux_context(target_path, S_IFDIR); |
| 76 |
if (mkdir(target_path, 0755) < 0 && errno != EEXIST) { |
| 77 |
kmsg("LVM: Failed to create target directory %s: %m.\n", target_path); |
| 78 |
r = 0; goto out; |
| 79 |
} |
| 80 |
|
| 81 |
if (dm_snprintf(target_path, PATH_MAX, "%s/%s.wants/%s", dir, target, unit) < 0) { |
| 82 |
r = 0; goto out; |
| 83 |
} |
| 84 |
(void) dm_prepare_selinux_context(target_path, S_IFLNK); |
| 85 |
if (symlink(unit_path, target_path) < 0) { |
| 86 |
kmsg("LVM: Failed to create symlink for unit %s: %m.\n", unit); |
| 87 |
r = 0; |
| 88 |
} |
| 89 |
out: |
| 90 |
dm_prepare_selinux_context(NULL, 0); |
| 91 |
return r; |
| 92 |
} |
| 93 |
|
| 94 |
static int generate_unit(const char *dir, int early) |
| 95 |
{ |
| 96 |
FILE *f; |
| 97 |
const char *unit = early ? UNIT_NAME_EARLY : UNIT_NAME; |
| 98 |
|
| 99 |
if (dm_snprintf(unit_path, PATH_MAX, "%s/%s", dir, unit) < 0) |
| 100 |
return 0; |
| 101 |
|
| 102 |
if (!(f = fopen(unit_path, "wxe"))) { |
| 103 |
kmsg("LVM: Failed to create unit file %s: %m.\n", unit); |
| 104 |
return 0; |
| 105 |
} |
| 106 |
|
| 107 |
fputs("# Automatically generated by lvm2-activation-generator.\n" |
| 108 |
"#\n" |
| 109 |
"# This unit is responsible for direct activation of LVM2 logical volumes\n" |
| 110 |
"# if lvmetad daemon is not used (global/use_lvmetad=0 lvm.conf setting),\n" |
| 111 |
"# hence volume autoactivation is not applicable.\n" |
| 112 |
"# Direct LVM2 activation requires udev to be settled!\n\n" |
| 113 |
"[Unit]\n" |
| 114 |
"Description=Activation of LVM2 logical volumes\n" |
| 115 |
"Documentation=man:lvm(8) man:vgchange(8)\n" |
| 116 |
"SourcePath=/etc/lvm/lvm.conf\n" |
| 117 |
"DefaultDependencies=no\n", f); |
| 118 |
|
| 119 |
if (early) { |
| 120 |
fputs("After=systemd-udev-settle.service\n", f); |
| 121 |
fputs("Before=cryptsetup.target\n", f); |
| 122 |
} else |
| 123 |
fputs("After=lvm2-activation-early.service cryptsetup.target\n", f); |
| 124 |
|
| 125 |
fputs("Before=local-fs.target shutdown.target\n" |
| 126 |
"Wants=systemd-udev-settle.service\n\n" |
| 127 |
"[Service]\n" |
| 128 |
"ExecStart=@sbindir@/lvm vgchange -aay --sysinit\n" |
| 129 |
"Type=oneshot\n", f); |
| 130 |
|
| 131 |
if (fclose(f) < 0) { |
| 132 |
kmsg("LVM: Failed to write unit file %s: %m.\n", unit); |
| 133 |
return 0; |
| 134 |
} |
| 135 |
|
| 136 |
if (!register_unit_with_target(dir, unit, UNIT_TARGET)) { |
| 137 |
kmsg("LVM: Failed to register unit %s with target %s.\n", unit, UNIT_TARGET); |
| 138 |
return 0; |
| 139 |
} |
| 140 |
|
| 141 |
return 1; |
| 142 |
} |
| 143 |
|
| 144 |
int main(int argc, char *argv[]) |
| 145 |
{ |
| 146 |
const char *dir; |
| 147 |
int r = EXIT_SUCCESS; |
| 148 |
|
| 149 |
kmsg_fd = open(KMSG_DEV_PATH, O_WRONLY|O_NOCTTY); |
| 150 |
|
| 151 |
if (argc > 1 && argc != 4) { |
| 152 |
kmsg("LVM: Activation generator takes three or no arguments.\n"); |
| 153 |
r = EXIT_FAILURE; goto out; |
| 154 |
} |
| 155 |
|
| 156 |
/* If lvmetad used, rely on autoactivation instead of direct activation. */ |
| 157 |
if (lvm_uses_lvmetad()) { |
| 158 |
kmsg("LVM: Logical Volume autoactivation enabled.\n"); |
| 159 |
goto out; |
| 160 |
} |
| 161 |
|
| 162 |
dir = argc > 1 ? argv[1] : DEFAULT_UNIT_DIR; |
| 163 |
|
| 164 |
if (!generate_unit(dir, 1) || !generate_unit(dir, 0)) |
| 165 |
r = EXIT_FAILURE; |
| 166 |
out: |
| 167 |
kmsg("LVM: Activation generator %s.\n", r ? "failed" : "successfully completed"); |
| 168 |
if (kmsg_fd != -1) |
| 169 |
(void) close(kmsg_fd); |
| 170 |
return r; |
| 171 |
} |