testcase for the last fix
[oweals/busybox.git] / procps / sysctl.c
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 tarball for details.
8  *
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  *
15  */
16
17 #include "libbb.h"
18
19 static int sysctl_read_setting(const char *setting);
20 static int sysctl_write_setting(const char *setting);
21 static int sysctl_display_all(const char *path);
22 static int sysctl_preload_file_and_exit(const char *filename);
23
24 static const char ETC_SYSCTL_CONF[] ALIGN1 = "/etc/sysctl.conf";
25 static const char PROC_SYS[] ALIGN1 = "/proc/sys/";
26 enum { strlen_PROC_SYS = sizeof(PROC_SYS) - 1 };
27
28 /* error messages */
29 static const char ERR_MALFORMED_SETTING[] ALIGN1 =
30         "error: malformed setting '%s'";
31 static const char ERR_NO_EQUALS[] ALIGN1 =
32         "error: '%s' must be of the form name=value";
33 static const char ERR_INVALID_KEY[] ALIGN1 =
34         "error: '%s' is an unknown key";
35 static const char ERR_UNKNOWN_WRITING[] ALIGN1 =
36         "error setting key '%s'";
37 static const char ERR_UNKNOWN_READING[] ALIGN1 =
38         "error reading key '%s'";
39 static const char ERR_PERMISSION_DENIED[] ALIGN1 =
40         "error: permission denied on key '%s'";
41 static const char WARN_BAD_LINE[] ALIGN1 =
42         "warning: %s(%d): invalid syntax, continuing";
43
44
45 static void dwrite_str(int fd, const char *buf)
46 {
47         write(fd, buf, strlen(buf));
48 }
49
50 enum {
51         FLAG_SHOW_KEYS       = 1 << 0,
52         FLAG_SHOW_KEY_ERRORS = 1 << 1,
53         FLAG_TABLE_FORMAT    = 1 << 2, /* not implemented */
54         FLAG_SHOW_ALL        = 1 << 3,
55         FLAG_PRELOAD_FILE    = 1 << 4,
56         FLAG_WRITE           = 1 << 5,
57 };
58
59 int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
60 int sysctl_main(int argc UNUSED_PARAM, char **argv)
61 {
62         int retval;
63         int opt;
64
65         opt = getopt32(argv, "+neAapw"); /* '+' - stop on first non-option */
66         argv += optind;
67         opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
68         option_mask32 ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
69
70         if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL))
71                 return sysctl_display_all(PROC_SYS);
72         if (opt & FLAG_PRELOAD_FILE)
73                 return sysctl_preload_file_and_exit(*argv ? *argv : ETC_SYSCTL_CONF);
74
75         retval = 0;
76         while (*argv) {
77                 if (opt & FLAG_WRITE)
78                         retval |= sysctl_write_setting(*argv);
79                 else
80                         retval |= sysctl_read_setting(*argv);
81                 argv++;
82         }
83
84         return retval;
85 } /* end sysctl_main() */
86
87 /*
88  * preload the sysctl's from a conf file
89  * - we parse the file and then reform it (strip out whitespace)
90  */
91
92 static int sysctl_preload_file_and_exit(const char *filename)
93 {
94         char *token[2];
95         parser_t *parser;
96
97         parser = config_open(filename);
98         while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { // TODO: ';' is comment char too
99 //              if (!token[1]) {
100 //                      bb_error_msg(WARN_BAD_LINE, filename, parser->lineno);
101 //              } else {
102                 {
103 #if 0
104                         char *s = xasprintf("%s=%s", token[0], token[1]);
105                         sysctl_write_setting(s);
106                         free(s);
107 #else // PLAY_WITH_FIRE for -4 bytes?
108                         sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition
109                         sysctl_write_setting(parser->line);
110 #endif
111                 }
112         }
113         if (ENABLE_FEATURE_CLEAN_UP)
114                 config_close(parser);
115         return 0;
116 } /* end sysctl_preload_file_and_exit() */
117
118 /*
119  *     Write a single sysctl setting
120  */
121 static int sysctl_write_setting(const char *setting)
122 {
123         int retval;
124         const char *name;
125         const char *value;
126         const char *equals;
127         char *tmpname, *outname, *cptr;
128         int fd;
129
130         name = setting;
131         equals = strchr(setting, '=');
132         if (!equals) {
133                 bb_error_msg(ERR_NO_EQUALS, setting);
134                 return EXIT_FAILURE;
135         }
136
137         value = equals + 1;     /* point to the value in name=value */
138         if (name == equals || !*value) {
139                 bb_error_msg(ERR_MALFORMED_SETTING, setting);
140                 return EXIT_FAILURE;
141         }
142
143         tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name);
144         outname = xstrdup(tmpname + strlen_PROC_SYS);
145
146         while ((cptr = strchr(tmpname, '.')) != NULL)
147                 *cptr = '/';
148
149         while ((cptr = strchr(outname, '/')) != NULL)
150                 *cptr = '.';
151
152         fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
153         if (fd < 0) {
154                 switch (errno) {
155                 case ENOENT:
156                         if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
157                                 bb_error_msg(ERR_INVALID_KEY, outname);
158                         break;
159                 case EACCES:
160                         bb_perror_msg(ERR_PERMISSION_DENIED, outname);
161                         break;
162                 default:
163                         bb_perror_msg(ERR_UNKNOWN_WRITING, outname);
164                         break;
165                 }
166                 retval = EXIT_FAILURE;
167         } else {
168                 dwrite_str(fd, value);
169                 close(fd);
170                 if (option_mask32 & FLAG_SHOW_KEYS) {
171                         printf("%s = ", outname);
172                 }
173                 puts(value);
174                 retval = EXIT_SUCCESS;
175         }
176
177         free(tmpname);
178         free(outname);
179         return retval;
180 } /* end sysctl_write_setting() */
181
182 /*
183  *     Read a sysctl setting
184  */
185 static int sysctl_read_setting(const char *name)
186 {
187         int retval;
188         char *tmpname, *outname, *cptr;
189         char inbuf[1025];
190         FILE *fp;
191
192         if (!*name) {
193                 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
194                         bb_error_msg(ERR_INVALID_KEY, name);
195                 return -1;
196         }
197
198         tmpname = concat_path_file(PROC_SYS, name);
199         outname = xstrdup(tmpname + strlen_PROC_SYS);
200
201         while ((cptr = strchr(tmpname, '.')) != NULL)
202                 *cptr = '/';
203         while ((cptr = strchr(outname, '/')) != NULL)
204                 *cptr = '.';
205
206         fp = fopen_for_read(tmpname);
207         if (fp == NULL) {
208                 switch (errno) {
209                 case ENOENT:
210                         if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
211                                 bb_error_msg(ERR_INVALID_KEY, outname);
212                         break;
213                 case EACCES:
214                         bb_error_msg(ERR_PERMISSION_DENIED, outname);
215                         break;
216                 default:
217                         bb_perror_msg(ERR_UNKNOWN_READING, outname);
218                         break;
219                 }
220                 retval = EXIT_FAILURE;
221         } else {
222                 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
223                         if (option_mask32 & FLAG_SHOW_KEYS) {
224                                 printf("%s = ", outname);
225                         }
226                         fputs(inbuf, stdout);
227                 }
228                 fclose(fp);
229                 retval = EXIT_SUCCESS;
230         }
231
232         free(tmpname);
233         free(outname);
234         return retval;
235 } /* end sysctl_read_setting() */
236
237 /*
238  *     Display all the sysctl settings
239  */
240 static int sysctl_display_all(const char *path)
241 {
242         int retval = 0;
243         DIR *dp;
244         struct dirent *de;
245         char *tmpdir;
246         struct stat ts;
247
248         dp = opendir(path);
249         if (!dp) {
250                 return EXIT_FAILURE;
251         }
252         while ((de = readdir(dp)) != NULL) {
253                 tmpdir = concat_subpath_file(path, de->d_name);
254                 if (tmpdir == NULL)
255                         continue; /* . or .. */
256                 if (stat(tmpdir, &ts) != 0) {
257                         bb_perror_msg(tmpdir);
258                 } else if (S_ISDIR(ts.st_mode)) {
259                         retval |= sysctl_display_all(tmpdir);
260                 } else {
261                         retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS);
262                 }
263                 free(tmpdir);
264         } /* end while */
265         closedir(dp);
266
267         return retval;
268 } /* end sysctl_display_all() */