diff --git a/libmount/samples/mount.c b/libmount/samples/mount.c index 2d995fd18..08b92332b 100644 --- a/libmount/samples/mount.c +++ b/libmount/samples/mount.c @@ -136,10 +136,8 @@ static void print_all(struct libmnt_context *cxt, char *pattern, int show_label) /* * mount -a [-F] - * ... -F is not supported yet (TODO) */ -static int mount_all(struct libmnt_context *cxt, - int forkme __attribute__((unused))) +static int mount_all(struct libmnt_context *cxt) { struct libmnt_iter *itr; struct libmnt_fs *fs; @@ -160,24 +158,39 @@ static int mount_all(struct libmnt_context *cxt, printf(ignored == 1 ? _("%-25s: ignored\n") : _("%-25s: already mounted\n"), tgt); - } else if (!mnt_context_get_status(cxt)) { - if (mntrc > 0) { - errno = mntrc; - printf(_("%-25s: failed: %s\n"), tgt, - strerror(mntrc)); - rc |= EX_FAIL; - } else { - printf(_("%-25s: failed\n"), tgt); - rc |= EX_SYSERR; - } - } else { - if (mnt_context_is_verbose(cxt)) - printf("%-25s: successfully mounted\n", tgt); - rc |= EX_SOMEOK; + } else if (mnt_context_is_fork(cxt)) { + printf("%-25s: mount successfully forked\n", tgt); + + } else { + if (!mnt_context_get_status(cxt)) { + if (mntrc > 0) { + errno = mntrc; + printf(_("%-25s: failed: %s\n"), tgt, + strerror(mntrc)); + rc |= EX_FAIL; + } else { + printf(_("%-25s: failed\n"), tgt); + rc |= EX_SYSERR; + } + } else { + if (mnt_context_is_verbose(cxt)) + printf("%-25s: successfully mounted\n", tgt); + + rc |= EX_SOMEOK; + } } } + if (mnt_context_is_parent(cxt)) { + /* wait for mount --fork children */ + int nerrs = 0, nchildren = 0; + + rc = mnt_context_wait_for_children(cxt, &nchildren, &nerrs); + if (!rc && nchildren) + rc = nchildren == nerrs ? EX_FAIL : EX_SOMEOK; + } + return rc; } @@ -251,7 +264,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) int main(int argc, char **argv) { - int c, rc = EX_SUCCESS, all = 0, forkme = 0, show_labels = 0; + int c, rc = EX_SUCCESS, all = 0, show_labels = 0; struct libmnt_context *cxt; char *source = NULL, *srcbuf = NULL; char *types = NULL; @@ -332,7 +345,7 @@ int main(int argc, char **argv) mnt_context_enable_fake(cxt, TRUE); break; case 'F': - forkme = 1; + mnt_context_enable_fork(cxt, TRUE); break; case 'h': usage(stdout); @@ -445,7 +458,7 @@ int main(int argc, char **argv) /* * A) Mount all */ - rc = mount_all(cxt, forkme); + rc = mount_all(cxt); goto done; } else if (argc == 0 && source) { diff --git a/libmount/src/context.c b/libmount/src/context.c index e971003cb..c61a14473 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -33,6 +33,8 @@ #include "mountP.h" +#include + /** * mnt_new_context: * @@ -94,6 +96,8 @@ void mnt_free_context(struct libmnt_context *cxt) mnt_free_lock(cxt->lock); mnt_free_update(cxt->update); + free(cxt->children); + DBG(CXT, mnt_debug_h(cxt, "<---- free")); free(cxt); } @@ -161,6 +165,7 @@ int mnt_reset_context(struct libmnt_context *cxt) cxt->flags |= (fl & MNT_FL_NOHELPERS); cxt->flags |= (fl & MNT_FL_LOOPDEL); cxt->flags |= (fl & MNT_FL_LAZY); + cxt->flags |= (fl & MNT_FL_FORK); cxt->flags |= (fl & MNT_FL_FORCE); cxt->flags |= (fl & MNT_FL_NOCANONICALIZE); cxt->flags |= (fl & MNT_FL_RDONLY_UMOUNT); @@ -171,10 +176,13 @@ static int set_flag(struct libmnt_context *cxt, int flag, int enable) { if (!cxt) return -EINVAL; - if (enable) + if (enable) { + DBG(CXT, mnt_debug_h(cxt, "enabling flag %04x", flag)); cxt->flags |= flag; - else + } else { + DBG(CXT, mnt_debug_h(cxt, "disabling flag %04x", flag)); cxt->flags &= ~flag; + } return 0; } @@ -253,6 +261,21 @@ int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable) return set_flag(cxt, MNT_FL_LAZY, enable); } +/** + * mnt_context_enable_fork: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable fork(2) call in mnt_context_next_mount() (see mount(8) man + * page, option -F). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_fork(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_FORK, enable); +} + /** * mnt_context_is_lazy: * @cxt: mount context @@ -1683,6 +1706,116 @@ int mnt_context_is_fs_mounted(struct libmnt_context *cxt, return 0; } +static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid) +{ + pid_t *pids; + + if (!cxt) + return -EINVAL; + + pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1); + if (!pids) + return -ENOMEM; + + DBG(CXT, mnt_debug_h(cxt, "add new child %d", pid)); + cxt->children = pids; + cxt->children[cxt->nchildren++] = pid; + + return 0; +} + +int mnt_fork_context(struct libmnt_context *cxt) +{ + int rc = 0; + pid_t pid; + + if (!mnt_context_is_parent(cxt)) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "forking context")); + + DBG_FLUSH; + + pid = fork(); + + switch (pid) { + case -1: /* error */ + DBG(CXT, mnt_debug_h(cxt, "fork failed %m")); + return -errno; + + case 0: /* child */ + cxt->pid = getpid(); + cxt->flags &= ~MNT_FL_FORK; + DBG(CXT, mnt_debug_h(cxt, "child created")); + break; + + default: + rc = mnt_context_add_child(cxt, pid); + break; + } + + return rc; +} + +int mnt_context_wait_for_children(struct libmnt_context *cxt, + int *nchildren, int *nerrs) +{ + int i; + + if (!cxt) + return -EINVAL; + + assert(mnt_context_is_parent(cxt)); + + for (i = 0; i < cxt->nchildren; i++) { + pid_t pid = cxt->children[i]; + int rc = 0, ret = 0; + + if (!pid) + continue; + do { + DBG(CXT, mnt_debug_h(cxt, + "waiting for child (%d/%d): %d", + i + 1, cxt->nchildren, pid)); + errno = 0; + rc = waitpid(pid, &ret, 0); + + } while (rc == -1 && errno == EINTR); + + if (nchildren) + (*nchildren)++; + + if (rc != -1 && nerrs) { + if (WIFEXITED(ret)) + (*nerrs) += WEXITSTATUS(ret) == 0 ? 0 : 1; + else + (*nerrs)++; + } + cxt->children[i] = 0; + } + + cxt->nchildren = 0; + free(cxt->children); + cxt->children = NULL; + return 0; +} + +int mnt_context_is_fork(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_FORK); +} + + +int mnt_context_is_parent(struct libmnt_context *cxt) +{ + return mnt_context_is_fork(cxt) && cxt->pid == 0; +} + +int mnt_context_is_child(struct libmnt_context *cxt) +{ + return !mnt_context_is_fork(cxt) && cxt->pid; +} + #ifdef TEST_PROGRAM struct libmnt_lock *lock; diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index 7b4c0bae3..8fba9c6be 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -762,12 +762,30 @@ int mnt_context_next_mount(struct libmnt_context *cxt, return 0; } + if (mnt_context_is_fork(cxt)) { + rc = mnt_fork_context(cxt); + if (rc) + return rc; /* fork error */ + + if (mnt_context_is_parent(cxt)) { + return 0; /* parent */ + } + } + + /* child or non-forked */ + rc = mnt_context_set_fs(cxt, *fs); - if (rc) - return rc; - rc = mnt_context_mount(cxt); - if (mntrc) - *mntrc = rc; + if (!rc) { + rc = mnt_context_mount(cxt); + if (mntrc) + *mntrc = rc; + } + + if (mnt_context_is_child(cxt)) { + DBG(CXT, mnt_debug_h(cxt, "next-mount: child exit [rc=%d]", rc)); + DBG_FLUSH; + exit(rc); + } return 0; } diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index c991764b9..95a710e2f 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -390,6 +390,7 @@ extern int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable); extern int mnt_context_enable_force(struct libmnt_context *cxt, int enable); extern int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable); extern int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable); +extern int mnt_context_enable_fork(struct libmnt_context *cxt, int enable); extern int mnt_context_get_optsmode(struct libmnt_context *cxt); extern int mnt_context_is_lazy(struct libmnt_context *cxt); @@ -400,6 +401,13 @@ extern int mnt_context_is_nomtab(struct libmnt_context *cxt); extern int mnt_context_is_force(struct libmnt_context *cxt); extern int mnt_context_is_verbose(struct libmnt_context *cxt); +extern int mnt_context_is_fork(struct libmnt_context *cxt); +extern int mnt_context_is_parent(struct libmnt_context *cxt); +extern int mnt_context_is_child(struct libmnt_context *cxt); + +extern int mnt_context_wait_for_children(struct libmnt_context *cxt, + int *nchildren, int *nerrs); + extern int mnt_context_is_fs_mounted(struct libmnt_context *cxt, struct libmnt_fs *fs, int *mounted); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index 6f4eda71d..6f4d1c02b 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -208,5 +208,10 @@ global: MOUNT_2.21 { global: + mnt_context_enable_fork; + mnt_context_is_child; + mnt_context_is_fork; + mnt_context_is_parent; mnt_context_next_umount; + mnt_context_wait_for_children; } MOUNT_2.20; diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index cce4c5c35..9be3aabed 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -61,12 +61,16 @@ # define DBG(m, x) do { \ if ((MNT_DEBUG_ ## m) & libmount_debug_mask) { \ - fprintf(stderr, "libmount: %8s: ", # m); \ + fprintf(stderr, "%d: libmount: %8s: ", getpid(), # m); \ x; \ } \ } while (0) -# define DBG_FLUSH do { fflush(stderr); } while(0) +# define DBG_FLUSH do { \ + if (libmount_debug_mask && \ + libmount_debug_mask != MNT_DEBUG_INIT) \ + fflush(stderr); \ + } while(0) extern int libmount_debug_mask; @@ -299,6 +303,11 @@ struct libmnt_context char *orig_user; /* original (non-fixed) user= option */ + pid_t *children; /* "mount -a --fork" PIDs */ + int nchildren; /* number of children */ + pid_t pid; /* 0=parent; PID=child */ + + int syscall_status; /* 1: not called yet, 0: success, <0: -errno */ }; @@ -313,6 +322,7 @@ struct libmnt_context #define MNT_FL_FORCE (1 << 8) #define MNT_FL_NOCANONICALIZE (1 << 9) #define MNT_FL_RDONLY_UMOUNT (1 << 11) /* remount,ro after EBUSY umount(2) */ +#define MNT_FL_FORK (1 << 12) #define MNT_FL_EXTERN_FS (1 << 15) /* cxt->fs is not private */ #define MNT_FL_EXTERN_FSTAB (1 << 16) /* cxt->fstab is not private */ @@ -370,6 +380,8 @@ extern int mnt_context_setup_loopdev(struct libmnt_context *cxt); extern int mnt_context_delete_loopdev(struct libmnt_context *cxt); extern int mnt_context_clear_loopdev(struct libmnt_context *cxt); +extern int mnt_fork_context(struct libmnt_context *cxt); + /* tab_update.c */ extern struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd); extern int mnt_update_set_filename(struct libmnt_update *upd,