blob: c095d16d066e6cd017335660211b1fbd19caca06 [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
73/*
Denis Vlasenko5a28a252007-10-29 19:22:13 +000074 * preload the sysctl's from a conf file
75 * - we parse the file and then reform it (strip out whitespace)
Eric Andersenb9050282003-12-24 06:02:11 +000076 */
Glenn L McGrathab821542003-12-26 02:19:34 +000077
Denis Vlasenko64309f82007-11-29 06:40:28 +000078static int sysctl_preload_file_and_exit(const char *filename)
Eric Andersenb9050282003-12-24 06:02:11 +000079{
Denis Vlasenko4a717e02008-07-20 13:01:56 +000080 char *token[2];
81 parser_t *parser;
Eric Andersenb9050282003-12-24 06:02:11 +000082
Denis Vlasenko4a717e02008-07-20 13:01:56 +000083 parser = config_open(filename);
Denis Vlasenko084266e2008-07-26 23:08:31 +000084 while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { // TODO: ';' is comment char too
Denis Vlasenko5415c852008-07-21 23:05:26 +000085// if (!token[1]) {
Denis Vlasenkocfdb7112008-10-15 08:29:17 +000086// bb_error_msg("warning: %s(%d): invalid syntax, continuing",
87// filename, parser->lineno);
Denis Vlasenko5415c852008-07-21 23:05:26 +000088// } else {
89 {
Denis Vlasenko4a717e02008-07-20 13:01:56 +000090#if 0
91 char *s = xasprintf("%s=%s", token[0], token[1]);
92 sysctl_write_setting(s);
93 free(s);
94#else // PLAY_WITH_FIRE for -4 bytes?
95 sprintf(parser->line, "%s=%s", token[0], token[1]); // must have room by definition
96 sysctl_write_setting(parser->line);
97#endif
Eric Andersenb9050282003-12-24 06:02:11 +000098 }
Eric Andersenb9050282003-12-24 06:02:11 +000099 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000100 if (ENABLE_FEATURE_CLEAN_UP)
Denis Vlasenko4a717e02008-07-20 13:01:56 +0000101 config_close(parser);
Eric Andersenb9050282003-12-24 06:02:11 +0000102 return 0;
Denis Vlasenko64309f82007-11-29 06:40:28 +0000103} /* end sysctl_preload_file_and_exit() */
Eric Andersenb9050282003-12-24 06:02:11 +0000104
105/*
106 * Write a single sysctl setting
107 */
Denis Vlasenko64309f82007-11-29 06:40:28 +0000108static int sysctl_write_setting(const char *setting)
Eric Andersenb9050282003-12-24 06:02:11 +0000109{
Denis Vlasenko64309f82007-11-29 06:40:28 +0000110 int retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000111 const char *name;
Eric Andersenb9050282003-12-24 06:02:11 +0000112 const char *value;
113 const char *equals;
114 char *tmpname, *outname, *cptr;
Denis Vlasenko64309f82007-11-29 06:40:28 +0000115 int fd;
Eric Andersenb9050282003-12-24 06:02:11 +0000116
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000117 name = setting;
118 equals = strchr(setting, '=');
119 if (!equals) {
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000120 bb_error_msg("error: '%s' must be of the form name=value", setting);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000121 return EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000122 }
123
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000124 value = equals + 1; /* point to the value in name=value */
125 if (name == equals || !*value) {
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000126 bb_error_msg("error: malformed setting '%s'", setting);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000127 return EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000128 }
129
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000130 tmpname = xasprintf("%s%.*s", PROC_SYS, (int)(equals - name), name);
131 outname = xstrdup(tmpname + strlen_PROC_SYS);
Eric Andersenb9050282003-12-24 06:02:11 +0000132
Denis Vlasenko58cc52a2008-10-15 08:22:55 +0000133 sysctl_dots_to_slashes(tmpname);
Eric Andersenb9050282003-12-24 06:02:11 +0000134
135 while ((cptr = strchr(outname, '/')) != NULL)
136 *cptr = '.';
137
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000138 fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
139 if (fd < 0) {
Eric Andersenb9050282003-12-24 06:02:11 +0000140 switch (errno) {
141 case ENOENT:
Denis Vlasenko64309f82007-11-29 06:40:28 +0000142 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000143 bb_error_msg(msg_unknown_key, outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000144 break;
145 default:
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000146 bb_perror_msg("error setting key '%s'", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000147 break;
148 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000149 retval = EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000150 } else {
Glenn L McGrathab821542003-12-26 02:19:34 +0000151 dwrite_str(fd, value);
Eric Andersenb9050282003-12-24 06:02:11 +0000152 close(fd);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000153 if (option_mask32 & FLAG_SHOW_KEYS) {
154 printf("%s = ", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000155 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000156 puts(value);
157 retval = EXIT_SUCCESS;
Eric Andersenb9050282003-12-24 06:02:11 +0000158 }
159
Eric Andersenb9050282003-12-24 06:02:11 +0000160 free(tmpname);
161 free(outname);
162 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000163} /* end sysctl_write_setting() */
Eric Andersenb9050282003-12-24 06:02:11 +0000164
165/*
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000166 * Read a sysctl setting
Eric Andersenb9050282003-12-24 06:02:11 +0000167 */
Denis Vlasenko64309f82007-11-29 06:40:28 +0000168static int sysctl_read_setting(const char *name)
Eric Andersenb9050282003-12-24 06:02:11 +0000169{
Denis Vlasenko64309f82007-11-29 06:40:28 +0000170 int retval;
Eric Andersenb9050282003-12-24 06:02:11 +0000171 char *tmpname, *outname, *cptr;
172 char inbuf[1025];
Eric Andersenb9050282003-12-24 06:02:11 +0000173 FILE *fp;
174
Denis Vlasenko64309f82007-11-29 06:40:28 +0000175 if (!*name) {
176 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000177 bb_error_msg(msg_unknown_key, name);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000178 return -1;
179 }
Eric Andersenb9050282003-12-24 06:02:11 +0000180
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000181 tmpname = concat_path_file(PROC_SYS, name);
182 outname = xstrdup(tmpname + strlen_PROC_SYS);
Eric Andersenb9050282003-12-24 06:02:11 +0000183
Denis Vlasenko58cc52a2008-10-15 08:22:55 +0000184 sysctl_dots_to_slashes(tmpname);
185
Eric Andersenb9050282003-12-24 06:02:11 +0000186 while ((cptr = strchr(outname, '/')) != NULL)
187 *cptr = '.';
188
Denis Vlasenko5415c852008-07-21 23:05:26 +0000189 fp = fopen_for_read(tmpname);
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000190 if (fp == NULL) {
Eric Andersenb9050282003-12-24 06:02:11 +0000191 switch (errno) {
192 case ENOENT:
Denis Vlasenko64309f82007-11-29 06:40:28 +0000193 if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000194 bb_error_msg(msg_unknown_key, outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000195 break;
196 default:
Denis Vlasenkocfdb7112008-10-15 08:29:17 +0000197 bb_perror_msg("error reading key '%s'", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000198 break;
199 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000200 retval = EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000201 } else {
202 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000203 if (option_mask32 & FLAG_SHOW_KEYS) {
204 printf("%s = ", outname);
Eric Andersenb9050282003-12-24 06:02:11 +0000205 }
Denis Vlasenko64309f82007-11-29 06:40:28 +0000206 fputs(inbuf, stdout);
Eric Andersenb9050282003-12-24 06:02:11 +0000207 }
208 fclose(fp);
Denis Vlasenko64309f82007-11-29 06:40:28 +0000209 retval = EXIT_SUCCESS;
Eric Andersenb9050282003-12-24 06:02:11 +0000210 }
211
212 free(tmpname);
213 free(outname);
214 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000215} /* end sysctl_read_setting() */
Eric Andersenb9050282003-12-24 06:02:11 +0000216
217/*
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000218 * Display all the sysctl settings
Eric Andersenb9050282003-12-24 06:02:11 +0000219 */
Denis Vlasenko64309f82007-11-29 06:40:28 +0000220static int sysctl_display_all(const char *path)
Eric Andersenb9050282003-12-24 06:02:11 +0000221{
222 int retval = 0;
Eric Andersenb9050282003-12-24 06:02:11 +0000223 DIR *dp;
224 struct dirent *de;
225 char *tmpdir;
226 struct stat ts;
227
Denis Vlasenko51742f42007-04-12 00:32:05 +0000228 dp = opendir(path);
229 if (!dp) {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000230 return EXIT_FAILURE;
Eric Andersenb9050282003-12-24 06:02:11 +0000231 }
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000232 while ((de = readdir(dp)) != NULL) {
233 tmpdir = concat_subpath_file(path, de->d_name);
234 if (tmpdir == NULL)
Denis Vlasenko64309f82007-11-29 06:40:28 +0000235 continue; /* . or .. */
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000236 if (stat(tmpdir, &ts) != 0) {
237 bb_perror_msg(tmpdir);
238 } else if (S_ISDIR(ts.st_mode)) {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000239 retval |= sysctl_display_all(tmpdir);
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000240 } else {
Denis Vlasenko64309f82007-11-29 06:40:28 +0000241 retval |= sysctl_read_setting(tmpdir + strlen_PROC_SYS);
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000242 }
243 free(tmpdir);
244 } /* end while */
245 closedir(dp);
Eric Andersenb9050282003-12-24 06:02:11 +0000246
247 return retval;
Denis Vlasenko5a28a252007-10-29 19:22:13 +0000248} /* end sysctl_display_all() */
Denis Vlasenko58cc52a2008-10-15 08:22:55 +0000249
250static void sysctl_dots_to_slashes(char *name)
251{
252 char *cptr = name;
253
254 /* Example from bug 3894:
255 * net.ipv4.conf.eth0.100.mc_forwarding ->
256 * net/ipv4/conf/eth0.100/mc_forwarding */
257 while (*cptr != '\0') {
258 if (*cptr == '.') {
259 *cptr = '\0';
260 if (access(name, F_OK) == 0)
261 *cptr = '/';
262 else
263 *cptr = '.';
264 }
265 cptr++;
266 }
267} /* end sysctl_dots_to_slashes() */