1919 lines · 44.2 KB · C
RawDownload
/* *  linux/kernel/sys.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/export.h>#include <linux/mm.h>#include <linux/utsname.h>#include <linux/mman.h>#include <linux/reboot.h>#include <linux/prctl.h>#include <linux/highuid.h>#include <linux/fs.h>#include <linux/kmod.h>#include <linux/perf_event.h>#include <linux/resource.h>#include <linux/kernel.h>#include <linux/kexec.h>#include <linux/workqueue.h>#include <linux/capability.h>#include <linux/device.h>#include <linux/key.h>#include <linux/times.h>#include <linux/posix-timers.h>#include <linux/security.h>#include <linux/dcookies.h>#include <linux/suspend.h>#include <linux/tty.h>#include <linux/signal.h>#include <linux/cn_proc.h>#include <linux/getcpu.h>#include <linux/task_io_accounting_ops.h>#include <linux/seccomp.h>#include <linux/cpu.h>#include <linux/personality.h>#include <linux/ptrace.h>#include <linux/fs_struct.h>#include <linux/gfp.h>#include <linux/syscore_ops.h>#include <linux/version.h>#include <linux/ctype.h> #include <linux/compat.h>#include <linux/syscalls.h>#include <linux/kprobes.h>#include <linux/user_namespace.h> #include <linux/kmsg_dump.h>/* Move somewhere else to avoid recompiling? */#include <generated/utsrelease.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#ifndef GET_ENDIAN# define GET_ENDIAN(a,b)	(-EINVAL)#endif#ifndef SET_ENDIAN# define SET_ENDIAN(a,b)	(-EINVAL)#endif#ifndef GET_TSC_CTL# define GET_TSC_CTL(a)		(-EINVAL)#endif#ifndef SET_TSC_CTL# define SET_TSC_CTL(a)		(-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; #ifdef CONFIG_UID16EXPORT_SYMBOL(overflowuid);EXPORT_SYMBOL(overflowgid);#endif /* * 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; EXPORT_SYMBOL(fs_overflowuid);EXPORT_SYMBOL(fs_overflowgid); /* * this indicates whether you can reboot with ctrl-alt-del: the default is yes */ int C_A_D = 1;struct pid *cad_pid;EXPORT_SYMBOL(cad_pid); /* * If set, this is used for preparing the system to power off. */ void (*pm_power_off_prepare)(void); /* * Returns true if current's euid is same as p's uid or euid, * or has CAP_SYS_NICE to p's user_ns. * * Called with rcu_read_lock, creds are safe */static bool set_one_prio_perm(struct task_struct *p){	const struct cred *cred = current_cred(), *pcred = __task_cred(p); 	if (pcred->user->user_ns == cred->user->user_ns &&	    (pcred->uid  == cred->euid ||	     pcred->euid == cred->euid))		return true;	if (ns_capable(pcred->user->user_ns, CAP_SYS_NICE))		return true;	return false;} /* * set the priority of a task * - the caller must hold the RCU read lock */static int set_one_prio(struct task_struct *p, int niceval, int error){	int no_nice; 	if (!set_one_prio_perm(p)) {		error = -EPERM;		goto out;	}	if (niceval < task_nice(p) && !can_nice(p, niceval)) {		error = -EACCES;		goto out;	}	no_nice = security_task_setnice(p, niceval);	if (no_nice) {		error = no_nice;		goto out;	}	if (error == -ESRCH)		error = 0;	set_user_nice(p, niceval);out:	return error;} SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval){	struct task_struct *g, *p;	struct user_struct *user;	const struct cred *cred = current_cred();	int error = -EINVAL;	struct pid *pgrp; 	if (which > PRIO_USER || which < PRIO_PROCESS)		goto out; 	/* normalize: avoid signed division (rounding problems) */	error = -ESRCH;	if (niceval < -20)		niceval = -20;	if (niceval > 19)		niceval = 19; 	rcu_read_lock();	read_lock(&tasklist_lock);	switch (which) {		case PRIO_PROCESS:			if (who)				p = find_task_by_vpid(who);			else				p = current;			if (p)				error = set_one_prio(p, niceval, error);			break;		case PRIO_PGRP:			if (who)				pgrp = find_vpid(who);			else				pgrp = task_pgrp(current);			do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {				error = set_one_prio(p, niceval, error);			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);			break;		case PRIO_USER:			user = (struct user_struct *) cred->user;			if (!who)				who = cred->uid;			else if ((who != cred->uid) &&				 !(user = find_user(who)))				goto out_unlock;	/* No processes for this user */ 			do_each_thread(g, p) {				if (__task_cred(p)->uid == who)					error = set_one_prio(p, niceval, error);			} while_each_thread(g, p);			if (who != cred->uid)				free_uid(user);		/* For find_user() */			break;	}out_unlock:	read_unlock(&tasklist_lock);	rcu_read_unlock();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. */SYSCALL_DEFINE2(getpriority, int, which, int, who){	struct task_struct *g, *p;	struct user_struct *user;	const struct cred *cred = current_cred();	long niceval, retval = -ESRCH;	struct pid *pgrp; 	if (which > PRIO_USER || which < PRIO_PROCESS)		return -EINVAL; 	rcu_read_lock();	read_lock(&tasklist_lock);	switch (which) {		case PRIO_PROCESS:			if (who)				p = find_task_by_vpid(who);			else				p = current;			if (p) {				niceval = 20 - task_nice(p);				if (niceval > retval)					retval = niceval;			}			break;		case PRIO_PGRP:			if (who)				pgrp = find_vpid(who);			else				pgrp = task_pgrp(current);			do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {				niceval = 20 - task_nice(p);				if (niceval > retval)					retval = niceval;			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);			break;		case PRIO_USER:			user = (struct user_struct *) cred->user;			if (!who)				who = cred->uid;			else if ((who != cred->uid) &&				 !(user = find_user(who)))				goto out_unlock;	/* No processes for this user */ 			do_each_thread(g, p) {				if (__task_cred(p)->uid == who) {					niceval = 20 - task_nice(p);					if (niceval > retval)						retval = niceval;				}			} while_each_thread(g, p);			if (who != cred->uid)				free_uid(user);		/* for find_user() */			break;	}out_unlock:	read_unlock(&tasklist_lock);	rcu_read_unlock(); 	return retval;} /** *	emergency_restart - reboot the system * *	Without shutting down any hardware or taking any locks *	reboot the system.  This is called when we know we are in *	trouble so this is our best effort to reboot.  This is *	safe to call in interrupt context. */void emergency_restart(void){	kmsg_dump(KMSG_DUMP_EMERG);	machine_emergency_restart();}EXPORT_SYMBOL_GPL(emergency_restart); void kernel_restart_prepare(char *cmd){	blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);	system_state = SYSTEM_RESTART;	usermodehelper_disable();	device_shutdown();	syscore_shutdown();} /** *	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 blocking_notifier_chain_register() *	always returns zero. */int register_reboot_notifier(struct notifier_block *nb){	return blocking_notifier_chain_register(&reboot_notifier_list, nb);}EXPORT_SYMBOL(register_reboot_notifier); /** *	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 blocking_notifier_chain_unregister(&reboot_notifier_list, nb);}EXPORT_SYMBOL(unregister_reboot_notifier); /** *	kernel_restart - reboot the system *	@cmd: pointer to buffer containing command to execute for restart *		or %NULL * *	Shutdown everything and perform a clean reboot. *	This is not safe to call in interrupt context. */void kernel_restart(char *cmd){	kernel_restart_prepare(cmd);	if (!cmd)		printk(KERN_EMERG "Restarting system.\n");	else		printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);	kmsg_dump(KMSG_DUMP_RESTART);	machine_restart(cmd);}EXPORT_SYMBOL_GPL(kernel_restart); static void kernel_shutdown_prepare(enum system_states state){	blocking_notifier_call_chain(&reboot_notifier_list,		(state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);	system_state = state;	usermodehelper_disable();	device_shutdown();}/** *	kernel_halt - halt the system * *	Shutdown everything and perform a clean system halt. */void kernel_halt(void){	kernel_shutdown_prepare(SYSTEM_HALT);	syscore_shutdown();	printk(KERN_EMERG "System halted.\n");	kmsg_dump(KMSG_DUMP_HALT);	machine_halt();} EXPORT_SYMBOL_GPL(kernel_halt); /** *	kernel_power_off - power_off the system * *	Shutdown everything and perform a clean system power_off. */void kernel_power_off(void){	kernel_shutdown_prepare(SYSTEM_POWER_OFF);	if (pm_power_off_prepare)		pm_power_off_prepare();	disable_nonboot_cpus();	syscore_shutdown();	printk(KERN_EMERG "Power down.\n");	kmsg_dump(KMSG_DUMP_POWEROFF);	machine_power_off();}EXPORT_SYMBOL_GPL(kernel_power_off); static DEFINE_MUTEX(reboot_mutex); /* * 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. */SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,		void __user *, arg){	char buffer[256];	int ret = 0; 	/* 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 &&	                magic2 != LINUX_REBOOT_MAGIC2C))		return -EINVAL; 	/* Instead of trying to make the power_off code look like	 * halt when pm_power_off is not set do it the easy way.	 */	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)		cmd = LINUX_REBOOT_CMD_HALT; 	mutex_lock(&reboot_mutex);	switch (cmd) {	case LINUX_REBOOT_CMD_RESTART:		kernel_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:		kernel_halt();		do_exit(0);		panic("cannot halt"); 	case LINUX_REBOOT_CMD_POWER_OFF:		kernel_power_off();		do_exit(0);		break; 	case LINUX_REBOOT_CMD_RESTART2:		if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {			ret = -EFAULT;			break;		}		buffer[sizeof(buffer) - 1] = '\0'; 		kernel_restart(buffer);		break; #ifdef CONFIG_KEXEC	case LINUX_REBOOT_CMD_KEXEC:		ret = kernel_kexec();		break;#endif #ifdef CONFIG_HIBERNATION	case LINUX_REBOOT_CMD_SW_SUSPEND:		ret = hibernate();		break;#endif 	default:		ret = -EINVAL;

Lines 1–500 of 1919.