⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tty_io.c

📁 unix/linux 编程实践一书的所有源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
static struct wait_queue *vt_activate_queue = NULL;/* * Sleeps until a vt is activated, or the task is interrupted. Returns * 0 if activation, -1 if interrupted. */int vt_waitactive(void){	interruptible_sleep_on(&vt_activate_queue);	return (current->signal & ~current->blocked) ? -1 : 0;}#define vt_wake_waitactive() wake_up(&vt_activate_queue)void reset_vc(unsigned int new_console){	vt_cons[new_console]->vc_mode = KD_TEXT;	kbd_table[new_console].kbdmode = VC_XLATE;	vt_cons[new_console]->vt_mode.mode = VT_AUTO;	vt_cons[new_console]->vt_mode.waitv = 0;	vt_cons[new_console]->vt_mode.relsig = 0;	vt_cons[new_console]->vt_mode.acqsig = 0;	vt_cons[new_console]->vt_mode.frsig = 0;	vt_cons[new_console]->vt_pid = -1;	vt_cons[new_console]->vt_newvt = -1;	reset_palette (new_console) ;}/* * Performs the back end of a vt switch */void complete_change_console(unsigned int new_console){	unsigned char old_vc_mode;        if ((new_console == fg_console) || (vt_dont_switch))                return;        if (!vc_cons_allocated(new_console))                return;	last_console = fg_console;	/*	 * If we're switching, we could be going from KD_GRAPHICS to	 * KD_TEXT mode or vice versa, which means we need to blank or	 * unblank the screen later.	 */	old_vc_mode = vt_cons[fg_console]->vc_mode;	update_screen(new_console);	/*	 * If this new console is under process control, send it a signal	 * telling it that it has acquired. Also check if it has died and	 * clean up (similar to logic employed in change_console())	 */	if (vt_cons[new_console]->vt_mode.mode == VT_PROCESS)	{		/*		 * Send the signal as privileged - kill_proc() will		 * tell us if the process has gone or something else		 * is awry		 */		if (kill_proc(vt_cons[new_console]->vt_pid,			      vt_cons[new_console]->vt_mode.acqsig,			      1) != 0)		{		/*		 * The controlling process has died, so we revert back to		 * normal operation. In this case, we'll also change back		 * to KD_TEXT mode. I'm not sure if this is strictly correct		 * but it saves the agony when the X server dies and the screen		 * remains blanked due to KD_GRAPHICS! It would be nice to do		 * this outside of VT_PROCESS but there is no single process		 * to account for and tracking tty count may be undesirable.		 */		        reset_vc(new_console);		}	}	/*	 * We do this here because the controlling process above may have	 * gone, and so there is now a new vc_mode	 */	if (old_vc_mode != vt_cons[new_console]->vc_mode)	{		if (vt_cons[new_console]->vc_mode == KD_TEXT)			do_unblank_screen();		else			do_blank_screen(1);	}	/* Set the colour palette for this VT */	if (vt_cons[new_console]->vc_mode == KD_TEXT)		set_palette() ;		/*	 * Wake anyone waiting for their VT to activate	 */	vt_wake_waitactive();	return;}/* * Performs the front-end of a vt switch */void change_console(unsigned int new_console){        if ((new_console == fg_console) || (vt_dont_switch))                return;        if (!vc_cons_allocated(new_console))		return;	/*	 * If this vt is in process mode, then we need to handshake with	 * that process before switching. Essentially, we store where that	 * vt wants to switch to and wait for it to tell us when it's done	 * (via VT_RELDISP ioctl).	 *	 * We also check to see if the controlling process still exists.	 * If it doesn't, we reset this vt to auto mode and continue.	 * This is a cheap way to track process control. The worst thing	 * that can happen is: we send a signal to a process, it dies, and	 * the switch gets "lost" waiting for a response; hopefully, the	 * user will try again, we'll detect the process is gone (unless	 * the user waits just the right amount of time :-) and revert the	 * vt to auto control.	 */	if (vt_cons[fg_console]->vt_mode.mode == VT_PROCESS)	{		/*		 * Send the signal as privileged - kill_proc() will		 * tell us if the process has gone or something else		 * is awry		 */		if (kill_proc(vt_cons[fg_console]->vt_pid,			      vt_cons[fg_console]->vt_mode.relsig,			      1) == 0)		{			/*			 * It worked. Mark the vt to switch to and			 * return. The process needs to send us a			 * VT_RELDISP ioctl to complete the switch.			 */			vt_cons[fg_console]->vt_newvt = new_console;			return;		}		/*		 * The controlling process has died, so we revert back to		 * normal operation. In this case, we'll also change back		 * to KD_TEXT mode. I'm not sure if this is strictly correct		 * but it saves the agony when the X server dies and the screen		 * remains blanked due to KD_GRAPHICS! It would be nice to do		 * this outside of VT_PROCESS but there is no single process		 * to account for and tracking tty count may be undesirable.		 */		reset_vc(fg_console);		/*		 * Fall through to normal (VT_AUTO) handling of the switch...		 */	}	/*	 * Ignore all switches in KD_GRAPHICS+VT_AUTO mode	 */	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)		return;	complete_change_console(new_console);}void wait_for_keypress(void){	sleep_on(&keypress_wait);}void stop_tty(struct tty_struct *tty){	if (tty->stopped)		return;	tty->stopped = 1;	if (tty->link && tty->link->packet) {		tty->ctrl_status &= ~TIOCPKT_START;		tty->ctrl_status |= TIOCPKT_STOP;		wake_up_interruptible(&tty->link->read_wait);	}	if (tty->driver.stop)		(tty->driver.stop)(tty);}void start_tty(struct tty_struct *tty){	if (!tty->stopped)		return;	tty->stopped = 0;	if (tty->link && tty->link->packet) {		tty->ctrl_status &= ~TIOCPKT_STOP;		tty->ctrl_status |= TIOCPKT_START;		wake_up_interruptible(&tty->link->read_wait);	}	if (tty->driver.start)		(tty->driver.start)(tty);	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);	wake_up_interruptible(&tty->write_wait);}static int tty_read(struct inode * inode, struct file * file, char * buf, int count){	int i;	struct tty_struct * tty;	tty = (struct tty_struct *)file->private_data;	if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))		return -EIO;	if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))		return -EIO;	/* This check not only needs to be done before reading, but also	   whenever read_chan() gets woken up after sleeping, so I've	   moved it to there.  This should only be done for the N_TTY	   line discipline, anyway.  Same goes for write_chan(). -- jlc. */#if 0	if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */	    (tty->pgrp > 0) &&	    (current->tty == tty) &&	    (tty->pgrp != current->pgrp))		if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))			return -EIO;		else {			(void) kill_pg(current->pgrp, SIGTTIN, 1);			return -ERESTARTSYS;		}#endif	if (tty->ldisc.read)		/* XXX casts are for what kernel-wide prototypes should be. */		i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count);	else		i = -EIO;	if (i > 0)		inode->i_atime = CURRENT_TIME;	return i;}/* * Split writes up in sane blocksizes to avoid * denial-of-service type attacks */static inline int do_tty_write(	int (*write)(struct tty_struct *, struct file *, const unsigned char *, unsigned int),	struct inode *inode,	struct tty_struct *tty,	struct file *file,	const unsigned char *buf,	unsigned int count){	int ret = 0, written = 0;	for (;;) {		unsigned int size = PAGE_SIZE*2;		if (size > count)			size = count;		ret = write(tty, file, buf, size);		if (ret <= 0)			break;		written += ret;		buf += ret;		count -= ret;		if (!count)			break;		ret = -ERESTARTSYS;		if (current->signal & ~current->blocked)			break;		if (need_resched)			schedule();	}	if (written) {		inode->i_mtime = CURRENT_TIME;		ret = written;	}	return ret;}static int tty_write(struct inode * inode, struct file * file, const char * buf, int count){	int is_console;	struct tty_struct * tty;	is_console = (inode->i_rdev == CONSOLE_DEV);	if (is_console && redirect)		tty = redirect;	else		tty = (struct tty_struct *)file->private_data;	if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))		return -EIO;	if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))		return -EIO;#if 0	if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&	    (current->tty == tty) && (tty->pgrp != current->pgrp)) {		if (is_orphaned_pgrp(current->pgrp))			return -EIO;		if (!is_ignored(SIGTTOU)) {			(void) kill_pg(current->pgrp, SIGTTOU, 1);			return -ERESTARTSYS;		}	}#endif	if (!tty->ldisc.write)		return -EIO;	return do_tty_write(tty->ldisc.write,		inode, tty, file,		(const unsigned char *)buf,		(unsigned int)count);}/* Semaphore to protect creating and releasing a tty */static struct semaphore tty_sem = MUTEX;static void down_tty_sem(int index){	down(&tty_sem);}static void up_tty_sem(int index){	up(&tty_sem);}static void release_mem(struct tty_struct *tty, int idx);/* * Rewritten to remove races and properly clean up after a failed open.   * The new code protects the open with a semaphore, so it's really  * quite straightforward.  The semaphore locking can probably be * relaxed for the (most common) case of reopening a tty. */static int init_dev(kdev_t device, struct tty_struct **ret_tty){	struct tty_struct *tty, *o_tty;	struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;	struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;	struct tty_driver *driver;		int retval;	int idx;	driver = get_tty_driver(device);	if (!driver)		return -ENODEV;	idx = MINOR(device) - driver->minor_start;	/* 	 * Check whether we need to acquire the tty semaphore to avoid	 * race conditions.  For now, play it safe.	 */	down_tty_sem(idx);	/* check whether we're reopening an existing tty */	tty = driver->table[idx];	if(tty) goto fast_track;	/*	 * First time open is complex, especially for PTY devices.	 * This code guarantees that either everything succeeds and the	 * TTY is ready for operation, or else the table slots are vacated	 * and the allocated memory released.  (Except that the termios 	 * and locked termios may be retained.)	 */	o_tty = NULL;	tp = o_tp = NULL;	ltp = o_ltp = NULL;	tty = (struct tty_struct*) get_free_page(GFP_KERNEL);	if(!tty)		goto fail_no_mem;	initialize_tty_struct(tty);	tty->device = device;	tty->driver = *driver;	tp_loc = &driver->termios[idx];	if (!*tp_loc) {		tp = (struct termios *) kmalloc(sizeof(struct termios),						GFP_KERNEL);		if (!tp)			goto free_mem_out;		*tp = driver->init_termios;	}	ltp_loc = &driver->termios_locked[idx];	if (!*ltp_loc) {		ltp = (struct termios *) kmalloc(sizeof(struct termios),						 GFP_KERNEL);		if (!ltp)			goto free_mem_out;		memset(ltp, 0, sizeof(struct termios));	}	if (driver->type == TTY_DRIVER_TYPE_PTY) {		o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);		if (!o_tty)			goto free_mem_out;		initialize_tty_struct(o_tty);		o_tty->device = (kdev_t) MKDEV(driver->other->major,					driver->other->minor_start + idx);		o_tty->driver = *driver->other;		o_tp_loc  = &driver->other->termios[idx];		if (!*o_tp_loc) {			o_tp = (struct termios *)				kmalloc(sizeof(struct termios), GFP_KERNEL);			if (!o_tp)				goto free_mem_out;			*o_tp = driver->other->init_termios;		}		o_ltp_loc = &driver->other->termios_locked[idx];		if (!*o_ltp_loc) {			o_ltp = (struct termios *)				kmalloc(sizeof(struct termios), GFP_KERNEL);			if (!o_ltp)				goto free_mem_out;			memset(o_ltp, 0, sizeof(struct termios));		}		/*		 * Everything allocated ... set up the o_tty structure.		 */		driver->other->table[idx] = o_tty;		if (!*o_tp_loc)			*o_tp_loc = o_tp;		if (!*o_ltp_loc)			*o_ltp_loc = o_ltp;		o_tty->termios = *o_tp_loc;		o_tty->termios_locked = *o_ltp_loc;		(*driver->other->refcount)++;		if (driver->subtype == PTY_TYPE_MASTER)			o_tty->count++;		/* Establish the links in both directions */		tty->link   = o_tty;		o_tty->link = tty;	}	/* 	 * All structures have been allocated, so now we install them.	 * Failures after this point use release_mem to clean up, so 	 * there's no need to null out the local pointers.	 */	driver->table[idx] = tty;	if (!*tp_loc)		*tp_loc = tp;	if (!*ltp_loc)		*ltp_loc = ltp;	tty->termios = *tp_loc;	tty->termios_locked = *ltp_loc;	(*driver->refcount)++;	tty->count++;	/* 	 * Structures all installed ... call the ldisc open routines.	 * If we fail here just call release_mem to clean up.  No need	 * to decrement the use counts, as release_mem doesn't care.	 */	if (tty->ldisc.open) {		retval = (tty->ldisc.open)(tty);		if (retval)			goto release_mem_out;	}	if (o_tty && o_tty->ldisc.open) {		retval = (o_tty->ldisc.open)(o_tty);		if (retval) {			if (tty->ldisc.close)				(tty->ldisc.close)(tty);			goto release_mem_out;		}	}	goto success;	/*	 * This fast open can be used if the tty is already open.	 * No memory is allocated, and the only failures are from	 * attempting to open a closing tty or attempting multiple	 * opens on a pty master.	 */fast_track:	retval = -EIO;	if (test_bit(TTY_CLOSING, &tty->flags))		goto end_init;	if (driver->type == TTY_DRIVER_TYPE_PTY &&	    driver->subtype == PTY_TYPE_MASTER) {		/*		 * special case for PTY masters: only one open permitted, 		 * and the slave side open count is incremented as well.		 */		if (tty->count)			goto end_init;		tty->link->count++;	}	tty->count++;	tty->driver = *driver; /* N.B. why do this every time?? */success:	retval = 0;	*ret_tty = tty;		/* All paths come through here to release the semaphore */end_init:	up_tty_sem(idx);	return retval;	/* Release locally allocated memory ... nothing placed in slots */free_mem_out:	if (o_tp)

⌨️ 快捷键说明

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