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