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) | |
download | busybox-w32-54d10059c93cdbacd709147c1bda45c2039059eb.tar.gz busybox-w32-54d10059c93cdbacd709147c1bda45c2039059eb.tar.bz2 busybox-w32-54d10059c93cdbacd709147c1bda45c2039059eb.zip |
*: 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 @@ | |||
16 | 16 | ||
17 | #include "libbb.h" | 17 | #include "libbb.h" |
18 | 18 | ||
19 | static int sysctl_read_setting(const char *setting); | 19 | static int sysctl_act_on_setting(char *setting); |
20 | static int sysctl_write_setting(const char *setting); | ||
21 | static int sysctl_display_all(const char *path); | 20 | static int sysctl_display_all(const char *path); |
22 | static int sysctl_preload_file_and_exit(const char *filename); | 21 | static int sysctl_handle_preload_file(const char *filename); |
23 | static void sysctl_dots_to_slashes(char *name); | 22 | static void sysctl_dots_to_slashes(char *name); |
24 | 23 | ||
25 | static const char ETC_SYSCTL_CONF[] ALIGN1 = "/etc/sysctl.conf"; | ||
26 | static const char PROC_SYS[] ALIGN1 = "/proc/sys/"; | ||
27 | enum { strlen_PROC_SYS = sizeof(PROC_SYS) - 1 }; | ||
28 | |||
29 | static const char msg_unknown_key[] ALIGN1 = | ||
30 | "error: '%s' is an unknown key"; | ||
31 | |||
32 | static void dwrite_str(int fd, const char *buf) | 24 | static void dwrite_str(int fd, const char *buf) |
33 | { | 25 | { |
34 | write(fd, buf, strlen(buf)); | 26 | write(fd, buf, strlen(buf)); |
@@ -52,212 +44,209 @@ int sysctl_main(int argc UNUSED_PARAM, char **argv) | |||
52 | opt = getopt32(argv, "+neAapw"); /* '+' - stop on first non-option */ | 44 | opt = getopt32(argv, "+neAapw"); /* '+' - stop on first non-option */ |
53 | argv += optind; | 45 | argv += optind; |
54 | opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); | 46 | opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); |
55 | option_mask32 ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); | 47 | option_mask32 = opt; |
56 | 48 | ||
57 | if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) | 49 | if (opt & FLAG_PRELOAD_FILE) { |
58 | return sysctl_display_all(PROC_SYS); | 50 | option_mask32 |= FLAG_WRITE; |
59 | if (opt & FLAG_PRELOAD_FILE) | 51 | /* xchdir("/proc/sys") is inside */ |
60 | return sysctl_preload_file_and_exit(*argv ? *argv : ETC_SYSCTL_CONF); | 52 | return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf"); |
53 | } | ||
54 | xchdir("/proc/sys"); | ||
55 | /* xchroot(".") - if you are paranoid */ | ||
56 | if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) { | ||
57 | return sysctl_display_all("."); | ||
58 | } | ||
61 | 59 | ||
62 | retval = 0; | 60 | retval = 0; |
63 | while (*argv) { | 61 | while (*argv) { |
64 | if (opt & FLAG_WRITE) | 62 | sysctl_dots_to_slashes(*argv); |
65 | retval |= sysctl_write_setting(*argv); | 63 | retval |= sysctl_display_all(*argv); |
66 | else | ||
67 | retval |= sysctl_read_setting(*argv); | ||
68 | argv++; | 64 | argv++; |
69 | } | 65 | } |
70 | 66 | ||
71 | return retval; | 67 | return retval; |
72 | } /* end sysctl_main() */ | 68 | } |
73 | 69 | ||
74 | /* Set sysctl's from a conf file. Format example: | 70 | /* Set sysctl's from a conf file. Format example: |
75 | * # Controls IP packet forwarding | 71 | * # Controls IP packet forwarding |
76 | * net.ipv4.ip_forward = 0 | 72 | * net.ipv4.ip_forward = 0 |
77 | */ | 73 | */ |
78 | static int sysctl_preload_file_and_exit(const char *filename) | 74 | static int sysctl_handle_preload_file(const char *filename) |
79 | { | 75 | { |
80 | char *token[2]; | 76 | char *token[2]; |
81 | parser_t *parser; | 77 | parser_t *parser; |
82 | 78 | ||
83 | parser = config_open(filename); | 79 | parser = config_open(filename); |
80 | /* Must do it _after_ config_open(): */ | ||
81 | xchdir("/proc/sys"); | ||
82 | /* xchroot(".") - if you are paranoid */ | ||
83 | |||
84 | // TODO: ';' is comment char too | 84 | // TODO: ';' is comment char too |
85 | while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { | 85 | while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { |
86 | #if 0 | 86 | /* Save ~4 bytes by using parser internals */ |
87 | char *s = xasprintf("%s=%s", token[0], token[1]); | 87 | /* parser->line is big enough for sprintf */ |
88 | sysctl_write_setting(s); | 88 | sprintf(parser->line, "%s=%s", token[0], token[1]); |
89 | free(s); | 89 | sysctl_dots_to_slashes(parser->line); |
90 | #else /* Save ~4 bytes by using parser internals */ | 90 | sysctl_display_all(parser->line); |
91 | sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition | ||
92 | sysctl_write_setting(parser->line); | ||
93 | #endif | ||
94 | } | 91 | } |
95 | if (ENABLE_FEATURE_CLEAN_UP) | 92 | if (ENABLE_FEATURE_CLEAN_UP) |
96 | config_close(parser); | 93 | config_close(parser); |
97 | return 0; | 94 | return 0; |
98 | } /* end sysctl_preload_file_and_exit() */ | 95 | } |
99 | 96 | ||
100 | static int sysctl_write_setting(const char *setting) | 97 | static int sysctl_act_on_setting(char *setting) |
101 | { | 98 | { |
102 | int retval; | 99 | int fd, retval = EXIT_SUCCESS; |
103 | const char *name; | 100 | char *cptr, *outname, *value; |
104 | const char *value; | ||
105 | const char *equals; | ||
106 | char *tmpname, *outname, *cptr; | ||
107 | int fd; | ||
108 | 101 | ||
109 | name = setting; | 102 | outname = xstrdup(setting); |
110 | equals = strchr(setting, '='); | ||
111 | if (!equals) { | ||
112 | bb_error_msg("error: '%s' must be of the form name=value", setting); | ||
113 | return EXIT_FAILURE; | ||
114 | } | ||
115 | 103 | ||
116 | value = equals + 1; /* point to the value in name=value */ | 104 | cptr = outname; |
117 | if (name == equals || !*value) { | 105 | while (*cptr) { |
118 | bb_error_msg("error: malformed setting '%s'", setting); | 106 | if (*cptr == '/') |
119 | return EXIT_FAILURE; | 107 | *cptr = '.'; |
108 | cptr++; | ||
120 | } | 109 | } |
121 | 110 | ||
122 | tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name); | 111 | if (option_mask32 & FLAG_WRITE) { |
123 | outname = xstrdup(tmpname + strlen_PROC_SYS); | 112 | cptr = strchr(setting, '='); |
124 | 113 | if (cptr == NULL) { | |
125 | sysctl_dots_to_slashes(tmpname); | 114 | bb_error_msg("error: '%s' must be of the form name=value", |
126 | 115 | outname); | |
127 | while ((cptr = strchr(outname, '/')) != NULL) | 116 | retval = EXIT_FAILURE; |
128 | *cptr = '.'; | 117 | goto end; |
118 | } | ||
119 | value = cptr + 1; /* point to the value in name=value */ | ||
120 | if (setting == cptr || !*value) { | ||
121 | bb_error_msg("error: malformed setting '%s'", outname); | ||
122 | retval = EXIT_FAILURE; | ||
123 | goto end; | ||
124 | } | ||
125 | *cptr = '\0'; | ||
126 | fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666); | ||
127 | } else { | ||
128 | fd = open(setting, O_RDONLY); | ||
129 | } | ||
129 | 130 | ||
130 | fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666); | ||
131 | if (fd < 0) { | 131 | if (fd < 0) { |
132 | switch (errno) { | 132 | switch (errno) { |
133 | case ENOENT: | 133 | case ENOENT: |
134 | if (option_mask32 & FLAG_SHOW_KEY_ERRORS) | 134 | if (option_mask32 & FLAG_SHOW_KEY_ERRORS) |
135 | bb_error_msg(msg_unknown_key, outname); | 135 | bb_error_msg("error: '%s' is an unknown key", outname); |
136 | break; | 136 | break; |
137 | default: | 137 | default: |
138 | bb_perror_msg("error setting key '%s'", outname); | 138 | bb_perror_msg("error %sing key '%s'", |
139 | option_mask32 & FLAG_WRITE ? | ||
140 | "sett" : "read", | ||
141 | outname); | ||
139 | break; | 142 | break; |
140 | } | 143 | } |
141 | retval = EXIT_FAILURE; | 144 | retval = EXIT_FAILURE; |
142 | } else { | 145 | goto end; |
146 | } | ||
147 | |||
148 | if (option_mask32 & FLAG_WRITE) { | ||
143 | dwrite_str(fd, value); | 149 | dwrite_str(fd, value); |
144 | close(fd); | 150 | close(fd); |
145 | if (option_mask32 & FLAG_SHOW_KEYS) { | 151 | if (option_mask32 & FLAG_SHOW_KEYS) |
146 | printf("%s = ", outname); | 152 | printf("%s = ", outname); |
147 | } | ||
148 | puts(value); | 153 | puts(value); |
149 | retval = EXIT_SUCCESS; | 154 | } else { |
150 | } | 155 | char c; |
151 | |||
152 | free(tmpname); | ||
153 | free(outname); | ||
154 | return retval; | ||
155 | } /* end sysctl_write_setting() */ | ||
156 | |||
157 | static int sysctl_read_setting(const char *name) | ||
158 | { | ||
159 | int retval; | ||
160 | char *tmpname, *outname, *cptr; | ||
161 | char inbuf[1025]; | ||
162 | FILE *fp; | ||
163 | |||
164 | if (!*name) { | ||
165 | if (option_mask32 & FLAG_SHOW_KEY_ERRORS) | ||
166 | bb_error_msg(msg_unknown_key, name); | ||
167 | return -1; | ||
168 | } | ||
169 | |||
170 | tmpname = concat_path_file(PROC_SYS, name); | ||
171 | outname = xstrdup(tmpname + strlen_PROC_SYS); | ||
172 | |||
173 | sysctl_dots_to_slashes(tmpname); | ||
174 | |||
175 | while ((cptr = strchr(outname, '/')) != NULL) | ||
176 | *cptr = '.'; | ||
177 | 156 | ||
178 | fp = fopen_for_read(tmpname); | 157 | value = cptr = xmalloc_read(fd, NULL); |
179 | if (fp == NULL) { | 158 | close(fd); |
180 | switch (errno) { | 159 | if (value == NULL) { |
181 | case ENOENT: | ||
182 | if (option_mask32 & FLAG_SHOW_KEY_ERRORS) | ||
183 | bb_error_msg(msg_unknown_key, outname); | ||
184 | break; | ||
185 | default: | ||
186 | bb_perror_msg("error reading key '%s'", outname); | 160 | bb_perror_msg("error reading key '%s'", outname); |
187 | break; | 161 | goto end; |
188 | } | 162 | } |
189 | retval = EXIT_FAILURE; | 163 | |
190 | } else { | 164 | /* dev.cdrom.info and sunrpc.transports, for example, |
191 | while (fgets(inbuf, sizeof(inbuf) - 1, fp)) { | 165 | * are multi-line. Try "sysctl sunrpc.transports" |
192 | if (option_mask32 & FLAG_SHOW_KEYS) { | 166 | */ |
167 | while ((c = *cptr) != '\0') { | ||
168 | if (option_mask32 & FLAG_SHOW_KEYS) | ||
193 | printf("%s = ", outname); | 169 | printf("%s = ", outname); |
170 | while (1) { | ||
171 | fputc(c, stdout); | ||
172 | cptr++; | ||
173 | if (c == '\n') | ||
174 | break; | ||
175 | c = *cptr; | ||
176 | if (c == '\0') | ||
177 | break; | ||
194 | } | 178 | } |
195 | fputs(inbuf, stdout); | ||
196 | } | 179 | } |
197 | fclose(fp); | 180 | free(value); |
198 | retval = EXIT_SUCCESS; | ||
199 | } | 181 | } |
200 | 182 | end: | |
201 | free(tmpname); | ||
202 | free(outname); | 183 | free(outname); |
203 | return retval; | 184 | return retval; |
204 | } /* end sysctl_read_setting() */ | 185 | } |
205 | 186 | ||
206 | static int sysctl_display_all(const char *path) | 187 | static int sysctl_display_all(const char *path) |
207 | { | 188 | { |
208 | int retval = EXIT_SUCCESS; | 189 | DIR *dirp; |
209 | DIR *dp; | 190 | struct stat buf; |
210 | struct dirent *de; | 191 | struct dirent *entry; |
211 | char *tmpdir; | 192 | char *next; |
212 | struct stat ts; | 193 | int retval = 0; |
213 | 194 | ||
214 | dp = opendir(path); | 195 | stat(path, &buf); |
215 | if (!dp) { | 196 | if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) { |
216 | return EXIT_FAILURE; | 197 | dirp = opendir(path); |
217 | } | 198 | if (dirp == NULL) |
218 | while ((de = readdir(dp)) != NULL) { | 199 | return -1; |
219 | tmpdir = concat_subpath_file(path, de->d_name); | 200 | while ((entry = readdir(dirp)) != NULL) { |
220 | if (tmpdir == NULL) | 201 | next = concat_subpath_file( |
221 | continue; /* . or .. */ | 202 | path, entry->d_name); |
222 | if (stat(tmpdir, &ts) != 0) { | 203 | if (next == NULL) |
223 | bb_perror_msg(tmpdir); | 204 | continue; /* d_name is "." or ".." */ |
224 | } else if (S_ISDIR(ts.st_mode)) { | 205 | /* if path was ".", drop "./" prefix: */ |
225 | retval |= sysctl_display_all(tmpdir); | 206 | retval |= sysctl_display_all((next[0] == '.' && next[1] == '/') ? |
226 | } else { | 207 | next + 2 : next); |
227 | retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS); | 208 | free(next); |
228 | } | 209 | } |
229 | free(tmpdir); | 210 | closedir(dirp); |
230 | } /* end while */ | 211 | } else { |
231 | closedir(dp); | 212 | char *name = xstrdup(path); |
213 | retval |= sysctl_act_on_setting(name); | ||
214 | free(name); | ||
215 | } | ||
232 | 216 | ||
233 | return retval; | 217 | return retval; |
234 | } /* end sysctl_display_all() */ | 218 | } |
235 | 219 | ||
236 | static void sysctl_dots_to_slashes(char *name) | 220 | static void sysctl_dots_to_slashes(char *name) |
237 | { | 221 | { |
238 | char *cptr, *last_good, *end; | 222 | char *cptr, *last_good, *end; |
239 | 223 | ||
240 | /* It can be good as-is! */ | 224 | /* Convert minimum number of '.' to '/' so that |
241 | if (access(name, F_OK) == 0) | 225 | * we end up with existing file's name. |
242 | return; | 226 | * |
243 | 227 | * Example from bug 3894: | |
244 | /* Example from bug 3894: | ||
245 | * net.ipv4.conf.eth0.100.mc_forwarding -> | 228 | * net.ipv4.conf.eth0.100.mc_forwarding -> |
246 | * net/ipv4/conf/eth0.100/mc_forwarding. NB: | 229 | * net/ipv4/conf/eth0.100/mc_forwarding |
247 | * net/ipv4/conf/eth0/mc_forwarding *also exists*, | 230 | * NB: net/ipv4/conf/eth0/mc_forwarding *also exists*, |
248 | * therefore we must start from the end, and if | 231 | * therefore we must start from the end, and if |
249 | * we replaced even one . -> /, start over again, | 232 | * we replaced even one . -> /, start over again, |
250 | * but never replace dots before the position | 233 | * but never replace dots before the position |
251 | * where replacement occurred. */ | 234 | * where last replacement occurred. |
252 | end = name + strlen(name) - 1; | 235 | */ |
236 | end = name + strlen(name); | ||
253 | last_good = name - 1; | 237 | last_good = name - 1; |
238 | *end = '.'; /* trick the loop in trying full name too */ | ||
239 | |||
254 | again: | 240 | again: |
255 | cptr = end; | 241 | cptr = end; |
256 | while (cptr > last_good) { | 242 | while (cptr > last_good) { |
257 | if (*cptr == '.') { | 243 | if (*cptr == '.') { |
258 | *cptr = '\0'; | 244 | *cptr = '\0'; |
245 | //bb_error_msg("trying:'%s'", name); | ||
259 | if (access(name, F_OK) == 0) { | 246 | if (access(name, F_OK) == 0) { |
260 | *cptr = '/'; | 247 | *cptr = '/'; |
248 | *end = '\0'; /* prevent trailing '/' */ | ||
249 | //bb_error_msg("replaced:'%s'", name); | ||
261 | last_good = cptr; | 250 | last_good = cptr; |
262 | goto again; | 251 | goto again; |
263 | } | 252 | } |
@@ -265,4 +254,5 @@ static void sysctl_dots_to_slashes(char *name) | |||
265 | } | 254 | } |
266 | cptr--; | 255 | cptr--; |
267 | } | 256 | } |
268 | } /* end sysctl_dots_to_slashes() */ | 257 | *end = '\0'; |
258 | } | ||