ipc: do not recompute msgmni anymore if explicitly set by user
[linux-2.6-block.git] / ipc / ipc_sysctl.c
CommitLineData
a5494dcd
EB
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>
ae5e1b22 17#include <linux/ipc_namespace.h>
a5494dcd 18
a5494dcd
EB
19static void *get_ipc(ctl_table *table)
20{
21 char *which = table->data;
22 struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
23 which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns;
24 return which;
25}
a5494dcd
EB
26
27#ifdef CONFIG_PROC_FS
28static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp,
29 void __user *buffer, size_t *lenp, loff_t *ppos)
30{
31 struct ctl_table ipc_table;
32 memcpy(&ipc_table, table, sizeof(ipc_table));
33 ipc_table.data = get_ipc(table);
34
35 return proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos);
36}
37
91cfb2b4
ND
38static int proc_ipc_callback_dointvec(ctl_table *table, int write,
39 struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
40{
41 size_t lenp_bef = *lenp;
42 int rc;
43
44 rc = proc_ipc_dointvec(table, write, filp, buffer, lenp, ppos);
45
46 if (write && !rc && lenp_bef == *lenp)
47 /*
48 * Tunable has successfully been changed from userland:
49 * disable its automatic recomputing.
50 */
51 unregister_ipcns_notifier(current->nsproxy->ipc_ns);
52
53 return rc;
54}
55
a5494dcd
EB
56static int proc_ipc_doulongvec_minmax(ctl_table *table, int write,
57 struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
58{
59 struct ctl_table ipc_table;
60 memcpy(&ipc_table, table, sizeof(ipc_table));
61 ipc_table.data = get_ipc(table);
62
63 return proc_doulongvec_minmax(&ipc_table, write, filp, buffer,
64 lenp, ppos);
65}
66
67#else
68#define proc_ipc_doulongvec_minmax NULL
69#define proc_ipc_dointvec NULL
91cfb2b4 70#define proc_ipc_callback_dointvec NULL
a5494dcd
EB
71#endif
72
73#ifdef CONFIG_SYSCTL_SYSCALL
74/* The generic sysctl ipc data routine. */
75static int sysctl_ipc_data(ctl_table *table, int __user *name, int nlen,
76 void __user *oldval, size_t __user *oldlenp,
77 void __user *newval, size_t newlen)
78{
79 size_t len;
80 void *data;
81
82 /* Get out of I don't have a variable */
83 if (!table->data || !table->maxlen)
84 return -ENOTDIR;
85
86 data = get_ipc(table);
87 if (!data)
88 return -ENOTDIR;
89
90 if (oldval && oldlenp) {
91 if (get_user(len, oldlenp))
92 return -EFAULT;
93 if (len) {
94 if (len > table->maxlen)
95 len = table->maxlen;
96 if (copy_to_user(oldval, data, len))
97 return -EFAULT;
98 if (put_user(len, oldlenp))
99 return -EFAULT;
100 }
101 }
102
103 if (newval && newlen) {
104 if (newlen > table->maxlen)
105 newlen = table->maxlen;
106
107 if (copy_from_user(data, newval, newlen))
108 return -EFAULT;
109 }
110 return 1;
111}
91cfb2b4
ND
112
113static int sysctl_ipc_registered_data(ctl_table *table, int __user *name,
114 int nlen, void __user *oldval, size_t __user *oldlenp,
115 void __user *newval, size_t newlen)
116{
117 int rc;
118
119 rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval,
120 newlen);
121
122 if (newval && newlen && rc > 0)
123 /*
124 * Tunable has successfully been changed from userland:
125 * disable its automatic recomputing.
126 */
127 unregister_ipcns_notifier(current->nsproxy->ipc_ns);
128
129 return rc;
130}
a5494dcd
EB
131#else
132#define sysctl_ipc_data NULL
91cfb2b4 133#define sysctl_ipc_registered_data NULL
a5494dcd
EB
134#endif
135
136static struct ctl_table ipc_kern_table[] = {
137 {
138 .ctl_name = KERN_SHMMAX,
139 .procname = "shmmax",
140 .data = &init_ipc_ns.shm_ctlmax,
141 .maxlen = sizeof (init_ipc_ns.shm_ctlmax),
142 .mode = 0644,
143 .proc_handler = proc_ipc_doulongvec_minmax,
144 .strategy = sysctl_ipc_data,
145 },
146 {
147 .ctl_name = KERN_SHMALL,
148 .procname = "shmall",
149 .data = &init_ipc_ns.shm_ctlall,
150 .maxlen = sizeof (init_ipc_ns.shm_ctlall),
151 .mode = 0644,
152 .proc_handler = proc_ipc_doulongvec_minmax,
153 .strategy = sysctl_ipc_data,
154 },
155 {
156 .ctl_name = KERN_SHMMNI,
157 .procname = "shmmni",
158 .data = &init_ipc_ns.shm_ctlmni,
159 .maxlen = sizeof (init_ipc_ns.shm_ctlmni),
160 .mode = 0644,
161 .proc_handler = proc_ipc_dointvec,
162 .strategy = sysctl_ipc_data,
163 },
164 {
165 .ctl_name = KERN_MSGMAX,
166 .procname = "msgmax",
167 .data = &init_ipc_ns.msg_ctlmax,
168 .maxlen = sizeof (init_ipc_ns.msg_ctlmax),
169 .mode = 0644,
170 .proc_handler = proc_ipc_dointvec,
171 .strategy = sysctl_ipc_data,
172 },
173 {
174 .ctl_name = KERN_MSGMNI,
175 .procname = "msgmni",
176 .data = &init_ipc_ns.msg_ctlmni,
177 .maxlen = sizeof (init_ipc_ns.msg_ctlmni),
178 .mode = 0644,
91cfb2b4
ND
179 .proc_handler = proc_ipc_callback_dointvec,
180 .strategy = sysctl_ipc_registered_data,
a5494dcd
EB
181 },
182 {
183 .ctl_name = KERN_MSGMNB,
184 .procname = "msgmnb",
185 .data = &init_ipc_ns.msg_ctlmnb,
186 .maxlen = sizeof (init_ipc_ns.msg_ctlmnb),
187 .mode = 0644,
188 .proc_handler = proc_ipc_dointvec,
189 .strategy = sysctl_ipc_data,
190 },
191 {
192 .ctl_name = KERN_SEM,
193 .procname = "sem",
194 .data = &init_ipc_ns.sem_ctls,
195 .maxlen = 4*sizeof (int),
196 .mode = 0644,
197 .proc_handler = proc_ipc_dointvec,
198 .strategy = sysctl_ipc_data,
199 },
200 {}
201};
202
203static struct ctl_table ipc_root_table[] = {
204 {
205 .ctl_name = CTL_KERN,
206 .procname = "kernel",
207 .mode = 0555,
208 .child = ipc_kern_table,
209 },
210 {}
211};
212
213static int __init ipc_sysctl_init(void)
214{
0b4d4147 215 register_sysctl_table(ipc_root_table);
a5494dcd
EB
216 return 0;
217}
218
219__initcall(ipc_sysctl_init);