ipc: do not recompute msgmni anymore if explicitly set by user
[linux-3.10.git] / ipc / ipc_sysctl.c
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
19 static 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 }
26
27 #ifdef CONFIG_PROC_FS
28 static 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
38 static 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
56 static 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
70 #define proc_ipc_callback_dointvec NULL
71 #endif
72
73 #ifdef CONFIG_SYSCTL_SYSCALL
74 /* The generic sysctl ipc data routine. */
75 static 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 }
112
113 static 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 }
131 #else
132 #define sysctl_ipc_data NULL
133 #define sysctl_ipc_registered_data NULL
134 #endif
135
136 static 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,
179                 .proc_handler   = proc_ipc_callback_dointvec,
180                 .strategy       = sysctl_ipc_registered_data,
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
203 static 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
213 static int __init ipc_sysctl_init(void)
214 {
215         register_sysctl_table(ipc_root_table);
216         return 0;
217 }
218
219 __initcall(ipc_sysctl_init);