tty_io.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,610 行 · 第 1/5 页

C
2,610
字号
	 * The closing flags are now consistent with the open counts on 	 * both sides, and we've completed the last operation that could 	 * block, so it's safe to proceed with closing.	 */	if (pty_master) {		if (--o_tty->count < 0) {			printk(KERN_WARNING "release_dev: bad pty slave count "					    "(%d) for %s\n",			       o_tty->count, tty_name(o_tty, buf));			o_tty->count = 0;		}	}	if (--tty->count < 0) {		printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",		       tty->count, tty_name(tty, buf));		tty->count = 0;	}	/*	 * We've decremented tty->count, so we need to remove this file	 * descriptor off the tty->tty_files list; this serves two	 * purposes:	 *  - check_tty_count sees the correct number of file descriptors	 *    associated with this tty.	 *  - do_tty_hangup no longer sees this file descriptor as	 *    something that needs to be handled for hangups.	 */	file_kill(filp);	filp->private_data = NULL;	/*	 * Perform some housekeeping before deciding whether to return.	 *	 * Set the TTY_CLOSING flag if this was the last open.  In the	 * case of a pty we may have to wait around for the other side	 * to close, and TTY_CLOSING makes sure we can't be reopened.	 */	if(tty_closing)		set_bit(TTY_CLOSING, &tty->flags);	if(o_tty_closing)		set_bit(TTY_CLOSING, &o_tty->flags);	/*	 * If _either_ side is closing, make sure there aren't any	 * processes that still think tty or o_tty is their controlling	 * tty.	 */	if (tty_closing || o_tty_closing) {		struct task_struct *p;		read_lock(&tasklist_lock);		do_each_task_pid(tty->session, PIDTYPE_SID, p) {			p->signal->tty = NULL;		} while_each_task_pid(tty->session, PIDTYPE_SID, p);		if (o_tty)			do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {				p->signal->tty = NULL;			} while_each_task_pid(o_tty->session, PIDTYPE_SID, p);		read_unlock(&tasklist_lock);	}	/* check whether both sides are closing ... */	if (!tty_closing || (o_tty && !o_tty_closing))		return;	#ifdef TTY_DEBUG_HANGUP	printk(KERN_DEBUG "freeing tty structure...");#endif	/*	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then	 * kill any delayed work. As this is the final close it does not	 * race with the set_ldisc code path.	 */	clear_bit(TTY_LDISC, &tty->flags);	clear_bit(TTY_DONT_FLIP, &tty->flags);	cancel_delayed_work(&tty->flip.work);	/*	 * Wait for ->hangup_work and ->flip.work handlers to terminate	 */	 	flush_scheduled_work();		/*	 * Wait for any short term users (we know they are just driver	 * side waiters as the file is closing so user count on the file	 * side is zero.	 */	spin_lock_irqsave(&tty_ldisc_lock, flags);	while(tty->ldisc.refcount)	{		spin_unlock_irqrestore(&tty_ldisc_lock, flags);		wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);		spin_lock_irqsave(&tty_ldisc_lock, flags);	}	spin_unlock_irqrestore(&tty_ldisc_lock, flags);	/*	 * Shutdown the current line discipline, and reset it to N_TTY.	 * N.B. why reset ldisc when we're releasing the memory??	 *	 * FIXME: this MUST get fixed for the new reflocking	 */	if (tty->ldisc.close)		(tty->ldisc.close)(tty);	tty_ldisc_put(tty->ldisc.num);		/*	 *	Switch the line discipline back	 */	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));	tty_set_termios_ldisc(tty,N_TTY); 	if (o_tty) {		/* FIXME: could o_tty be in setldisc here ? */		clear_bit(TTY_LDISC, &o_tty->flags);		if (o_tty->ldisc.close)			(o_tty->ldisc.close)(o_tty);		tty_ldisc_put(o_tty->ldisc.num);		tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));		tty_set_termios_ldisc(o_tty,N_TTY); 	}	/*	 * The release_mem function takes care of the details of clearing	 * the slots and preserving the termios structure.	 */	release_mem(tty, idx);#ifdef CONFIG_UNIX98_PTYS	/* Make this pty number available for reallocation */	if (devpts) {		down(&allocated_ptys_lock);		idr_remove(&allocated_ptys, idx);		up(&allocated_ptys_lock);	}#endif}/* * tty_open and tty_release keep up the tty count that contains the * number of opens done on a tty. We cannot use the inode-count, as * different inodes might point to the same tty. * * Open-counting is needed for pty masters, as well as for keeping * track of serial lines: DTR is dropped when the last close happens. * (This is not done solely through tty->count, now.  - Ted 1/27/92) * * The termios state of a pty is reset on first open so that * settings don't persist across reuse. */static int tty_open(struct inode * inode, struct file * filp){	struct tty_struct *tty;	int noctty, retval;	struct tty_driver *driver;	int index;	dev_t device = inode->i_rdev;	unsigned short saved_flags = filp->f_flags;	nonseekable_open(inode, filp);	retry_open:	noctty = filp->f_flags & O_NOCTTY;	index  = -1;	retval = 0;	if (device == MKDEV(TTYAUX_MAJOR,0)) {		if (!current->signal->tty)			return -ENXIO;		driver = current->signal->tty->driver;		index = current->signal->tty->index;		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */		/* noctty = 1; */		goto got_driver;	}#ifdef CONFIG_VT	if (device == MKDEV(TTY_MAJOR,0)) {		extern int fg_console;		extern struct tty_driver *console_driver;		driver = console_driver;		index = fg_console;		noctty = 1;		goto got_driver;	}#endif	if (device == MKDEV(TTYAUX_MAJOR,1)) {		driver = console_device(&index);		if (driver) {			/* Don't let /dev/console block */			filp->f_flags |= O_NONBLOCK;			noctty = 1;			goto got_driver;		}		return -ENODEV;	}	driver = get_tty_driver(device, &index);	if (!driver)		return -ENODEV;got_driver:	retval = init_dev(driver, index, &tty);	if (retval)		return retval;	filp->private_data = tty;	file_move(filp, &tty->tty_files);	check_tty_count(tty, "tty_open");	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&	    tty->driver->subtype == PTY_TYPE_MASTER)		noctty = 1;#ifdef TTY_DEBUG_HANGUP	printk(KERN_DEBUG "opening %s...", tty->name);#endif	if (!retval) {		if (tty->driver->open)			retval = tty->driver->open(tty, filp);		else			retval = -ENODEV;	}	filp->f_flags = saved_flags;	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))		retval = -EBUSY;	if (retval) {#ifdef TTY_DEBUG_HANGUP		printk(KERN_DEBUG "error %d in opening %s...", retval,		       tty->name);#endif		release_dev(filp);		if (retval != -ERESTARTSYS)			return retval;		if (signal_pending(current))			return retval;		schedule();		/*		 * Need to reset f_op in case a hangup happened.		 */		if (filp->f_op == &hung_up_tty_fops)			filp->f_op = &tty_fops;		goto retry_open;	}	if (!noctty &&	    current->signal->leader &&	    !current->signal->tty &&	    tty->session == 0) {	    	task_lock(current);		current->signal->tty = tty;		task_unlock(current);		current->signal->tty_old_pgrp = 0;		tty->session = current->signal->session;		tty->pgrp = process_group(current);	}	return 0;}#ifdef CONFIG_UNIX98_PTYSstatic int ptmx_open(struct inode * inode, struct file * filp){	struct tty_struct *tty;	int retval;	int index;	int idr_ret;	nonseekable_open(inode, filp);	/* find a device that is not in use. */	down(&allocated_ptys_lock);	if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {		up(&allocated_ptys_lock);		return -ENOMEM;	}	idr_ret = idr_get_new(&allocated_ptys, NULL, &index);	if (idr_ret < 0) {		up(&allocated_ptys_lock);		if (idr_ret == -EAGAIN)			return -ENOMEM;		return -EIO;	}	if (index >= pty_limit) {		idr_remove(&allocated_ptys, index);		up(&allocated_ptys_lock);		return -EIO;	}	up(&allocated_ptys_lock);	retval = init_dev(ptm_driver, index, &tty);	if (retval)		goto out;	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */	filp->private_data = tty;	file_move(filp, &tty->tty_files);	retval = -ENOMEM;	if (devpts_pty_new(tty->link))		goto out1;	check_tty_count(tty, "tty_open");	retval = ptm_driver->open(tty, filp);	if (!retval)		return 0;out1:	release_dev(filp);out:	down(&allocated_ptys_lock);	idr_remove(&allocated_ptys, index);	up(&allocated_ptys_lock);	return retval;}#endifstatic int tty_release(struct inode * inode, struct file * filp){	lock_kernel();	release_dev(filp);	unlock_kernel();	return 0;}/* No kernel lock held - fine */static unsigned int tty_poll(struct file * filp, poll_table * wait){	struct tty_struct * tty;	struct tty_ldisc *ld;	int ret = 0;	tty = (struct tty_struct *)filp->private_data;	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll"))		return 0;			ld = tty_ldisc_ref_wait(tty);	if (ld->poll)		ret = (ld->poll)(tty, filp, wait);	tty_ldisc_deref(ld);	return ret;}static int tty_fasync(int fd, struct file * filp, int on){	struct tty_struct * tty;	int retval;	tty = (struct tty_struct *)filp->private_data;	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_fasync"))		return 0;		retval = fasync_helper(fd, filp, on, &tty->fasync);	if (retval <= 0)		return retval;	if (on) {		if (!waitqueue_active(&tty->read_wait))			tty->minimum_to_wake = 1;		retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0);		if (retval)			return retval;	} else {		if (!tty->fasync && !waitqueue_active(&tty->read_wait))			tty->minimum_to_wake = N_TTY_BUF_SIZE;	}	return 0;}static int tiocsti(struct tty_struct *tty, char __user *p){	char ch, mbz = 0;	struct tty_ldisc *ld;		if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))		return -EPERM;	if (get_user(ch, p))		return -EFAULT;	ld = tty_ldisc_ref_wait(tty);	ld->receive_buf(tty, &ch, &mbz, 1);	tty_ldisc_deref(ld);	return 0;}static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg){	if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))		return -EFAULT;	return 0;}static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,	struct winsize __user * arg){	struct winsize tmp_ws;	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))		return -EFAULT;	if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))		return 0;#ifdef CONFIG_VT	if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {		unsigned int currcons = tty->index;		int rc;		acquire_console_sem();		rc = vc_resize(currcons, tmp_ws.ws_col, tmp_ws.ws_row);		release_console_sem();		if (rc)			return -ENXIO;	}#endif	if (tty->pgrp > 0)		kill_pg(tty->pgrp, SIGWINCH, 1);	if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))		kill_pg(real_tty->pgrp, SIGWINCH, 1);	tty->winsize = tmp_ws;	real_tty->winsize = tmp_ws;	return 0;}static int tioccons(struct file *file){	if (file->f_op->write == redirected_tty_write) {		struct file *f;		if (!capable(CAP_SYS_ADMIN))			return -EPERM;		spin_lock(&redirect_lock);		f = redirect;		redirect = NULL;		spin_unlock(&redirect_lock);		if (f)			fput(f);		return 0;	}	spin_lock(&redirect_lock);	if (redirect) {		spin_unlock(&redirect_lock);		return -EBUSY;	}	get_file(file);	redirect = file;	spin_unlock(&redirect_lock);	return 0;}static int fionbio(struct file *file, int __user *p){	int nonblock;	if (get_user(nonblock, p))		return -EFAULT;	if (nonblock)		file->f_flags |= O_NONBLOCK;	else		file->f_flags &= ~O_NONBLOCK;	return 0;}static int tiocsctty(struct tty_struct *tty, int arg){	task_t *p;	if (current->signal->leader &&	    (current->signal->session == tty->session))		return 0;	/*	 * The process must be a session leader and	 * not have a controlling tty already.	 */	if (!current->signal->leader || current->signal->tty)		return -EPERM;	if (tty->session > 0) {		/*		 * This tty is already the controlling		 * tty for another session group!		 */		if ((arg == 1) && capable(CAP_SYS_ADMIN)) {			/*			 * Steal it away			 */			read_lock(&tasklist_lock);			do_each_task_pid(tty->session, PIDTYPE_SID, p) {				p->signal->tty = NULL;			} while_each_task_pid(tty->session, PIDTYPE_SID, p);			read_unlock(&tasklist_lock);		} else			return -EPERM;	}	task_lock(current);	current->signal->tty = tty;	task_unlock(current);	current->signal->tty_old_pgrp = 0;	tty->session = current->signal->session;	tty->pgrp = process_group(current);	return 0;}static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p){	/*	 * (tty == real_tty) is a cheap way of	 * testing if the tty is NOT a master pty.	 */	if (tty == real_tty && current->signal->tty != real_tty)		return -ENOTTY;	return put_user(real_tty->pgrp, p);}static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p){	pid_t pgrp;	int retval = tty_check_change(real_tty);	if (retval == -EIO)		return -ENOTTY;	if (retval)		return retval;	if (!current->signal->tty ||	    (current->signal->tty != real_tty) ||	    (real_tty->session != current->signal->session))		return -ENOTTY;	if (get_user(pgrp, p))		return -EFAULT;	if (pgrp < 0)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?