1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2007 |
4 | * |
5 | * Author: Eric Biederman <ebiederm@xmision.com> |
6 | */ |
7 | |
8 | #include <linux/export.h> |
9 | #include <linux/uts.h> |
10 | #include <linux/utsname.h> |
11 | #include <linux/random.h> |
12 | #include <linux/sysctl.h> |
13 | #include <linux/wait.h> |
14 | #include <linux/rwsem.h> |
15 | |
16 | #ifdef CONFIG_PROC_SYSCTL |
17 | |
18 | static void *get_uts(struct ctl_table *table) |
19 | { |
20 | char *which = table->data; |
21 | struct uts_namespace *uts_ns; |
22 | |
23 | uts_ns = current->nsproxy->uts_ns; |
24 | which = (which - (char *)&init_uts_ns) + (char *)uts_ns; |
25 | |
26 | return which; |
27 | } |
28 | |
29 | /* |
30 | * Special case of dostring for the UTS structure. This has locks |
31 | * to observe. Should this be in kernel/sys.c ???? |
32 | */ |
33 | static int proc_do_uts_string(struct ctl_table *table, int write, |
34 | void *buffer, size_t *lenp, loff_t *ppos) |
35 | { |
36 | struct ctl_table uts_table; |
37 | int r; |
38 | char tmp_data[__NEW_UTS_LEN + 1]; |
39 | |
40 | memcpy(&uts_table, table, sizeof(uts_table)); |
41 | uts_table.data = tmp_data; |
42 | |
43 | /* |
44 | * Buffer the value in tmp_data so that proc_dostring() can be called |
45 | * without holding any locks. |
46 | * We also need to read the original value in the write==1 case to |
47 | * support partial writes. |
48 | */ |
49 | down_read(sem: &uts_sem); |
50 | memcpy(tmp_data, get_uts(table), sizeof(tmp_data)); |
51 | up_read(sem: &uts_sem); |
52 | r = proc_dostring(&uts_table, write, buffer, lenp, ppos); |
53 | |
54 | if (write) { |
55 | /* |
56 | * Write back the new value. |
57 | * Note that, since we dropped uts_sem, the result can |
58 | * theoretically be incorrect if there are two parallel writes |
59 | * at non-zero offsets to the same sysctl. |
60 | */ |
61 | add_device_randomness(buf: tmp_data, len: sizeof(tmp_data)); |
62 | down_write(sem: &uts_sem); |
63 | memcpy(get_uts(table), tmp_data, sizeof(tmp_data)); |
64 | up_write(sem: &uts_sem); |
65 | proc_sys_poll_notify(poll: table->poll); |
66 | } |
67 | |
68 | return r; |
69 | } |
70 | #else |
71 | #define proc_do_uts_string NULL |
72 | #endif |
73 | |
74 | static DEFINE_CTL_TABLE_POLL(hostname_poll); |
75 | static DEFINE_CTL_TABLE_POLL(domainname_poll); |
76 | |
77 | // Note: update 'enum uts_proc' to match any changes to this table |
78 | static struct ctl_table uts_kern_table[] = { |
79 | { |
80 | .procname = "arch" , |
81 | .data = init_uts_ns.name.machine, |
82 | .maxlen = sizeof(init_uts_ns.name.machine), |
83 | .mode = 0444, |
84 | .proc_handler = proc_do_uts_string, |
85 | }, |
86 | { |
87 | .procname = "ostype" , |
88 | .data = init_uts_ns.name.sysname, |
89 | .maxlen = sizeof(init_uts_ns.name.sysname), |
90 | .mode = 0444, |
91 | .proc_handler = proc_do_uts_string, |
92 | }, |
93 | { |
94 | .procname = "osrelease" , |
95 | .data = init_uts_ns.name.release, |
96 | .maxlen = sizeof(init_uts_ns.name.release), |
97 | .mode = 0444, |
98 | .proc_handler = proc_do_uts_string, |
99 | }, |
100 | { |
101 | .procname = "version" , |
102 | .data = init_uts_ns.name.version, |
103 | .maxlen = sizeof(init_uts_ns.name.version), |
104 | .mode = 0444, |
105 | .proc_handler = proc_do_uts_string, |
106 | }, |
107 | { |
108 | .procname = "hostname" , |
109 | .data = init_uts_ns.name.nodename, |
110 | .maxlen = sizeof(init_uts_ns.name.nodename), |
111 | .mode = 0644, |
112 | .proc_handler = proc_do_uts_string, |
113 | .poll = &hostname_poll, |
114 | }, |
115 | { |
116 | .procname = "domainname" , |
117 | .data = init_uts_ns.name.domainname, |
118 | .maxlen = sizeof(init_uts_ns.name.domainname), |
119 | .mode = 0644, |
120 | .proc_handler = proc_do_uts_string, |
121 | .poll = &domainname_poll, |
122 | }, |
123 | {} |
124 | }; |
125 | |
126 | #ifdef CONFIG_PROC_SYSCTL |
127 | /* |
128 | * Notify userspace about a change in a certain entry of uts_kern_table, |
129 | * identified by the parameter proc. |
130 | */ |
131 | void uts_proc_notify(enum uts_proc proc) |
132 | { |
133 | struct ctl_table *table = &uts_kern_table[proc]; |
134 | |
135 | proc_sys_poll_notify(poll: table->poll); |
136 | } |
137 | #endif |
138 | |
139 | static int __init utsname_sysctl_init(void) |
140 | { |
141 | register_sysctl("kernel" , uts_kern_table); |
142 | return 0; |
143 | } |
144 | |
145 | device_initcall(utsname_sysctl_init); |
146 | |