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