5100f57e7d8da807a5b80909eec7a6bf0d88dcc6
[platform/upstream/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 /*
20  *    Function Prototypes
21  */
22 static int sysctl_read_setting(const char *setting, int output);
23 static int sysctl_write_setting(const char *setting, int output);
24 static int sysctl_preload_file(const char *filename, int output);
25 static int sysctl_display_all(const char *path, int output, int show_table);
26
27 /*
28  *    Globals...
29  */
30 static const char PROC_PATH[] ALIGN1 = "/proc/sys/";
31 static const char DEFAULT_PRELOAD[] ALIGN1 = "/etc/sysctl.conf";
32
33 /* error messages */
34 static const char ERR_UNKNOWN_PARAMETER[] ALIGN1 =
35         "error: Unknown parameter '%s'\n";
36 static const char ERR_MALFORMED_SETTING[] ALIGN1 =
37         "error: Malformed setting '%s'\n";
38 static const char ERR_NO_EQUALS[] ALIGN1 =
39         "error: '%s' must be of the form name=value\n";
40 static const char ERR_INVALID_KEY[] ALIGN1 =
41         "error: '%s' is an unknown key\n";
42 static const char ERR_UNKNOWN_WRITING[] ALIGN1 =
43         "error: unknown error %d setting key '%s'\n";
44 static const char ERR_UNKNOWN_READING[] ALIGN1 =
45         "error: unknown error %d reading key '%s'\n";
46 static const char ERR_PERMISSION_DENIED[] ALIGN1 =
47         "error: permission denied on key '%s'\n";
48 static const char ERR_PRELOAD_FILE[] ALIGN1 =
49         "error: cannot open preload file '%s'\n";
50 static const char WARN_BAD_LINE[] ALIGN1 =
51         "warning: %s(%d): invalid syntax, continuing...\n";
52
53
54 static void dwrite_str(int fd, const char *buf)
55 {
56         write(fd, buf, strlen(buf));
57 }
58
59 /*
60  *    sysctl_main()...
61  */
62 int sysctl_main(int argc, char **argv);
63 int sysctl_main(int argc, char **argv)
64 {
65         int retval = 0;
66         int output = 1;
67         int write_mode = 0;
68         int switches_allowed = 1;
69
70         if (argc < 2)
71                 bb_show_usage();
72
73         argv++;
74
75         for (; argv && *argv && **argv; argv++) {
76                 if (switches_allowed && **argv == '-') {        /* we have a switch */
77                         switch ((*argv)[1]) {
78                         case 'n':
79                                 output = 0;
80                                 break;
81                         case 'w':
82                                 write_mode = 1;
83                                 switches_allowed = 0;
84                                 break;
85                         case 'p':
86                                 argv++;
87                                 return
88                                         sysctl_preload_file(((argv && *argv
89                                                                                   && **argv) ? *argv :
90                                                                                  DEFAULT_PRELOAD), output);
91                         case 'a':
92                         case 'A':
93                                 switches_allowed = 0;
94                                 return sysctl_display_all(PROC_PATH, output,
95                                                                                   ((*argv)[1] == 'a') ? 0 : 1);
96                         case 'h':
97                         case '?':
98                                 bb_show_usage();
99                         default:
100                                 bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv);
101                                 bb_show_usage();
102                         }
103                 } else {
104                         switches_allowed = 0;
105                         if (write_mode)
106                                 retval = sysctl_write_setting(*argv, output);
107                         else
108                                 sysctl_read_setting(*argv, output);
109                 }
110         }
111         return retval;
112 }                                               /* end sysctl_main() */
113
114
115
116 /*
117  *     sysctl_preload_file
118  *      preload the sysctl's from a conf file
119  *           - we parse the file and then reform it (strip out whitespace)
120  */
121 #define PRELOAD_BUF 256
122
123 int sysctl_preload_file(const char *filename, int output)
124 {
125         int lineno = 0;
126         char oneline[PRELOAD_BUF];
127         char buffer[PRELOAD_BUF];
128         char *name, *value, *ptr;
129         FILE *fp = NULL;
130
131         if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
132                 bb_error_msg_and_die(ERR_PRELOAD_FILE, filename);
133         }
134
135         while (fgets(oneline, sizeof(oneline) - 1, fp)) {
136                 oneline[sizeof(oneline) - 1] = '\0';
137                 lineno++;
138                 trim(oneline);
139                 ptr = (char *) oneline;
140
141                 if (*ptr == '#' || *ptr == ';')
142                         continue;
143
144                 if (strlen(ptr) < 2)
145                         continue;
146
147                 name = strtok(ptr, "=");
148                 if (!name || !*name) {
149                         bb_error_msg(WARN_BAD_LINE, filename, lineno);
150                         continue;
151                 }
152
153                 trim(name);
154
155                 value = strtok(NULL, "\n\r");
156                 if (!value || !*value) {
157                         bb_error_msg(WARN_BAD_LINE, filename, lineno);
158                         continue;
159                 }
160
161                 while ((*value == ' ' || *value == '\t') && *value != 0)
162                         value++;
163                 /* safe because sizeof(oneline) == sizeof(buffer) */
164                 sprintf(buffer, "%s=%s", name, value);
165                 sysctl_write_setting(buffer, output);
166         }
167         fclose(fp);
168         return 0;
169 }                                               /* end sysctl_preload_file() */
170
171
172 /*
173  *     Write a single sysctl setting
174  */
175 int sysctl_write_setting(const char *setting, int output)
176 {
177         int retval = 0;
178         const char *name = setting;
179         const char *value;
180         const char *equals;
181         char *tmpname, *outname, *cptr;
182         int fd = -1;
183
184         if (!name)                      /* probably dont' want to display this  err */
185                 return 0;
186
187         if (!(equals = strchr(setting, '='))) {
188                 bb_error_msg(ERR_NO_EQUALS, setting);
189                 return -1;
190         }
191
192         value = equals + sizeof(char);  /* point to the value in name=value */
193
194         if (!*name || !*value || name == equals) {
195                 bb_error_msg(ERR_MALFORMED_SETTING, setting);
196                 return -2;
197         }
198
199         tmpname = xasprintf("%s%.*s", PROC_PATH, (int)(equals - name), name);
200         outname = xstrdup(tmpname + strlen(PROC_PATH));
201
202         while ((cptr = strchr(tmpname, '.')) != NULL)
203                 *cptr = '/';
204
205         while ((cptr = strchr(outname, '/')) != NULL)
206                 *cptr = '.';
207
208         fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
209         if (fd < 0) {
210                 switch (errno) {
211                 case ENOENT:
212                         bb_error_msg(ERR_INVALID_KEY, outname);
213                         break;
214                 case EACCES:
215                         bb_perror_msg(ERR_PERMISSION_DENIED, outname);
216                         break;
217                 default:
218                         bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
219                         break;
220                 }
221                 retval = -1;
222         } else {
223                 dwrite_str(fd, value);
224                 close(fd);
225                 if (output) {
226                         dwrite_str(STDOUT_FILENO, outname);
227                         dwrite_str(STDOUT_FILENO, " = ");
228                 }
229                 dwrite_str(STDOUT_FILENO, value);
230                 dwrite_str(STDOUT_FILENO, "\n");
231         }
232
233         /* cleanup */
234         free(tmpname);
235         free(outname);
236         return retval;
237 }                                               /* end sysctl_write_setting() */
238
239
240 /*
241  *     Read a sysctl setting
242  *
243  */
244 int sysctl_read_setting(const char *setting, int output)
245 {
246         int retval = 0;
247         char *tmpname, *outname, *cptr;
248         char inbuf[1025];
249         const char *name = setting;
250         FILE *fp;
251
252         if (!setting || !*setting)
253                 bb_error_msg(ERR_INVALID_KEY, setting);
254
255         tmpname = concat_path_file(PROC_PATH, name);
256         outname = xstrdup(tmpname + strlen(PROC_PATH));
257
258         while ((cptr = strchr(tmpname, '.')) != NULL)
259                 *cptr = '/';
260         while ((cptr = strchr(outname, '/')) != NULL)
261                 *cptr = '.';
262
263         if ((fp = fopen(tmpname, "r")) == NULL) {
264                 switch (errno) {
265                 case ENOENT:
266                         bb_error_msg(ERR_INVALID_KEY, outname);
267                         break;
268                 case EACCES:
269                         bb_error_msg(ERR_PERMISSION_DENIED, outname);
270                         break;
271                 default:
272                         bb_error_msg(ERR_UNKNOWN_READING, errno, outname);
273                         break;
274                 }
275                 retval = -1;
276         } else {
277                 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
278                         if (output) {
279                                 dwrite_str(STDOUT_FILENO, outname);
280                                 dwrite_str(STDOUT_FILENO, " = ");
281                         }
282                         dwrite_str(STDOUT_FILENO, inbuf);
283                 }
284                 fclose(fp);
285         }
286
287         free(tmpname);
288         free(outname);
289         return retval;
290 }                                               /* end sysctl_read_setting() */
291
292
293
294 /*
295  *     Display all the sysctl settings
296  *
297  */
298 int sysctl_display_all(const char *path, int output, int show_table)
299 {
300         int retval = 0;
301         int retval2;
302         DIR *dp;
303         struct dirent *de;
304         char *tmpdir;
305         struct stat ts;
306
307         dp = opendir(path);
308         if (!dp) {
309                 retval = -1;
310         } else {
311                 while ((de = readdir(dp)) != NULL) {
312                         tmpdir = concat_subpath_file(path, de->d_name);
313                         if (tmpdir == NULL)
314                                 continue;
315                         retval2 = stat(tmpdir, &ts);
316                         if (retval2 != 0)
317                                 bb_perror_msg(tmpdir);
318                         else {
319                                 if (S_ISDIR(ts.st_mode)) {
320                                         sysctl_display_all(tmpdir, output, show_table);
321                                 } else
322                                         retval |=
323                                                 sysctl_read_setting(tmpdir + strlen(PROC_PATH),
324                                                                                         output);
325
326                         }
327                         free(tmpdir);
328                 }                               /* end while */
329                 closedir(dp);
330         }
331
332         return retval;
333 }                                               /* end sysctl_display_all() */