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