1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdio.h>
6 #include <unistd.h>
7
8 #include "af-list.h"
9 #include "fd-util.h"
10 #include "fileio.h"
11 #include "log.h"
12 #include "macro.h"
13 #include "path-util.h"
14 #include "socket-util.h"
15 #include "string-util.h"
16 #include "sysctl-util.h"
17
sysctl_normalize(char * s)18 char *sysctl_normalize(char *s) {
19 char *n;
20
21 n = strpbrk(s, "/.");
22
23 /* If the first separator is a slash, the path is
24 * assumed to be normalized and slashes remain slashes
25 * and dots remains dots. */
26
27 if (n && *n == '.')
28 /* Dots become slashes and slashes become dots. Fun. */
29 do {
30 if (*n == '.')
31 *n = '/';
32 else
33 *n = '.';
34
35 n = strpbrk(n + 1, "/.");
36 } while (n);
37
38 path_simplify(s);
39
40 /* Kill the leading slash, but keep the first character of the string in the same place. */
41 if (s[0] == '/' && s[1] != 0)
42 memmove(s, s+1, strlen(s));
43
44 return s;
45 }
46
sysctl_write(const char * property,const char * value)47 int sysctl_write(const char *property, const char *value) {
48 char *p;
49
50 assert(property);
51 assert(value);
52
53 p = strjoina("/proc/sys/", property);
54
55 path_simplify(p);
56 if (!path_is_normalized(p))
57 return -EINVAL;
58
59 log_debug("Setting '%s' to '%s'", p, value);
60
61 return write_string_file(p, value, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL);
62 }
63
sysctl_writef(const char * property,const char * format,...)64 int sysctl_writef(const char *property, const char *format, ...) {
65 _cleanup_free_ char *v = NULL;
66 va_list ap;
67 int r;
68
69 va_start(ap, format);
70 r = vasprintf(&v, format, ap);
71 va_end(ap);
72
73 if (r < 0)
74 return -ENOMEM;
75
76 return sysctl_write(property, v);
77 }
78
sysctl_write_ip_property(int af,const char * ifname,const char * property,const char * value)79 int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value) {
80 const char *p;
81
82 assert(property);
83 assert(value);
84
85 if (!IN_SET(af, AF_INET, AF_INET6))
86 return -EAFNOSUPPORT;
87
88 if (ifname) {
89 if (!ifname_valid_full(ifname, IFNAME_VALID_SPECIAL))
90 return -EINVAL;
91
92 p = strjoina("net/", af_to_ipv4_ipv6(af), "/conf/", ifname, "/", property);
93 } else
94 p = strjoina("net/", af_to_ipv4_ipv6(af), "/", property);
95
96 return sysctl_write(p, value);
97 }
98
sysctl_read(const char * property,char ** ret)99 int sysctl_read(const char *property, char **ret) {
100 char *p;
101 int r;
102
103 assert(property);
104
105 p = strjoina("/proc/sys/", property);
106
107 path_simplify(p);
108 if (!path_is_normalized(p)) /* Filter out attempts to write to /proc/sys/../../…, just in case */
109 return -EINVAL;
110
111 r = read_full_virtual_file(p, ret, NULL);
112 if (r < 0)
113 return r;
114 if (ret)
115 delete_trailing_chars(*ret, NEWLINE);
116
117 return r;
118 }
119
sysctl_read_ip_property(int af,const char * ifname,const char * property,char ** ret)120 int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret) {
121 const char *p;
122
123 assert(property);
124
125 if (!IN_SET(af, AF_INET, AF_INET6))
126 return -EAFNOSUPPORT;
127
128 if (ifname) {
129 if (!ifname_valid_full(ifname, IFNAME_VALID_SPECIAL))
130 return -EINVAL;
131
132 p = strjoina("net/", af_to_ipv4_ipv6(af), "/conf/", ifname, "/", property);
133 } else
134 p = strjoina("net/", af_to_ipv4_ipv6(af), "/", property);
135
136 return sysctl_read(p, ret);
137 }
138