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