source: trunk/third/kermit/ckuscr.c @ 10780

Revision 10780, 17.6 KB checked in by brlewis, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r10779, which included commits to RCS files with non-trunk default branches.
Line 
1#include "ckcsym.h"
2#ifdef NOLOCAL
3char *loginv = "";
4#else
5#ifndef NOICP
6#ifndef NOSCRIPT
7char *loginv = "Script Command, 6.0.028, 8 Feb 96";
8
9/*  C K U S C R  --  Login script for logging onto remote system */
10
11/*
12  Copyright (C) 1985, 1996, Trustees of Columbia University in the City of New
13  York.  The C-Kermit software may not be, in whole or in part, licensed or
14  sold for profit as a software product itself, nor may it be included in or
15  distributed with commercial products or otherwise distributed by commercial
16  concerns to their clients or customers without written permission of the
17  Office of Kermit Development and Distribution, Columbia University.  This
18  copyright notice must not be removed, altered, or obscured.
19
20  Original (version 1, 1985) author: Herm Fischer, Encino, CA.
21  Contributed to Columbia University in 1985 for inclusion in C-Kermit 4.0.
22  Author and maintainer since 1985: Frank da Cruz, Columbia University,
23  fdc@columbia.edu.
24*/
25
26/*
27  The module takes a UUCP-style script of the "expect send [expect send] ..."
28  format.  It is intended to operate similarly to the way the common
29  UUCP L.sys login entries work.  Conditional responses are supported:
30  expect[-send-expect[...]], as with UUCP.  The send keyword EOT sends a
31  Control-d, and the keyword BREAK sends a break.  Letters prefixed
32  by '~' are '~b' backspace, '~s' space, '~n' linefeed, '~r' return, '~x' xon,
33  '~t' tab, '~q' ? (not allowed on kermit command lines), '~' ~, '~'',
34  '~"', '~c' don't append return, '~o[o[o]]' octal character.  As with
35  some uucp systems, sent strings are followed by ~r (not ~n) unless they
36  end with ~c. Null expect strings (e.g., ~0 or --) cause a short
37  delay, and are useful for sending sequences requiring slight pauses.
38
39  This module calls externally defined system-dependent functions for
40  communications i/o, as defined in CKCPLM.DOC, the C-Kermit Program Logic
41  Manual, and thus should be portable to all systems that implement those
42  functions, and where alarm() and signal() work as they do in UNIX.
43*/
44#include "ckcdeb.h"
45#include <signal.h>
46#ifdef NT
47#include <setjmpex.h>
48#else /* NT */
49#include <setjmp.h>
50#endif /* NT */
51#include "ckcasc.h"
52#include "ckcker.h"
53#include "ckuusr.h"
54#include "ckcnet.h"
55#include "ckcsig.h"
56
57_PROTOTYP( VOID flushi, (void) );
58_PROTOTYP( static VOID myflsh, (void) );
59_PROTOTYP( static int sequenc, (void) );
60_PROTOTYP( static VOID recvseq, (void) );
61_PROTOTYP( static int outseq, (void) );
62
63#ifdef MAC
64#define signal msignal
65#define SIGTYP long
66#define alarm malarm
67#define SIG_IGN 0
68#define SIGALRM 1
69#define SIGINT  2
70SIGTYP (*msignal(int type, SIGTYP (*func)(int)))(int);
71#endif /* MAC */
72
73#ifdef AMIGA
74#define signal asignal
75#define alarm aalarm
76#define SIGALRM (_NUMSIG+1)
77#define SIGTYP void
78SIGTYP (*asignal(int type, SIGTYP (*func)(int)))(int);
79unsigned aalarm(unsigned);
80#endif /* AMIGA */
81
82#ifdef STRATUS
83/* VOS doesn't have alarm(), but it does have some things we can work with. */
84/* however, we have to catch all the signals in one place to do this, so    */
85/* we intercept the signal() routine and call it from our own replacement.  */
86#define signal vsignal
87#define alarm valarm
88SIGTYP (*vsignal(int type, SIGTYP (*func)(int)))(int);
89int valarm(int interval);
90#endif /* STRATUS */
91
92extern int sessft;
93extern int local, flow, seslog, mdmtyp, msgflg, duplex, backgrd, secho, quiet;
94#ifdef NETCONN
95extern int network, ttnproto;
96#endif /* NETCONN */
97extern long speed;
98extern char ttname[];
99
100#ifdef NTSIG
101extern int TlsIndex;
102#endif /* NTSIG */
103
104#ifndef NOSPL
105#ifdef DCMDBUF
106extern struct cmdptr *cmdstk;
107#else
108extern struct cmdptr cmdstk[];
109#endif /* DCMDBUF */
110extern int techo, cmdlvl;
111extern int mecho;
112#endif /* NOSPL */
113
114static int scr_echo;                    /* Whether to echo script commands */
115
116static int exp_alrm = 15;               /* Time to wait for expect string */
117#define SND_ALRM 15                     /* Time to allow for sending string */
118#define NULL_EXP 2                      /* Time to pause on null expect strg*/
119#define DEL_MSEC 300                    /* Milliseconds to pause on ~d */
120
121#define SBUFL 512               
122static char seq_buf[SBUFL], *s;         /* expect-send sequence buffer */
123static int got_it, no_cr;
124
125/*  Connect state parent/child communication signal handlers */
126
127#ifdef COMMENT
128#ifdef CK_POSIX_SIG
129static sigjmp_buf alrmrng;
130#else
131static jmp_buf alrmrng;
132#endif /* CK_POSIX_SIG */
133#else
134static ckjmpbuf alrmrng;
135#endif /* COMMENT */
136
137static SIGTYP
138#ifdef CK_ANSIC
139scrtime(int foo)                        /* modem read failure handler, */
140#else
141scrtime(foo) int foo;                   /* Alarm handler */
142#endif /* CK_ANSIC */
143/* scrtime */ {
144
145#ifdef BEBOX
146    alarm_expired();
147#endif /* BEBOX */
148#ifdef NTSIG
149    if (foo == SIGALRM)
150      PostAlarmSigSem();
151    else
152      PostCtrlCSem();
153#else /* NTSIG */
154#ifdef NT
155    cklongjmp(ckjaddr(alrmrng),1);
156#else /* NT */
157    cklongjmp(alrmrng,1);
158#endif /* NT */
159#endif /* NTSIG */
160    SIGRETURN;
161}
162
163/*
164 Sequence interpreter -- pick up next sequence from command string,
165 decode escapes and place into seq_buf.
166
167 If string contains a ~d (delay) then sequenc() returns a 1 expecting
168 to be called again after the ~d executes.
169*/
170static int
171sequenc() {
172    int i;
173    char c, oct_char;
174
175    no_cr = 0;                          /* output needs cr appended */
176    for (i = 0; i < SBUFL; ) {         
177        if (*s == '\0' || *s == '-' || isspace(*s) ) { /* done */
178            seq_buf[i] = '\0';
179            return(0) ;
180        }
181        if (*s == '~') {                /* escape character */
182            s++;
183            switch (c = *s) {
184                case 'n':  seq_buf[i++] = LF; break;
185                case 'r':  seq_buf[i++] = CR; break;
186                case 't':  seq_buf[i++] = '\t'; break;
187                case 'b':  seq_buf[i++] = '\b'; break;
188                case 'q':  seq_buf[i++] = '?';  break;
189#ifdef COMMENT
190/* The default case should catch these now... */
191                case '~':  seq_buf[i++] = '~';  break;
192                case '-':  seq_buf[i++] = '-';  break;
193#endif /* COMMENT */
194                case '\'': seq_buf[i++] = '\''; break;
195                case '\"': seq_buf[i++] = '\"'; break;
196                case 's':  seq_buf[i++] = ' ';  break;
197                case 'x':  seq_buf[i++] = '\021'; break;
198                case 'c':  no_cr = 1; break;
199                case 'd': {                     /* send what we have & then */
200                    seq_buf[i] = '\0';          /* expect to send rest after */
201                    no_cr = 1;                  /* sender delays a little */
202                    s++;
203                    return(1);
204                }
205                case 'w': {                     /* wait count */
206                    exp_alrm = 15;              /* default to 15 sec */
207                    if (isdigit(*(s+1))) {
208                        s++;
209                        exp_alrm = *s & 15;
210                        if (isdigit(*(s+1)) ) {
211                            s++;
212                            exp_alrm = exp_alrm * 10 + (*s & 15);
213                        }
214                    }
215                    break;
216                }
217                default:
218                    if ( isdigit(c) ) {         /* octal character */
219                        oct_char = (char) (c & 7); /* most significant digit */
220                        if (isdigit( *(s+1) ) ) {
221                            s++;
222                            oct_char = (char) ((oct_char<<3) | ( *s & 7 ));
223                            if (isdigit( *(s+1) ) ) {
224                                s++;
225                                oct_char = (char) ((oct_char<<3) | ( *s & 7 ));
226                            }
227                        }
228                        seq_buf[i++] = oct_char;
229                        break;
230                    } else seq_buf[i++] = *s; /* Treat ~ as quote */
231              }
232        } else seq_buf[i++] = *s;       /* Plain old character */
233        s++;
234    }
235    seq_buf[i] = '\0';
236    return(0);                          /* end of space, return anyway */
237}
238
239
240/* Output buffering for "recvseq" and "flushi" */
241
242#define MAXBURST 256            /* maximum size of input burst */
243static CHAR conbuf[MAXBURST];   /* buffer to hold output for console */
244static int concnt = 0;          /* number of characters buffered */
245static CHAR sesbuf[MAXBURST];   /* buffer to hold output for session log */
246static int sescnt = 0;          /* number of characters buffered */
247
248static VOID
249myflsh() {
250    if (concnt > 0) {
251        conxo(concnt, (char *) conbuf);
252        concnt = 0;
253    }
254    if (sescnt > 0) {
255        if (zsoutx(ZSFILE, (char *) sesbuf, sescnt) < 0) seslog = 0;
256        sescnt = 0;
257    }
258}
259
260/* these variables are used to pass data between the recvseq() */
261/* and the dorseq().  They are necessary because in some versions */
262/* dorseq() is executed in a separate thread and data cannot be */
263/* passed by parameter. */
264
265static char *rseqe, * rseqgot, * rseqtrace ;
266static int rseql;
267
268static SIGTYP
269#ifdef CK_ANSIC
270dorseq(void * threadinfo)
271#else /* CK_ANSIC */
272dorseq(threadinfo) VOID * threadinfo;
273#endif /* CK_ANSIC */
274/* dorseq */ {
275    int i, x;
276    int burst = 0;                      /* chars remaining in input burst */
277
278#ifdef NTSIG
279    if (threadinfo) {                   /* Thread local storage... */
280        TlsSetValue(TlsIndex,threadinfo);
281    }
282#endif /* NTSIG */
283
284    while (!got_it) {
285        for (i = 0; i < rseql-1; i++) rseqgot[i] = rseqgot[i+1];
286        x = ttinc(0);                   /* Read a character */
287        debug(F101,"recvseq","",x);
288        if (x < 0) {
289#ifdef NTSIG
290            ckThreadEnd(threadinfo);
291#endif /* NTSIG */
292            SIGRETURN;                  /* Check for error */
293        }
294#ifdef NETCONN
295#ifdef TNCODE
296/* Check for telnet protocol negotiation */
297        if (network &&
298            (ttnproto == NP_TELNET) &&
299            ( (x & 0xff) == IAC) ) { /* Break from input burst for "tn_doop" */
300            myflsh();
301            burst = 0;
302            switch (tn_doop((CHAR)(x & 0xff),duplex,ttinc)) {
303              case 2: duplex = 0; continue;
304              case 1: duplex = 1;
305              default: continue;
306            }
307        }
308#endif /* TNCODE */
309#endif /* NETCONN */
310        rseqgot[rseql-1] = (char) (x & 0x7f); /* Got a character */
311        burst--;                        /* One less waiting */
312        if (scr_echo) conbuf[concnt++] = rseqgot[rseql-1]; /* Buffer it */
313        if (seslog)                     /* Log it in session log */
314#ifdef UNIX
315          if (sessft != 0 || rseqgot[rseql-1] != '\r')
316#else
317#ifdef OSK
318            if (sessft != 0 || rseqgot[rseql-1] != '\012')
319#endif /* OSK */
320#endif /* UNIX */
321              sesbuf[sescnt++] = rseqgot[rseql-1];
322        if ((int)strlen(rseqtrace) < SBUFL-2 )
323          strcat(rseqtrace,dbchr(rseqgot[rseql-1]));
324        got_it = (!strncmp(rseqe, rseqgot, rseql));
325        if (burst <= 0) {               /* Flush buffered output */
326            myflsh();
327            if ((burst = ttchk()) < 0) { /* Get size of next input burst */
328#ifdef NTSIG
329                ckThreadEnd(threadinfo);
330#endif /* NTSIG */
331                SIGRETURN;
332            }
333            /* prevent overflow of "conbuf" and "sesbuf" */
334            if (burst > MAXBURST)
335              burst = MAXBURST;
336        }
337    }   
338#ifdef NTSIG
339    ckThreadEnd(threadinfo);
340#endif /* NTSIG */
341    SIGRETURN;
342}
343
344static SIGTYP
345#ifdef CK_ANSIC
346failrseq(void * threadinfo)
347#else /* CK_ANSIC */
348failrseq(threadinfo) VOID * threadinfo;
349#endif /* CK_ANSIC */
350/* failrseq */ {
351     got_it = 0;                        /* Timed out here */   
352     SIGRETURN;
353}
354
355/*
356  Receive sequence -- see if expected response comes,
357  return success (or failure) in got_it.
358*/
359static VOID
360recvseq() {
361    char *e, got[7], trace[SBUFL];
362    int i, l;
363   
364    sequenc();
365    l = (int)strlen(e=seq_buf);         /* no more than 7 chars allowed */
366    if (l > 7) {
367        e += l-7;
368        l = 7;
369    }
370    tlog(F111,"expecting sequence",e,(long) l);
371    if (l == 0) {                       /* null sequence, delay a little */
372        sleep (NULL_EXP);
373        got_it = 1;
374        tlog(F100,"got it (null sequence)","",0L);
375        return;
376    }
377    *trace = '\0';
378    for (i = 0; i < 7; i++) got[i]='\0';
379
380    rseqtrace = trace;
381    rseqe = e;
382    rseqgot = got;
383    rseql = l;
384
385    alrm_execute(ckjaddr(alrmrng), exp_alrm, scrtime, dorseq, failrseq);
386
387    tlog(F110,"received sequence: ",trace,0L);
388    tlog(F101,"returning with got-it code","",(long) got_it);
389    myflsh();                           /* Flush buffered output */
390    return;
391}
392
393/*
394 Output A Sequence starting at pointer s,
395 return 0 if okay,
396 1 if failed to read (modem hangup or whatever)
397*/
398static int oseqret = 0;                 /* Return code for outseq */
399                                        /* Out here to prevent clobbering */
400                                        /* by longjmp. */
401
402static SIGTYP
403#ifdef CK_ANSIC
404dooseq(void * threadinfo)
405#else /* CK_ANSIC */
406dooseq(threadinfo) VOID * threadinfo;
407#endif /* CK_ANSIC */
408{
409    int l;
410    char *sb;
411#ifdef TCPSOCKET
412    extern int tn_nlm, tn_b_nlm, me_binary;
413#endif /* TCPSOCKET */
414
415#ifdef NTSIG
416    if (threadinfo) {                   /* Thread local storage... */
417        TlsSetValue(TlsIndex,threadinfo);
418    }
419#endif /* NTSIG */
420
421    l = (int)strlen(seq_buf);
422    tlog(F111,"sending sequence ",seq_buf,(long) l);
423
424    if (!strcmp(seq_buf,"EOT")) {
425        ttoc(dopar('\004'));
426        if (scr_echo) conol("<EOT>");
427        if (seslog && duplex) if (zsout(ZSFILE,"<EOT>") < 0)
428          seslog = 0;
429    } else if (!strcmp(seq_buf,"BREAK") ||
430               !strcmp(seq_buf,"\\b") ||
431               !strcmp(seq_buf,"\\B")) {
432        ttsndb();
433        if (scr_echo) conol("<BREAK>");
434        if (seslog) if (zsout(ZSFILE,"{BREAK}") < 0) seslog = 0;
435    } else {
436        if (l > 0) {
437            for ( sb = seq_buf; *sb; sb++)
438              *sb = dopar(*sb); /* add parity */
439            ttol((CHAR *)seq_buf,l); /* send it */
440            if (scr_echo && duplex) conxo(l,seq_buf);
441            if (seslog && duplex) /* log it */
442              if (zsout(ZSFILE,seq_buf) < 0)
443                seslog=0;
444        }
445        if (!no_cr) {
446            ttoc( dopar(CR) );
447#ifdef TCPSOCKET
448            if (network && ttnproto == NP_TELNET) {
449                if (!me_binary && tn_nlm != TNL_CR)
450                  ttoc((char)((tn_nlm == TNL_CRLF) ?
451                              dopar(LF) : dopar(NUL)));
452                else if (me_binary &&
453                         (tn_b_nlm == TNL_CRLF || tn_b_nlm == TNL_CRNUL))
454                  ttoc((char)((tn_b_nlm == TNL_CRLF) ?
455                              dopar(LF) : dopar(NUL)));
456            }
457#endif /* TCPSOCKET */
458            if (seslog && duplex)
459              if (zchout(ZSFILE,dopar(CR)) < 0)
460                seslog = 0;
461        }
462    }   
463#ifdef NTSIG
464    ckThreadEnd(threadinfo);
465#endif /* NTSIG */
466    SIGRETURN;
467}
468
469SIGTYP
470#ifdef CK_ANSIC
471failoseq(void * threadinfo)
472#else /* CK_ANSIC */
473failoseq(threadinfo) VOID * threadinfo;
474#endif /* CK_ANSIC */
475/* failoseq */ {
476     oseqret = -1;              /* else -- alarm rang */   
477     SIGRETURN;
478}
479
480static int
481outseq() {
482    int delay;
483
484    oseqret = 0;                        /* Initialize return code */
485    while(1) {
486        delay = sequenc(); 
487        alrm_execute( ckjaddr(alrmrng), SND_ALRM, scrtime, dooseq, failoseq ) ;
488
489        if (!delay)
490          return(oseqret);
491#ifndef MAC
492        msleep(DEL_MSEC);               /* delay, loop to next send */
493#endif /* MAC */
494    }
495}
496
497
498/*  L O G I N  --  (historical misnomer) Execute the SCRIPT command */
499
500int
501dologin(cmdstr) char *cmdstr; {
502
503#ifdef OS2
504#ifdef NT
505    SIGTYP (* savealm)(int);            /* Save incoming alarm function */
506#else /* NT */
507    SIGTYP (* volatile savealm)(int);   /* Save incoming alarm function */
508#endif /* NT */
509#else /* OS2 */
510    SIGTYP (*savealm)();                /* Save incoming alarm function */
511#endif /* OS2 */
512    char *e;
513
514    s = cmdstr;                         /* Make global to this module */
515
516    tlog(F100,loginv,"",0L);
517
518    if (speed < 0L) speed = ttgspd();
519    if (ttopen(ttname,&local,mdmtyp,0) < 0) {
520        sprintf(seq_buf,"Sorry, can't open %s",ttname);
521        perror(seq_buf);
522        return(0);
523    }
524    /* Whether to echo script commands ... */
525    scr_echo = (!quiet && !backgrd && secho);
526#ifndef NOSPL
527    if (scr_echo && cmdlvl > 1) {
528        if (cmdstk[cmdlvl].src == CMD_TF)
529          scr_echo = techo;
530        if (cmdstk[cmdlvl].src == CMD_MD)
531          scr_echo = mecho;
532    }
533#endif /* NOSPL */
534    if (scr_echo) {
535#ifdef NETCONN
536        if (network)
537          printf("Executing SCRIPT to host %s.\n",ttname);
538        else
539#endif /* NETCONN */
540          printf("Executing SCRIPT through %s, speed %ld.\n",ttname,speed);
541    }
542    *seq_buf = 0;
543    for (e = s; *e; e++) strcat(seq_buf, dbchr(*e) );
544#ifdef COMMENT
545/* Skip this because it tends to contain a password... */
546    if (scr_echo) printf("SCRIPT string: %s\n",seq_buf);
547#endif /* COMMENT */
548    tlog(F110,"SCRIPT string: ",seq_buf, 0L);
549
550/* Condition console terminal and communication line... */
551
552    if (ttvt(speed,flow) < 0) {
553        printf("Sorry, Can't condition communication line\n");
554        return(0);
555    }
556    /* Save initial timer interrupt value */
557    savealm = signal(SIGALRM,SIG_IGN);
558
559    flushi();                           /* Flush stale input */
560
561/* start expect - send sequence */
562
563    while (*s) {                        /* While not done with buffer */
564
565        while (*s && isspace(*s)) s++;  /* Skip over separating whitespaces */
566                                        /* Gather up expect sequence */
567        got_it = 0;
568        recvseq();
569
570        while (!got_it) {               /* Have it yet? */
571            if (*s++ != '-')            /* No, is there a conditional send? */
572              goto failret;             /* No, return failure */
573            flushi();                   /* Yes, flush out input buffer */
574            if (outseq())               /* If unable to send, */
575              goto failret;             /* return failure. */
576            if (*s++ != '-')            /* If no conditional response here, */
577              goto failret;             /* return failure. */
578            recvseq();                  /* All OK, read response from host. */
579        }                               /* Loop back and check got_it */
580
581        while (*s && !isspace(*s++) ) ; /* Skip over conditionals */
582        while (*s && isspace(*s)) s++;  /* Skip over separating whitespaces */
583        flushi();                       /* Flush */
584        if (*s) if (outseq()) goto failret; /* If any */
585    }
586    signal(SIGALRM,savealm);
587    if (scr_echo) printf("Script successful.\n");
588    tlog(F100,"Script successful.","",0L);
589    return(1);
590
591failret:
592    signal(SIGALRM,savealm);
593    if (scr_echo) printf("Sorry, script failed\n");
594    tlog(F100,"Script failed","",0L);
595    return(0);
596}
597
598/*  F L U S H I  --  Flush, but log, SCRIPT input buffer  */
599
600VOID
601flushi() {
602    int n, x;
603#ifdef NETCONN
604#ifdef TNCODE
605    int is_tn=0;
606#endif /* TNCODE */
607#endif /* NETCONN */
608
609#ifdef TNCODE
610        /* TELNET input must be scanned for IAC */
611        is_tn = (network && (ttnproto == NP_TELNET));
612#endif /* TNCODE */
613    if (
614        seslog                          /* Logging session? */
615        || scr_echo                     /* Or console echoing? */
616#ifdef NETCONN
617#ifdef TNCODE
618        /* TELNET input must be scanned for IAC */
619        || is_tn
620#endif /* TNCODE */
621#endif /* NETCONN */
622        ) {
623        if ((n = ttchk()) < 0)          /* Yes, anything in buffer? */
624          return;
625        if (n > MAXBURST) n = MAXBURST; /* Make sure not too much, */
626        myflsh();                       /* and that buffers are empty. */
627        while (n-- > 0) {
628            x = ttinc(0);               /* Collect a character */
629#ifdef NETCONN
630#ifdef TNCODE
631/* Check for telnet protocol negotiation */
632            if (is_tn && ((x & 0xff) == IAC) ) {
633                myflsh();               /* Sync output */
634                switch (tn_doop((CHAR)(x & 0xff),duplex,ttinc)) {
635                  case 2: duplex = 0; break;
636                  case 1: duplex = 1;
637                  default: break;
638                }
639
640                /* Recalculate flush count */
641                if ((n = ttchk()) < 0)
642                  return;
643                if (n > MAXBURST) n = MAXBURST;
644                continue;
645            }
646#endif /* TNCODE */
647#endif /* NETCONN */
648            if (scr_echo) conbuf[concnt++] = (CHAR) x; /* buffer for console */
649            if (seslog)
650#ifdef UNIX
651              if (sessft != 0 || x != '\r')
652#else
653#ifdef OSK
654              if (sessft != 0 || x != '\012')
655#endif /* OSK */
656#endif /* UNIX */
657                sesbuf[sescnt++] = (CHAR) x; /* buffer for session log */
658        }
659        myflsh();
660    } else ttflui();                    /* Otherwise just flush. */
661}
662
663#else /* NOSCRIPT */
664char *loginv = "Script Command Disabled";
665#endif /* NOSCRIPT */
666#endif /* NOICP */
667#endif /* NOLOCAL */
Note: See TracBrowser for help on using the repository browser.