aboutsummaryrefslogtreecommitdiff
path: root/procps
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-12-24 03:11:43 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-12-24 03:11:43 +0000
commit54d10059c93cdbacd709147c1bda45c2039059eb (patch)
tree20ea089eafe51723d93f28ed5b5ab7089bc2c9ff /procps
parent4e12b1a2a9e68685dff61acaee1e1f6c377d978c (diff)
downloadbusybox-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.c272
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
19static int sysctl_read_setting(const char *setting); 19static int sysctl_act_on_setting(char *setting);
20static int sysctl_write_setting(const char *setting);
21static int sysctl_display_all(const char *path); 20static int sysctl_display_all(const char *path);
22static int sysctl_preload_file_and_exit(const char *filename); 21static int sysctl_handle_preload_file(const char *filename);
23static void sysctl_dots_to_slashes(char *name); 22static void sysctl_dots_to_slashes(char *name);
24 23
25static const char ETC_SYSCTL_CONF[] ALIGN1 = "/etc/sysctl.conf";
26static const char PROC_SYS[] ALIGN1 = "/proc/sys/";
27enum { strlen_PROC_SYS = sizeof(PROC_SYS) - 1 };
28
29static const char msg_unknown_key[] ALIGN1 =
30 "error: '%s' is an unknown key";
31
32static void dwrite_str(int fd, const char *buf) 24static 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 */
78static int sysctl_preload_file_and_exit(const char *filename) 74static 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
100static int sysctl_write_setting(const char *setting) 97static 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
157static 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
206static int sysctl_display_all(const char *path) 187static 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
236static void sysctl_dots_to_slashes(char *name) 220static 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}