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