blob: 90e47ea9ab7816e1b10679f4e0f7bd634479adf0 [file] [log] [blame]
Eric Andersenb9050282003-12-24 06:02:11 +00001/*
2 * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
3 *
Rob Landley8b1f11d2006-04-17 21:49:34 +00004 * Copyright 1999 George Staikos
Bernhard Reutner-Fischercb448162006-04-12 07:35:12 +00005 *
6 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersenb9050282003-12-24 06:02:11 +00007 *
8 * Changelog:
9 * v1.01:
10 * - added -p <preload> to preload values from a file
11 * v1.01.1
12 * - busybox applet aware by <solar@gentoo.org>
Eric Andersenc7bda1c2004-03-15 08:29:22 +000013 *
Eric Andersenb9050282003-12-24 06:02:11 +000014 */
15
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000016#include "libbb.h"
Eric Andersenb9050282003-12-24 06:02:11 +000017
Denis Vlasenko64309f82007-11-29 06:40:28 +000018static int sysctl_read_setting(const char *setting);
19static int sysctl_write_setting(const char *setting);
20static int sysctl_display_all(const char *path);
21static int sysctl_preload_file_and_exit(const char *filename);
Denis Vlasenko58cc52a2008-10-15 08:22:55 +000022static void sysctl_dots_to_slashes(char *name);
Eric Andersenb9050282003-12-24 06:02:11 +000023
Denis Vlasenko5a28a252007-10-29 19:22:13 +000024static const char ETC_SYSCTL_CONF[] ALIGN1 = "/etc/sysctl.conf";
25static const char PROC_SYS[] ALIGN1 = "/proc/sys/";
26enum { strlen_PROC_SYS = sizeof(PROC_SYS) - 1 };
Eric Andersenb9050282003-12-24 06:02:11 +000027
Denis Vlasenkocfdb7112008-10-15 08:29:17 +000028static const char msg_unknown_key[] ALIGN1 =
Denis Vlasenko5a28a252007-10-29 19:22:13 +000029 "error: '%s' is an unknown key";
Eric Andersenb9050282003-12-24 06:02:11 +000030
Glenn L McGrathab821542003-12-26 02:19:34 +000031static void dwrite_str(int fd, const char *buf)
32{
33 write(fd, buf, strlen(buf));
34}
35
Denis Vlasenko64309f82007-11-29 06:40:28 +000036enum {
37 FLAG_SHOW_KEYS = 1 << 0,
38 FLAG_SHOW_KEY_ERRORS = 1 << 1,
39 FLAG_TABLE_FORMAT = 1 << 2, /* not implemented */
40 FLAG_SHOW_ALL = 1 << 3,
41 FLAG_PRELOAD_FILE = 1 << 4,
42 FLAG_WRITE = 1 << 5,
43};
44
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000045int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000046int sysctl_main(int argc UNUSED_PARAM, char **argv)
Eric Andersenb9050282003-12-24 06:02:11 +000047{
Denis Vlasenko64309f82007-11-29 06:40:28 +000048 int retval;
49 int opt;
Eric Andersenb9050282003-12-24 06:02:11 +000050
Denis Vlasenko64309f82007-11-29 06:40:28 +000051 opt = getopt32(argv, "+neAapw"); /* '+' - stop on first non-option */
52 argv += optind;
53 opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
54 option_mask32 ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
Eric Andersenb9050282003-12-24 06:02:11 +000055
Denis Vlasenko64309f82007-11-29 06:40:28 +000056 if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL))
57 return sysctl_display_all(PROC_SYS);
58 if (opt & FLAG_PRELOAD_FILE)
59 return sysctl_preload_file_and_exit(*argv ? *argv : ETC_SYSCTL_CONF);
Eric Andersenb9050282003-12-24 06:02:11 +000060
Denis Vlasenko64309f82007-11-29 06:40:28 +000061 retval = 0;
62 while (*argv) {
63 if (opt & FLAG_WRITE)
64 retval |= sysctl_write_setting(*argv);
65 else
66 retval |= sysctl_read_setting(*argv);
67 argv++;
Eric Andersenb9050282003-12-24 06:02:11 +000068 }
Denis Vlasenko64309f82007-11-29 06:40:28 +000069
Eric Andersenb9050282003-12-24 06:02:11 +000070 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +000071} /* end sysctl_main() */
Eric Andersenb9050282003-12-24 06:02:11 +000072
Denis Vlasenko81944c92008-10-15 08:45:54 +000073/* Set sysctl's from a conf file. Format example:
74 * # Controls IP packet forwarding
75 * net.ipv4.ip_forward = 0
Eric Andersenb9050282003-12-24 06:02:11 +000076 */
Denis Vlasenko64309f82007-11-29 06:40:28 +000077static int sysctl_preload_file_and_exit(const char *filename)
Eric Andersenb9050282003-12-24 06:02:11 +000078{
Denis Vlasenko4a717e02008-07-20 13:01:56 +000079 char *token[2];
80 parser_t *parser;
Eric Andersenb9050282003-12-24 06:02:11 +000081
Denis Vlasenko4a717e02008-07-20 13:01:56 +000082 parser = config_open(filename);
Denis Vlasenko81944c92008-10-15 08:45:54 +000083// TODO: ';' is comment char too
84 while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) {
Denis Vlasenko4a717e02008-07-20 13:01:56 +000085#if 0
Denis Vlasenko81944c92008-10-15 08:45:54 +000086 char *s = xasprintf("%s=%s", token[0], token[1]);
87 sysctl_write_setting(s);
88 free(s);
89#else /* Save ~4 bytes by using parser internals */
90 sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition
91 sysctl_write_setting(parser->line);
Denis Vlasenko4a717e02008-07-20 13:01:56 +000092#endif
Eric Andersenb9050282003-12-24 06:02:11 +000093 }
Denis Vlasenko64309f82007-11-29 06:40:28 +000094 if (ENABLE_FEATURE_CLEAN_UP)
Denis Vlasenko4a717e02008-07-20 13:01:56 +000095 config_close(parser);
Eric Andersenb9050282003-12-24 06:02:11 +000096 return 0;
Denis Vlasenko64309f82007-11-29 06:40:28 +000097} /* end sysctl_preload_file_and_exit() */
Eric Andersenb9050282003-12-24 06:02:11 +000098
Denis Vlasenko64309f82007-11-29 06:40:28 +000099static int sysctl_write_setting(const char *setting)
Eric Andersenb9050282003-12-24 06:02:11 +0000100{
Denis Vlasenko64309f82007-11-29 06:40:28 +0000101 int retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000102 const char *name;
Eric Andersenb9050282003-12-24 06:02:11 +0000103 const char *value;
104 const char *equals;
105 char *tmpname, *outname, *cptr;
Denis Vlasenko64309f82007-11-29 06:40:28 +0000106 int fd;
Eric Andersenb9050282003-12-24 06:02:11 +0000107
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000108 name = setting;
109 equals = strchr(setting, '=');
110 if (!equals) {
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000111 bb_error_msg("error: '%s' must be of the form name=value", setting);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000112 return EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000113 }
114
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000115 value = equals + 1; /* point to the value in name=value */
116 if (name == equals || !*value) {
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000117 bb_error_msg("error: malformed setting '%s'", setting);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000118 return EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000119 }
120
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000121 tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name);
122 outname = xstrdup(tmpname + strlen_PROC_SYS);
Eric Andersenb9050282003-12-24 06:02:11 +0000123
Denis Vlasenko58cc52a2008-10-15 08:22:55 +0000124 sysctl_dots_to_slashes(tmpname);
Eric Andersenb9050282003-12-24 06:02:11 +0000125
126 while ((cptr = strchr(outname, '/')) != NULL)
127 *cptr = '.';
128
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000129 fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
130 if (fd < 0) {
Eric Andersenb9050282003-12-24 06:02:11 +0000131 switch (errno) {
132 case ENOENT:
Denis Vlasenko64309f82007-11-29 06:40:28 +0000133 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000134 bb_error_msg(msg_unknown_key, outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000135 break;
136 default:
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000137 bb_perror_msg("error setting key '%s'", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000138 break;
139 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000140 retval = EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000141 } else {
Glenn L McGrathab821542003-12-26 02:19:34 +0000142 dwrite_str(fd, value);
Eric Andersenb9050282003-12-24 06:02:11 +0000143 close(fd);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000144 if (option_mask32 & FLAG_SHOW_KEYS) {
145 printf("%s = ", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000146 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000147 puts(value);
148 retval = EXIT_SUCCESS;
Eric Andersenb9050282003-12-24 06:02:11 +0000149 }
150
Eric Andersenb9050282003-12-24 06:02:11 +0000151 free(tmpname);
152 free(outname);
153 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000154} /* end sysctl_write_setting() */
Eric Andersenb9050282003-12-24 06:02:11 +0000155
Denis Vlasenko64309f82007-11-29 06:40:28 +0000156static int sysctl_read_setting(const char *name)
Eric Andersenb9050282003-12-24 06:02:11 +0000157{
Denis Vlasenko64309f82007-11-29 06:40:28 +0000158 int retval;
Eric Andersenb9050282003-12-24 06:02:11 +0000159 char *tmpname, *outname, *cptr;
160 char inbuf[1025];
Eric Andersenb9050282003-12-24 06:02:11 +0000161 FILE *fp;
162
Denis Vlasenko64309f82007-11-29 06:40:28 +0000163 if (!*name) {
164 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000165 bb_error_msg(msg_unknown_key, name);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000166 return -1;
167 }
Eric Andersenb9050282003-12-24 06:02:11 +0000168
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000169 tmpname = concat_path_file(PROC_SYS, name);
170 outname = xstrdup(tmpname + strlen_PROC_SYS);
Eric Andersenb9050282003-12-24 06:02:11 +0000171
Denis Vlasenko58cc52a2008-10-15 08:22:55 +0000172 sysctl_dots_to_slashes(tmpname);
173
Eric Andersenb9050282003-12-24 06:02:11 +0000174 while ((cptr = strchr(outname, '/')) != NULL)
175 *cptr = '.';
176
Denis Vlasenko5415c852008-07-21 23:05:26 +0000177 fp = fopen_for_read(tmpname);
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000178 if (fp == NULL) {
Eric Andersenb9050282003-12-24 06:02:11 +0000179 switch (errno) {
180 case ENOENT:
Denis Vlasenko64309f82007-11-29 06:40:28 +0000181 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000182 bb_error_msg(msg_unknown_key, outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000183 break;
184 default:
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000185 bb_perror_msg("error reading key '%s'", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000186 break;
187 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000188 retval = EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000189 } else {
190 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000191 if (option_mask32 & FLAG_SHOW_KEYS) {
192 printf("%s = ", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000193 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000194 fputs(inbuf, stdout);
Eric Andersenb9050282003-12-24 06:02:11 +0000195 }
196 fclose(fp);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000197 retval = EXIT_SUCCESS;
Eric Andersenb9050282003-12-24 06:02:11 +0000198 }
199
200 free(tmpname);
201 free(outname);
202 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000203} /* end sysctl_read_setting() */
Eric Andersenb9050282003-12-24 06:02:11 +0000204
Denis Vlasenko64309f82007-11-29 06:40:28 +0000205static int sysctl_display_all(const char *path)
Eric Andersenb9050282003-12-24 06:02:11 +0000206{
Denis Vlasenko81944c92008-10-15 08:45:54 +0000207 int retval = EXIT_SUCCESS;
Eric Andersenb9050282003-12-24 06:02:11 +0000208 DIR *dp;
209 struct dirent *de;
210 char *tmpdir;
211 struct stat ts;
212
Denis Vlasenko51742f42007-04-12 00:32:05 +0000213 dp = opendir(path);
214 if (!dp) {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000215 return EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000216 }
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000217 while ((de = readdir(dp)) != NULL) {
218 tmpdir = concat_subpath_file(path, de->d_name);
219 if (tmpdir == NULL)
Denis Vlasenko64309f82007-11-29 06:40:28 +0000220 continue; /* . or .. */
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000221 if (stat(tmpdir, &ts) != 0) {
222 bb_perror_msg(tmpdir);
223 } else if (S_ISDIR(ts.st_mode)) {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000224 retval |= sysctl_display_all(tmpdir);
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000225 } else {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000226 retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS);
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000227 }
228 free(tmpdir);
229 } /* end while */
230 closedir(dp);
Eric Andersenb9050282003-12-24 06:02:11 +0000231
232 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000233} /* end sysctl_display_all() */
Denis Vlasenko58cc52a2008-10-15 08:22:55 +0000234
235static void sysctl_dots_to_slashes(char *name)
236{
237 char *cptr = name;
238
239 /* Example from bug 3894:
240 * net.ipv4.conf.eth0.100.mc_forwarding ->
241 * net/ipv4/conf/eth0.100/mc_forwarding */
242 while (*cptr != '\0') {
243 if (*cptr == '.') {
244 *cptr = '\0';
245 if (access(name, F_OK) == 0)
246 *cptr = '/';
247 else
248 *cptr = '.';
249 }
250 cptr++;
251 }
252} /* end sysctl_dots_to_slashes() */