]> git.proxmox.com Git - pve-cluster.git/blobdiff - data/src/pmxcfs.c
buildsys: add sbuild convenience target
[pve-cluster.git] / data / src / pmxcfs.c
index 8f35c9212262e82ca2f42d0898cd87d8923ab4b0..d78a248e760241301c1cb614ee5fedd71bf440c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2010 Proxmox Server Solutions GmbH
+  Copyright (C) 2010 - 2020 Proxmox Server Solutions GmbH
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
@@ -41,7 +41,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+
 #include <qb/qbdefs.h>
 #include <qb/qbutil.h>
 #include <qb/qblog.h>
@@ -58,8 +58,9 @@
 
 #define DBFILENAME VARLIBDIR "/config.db"
 #define LOCKFILE VARLIBDIR "/.pmxcfs.lockfile"
+#define RESTART_FLAG_FILE RUNDIR "/cfs-restart-flag"
 
-#define CFSDIR "/etc/pve" 
+#define CFSDIR "/etc/pve"
 
 cfs_t cfs = {
        .debug = 0,
@@ -80,7 +81,7 @@ static void glib_log_handler(const gchar *log_domain,
                             gpointer user_data)
 {
 
-       cfs_log(log_domain, log_level, NULL, 0, NULL, message);
+       cfs_log(log_domain, log_level, NULL, 0, NULL, "%s", message);
 }
 
 static gboolean write_pidfile(pid_t pid)
@@ -106,9 +107,9 @@ static cfs_plug_t *find_plug(const char *path, char **sub)
 
        cfs_plug_t *plug = root_plug->lookup_plug(root_plug, &subpath);
 
-       cfs_debug("find_plug end %s = %p (%s)", path, plug, subpath);
+       cfs_debug("find_plug end %s = %p (%s)", path, (void *) plug, subpath);
 
-       if (subpath && subpath[0]) 
+       if (subpath && subpath[0])
                *sub = g_strdup(subpath);
 
        g_free(tmppath);
@@ -129,16 +130,21 @@ static int cfs_fuse_getattr(const char *path, struct stat *stbuf)
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (plug && plug->ops && plug->ops->getattr) {
                ret = plug->ops->getattr(plug, subpath ? subpath : "", stbuf);
 
                stbuf->st_gid = cfs.gid;
 
-               stbuf->st_mode &= 0777750; // no access for other users
-
-               if (path_is_private(path))
+               if (path_is_private(path)) {
                        stbuf->st_mode &= 0777700;
+               } else {
+                       if (S_ISDIR(stbuf->st_mode) || S_ISLNK(stbuf->st_mode)) {
+                               stbuf->st_mode &= 0777755; // access for other users
+                       } else {
+                               stbuf->st_mode &= 0777750; // no access for other users
+                       }
+               }
        }
 
        cfs_debug("leave cfs_fuse_getattr %s (%d)", path, ret);
@@ -162,7 +168,7 @@ static int cfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
@@ -170,10 +176,45 @@ static int cfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                ret = plug->ops->readdir(plug, subpath ? subpath : "", buf, filler, 0, fi);
 ret:
        cfs_debug("leave cfs_fuse_readdir %s (%d)", path, ret);
-               
+
        if (subpath)
                g_free(subpath);
-       
+
+       return ret;
+}
+
+static int cfs_fuse_chmod(const char *path, mode_t mode)
+{
+       int ret = -EPERM;
+
+       cfs_debug("enter cfs_fuse_chmod %s", path);
+
+       mode_t allowed_mode = (S_IRUSR | S_IWUSR);
+       if (!path_is_private(path))
+               allowed_mode |= (S_IRGRP);
+
+       // allow only setting our supported modes (0600 for priv, 0640 for rest)
+       // mode has additional bits set, which we ignore; see stat(2)
+       if ((mode & ALLPERMS) == allowed_mode)
+               ret = 0;
+
+       cfs_debug("leave cfs_fuse_chmod %s (%d) mode: %o", path, ret, (int)mode);
+
+       return ret;
+}
+
+static int cfs_fuse_chown(const char *path, uid_t user, gid_t group)
+{
+       int ret = -EPERM;
+
+       cfs_debug("enter cfs_fuse_chown %s", path);
+
+       // we get -1 if no change should be made
+       if ((user == 0 || user == -1) && (group == cfs.gid || group == -1))
+               ret = 0;
+
+       cfs_debug("leave cfs_fuse_chown %s (%d) (uid: %d; gid: %d)", path, ret, user, group);
+
        return ret;
 }
 
