This source file includes following definitions.
- sysctl_init
- do_sysctl
- sys_sysctl
- in_egroup_p
- test_perm
- ctl_perm
- parse_table
- do_sysctl_strategy
- do_securelevel_strategy
- register_sysctl_table
- unregister_sysctl_table
- register_proc_table
- unregister_proc_table
- do_rw_proc
- proc_readsys
- proc_writesys
- proc_sys_permission
- proc_dostring
- proc_dointvec
- proc_dostring
- proc_dointvec
- sysctl_string
- do_string
- do_int
- do_struct
1
2
3
4
5
6
7
8 #include <linux/config.h>
9 #include <linux/sched.h>
10 #include <linux/mm.h>
11 #include <linux/sysctl.h>
12 #include <linux/swapctl.h>
13 #include <linux/proc_fs.h>
14 #include <linux/malloc.h>
15 #include <linux/stat.h>
16 #include <linux/ctype.h>
17 #include <asm/bitops.h>
18 #include <asm/segment.h>
19
20 #include <linux/utsname.h>
21 #include <linux/swapctl.h>
22
23 static ctl_table root_table[];
24 static struct ctl_table_header root_table_header =
25 {root_table, DNODE_SINGLE(&root_table_header)};
26
27 static int parse_table(int *, int, void *, size_t *, void *, size_t,
28 ctl_table *, void **);
29
30 static ctl_table kern_table[];
31 static ctl_table vm_table[];
32
33
34
35 #ifdef CONFIG_PROC_FS
36
37 static int proc_readsys(struct inode * inode, struct file * file,
38 char * buf, int count);
39 static int proc_writesys(struct inode * inode, struct file * file,
40 const char * buf, int count);
41 static int proc_sys_permission(struct inode *, int);
42
43 struct file_operations proc_sys_file_operations =
44 {
45 NULL,
46 proc_readsys,
47 proc_writesys,
48 NULL,
49 NULL,
50 NULL,
51 NULL,
52 NULL,
53 NULL,
54 NULL
55 };
56
57 struct inode_operations proc_sys_inode_operations =
58 {
59 &proc_sys_file_operations,
60 NULL,
61 NULL,
62 NULL,
63 NULL,
64 NULL,
65 NULL,
66 NULL,
67 NULL,
68 NULL,
69 NULL,
70 NULL,
71 NULL,
72 NULL,
73 NULL,
74 NULL,
75 proc_sys_permission
76 };
77
78 extern struct proc_dir_entry proc_sys_root;
79
80 static void register_proc_table(ctl_table *, struct proc_dir_entry *);
81 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
82 #endif
83
84 static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
85 void *, size_t, void **);
86
87
88
89 static ctl_table root_table[] = {
90 {CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
91 {CTL_VM, "vm", NULL, 0, 0555, vm_table},
92 {0}
93 };
94
95 static ctl_table kern_table[] = {
96 {KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
97 0444, NULL, &proc_dostring, &sysctl_string},
98 {KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
99 0444, NULL, &proc_dostring, &sysctl_string},
100 {KERN_VERSION, "version", system_utsname.version, 64,
101 0444, NULL, &proc_dostring, &sysctl_string},
102 {KERN_NODENAME, "hostname", system_utsname.nodename, 64,
103 0644, NULL, &proc_dostring, &sysctl_string},
104 {KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
105 0644, NULL, &proc_dostring, &sysctl_string},
106 {KERN_NRINODE, "inode-nr", &nr_inodes, 2*sizeof(int),
107 0444, NULL, &proc_dointvec},
108 {KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
109 0644, NULL, &proc_dointvec},
110 {KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
111 0444, NULL, &proc_dointvec},
112 {KERN_MAXFILE, "file-max", &max_files, sizeof(int),
113 0644, NULL, &proc_dointvec},
114 {KERN_SECURELVL, "securelevel", &securelevel, sizeof(int),
115 0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy},
116 {0}
117 };
118
119 static ctl_table vm_table[] = {
120 {VM_SWAPCTL, "swapctl",
121 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
122 {VM_KSWAPD, "kswapd",
123 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
124 {0}
125 };
126
127 void sysctl_init(void)
128 {
129 #ifdef CONFIG_PROC_FS
130 register_proc_table(root_table, &proc_sys_root);
131 #endif
132 }
133
134
135 int do_sysctl (int *name, int nlen,
136 void *oldval, size_t *oldlenp,
137 void *newval, size_t newlen)
138 {
139 int error;
140 struct ctl_table_header *tmp;
141 void *context;
142
143 if (nlen == 0 || nlen >= CTL_MAXNAME)
144 return -ENOTDIR;
145
146 error = verify_area(VERIFY_READ,name,nlen*sizeof(int));
147 if (error) return error;
148 if (oldval) {
149 if (!oldlenp)
150 return -EFAULT;
151 error = verify_area(VERIFY_WRITE,oldlenp,sizeof(size_t));
152 if (error) return error;
153 error = verify_area(VERIFY_WRITE,oldval,get_user(oldlenp));
154 if (error) return error;
155 }
156 if (newval) {
157 error = verify_area(VERIFY_READ,newval,newlen);
158 if (error) return error;
159 }
160 tmp = &root_table_header;
161 do {
162 context = 0;
163 error = parse_table(name, nlen, oldval, oldlenp,
164 newval, newlen, root_table, &context);
165 if (context)
166 kfree(context);
167 if (error != ENOTDIR)
168 return error;
169 tmp = tmp->DLIST_NEXT(ctl_entry);
170 } while (tmp != &root_table_header);
171 return -ENOTDIR;
172 }
173
174 extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
175 {
176 struct __sysctl_args tmp;
177 int error;
178 error = verify_area(VERIFY_READ, args, sizeof(*args));
179 if (error)
180 return error;
181 memcpy_fromfs(&tmp, args, sizeof(tmp));
182 return do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
183 tmp.newval, tmp.newlen);
184 }
185
186
187 static int in_egroup_p(gid_t grp)
188 {
189 int i;
190
191 if (grp == current->euid)
192 return 1;
193
194 for (i = 0; i < NGROUPS; i++) {
195 if (current->groups[i] == NOGROUP)
196 break;
197 if (current->groups[i] == grp)
198 return 1;
199 }
200 return 0;
201 }
202
203
204 static int test_perm(int mode, int op)
205 {
206 if (!current->euid)
207 mode >>= 6;
208 else if (in_egroup_p(0))
209 mode >>= 3;
210 if ((mode & op & 0007) == op)
211 return 0;
212 return -EACCES;
213 }
214 static inline int ctl_perm(ctl_table *table, int op)
215 {
216 return test_perm(table->mode, op);
217 }
218
219 static int parse_table(int *name, int nlen,
220 void *oldval, size_t *oldlenp,
221 void *newval, size_t newlen,
222 ctl_table *table, void **context)
223 {
224 int error;
225 repeat:
226 if (!nlen)
227 return -ENOTDIR;
228
229 for ( ; table->ctl_name; table++) {
230 if (get_user(name) == table->ctl_name ||
231 table->ctl_name == CTL_ANY) {
232 if (table->child) {
233 if (ctl_perm(table, 001))
234 return -EPERM;
235 if (table->strategy) {
236 error = table->strategy(
237 table, name, nlen,
238 oldval, oldlenp,
239 newval, newlen, context);
240 if (error)
241 return error;
242 }
243 name++;
244 nlen--;
245 table = table->child;
246 goto repeat;
247 }
248 error = do_sysctl_strategy(table, name, nlen,
249 oldval, oldlenp,
250 newval, newlen, context);
251 return error;
252 }
253 };
254 return -ENOTDIR;
255 }
256
257
258 int do_sysctl_strategy (ctl_table *table,
259 int *name, int nlen,
260 void *oldval, size_t *oldlenp,
261 void *newval, size_t newlen, void **context)
262 {
263 int op = 0, rc, len;
264
265 if (oldval)
266 op |= 004;
267 if (newval)
268 op |= 002;
269 if (ctl_perm(table, op))
270 return -EPERM;
271
272 if (table->strategy) {
273 rc = table->strategy(table, name, nlen, oldval, oldlenp,
274 newval, newlen, context);
275 if (rc < 0)
276 return rc;
277 if (rc > 0)
278 return 0;
279 }
280
281
282
283 if (table->data && table->maxlen) {
284 if (oldval && oldlenp && get_user(oldlenp)) {
285 len = get_user(oldlenp);
286 if (len > table->maxlen)
287 len = table->maxlen;
288 memcpy_tofs(oldval, table->data, len);
289 put_user(len, oldlenp);
290 }
291 if (newval && newlen) {
292 len = newlen;
293 if (len > table->maxlen)
294 len = table->maxlen;
295 memcpy_fromfs(table->data, newval, len);
296 }
297 }
298 return 0;
299 }
300
301
302
303
304
305
306 static int do_securelevel_strategy (ctl_table *table,
307 int *name, int nlen,
308 void *oldval, size_t *oldlenp,
309 void *newval, size_t newlen, void **context)
310 {
311 int level;
312
313 if (newval && newlen) {
314 if (newlen != sizeof (int))
315 return -EINVAL;
316 memcpy_fromfs (&level, newval, newlen);
317 if (level < securelevel && current->pid != 1)
318 return -EPERM;
319 }
320 return 0;
321 }
322
323 struct ctl_table_header *register_sysctl_table(ctl_table * table,
324 int insert_at_head)
325 {
326 struct ctl_table_header *tmp;
327 tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
328 if (!tmp)
329 return 0;
330 *tmp = ((struct ctl_table_header) {table, DNODE_NULL});
331 if (insert_at_head)
332 DLIST_INSERT_AFTER(&root_table_header, tmp, ctl_entry);
333 else
334 DLIST_INSERT_BEFORE(&root_table_header, tmp, ctl_entry);
335 #ifdef CONFIG_PROC_FS
336 register_proc_table(table, &proc_sys_root);
337 #endif
338 return tmp;
339 }
340
341 void unregister_sysctl_table(struct ctl_table_header * table)
342 {
343 DLIST_DELETE(table, ctl_entry);
344 #ifdef CONFIG_PROC_FS
345 unregister_proc_table(table->ctl_table, &proc_sys_root);
346 #endif
347 }
348
349
350
351
352
353 #ifdef CONFIG_PROC_FS
354
355
356 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
357 {
358 struct proc_dir_entry *de;
359
360 for (; table->ctl_name; table++) {
361
362 if (!table->procname)
363 continue;
364
365 if (!table->proc_handler &&
366 !table->child)
367 continue;
368
369 de = kmalloc(sizeof(*de), GFP_KERNEL);
370 if (!de) continue;
371 de->namelen = strlen(table->procname);
372 de->name = table->procname;
373 de->mode = table->mode;
374 de->nlink = 1;
375 de->uid = 0;
376 de->gid = 0;
377 de->size = 0;
378 de->get_info = 0;
379 de->fill_inode = 0;
380 de->next = de->subdir = 0;
381 de->data = (void *) table;
382
383 if (table->proc_handler) {
384 de->ops = &proc_sys_inode_operations;
385 de->mode |= S_IFREG;
386 }
387
388 else {
389 de->ops = &proc_dir_inode_operations;
390 de->nlink++;
391 de->mode |= S_IFDIR;
392 }
393 table->de = de;
394 proc_register_dynamic(root, de);
395 if (de->mode & S_IFDIR )
396 register_proc_table(table->child, de);
397 }
398 }
399
400 static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
401 {
402 struct proc_dir_entry *de;
403 for (; table->ctl_name; table++) {
404 if (!(de = table->de))
405 continue;
406 if (de->mode & S_IFDIR) {
407 if (!table->child) {
408 printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
409 continue;
410 }
411 unregister_proc_table(table->child, de);
412 }
413 proc_unregister(root, de->low_ino);
414 kfree(de);
415 }
416 }
417
418
419 static int do_rw_proc(int write, struct inode * inode, struct file * file,
420 char * buf, int count)
421 {
422 int error, op;
423 struct proc_dir_entry *de;
424 struct ctl_table *table;
425 size_t res;
426
427 error = verify_area(write ? VERIFY_READ : VERIFY_WRITE, buf, count);
428 if (error)
429 return error;
430
431 de = (struct proc_dir_entry*) inode->u.generic_ip;
432 if (!de || !de->data)
433 return -ENOTDIR;
434 table = (struct ctl_table *) de->data;
435 if (!table || !table->proc_handler)
436 return -ENOTDIR;
437 op = (write ? 002 : 004);
438 if (ctl_perm(table, op))
439 return -EPERM;
440
441 res = count;
442 error = (*table->proc_handler) (table, write, file, buf, &res);
443 if (error)
444 return error;
445 return res;
446 }
447
448 static int proc_readsys(struct inode * inode, struct file * file,
449 char * buf, int count)
450 {
451 return do_rw_proc(0, inode, file, buf, count);
452 }
453
454 static int proc_writesys(struct inode * inode, struct file * file,
455 const char * buf, int count)
456 {
457 return do_rw_proc(1, inode, file, (char *) buf, count);
458 }
459
460 static int proc_sys_permission(struct inode *inode, int op)
461 {
462 return test_perm(inode->i_mode, op);
463 }
464
465 int proc_dostring(ctl_table *table, int write, struct file *filp,
466 void *buffer, size_t *lenp)
467 {
468 int len;
469 char *p, c;
470
471 if (!table->data || !table->maxlen || !*lenp ||
472 (filp->f_pos && !write)) {
473 *lenp = 0;
474 return 0;
475 }
476
477 if (write) {
478 len = 0;
479 p = buffer;
480 while (len < *lenp &&
481 (c = get_user(p++)) != 0 && c != '\n')
482 len++;
483 if (len >= table->maxlen)
484 len = table->maxlen-1;
485 memcpy_fromfs(table->data, buffer, len);
486 ((char *) table->data)[len] = 0;
487 filp->f_pos += *lenp;
488 } else {
489 len = strlen(table->data) + 1;
490 if (len > table->maxlen)
491 len = table->maxlen;
492 if (len > *lenp)
493 len = *lenp;
494 if (len) {
495 memcpy_tofs(buffer, table->data, len-1);
496 put_user(0, ((char *) buffer) + len - 1);
497 }
498 if (len < *lenp) {
499 put_user('\n', ((char *) buffer) + len);
500 len++;
501 }
502 *lenp = len;
503 filp->f_pos += len;
504 }
505 return 0;
506 }
507
508 int proc_dointvec(ctl_table *table, int write, struct file *filp,
509 void *buffer, size_t *lenp)
510 {
511 int *i, vleft, first=1, len, left, neg, val;
512 #define TMPBUFLEN 20
513 char buf[TMPBUFLEN], *p;
514
515 if (!table->data || !table->maxlen || !*lenp ||
516 (filp->f_pos && !write)) {
517 *lenp = 0;
518 return 0;
519 }
520
521 i = (int *) table->data;
522 vleft = table->maxlen / sizeof(int);
523 left = *lenp;
524
525 for (; left && vleft--; i++, first=0) {
526 if (write) {
527 while (left && isspace(get_user((char *) buffer)))
528 left--, ((char *) buffer)++;
529 if (!left)
530 break;
531 neg = 0;
532 len = left;
533 if (len > TMPBUFLEN-1)
534 len = TMPBUFLEN-1;
535 memcpy_fromfs(buf, buffer, len);
536 buf[len] = 0;
537 p = buf;
538 if (*p == '-' && left > 1) {
539 neg = 1;
540 left--, p++;
541 }
542 if (*p < '0' || *p > '9')
543 break;
544 val = simple_strtoul(p, &p, 0);
545 len = p-buf;
546 if ((len < left) && *p && !isspace(*p))
547 break;
548 if (neg)
549 val = -val;
550 buffer += len;
551 left -= len;
552 *i = val;
553 } else {
554 p = buf;
555 if (!first)
556 *p++ = '\t';
557 sprintf(p, "%d", *i);
558 len = strlen(buf);
559 if (len > left)
560 len = left;
561 memcpy_tofs(buffer, buf, len);
562 left -= len;
563 buffer += len;
564 }
565 }
566
567 if (!write && !first && left) {
568 put_user('\n', (char *) buffer);
569 left--, buffer++;
570 }
571 if (write) {
572 p = (char *) buffer;
573 while (left && isspace(get_user(p++)))
574 left--;
575 }
576 if (write && first)
577 return -EINVAL;
578 *lenp -= left;
579 filp->f_pos += *lenp;
580 return 0;
581 }
582
583 #else
584
585 int proc_dostring(ctl_table *table, int write, struct file *filp,
586 void *buffer, size_t *lenp)
587 {
588 return -ENOSYS;
589 }
590
591 int proc_dointvec(ctl_table *table, int write, struct file *filp,
592 void *buffer, size_t *lenp)
593 {
594 return -ENOSYS;
595 }
596
597 #endif
598
599
600
601
602
603
604
605 int sysctl_string(ctl_table *table, int *name, int nlen,
606 void *oldval, size_t *oldlenp,
607 void *newval, size_t newlen, void **context)
608 {
609 int l, len;
610
611 if (!table->data || !table->maxlen)
612 return -ENOTDIR;
613
614 if (oldval && oldlenp && get_user(oldlenp)) {
615 len = get_user(oldlenp);
616 l = strlen(table->data);
617 if (len > l) len = l;
618 if (len >= table->maxlen)
619 len = table->maxlen;
620 memcpy_tofs(oldval, table->data, len);
621 put_user(0, ((char *) oldval) + len);
622 put_user(len, oldlenp);
623 }
624 if (newval && newlen) {
625 len = newlen;
626 if (len > table->maxlen)
627 len = table->maxlen;
628 memcpy_fromfs(table->data, newval, len);
629 if (len == table->maxlen)
630 len--;
631 ((char *) table->data)[len] = 0;
632 }
633 return 0;
634 }
635
636 int do_string (
637 void *oldval, size_t *oldlenp, void *newval, size_t newlen,
638 int rdwr, char *data, size_t max)
639 {
640 int l = strlen(data) + 1;
641 if (newval && !rdwr)
642 return -EPERM;
643 if (newval && newlen >= max)
644 return -EINVAL;
645 if (oldval) {
646 if (l > get_user(oldlenp))
647 return -ENOMEM;
648 put_user(l, oldlenp);
649 memcpy_tofs(oldval, data, l);
650 }
651 if (newval) {
652 memcpy_fromfs(data, newval, newlen);
653 data[newlen] = 0;
654 }
655 return 0;
656 }
657
658 int do_int (
659 void *oldval, size_t *oldlenp, void *newval, size_t newlen,
660 int rdwr, int *data)
661 {
662 if (newval && !rdwr)
663 return -EPERM;
664 if (newval && newlen != sizeof(int))
665 return -EINVAL;
666 if (oldval) {
667 if (get_user(oldlenp) < sizeof(int))
668 return -ENOMEM;
669 put_user(sizeof(int), oldlenp);
670 memcpy_tofs(oldval, data, sizeof(int));
671 }
672 if (newval)
673 memcpy_fromfs(data, newval, sizeof(int));
674 return 0;
675 }
676
677 int do_struct (
678 void *oldval, size_t *oldlenp, void *newval, size_t newlen,
679 int rdwr, void *data, size_t len)
680 {
681 if (newval && !rdwr)
682 return -EPERM;
683 if (newval && newlen != len)
684 return -EINVAL;
685 if (oldval) {
686 if (get_user(oldlenp) < len)
687 return -ENOMEM;
688 put_user(len, oldlenp);
689 memcpy_tofs(oldval, data, len);
690 }
691 if (newval)
692 memcpy_fromfs(data, newval, len);
693 return 0;
694 }
695