source: MondoRescue/branches/3.3/mindi-busybox/procps/sysctl.c@ 3837

Last change on this file since 3837 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 7.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
4 *
5 * Copyright 1999 George Staikos
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 *
9 * Changelog:
10 * v1.01 - added -p <preload> to preload values from a file
11 * v1.01.1 - busybox applet aware by <solar@gentoo.org>
12 */
13
14//usage:#define sysctl_trivial_usage
15//usage: "[OPTIONS] [KEY[=VALUE]]..."
16//usage:#define sysctl_full_usage "\n\n"
17//usage: "Show/set kernel parameters\n"
18//usage: "\n -e Don't warn about unknown keys"
19//usage: "\n -n Don't show key names"
20//usage: "\n -a Show all values"
21/* Same as -a, no need to show it */
22/* //usage: "\n -A Show all values in table form" */
23//usage: "\n -w Set values"
24//usage: "\n -p FILE Set values from FILE (default /etc/sysctl.conf)"
25//usage: "\n -q Set values silently"
26//usage:
27//usage:#define sysctl_example_usage
28//usage: "sysctl [-n] [-e] variable...\n"
29//usage: "sysctl [-n] [-e] [-q] -w variable=value...\n"
30//usage: "sysctl [-n] [-e] -a\n"
31//usage: "sysctl [-n] [-e] [-q] -p file (default /etc/sysctl.conf)\n"
32//usage: "sysctl [-n] [-e] -A\n"
33
34#include "libbb.h"
35
36enum {
37 FLAG_SHOW_KEYS = 1 << 0,
38 FLAG_SHOW_KEY_ERRORS = 1 << 1,
39 FLAG_TABLE_FORMAT = 1 << 2, /* not implemented */
40 FLAG_SHOW_ALL = 1 << 3,
41 FLAG_PRELOAD_FILE = 1 << 4,
42/* TODO: procps 3.2.8 seems to not require -w for KEY=VAL to work: */
43 FLAG_WRITE = 1 << 5,
44 FLAG_QUIET = 1 << 6,
45};
46#define OPTION_STR "neAapwq"
47
48static void sysctl_dots_to_slashes(char *name)
49{
50 char *cptr, *last_good, *end;
51
52 /* Convert minimum number of '.' to '/' so that
53 * we end up with existing file's name.
54 *
55 * Example from bug 3894:
56 * net.ipv4.conf.eth0.100.mc_forwarding ->
57 * net/ipv4/conf/eth0.100/mc_forwarding
58 * NB: net/ipv4/conf/eth0/mc_forwarding *also exists*,
59 * therefore we must start from the end, and if
60 * we replaced even one . -> /, start over again,
61 * but never replace dots before the position
62 * where last replacement occurred.
63 *
64 * Another bug we later had is that
65 * net.ipv4.conf.eth0.100
66 * (without .mc_forwarding) was mishandled.
67 *
68 * To set up testing: modprobe 8021q; vconfig add eth0 100
69 */
70 end = name + strlen(name);
71 last_good = name - 1;
72 *end = '.'; /* trick the loop into trying full name too */
73
74 again:
75 cptr = end;
76 while (cptr > last_good) {
77 if (*cptr == '.') {
78 *cptr = '\0';
79 //bb_error_msg("trying:'%s'", name);
80 if (access(name, F_OK) == 0) {
81 *cptr = '/';
82 //bb_error_msg("replaced:'%s'", name);
83 last_good = cptr;
84 goto again;
85 }
86 *cptr = '.';
87 }
88 cptr--;
89 }
90 *end = '\0';
91}
92
93static int sysctl_act_on_setting(char *setting)
94{
95 int fd, retval = EXIT_SUCCESS;
96 char *cptr, *outname;
97 char *value = value; /* for compiler */
98
99 outname = xstrdup(setting);
100
101 cptr = outname;
102 while (*cptr) {
103 if (*cptr == '/')
104 *cptr = '.';
105 cptr++;
106 }
107
108 if (option_mask32 & FLAG_WRITE) {
109 cptr = strchr(setting, '=');
110 if (cptr == NULL) {
111 bb_error_msg("error: '%s' must be of the form name=value",
112 outname);
113 retval = EXIT_FAILURE;
114 goto end;
115 }
116 value = cptr + 1; /* point to the value in name=value */
117 if (setting == cptr || !*value) {
118 bb_error_msg("error: malformed setting '%s'", outname);
119 retval = EXIT_FAILURE;
120 goto end;
121 }
122 *cptr = '\0';
123 outname[cptr - setting] = '\0';
124 /* procps 3.2.7 actually uses these flags */
125 fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666);
126 } else {
127 fd = open(setting, O_RDONLY);
128 }
129
130 if (fd < 0) {
131 switch (errno) {
132 case EACCES:
133 /* Happens for write-only settings, e.g. net.ipv6.route.flush */
134 goto end;
135 case ENOENT:
136 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
137 bb_error_msg("error: '%s' is an unknown key", outname);
138 break;
139 default:
140 bb_perror_msg("error %sing key '%s'",
141 option_mask32 & FLAG_WRITE ?
142 "sett" : "read",
143 outname);
144 break;
145 }
146 retval = EXIT_FAILURE;
147 goto end;
148 }
149
150 if (option_mask32 & FLAG_WRITE) {
151//TODO: procps 3.2.7 writes "value\n", note trailing "\n"
152 xwrite_str(fd, value);
153 close(fd);
154 if (!(option_mask32 & FLAG_QUIET)) {
155 if (option_mask32 & FLAG_SHOW_KEYS)
156 printf("%s = ", outname);
157 puts(value);
158 }
159 } else {
160 char c;
161
162 value = cptr = xmalloc_read(fd, NULL);
163 close(fd);
164 if (value == NULL) {
165 bb_perror_msg("error reading key '%s'", outname);
166 goto end;
167 }
168
169 /* dev.cdrom.info and sunrpc.transports, for example,
170 * are multi-line. Try "sysctl sunrpc.transports"
171 */
172 while ((c = *cptr) != '\0') {
173 if (option_mask32 & FLAG_SHOW_KEYS)
174 printf("%s = ", outname);
175 while (1) {
176 fputc(c, stdout);
177 cptr++;
178 if (c == '\n')
179 break;
180 c = *cptr;
181 if (c == '\0')
182 break;
183 }
184 }
185 free(value);
186 }
187 end:
188 free(outname);
189 return retval;
190}
191
192static int sysctl_act_recursive(const char *path)
193{
194 DIR *dirp;
195 struct stat buf;
196 struct dirent *entry;
197 char *next;
198 int retval = 0;
199
200 stat(path, &buf);
201 if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) {
202 dirp = opendir(path);
203 if (dirp == NULL)
204 return -1;
205 while ((entry = readdir(dirp)) != NULL) {
206 next = concat_subpath_file(path, entry->d_name);
207 if (next == NULL)
208 continue; /* d_name is "." or ".." */
209 /* if path was ".", drop "./" prefix: */
210 retval |= sysctl_act_recursive((next[0] == '.' && next[1] == '/') ?
211 next + 2 : next);
212 free(next);
213 }
214 closedir(dirp);
215 } else {
216 char *name = xstrdup(path);
217 retval |= sysctl_act_on_setting(name);
218 free(name);
219 }
220
221 return retval;
222}
223
224/* Set sysctl's from a conf file. Format example:
225 * # Controls IP packet forwarding
226 * net.ipv4.ip_forward = 0
227 */
228static int sysctl_handle_preload_file(const char *filename)
229{
230 char *token[2];
231 parser_t *parser;
232
233 parser = config_open(filename);
234 /* Must do it _after_ config_open(): */
235 xchdir("/proc/sys");
236 /* xchroot("/proc/sys") - if you are paranoid */
237
238//TODO: ';' is comment char too
239//TODO: comment may be only at line start. "var=1 #abc" - "1 #abc" is the value
240// (but _whitespace_ from ends should be trimmed first (and we do it right))
241//TODO: "var==1" is mishandled (must use "=1" as a value, but uses "1")
242// can it be fixed by removing PARSE_COLLAPSE bit?
243 while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) {
244 char *tp;
245 sysctl_dots_to_slashes(token[0]);
246 tp = xasprintf("%s=%s", token[0], token[1]);
247 sysctl_act_recursive(tp);
248 free(tp);
249 }
250 if (ENABLE_FEATURE_CLEAN_UP)
251 config_close(parser);
252 return 0;
253}
254
255int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
256int sysctl_main(int argc UNUSED_PARAM, char **argv)
257{
258 int retval;
259 int opt;
260
261 opt = getopt32(argv, "+" OPTION_STR); /* '+' - stop on first non-option */
262 argv += optind;
263 opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
264 option_mask32 = opt;
265
266 if (opt & FLAG_PRELOAD_FILE) {
267 option_mask32 |= FLAG_WRITE;
268 /* xchdir("/proc/sys") is inside */
269 return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf");
270 }
271 xchdir("/proc/sys");
272 /* xchroot("/proc/sys") - if you are paranoid */
273 if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) {
274 return sysctl_act_recursive(".");
275 }
276
277 retval = 0;
278 while (*argv) {
279 sysctl_dots_to_slashes(*argv);
280 retval |= sysctl_act_recursive(*argv);
281 argv++;
282 }
283
284 return retval;
285}
Note: See TracBrowser for help on using the repository browser.