1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <termio.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <poll.h>
38 #include <unistd.h>
39 #include "sys/stropts.h"
40 #include <sys/resource.h>
41 #include "sac.h"
42 #include "ttymon.h"
43 #include "tmstruct.h"
44 #include "tmextern.h"
45 #ifdef SYS_NAME
46 #include <sys/utsname.h>
47 #endif
48
49 static void openline();
50 static void invoke_service();
51 static char *do_autobaud();
52 static struct Gdef *next_speed();
53 static int check_hup();
54
55 extern struct Gdef *get_speed();
56 extern struct strbuf *peek_ptr, *do_peek();
57
58 /*
59 * tmchild - process that handles peeking data, determine baud rate
60 * and invoke service on each individual port.
61 *
62 */
63 void
tmchild(pmtab)64 tmchild(pmtab)
65 struct pmtab *pmtab;
66 {
67 register struct Gdef *speedef;
68 char *auto_speed = "";
69 struct sigaction sigact;
70
71 #ifdef DEBUG
72 debug("in tmchild");
73 #endif
74 peek_ptr = NULL;
75 if (pmtab->p_status != GETTY) {
76 child_sigcatch();
77 (void) close(PCpipe[0]); /* close parent end of the pipe */
78 if (ioctl(PCpipe[1], I_SETSIG, S_HANGUP) == -1) {
79 log("I_SETSIG failed: %s", strerror(errno));
80 exit(1);
81 }
82 /*
83 * the following check is to make sure no hangup
84 * happens before registering for SIGPOLL
85 */
86 if (check_hup(PCpipe[1])) {
87 #ifdef DEBUG
88 debug("PCpipe hungup, tmchild exiting");
89 #endif
90 exit(1);
91 }
92
93 if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) {
94 if (pmtab->p_fd > 0) {
95 (void) close(pmtab->p_fd);
96 pmtab->p_fd = 0;
97 }
98 }
99
100 /*
101 * become the session leader so that a controlling tty
102 * will be allocated.
103 */
104 (void) setsid();
105 }
106 speedef = get_speed(pmtab->p_ttylabel);
107 openline(pmtab, speedef);
108 if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) {
109 if (pmtab->p_fd >= 0) {
110 if ((pmtab->p_modules != NULL)&&(*(pmtab->p_modules) != '\0')) {
111 if (push_linedisc(pmtab->p_fd, pmtab->p_modules, pmtab->p_device) == -1) {
112 (void) close(pmtab->p_fd);
113 return;
114 }
115 }
116 }
117 }
118 if ((pmtab->p_ttyflags & C_FLAG) &&
119 (State != PM_DISABLED) &&
120 (!(pmtab->p_flags & X_FLAG))) {
121 /*
122 * if "c" flag is set, and the port is not disabled
123 * invoke service immediately
124 */
125 if (set_termio(0, speedef->g_fflags, NULL,FALSE,CANON) == -1) {
126 log("set final termio failed");
127 exit(1);
128 }
129 invoke_service(pmtab);
130 exit(1); /*NOTREACHED*/
131 }
132 if (speedef->g_autobaud & A_FLAG) {
133 auto_speed = do_autobaud(pmtab, speedef);
134 }
135 if (set_termio(0, speedef->g_fflags, NULL, FALSE, CANON) == -1) {
136 log("set final termio failed");
137 exit(1);
138 }
139 if ((pmtab->p_ttyflags & (R_FLAG|A_FLAG)) ||
140 (pmtab->p_status == GETTY) || (pmtab->p_timeout > 0)) {
141 write_prompt(1, pmtab, TRUE, TRUE);
142 if (pmtab->p_timeout) {
143 sigact.sa_flags = 0;
144 sigact.sa_handler = timedout;
145 (void) sigemptyset(&sigact.sa_mask);
146 (void) sigaction(SIGALRM, &sigact, NULL);
147 (void) alarm((unsigned)pmtab->p_timeout);
148 }
149 }
150 else if ((pmtab->p_ttyflags & (B_FLAG)))
151 write_prompt(pmtab->p_fd, pmtab, TRUE, TRUE);
152
153
154 /* Loop until user is successful in invoking service. */
155 for (;;) {
156
157 /* Peek the user's typed response and respond appropriately. */
158 switch (poll_data()) {
159 case GOODNAME:
160 #ifdef DEBUG
161 debug("got GOODNAME");
162 #endif
163 if (pmtab->p_timeout) {
164 (void) alarm((unsigned)0);
165 sigact.sa_flags = 0;
166 sigact.sa_handler = SIG_DFL;
167 (void) sigemptyset(&sigact.sa_mask);
168 (void) sigaction(SIGALRM, &sigact, NULL);
169 }
170 if ((State == PM_DISABLED)||(pmtab->p_flags & X_FLAG)){
171 write_prompt(1, pmtab, TRUE, FALSE);
172 break;
173 }
174 if (set_termio(0, speedef->g_fflags, auto_speed,
175 FALSE, CANON) == -1) {
176 log("set final termio failed");
177 exit(1);
178 }
179 invoke_service(pmtab);
180 exit(1); /*NOTREACHED*/
181
182 case BADSPEED:
183 /* wrong speed! try next speed in the list. */
184 speedef = next_speed(speedef);
185 #ifdef DEBUG
186 debug("BADSPEED: setup next speed");
187 #endif
188 if (speedef->g_autobaud & A_FLAG) {
189 if (auto_termio(0) == -1) {
190 exit(1);
191 }
192 auto_speed = do_autobaud(pmtab, speedef);
193 }
194 else {
195 auto_speed = NULL;
196 /*
197 * this reset may fail if the speed is not
198 * supported by the system
199 * we just cycle through it to the next one
200 */
201 if (set_termio(0, speedef->g_iflags, NULL,
202 FALSE, CANON) != 0) {
203 log("Warning -- speed of <%s> may "
204 "be not supported by the system",
205 speedef->g_id);
206 }
207 }
208 write_prompt(1, pmtab, TRUE, TRUE);
209 break;
210
211 case NONAME:
212 #ifdef DEBUG
213 debug("got NONAME");
214 #endif
215 write_prompt(1, pmtab, FALSE, FALSE);
216 break;
217
218 } /* end switch */
219
220 peek_ptr = NULL;
221 if (pmtab->p_timeout) {
222 sigact.sa_flags = 0;
223 sigact.sa_handler = timedout;
224 (void) sigemptyset(&sigact.sa_mask);
225 (void) sigaction(SIGALRM, &sigact, NULL);
226 (void) alarm((unsigned)pmtab->p_timeout);
227 }
228 } /* end for loop */
229 }
230
231 static void
openline(pmtab,speedef)232 openline(pmtab, speedef)
233 struct pmtab *pmtab;
234 struct Gdef *speedef;
235 {
236 char buffer[5];
237 int rtn = 0;
238 int line_count;
239
240 #ifdef DEBUG
241 debug("in openline");
242 #endif
243 if (pmtab->p_status != GETTY) {
244 (void) close(0);
245 /* open should return fd 0, if not, then close it */
246 if ((pmtab->p_fd = open(pmtab->p_device, O_RDWR)) != 0) {
247 log("open \"%s\" failed: %s", pmtab->p_device,
248 strerror(errno));
249 exit(1);
250 }
251 }
252 (void) close(1);
253 (void) close(2);
254 (void) dup(0);
255 (void) dup(0);
256
257 if (pmtab->p_ttyflags & R_FLAG) { /* wait_read is needed */
258 if (pmtab->p_count) {
259 if (peek_ptr != NULL)
260 if ((peek_ptr->buf[0]&0x7F) == '\n' ||
261 (peek_ptr->buf[0]&0x7F) == '\r')
262 pmtab->p_count--;
263
264 /*
265 * - wait for "p_count" lines
266 * - datakit switch does not
267 * know you are a host or a terminal
268 * - so it send you several lines of msg
269 * - we need to swallow that msg
270 * - we assume the baud rate is correct
271 * - if it is not, '\n' will not look like '\n'
272 * and we will wait forever here
273 */
274 if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) {
275 log("set final termio failed");
276 exit(1);
277 }
278 for (line_count = 0; line_count < pmtab->p_count; ) {
279 if (read(0, buffer, 1) < 0
280 || *buffer == '\0'
281 || *buffer == '\004') {
282 (void) close(0);
283 exit(0);
284 }
285 if (*buffer == '\n')
286 line_count++;
287 }
288 }
289 else { /* wait for 1 char */
290 if (peek_ptr == NULL) {
291 if (set_termio(0, NULL, NULL,TRUE,RAW) == -1) {
292 log("set termio RAW failed");
293 exit(1);
294 }
295 rtn = read(0, buffer, 1);
296 } else
297 *buffer = (peek_ptr->buf[0]&0x7F);
298
299 /*
300 * NOTE: Cu on a direct line when ~. is encountered will
301 * send EOTs to the other side. EOT=\004
302 */
303 if (rtn < 0 || *buffer == '\004') {
304 (void) close(0);
305 exit(0);
306 }
307 }
308 peek_ptr = NULL;
309 if (!(pmtab->p_ttyflags & A_FLAG)) { /* autobaud not enabled */
310 if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) {
311 log("set final termio failed");
312 exit(1);
313 }
314 }
315 }
316 if (pmtab->p_ttyflags & B_FLAG) { /* port is bi-directional */
317 /* set advisory lock on the line */
318 if (tm_lock(0) != 0) {
319 /*
320 * device is locked
321 * child exits and let the parent wait for
322 * the lock to go away
323 */
324 exit(0);
325 }
326 /* change ownership back to root */
327 (void) fchown(0, ROOTUID, Tty_gid);
328 (void) fchmod(0, 0620);
329 }
330 return;
331 }
332
333 /*
334 * write_prompt - write the msg to fd
335 * - if flush is set, flush input queue
336 * - if clear is set, write a new line
337 */
338 void
write_prompt(fd,pmtab,flush,clear)339 write_prompt(fd, pmtab, flush, clear)
340 int fd;
341 struct pmtab *pmtab;
342 int flush, clear;
343 {
344
345 #ifdef DEBUG
346 debug("in write_prompt");
347 #endif
348 if (flush)
349 flush_input(fd);
350 if (clear) {
351 (void) write(fd, "\r\n", 2);
352 }
353 #ifdef SYS_NAME
354 sys_name(fd);
355 #endif
356 /* Print prompt/disable message. */
357 if ((State == PM_DISABLED) || (pmtab->p_flags & X_FLAG))
358 (void)write(fd, pmtab->p_dmsg, (unsigned)strlen(pmtab->p_dmsg));
359 else
360 (void) write(fd, pmtab->p_prompt,
361 (unsigned)strlen(pmtab->p_prompt));
362 }
363
364 /*
365 * timedout - input period timed out
366 */
367 void
timedout()368 timedout()
369 {
370 exit(1);
371 }
372
373 #ifdef SYS_NAME
374 /*
375 * void sys_name() - generate a msg with system id
376 * - print out /etc/issue file if it exists
377 */
378 void
sys_name(fd)379 sys_name(fd)
380 int fd;
381 {
382 char *ptr, buffer[BUFSIZ];
383 FILE *fp;
384
385 #if 0 /* 1111333 - don't print node name, we already do this elsewhere */
386 struct utsname utsname;
387
388 if (uname(&utsname) != FAILURE) {
389 (void) sprintf(buffer, "%.9s\r\n", utsname.nodename);
390 (void) write(fd, buffer, strlen(buffer));
391 }
392 #endif
393
394 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
395 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) {
396 (void) write(fd, ptr, strlen(ptr));
397 }
398 (void) fclose(fp);
399 }
400 }
401 #endif
402
403
404 /*
405 * do_autobaud - do autobaud
406 * - if it succeed, set the new speed and return
407 * - if it failed, it will get the nextlabel
408 * - if next entry is also autobaud,
409 * it will loop back to do autobaud again
410 * - otherwise, it will set new termio and return
411 */
412 static char *
do_autobaud(pmtab,speedef)413 do_autobaud(pmtab, speedef)
414 struct pmtab *pmtab;
415 struct Gdef *speedef;
416 {
417 int done = FALSE;
418 char *auto_speed;
419 #ifdef DEBUG
420 debug("in do_autobaud");
421 #endif
422 while (!done) {
423 if ((auto_speed = autobaud(0, pmtab->p_timeout)) == NULL) {
424 speedef = next_speed(speedef);
425 if (speedef->g_autobaud & A_FLAG) {
426 continue;
427 }
428 else {
429 if (set_termio(0, speedef->g_iflags, NULL,
430 TRUE, CANON) != 0) {
431 exit(1);
432 }
433 done = TRUE;
434 }
435 }
436 else {
437 if (set_termio(0, speedef->g_fflags, auto_speed,
438 TRUE, CANON) != 0) {
439 exit(1);
440 }
441 done = TRUE;
442 }
443 }
444 #ifdef DEBUG
445 debug("autobaud done");
446 #endif
447 return (auto_speed);
448 }
449
450 /*
451 * next_speed(speedef)
452 * - find the next entry according to nextlabel. If "nextlabel"
453 * is not valid, go back to the old ttylabel.
454 */
455
456 static struct Gdef *
next_speed(speedef)457 next_speed(speedef)
458 struct Gdef *speedef;
459 {
460 struct Gdef *sp;
461
462 if (strcmp(speedef->g_nextid, speedef->g_id) == 0)
463 return (speedef);
464 if ((sp = find_def(speedef->g_nextid)) == NULL) {
465 log("%s's next speed-label (%s) is bad.", speedef->g_id,
466 speedef->g_nextid);
467
468 /* go back to the original entry. */
469 if ((sp = find_def(speedef->g_id)) == NULL) {
470 /* if failed, complain and quit. */
471 log("unable to find (%s) again", speedef->g_id);
472 exit(1);
473 }
474 }
475 return (sp);
476 }
477
478 /*
479 * inform_parent() - inform ttymon that tmchild is going to exec service
480 */
481 static void
inform_parent(fd)482 inform_parent(fd)
483 int fd;
484 {
485 pid_t pid;
486
487 pid = getpid();
488 (void) write(fd, &pid, sizeof (pid));
489 }
490
491 static char pbuf[BUFSIZ]; /* static buf for TTYPROMPT */
492 static char hbuf[BUFSIZ]; /* static buf for HOME */
493 static char tbuf[BUFSIZ]; /* static buf for TERM */
494
495 /*
496 * void invoke_service - invoke the service
497 */
498
499 static void
invoke_service(pmtab)500 invoke_service(pmtab)
501 struct pmtab *pmtab;
502 {
503 char *argvp[MAXARGS]; /* service cmd args */
504 int cnt = 0; /* arg counter */
505 int i, fd;
506 struct sigaction sigact;
507 extern struct rlimit Rlimit;
508
509 #ifdef DEBUG
510 debug("in invoke_service");
511 #endif
512
513 if (tcgetsid(0) != getsid(getpid())) {
514 cons_printf("Warning -- ttymon cannot allocate controlling "
515 "tty on \"%s\",\n", pmtab->p_device);
516 cons_printf("\tThere may be another session active on this "
517 "port.\n");
518
519 if (strcmp("/dev/console", pmtab->p_device) != 0) {
520 /*
521 * if not on console, write to stderr to warn the user
522 * also.
523 */
524 (void) fprintf(stderr, "Warning -- ttymon cannot "
525 "allocate controlling tty on \"%s\",\n",
526 pmtab->p_device);
527 (void) fprintf(stderr, "\tthere may be another session "
528 "active on this port.\n");
529 }
530 }
531
532 if (pmtab->p_status != GETTY) {
533 inform_parent(PCpipe[1]);
534 sigact.sa_flags = 0;
535 sigact.sa_handler = SIG_DFL;
536 (void) sigemptyset(&sigact.sa_mask);
537 (void) sigaction(SIGPOLL, &sigact, NULL);
538 }
539
540 if (pmtab->p_flags & U_FLAG) {
541 if (account(pmtab->p_device) != 0) {
542 log("invoke_service: account failed");
543 exit(1);
544 }
545 }
546
547 /* parse command line */
548 mkargv(pmtab->p_server, &argvp[0], &cnt, MAXARGS-1);
549
550 if (!(pmtab->p_ttyflags & C_FLAG)) {
551 (void) sprintf(pbuf, "TTYPROMPT=%s", pmtab->p_prompt);
552 if (putenv(pbuf)) {
553 log("cannot expand service <%s> environment", argvp[0]);
554 exit(1);
555 }
556 }
557 if (pmtab->p_status != GETTY) {
558 (void) sprintf(hbuf, "HOME=%s", pmtab->p_dir);
559 if (putenv(hbuf)) {
560 log("cannot expand service <%s> environment", argvp[0]);
561 exit(1);
562 }
563 #ifdef DEBUG
564 debug("about to run config script");
565 #endif
566 if ((i = doconfig(0, pmtab->p_tag, 0)) != 0) {
567 if (i < 0) {
568 log("doconfig failed, system error");
569 }
570 else {
571 log("doconfig failed on line %d of script %s",
572 i, pmtab->p_tag);
573 }
574 exit(1);
575 }
576 }
577
578 if (setgid(pmtab->p_gid)) {
579 log("cannot set group id to %ld: %s", pmtab->p_gid,
580 strerror(errno));
581 exit(1);
582 }
583
584 if (setuid(pmtab->p_uid)) {
585 log("cannot set user id to %ld: %s", pmtab->p_uid,
586 strerror(errno));
587 exit(1);
588 }
589
590 if (chdir(pmtab->p_dir)) {
591 log("cannot chdir to %s: %s", pmtab->p_dir, strerror(errno));
592 exit(1);
593 }
594
595 if (pmtab->p_uid != ROOTUID) {
596 /* change ownership and mode of device */
597 (void) fchown(0, pmtab->p_uid, Tty_gid);
598 (void) fchmod(0, 0620);
599 }
600
601
602 if (pmtab->p_status != GETTY) {
603 sigact.sa_flags = 0;
604 sigact.sa_handler = SIG_DFL;
605 (void) sigemptyset(&sigact.sa_mask);
606 (void) sigaction(SIGINT, &sigact, NULL);
607 if (setrlimit(RLIMIT_NOFILE, &Rlimit) == -1) {
608 log("setrlimit failed: %s", strerror(errno));
609 exit(1);
610 }
611 /* invoke the service */
612 log("Starting service (%s) on %s", argvp[0], pmtab->p_device);
613 }
614
615 if (pmtab->p_termtype != (char *)NULL) {
616 (void) sprintf(tbuf, "TERM=%s", pmtab->p_termtype);
617 if (putenv(tbuf)) {
618 log("cannot expand service <%s> environment", argvp[0]);
619 exit(1);
620 }
621 }
622 /* restore signal handlers and mask */
623 (void) sigaction(SIGINT, &Sigint, NULL);
624 (void) sigaction(SIGALRM, &Sigalrm, NULL);
625 (void) sigaction(SIGPOLL, &Sigpoll, NULL);
626 (void) sigaction(SIGQUIT, &Sigquit, NULL);
627 (void) sigaction(SIGCLD, &Sigcld, NULL);
628 (void) sigaction(SIGTERM, &Sigterm, NULL);
629 #ifdef DEBUG
630 (void) sigaction(SIGUSR1, &Sigusr1, NULL);
631 (void) sigaction(SIGUSR2, &Sigusr2, NULL);
632 #endif
633 (void) sigprocmask(SIG_SETMASK, &Origmask, NULL);
634 (void) execve(argvp[0], argvp, environ);
635
636 /* exec returns only on failure! */
637 log("tmchild: exec service failed: %s", strerror(errno));
638 exit(1);
639 }
640
641 /*
642 * check_hup(fd) - do a poll on fd to check if it is in hangup state
643 * - return 1 if hangup, otherwise return 0
644 */
645
646 static int
check_hup(fd)647 check_hup(fd)
648 int fd;
649 {
650 int ret;
651 struct pollfd pfd[1];
652
653 pfd[0].fd = fd;
654 pfd[0].events = POLLHUP;
655 for (;;) {
656 ret = poll(pfd, 1, 0);
657 if (ret < 0) {
658 if (errno == EINTR)
659 continue;
660 log("check_hup: poll failed: %s", strerror(errno));
661 exit(1);
662 }
663 else if (ret > 0) {
664 if (pfd[0].revents & POLLHUP) {
665 return (1);
666 }
667 }
668 return (0);
669 }
670 }
671
672 /*
673 * sigpoll() - SIGPOLL handle for tmchild
674 * - when SIGPOLL is received by tmchild,
675 * the pipe between ttymon and tmchild is broken.
676 * Something must happen to ttymon.
677 */
678 void
sigpoll()679 sigpoll()
680 {
681 #ifdef DEBUG
682 debug("tmchild got SIGPOLL, exiting");
683 #endif
684 exit(1);
685 }
686