libuutil: move under bmake
[unleashed.git] / usr / src / cmd / ttymon / ttymon.c
blob6593f1338c1c3788467ce93e8efed8a9e579ac96
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_ext.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <poll.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/stropts.h>
39 #include <sys/resource.h>
40 #include <sys/termios.h>
41 #include <pwd.h>
42 #include <grp.h>
43 #include <unistd.h>
44 #include <ulimit.h>
46 #include "sac.h"
47 #include "ttymon.h"
48 #include "tmstruct.h"
49 #include "tmextern.h"
51 static int Initialized;
53 extern int Retry;
54 extern struct pollfd *Pollp;
55 static void initialize();
56 static void open_all();
57 static int set_poll();
58 static int check_spawnlimit();
59 static int mod_ttydefs();
61 void open_device();
62 void set_softcar();
64 extern int check_session();
65 extern void sigalarm();
66 extern void revokedevaccess(char *, uid_t, gid_t, mode_t);
67 /* can't include libdevinfo.h */
68 extern int di_devperm_logout(const char *);
71 * ttymon - a port monitor under SAC
72 * - monitor ports, set terminal modes, baud rate
73 * and line discipline for the port
74 * - invoke service on port if connection request received
75 * - Usage: ttymon
76 * ttymon -g [options]
77 * Valid options are
78 * -h
79 * -d device
80 * -l ttylabel
81 * -t timeout
82 * -m modules
83 * -p prompt
85 * - ttymon without args is invoked by SAC
86 * - ttymon -g is invoked by process that needs to
87 * have login service on the fly
90 int
91 main(int argc, char *argv[])
93 int nfds;
94 extern char *lastname();
97 * Only the superuser should execute this command.
99 if (getuid() != 0)
100 return (1);
102 /* remember original signal mask and dispositions */
103 (void) sigprocmask(SIG_SETMASK, NULL, &Origmask);
104 (void) sigaction(SIGINT, NULL, &Sigint);
105 (void) sigaction(SIGALRM, NULL, &Sigalrm);
106 (void) sigaction(SIGPOLL, NULL, &Sigpoll);
107 (void) sigaction(SIGQUIT, NULL, &Sigquit);
108 (void) sigaction(SIGCLD, NULL, &Sigcld);
109 (void) sigaction(SIGTERM, NULL, &Sigterm);
110 #ifdef DEBUG
111 (void) sigaction(SIGUSR1, NULL, &Sigusr1);
112 (void) sigaction(SIGUSR2, NULL, &Sigusr2);
113 #endif
116 * SIGQUIT needs to be ignored. Otherwise, hitting ^\ from
117 * console kills ttymon.
119 (void) signal(SIGQUIT, SIG_IGN);
121 if ((argc > 1) || (strcmp(lastname(argv[0]), "getty") == 0)) {
122 ttymon_express(argc, argv);
123 return (1); /*NOTREACHED*/
126 initialize();
128 for (;;) {
129 nfds = set_poll(Pollp);
130 if (!Reread_flag) {
131 if (nfds > 0)
132 do_poll(Pollp, nfds);
133 else
134 (void) pause();
137 * READDB messages may arrive during poll or pause.
138 * So the flag needs to be checked again.
140 if (Reread_flag) {
141 Reread_flag = FALSE;
142 re_read();
144 while (Retry) {
145 Retry = FALSE;
146 open_all();
151 static void
152 initialize()
154 struct pmtab *tp;
155 register struct passwd *pwdp;
156 register struct group *gp;
157 struct rlimit rlimit;
158 extern struct rlimit Rlimit;
159 extern uid_t Uucp_uid;
160 extern gid_t Tty_gid;
162 #ifdef DEBUG
163 extern opendebug();
164 #endif
165 Initialized = FALSE;
167 * get_environ() must be called first,
168 * otherwise we don't know where the log file is
170 get_environ();
171 openttymonlog();
172 openpid();
173 openpipes();
174 setup_PCpipe();
176 log("PMTAG: %s", Tag);
177 log("Starting state: %s",
178 (State == PM_ENABLED) ? "enabled" : "disabled");
180 #ifdef DEBUG
181 opendebug(FALSE);
182 debug("***** ttymon in initialize *****");
183 log("debug mode is \t on");
184 #endif
186 catch_signals();
188 /* register to receive SIGPOLL when data comes to pmpipe */
189 if (ioctl(Pfd, I_SETSIG, S_INPUT) < 0)
190 fatal("I_SETSIG on pmpipe failed: %s", strerror(errno));
192 sacpoll(); /* this is needed because there may be data already */
194 Maxfiles = (int)ulimit(4, 0L); /* get max number of open files */
195 if (Maxfiles < 0)
196 fatal("ulimit(4,0L) failed: %s", strerror(errno));
198 if (getrlimit(RLIMIT_NOFILE, &Rlimit) == -1)
199 fatal("getrlimit failed: %s", strerror(errno));
201 rlimit.rlim_cur = rlimit.rlim_max = Rlimit.rlim_max;
202 if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
203 fatal("setrlimit failed: %s", strerror(errno));
205 (void) enable_extended_FILE_stdio(-1, -1);
207 Maxfiles = rlimit.rlim_cur;
208 Maxfds = Maxfiles - FILE_RESERVED;
210 log("max open files = %d", Maxfiles);
211 log("max ports ttymon can monitor = %d", Maxfds);
213 read_pmtab();
216 * setup poll array
217 * - we allocate 10 extra pollfd so that
218 * we do not have to re-malloc when there is
219 * minor fluctuation in Nentries
221 Npollfd = Nentries + 10;
222 if (Npollfd > Maxfds)
223 Npollfd = Maxfds;
224 if ((Pollp = (struct pollfd *)
225 malloc((unsigned)(Npollfd * sizeof (struct pollfd))))
226 == NULL)
227 fatal("malloc for Pollp failed");
229 (void) mod_ttydefs(); /* just to initialize Mtime */
230 if (check_version(TTYDEFS_VERS, TTYDEFS) != 0)
231 fatal("check /etc/ttydefs version failed");
233 read_ttydefs(NULL, FALSE);
235 /* initialize global variables, Uucp_uid & Tty_gid */
236 if ((pwdp = getpwnam(UUCP)) != NULL)
237 Uucp_uid = pwdp->pw_uid;
238 if ((gp = getgrnam(TTY)) == NULL)
239 log("no group entry for <tty>, default is used");
240 else
241 Tty_gid = gp->gr_gid;
242 endgrent();
243 endpwent();
244 #ifdef DEBUG
245 debug("Uucp_uid = %u, Tty_gid = %u", Uucp_uid, Tty_gid);
246 #endif
248 log("Initialization Completed");
250 /* open the devices ttymon monitors */
251 Retry = TRUE;
252 while (Retry) {
253 Retry = FALSE;
254 for (tp = PMtab; tp; tp = tp->p_next) {
255 if ((tp->p_status > 0) && (tp->p_fd == 0) &&
256 (tp->p_pid == 0) && !(tp->p_ttyflags & I_FLAG) &&
257 (!((State == PM_DISABLED) &&
258 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
259 open_device(tp);
260 if (tp->p_fd > 0)
261 got_carrier(tp);
265 Initialized = TRUE;
268 static void free_defs();
271 * open_all - open devices in pmtab if the entry is
272 * - valid, fd = 0, and pid = 0
274 static void
275 open_all()
277 struct pmtab *tp;
278 int check_modtime;
279 sigset_t cset;
280 sigset_t tset;
282 #ifdef DEBUG
283 debug("in open_all");
284 #endif
285 check_modtime = TRUE;
287 for (tp = PMtab; tp; tp = tp->p_next) {
288 if ((tp->p_status > 0) && (tp->p_fd == 0) &&
289 (tp->p_pid == 0) &&
290 !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) &&
291 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
293 * if we have not check modification time and
294 * /etc/ttydefs was modified, need to re-read it
296 if (check_modtime && mod_ttydefs()) {
297 check_modtime = FALSE;
298 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
299 tset = cset;
300 (void) sigaddset(&tset, SIGCLD);
301 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
302 free_defs();
303 #ifdef DEBUG
304 debug("/etc/ttydefs is modified, re-read it");
305 #endif
306 read_ttydefs(NULL, FALSE);
307 (void) sigprocmask(SIG_SETMASK, &cset, NULL);
309 open_device(tp);
310 if (tp->p_fd > 0)
311 got_carrier(tp);
312 } else if (((tp->p_status == LOCKED) ||
313 (tp->p_status == SESSION) ||
314 (tp->p_status == UNACCESS)) &&
315 (tp->p_fd > 0) &&
316 (!((State == PM_DISABLED) &&
317 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
318 if (check_modtime && mod_ttydefs()) {
319 check_modtime = FALSE;
320 (void) sigprocmask(SIG_SETMASK, NULL, &cset);
321 tset = cset;
322 (void) sigaddset(&tset, SIGCLD);
323 (void) sigprocmask(SIG_SETMASK, &tset, NULL);
324 free_defs();
325 #ifdef DEBUG
326 debug("/etc/ttydefs is modified, re-read it");
327 #endif
328 read_ttydefs(NULL, FALSE);
329 (void) sigprocmask(SIG_SETMASK, &cset, NULL);
331 tp->p_status = VALID;
332 open_device(tp);
333 if (tp->p_fd > 0)
334 got_carrier(tp);
339 void
340 set_softcar(pmptr)
341 struct pmtab *pmptr;
344 int fd, val = 0;
346 #ifdef DEBUG
347 debug("in set_softcar");
348 #endif
350 * If soft carrier is not set one way or
351 * the other, leave it alone.
353 if (*pmptr->p_softcar == '\0')
354 return;
356 if (*pmptr->p_softcar == 'y')
357 val = 1;
359 if ((fd = open(pmptr->p_device, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) {
360 log("open (%s) failed: %s", pmptr->p_device, strerror(errno));
361 return;
364 if (ioctl(fd, TIOCSSOFTCAR, &val) < 0)
365 log("set soft-carrier (%s) failed: %s", pmptr->p_device,
366 strerror(errno));
368 close(fd);
373 * open_device(pmptr) - open the device
374 * - check device lock
375 * - change owner of device
376 * - push line disciplines
377 * - set termio
380 void
381 open_device(pmptr)
382 struct pmtab *pmptr;
384 int fd, tmpfd;
385 struct sigaction sigact;
387 #ifdef DEBUG
388 debug("in open_device");
389 #endif
391 if (pmptr->p_status == GETTY) {
392 revokedevaccess(pmptr->p_device, 0, 0, 0);
394 if ((fd = open(pmptr->p_device, O_RDWR)) == -1)
395 fatal("open (%s) failed: %s", pmptr->p_device,
396 strerror(errno));
398 } else {
399 if (check_spawnlimit(pmptr) == -1) {
400 pmptr->p_status = NOTVALID;
401 log("service <%s> is respawning too rapidly",
402 pmptr->p_tag);
403 return;
405 if (pmptr->p_fd > 0) { /* file already open */
406 fd = pmptr->p_fd;
407 pmptr->p_fd = 0;
408 } else if ((fd = open(pmptr->p_device, O_RDWR|O_NONBLOCK))
409 == -1) {
410 log("open (%s) failed: %s", pmptr->p_device,
411 strerror(errno));
412 if ((errno == ENODEV) || (errno == EBUSY)) {
413 pmptr->p_status = UNACCESS;
414 Nlocked++;
415 if (Nlocked == 1) {
416 sigact.sa_flags = 0;
417 sigact.sa_handler = sigalarm;
418 (void) sigemptyset(&sigact.sa_mask);
419 (void) sigaction(SIGALRM, &sigact, NULL);
420 (void) alarm(ALARMTIME);
422 } else
423 Retry = TRUE;
424 return;
426 /* set close-on-exec flag */
427 if (fcntl(fd, F_SETFD, 1) == -1)
428 fatal("F_SETFD fcntl failed: %s", strerror(errno));
430 if (tm_checklock(fd) != 0) {
431 pmptr->p_status = LOCKED;
432 (void) close(fd);
433 Nlocked++;
434 if (Nlocked == 1) {
435 sigact.sa_flags = 0;
436 sigact.sa_handler = sigalarm;
437 (void) sigemptyset(&sigact.sa_mask);
438 (void) sigaction(SIGALRM, &sigact, NULL);
439 (void) alarm(ALARMTIME);
441 return;
443 if (check_session(fd) != 0) {
444 if ((Initialized) && (pmptr->p_inservice != SESSION)) {
445 log("Warning -- active session exists on <%s>",
446 pmptr->p_device);
447 } else {
449 * this may happen if a service is running
450 * and ttymon dies and is restarted,
451 * or another process is running on the
452 * port.
454 pmptr->p_status = SESSION;
455 pmptr->p_inservice = 0;
456 (void) close(fd);
457 Nlocked++;
458 if (Nlocked == 1) {
459 sigact.sa_flags = 0;
460 sigact.sa_handler = sigalarm;
461 (void) sigemptyset(&sigact.sa_mask);
462 (void) sigaction(SIGALRM, &sigact,
463 NULL);
464 (void) alarm(ALARMTIME);
466 return;
469 pmptr->p_inservice = 0;
472 if (pmptr->p_ttyflags & H_FLAG) {
473 /* drop DTR */
474 (void) hang_up_line(fd);
476 * After hang_up_line, the stream is in STRHUP state.
477 * We need to do another open to reinitialize streams
478 * then we can close one fd
480 if ((tmpfd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) {
481 log("open (%s) failed: %s", pmptr->p_device,
482 strerror(errno));
483 Retry = TRUE;
484 (void) close(fd);
485 return;
487 (void) close(tmpfd);
490 #ifdef DEBUG
491 debug("open_device (%s), fd = %d", pmptr->p_device, fd);
492 #endif
494 /* Change ownership of the tty line to root/uucp and */
495 /* set protections to only allow root/uucp to read the line. */
497 if (pmptr->p_ttyflags & (B_FLAG|C_FLAG))
498 (void) fchown(fd, Uucp_uid, Tty_gid);
499 else
500 (void) fchown(fd, ROOTUID, Tty_gid);
501 (void) fchmod(fd, 0620);
503 if ((pmptr->p_modules != NULL)&&(*(pmptr->p_modules) != '\0')) {
504 if (push_linedisc(fd, pmptr->p_modules, pmptr->p_device)
505 == -1) {
506 Retry = TRUE;
507 (void) close(fd);
508 return;
512 if (initial_termio(fd, pmptr) == -1) {
513 Retry = TRUE;
514 (void) close(fd);
515 return;
518 di_devperm_logout((const char *)pmptr->p_device);
519 pmptr->p_fd = fd;
523 * set_poll(fdp) - put all fd's in a pollfd array
524 * - set poll event to POLLIN and POLLMSG
525 * - return number of fd to be polled
528 static int
529 set_poll(fdp)
530 struct pollfd *fdp;
532 struct pmtab *tp;
533 int nfd = 0;
535 for (tp = PMtab; tp; tp = tp->p_next) {
536 if (tp->p_fd > 0) {
537 fdp->fd = tp->p_fd;
538 fdp->events = POLLIN;
539 fdp++;
540 nfd++;
543 return (nfd);
547 * check_spawnlimit - return 0 if spawnlimit is not reached
548 * - otherwise return -1
550 static int
551 check_spawnlimit(pmptr)
552 struct pmtab *pmptr;
554 time_t now;
556 (void) time(&now);
557 if (pmptr->p_time == 0L)
558 pmptr->p_time = now;
559 if (pmptr->p_respawn >= SPAWN_LIMIT) {
560 if ((now - pmptr->p_time) < SPAWN_INTERVAL) {
561 pmptr->p_time = now;
562 pmptr->p_respawn = 0;
563 return (-1);
565 pmptr->p_time = now;
566 pmptr->p_respawn = 0;
568 pmptr->p_respawn++;
569 return (0);
573 * mod_ttydefs - to check if /etc/ttydefs has been modified
574 * - return TRUE if file modified
575 * - otherwise, return FALSE
577 static int
578 mod_ttydefs()
580 struct stat statbuf;
581 extern long Mtime;
582 if (stat(TTYDEFS, &statbuf) == -1) {
583 /* if stat failed, don't bother reread ttydefs */
584 return (FALSE);
586 if ((long)statbuf.st_mtime != Mtime) {
587 Mtime = (long)statbuf.st_mtime;
588 return (TRUE);
590 return (FALSE);
594 * free_defs - free the Gdef table
596 static void
597 free_defs()
599 int i;
600 struct Gdef *tp;
601 tp = &Gdef[0];
602 for (i = 0; i < Ndefs; i++, tp++) {
603 free(tp->g_id);
604 free(tp->g_iflags);
605 free(tp->g_fflags);
606 free(tp->g_nextid);
607 tp->g_id = NULL;
608 tp->g_iflags = NULL;
609 tp->g_fflags = NULL;
610 tp->g_nextid = NULL;
612 Ndefs = 0;
616 * struct Gdef *get_speed(ttylabel)
617 * - search "/etc/ttydefs" for speed and term. specification
618 * using "ttylabel". If "ttylabel" is NULL, default
619 * to DEFAULT
620 * arg: ttylabel - label/id of speed settings.
623 struct Gdef *
624 get_speed(char *ttylabel)
626 register struct Gdef *sp;
627 extern struct Gdef DEFAULT;
629 if ((ttylabel != NULL) && (*ttylabel != '\0')) {
630 if ((sp = find_def(ttylabel)) == NULL) {
631 log("unable to find <%s> in \"%s\"", ttylabel, TTYDEFS);
632 sp = &DEFAULT; /* use default */
634 } else sp = &DEFAULT; /* use default */
635 return (sp);
639 * setup_PCpipe() - setup the pipe between Parent and Children
640 * - the pipe is used for a tmchild to send its
641 * pid to inform ttymon that it is about to
642 * invoke service
643 * - the pipe also serves as a mean for tmchild
644 * to detect failure of ttymon
646 void
647 setup_PCpipe()
649 int flag = 0;
651 if (pipe(PCpipe) == -1)
652 fatal("pipe() failed: %s", strerror(errno));
654 /* set close-on-exec flag */
655 if (fcntl(PCpipe[0], F_SETFD, 1) == -1)
656 fatal("F_SETFD fcntl failed: %s", strerror(errno));
658 if (fcntl(PCpipe[1], F_SETFD, 1) == -1)
659 fatal("F_SETFD fcntl failed: %s", strerror(errno));
661 /* set O_NONBLOCK flag */
662 if (fcntl(PCpipe[0], F_GETFL, flag) == -1)
663 fatal("F_GETFL failed: %s", strerror(errno));
665 flag |= O_NONBLOCK;
666 if (fcntl(PCpipe[0], F_SETFL, flag) == -1)
667 fatal("F_SETFL failed: %s", strerror(errno));
669 /* set message discard mode */
670 if (ioctl(PCpipe[0], I_SRDOPT, RMSGD) == -1)
671 fatal("I_SRDOPT RMSGD failed: %s", strerror(errno));
673 /* register to receive SIGPOLL when data come */
674 if (ioctl(PCpipe[0], I_SETSIG, S_INPUT) == -1)
675 fatal("I_SETSIG S_INPUT failed: %s", strerror(errno));
677 #ifdef DEBUG
678 log("PCpipe[0]\t = %d", PCpipe[0]);
679 log("PCpipe[1]\t = %d", PCpipe[1]);
680 #endif