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 <ctype.h>
30 #include <stdio_ext.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <poll.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/stropts.h>
40 #include <sys/resource.h>
41 #include <sys/termios.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <unistd.h>
45 #include <ulimit.h>
46 #include <libdevinfo.h>
47
48 #include "sac.h"
49 #include "ttymon.h"
50 #include "tmstruct.h"
51 #include "tmextern.h"
52
53 static int Initialized;
54
55 static void initialize(void);
56 static void open_all(void);
57 static int set_poll(struct pollfd *);
58 static int check_spawnlimit(struct pmtab *);
59 static int mod_ttydefs(void);
60
61 void open_device(struct pmtab *);
62 void set_softcar(struct pmtab *);
63
64 /*
65 * ttymon - a port monitor under SAC
66 * - monitor ports, set terminal modes, baud rate
67 * and line discipline for the port
68 * - invoke service on port if connection request received
69 * - Usage: ttymon
70 * ttymon -g [options]
71 * Valid options are
72 * -h
73 * -d device
74 * -l ttylabel
75 * -t timeout
76 * -m modules
77 * -p prompt
78 *
79 * - ttymon without args is invoked by SAC
80 * - ttymon -g is invoked by process that needs to
81 * have login service on the fly
82 */
83
84 int
main(int argc,char * argv[])85 main(int argc, char *argv[])
86 {
87 int nfds;
88
89 /*
90 * Only the superuser should execute this command.
91 */
92 if (getuid() != 0)
93 return (1);
94
95 /* remember original signal mask and dispositions */
96 (void) sigprocmask(SIG_SETMASK, NULL, &Origmask);
97 (void) sigaction(SIGINT, NULL, &Sigint);
98 (void) sigaction(SIGALRM, NULL, &Sigalrm);
99 (void) sigaction(SIGPOLL, NULL, &Sigpoll);
100 (void) sigaction(SIGQUIT, NULL, &Sigquit);
101 (void) sigaction(SIGCLD, NULL, &Sigcld);
102 (void) sigaction(SIGTERM, NULL, &Sigterm);
103 #ifdef DEBUG
104 (void) sigaction(SIGUSR1, NULL, &Sigusr1);
105 (void) sigaction(SIGUSR2, NULL, &Sigusr2);
106 #endif
107
108 /*
109 * SIGQUIT needs to be ignored. Otherwise, hitting ^\ from
110 * console kills ttymon.
111 */
112 (void) signal(SIGQUIT, SIG_IGN);
113
114 if ((argc > 1) || (strcmp(lastname(argv[0]), "getty") == 0)) {
115 ttymon_express(argc, argv);
116 return (1); /*NOTREACHED*/
117 }
118
119 initialize();
120
121 for (;;) {
122 nfds = set_poll(Pollp);
123 if (!Reread_flag) {
124 if (nfds > 0)
125 do_poll(Pollp, nfds);
126 else
127 (void) pause();
128 }
129 /*
130 * READDB messages may arrive during poll or pause.
131 * So the flag needs to be checked again.
132 */
133 if (Reread_flag) {
134 Reread_flag = FALSE;
135 re_read();
136 }
137 while (Retry) {
138 Retry = FALSE;
139 open_all();
140 }
141 }
142 }
143
144 static void
initialize(void)145 initialize(void)
146 {
147 struct pmtab *tp;
148 struct passwd *pwdp;
149 struct group *gp;
150 struct rlimit rlimit;
151
152 Initialized = FALSE;
153 /*
154 * get_environ() must be called first,
155 * otherwise we don't know where the log file is
156 */
157 get_environ();
158 openttymonlog();
159 openpid();
160 openpipes();
161 setup_PCpipe();
162
163 log("PMTAG: %s", Tag);
164 log("Starting state: %s",
165 (State == PM_ENABLED) ? "enabled" : "disabled");
166
167 #ifdef DEBUG
168 opendebug(FALSE);
169 debug("***** ttymon in initialize *****");
170 log("debug mode is \t on");
171 #endif
172
173 catch_signals();
174
175 /* register to receive SIGPOLL when data comes to pmpipe */
176 if (ioctl(Pfd, I_SETSIG, S_INPUT) < 0)
177 fatal("I_SETSIG on pmpipe failed: %s", strerror(errno));
178
179 sacpoll(); /* this is needed because there may be data already */
180
181 Maxfiles = (int)ulimit(4, 0L); /* get max number of open files */
182 if (Maxfiles < 0)
183 fatal("ulimit(4,0L) failed: %s", strerror(errno));
184
185 if (getrlimit(RLIMIT_NOFILE, &Rlimit) == -1)
186 fatal("getrlimit failed: %s", strerror(errno));
187
188 rlimit.rlim_cur = rlimit.rlim_max = Rlimit.rlim_max;
189 if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
190 fatal("setrlimit failed: %s", strerror(errno));
191
192 (void) enable_extended_FILE_stdio(-1, -1);
193
194 Maxfiles = rlimit.rlim_cur;
195 Maxfds = Maxfiles - FILE_RESERVED;
196
197 log("max open files = %d", Maxfiles);
198 log("max ports ttymon can monitor = %d", Maxfds);
199
200 read_pmtab();
201
202 /*
203 * setup poll array
204 * - we allocate 10 extra pollfd so that
205 * we do not have to re-malloc when there is
206 * minor fluctuation in Nentries
207 */
208 Npollfd = Nentries + 10;
209 if (Npollfd > Maxfds)
210 Npollfd = Maxfds;
211 if ((Pollp = (struct pollfd *)
212 malloc((unsigned)(Npollfd * sizeof (struct pollfd))))
213 == (struct pollfd *)NULL)
214 fatal("malloc for Pollp failed");
215
216 (void) mod_ttydefs(); /* just to initialize Mtime */
217 if (check_version(TTYDEFS_VERS, TTYDEFS) != 0)
218 fatal("check /etc/ttydefs version failed");
219
220 read_ttydefs(NULL, FALSE);
221
222 /* initialize global variables, Uucp_uid & Tty_gid */
223 if ((pwdp = getpwnam(UUCP)) != NULL)
224 Uucp_uid = pwdp->pw_uid;
225 if ((gp = getgrnam(TTY)) == NULL)
226 log("no group entry for <tty>, default is used");
227 else
228 Tty_gid = gp->gr_gid;
229 endgrent();
230 endpwent();
231 #ifdef DEBUG
232 debug("Uucp_uid = %u, Tty_gid = %u", Uucp_uid, Tty_gid);
233 #endif
234
235 log("Initialization Completed");
236
237 /* open the devices ttymon monitors */
238 Retry = TRUE;
239 while (Retry) {
240 Retry = FALSE;
241 for (tp = PMtab; tp; tp = tp->p_next) {
242 if ((tp->p_status > 0) && (tp->p_fd == 0) &&
243 (tp->p_childpid == 0) &&
244 !(tp->p_ttyflags & I_FLAG) &&
245 (!((State == PM_DISABLED) &&
246 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
247 open_device(tp);
248 if (tp->p_fd > 0)
249 got_carrier(tp);
250 }
251 }
252 }
253 Initialized = TRUE;
254 }
255
256 static void free_defs(void);
257
258 /*
259 * open_all - open devices in pmtab if the entry is
260 * - valid, fd = 0, and pid = 0
261 */
262 static void
open_all(void)263 open_all(void)
264 {
265 struct pmtab *tp;
266 int check_modtime;
267 sigset_t cset;
268 sigset_t tset;
269
270 #ifdef DEBUG
271 debug("in open_all");
272 #endif
273 check_modtime = TRUE;
274
275 for (tp = PMtab; tp; tp = tp->p_next) {
276 if ((tp->p_status > 0) && (tp->p_fd == 0) &&
277 (tp->p_childpid == 0) &&
278 !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) &&
279 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
280 /*
281 * if we have not check modification time and
282 * /etc/ttydefs was modified, need to re-read it
283 */
284 if (check_modtime && mod_ttydefs()) {
285 check_modtime = FALSE;
286 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
287 tset = cset;
288 (void) sigaddset(&tset, SIGCLD);
289 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
290 free_defs();
291 #ifdef DEBUG
292 debug("/etc/ttydefs is modified, re-read it");
293 #endif
294 read_ttydefs(NULL, FALSE);
295 (void) sigprocmask(SIG_SETMASK, &cset, NULL);
296 }
297 open_device(tp);
298 if (tp->p_fd > 0)
299 got_carrier(tp);
300 } else if (((tp->p_status == LOCKED) ||
301 (tp->p_status == SESSION) ||
302 (tp->p_status == UNACCESS)) &&
303 (tp->p_fd > 0) &&
304 (!((State == PM_DISABLED) &&
305 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
306 if (check_modtime && mod_ttydefs()) {
307 check_modtime = FALSE;
308 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
309 tset = cset;
310 (void) sigaddset(&tset, SIGCLD);
311 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
312 free_defs();
313 #ifdef DEBUG
314 debug("/etc/ttydefs is modified, re-read it");
315 #endif
316 read_ttydefs(NULL, FALSE);
317 (void) sigprocmask(SIG_SETMASK, &cset, NULL);
318 }
319 tp->p_status = VALID;
320 open_device(tp);
321 if (tp->p_fd > 0)
322 got_carrier(tp);
323 }
324 }
325 }
326
327 void
set_softcar(struct pmtab * pmptr)328 set_softcar(struct pmtab *pmptr)
329 {
330
331 int fd, val = 0;
332
333 #ifdef DEBUG
334 debug("in set_softcar");
335 #endif
336 /*
337 * If soft carrier is not set one way or
338 * the other, leave it alone.
339 */
340 if (*pmptr->p_softcar == '\0')
341 return;
342
343 if (*pmptr->p_softcar == 'y')
344 val = 1;
345
346 if ((fd = open(pmptr->p_device, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) {
347 log("open (%s) failed: %s", pmptr->p_device, strerror(errno));
348 return;
349 }
350
351 if (ioctl(fd, TIOCSSOFTCAR, &val) < 0)
352 log("set soft-carrier (%s) failed: %s", pmptr->p_device,
353 strerror(errno));
354
355 (void) close(fd);
356 }
357
358
359 /*
360 * open_device(pmptr) - open the device
361 * - check device lock
362 * - change owner of device
363 * - push line disciplines
364 * - set termio
365 */
366
367 void
open_device(struct pmtab * pmptr)368 open_device(struct pmtab *pmptr)
369 {
370 int fd, tmpfd;
371 struct sigaction sigact;
372
373 #ifdef DEBUG
374 debug("in open_device");
375 #endif
376
377 if (pmptr->p_status == GETTY) {
378 revokedevaccess(pmptr->p_device, 0, 0, 0);
379
380 if ((fd = open(pmptr->p_device, O_RDWR)) == -1)
381 fatal("open (%s) failed: %s", pmptr->p_device,
382 strerror(errno));
383
384 } else {
385 if (check_spawnlimit(pmptr) == -1) {
386 pmptr->p_status = NOTVALID;
387 log("service <%s> is respawning too rapidly",
388 pmptr->p_tag);
389 return;
390 }
391 if (pmptr->p_fd > 0) { /* file already open */
392 fd = pmptr->p_fd;
393 pmptr->p_fd = 0;
394 } else if ((fd = open(pmptr->p_device, O_RDWR|O_NONBLOCK))
395 == -1) {
396 log("open (%s) failed: %s", pmptr->p_device,
397 strerror(errno));
398 if ((errno == ENODEV) || (errno == EBUSY)) {
399 pmptr->p_status = UNACCESS;
400 Nlocked++;
401 if (Nlocked == 1) {
402 sigact.sa_flags = 0;
403 sigact.sa_handler = sigalarm;
404 (void) sigemptyset(&sigact.sa_mask);
405 (void) sigaction(SIGALRM, &sigact,
406 NULL);
407 (void) alarm(ALARMTIME);
408 }
409 } else
410 Retry = TRUE;
411 return;
412 }
413 /* set close-on-exec flag */
414 if (fcntl(fd, F_SETFD, 1) == -1)
415 fatal("F_SETFD fcntl failed: %s", strerror(errno));
416
417 if (tm_checklock(fd) != 0) {
418 pmptr->p_status = LOCKED;
419 (void) close(fd);
420 Nlocked++;
421 if (Nlocked == 1) {
422 sigact.sa_flags = 0;
423 sigact.sa_handler = sigalarm;
424 (void) sigemptyset(&sigact.sa_mask);
425 (void) sigaction(SIGALRM, &sigact, NULL);
426 (void) alarm(ALARMTIME);
427 }
428 return;
429 }
430 if (check_session(fd) != 0) {
431 if ((Initialized) && (pmptr->p_inservice != SESSION)) {
432 log("Warning -- active session exists on <%s>",
433 pmptr->p_device);
434 } else {
435 /*
436 * this may happen if a service is running
437 * and ttymon dies and is restarted,
438 * or another process is running on the
439 * port.
440 */
441 pmptr->p_status = SESSION;
442 pmptr->p_inservice = 0;
443 (void) close(fd);
444 Nlocked++;
445 if (Nlocked == 1) {
446 sigact.sa_flags = 0;
447 sigact.sa_handler = sigalarm;
448 (void) sigemptyset(&sigact.sa_mask);
449 (void) sigaction(SIGALRM, &sigact,
450 NULL);
451 (void) alarm(ALARMTIME);
452 }
453 return;
454 }
455 }
456 pmptr->p_inservice = 0;
457 }
458
459 if (pmptr->p_ttyflags & H_FLAG) {
460 /* drop DTR */
461 (void) hang_up_line(fd);
462 /*
463 * After hang_up_line, the stream is in STRHUP state.
464 * We need to do another open to reinitialize streams
465 * then we can close one fd
466 */
467 if ((tmpfd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) {
468 log("open (%s) failed: %s", pmptr->p_device,
469 strerror(errno));
470 Retry = TRUE;
471 (void) close(fd);
472 return;
473 }
474 (void) close(tmpfd);
475 }
476
477 #ifdef DEBUG
478 debug("open_device (%s), fd = %d", pmptr->p_device, fd);
479 #endif
480
481 /* Change ownership of the tty line to root/uucp and */
482 /* set protections to only allow root/uucp to read the line. */
483
484 if (pmptr->p_ttyflags & (B_FLAG|C_FLAG))
485 (void) fchown(fd, Uucp_uid, Tty_gid);
486 else
487 (void) fchown(fd, ROOTUID, Tty_gid);
488 (void) fchmod(fd, 0620);
489
490 if ((pmptr->p_modules != NULL)&&(*(pmptr->p_modules) != '\0')) {
491 if (push_linedisc(fd, pmptr->p_modules, pmptr->p_device)
492 == -1) {
493 Retry = TRUE;
494 (void) close(fd);
495 return;
496 }
497 }
498
499 if (initial_termio(fd, pmptr) == -1) {
500 Retry = TRUE;
501 (void) close(fd);
502 return;
503 }
504
505 (void) di_devperm_logout((const char *)pmptr->p_device);
506 pmptr->p_fd = fd;
507 }
508
509 /*
510 * set_poll(fdp) - put all fd's in a pollfd array
511 * - set poll event to POLLIN and POLLMSG
512 * - return number of fd to be polled
513 */
514
515 static int
set_poll(struct pollfd * fdp)516 set_poll(struct pollfd *fdp)
517 {
518 struct pmtab *tp;
519 int nfd = 0;
520
521 for (tp = PMtab; tp; tp = tp->p_next) {
522 if (tp->p_fd > 0) {
523 fdp->fd = tp->p_fd;
524 fdp->events = POLLIN;
525 fdp++;
526 nfd++;
527 }
528 }
529 return (nfd);
530 }
531
532 /*
533 * check_spawnlimit - return 0 if spawnlimit is not reached
534 * - otherwise return -1
535 */
536 static int
check_spawnlimit(struct pmtab * pmptr)537 check_spawnlimit(struct pmtab *pmptr)
538 {
539 time_t now;
540
541 (void) time(&now);
542 if (pmptr->p_time == 0L)
543 pmptr->p_time = now;
544 if (pmptr->p_respawn >= SPAWN_LIMIT) {
545 if ((now - pmptr->p_time) < SPAWN_INTERVAL) {
546 pmptr->p_time = now;
547 pmptr->p_respawn = 0;
548 return (-1);
549 }
550 pmptr->p_time = now;
551 pmptr->p_respawn = 0;
552 }
553 pmptr->p_respawn++;
554 return (0);
555 }
556
557 /*
558 * mod_ttydefs - to check if /etc/ttydefs has been modified
559 * - return TRUE if file modified
560 * - otherwise, return FALSE
561 */
562 static int
mod_ttydefs(void)563 mod_ttydefs(void)
564 {
565 struct stat statbuf;
566
567 if (stat(TTYDEFS, &statbuf) == -1) {
568 /* if stat failed, don't bother reread ttydefs */
569 return (FALSE);
570 }
571 if ((long)statbuf.st_mtime != Mtime) {
572 Mtime = (long)statbuf.st_mtime;
573 return (TRUE);
574 }
575 return (FALSE);
576 }
577
578 /*
579 * free_defs - free the Gdef table
580 */
581 static void
free_defs(void)582 free_defs(void)
583 {
584 int i;
585 struct Gdef *tp;
586 tp = &Gdef[0];
587 for (i = 0; i < Ndefs; i++, tp++) {
588 free(tp->g_id);
589 free(tp->g_iflags);
590 free(tp->g_fflags);
591 free(tp->g_nextid);
592 tp->g_id = NULL;
593 tp->g_iflags = NULL;
594 tp->g_fflags = NULL;
595 tp->g_nextid = NULL;
596 }
597 Ndefs = 0;
598 }
599
600 /*
601 * rebuild flags entry using speed from ttymode.
602 */
603 static char *
merge_flags(char * src,char * ttymode)604 merge_flags(char *src, char *ttymode)
605 {
606 char *data, *ptr, *flags;
607
608 /* copy speed entry */
609 data = strsave(src);
610 flags = strsave(ttymode);
611 ptr = strchr(flags, ',');
612 if (ptr == NULL) { /* ttymode is corrupted */
613 free(flags);
614 return (data);
615 }
616 *ptr = '\0';
617 ptr = flags;
618 flags = strsave(flags);
619 free(ptr);
620
621 /*
622 * The flags line is supposed to have stty keywords separated by space.
623 * We need to split up the keywords, replace the speed and
624 * reconstruct the flags line.
625 */
626
627 ptr = strtok(data, " \t");
628 if (ptr == NULL) {
629 free(data);
630 return (flags);
631 }
632
633 do {
634 char *tmp;
635
636 /* skip speed */
637 if (isdigit(*ptr))
638 continue;
639
640 if (asprintf(&tmp, "%s %s", flags, ptr) <= 0) {
641 /* should we complain? */
642 break;
643 }
644 free(flags);
645 flags = tmp;
646 } while ((ptr = strtok(NULL, " \t")) != NULL);
647
648 free(data);
649 return (flags);
650 }
651
652 /*
653 * struct Gdef *get_speed(ttylabel)
654 * - search "/etc/ttydefs" for speed and term. specification
655 * using "ttylabel". If "ttylabel" is NULL, default
656 * to DEFAULT
657 * - for /dev/console, if we are in fact using serial console,
658 * use ttyX-mode value to get speed. This allows us to use
659 * the value set for serial console either from firmware (or BMC sol),
660 * or boot loader default.
661 * arg: ttylabel - label/id of speed settings.
662 */
663
664 struct Gdef *
get_speed(struct pmtab * pmptr)665 get_speed(struct pmtab *pmptr)
666 {
667 static struct Gdef serial = { 0 };
668 struct Gdef *sp;
669 char *ttylabel = pmptr->p_ttylabel;
670
671 if ((ttylabel != NULL) && (*ttylabel != '\0')) {
672 if ((sp = find_def(ttylabel)) == NULL) {
673 log("unable to find <%s> in \"%s\"", ttylabel, TTYDEFS);
674 sp = &DEFAULT; /* use default */
675 }
676 } else {
677 sp = &DEFAULT; /* use default */
678 }
679
680 /*
681 * if this is not /dev/console or /dev/console is not using serial,
682 * we are done.
683 */
684 if (pmptr->p_ttymode == NULL ||
685 strcmp(pmptr->p_device, "/dev/console") != 0)
686 return (sp);
687
688 /* is entry for serial set up? */
689 if (serial.g_id == NULL) {
690 /*
691 * Copy data from sp, except we need to update inital and
692 * final flags.
693 */
694 serial.g_id = strsave(sp->g_id);
695 serial.g_iflags = merge_flags(sp->g_iflags, pmptr->p_ttymode);
696 serial.g_fflags = merge_flags(sp->g_fflags, pmptr->p_ttymode);
697 serial.g_autobaud = sp->g_autobaud;
698 serial.g_nextid = strsave(sp->g_nextid);
699 }
700 return (&serial);
701 }
702
703 /*
704 * setup_PCpipe() - setup the pipe between Parent and Children
705 * - the pipe is used for a tmchild to send its
706 * pid to inform ttymon that it is about to
707 * invoke service
708 * - the pipe also serves as a mean for tmchild
709 * to detect failure of ttymon
710 */
711 void
setup_PCpipe(void)712 setup_PCpipe(void)
713 {
714 int flag = 0;
715
716 if (pipe(PCpipe) == -1)
717 fatal("pipe() failed: %s", strerror(errno));
718
719 /* set close-on-exec flag */
720 if (fcntl(PCpipe[0], F_SETFD, 1) == -1)
721 fatal("F_SETFD fcntl failed: %s", strerror(errno));
722
723 if (fcntl(PCpipe[1], F_SETFD, 1) == -1)
724 fatal("F_SETFD fcntl failed: %s", strerror(errno));
725
726 /* set O_NONBLOCK flag */
727 if (fcntl(PCpipe[0], F_GETFL, flag) == -1)
728 fatal("F_GETFL failed: %s", strerror(errno));
729
730 flag |= O_NONBLOCK;
731 if (fcntl(PCpipe[0], F_SETFL, flag) == -1)
732 fatal("F_SETFL failed: %s", strerror(errno));
733
734 /* set message discard mode */
735 if (ioctl(PCpipe[0], I_SRDOPT, RMSGD) == -1)
736 fatal("I_SRDOPT RMSGD failed: %s", strerror(errno));
737
738 /* register to receive SIGPOLL when data come */
739 if (ioctl(PCpipe[0], I_SETSIG, S_INPUT) == -1)
740 fatal("I_SETSIG S_INPUT failed: %s", strerror(errno));
741
742 #ifdef DEBUG
743 log("PCpipe[0]\t = %d", PCpipe[0]);
744 log("PCpipe[1]\t = %d", PCpipe[1]);
745 #endif
746 }
747