📄 sys.c
字号:
/* * linux/kernel/sys.c * * Copyright (C) 1991, 1992 Linus Torvalds */#include <linux/config.h>#include <linux/module.h>#include <linux/mm.h>#include <linux/utsname.h>#include <linux/mman.h>#include <linux/smp_lock.h>#include <linux/notifier.h>#include <linux/reboot.h>#include <linux/prctl.h>#include <linux/init.h>#include <linux/highuid.h>#include <linux/dcookies.h>#include <linux/fs.h>#include <linux/times.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/unistd.h>#ifndef SET_UNALIGN_CTL# define SET_UNALIGN_CTL(a,b) (-EINVAL)#endif#ifndef GET_UNALIGN_CTL# define GET_UNALIGN_CTL(a,b) (-EINVAL)#endif#ifndef SET_FPEMU_CTL# define SET_FPEMU_CTL(a,b) (-EINVAL)#endif#ifndef GET_FPEMU_CTL# define GET_FPEMU_CTL(a,b) (-EINVAL)#endif#ifndef SET_FPEXC_CTL# define SET_FPEXC_CTL(a,b) (-EINVAL)#endif#ifndef GET_FPEXC_CTL# define GET_FPEXC_CTL(a,b) (-EINVAL)#endif/* * this is where the system-wide overflow UID and GID are defined, for * architectures that now have 32-bit UID/GID but didn't in the past */int overflowuid = DEFAULT_OVERFLOWUID;int overflowgid = DEFAULT_OVERFLOWGID;/* * the same as above, but for filesystems which can only store a 16-bit * UID and GID. as such, this is needed on all architectures */int fs_overflowuid = DEFAULT_FS_OVERFLOWUID;int fs_overflowgid = DEFAULT_FS_OVERFLOWUID;/* * this indicates whether you can reboot with ctrl-alt-del: the default is yes */int C_A_D = 1;int cad_pid = 1;extern int system_running;/* * Notifier list for kernel code which wants to be called * at shutdown. This is used to stop any idling DMA operations * and the like. */static struct notifier_block *reboot_notifier_list;rwlock_t notifier_lock = RW_LOCK_UNLOCKED;/** * notifier_chain_register - Add notifier to a notifier chain * @list: Pointer to root list pointer * @n: New entry in notifier chain * * Adds a notifier to a notifier chain. * * Currently always returns zero. */ int notifier_chain_register(struct notifier_block **list, struct notifier_block *n){ write_lock(¬ifier_lock); while(*list) { if(n->priority > (*list)->priority) break; list= &((*list)->next); } n->next = *list; *list=n; write_unlock(¬ifier_lock); return 0;}/** * notifier_chain_unregister - Remove notifier from a notifier chain * @nl: Pointer to root list pointer * @n: New entry in notifier chain * * Removes a notifier from a notifier chain. * * Returns zero on success, or %-ENOENT on failure. */ int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n){ write_lock(¬ifier_lock); while((*nl)!=NULL) { if((*nl)==n) { *nl=n->next; write_unlock(¬ifier_lock); return 0; } nl=&((*nl)->next); } write_unlock(¬ifier_lock); return -ENOENT;}/** * notifier_call_chain - Call functions in a notifier chain * @n: Pointer to root pointer of notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function * * Calls each function in a notifier chain in turn. * * If the return value of the notifier can be and'd * with %NOTIFY_STOP_MASK, then notifier_call_chain * will return immediately, with the return value of * the notifier function which halted execution. * Otherwise, the return value is the return value * of the last notifier function called. */ int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v){ int ret=NOTIFY_DONE; struct notifier_block *nb = *n; while(nb) { ret=nb->notifier_call(nb,val,v); if(ret&NOTIFY_STOP_MASK) { return ret; } nb=nb->next; } return ret;}/** * register_reboot_notifier - Register function to be called at reboot time * @nb: Info about notifier function to be called * * Registers a function with the list of functions * to be called at reboot time. * * Currently always returns zero, as notifier_chain_register * always returns zero. */ int register_reboot_notifier(struct notifier_block * nb){ return notifier_chain_register(&reboot_notifier_list, nb);}#if !defined(CONFIG_PROFILING)long asmlinkage sys_lookup_dcookie(void){ return -ENOSYS;}#endif/** * unregister_reboot_notifier - Unregister previously registered reboot notifier * @nb: Hook to be unregistered * * Unregisters a previously registered reboot * notifier function. * * Returns zero on success, or %-ENOENT on failure. */ int unregister_reboot_notifier(struct notifier_block * nb){ return notifier_chain_unregister(&reboot_notifier_list, nb);}asmlinkage long sys_ni_syscall(void){ return -ENOSYS;}static int set_one_prio(struct task_struct *p, int niceval, int error){ if (p->uid != current->euid && p->uid != current->uid && !capable(CAP_SYS_NICE)) { error = -EPERM; goto out; } if (error == -ESRCH) error = 0; if (niceval < task_nice(p) && !capable(CAP_SYS_NICE)) error = -EACCES; else set_user_nice(p, niceval);out: return error;}asmlinkage long sys_setpriority(int which, int who, int niceval){ struct task_struct *g, *p; struct user_struct *user; struct pid *pid; struct list_head *l; int error = -EINVAL; if (which > 2 || which < 0) goto out; /* normalize: avoid signed division (rounding problems) */ error = -ESRCH; if (niceval < -20) niceval = -20; if (niceval > 19) niceval = 19; read_lock(&tasklist_lock); switch (which) { case PRIO_PROCESS: if (!who) who = current->pid; p = find_task_by_pid(who); if (p) error = set_one_prio(p, niceval, error); break; case PRIO_PGRP: if (!who) who = current->pgrp; for_each_task_pid(who, PIDTYPE_PGID, p, l, pid) error = set_one_prio(p, niceval, error); break; case PRIO_USER: if (!who) user = current->user; else user = find_user(who); if (!user) goto out_unlock; do_each_thread(g, p) if (p->uid == who) error = set_one_prio(p, niceval, error); while_each_thread(g, p); break; }out_unlock: read_unlock(&tasklist_lock);out: return error;}/* * Ugh. To avoid negative return values, "getpriority()" will * not return the normal nice-value, but a negated value that * has been offset by 20 (ie it returns 40..1 instead of -20..19) * to stay compatible. */asmlinkage long sys_getpriority(int which, int who){ struct task_struct *g, *p; struct list_head *l; struct pid *pid; struct user_struct *user; long niceval, retval = -ESRCH; if (which > 2 || which < 0) return -EINVAL; read_lock(&tasklist_lock); switch (which) { case PRIO_PROCESS: if (!who) who = current->pid; p = find_task_by_pid(who); if (p) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; } break; case PRIO_PGRP: if (!who) who = current->pgrp; for_each_task_pid(who, PIDTYPE_PGID, p, l, pid) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; } break; case PRIO_USER: if (!who) user = current->user; else user = find_user(who); if (!user) goto out_unlock; do_each_thread(g, p) if (p->uid == who) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; } while_each_thread(g, p); break; }out_unlock: read_unlock(&tasklist_lock); return retval;}/* * Reboot system call: for obvious reasons only root may call it, * and even root needs to set up some magic numbers in the registers * so that some mistake won't make this reboot the whole machine. * You can also set the meaning of the ctrl-alt-del-key here. * * reboot doesn't sync: do that yourself before calling this. */asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg){ char buffer[256]; /* We only trust the superuser with rebooting the system. */ if (!capable(CAP_SYS_BOOT)) return -EPERM; /* For safety, we require "magic" arguments. */ if (magic1 != LINUX_REBOOT_MAGIC1 || (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && magic2 != LINUX_REBOOT_MAGIC2B)) return -EINVAL; lock_kernel(); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); printk(KERN_EMERG "Restarting system.\n"); machine_restart(NULL); break; case LINUX_REBOOT_CMD_CAD_ON: C_A_D = 1; break; case LINUX_REBOOT_CMD_CAD_OFF: C_A_D = 0; break; case LINUX_REBOOT_CMD_HALT: notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); printk(KERN_EMERG "System halted.\n"); machine_halt(); do_exit(0); break; case LINUX_REBOOT_CMD_POWER_OFF: notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); printk(KERN_EMERG "Power down.\n"); machine_power_off(); do_exit(0); break; case LINUX_REBOOT_CMD_RESTART2: if (strncpy_from_user(&buffer[0], (char *)arg, sizeof(buffer) - 1) < 0) { unlock_kernel(); return -EFAULT; } buffer[sizeof(buffer) - 1] = '\0'; notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); machine_restart(buffer); break; default: unlock_kernel(); return -EINVAL; } unlock_kernel(); return 0;}static void deferred_cad(void *dummy){ notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); machine_restart(NULL);}/* * This function gets called by ctrl-alt-del - ie the keyboard interrupt. * As it's called within an interrupt, it may NOT sync: the only choice * is whether to reboot at once, or just ignore the ctrl-alt-del. */void ctrl_alt_del(void){ static struct tq_struct cad_tq = { routine: deferred_cad, }; if (C_A_D) schedule_task(&cad_tq); else kill_proc(cad_pid, SIGINT, 1);} /* * Unprivileged users may change the real gid to the effective gid * or vice versa. (BSD-style) * * If you set the real gid at all, or set the effective gid to a value not * equal to the real gid, then the saved gid is set to the new effective gid. * * This makes it possible for a setgid program to completely drop its * privileges, which is often a useful assertion to make when you are doing * a security audit over a program. * * The general idea is that a program which uses just setregid() will be * 100% compatible with BSD. A program which uses just setgid() will be * 100% compatible with POSIX with saved IDs. * * SMP: There are not races, the GIDs are checked only by filesystem * operations (as far as semantic preservation is concerned). */asmlinkage long sys_setregid(gid_t rgid, gid_t egid){ int old_rgid = current->gid; int old_egid = current->egid; int new_rgid = old_rgid; int new_egid = old_egid; if (rgid != (gid_t) -1) { if ((old_rgid == rgid) || (current->egid==rgid) || capable(CAP_SETGID)) new_rgid = rgid; else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -