aboutsummaryrefslogtreecommitdiff
path: root/toys/other/sysctl.c
blob: 37735942783607e7047fb1c4b60ef1ca0b7d58f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* sysctl.c - A utility to read and manipulate the sysctl parameters.
 *
 * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
 * Copyright 2014 Kyungwan Han <asura321@gmail.com>
 *
 * No Standard
 
USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))

config SYSCTL
  bool "sysctl"
  default y
  help
    usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...]

    Read/write system control data (under /proc/sys).

    -a,A	Show all values
    -e	Don't warn about unknown keys
    -N	Don't print key values
    -n	Don't print key names
    -p	Read values from FILE (default /etc/sysctl.conf)
    -q	Don't show value after write
    -w	Only write values (object to reading)
*/
#define FOR_sysctl
#include "toys.h"

// Null terminate at =, return value
static char *split_key(char *key)
{
  char *value = strchr(key, '=');

  if (value) *(value++)=0;

  return value;
}

static void replace_char(char *str, char old, char new)
{
  for (; *str; str++) if (*str == old) *str = new;
}

static void key_error(char *key)
{
  if (errno == ENOENT) {
    if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key);
  } else perror_msg("key '%s'", key);
}

static int write_key(char *path, char *key, char *value)
{
  int fd = open(path, O_WRONLY);

  if (fd < 0) {
    key_error(key);

    return 0;
  }
  xwrite(fd, value, strlen(value));
  xclose(fd);

  return 1;
}

// Display all keys under a path
static int do_show_keys(struct dirtree *dt)
{
  char *path, *data, *key;

  if (!dirtree_notdotdot(dt)) return 0; // Skip . and ..
  if (S_ISDIR(dt->st.st_mode)) return DIRTREE_RECURSE;

  path = dirtree_path(dt, 0);
  data = readfile(path, 0, 0);
  replace_char(key = path + 10, '/', '.'); // skip "/proc/sys/"
  if (!data) key_error(key);
  else {
    // Print the parts that aren't switched off by flags.
    if (!(toys.optflags & FLAG_n)) xprintf("%s", key);
    if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = ");
    for (key = data+strlen(data); key > data && isspace(*--key); *key = 0);
    if (!(toys.optflags & FLAG_N)) xprintf("%s", data);
    if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n');
  }

  free(data);
  free(path);

  return 0;
}

// Read/write entries under a key. Accepts "key=value" in key if !value
static void process_key(char *key, char *value)
{
  char *path;

  if (!value) value = split_key(key);
  if ((toys.optflags & FLAG_w) && !value) {
    error_msg("'%s' not key=value", key);

    return;
  }

  path = xmprintf("/proc/sys/%s", key);
  replace_char(path, '.', '/');
  // Note: failure to assign to a non-leaf node suppresses the display.
  if (!(value && (!write_key(path, key, value) || (toys.optflags & FLAG_q)))) {
    if (!access(path, R_OK)) dirtree_read(path, do_show_keys);
    else key_error(key);
  }
  free(path);
}

void sysctl_main()
{
  char **args = 0;

  // Display all keys
  if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys);

  // read file
  else if (toys.optflags & FLAG_p) {
    FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r");
    size_t len;

    for (;;) {
      char *line = 0, *key, *val;

      if (-1 == (len = getline(&line, &len, fp))) break;
      key = line;
      while (isspace(*key)) key++;
      if (*key == '#' || *key == ';' || !*key) continue;
      while (len && isspace(line[len-1])) line[--len] = 0;
      if (!(val = split_key(line))) {
        error_msg("'%s' not key=value", line);
        continue;
      }

      // Trim whitespace around =
      len = (val-line)-1;
      while (len && isspace(line[len-1])) line[--len] = 0;
      while (isspace(*val)) val++;;

      process_key(key, val);
      free(line);
    }
    fclose(fp);

  // Loop through arguments, displaying or assigning as appropriate
  } else {
    if (!*toys.optargs) help_exit("Needs 1 arg");
    for (args = toys.optargs; *args; args++) process_key(*args, 0);
  }
}