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]
22 * Copyright 2007 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 #pragma ident "%Z%%M% %I% %E% SMI"
33 * ct [-h] [-v] [-w n] [-x n] [-s speed] telno ...
35 * dials the given telephone number, waits for the
36 * modem to answer, and initiates a login process.
38 * ct uses several routines from uucp:
39 * - getto(flds) takes a vector of fields needed to make
40 * a connection and returns a file descriptor or -1
41 * - rddev( ... ) takes several arguments and returns lines
42 * from the /etc/uucp/Devices that match the type
43 * (in ct the type will be ACU)
44 * - fdig(string) takes a string that is zero or more
45 * alphabetic characters follow by a number (baud rate)
46 * and returns a pointer to the first digit in the string.
47 * - fn_cklock(dev) takes a device name [/dev/]term/11 and
48 * checks whether the appropriate lock file exists. It returns
50 * - rmlock(pointer) removes the lock file. In ct pointer is
51 * always CNULL (a null pointer) causing rmlock to remove
52 * all lock files associated with this execution of ct.
67 #define TTYGID (gid_t) 7 /* group id for terminal */
68 #define TTYMOD (mode_t) 0622
70 #define TELNOSIZE 32 /* maximum phone # size is 31 */
71 #define LEGAL "0123456789-*#="
72 #define USAGE "[-h] [-v] [-w n] [-x n] [-s speed] telno ..."
73 #define LOG "/var/adm/ctlog"
74 #define TTYMON "/usr/lib/saf/ttymon"
79 int _Status
; /* exit status of child */
82 pid_t _Pid
= 0; /* process id of child */
86 _Tty
[sizeof DEV
+12] = "", /* /dev/term/xx for connection device */
87 *_Dev
[D_MAX
+ 1], /* Filled in by rddev and used globally */
88 _Devbuf
[BUFSIZ
]; /* buffer for rddev */
92 *_Num
, /* pointer to a phone number */
93 *_Flds
[7]; /* Filled in as if finds() in uucp did it */
103 extern char *optarg
, *fdig();
104 extern void cleanup();
105 extern struct passwd
*getpwuid ();
107 extern int getto(), rddev();
108 static int gdev(), logproc(), exists();
109 static void startat(), stopat(), disconnect(), zero();
112 * These two dummy routines are needed because the uucp routines
113 * used by ct reference them, but they will never be
114 * called when executing from ct
120 assert (s1
, s2
, i1
, s3
, i2
)
123 { } /* for ASSERT in gnamef.c */
129 { } /* so we can load ulockf() */
131 jmp_buf Sjbuf
; /* used by uucp routines */
143 logprocflag
, /* is there a login process on the line */
144 hangup
= 1, /* hangup by default */
145 minutes
= 0; /* number of minutes to wait for dialer */
147 struct termio termio
;
148 typedef void (*save_sig
)();
152 extern void setservice(), devreset();
153 extern int sysaccess();
155 save_hup
= signal (SIGHUP
, cleanup
);
156 save_quit
= signal (SIGQUIT
, cleanup
);
157 save_int
= signal (SIGINT
, cleanup
);
158 (void) signal (SIGTERM
, cleanup
);
159 (void) strcpy (Progname
, "ct");
162 if ( sysaccess(EACCESS_DEVICES
) != 0 ) {
163 (void) fprintf(stderr
, "ct: can't access Devices file\n");
167 /* Set up the _Flds vector as if finds() [from uucico] built it */
168 _Flds
[F_NAME
] = "dummy"; /* never used */
169 _Flds
[F_TIME
] = "Any"; /* never used */
170 _Flds
[F_TYPE
] = "ACU";
171 _Flds
[F_CLASS
] = "1200"; /* default at 1200 */
172 _Flds
[F_PHONE
] = ""; /* filled in by arguments */
173 _Flds
[F_LOGIN
] = ""; /* never used */
176 while ((c
= getopt (argc
, argv
, "hvw:s:x:")) != EOF
) {
187 minutes
= atoi (optarg
);
189 (void) fprintf(stderr
,
190 "\tusage: %s %s\n", Progname
, USAGE
);
191 (void) fprintf(stderr
, "(-w %s) Wait time must be > 0\n",
198 _Flds
[F_CLASS
] = optarg
;
202 Debug
= atoi(optarg
);
203 if (Debug
< 0 || Debug
> 9) {
204 (void) fprintf(stderr
,
205 "\tusage: %s %s\n", Progname
, USAGE
);
206 (void) fprintf(stderr
, "(-x %s) value must be 0-9\n",
213 (void) fprintf(stderr
, "\tusage: %s %s\n", Progname
, USAGE
);
219 if (optind
== argc
) {
220 (void) fprintf(stderr
, "\tusage: %s %s\n", Progname
, USAGE
);
221 (void) fprintf(stderr
, "No phone numbers specified!\n");
225 /* check for valid phone number(s) */
226 for (count
= argc
- 1; count
>= optind
; --count
) {
228 if (strlen(_Num
) >= (size_t)(TELNOSIZE
- 1)) {
229 (void) fprintf(stderr
, "ct: phone number too long -- %s\n", _Num
);
232 if ((int)strspn(_Num
, LEGAL
) < (int)strlen(_Num
)) {
233 (void) fprintf(stderr
, "ct: bad phone number -- %s\n", _Num
);
240 /************************************************************/
241 /* Begin Loop: Find an available Dialer */
242 /************************************************************/
243 for (count
= 0;; count
++) { /* count will be wait time after first
244 * time through the loop.
245 * break will be used exit loop.
247 if ( (found
= gdev (_Flds
)) > 0) { /* found a dialer */
248 (void) fprintf(stdout
, "Allocated dialer at %s baud\n",
252 else if (found
== 0) { /* no dialers of that on system */
253 (void) fprintf(stdout
, "No %s dialers on this system\n",
254 fdig(_Flds
[F_CLASS
]) );
258 if (!first
) { /* not the first time in loop */
259 VERBOSE("%s busy", (found
== -1) ? "Dialer is" : "Dialers are");
260 VERBOSE(" (%d minute(s))\n", count
);
261 if (count
< minutes
) {
265 /* This is the end of the loop - no time left */
269 /**************************************************************/
270 /* First time through loop - get wait minutes if no -w option */
271 /**************************************************************/
273 (void) fprintf(stdout
, "The (%d) %s dialer%s busy\n", -found
,
274 _Flds
[F_CLASS
], (found
== -1 ? " is" : "s are"));
275 if (minutes
) { /* -w already set wait minutes */
276 (void) fprintf(stdout
, "Waiting for %d minute%s\n", minutes
,
277 (minutes
> 1 ? "s" : "") );
282 if (!isatty(0) ) { /* not a terminal - get out */
286 /* Ask user if she/he wants to wait */
287 (void) fputs("Do you want to wait for dialer? (y for yes): ", stdout
);
288 if ((c
= getchar ()) == EOF
|| tolower (c
) != 'y')
290 while ( (c
= getchar()) != EOF
&& c
!= '\n')
293 (void) fputs ("Time, in minutes? ", stdout
);
294 (void) scanf ("%d", &minutes
);
295 while ( (c
= getchar()) != EOF
&& c
!= '\n')
301 (void) fputs ("Waiting for dialer\n", stdout
);
306 /************************************************************/
307 /* End Loop: Find an available Dialer */
308 /************************************************************/
310 /* check why loop terminated */
311 if (found
< 0) { /* no dialer found - get out */
312 (void) fputs("*** TIMEOUT ***\n", stdout
);
316 (void) signal(SIGHUP
, SIG_IGN
);
317 /* found a dialer. now try to call */
321 if (hangup
) { /* -h option not specified */
323 (void) fputs ("Confirm hang-up? (y/n): ", stdout
);
324 switch (c
=tolower(getchar())) {
332 while ( c
!= EOF
&& c
!= '\n' )
338 /* close stderr if it is not redirected */
345 (void) ioctl (0, TCGETA
, &termio
);
346 termio
.c_cflag
= 0; /* speed to zero for hangup */
347 (void) ioctl (0, TCSETAW
, &termio
); /* hang up terminal */
353 /* Try each phone number until a connection is made, or non work */
354 for (count
= optind
; count
< argc
; count
++) {
355 /* call getto routine to make connection */
356 _Flds
[F_PHONE
] = argv
[count
];
357 rmlock(CNULL
); /* remove temporary lock set by gdev */
362 * If there is a login process on the line, get rid
363 * of the lock file quickly so that when the process
364 * reads the first character, the lock file will be gone
365 * indicating that the process should handle the data.
367 if ( (logprocflag
= logproc(Dc
)) ) /* really an assignment! */
370 _Fdl
= fdopen(fdl
, "r+");
371 (void) sprintf(_Tty
, "%s%s", DEV
, Dc
);
372 /* NOTE: Dc is set in the caller routines */
377 /* check why the loop ended (connected or no more numbers to try) */
381 /****** Successfully made connection ******/
382 VERBOSE("Connected\n%s", "");
385 if (!strcmp(_Dev
[D_CALLER
], "DK")) {
386 strcpy(_Tty
, dtnamer(dkminor(fdl
)));
387 strcpy(Dc
, (strrchr(_Tty
, '/')+1));
388 if ((_Fdl
= fopen(_Tty
, "r+")) == NULL
) {
389 (void) fprintf(stderr
, "ct: Cannot open %s, errno %d\n",
396 /* ignore some signals if they were ignored upon invocation of ct */
397 /* or else, have them go to graceful disconnect */
398 if (save_hup
== SIG_IGN
)
399 (void) signal (SIGHUP
, SIG_IGN
);
401 (void) signal (SIGHUP
, disconnect
);
403 if (save_quit
== SIG_IGN
)
404 (void) signal (SIGQUIT
, SIG_IGN
);
406 (void) signal (SIGQUIT
, disconnect
);
408 if (save_int
== SIG_IGN
)
409 (void) signal (SIGINT
, SIG_IGN
);
411 (void) signal (SIGINT
, disconnect
);
413 (void) signal (SIGTERM
, disconnect
);
414 (void) signal (SIGALRM
, disconnect
);
416 (void) sleep (2); /* time for phone line/modem to settle */
418 _Log_on
= time ((time_t *) 0);
421 * if there is a login process on this line,
422 * tell the user to hit a carriage return to make
423 * the waiting process get past the inital read,
426 if (logprocflag
) { /* there is a login process on the line */
427 (void) fputs("Hit carriage return ", _Fdl
);
429 CDEBUG(4, "there is a login process; exit\n%s", "");
433 CDEBUG(4, "start login process (%s ", TTYMON
);
434 CDEBUG(4, "-g -h -t 60 -l %s)\n", fdig(_Flds
[F_CLASS
]));
437 switch(_Pid
= fork()) {
438 case -1: /* fork failed */
439 if ((!hangup
|| Verbose
))
440 (void) fputs ("ct: can't fork for login process\n", stderr
);
444 case 0: /* child process */
447 /* ttymon will use open fd 0 for connection */
452 (void) signal(SIGHUP
, SIG_DFL
); /* so child will exit on hangup */
453 (void) execl(TTYMON
, "ttymon", "-g", "-h", "-t", "60",
454 "-l", fdig(_Flds
[F_CLASS
]), (char *) 0);
459 default: /* parent process */
465 while ((w_ret
= wait(&_Status
)) != _Pid
)
466 if (w_ret
== -1 && errno
!= EINTR
) {
467 VERBOSE("ct: wait failed errno=%d\n", errno
);
470 if ((_Status
& 0xff00) < 0) {
472 VERBOSE("ct: can't exec login process\n%s", "");
476 stopat(_Flds
[F_PHONE
]);
478 rewind (_Fdl
); /* flush line */
479 (void) fputs ("\nReconnect? ", _Fdl
);
485 if (c
== EOF
|| tolower (c
) == 'n')
486 disconnect (0); /* normal disconnect */
487 while ( (c
= getc(_Fdl
)) != EOF
&& c
!= '\n')
496 struct termio termio
;
499 (void) signal (SIGALRM
, SIG_IGN
);
500 (void) signal (SIGINT
, SIG_IGN
);
501 (void) signal (SIGTERM
, SIG_IGN
);
503 _Log_elpsd
= time ((time_t *) 0) - _Log_on
;
505 (void) ioctl (fileno(_Fdl
), TCGETA
, &termio
);
506 termio
.c_cflag
= 0; /* speed to zero for hangup */
507 (void) ioctl (fileno(_Fdl
), TCSETAW
, &termio
); /* hang up terminal */
508 (void) fclose (_Fdl
);
510 DEBUG(5, "Disconnect(%d)\n", code
);
511 VERBOSE("Disconnected\n%s", "");
513 /* For normal disconnect or timeout on "Reconnect?" message,
514 we already cleaned up above */
516 if ((code
!= 0) && (code
!= SIGALRM
))
517 stopat(_Flds
[F_PHONE
]);
523 * clean and exit with "code" status
529 CDEBUG(5, "cleanup(%d)\n", code
);
532 CDEBUG(5, "chmod/chown %s\n", _Tty
);
533 if (chown(_Tty
, UUCPUID
, TTYGID
) < 0 ) {
534 CDEBUG(5, "Can't chown to uid=%u, ", UUCPUID
);
535 CDEBUG(5, "gid=%u\n", TTYGID
);
537 if (chmod(_Tty
, TTYMOD
) < 0) {
538 CDEBUG(5, "Can't chmod to %lo\n", (unsigned long) TTYMOD
);
541 if (_Pid
) { /* kill the child process */
542 (void) signal(SIGHUP
, SIG_IGN
);
543 (void) signal(SIGQUIT
, SIG_IGN
);
544 (void) kill (_Pid
, SIGKILL
);
550 * Find an available line with a dialer on it.
551 * Set a temporary lock file for the line.
554 * <0 - failed - return the number of possible dialers
555 * 0 - not dialers of requested class on the system.
563 extern void devreset();
566 while (rddev ("ACU", _Dev
, _Devbuf
, D_MAX
) != FAIL
) {
567 /* check caller type */
568 if (!EQUALS (flds
[F_TYPE
] /* "ACU" */, _Dev
[D_TYPE
]))
570 /* check class, check (and possibly set) speed */
571 if (!EQUALS (flds
[F_CLASS
] /* speed */, _Dev
[D_CLASS
]))
575 if (fn_cklock(_Dev
[D_LINE
]) == FAIL
)
578 /* found available dialer and set temporary lock */
586 * Check if there is a login process active on this line.
588 * 0 - there is no login process on this line
589 * 1 - found a login process on this line
598 while ((u
= getutxent()) != NULL
) {
599 if (u
->ut_type
== LOGIN_PROCESS
600 && EQUALS(u
->ut_line
, line
)
601 && EQUALS(u
->ut_user
, "LOGIN") ) {
602 CDEBUG(7, "ut_line %s, ", u
->ut_line
);
603 CDEBUG(7, "ut_user %s, ", u
->ut_user
);
604 CDEBUG(7, "ut_id %.4s, ", u
->ut_id
);
605 CDEBUG(7, "ut_pid %d\n", u
->ut_pid
);
607 /* see if the process is still active */
608 if (kill(u
->ut_pid
, 0) == 0 || errno
== EPERM
) {
609 CDEBUG(4, "process still active\n%s", "");
618 * Create an entry in utmpx file if one does not already exist.
623 struct utmpx utmpxbuf
, *u
;
626 /* Set up the prototype for the utmpx structure we want to write. */
629 zero (&u
-> ut_user
[0], sizeof (u
-> ut_user
));
630 zero (&u
-> ut_line
[0], sizeof (u
-> ut_line
));
632 /* Fill in the various fields of the utmpx structure. */
636 u
-> ut_id
[2] = _Tty
[strlen(_Tty
)-2];
637 u
-> ut_id
[3] = _Tty
[strlen(_Tty
)-1];
638 u
-> ut_pid
= getpid ();
640 u
-> ut_exit
.e_termination
= 0;
641 u
-> ut_exit
.e_exit
= 0;
642 u
-> ut_type
= INIT_PROCESS
;
643 time (&u
-> ut_xtime
);
644 setutxent (); /* Start at beginning of utmpx file. */
646 /* For INIT_PROCESSes put in the name of the program in the */
647 /* "ut_user" field. */
649 strncpy (&u
-> ut_user
[0], "ttymon", sizeof (u
-> ut_user
));
650 strncpy (&u
-> ut_line
[0], Dc
, sizeof (u
-> ut_line
));
652 /* Write out the updated entry to utmpx file. */
655 /* Now attempt to add to the end of the wtmpx file. Do not create */
656 /* if it doesn't already exist. Do not overwrite any info already */
659 if ((fd
= open(WTMPX_FILE
, O_WRONLY
| O_APPEND
)) != -1) {
660 (void) write(fd
, u
, sizeof(*u
));
668 * Change utmpx file entry to "dead".
669 * Make entry in ct log.
676 struct utmpx utmpxbuf
, *u
;
680 /* Set up the prototype for the utmpx structure we want to write. */
684 zero (&u
-> ut_user
[0], sizeof (u
-> ut_user
));
685 zero (&u
-> ut_line
[0], sizeof (u
-> ut_line
));
687 /* Fill in the various fields of the utmpx structure. */
691 u
-> ut_id
[2] = _Tty
[strlen(_Tty
)-2];
692 u
-> ut_id
[3] = _Tty
[strlen(_Tty
)-1];
693 u
-> ut_pid
= (pid_t
) _Pid
;
694 u
-> ut_type
= USER_PROCESS
;
696 /* Find the old entry in the utmpx file with the user name and */
699 if (u
= getutxid (u
)) {
704 u
-> ut_exit
.e_termination
= _Status
& 0xff;
705 u
-> ut_exit
.e_exit
= (_Status
>> 8) & 0xff;
706 u
-> ut_type
= DEAD_PROCESS
;
707 time (&u
-> ut_xtime
);
709 /* Write out the updated entry to utmpx file. */
713 /* Now attempt to add to the end of the wtmpx file. Do not create */
714 /* if it doesn't already exist. Do not overwrite any info already */
717 if ((fd
= open(WTMPX_FILE
, O_WRONLY
| O_APPEND
)) != -1) {
718 (void) write(fd
, u
, sizeof(*u
));
723 /* Do the log accounting */
725 if (exists (LOG
) && (fp
= fopen (LOG
, "a")) != NULL
) {
731 /* ignore user set TZ for logfile purposes */
732 if ( (aptr
= getenv ("TZ")) != NULL
)
735 (aptr
= ctime (&_Log_on
))[16] = '\0';
736 hrs
= _Log_elpsd
/ 3600;
737 mins
= (_Log_elpsd
%= 3600) / 60;
738 secs
= _Log_elpsd
% 60;
739 (void) fprintf(fp
, "%-8s ", getpwuid (getuid ()) -> pw_name
);
740 (void) fprintf(fp
, "(%4s) %s ", fdig(_Flds
[F_CLASS
]), aptr
);
742 (void) fprintf(fp
, "%2d:%.2d", hrs
, mins
);
744 (void) fprintf(fp
, " %2d", mins
);
745 (void) fprintf(fp
, ":%.2d %s\n", secs
, num
);
757 if (stat (file
, &statb
) == -1 && errno
== ENOENT
)