mirror of
https://github.com/jiangcuo/zfsonlinux.git
synced 2025-08-26 00:18:40 +00:00
781 lines
22 KiB
Diff
781 lines
22 KiB
Diff
From 6e60b28bc979477ba8d48347dd556acc5296bb25 Mon Sep 17 00:00:00 2001
|
|
From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
|
|
Date: Tue, 19 Apr 2022 15:58:46 +0100
|
|
Subject: [PATCH 1/3] Revert "etc/systemd/zfs-mount-generator: serialise,
|
|
handle keylocation=http[s]://"
|
|
|
|
This reverts commit fe6f2651f55de3bf68ac4729386b5e85aa23a447.
|
|
|
|
This is to continue support for ubuntu specific zsys patch that
|
|
currently relies on the shell based implementation of the
|
|
generator. See https://bugs.launchpad.net/bugs/1958142
|
|
|
|
Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
|
|
---
|
|
.../system-generators/zfs-mount-generator.c | 437 +++++++++++-------
|
|
man/man8/zfs-mount-generator.8.in | 19 +-
|
|
2 files changed, 275 insertions(+), 181 deletions(-)
|
|
|
|
diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c
|
|
index f4c6c26a0b..b806339deb 100644
|
|
--- a/etc/systemd/system-generators/zfs-mount-generator.c
|
|
+++ b/etc/systemd/system-generators/zfs-mount-generator.c
|
|
@@ -27,6 +27,9 @@
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
+#include <sys/wait.h>
|
|
+#include <sys/mman.h>
|
|
+#include <semaphore.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
@@ -41,16 +44,25 @@
|
|
#include <errno.h>
|
|
#include <libzfs.h>
|
|
|
|
-/*
|
|
- * For debugging only.
|
|
- *
|
|
- * Free statics with trivial life-times,
|
|
- * but saved line filenames are replaced with a static string.
|
|
- */
|
|
-#define FREE_STATICS false
|
|
-
|
|
-#define nitems(arr) (sizeof (arr) / sizeof (*arr))
|
|
#define STRCMP ((int(*)(const void *, const void *))&strcmp)
|
|
+#define PID_T_CMP ((int(*)(const void *, const void *))&pid_t_cmp)
|
|
+
|
|
+static int
|
|
+pid_t_cmp(const pid_t *lhs, const pid_t *rhs)
|
|
+{
|
|
+ /*
|
|
+ * This is always valid, quoth sys_types.h(7posix):
|
|
+ * > blksize_t, pid_t, and ssize_t shall be signed integer types.
|
|
+ */
|
|
+ return (*lhs - *rhs);
|
|
+}
|
|
+
|
|
+#define EXIT_ENOMEM() \
|
|
+ do { \
|
|
+ fprintf(stderr, PROGNAME "[%d]: " \
|
|
+ "not enough memory (L%d)!\n", getpid(), __LINE__); \
|
|
+ _exit(1); \
|
|
+ } while (0)
|
|
|
|
|
|
#define PROGNAME "zfs-mount-generator"
|
|
@@ -68,11 +80,20 @@
|
|
#define URI_REGEX_S "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):\\/\\/\\(.*\\)$"
|
|
static regex_t uri_regex;
|
|
|
|
+static char *argv0;
|
|
+
|
|
static const char *destdir = "/tmp";
|
|
static int destdir_fd = -1;
|
|
|
|
static void *known_pools = NULL; /* tsearch() of C strings */
|
|
-static void *noauto_files = NULL; /* tsearch() of C strings */
|
|
+static struct {
|
|
+ sem_t noauto_not_on_sem;
|
|
+
|
|
+ sem_t noauto_names_sem;
|
|
+ size_t noauto_names_len;
|
|
+ size_t noauto_names_max;
|
|
+ char noauto_names[][NAME_MAX];
|
|
+} *noauto_files;
|
|
|
|
|
|
static char *
|
|
@@ -82,12 +103,8 @@ systemd_escape(const char *input, const char *prepend, const char *append)
|
|
size_t applen = strlen(append);
|
|
size_t prelen = strlen(prepend);
|
|
char *ret = malloc(4 * len + prelen + applen + 1);
|
|
- if (!ret) {
|
|
- fprintf(stderr, PROGNAME "[%d]: "
|
|
- "out of memory to escape \"%s%s%s\"!\n",
|
|
- getpid(), prepend, input, append);
|
|
- return (NULL);
|
|
- }
|
|
+ if (!ret)
|
|
+ EXIT_ENOMEM();
|
|
|
|
memcpy(ret, prepend, prelen);
|
|
char *out = ret + prelen;
|
|
@@ -149,12 +166,8 @@ systemd_escape_path(char *input, const char *prepend, const char *append)
|
|
{
|
|
if (strcmp(input, "/") == 0) {
|
|
char *ret;
|
|
- if (asprintf(&ret, "%s-%s", prepend, append) == -1) {
|
|
- fprintf(stderr, PROGNAME "[%d]: "
|
|
- "out of memory to escape \"%s%s%s\"!\n",
|
|
- getpid(), prepend, input, append);
|
|
- ret = NULL;
|
|
- }
|
|
+ if (asprintf(&ret, "%s-%s", prepend, append) == -1)
|
|
+ EXIT_ENOMEM();
|
|
return (ret);
|
|
} else {
|
|
/*
|
|
@@ -196,10 +209,6 @@ fopenat(int dirfd, const char *pathname, int flags,
|
|
static int
|
|
line_worker(char *line, const char *cachefile)
|
|
{
|
|
- int ret = 0;
|
|
- void *tofree_all[8];
|
|
- void **tofree = tofree_all;
|
|
-
|
|
char *toktmp;
|
|
/* BEGIN CSTYLED */
|
|
const char *dataset = strtok_r(line, "\t", &toktmp);
|
|
@@ -231,9 +240,11 @@ line_worker(char *line, const char *cachefile)
|
|
if (p_nbmand == NULL) {
|
|
fprintf(stderr, PROGNAME "[%d]: %s: not enough tokens!\n",
|
|
getpid(), dataset);
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
+ strncpy(argv0, dataset, strlen(argv0));
|
|
+
|
|
/* Minimal pre-requisites to mount a ZFS dataset */
|
|
const char *after = "zfs-import.target";
|
|
const char *wants = "zfs-import.target";
|
|
@@ -269,31 +280,28 @@ line_worker(char *line, const char *cachefile)
|
|
|
|
|
|
if (strcmp(p_encroot, "-") != 0) {
|
|
- char *keyloadunit = *(tofree++) =
|
|
+ char *keyloadunit =
|
|
systemd_escape(p_encroot, "zfs-load-key@", ".service");
|
|
- if (keyloadunit == NULL)
|
|
- goto err;
|
|
|
|
if (strcmp(dataset, p_encroot) == 0) {
|
|
const char *keymountdep = NULL;
|
|
bool is_prompt = false;
|
|
- bool need_network = false;
|
|
|
|
regmatch_t uri_matches[3];
|
|
if (regexec(&uri_regex, p_keyloc,
|
|
- nitems(uri_matches), uri_matches, 0) == 0) {
|
|
- p_keyloc[uri_matches[1].rm_eo] = '\0';
|
|
+ sizeof (uri_matches) / sizeof (*uri_matches),
|
|
+ uri_matches, 0) == 0) {
|
|
p_keyloc[uri_matches[2].rm_eo] = '\0';
|
|
- const char *scheme =
|
|
- &p_keyloc[uri_matches[1].rm_so];
|
|
const char *path =
|
|
&p_keyloc[uri_matches[2].rm_so];
|
|
|
|
- if (strcmp(scheme, "https") == 0 ||
|
|
- strcmp(scheme, "http") == 0)
|
|
- need_network = true;
|
|
- else
|
|
- keymountdep = path;
|
|
+ /*
|
|
+ * Assumes all URI keylocations need
|
|
+ * the mount for their path;
|
|
+ * http://, for example, wouldn't
|
|
+ * (but it'd need network-online.target et al.)
|
|
+ */
|
|
+ keymountdep = path;
|
|
} else {
|
|
if (strcmp(p_keyloc, "prompt") != 0)
|
|
fprintf(stderr, PROGNAME "[%d]: %s: "
|
|
@@ -313,7 +321,7 @@ line_worker(char *line, const char *cachefile)
|
|
"couldn't open %s under %s: %s\n",
|
|
getpid(), dataset, keyloadunit, destdir,
|
|
strerror(errno));
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
fprintf(keyloadunit_f,
|
|
@@ -327,22 +335,20 @@ line_worker(char *line, const char *cachefile)
|
|
"After=%s\n",
|
|
dataset, cachefile, wants, after);
|
|
|
|
- if (need_network)
|
|
- fprintf(keyloadunit_f,
|
|
- "Wants=network-online.target\n"
|
|
- "After=network-online.target\n");
|
|
-
|
|
if (p_systemd_requires)
|
|
fprintf(keyloadunit_f,
|
|
"Requires=%s\n", p_systemd_requires);
|
|
|
|
- if (p_systemd_requiresmountsfor)
|
|
- fprintf(keyloadunit_f,
|
|
- "RequiresMountsFor=%s\n",
|
|
- p_systemd_requiresmountsfor);
|
|
- if (keymountdep)
|
|
- fprintf(keyloadunit_f,
|
|
- "RequiresMountsFor='%s'\n", keymountdep);
|
|
+ if (p_systemd_requiresmountsfor || keymountdep) {
|
|
+ fprintf(keyloadunit_f, "RequiresMountsFor=");
|
|
+ if (p_systemd_requiresmountsfor)
|
|
+ fprintf(keyloadunit_f,
|
|
+ "%s ", p_systemd_requiresmountsfor);
|
|
+ if (keymountdep)
|
|
+ fprintf(keyloadunit_f,
|
|
+ "'%s'", keymountdep);
|
|
+ fprintf(keyloadunit_f, "\n");
|
|
+ }
|
|
|
|
/* BEGIN CSTYLED */
|
|
fprintf(keyloadunit_f,
|
|
@@ -387,13 +393,9 @@ line_worker(char *line, const char *cachefile)
|
|
if (after[0] == '\0')
|
|
after = keyloadunit;
|
|
else if (asprintf(&toktmp, "%s %s", after, keyloadunit) != -1)
|
|
- after = *(tofree++) = toktmp;
|
|
- else {
|
|
- fprintf(stderr, PROGNAME "[%d]: %s: "
|
|
- "out of memory to generate after=\"%s %s\"!\n",
|
|
- getpid(), dataset, after, keyloadunit);
|
|
- goto err;
|
|
- }
|
|
+ after = toktmp;
|
|
+ else
|
|
+ EXIT_ENOMEM();
|
|
}
|
|
|
|
|
|
@@ -402,12 +404,12 @@ line_worker(char *line, const char *cachefile)
|
|
strcmp(p_systemd_ignore, "off") == 0) {
|
|
/* ok */
|
|
} else if (strcmp(p_systemd_ignore, "on") == 0)
|
|
- goto end;
|
|
+ return (0);
|
|
else {
|
|
fprintf(stderr, PROGNAME "[%d]: %s: "
|
|
"invalid org.openzfs.systemd:ignore=%s\n",
|
|
getpid(), dataset, p_systemd_ignore);
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
/* Check for canmount */
|
|
@@ -416,21 +418,21 @@ line_worker(char *line, const char *cachefile)
|
|
} else if (strcmp(p_canmount, "noauto") == 0)
|
|
noauto = true;
|
|
else if (strcmp(p_canmount, "off") == 0)
|
|
- goto end;
|
|
+ return (0);
|
|
else {
|
|
fprintf(stderr, PROGNAME "[%d]: %s: invalid canmount=%s\n",
|
|
getpid(), dataset, p_canmount);
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
/* Check for legacy and blank mountpoints */
|
|
if (strcmp(p_mountpoint, "legacy") == 0 ||
|
|
strcmp(p_mountpoint, "none") == 0)
|
|
- goto end;
|
|
+ return (0);
|
|
else if (p_mountpoint[0] != '/') {
|
|
fprintf(stderr, PROGNAME "[%d]: %s: invalid mountpoint=%s\n",
|
|
getpid(), dataset, p_mountpoint);
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
/* Escape the mountpoint per systemd policy */
|
|
@@ -440,7 +442,7 @@ line_worker(char *line, const char *cachefile)
|
|
fprintf(stderr,
|
|
PROGNAME "[%d]: %s: abnormal simplified mountpoint: %s\n",
|
|
getpid(), dataset, p_mountpoint);
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
|
|
@@ -550,7 +552,8 @@ line_worker(char *line, const char *cachefile)
|
|
* files if we're sure they were created by us. (see 5.)
|
|
* 2. We handle files differently based on canmount.
|
|
* Units with canmount=on always have precedence over noauto.
|
|
- * This is enforced by processing these units before all others.
|
|
+ * This is enforced by the noauto_not_on_sem semaphore,
|
|
+ * which is only unlocked when the last canmount=on process exits.
|
|
* It is important to use p_canmount and not noauto here,
|
|
* since we categorise by canmount while other properties,
|
|
* e.g. org.openzfs.systemd:wanted-by, also modify noauto.
|
|
@@ -558,7 +561,7 @@ line_worker(char *line, const char *cachefile)
|
|
* Additionally, we use noauto_files to track the unit file names
|
|
* (which are the systemd-escaped mountpoints) of all (exclusively)
|
|
* noauto datasets that had a file created.
|
|
- * 4. If the file to be created is found in the tracking tree,
|
|
+ * 4. If the file to be created is found in the tracking array,
|
|
* we do NOT create it.
|
|
* 5. If a file exists for a noauto dataset,
|
|
* we check whether the file name is in the array.
|
|
@@ -568,14 +571,29 @@ line_worker(char *line, const char *cachefile)
|
|
* further noauto datasets creating a file for this path again.
|
|
*/
|
|
|
|
+ {
|
|
+ sem_t *our_sem = (strcmp(p_canmount, "on") == 0) ?
|
|
+ &noauto_files->noauto_names_sem :
|
|
+ &noauto_files->noauto_not_on_sem;
|
|
+ while (sem_wait(our_sem) == -1 && errno == EINTR)
|
|
+ ;
|
|
+ }
|
|
+
|
|
struct stat stbuf;
|
|
bool already_exists = fstatat(destdir_fd, mountfile, &stbuf, 0) == 0;
|
|
- bool is_known = tfind(mountfile, &noauto_files, STRCMP) != NULL;
|
|
|
|
- *(tofree++) = (void *)mountfile;
|
|
+ bool is_known = false;
|
|
+ for (size_t i = 0; i < noauto_files->noauto_names_len; ++i) {
|
|
+ if (strncmp(
|
|
+ noauto_files->noauto_names[i], mountfile, NAME_MAX) == 0) {
|
|
+ is_known = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
if (already_exists) {
|
|
if (is_known) {
|
|
- /* If it's in noauto_files, we must be noauto too */
|
|
+ /* If it's in $noauto_files, we must be noauto too */
|
|
|
|
/* See 5 */
|
|
errno = 0;
|
|
@@ -596,31 +614,43 @@ line_worker(char *line, const char *cachefile)
|
|
}
|
|
|
|
/* File exists: skip current dataset */
|
|
- goto end;
|
|
+ if (strcmp(p_canmount, "on") == 0)
|
|
+ sem_post(&noauto_files->noauto_names_sem);
|
|
+ return (0);
|
|
} else {
|
|
if (is_known) {
|
|
/* See 4 */
|
|
- goto end;
|
|
+ if (strcmp(p_canmount, "on") == 0)
|
|
+ sem_post(&noauto_files->noauto_names_sem);
|
|
+ return (0);
|
|
} else if (strcmp(p_canmount, "noauto") == 0) {
|
|
- if (tsearch(mountfile, &noauto_files, STRCMP) == NULL)
|
|
+ if (noauto_files->noauto_names_len ==
|
|
+ noauto_files->noauto_names_max)
|
|
fprintf(stderr, PROGNAME "[%d]: %s: "
|
|
- "out of memory for noauto datasets! "
|
|
- "Not tracking %s.\n",
|
|
- getpid(), dataset, mountfile);
|
|
- else
|
|
- /* mountfile escaped to noauto_files */
|
|
- *(--tofree) = NULL;
|
|
+ "noauto dataset limit (%zu) reached! "
|
|
+ "Not tracking %s. Please report this to "
|
|
+ "https://github.com/openzfs/zfs\n",
|
|
+ getpid(), dataset,
|
|
+ noauto_files->noauto_names_max, mountfile);
|
|
+ else {
|
|
+ strncpy(noauto_files->noauto_names[
|
|
+ noauto_files->noauto_names_len],
|
|
+ mountfile, NAME_MAX);
|
|
+ ++noauto_files->noauto_names_len;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
FILE *mountfile_f = fopenat(destdir_fd, mountfile,
|
|
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w", 0644);
|
|
+ if (strcmp(p_canmount, "on") == 0)
|
|
+ sem_post(&noauto_files->noauto_names_sem);
|
|
if (!mountfile_f) {
|
|
fprintf(stderr,
|
|
PROGNAME "[%d]: %s: couldn't open %s under %s: %s\n",
|
|
getpid(), dataset, mountfile, destdir, strerror(errno));
|
|
- goto err;
|
|
+ return (1);
|
|
}
|
|
|
|
fprintf(mountfile_f,
|
|
@@ -669,17 +699,12 @@ line_worker(char *line, const char *cachefile)
|
|
(void) fclose(mountfile_f);
|
|
|
|
if (!requiredby && !wantedby)
|
|
- goto end;
|
|
+ return (0);
|
|
|
|
/* Finally, create the appropriate dependencies */
|
|
char *linktgt;
|
|
- if (asprintf(&linktgt, "../%s", mountfile) == -1) {
|
|
- fprintf(stderr, PROGNAME "[%d]: %s: "
|
|
- "out of memory for dependents of %s!\n",
|
|
- getpid(), dataset, mountfile);
|
|
- goto err;
|
|
- }
|
|
- *(tofree++) = linktgt;
|
|
+ if (asprintf(&linktgt, "../%s", mountfile) == -1)
|
|
+ EXIT_ENOMEM();
|
|
|
|
char *dependencies[][2] = {
|
|
{"wants", wantedby},
|
|
@@ -694,14 +719,8 @@ line_worker(char *line, const char *cachefile)
|
|
reqby;
|
|
reqby = strtok_r(NULL, " ", &toktmp)) {
|
|
char *depdir;
|
|
- if (asprintf(
|
|
- &depdir, "%s.%s", reqby, (*dep)[0]) == -1) {
|
|
- fprintf(stderr, PROGNAME "[%d]: %s: "
|
|
- "out of memory for dependent dir name "
|
|
- "\"%s.%s\"!\n",
|
|
- getpid(), dataset, reqby, (*dep)[0]);
|
|
- continue;
|
|
- }
|
|
+ if (asprintf(&depdir, "%s.%s", reqby, (*dep)[0]) == -1)
|
|
+ EXIT_ENOMEM();
|
|
|
|
(void) mkdirat(destdir_fd, depdir, 0755);
|
|
int depdir_fd = openat(destdir_fd, depdir,
|
|
@@ -727,24 +746,7 @@ line_worker(char *line, const char *cachefile)
|
|
}
|
|
}
|
|
|
|
-end:
|
|
- if (tofree >= tofree_all + nitems(tofree_all)) {
|
|
- /*
|
|
- * This won't happen as-is:
|
|
- * we've got 8 slots and allocate 4 things at most.
|
|
- */
|
|
- fprintf(stderr,
|
|
- PROGNAME "[%d]: %s: need to free %zu > %zu!\n",
|
|
- getpid(), dataset, tofree - tofree_all, nitems(tofree_all));
|
|
- ret = tofree - tofree_all;
|
|
- }
|
|
-
|
|
- while (tofree-- != tofree_all)
|
|
- free(*tofree);
|
|
- return (ret);
|
|
-err:
|
|
- ret = 1;
|
|
- goto end;
|
|
+ return (0);
|
|
}
|
|
|
|
|
|
@@ -778,11 +780,12 @@ main(int argc, char **argv)
|
|
if (kmfd >= 0) {
|
|
(void) dup2(kmfd, STDERR_FILENO);
|
|
(void) close(kmfd);
|
|
-
|
|
- setlinebuf(stderr);
|
|
}
|
|
}
|
|
|
|
+ uint8_t debug = 0;
|
|
+
|
|
+ argv0 = argv[0];
|
|
switch (argc) {
|
|
case 1:
|
|
/* Use default */
|
|
@@ -841,9 +844,33 @@ main(int argc, char **argv)
|
|
}
|
|
}
|
|
|
|
- bool debug = false;
|
|
+ {
|
|
+ /*
|
|
+ * We could just get a gigabyte here and Not Care,
|
|
+ * but if vm.overcommit_memory=2, then MAP_NORESERVE is ignored
|
|
+ * and we'd try (and likely fail) to rip it out of swap
|
|
+ */
|
|
+ noauto_files = mmap(NULL, 4 * 1024 * 1024,
|
|
+ PROT_READ | PROT_WRITE,
|
|
+ MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
|
+ if (noauto_files == MAP_FAILED) {
|
|
+ fprintf(stderr,
|
|
+ PROGNAME "[%d]: couldn't allocate IPC region: %s\n",
|
|
+ getpid(), strerror(errno));
|
|
+ _exit(1);
|
|
+ }
|
|
+
|
|
+ sem_init(&noauto_files->noauto_not_on_sem, true, 0);
|
|
+ sem_init(&noauto_files->noauto_names_sem, true, 1);
|
|
+ noauto_files->noauto_names_len = 0;
|
|
+ /* Works out to 16447ish, *well* enough */
|
|
+ noauto_files->noauto_names_max =
|
|
+ (4 * 1024 * 1024 - sizeof (*noauto_files)) / NAME_MAX;
|
|
+ }
|
|
+
|
|
char *line = NULL;
|
|
size_t linelen = 0;
|
|
+ struct timespec time_start = {};
|
|
{
|
|
const char *dbgenv = getenv("ZFS_DEBUG");
|
|
if (dbgenv)
|
|
@@ -852,7 +879,7 @@ main(int argc, char **argv)
|
|
FILE *cmdline = fopen("/proc/cmdline", "re");
|
|
if (cmdline != NULL) {
|
|
if (getline(&line, &linelen, cmdline) >= 0)
|
|
- debug = strstr(line, "debug");
|
|
+ debug = strstr(line, "debug") ? 2 : 0;
|
|
(void) fclose(cmdline);
|
|
}
|
|
}
|
|
@@ -861,17 +888,19 @@ main(int argc, char **argv)
|
|
dup2(STDERR_FILENO, STDOUT_FILENO);
|
|
}
|
|
|
|
- struct timespec time_start = {};
|
|
+ size_t forked_canmount_on = 0;
|
|
+ size_t forked_canmount_not_on = 0;
|
|
+ size_t canmount_on_pids_len = 128;
|
|
+ pid_t *canmount_on_pids =
|
|
+ malloc(canmount_on_pids_len * sizeof (*canmount_on_pids));
|
|
+ if (canmount_on_pids == NULL)
|
|
+ canmount_on_pids_len = 0;
|
|
+
|
|
if (debug)
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &time_start);
|
|
|
|
- struct line {
|
|
- char *line;
|
|
- const char *fname;
|
|
- struct line *next;
|
|
- } *lines_canmount_not_on = NULL;
|
|
-
|
|
- int ret = 0;
|
|
+ ssize_t read;
|
|
+ pid_t pid;
|
|
struct dirent *cachent;
|
|
while ((cachent = readdir(fslist_dir)) != NULL) {
|
|
if (strcmp(cachent->d_name, ".") == 0 ||
|
|
@@ -887,67 +916,129 @@ main(int argc, char **argv)
|
|
continue;
|
|
}
|
|
|
|
- const char *filename = FREE_STATICS ? "(elided)" : NULL;
|
|
-
|
|
- ssize_t read;
|
|
while ((read = getline(&line, &linelen, cachefile)) >= 0) {
|
|
line[read - 1] = '\0'; /* newline */
|
|
|
|
- char *canmount = line;
|
|
- canmount += strcspn(canmount, "\t");
|
|
- canmount += strspn(canmount, "\t");
|
|
- canmount += strcspn(canmount, "\t");
|
|
- canmount += strspn(canmount, "\t");
|
|
- bool canmount_on = strncmp(canmount, "on", 2) == 0;
|
|
-
|
|
- if (canmount_on)
|
|
- ret |= line_worker(line, cachent->d_name);
|
|
- else {
|
|
- if (filename == NULL)
|
|
- filename =
|
|
- strdup(cachent->d_name) ?: "(?)";
|
|
-
|
|
- struct line *l = calloc(1, sizeof (*l));
|
|
- char *nl = strdup(line);
|
|
- if (l == NULL || nl == NULL) {
|
|
- fprintf(stderr, PROGNAME "[%d]: "
|
|
- "out of memory for \"%s\" in %s\n",
|
|
- getpid(), line, cachent->d_name);
|
|
- free(l);
|
|
- free(nl);
|
|
- continue;
|
|
+ switch (pid = fork()) {
|
|
+ case -1:
|
|
+ fprintf(stderr,
|
|
+ PROGNAME "[%d]: couldn't fork for %s: %s\n",
|
|
+ getpid(), line, strerror(errno));
|
|
+ break;
|
|
+ case 0: /* child */
|
|
+ _exit(line_worker(line, cachent->d_name));
|
|
+ default: { /* parent */
|
|
+ char *tmp;
|
|
+ char *dset = strtok_r(line, "\t", &tmp);
|
|
+ strtok_r(NULL, "\t", &tmp);
|
|
+ char *canmount = strtok_r(NULL, "\t", &tmp);
|
|
+ bool canmount_on =
|
|
+ canmount && strncmp(canmount, "on", 2) == 0;
|
|
+
|
|
+ if (debug >= 2)
|
|
+ printf(PROGNAME ": forked %d, "
|
|
+ "canmount_on=%d, dataset=%s\n",
|
|
+ (int)pid, canmount_on, dset);
|
|
+
|
|
+ if (canmount_on &&
|
|
+ forked_canmount_on ==
|
|
+ canmount_on_pids_len) {
|
|
+ size_t new_len =
|
|
+ (canmount_on_pids_len ?: 16) * 2;
|
|
+ void *new_pidlist =
|
|
+ realloc(canmount_on_pids,
|
|
+ new_len *
|
|
+ sizeof (*canmount_on_pids));
|
|
+ if (!new_pidlist) {
|
|
+ fprintf(stderr,
|
|
+ PROGNAME "[%d]: "
|
|
+ "out of memory! "
|
|
+ "Mount ordering may be "
|
|
+ "affected.\n", getpid());
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ canmount_on_pids = new_pidlist;
|
|
+ canmount_on_pids_len = new_len;
|
|
}
|
|
- l->line = nl;
|
|
- l->fname = filename;
|
|
- l->next = lines_canmount_not_on;
|
|
- lines_canmount_not_on = l;
|
|
+
|
|
+ if (canmount_on) {
|
|
+ canmount_on_pids[forked_canmount_on] =
|
|
+ pid;
|
|
+ ++forked_canmount_on;
|
|
+ } else
|
|
+ ++forked_canmount_not_on;
|
|
+ break;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
- fclose(cachefile);
|
|
+ (void) fclose(cachefile);
|
|
}
|
|
free(line);
|
|
|
|
- while (lines_canmount_not_on) {
|
|
- struct line *l = lines_canmount_not_on;
|
|
- lines_canmount_not_on = l->next;
|
|
+ if (forked_canmount_on == 0) {
|
|
+ /* No canmount=on processes to finish, so don't deadlock here */
|
|
+ for (size_t i = 0; i < forked_canmount_not_on; ++i)
|
|
+ sem_post(&noauto_files->noauto_not_on_sem);
|
|
+ } else {
|
|
+ /* Likely a no-op, since we got these from a narrow fork loop */
|
|
+ qsort(canmount_on_pids, forked_canmount_on,
|
|
+ sizeof (*canmount_on_pids), PID_T_CMP);
|
|
+ }
|
|
|
|
- ret |= line_worker(l->line, l->fname);
|
|
- if (FREE_STATICS) {
|
|
- free(l->line);
|
|
- free(l);
|
|
+ int status, ret = 0;
|
|
+ struct rusage usage;
|
|
+ size_t forked_canmount_on_max = forked_canmount_on;
|
|
+ while ((pid = wait4(-1, &status, 0, &usage)) != -1) {
|
|
+ ret |= WEXITSTATUS(status) | WTERMSIG(status);
|
|
+
|
|
+ if (forked_canmount_on != 0) {
|
|
+ if (bsearch(&pid, canmount_on_pids,
|
|
+ forked_canmount_on_max, sizeof (*canmount_on_pids),
|
|
+ PID_T_CMP))
|
|
+ --forked_canmount_on;
|
|
+
|
|
+ if (forked_canmount_on == 0) {
|
|
+ /*
|
|
+ * All canmount=on processes have finished,
|
|
+ * let all the lower-priority ones finish now
|
|
+ */
|
|
+ for (size_t i = 0;
|
|
+ i < forked_canmount_not_on; ++i)
|
|
+ sem_post(
|
|
+ &noauto_files->noauto_not_on_sem);
|
|
+ }
|
|
}
|
|
+
|
|
+ if (debug >= 2)
|
|
+ printf(PROGNAME ": %d done, user=%llu.%06us, "
|
|
+ "system=%llu.%06us, maxrss=%ldB, ex=0x%x\n",
|
|
+ (int)pid,
|
|
+ (unsigned long long) usage.ru_utime.tv_sec,
|
|
+ (unsigned int) usage.ru_utime.tv_usec,
|
|
+ (unsigned long long) usage.ru_stime.tv_sec,
|
|
+ (unsigned int) usage.ru_stime.tv_usec,
|
|
+ usage.ru_maxrss * 1024, status);
|
|
}
|
|
|
|
if (debug) {
|
|
struct timespec time_end = {};
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &time_end);
|
|
|
|
- struct rusage usage;
|
|
getrusage(RUSAGE_SELF, &usage);
|
|
printf(
|
|
"\n"
|
|
- PROGNAME ": "
|
|
+ PROGNAME ": self : "
|
|
+ "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
|
|
+ (unsigned long long) usage.ru_utime.tv_sec,
|
|
+ (unsigned int) usage.ru_utime.tv_usec,
|
|
+ (unsigned long long) usage.ru_stime.tv_sec,
|
|
+ (unsigned int) usage.ru_stime.tv_usec,
|
|
+ usage.ru_maxrss * 1024);
|
|
+
|
|
+ getrusage(RUSAGE_CHILDREN, &usage);
|
|
+ printf(PROGNAME ": children: "
|
|
"user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
|
|
(unsigned long long) usage.ru_utime.tv_sec,
|
|
(unsigned int) usage.ru_utime.tv_usec,
|
|
@@ -977,7 +1068,7 @@ main(int argc, char **argv)
|
|
time_init.tv_nsec / 1000000000;
|
|
time_init.tv_nsec %= 1000000000;
|
|
|
|
- printf(PROGNAME ": "
|
|
+ printf(PROGNAME ": wall : "
|
|
"total=%llu.%09llus = "
|
|
"init=%llu.%09llus + real=%llu.%09llus\n",
|
|
(unsigned long long) time_init.tv_sec,
|
|
@@ -986,15 +1077,7 @@ main(int argc, char **argv)
|
|
(unsigned long long) time_start.tv_nsec,
|
|
(unsigned long long) time_end.tv_sec,
|
|
(unsigned long long) time_end.tv_nsec);
|
|
-
|
|
- fflush(stdout);
|
|
}
|
|
|
|
- if (FREE_STATICS) {
|
|
- closedir(fslist_dir);
|
|
- tdestroy(noauto_files, free);
|
|
- tdestroy(known_pools, free);
|
|
- regfree(&uri_regex);
|
|
- }
|
|
_exit(ret);
|
|
}
|
|
diff --git a/man/man8/zfs-mount-generator.8.in b/man/man8/zfs-mount-generator.8.in
|
|
index ae8937038e..7aa332ba81 100644
|
|
--- a/man/man8/zfs-mount-generator.8.in
|
|
+++ b/man/man8/zfs-mount-generator.8.in
|
|
@@ -142,11 +142,22 @@ ZEDLET, if enabled
|
|
.Pq see Xr zed 8 .
|
|
.
|
|
.Sh ENVIRONMENT
|
|
-If the
|
|
+The
|
|
.Sy ZFS_DEBUG
|
|
-environment variable is nonzero
|
|
-.Pq or unset and Pa /proc/cmdline No contains Qq Sy debug ,
|
|
-print summary accounting information at the end.
|
|
+environment variable can either be
|
|
+.Sy 0
|
|
+(default),
|
|
+.Sy 1
|
|
+(print summary accounting information at the end), or at least
|
|
+.Sy 2
|
|
+(print accounting information for each subprocess as it finishes).
|
|
+.
|
|
+If not present,
|
|
+.Pa /proc/cmdline
|
|
+is additionally checked for
|
|
+.Qq debug ,
|
|
+in which case the debug level is set to
|
|
+.Sy 2 .
|
|
.
|
|
.Sh EXAMPLES
|
|
To begin, enable tracking for the pool:
|
|
--
|
|
2.32.0
|
|
|