From a3f326cd6619cadb44cb4bb051b1d4e54bcd3477 Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Mon, 31 Mar 2014 16:39:26 +0200 Subject: [PATCH] swapon/swapoff: size reduction, cleanup, fixes, improvements 1) real swapon/swapoff handles also devices on the commandline with -a; 2) xstat(device) in swap_enable_disable aborts on error when cycling through fstab so some devices are not handled; 3) duplicated code for ENABLE_FEATURE_SWAPON_DISCARD and ENABLE_FEATURE_SWAPON_PRI was moved to functions. 4) silence some error messages with -a; 5) minor cleanups and code refactoring reduced the size as per bloat-check: 6) I also added support for /proc/swaps handling to swapoff: "When the -a flag is given, swapping is disabled on all known swap devices and files (as found in /proc/swaps or /etc/fstab)." So now swapoff first cycles through /proc/swaps and then through fstab to swapoff all devices. function old new delta set_discard_flag - 106 +106 swap_enable_disable 147 238 +91 set_priority_flag - 79 +79 retrieve_file_data 470 467 -3 swap_on_off_main 638 418 -220 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 1/2 up/down: 276/-223) Total: 53 bytes Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 276 ++++++++++++++++++++++++----------------- 1 file changed, 161 insertions(+), 115 deletions(-) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index a7ad6db79..acdb67729 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -63,90 +63,142 @@ struct globals { } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define g_flags (G.flags) +#define save_g_flags() int save_g_flags = g_flags +#define restore_g_flags() g_flags = save_g_flags #else #define g_flags 0 +#define save_g_flags() ((void)0) +#define restore_g_flags() ((void)0) #endif #define INIT_G() do { } while (0) +#define do_swapoff (applet_name[5] == 'f') + +/* Command line options */ +enum { + OPTBIT_a, /* -a all */ + IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */ + IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */ + OPT_a = 1 << OPTBIT_a, + OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0, + OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0, +}; + +#define OPT_ALL (option_mask32 & OPT_a) +#define OPT_DISCARD (option_mask32 & OPT_d) +#define OPT_PRIO (option_mask32 & OPT_p) + static int swap_enable_disable(char *device) { - int status; + int err = 0; + int quiet = 0; struct stat st; resolve_mount_spec(&device); - xstat(device, &st); - -#if ENABLE_DESKTOP - /* test for holes */ - if (S_ISREG(st.st_mode)) - if (st.st_blocks * (off_t)512 < st.st_size) - bb_error_msg("warning: swap file has holes"); -#endif - if (applet_name[5] == 'n') - status = swapon(device, g_flags); - else - status = swapoff(device); + if (do_swapoff) { + err = swapoff(device); + /* Don't complain on OPT_ALL if not a swap device or if it doesn't exist */ + quiet = (OPT_ALL && (errno == EINVAL || errno == ENOENT)); + } else { + /* swapon */ + err = stat(device, &st); + if (!err) { + if (ENABLE_DESKTOP && S_ISREG(st.st_mode)) { + if (st.st_blocks * (off_t)512 < st.st_size) { + bb_error_msg("%s: file has holes", device); + return 1; + } + } + err = swapon(device, g_flags); + /* Don't complain on swapon -a if device is already in use */ + quiet = (OPT_ALL && errno == EBUSY); + } + } - if (status != 0) { - bb_simple_perror_msg(device); + if (err) { + if (!quiet) + bb_simple_perror_msg(device); return 1; } - return 0; } -static int do_em_all(void) +#if ENABLE_FEATURE_SWAPON_DISCARD +static void set_discard_flag(char *s) { - struct mntent *m; - FILE *f; - int err; -#ifdef G - int cl_flags = g_flags; + /* Unset the flag first to allow fstab options to override */ + /* options set on the command line */ + g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD; + + if (!s) /* No optional policy value on the commandline */ + return; + /* Skip prepended '=' */ + if (*s == '=') + s++; + /* For fstab parsing: remove other appended options */ + *strchrnul(s, ',') = '\0'; + + if (strcmp(s, "once") == 0) + g_flags |= SWAP_FLAG_DISCARD_ONCE; + if (strcmp(s, "pages") == 0) + g_flags |= SWAP_FLAG_DISCARD_PAGES; +} +#else +#define set_discard_flag(s) ((void)0) +#endif + +#if ENABLE_FEATURE_SWAPON_PRI +static void set_priority_flag(char *s) +{ + unsigned prio; + + /* For fstab parsing: remove other appended options */ + *strchrnul(s, ',') = '\0'; + /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ + prio = bb_strtou(s, NULL, 10); + if (!errno) { + /* Unset the flag first to allow fstab options to override */ + /* options set on the command line */ + g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER | + MIN(prio, SWAP_FLAG_PRIO_MASK); + } +} +#else +#define set_priority_flag(s) ((void)0) #endif - f = setmntent("/etc/fstab", "r"); - if (f == NULL) - bb_perror_msg_and_die("/etc/fstab"); +static int do_em_all_in_fstab(void) +{ + struct mntent *m; + int err = 0; + FILE *f = xfopen_for_read("/etc/fstab"); - err = 0; while ((m = getmntent(f)) != NULL) { if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) { /* swapon -a should ignore entries with noauto, - * but swapoff -a should process them */ - if (applet_name[5] != 'n' - || hasmntopt(m, MNTOPT_NOAUTO) == NULL - ) { -#if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI - char *p; - g_flags = cl_flags; /* each swap space might have different flags */ -#if ENABLE_FEATURE_SWAPON_DISCARD - p = hasmntopt(m, "discard"); - if (p) { - if (p[7] == '=') { - if (strncmp(p + 8, "once", 4) == 0 && (p[12] == ',' || p[12] == '\0')) - g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE; - else if (strncmp(p + 8, "pages", 5) == 0 && (p[13] == ',' || p[13] == '\0')) - g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES; + * but swapoff -a should process them + */ + if (do_swapoff || hasmntopt(m, MNTOPT_NOAUTO) == NULL) { + /* each swap space might have different flags */ + /* save global flags for the next round */ + save_g_flags(); + if (ENABLE_FEATURE_SWAPON_DISCARD) { + char *p = hasmntopt(m, "discard"); + if (p) { + /* move to '=' or to end of string */ + p += 7; + set_discard_flag(p); } - else if (p[7] == ',' || p[7] == '\0') - g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD; } -#endif -#if ENABLE_FEATURE_SWAPON_PRI - p = hasmntopt(m, "pri"); - if (p) { - /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ - unsigned prio = bb_strtou(p + 4, NULL, 10); - /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */ - if (errno != ERANGE) { - g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER | - MIN(prio, SWAP_FLAG_PRIO_MASK); + if (ENABLE_FEATURE_SWAPON_PRI) { + char *p = hasmntopt(m, "pri"); + if (p) { + set_priority_flag(p + 4); } } -#endif -#endif - err += swap_enable_disable(m->mnt_fsname); + err |= swap_enable_disable(m->mnt_fsname); + restore_g_flags(); } } } @@ -157,74 +209,68 @@ static int do_em_all(void) return err; } +static int do_all_in_proc_swaps(void) +{ + char *line; + int err = 0; + FILE *f = fopen_for_read("/proc/swaps"); + /* Don't complain if missing */ + if (f) { + while ((line = xmalloc_fgetline(f)) != NULL) { + if (line[0] == '/') { + *strchrnul(line, ' ') = '\0'; + err |= swap_enable_disable(line); + } + free(line); + } + if (ENABLE_FEATURE_CLEAN_UP) + fclose(f); + } + + return err; +} + +#define OPTSTR_SWAPON "a" \ + IF_FEATURE_SWAPON_DISCARD("d::") \ + IF_FEATURE_SWAPON_PRI("p:") + int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int swap_on_off_main(int argc UNUSED_PARAM, char **argv) { - int ret; -#if ENABLE_FEATURE_SWAPON_DISCARD - char *discard = NULL; -#endif -#if ENABLE_FEATURE_SWAPON_PRI - unsigned prio; -#endif + IF_FEATURE_SWAPON_PRI(char *prio;) + IF_FEATURE_SWAPON_DISCARD(char *discard = NULL;) + int ret = 0; INIT_G(); -#if !ENABLE_FEATURE_SWAPON_DISCARD && !ENABLE_FEATURE_SWAPON_PRI - ret = getopt32(argv, "a"); -#else -#if ENABLE_FEATURE_SWAPON_PRI - if (applet_name[5] == 'n') - opt_complementary = "p+"; -#endif - ret = getopt32(argv, (applet_name[5] == 'n') ? -#if ENABLE_FEATURE_SWAPON_DISCARD - "d::" -#endif -#if ENABLE_FEATURE_SWAPON_PRI - "p:" -#endif - "a" : "a" -#if ENABLE_FEATURE_SWAPON_DISCARD - , &discard -#endif -#if ENABLE_FEATURE_SWAPON_PRI - , &prio -#endif - ); -#endif - -#if ENABLE_FEATURE_SWAPON_DISCARD - if (ret & 1) { // -d - if (!discard) - g_flags |= SWAP_FLAG_DISCARD; - else if (strcmp(discard, "once") == 0) - g_flags |= SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE; - else if (strcmp(discard, "pages") == 0) - g_flags |= SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES; - else - bb_show_usage(); - } - ret >>= 1; -#endif -#if ENABLE_FEATURE_SWAPON_PRI - if (ret & 1) // -p - g_flags |= SWAP_FLAG_PREFER | - MIN(prio, SWAP_FLAG_PRIO_MASK); - ret >>= 1; -#endif - - if (ret /* & 1: not needed */) // -a - return do_em_all(); + getopt32(argv, do_swapoff ? "a" : OPTSTR_SWAPON + IF_FEATURE_SWAPON_DISCARD(, &discard) + IF_FEATURE_SWAPON_PRI(, &prio) + ); argv += optind; - if (!*argv) - bb_show_usage(); - /* ret = 0; redundant */ - do { - ret += swap_enable_disable(*argv); - } while (*++argv); + if (OPT_DISCARD) { + set_discard_flag(discard); + } + if (OPT_PRIO) { + set_priority_flag(prio); + } + if (OPT_ALL) { + /* swapoff -a does also /proc/swaps */ + if (do_swapoff) + ret = do_all_in_proc_swaps(); + ret |= do_em_all_in_fstab(); + } else if (!*argv) { + /* if not -a we need at least one arg */ + bb_show_usage(); + } + /* Unset -a now to allow for more messages in swap_enable_disable */ + option_mask32 = option_mask32 & ~OPT_a; + /* Now process devices on the commandline if any */ + while (*argv) { + ret |= swap_enable_disable(*argv++); + } return ret; } -- 2.25.1