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