1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43#include "libbb.h"
44
45enum {
46 FLAG_SHOW_KEYS = 1 << 0,
47 FLAG_SHOW_KEY_ERRORS = 1 << 1,
48 FLAG_TABLE_FORMAT = 1 << 2,
49 FLAG_SHOW_ALL = 1 << 3,
50 FLAG_PRELOAD_FILE = 1 << 4,
51
52 FLAG_WRITE = 1 << 5,
53 FLAG_QUIET = 1 << 6,
54};
55#define OPTION_STR "neAapwq"
56
57static void sysctl_dots_to_slashes(char *name)
58{
59 char *cptr, *last_good, *end;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 end = name + strlen(name);
80 last_good = name - 1;
81 *end = '.';
82
83 again:
84 cptr = end;
85 while (cptr > last_good) {
86 if (*cptr == '.') {
87 *cptr = '\0';
88
89 if (access(name, F_OK) == 0) {
90 *cptr = '/';
91
92 last_good = cptr;
93 goto again;
94 }
95 *cptr = '.';
96 }
97 cptr--;
98 }
99 *end = '\0';
100}
101
102static int sysctl_act_on_setting(char *setting)
103{
104 int fd, retval = EXIT_SUCCESS;
105 char *cptr, *outname;
106 char *value = value;
107 bool writing = (option_mask32 & FLAG_WRITE);
108
109 outname = xstrdup(setting);
110
111 cptr = outname;
112 while (*cptr) {
113 if (*cptr == '/')
114 *cptr = '.';
115 cptr++;
116 }
117
118 cptr = strchr(setting, '=');
119 if (cptr)
120 writing = 1;
121 if (writing) {
122 if (cptr == NULL) {
123 bb_error_msg("error: '%s' must be of the form name=value",
124 outname);
125 retval = EXIT_FAILURE;
126 goto end;
127 }
128 value = cptr + 1;
129 if (setting == cptr || !*value) {
130 bb_error_msg("error: malformed setting '%s'", outname);
131 retval = EXIT_FAILURE;
132 goto end;
133 }
134 *cptr = '\0';
135 outname[cptr - setting] = '\0';
136
137 fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666);
138 } else {
139 fd = open(setting, O_RDONLY);
140 }
141
142 if (fd < 0) {
143 switch (errno) {
144 case EACCES:
145
146 goto end;
147 case ENOENT:
148 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
149 bb_error_msg("error: '%s' is an unknown key", outname);
150 break;
151 default:
152 bb_perror_msg("error %sing key '%s'",
153 writing ?
154 "sett" : "read",
155 outname);
156 break;
157 }
158 retval = EXIT_FAILURE;
159 goto end;
160 }
161
162 if (writing) {
163
164 xwrite_str(fd, value);
165 close(fd);
166 if (!(option_mask32 & FLAG_QUIET)) {
167 if (option_mask32 & FLAG_SHOW_KEYS)
168 printf("%s = ", outname);
169 puts(value);
170 }
171 } else {
172 char c;
173
174 value = cptr = xmalloc_read(fd, NULL);
175 close(fd);
176 if (value == NULL) {
177 bb_perror_msg("error reading key '%s'", outname);
178 goto end;
179 }
180
181
182
183
184 while ((c = *cptr) != '\0') {
185 if (option_mask32 & FLAG_SHOW_KEYS)
186 printf("%s = ", outname);
187 while (1) {
188 fputc(c, stdout);
189 cptr++;
190 if (c == '\n')
191 break;
192 c = *cptr;
193 if (c == '\0')
194 break;
195 }
196 }
197 free(value);
198 }
199 end:
200 free(outname);
201 return retval;
202}
203
204static int sysctl_act_recursive(const char *path)
205{
206 DIR *dirp;
207 struct stat buf;
208 struct dirent *entry;
209 char *next;
210 int retval = 0;
211
212 stat(path, &buf);
213 if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) {
214 dirp = opendir(path);
215 if (dirp == NULL)
216 return -1;
217 while ((entry = readdir(dirp)) != NULL) {
218 next = concat_subpath_file(path, entry->d_name);
219 if (next == NULL)
220 continue;
221
222 retval |= sysctl_act_recursive((next[0] == '.' && next[1] == '/') ?
223 next + 2 : next);
224 free(next);
225 }
226 closedir(dirp);
227 } else {
228 char *name = xstrdup(path);
229 retval |= sysctl_act_on_setting(name);
230 free(name);
231 }
232
233 return retval;
234}
235
236
237
238
239
240static int sysctl_handle_preload_file(const char *filename)
241{
242 char *token[2];
243 parser_t *parser;
244 int parse_flags;
245
246 parser = config_open(filename);
247
248 xchdir("/proc/sys");
249
250 parse_flags = 0;
251 parse_flags &= ~PARSE_COLLAPSE;
252 parse_flags &= ~PARSE_TRIM;
253 parse_flags |= PARSE_GREEDY;
254 parse_flags &= ~PARSE_MIN_DIE;
255 parse_flags &= ~PARSE_EOL_COMMENTS;
256 parse_flags |= PARSE_ALT_COMMENTS;
257
258 parse_flags |= PARSE_WS_COMMENTS;
259 while (config_read(parser, token, 2, 2, ";#=", parse_flags)) {
260 char *tp;
261
262 trim(token[1]);
263 tp = trim(token[0]);
264 sysctl_dots_to_slashes(token[0]);
265
266
267 *tp++ = '=';
268 overlapping_strcpy(tp, token[1]);
269
270 sysctl_act_on_setting(token[0]);
271 }
272 if (ENABLE_FEATURE_CLEAN_UP)
273 config_close(parser);
274 return 0;
275}
276
277int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
278int sysctl_main(int argc UNUSED_PARAM, char **argv)
279{
280 int retval;
281 int opt;
282
283 opt = getopt32(argv, "+" OPTION_STR);
284 argv += optind;
285 opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
286 option_mask32 = opt;
287
288 if (opt & FLAG_PRELOAD_FILE) {
289 int cur_dir_fd;
290 option_mask32 |= FLAG_WRITE;
291 if (!*argv)
292 *--argv = (char*)"/etc/sysctl.conf";
293 cur_dir_fd = xopen(".", O_RDONLY | O_DIRECTORY);
294 do {
295
296 sysctl_handle_preload_file(*argv);
297 xfchdir(cur_dir_fd);
298 } while (*++argv);
299 return 0;
300 }
301 xchdir("/proc/sys");
302 if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) {
303 return sysctl_act_recursive(".");
304 }
305
306 retval = 0;
307 while (*argv) {
308 sysctl_dots_to_slashes(*argv);
309 retval |= sysctl_act_recursive(*argv);
310 argv++;
311 }
312
313 return retval;
314}
315