Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/procps/sysctl.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/procps/sysctl.c
r1765 r2725 5 5 * Copyright 1999 George Staikos 6 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 8 * 9 9 * Changelog: 10 * v1.01: 11 * - added -p <preload> to preload values from a file 12 * v1.01.1 13 * - busybox applet aware by <solar@gentoo.org> 14 * 10 * v1.01 - added -p <preload> to preload values from a file 11 * v1.01.1 - busybox applet aware by <solar@gentoo.org> 15 12 */ 16 13 17 14 #include "libbb.h" 18 15 19 /* 20 * Function Prototypes 21 */ 22 static int sysctl_read_setting(const char *setting, int output); 23 static int sysctl_write_setting(const char *setting, int output); 24 static int sysctl_preload_file(const char *filename, int output); 25 static int sysctl_display_all(const char *path, int output, int show_table); 26 27 /* 28 * Globals... 29 */ 30 static const char PROC_PATH[] ALIGN1 = "/proc/sys/"; 31 static const char DEFAULT_PRELOAD[] ALIGN1 = "/etc/sysctl.conf"; 32 33 /* error messages */ 34 static const char ERR_UNKNOWN_PARAMETER[] ALIGN1 = 35 "error: Unknown parameter '%s'\n"; 36 static const char ERR_MALFORMED_SETTING[] ALIGN1 = 37 "error: Malformed setting '%s'\n"; 38 static const char ERR_NO_EQUALS[] ALIGN1 = 39 "error: '%s' must be of the form name=value\n"; 40 static const char ERR_INVALID_KEY[] ALIGN1 = 41 "error: '%s' is an unknown key\n"; 42 static const char ERR_UNKNOWN_WRITING[] ALIGN1 = 43 "error: unknown error %d setting key '%s'\n"; 44 static const char ERR_UNKNOWN_READING[] ALIGN1 = 45 "error: unknown error %d reading key '%s'\n"; 46 static const char ERR_PERMISSION_DENIED[] ALIGN1 = 47 "error: permission denied on key '%s'\n"; 48 static const char ERR_PRELOAD_FILE[] ALIGN1 = 49 "error: cannot open preload file '%s'\n"; 50 static const char WARN_BAD_LINE[] ALIGN1 = 51 "warning: %s(%d): invalid syntax, continuing...\n"; 52 53 54 static void dwrite_str(int fd, const char *buf) 55 { 56 write(fd, buf, strlen(buf)); 57 } 58 59 /* 60 * sysctl_main()... 61 */ 62 int sysctl_main(int argc, char **argv); 63 int sysctl_main(int argc, char **argv) 64 { 65 int retval = 0; 66 int output = 1; 67 int write_mode = 0; 68 int switches_allowed = 1; 69 70 if (argc < 2) 71 bb_show_usage(); 72 73 argv++; 74 75 for (; argv && *argv && **argv; argv++) { 76 if (switches_allowed && **argv == '-') { /* we have a switch */ 77 switch ((*argv)[1]) { 78 case 'n': 79 output = 0; 80 break; 81 case 'w': 82 write_mode = 1; 83 switches_allowed = 0; 84 break; 85 case 'p': 86 argv++; 87 return 88 sysctl_preload_file(((argv && *argv 89 && **argv) ? *argv : 90 DEFAULT_PRELOAD), output); 91 case 'a': 92 case 'A': 93 switches_allowed = 0; 94 return sysctl_display_all(PROC_PATH, output, 95 ((*argv)[1] == 'a') ? 0 : 1); 96 case 'h': 97 case '?': 98 bb_show_usage(); 99 default: 100 bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv); 101 bb_show_usage(); 16 enum { 17 FLAG_SHOW_KEYS = 1 << 0, 18 FLAG_SHOW_KEY_ERRORS = 1 << 1, 19 FLAG_TABLE_FORMAT = 1 << 2, /* not implemented */ 20 FLAG_SHOW_ALL = 1 << 3, 21 FLAG_PRELOAD_FILE = 1 << 4, 22 FLAG_WRITE = 1 << 5, 23 }; 24 #define OPTION_STR "neAapw" 25 26 static void sysctl_dots_to_slashes(char *name) 27 { 28 char *cptr, *last_good, *end; 29 30 /* Convert minimum number of '.' to '/' so that 31 * we end up with existing file's name. 32 * 33 * Example from bug 3894: 34 * net.ipv4.conf.eth0.100.mc_forwarding -> 35 * net/ipv4/conf/eth0.100/mc_forwarding 36 * NB: net/ipv4/conf/eth0/mc_forwarding *also exists*, 37 * therefore we must start from the end, and if 38 * we replaced even one . -> /, start over again, 39 * but never replace dots before the position 40 * where last replacement occurred. 41 * 42 * Another bug we later had is that 43 * net.ipv4.conf.eth0.100 44 * (without .mc_forwarding) was mishandled. 45 * 46 * To set up testing: modprobe 8021q; vconfig add eth0 100 47 */ 48 end = name + strlen(name); 49 last_good = name - 1; 50 *end = '.'; /* trick the loop into trying full name too */ 51 52 again: 53 cptr = end; 54 while (cptr > last_good) { 55 if (*cptr == '.') { 56 *cptr = '\0'; 57 //bb_error_msg("trying:'%s'", name); 58 if (access(name, F_OK) == 0) { 59 *cptr = '/'; 60 //bb_error_msg("replaced:'%s'", name); 61 last_good = cptr; 62 goto again; 102 63 } 103 } else { 104 switches_allowed = 0; 105 if (write_mode) 106 retval = sysctl_write_setting(*argv, output); 107 else 108 sysctl_read_setting(*argv, output); 109 } 110 } 111 return retval; 112 } /* end sysctl_main() */ 113 114 115 116 /* 117 * sysctl_preload_file 118 * preload the sysctl's from a conf file 119 * - we parse the file and then reform it (strip out whitespace) 120 */ 121 #define PRELOAD_BUF 256 122 123 int sysctl_preload_file(const char *filename, int output) 124 { 125 int lineno = 0; 126 char oneline[PRELOAD_BUF]; 127 char buffer[PRELOAD_BUF]; 128 char *name, *value, *ptr; 129 FILE *fp = NULL; 130 131 if (!filename || ((fp = fopen(filename, "r")) == NULL)) { 132 bb_error_msg_and_die(ERR_PRELOAD_FILE, filename); 133 } 134 135 while (fgets(oneline, sizeof(oneline) - 1, fp)) { 136 oneline[sizeof(oneline) - 1] = '\0'; 137 lineno++; 138 trim(oneline); 139 ptr = (char *) oneline; 140 141 if (*ptr == '#' || *ptr == ';') 142 continue; 143 144 if (strlen(ptr) < 2) 145 continue; 146 147 name = strtok(ptr, "="); 148 if (!name || !*name) { 149 bb_error_msg(WARN_BAD_LINE, filename, lineno); 150 continue; 151 } 152 153 trim(name); 154 155 value = strtok(NULL, "\n\r"); 156 if (!value || !*value) { 157 bb_error_msg(WARN_BAD_LINE, filename, lineno); 158 continue; 159 } 160 161 while ((*value == ' ' || *value == '\t') && *value != 0) 162 value++; 163 /* safe because sizeof(oneline) == sizeof(buffer) */ 164 sprintf(buffer, "%s=%s", name, value); 165 sysctl_write_setting(buffer, output); 166 } 167 fclose(fp); 168 return 0; 169 } /* end sysctl_preload_file() */ 170 171 172 /* 173 * Write a single sysctl setting 174 */ 175 int sysctl_write_setting(const char *setting, int output) 176 { 177 int retval = 0; 178 const char *name = setting; 179 const char *value; 180 const char *equals; 181 char *tmpname, *outname, *cptr; 182 int fd = -1; 183 184 if (!name) /* probably dont' want to display this err */ 185 return 0; 186 187 if (!(equals = strchr(setting, '='))) { 188 bb_error_msg(ERR_NO_EQUALS, setting); 189 return -1; 190 } 191 192 value = equals + sizeof(char); /* point to the value in name=value */ 193 194 if (!*name || !*value || name == equals) { 195 bb_error_msg(ERR_MALFORMED_SETTING, setting); 196 return -2; 197 } 198 199 tmpname = xasprintf("%s%.*s", PROC_PATH, (int)(equals - name), name); 200 outname = xstrdup(tmpname + strlen(PROC_PATH)); 201 202 while ((cptr = strchr(tmpname, '.')) != NULL) 203 *cptr = '/'; 204 205 while ((cptr = strchr(outname, '/')) != NULL) 206 *cptr = '.'; 207 208 fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666); 64 *cptr = '.'; 65 } 66 cptr--; 67 } 68 *end = '\0'; 69 } 70 71 static int sysctl_act_on_setting(char *setting) 72 { 73 int fd, retval = EXIT_SUCCESS; 74 char *cptr, *outname; 75 char *value = value; /* for compiler */ 76 77 outname = xstrdup(setting); 78 79 cptr = outname; 80 while (*cptr) { 81 if (*cptr == '/') 82 *cptr = '.'; 83 cptr++; 84 } 85 86 if (option_mask32 & FLAG_WRITE) { 87 cptr = strchr(setting, '='); 88 if (cptr == NULL) { 89 bb_error_msg("error: '%s' must be of the form name=value", 90 outname); 91 retval = EXIT_FAILURE; 92 goto end; 93 } 94 value = cptr + 1; /* point to the value in name=value */ 95 if (setting == cptr || !*value) { 96 bb_error_msg("error: malformed setting '%s'", outname); 97 retval = EXIT_FAILURE; 98 goto end; 99 } 100 *cptr = '\0'; 101 outname[cptr - setting] = '\0'; 102 /* procps 3.2.7 actually uses these flags */ 103 fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666); 104 } else { 105 fd = open(setting, O_RDONLY); 106 } 107 209 108 if (fd < 0) { 210 109 switch (errno) { 211 110 case ENOENT: 212 bb_error_msg(ERR_INVALID_KEY, outname); 213 break; 214 case EACCES: 215 bb_perror_msg(ERR_PERMISSION_DENIED, outname); 111 if (option_mask32 & FLAG_SHOW_KEY_ERRORS) 112 bb_error_msg("error: '%s' is an unknown key", outname); 216 113 break; 217 114 default: 218 bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname); 115 bb_perror_msg("error %sing key '%s'", 116 option_mask32 & FLAG_WRITE ? 117 "sett" : "read", 118 outname); 219 119 break; 220 120 } 221 retval = -1; 121 retval = EXIT_FAILURE; 122 goto end; 123 } 124 125 if (option_mask32 & FLAG_WRITE) { 126 //TODO: procps 3.2.7 writes "value\n", note trailing "\n" 127 xwrite_str(fd, value); 128 close(fd); 129 if (option_mask32 & FLAG_SHOW_KEYS) 130 printf("%s = ", outname); 131 puts(value); 222 132 } else { 223 dwrite_str(fd, value); 133 char c; 134 135 value = cptr = xmalloc_read(fd, NULL); 224 136 close(fd); 225 if (output) { 226 dwrite_str(STDOUT_FILENO, outname); 227 dwrite_str(STDOUT_FILENO, " = "); 228 } 229 dwrite_str(STDOUT_FILENO, value); 230 dwrite_str(STDOUT_FILENO, "\n"); 231 } 232 233 /* cleanup */ 234 free(tmpname); 137 if (value == NULL) { 138 bb_perror_msg("error reading key '%s'", outname); 139 goto end; 140 } 141 142 /* dev.cdrom.info and sunrpc.transports, for example, 143 * are multi-line. Try "sysctl sunrpc.transports" 144 */ 145 while ((c = *cptr) != '\0') { 146 if (option_mask32 & FLAG_SHOW_KEYS) 147 printf("%s = ", outname); 148 while (1) { 149 fputc(c, stdout); 150 cptr++; 151 if (c == '\n') 152 break; 153 c = *cptr; 154 if (c == '\0') 155 break; 156 } 157 } 158 free(value); 159 } 160 end: 235 161 free(outname); 236 162 return retval; 237 } /* end sysctl_write_setting() */ 238 239 240 /* 241 * Read a sysctl setting 242 * 163 } 164 165 static int sysctl_act_recursive(const char *path) 166 { 167 DIR *dirp; 168 struct stat buf; 169 struct dirent *entry; 170 char *next; 171 int retval = 0; 172 173 stat(path, &buf); 174 if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) { 175 dirp = opendir(path); 176 if (dirp == NULL) 177 return -1; 178 while ((entry = readdir(dirp)) != NULL) { 179 next = concat_subpath_file(path, entry->d_name); 180 if (next == NULL) 181 continue; /* d_name is "." or ".." */ 182 /* if path was ".", drop "./" prefix: */ 183 retval |= sysctl_act_recursive((next[0] == '.' && next[1] == '/') ? 184 next + 2 : next); 185 free(next); 186 } 187 closedir(dirp); 188 } else { 189 char *name = xstrdup(path); 190 retval |= sysctl_act_on_setting(name); 191 free(name); 192 } 193 194 return retval; 195 } 196 197 /* Set sysctl's from a conf file. Format example: 198 * # Controls IP packet forwarding 199 * net.ipv4.ip_forward = 0 243 200 */ 244 int sysctl_read_setting(const char *setting, int output) 245 { 246 int retval = 0; 247 char *tmpname, *outname, *cptr; 248 char inbuf[1025]; 249 const char *name = setting; 250 FILE *fp; 251 252 if (!setting || !*setting) 253 bb_error_msg(ERR_INVALID_KEY, setting); 254 255 tmpname = concat_path_file(PROC_PATH, name); 256 outname = xstrdup(tmpname + strlen(PROC_PATH)); 257 258 while ((cptr = strchr(tmpname, '.')) != NULL) 259 *cptr = '/'; 260 while ((cptr = strchr(outname, '/')) != NULL) 261 *cptr = '.'; 262 263 if ((fp = fopen(tmpname, "r")) == NULL) { 264 switch (errno) { 265 case ENOENT: 266 bb_error_msg(ERR_INVALID_KEY, outname); 267 break; 268 case EACCES: 269 bb_error_msg(ERR_PERMISSION_DENIED, outname); 270 break; 271 default: 272 bb_error_msg(ERR_UNKNOWN_READING, errno, outname); 273 break; 274 } 275 retval = -1; 276 } else { 277 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) { 278 if (output) { 279 dwrite_str(STDOUT_FILENO, outname); 280 dwrite_str(STDOUT_FILENO, " = "); 281 } 282 dwrite_str(STDOUT_FILENO, inbuf); 283 } 284 fclose(fp); 285 } 286 287 free(tmpname); 288 free(outname); 201 static int sysctl_handle_preload_file(const char *filename) 202 { 203 char *token[2]; 204 parser_t *parser; 205 206 parser = config_open(filename); 207 /* Must do it _after_ config_open(): */ 208 xchdir("/proc/sys"); 209 /* xchroot(".") - if you are paranoid */ 210 211 //TODO: ';' is comment char too 212 //TODO: comment may be only at line start. "var=1 #abc" - "1 #abc" is the value 213 // (but _whitespace_ from ends should be trimmed first (and we do it right)) 214 //TODO: "var==1" is mishandled (must use "=1" as a value, but uses "1") 215 // can it be fixed by removing PARSE_COLLAPSE bit? 216 while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { 217 char *tp; 218 sysctl_dots_to_slashes(token[0]); 219 tp = xasprintf("%s=%s", token[0], token[1]); 220 sysctl_act_recursive(tp); 221 free(tp); 222 } 223 if (ENABLE_FEATURE_CLEAN_UP) 224 config_close(parser); 225 return 0; 226 } 227 228 int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 229 int sysctl_main(int argc UNUSED_PARAM, char **argv) 230 { 231 int retval; 232 int opt; 233 234 opt = getopt32(argv, "+" OPTION_STR); /* '+' - stop on first non-option */ 235 argv += optind; 236 opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); 237 option_mask32 = opt; 238 239 if (opt & FLAG_PRELOAD_FILE) { 240 option_mask32 |= FLAG_WRITE; 241 /* xchdir("/proc/sys") is inside */ 242 return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf"); 243 } 244 xchdir("/proc/sys"); 245 /* xchroot(".") - if you are paranoid */ 246 if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) { 247 return sysctl_act_recursive("."); 248 } 249 250 retval = 0; 251 while (*argv) { 252 sysctl_dots_to_slashes(*argv); 253 retval |= sysctl_act_recursive(*argv); 254 argv++; 255 } 256 289 257 return retval; 290 } /* end sysctl_read_setting() */ 291 292 293 294 /* 295 * Display all the sysctl settings 296 * 297 */ 298 int sysctl_display_all(const char *path, int output, int show_table) 299 { 300 int retval = 0; 301 int retval2; 302 DIR *dp; 303 struct dirent *de; 304 char *tmpdir; 305 struct stat ts; 306 307 dp = opendir(path); 308 if (!dp) { 309 retval = -1; 310 } else { 311 while ((de = readdir(dp)) != NULL) { 312 tmpdir = concat_subpath_file(path, de->d_name); 313 if (tmpdir == NULL) 314 continue; 315 retval2 = stat(tmpdir, &ts); 316 if (retval2 != 0) 317 bb_perror_msg(tmpdir); 318 else { 319 if (S_ISDIR(ts.st_mode)) { 320 sysctl_display_all(tmpdir, output, show_table); 321 } else 322 retval |= 323 sysctl_read_setting(tmpdir + strlen(PROC_PATH), 324 output); 325 326 } 327 free(tmpdir); 328 } /* end while */ 329 closedir(dp); 330 } 331 332 return retval; 333 } /* end sysctl_display_all() */ 258 }
Note:
See TracChangeset
for help on using the changeset viewer.