diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-12-24 03:11:43 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-12-24 03:11:43 +0000 |
commit | 54d10059c93cdbacd709147c1bda45c2039059eb (patch) | |
tree | 20ea089eafe51723d93f28ed5b5ab7089bc2c9ff /procps | |
parent | 4e12b1a2a9e68685dff61acaee1e1f6c377d978c (diff) |
*: tweak error messages
sysctl: shrink; support recursing if name is a directory:
"sysctl net.ipv4.conf". Patch by xmaks AT email.cz
text data bss dec hex filename
793659 504 7492 801655 c3b77 busybox_old
793576 504 7492 801572 c3b24 busybox_unstripped
Diffstat (limited to 'procps')
-rw-r--r-- | procps/sysctl.c | 272 |
1 files changed, 131 insertions, 141 deletions
diff --git a/procps/sysctl.c b/procps/sysctl.c index 860c84062..23fb16e9e 100644 --- a/procps/sysctl.c +++ b/procps/sysctl.c @@ -16,19 +16,11 @@ #include "libbb.h" -static int sysctl_read_setting(const char *setting); -static int sysctl_write_setting(const char *setting); +static int sysctl_act_on_setting(char *setting); static int sysctl_display_all(const char *path); -static int sysctl_preload_file_and_exit(const char *filename); +static int sysctl_handle_preload_file(const char *filename); static void sysctl_dots_to_slashes(char *name); -static const char ETC_SYSCTL_CONF[] ALIGN1 = "/etc/sysctl.conf"; -static const char PROC_SYS[] ALIGN1 = "/proc/sys/"; -enum { strlen_PROC_SYS = sizeof(PROC_SYS) - 1 }; - -static const char msg_unknown_key[] ALIGN1 = - "error: '%s' is an unknown key"; - static void dwrite_str(int fd, const char *buf) { write(fd, buf, strlen(buf)); @@ -52,212 +44,209 @@ int sysctl_main(int argc UNUSED_PARAM, char **argv) opt = getopt32(argv, "+neAapw"); /* '+' - stop on first non-option */ argv += optind; opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); - option_mask32 ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); + option_mask32 = opt; - if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) - return sysctl_display_all(PROC_SYS); - if (opt & FLAG_PRELOAD_FILE) - return sysctl_preload_file_and_exit(*argv ? *argv : ETC_SYSCTL_CONF); + if (opt & FLAG_PRELOAD_FILE) { + option_mask32 |= FLAG_WRITE; + /* xchdir("/proc/sys") is inside */ + return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf"); + } + xchdir("/proc/sys"); + /* xchroot(".") - if you are paranoid */ + if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) { + return sysctl_display_all("."); + } retval = 0; while (*argv) { - if (opt & FLAG_WRITE) - retval |= sysctl_write_setting(*argv); - else - retval |= sysctl_read_setting(*argv); + sysctl_dots_to_slashes(*argv); + retval |= sysctl_display_all(*argv); argv++; } return retval; -} /* end sysctl_main() */ +} /* Set sysctl's from a conf file. Format example: * # Controls IP packet forwarding * net.ipv4.ip_forward = 0 */ -static int sysctl_preload_file_and_exit(const char *filename) +static int sysctl_handle_preload_file(const char *filename) { char *token[2]; parser_t *parser; parser = config_open(filename); + /* Must do it _after_ config_open(): */ + xchdir("/proc/sys"); + /* xchroot(".") - if you are paranoid */ + // TODO: ';' is comment char too while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { -#if 0 - char *s = xasprintf("%s=%s", token[0], token[1]); - sysctl_write_setting(s); - free(s); -#else /* Save ~4 bytes by using parser internals */ - sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition - sysctl_write_setting(parser->line); -#endif + /* Save ~4 bytes by using parser internals */ + /* parser->line is big enough for sprintf */ + sprintf(parser->line, "%s=%s", token[0], token[1]); + sysctl_dots_to_slashes(parser->line); + sysctl_display_all(parser->line); } if (ENABLE_FEATURE_CLEAN_UP) config_close(parser); return 0; -} /* end sysctl_preload_file_and_exit() */ +} -static int sysctl_write_setting(const char *setting) +static int sysctl_act_on_setting(char *setting) { - int retval; - const char *name; - const char *value; - const char *equals; - char *tmpname, *outname, *cptr; - int fd; + int fd, retval = EXIT_SUCCESS; + char *cptr, *outname, *value; - name = setting; - equals = strchr(setting, '='); - if (!equals) { - bb_error_msg("error: '%s' must be of the form name=value", setting); - return EXIT_FAILURE; - } + outname = xstrdup(setting); - value = equals + 1; /* point to the value in name=value */ - if (name == equals || !*value) { - bb_error_msg("error: malformed setting '%s'", setting); - return EXIT_FAILURE; + cptr = outname; + while (*cptr) { + if (*cptr == '/') + *cptr = '.'; + cptr++; } - tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name); - outname = xstrdup(tmpname + strlen_PROC_SYS); - - sysctl_dots_to_slashes(tmpname); - - while ((cptr = strchr(outname, '/')) != NULL) - *cptr = '.'; + if (option_mask32 & FLAG_WRITE) { + cptr = strchr(setting, '='); + if (cptr == NULL) { + bb_error_msg("error: '%s' must be of the form name=value", + outname); + retval = EXIT_FAILURE; + goto end; + } + value = cptr + 1; /* point to the value in name=value */ + if (setting == cptr || !*value) { + bb_error_msg("error: malformed setting '%s'", outname); + retval = EXIT_FAILURE; + goto end; + } + *cptr = '\0'; + fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } else { + fd = open(setting, O_RDONLY); + } - fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { switch (errno) { case ENOENT: if (option_mask32 & FLAG_SHOW_KEY_ERRORS) - bb_error_msg(msg_unknown_key, outname); + bb_error_msg("error: '%s' is an unknown key", outname); break; default: - bb_perror_msg("error setting key '%s'", outname); + bb_perror_msg("error %sing key '%s'", + option_mask32 & FLAG_WRITE ? + "sett" : "read", + outname); break; } retval = EXIT_FAILURE; - } else { + goto end; + } + + if (option_mask32 & FLAG_WRITE) { dwrite_str(fd, value); close(fd); - if (option_mask32 & FLAG_SHOW_KEYS) { + if (option_mask32 & FLAG_SHOW_KEYS) printf("%s = ", outname); - } puts(value); - retval = EXIT_SUCCESS; - } - - free(tmpname); - free(outname); - return retval; -} /* end sysctl_write_setting() */ - -static int sysctl_read_setting(const char *name) -{ - int retval; - char *tmpname, *outname, *cptr; - char inbuf[1025]; - FILE *fp; - - if (!*name) { - if (option_mask32 & FLAG_SHOW_KEY_ERRORS) - bb_error_msg(msg_unknown_key, name); - return -1; - } - - tmpname = concat_path_file(PROC_SYS, name); - outname = xstrdup(tmpname + strlen_PROC_SYS); - - sysctl_dots_to_slashes(tmpname); - - while ((cptr = strchr(outname, '/')) != NULL) - *cptr = '.'; + } else { + char c; - fp = fopen_for_read(tmpname); - if (fp == NULL) { - switch (errno) { - case ENOENT: - if (option_mask32 & FLAG_SHOW_KEY_ERRORS) - bb_error_msg(msg_unknown_key, outname); - break; - default: + value = cptr = xmalloc_read(fd, NULL); + close(fd); + if (value == NULL) { bb_perror_msg("error reading key '%s'", outname); - break; + goto end; } - retval = EXIT_FAILURE; - } else { - while (fgets(inbuf, sizeof(inbuf) - 1, fp)) { - if (option_mask32 & FLAG_SHOW_KEYS) { + + /* dev.cdrom.info and sunrpc.transports, for example, + * are multi-line. Try "sysctl sunrpc.transports" + */ + while ((c = *cptr) != '\0') { + if (option_mask32 & FLAG_SHOW_KEYS) printf("%s = ", outname); + while (1) { + fputc(c, stdout); + cptr++; + if (c == '\n') + break; + c = *cptr; + if (c == '\0') + break; } - fputs(inbuf, stdout); } - fclose(fp); - retval = EXIT_SUCCESS; + free(value); } - - free(tmpname); + end: free(outname); return retval; -} /* end sysctl_read_setting() */ +} static int sysctl_display_all(const char *path) { - int retval = EXIT_SUCCESS; - DIR *dp; - struct dirent *de; - char *tmpdir; - struct stat ts; - - dp = opendir(path); - if (!dp) { - return EXIT_FAILURE; - } - while ((de = readdir(dp)) != NULL) { - tmpdir = concat_subpath_file(path, de->d_name); - if (tmpdir == NULL) - continue; /* . or .. */ - if (stat(tmpdir, &ts) != 0) { - bb_perror_msg(tmpdir); - } else if (S_ISDIR(ts.st_mode)) { - retval |= sysctl_display_all(tmpdir); - } else { - retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS); + DIR *dirp; + struct stat buf; + struct dirent *entry; + char *next; + int retval = 0; + + stat(path, &buf); + if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) { + dirp = opendir(path); + if (dirp == NULL) + return -1; + while ((entry = readdir(dirp)) != NULL) { + next = concat_subpath_file( + path, entry->d_name); + if (next == NULL) + continue; /* d_name is "." or ".." */ + /* if path was ".", drop "./" prefix: */ + retval |= sysctl_display_all((next[0] == '.' && next[1] == '/') ? + next + 2 : next); + free(next); } - free(tmpdir); - } /* end while */ - closedir(dp); + closedir(dirp); + } else { + char *name = xstrdup(path); + retval |= sysctl_act_on_setting(name); + free(name); + } return retval; -} /* end sysctl_display_all() */ +} static void sysctl_dots_to_slashes(char *name) { char *cptr, *last_good, *end; - /* It can be good as-is! */ - if (access(name, F_OK) == 0) - return; - - /* Example from bug 3894: + /* Convert minimum number of '.' to '/' so that + * we end up with existing file's name. + * + * Example from bug 3894: * net.ipv4.conf.eth0.100.mc_forwarding -> - * net/ipv4/conf/eth0.100/mc_forwarding. NB: - * net/ipv4/conf/eth0/mc_forwarding *also exists*, + * net/ipv4/conf/eth0.100/mc_forwarding + * NB: net/ipv4/conf/eth0/mc_forwarding *also exists*, * therefore we must start from the end, and if * we replaced even one . -> /, start over again, * but never replace dots before the position - * where replacement occurred. */ - end = name + strlen(name) - 1; + * where last replacement occurred. + */ + end = name + strlen(name); last_good = name - 1; + *end = '.'; /* trick the loop in trying full name too */ + again: cptr = end; while (cptr > last_good) { if (*cptr == '.') { *cptr = '\0'; + //bb_error_msg("trying:'%s'", name); if (access(name, F_OK) == 0) { *cptr = '/'; + *end = '\0'; /* prevent trailing '/' */ + //bb_error_msg("replaced:'%s'", name); last_good = cptr; goto again; } @@ -265,4 +254,5 @@ static void sysctl_dots_to_slashes(char *name) } cptr--; } -} /* end sysctl_dots_to_slashes() */ + *end = '\0'; +} |