8706 libc lint library missing endian.h functions
[unleashed.git] / usr / src / cmd / ttymon / tmchild.c
blob8aa97ae17081b8267b5b4754f67d966894feb91b
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
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
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();
55 extern struct Gdef *get_speed();
56 extern struct strbuf *peek_ptr, *do_peek();
59 * tmchild - process that handles peeking data, determine baud rate
60 * and invoke service on each individual port.
63 void
64 tmchild(pmtab)
65 struct pmtab *pmtab;
67 register struct Gdef *speedef;
68 char *auto_speed = "";
69 struct sigaction sigact;
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);
83 * the following check is to make sure no hangup
84 * happens before registering for SIGPOLL
86 if (check_hup(PCpipe[1])) {
87 #ifdef DEBUG
88 debug("PCpipe hungup, tmchild exiting");
89 #endif
90 exit(1);
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;
101 * become the session leader so that a controlling tty
102 * will be allocated.
104 (void) setsid();
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;
118 if ((pmtab->p_ttyflags & C_FLAG) &&
119 (State != PM_DISABLED) &&
120 (!(pmtab->p_flags & X_FLAG))) {
122 * if "c" flag is set, and the port is not disabled
123 * invoke service immediately
125 if (set_termio(0, speedef->g_fflags, NULL,FALSE,CANON) == -1) {
126 log("set final termio failed");
127 exit(1);
129 invoke_service(pmtab);
130 exit(1); /*NOTREACHED*/
132 if (speedef->g_autobaud & A_FLAG) {
133 auto_speed = do_autobaud(pmtab, speedef);
135 if (set_termio(0, speedef->g_fflags, NULL, FALSE, CANON) == -1) {
136 log("set final termio failed");
137 exit(1);
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);
150 else if ((pmtab->p_ttyflags & (B_FLAG)))
151 write_prompt(pmtab->p_fd, pmtab, TRUE, TRUE);
154 /* Loop until user is successful in invoking service. */
155 for (;;) {
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);
170 if ((State == PM_DISABLED)||(pmtab->p_flags & X_FLAG)){
171 write_prompt(1, pmtab, TRUE, FALSE);
172 break;
174 if (set_termio(0, speedef->g_fflags, auto_speed,
175 FALSE, CANON) == -1) {
176 log("set final termio failed");
177 exit(1);
179 invoke_service(pmtab);
180 exit(1); /*NOTREACHED*/
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);
192 auto_speed = do_autobaud(pmtab, speedef);
194 else {
195 auto_speed = NULL;
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
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);
208 write_prompt(1, pmtab, TRUE, TRUE);
209 break;
211 case NONAME:
212 #ifdef DEBUG
213 debug("got NONAME");
214 #endif
215 write_prompt(1, pmtab, FALSE, FALSE);
216 break;
218 } /* end switch */
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);
228 } /* end for loop */
231 static void
232 openline(pmtab, speedef)
233 struct pmtab *pmtab;
234 struct Gdef *speedef;
236 char buffer[5];
237 int rtn = 0;
238 int line_count;
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);
252 (void) close(1);
253 (void) close(2);
254 (void) dup(0);
255 (void) dup(0);
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--;
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
274 if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) {
275 log("set final termio failed");
276 exit(1);
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);
285 if (*buffer == '\n')
286 line_count++;
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);
295 rtn = read(0, buffer, 1);
296 } else
297 *buffer = (peek_ptr->buf[0]&0x7F);
300 * NOTE: Cu on a direct line when ~. is encountered will
301 * send EOTs to the other side. EOT=\004
303 if (rtn < 0 || *buffer == '\004') {
304 (void) close(0);
305 exit(0);
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);
316 if (pmtab->p_ttyflags & B_FLAG) { /* port is bi-directional */
317 /* set advisory lock on the line */
318 if (tm_lock(0) != 0) {
320 * device is locked
321 * child exits and let the parent wait for
322 * the lock to go away
324 exit(0);
326 /* change ownership back to root */
327 (void) fchown(0, ROOTUID, Tty_gid);
328 (void) fchmod(0, 0620);
330 return;
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
338 void
339 write_prompt(fd, pmtab, flush, clear)
340 int fd;
341 struct pmtab *pmtab;
342 int flush, clear;
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);
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));
365 * timedout - input period timed out
367 void
368 timedout()
370 exit(1);
373 #ifdef SYS_NAME
375 * void sys_name() - generate a msg with system id
376 * - print out /etc/issue file if it exists
378 void
379 sys_name(fd)
380 int fd;
382 char *ptr, buffer[BUFSIZ];
383 FILE *fp;
385 #if 0 /* 1111333 - don't print node name, we already do this elsewhere */
386 struct utsname utsname;
388 if (uname(&utsname) != FAILURE) {
389 (void) sprintf(buffer, "%.9s\r\n", utsname.nodename);
390 (void) write(fd, buffer, strlen(buffer));
392 #endif
394 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
395 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) {
396 (void) write(fd, ptr, strlen(ptr));
398 (void) fclose(fp);
401 #endif
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
412 static char *
413 do_autobaud(pmtab, speedef)
414 struct pmtab *pmtab;
415 struct Gdef *speedef;
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;
428 else {
429 if (set_termio(0, speedef->g_iflags, NULL,
430 TRUE, CANON) != 0) {
431 exit(1);
433 done = TRUE;
436 else {
437 if (set_termio(0, speedef->g_fflags, auto_speed,
438 TRUE, CANON) != 0) {
439 exit(1);
441 done = TRUE;
444 #ifdef DEBUG
445 debug("autobaud done");
446 #endif
447 return (auto_speed);
451 * next_speed(speedef)
452 * - find the next entry according to nextlabel. If "nextlabel"
453 * is not valid, go back to the old ttylabel.
456 static struct Gdef *
457 next_speed(speedef)
458 struct Gdef *speedef;
460 struct Gdef *sp;
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);
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);
475 return (sp);
479 * inform_parent() - inform ttymon that tmchild is going to exec service
481 static void
482 inform_parent(fd)
483 int fd;
485 pid_t pid;
487 pid = getpid();
488 (void) write(fd, &pid, sizeof (pid));
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 */
496 * void invoke_service - invoke the service
499 static void
500 invoke_service(pmtab)
501 struct pmtab *pmtab;
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;
509 #ifdef DEBUG
510 debug("in invoke_service");
511 #endif
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");
519 if (strcmp("/dev/console", pmtab->p_device) != 0) {
521 * if not on console, write to stderr to warn the user
522 * also.
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");
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);
540 if (pmtab->p_flags & U_FLAG) {
541 if (account(pmtab->p_device) != 0) {
542 log("invoke_service: account failed");
543 exit(1);
547 /* parse command line */
548 mkargv(pmtab->p_server, &argvp[0], &cnt, MAXARGS-1);
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);
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);
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");
570 else {
571 log("doconfig failed on line %d of script %s",
572 i, pmtab->p_tag);
574 exit(1);
578 if (setgid(pmtab->p_gid)) {
579 log("cannot set group id to %ld: %s", pmtab->p_gid,
580 strerror(errno));
581 exit(1);
584 if (setuid(pmtab->p_uid)) {
585 log("cannot set user id to %ld: %s", pmtab->p_uid,
586 strerror(errno));
587 exit(1);
590 if (chdir(pmtab->p_dir)) {
591 log("cannot chdir to %s: %s", pmtab->p_dir, strerror(errno));
592 exit(1);
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);
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);
611 /* invoke the service */
612 log("Starting service (%s) on %s", argvp[0], pmtab->p_device);
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);
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);
636 /* exec returns only on failure! */
637 log("tmchild: exec service failed: %s", strerror(errno));
638 exit(1);
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
646 static int
647 check_hup(fd)
648 int fd;
650 int ret;
651 struct pollfd pfd[1];
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);
663 else if (ret > 0) {
664 if (pfd[0].revents & POLLHUP) {
665 return (1);
668 return (0);
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.
678 void
679 sigpoll()
681 #ifdef DEBUG
682 debug("tmchild got SIGPOLL, exiting");
683 #endif
684 exit(1);