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