mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-23 21:35:29 +03:00
5529aec0d6
./configure --with-clvmd wil do this by default. Or you can choose which you want with ./configure --with-clvmd=gulm or ./configure --with-clvmd=cman When clvmd with both included is run, it will automatically detect the cluster manager in use.
372 lines
8.5 KiB
C
372 lines
8.5 KiB
C
/*
|
|
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/* Routines dealing with the System LV */
|
|
|
|
#include <pthread.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/un.h>
|
|
#include <sys/time.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/utsname.h>
|
|
#include <syslog.h>
|
|
#include <netinet/in.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <mntent.h>
|
|
|
|
#include "libdlm.h"
|
|
#include "log.h"
|
|
#include "list.h"
|
|
#include "locking.h"
|
|
#include "system-lv.h"
|
|
#include "clvm.h"
|
|
#include "clvmd-comms.h"
|
|
#include "clvmd.h"
|
|
#ifdef HAVE_CCS
|
|
#include "ccs.h"
|
|
#endif
|
|
|
|
#define SYSTEM_LV_FILESYSTEM "ext2"
|
|
#define SYSTEM_LV_MOUNTPOINT "/tmp/.clvmd-XXXXXX"
|
|
|
|
extern char *config_filename(void);
|
|
|
|
static char system_lv_name[PATH_MAX] = { '\0' };
|
|
static char mount_point[PATH_MAX] = { '\0' };
|
|
static int mounted = 0;
|
|
static int mounted_rw = 0;
|
|
static int lockid;
|
|
static const char *lock_name = "CLVM_SYSTEM_LV";
|
|
|
|
/* Look in /proc/mounts or (as a last resort) /etc/mtab to
|
|
see if the system-lv is mounted. If it is mounted and we
|
|
think it's not then abort because we don't have the right
|
|
lock status and we don't know what other processes are doing with it.
|
|
|
|
Returns 1 for mounted, 0 for not mounted so it matches the condition
|
|
of the "mounted" static variable above.
|
|
*/
|
|
static int is_really_mounted(void)
|
|
{
|
|
FILE *mountfile;
|
|
struct mntent *ment;
|
|
|
|
mountfile = setmntent("/proc/mounts", "r");
|
|
if (!mountfile) {
|
|
mountfile = setmntent("/etc/mtab", "r");
|
|
if (!mountfile) {
|
|
log_error("Unable to open /proc/mounts or /etc/mtab");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Look for system LV name in the file */
|
|
do {
|
|
ment = getmntent(mountfile);
|
|
if (ment) {
|
|
if (strcmp(ment->mnt_fsname, system_lv_name) == 0) {
|
|
endmntent(mountfile);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
while (ment);
|
|
|
|
endmntent(mountfile);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the system LV name from the config file */
|
|
static int find_system_lv(void)
|
|
{
|
|
if (system_lv_name[0] == '\0') {
|
|
#ifdef HAVE_CCS
|
|
int error;
|
|
ccs_node_t *ctree;
|
|
|
|
/* Read the cluster config file */
|
|
/* Open the config file */
|
|
error = open_ccs_file(&ctree, "clvm.ccs");
|
|
if (error) {
|
|
perror("reading config file");
|
|
return -1;
|
|
}
|
|
|
|
strcpy(system_lv_name, find_ccs_str(ctree,
|
|
"cluster/systemlv", '/',
|
|
"/dev/vg/system_lv"));
|
|
|
|
/* Finished with config file */
|
|
close_ccs_file(ctree);
|
|
#else
|
|
if (getenv("CLVMD_SYSTEM_LV"))
|
|
strcpy(system_lv_name, getenv("CLVMD_SYSTEM_LV"));
|
|
else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/* See if it has been mounted outside our control */
|
|
if (is_really_mounted() != mounted) {
|
|
log_error
|
|
("The system LV state has been mounted/umounted outside the control of clvmd\n"
|
|
"it cannot not be used for cluster communications until this is fixed.\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* No prizes */
|
|
int system_lv_umount(void)
|
|
{
|
|
if (!mounted)
|
|
return 0;
|
|
|
|
if (umount(mount_point) < 0) {
|
|
log_error("umount of system LV (%s) failed: %m\n",
|
|
system_lv_name);
|
|
return -1;
|
|
}
|
|
|
|
sync_unlock(lock_name, lockid);
|
|
mounted = 0;
|
|
|
|
/* Remove the mount point */
|
|
rmdir(mount_point);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int system_lv_mount(int readwrite)
|
|
{
|
|
int status;
|
|
int saved_errno;
|
|
int fd;
|
|
|
|
if (find_system_lv()) {
|
|
errno = EBUSY;
|
|
return -1;
|
|
}
|
|
|
|
/* Is it already mounted suitably? */
|
|
if (mounted) {
|
|
if (!readwrite || (readwrite && mounted_rw)) {
|
|
return 0;
|
|
} else {
|
|
/* Mounted RO and we need RW */
|
|
if (system_lv_umount() < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Randomize the mount point */
|
|
strcpy(mount_point, SYSTEM_LV_MOUNTPOINT);
|
|
fd = mkstemp(mount_point);
|
|
if (fd < 0) {
|
|
log_error("mkstemp for system LV mount point failed: %m\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Race condition here but there's no mkstemp for directories */
|
|
close(fd);
|
|
unlink(mount_point);
|
|
mkdir(mount_point, 0600);
|
|
|
|
/* Make sure we have a system-lv lock */
|
|
status =
|
|
sync_lock(lock_name, (readwrite) ? LKM_EXMODE : LKM_CRMODE, 0,
|
|
&lockid);
|
|
if (status < 0)
|
|
return -1;
|
|
|
|
/* Mount it */
|
|
if (mount(system_lv_name, mount_point, SYSTEM_LV_FILESYSTEM,
|
|
MS_MGC_VAL | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_SYNCHRONOUS
|
|
| (readwrite ? 0 : MS_RDONLY), NULL) < 0) {
|
|
/* mount(2) returns EINVAL if the volume has no FS on it. So, if we want to
|
|
write to it we try to make a filesystem in it and retry the mount */
|
|
if (errno == EINVAL && readwrite) {
|
|
char cmd[256];
|
|
|
|
log_error("Attempting mkfs on system LV device %s\n",
|
|
system_lv_name);
|
|
snprintf(cmd, sizeof(cmd), "/sbin/mkfs -t %s %s",
|
|
SYSTEM_LV_FILESYSTEM, system_lv_name);
|
|
system(cmd);
|
|
|
|
if (mount
|
|
(system_lv_name, mount_point, SYSTEM_LV_FILESYSTEM,
|
|
MS_MGC_VAL | MS_NOSUID | MS_NODEV | MS_NOEXEC |
|
|
MS_SYNCHRONOUS | (readwrite ? 0 : MS_RDONLY),
|
|
NULL) == 0)
|
|
goto mounted;
|
|
}
|
|
|
|
saved_errno = errno;
|
|
log_error("mount of system LV (%s, %s, %s) failed: %m\n",
|
|
system_lv_name, mount_point, SYSTEM_LV_FILESYSTEM);
|
|
sync_unlock(lock_name, lockid);
|
|
errno = saved_errno;
|
|
return -1;
|
|
}
|
|
|
|
mounted:
|
|
/* Set the internal flags */
|
|
mounted = 1;
|
|
mounted_rw = readwrite;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Erase *all* files in the root directory of the system LV.
|
|
This *MUST* be called with an appropriate lock held!
|
|
The LV is left mounted RW because it is assumed that the
|
|
caller wants to write something here after clearing some space */
|
|
int system_lv_eraseall(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *ent;
|
|
char fname[PATH_MAX];
|
|
|
|
/* Must be mounted R/W */
|
|
system_lv_mount(1);
|
|
|
|
dir = opendir(mount_point);
|
|
if (!dir)
|
|
return -1;
|
|
|
|
while ((ent = readdir(dir))) {
|
|
struct stat st;
|
|
snprintf(fname, sizeof(fname), "%s/%s", mount_point,
|
|
ent->d_name);
|
|
|
|
if (stat(fname, &st)) {
|
|
if (S_ISREG(st.st_mode))
|
|
unlink(fname);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
return 0;
|
|
}
|
|
|
|
/* This is a "high-level" routine - it mounts the system LV, writes
|
|
the data into a file named after this node and then umounts the LV
|
|
again */
|
|
int system_lv_write_data(char *data, ssize_t len)
|
|
{
|
|
struct utsname nodeinfo;
|
|
char fname[PATH_MAX];
|
|
int outfile;
|
|
ssize_t thiswrite;
|
|
ssize_t written;
|
|
|
|
if (system_lv_mount(1))
|
|
return -1;
|
|
|
|
/* Build the file name we are goingto use. */
|
|
uname(&nodeinfo);
|
|
snprintf(fname, sizeof(fname), "%s/%s", mount_point, nodeinfo.nodename);
|
|
|
|
/* Open the file for output */
|
|
outfile = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0600);
|
|
if (outfile < 0) {
|
|
int saved_errno = errno;
|
|
system_lv_umount();
|
|
errno = saved_errno;
|
|
return -1;
|
|
}
|
|
|
|
written = 0;
|
|
do {
|
|
thiswrite = write(outfile, data + written, len - written);
|
|
if (thiswrite > 0)
|
|
written += thiswrite;
|
|
|
|
} while (written < len && thiswrite > 0);
|
|
|
|
close(outfile);
|
|
|
|
system_lv_umount();
|
|
return (thiswrite < 0) ? -1 : 0;
|
|
}
|
|
|
|
/* This is a "high-level" routine - it mounts the system LV, reads
|
|
the data from a named file and then umounts the LV
|
|
again */
|
|
int system_lv_read_data(char *fname_base, char *data, ssize_t *len)
|
|
{
|
|
char fname[PATH_MAX];
|
|
int outfile;
|
|
struct stat st;
|
|
ssize_t filesize;
|
|
ssize_t thisread;
|
|
ssize_t readbytes;
|
|
|
|
if (system_lv_mount(0))
|
|
return -1;
|
|
|
|
/* Build the file name we are going to use. */
|
|
snprintf(fname, sizeof(fname), "%s/%s", mount_point, fname_base);
|
|
|
|
/* Get the file size and stuff. Actually we only need the file size but
|
|
this will also check that the file exists */
|
|
if (stat(fname, &st) < 0) {
|
|
int saved_errno = errno;
|
|
|
|
log_error("stat of file %s on system LV failed: %m\n", fname);
|
|
system_lv_umount();
|
|
errno = saved_errno;
|
|
return -1;
|
|
}
|
|
filesize = st.st_size;
|
|
|
|
outfile = open(fname, O_RDONLY);
|
|
if (outfile < 0) {
|
|
int saved_errno = errno;
|
|
|
|
log_error("open of file %s on system LV failed: %m\n", fname);
|
|
system_lv_umount();
|
|
errno = saved_errno;
|
|
return -1;
|
|
}
|
|
|
|
readbytes = 0;
|
|
do {
|
|
thisread =
|
|
read(outfile, data + readbytes, filesize - readbytes);
|
|
if (thisread > 0)
|
|
readbytes += thisread;
|
|
|
|
} while (readbytes < filesize && thisread > 0);
|
|
|
|
close(outfile);
|
|
|
|
system_lv_umount();
|
|
|
|
*len = readbytes;
|
|
return (thisread < 0) ? -1 : 0;
|
|
}
|