ipc_sysctl.c revision a5c5928b
1/*
2 *  Copyright (C) 2007
3 *
4 *  Author: Eric Biederman <ebiederm@xmision.com>
5 *
6 *  This program is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU General Public License as
8 *  published by the Free Software Foundation, version 2 of the
9 *  License.
10 */
11
12#include <linux/module.h>
13#include <linux/ipc.h>
14#include <linux/nsproxy.h>
15#include <linux/sysctl.h>
16#include <linux/uaccess.h>
17#include <linux/ipc_namespace.h>
18#include <linux/msg.h>
19#include "util.h"
20
21static void *get_ipc(struct ctl_table *table)
22{
23	char *which = table->data;
24	struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
25	which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns;
26	return which;
27}
28
29#ifdef CONFIG_PROC_SYSCTL
30static int proc_ipc_dointvec(struct ctl_table *table, int write,
31	void __user *buffer, size_t *lenp, loff_t *ppos)
32{
33	struct ctl_table ipc_table;
34
35	memcpy(&ipc_table, table, sizeof(ipc_table));
36	ipc_table.data = get_ipc(table);
37
38	return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
39}
40
41static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write,
42	void __user *buffer, size_t *lenp, loff_t *ppos)
43{
44	struct ctl_table ipc_table;
45
46	memcpy(&ipc_table, table, sizeof(ipc_table));
47	ipc_table.data = get_ipc(table);
48
49	return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
50}
51
52static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
53	void __user *buffer, size_t *lenp, loff_t *ppos)
54{
55	struct ipc_namespace *ns = current->nsproxy->ipc_ns;
56	int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
57
58	if (err < 0)
59		return err;
60	if (ns->shm_rmid_forced)
61		shm_destroy_orphaned(ns);
62	return err;
63}
64
65static int proc_ipc_callback_dointvec_minmax(struct ctl_table *table, int write,
66	void __user *buffer, size_t *lenp, loff_t *ppos)
67{
68	struct ctl_table ipc_table;
69	size_t lenp_bef = *lenp;
70	int rc;
71
72	memcpy(&ipc_table, table, sizeof(ipc_table));
73	ipc_table.data = get_ipc(table);
74
75	rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
76
77	if (write && !rc && lenp_bef == *lenp)
78		/*
79		 * Tunable has successfully been changed by hand. Disable its
80		 * automatic adjustment. This simply requires unregistering
81		 * the notifiers that trigger recalculation.
82		 */
83		unregister_ipcns_notifier(current->nsproxy->ipc_ns);
84
85	return rc;
86}
87
88static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write,
89	void __user *buffer, size_t *lenp, loff_t *ppos)
90{
91	struct ctl_table ipc_table;
92	memcpy(&ipc_table, table, sizeof(ipc_table));
93	ipc_table.data = get_ipc(table);
94
95	return proc_doulongvec_minmax(&ipc_table, write, buffer,
96					lenp, ppos);
97}
98
99/*
100 * Routine that is called when the file "auto_msgmni" has successfully been
101 * written.
102 * Two values are allowed:
103 * 0: unregister msgmni's callback routine from the ipc namespace notifier
104 *    chain. This means that msgmni won't be recomputed anymore upon memory
105 *    add/remove or ipc namespace creation/removal.
106 * 1: register back the callback routine.
107 */
108static void ipc_auto_callback(int val)
109{
110	if (!val)
111		unregister_ipcns_notifier(current->nsproxy->ipc_ns);
112	else {
113		/*
114		 * Re-enable automatic recomputing only if not already
115		 * enabled.
116		 */
117		recompute_msgmni(current->nsproxy->ipc_ns);
118		cond_register_ipcns_notifier(current->nsproxy->ipc_ns);
119	}
120}
121
122static int proc_ipcauto_dointvec_minmax(struct ctl_table *table, int write,
123	void __user *buffer, size_t *lenp, loff_t *ppos)
124{
125	struct ctl_table ipc_table;
126	size_t lenp_bef = *lenp;
127	int oldval;
128	int rc;
129
130	memcpy(&ipc_table, table, sizeof(ipc_table));
131	ipc_table.data = get_ipc(table);
132	oldval = *((int *)(ipc_table.data));
133
134	rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
135
136	if (write && !rc && lenp_bef == *lenp) {
137		int newval = *((int *)(ipc_table.data));
138		/*
139		 * The file "auto_msgmni" has correctly been set.
140		 * React by (un)registering the corresponding tunable, if the
141		 * value has changed.
142		 */
143		if (newval != oldval)
144			ipc_auto_callback(newval);
145	}
146
147	return rc;
148}
149
150#else
151#define proc_ipc_doulongvec_minmax NULL
152#define proc_ipc_dointvec	   NULL
153#define proc_ipc_dointvec_minmax   NULL
154#define proc_ipc_dointvec_minmax_orphans   NULL
155#define proc_ipc_callback_dointvec_minmax  NULL
156#define proc_ipcauto_dointvec_minmax NULL
157#endif
158
159static int zero;
160static int one = 1;
161static int int_max = INT_MAX;
162
163static struct ctl_table ipc_kern_table[] = {
164	{
165		.procname	= "shmmax",
166		.data		= &init_ipc_ns.shm_ctlmax,
167		.maxlen		= sizeof(init_ipc_ns.shm_ctlmax),
168		.mode		= 0644,
169		.proc_handler	= proc_ipc_doulongvec_minmax,
170	},
171	{
172		.procname	= "shmall",
173		.data		= &init_ipc_ns.shm_ctlall,
174		.maxlen		= sizeof(init_ipc_ns.shm_ctlall),
175		.mode		= 0644,
176		.proc_handler	= proc_ipc_doulongvec_minmax,
177	},
178	{
179		.procname	= "shmmni",
180		.data		= &init_ipc_ns.shm_ctlmni,
181		.maxlen		= sizeof(init_ipc_ns.shm_ctlmni),
182		.mode		= 0644,
183		.proc_handler	= proc_ipc_dointvec,
184	},
185	{
186		.procname	= "shm_rmid_forced",
187		.data		= &init_ipc_ns.shm_rmid_forced,
188		.maxlen		= sizeof(init_ipc_ns.shm_rmid_forced),
189		.mode		= 0644,
190		.proc_handler	= proc_ipc_dointvec_minmax_orphans,
191		.extra1		= &zero,
192		.extra2		= &one,
193	},
194	{
195		.procname	= "msgmax",
196		.data		= &init_ipc_ns.msg_ctlmax,
197		.maxlen		= sizeof(init_ipc_ns.msg_ctlmax),
198		.mode		= 0644,
199		.proc_handler	= proc_ipc_dointvec_minmax,
200		.extra1		= &zero,
201		.extra2		= &int_max,
202	},
203	{
204		.procname	= "msgmni",
205		.data		= &init_ipc_ns.msg_ctlmni,
206		.maxlen		= sizeof(init_ipc_ns.msg_ctlmni),
207		.mode		= 0644,
208		.proc_handler	= proc_ipc_callback_dointvec_minmax,
209		.extra1		= &zero,
210		.extra2		= &int_max,
211	},
212	{
213		.procname	=  "msgmnb",
214		.data		= &init_ipc_ns.msg_ctlmnb,
215		.maxlen		= sizeof(init_ipc_ns.msg_ctlmnb),
216		.mode		= 0644,
217		.proc_handler	= proc_ipc_dointvec_minmax,
218		.extra1		= &zero,
219		.extra2		= &int_max,
220	},
221	{
222		.procname	= "sem",
223		.data		= &init_ipc_ns.sem_ctls,
224		.maxlen		= 4*sizeof(int),
225		.mode		= 0644,
226		.proc_handler	= proc_ipc_dointvec,
227	},
228	{
229		.procname	= "auto_msgmni",
230		.data		= &init_ipc_ns.auto_msgmni,
231		.maxlen		= sizeof(int),
232		.mode		= 0644,
233		.proc_handler	= proc_ipcauto_dointvec_minmax,
234		.extra1		= &zero,
235		.extra2		= &one,
236	},
237#ifdef CONFIG_CHECKPOINT_RESTORE
238	{
239		.procname	= "sem_next_id",
240		.data		= &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
241		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
242		.mode		= 0644,
243		.proc_handler	= proc_ipc_dointvec_minmax,
244		.extra1		= &zero,
245		.extra2		= &int_max,
246	},
247	{
248		.procname	= "msg_next_id",
249		.data		= &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
250		.maxlen		= sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
251		.mode		= 0644,
252		.proc_handler	= proc_ipc_dointvec_minmax,
253		.extra1		= &zero,
254		.extra2		= &int_max,
255	},
256	{
257		.procname	= "shm_next_id",
258		.data		= &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
259		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
260		.mode		= 0644,
261		.proc_handler	= proc_ipc_dointvec_minmax,
262		.extra1		= &zero,
263		.extra2		= &int_max,
264	},
265#endif
266	{}
267};
268
269static struct ctl_table ipc_root_table[] = {
270	{
271		.procname	= "kernel",
272		.mode		= 0555,
273		.child		= ipc_kern_table,
274	},
275	{}
276};
277
278static int __init ipc_sysctl_init(void)
279{
280	register_sysctl_table(ipc_root_table);
281	return 0;
282}
283
284device_initcall(ipc_sysctl_init);
285