1/* sysctl.c - A utility to read and manipulate the sysctl parameters.
2 *
3 * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
4 * Copyright 2014 Kyungwan Han <asura321@gmail.com>
5 *
6 * No Standard
7
8USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))
9
10config SYSCTL
11  bool "sysctl"
12  default y
13  help
14    usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...]
15
16    Read/write system control data (under /proc/sys).
17
18    -a,A	Show all values
19    -e	Don't warn about unknown keys
20    -N	Don't print key values
21    -n	Don't print key names
22    -p [FILE]	Read values from FILE (default /etc/sysctl.conf)
23    -q	Don't show value after write
24    -w	Only write values (object to reading)
25*/
26#define FOR_sysctl
27#include "toys.h"
28
29// Null terminate at =, return value
30static char *split_key(char *key)
31{
32  char *value = strchr(key, '=');
33
34  if (value) *(value++)=0;
35
36  return value;
37}
38
39static void replace_char(char *str, char old, char new)
40{
41  for (; *str; str++) if (*str == old) *str = new;
42}
43
44static void key_error(char *key)
45{
46  if (errno == ENOENT) {
47    if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key);
48  } else perror_msg("key '%s'", key);
49}
50
51static int write_key(char *path, char *key, char *value)
52{
53  int fd = open(path, O_WRONLY);;
54
55  if (fd < 0) {
56    key_error(key);
57
58    return 0;
59  }
60  xwrite(fd, value, strlen(value));
61  xclose(fd);
62
63  return 1;
64}
65
66// Display all keys under a path
67static int do_show_keys(struct dirtree *dt)
68{
69  char *path, *data, *key;
70
71  if (!dirtree_notdotdot(dt)) return 0; // Skip . and ..
72  if (S_ISDIR(dt->st.st_mode)) return DIRTREE_RECURSE;
73
74  path = dirtree_path(dt, 0);
75  data = readfile(path, 0, 0);
76  replace_char(key = path + 10, '/', '.'); // skip "/proc/sys/"
77  if (!data) key_error(key);
78  else {
79    // Print the parts that aren't switched off by flags.
80    if (!(toys.optflags & FLAG_n)) xprintf("%s", key);
81    if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = ");
82    for (key = data+strlen(data); key > data && isspace(*--key); *key = 0);
83    if (!(toys.optflags & FLAG_N)) xprintf("%s", data);
84    if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n');
85  }
86
87  free(data);
88  free(path);
89
90  return 0;
91}
92
93// Read/write entries under a key. Accepts "key=value" in key if !value
94static void process_key(char *key, char *value)
95{
96  char *path;
97
98  if (!value) value = split_key(key);
99  if ((toys.optflags & FLAG_w) && !value) {
100    error_msg("'%s' not key=value", key);
101
102    return;
103  }
104
105  path = xmprintf("/proc/sys/%s", key);
106  replace_char(path, '.', '/');
107  // Note: failure to assign to a non-leaf node suppresses the display.
108  if (!(value && (!write_key(path, key, value) || (toys.optflags & FLAG_q)))) {
109    if (!access(path, R_OK)) dirtree_read(path, do_show_keys);
110    else key_error(key);
111  }
112  free(path);
113}
114
115void sysctl_main()
116{
117  char **args = 0;
118
119  // Display all keys
120  if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys);
121
122  // read file
123  else if (toys.optflags & FLAG_p) {
124    FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r");
125    size_t len;
126
127    for (;;) {
128      char *line = 0, *key, *val;
129
130      if (-1 == (len = getline(&line, &len, fp))) break;
131      key = line;
132      while (isspace(*key)) key++;
133      if (*key == '#' || *key == ';' || !*key) continue;
134      while (len && isspace(line[len-1])) line[--len] = 0;
135      if (!(val = split_key(line))) {
136        error_msg("'%s' not key=value", line);
137        continue;
138      }
139
140      // Trim whitespace around =
141      len = (val-line)-1;
142      while (len && isspace(line[len-1])) line[--len] = 0;
143      while (isspace(*val)) val++;;
144
145      process_key(key, val);
146      free(line);
147    }
148    fclose(fp);
149
150  // Loop through arguments, displaying or assigning as appropriate
151  } else for (args = toys.optargs; *args; args++) process_key(*args, 0);
152}
153