Skip to content

Commit

Permalink
move cli processing to the compiled code
Browse files Browse the repository at this point in the history
The shell script is no longer responsible for parsing the command
line at all.
  • Loading branch information
vapier committed May 21, 2019
1 parent ccdacdd commit e2afb33
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 52 deletions.
166 changes: 166 additions & 0 deletions src/cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright 2017-2019 Gentoo Foundation
* Copyright 2017-2019 The Chromium OS Authors
* Released under the 2-clause BSD license.
*/

#include "tmpfiles.h"

bool verbose = false;
bool dryrun = false;
bool do_default = true;
bool do_boot = false;
bool do_clean = false;
bool do_create = false;
bool do_remove = false;

ordered_set_t cli_files = {};
ordered_set_t rules_prefix = {};
ordered_set_t rules_exclude_prefix = {};

/* Order here doesn't matter. All must have a value of 256+ though. */
typedef enum {
OPT_BOOT = 0x100,
OPT_CLEAN,
OPT_CREATE,
OPT_DRY_RUN,
OPT_EXCLUDE_PREFIX,
OPT_PREFIX,
OPT_REMOVE,
OPT_VERBOSE,
OPT_VERSION,
} options;

/*
* Order here reflects output order in --help output, so things are loosely
* grouped by relationship. Otherwise, it doesn't matter.
*/
#define PARSE_FLAGS "h"
#define a_argument required_argument
static struct option const opts[] = {
{"boot", no_argument, NULL, OPT_BOOT},
{"create", no_argument, NULL, OPT_CREATE},
{"clean", no_argument, NULL, OPT_CLEAN},
{"remove", no_argument, NULL, OPT_REMOVE},
{"prefix", a_argument, NULL, OPT_PREFIX},
{"exclude-prefix", a_argument, NULL, OPT_EXCLUDE_PREFIX},
{"dry-run", no_argument, NULL, OPT_DRY_RUN},
{"dryrun", no_argument, NULL, OPT_DRY_RUN},
{"verbose", no_argument, NULL, OPT_VERBOSE},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, OPT_VERSION},
{NULL, no_argument, NULL, 0x0}
};

/* Order here must match opts array above. */
static const char * const opts_help[] = {
"Process all boot entries (have a ! in the command)",
"Process all f, F, w, d, D, v, p, L, c, b, m and z, Z, t, T, a, A commands",
"All paths with an age setting will be cleaned",
"Remove directories marked with D or R, and paths marked with r or R",
"Only process entries that match the specified paths",
"Ignore all entries that match the specified paths",
"Don't make any changes; just show what would be done",
"Alias to --dry-run",
"Run in verbose mode",
"Print this help and exit",
"Print version and exit",
NULL,
};

attribute_noreturn
void usage(int status)
{
/* Increase this if a new --longer-option is added. */
const int padding = 25;
size_t i;
int ret;
FILE *fp = status == 0 ? stdout : stderr;

fprintf(fp,
"Usage: tmpfiles [options] [files]\n"
"\n"
"If [files] are specified, only those will be processed.\n"
"Basenames are searched, while non-basenames are read directly.\n"
"\n"
"Options:\n"
);

for (i = 0; opts[i].name; ++i) {
if (opts[i].val > '~' || opts[i].val < ' ')
fprintf(fp, " ");
else
fprintf(fp, " -%c, ", opts[i].val);

ret = fprintf(fp, "--%s ", opts[i].name);
fprintf(fp, "%-*s", padding - ret,
opts[i].has_arg == no_argument ? "" : "<arg>");
fprintf(fp, " %s\n", opts_help[i]);
}

exit(status);
}

#ifndef VERSION
#define VERSION "git"
#endif
attribute_noreturn
static void version(void)
{
printf("opentmpfiles %s\n", VERSION);
exit(0);
}

