/* sysctl.c - A utility to read and manipulate the sysctl parameters. * * Copyright 2014 Bilal Qureshi * Copyright 2014 Kyungwan Han * * No Standard USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_USR|TOYFLAG_BIN)) config SYSCTL bool "sysctl" default n help usage: sysctl [OPTIONS] [KEY[=VALUE]]... Configure kernel parameters at runtime. -a, A Show all values -e Don't warn about unknown keys -N Show only key names -n Show only key values -p FILE Set values from FILE (default /etc/sysctl.conf) -q Set values silently -w Set values */ #define FOR_sysctl #include "toys.h" #define PROC_SYS_DIR "/proc/sys" #define MAX_BYTES_LINE 1024 static void parse_key_name_value(char *param, char **key_name, char **key_value) { char *name, *value; if (!(name = strchr(param, '='))) goto show_error_msg; value = name; value++; if (!(*value)) goto show_error_msg; *name = '\0'; *key_name = param; *key_value = value; return; show_error_msg: error_msg("error: '%s' must be of the form name=value", param); } static void replace_char(char *str, char old, char new) { for (; *str; str++) if (*str == old) *str = new; } static void handle_file_error(char *key_name) { replace_char(key_name, '/', '.'); if (errno == ENOENT) { if (!(toys.optflags & FLAG_e)) error_msg("error: '%s' is an unknown key", key_name); else toys.exitval = 1; } else perror_msg("error: %s key '%s'", (toys.optflags & FLAG_w) ? "setting" : "reading", key_name); } static void write_to_file(char *fpath, char *key_name, char *key_value) { int fd; replace_char(key_name, '.', '/'); sprintf(toybuf, "%s/%s", fpath, key_name); if ((fd = open(toybuf, O_WRONLY)) < 0) { handle_file_error(key_name); return; } xwrite(fd, key_value, strlen(key_value)); xclose(fd); if (!(toys.optflags & FLAG_q)) { replace_char(key_name, '/', '.'); if (toys.optflags & FLAG_N) xprintf("%s\n", key_name); else if (toys.optflags & FLAG_n) xprintf("%s\n", key_value); else xprintf("%s = %s\n", key_name, key_value); } } static char *get_key_value(char *buff, int *offset) { char *line, *tmp = buff + *offset; int index = 0, multiplier = 1; if (!*tmp) return 0; line = xmalloc(MAX_BYTES_LINE); for (; *tmp != '\n'; tmp++) { line[index++] = *tmp; if (MAX_BYTES_LINE == index) { // buffer overflow multiplier++; line = (char *) xrealloc(line, multiplier * MAX_BYTES_LINE); } } line[index++] = '\0'; *offset += index; return line; } static void trim_spaces(char **param) { int len = 0; char *str = *param, *p_start = str, *p_end; if (p_start) { // start pointer to string p_end = str + strlen(str) - 1; // end pointer to string while (*p_start == ' ') p_start++; str = p_start; while (*p_end == ' ') p_end--; p_end++; *p_end = '\0'; len = (int) (p_end - str) + 1; memmove(*param, str, len); } } // Read config file and write values to corresponding key name files static void read_config_file(char *fname) { char *line, *name = NULL, *value = NULL; int fd = xopen(fname, O_RDONLY); for (; (line = get_line(fd)); free(line), name = NULL, value = NULL) { char *ptr = line; while (*ptr == ' ' || *ptr == '\t') ptr++; if (*ptr != '#' && *ptr != ';' && *ptr !='\n' && *ptr) { parse_key_name_value(ptr, &name, &value); trim_spaces(&name); trim_spaces(&value); if (name && value) write_to_file(PROC_SYS_DIR, name, value); } } xclose(fd); } // Open file for each and every key name and read file contents void read_key_values(char *fpath) { char *key_value, *fdata, *key_name, *tmp = xstrdup(fpath); int offset = 0; key_name = (tmp + strlen(PROC_SYS_DIR) + 1); replace_char(key_name, '/', '.'); if (!(fdata = readfile(fpath, NULL, 0))) { handle_file_error(key_name); free(tmp); return; } if (toys.optflags & FLAG_N) { xprintf("%s\n", key_name); free(tmp); free(fdata); return; } for (; (key_value = get_key_value(fdata, &offset)); free(key_value)) { if (!(toys.optflags & FLAG_q)) { if (!(toys.optflags & FLAG_n)) xprintf("%s = ", key_name); xprintf("%s\n", key_value); } } free(tmp); free(fdata); } static int do_flag_a(struct dirtree *dt) { char *fpath; if (!dirtree_notdotdot(dt)) return 0; // Skip . and .. if (S_ISDIR(dt->st.st_mode)) return DIRTREE_RECURSE; if ((fpath = dirtree_path(dt, 0))) { read_key_values(fpath); free(fpath); } return 0; } void sysctl_main() { char *name = 0, *value = 0, **args = 0; if (toys.optflags & FLAG_a) { dirtree_read(PROC_SYS_DIR, do_flag_a); return; } if (toys.optflags & FLAG_p) { if (*toys.optargs) read_config_file(*toys.optargs); else read_config_file("/etc/sysctl.conf"); return; } if (toys.optflags & FLAG_w) { for (args = toys.optargs; *args; args++, name = NULL, value = NULL) { parse_key_name_value(*args, &name, &value); if (name && value) write_to_file(PROC_SYS_DIR, name, value); } return; } for (args = toys.optargs; *args; args++) { replace_char(*args, '.', '/'); sprintf(toybuf, "%s/%s", PROC_SYS_DIR, *args); read_key_values(toybuf); } }