@@ -185,7 +226,7 @@ static int cfs_fuse_mkdir(const char *path, mode_t mode)
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
@@ -209,7 +250,7 @@ static int cfs_fuse_rmdir(const char *path)
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
@@ -266,8 +307,8 @@ static int cfs_fuse_open(const char *path, struct fuse_file_info *fi)
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
-       if (plug && plug->ops) { 
+
+       if (plug && plug->ops) {
                if ((subpath || !plug->ops->readdir) && plug->ops->open) {
                        ret = plug->ops->open(plug, subpath ? subpath : "", fi);
                }
@@ -286,14 +327,14 @@ static int cfs_fuse_read(const char *path, char *buf, size_t size, off_t offset,
 {
        (void) fi;
 
-       cfs_debug("enter cfs_fuse_read %s %lu %ld", path, size, offset);
+       cfs_debug("enter cfs_fuse_read %s %zu %jd", path, size, offset);
 
        int ret = -EACCES;
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
-       if (plug && plug->ops) { 
+
+       if (plug && plug->ops) {
                if ((subpath || !plug->ops->readdir) && plug->ops->read)
                        ret = plug->ops->read(plug, subpath ? subpath : "", buf, size, offset, fi);
        }
@@ -311,19 +352,19 @@ static int cfs_fuse_write(const char *path, const char *buf, size_t size,
 {
        (void) fi;
 
-       cfs_debug("enter cfs_fuse_write %s %lu %ld", path, size, offset);
+       cfs_debug("enter cfs_fuse_write %s %zu %jd", path, size, offset);
 
        int ret = -EACCES;
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
-       if (plug && plug->ops) { 
+
+       if (plug && plug->ops) {
                if ((subpath || !plug->ops->readdir) && plug->ops->write)
-               ret = plug->ops->write(plug, subpath ? subpath : "", 
+               ret = plug->ops->write(plug, subpath ? subpath : "",
                                       buf, size, offset, fi);
        }
-       
+
        cfs_debug("leave cfs_fuse_write %s (%d)", path, ret);
 
        if (subpath)
@@ -334,14 +375,14 @@ static int cfs_fuse_write(const char *path, const char *buf, size_t size,
 
 static int cfs_fuse_truncate(const char *path, off_t size)
 {
-       cfs_debug("enter cfs_fuse_truncate %s %ld", path, size);
+       cfs_debug("enter cfs_fuse_truncate %s %jd", path, size);
 
        int ret = -EACCES;
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
-       if (plug && plug->ops) { 
+
+       if (plug && plug->ops) {
                if ((subpath || !plug->ops->readdir) && plug->ops->truncate)
                        ret = plug->ops->truncate(plug, subpath ? subpath : "", size);
        }
@@ -362,7 +403,7 @@ static int cfs_fuse_create(const char *path, mode_t mode, struct fuse_file_info
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
@@ -386,7 +427,7 @@ static int cfs_fuse_unlink(const char *path)
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
@@ -398,7 +439,7 @@ ret:
 
        if (subpath)
                g_free(subpath);
-       
+
        return ret;
 }
 
@@ -410,13 +451,13 @@ static int cfs_fuse_readlink(const char *path, char *buf, size_t max)
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
        if (plug->ops && plug->ops->readlink)
                ret = plug->ops->readlink(plug, subpath ? subpath : "", buf, max);
-       
+
 ret:
        cfs_debug("leave cfs_fuse_readlink %s (%d)", path, ret);
 
@@ -434,13 +475,13 @@ static int cfs_fuse_utimens(const char *path, const struct timespec tv[2])
 
        char *subpath = NULL;
        cfs_plug_t *plug = find_plug(path, &subpath);
-       
+
        if (!plug)
                goto ret;
 
        if (plug->ops && plug->ops->utimens)
                ret = plug->ops->utimens(plug, subpath ? subpath : "", tv);
-       
+
 ret:
        cfs_debug("leave cfs_fuse_utimens %s (%d)", path, ret);
 
@@ -479,7 +520,9 @@ static struct fuse_operations fuse_ops = {
        .readlink = cfs_fuse_readlink,
        .utimens = cfs_fuse_utimens,
        .statfs = cfs_fuse_statfs,
-       .init = cfs_fuse_init
+       .init = cfs_fuse_init,
+       .chown = cfs_fuse_chown,
+       .chmod = cfs_fuse_chmod
 };
 
 static char *
@@ -557,29 +600,53 @@ create_dot_clusterlog_cb(cfs_plug_t *plug)
 static char *
 read_debug_setting_cb(cfs_plug_t *plug)
 {
-       return g_strdup_printf("%d\n", !!cfs.debug); 
+       return g_strdup_printf("%d\n", !!cfs.debug);
 }
 
-static void 
-update_qb_log_settings(void) 
+static void
+my_qb_log_filter(struct qb_log_callsite *cs)
 {
-       qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+       int32_t priority = cfs.debug ? LOG_DEBUG : LOG_INFO;
+
+       if (qb_bit_is_set(cs->tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
+               if (cs->priority <= (cfs.debug ? priority : LOG_WARNING)) {
+                       qb_bit_set(cs->targets, QB_LOG_SYSLOG);
+               } else {
+                       qb_bit_clear(cs->targets, QB_LOG_SYSLOG);
+               }
+               if (cs->priority <= priority) {
+                       qb_bit_set(cs->targets, QB_LOG_STDERR);
+               } else {
+                       qb_bit_clear(cs->targets, QB_LOG_STDERR);
+               }
+       } else {
+               if (cs->priority <= priority) {
+                       qb_bit_set(cs->targets, QB_LOG_SYSLOG);
+                       qb_bit_set(cs->targets, QB_LOG_STDERR);
+               } else {
+                       qb_bit_clear(cs->targets, QB_LOG_SYSLOG);
+                       qb_bit_clear(cs->targets, QB_LOG_STDERR);
+               }
+       }
+}
+
+static void
+update_qb_log_settings(void)
+{
+       qb_log_filter_fn_set(my_qb_log_filter);
 
        if (cfs.debug) {
                qb_log_format_set(QB_LOG_SYSLOG, "[%g] %p: %b (%f:%l:%n)");
                qb_log_format_set(QB_LOG_STDERR, "[%g] %p: %b (%f:%l:%n)");
-               qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP, LOG_INFO - LOG_DEBUG);
-               qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
        } else {
                qb_log_format_set(QB_LOG_SYSLOG, "[%g] %p: %b");
                qb_log_format_set(QB_LOG_STDERR, "[%g] %p: %b");
-               qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP, LOG_DEBUG - LOG_INFO);
-               qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO);
        }
 }
 
-static int write_debug_setting_cb(
-       cfs_plug_t *plug, 
+static int
+write_debug_setting_cb(
+       cfs_plug_t *plug,
        const char *buf,
        size_t size)
 {
@@ -607,8 +674,7 @@ static int write_debug_setting_cb(
        return res;
 }
 
-
-static void 
+static void
 create_symlinks(cfs_plug_base_t *bplug, const char *nodename)
 {
        g_return_if_fail(bplug != NULL);
@@ -623,15 +689,21 @@ create_symlinks(cfs_plug_base_t *bplug, const char *nodename)
        lnk = cfs_plug_link_new("qemu-server", lnktarget);
        g_free(lnktarget);
        cfs_plug_base_insert(bplug, (cfs_plug_t*)lnk);
-       
+
+       // FIXME: remove openvz stuff for 7.x
        lnktarget = g_strdup_printf("nodes/%s/openvz", nodename);
        lnk = cfs_plug_link_new("openvz", lnktarget);
        g_free(lnktarget);
        cfs_plug_base_insert(bplug, (cfs_plug_t*)lnk);
 
+       lnktarget = g_strdup_printf("nodes/%s/lxc", nodename);
+       lnk = cfs_plug_link_new("lxc", lnktarget);
+       g_free(lnktarget);
+       cfs_plug_base_insert(bplug, (cfs_plug_t*)lnk);
+
        cfs_plug_func_t *fplug = cfs_plug_func_new(".version", 0440, create_dot_version_cb, NULL);
        cfs_plug_base_insert(bplug, (cfs_plug_t*)fplug);
-       
+
        fplug = cfs_plug_func_new(".members", 0440, create_dot_members_cb, NULL);
        cfs_plug_base_insert(bplug, (cfs_plug_t*)fplug);
 
@@ -651,30 +723,37 @@ create_symlinks(cfs_plug_base_t *bplug, const char *nodename)
 }
 
 static char *
-lookup_node_ip(const char *nodename) 
+lookup_node_ip(const char *nodename)
 {
+       char buf[INET6_ADDRSTRLEN];
        struct addrinfo *ainfo;
        struct addrinfo ahints;
+       char *res = NULL;
        memset(&ahints, 0, sizeof(ahints));
 
        if (getaddrinfo(nodename, NULL, &ahints, &ainfo))
                return NULL;
 
        if (ainfo->ai_family == AF_INET) {
-               char buf[INET6_ADDRSTRLEN];
                struct sockaddr_in *sa = (struct sockaddr_in *)ainfo->ai_addr;
                inet_ntop(ainfo->ai_family, &sa->sin_addr, buf, sizeof(buf));
                if (strncmp(buf, "127.", 4) != 0) {
-                       return g_strdup(buf);
+                       res = g_strdup(buf);
+               }
+       } else if (ainfo->ai_family == AF_INET6) {
+               struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ainfo->ai_addr;
+               inet_ntop(ainfo->ai_family, &sa->sin6_addr, buf, sizeof(buf));
+               if (strcmp(buf, "::1") != 0) {
+                       res = g_strdup(buf);
                }
        }
 
-       // ipv6 support ?
+       freeaddrinfo(ainfo);
 
-       return NULL;
+       return res;
 }
 
-static const char* 
+static const char*
 log_tags_stringify(uint32_t tags) {
        if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
                return "libqb";
@@ -693,6 +772,7 @@ int main(int argc, char *argv[])
 {
        int ret = -1;
        int lockfd = -1;
+       int pipefd[2];
 
        gboolean foreground = FALSE;
        gboolean force_local_mode = FALSE;
@@ -702,6 +782,10 @@ int main(int argc, char *argv[])
        dfsm_t *status_fsm = NULL;
 
        qb_log_init("pmxcfs", LOG_DAEMON, LOG_DEBUG);
+       /* remove default filter */
+       qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_REMOVE,
+                         QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+
        qb_log_tags_stringify_fn_set(log_tags_stringify);
 
        qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
@@ -717,8 +801,8 @@ int main(int argc, char *argv[])
        GOptionEntry entries[] = {
                { "debug", 'd', 0, G_OPTION_ARG_NONE, &cfs.debug, "Turn on debug messages", NULL },
                { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Do not daemonize server", NULL },
-               { "local", 'l', 0, G_OPTION_ARG_NONE, &force_local_mode, 
-                 "Force local mode (ignore cluster.conf, force quorum)", NULL },
+               { "local", 'l', 0, G_OPTION_ARG_NONE, &force_local_mode,
+                 "Force local mode (ignore corosync.conf, force quorum)", NULL },
                { NULL },
        };
 
@@ -751,14 +835,14 @@ int main(int argc, char *argv[])
                qb_log_fini();
                exit (-1);
        }
-       
-       for (int i=0; i < sizeof(utsname.nodename); i++) {
-               if (utsname.nodename[i] =='.') utsname.nodename[i] = 0;
-       }
+
+       char *dot = strchr(utsname.nodename, '.');
+       if (dot)
+               *dot = 0;
 
        cfs.nodename = g_strdup(utsname.nodename);
 
-       if (!(cfs.ip = lookup_node_ip(cfs.nodename))) { 
+       if (!(cfs.ip = lookup_node_ip(cfs.nodename))) {
                cfs_critical("Unable to get local IP address");
                qb_log_fini();
                exit(-1);
@@ -772,13 +856,13 @@ int main(int argc, char *argv[])
        }
        cfs.gid = www_data->gr_gid;
 
-       g_thread_init(NULL);
-
        umask(027);
 
        mkdir(VARLIBDIR, 0755);
+       mkdir(RUNDIR, 0755);
+       chown(RUNDIR, 0, cfs.gid);
 
-       if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND)) == -1) {
+       if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0600)) == -1) {
                cfs_critical("unable to create lock '%s': %s", LOCKFILE, strerror (errno));
                goto err;
        }
@@ -786,11 +870,11 @@ int main(int argc, char *argv[])
        for (int i = 10; i >= 0; i--) {
                if (flock(lockfd, LOCK_EX|LOCK_NB) != 0) {
                        if (!i) {
-                               cfs_critical("unable to aquire pmxcfs lock: %s", strerror (errno));
+                               cfs_critical("unable to acquire pmxcfs lock: %s", strerror (errno));
                                goto err;
                        }
                        if (i == 10)
-                               cfs_message("unable to aquire pmxcfs lock - trying again");
+                               cfs_message("unable to acquire pmxcfs lock - trying again");
 
                        sleep(1);
                }
@@ -805,7 +889,7 @@ int main(int argc, char *argv[])
                goto err;
        }
 
-       // automatically import cluster.conf from host
+       // automatically import corosync.conf from host
        if (create && !force_local_mode) {
                char *cdata = NULL;
                gsize clen = 0;
@@ -813,46 +897,48 @@ int main(int argc, char *argv[])
 
                        guint32 mtime = time(NULL);
 
-                       memdb_create(memdb, "/cluster.conf", 0, mtime);
-                       if (memdb_write(memdb, "/cluster.conf", 0, mtime, cdata, clen, 0, 1) < 0) {
-                               cfs_critical("memdb_write failed - unable to import cluster.conf");
+                       memdb_create(memdb, "/corosync.conf", 0, mtime);
+                       if (memdb_write(memdb, "/corosync.conf", 0, mtime, cdata, clen, 0, 1) < 0) {
+                               cfs_critical("memdb_write failed - unable to import corosync.conf");
                                goto err;
                        }
                }
        }
 
-       // does cluster.conf exist?
+       // does corosync.conf exist?
        gpointer conf_data = NULL;
-       int len = memdb_read(memdb, "cluster.conf", &conf_data);
+       int len = memdb_read(memdb, "corosync.conf", &conf_data);
        if (len >= 0) {
                if (force_local_mode) {
-                       cfs_message("forcing local mode (althought cluster.conf exists)");
+                       cfs_message("forcing local mode (although corosync.conf exists)");
                        cfs_set_quorate(1, TRUE);
                } else {
                        if (!(dcdb = dcdb_new(memdb)))
                                goto err;
-                       dcdb_sync_cluster_conf(memdb, 1);
+                       dcdb_sync_corosync_conf(memdb, 1);
                }
        } else {
-               cfs_debug("using local mode (cluster.conf does not exist)");
+               cfs_debug("using local mode (corosync.conf does not exist)");
                cfs_set_quorate(1, TRUE);
        }
        if (conf_data) g_free(conf_data);
 
        cfs_plug_memdb_t *config = cfs_plug_memdb_new("memdb", memdb, dcdb);
-       
+
        cfs_plug_base_t *bplug = cfs_plug_base_new("", (cfs_plug_t *)config);
 
        create_symlinks(bplug, cfs.nodename);
 
        root_plug = (cfs_plug_t *)bplug;
 
-       system("umount -f " CFSDIR " >/dev/null 2>&1");
+       umount2(CFSDIR, MNT_FORCE);
+
+       mkdir(CFSDIR, 0755);
 
        char *fa[] = { "-f", "-odefault_permissions", "-oallow_other", NULL};
 
-       struct fuse_args fuse_args = FUSE_ARGS_INIT(sizeof (fa)/sizeof(gpointer) - 1, fa); 
-       
+       struct fuse_args fuse_args = FUSE_ARGS_INIT(sizeof (fa)/sizeof(gpointer) - 1, fa);
+
        struct fuse_chan *fuse_chan = fuse_mount(CFSDIR, &fuse_args);
        if (!fuse_chan) {
                cfs_critical("fuse_mount error: %s", strerror(errno));
@@ -867,17 +953,39 @@ int main(int argc, char *argv[])
        fuse_set_signal_handlers(fuse_get_session(fuse));
 
        if (!foreground) {
+               if (pipe(pipefd) == -1) {
+                       cfs_critical("pipe error: %s", strerror(errno));
+                       goto err;
+               }
+
                pid_t cpid = fork();
 
                if (cpid == -1) {
                        cfs_critical("failed to daemonize program - %s", strerror (errno));
                        goto err;
                } else if (cpid) {
+                       int readbytes, errno_tmp;
+                       char readbuffer;
+                       close(pipefd[1]);
+                       readbytes = read(pipefd[0], &readbuffer, sizeof(readbuffer));
+                       errno_tmp = errno;
+                       close(pipefd[0]);
+                       if (readbytes == -1) {
+                               cfs_critical("read error: %s", strerror(errno_tmp));
+                               kill(cpid, SIGKILL);
+                               goto err;
+                       } else if (readbytes != 1 || readbuffer != '1') {
+                               cfs_critical("child failed to send '1'");
+                               kill(cpid, SIGKILL);
+                               goto err;
+                       }
+                       /* child finished starting up */
                        write_pidfile(cpid);
                        qb_log_fini();
                        _exit (0);
                } else {
                        int nullfd;
+                       close(pipefd[0]);
 
                        chroot("/");
 
@@ -933,8 +1041,19 @@ int main(int argc, char *argv[])
 
        server_start(memdb);
 
+       unlink(RESTART_FLAG_FILE);
+
+       if (!foreground) {
+               /* finished starting up, signaling parent */
+               write(pipefd[1], "1", 1);
+               close(pipefd[1]);
+       }
+
        ret = fuse_loop_mt(fuse);
 
+       open(RESTART_FLAG_FILE, O_CREAT|O_NOCTTY|O_NONBLOCK, S_IRUSR | S_IRGRP);
+       chown(RESTART_FLAG_FILE, 0, cfs.gid);
+
        cfs_message("teardown filesystem");
 
        server_stop();
@@ -963,16 +1082,15 @@ int main(int argc, char *argv[])
        if (service_status)
                service_dfsm_destroy(service_status);
 
-       sleep(1); /* do not restart too fast */
  ret:
 
-       if (status_fsm) 
+       if (status_fsm)
                dfsm_destroy(status_fsm);
 
        if (dcdb)
                dfsm_destroy(dcdb);
 
-       if (memdb) 
+       if (memdb)
                memdb_close(memdb);
 
        if (wrote_pidfile)