From c207c9240cf9d7c6430ff43f213bbd954307669c Mon Sep 17 00:00:00 2001 From: Karthik Prabhu Vinod Date: Fri, 4 Oct 2019 22:28:47 -0700 Subject: [PATCH] Add --path to swupd autoupdate mechanism Signed-off-by: Karthik Prabhu Vinod Fixes #527 --- .travis.yml | 5 +- Makefile.am | 2 +- src/autoupdate.c | 117 ++++++++++++++-------- src/lib/sys.h | 6 ++ swupd.bash | 2 +- swupd.zsh | 1 + test/functional/README.md | 1 + test/functional/autoupdate/aup-basic.bats | 63 ++++++++++++ test/functional/check_ids.bash | 2 +- test/functional/testlib.bash | 2 +- 10 files changed, 154 insertions(+), 47 deletions(-) create mode 100755 test/functional/autoupdate/aup-basic.bats diff --git a/.travis.yml b/.travis.yml index d394df89..06f16e2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ env: - UPDATE_SUBGROUP2_TOTAL="$((UPDATE_TOTAL - UPDATE_SUBGROUP1_TOTAL))" - UPDATE_SUBGROUP1="$(find test/functional/update -name *.bats | head -n $UPDATE_SUBGROUP1_TOTAL | tr '\n' ' ')" - UPDATE_SUBGROUP2="$(find test/functional/update -name *.bats | tail -n $UPDATE_SUBGROUP2_TOTAL | tr '\n' ' ')" - - GROUP1="$UPDATE_SUBGROUP1 $(find test/functional/{bundleinfo,verify-legacy} -name *.bats -printf '%p ')" + - GROUP1="$UPDATE_SUBGROUP1 $(find test/functional/{autoupdate,bundleinfo,verify-legacy} -name *.bats -printf '%p ')" - GROUP2="$UPDATE_SUBGROUP2 $(find test/functional/{checkupdate,hashdump,mirror,usability} -name *.bats \( ! -name usa-config-file.bats \) -printf '%p ')" - GROUP3="$(find test/functional/{diagnose,search,os-install,repair} -name *.bats -printf '%p ')" - GROUP4="$(find test/functional/{bundleadd,bundleremove,bundlelist,signature} -name *.bats -printf '%p ')" @@ -27,7 +27,7 @@ jobs: name: "Static Analysis & Unit Tests" script: make compliant && make shellcheck && sudo sh -c 'umask 0022 && make unit-check' && make docs-coverage - stage: test - name: "Functional Tests - update (group 1), bundle-info, verify-legacy" + name: "Functional Tests - update (group 1), autoupdate, bundle-info, verify-legacy" script: env TESTS="$GROUP1" make -e check - stage: test name: "Functional Tests - update (group 2), checkupdate, hashdump, mirror, usability" @@ -50,6 +50,7 @@ install: - sudo apt-get install doxygen - sudo apt-get install check - sudo pip install coverxygen + - sudo ln -s /bin/systemctl /usr/bin/systemctl # build bsdiff - wget https://github.com/clearlinux/bsdiff/releases/download/v1.0.2/bsdiff-1.0.2.tar.xz diff --git a/Makefile.am b/Makefile.am index 36716595..5aade5ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -165,6 +165,7 @@ BATS_LOG_DRIVER = $(tap_driver) TESTS = $(dist_check_SCRIPTS) $(UNIT_TESTS) BATS = \ + test/functional/autoupdate/aup-basic.bats \ test/functional/bundleadd/add-alias-basic.bats \ test/functional/bundleadd/add-also-add-flag.bats \ test/functional/bundleadd/add-bad-hash.bats \ @@ -341,7 +342,6 @@ BATS = \ test/functional/verify-legacy/verify-bundle.bats \ test/functional/verify-legacy/verify-flags.bats - UNIT_TESTS = \ test/unit/test_list.test \ test/unit/test_manifest.test \ diff --git a/src/autoupdate.c b/src/autoupdate.c index 5e3ea081..fb9fdad7 100644 --- a/src/autoupdate.c +++ b/src/autoupdate.c @@ -29,56 +29,74 @@ #include "swupd.h" -static void print_help(const char *name) +static void print_help(void) { print("Usage:\n"); - print(" swupd %s [options]\n\n", basename((char *)name)); - print("Help Options:\n"); - print(" -h, --help Show help options\n"); + print(" swupd autoupdate [OPTION...]\n\n"); + + global_print_help(); + + print("Options:\n"); print(" --enable enable autoupdates\n"); print(" --disable disable autoupdates\n"); print("\n"); } -static int enable; -static int disable; +#define FLAG_ENABLE 2000 +#define FLAG_DISABLE 2001 +static bool cmdline_option_enable; +static bool cmdline_option_disable; static const struct option prog_opts[] = { { "help", no_argument, 0, 'h' }, - { "enable", no_argument, &enable, 1 }, - { "disable", no_argument, &disable, 1 }, - { 0, 0, 0, 0 } + { "enable", no_argument, 0, FLAG_ENABLE }, + { "disable", no_argument, 0, FLAG_DISABLE }, +}; + +static bool parse_opt(int opt, UNUSED_PARAM char *optarg) +{ + switch (opt) { + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case FLAG_ENABLE: + cmdline_option_enable = optarg_to_bool(optarg); + return true; + case FLAG_DISABLE: + cmdline_option_disable = optarg_to_bool(optarg); + return true; + default: + return false; + } + return false; +} + +static const struct global_options opts = { + prog_opts, + sizeof(prog_opts) / sizeof(struct option), + parse_opt, + print_help, }; static bool parse_options(int argc, char **argv) { - int opt; + int ind = global_parse_options(argc, argv, &opts); - while ((opt = getopt_long(argc, argv, "h", prog_opts, NULL)) != -1) { - switch (opt) { - case '?': - print_help(argv[0]); - exit(SWUPD_INVALID_OPTION); - case 'h': - print_help(argv[0]); - exit(EXIT_SUCCESS); - case 0: /* getopt_long has set the flag */ - break; - default: - error("unrecognized option\n\n"); - goto err; - } + if (ind < 0) { + return false; } - if (argc > optind) { + if (argc > ind) { error("unexpected arguments\n\n"); - goto err; + return false; + } + + if (cmdline_option_enable && cmdline_option_disable) { + error("Can not use --enable and --disable options at the same time\n\n"); + return false; } return true; -err: - print_help(argv[0]); - return false; } static void policy_warn(void) @@ -89,38 +107,50 @@ static void policy_warn(void) enum swupd_code autoupdate_main(int argc, char **argv) { + if (!parse_options(argc, argv)) { + print_help(); return SWUPD_INVALID_OPTION; } - if (enable && disable) { - error("Can not enable and disable at the same time\n"); - return SWUPD_INVALID_OPTION; - } - if (!systemctl_active()) { + + if (!globals.path_prefix && !systemctl_active()) { error("Systemd is inactive - unable to proceed\n"); return SWUPD_SUBPROCESS_ERROR; } - if (enable) { + if (cmdline_option_enable) { int rc; check_root(); info("Running systemctl to enable updates\n"); - rc = systemctl_cmd("unmask", "--now", "swupd-update.service", "swupd-update.timer", NULL); + rc = systemctl_cmd_path(globals.path_prefix, + "unmask", "--now", "swupd-update.service", "swupd-update.timer", NULL); if (rc != 0) { return SWUPD_SUBPROCESS_ERROR; } - rc = systemctl_restart("swupd-update.timer"); + + /* If globals.path_prefix is given and we have succeeded so far, + * make sense to return SWUPD_OK at this point. In that case, + * a swupd timer restart cannot be done as there is no + * system daemon to communicate for rootfs. + */ + if (globals.path_prefix) { + warn("Running autoupdate with --path will not restart swupd-update.timer. This will have to be done manually\n"); + return SWUPD_OK; + } + + rc = systemctl_cmd_path(globals.path_prefix, "restart", "swupd-update.timer", NULL); if (rc != 0) { return SWUPD_SUBPROCESS_ERROR; } return SWUPD_OK; - } else if (disable) { + } else if (cmdline_option_disable) { int rc; check_root(); policy_warn(); info("Running systemctl to disable updates\n"); - rc = systemctl_cmd("mask", "--now", "swupd-update.service", "swupd-update.timer", NULL); + rc = systemctl_cmd_path(globals.path_prefix, + "mask", "--now", "swupd-update.service", "swupd-update.timer", NULL); if (rc) { return SWUPD_SUBPROCESS_ERROR; } @@ -128,8 +158,8 @@ enum swupd_code autoupdate_main(int argc, char **argv) } else { int rc1, rc2; const int STATUS_UNKNOWN = 4; - rc1 = systemctl_cmd("is-enabled", "swupd-update.service", NULL); - rc2 = systemctl_cmd("is-active", "swupd-update.timer", NULL); + rc1 = systemctl_cmd_path(globals.path_prefix, "is-enabled", "swupd-update.service", NULL); + rc2 = systemctl_cmd_path(globals.path_prefix, "is-active", "swupd-update.timer", NULL); if (rc1 == SWUPD_OK && rc2 == SWUPD_OK) { print("Enabled\n"); return SWUPD_OK; @@ -137,6 +167,11 @@ enum swupd_code autoupdate_main(int argc, char **argv) /* systemctl returns 1,2, or 3 when program dead or not running */ error("Unable to determine autoupdate status\n"); return SWUPD_SUBPROCESS_ERROR; + // Swupd-service is unmasked, static but timer is inactive for --path + } else if (rc1 == SWUPD_OK && rc2 != SWUPD_OK && globals.path_prefix) { + info("Autoupdate is enabled, but the timer is not running\n"); + error("Unable to determine autoupdate status with --path option\n"); + return SWUPD_SUBPROCESS_ERROR; } else { print("Disabled\n"); return SWUPD_NO; diff --git a/src/lib/sys.h b/src/lib/sys.h index 99754ce0..5899e49f 100644 --- a/src/lib/sys.h +++ b/src/lib/sys.h @@ -203,6 +203,12 @@ int sys_rm(const char *filename); */ #define systemctl_cmd(...) run_command_quiet(SYSTEMCTL, __VA_ARGS__) +/** + * @brief Run a systemctl command with the informed parameters in a path + */ +#define systemctl_cmd_path(path, ...) \ + path ? run_command_quiet(SYSTEMCTL, "--root", path, __VA_ARGS__) : run_command_quiet(SYSTEMCTL, __VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/swupd.bash b/swupd.bash index fec51530..0646152d 100644 --- a/swupd.bash +++ b/swupd.bash @@ -34,7 +34,7 @@ _swupd() opts="$global " break;; ("autoupdate") - opts="--help --enable --disable " + opts="$global --help --enable --disable " break;; ("check-update") opts="$global " diff --git a/swupd.zsh b/swupd.zsh index bb8607e2..fe24b50b 100644 --- a/swupd.zsh +++ b/swupd.zsh @@ -185,6 +185,7 @@ if [[ -n "$state" ]]; then ;; autoupdate) local -a autoupdates; autoupdates=( + $global_opts '(-)--enable[enable autoupdates]' '(-)--disable[disable autoupdates]' '(-)'{-h,--help}'[Show help options]' diff --git a/test/functional/README.md b/test/functional/README.md index 3efbaf2e..b8e4364e 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -125,6 +125,7 @@ or more of the [assertions](#assertions) provided by the test library. | Main Business Scenarios | Commands | Group ID | | ---------------------------------------------- | ------------- | -------- | +| Enables/Disables autoupdate | auto-update | AUT | | Install new bundles | bundle-add | ADD | | Uninstall bundles | bundle-remove | REM | | List bundles | bundle-list | LST | diff --git a/test/functional/autoupdate/aup-basic.bats b/test/functional/autoupdate/aup-basic.bats new file mode 100755 index 00000000..139e77f5 --- /dev/null +++ b/test/functional/autoupdate/aup-basic.bats @@ -0,0 +1,63 @@ +#!/usr/bin/env bats + +# Author: Karthik Prabhu Vinod +# Email: karthik.prabhu.vinod@intel.com + +load "../testlib" + +test_setup() { + + if [ -z "$TRAVIS" ]; then + skip "This test is intended to run only in Travis (use TRAVIS=true to run it anyway)..." + fi + + create_test_environment "$TEST_NAME" + +} + +test_teardown() { + + if [ -z "$TRAVIS" ]; then + return + fi + + destroy_test_environment "$TEST_NAME" + +} + +@test "AUT001: Basic test, Check auto-update enable" { + + # perform autoupdate disable + run sudo sh -c "$SWUPD autoupdate --disable --path=$PATH_PREFIX" + assert_status_is "$SWUPD_OK" + expected_output=$(cat <<-EOM + Warning: disabling automatic updates may take you out of compliance with your IT policy + Running systemctl to disable updates + EOM + ) + assert_is_output "$expected_output" + + # check for files(should exist and point to /dev/null) + run sudo sh -c "test -c $PATH_PREFIX/etc/systemd/system/swupd-update.service" + assert_status_is "$SWUPD_OK" + run sudo sh -c "test -c $PATH_PREFIX/etc/systemd/system/swupd-update.timer" + assert_status_is "$SWUPD_OK" + + # perform autoupdate enable + run sudo sh -c "$SWUPD autoupdate --enable $SWUPD_OPTS" + + assert_status_is "$SWUPD_OK" + expected_output=$(cat <<-EOM + Running systemctl to enable updates + Warning: Running autoupdate with --path will not restart swupd-update.timer. This will have to be done manually + EOM + ) + assert_is_output "$expected_output" + + # check for files(should not exist) + run sudo sh -c "test -c $PATH_PREFIX/etc/systemd/system/swupd-update.service" + assert_status_is_not "$SWUPD_OK" + run sudo sh -c "test -c $PATH_PREFIX/etc/systemd/system/swupd-update.timer" + assert_status_is_not "$SWUPD_OK" + +} diff --git a/test/functional/check_ids.bash b/test/functional/check_ids.bash index 6391661a..5ddbdb07 100755 --- a/test/functional/check_ids.bash +++ b/test/functional/check_ids.bash @@ -5,7 +5,7 @@ FUNC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=/dev/null source "$FUNC_DIR"/testlib.bash -declare -A groups=( ["bundleadd"]="ADD" ["bundlelist"]="LST" ["bundleremove"]="REM" \ +declare -A groups=( ["autoupdate"]="AUT" ["bundleadd"]="ADD" ["bundlelist"]="LST" ["bundleremove"]="REM" \ ["checkupdate"]="CHK" ["hashdump"]="HSD" ["mirror"]="MIR" ["search"]="SRH" \ ["update"]="UPD" ["usability"]="USA" ["verify"]="VER" ) diff --git a/test/functional/testlib.bash b/test/functional/testlib.bash index a0d10797..553cb229 100644 --- a/test/functional/testlib.bash +++ b/test/functional/testlib.bash @@ -3171,6 +3171,7 @@ get_next_available_id() { # swupd_function id=$((id+1)) test_dir=$(basename "$(realpath "$test_dir")") case "$test_dir" in + autoupdate) group=AUT;; bundleadd) group=ADD;; bundleremove) group=REM;; bundlelist) group=LST;; @@ -3184,7 +3185,6 @@ get_next_available_id() { # swupd_function completion) group=USA;; usability) group=USA;; signature) group=SIG;; - autoupdate) group=AUT;; info) group=INF;; clean) group=CLN;; os-install) group=INS;;