diff --git a/plugins/bootmisc.c b/plugins/bootmisc.c index e0879fb2..1084e115 100644 --- a/plugins/bootmisc.c +++ b/plugins/bootmisc.c @@ -38,7 +38,6 @@ #include "finit.h" #include "helpers.h" #include "plugin.h" -#include "tmpfiles.h" #include "util.h" #include "utmp-api.h" @@ -163,7 +162,7 @@ static void setup(void *arg) kernel_links(); /* Create all system tmpfiles.d(5) */ - tmpfilesd(); + run_interactive(_PATH_TMPFILES " --create", "Creating required directories and files"); /* Set BOOT_TIME UTMP entry */ utmp_set_boot(); diff --git a/src/Makefile.am b/src/Makefile.am index ff3aca05..7f911c9a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,7 @@ AM_LDFLAGS = -export-dynamic endif sbin_PROGRAMS = finit initctl -pkglibexec_PROGRAMS = getty logit runparts +pkglibexec_PROGRAMS = getty logit runparts tmpfiles if SULOGIN pkglibexec_PROGRAMS += sulogin endif @@ -43,6 +43,13 @@ sulogin_SOURCES = sulogin.c sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99 sulogin_LDADD = -lcrypt +tmpfiles_SOURCES = tmpfiles.c helpers.c util.c exec.c log.c \ + svc.c sig.c sm.c api.c mount.c service.c schedule.c cond.c cond-w.c pid.c plugin.c \ + utmp-api.c client.c cgroup.c mdadm.c tty.c iwatch.c conf.c devmon.c logrotate.c +tmpfiles_CFLAGS = -W -Wall -Wextra -std=gnu99 -D__FINIT__ +tmpfiles_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) +tmpfiles_LDADD = $(lite_LIBS) $(uev_LIBS) + logit_SOURCES = logit.c logrotate.c logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 logit_CFLAGS += $(lite_CFLAGS) @@ -66,7 +73,6 @@ finit_SOURCES = api.c cgroup.c cgroup.h \ sig.c sig.h \ sm.c sm.h \ svc.c svc.h \ - tmpfiles.c tmpfiles.h \ tty.c tty.h \ util.c util.h \ utmp-api.c utmp-api.h diff --git a/src/finit.h b/src/finit.h index b536f887..04ca5597 100644 --- a/src/finit.h +++ b/src/finit.h @@ -59,6 +59,7 @@ #define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts" #define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin" #define _PATH_GETTY FINIT_EXECPATH_ "/getty" +#define _PATH_TMPFILES FINIT_EXECPATH_ "/tmpfiles" #define CMD_SIZE 1024 #define LINE_SIZE 1024 diff --git a/src/helpers.h b/src/helpers.h index 247a1e1b..25758da0 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -26,6 +26,7 @@ #define FINIT_HELPERS_H_ #include +#include #include #include #include @@ -206,6 +207,22 @@ static inline char *fgetval(const char *line, const char *key, char *sep) return realloc(copy, len); } +static inline int is_dir_empty(const char *path) +{ + struct dirent **namelist; + int num; + + num = scandir(path, &namelist, NULL, NULL); + if (num < 0) + return 0; + + for (int i = 0; i < num; i++) + free(namelist[i]); + free(namelist); + + return num >= 3; +} + #endif /* FINIT_HELPERS_H_ */ /** diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 671d6838..8bd0d34c 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -23,12 +23,51 @@ #include "config.h" /* Generated by configure script */ +#include +#include +#include +#include +#include +#include #include -#include "finit.h" + +#include +#include +#ifdef _LIBITE_LITE +# include +#else +# include +#endif + +#include "svc.h" #include "helpers.h" -#include "tmpfiles.h" +#include "log.h" #include "util.h" +int debug; +int runlevel; +char *osheading; +svc_t *wdog; +uev_ctx_t *ctx; +int syncsec; +int rescue; +char *network; +int cfglevel; +char *finit_rcsd; +char *sdown; +char *hostname; +int bootstrap; +int single; +int readiness; +char *finit_conf; +char *fstab; +int prevlevel; +int cmdlevel; +int kerndebug; + +int c_flag = 0; +int r_flag = 0; + static int glob_do(const char *path, int (*cb)(const char *)) { int rc = 0; @@ -279,182 +318,214 @@ static void tmpfiles(char *line) strc = stat(path, &st); - switch (type[0]) { - case 'b': - rc = parse_mm(arg, &major, &minor); - if (rc) + // file and directory removal logic + if (r_flag) { + switch (type[0]) { + case 'b': + case 'c': + case 'C': + case 'd': break; - if (!strc) { - if (type[1] != '+') - break; - erase(path); - } - mkparent(path, 0755); - rc = blkdev(path, mode ?: 0644, major, minor); - break; - case 'c': - rc = parse_mm(arg, &major, &minor); - if (rc) - break; - if (!strc) { - if (type[1] != '+') - break; - erase(path); - } - mkparent(path, 0755); - rc = chardev(path, mode ?: 0644, major, minor); - break; - case 'C': - if (!arg) { - paste(buf, sizeof(buf), "/usr/share/factory", path); - arg = buf; + case 'D': + if (fisdir(path)) { + nftw(path, do_delete, 20, FTW_DEPTH | FTW_PHYS); + } + break; + case 'e': + case 'f': + case 'F': + case 'l': /* Finit extension, like 'L' but only if target exists */ + case 'L': + case 'p': + break; + case 'r': + rc = glob_do(path, erase); + if (rc && errno == ENOENT) + rc = 0; + break; + case 'R': + rc = glob_do(path, rmrf); + break; + case 'w': + break; + case 'X': + case 'x': + dbg("Unsupported x/X command, ignoring %s, no support for clean at runtime.", path); + break; + case 'Z': + case 'z': + break; + default: + errx(1, "Unsupported tmpfiles command '%s'", type); + return; } - if (!strc) { - struct dirent **namelist; - int num; + } - if (!S_ISDIR(st.st_mode)) + // file & directory creation logic + if (c_flag) { + switch (type[0]) { + case 'b': + rc = parse_mm(arg, &major, &minor); + if (rc) break; - num = scandir(path, &namelist, NULL, NULL); - free(namelist); - if (num >= 3) - break; /* not empty */ - } - if (fisdir(arg) && !fisslashdir(arg)) { - size_t len = strlen(arg) + 2; - - dst = malloc(len); - if (!dst) + if (!strc) { + if (type[1] != '+') + break; + erase(path); + } + mkparent(path, 0755); + rc = blkdev(path, mode ?: 0644, major, minor); + break; + case 'c': + rc = parse_mm(arg, &major, &minor); + if (rc) break; - snprintf(dst, len, "%s/", arg); - arg = dst; - } - mkparent(path, 0755); - rc = rsync(arg, path, LITE_FOPT_KEEP_MTIME, NULL); - if (rc && errno == ENOENT) - rc = 0; - break; - case 'd': - case 'D': - mkparent(path, 0755); - rc = mksubsys(path, mode ?: 0755, user, group); - break; - case 'e': - if (glob(path, GLOB_NOESCAPE, NULL, &gl)) - break; - - for (size_t i = 0; i < gl.gl_pathc; i++) - rc += mksubsys(gl.gl_pathv[i], mode ?: 0755, user, group); - break; - case 'f': - case 'F': - mkparent(path, 0755); - if (type[1] == '+' || type[0] == 'F') { - /* f+/F will create or truncate the file */ + if (!strc) { + if (type[1] != '+') + break; + erase(path); + } + mkparent(path, 0755); + rc = chardev(path, mode ?: 0644, major, minor); + break; + case 'C': if (!arg) { - rc = create(path, mode ?: 0644, user, group); + paste(buf, sizeof(buf), "/usr/share/factory", path); + arg = buf; + } + if (fisdir(path) && !is_dir_empty(path)) break; + if (fisdir(arg) && !fisslashdir(arg)) { + size_t len = strlen(arg) + 2; + + dst = malloc(len); + if (!dst) + break; + snprintf(dst, len, "%s/", arg); + arg = dst; } - fp = fopen(path, "w+"); - } else { - /* f will create the file if it doesn't exist */ - if (strc) - fp = fopen(path, "w"); - } - - if (fp) { - write_arg(fp, arg); - rc = fclose(fp); - } - break; - case 'l': /* Finit extension, like 'L' but only if target exists */ - if (!arg) { - paste(buf, sizeof(buf), "/usr/share/factory", path); - if (stat(buf, &ast)) + mkparent(path, 0755); + rc = rsync(arg, path, LITE_FOPT_KEEP_MTIME, NULL); + if (rc && errno == ENOENT) + rc = 0; + break; + case 'd': + case 'D': + mkparent(path, 0755); + rc = mksubsys(path, mode ?: 0755, user, group); + break; + case 'e': + if (glob(path, GLOB_NOESCAPE, NULL, &gl)) break; - } else if (arg[0] != '/') { - char *tmp; - tmp = dirname(strdupa(path)); - paste(buf, sizeof(buf), tmp, arg); - dst = realpath(buf, NULL); - if (!dst) - break; - if (stat(dst, &ast)) + for (size_t i = 0; i < gl.gl_pathc; i++) + rc += mksubsys(gl.gl_pathv[i], mode ?: 0755, user, group); + break; + case 'f': + case 'F': + mkparent(path, 0755); + if (type[1] == '+' || type[0] == 'F') { + /* f+/F will create or truncate the file */ + if (!arg) { + rc = create(path, mode ?: 0644, user, group); + break; + } + fp = fopen(path, "w+"); + } else { + /* f will create the file if it doesn't exist */ + if (strc) + fp = fopen(path, "w"); + } + + if (fp) { + write_arg(fp, arg); + rc = fclose(fp); + } + break; + case 'l': /* Finit extension, like 'L' but only if target exists */ + if (!arg) { + paste(buf, sizeof(buf), "/usr/share/factory", path); + if (stat(buf, &ast)) + break; + } else if (arg[0] != '/') { + char *tmp; + + tmp = dirname(strdupa(path)); + paste(buf, sizeof(buf), tmp, arg); + dst = realpath(buf, NULL); + if (!dst) + break; + if (stat(dst, &ast)) + break; + } else { + if (stat(arg, &ast)) + break; + } + /* fallthrough */ + case 'L': + if (!strc) { + if (type[1] != '+') + break; + rmrf(path); + } + mkparent(path, 0755); + if (!arg) { + paste(buf, sizeof(buf), "/usr/share/factory", path); + arg = buf; + } + rc = ln(arg, path); + if (rc && errno == EEXIST) + rc = 0; + break; + case 'p': + if (!strc) { + if (type[1] != '+') + break; + erase(path); + } + mkparent(path, 0755); + rc = mkfifo(path, mode ?: 0644); + break; + case 'r': + case 'R': + break; + case 'w': + if (!arg) break; - } else { - if (stat(arg, &ast)) + + if (glob(path, GLOB_NOESCAPE, NULL, &gl)) break; - } - /* fallthrough */ - case 'L': - if (!strc) { - if (type[1] != '+') + + for (size_t i = 0; i < gl.gl_pathc; i++) { + fp = fopen(gl.gl_pathv[i], type[1] == '+' ? "a" : "w"); + if (fp) { + write_arg(fp, arg); + rc = fclose(fp); + } + } + break; + case 'X': + case 'x': + dbg("Unsupported x/X command, ignoring %s, no support for clean at runtime.", path); + break; + case 'Z': + opts = "-R"; + /* fallthrough */ + case 'z': + if (!whichp("restorecon")) break; - rmrf(path); - } - mkparent(path, 0755); - if (!arg) { - paste(buf, sizeof(buf), "/usr/share/factory", path); - arg = buf; - } - rc = ln(arg, path); - if (rc && errno == EEXIST) - rc = 0; - break; - case 'p': - if (!strc) { - if (type[1] != '+') + if (glob(path, GLOB_NOESCAPE, NULL, &gl)) break; - erase(path); - } - mkparent(path, 0755); - rc = mkfifo(path, mode ?: 0644); - break; - case 'r': - rc = glob_do(path, erase); - if (rc && errno == ENOENT) - rc = 0; - break; - case 'R': - rc = glob_do(path, rmrf); - break; - case 'w': - if (!arg) - break; - - if (glob(path, GLOB_NOESCAPE, NULL, &gl)) - break; - - for (size_t i = 0; i < gl.gl_pathc; i++) { - fp = fopen(gl.gl_pathv[i], type[1] == '+' ? "a" : "w"); - if (fp) { - write_arg(fp, arg); - rc = fclose(fp); + + for (size_t i = 0; i < gl.gl_pathc; i++) { + snprintf(buf, sizeof(buf), "restorecon %s %s", opts, gl.gl_pathv[i]); + run(buf, "restorecon"); } + break; + default: + errx(1, "Unsupported tmpfiles command '%s'", type); + return; } - break; - case 'X': - case 'x': - dbg("Unsupported x/X command, ignoring %s, no support for clean at runtime.", path); - break; - case 'Z': - opts = "-R"; - /* fallthrough */ - case 'z': - if (!whichp("restorecon")) - break; - if (glob(path, GLOB_NOESCAPE, NULL, &gl)) - break; - - for (size_t i = 0; i < gl.gl_pathc; i++) { - snprintf(buf, sizeof(buf), "restorecon %s %s", opts, gl.gl_pathv[i]); - run(buf, "restorecon"); - } - break; - default: - errx(1, "Unsupported tmpfiles command '%s'", type); - return; } if (dst) @@ -464,14 +535,62 @@ static void tmpfiles(char *line) warn("Failed %s operation on path %s", type, path); } -/* - * Only the three last tmpfiles.d/ directories are defined in - * tmpfiles.d(5) as system search paths. Finit adds two more - * before that to have Finit specific ones sorted first, and - * a configure prefix specific one after that for user needs. - */ -void tmpfilesd(void) +static int usage(int rc) +{ + fprintf(stderr, + "Usage: tmpfiles [COMMAND...]\n" + "\n" + "Commands:\n" + " -c, --create Create files and directories\n" + " -r, --remove Remove files and directories marked for removal\n" + " -h, --help This help text\n" + "\n"); + + return rc; +} + +int main(int argc, char *argv[]) { + struct option long_options[] = { + { "create", 0, NULL, 'c' }, + { "remove", 0, NULL, 'r' }, + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + while ((c = getopt_long(argc, argv, "crh?", long_options, NULL)) != EOF) { + switch(c) { + case 'c': + c_flag = 1; + break; + + case 'r': + r_flag = 1; + break; + + case 'h': + case '?': + return usage(0); + + default: + return usage(1); + } + } + + if (c_flag + r_flag == 0) { + fprintf(stderr, "You need to specify at least one of --create or --remove.\n"); + return 1; + } + + /* + * Only the three last tmpfiles.d/ directories are defined in + * tmpfiles.d(5) as system search paths. Finit adds two more + * before that to have Finit specific ones sorted first, and + * a configure prefix specific one after that for user needs. + */ + /* in priority order */ char *dir[] = { FINIT_TMPFILES "/*.conf", diff --git a/src/tmpfiles.h b/src/tmpfiles.h deleted file mode 100644 index 440274bd..00000000 --- a/src/tmpfiles.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Limited tmpfiles.d implementation - * - * Copyright (c) 2024 Joachim Wiberg - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef TMPFILES_H_ -#define TMPFILES_H_ - -#include -#include -#include -#include - -#include -#include -#ifdef _LIBITE_LITE -# include -#else -# include -#endif - -void tmpfilesd(void); - -#endif /* TMPFILES_H_ */