xref: /minix3/minix/commands/term/term.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* term - terminal simulator		Author: Andy Tanenbaum */
2*433d6423SLionel Sambuc 
3*433d6423SLionel Sambuc /* This program allows the user to turn a MINIX system into a dumb
4*433d6423SLionel Sambuc  * terminal to communicate with a remote computer through one of the ttys.
5*433d6423SLionel Sambuc  * It forks into two processes.  The parent sits in a tight loop copying
6*433d6423SLionel Sambuc  * from stdin to the tty.  The child sits in a tight loop copying from
7*433d6423SLionel Sambuc  * the tty to stdout.
8*433d6423SLionel Sambuc  *
9*433d6423SLionel Sambuc  * 2 Sept 88 BDE (Bruce D. Evans): Massive changes to make current settings the
10*433d6423SLionel Sambuc  * default, allow any file as the "tty", support fancy baud rates and remove
11*433d6423SLionel Sambuc  * references to and dependencies on modems and keyboards, so (e.g.)
12*433d6423SLionel Sambuc  * a local login on /dev/tty1 can do an external login on /dev/tty2.
13*433d6423SLionel Sambuc  *
14*433d6423SLionel Sambuc  * 3 Sept 88 BDE: Split parent again to main process copies from stdin to a
15*433d6423SLionel Sambuc  * pipe which is copied to the tty.  This stops a blocked write to the
16*433d6423SLionel Sambuc  * tty from hanging the program.
17*433d6423SLionel Sambuc  *
18*433d6423SLionel Sambuc  * 11 Oct 88 BDE: Cleaned up baud rates and parity stripping.
19*433d6423SLionel Sambuc  *
20*433d6423SLionel Sambuc  * 09 Oct 90 MAT (Michael A. Temari): Fixed bug where terminal isn't reset
21*433d6423SLionel Sambuc  * if an error occurs.
22*433d6423SLionel Sambuc  *
23*433d6423SLionel Sambuc  * Nov 90 BDE: Don't broadcast kill(0, SIGINT) since two or more of these
24*433d6423SLionel Sambuc  * in a row will kill the parent shell.
25*433d6423SLionel Sambuc  *
26*433d6423SLionel Sambuc  * 19 Oct 89 RW (Ralf Wenk): Adapted to MINIX ST 1.1 + RS232 driver. Split
27*433d6423SLionel Sambuc  * error into error_n and error. Added resetting of the terminal settings
28*433d6423SLionel Sambuc  * in error.
29*433d6423SLionel Sambuc  *
30*433d6423SLionel Sambuc  * 24 Nov 90 RW: Adapted to MINIX ST 1.5.10.2. Forked processes are now
31*433d6423SLionel Sambuc  * doing an exec to get a better performance. This idea is stolen from
32*433d6423SLionel Sambuc  * a terminal program written by Felix Croes.
33*433d6423SLionel Sambuc  *
34*433d6423SLionel Sambuc  * 01 May 91 RW: Merged the MINIX ST patches with Andys current version.
35*433d6423SLionel Sambuc  * Most of the 19 Oct 89 patches are deleted because they are already there.
36*433d6423SLionel Sambuc  *
37*433d6423SLionel Sambuc  * 10 Mar 96 KJB: Termios adaption, cleanup, command key interface.
38*433d6423SLionel Sambuc  *
39*433d6423SLionel Sambuc  * 27 Nov 96 KJB: Add -c flag that binds commands to keys.
40*433d6423SLionel Sambuc  *
41*433d6423SLionel Sambuc  * Example usage:
42*433d6423SLionel Sambuc  *	term			: baud, bits/char, parity from /dev/tty1
43*433d6423SLionel Sambuc  *	term 9600 7 even	: 9600 baud, 7 bits/char, even parity
44*433d6423SLionel Sambuc  *	term odd 300 7		:  300 baud, 7 bits/char, odd parity
45*433d6423SLionel Sambuc  *	term /dev/tty2		: use /dev/tty2 rather than /dev/tty1
46*433d6423SLionel Sambuc  *				: Any argument starting with "/" is
47*433d6423SLionel Sambuc  *				: taken as the communication device.
48*433d6423SLionel Sambuc  *	term 8 57600 /dev/tty2 -atdt4441234	: if an argument begins with
49*433d6423SLionel Sambuc  *						: - , the rest of that arg is
50*433d6423SLionel Sambuc  *						: sent to the modem as a
51*433d6423SLionel Sambuc  *						: dial string
52*433d6423SLionel Sambuc  */
53*433d6423SLionel Sambuc 
54*433d6423SLionel Sambuc #include <sys/types.h>
55*433d6423SLionel Sambuc #include <fcntl.h>
56*433d6423SLionel Sambuc #include <termios.h>
57*433d6423SLionel Sambuc #include <signal.h>
58*433d6423SLionel Sambuc #include <stdlib.h>
59*433d6423SLionel Sambuc #include <string.h>
60*433d6423SLionel Sambuc #include <unistd.h>
61*433d6423SLionel Sambuc #include <stdarg.h>
62*433d6423SLionel Sambuc #include <errno.h>
63*433d6423SLionel Sambuc #include <sys/wait.h>
64*433d6423SLionel Sambuc #include <sys/stat.h>
65*433d6423SLionel Sambuc 
66*433d6423SLionel Sambuc #define CHUNK 1024		/* how much to read at once */
67*433d6423SLionel Sambuc 
68*433d6423SLionel Sambuc char TERM_LINE[] = "/dev/modem";/* default serial port to use */
69*433d6423SLionel Sambuc 
70*433d6423SLionel Sambuc 				/* device lock file */
71*433d6423SLionel Sambuc char lockfile[] = "/usr/spool/locks/LK.iii.jjj.kkk";
72*433d6423SLionel Sambuc 
73*433d6423SLionel Sambuc char *commdev;			/* communications device a.k.a. "modem". */
74*433d6423SLionel Sambuc int commfd;			/* open file no. for comm device */
75*433d6423SLionel Sambuc struct termios tccomm;		/* terminal parameters for commfd */
76*433d6423SLionel Sambuc struct termios tcstdin;		/* terminal parameters for stdin */
77*433d6423SLionel Sambuc struct termios tcsavestdin;	/* saved terminal parameters for stdin */
78*433d6423SLionel Sambuc 
79*433d6423SLionel Sambuc /* Special key to get term's attention. */
80*433d6423SLionel Sambuc #define HOTKEY	'\035'		/* CTRL-] */
81*433d6423SLionel Sambuc 
82*433d6423SLionel Sambuc struct param_s {
83*433d6423SLionel Sambuc   char *pattern;
84*433d6423SLionel Sambuc   unsigned value;
85*433d6423SLionel Sambuc   enum { BAD, BITS, PARITY, SPEED } type;
86*433d6423SLionel Sambuc } params[] = {
87*433d6423SLionel Sambuc   { "5",	CS5,		BITS	},
88*433d6423SLionel Sambuc   { "6",	CS6,		BITS	},
89*433d6423SLionel Sambuc   { "7",	CS7,		BITS	},
90*433d6423SLionel Sambuc   { "8",	CS8,		BITS	},
91*433d6423SLionel Sambuc 
92*433d6423SLionel Sambuc   { "even",	PARENB,		PARITY	},
93*433d6423SLionel Sambuc   { "odd",	PARENB|PARODD,	PARITY	},
94*433d6423SLionel Sambuc 
95*433d6423SLionel Sambuc   { "50",	B50,		SPEED	},
96*433d6423SLionel Sambuc   { "75",	B75,		SPEED	},
97*433d6423SLionel Sambuc   { "110",	B110,		SPEED	},
98*433d6423SLionel Sambuc   { "134",	B134,		SPEED	},
99*433d6423SLionel Sambuc   { "200",	B200,		SPEED	},
100*433d6423SLionel Sambuc   { "300",	B300,		SPEED	},
101*433d6423SLionel Sambuc   { "600",	B600,		SPEED	},
102*433d6423SLionel Sambuc   { "1200",	B1200,		SPEED	},
103*433d6423SLionel Sambuc   { "1800",	B1800,		SPEED	},
104*433d6423SLionel Sambuc   { "2400",	B2400,		SPEED	},
105*433d6423SLionel Sambuc   { "4800",	B4800,		SPEED	},
106*433d6423SLionel Sambuc   { "9600",	B9600,		SPEED	},
107*433d6423SLionel Sambuc   { "19200",	B19200,		SPEED	},
108*433d6423SLionel Sambuc   { "38400",	B38400,		SPEED	},
109*433d6423SLionel Sambuc   { "57600",	B57600,		SPEED	},
110*433d6423SLionel Sambuc   { "115200",	B115200,	SPEED	},
111*433d6423SLionel Sambuc   { "",		0,		BAD	},	/* BAD type to end list */
112*433d6423SLionel Sambuc };
113*433d6423SLionel Sambuc 
114*433d6423SLionel Sambuc #define NIL ((char *) NULL)		/* tell(fd, ..., NIL) */
115*433d6423SLionel Sambuc 
116*433d6423SLionel Sambuc int main(int argc, char *argv[]);
117*433d6423SLionel Sambuc int isdialstr(char *arg);
118*433d6423SLionel Sambuc void tell(int fd, ...);
119*433d6423SLionel Sambuc void reader(int on);
120*433d6423SLionel Sambuc void shell(char *cmd);
121*433d6423SLionel Sambuc void lock_device(char *device);
122*433d6423SLionel Sambuc void fatal(char *label);
123*433d6423SLionel Sambuc void setnum(char *s, int n);
124*433d6423SLionel Sambuc void set_uart(int argc, char *argv[], struct termios *tcp);
125*433d6423SLionel Sambuc void set_raw(struct termios *tcp);
126*433d6423SLionel Sambuc void quit(int code);
127*433d6423SLionel Sambuc 
main(argc,argv)128*433d6423SLionel Sambuc int main(argc, argv)
129*433d6423SLionel Sambuc int argc;
130*433d6423SLionel Sambuc char *argv[];
131*433d6423SLionel Sambuc {
132*433d6423SLionel Sambuc   int i;
133*433d6423SLionel Sambuc   unsigned char key;
134*433d6423SLionel Sambuc   int candial;
135*433d6423SLionel Sambuc 
136*433d6423SLionel Sambuc   for (i = 1; i < argc; ++i) {
137*433d6423SLionel Sambuc 	if (argv[i][0] == '/') {
138*433d6423SLionel Sambuc 		if (commdev != NULL) {
139*433d6423SLionel Sambuc 			tell(2, "term: too many communication devices\n", NIL);
140*433d6423SLionel Sambuc 			exit(1);
141*433d6423SLionel Sambuc 		}
142*433d6423SLionel Sambuc 		commdev = argv[i];
143*433d6423SLionel Sambuc 	}
144*433d6423SLionel Sambuc   }
145*433d6423SLionel Sambuc   if (commdev == NULL) commdev = TERM_LINE;
146*433d6423SLionel Sambuc 
147*433d6423SLionel Sambuc   /* Save tty attributes of the terminal. */
148*433d6423SLionel Sambuc   if (tcgetattr(0, &tcsavestdin) < 0) {
149*433d6423SLionel Sambuc 	tell(2, "term: standard input is not a terminal\n", NIL);
150*433d6423SLionel Sambuc 	exit(1);
151*433d6423SLionel Sambuc   }
152*433d6423SLionel Sambuc 
153*433d6423SLionel Sambuc   lock_device(commdev);
154*433d6423SLionel Sambuc 
155*433d6423SLionel Sambuc   commfd = open(commdev, O_RDWR);
156*433d6423SLionel Sambuc   if (commfd < 0) {
157*433d6423SLionel Sambuc 	tell(2, "term: can't open ", commdev, ": ", strerror(errno), "\n", NIL);
158*433d6423SLionel Sambuc 	quit(1);
159*433d6423SLionel Sambuc   }
160*433d6423SLionel Sambuc 
161*433d6423SLionel Sambuc   /* Compute RAW modes of terminal and modem. */
162*433d6423SLionel Sambuc   if (tcgetattr(commfd, &tccomm) < 0) {
163*433d6423SLionel Sambuc 	tell(2, "term: ", commdev, " is not a terminal\n", NIL);
164*433d6423SLionel Sambuc 	quit(1);
165*433d6423SLionel Sambuc   }
166*433d6423SLionel Sambuc   signal(SIGINT, quit);
167*433d6423SLionel Sambuc   signal(SIGTERM, quit);
168*433d6423SLionel Sambuc   tcstdin = tcsavestdin;
169*433d6423SLionel Sambuc   set_raw(&tcstdin);
170*433d6423SLionel Sambuc   set_raw(&tccomm);
171*433d6423SLionel Sambuc   set_uart(argc, argv, &tccomm);
172*433d6423SLionel Sambuc   tcsetattr(0, TCSANOW, &tcstdin);
173*433d6423SLionel Sambuc   tcsetattr(commfd, TCSANOW, &tccomm);
174*433d6423SLionel Sambuc 
175*433d6423SLionel Sambuc   /* Start a reader process to copy modem output to the screen. */
176*433d6423SLionel Sambuc   reader(1);
177*433d6423SLionel Sambuc 
178*433d6423SLionel Sambuc   /* Welcome message. */
179*433d6423SLionel Sambuc   tell(1, "Connected to ", commdev,
180*433d6423SLionel Sambuc 			", command key is CTRL-], type ^]? for help\r\n", NIL);
181*433d6423SLionel Sambuc 
182*433d6423SLionel Sambuc   /* Dial. */
183*433d6423SLionel Sambuc   candial = 0;
184*433d6423SLionel Sambuc   for (i = 1; i < argc; ++i) {
185*433d6423SLionel Sambuc 	if (!isdialstr(argv[i])) continue;
186*433d6423SLionel Sambuc 	tell(commfd, argv[i] + 1, "\r", NIL);
187*433d6423SLionel Sambuc 	candial = 1;
188*433d6423SLionel Sambuc   }
189*433d6423SLionel Sambuc 
190*433d6423SLionel Sambuc   /* Main loop of the terminal simulator. */
191*433d6423SLionel Sambuc   while (read(0, &key, 1) == 1) {
192*433d6423SLionel Sambuc 	if (key == HOTKEY) {
193*433d6423SLionel Sambuc 		/* Command key typed. */
194*433d6423SLionel Sambuc 		if (read(0, &key, 1) != 1) continue;
195*433d6423SLionel Sambuc 
196*433d6423SLionel Sambuc 		switch (key) {
197*433d6423SLionel Sambuc 		default:
198*433d6423SLionel Sambuc 			/* Added command? */
199*433d6423SLionel Sambuc 			for (i = 1; i < argc; ++i) {
200*433d6423SLionel Sambuc 				char *arg = argv[i];
201*433d6423SLionel Sambuc 
202*433d6423SLionel Sambuc 				if (arg[0] == '-' && arg[1] == 'c'
203*433d6423SLionel Sambuc 							&& arg[2] == key) {
204*433d6423SLionel Sambuc 					reader(0);
205*433d6423SLionel Sambuc 					tcsetattr(0, TCSANOW, &tcsavestdin);
206*433d6423SLionel Sambuc 					shell(arg+3);
207*433d6423SLionel Sambuc 					tcsetattr(0, TCSANOW, &tcstdin);
208*433d6423SLionel Sambuc 					reader(1);
209*433d6423SLionel Sambuc 					break;
210*433d6423SLionel Sambuc 				}
211*433d6423SLionel Sambuc 			}
212*433d6423SLionel Sambuc 			if (i < argc) break;
213*433d6423SLionel Sambuc 
214*433d6423SLionel Sambuc 			/* Unrecognized command, print list. */
215*433d6423SLionel Sambuc 			tell(1, "\r\nTerm commands:\r\n",
216*433d6423SLionel Sambuc 				" ? - this help\r\n",
217*433d6423SLionel Sambuc 				candial ? " d - redial\r\n" : "",
218*433d6423SLionel Sambuc 				" s - subshell (e.g. for file transfer)\r\n",
219*433d6423SLionel Sambuc 				" h - hangup (+++ ATH)\r\n",
220*433d6423SLionel Sambuc 				" b - send a break\r\n",
221*433d6423SLionel Sambuc 				" q - exit term\r\n",
222*433d6423SLionel Sambuc 				NIL);
223*433d6423SLionel Sambuc 			for (i = 1; i < argc; ++i) {
224*433d6423SLionel Sambuc 				char *arg = argv[i];
225*433d6423SLionel Sambuc 				static char cmd[] = " x - ";
226*433d6423SLionel Sambuc 
227*433d6423SLionel Sambuc 				if (arg[0] == '-' && arg[1] == 'c'
228*433d6423SLionel Sambuc 							&& arg[2] != 0) {
229*433d6423SLionel Sambuc 					cmd[1] = arg[2];
230*433d6423SLionel Sambuc 					tell(1, cmd, arg+3, "\r\n", NIL);
231*433d6423SLionel Sambuc 				}
232*433d6423SLionel Sambuc 			}
233*433d6423SLionel Sambuc 			tell(1, "^] - send a CTRL-]\r\n\n",
234*433d6423SLionel Sambuc 				NIL);
235*433d6423SLionel Sambuc 			break;
236*433d6423SLionel Sambuc 		case 'd':
237*433d6423SLionel Sambuc 			/* Redial by sending the dial commands again. */
238*433d6423SLionel Sambuc 			for (i = 1; i < argc; ++i) {
239*433d6423SLionel Sambuc 				if (!isdialstr(argv[i])) continue;
240*433d6423SLionel Sambuc 				tell(commfd, argv[i] + 1, "\r", NIL);
241*433d6423SLionel Sambuc 			}
242*433d6423SLionel Sambuc 			break;
243*433d6423SLionel Sambuc 		case 's':
244*433d6423SLionel Sambuc 			/* Subshell. */
245*433d6423SLionel Sambuc 			reader(0);
246*433d6423SLionel Sambuc 			tcsetattr(0, TCSANOW, &tcsavestdin);
247*433d6423SLionel Sambuc 			shell(NULL);
248*433d6423SLionel Sambuc 			tcsetattr(0, TCSANOW, &tcstdin);
249*433d6423SLionel Sambuc 			reader(1);
250*433d6423SLionel Sambuc 			break;
251*433d6423SLionel Sambuc 		case 'h':
252*433d6423SLionel Sambuc 			/* Hangup by using the +++ escape and ATH command. */
253*433d6423SLionel Sambuc 			sleep(2);
254*433d6423SLionel Sambuc 			tell(commfd, "+++", NIL);
255*433d6423SLionel Sambuc 			sleep(2);
256*433d6423SLionel Sambuc 			tell(commfd, "ATH\r", NIL);
257*433d6423SLionel Sambuc 			break;
258*433d6423SLionel Sambuc 		case 'b':
259*433d6423SLionel Sambuc 			/* Send a break. */
260*433d6423SLionel Sambuc 			tcsendbreak(commfd, 0);
261*433d6423SLionel Sambuc 			break;
262*433d6423SLionel Sambuc 		case 'q':
263*433d6423SLionel Sambuc 			/* Exit term. */
264*433d6423SLionel Sambuc 			quit(0);
265*433d6423SLionel Sambuc 		case HOTKEY:
266*433d6423SLionel Sambuc 			(void) write(commfd, &key, 1);
267*433d6423SLionel Sambuc 			break;
268*433d6423SLionel Sambuc 		}
269*433d6423SLionel Sambuc 	} else {
270*433d6423SLionel Sambuc 		/* Send keyboard input down the serial line. */
271*433d6423SLionel Sambuc 		if (write(commfd, &key, 1) != 1) break;
272*433d6423SLionel Sambuc 	}
273*433d6423SLionel Sambuc   }
274*433d6423SLionel Sambuc   tell(2, "term: nothing to copy from input to ", commdev, "?\r\n", NIL);
275*433d6423SLionel Sambuc   quit(1);
276*433d6423SLionel Sambuc }
277*433d6423SLionel Sambuc 
278*433d6423SLionel Sambuc 
isdialstr(char * arg)279*433d6423SLionel Sambuc int isdialstr(char *arg)
280*433d6423SLionel Sambuc {
281*433d6423SLionel Sambuc /* True iff arg is the start of a dial string, i.e. "-at...". */
282*433d6423SLionel Sambuc 
283*433d6423SLionel Sambuc   return (arg[0] == '-'
284*433d6423SLionel Sambuc   	&& (arg[1] == 'a' || arg[1] == 'A')
285*433d6423SLionel Sambuc   	&& (arg[2] == 't' || arg[2] == 'T'));
286*433d6423SLionel Sambuc }
287*433d6423SLionel Sambuc 
288*433d6423SLionel Sambuc 
tell(int fd,...)289*433d6423SLionel Sambuc void tell(int fd, ...)
290*433d6423SLionel Sambuc {
291*433d6423SLionel Sambuc /* Write strings to file descriptor 'fd'. */
292*433d6423SLionel Sambuc   va_list ap;
293*433d6423SLionel Sambuc   char *s;
294*433d6423SLionel Sambuc 
295*433d6423SLionel Sambuc   va_start(ap, fd);
296*433d6423SLionel Sambuc   while ((s = va_arg(ap, char *)) != NIL) write(fd, s, strlen(s));
297*433d6423SLionel Sambuc   va_end(ap);
298*433d6423SLionel Sambuc }
299*433d6423SLionel Sambuc 
300*433d6423SLionel Sambuc 
reader(on)301*433d6423SLionel Sambuc void reader(on)
302*433d6423SLionel Sambuc int on;
303*433d6423SLionel Sambuc {
304*433d6423SLionel Sambuc /* Start or end a process that copies from the modem to the screen. */
305*433d6423SLionel Sambuc 
306*433d6423SLionel Sambuc   static pid_t pid;
307*433d6423SLionel Sambuc   char buf[CHUNK];
308*433d6423SLionel Sambuc   ssize_t n, m, r;
309*433d6423SLionel Sambuc 
310*433d6423SLionel Sambuc   if (!on) {
311*433d6423SLionel Sambuc 	/* End the reader process (if any). */
312*433d6423SLionel Sambuc 	if (pid == 0) return;
313*433d6423SLionel Sambuc 	kill(pid, SIGKILL);
314*433d6423SLionel Sambuc 	(void) waitpid(pid, (int *) NULL, 0);
315*433d6423SLionel Sambuc 	pid = 0;
316*433d6423SLionel Sambuc 	return;
317*433d6423SLionel Sambuc   }
318*433d6423SLionel Sambuc 
319*433d6423SLionel Sambuc   /* Start a reader */
320*433d6423SLionel Sambuc   pid = fork();
321*433d6423SLionel Sambuc   if (pid < 0) {
322*433d6423SLionel Sambuc 	tell(2, "term: fork() failed: ", strerror(errno), "\r\n", NIL);
323*433d6423SLionel Sambuc 	quit(1);
324*433d6423SLionel Sambuc   }
325*433d6423SLionel Sambuc   if (pid == 0) {
326*433d6423SLionel Sambuc 	/* Child: Copy from the modem to the screen. */
327*433d6423SLionel Sambuc 
328*433d6423SLionel Sambuc 	while ((n = read(commfd, buf, sizeof(buf))) > 0) {
329*433d6423SLionel Sambuc 		m = 0;
330*433d6423SLionel Sambuc 		while (m < n && (r = write(1, buf + m, n - m)) > 0) m += r;
331*433d6423SLionel Sambuc 	}
332*433d6423SLionel Sambuc 	tell(2, "term: nothing to copy from ", commdev, " to output?\r\n", NIL);
333*433d6423SLionel Sambuc 	kill(getppid(), SIGTERM);
334*433d6423SLionel Sambuc 	_exit(1);
335*433d6423SLionel Sambuc   }
336*433d6423SLionel Sambuc   /* One reader on the loose. */
337*433d6423SLionel Sambuc }
338*433d6423SLionel Sambuc 
339*433d6423SLionel Sambuc 
shell(char * cmd)340*433d6423SLionel Sambuc void shell(char *cmd)
341*433d6423SLionel Sambuc {
342*433d6423SLionel Sambuc /* Invoke a subshell to allow one to run zmodem for instance.  Run sh -c 'cmd'
343*433d6423SLionel Sambuc  * instead if 'cmd' non-null.
344*433d6423SLionel Sambuc  */
345*433d6423SLionel Sambuc 
346*433d6423SLionel Sambuc   pid_t pid;
347*433d6423SLionel Sambuc   char *shell, *sh0;
348*433d6423SLionel Sambuc   void(*isav) (int);
349*433d6423SLionel Sambuc   void(*qsav) (int);
350*433d6423SLionel Sambuc   void(*tsav) (int);
351*433d6423SLionel Sambuc 
352*433d6423SLionel Sambuc   if (cmd == NULL) {
353*433d6423SLionel Sambuc 	tell(1, "\nExit the shell to return to term, ",
354*433d6423SLionel Sambuc 		commdev, " is open on file descriptor 9.\n", NIL);
355*433d6423SLionel Sambuc   }
356*433d6423SLionel Sambuc 
357*433d6423SLionel Sambuc   if (cmd != NULL || (shell = getenv("SHELL")) == NULL) shell = "/bin/sh";
358*433d6423SLionel Sambuc   if ((sh0 = strrchr(shell, '/')) == NULL) sh0 = shell; else sh0++;
359*433d6423SLionel Sambuc 
360*433d6423SLionel Sambuc   /* Start a shell */
361*433d6423SLionel Sambuc   pid = fork();
362*433d6423SLionel Sambuc   if (pid < 0) {
363*433d6423SLionel Sambuc 	tell(2, "term: fork() failed: ", strerror(errno), "\n", NIL);
364*433d6423SLionel Sambuc 	return;
365*433d6423SLionel Sambuc   }
366*433d6423SLionel Sambuc   if (pid == 0) {
367*433d6423SLionel Sambuc 	/* Child: Exec the shell. */
368*433d6423SLionel Sambuc 	setgid(getgid());
369*433d6423SLionel Sambuc 	setuid(getuid());
370*433d6423SLionel Sambuc 
371*433d6423SLionel Sambuc 	if (commfd != 9) { dup2(commfd, 9); close(commfd); }
372*433d6423SLionel Sambuc 
373*433d6423SLionel Sambuc 	if (cmd == NULL) {
374*433d6423SLionel Sambuc 		execl(shell, sh0, (char *) NULL);
375*433d6423SLionel Sambuc 	} else {
376*433d6423SLionel Sambuc 		execl(shell, sh0, "-c", cmd, (char *) NULL);
377*433d6423SLionel Sambuc 	}
378*433d6423SLionel Sambuc 	tell(2, "term: can't execute ", shell, ": ", strerror(errno), "\n",NIL);
379*433d6423SLionel Sambuc 	_exit(1);
380*433d6423SLionel Sambuc   }
381*433d6423SLionel Sambuc   /* Wait for the shell to exit. */
382*433d6423SLionel Sambuc   isav = signal(SIGINT, SIG_IGN);
383*433d6423SLionel Sambuc   qsav = signal(SIGQUIT, SIG_IGN);
384*433d6423SLionel Sambuc   tsav = signal(SIGTERM, SIG_IGN);
385*433d6423SLionel Sambuc   (void) waitpid(pid, (int *) 0, 0);
386*433d6423SLionel Sambuc   (void) signal(SIGINT, isav);
387*433d6423SLionel Sambuc   (void) signal(SIGQUIT, qsav);
388*433d6423SLionel Sambuc   (void) signal(SIGTERM, tsav);
389*433d6423SLionel Sambuc   tell(1, "\n[back to term]\n", NIL);
390*433d6423SLionel Sambuc }
391*433d6423SLionel Sambuc 
392*433d6423SLionel Sambuc 
lock_device(device)393*433d6423SLionel Sambuc void lock_device(device)
394*433d6423SLionel Sambuc char *device;
395*433d6423SLionel Sambuc {
396*433d6423SLionel Sambuc /* Lock a device by creating a lock file using SYSV style locking. */
397*433d6423SLionel Sambuc 
398*433d6423SLionel Sambuc   struct stat stbuf;
399*433d6423SLionel Sambuc   unsigned int pid;
400*433d6423SLionel Sambuc   int fd;
401*433d6423SLionel Sambuc   int n;
402*433d6423SLionel Sambuc   int u;
403*433d6423SLionel Sambuc 
404*433d6423SLionel Sambuc   if (stat(device, &stbuf) < 0) fatal(device);
405*433d6423SLionel Sambuc 
406*433d6423SLionel Sambuc   if (!S_ISCHR(stbuf.st_mode)) {
407*433d6423SLionel Sambuc 	tell(2, "term: ", device, " is not a character device\n", NIL);
408*433d6423SLionel Sambuc 	exit(1);
409*433d6423SLionel Sambuc   }
410*433d6423SLionel Sambuc 
411*433d6423SLionel Sambuc   /* Compute the lock file name. */
412*433d6423SLionel Sambuc   setnum(lockfile + 23, (stbuf.st_dev >> 8) & 0xFF);	/* FS major (why?) */
413*433d6423SLionel Sambuc   setnum(lockfile + 27, (stbuf.st_rdev >> 8) & 0xFF);	/* device major */
414*433d6423SLionel Sambuc   setnum(lockfile + 31, (stbuf.st_rdev >> 0) & 0xFF);	/* device minor */
415*433d6423SLionel Sambuc 
416*433d6423SLionel Sambuc   /* Try to make a lock file and put my pid in it. */
417*433d6423SLionel Sambuc   u = umask(0);
418*433d6423SLionel Sambuc   for (;;) {
419*433d6423SLionel Sambuc 	if ((fd = open(lockfile, O_RDONLY)) < 0) {
420*433d6423SLionel Sambuc 		/* No lock file, try to lock it myself. */
421*433d6423SLionel Sambuc 		if (errno != ENOENT) fatal(device);
422*433d6423SLionel Sambuc 		if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0444)) < 0) {
423*433d6423SLionel Sambuc 			if (errno == EEXIST) continue;
424*433d6423SLionel Sambuc 			fatal(lockfile);
425*433d6423SLionel Sambuc 		}
426*433d6423SLionel Sambuc 		pid = getpid();
427*433d6423SLionel Sambuc 		n = write(fd, &pid, sizeof(pid));
428*433d6423SLionel Sambuc 		if (n < 0) {
429*433d6423SLionel Sambuc 			n = errno;
430*433d6423SLionel Sambuc 			(void) unlink(lockfile);
431*433d6423SLionel Sambuc 			errno = n;
432*433d6423SLionel Sambuc 			fatal(lockfile);
433*433d6423SLionel Sambuc 		}
434*433d6423SLionel Sambuc 		close(fd);
435*433d6423SLionel Sambuc 		break;
436*433d6423SLionel Sambuc 	} else {
437*433d6423SLionel Sambuc 		/* Already there, but who owns it? */
438*433d6423SLionel Sambuc 		n = read(fd, &pid, sizeof(pid));
439*433d6423SLionel Sambuc 		if (n < 0) fatal(device);
440*433d6423SLionel Sambuc 		close(fd);
441*433d6423SLionel Sambuc 		if (n == sizeof(pid) && !(kill(pid, 0) < 0 && errno == ESRCH)) {
442*433d6423SLionel Sambuc 			/* It is locked by a running process. */
443*433d6423SLionel Sambuc 			tell(2, "term: ", device,
444*433d6423SLionel Sambuc 				" is in use by another program\n", NIL);
445*433d6423SLionel Sambuc 			if (getpgrp() == getpid()) sleep(3);
446*433d6423SLionel Sambuc 			exit(1);
447*433d6423SLionel Sambuc 		}
448*433d6423SLionel Sambuc 		/* Stale lock. */
449*433d6423SLionel Sambuc 		tell(1, "Removing stale lock ", lockfile, "\n", NIL);
450*433d6423SLionel Sambuc 		if (unlink(lockfile) < 0 && errno != ENOENT) fatal(lockfile);
451*433d6423SLionel Sambuc 	}
452*433d6423SLionel Sambuc   }
453*433d6423SLionel Sambuc   /* Lock achieved, but what if two terms encounters a stale lock at the same
454*433d6423SLionel Sambuc    * time?
455*433d6423SLionel Sambuc    */
456*433d6423SLionel Sambuc   umask(u);
457*433d6423SLionel Sambuc }
458*433d6423SLionel Sambuc 
459*433d6423SLionel Sambuc 
fatal(char * label)460*433d6423SLionel Sambuc void fatal(char *label)
461*433d6423SLionel Sambuc {
462*433d6423SLionel Sambuc   tell(2, "term: ", label, ": ", strerror(errno), "\n", NIL);
463*433d6423SLionel Sambuc   exit(1);
464*433d6423SLionel Sambuc }
465*433d6423SLionel Sambuc 
466*433d6423SLionel Sambuc 
setnum(char * s,int n)467*433d6423SLionel Sambuc void setnum(char *s, int n)
468*433d6423SLionel Sambuc {
469*433d6423SLionel Sambuc /* Poke 'n' into string 's' backwards as three decimal digits. */
470*433d6423SLionel Sambuc   int i;
471*433d6423SLionel Sambuc 
472*433d6423SLionel Sambuc   for (i = 0; i < 3; i++) { *--s = '0' + (n % 10); n /= 10; }
473*433d6423SLionel Sambuc }
474*433d6423SLionel Sambuc 
475*433d6423SLionel Sambuc 
set_uart(argc,argv,tcp)476*433d6423SLionel Sambuc void set_uart(argc, argv, tcp)
477*433d6423SLionel Sambuc int argc;
478*433d6423SLionel Sambuc char *argv[];
479*433d6423SLionel Sambuc struct termios *tcp;
480*433d6423SLionel Sambuc {
481*433d6423SLionel Sambuc /* Set up the UART parameters. */
482*433d6423SLionel Sambuc 
483*433d6423SLionel Sambuc   int i;
484*433d6423SLionel Sambuc   char *arg;
485*433d6423SLionel Sambuc   struct param_s *param;
486*433d6423SLionel Sambuc 
487*433d6423SLionel Sambuc   /* Examine all the parameters and check for validity. */
488*433d6423SLionel Sambuc   for (i = 1; i < argc; ++i) {
489*433d6423SLionel Sambuc 	arg = argv[i];
490*433d6423SLionel Sambuc 	if (arg[0] == '/' || arg[0] == '-') continue;
491*433d6423SLionel Sambuc 
492*433d6423SLionel Sambuc 	/* Check parameter for legality. */
493*433d6423SLionel Sambuc 	for (param = &params[0];
494*433d6423SLionel Sambuc 	     param->type != BAD && strcmp(arg, param->pattern) != 0;
495*433d6423SLionel Sambuc 	     ++param);
496*433d6423SLionel Sambuc 	switch (param->type) {
497*433d6423SLionel Sambuc 	    case BAD:
498*433d6423SLionel Sambuc 		tell(2, "Invalid parameter: ", arg, "\n", NIL);
499*433d6423SLionel Sambuc 		quit(1);
500*433d6423SLionel Sambuc 		break;
501*433d6423SLionel Sambuc 	    case BITS:
502*433d6423SLionel Sambuc 		tcp->c_cflag &= ~CSIZE;
503*433d6423SLionel Sambuc 		tcp->c_cflag |= param->value;
504*433d6423SLionel Sambuc 		break;
505*433d6423SLionel Sambuc 	    case PARITY:
506*433d6423SLionel Sambuc 		tcp->c_cflag &= PARENB | PARODD;
507*433d6423SLionel Sambuc 		tcp->c_cflag |= param->value;
508*433d6423SLionel Sambuc 		break;
509*433d6423SLionel Sambuc 	    case SPEED:
510*433d6423SLionel Sambuc 		cfsetispeed(tcp, (speed_t) param->value);
511*433d6423SLionel Sambuc 		cfsetospeed(tcp, (speed_t) param->value);
512*433d6423SLionel Sambuc 		break;
513*433d6423SLionel Sambuc 	}
514*433d6423SLionel Sambuc   }
515*433d6423SLionel Sambuc }
516*433d6423SLionel Sambuc 
517*433d6423SLionel Sambuc 
set_raw(tcp)518*433d6423SLionel Sambuc void set_raw(tcp)
519*433d6423SLionel Sambuc struct termios *tcp;
520*433d6423SLionel Sambuc {
521*433d6423SLionel Sambuc   /* Set termios attributes for RAW mode. */
522*433d6423SLionel Sambuc 
523*433d6423SLionel Sambuc   tcp->c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF);
524*433d6423SLionel Sambuc   tcp->c_lflag &= ~(ICANON|IEXTEN|ISIG|ECHO|ECHONL);
525*433d6423SLionel Sambuc   tcp->c_oflag &= ~(OPOST);
526*433d6423SLionel Sambuc   tcp->c_cc[VMIN] = 1;
527*433d6423SLionel Sambuc   tcp->c_cc[VTIME] = 0;
528*433d6423SLionel Sambuc }
529*433d6423SLionel Sambuc 
530*433d6423SLionel Sambuc 
quit(code)531*433d6423SLionel Sambuc void quit(code)
532*433d6423SLionel Sambuc int code;
533*433d6423SLionel Sambuc {
534*433d6423SLionel Sambuc /* Stop the reader process, reset the terminal, and exit. */
535*433d6423SLionel Sambuc   reader(0);
536*433d6423SLionel Sambuc   tcsetattr(0, TCSANOW, &tcsavestdin);
537*433d6423SLionel Sambuc   (void) unlink(lockfile);
538*433d6423SLionel Sambuc   exit(code);
539*433d6423SLionel Sambuc }
540