1*61145dc2SMartin Matuska // SPDX-License-Identifier: MIT
216038816SMartin Matuska /*
316038816SMartin Matuska * Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
416038816SMartin Matuska * Copyright (c) 2020 InsanePrawn <insane.prawny@gmail.com>
516038816SMartin Matuska *
616038816SMartin Matuska * Permission is hereby granted, free of charge, to any person obtaining
716038816SMartin Matuska * a copy of this software and associated documentation files (the
816038816SMartin Matuska * "Software"), to deal in the Software without restriction, including
916038816SMartin Matuska * without limitation the rights to use, copy, modify, merge, publish,
1016038816SMartin Matuska * distribute, sublicense, and/or sell copies of the Software, and to
1116038816SMartin Matuska * permit persons to whom the Software is furnished to do so, subject to
1216038816SMartin Matuska * the following conditions:
1316038816SMartin Matuska *
1416038816SMartin Matuska * The above copyright notice and this permission notice shall be
1516038816SMartin Matuska * included in all copies or substantial portions of the Software.
1616038816SMartin Matuska *
1716038816SMartin Matuska * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1816038816SMartin Matuska * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1916038816SMartin Matuska * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2016038816SMartin Matuska * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
2116038816SMartin Matuska * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
2216038816SMartin Matuska * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2316038816SMartin Matuska * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2416038816SMartin Matuska */
2516038816SMartin Matuska
2616038816SMartin Matuska
2716038816SMartin Matuska #include <sys/resource.h>
2816038816SMartin Matuska #include <sys/types.h>
2916038816SMartin Matuska #include <sys/time.h>
3016038816SMartin Matuska #include <sys/stat.h>
3116038816SMartin Matuska #include <stdbool.h>
3216038816SMartin Matuska #include <unistd.h>
3316038816SMartin Matuska #include <fcntl.h>
3416038816SMartin Matuska #include <stdio.h>
3516038816SMartin Matuska #include <time.h>
3616038816SMartin Matuska #include <regex.h>
3716038816SMartin Matuska #include <search.h>
3816038816SMartin Matuska #include <dirent.h>
3916038816SMartin Matuska #include <string.h>
4016038816SMartin Matuska #include <stdlib.h>
4116038816SMartin Matuska #include <limits.h>
4216038816SMartin Matuska #include <errno.h>
4316038816SMartin Matuska #include <libzfs.h>
4416038816SMartin Matuska
4516038816SMartin Matuska /*
46681ce946SMartin Matuska * For debugging only.
47681ce946SMartin Matuska *
48681ce946SMartin Matuska * Free statics with trivial life-times,
49681ce946SMartin Matuska * but saved line filenames are replaced with a static string.
5016038816SMartin Matuska */
51681ce946SMartin Matuska #define FREE_STATICS false
5216038816SMartin Matuska
53681ce946SMartin Matuska #define nitems(arr) (sizeof (arr) / sizeof (*arr))
54681ce946SMartin Matuska #define STRCMP ((int(*)(const void *, const void *))&strcmp)
5516038816SMartin Matuska
5616038816SMartin Matuska
5716038816SMartin Matuska #define PROGNAME "zfs-mount-generator"
5816038816SMartin Matuska #define FSLIST SYSCONFDIR "/zfs/zfs-list.cache"
5916038816SMartin Matuska #define ZFS SBINDIR "/zfs"
6016038816SMartin Matuska
6116038816SMartin Matuska #define OUTPUT_HEADER \
6216038816SMartin Matuska "# Automatically generated by " PROGNAME "\n" \
6316038816SMartin Matuska "\n"
6416038816SMartin Matuska
6516038816SMartin Matuska /*
6616038816SMartin Matuska * Starts like the one in libzfs_util.c but also matches "//"
6716038816SMartin Matuska * and captures until the end, since we actually use it for path extraxion
6816038816SMartin Matuska */
6916038816SMartin Matuska #define URI_REGEX_S "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):\\/\\/\\(.*\\)$"
7016038816SMartin Matuska static regex_t uri_regex;
7116038816SMartin Matuska
7216038816SMartin Matuska static const char *destdir = "/tmp";
7316038816SMartin Matuska static int destdir_fd = -1;
7416038816SMartin Matuska
7516038816SMartin Matuska static void *known_pools = NULL; /* tsearch() of C strings */
76681ce946SMartin Matuska static void *noauto_files = NULL; /* tsearch() of C strings */
7716038816SMartin Matuska
7816038816SMartin Matuska
7916038816SMartin Matuska static char *
systemd_escape(const char * input,const char * prepend,const char * append)8016038816SMartin Matuska systemd_escape(const char *input, const char *prepend, const char *append)
8116038816SMartin Matuska {
8216038816SMartin Matuska size_t len = strlen(input);
8316038816SMartin Matuska size_t applen = strlen(append);
8416038816SMartin Matuska size_t prelen = strlen(prepend);
8516038816SMartin Matuska char *ret = malloc(4 * len + prelen + applen + 1);
86681ce946SMartin Matuska if (!ret) {
87681ce946SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
88681ce946SMartin Matuska "out of memory to escape \"%s%s%s\"!\n",
89681ce946SMartin Matuska getpid(), prepend, input, append);
90681ce946SMartin Matuska return (NULL);
91681ce946SMartin Matuska }
9216038816SMartin Matuska
9316038816SMartin Matuska memcpy(ret, prepend, prelen);
9416038816SMartin Matuska char *out = ret + prelen;
9516038816SMartin Matuska
9616038816SMartin Matuska const char *cur = input;
9716038816SMartin Matuska if (*cur == '.') {
9816038816SMartin Matuska memcpy(out, "\\x2e", 4);
9916038816SMartin Matuska out += 4;
10016038816SMartin Matuska ++cur;
10116038816SMartin Matuska }
10216038816SMartin Matuska for (; *cur; ++cur) {
10316038816SMartin Matuska if (*cur == '/')
10416038816SMartin Matuska *(out++) = '-';
10516038816SMartin Matuska else if (strchr(
10616038816SMartin Matuska "0123456789"
10716038816SMartin Matuska "abcdefghijklmnopqrstuvwxyz"
10816038816SMartin Matuska "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
10916038816SMartin Matuska ":_.", *cur))
11016038816SMartin Matuska *(out++) = *cur;
11116038816SMartin Matuska else {
11216038816SMartin Matuska sprintf(out, "\\x%02x", (int)*cur);
11316038816SMartin Matuska out += 4;
11416038816SMartin Matuska }
11516038816SMartin Matuska }
11616038816SMartin Matuska
11716038816SMartin Matuska memcpy(out, append, applen + 1);
11816038816SMartin Matuska return (ret);
11916038816SMartin Matuska }
12016038816SMartin Matuska
12116038816SMartin Matuska static void
simplify_path(char * path)12216038816SMartin Matuska simplify_path(char *path)
12316038816SMartin Matuska {
12416038816SMartin Matuska char *out = path;
12516038816SMartin Matuska for (char *cur = path; *cur; ++cur) {
12616038816SMartin Matuska if (*cur == '/') {
12716038816SMartin Matuska while (*(cur + 1) == '/')
12816038816SMartin Matuska ++cur;
12916038816SMartin Matuska *(out++) = '/';
13016038816SMartin Matuska } else
13116038816SMartin Matuska *(out++) = *cur;
13216038816SMartin Matuska }
13316038816SMartin Matuska
13416038816SMartin Matuska *(out++) = '\0';
13516038816SMartin Matuska }
13616038816SMartin Matuska
13716038816SMartin Matuska static bool
strendswith(const char * what,const char * suff)13816038816SMartin Matuska strendswith(const char *what, const char *suff)
13916038816SMartin Matuska {
14016038816SMartin Matuska size_t what_l = strlen(what);
14116038816SMartin Matuska size_t suff_l = strlen(suff);
14216038816SMartin Matuska
14316038816SMartin Matuska return ((what_l >= suff_l) &&
14416038816SMartin Matuska (strcmp(what + what_l - suff_l, suff) == 0));
14516038816SMartin Matuska }
14616038816SMartin Matuska
14716038816SMartin Matuska /* Assumes already-simplified path, doesn't modify input */
14816038816SMartin Matuska static char *
systemd_escape_path(char * input,const char * prepend,const char * append)14916038816SMartin Matuska systemd_escape_path(char *input, const char *prepend, const char *append)
15016038816SMartin Matuska {
15116038816SMartin Matuska if (strcmp(input, "/") == 0) {
15216038816SMartin Matuska char *ret;
153681ce946SMartin Matuska if (asprintf(&ret, "%s-%s", prepend, append) == -1) {
154681ce946SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
155681ce946SMartin Matuska "out of memory to escape \"%s%s%s\"!\n",
156681ce946SMartin Matuska getpid(), prepend, input, append);
157681ce946SMartin Matuska ret = NULL;
158681ce946SMartin Matuska }
15916038816SMartin Matuska return (ret);
16016038816SMartin Matuska } else {
16116038816SMartin Matuska /*
16216038816SMartin Matuska * path_is_normalized() (flattened for absolute paths here),
16316038816SMartin Matuska * required for proper escaping
16416038816SMartin Matuska */
16516038816SMartin Matuska if (strstr(input, "/./") || strstr(input, "/../") ||
16616038816SMartin Matuska strendswith(input, "/.") || strendswith(input, "/.."))
16716038816SMartin Matuska return (NULL);
16816038816SMartin Matuska
16916038816SMartin Matuska
17016038816SMartin Matuska if (input[0] == '/')
17116038816SMartin Matuska ++input;
17216038816SMartin Matuska
17316038816SMartin Matuska char *back = &input[strlen(input) - 1];
17416038816SMartin Matuska bool deslash = *back == '/';
17516038816SMartin Matuska if (deslash)
17616038816SMartin Matuska *back = '\0';
17716038816SMartin Matuska
17816038816SMartin Matuska char *ret = systemd_escape(input, prepend, append);
17916038816SMartin Matuska
18016038816SMartin Matuska if (deslash)
18116038816SMartin Matuska *back = '/';
18216038816SMartin Matuska return (ret);
18316038816SMartin Matuska }
18416038816SMartin Matuska }
18516038816SMartin Matuska
18616038816SMartin Matuska static FILE *
fopenat(int dirfd,const char * pathname,int flags,const char * stream_mode,mode_t mode)18716038816SMartin Matuska fopenat(int dirfd, const char *pathname, int flags,
18816038816SMartin Matuska const char *stream_mode, mode_t mode)
18916038816SMartin Matuska {
19016038816SMartin Matuska int fd = openat(dirfd, pathname, flags, mode);
19116038816SMartin Matuska if (fd < 0)
19216038816SMartin Matuska return (NULL);
19316038816SMartin Matuska
19416038816SMartin Matuska return (fdopen(fd, stream_mode));
19516038816SMartin Matuska }
19616038816SMartin Matuska
19716038816SMartin Matuska static int
line_worker(char * line,const char * cachefile)19816038816SMartin Matuska line_worker(char *line, const char *cachefile)
19916038816SMartin Matuska {
200681ce946SMartin Matuska int ret = 0;
201681ce946SMartin Matuska void *tofree_all[8];
202681ce946SMartin Matuska void **tofree = tofree_all;
203681ce946SMartin Matuska
20416038816SMartin Matuska char *toktmp;
20516038816SMartin Matuska /* BEGIN CSTYLED */
20616038816SMartin Matuska const char *dataset = strtok_r(line, "\t", &toktmp);
20716038816SMartin Matuska char *p_mountpoint = strtok_r(NULL, "\t", &toktmp);
20816038816SMartin Matuska const char *p_canmount = strtok_r(NULL, "\t", &toktmp);
20916038816SMartin Matuska const char *p_atime = strtok_r(NULL, "\t", &toktmp);
21016038816SMartin Matuska const char *p_relatime = strtok_r(NULL, "\t", &toktmp);
21116038816SMartin Matuska const char *p_devices = strtok_r(NULL, "\t", &toktmp);
21216038816SMartin Matuska const char *p_exec = strtok_r(NULL, "\t", &toktmp);
21316038816SMartin Matuska const char *p_readonly = strtok_r(NULL, "\t", &toktmp);
21416038816SMartin Matuska const char *p_setuid = strtok_r(NULL, "\t", &toktmp);
21516038816SMartin Matuska const char *p_nbmand = strtok_r(NULL, "\t", &toktmp);
21616038816SMartin Matuska const char *p_encroot = strtok_r(NULL, "\t", &toktmp) ?: "-";
21716038816SMartin Matuska char *p_keyloc = strtok_r(NULL, "\t", &toktmp) ?: strdupa("none");
21816038816SMartin Matuska const char *p_systemd_requires = strtok_r(NULL, "\t", &toktmp) ?: "-";
21916038816SMartin Matuska const char *p_systemd_requiresmountsfor = strtok_r(NULL, "\t", &toktmp) ?: "-";
22016038816SMartin Matuska const char *p_systemd_before = strtok_r(NULL, "\t", &toktmp) ?: "-";
22116038816SMartin Matuska const char *p_systemd_after = strtok_r(NULL, "\t", &toktmp) ?: "-";
22216038816SMartin Matuska char *p_systemd_wantedby = strtok_r(NULL, "\t", &toktmp) ?: strdupa("-");
22316038816SMartin Matuska char *p_systemd_requiredby = strtok_r(NULL, "\t", &toktmp) ?: strdupa("-");
22416038816SMartin Matuska const char *p_systemd_nofail = strtok_r(NULL, "\t", &toktmp) ?: "-";
22516038816SMartin Matuska const char *p_systemd_ignore = strtok_r(NULL, "\t", &toktmp) ?: "-";
22616038816SMartin Matuska /* END CSTYLED */
22716038816SMartin Matuska
22815f0b8c3SMartin Matuska size_t pool_len = strlen(dataset);
22915f0b8c3SMartin Matuska if ((toktmp = strchr(dataset, '/')) != NULL)
23015f0b8c3SMartin Matuska pool_len = toktmp - dataset;
23115f0b8c3SMartin Matuska const char *pool = *(tofree++) = strndup(dataset, pool_len);
23216038816SMartin Matuska
23316038816SMartin Matuska if (p_nbmand == NULL) {
23416038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: not enough tokens!\n",
23516038816SMartin Matuska getpid(), dataset);
236681ce946SMartin Matuska goto err;
23716038816SMartin Matuska }
23816038816SMartin Matuska
23916038816SMartin Matuska /* Minimal pre-requisites to mount a ZFS dataset */
24016038816SMartin Matuska const char *after = "zfs-import.target";
24116038816SMartin Matuska const char *wants = "zfs-import.target";
24216038816SMartin Matuska const char *bindsto = NULL;
24316038816SMartin Matuska char *wantedby = NULL;
24416038816SMartin Matuska char *requiredby = NULL;
24516038816SMartin Matuska bool noauto = false;
24616038816SMartin Matuska bool wantedby_append = true;
24716038816SMartin Matuska
24816038816SMartin Matuska /*
24916038816SMartin Matuska * zfs-import.target is not needed if the pool is already imported.
25016038816SMartin Matuska * This avoids a dependency loop on root-on-ZFS systems:
25116038816SMartin Matuska * systemd-random-seed.service After (via RequiresMountsFor)
25216038816SMartin Matuska * var-lib.mount After
25316038816SMartin Matuska * zfs-import.target After
25416038816SMartin Matuska * zfs-import-{cache,scan}.service After
25516038816SMartin Matuska * cryptsetup.service After
25616038816SMartin Matuska * systemd-random-seed.service
25716038816SMartin Matuska */
25816038816SMartin Matuska if (tfind(pool, &known_pools, STRCMP)) {
25916038816SMartin Matuska after = "";
26016038816SMartin Matuska wants = "";
26116038816SMartin Matuska }
26216038816SMartin Matuska
26316038816SMartin Matuska if (strcmp(p_systemd_after, "-") == 0)
26416038816SMartin Matuska p_systemd_after = NULL;
26516038816SMartin Matuska if (strcmp(p_systemd_before, "-") == 0)
26616038816SMartin Matuska p_systemd_before = NULL;
26716038816SMartin Matuska if (strcmp(p_systemd_requires, "-") == 0)
26816038816SMartin Matuska p_systemd_requires = NULL;
26916038816SMartin Matuska if (strcmp(p_systemd_requiresmountsfor, "-") == 0)
27016038816SMartin Matuska p_systemd_requiresmountsfor = NULL;
27116038816SMartin Matuska
27216038816SMartin Matuska
27316038816SMartin Matuska if (strcmp(p_encroot, "-") != 0) {
274681ce946SMartin Matuska char *keyloadunit = *(tofree++) =
27516038816SMartin Matuska systemd_escape(p_encroot, "zfs-load-key@", ".service");
276681ce946SMartin Matuska if (keyloadunit == NULL)
277681ce946SMartin Matuska goto err;
27816038816SMartin Matuska
27916038816SMartin Matuska if (strcmp(dataset, p_encroot) == 0) {
28016038816SMartin Matuska const char *keymountdep = NULL;
28116038816SMartin Matuska bool is_prompt = false;
282681ce946SMartin Matuska bool need_network = false;
28316038816SMartin Matuska
28416038816SMartin Matuska regmatch_t uri_matches[3];
28516038816SMartin Matuska if (regexec(&uri_regex, p_keyloc,
286681ce946SMartin Matuska nitems(uri_matches), uri_matches, 0) == 0) {
287681ce946SMartin Matuska p_keyloc[uri_matches[1].rm_eo] = '\0';
28816038816SMartin Matuska p_keyloc[uri_matches[2].rm_eo] = '\0';
289681ce946SMartin Matuska const char *scheme =
290681ce946SMartin Matuska &p_keyloc[uri_matches[1].rm_so];
29116038816SMartin Matuska const char *path =
29216038816SMartin Matuska &p_keyloc[uri_matches[2].rm_so];
29316038816SMartin Matuska
294681ce946SMartin Matuska if (strcmp(scheme, "https") == 0 ||
295681ce946SMartin Matuska strcmp(scheme, "http") == 0)
296681ce946SMartin Matuska need_network = true;
297681ce946SMartin Matuska else
29816038816SMartin Matuska keymountdep = path;
29916038816SMartin Matuska } else {
30016038816SMartin Matuska if (strcmp(p_keyloc, "prompt") != 0)
30116038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
30216038816SMartin Matuska "unknown non-URI keylocation=%s\n",
30316038816SMartin Matuska getpid(), dataset, p_keyloc);
30416038816SMartin Matuska
30516038816SMartin Matuska is_prompt = true;
30616038816SMartin Matuska }
30716038816SMartin Matuska
30816038816SMartin Matuska
30916038816SMartin Matuska /* Generate the key-load .service unit */
31016038816SMartin Matuska FILE *keyloadunit_f = fopenat(destdir_fd, keyloadunit,
31116038816SMartin Matuska O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w",
31216038816SMartin Matuska 0644);
31316038816SMartin Matuska if (!keyloadunit_f) {
31416038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
31516038816SMartin Matuska "couldn't open %s under %s: %s\n",
31616038816SMartin Matuska getpid(), dataset, keyloadunit, destdir,
31716038816SMartin Matuska strerror(errno));
318681ce946SMartin Matuska goto err;
31916038816SMartin Matuska }
32016038816SMartin Matuska
32116038816SMartin Matuska fprintf(keyloadunit_f,
32216038816SMartin Matuska OUTPUT_HEADER
32316038816SMartin Matuska "[Unit]\n"
32416038816SMartin Matuska "Description=Load ZFS key for %s\n"
32516038816SMartin Matuska "SourcePath=" FSLIST "/%s\n"
32616038816SMartin Matuska "Documentation=man:zfs-mount-generator(8)\n"
32716038816SMartin Matuska "DefaultDependencies=no\n"
32816038816SMartin Matuska "Wants=%s\n"
32916038816SMartin Matuska "After=%s\n",
33016038816SMartin Matuska dataset, cachefile, wants, after);
33116038816SMartin Matuska
332681ce946SMartin Matuska if (need_network)
333681ce946SMartin Matuska fprintf(keyloadunit_f,
334681ce946SMartin Matuska "Wants=network-online.target\n"
335681ce946SMartin Matuska "After=network-online.target\n");
336681ce946SMartin Matuska
33716038816SMartin Matuska if (p_systemd_requires)
33816038816SMartin Matuska fprintf(keyloadunit_f,
33916038816SMartin Matuska "Requires=%s\n", p_systemd_requires);
34016038816SMartin Matuska
34116038816SMartin Matuska if (p_systemd_requiresmountsfor)
34216038816SMartin Matuska fprintf(keyloadunit_f,
343681ce946SMartin Matuska "RequiresMountsFor=%s\n",
344681ce946SMartin Matuska p_systemd_requiresmountsfor);
34516038816SMartin Matuska if (keymountdep)
34616038816SMartin Matuska fprintf(keyloadunit_f,
347681ce946SMartin Matuska "RequiresMountsFor='%s'\n", keymountdep);
34816038816SMartin Matuska
34916038816SMartin Matuska /* BEGIN CSTYLED */
35016038816SMartin Matuska fprintf(keyloadunit_f,
35116038816SMartin Matuska "\n"
35216038816SMartin Matuska "[Service]\n"
35316038816SMartin Matuska "Type=oneshot\n"
35416038816SMartin Matuska "RemainAfterExit=yes\n"
35516038816SMartin Matuska "# This avoids a dependency loop involving systemd-journald.socket if this\n"
35616038816SMartin Matuska "# dataset is a parent of the root filesystem.\n"
35716038816SMartin Matuska "StandardOutput=null\n"
35816038816SMartin Matuska "StandardError=null\n"
35916038816SMartin Matuska "ExecStart=/bin/sh -euc '"
36016038816SMartin Matuska "[ \"$$(" ZFS " get -H -o value keystatus \"%s\")\" = \"unavailable\" ] || exit 0;",
36116038816SMartin Matuska dataset);
36216038816SMartin Matuska if (is_prompt)
36316038816SMartin Matuska fprintf(keyloadunit_f,
36416038816SMartin Matuska "for i in 1 2 3; do "
36516038816SMartin Matuska "systemd-ask-password --id=\"zfs:%s\" \"Enter passphrase for %s:\" |"
36616038816SMartin Matuska "" ZFS " load-key \"%s\" && exit 0;"
36716038816SMartin Matuska "done;"
36816038816SMartin Matuska "exit 1",
36916038816SMartin Matuska dataset, dataset, dataset);
37016038816SMartin Matuska else
37116038816SMartin Matuska fprintf(keyloadunit_f,
37216038816SMartin Matuska "exec " ZFS " load-key \"%s\"",
37316038816SMartin Matuska dataset);
37416038816SMartin Matuska
37516038816SMartin Matuska fprintf(keyloadunit_f,
37616038816SMartin Matuska "'\n"
37716038816SMartin Matuska "ExecStop=/bin/sh -euc '"
37816038816SMartin Matuska "[ \"$$(" ZFS " get -H -o value keystatus \"%s\")\" = \"available\" ] || exit 0;"
37916038816SMartin Matuska "exec " ZFS " unload-key \"%s\""
38016038816SMartin Matuska "'\n",
38116038816SMartin Matuska dataset, dataset);
38216038816SMartin Matuska /* END CSTYLED */
38316038816SMartin Matuska
38416038816SMartin Matuska (void) fclose(keyloadunit_f);
38516038816SMartin Matuska }
38616038816SMartin Matuska
38716038816SMartin Matuska /* Update dependencies for the mount file to want this */
38816038816SMartin Matuska bindsto = keyloadunit;
38916038816SMartin Matuska if (after[0] == '\0')
39016038816SMartin Matuska after = keyloadunit;
39116038816SMartin Matuska else if (asprintf(&toktmp, "%s %s", after, keyloadunit) != -1)
392681ce946SMartin Matuska after = *(tofree++) = toktmp;
393681ce946SMartin Matuska else {
394681ce946SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
395681ce946SMartin Matuska "out of memory to generate after=\"%s %s\"!\n",
396681ce946SMartin Matuska getpid(), dataset, after, keyloadunit);
397681ce946SMartin Matuska goto err;
398681ce946SMartin Matuska }
39916038816SMartin Matuska }
40016038816SMartin Matuska
40116038816SMartin Matuska
40216038816SMartin Matuska /* Skip generation of the mount unit if org.openzfs.systemd:ignore=on */
40316038816SMartin Matuska if (strcmp(p_systemd_ignore, "-") == 0 ||
40416038816SMartin Matuska strcmp(p_systemd_ignore, "off") == 0) {
40516038816SMartin Matuska /* ok */
40616038816SMartin Matuska } else if (strcmp(p_systemd_ignore, "on") == 0)
407681ce946SMartin Matuska goto end;
40816038816SMartin Matuska else {
40916038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
41016038816SMartin Matuska "invalid org.openzfs.systemd:ignore=%s\n",
41116038816SMartin Matuska getpid(), dataset, p_systemd_ignore);
412681ce946SMartin Matuska goto err;
41316038816SMartin Matuska }
41416038816SMartin Matuska
41516038816SMartin Matuska /* Check for canmount */
41616038816SMartin Matuska if (strcmp(p_canmount, "on") == 0) {
41716038816SMartin Matuska /* ok */
41816038816SMartin Matuska } else if (strcmp(p_canmount, "noauto") == 0)
41916038816SMartin Matuska noauto = true;
42016038816SMartin Matuska else if (strcmp(p_canmount, "off") == 0)
421681ce946SMartin Matuska goto end;
42216038816SMartin Matuska else {
42316038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid canmount=%s\n",
42416038816SMartin Matuska getpid(), dataset, p_canmount);
425681ce946SMartin Matuska goto err;
42616038816SMartin Matuska }
42716038816SMartin Matuska
42816038816SMartin Matuska /* Check for legacy and blank mountpoints */
42916038816SMartin Matuska if (strcmp(p_mountpoint, "legacy") == 0 ||
43016038816SMartin Matuska strcmp(p_mountpoint, "none") == 0)
431681ce946SMartin Matuska goto end;
43216038816SMartin Matuska else if (p_mountpoint[0] != '/') {
43316038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid mountpoint=%s\n",
43416038816SMartin Matuska getpid(), dataset, p_mountpoint);
435681ce946SMartin Matuska goto err;
43616038816SMartin Matuska }
43716038816SMartin Matuska
43816038816SMartin Matuska /* Escape the mountpoint per systemd policy */
43916038816SMartin Matuska simplify_path(p_mountpoint);
44016038816SMartin Matuska const char *mountfile = systemd_escape_path(p_mountpoint, "", ".mount");
44116038816SMartin Matuska if (mountfile == NULL) {
44216038816SMartin Matuska fprintf(stderr,
44316038816SMartin Matuska PROGNAME "[%d]: %s: abnormal simplified mountpoint: %s\n",
44416038816SMartin Matuska getpid(), dataset, p_mountpoint);
445681ce946SMartin Matuska goto err;
44616038816SMartin Matuska }
44716038816SMartin Matuska
44816038816SMartin Matuska
44916038816SMartin Matuska /*
45016038816SMartin Matuska * Parse options, cf. lib/libzfs/libzfs_mount.c:zfs_add_options
45116038816SMartin Matuska *
45216038816SMartin Matuska * The longest string achievable here is
45316038816SMartin Matuska * ",atime,strictatime,nodev,noexec,rw,nosuid,nomand".
45416038816SMartin Matuska */
45516038816SMartin Matuska char opts[64] = "";
45616038816SMartin Matuska
45716038816SMartin Matuska /* atime */
45816038816SMartin Matuska if (strcmp(p_atime, "on") == 0) {
45916038816SMartin Matuska /* relatime */
46016038816SMartin Matuska if (strcmp(p_relatime, "on") == 0)
46116038816SMartin Matuska strcat(opts, ",atime,relatime");
46216038816SMartin Matuska else if (strcmp(p_relatime, "off") == 0)
46316038816SMartin Matuska strcat(opts, ",atime,strictatime");
46416038816SMartin Matuska else
46516038816SMartin Matuska fprintf(stderr,
46616038816SMartin Matuska PROGNAME "[%d]: %s: invalid relatime=%s\n",
46716038816SMartin Matuska getpid(), dataset, p_relatime);
46816038816SMartin Matuska } else if (strcmp(p_atime, "off") == 0) {
46916038816SMartin Matuska strcat(opts, ",noatime");
47016038816SMartin Matuska } else
47116038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid atime=%s\n",
47216038816SMartin Matuska getpid(), dataset, p_atime);
47316038816SMartin Matuska
47416038816SMartin Matuska /* devices */
47516038816SMartin Matuska if (strcmp(p_devices, "on") == 0)
47616038816SMartin Matuska strcat(opts, ",dev");
47716038816SMartin Matuska else if (strcmp(p_devices, "off") == 0)
47816038816SMartin Matuska strcat(opts, ",nodev");
47916038816SMartin Matuska else
48016038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid devices=%s\n",
48116038816SMartin Matuska getpid(), dataset, p_devices);
48216038816SMartin Matuska
48316038816SMartin Matuska /* exec */
48416038816SMartin Matuska if (strcmp(p_exec, "on") == 0)
48516038816SMartin Matuska strcat(opts, ",exec");
48616038816SMartin Matuska else if (strcmp(p_exec, "off") == 0)
48716038816SMartin Matuska strcat(opts, ",noexec");
48816038816SMartin Matuska else
48916038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid exec=%s\n",
49016038816SMartin Matuska getpid(), dataset, p_exec);
49116038816SMartin Matuska
49216038816SMartin Matuska /* readonly */
49316038816SMartin Matuska if (strcmp(p_readonly, "on") == 0)
49416038816SMartin Matuska strcat(opts, ",ro");
49516038816SMartin Matuska else if (strcmp(p_readonly, "off") == 0)
49616038816SMartin Matuska strcat(opts, ",rw");
49716038816SMartin Matuska else
49816038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid readonly=%s\n",
49916038816SMartin Matuska getpid(), dataset, p_readonly);
50016038816SMartin Matuska
50116038816SMartin Matuska /* setuid */
50216038816SMartin Matuska if (strcmp(p_setuid, "on") == 0)
50316038816SMartin Matuska strcat(opts, ",suid");
50416038816SMartin Matuska else if (strcmp(p_setuid, "off") == 0)
50516038816SMartin Matuska strcat(opts, ",nosuid");
50616038816SMartin Matuska else
50716038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid setuid=%s\n",
50816038816SMartin Matuska getpid(), dataset, p_setuid);
50916038816SMartin Matuska
51016038816SMartin Matuska /* nbmand */
51116038816SMartin Matuska if (strcmp(p_nbmand, "on") == 0)
51216038816SMartin Matuska strcat(opts, ",mand");
51316038816SMartin Matuska else if (strcmp(p_nbmand, "off") == 0)
51416038816SMartin Matuska strcat(opts, ",nomand");
51516038816SMartin Matuska else
51616038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: invalid nbmand=%s\n",
51716038816SMartin Matuska getpid(), dataset, p_setuid);
51816038816SMartin Matuska
51916038816SMartin Matuska if (strcmp(p_systemd_wantedby, "-") != 0) {
52016038816SMartin Matuska noauto = true;
52116038816SMartin Matuska
52216038816SMartin Matuska if (strcmp(p_systemd_wantedby, "none") != 0)
52316038816SMartin Matuska wantedby = p_systemd_wantedby;
52416038816SMartin Matuska }
52516038816SMartin Matuska
52616038816SMartin Matuska if (strcmp(p_systemd_requiredby, "-") != 0) {
52716038816SMartin Matuska noauto = true;
52816038816SMartin Matuska
52916038816SMartin Matuska if (strcmp(p_systemd_requiredby, "none") != 0)
53016038816SMartin Matuska requiredby = p_systemd_requiredby;
53116038816SMartin Matuska }
53216038816SMartin Matuska
53316038816SMartin Matuska /*
53416038816SMartin Matuska * For datasets with canmount=on, a dependency is created for
53516038816SMartin Matuska * local-fs.target by default. To avoid regressions, this dependency
53616038816SMartin Matuska * is reduced to "wants" rather than "requires" when nofail!=off.
53716038816SMartin Matuska * **THIS MAY CHANGE**
53816038816SMartin Matuska * noauto=on disables this behavior completely.
53916038816SMartin Matuska */
54016038816SMartin Matuska if (!noauto) {
54116038816SMartin Matuska if (strcmp(p_systemd_nofail, "off") == 0)
54216038816SMartin Matuska requiredby = strdupa("local-fs.target");
54316038816SMartin Matuska else {
54416038816SMartin Matuska wantedby = strdupa("local-fs.target");
54516038816SMartin Matuska wantedby_append = strcmp(p_systemd_nofail, "on") != 0;
54616038816SMartin Matuska }
54716038816SMartin Matuska }
54816038816SMartin Matuska
54916038816SMartin Matuska /*
55016038816SMartin Matuska * Handle existing files:
55116038816SMartin Matuska * 1. We never overwrite existing files, although we may delete
55216038816SMartin Matuska * files if we're sure they were created by us. (see 5.)
55316038816SMartin Matuska * 2. We handle files differently based on canmount.
55416038816SMartin Matuska * Units with canmount=on always have precedence over noauto.
555681ce946SMartin Matuska * This is enforced by processing these units before all others.
55616038816SMartin Matuska * It is important to use p_canmount and not noauto here,
55716038816SMartin Matuska * since we categorise by canmount while other properties,
55816038816SMartin Matuska * e.g. org.openzfs.systemd:wanted-by, also modify noauto.
55916038816SMartin Matuska * 3. If no unit file exists for a noauto dataset, we create one.
56016038816SMartin Matuska * Additionally, we use noauto_files to track the unit file names
56116038816SMartin Matuska * (which are the systemd-escaped mountpoints) of all (exclusively)
56216038816SMartin Matuska * noauto datasets that had a file created.
563681ce946SMartin Matuska * 4. If the file to be created is found in the tracking tree,
56416038816SMartin Matuska * we do NOT create it.
56516038816SMartin Matuska * 5. If a file exists for a noauto dataset,
56616038816SMartin Matuska * we check whether the file name is in the array.
56716038816SMartin Matuska * If it is, we have multiple noauto datasets for the same
56816038816SMartin Matuska * mountpoint. In such cases, we remove the file for safety.
56916038816SMartin Matuska * We leave the file name in the tracking array to avoid
57016038816SMartin Matuska * further noauto datasets creating a file for this path again.
57116038816SMartin Matuska */
57216038816SMartin Matuska
57316038816SMartin Matuska struct stat stbuf;
57416038816SMartin Matuska bool already_exists = fstatat(destdir_fd, mountfile, &stbuf, 0) == 0;
575681ce946SMartin Matuska bool is_known = tfind(mountfile, &noauto_files, STRCMP) != NULL;
57616038816SMartin Matuska
577681ce946SMartin Matuska *(tofree++) = (void *)mountfile;
57816038816SMartin Matuska if (already_exists) {
57916038816SMartin Matuska if (is_known) {
580681ce946SMartin Matuska /* If it's in noauto_files, we must be noauto too */
58116038816SMartin Matuska
58216038816SMartin Matuska /* See 5 */
58316038816SMartin Matuska errno = 0;
58416038816SMartin Matuska (void) unlinkat(destdir_fd, mountfile, 0);
58516038816SMartin Matuska
58616038816SMartin Matuska /* See 2 */
58716038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
58816038816SMartin Matuska "removing duplicate noauto unit %s%s%s\n",
58916038816SMartin Matuska getpid(), dataset, mountfile,
59016038816SMartin Matuska errno ? "" : " failed: ",
59116038816SMartin Matuska errno ? "" : strerror(errno));
59216038816SMartin Matuska } else {
59316038816SMartin Matuska /* Don't log for canmount=noauto */
59416038816SMartin Matuska if (strcmp(p_canmount, "on") == 0)
59516038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
59616038816SMartin Matuska "%s already exists. Skipping.\n",
59716038816SMartin Matuska getpid(), dataset, mountfile);
59816038816SMartin Matuska }
59916038816SMartin Matuska
60016038816SMartin Matuska /* File exists: skip current dataset */
601681ce946SMartin Matuska goto end;
60216038816SMartin Matuska } else {
60316038816SMartin Matuska if (is_known) {
60416038816SMartin Matuska /* See 4 */
605681ce946SMartin Matuska goto end;
60616038816SMartin Matuska } else if (strcmp(p_canmount, "noauto") == 0) {
607681ce946SMartin Matuska if (tsearch(mountfile, &noauto_files, STRCMP) == NULL)
60816038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
609681ce946SMartin Matuska "out of memory for noauto datasets! "
610681ce946SMartin Matuska "Not tracking %s.\n",
611681ce946SMartin Matuska getpid(), dataset, mountfile);
612681ce946SMartin Matuska else
613681ce946SMartin Matuska /* mountfile escaped to noauto_files */
614681ce946SMartin Matuska *(--tofree) = NULL;
61516038816SMartin Matuska }
61616038816SMartin Matuska }
61716038816SMartin Matuska
61816038816SMartin Matuska
61916038816SMartin Matuska FILE *mountfile_f = fopenat(destdir_fd, mountfile,
62016038816SMartin Matuska O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, "w", 0644);
62116038816SMartin Matuska if (!mountfile_f) {
62216038816SMartin Matuska fprintf(stderr,
62316038816SMartin Matuska PROGNAME "[%d]: %s: couldn't open %s under %s: %s\n",
62416038816SMartin Matuska getpid(), dataset, mountfile, destdir, strerror(errno));
625681ce946SMartin Matuska goto err;
62616038816SMartin Matuska }
62716038816SMartin Matuska
62816038816SMartin Matuska fprintf(mountfile_f,
62916038816SMartin Matuska OUTPUT_HEADER
63016038816SMartin Matuska "[Unit]\n"
63116038816SMartin Matuska "SourcePath=" FSLIST "/%s\n"
63216038816SMartin Matuska "Documentation=man:zfs-mount-generator(8)\n"
63316038816SMartin Matuska "\n"
63416038816SMartin Matuska "Before=",
63516038816SMartin Matuska cachefile);
63616038816SMartin Matuska
63716038816SMartin Matuska if (p_systemd_before)
63816038816SMartin Matuska fprintf(mountfile_f, "%s ", p_systemd_before);
63916038816SMartin Matuska fprintf(mountfile_f, "zfs-mount.service"); /* Ensures we don't race */
64016038816SMartin Matuska if (requiredby)
64116038816SMartin Matuska fprintf(mountfile_f, " %s", requiredby);
64216038816SMartin Matuska if (wantedby && wantedby_append)
64316038816SMartin Matuska fprintf(mountfile_f, " %s", wantedby);
64416038816SMartin Matuska
64516038816SMartin Matuska fprintf(mountfile_f,
64616038816SMartin Matuska "\n"
64716038816SMartin Matuska "After=");
64816038816SMartin Matuska if (p_systemd_after)
64916038816SMartin Matuska fprintf(mountfile_f, "%s ", p_systemd_after);
65016038816SMartin Matuska fprintf(mountfile_f, "%s\n", after);
65116038816SMartin Matuska
65216038816SMartin Matuska fprintf(mountfile_f, "Wants=%s\n", wants);
65316038816SMartin Matuska
65416038816SMartin Matuska if (bindsto)
65516038816SMartin Matuska fprintf(mountfile_f, "BindsTo=%s\n", bindsto);
65616038816SMartin Matuska if (p_systemd_requires)
65716038816SMartin Matuska fprintf(mountfile_f, "Requires=%s\n", p_systemd_requires);
65816038816SMartin Matuska if (p_systemd_requiresmountsfor)
65916038816SMartin Matuska fprintf(mountfile_f,
66016038816SMartin Matuska "RequiresMountsFor=%s\n", p_systemd_requiresmountsfor);
66116038816SMartin Matuska
66216038816SMartin Matuska fprintf(mountfile_f,
66316038816SMartin Matuska "\n"
66416038816SMartin Matuska "[Mount]\n"
66516038816SMartin Matuska "Where=%s\n"
66616038816SMartin Matuska "What=%s\n"
66716038816SMartin Matuska "Type=zfs\n"
66816038816SMartin Matuska "Options=defaults%s,zfsutil\n",
66916038816SMartin Matuska p_mountpoint, dataset, opts);
67016038816SMartin Matuska
67116038816SMartin Matuska (void) fclose(mountfile_f);
67216038816SMartin Matuska
67316038816SMartin Matuska if (!requiredby && !wantedby)
674681ce946SMartin Matuska goto end;
67516038816SMartin Matuska
67616038816SMartin Matuska /* Finally, create the appropriate dependencies */
67716038816SMartin Matuska char *linktgt;
678681ce946SMartin Matuska if (asprintf(&linktgt, "../%s", mountfile) == -1) {
679681ce946SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
680681ce946SMartin Matuska "out of memory for dependents of %s!\n",
681681ce946SMartin Matuska getpid(), dataset, mountfile);
682681ce946SMartin Matuska goto err;
683681ce946SMartin Matuska }
684681ce946SMartin Matuska *(tofree++) = linktgt;
68516038816SMartin Matuska
686a0b956f5SMartin Matuska struct dep {
687a0b956f5SMartin Matuska const char *type;
688a0b956f5SMartin Matuska char *list;
689a0b956f5SMartin Matuska } deps[] = {
69016038816SMartin Matuska {"wants", wantedby},
69116038816SMartin Matuska {"requires", requiredby},
69216038816SMartin Matuska {}
69316038816SMartin Matuska };
694a0b956f5SMartin Matuska for (struct dep *dep = deps; dep->type; ++dep) {
695a0b956f5SMartin Matuska if (!dep->list)
69616038816SMartin Matuska continue;
69716038816SMartin Matuska
698a0b956f5SMartin Matuska for (char *reqby = strtok_r(dep->list, " ", &toktmp);
69916038816SMartin Matuska reqby;
70016038816SMartin Matuska reqby = strtok_r(NULL, " ", &toktmp)) {
70116038816SMartin Matuska char *depdir;
702681ce946SMartin Matuska if (asprintf(
703a0b956f5SMartin Matuska &depdir, "%s.%s", reqby, dep->type) == -1) {
704681ce946SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
705681ce946SMartin Matuska "out of memory for dependent dir name "
706681ce946SMartin Matuska "\"%s.%s\"!\n",
707a0b956f5SMartin Matuska getpid(), dataset, reqby, dep->type);
708681ce946SMartin Matuska continue;
709681ce946SMartin Matuska }
71016038816SMartin Matuska
71116038816SMartin Matuska (void) mkdirat(destdir_fd, depdir, 0755);
71216038816SMartin Matuska int depdir_fd = openat(destdir_fd, depdir,
71316038816SMartin Matuska O_PATH | O_DIRECTORY | O_CLOEXEC);
71416038816SMartin Matuska if (depdir_fd < 0) {
71516038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
71616038816SMartin Matuska "couldn't open %s under %s: %s\n",
71716038816SMartin Matuska getpid(), dataset, depdir, destdir,
71816038816SMartin Matuska strerror(errno));
71916038816SMartin Matuska free(depdir);
72016038816SMartin Matuska continue;
72116038816SMartin Matuska }
72216038816SMartin Matuska
72316038816SMartin Matuska if (symlinkat(linktgt, depdir_fd, mountfile) == -1)
72416038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: %s: "
72516038816SMartin Matuska "couldn't symlink at "
72616038816SMartin Matuska "%s under %s under %s: %s\n",
72716038816SMartin Matuska getpid(), dataset, mountfile,
72816038816SMartin Matuska depdir, destdir, strerror(errno));
72916038816SMartin Matuska
73016038816SMartin Matuska (void) close(depdir_fd);
73116038816SMartin Matuska free(depdir);
73216038816SMartin Matuska }
73316038816SMartin Matuska }
73416038816SMartin Matuska
735681ce946SMartin Matuska end:
736681ce946SMartin Matuska if (tofree >= tofree_all + nitems(tofree_all)) {
737681ce946SMartin Matuska /*
738681ce946SMartin Matuska * This won't happen as-is:
73915f0b8c3SMartin Matuska * we've got 8 slots and allocate 5 things at most.
740681ce946SMartin Matuska */
741681ce946SMartin Matuska fprintf(stderr,
742681ce946SMartin Matuska PROGNAME "[%d]: %s: need to free %zu > %zu!\n",
743681ce946SMartin Matuska getpid(), dataset, tofree - tofree_all, nitems(tofree_all));
744681ce946SMartin Matuska ret = tofree - tofree_all;
745681ce946SMartin Matuska }
746681ce946SMartin Matuska
747681ce946SMartin Matuska while (tofree-- != tofree_all)
748681ce946SMartin Matuska free(*tofree);
749681ce946SMartin Matuska return (ret);
750681ce946SMartin Matuska err:
751681ce946SMartin Matuska ret = 1;
752681ce946SMartin Matuska goto end;
75316038816SMartin Matuska }
75416038816SMartin Matuska
75516038816SMartin Matuska
75616038816SMartin Matuska static int
pool_enumerator(zpool_handle_t * pool,void * data)75716038816SMartin Matuska pool_enumerator(zpool_handle_t *pool, void *data __attribute__((unused)))
75816038816SMartin Matuska {
75916038816SMartin Matuska int ret = 0;
76016038816SMartin Matuska
76116038816SMartin Matuska /*
76216038816SMartin Matuska * Pools are guaranteed-unique by the kernel,
76316038816SMartin Matuska * no risk of leaking dupes here
76416038816SMartin Matuska */
76516038816SMartin Matuska char *name = strdup(zpool_get_name(pool));
76616038816SMartin Matuska if (!name || !tsearch(name, &known_pools, STRCMP)) {
76716038816SMartin Matuska free(name);
76816038816SMartin Matuska ret = ENOMEM;
76916038816SMartin Matuska }
77016038816SMartin Matuska
77116038816SMartin Matuska zpool_close(pool);
77216038816SMartin Matuska return (ret);
77316038816SMartin Matuska }
77416038816SMartin Matuska
77516038816SMartin Matuska int
main(int argc,char ** argv)77616038816SMartin Matuska main(int argc, char **argv)
77716038816SMartin Matuska {
77816038816SMartin Matuska struct timespec time_init = {};
77916038816SMartin Matuska clock_gettime(CLOCK_MONOTONIC_RAW, &time_init);
78016038816SMartin Matuska
78116038816SMartin Matuska {
78216038816SMartin Matuska int kmfd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
78316038816SMartin Matuska if (kmfd >= 0) {
78416038816SMartin Matuska (void) dup2(kmfd, STDERR_FILENO);
78516038816SMartin Matuska (void) close(kmfd);
786681ce946SMartin Matuska
787681ce946SMartin Matuska setlinebuf(stderr);
78816038816SMartin Matuska }
78916038816SMartin Matuska }
79016038816SMartin Matuska
79116038816SMartin Matuska switch (argc) {
79216038816SMartin Matuska case 1:
79316038816SMartin Matuska /* Use default */
79416038816SMartin Matuska break;
79516038816SMartin Matuska case 2:
79616038816SMartin Matuska case 4:
79716038816SMartin Matuska destdir = argv[1];
79816038816SMartin Matuska break;
79916038816SMartin Matuska default:
80016038816SMartin Matuska fprintf(stderr,
80116038816SMartin Matuska PROGNAME "[%d]: wrong argument count: %d\n",
80216038816SMartin Matuska getpid(), argc - 1);
80316038816SMartin Matuska _exit(1);
80416038816SMartin Matuska }
80516038816SMartin Matuska
80616038816SMartin Matuska {
80716038816SMartin Matuska destdir_fd = open(destdir, O_PATH | O_DIRECTORY | O_CLOEXEC);
80816038816SMartin Matuska if (destdir_fd < 0) {
80916038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
81016038816SMartin Matuska "can't open destination directory %s: %s\n",
81116038816SMartin Matuska getpid(), destdir, strerror(errno));
81216038816SMartin Matuska _exit(1);
81316038816SMartin Matuska }
81416038816SMartin Matuska }
81516038816SMartin Matuska
81616038816SMartin Matuska DIR *fslist_dir = opendir(FSLIST);
81716038816SMartin Matuska if (!fslist_dir) {
81816038816SMartin Matuska if (errno != ENOENT)
81916038816SMartin Matuska fprintf(stderr,
82016038816SMartin Matuska PROGNAME "[%d]: couldn't open " FSLIST ": %s\n",
82116038816SMartin Matuska getpid(), strerror(errno));
82216038816SMartin Matuska _exit(0);
82316038816SMartin Matuska }
82416038816SMartin Matuska
82516038816SMartin Matuska {
82616038816SMartin Matuska libzfs_handle_t *libzfs = libzfs_init();
82716038816SMartin Matuska if (libzfs) {
82816038816SMartin Matuska if (zpool_iter(libzfs, pool_enumerator, NULL) != 0)
82916038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
83016038816SMartin Matuska "error listing pools, ignoring\n",
83116038816SMartin Matuska getpid());
83216038816SMartin Matuska libzfs_fini(libzfs);
83316038816SMartin Matuska } else
83416038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
83516038816SMartin Matuska "couldn't start libzfs, ignoring\n",
83616038816SMartin Matuska getpid());
83716038816SMartin Matuska }
83816038816SMartin Matuska
83916038816SMartin Matuska {
84016038816SMartin Matuska int regerr = regcomp(&uri_regex, URI_REGEX_S, 0);
84116038816SMartin Matuska if (regerr != 0) {
84216038816SMartin Matuska fprintf(stderr,
84316038816SMartin Matuska PROGNAME "[%d]: invalid regex: %d\n",
84416038816SMartin Matuska getpid(), regerr);
84516038816SMartin Matuska _exit(1);
84616038816SMartin Matuska }
84716038816SMartin Matuska }
84816038816SMartin Matuska
849681ce946SMartin Matuska bool debug = false;
85016038816SMartin Matuska char *line = NULL;
85116038816SMartin Matuska size_t linelen = 0;
85216038816SMartin Matuska {
85316038816SMartin Matuska const char *dbgenv = getenv("ZFS_DEBUG");
85416038816SMartin Matuska if (dbgenv)
85516038816SMartin Matuska debug = atoi(dbgenv);
85616038816SMartin Matuska else {
85716038816SMartin Matuska FILE *cmdline = fopen("/proc/cmdline", "re");
85816038816SMartin Matuska if (cmdline != NULL) {
85916038816SMartin Matuska if (getline(&line, &linelen, cmdline) >= 0)
860681ce946SMartin Matuska debug = strstr(line, "debug");
86116038816SMartin Matuska (void) fclose(cmdline);
86216038816SMartin Matuska }
86316038816SMartin Matuska }
86416038816SMartin Matuska
86516038816SMartin Matuska if (debug && !isatty(STDOUT_FILENO))
86616038816SMartin Matuska dup2(STDERR_FILENO, STDOUT_FILENO);
86716038816SMartin Matuska }
86816038816SMartin Matuska
869681ce946SMartin Matuska struct timespec time_start = {};
87016038816SMartin Matuska if (debug)
87116038816SMartin Matuska clock_gettime(CLOCK_MONOTONIC_RAW, &time_start);
87216038816SMartin Matuska
873681ce946SMartin Matuska struct line {
874681ce946SMartin Matuska char *line;
875681ce946SMartin Matuska const char *fname;
876681ce946SMartin Matuska struct line *next;
877681ce946SMartin Matuska } *lines_canmount_not_on = NULL;
878681ce946SMartin Matuska
879681ce946SMartin Matuska int ret = 0;
88016038816SMartin Matuska struct dirent *cachent;
88116038816SMartin Matuska while ((cachent = readdir(fslist_dir)) != NULL) {
88216038816SMartin Matuska if (strcmp(cachent->d_name, ".") == 0 ||
88316038816SMartin Matuska strcmp(cachent->d_name, "..") == 0)
88416038816SMartin Matuska continue;
88516038816SMartin Matuska
88616038816SMartin Matuska FILE *cachefile = fopenat(dirfd(fslist_dir), cachent->d_name,
88716038816SMartin Matuska O_RDONLY | O_CLOEXEC, "r", 0);
88816038816SMartin Matuska if (!cachefile) {
88916038816SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
89016038816SMartin Matuska "couldn't open %s under " FSLIST ": %s\n",
89116038816SMartin Matuska getpid(), cachent->d_name, strerror(errno));
89216038816SMartin Matuska continue;
89316038816SMartin Matuska }
89416038816SMartin Matuska
895681ce946SMartin Matuska const char *filename = FREE_STATICS ? "(elided)" : NULL;
896681ce946SMartin Matuska
897681ce946SMartin Matuska ssize_t read;
89816038816SMartin Matuska while ((read = getline(&line, &linelen, cachefile)) >= 0) {
89916038816SMartin Matuska line[read - 1] = '\0'; /* newline */
90016038816SMartin Matuska
901681ce946SMartin Matuska char *canmount = line;
902681ce946SMartin Matuska canmount += strcspn(canmount, "\t");
903681ce946SMartin Matuska canmount += strspn(canmount, "\t");
904681ce946SMartin Matuska canmount += strcspn(canmount, "\t");
905681ce946SMartin Matuska canmount += strspn(canmount, "\t");
906681ce946SMartin Matuska bool canmount_on = strncmp(canmount, "on", 2) == 0;
90716038816SMartin Matuska
908681ce946SMartin Matuska if (canmount_on)
909681ce946SMartin Matuska ret |= line_worker(line, cachent->d_name);
910681ce946SMartin Matuska else {
911681ce946SMartin Matuska if (filename == NULL)
912681ce946SMartin Matuska filename =
913681ce946SMartin Matuska strdup(cachent->d_name) ?: "(?)";
91416038816SMartin Matuska
915681ce946SMartin Matuska struct line *l = calloc(1, sizeof (*l));
916681ce946SMartin Matuska char *nl = strdup(line);
917681ce946SMartin Matuska if (l == NULL || nl == NULL) {
918681ce946SMartin Matuska fprintf(stderr, PROGNAME "[%d]: "
919681ce946SMartin Matuska "out of memory for \"%s\" in %s\n",
920681ce946SMartin Matuska getpid(), line, cachent->d_name);
921681ce946SMartin Matuska free(l);
922681ce946SMartin Matuska free(nl);
92316038816SMartin Matuska continue;
92416038816SMartin Matuska }
925681ce946SMartin Matuska l->line = nl;
926681ce946SMartin Matuska l->fname = filename;
927681ce946SMartin Matuska l->next = lines_canmount_not_on;
928681ce946SMartin Matuska lines_canmount_not_on = l;
92916038816SMartin Matuska }
93016038816SMartin Matuska }
93116038816SMartin Matuska
932681ce946SMartin Matuska fclose(cachefile);
93316038816SMartin Matuska }
93416038816SMartin Matuska free(line);
93516038816SMartin Matuska
936681ce946SMartin Matuska while (lines_canmount_not_on) {
937681ce946SMartin Matuska struct line *l = lines_canmount_not_on;
938681ce946SMartin Matuska lines_canmount_not_on = l->next;
939681ce946SMartin Matuska
940681ce946SMartin Matuska ret |= line_worker(l->line, l->fname);
941681ce946SMartin Matuska if (FREE_STATICS) {
942681ce946SMartin Matuska free(l->line);
943681ce946SMartin Matuska free(l);
94416038816SMartin Matuska }
94516038816SMartin Matuska }
94616038816SMartin Matuska
94716038816SMartin Matuska if (debug) {
94816038816SMartin Matuska struct timespec time_end = {};
94916038816SMartin Matuska clock_gettime(CLOCK_MONOTONIC_RAW, &time_end);
95016038816SMartin Matuska
951681ce946SMartin Matuska struct rusage usage;
95216038816SMartin Matuska getrusage(RUSAGE_SELF, &usage);
95316038816SMartin Matuska printf(
95416038816SMartin Matuska "\n"
955681ce946SMartin Matuska PROGNAME ": "
95616038816SMartin Matuska "user=%llu.%06us, system=%llu.%06us, maxrss=%ldB\n",
95716038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec,
95816038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec,
95916038816SMartin Matuska (unsigned long long) usage.ru_stime.tv_sec,
96016038816SMartin Matuska (unsigned int) usage.ru_stime.tv_usec,
96116038816SMartin Matuska usage.ru_maxrss * 1024);
96216038816SMartin Matuska
96316038816SMartin Matuska if (time_start.tv_nsec > time_end.tv_nsec) {
96416038816SMartin Matuska time_end.tv_nsec =
96516038816SMartin Matuska 1000000000 + time_end.tv_nsec - time_start.tv_nsec;
96616038816SMartin Matuska time_end.tv_sec -= 1;
96716038816SMartin Matuska } else
96816038816SMartin Matuska time_end.tv_nsec -= time_start.tv_nsec;
96916038816SMartin Matuska time_end.tv_sec -= time_start.tv_sec;
97016038816SMartin Matuska
97116038816SMartin Matuska if (time_init.tv_nsec > time_start.tv_nsec) {
97216038816SMartin Matuska time_start.tv_nsec =
97316038816SMartin Matuska 1000000000 + time_start.tv_nsec - time_init.tv_nsec;
97416038816SMartin Matuska time_start.tv_sec -= 1;
97516038816SMartin Matuska } else
97616038816SMartin Matuska time_start.tv_nsec -= time_init.tv_nsec;
97716038816SMartin Matuska time_start.tv_sec -= time_init.tv_sec;
97816038816SMartin Matuska
97916038816SMartin Matuska time_init.tv_nsec = time_start.tv_nsec + time_end.tv_nsec;
98016038816SMartin Matuska time_init.tv_sec =
98116038816SMartin Matuska time_start.tv_sec + time_end.tv_sec +
98216038816SMartin Matuska time_init.tv_nsec / 1000000000;
98316038816SMartin Matuska time_init.tv_nsec %= 1000000000;
98416038816SMartin Matuska
985681ce946SMartin Matuska printf(PROGNAME ": "
98616038816SMartin Matuska "total=%llu.%09llus = "
98716038816SMartin Matuska "init=%llu.%09llus + real=%llu.%09llus\n",
98816038816SMartin Matuska (unsigned long long) time_init.tv_sec,
98916038816SMartin Matuska (unsigned long long) time_init.tv_nsec,
99016038816SMartin Matuska (unsigned long long) time_start.tv_sec,
99116038816SMartin Matuska (unsigned long long) time_start.tv_nsec,
99216038816SMartin Matuska (unsigned long long) time_end.tv_sec,
99316038816SMartin Matuska (unsigned long long) time_end.tv_nsec);
994681ce946SMartin Matuska
995681ce946SMartin Matuska fflush(stdout);
99616038816SMartin Matuska }
99716038816SMartin Matuska
998681ce946SMartin Matuska if (FREE_STATICS) {
999681ce946SMartin Matuska closedir(fslist_dir);
1000681ce946SMartin Matuska tdestroy(noauto_files, free);
1001681ce946SMartin Matuska tdestroy(known_pools, free);
1002681ce946SMartin Matuska regfree(&uri_regex);
1003681ce946SMartin Matuska }
100416038816SMartin Matuska _exit(ret);
100516038816SMartin Matuska }
1006