linux/ipc/ipc_sysctl.c
<<
>>
Prefs
   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(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/*
  30 * Routine that is called when the file "auto_msgmni" has successfully been
  31 * written.
  32 * Two values are allowed:
  33 * 0: unregister msgmni's callback routine from the ipc namespace notifier
  34 *    chain. This means that msgmni won't be recomputed anymore upon memory
  35 *    add/remove or ipc namespace creation/removal.
  36 * 1: register back the callback routine.
  37 */
  38static void ipc_auto_callback(int val)
  39{
  40        if (!val)
  41                unregister_ipcns_notifier(current->nsproxy->ipc_ns);
  42        else {
  43                /*
  44                 * Re-enable automatic recomputing only if not already
  45                 * enabled.
  46                 */
  47                recompute_msgmni(current->nsproxy->ipc_ns);
  48                cond_register_ipcns_notifier(current->nsproxy->ipc_ns);
  49        }
  50}
  51
  52#ifdef CONFIG_PROC_FS
  53static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp,
  54        void __user *buffer, size_t *lenp, loff_t *ppos)
  55{
  56        struct ctl_table ipc_table;
  57        memcpy(&ipc_table, table, sizeof(ipc_table));
  58        ipc_table.data = get_ipc(table);
  59
  60        return proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos);
  61}
  62
  63static int proc_ipc_callback_dointvec(ctl_table *table, int write,
  64        struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
  65{
  66        struct ctl_table ipc_table;
  67        size_t lenp_bef = *lenp;
  68        int rc;
  69
  70        memcpy(&ipc_table, table, sizeof(ipc_table));
  71        ipc_table.data = get_ipc(table);
  72
  73        rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos);
  74
  75        if (write && !rc && lenp_bef == *lenp)
  76                /*
  77                 * Tunable has successfully been changed by hand. Disable its
  78                 * automatic adjustment. This simply requires unregistering
  79                 * the notifiers that trigger recalculation.
  80                 */
  81                unregister_ipcns_notifier(current->nsproxy->ipc_ns);
  82
  83        return rc;
  84}
  85
  86static int proc_ipc_doulongvec_minmax(ctl_table *table, int write,
  87        struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
  88{
  89        struct ctl_table ipc_table;
  90        memcpy(&ipc_table, table, sizeof(ipc_table));
  91        ipc_table.data = get_ipc(table);
  92
  93        return proc_doulongvec_minmax(&ipc_table, write, filp, buffer,
  94                                        lenp, ppos);
  95}
  96
  97static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
  98        struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
  99{
 100        struct ctl_table ipc_table;
 101        size_t lenp_bef = *lenp;
 102        int oldval;
 103        int rc;
 104
 105        memcpy(&ipc_table, table, sizeof(ipc_table));
 106        ipc_table.data = get_ipc(table);
 107        oldval = *((int *)(ipc_table.data));
 108
 109        rc = proc_dointvec_minmax(&ipc_table, write, filp, buffer, lenp, ppos);
 110
 111        if (write && !rc && lenp_bef == *lenp) {
 112                int newval = *((int *)(ipc_table.data));
 113                /*
 114                 * The file "auto_msgmni" has correctly been set.
 115                 * React by (un)registering the corresponding tunable, if the
 116                 * value has changed.
 117                 */
 118                if (newval != oldval)
 119                        ipc_auto_callback(newval);
 120        }
 121
 122        return rc;
 123}
 124
 125#else
 126#define proc_ipc_doulongvec_minmax NULL
 127#define proc_ipc_dointvec          NULL
 128#define proc_ipc_callback_dointvec NULL
 129#define proc_ipcauto_dointvec_minmax NULL
 130#endif
 131
 132#ifdef CONFIG_SYSCTL_SYSCALL
 133/* The generic sysctl ipc data routine. */
 134static int sysctl_ipc_data(ctl_table *table, int __user *name, int nlen,
 135                void __user *oldval, size_t __user *oldlenp,
 136                void __user *newval, size_t newlen)
 137{
 138        size_t len;
 139        void *data;
 140
 141        /* Get out of I don't have a variable */
 142        if (!table->data || !table->maxlen)
 143                return -ENOTDIR;
 144
 145        data = get_ipc(table);
 146        if (!data)
 147                return -ENOTDIR;
 148
 149        if (oldval && oldlenp) {
 150                if (get_user(len, oldlenp))
 151                        return -EFAULT;
 152                if (len) {
 153                        if (len > table->maxlen)
 154                                len = table->maxlen;
 155                        if (copy_to_user(oldval, data, len))
 156                                return -EFAULT;
 157                        if (put_user(len, oldlenp))
 158                                return -EFAULT;
 159                }
 160        }
 161
 162        if (newval && newlen) {
 163                if (newlen > table->maxlen)
 164                        newlen = table->maxlen;
 165
 166                if (copy_from_user(data, newval, newlen))
 167                        return -EFAULT;
 168        }
 169        return 1;
 170}
 171
 172static int sysctl_ipc_registered_data(ctl_table *table, int __user *name,
 173                int nlen, void __user *oldval, size_t __user *oldlenp,
 174                void __user *newval, size_t newlen)
 175{
 176        int rc;
 177
 178        rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval,
 179                newlen);
 180
 181        if (newval && newlen && rc > 0)
 182                /*
 183                 * Tunable has successfully been changed from userland
 184                 */
 185                unregister_ipcns_notifier(current->nsproxy->ipc_ns);
 186
 187        return rc;
 188}
 189#else
 190#define sysctl_ipc_data NULL
 191#define sysctl_ipc_registered_data NULL
 192#endif
 193
 194static int zero;
 195static int one = 1;
 196
 197static struct ctl_table ipc_kern_table[] = {
 198        {
 199                .ctl_name       = KERN_SHMMAX,
 200                .procname       = "shmmax",
 201                .data           = &init_ipc_ns.shm_ctlmax,
 202                .maxlen         = sizeof (init_ipc_ns.shm_ctlmax),
 203                .mode           = 0644,
 204                .proc_handler   = proc_ipc_doulongvec_minmax,
 205                .strategy       = sysctl_ipc_data,
 206        },
 207        {
 208                .ctl_name       = KERN_SHMALL,
 209                .procname       = "shmall",
 210                .data           = &init_ipc_ns.shm_ctlall,
 211                .maxlen         = sizeof (init_ipc_ns.shm_ctlall),
 212                .mode           = 0644,
 213                .proc_handler   = proc_ipc_doulongvec_minmax,
 214                .strategy       = sysctl_ipc_data,
 215        },
 216        {
 217                .ctl_name       = KERN_SHMMNI,
 218                .procname       = "shmmni",
 219                .data           = &init_ipc_ns.shm_ctlmni,
 220                .maxlen         = sizeof (init_ipc_ns.shm_ctlmni),
 221                .mode           = 0644,
 222                .proc_handler   = proc_ipc_dointvec,
 223                .strategy       = sysctl_ipc_data,
 224        },
 225        {
 226                .ctl_name       = KERN_MSGMAX,
 227                .procname       = "msgmax",
 228                .data           = &init_ipc_ns.msg_ctlmax,
 229                .maxlen         = sizeof (init_ipc_ns.msg_ctlmax),
 230                .mode           = 0644,
 231                .proc_handler   = proc_ipc_dointvec,
 232                .strategy       = sysctl_ipc_data,
 233        },
 234        {
 235                .ctl_name       = KERN_MSGMNI,
 236                .procname       = "msgmni",
 237                .data           = &init_ipc_ns.msg_ctlmni,
 238                .maxlen         = sizeof (init_ipc_ns.msg_ctlmni),
 239                .mode           = 0644,
 240                .proc_handler   = proc_ipc_callback_dointvec,
 241                .strategy       = sysctl_ipc_registered_data,
 242        },
 243        {
 244                .ctl_name       = KERN_MSGMNB,
 245                .procname       =  "msgmnb",
 246                .data           = &init_ipc_ns.msg_ctlmnb,
 247                .maxlen         = sizeof (init_ipc_ns.msg_ctlmnb),
 248                .mode           = 0644,
 249                .proc_handler   = proc_ipc_dointvec,
 250                .strategy       = sysctl_ipc_data,
 251        },
 252        {
 253                .ctl_name       = KERN_SEM,
 254                .procname       = "sem",
 255                .data           = &init_ipc_ns.sem_ctls,
 256                .maxlen         = 4*sizeof (int),
 257                .mode           = 0644,
 258                .proc_handler   = proc_ipc_dointvec,
 259                .strategy       = sysctl_ipc_data,
 260        },
 261        {
 262                .ctl_name       = CTL_UNNUMBERED,
 263                .procname       = "auto_msgmni",
 264                .data           = &init_ipc_ns.auto_msgmni,
 265                .maxlen         = sizeof(int),
 266                .mode           = 0644,
 267                .proc_handler   = proc_ipcauto_dointvec_minmax,
 268                .extra1         = &zero,
 269                .extra2         = &one,
 270        },
 271        {}
 272};
 273
 274static struct ctl_table ipc_root_table[] = {
 275        {
 276                .ctl_name       = CTL_KERN,
 277                .procname       = "kernel",
 278                .mode           = 0555,
 279                .child          = ipc_kern_table,
 280        },
 281        {}
 282};
 283
 284static int __init ipc_sysctl_init(void)
 285{
 286        register_sysctl_table(ipc_root_table);
 287        return 0;
 288}
 289
 290__initcall(ipc_sysctl_init);
 291