void parseargs(int argc, char *argv[])
{
int i;

ordered_set_init(&cli_files);
ordered_set_init(&rules_prefix);
ordered_set_init(&rules_exclude_prefix);

while ((i = getopt_long(argc, argv, PARSE_FLAGS, opts, NULL)) != -1) {
switch (i) {
case OPT_BOOT:
do_default = false;
do_boot = true;
break;
case OPT_CLEAN:
do_default = false;
do_clean = true;
break;
case OPT_CREATE:
do_default = false;
do_create = true;
break;
case OPT_REMOVE:
do_default = false;
do_remove = true;
break;

case OPT_EXCLUDE_PREFIX:
ordered_set_add(&rules_exclude_prefix, optarg);
break;
case OPT_PREFIX:
ordered_set_add(&rules_prefix, optarg);
break;

case OPT_DRY_RUN:
dryrun = true;
break;
case OPT_VERBOSE:
verbose = true;
break;
case OPT_VERSION:
version();
break;
case 'h':
usage(0);
default:
usage(1);
}
}

for (i = optind; i < argc; ++i)
ordered_set_add(&cli_files, argv[i]);
}
38 changes: 38 additions & 0 deletions src/cli.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2017-2019 Gentoo Foundation
* Copyright 2017-2019 The Chromium OS Authors
* Released under the 2-clause BSD license.
*/

#ifndef TMPFILES_CLI_H
#define TMPFILES_CLI_H

#include "set.h"

#ifdef __cplusplus
extern "C" {
#endif

/* State variables for the program. */
extern bool verbose;
extern bool dryrun;
extern bool do_boot;
extern bool do_default;
extern bool do_create;
extern bool do_clean;
extern bool do_remove;
extern ordered_set_t cli_files;
extern ordered_set_t rules_prefix;
extern ordered_set_t rules_exclude_prefix;

/* Parse the command line arguments. */
extern void parseargs(int argc, char *argv[]);

/* Show usage details and exit. */
attribute_noreturn void usage(int status);

#ifdef __cplusplus
}
#endif

#endif
3 changes: 3 additions & 0 deletions src/headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@
#include <string.h>
#include <unistd.h>

/* Some compiler attributes we find useful. */
#define attribute_noreturn __attribute__((__noreturn__))

#endif
50 changes: 49 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,56 @@

#include "tmpfiles.h"

static const char *flatten_set(ordered_set_t *set)
{
char *ret = NULL;
size_t i;
size_t len = 0;

for (i = 0; i < set->count; ++i)
len += strlen(set->elements[i]) + 1;

if (len == 0)
return "";

char *p = ret = xmalloc(len);
for (i = 0; i < set->count; ++i) {
char *ele = set->elements[i];
size_t len = strlen(ele);
memcpy(p, ele, len);
p[len] = ' ';
p += len + 1;
}
ret[len - 1] = '\0';
return ret;
}

static void reset_env(void)
{
setenv("BOOT", do_boot ? "1" : "0", 1);
setenv("CLEAN", do_clean ? "1" : "0", 1);
setenv("CREATE", do_create ? "1" : "0", 1);
setenv("DRYRUN", dryrun ? "1" : "0", 1);
setenv("REMOVE", do_remove ? "1" : "0", 1);
setenv("VERBOSE", verbose ? "1" : "0", 1);
setenv("EXCLUDE", flatten_set(&rules_exclude_prefix), 1);
setenv("PREFIX", flatten_set(&rules_prefix), 1);
setenv("FILES", flatten_set(&cli_files), 1);
}

int main(int argc, char *argv[])
{
int ret = execvp("tmpfiles.sh", argv);
parseargs(argc, argv);

if (do_clean)
errx(1, "--clean is not yet implemented");

if (do_create && do_remove)
errx(1, "--create and --remove are mutually exclusive");

/* Clear out env vars the shell script expects. */
reset_env();

int ret = execlp("tmpfiles.sh", "tmpfiles.sh", "--run-by-tmpfiles-wrapper", NULL);
err(ret, "Could not execute tmpfiles.sh");
}
1 change: 1 addition & 0 deletions src/module.mk
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
SRC-tmpfiles := \
cli.c \
main.c \

