?? sysctl.c
字號:
* The members of the &ctl_table structure are used as follows: * * ctl_name - This is the numeric sysctl value used by sysctl(2). The number * must be unique within that level of sysctl * * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not * enter a sysctl file * * data - a pointer to data for use by proc_handler * * maxlen - the maximum size in bytes of the data * * mode - the file permissions for the /proc/sys file, and for sysctl(2) * * child - a pointer to the child sysctl table if this entry is a directory, or * %NULL. * * proc_handler - the text handler routine (described below) * * strategy - the strategy routine (described below) * * de - for internal use by the sysctl routines * * extra1, extra2 - extra pointers usable by the proc handler routines * * Leaf nodes in the sysctl tree will be represented by a single file * under /proc; non-leaf nodes will be represented by directories. * * sysctl(2) can automatically manage read and write requests through * the sysctl table. The data and maxlen fields of the ctl_table * struct enable minimal validation of the values being written to be * performed, and the mode field allows minimal authentication. * * More sophisticated management can be enabled by the provision of a * strategy routine with the table entry. This will be called before * any automatic read or write of the data is performed. * * The strategy routine may return * * < 0 - Error occurred (error is passed to user process) * * 0 - OK - proceed with automatic read or write. * * > 0 - OK - read or write has been done by the strategy routine, so * return immediately. * * There must be a proc_handler routine for any terminal nodes * mirrored under /proc/sys (non-terminals are handled by a built-in * directory handler). Several default handlers are available to * cover common cases - * * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), * proc_dointvec_minmax(), proc_doulongvec_ms_jiffies_minmax(), * proc_doulongvec_minmax() * * It is the handler's job to read the input buffer from user memory * and process it. The handler should return 0 on success. * * This routine returns %NULL on a failure to register, and a pointer * to the table header on success. */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); if (!tmp) return NULL; tmp->ctl_table = table; INIT_LIST_HEAD(&tmp->ctl_entry); if (insert_at_head) list_add(&tmp->ctl_entry, &root_table_header.ctl_entry); else list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);#ifdef CONFIG_PROC_FS register_proc_table(table, proc_sys_root);#endif return tmp;}/** * unregister_sysctl_table - unregister a sysctl table heirarchy * @header: the header returned from register_sysctl_table * * Unregisters the sysctl table and all children. proc entries may not * actually be removed until they are no longer used by anyone. */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 */static void register_proc_table(ctl_table * table, struct proc_dir_entry *root){ struct proc_dir_entry *de; int len; mode_t mode; for (; table->ctl_name; table++) { /* Can't do anything without a proc name. */ if (!table->procname) continue; /* Maybe we can't do anything with it... */ if (!table->proc_handler && !table->child) { printk(KERN_WARNING "SYSCTL: Can't register %s\n", table->procname); continue; } len = strlen(table->procname); mode = table->mode; de = NULL; if (table->proc_handler) mode |= S_IFREG; else { mode |= S_IFDIR; for (de = root->subdir; de; de = de->next) { if (proc_match(len, table->procname, de)) break; } /* If the subdir exists already, de is non-NULL */ } if (!de) { de = create_proc_entry(table->procname, mode, root); if (!de) continue; de->data = (void *) table; if (table->proc_handler) { de->proc_fops = &proc_sys_file_operations; de->proc_iops = &proc_sys_inode_operations; } } table->de = de; if (de->mode & S_IFDIR) register_proc_table(table->child, de); }}/* * Unregister a /proc sysctl table and any subdirectories. */static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root){ struct proc_dir_entry *de; for (; table->ctl_name; table++) { if (!(de = table->de)) continue; if (de->mode & S_IFDIR) { if (!table->child) { printk (KERN_ALERT "Help - malformed sysctl tree on free\n"); continue; } unregister_proc_table(table->child, de); /* Don't unregister directories which still have entries.. */ if (de->subdir) continue; } /* Don't unregister proc entries that are still being used.. */ if (atomic_read(&de->count)) continue; table->de = NULL; remove_proc_entry(table->procname, root); }}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; if (!de || !de->data) return -ENOTDIR; table = (struct ctl_table *) de->data; if (!table || !table->proc_handler) return -ENOTDIR; op = (write ? 002 : 004); if (ctl_perm(table, op)) return -EPERM; res = count; /* * FIXME: we need to pass on ppos to the handler. */ error = (*table->proc_handler) (table, write, file, buf, &res); if (error) return error; return res;}static ssize_t proc_readsys(struct file * file, char * buf, size_t count, loff_t *ppos){ return do_rw_proc(0, file, buf, count, ppos);}static ssize_t proc_writesys(struct file * file, const char * buf, size_t count, loff_t *ppos){ return do_rw_proc(1, file, (char *) buf, count, ppos);}static int proc_sys_permission(struct inode *inode, int op){ return test_perm(inode->i_mode, op);}/** * proc_dostring - read a string sysctl * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * * Reads/writes a string from/to the user buffer. If the kernel * buffer provided is not large enough to hold the string, the * string is truncated. The copied string is %NULL-terminated. * If the string is being read by the user process, it is copied * and a newline '\n' is added. It is truncated if the buffer is * not large enough. * * Returns 0 on success. */int proc_dostring(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp){ size_t len; char *p, c; if (!table->data || !table->maxlen || !*lenp || (filp->f_pos && !write)) { *lenp = 0; return 0; } if (write) { len = 0; p = buffer; while (len < *lenp) { if(get_user(c, p++)) return -EFAULT; if (c == 0 || c == '\n') break; len++; } if (len >= table->maxlen) len = table->maxlen-1; if(copy_from_user(table->data, buffer, len)) return -EFAULT; ((char *) table->data)[len] = 0; filp->f_pos += *lenp; } else { len = strlen(table->data); if (len > table->maxlen) len = table->maxlen; if (len > *lenp) len = *lenp; if (len) if(copy_to_user(buffer, table->data, len)) return -EFAULT; if (len < *lenp) { if(put_user('\n', ((char *) buffer) + len)) return -EFAULT; len++; } *lenp = len; filp->f_pos += len; } return 0;}/* * Special case of dostring for the UTS structure. This has locks * to observe. Should this be in kernel/sys.c ???? */ static int proc_doutsstring(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp){ int r; if (!write) { down_read(&uts_sem); r=proc_dostring(table,0,filp,buffer,lenp); up_read(&uts_sem); } else { down_write(&uts_sem); r=proc_dostring(table,1,filp,buffer,lenp); up_write(&uts_sem); } return r;}#define OP_SET 0#define OP_AND 1#define OP_OR 2#define OP_MAX 3#define OP_MIN 4static 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, neg, val; size_t left, len; #define TMPBUFLEN 20 char buf[TMPBUFLEN], *p; if (!table->data || !table->maxlen || !*lenp || (filp->f_pos && !write)) { *lenp = 0; return 0; } i = (int *) table->data; vleft = table->maxlen / sizeof(int); left = *lenp; for (; left && vleft--; i++, first=0) { if (write) { while (left) { char c; if(get_user(c,(char *) buffer)) return -EFAULT; if (!isspace(c)) break; left--; ((char *) buffer)++; } if (!left) break; neg = 0; len = left; if (len > TMPBUFLEN-1) len = TMPBUFLEN-1; if(copy_from_user(buf, buffer, len)) return -EFAULT; buf[len] = 0; p = buf; if (*p == '-' && left > 1) { neg = 1; left--, p++; } if (*p < '0' || *p > '9') break; val = simple_strtoul(p, &p, 0) * conv; len = p-buf; if ((len < left) && *p && !isspace(*p)) break; if (neg) val = -val; buffer += len; left -= len; switch(op) { case OP_SET: *i = val; break; case OP_AND: *i &= val; break; case OP_OR: *i |= val; break; case OP_MAX: if(*i < val) *i = val; break; case OP_MIN: if(*i > val) *i = val; break; } } else { p = buf; if (!first) *p++ = '\t'; sprintf(p, "%d", (*i) / conv); len = strlen(buf); if (len > left) len = left; if(copy_to_user(buffer, buf, len)) return -EFAULT; left -= len; buffer += len; } } if (!write && !first && left) { if(put_user('\n', (char *) buffer)) return -EFAULT; left--, buffer++; } if (write) { p = (char *) buffer; while (left) { char c; if(get_user(c, p++)) return -EFAULT; if (!isspace(c)) break; left--; } } if (write && first) return -EINVAL; *lenp -= left; filp->f_pos += *lenp; return 0;}/** * proc_dointvec - read a vector of integers * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success. */int proc_dointvec(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp){ return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET);}/* * init may raise the set. */ int proc_dointvec_bset(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp){ if (!capable(CAP_SYS_MODULE)) { return -EPERM; } return do_proc_dointvec(table,write,filp,buffer,lenp,1, (current->pid == 1) ? OP_SET : OP_AND);}/** * proc_dointvec_minmax - read a vector of integers with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success. */int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp){ int *i, *min, *max, vleft, first=1, neg, val; size_t len, left; #define TMPBUFLEN 20 char buf[TMPBUFLEN], *p; if (!table->data || !table->maxlen || !*lenp || (filp->f_pos && !write)) { *lenp = 0; return 0; } i = (int *) table->data; min = (int *) table->extra1; max = (int *) table->extra2; vleft = table->maxlen / sizeof(int); left = *lenp; for (; left && vleft--; i++, first=0) { if (write) { while (left) { char c; if(get_user(c, (char *) buffer)) return -EFAULT;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -