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#ifdef CONFIG_PROC_SYSCTL
  30static int proc_ipc_dointvec(ctl_table *table, int write,
  31        void __user *buffer, size_t *lenp, loff_t *ppos)
  32{
  33        struct ctl_table ipc_table;
  34        memcpy(&ipc_table, table, sizeof(ipc_table));
  35        ipc_table.data = get_ipc(table);
  36
  37        return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
  38}
  39
  40static int proc_ipc_callback_dointvec(ctl_table *table, int write,
  41        void __user *buffer, size_t *lenp, loff_t *ppos)
  42{
  43        struct ctl_table ipc_table;
  44        size_t lenp_bef = *lenp;
  45        int rc;
  46
  47        memcpy(&ipc_table, table, sizeof(ipc_table));
  48        ipc_table.data = get_ipc(table);
  49
  50        rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
  51
  52        if (write && !rc && lenp_bef == *lenp)
  53                /*
  54                 * Tunable has successfully been changed by hand. Disable its
  55                 * automatic adjustment. This simply requires unregistering
  56                 * the notifiers that trigger recalculation.
  57                 */
  58                unregister_ipcns_notifier(current->nsproxy->ipc_ns);
  59
  60        return rc;
  61}
  62
  63static int proc_ipc_doulongvec_minmax(ctl_table *table, int write,
  64        void __user *buffer, size_t *lenp, loff_t *ppos)
  65{
  66        struct ctl_table ipc_table;
  67        memcpy(&ipc_table, table, sizeof(ipc_table));
  68        ipc_table.data = get_ipc(table);
  69
  70        return proc_doulongvec_minmax(&ipc_table, write, buffer,
  71                                        lenp, ppos);
  72}
  73
  74/*
  75 * Routine that is called when the file "auto_msgmni" has successfully been
  76 * written.
  77 * Two values are allowed:
  78 * 0: unregister msgmni's callback routine from the ipc namespace notifier
  79 *    chain. This means that msgmni won't be recomputed anymore upon memory
  80 *    add/remove or ipc namespace creation/removal.
  81 * 1: register back the callback routine.
  82 */
  83static void ipc_auto_callback(int val)
  84{
  85        if (!val)
  86                unregister_ipcns_notifier(current->nsproxy->ipc_ns);
  87        else {
  88                /*
  89                 * Re-enable automatic recomputing only if not already
  90                 * enabled.
  91                 */
  92                recompute_msgmni(current->nsproxy->ipc_ns);
  93                cond_register_ipcns_notifier(current->nsproxy->ipc_ns);
  94        }
  95}
  96
  97static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
  98        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, 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,
 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,
 173                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, oldval, oldlenp, newval, newlen);
 179
 180        if (newval && newlen && rc > 0)
 181                /*
 182                 * Tunable has successfully been changed from userland
 183                 */
 184                unregister_ipcns_notifier(current->nsproxy->ipc_ns);
 185
 186        return rc;
 187}
 188#else
 189#define sysctl_ipc_data NULL
 190#define sysctl_ipc_registered_data NULL
 191#endif
 192
 193static int zero;
 194static int one = 1;
 195
 196static struct ctl_table ipc_kern_table[] = {
 197        {
 198                .ctl_name       = KERN_SHMMAX,
 199                .procname       = "shmmax",
 200                .data           = &init_ipc_ns.shm_ctlmax,
 201                .maxlen         = sizeof (init_ipc_ns.shm_ctlmax),
 202                .mode           = 0644,
 203                .proc_handler   = proc_ipc_doulongvec_minmax,
 204                .strategy       = sysctl_ipc_data,
 205        },
 206        {
 207                .ctl_name       = KERN_SHMALL,
 208                .procname       = "shmall",
 209                .data           = &init_ipc_ns.shm_ctlall,
 210                .maxlen         = sizeof (init_ipc_ns.shm_ctlall),
 211                .mode           = 0644,
 212                .proc_handler   = proc_ipc_doulongvec_minmax,
 213                .strategy       = sysctl_ipc_data,
 214        },
 215        {
 216                .ctl_name       = KERN_SHMMNI,
 217                .procname       = "shmmni",
 218                .data           = &init_ipc_ns.shm_ctlmni,
 219                .maxlen         = sizeof (init_ipc_ns.shm_ctlmni),
 220                .mode           = 0644,
 221                .proc_handler   = proc_ipc_dointvec,
 222                .strategy       = sysctl_ipc_data,
 223        },
 224        {
 225                .ctl_name       = KERN_MSGMAX,
 226                .procname       = "msgmax",
 227                .data           = &init_ipc_ns.msg_ctlmax,
 228                .maxlen         = sizeof (init_ipc_ns.msg_ctlmax),
 229                .mode           = 0644,
 230                .proc_handler   = proc_ipc_dointvec,
 231                .strategy       = sysctl_ipc_data,
 232        },
 233        {
 234                .ctl_name       = KERN_MSGMNI,
 235                .procname       = "msgmni",
 236                .data           = &init_ipc_ns.msg_ctlmni,
 237                .maxlen         = sizeof (init_ipc_ns.msg_ctlmni),
 238                .mode           = 0644,
 239                .proc_handler   = proc_ipc_callback_dointvec,
 240                .strategy       = sysctl_ipc_registered_data,
 241        },
 242        {
 243                .ctl_name       = KERN_MSGMNB,
 244                .procname       =  "msgmnb",
 245                .data           = &init_ipc_ns.msg_ctlmnb,
 246                .maxlen         = sizeof (init_ipc_ns.msg_ctlmnb),
 247                .mode           = 0644,
 248                .proc_handler   = proc_ipc_dointvec,
 249                .strategy       = sysctl_ipc_data,
 250        },
 251        {
 252                .ctl_name       = KERN_SEM,
 253                .procname       = "sem",
 254                .data           = &init_ipc_ns.sem_ctls,
 255                .maxlen         = 4*sizeof (int),
 256                .mode           = 0644,
 257                .proc_handler   = proc_ipc_dointvec,
 258                .strategy       = sysctl_ipc_data,
 259        },
 260        {
 261                .ctl_name       = CTL_UNNUMBERED,
 262                .procname       = "auto_msgmni",
 263                .data           = &init_ipc_ns.auto_msgmni,
 264                .maxlen         = sizeof(int),
 265                .mode           = 0644,
 266                .proc_handler   = proc_ipcauto_dointvec_minmax,
 267                .extra1         = &zero,
 268                .extra2         = &one,
 269        },
 270        {}
 271};
 272
 273static struct ctl_table ipc_root_table[] = {
 274        {
 275                .ctl_name       = CTL_KERN,
 276                .procname       = "kernel",
 277                .mode           = 0555,
 278                .child          = ipc_kern_table,
 279        },
 280        {}
 281};
 282
 283static int __init ipc_sysctl_init(void)
 284{
 285        register_sysctl_table(ipc_root_table);
 286        return 0;
 287}
 288
 289__initcall(ipc_sysctl_init);
 290