libbb: [x]fopen_for_{read,write} introduced and used.
[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         if (!parser)
99                 return 1;
100
101         while (config_read(parser, token, 2, 2, "# \t=", PARSE_LAST_IS_GREEDY)) { // TODO: ';' is comment char too
102 //              if (!token[1]) {
103 //                      bb_error_msg(WARN_BAD_LINE, filename, parser->lineno);
104 //              } else {
105                 {
106 #if 0
107                         char *s = xasprintf("%s=%s", token[0], token[1]);
108                         sysctl_write_setting(s);
109                         free(s);
110 #else // PLAY_WITH_FIRE for -4 bytes?
111                         sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition
112                         sysctl_write_setting(parser->line);
113 #endif
114                 }
115         }
116         if (ENABLE_FEATURE_CLEAN_UP)
117                 config_close(parser);
118         return 0;
119 } /* end sysctl_preload_file_and_exit() */
120
121 /*
122  *     Write a single sysctl setting
123  */
124 static int sysctl_write_setting(const char *setting)
125 {
126         int retval;
127         const char *name;
128         const char *value;
129         const char *equals;
130         char *tmpname, *outname, *cptr;
131         int fd;
132
133         name = setting;
134         equals = strchr(setting, '=');
135         if (!equals) {
136                 bb_error_msg(ERR_NO_EQUALS, setting);
137                 return EXIT_FAILURE;
138         }
139
140         value = equals + 1;     /* point to the value in name=value */
141         if (name == equals || !*value) {
142                 bb_error_msg(ERR_MALFORMED_SETTING, setting);
143                 return EXIT_FAILURE;
144         }
145
146         tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name);
147         outname = xstrdup(tmpname + strlen_PROC_SYS);
148
149         while ((cptr = strchr(tmpname, '.')) != NULL)
150                 *cptr = '/';
151
152         while ((cptr = strchr(outname, '/')) != NULL)
153                 *cptr = '.';
154
155         fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
156         if (fd < 0) {
157                 switch (errno) {
158                 case ENOENT:
159                         if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
160                                 bb_error_msg(ERR_INVALID_KEY, outname);
161                         break;
162                 case EACCES:
163                         bb_perror_msg(ERR_PERMISSION_DENIED, outname);
164                         break;
165                 default:
166                         bb_perror_msg(ERR_UNKNOWN_WRITING, outname);
167                         break;
168                 }
169                 retval = EXIT_FAILURE;
170         } else {
171                 dwrite_str(fd, value);
172                 close(fd);
173                 if (option_mask32 & FLAG_SHOW_KEYS) {
174                         printf("%s = ", outname);
175                 }
176                 puts(value);
177                 retval = EXIT_SUCCESS;
178         }
179
180         free(tmpname);
181         free(outname);
182         return retval;
183 } /* end sysctl_write_setting() */
184
185 /*
186  *     Read a sysctl setting
187  */
188 static int sysctl_read_setting(const char *name)
189 {
190         int retval;
191         char *tmpname, *outname, *cptr;
192         char inbuf[1025];
193         FILE *fp;
194
195         if (!*name) {
196                 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
197                         bb_error_msg(ERR_INVALID_KEY, name);
198                 return -1;
199         }
200
201         tmpname = concat_path_file(PROC_SYS, name);
202         outname = xstrdup(tmpname + strlen_PROC_SYS);
203
204         while ((cptr = strchr(tmpname, '.')) != NULL)
205                 *cptr = '/';
206         while ((cptr = strchr(outname, '/')) != NULL)
207                 *cptr = '.';
208
209         fp = fopen_for_read(tmpname);
210         if (fp == NULL) {
211                 switch (errno) {
212                 case ENOENT:
213                         if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
214                                 bb_error_msg(ERR_INVALID_KEY, outname);
215                         break;
216                 case EACCES:
217                         bb_error_msg(ERR_PERMISSION_DENIED, outname);
218                         break;
219                 default:
220                         bb_perror_msg(ERR_UNKNOWN_READING, outname);
221                         break;
222                 }
223                 retval = EXIT_FAILURE;
224         } else {
225                 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
226                         if (option_mask32 & FLAG_SHOW_KEYS) {
227                                 printf("%s = ", outname);
228                         }
229                         fputs(inbuf, stdout);
230                 }
231                 fclose(fp);
232                 retval = EXIT_SUCCESS;
233         }
234
235         free(tmpname);
236         free(outname);
237         return retval;
238 } /* end sysctl_read_setting() */
239
240 /*
241  *     Display all the sysctl settings
242  */
243 static int sysctl_display_all(const char *path)
244 {
245         int retval = 0;
246         DIR *dp;
247         struct dirent *de;
248         char *tmpdir;
249         struct stat ts;
250
251         dp = opendir(path);
252         if (!dp) {
253                 return EXIT_FAILURE;
254         }
255         while ((de = readdir(dp)) != NULL) {
256                 tmpdir = concat_subpath_file(path, de->d_name);
257                 if (tmpdir == NULL)
258                         continue; /* . or .. */
259                 if (stat(tmpdir, &ts) != 0) {
260                         bb_perror_msg(tmpdir);
261                 } else if (S_ISDIR(ts.st_mode)) {
262                         retval |= sysctl_display_all(tmpdir);
263                 } else {
264                         retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS);
265                 }
266                 free(tmpdir);
267         } /* end while */
268         closedir(dp);
269
270         return retval;
271 } /* end sysctl_display_all() */