2 * Chat -- a program for automatic session establishment (i.e. dial
3 * the phone and log in).
5 * This software is in the public domain.
7 * Please send all bug reports, requests for information, etc. to:
9 * Al Longyear (longyear@netcom.com)
10 * (I was the last person to change this code.)
12 * The original author is:
14 * Karl Fox <karl@MorningStar.Com>
15 * Morning Star Technologies, Inc.
21 static char rcsid
[] = "$Id: chat.c,v 1.4 1994/05/30 00:30:37 paulus Exp $";
29 #include <sys/types.h>
39 # if defined(SUNOS) && SUNOS >= 41
67 /*************** Micro getopt() *********************************************/
68 #define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
69 (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
70 &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
71 #define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
72 (_O=4,(char*)0):(char*)0)
73 #define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
74 #define ARG(c,v) (c?(--c,*v++):(char*)0)
76 static int _O
= 0; /* Internal state */
77 /*************** Micro getopt() *********************************************/
84 # define LOCK_DIR "/var/spool/lock"
88 # define LOCK_DIR "/usr/spool/locks"
90 # define LOCK_DIR "/usr/spool/uucp"
96 #define DEFAULT_CHAT_TIMEOUT 45
100 char *lock_file
= (char *)0;
101 char *chat_file
= (char *)0;
102 int timeout
= DEFAULT_CHAT_TIMEOUT
;
104 int have_tty_parameters
= 0;
106 struct termio saved_tty_parameters
;
109 struct termios saved_tty_parameters
;
112 char *abort_string
[MAX_ABORTS
], *fail_reason
= (char *)0,
114 int n_aborts
= 0, abort_next
= 0, timeout_next
= 0;
116 void *dup_mem
__P((void *b
, size_t c
));
117 void *copy_of
__P((char *s
));
118 void usage
__P((void));
119 void logf
__P((const char *str
));
120 void logflush
__P((void));
121 void fatal
__P((const char *msg
));
122 void sysfatal
__P((const char *msg
));
123 SIGTYPE sigalrm
__P((int signo
));
124 SIGTYPE sigint
__P((int signo
));
125 SIGTYPE sigterm
__P((int signo
));
126 SIGTYPE sighup
__P((int signo
));
127 void unalarm
__P((void));
128 void init
__P((void));
129 void set_tty_parameters
__P((void));
130 void break_sequence
__P((void));
131 void terminate
__P((int status
));
132 void do_file
__P((char *chat_file
));
133 void lock
__P((void));
134 void delay
__P((void));
135 int get_string
__P((register char *string
));
136 int put_string
__P((register char *s
));
137 int write_char
__P((int c
));
138 int put_char
__P((char c
));
139 int get_char
__P((void));
140 void chat_send
__P((register char *s
));
141 char *character
__P((char c
));
142 void chat_expect
__P((register char *s
));
143 char *clean
__P((register char *s
, int sending
));
144 void unlock
__P((void));
145 void lock
__P((void));
146 void break_sequence
__P((void));
147 void terminate
__P((int status
));
148 void die
__P((void));
154 void *ans
= malloc (c
);
156 fatal ("memory error!\n");
164 return dup_mem (s
, strlen (s
) + 1);
168 * chat [ -v ] [ -t timeout ] [ -l lock-file ] [ -f chat-file ] \
169 * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
171 * Perform a UUCP-dialer-like chat script on stdin and stdout.
181 program_name
= *argv
;
183 while (option
= OPTION(argc
, argv
))
191 if (arg
= OPTARG(argc
, argv
))
192 chat_file
= copy_of(arg
);
199 if (arg
= OPTARG(argc
, argv
))
200 lock_file
= copy_of(arg
);
207 if (arg
= OPTARG(argc
, argv
))
219 openlog("chat", LOG_PID
);
221 openlog("chat", LOG_PID
| LOG_NDELAY
, LOG_LOCAL2
);
224 setlogmask(LOG_UPTO(LOG_INFO
));
226 setlogmask(LOG_UPTO(LOG_WARNING
));
232 if (chat_file
!= NULL
)
234 arg
= ARG(argc
, argv
);
242 while (arg
= ARG(argc
, argv
))
246 if (arg
= ARG(argc
, argv
))
255 * Process a chat script when read from a file.
258 void do_file (chat_file
)
261 int linect
, len
, sendflg
;
262 char *sp
, *arg
, quote
;
266 if ((cfp
= fopen (chat_file
, "r")) == NULL
)
268 syslog (LOG_ERR
, "%s -- open failed: %m", chat_file
);
275 while (fgets(buf
, STR_LEN
, cfp
) != NULL
)
277 sp
= strchr (buf
, '\n');
285 if (*sp
== ' ' || *sp
== '\t')
291 if (*sp
== '"' || *sp
== '\'')
299 syslog (LOG_ERR
, "unterminated quote (line %d)",
312 while (*sp
!= '\0' && *sp
!= ' ' && *sp
!= '\t')
334 * We got an error parsing the command line.
339 Usage: %s [-v] [-l lock-file] [-t timeout] {-f chat-file || chat-script}\n",
350 p
= line
+ strlen(line
);
353 if (str
[strlen(str
)-1] == '\n')
355 syslog (LOG_INFO
, "%s", line
);
364 syslog(LOG_INFO
, "%s", line
);
370 * Unlock and terminate with an error.
379 * Print an error message and terminate.
385 syslog(LOG_ERR
, "%s", msg
);
391 * Print an error message along with the system error message and
398 syslog(LOG_ERR
, "%s: %m", msg
);
405 SIGTYPE
sigalrm(signo
)
411 alarmed
= 1; /* Reset alarm to avoid race window */
412 signal(SIGALRM
, sigalrm
); /* that can cause hanging in read() */
415 if ((flags
= fcntl(0, F_GETFL
, 0)) == -1)
416 sysfatal("Can't get file mode flags on stdin");
418 if (fcntl(0, F_SETFL
, flags
| FNDELAY
) == -1)
419 sysfatal("Can't set file mode flags on stdin");
423 syslog(LOG_INFO
, "alarm");
431 if ((flags
= fcntl(0, F_GETFL
, 0)) == -1)
432 sysfatal("Can't get file mode flags on stdin");
434 if (fcntl(0, F_SETFL
, flags
& ~FNDELAY
) == -1)
435 sysfatal("Can't set file mode flags on stdin");
438 SIGTYPE
sigint(signo
)
444 SIGTYPE
sigterm(signo
)
450 SIGTYPE
sighup(signo
)
458 signal(SIGINT
, sigint
);
459 signal(SIGTERM
, sigterm
);
460 signal(SIGHUP
, sighup
);
465 set_tty_parameters();
466 signal(SIGALRM
, sigalrm
);
471 void set_tty_parameters()
476 if (ioctl(0, TCGETA
, &t
) < 0)
477 sysfatal("Can't get terminal parameters");
482 if (tcgetattr(0, &t
) < 0)
483 sysfatal("Can't get terminal parameters");
486 saved_tty_parameters
= t
;
487 have_tty_parameters
= 1;
489 t
.c_iflag
|= IGNBRK
| ISTRIP
| IGNPAR
;
492 t
.c_cc
[VERASE
] = t
.c_cc
[VKILL
] = 0;
497 if (ioctl(0, TCSETA
, &t
) < 0)
498 sysfatal("Can't set terminal parameters");
501 if (tcsetattr(0, TCSANOW
, &t
) < 0)
502 sysfatal("Can't set terminal parameters");
506 void break_sequence()
513 void terminate(status
)
516 if (have_tty_parameters
&&
518 ioctl(0, TCSETA
, &saved_tty_parameters
) < 0
521 tcsetattr(0, TCSANOW
, &saved_tty_parameters
) < 0
524 syslog(LOG_ERR
, "Can't restore terminal parameters: %m");
532 * Create a lock file for the named lock device
538 char hdb_lock_buffer
[12];
541 lock_file
= strcat(strcat(strcpy(malloc(strlen(LOCK_DIR
)
542 + 1 + strlen(lock_file
) + 1),
543 LOCK_DIR
), "/"), lock_file
);
545 if ((fd
= open(lock_file
, O_EXCL
| O_CREAT
| O_RDWR
, 0644)) < 0)
548 lock_file
= (char *)0; /* Don't remove someone else's lock file! */
549 syslog(LOG_ERR
, "Can't get lock file '%s': %m", s
);
554 sprintf(hdb_lock_buffer
, "%10d\n", getpid());
555 write(fd
, hdb_lock_buffer
, 11);
558 write(fd
, &pid
, sizeof pid
);
565 * Remove our lockfile
572 lock_file
= (char *)0;
577 * 'Clean up' this string.
579 char *clean(s
, sending
)
583 char temp
[STR_LEN
], cur_chr
;
585 int add_return
= sending
;
586 #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
630 if (sending
&& *s
== '\0')
677 if (isoctal (cur_chr
))
683 cur_chr
|= *s
++ - '0';
687 cur_chr
|= *s
++ - '0';
691 if (cur_chr
!= 0 || sending
)
693 if (sending
&& (cur_chr
== '\\' || cur_chr
== 0))
710 *s1
++ = '\0'; /* guarantee closure */
711 *s1
++ = '\0'; /* terminate the string */
712 return dup_mem (temp
, (size_t) (s1
- temp
)); /* may have embedded nuls */
716 * Process the expect string
721 if (strcmp(s
, "ABORT") == 0)
727 if (strcmp(s
, "TIMEOUT") == 0)
735 register char *hyphen
;
737 for (hyphen
= s
; *hyphen
; ++hyphen
)
739 if (hyphen
== s
|| hyphen
[-1] != '\\')
752 for (hyphen
= s
; *hyphen
; ++hyphen
)
754 if (hyphen
== s
|| hyphen
[-1] != '\\')
777 syslog(LOG_INFO
, "Failed (%s)", fail_reason
);
779 syslog(LOG_INFO
, "Failed");
790 static char string
[10];
793 meta
= (c
& 0x80) ? "M-" : "";
797 sprintf(string
, "%s^%c", meta
, (int)c
+ '@');
800 sprintf(string
, "%s^?", meta
);
802 sprintf(string
, "%s%c", meta
, c
);
808 * process the reply string
819 if (n_aborts
>= MAX_ABORTS
)
820 fatal("Too many ABORT strings");
824 if (strlen(s1
) > strlen(s
) || strlen(s1
) > sizeof fail_buffer
- 1)
826 syslog(LOG_WARNING
, "Illegal or too-long ABORT string ('%s')", s
);
830 abort_string
[n_aborts
++] = s1
;
836 for (s1
= s
; *s1
; ++s1
)
837 logf(character(*s1
));
849 timeout
= DEFAULT_CHAT_TIMEOUT
;
853 syslog(LOG_INFO
, "timeout set to %d seconds", timeout
);
858 if (strcmp(s
, "EOT") == 0)
861 if (strcmp(s
, "BREAK") == 0)
863 if ( ! put_string(s
))
865 syslog(LOG_INFO
, "Failed");
877 status
= read(0, &c
, 1);
882 return ((int)c
& 0x7F);
885 syslog(LOG_WARNING
, "warning: read() on stdin returned %d",
889 if ((status
= fcntl(0, F_GETFL
, 0)) == -1)
890 sysfatal("Can't get file mode flags on stdin");
892 if (fcntl(0, F_SETFL
, status
& ~FNDELAY
) == -1)
893 sysfatal("Can't set file mode flags on stdin");
906 status
= write(1, &c
, 1);
914 syslog(LOG_WARNING
, "warning: write() on stdout returned %d",
918 if ((status
= fcntl(0, F_GETFL
, 0)) == -1)
919 sysfatal("Can't get file mode flags on stdin");
921 if (fcntl(0, F_SETFL
, status
& ~FNDELAY
) == -1)
922 sysfatal("Can't set file mode flags on stdin");
931 if (alarmed
|| put_char(c
) < 0)
935 alarm(0); alarmed
= 0;
939 if (errno
== EINTR
|| errno
== EWOULDBLOCK
)
940 syslog(LOG_INFO
, " -- write timed out");
942 syslog(LOG_INFO
, " -- write failed: %m");
962 register char *s1
= s
;
964 for (s1
= s
; *s1
; ++s1
)
965 logf(character(*s1
));
971 alarm(timeout
); alarmed
= 0;
975 register char c
= *s
++;
996 usleep(10000); /* 1/100th of a second. */
1000 if (!write_char (c
))
1012 * 'Wait for' this string to appear on this file descriptor.
1014 int get_string(string
)
1015 register char *string
;
1018 int c
, printed
= 0, len
, minlen
;
1019 register char *s
= temp
, *end
= s
+ STR_LEN
;
1021 fail_reason
= (char *)0;
1022 string
= clean(string
, 0);
1023 len
= strlen(string
);
1024 minlen
= (len
> sizeof(fail_buffer
)? len
: sizeof(fail_buffer
)) - 1;
1032 for (s1
= string
; *s1
; ++s1
)
1033 logf(character(*s1
));
1040 syslog(LOG_INFO
, "expect string is too long");
1048 syslog(LOG_INFO
, "got it");
1054 alarm(timeout
); alarmed
= 0;
1056 while ( ! alarmed
&& (c
= get_char()) >= 0)
1070 if (s
- temp
>= len
&&
1071 c
== string
[len
- 1] &&
1072 strncmp(s
- len
, string
, len
) == 0)
1076 logf(" -- got it\n");
1079 alarm(0); alarmed
= 0;
1083 for (n
= 0; n
< n_aborts
; ++n
)
1084 if (s
- temp
>= (abort_len
= strlen(abort_string
[n
])) &&
1085 strncmp(s
- abort_len
, abort_string
[n
], abort_len
) == 0)
1089 logf(" -- failed\n");
1092 alarm(0); alarmed
= 0;
1093 strcpy(fail_reason
= fail_buffer
, abort_string
[n
]);
1099 strncpy(temp
, s
- minlen
, minlen
);
1103 if (alarmed
&& verbose
)
1104 syslog(LOG_WARNING
, "warning: alarm synchronization problem");
1109 if (verbose
&& printed
)
1112 logf(" -- read timed out\n");
1116 syslog(LOG_INFO
, " -- read failed: %m");
1126 #include <sys/types.h>
1127 #include <sys/time.h>
1130 usleep -- support routine for 4.2BSD system call emulations
1131 last edit: 29-Oct-1984 D A Gwyn
1134 extern int select();
1137 usleep( usec
) /* returns 0 if ok, else -1 */
1138 long usec
; /* delay in microseconds */
1140 static struct /* `timeval' */
1142 long tv_sec
; /* seconds */
1143 long tv_usec
; /* microsecs */
1144 } delay
; /* _select() timeout */
1146 delay
.tv_sec
= usec
/ 1000000L;
1147 delay
.tv_usec
= usec
% 1000000L;
1149 return select( 0, (long *)0, (long *)0, (long *)0, &delay
);
1154 * Delay an amount appropriate for between typed characters.
1161 for (i
= 0; i
< 30000; ++i
) /* ... did we just say appropriate? */
1163 # else /* NO_USLEEP */
1165 # endif /* NO_USLEEP */