]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - mdmon.c
mdmon: terminate clean
[thirdparty/mdadm.git] / mdmon.c
diff --git a/mdmon.c b/mdmon.c
index 99f2f2c419ba052c6a78d55dc3c7d69a3ba84389..7ef0c80f5b38568c7f1ecfda04fe0bcd79bb4ed5 100644 (file)
--- a/mdmon.c
+++ b/mdmon.c
@@ -32,6 +32,7 @@
 #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>
@@ -48,11 +49,12 @@ struct active_array *pending_discard;
 
 int mon_tid, mgr_tid;
 
+int sigterm;
+
 int run_child(void *v)
 {
        struct supertype *c = v;
 
-       mon_tid = syscall(SYS_gettid);
        do_monitor(c);
        return 0;
 }
@@ -60,16 +62,14 @@ int run_child(void *v)
 int clone_monitor(struct supertype *container)
 {
        static char stack[4096];
-       int rv;
-
 
-       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);
 
        mgr_tid = syscall(SYS_gettid);
-       
-       return rv;
+
+       return mon_tid;
 }
 
 static struct superswitch *find_metadata_methods(char *vers)
@@ -82,19 +82,26 @@ static struct superswitch *find_metadata_methods(char *vers)
 }
 
 
-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;
 }
 
@@ -136,17 +143,25 @@ 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);
 }
 
-static int make_control_sock(char *devname)
+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);
@@ -166,11 +181,35 @@ static int make_control_sock(char *devname)
        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;
@@ -178,6 +217,9 @@ int main(int argc, char *argv[])
        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");
@@ -195,6 +237,30 @@ int main(int argc, char *argv[])
                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));
@@ -211,21 +277,27 @@ int main(int argc, char *argv[])
                                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,
@@ -258,7 +330,7 @@ int main(int argc, char *argv[])
        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;
        }
@@ -271,6 +343,25 @@ int main(int argc, char *argv[])
                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
@@ -278,15 +369,23 @@ int main(int argc, char *argv[])
         */
        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);
        }