SRC-libtmpfiles.so := \
Expand Down
1 change: 1 addition & 0 deletions src/tmpfiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@

#include "xfuncs.h"
#include "set.h"
#include "cli.h"

#endif
9 changes: 9 additions & 0 deletions test/cli_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright 2017-2019 Gentoo Foundation
* Copyright 2017-2019 The Chromium OS Authors
* Released under the 2-clause BSD license.
*/

/* Tests for the cli module. */

#include "test.h"
1 change: 1 addition & 0 deletions test/module.mk
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
LDFLAGS-tmpfiles_test = -pthread
LDLIBS-tmpfiles_test = -lgtest
SRC-tmpfiles_test := \
cli_test.cc \
set_test.cc \
xfuncs_test.cc \

Expand Down
61 changes: 10 additions & 51 deletions tmpfiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@
# as of 2012/03/12 and also implements some more recent features
#

DRYRUN=0
# Any errors we've accumulated from processing.
error=0
# XXX: No idea why this is here.
LINENO=0

# Make sure we aren't run directly anymore.
if [ $# -ne 1 ] || [ "$1" != "--run-by-tmpfiles-wrapper" ]; then
echo "tmpfiles.sh: this must not be run directly; use tmpfiles instead" >&2
exit 1
fi

checkprefix() {
n=$1
Expand All @@ -34,11 +43,6 @@ warninvalid() {
error=$(( error+1 ))
} >&2

invalid_option() {
printf "tmpfiles: invalid option '%s'\n" "$1" >&2
exit 1
}

dryrun_or_real() {
local dryrun=
if [ $DRYRUN -eq 1 ]; then
Expand Down Expand Up @@ -429,49 +433,6 @@ _Z() {
CHOPTS=-R relabel "$@"
}

usage() {
printf 'usage: %s [--exclude-prefix=path] [--prefix=path] [--boot] [--create] [--remove] [--clean] [--verbose] [--dry-run]\n' "${0##*/}"
exit ${1:-0}
}

version() {
# We don't record the version info anywhere currently.
echo "opentmpfiles"
exit 0
}

BOOT=0 CREATE=0 REMOVE=0 CLEAN=0 VERBOSE=0 DRYRUN=0 error=0 LINENO=0
EXCLUDE=
PREFIX=
FILES=

while [ $# -gt 0 ]; do
case $1 in
--boot) BOOT=1 ;;
--create) CREATE=1 ;;
--remove) REMOVE=1 ;;
--clean) CLEAN=1 ;; # TODO: Not implemented
--verbose) VERBOSE=1 ;;
--dryrun|--dry-run) DRYRUN=1 ;;
--exclude-prefix=*) EXCLUDE="${EXCLUDE}${1##--exclude-prefix=} " ;;
--prefix=*) PREFIX="${PREFIX}${1##--prefix=} " ;;
-h|--help) usage ;;
--version) version ;;
-*) invalid_option "$1" ;;
*) FILES="${FILES} $1"
esac
shift
done

if [ $(( CLEAN )) -eq 1 ] ; then
printf '%s clean mode is not implemented\n' "${0##*/}"
exit 1
fi

if [ "$CREATE$REMOVE" = '00' ]; then
usage 1 >&2
fi

# XXX: The harcoding of /usr/lib/ is an explicit choice by upstream
tmpfiles_dirs='/usr/lib/tmpfiles.d /run/tmpfiles.d /etc/tmpfiles.d'
tmpfiles_basenames=''
Expand Down Expand Up @@ -510,8 +471,6 @@ for b in ${FILES} ; do
fi
done

error=0

# loop through the gathered fragments, sorted globally by filename.
# `/run/tmpfiles/foo.conf' will always be read after `/etc/tmpfiles.d/bar.conf'
FILE=
Expand Down

0 comments on commit e2afb33

Please sign in to comment.