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

📄 term.c

📁 操作系统源代码
💻 C
字号:
/* term - terminal simulator		Author: Andy Tanenbaum *//* This program allows the user to turn a MINIX system into a dumb * terminal to communicate with a remote computer through one of the ttys. * It forks into two processes.  The parent sits in a tight loop copying * from stdin to the tty.  The child sits in a tight loop copying from * the tty to stdout. * * 2 Sept 88 BDE (Bruce D. Evans): Massive changes to make current settings the * default, allow any file as the "tty", support fancy baud rates and remove * references to and dependencies on modems and keyboards, so (e.g.) * a local login on /dev/tty1 can do an external login on /dev/tty2. * * 3 Sept 88 BDE: Split parent again to main process copies from stdin to a * pipe which is copied to the tty.  This stops a blocked write to the * tty from hanging the program. * * 11 Oct 88 BDE: Cleaned up baud rates and parity stripping. * * 09 Oct 90 MAT (Michael A. Temari): Fixed bug where terminal isn't reset * if an error occurs. * * Nov 90 BDE: Don't broadcast kill(0, SIGINT) since two or more of these * in a row will kill the parent shell. * * 19 Oct 89 RW (Ralf Wenk): Adapted to MINIX ST 1.1 + RS232 driver. Split * error into error_n and error. Added resetting of the terminal settings * in error. * * 24 Nov 90 RW: Adapted to MINIX ST 1.5.10.2. Forked processes are now * doing an exec to get a better performance. This idea is stolen from * a terminal program written by Felix Croes. * * 01 May 91 RW: Merged the MINIX ST patches with Andys current version. * Most of the 19 Oct 89 patches are deleted because they are already there. * * 10 Mar 96 KJB: Termios adaption, cleanup, command key interface. * * Example usage: *	term			: baud, bits/char, parity from /dev/tty1 *	term 9600 7 even	: 9600 baud, 7 bits/char, even parity *	term odd 300 7		:  300 baud, 7 bits/char, odd parity *	term /dev/tty2		: use /dev/tty2 rather than /dev/tty1 *				: Any argument starting with "/" is *				: taken as the communication device. *	term 8 57600 /dev/tty2 -atdt4441234	: if an argument begins with *						: - , the rest of that arg is *						: sent to the modem as a *						: dial string */#include <sys/types.h>#include <fcntl.h>#include <termios.h>#include <signal.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <stdarg.h>#include <errno.h>#include <sys/wait.h>#include <sys/stat.h>#define CHUNK 1024		/* how much to read at once */char TERM_LINE[] = "/dev/modem";/* default serial port to use */				/* device lock file */char lockfile[] = "/usr/spool/locks/LK.iii.jjj.kkk";char *commdev;			/* communications device a.k.a. "modem". */int commfd;			/* open file no. for comm device */struct termios tccomm;		/* terminal parameters for commfd */struct termios tcstdin;		/* terminal parameters for stdin */struct termios tcsavestdin;	/* saved terminal parameters for stdin *//* Special key to get term's attention. */#define HOTKEY	'\035'		/* CTRL-] */struct param_s {  char *pattern;  unsigned value;  enum { BAD, BITS, PARITY, SPEED } type;} params[] = {  { "5",	CS5,		BITS	},  { "6",	CS6,		BITS	},  { "7",	CS7,		BITS	},  { "8",	CS8,		BITS	},  { "even",	PARENB,		PARITY	},  { "odd",	PARENB|PARODD,	PARITY	},  { "50",	B50,		SPEED	},  { "75",	B75,		SPEED	},  { "110",	B110,		SPEED	},  { "134",	B134,		SPEED	},  { "200",	B200,		SPEED	},  { "300",	B300,		SPEED	},  { "600",	B600,		SPEED	},  { "1200",	B1200,		SPEED	},  { "1800",	B1800,		SPEED	},  { "2400",	B2400,		SPEED	},  { "4800",	B4800,		SPEED	},  { "9600",	B9600,		SPEED	},  { "19200",	B19200,		SPEED	},  { "38400",	B38400,		SPEED	},  { "57600",	B57600,		SPEED	},  { "115200",	B115200,	SPEED	},  { "",		0,		BAD	},	/* BAD type to end list */};#define NIL ((char *) NULL)		/* tell(fd, ..., NIL) */_PROTOTYPE(int main, (int argc, char *argv[]));_PROTOTYPE(void tell, (int fd, ...));_PROTOTYPE(void reader, (int on));_PROTOTYPE(void shell, (void));_PROTOTYPE(void lock_device, (char *device));_PROTOTYPE(void fatal, (char *label));_PROTOTYPE(void setnum, (char *s, int n));_PROTOTYPE(void set_uart, (int argc, char *argv[], struct termios *tcp));_PROTOTYPE(void set_raw, (struct termios *tcp));_PROTOTYPE(void quit, (int code));int main(argc, argv)int argc;char *argv[];{  int i;  unsigned char key;  int candial;  for (i = 1; i < argc; ++i) {	if (argv[i][0] == '/') {		if (commdev != NULL) {			tell(2, "term: too many communication devices\n", NIL);			exit(1);		}		commdev = argv[i];	}  }  if (commdev == NULL) commdev = TERM_LINE;  /* Save tty attributes of the terminal. */  if (tcgetattr(0, &tcsavestdin) < 0) {	tell(2, "term: standard input is not a terminal\n", NIL);	exit(1);  }  lock_device(commdev);  commfd = open(commdev, O_RDWR);  if (commfd < 0) {	tell(2, "term: can't open ", commdev, ": ", strerror(errno), "\n", NIL);	quit(1);  }  /* Compute RAW modes of terminal and modem. */  if (tcgetattr(commfd, &tccomm) < 0) {	tell(2, "term: ", commdev, " is not a terminal\n", NIL);	quit(1);  }  signal(SIGINT, quit);  signal(SIGTERM, quit);  tcstdin = tcsavestdin;  set_raw(&tcstdin);  set_raw(&tccomm);  set_uart(argc, argv, &tccomm);  (void) tcsetattr(0, TCSANOW, &tcstdin);  (void) tcsetattr(commfd, TCSANOW, &tccomm);  /* Start a reader process to copy modem output to the screen. */  reader(1);  /* Welcome message. */  tell(1, "Connected to ", commdev,			", command key is CTRL-], type ^]h for help\r\n", NIL);  /* Dial. */  candial = 0;  for (i = 1; i < argc; ++i) {	if (argv[i][0] != '-') continue;	tell(commfd, argv[i] + 1, "\r", NIL);	candial = 1;  }  /* Main loop of the terminal simulator. */  while (read(0, &key, 1) == 1) {	if (key == HOTKEY) {		/* Command key typed. */		if (read(0, &key, 1) != 1) continue;		switch (key) {		default:			tell(1, "\r\nTerm commands:\r\n",				" ? - this help\r\n",				candial ? " d - redial\r\n" : "",				" s - subshell (e.g. for file transfer)\r\n",				" h - hangup (+++ ATH)\r\n",				" b - send a break\r\n",				" q - exit term\r\n",				"^] - send a CTRL-]\r\n\n",				NIL);			break;		case 'd':			/* Redial by sending the dial commands again. */			for (i = 1; i < argc; ++i) {				if (argv[i][0] != '-') continue;				tell(commfd, argv[i] + 1, "\r", NIL);			}			break;		case 's':			/* Subshell. */			reader(0);			(void) tcsetattr(0, TCSANOW, &tcsavestdin);			shell();			(void) tcsetattr(0, TCSANOW, &tcstdin);			reader(1);			break;		case 'h':			/* Hangup by using the +++ escape and ATH command. */			sleep(2);			tell(commfd, "+++", NIL);			sleep(2);			tell(commfd, "ATH\r", NIL);			break;		case 'b':			/* Send a break. */			tcsendbreak(commfd, 0);			break;		case 'q':			/* Exit term. */			quit(0);		case HOTKEY:			(void) write(commfd, &key, 1);			break;		}	} else {		/* Send keyboard input down the serial line. */		if (write(commfd, &key, 1) != 1) break;	}  }  tell(2, "term: nothing to copy from input to ", commdev, "?\r\n", NIL);  quit(1);}void tell(int fd, ...){/* Write strings to file descriptor 'fd'. */  va_list ap;  char *s;  va_start(ap, fd);  while ((s = va_arg(ap, char *)) != NIL) write(fd, s, strlen(s));  va_end(ap);}void reader(on)int on;{/* Start or end a process that copies from the modem to the screen. */  static pid_t pid;  char buf[CHUNK];  ssize_t n, m, r;  if (!on) {	/* End the reader process (if any). */	if (pid == 0) return;	kill(pid, SIGKILL);	(void) waitpid(pid, (int *) NULL, 0);	pid = 0;	return;  }  /* Start a reader */  pid = fork();  if (pid < 0) {	tell(2, "term: fork() failed: ", strerror(errno), "\r\n", NIL);	quit(1);  }  if (pid == 0) {	/* Child: Copy from the modem to the screen. */	while ((n = read(commfd, buf, sizeof(buf))) > 0) {		m = 0;		while (m < n && (r = write(1, buf + m, n - m)) > 0) m += r;	}	tell(2, "term: nothing to copy from ", commdev, " to output?\r\n", NIL);	kill(getppid(), SIGTERM);	_exit(1);  }  /* One reader on the loose. */}void shell(){/* Invoke a subshell to allow one to run zmodem for instance. */  pid_t pid;  char *shell, *sh0;  _PROTOTYPE(void (*isav), (int));  _PROTOTYPE(void (*qsav), (int));  _PROTOTYPE(void (*tsav), (int));  tell(1, "\nExit the shell to return to term, ",	commdev, " is open on file descriptor 9.\n", NIL);  if ((shell = getenv("SHELL")) == NULL) shell = "/bin/sh";  if ((sh0 = strrchr(shell, '/')) == NULL) sh0 = shell; else sh0++;  /* Start a shell */  pid = fork();  if (pid < 0) {	tell(2, "term: fork() failed: ", strerror(errno), "\n", NIL);	return;  }  if (pid == 0) {	/* Child: Exec the shell. */	setgid(getgid());	setuid(getuid());	if (commfd != 9) { dup2(commfd, 9); close(commfd); }	execl(shell, sh0, (char *) NULL);	tell(2, "term: can't execute ", shell, ": ", strerror(errno), "\n",NIL);	_exit(1);  }  /* Wait for the shell to exit. */  isav = signal(SIGINT, SIG_IGN);  qsav = signal(SIGQUIT, SIG_IGN);  tsav = signal(SIGTERM, SIG_IGN);  (void) waitpid(pid, (int *) 0, 0);  (void) signal(SIGINT, isav);  (void) signal(SIGQUIT, qsav);  (void) signal(SIGTERM, tsav);  tell(1, "\n[back to term]\n", NIL);}void lock_device(device)char *device;{/* Lock a device by creating a lock file using SYSV style locking. */  struct stat stbuf;  unsigned int pid;  int fd;  int n;  int u;  if (stat(device, &stbuf) < 0) fatal(device);  if (!S_ISCHR(stbuf.st_mode)) {	tell(2, "term: ", device, " is not a character device\n", NIL);	exit(1);  }  /* Compute the lock file name. */  setnum(lockfile + 23, (stbuf.st_dev >> 8) & 0xFF);	/* FS major (why?) */  setnum(lockfile + 27, (stbuf.st_rdev >> 8) & 0xFF);	/* device major */  setnum(lockfile + 31, (stbuf.st_rdev >> 0) & 0xFF);	/* device minor */  /* Try to make a lock file and put my pid in it. */  u = umask(0);  for (;;) {	if ((fd = open(lockfile, O_RDONLY)) < 0) {		/* No lock file, try to lock it myself. */		if (errno != ENOENT) fatal(device);		if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0444)) < 0) {			if (errno == EEXIST) continue;			fatal(lockfile);		}		pid = getpid();		n = write(fd, &pid, sizeof(pid));		if (n < 0) {			n = errno;			(void) unlink(lockfile);			errno = n;			fatal(lockfile);		}		close(fd);		break;	} else {		/* Already there, but who owns it? */		n = read(fd, &pid, sizeof(pid));		if (n < 0) fatal(device);		close(fd);		if (n == sizeof(pid) && kill(pid, 0) == 0) {			/* It is locked by a running process. */			tell(2, "term: ", device,				" is in use by another program\n", NIL);			if (getpgrp() == getpid()) sleep(3);			exit(1);		}		/* Stale lock. */		tell(1, "Removing stale lock ", lockfile, "\n", NIL);		if (unlink(lockfile) < 0 && errno != ENOENT) fatal(lockfile);	}  }  /* Lock achieved, but what if two terms encounters a stale lock at the same   * time?   */  umask(u);}void fatal(char *label){  tell(2, "term: ", label, ": ", strerror(errno), "\n", NIL);  exit(1);}void setnum(char *s, int n){/* Poke 'n' into string 's' backwards as three decimal digits. */  int i;  for (i = 0; i < 3; i++) { *--s = '0' + (n % 10); n /= 10; }}void set_uart(argc, argv, tcp)int argc;char *argv[];struct termios *tcp;{/* Set up the UART parameters. */  int i;  char *arg;  struct param_s *param;  /* Examine all the parameters and check for validity. */  for (i = 1; i < argc; ++i) {	arg = argv[i];	if (arg[0] == '/' || arg[0] == '-') continue;	/* Check parameter for legality. */	for (param = &params[0];	     param->type != BAD && strcmp(arg, param->pattern) != 0;	     ++param);	switch (param->type) {	    case BAD:		tell(2, "Invalid parameter: ", arg, "\n", NIL);		quit(1);		break;	    case BITS:		tcp->c_cflag &= ~CSIZE;		tcp->c_cflag |= param->value;		break;	    case PARITY:		tcp->c_cflag &= PARENB | PARODD;		tcp->c_cflag |= param->value;		break;	    case SPEED:		cfsetispeed(tcp, (speed_t) param->value);		cfsetospeed(tcp, (speed_t) param->value);		break;	}  }}void set_raw(tcp)struct termios *tcp;{  /* Set termios attributes for RAW mode. */  tcp->c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF);  tcp->c_lflag &= ~(ICANON|IEXTEN|ISIG|ECHO|ECHONL);  tcp->c_oflag &= ~(OPOST);  tcp->c_cc[VMIN] = 1;  tcp->c_cc[VTIME] = 0;}void quit(code)int code;{/* Stop the reader process, reset the terminal, and exit. */  reader(0);  tcsetattr(0, TCSANOW, &tcsavestdin);  (void) unlink(lockfile);  exit(code);}

⌨️ 快捷键说明

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