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 * Copyright (c) 2016 by Delphix. All rights reserved.
31 #pragma ident "%Z%%M% %I% %E% SMI"
35 * ct [-h] [-v] [-w n] [-x n] [-s speed] telno ...
37 * dials the given telephone number, waits for the
38 * modem to answer, and initiates a login process.
40 * ct uses several routines from uucp:
41 * - getto(flds) takes a vector of fields needed to make
42 * a connection and returns a file descriptor or -1
43 * - rddev( ... ) takes several arguments and returns lines
44 * from the /etc/uucp/Devices that match the type
45 * (in ct the type will be ACU)
46 * - fdig(string) takes a string that is zero or more
47 * alphabetic characters follow by a number (baud rate)
48 * and returns a pointer to the first digit in the string.
49 * - fn_cklock(dev) takes a device name [/dev/]term/11 and
50 * checks whether the appropriate lock file exists. It returns
52 * - rmlock(pointer) removes the lock file. In ct pointer is
53 * always CNULL (a null pointer) causing rmlock to remove
54 * all lock files associated with this execution of ct.
69 #define TTYGID (gid_t) 7 /* group id for terminal */
70 #define TTYMOD (mode_t) 0622
72 #define TELNOSIZE 32 /* maximum phone # size is 31 */
73 #define LEGAL "0123456789-*#="
74 #define USAGE "[-h] [-v] [-w n] [-x n] [-s speed] telno ..."
75 #define LOG "/var/adm/ctlog"
76 #define TTYMON "/usr/lib/saf/ttymon"
81 int _Status
; /* exit status of child */
84 pid_t _Pid
= 0; /* process id of child */
88 _Tty
[sizeof DEV
+12] = "", /* /dev/term/xx for connection device */
89 *_Dev
[D_MAX
+ 1], /* Filled in by rddev and used globally */
90 _Devbuf
[BUFSIZ
]; /* buffer for rddev */
94 *_Num
, /* pointer to a phone number */
95 *_Flds
[7]; /* Filled in as if finds() in uucp did it */
105 extern char *optarg
, *fdig();
106 extern void cleanup();
107 extern struct passwd
*getpwuid ();
109 extern int getto(), rddev();
110 static int gdev(), logproc(), exists();
111 static void startat(), stopat(), disconnect(), zero();
114 * These two dummy routines are needed because the uucp routines
115 * used by ct reference them, but they will never be
116 * called when executing from ct
122 assert (s1
, s2
, i1
, s3
, i2
)
125 { } /* for ASSERT in gnamef.c */
131 { } /* so we can load ulockf() */
133 jmp_buf Sjbuf
; /* used by uucp routines */
145 logprocflag
, /* is there a login process on the line */
146 hangup
= 1, /* hangup by default */
147 minutes
= 0; /* number of minutes to wait for dialer */
149 struct termio termio
;
150 typedef void (*save_sig
)();
154 extern void setservice(), devreset();
155 extern int sysaccess();
157 save_hup
= signal (SIGHUP
, cleanup
);
158 save_quit
= signal (SIGQUIT
, cleanup
);
159 save_int
= signal (SIGINT
, cleanup
);
160 (void) signal (SIGTERM
, cleanup
);
161 (void) strcpy (Progname
, "ct");
164 if ( sysaccess(EACCESS_DEVICES
) != 0 ) {
165 (void) fprintf(stderr
, "ct: can't access Devices file\n");
169 /* Set up the _Flds vector as if finds() [from uucico] built it */
170 _Flds
[F_NAME
] = "dummy"; /* never used */
171 _Flds
[F_TIME
] = "Any"; /* never used */
172 _Flds
[F_TYPE
] = "ACU";
173 _Flds
[F_CLASS
] = "1200"; /* default at 1200 */
174 _Flds
[F_PHONE
] = ""; /* filled in by arguments */
175 _Flds
[F_LOGIN
] = ""; /* never used */
178 while ((c
= getopt (argc
, argv
, "hvw:s:x:")) != EOF
) {
189 minutes
= atoi (optarg
);
191 (void) fprintf(stderr
,
192 "\tusage: %s %s\n", Progname
, USAGE
);
193 (void) fprintf(stderr
, "(-w %s) Wait time must be > 0\n",
200 _Flds
[F_CLASS
] = optarg
;
204 Debug
= atoi(optarg
);
205 if (Debug
< 0 || Debug
> 9) {
206 (void) fprintf(stderr
,
207 "\tusage: %s %s\n", Progname
, USAGE
);
208 (void) fprintf(stderr
, "(-x %s) value must be 0-9\n",
215 (void) fprintf(stderr
, "\tusage: %s %s\n", Progname
, USAGE
);
221 if (optind
== argc
) {
222 (void) fprintf(stderr
, "\tusage: %s %s\n", Progname
, USAGE
);
223 (void) fprintf(stderr
, "No phone numbers specified!\n");
227 /* check for valid phone number(s) */
228 for (count
= argc
- 1; count
>= optind
; --count
) {
230 if (strlen(_Num
) >= (size_t)(TELNOSIZE
- 1)) {
231 (void) fprintf(stderr
, "ct: phone number too long -- %s\n", _Num
);
234 if ((int)strspn(_Num
, LEGAL
) < (int)strlen(_Num
)) {
235 (void) fprintf(stderr
, "ct: bad phone number -- %s\n", _Num
);
242 /************************************************************/
243 /* Begin Loop: Find an available Dialer */
244 /************************************************************/
245 for (count
= 0;; count
++) { /* count will be wait time after first
246 * time through the loop.
247 * break will be used exit loop.
249 if ( (found
= gdev (_Flds
)) > 0) { /* found a dialer */
250 (void) fprintf(stdout
, "Allocated dialer at %s baud\n",
254 else if (found
== 0) { /* no dialers of that on system */
255 (void) fprintf(stdout
, "No %s dialers on this system\n",
256 fdig(_Flds
[F_CLASS
]) );
260 if (!first
) { /* not the first time in loop */
261 VERBOSE("%s busy", (found
== -1) ? "Dialer is" : "Dialers are");
262 VERBOSE(" (%d minute(s))\n", count
);
263 if (count
< minutes
) {
267 /* This is the end of the loop - no time left */
271 /**************************************************************/
272 /* First time through loop - get wait minutes if no -w option */
273 /**************************************************************/
275 (void) fprintf(stdout
, "The (%d) %s dialer%s busy\n", -found
,
276 _Flds
[F_CLASS
], (found
== -1 ? " is" : "s are"));
277 if (minutes
) { /* -w already set wait minutes */
278 (void) fprintf(stdout
, "Waiting for %d minute%s\n", minutes
,
279 (minutes
> 1 ? "s" : "") );
284 if (!isatty(0) ) { /* not a terminal - get out */
288 /* Ask user if they want to wait */
289 (void) fputs("Do you want to wait for dialer? (y for yes): ", stdout
);
290 if ((c
= getchar ()) == EOF
|| tolower (c
) != 'y')
292 while ( (c
= getchar()) != EOF
&& c
!= '\n')
295 (void) fputs ("Time, in minutes? ", stdout
);
296 (void) scanf ("%d", &minutes
);
297 while ( (c
= getchar()) != EOF
&& c
!= '\n')
303 (void) fputs ("Waiting for dialer\n", stdout
);
308 /************************************************************/
309 /* End Loop: Find an available Dialer */
310 /************************************************************/
312 /* check why loop terminated */
313 if (found
< 0) { /* no dialer found - get out */
314 (void) fputs("*** TIMEOUT ***\n", stdout
);
318 (void) signal(SIGHUP
, SIG_IGN
);
319 /* found a dialer. now try to call */
323 if (hangup
) { /* -h option not specified */
325 (void) fputs ("Confirm hang-up? (y/n): ", stdout
);
326 switch (c
=tolower(getchar())) {
334 while ( c
!= EOF
&& c
!= '\n' )
340 /* close stderr if it is not redirected */
347 (void) ioctl (0, TCGETA
, &termio
);
348 termio
.c_cflag
= 0; /* speed to zero for hangup */
349 (void) ioctl (0, TCSETAW
, &termio
); /* hang up terminal */
355 /* Try each phone number until a connection is made, or non work */
356 for (count
= optind
; count
< argc
; count
++) {
357 /* call getto routine to make connection */
358 _Flds
[F_PHONE
] = argv
[count
];
359 rmlock(CNULL
); /* remove temporary lock set by gdev */
364 * If there is a login process on the line, get rid
365 * of the lock file quickly so that when the process
366 * reads the first character, the lock file will be gone
367 * indicating that the process should handle the data.
369 if ( (logprocflag
= logproc(Dc
)) ) /* really an assignment! */
372 _Fdl
= fdopen(fdl
, "r+");
373 (void) sprintf(_Tty
, "%s%s", DEV
, Dc
);
374 /* NOTE: Dc is set in the caller routines */
379 /* check why the loop ended (connected or no more numbers to try) */
383 /****** Successfully made connection ******/
384 VERBOSE("Connected\n%s", "");
387 if (!strcmp(_Dev
[D_CALLER
], "DK")) {
388 strcpy(_Tty
, dtnamer(dkminor(fdl
)));
389 strcpy(Dc
, (strrchr(_Tty
, '/')+1));
390 if ((_Fdl
= fopen(_Tty
, "r+")) == NULL
) {
391 (void) fprintf(stderr
, "ct: Cannot open %s, errno %d\n",
398 /* ignore some signals if they were ignored upon invocation of ct */
399 /* or else, have them go to graceful disconnect */
400 if (save_hup
== SIG_IGN
)
401 (void) signal (SIGHUP
, SIG_IGN
);
403 (void) signal (SIGHUP
, disconnect
);
405 if (save_quit
== SIG_IGN
)
406 (void) signal (SIGQUIT
, SIG_IGN
);
408 (void) signal (SIGQUIT
, disconnect
);
410 if (save_int
== SIG_IGN
)
411 (void) signal (SIGINT
, SIG_IGN
);
413 (void) signal (SIGINT
, disconnect
);
415 (void) signal (SIGTERM
, disconnect
);
416 (void) signal (SIGALRM
, disconnect
);
418 (void) sleep (2); /* time for phone line/modem to settle */
420 _Log_on
= time ((time_t *) 0);
423 * if there is a login process on this line,
424 * tell the user to hit a carriage return to make
425 * the waiting process get past the inital read,
428 if (logprocflag
) { /* there is a login process on the line */
429 (void) fputs("Hit carriage return ", _Fdl
);
431 CDEBUG(4, "there is a login process; exit\n%s", "");
435 CDEBUG(4, "start login process (%s ", TTYMON
);
436 CDEBUG(4, "-g -h -t 60 -l %s)\n", fdig(_Flds
[F_CLASS
]));
439 switch(_Pid
= fork()) {
440 case -1: /* fork failed */
441 if ((!hangup
|| Verbose
))
442 (void) fputs ("ct: can't fork for login process\n", stderr
);
446 case 0: /* child process */
449 /* ttymon will use open fd 0 for connection */
454 (void) signal(SIGHUP
, SIG_DFL
); /* so child will exit on hangup */
455 (void) execl(TTYMON
, "ttymon", "-g", "-h", "-t", "60",
456 "-l", fdig(_Flds
[F_CLASS
]), (char *) 0);
461 default: /* parent process */
467 while ((w_ret
= wait(&_Status
)) != _Pid
)
468 if (w_ret
== -1 && errno
!= EINTR
) {
469 VERBOSE("ct: wait failed errno=%d\n", errno
);
472 if ((_Status
& 0xff00) < 0) {
474 VERBOSE("ct: can't exec login process\n%s", "");
478 stopat(_Flds
[F_PHONE
]);
480 rewind (_Fdl
); /* flush line */
481 (void) fputs ("\nReconnect? ", _Fdl
);
487 if (c
== EOF
|| tolower (c
) == 'n')
488 disconnect (0); /* normal disconnect */
489 while ( (c
= getc(_Fdl
)) != EOF
&& c
!= '\n')
498 struct termio termio
;
501 (void) signal (SIGALRM
, SIG_IGN
);
502 (void) signal (SIGINT
, SIG_IGN
);
503 (void) signal (SIGTERM
, SIG_IGN
);
505 _Log_elpsd
= time ((time_t *) 0) - _Log_on
;
507 (void) ioctl (fileno(_Fdl
), TCGETA
, &termio
);
508 termio
.c_cflag
= 0; /* speed to zero for hangup */
509 (void) ioctl (fileno(_Fdl
), TCSETAW
, &termio
); /* hang up terminal */
510 (void) fclose (_Fdl
);
512 DEBUG(5, "Disconnect(%d)\n", code
);
513 VERBOSE("Disconnected\n%s", "");
515 /* For normal disconnect or timeout on "Reconnect?" message,
516 we already cleaned up above */
518 if ((code
!= 0) && (code
!= SIGALRM
))
519 stopat(_Flds
[F_PHONE
]);
525 * clean and exit with "code" status
531 CDEBUG(5, "cleanup(%d)\n", code
);
534 CDEBUG(5, "chmod/chown %s\n", _Tty
);
535 if (chown(_Tty
, UUCPUID
, TTYGID
) < 0 ) {
536 CDEBUG(5, "Can't chown to uid=%u, ", UUCPUID
);
537 CDEBUG(5, "gid=%u\n", TTYGID
);
539 if (chmod(_Tty
, TTYMOD
) < 0) {
540 CDEBUG(5, "Can't chmod to %lo\n", (unsigned long) TTYMOD
);
543 if (_Pid
) { /* kill the child process */
544 (void) signal(SIGHUP
, SIG_IGN
);
545 (void) signal(SIGQUIT
, SIG_IGN
);
546 (void) kill (_Pid
, SIGKILL
);
552 * Find an available line with a dialer on it.
553 * Set a temporary lock file for the line.
556 * <0 - failed - return the number of possible dialers
557 * 0 - not dialers of requested class on the system.
565 extern void devreset();
568 while (rddev ("ACU", _Dev
, _Devbuf
, D_MAX
) != FAIL
) {
569 /* check caller type */
570 if (!EQUALS (flds
[F_TYPE
] /* "ACU" */, _Dev
[D_TYPE
]))
572 /* check class, check (and possibly set) speed */
573 if (!EQUALS (flds
[F_CLASS
] /* speed */, _Dev
[D_CLASS
]))
577 if (fn_cklock(_Dev
[D_LINE
]) == FAIL
)
580 /* found available dialer and set temporary lock */
588 * Check if there is a login process active on this line.
590 * 0 - there is no login process on this line
591 * 1 - found a login process on this line
600 while ((u
= getutxent()) != NULL
) {
601 if (u
->ut_type
== LOGIN_PROCESS
602 && EQUALS(u
->ut_line
, line
)
603 && EQUALS(u
->ut_user
, "LOGIN") ) {
604 CDEBUG(7, "ut_line %s, ", u
->ut_line
);
605 CDEBUG(7, "ut_user %s, ", u
->ut_user
);
606 CDEBUG(7, "ut_id %.4s, ", u
->ut_id
);
607 CDEBUG(7, "ut_pid %d\n", u
->ut_pid
);
609 /* see if the process is still active */
610 if (kill(u
->ut_pid
, 0) == 0 || errno
== EPERM
) {
611 CDEBUG(4, "process still active\n%s", "");
620 * Create an entry in utmpx file if one does not already exist.
625 struct utmpx utmpxbuf
, *u
;
628 /* Set up the prototype for the utmpx structure we want to write. */
631 zero (&u
-> ut_user
[0], sizeof (u
-> ut_user
));
632 zero (&u
-> ut_line
[0], sizeof (u
-> ut_line
));
634 /* Fill in the various fields of the utmpx structure. */
638 u
-> ut_id
[2] = _Tty
[strlen(_Tty
)-2];
639 u
-> ut_id
[3] = _Tty
[strlen(_Tty
)-1];
640 u
-> ut_pid
= getpid ();
642 u
-> ut_exit
.e_termination
= 0;
643 u
-> ut_exit
.e_exit
= 0;
644 u
-> ut_type
= INIT_PROCESS
;
645 time (&u
-> ut_xtime
);
646 setutxent (); /* Start at beginning of utmpx file. */
648 /* For INIT_PROCESSes put in the name of the program in the */
649 /* "ut_user" field. */
651 strncpy (&u
-> ut_user
[0], "ttymon", sizeof (u
-> ut_user
));
652 strncpy (&u
-> ut_line
[0], Dc
, sizeof (u
-> ut_line
));
654 /* Write out the updated entry to utmpx file. */
657 /* Now attempt to add to the end of the wtmpx file. Do not create */
658 /* if it doesn't already exist. Do not overwrite any info already */
661 if ((fd
= open(WTMPX_FILE
, O_WRONLY
| O_APPEND
)) != -1) {
662 (void) write(fd
, u
, sizeof(*u
));
670 * Change utmpx file entry to "dead".
671 * Make entry in ct log.
678 struct utmpx utmpxbuf
, *u
;
682 /* Set up the prototype for the utmpx structure we want to write. */
686 zero (&u
-> ut_user
[0], sizeof (u
-> ut_user
));
687 zero (&u
-> ut_line
[0], sizeof (u
-> ut_line
));
689 /* Fill in the various fields of the utmpx structure. */
693 u
-> ut_id
[2] = _Tty
[strlen(_Tty
)-2];
694 u
-> ut_id
[3] = _Tty
[strlen(_Tty
)-1];
695 u
-> ut_pid
= (pid_t
) _Pid
;
696 u
-> ut_type
= USER_PROCESS
;
698 /* Find the old entry in the utmpx file with the user name and */
701 if (u
= getutxid (u
)) {
706 u
-> ut_exit
.e_termination
= _Status
& 0xff;
707 u
-> ut_exit
.e_exit
= (_Status
>> 8) & 0xff;
708 u
-> ut_type
= DEAD_PROCESS
;
709 time (&u
-> ut_xtime
);
711 /* Write out the updated entry to utmpx file. */
715 /* Now attempt to add to the end of the wtmpx file. Do not create */
716 /* if it doesn't already exist. Do not overwrite any info already */
719 if ((fd
= open(WTMPX_FILE
, O_WRONLY
| O_APPEND
)) != -1) {
720 (void) write(fd
, u
, sizeof(*u
));
725 /* Do the log accounting */
727 if (exists (LOG
) && (fp
= fopen (LOG
, "a")) != NULL
) {
733 /* ignore user set TZ for logfile purposes */
734 if ( (aptr
= getenv ("TZ")) != NULL
)
737 (aptr
= ctime (&_Log_on
))[16] = '\0';
738 hrs
= _Log_elpsd
/ 3600;
739 mins
= (_Log_elpsd
%= 3600) / 60;
740 secs
= _Log_elpsd
% 60;
741 (void) fprintf(fp
, "%-8s ", getpwuid (getuid ()) -> pw_name
);
742 (void) fprintf(fp
, "(%4s) %s ", fdig(_Flds
[F_CLASS
]), aptr
);
744 (void) fprintf(fp
, "%2d:%.2d", hrs
, mins
);
746 (void) fprintf(fp
, " %2d", mins
);
747 (void) fprintf(fp
, ":%.2d %s\n", secs
, num
);
759 if (stat (file
, &statb
) == -1 && errno
== ENOENT
)