#include <unistd.h>
#include <stdlib.h>
+#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "mdadm.h"
#include "mdmon.h"
-struct active_array *array_list;
struct active_array *discard_this;
struct active_array *pending_discard;
-struct md_generic_cmd *active_cmd;
+
+int mon_tid, mgr_tid;
+
+int sigterm;
int run_child(void *v)
{
struct supertype *c = v;
+
do_monitor(c);
return 0;
}
int clone_monitor(struct supertype *container)
{
static char stack[4096];
- int rv;
- rv = pipe(container->mgr_pipe);
- if (rv < 0)
- return rv;
- rv = pipe(container->mon_pipe);
- if (rv < 0)
- goto err_mon_pipe;
-
- rv = clone(run_child, stack+4096-64,
+ mon_tid = clone(run_child, stack+4096-64,
CLONE_FS|CLONE_FILES|CLONE_VM|CLONE_SIGHAND|CLONE_THREAD,
container);
- if (rv < 0)
- goto err_clone;
- else
- return rv;
-
- err_clone:
- close(container->mon_pipe[0]);
- close(container->mon_pipe[1]);
- err_mon_pipe:
- close(container->mgr_pipe[0]);
- close(container->mgr_pipe[1]);
-
- return rv;
+
+ mgr_tid = syscall(SYS_gettid);
+
+ return mon_tid;
}
static struct superswitch *find_metadata_methods(char *vers)
{
if (strcmp(vers, "ddf") == 0)
return &super_ddf;
+ if (strcmp(vers, "imsm") == 0)
+ return &super_imsm;
return NULL;
}
-static int make_pidfile(char *devname, int o_excl)
+int make_pidfile(char *devname, int o_excl)
{
char path[100];
char pid[10];
int fd;
+ int n;
+
+ if (sigterm)
+ return -1;
+
sprintf(path, "/var/run/mdadm/%s.pid", devname);
fd = open(path, O_RDWR|O_CREAT|o_excl, 0600);
if (fd < 0)
- return -1;
+ return -errno;
sprintf(pid, "%d\n", getpid());
- write(fd, pid, strlen(pid));
+ n = write(fd, pid, strlen(pid));
close(fd);
+ if (n < 0)
+ return -errno;
return 0;
}
kill(pid, SIGTERM);
}
-static int make_control_sock(char *devname)
+void remove_pidfile(char *devname)
+{
+ char buf[100];
+
+ if (sigterm)
+ return;
+
+ sprintf(buf, "/var/run/mdadm/%s.pid", devname);
+ unlink(buf);
+ sprintf(buf, "/var/run/mdadm/%s.sock", devname);
+ unlink(buf);
+}
+
+int make_control_sock(char *devname)
{
char path[100];
int sfd;
long fl;
struct sockaddr_un addr;
+ if (sigterm)
+ return -1;
+
sprintf(path, "/var/run/mdadm/%s.sock", devname);
unlink(path);
sfd = socket(PF_LOCAL, SOCK_STREAM, 0);
return sfd;
}
+int socket_hup_requested;
+static void hup(int sig)
+{
+ socket_hup_requested = 1;
+}
+
+static void term(int sig)
+{
+ sigterm = 1;
+}
+
+static void wake_me(int sig)
+{
+
+}
+
+/* if we are debugging and starting mdmon by hand then don't fork */
+static int do_fork(void)
+{
+ #ifdef DEBUG
+ if (env_no_mdmon())
+ return 0;
+ #endif
+
+ return 1;
+}
+
+
+
int main(int argc, char *argv[])
{
int mdfd;
struct mdinfo *mdi, *di;
struct supertype *container;
+ sigset_t set;
+ struct sigaction act;
+ int pfd[2];
+ int status;
+ int ignore;
+
if (argc != 2) {
fprintf(stderr, "Usage: md-manage /device/name/for/container\n");
exit(2);
exit(1);
}
+ /* Fork, and have the child tell us when they are ready */
+ if (do_fork()) {
+ if (pipe(pfd) != 0) {
+ fprintf(stderr, "mdmon: failed to create pipe\n");
+ exit(1);
+ }
+ switch(fork()) {
+ case -1:
+ fprintf(stderr, "mdmon: failed to fork: %s\n",
+ strerror(errno));
+ exit(1);
+ case 0: /* child */
+ close(pfd[0]);
+ break;
+ default: /* parent */
+ close(pfd[1]);
+ if (read(pfd[0], &status, sizeof(status)) != sizeof(status)) {
+ wait(&status);
+ status = WEXITSTATUS(status);
+ }
+ exit(status);
+ }
+ } else
+ pfd[0] = pfd[1] = -1;
/* hopefully it is a container - we'll check later */
container = malloc(sizeof(*container));
- container->devfd = mdfd;
container->devnum = fd2devnum(mdfd);
container->devname = devnum2devname(container->devnum);
+ container->device_name = argv[1];
/* If this fails, we hope it already exists */
mkdir("/var/run/mdadm", 0600);
container->devname);
exit(3);
} else {
+ int err;
+
/* cleanup the old monitor, this one is taking over */
try_kill_monitor(container->devname);
- if (make_pidfile(container->devname, 0) < 0) {
+ err = make_pidfile(container->devname, 0);
+ if (err < 0) {
fprintf(stderr, "mdmon: %s Cannot create pidfile\n",
container->devname);
- exit(3);
+ if (err == -EROFS) {
+ /* FIXME implement a mechanism to
+ * prevent duplicate monitor instances
+ */
+ fprintf(stderr,
+ "mdmon: continuing on read-only file system\n");
+ } else
+ exit(3);
}
}
}
container->sock = make_control_sock(container->devname);
- if (container->sock < 0) {
- fprintf(stderr, "mdmon: Cannot create socket in /var/run/mdadm\n");
- exit(3);
- }
container->arrays = NULL;
mdi = sysfs_read(mdfd, container->devnum,
container->devs = NULL;
for (di = mdi->devs; di; di = di->next) {
struct mdinfo *cd = malloc(sizeof(*cd));
- cd = di;
+ *cd = *di;
cd->next = container->devs;
container->devs = cd;
}
exit(3);
}
+ /* Ok, this is close enough. We can say goodbye to our parent now.
+ */
+ status = 0;
+ if (write(pfd[1], &status, sizeof(status)) < 0)
+ fprintf(stderr, "mdmon: failed to notify our parent: %d\n",
+ getppid());
+ close(pfd[1]);
+
+ ignore = chdir("/");
+ setsid();
+ close(0);
+ open("/dev/null", O_RDWR);
+ close(1);
+ ignore = dup(0);
+#ifndef DEBUG
+ close(2);
+ ignore = dup(0);
+#endif
mlockall(MCL_FUTURE);
+ /* SIGUSR is sent between parent and child. So both block it
+ * and enable it only with pselect.
+ */
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR1);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGALRM);
+ sigaddset(&set, SIGTERM);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ act.sa_handler = wake_me;
+ act.sa_flags = 0;
+ sigaction(SIGUSR1, &act, NULL);
+ sigaction(SIGALRM, &act, NULL);
+ act.sa_handler = hup;
+ sigaction(SIGHUP, &act, NULL);
+ act.sa_handler = term;
+ sigaction(SIGTERM, &act, NULL);
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, NULL);
+
if (clone_monitor(container) < 0) {
- fprintf(stderr, "md-manage: failed to start monitor process: %s\n",
+ fprintf(stderr, "mdmon: failed to start monitor process: %s\n",
strerror(errno));
exit(2);
}