7812 Remove gender specific language
[unleashed.git] / usr / src / cmd / bnu / ct.c
blobe288464c343e9254bbcf99cb7ee733450c809c4f
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 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
51 * FAIL if it does.
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.
57 #include "uucp.h"
58 #include "sysfiles.h"
59 #include <pwd.h>
60 #include <utmpx.h>
62 #ifdef DATAKIT
63 #include <dk.h>
64 extern int dkminor();
65 #endif
67 #define ROOT 0
68 #define SYS 3
69 #define TTYGID (gid_t) 7 /* group id for terminal */
70 #define TTYMOD (mode_t) 0622
71 #define DEV "/dev/"
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"
77 #define TRUE 1
78 #define FALSE 0
80 static
81 int _Status; /* exit status of child */
83 static
84 pid_t _Pid = 0; /* process id of child */
86 static
87 char
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 */
92 static
93 char
94 *_Num, /* pointer to a phone number */
95 *_Flds[7]; /* Filled in as if finds() in uucp did it */
97 static
98 time_t _Log_on,
99 _Log_elpsd;
101 static
102 FILE *_Fdl;
104 extern int optind;
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
119 /*VARARGS*/
120 /*ARGSUSED*/
121 void
122 assert (s1, s2, i1, s3, i2)
123 char *s1, *s2, *s3;
124 int i1, i2;
125 { } /* for ASSERT in gnamef.c */
127 /*ARGSUSED*/
128 void
129 logent (s1, s2)
130 char *s1, *s2;
131 { } /* so we can load ulockf() */
133 jmp_buf Sjbuf; /* used by uucp routines */
136 main (argc, argv)
137 int argc;
138 char *argv[];
140 int c;
141 int found = 0,
142 errors = 0,
143 first = TRUE;
144 int count,
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 */
148 int fdl;
149 struct termio termio;
150 typedef void (*save_sig)();
151 save_sig save_hup,
152 save_quit,
153 save_int;
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");
163 setservice("cu");
164 if ( sysaccess(EACCESS_DEVICES) != 0 ) {
165 (void) fprintf(stderr, "ct: can't access Devices file\n");
166 cleanup(101);
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 */
176 _Flds[6] = NULL;
178 while ((c = getopt (argc, argv, "hvw:s:x:")) != EOF) {
179 switch (c) {
180 case 'h':
181 hangup = 0;
182 break;
184 case 'v':
185 Verbose = 1;
186 break;
188 case 'w':
189 minutes = atoi (optarg);
190 if (minutes < 1) {
191 (void) fprintf(stderr,
192 "\tusage: %s %s\n", Progname, USAGE);
193 (void) fprintf(stderr, "(-w %s) Wait time must be > 0\n",
194 optarg);
195 cleanup(101);
197 break;
199 case 's':
200 _Flds[F_CLASS] = optarg;
201 break;
203 case 'x':
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",
209 optarg);
210 cleanup(101);
212 break;
214 case '?':
215 (void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
216 cleanup(101);
217 /* NOTREACHED */
221 if (optind == argc) {
222 (void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
223 (void) fprintf(stderr, "No phone numbers specified!\n");
224 cleanup(101);
227 /* check for valid phone number(s) */
228 for (count = argc - 1; count >= optind; --count) {
229 _Num = argv[count];
230 if (strlen(_Num) >= (size_t)(TELNOSIZE - 1)) {
231 (void) fprintf(stderr, "ct: phone number too long -- %s\n", _Num);
232 ++errors;
234 if ((int)strspn(_Num, LEGAL) < (int)strlen(_Num)) {
235 (void) fprintf(stderr, "ct: bad phone number -- %s\n", _Num);
236 ++errors;
239 if (errors)
240 cleanup(101);
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",
251 _Flds[F_CLASS]);
252 break;
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]) );
257 cleanup(101);
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) {
264 sleep(60);
265 continue;
267 /* This is the end of the loop - no time left */
268 break;
271 /**************************************************************/
272 /* First time through loop - get wait minutes if no -w option */
273 /**************************************************************/
274 first = FALSE;
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" : "") );
280 sleep(60);
281 continue;
284 if (!isatty(0) ) { /* not a terminal - get out */
285 cleanup(101);
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')
291 cleanup(101);
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')
300 if (minutes <= 0)
301 cleanup(101);
303 (void) fputs ("Waiting for dialer\n", stdout);
304 sleep(60);
305 continue;
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);
315 cleanup(101);
318 (void) signal(SIGHUP, SIG_IGN);
319 /* found a dialer. now try to call */
320 if (!isatty(0))
321 hangup = 0;
323 if (hangup) { /* -h option not specified */
324 do {
325 (void) fputs ("Confirm hang-up? (y/n): ", stdout);
326 switch (c=tolower(getchar())) {
327 case EOF:
328 case 'n':
329 cleanup(101);
330 break;
331 case 'y':
332 break;
333 default:
334 while ( c != EOF && c != '\n' )
335 c=getchar();
336 break;
338 } while (c != 'y');
340 /* close stderr if it is not redirected */
341 if ( isatty(2) ) {
342 Verbose = 0;
343 Debug = 0;
344 (void) close (2);
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 */
350 (void) sleep (5);
352 (void) close(0);
353 (void) close(1);
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 */
360 devreset();
361 fdl = getto(_Flds);
362 if (fdl >= 0) {
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! */
370 rmlock(CNULL);
372 _Fdl = fdopen(fdl, "r+");
373 (void) sprintf(_Tty, "%s%s", DEV, Dc);
374 /* NOTE: Dc is set in the caller routines */
375 break;
379 /* check why the loop ended (connected or no more numbers to try) */
380 if (count == argc)
381 cleanup(101);
383 /****** Successfully made connection ******/
384 VERBOSE("Connected\n%s", "");
386 #ifdef DATAKIT
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",
392 _Tty, errno);
393 cleanup(101);
396 #endif
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);
402 else
403 (void) signal (SIGHUP, disconnect);
405 if (save_quit == SIG_IGN)
406 (void) signal (SIGQUIT, SIG_IGN);
407 else
408 (void) signal (SIGQUIT, disconnect);
410 if (save_int == SIG_IGN)
411 (void) signal (SIGINT, SIG_IGN);
412 else
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,
426 * Then exit.
428 if (logprocflag) { /* there is a login process on the line */
429 (void) fputs("Hit carriage return ", _Fdl);
430 (void) fclose(_Fdl);
431 CDEBUG(4, "there is a login process; exit\n%s", "");
432 exit(0);
435 CDEBUG(4, "start login process (%s ", TTYMON);
436 CDEBUG(4, "-g -h -t 60 -l %s)\n", fdig(_Flds[F_CLASS]));
437 for (;;) {
438 pid_t w_ret;
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);
443 cleanup(101);
444 /*NOTREACHED*/
446 case 0: /* child process */
447 startat ();
448 (void) close(2);
449 /* ttymon will use open fd 0 for connection */
450 if ( fdl != 0 ) {
451 (void) close(0);
452 dup(fdl);
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);
457 /* exec failed */
458 cleanup(101);
459 /*NOTREACHED*/
461 default: /* parent process */
462 break;
465 /* 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);
470 cleanup(101);
472 if ((_Status & 0xff00) < 0) {
473 if (!hangup)
474 VERBOSE("ct: can't exec login process\n%s", "");
475 cleanup(101);
478 stopat(_Flds[F_PHONE]);
480 rewind (_Fdl); /* flush line */
481 (void) fputs ("\nReconnect? ", _Fdl);
483 rewind (_Fdl);
484 (void) alarm (20);
485 c = getc (_Fdl);
487 if (c == EOF || tolower (c) == 'n')
488 disconnect (0); /* normal disconnect */
489 while ( (c = getc(_Fdl)) != EOF && c != '\n')
491 (void) alarm (0);
495 static void
496 disconnect (code)
498 struct termio termio;
500 (void) alarm(0);
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]);
521 cleanup(code);
525 * clean and exit with "code" status
527 void
528 cleanup (code)
529 int code;
531 CDEBUG(5, "cleanup(%d)\n", code);
532 rmlock (CNULL);
533 if (*_Tty != '\0') {
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);
548 exit (code);
551 /* gdev()
552 * Find an available line with a dialer on it.
553 * Set a temporary lock file for the line.
554 * Return:
555 * >0 - got a dialer
556 * <0 - failed - return the number of possible dialers
557 * 0 - not dialers of requested class on the system.
560 static int
561 gdev (flds)
562 char *flds[];
564 int count = 0;
565 extern void devreset();
567 devreset();
568 while (rddev ("ACU", _Dev, _Devbuf, D_MAX) != FAIL) {
569 /* check caller type */
570 if (!EQUALS (flds[F_TYPE] /* "ACU" */, _Dev[D_TYPE]))
571 continue;
572 /* check class, check (and possibly set) speed */
573 if (!EQUALS (flds[F_CLASS] /* speed */, _Dev[D_CLASS]))
574 continue;
575 count++;
577 if (fn_cklock(_Dev[D_LINE]) == FAIL)
578 continue;
580 /* found available dialer and set temporary lock */
581 return (count);
584 return (- count);
588 * Check if there is a login process active on this line.
589 * Return:
590 * 0 - there is no login process on this line
591 * 1 - found a login process on this line
594 static int
595 logproc(line)
596 char *line;
598 struct utmpx *u;
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", "");
612 return(1);
616 return(0);
620 * Create an entry in utmpx file if one does not already exist.
622 static void
623 startat ()
625 struct utmpx utmpxbuf, *u;
626 int fd;
628 /* Set up the prototype for the utmpx structure we want to write. */
630 u = &utmpxbuf;
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. */
636 u -> ut_id[0] = 'c';
637 u -> ut_id[1] = 't';
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. */
655 pututxline (u);
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 */
659 /* in file. */
661 if ((fd = open(WTMPX_FILE, O_WRONLY | O_APPEND)) != -1) {
662 (void) write(fd, u, sizeof(*u));
663 (void) close(fd);
665 endutxent ();
666 return;
670 * Change utmpx file entry to "dead".
671 * Make entry in ct log.
674 static void
675 stopat (num)
676 char *num;
678 struct utmpx utmpxbuf, *u;
679 int fd;
680 FILE * fp;
682 /* Set up the prototype for the utmpx structure we want to write. */
684 setutxent();
685 u = &utmpxbuf;
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. */
691 u -> ut_id[0] = 'c';
692 u -> ut_id[1] = 't';
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 */
699 /* copy it back. */
701 if (u = getutxid (u)) {
702 utmpxbuf = *u;
703 u = &utmpxbuf;
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. */
713 pututxline (u);
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 */
717 /* in file. */
719 if ((fd = open(WTMPX_FILE, O_WRONLY | O_APPEND)) != -1) {
720 (void) write(fd, u, sizeof(*u));
721 (void) close(fd);
723 endutxent ();
725 /* Do the log accounting */
727 if (exists (LOG) && (fp = fopen (LOG, "a")) != NULL) {
728 char *aptr;
729 int hrs,
730 mins,
731 secs;
733 /* ignore user set TZ for logfile purposes */
734 if ( (aptr = getenv ("TZ")) != NULL )
735 *aptr = '\0';
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);
743 if (hrs)
744 (void) fprintf(fp, "%2d:%.2d", hrs, mins);
745 else
746 (void) fprintf(fp, " %2d", mins);
747 (void) fprintf(fp, ":%.2d %s\n", secs, num);
748 (void) fclose (fp);
750 return;
753 static int
754 exists (file)
755 char *file;
757 struct stat statb;
759 if (stat (file, &statb) == -1 && errno == ENOENT)
760 return (0);
761 return (1);
764 static void
765 zero (adr, size)
766 char *adr;
767 int size;
769 while (size--)
770 *adr++ = '\0';
771 return;