/*
       * sysctl.c: General linux system control interface
       *
       * Begun 24 March 1995, Stephen Tweedie
       * Added /proc support, Dec 1995
       * Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
       * Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver.
       * Added kernel/java-{interpreter,appletviewer}, 96/5/10, Mike Shaver.
       * Dynamic registration fixes, Stephen Tweedie.
       * Added kswapd-interval, ctrl-alt-del, printk stuff, 1/8/97, Chris Horn.
       * Made sysctl support optional via CONFIG_SYSCTL, 1/10/97, Chris
       *  Horn.
       * Added proc_doulongvec_ms_jiffies_minmax, 09/08/99, Carlos H. Bauer.
       * Added proc_doulongvec_minmax, 09/08/99, Carlos H. Bauer.
       * Changed linked lists to use list.h instead of lists.h, 02/24/00, Bill
       *  Wendling.
       * The list_for_each() macro wasn't appropriate for the sysctl loop.
       *  Removed it and replaced it with older style, 03/23/00, Bill Wendling
       */
      
      #include <linux/config.h>
      #include <linux/malloc.h>
      #include <linux/sysctl.h>
      #include <linux/swapctl.h>
      #include <linux/proc_fs.h>
      #include <linux/ctype.h>
      #include <linux/utsname.h>
      #include <linux/capability.h>
      #include <linux/smp_lock.h>
      #include <linux/init.h>
      #include <linux/sysrq.h>
      #include <linux/highuid.h>
      
      #include <asm/uaccess.h>
      
      #ifdef CONFIG_ROOT_NFS
      #include <linux/nfs_fs.h>
      #endif
      
      #if defined(CONFIG_SYSCTL)
      
      /* External variables not in a header file. */
      extern int panic_timeout;
      extern int C_A_D;
      extern int bdf_prm[], bdflush_min[], bdflush_max[];
      extern int sysctl_overcommit_memory;
      extern int max_threads;
      extern int nr_queued_signals, max_queued_signals;
      extern int sysrq_enabled;
      
      /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
      static int maxolduid = 65535;
      static int minolduid;
      
      #ifdef CONFIG_KMOD
      extern char modprobe_path[];
      #endif
      #ifdef CONFIG_HOTPLUG
      extern char hotplug_path[];
      #endif
      #ifdef CONFIG_CHR_DEV_SG
      extern int sg_big_buff;
      #endif
      #ifdef CONFIG_SYSVIPC
      extern size_t shm_ctlmax;
      extern size_t shm_ctlall;
      extern int shm_ctlmni;
      extern int msg_ctlmax;
      extern int msg_ctlmnb;
      extern int msg_ctlmni;
      extern int sem_ctls[];
      #endif
      
      #ifdef __sparc__
      extern char reboot_command [];
      extern int stop_a_enabled;
      #endif
      #ifdef __powerpc__
      extern unsigned long htab_reclaim_on, zero_paged_on, powersave_nap;
      int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp);
      #endif
      
      #ifdef CONFIG_BSD_PROCESS_ACCT
      extern int acct_parm[];
      #endif
      
      extern int pgt_cache_water[];
      
      static int parse_table(int *, int, void *, size_t *, void *, size_t,
      		       ctl_table *, void **);
      static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp);
      
      static ctl_table root_table[];
      static struct ctl_table_header root_table_header =
      	{ root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) };
      
      static ctl_table kern_table[];
      static ctl_table vm_table[];
      #ifdef CONFIG_NET
      extern ctl_table net_table[];
      #endif
      static ctl_table proc_table[];
      static ctl_table fs_table[];
      static ctl_table debug_table[];
      static ctl_table dev_table[];
      extern ctl_table random_table[];
      
      /* /proc declarations: */
      
      #ifdef CONFIG_PROC_FS
      
      static ssize_t proc_readsys(struct file *, char *, size_t, loff_t *);
      static ssize_t proc_writesys(struct file *, const char *, size_t, loff_t *);
      static int proc_sys_permission(struct inode *, int);
      
      struct file_operations proc_sys_file_operations = {
      	read:		proc_readsys,
      	write:		proc_writesys,
      };
      
      static struct inode_operations proc_sys_inode_operations = {
      	permission:	proc_sys_permission,
      };
      
      extern struct proc_dir_entry *proc_sys_root;
      
      static void register_proc_table(ctl_table *, struct proc_dir_entry *);
      static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
      #endif
      
      extern int inodes_stat[];
      extern int dentry_stat[];
      
      /* The default sysctl tables: */
      
      static ctl_table root_table[] = {
      	{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
      	{CTL_VM, "vm", NULL, 0, 0555, vm_table},
      #ifdef CONFIG_NET
      	{CTL_NET, "net", NULL, 0, 0555, net_table},
      #endif
      	{CTL_PROC, "proc", NULL, 0, 0555, proc_table},
      	{CTL_FS, "fs", NULL, 0, 0555, fs_table},
      	{CTL_DEBUG, "debug", NULL, 0, 0555, debug_table},
              {CTL_DEV, "dev", NULL, 0, 0555, dev_table},
      	{0}
      };
      
      static ctl_table kern_table[] = {
      	{KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
      	 0444, NULL, &proc_doutsstring, &sysctl_string},
      	{KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
      	 0444, NULL, &proc_doutsstring, &sysctl_string},
      	{KERN_VERSION, "version", system_utsname.version, 64,
      	 0444, NULL, &proc_doutsstring, &sysctl_string},
      	{KERN_NODENAME, "hostname", system_utsname.nodename, 64,
      	 0644, NULL, &proc_doutsstring, &sysctl_string},
      	{KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
      	 0644, NULL, &proc_doutsstring, &sysctl_string},
      	{KERN_PANIC, "panic", &panic_timeout, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_CAP_BSET, "cap-bound", &cap_bset, sizeof(kernel_cap_t),
      	 0600, NULL, &proc_dointvec_bset},
      #ifdef CONFIG_BLK_DEV_INITRD
      	{KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      #endif
      #ifdef __sparc__
      	{KERN_SPARC_REBOOT, "reboot-cmd", reboot_command,
      	 256, 0644, NULL, &proc_dostring, &sysctl_string },
      	{KERN_SPARC_STOP_A, "stop-a", &stop_a_enabled, sizeof (int),
      	 0644, NULL, &proc_dointvec},
      #endif
      #ifdef __powerpc__
      	{KERN_PPC_HTABRECLAIM, "htab-reclaim", &htab_reclaim_on, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_PPC_ZEROPAGED, "zero-paged", &zero_paged_on, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_PPC_POWERSAVE_NAP, "powersave-nap", &powersave_nap, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_PPC_L2CR, "l2cr", NULL, 0,
      	 0644, NULL, &proc_dol2crvec},
      #endif
      	{KERN_CTLALTDEL, "ctrl-alt-del", &C_A_D, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int),
      	 0644, NULL, &proc_dointvec},
      #ifdef CONFIG_KMOD
      	{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
      	 0644, NULL, &proc_dostring, &sysctl_string },
      #endif
      #ifdef CONFIG_HOTPLUG
      	{KERN_HOTPLUG, "hotplug", &hotplug_path, 256,
      	 0644, NULL, &proc_dostring, &sysctl_string },
      #endif
      #ifdef CONFIG_CHR_DEV_SG
      	{KERN_SG_BIG_BUFF, "sg-big-buff", &sg_big_buff, sizeof (int),
      	 0444, NULL, &proc_dointvec},
      #endif
      #ifdef CONFIG_BSD_PROCESS_ACCT
      	{KERN_ACCT, "acct", &acct_parm, 3*sizeof(int),
      	0644, NULL, &proc_dointvec},
      #endif
      	{KERN_RTSIGNR, "rtsig-nr", &nr_queued_signals, sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{KERN_RTSIGMAX, "rtsig-max", &max_queued_signals, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      #ifdef CONFIG_SYSVIPC
      	{KERN_SHMMAX, "shmmax", &shm_ctlmax, sizeof (size_t),
      	 0644, NULL, &proc_doulongvec_minmax},
      	{KERN_SHMALL, "shmall", &shm_ctlall, sizeof (size_t),
      	 0644, NULL, &proc_doulongvec_minmax},
      	{KERN_SHMMNI, "shmmni", &shm_ctlmni, sizeof (int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_MSGMAX, "msgmax", &msg_ctlmax, sizeof (int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_MSGMNI, "msgmni", &msg_ctlmni, sizeof (int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_MSGMNB, "msgmnb", &msg_ctlmnb, sizeof (int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_SEM, "sem", &sem_ctls, 4*sizeof (int),
      	 0644, NULL, &proc_dointvec},
      #endif
      #ifdef CONFIG_MAGIC_SYSRQ
      	{KERN_SYSRQ, "sysrq", &sysrq_enabled, sizeof (int),
      	 0644, NULL, &proc_dointvec},
      #endif	 
      	{KERN_MAX_THREADS, "threads-max", &max_threads, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{KERN_RANDOM, "random", NULL, 0, 0555, random_table},
      	{KERN_OVERFLOWUID, "overflowuid", &overflowuid, sizeof(int), 0644, NULL,
      	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
      	 &minolduid, &maxolduid},
      	{KERN_OVERFLOWGID, "overflowgid", &overflowgid, sizeof(int), 0644, NULL,
      	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
      	 &minolduid, &maxolduid},
      	{0}
      };
      
      static ctl_table vm_table[] = {
      	{VM_FREEPG, "freepages", 
      	 &freepages, sizeof(freepages_t), 0444, NULL, &proc_dointvec},
      	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0644, NULL,
      	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
      	 &bdflush_min, &bdflush_max},
      	{VM_OVERCOMMIT_MEMORY, "overcommit_memory", &sysctl_overcommit_memory,
      	 sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec},
      	{VM_BUFFERMEM, "buffermem",
      	 &buffer_mem, sizeof(buffer_mem_t), 0644, NULL, &proc_dointvec},
      	{VM_PAGECACHE, "pagecache",
      	 &page_cache, sizeof(buffer_mem_t), 0644, NULL, &proc_dointvec},
      	{VM_PAGERDAEMON, "kswapd",
      	 &pager_daemon, sizeof(pager_daemon_t), 0644, NULL, &proc_dointvec},
      	{VM_PGT_CACHE, "pagetable_cache", 
      	 &pgt_cache_water, 2*sizeof(int), 0644, NULL, &proc_dointvec},
      	{VM_PAGE_CLUSTER, "page-cluster", 
      	 &page_cluster, sizeof(int), 0644, NULL, &proc_dointvec},
      	{0}
      };
      
      static ctl_table proc_table[] = {
      	{0}
      };
      
      static ctl_table fs_table[] = {
      	{FS_NRINODE, "inode-nr", &inodes_stat, 2*sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{FS_STATINODE, "inode-state", &inodes_stat, 7*sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{FS_NRFILE, "file-nr", &files_stat, 3*sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{FS_NRSUPER, "super-nr", &nr_super_blocks, sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{FS_MAXSUPER, "super-max", &max_super_blocks, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{FS_MAXDQUOT, "dquot-max", &max_dquots, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{FS_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int),
      	 0444, NULL, &proc_dointvec},
      	{FS_OVERFLOWUID, "overflowuid", &fs_overflowuid, sizeof(int), 0644, NULL,
      	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
      	 &minolduid, &maxolduid},
      	{FS_OVERFLOWGID, "overflowgid", &fs_overflowgid, sizeof(int), 0644, NULL,
      	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
      	 &minolduid, &maxolduid},
      	{FS_LEASES, "leases-enable", &leases_enable, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{FS_DIR_NOTIFY, "dir-notify-enable", &dir_notify_enable,
      	 sizeof(int), 0644, NULL, &proc_dointvec},
      	{FS_LEASE_TIME, "lease-break-time", &lease_break_time, sizeof(int),
      	 0644, NULL, &proc_dointvec},
      	{0}
      };
      
      static ctl_table debug_table[] = {
      	{0}
      };
      
      static ctl_table dev_table[] = {
      	{0}
      };  
      
      extern void init_irq_proc (void);
      
 311  void __init sysctl_init(void)
      {
      #ifdef CONFIG_PROC_FS
      	register_proc_table(root_table, proc_sys_root);
      	init_irq_proc();
      #endif
      }
      
 319  int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,
      	       void *newval, size_t newlen)
      {
      	struct list_head *tmp;
      
 324  	if (nlen == 0 || nlen >= CTL_MAXNAME)
 325  		return -ENOTDIR;
 326  	if (oldval) {
      		int old_len;
 328  		if (!oldlenp || get_user(old_len, oldlenp))
 329  			return -EFAULT;
      	}
      	tmp = &root_table_header.ctl_entry;
 332  	do {
      		struct ctl_table_header *head =
      			list_entry(tmp, struct ctl_table_header, ctl_entry);
      		void *context = NULL;
      		int error = parse_table(name, nlen, oldval, oldlenp, 
      					newval, newlen, head->ctl_table,
      					&context);
 339  		if (context)
      			kfree(context);
 341  		if (error != -ENOTDIR)
 342  			return error;
      		tmp = tmp->next;
 344  	} while (tmp != &root_table_header.ctl_entry);
 345  	return -ENOTDIR;
      }
      
 348  extern asmlinkage long sys_sysctl(struct __sysctl_args *args)
      {
      	struct __sysctl_args tmp;
      	int error;
      
 353  	if (copy_from_user(&tmp, args, sizeof(tmp)))
 354  		return -EFAULT;
      		
 356  	lock_kernel();
      	error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
      			  tmp.newval, tmp.newlen);
 359  	unlock_kernel();
 360  	return error;
      }
      
      /*
       * ctl_perm does NOT grant the superuser all rights automatically, because
       * some sysctl variables are readonly even to root.
       */
      
 368  static int test_perm(int mode, int op)
      {
 370  	if (!current->euid)
      		mode >>= 6;
 372  	else if (in_egroup_p(0))
      		mode >>= 3;
 374  	if ((mode & op & 0007) == op)
 375  		return 0;
 376  	return -EACCES;
      }
      
 379  static inline int ctl_perm(ctl_table *table, int op)
      {
 381  	return test_perm(table->mode, op);
      }
      
 384  static int parse_table(int *name, int nlen,
      		       void *oldval, size_t *oldlenp,
      		       void *newval, size_t newlen,
      		       ctl_table *table, void **context)
      {
      	int n;
      repeat:
 391  	if (!nlen)
 392  		return -ENOTDIR;
 393  	if (get_user(n, name))
 394  		return -EFAULT;
 395  	for ( ; table->ctl_name; table++) {
 396  		if (n == table->ctl_name || table->ctl_name == CTL_ANY) {
      			int error;
 398  			if (table->child) {
 399  				if (ctl_perm(table, 001))
 400  					return -EPERM;
 401  				if (table->strategy) {
      					error = table->strategy(
      						table, name, nlen,
      						oldval, oldlenp,
      						newval, newlen, context);
 406  					if (error)
 407  						return error;
      				}
      				name++;
      				nlen--;
      				table = table->child;
 412  				goto repeat;
      			}
      			error = do_sysctl_strategy(table, name, nlen,
      						   oldval, oldlenp,
      						   newval, newlen, context);
 417  			return error;
      		}
      	}
 420  	return -ENOTDIR;
      }
      
      /* Perform the actual read/write of a sysctl table entry. */
 424  int do_sysctl_strategy (ctl_table *table, 
      			int *name, int nlen,
      			void *oldval, size_t *oldlenp,
      			void *newval, size_t newlen, void **context)
      {
      	int op = 0, rc, len;
      
 431  	if (oldval)
      		op |= 004;
 433  	if (newval) 
      		op |= 002;
 435  	if (ctl_perm(table, op))
 436  		return -EPERM;
      
 438  	if (table->strategy) {
      		rc = table->strategy(table, name, nlen, oldval, oldlenp,
      				     newval, newlen, context);
 441  		if (rc < 0)
 442  			return rc;
 443  		if (rc > 0)
 444  			return 0;
      	}
      
      	/* If there is no strategy routine, or if the strategy returns
      	 * zero, proceed with automatic r/w */
 449  	if (table->data && table->maxlen) {
 450  		if (oldval && oldlenp) {
      			get_user(len, oldlenp);
 452  			if (len) {
 453  				if (len > table->maxlen)
      					len = table->maxlen;
 455  				if(copy_to_user(oldval, table->data, len))
 456  					return -EFAULT;
 457  				if(put_user(len, oldlenp))
 458  					return -EFAULT;
      			}
      		}
 461  		if (newval && newlen) {
      			len = newlen;
 463  			if (len > table->maxlen)
      				len = table->maxlen;
 465  			if(copy_from_user(table->data, newval, len))
 466  				return -EFAULT;
      		}
      	}
 469  	return 0;
      }
      
 472  struct ctl_table_header *register_sysctl_table(ctl_table * table, 
      					       int insert_at_head)
      {
      	struct ctl_table_header *tmp;
      	tmp = kmalloc(sizeof(struct ctl_table_header), GFP_KERNEL);
 477  	if (!tmp)
 478  		return 0;
      	tmp->ctl_table = table;
 480  	INIT_LIST_HEAD(&tmp->ctl_entry);
 481  	if (insert_at_head)
      		list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
 483  	else
      		list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
      #ifdef CONFIG_PROC_FS
      	register_proc_table(table, proc_sys_root);
      #endif
 488  	return tmp;
      }
      
      /*
       * Unlink and free a ctl_table.
       */
 494  void unregister_sysctl_table(struct ctl_table_header * header)
      {
      	list_del(&header->ctl_entry);
      #ifdef CONFIG_PROC_FS
      	unregister_proc_table(header->ctl_table, proc_sys_root);
      #endif
      	kfree(header);
      }
      
      /*
       * /proc/sys support
       */
      
      #ifdef CONFIG_PROC_FS
      
      /* Scan the sysctl entries in table and add them all into /proc */
 510  static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
      {
      	struct proc_dir_entry *de;
      	int len;
      	mode_t mode;
      	
 516  	for (; table->ctl_name; table++) {
      		/* Can't do anything without a proc name. */
 518  		if (!table->procname)
 519  			continue;
      		/* Maybe we can't do anything with it... */
 521  		if (!table->proc_handler && !table->child) {
      			printk(KERN_WARNING "SYSCTL: Can't register %s\n",
      				table->procname);
 524  			continue;
      		}
      
      		len = strlen(table->procname);
      		mode = table->mode;
      
      		de = NULL;
 531  		if (table->proc_handler)
      			mode |= S_IFREG;
 533  		else {
      			mode |= S_IFDIR;
 535  			for (de = root->subdir; de; de = de->next) {
 536  				if (proc_match(len, table->procname, de))
 537  					break;
      			}
      			/* If the subdir exists already, de is non-NULL */
      		}
      
 542  		if (!de) {
      			de = create_proc_entry(table->procname, mode, root);
 544  			if (!de)
 545  				continue;
      			de->data = (void *) table;
 547  			if (table->proc_handler) {
      				de->proc_fops = &proc_sys_file_operations;
      				de->proc_iops = &proc_sys_inode_operations;
      			}
      		}
      		table->de = de;
 553  		if (de->mode & S_IFDIR)
      			register_proc_table(table->child, de);
      	}
      }
      
      /*
       * Unregister a /proc sysctl table and any subdirectories.
       */
 561  static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
      {
      	struct proc_dir_entry *de;
 564  	for (; table->ctl_name; table++) {
 565  		if (!(de = table->de))
 566  			continue;
 567  		if (de->mode & S_IFDIR) {
 568  			if (!table->child) {
      				printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
 570  				continue;
      			}
      			unregister_proc_table(table->child, de);
      
      			/* Don't unregister directories which still have entries.. */
 575  			if (de->subdir)
 576  				continue;
      		}
      
      		/* Don't unregister proc entries that are still being used.. */
 580  		if (atomic_read(&de->count))
 581  			continue;
      
      		table->de = NULL;
      		remove_proc_entry(table->procname, root);
      	}
      }
      
 588  static ssize_t do_rw_proc(int write, struct file * file, char * buf,
      			  size_t count, loff_t *ppos)
      {
      	int op;
      	struct proc_dir_entry *de;
      	struct ctl_table *table;
      	size_t res;
      	ssize_t error;
      	
      	de = (struct proc_dir_entry*) file->f_dentry->d_inode->u.generic_ip;
 598  	if (!de || !de->data)
 599  		return -ENOTDIR;
      	table = (struct ctl_table *) de->data;
 601  	if (!table || !table->proc_handler)
 602  		return -ENOTDIR;
      	op = (write ? 002 : 004);
 604  	if (ctl_perm(table, op))
 605  		return -EPERM;
      	
      	res = count;
      
      	/*
      	 * FIXME: we need to pass on ppos to the handler.
      	 */
      
      	error = (*table->proc_handler) (table, write, file, buf, &res);
 614  	if (error)
 615  		return error;
 616  	return res;
      }
      
 619  static ssize_t proc_readsys(struct file * file, char * buf,
      			    size_t count, loff_t *ppos)
      {
 622  	return do_rw_proc(0, file, buf, count, ppos);
      }
      
 625  static ssize_t proc_writesys(struct file * file, const char * buf,
      			     size_t count, loff_t *ppos)
      {
 628  	return do_rw_proc(1, file, (char *) buf, count, ppos);
      }
      
 631  static int proc_sys_permission(struct inode *inode, int op)
      {
 633  	return test_perm(inode->i_mode, op);
      }
      
 636  int proc_dostring(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	int len;
      	char *p, c;
      	
      	if (!table->data || !table->maxlen || !*lenp ||
 643  	    (filp->f_pos && !write)) {
      		*lenp = 0;
 645  		return 0;
      	}
      	
 648  	if (write) {
      		len = 0;
      		p = buffer;
 651  		while (len < *lenp) {
 652  			if(get_user(c, p++))
 653  				return -EFAULT;
 654  			if (c == 0 || c == '\n')
 655  				break;
      			len++;
      		}
 658  		if (len >= table->maxlen)
      			len = table->maxlen-1;
 660  		if(copy_from_user(table->data, buffer, len))
 661  			return -EFAULT;
      		((char *) table->data)[len] = 0;
      		filp->f_pos += *lenp;
 664  	} else {
      		len = strlen(table->data);
 666  		if (len > table->maxlen)
      			len = table->maxlen;
 668  		if (len > *lenp)
      			len = *lenp;
 670  		if (len)
 671  			if(copy_to_user(buffer, table->data, len))
 672  				return -EFAULT;
 673  		if (len < *lenp) {
 674  			if(put_user('\n', ((char *) buffer) + len))
 675  				return -EFAULT;
      			len++;
      		}
      		*lenp = len;
      		filp->f_pos += len;
      	}
 681  	return 0;
      }
      
      /*
       *	Special case of dostring for the UTS structure. This has locks
       *	to observe. Should this be in kernel/sys.c ????
       */
       
 689  static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	int r;
      
 694  	if (!write) {
      		down_read(&uts_sem);
      		r=proc_dostring(table,0,filp,buffer,lenp);
      		up_read(&uts_sem);
 698  	} else {
      		down_write(&uts_sem);
      		r=proc_dostring(table,1,filp,buffer,lenp);
      		up_write(&uts_sem);
      	}
 703  	return r;
      }
      
      #define OP_SET	0
      #define OP_AND	1
      #define OP_OR	2
      #define OP_MAX	3
      #define OP_MIN	4
      
 712  static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp, int conv, int op)
      {
      	int *i, vleft, first=1, len, left, neg, val;
      	#define TMPBUFLEN 20
      	char buf[TMPBUFLEN], *p;
      	
      	if (!table->data || !table->maxlen || !*lenp ||
 720  	    (filp->f_pos && !write)) {
      		*lenp = 0;
 722  		return 0;
      	}
      	
      	i = (int *) table->data;
      	vleft = table->maxlen / sizeof(int);
      	left = *lenp;
      	
 729  	for (; left && vleft--; i++, first=0) {
 730  		if (write) {
 731  			while (left) {
      				char c;
 733  				if(get_user(c,(char *) buffer))
 734  					return -EFAULT;
 735  				if (!isspace(c))
 736  					break;
      				left--;
      				((char *) buffer)++;
      			}
 740  			if (!left)
 741  				break;
      			neg = 0;
      			len = left;
 744  			if (len > TMPBUFLEN-1)
      				len = TMPBUFLEN-1;
 746  			if(copy_from_user(buf, buffer, len))
 747  				return -EFAULT;
      			buf[len] = 0;
      			p = buf;
 750  			if (*p == '-' && left > 1) {
      				neg = 1;
      				left--, p++;
      			}
 754  			if (*p < '0' || *p > '9')
 755  				break;
      			val = simple_strtoul(p, &p, 0) * conv;
      			len = p-buf;
 758  			if ((len < left) && *p && !isspace(*p))
 759  				break;
 760  			if (neg)
      				val = -val;
      			buffer += len;
      			left -= len;
 764  			switch(op) {
 765  			case OP_SET:	*i = val; break;
 766  			case OP_AND:	*i &= val; break;
 767  			case OP_OR:	*i |= val; break;
 768  			case OP_MAX:	if(*i < val)
      						*i = val;
 770  					break;
 771  			case OP_MIN:	if(*i > val)
      						*i = val;
 773  					break;
      			}
 775  		} else {
      			p = buf;
 777  			if (!first)
      				*p++ = '\t';
      			sprintf(p, "%d", (*i) / conv);
      			len = strlen(buf);
 781  			if (len > left)
      				len = left;
 783  			if(copy_to_user(buffer, buf, len))
 784  				return -EFAULT;
      			left -= len;
      			buffer += len;
      		}
      	}
      
 790  	if (!write && !first && left) {
 791  		if(put_user('\n', (char *) buffer))
 792  			return -EFAULT;
      		left--, buffer++;
      	}
 795  	if (write) {
      		p = (char *) buffer;
 797  		while (left) {
      			char c;
 799  			if(get_user(c, p++))
 800  				return -EFAULT;
 801  			if (!isspace(c))
 802  				break;
      			left--;
      		}
      	}
 806  	if (write && first)
 807  		return -EINVAL;
      	*lenp -= left;
      	filp->f_pos += *lenp;
 810  	return 0;
      }
      
 813  int proc_dointvec(ctl_table *table, int write, struct file *filp,
      		     void *buffer, size_t *lenp)
      {
 816      return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET);
      }
      
      /*
       *	init may raise the set.
       */
       
 823  int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
      			void *buffer, size_t *lenp)
      {
 826  	if (!capable(CAP_SYS_MODULE)) {
 827  		return -EPERM;
      	}
      	return do_proc_dointvec(table,write,filp,buffer,lenp,1,
 830  				(current->pid == 1) ? OP_SET : OP_AND);
      }
      
 833  int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	int *i, *min, *max, vleft, first=1, len, left, neg, val;
      	#define TMPBUFLEN 20
      	char buf[TMPBUFLEN], *p;
      	
      	if (!table->data || !table->maxlen || !*lenp ||
 841  	    (filp->f_pos && !write)) {
      		*lenp = 0;
 843  		return 0;
      	}
      	
      	i = (int *) table->data;
      	min = (int *) table->extra1;
      	max = (int *) table->extra2;
      	vleft = table->maxlen / sizeof(int);
      	left = *lenp;
      	
 852  	for (; left && vleft--; i++, first=0) {
 853  		if (write) {
 854  			while (left) {
      				char c;
 856  				if(get_user(c, (char *) buffer))
 857  					return -EFAULT;
 858  				if (!isspace(c))
 859  					break;
      				left--;
      				((char *) buffer)++;
      			}
 863  			if (!left)
 864  				break;
      			neg = 0;
      			len = left;
 867  			if (len > TMPBUFLEN-1)
      				len = TMPBUFLEN-1;
 869  			if(copy_from_user(buf, buffer, len))
 870  				return -EFAULT;
      			buf[len] = 0;
      			p = buf;
 873  			if (*p == '-' && left > 1) {
      				neg = 1;
      				left--, p++;
      			}
 877  			if (*p < '0' || *p > '9')
 878  				break;
      			val = simple_strtoul(p, &p, 0);
      			len = p-buf;
 881  			if ((len < left) && *p && !isspace(*p))
 882  				break;
 883  			if (neg)
      				val = -val;
      			buffer += len;
      			left -= len;
      
 888  			if (min && val < *min++)
 889  				continue;
 890  			if (max && val > *max++)
 891  				continue;
      			*i = val;
 893  		} else {
      			p = buf;
 895  			if (!first)
      				*p++ = '\t';
      			sprintf(p, "%d", *i);
      			len = strlen(buf);
 899  			if (len > left)
      				len = left;
 901  			if(copy_to_user(buffer, buf, len))
 902  				return -EFAULT;
      			left -= len;
      			buffer += len;
      		}
      	}
      
 908  	if (!write && !first && left) {
 909  		if(put_user('\n', (char *) buffer))
 910  			return -EFAULT;
      		left--, buffer++;
      	}
 913  	if (write) {
      		p = (char *) buffer;
 915  		while (left) {
      			char c;
 917  			if(get_user(c, p++))
 918  				return -EFAULT;
 919  			if (!isspace(c))
 920  				break;
      			left--;
      		}
      	}
 924  	if (write && first)
 925  		return -EINVAL;
      	*lenp -= left;
      	filp->f_pos += *lenp;
 928  	return 0;
      }
      
      /*
       * an unsigned long function version
       */
      
 935  static int do_proc_doulongvec_minmax(ctl_table *table, int write,
      				     struct file *filp,
      				     void *buffer, size_t *lenp,
      				     unsigned long convmul,
      				     unsigned long convdiv)
      {
      #define TMPBUFLEN 20
      	unsigned long *i, *min, *max, val;
      	int vleft, first=1, len, left, neg;
      	char buf[TMPBUFLEN], *p;
      	
      	if (!table->data || !table->maxlen || !*lenp ||
 947  	    (filp->f_pos && !write)) {
      		*lenp = 0;
 949  		return 0;
      	}
      	
      	i = (unsigned long *) table->data;
      	min = (unsigned long *) table->extra1;
      	max = (unsigned long *) table->extra2;
      	vleft = table->maxlen / sizeof(unsigned long);
      	left = *lenp;
      	
 958  	for (; left && vleft--; i++, first=0) {
 959  		if (write) {
 960  			while (left) {
      				char c;
 962  				if(get_user(c, (char *) buffer))
 963  					return -EFAULT;
 964  				if (!isspace(c))
 965  					break;
      				left--;
      				((char *) buffer)++;
      			}
 969  			if (!left)
 970  				break;
      			neg = 0;
      			len = left;
 973  			if (len > TMPBUFLEN-1)
      				len = TMPBUFLEN-1;
 975  			if(copy_from_user(buf, buffer, len))
 976  				return -EFAULT;
      			buf[len] = 0;
      			p = buf;
 979  			if (*p == '-' && left > 1) {
      				neg = 1;
      				left--, p++;
      			}
 983  			if (*p < '0' || *p > '9')
 984  				break;
      			val = simple_strtoul(p, &p, 0) * convmul / convdiv ;
      			len = p-buf;
 987  			if ((len < left) && *p && !isspace(*p))
 988  				break;
 989  			if (neg)
      				val = -val;
      			buffer += len;
      			left -= len;
      
 994  			if(neg)
 995  				continue;
 996  			if (min && val < *min++)
 997  				continue;
 998  			if (max && val > *max++)
 999  				continue;
      			*i = val;
1001  		} else {
      			p = buf;
1003  			if (!first)
      				*p++ = '\t';
      			sprintf(p, "%lu", convdiv * (*i) / convmul);
      			len = strlen(buf);
1007  			if (len > left)
      				len = left;
1009  			if(copy_to_user(buffer, buf, len))
1010  				return -EFAULT;
      			left -= len;
      			buffer += len;
      		}
      	}
      
1016  	if (!write && !first && left) {
1017  		if(put_user('\n', (char *) buffer))
1018  			return -EFAULT;
      		left--, buffer++;
      	}
1021  	if (write) {
      		p = (char *) buffer;
1023  		while (left) {
      			char c;
1025  			if(get_user(c, p++))
1026  				return -EFAULT;
1027  			if (!isspace(c))
1028  				break;
      			left--;
      		}
      	}
1032  	if (write && first)
1033  		return -EINVAL;
      	*lenp -= left;
      	filp->f_pos += *lenp;
1036  	return 0;
      #undef TMPBUFLEN
      }
      
1040  int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
      			   void *buffer, size_t *lenp)
      {
1043      return do_proc_doulongvec_minmax(table, write, filp, buffer, lenp, 1l, 1l);
      }
      
1046  int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
      				      struct file *filp,
      				      void *buffer, size_t *lenp)
      {
          return do_proc_doulongvec_minmax(table, write, filp, buffer,
1051  				     lenp, HZ, 1000l);
      }
      
      
      /* Like proc_dointvec, but converts seconds to jiffies */
1056  int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
      			  void *buffer, size_t *lenp)
      {
1059      return do_proc_dointvec(table,write,filp,buffer,lenp,HZ,OP_SET);
      }
      
      #else /* CONFIG_PROC_FS */
      
      int proc_dostring(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
      			    void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
      			void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
      		    void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
      		    void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
      		    void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
      				      struct file *filp,
      				      void *buffer, size_t *lenp)
      {
          return -ENOSYS;
      }
      
      
      #endif /* CONFIG_PROC_FS */
      
      
      /*
       * General sysctl support routines 
       */
      
      /* The generic string strategy routine: */
1122  int sysctl_string(ctl_table *table, int *name, int nlen,
      		  void *oldval, size_t *oldlenp,
      		  void *newval, size_t newlen, void **context)
      {
      	int l, len;
      	
1128  	if (!table->data || !table->maxlen) 
1129  		return -ENOTDIR;
      	
1131  	if (oldval && oldlenp) {
1132  		if(get_user(len, oldlenp))
1133  			return -EFAULT;
1134  		if (len) {
      			l = strlen(table->data);
1136  			if (len > l) len = l;
1137  			if (len >= table->maxlen)
      				len = table->maxlen;
1139  			if(copy_to_user(oldval, table->data, len))
1140  				return -EFAULT;
1141  			if(put_user(0, ((char *) oldval) + len))
1142  				return -EFAULT;
1143  			if(put_user(len, oldlenp))
1144  				return -EFAULT;
      		}
      	}
1147  	if (newval && newlen) {
      		len = newlen;
1149  		if (len > table->maxlen)
      			len = table->maxlen;
1151  		if(copy_from_user(table->data, newval, len))
1152  			return -EFAULT;
1153  		if (len == table->maxlen)
      			len--;
      		((char *) table->data)[len] = 0;
      	}
1157  	return 0;
      }
      
      /*
       * This function makes sure that all of the integers in the vector
       * are between the minimum and maximum values given in the arrays
       * table->extra1 and table->extra2, respectively.
       */
1165  int sysctl_intvec(ctl_table *table, int *name, int nlen,
      		void *oldval, size_t *oldlenp,
      		void *newval, size_t newlen, void **context)
      {
      	int i, length, *vec, *min, *max;
      
1171  	if (newval && newlen) {
1172  		if (newlen % sizeof(int) != 0)
1173  			return -EINVAL;
      
1175  		if (!table->extra1 && !table->extra2)
1176  			return 0;
      
1178  		if (newlen > table->maxlen)
      			newlen = table->maxlen;
      		length = newlen / sizeof(int);
      
      		vec = (int *) newval;
      		min = (int *) table->extra1;
      		max = (int *) table->extra2;
      
1186  		for (i = 0; i < length; i++) {
      			int value;
      			get_user(value, vec + i);
1189  			if (min && value < min[i])
1190  				return -EINVAL;
1191  			if (max && value > max[i])
1192  				return -EINVAL;
      		}
      	}
1195  	return 0;
      }
      
      /* Strategy function to convert jiffies to seconds */ 
1199  int sysctl_jiffies(ctl_table *table, int *name, int nlen,
      		void *oldval, size_t *oldlenp,
      		void *newval, size_t newlen, void **context)
      {
1203  	if (oldval) {
      		size_t olen;
1205  		if (oldlenp) { 
1206  			if (get_user(olen, oldlenp))
1207  				return -EFAULT;
1208  			if (olen!=sizeof(int))
1209  				return -EINVAL; 
      		}
      		if (put_user(*(int *)(table->data) / HZ, (int *)oldval) || 
1212  		    (oldlenp && put_user(sizeof(int),oldlenp)))
1213  			return -EFAULT;
      	}
1215  	if (newval && newlen) { 
      		int new;
1217  		if (newlen != sizeof(int))
1218  			return -EINVAL; 
1219  		if (get_user(new, (int *)newval))
1220  			return -EFAULT;
      		*(int *)(table->data) = new*HZ; 
      	}
1223  	return 1;
      }
      
      
      #else /* CONFIG_SYSCTL */
      
      
      extern asmlinkage long sys_sysctl(struct __sysctl_args *args)
      {
      	return -ENOSYS;
      }
      
      int sysctl_string(ctl_table *table, int *name, int nlen,
      		  void *oldval, size_t *oldlenp,
      		  void *newval, size_t newlen, void **context)
      {
      	return -ENOSYS;
      }
      
      int sysctl_intvec(ctl_table *table, int *name, int nlen,
      		void *oldval, size_t *oldlenp,
      		void *newval, size_t newlen, void **context)
      {
      	return -ENOSYS;
      }
      
      int sysctl_jiffies(ctl_table *table, int *name, int nlen,
      		void *oldval, size_t *oldlenp,
      		void *newval, size_t newlen, void **context)
      {
      	return -ENOSYS;
      }
      
      int proc_dostring(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec(ctl_table *table, int write, struct file *filp,
      		  void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
      			void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
      		    void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
      			  void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
      		    void *buffer, size_t *lenp)
      {
      	return -ENOSYS;
      }
      
      int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
      				      struct file *filp,
      				      void *buffer, size_t *lenp)
      {
          return -ENOSYS;
      }
      
      struct ctl_table_header * register_sysctl_table(ctl_table * table, 
      						int insert_at_head)
      {
      	return 0;
      }
      
      void unregister_sysctl_table(struct ctl_table_header * table)
      {
      }
      
      #endif /* CONFIG_SYSCTL */