typedef unsigned char uchar; /* * Embedded Kermit demo, main program. */ /* * Author: Frank da Cruz, the Kermit Project, Columbia University, New * York. Copyright (C) 1995, 2002, Trustees of Columbia University in the * City of New York. All rights reserved. * For license see kermit.c. */ /* * This is a demo/test framework, to be replaced by a real control * program. It includes a simple Unix-style command-line parser to allow * setup and testing of the Kermit module. Skip past all this to where it * says REAL STUFF to see the real stuff. ANSI C required. Note: order * of the following includes is important. */ #ifdef __linux #include // for exit() #include // for access() #include // for isdigit() #include #endif /* __linux */ // handle name clash #ifdef X_OK #undef X_OK #endif #include "cdefs.h" /* Data types for all modules */ #include "debug.h" /* Debugging */ #include "platform.h" /* Platform-specific includes and * definitions */ #include "kermit.h" /* Kermit symbols and data structures */ #include "string.h" /* * Sample prototypes for i/o functions. The functions are defined in a * platform-specific i/o module. The function names are unknown to the * Kermit module. The names can be changed but not their calling * conventions. The following prototypes are keyed to unixio.c. */ int devopen (char *, long baud); /* Communications device/path */ int devsettings (char *); int devrestore (void); int devclose (void); int pktmode (short); int readpkt (struct k_data *, UCHAR *, int); /* Communications * i/o functions */ int tx_data (struct k_data *, UCHAR *, int); int inchk (struct k_data *); int openfile (struct k_data *, UCHAR *, int); /* File i/o * functions */ int writefile (struct k_data *, UCHAR *, int); int readfile (struct k_data *); int closefile (struct k_data *, UCHAR, int); ULONG fileinfo (struct k_data *, UCHAR *, UCHAR *, int, short *, short); /* * External data */ extern UCHAR o_buf[]; /* Must be defined in io.c */ extern UCHAR i_buf[]; /* Must be defined in io.c */ extern int errno; /* * Data global to this module */ struct k_data k; /* Kermit data structure */ struct k_response r; /* Kermit response structure */ char **xargv; /* Global pointer to arg vector */ UCHAR **cmlist = (UCHAR **) 0; /* Pointer to file list */ char *xname = "eksw"; /* Default program name */ int xargc; /* Global argument count */ int nfils = 0; /* Number of files in file list */ int action = 0; /* Send or Receive */ int xmode = 1; /* File-transfer mode */ int ftype = 1; /* Global file type 0=text 1=binary */ int keep = 0; /* Keep incompletely received files */ int sync_server = 0; /* Sync a server with E-pkt */ int dbg = 0; /* Debugging */ short fmode = -1; /* Transfer mode for this file */ int parity = 0; /* Parity */ int check = 3; /* Block check */ int remote = 1; /* 1 = Remote, 0 = Local */ #ifdef DEBUG int errorrate = 0; /* Simulated error rate */ int seed = 1234; /* Random number generator seed */ #endif /* DEBUG */ void doexit (int status) { devrestore (); /* Restore device */ devclose (); /* Close device */ exit (status); /* Exit with indicated status */ } void usage () { fprintf (stderr, "E-Kermit %s\n", VERSION); fprintf (stderr, "Usage: %s \n", xname); fprintf (stderr, "Options:\n"); fprintf (stderr, " -r Receive files\n"); fprintf (stderr, " -g Get files\n"); fprintf (stderr, " -s Send files\n"); fprintf (stderr, " -w # of window slots\n"); fprintf (stderr, " -l packet length\n"); fprintf (stderr, " -p [neoms] Parity: none, even, odd, mark, space\n"); fprintf (stderr, " -b [1235] Block check type: 1, 2, 3 or 5\n"); fprintf (stderr, " -k Keep incompletely received files\n"); fprintf (stderr, " -B Force binary mode\n"); fprintf (stderr, " -T Force text mode\n"); fprintf (stderr, " -R Remote mode (vs local)\n"); fprintf (stderr, " -L Local mode (vs remote)\n"); #ifdef DEBUG fprintf (stderr, " -E Simulated error rate (0-100)\n"); fprintf (stderr, " -d debugging output to \"debug.log\"\n"); #endif /* DEBUG */ fprintf (stderr, " -S Synchronize a kermit server\n"); fprintf (stderr, " -h Help (this message)\n"); doexit (FAILURE); } void static fatal (char *msg1, char *msg2, char *msg3) { /* Not to be called except */ if (msg1) { /* from this module */ fprintf (stderr, "%s: %s", xname, msg1); if (msg2) fprintf (stderr, "%s", msg2); if (msg3) fprintf (stderr, "%s", msg3); fprintf (stderr, "\n"); } doexit (FAILURE); } /* * Simple user interface for testing */ int doarg (char c) { /* Command-line option parser */ int x; /* Parses one option with its arg(s) */ char *xp, *s; struct stat statbuf; xp = *xargv + 1; /* Pointer for bundled args */ while (c) { #ifdef DEBUG if (errorrate) seed += (int) c; #endif /* DEBUG) */ switch (c) { case 'r': /* Receive */ if (action) fatal ("Conflicting actions", (char *) 0, (char *) 0); action = A_RECV; break; case 'g': /* Get */ if (action) fatal ("Conflicting actions", (char *) 0, (char *) 0); if (*(xp + 1)) fatal ("Invalid argument bundling after -g", (char *) 0, (char *) 0); nfils = 0; /* Initialize file counter, flag */ cmlist = (UCHAR **) (xargv + 1); /* Remember this pointer */ while (--xargc > 0) { /* Traverse the list */ xargv++; s = *xargv; #ifdef DEBUG if (errorrate) seed += (int) *s; #endif /* DEBUG) */ if (**xargv == '-') break; errno = 0; nfils++; } xargc++; xargv--; /* Adjust argv/argc */ if (nfils < 1) fatal ("Missing filename for -g", (char *) 0, (char *) 0); action = A_GET; break; case 's': /* Send */ if (action) fatal ("Conflicting actions", (char *) 0, (char *) 0); if (*(xp + 1)) fatal ("Invalid argument bundling after -s", (char *) 0, (char *) 0); nfils = 0; /* Initialize file counter, flag */ cmlist = (UCHAR **) (xargv + 1); /* Remember this pointer */ while (--xargc > 0) { /* Traverse the list */ xargv++; s = *xargv; #ifdef DEBUG if (errorrate) seed += (int) *s; #endif /* DEBUG) */ if (**xargv == '-') break; errno = 0; x = stat (s, &statbuf); if (x < 0) fatal ("File '", s, "' not found"); if (access (s, 4) < 0) fatal ("File '", s, "' not accessible"); nfils++; } xargc++; xargv--; /* Adjust argv/argc */ if (nfils < 1) fatal ("Missing filename for -s", (char *) 0, (char *) 0); action = A_SEND; break; case 'b': /* Block-check type */ #ifdef DEBUG case 'E': /* Simulated error rate */ #endif /* DEBUG */ case 'w': /* max window slots */ case 'l': /* max packet length */ if (*(xp + 1)) fatal ("Invalid argument bundling", (char *) 0, (char *) 0); xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal ("Missing option argument", (char *) 0, (char *) 0); s = *xargv; while (*s) { if (!isdigit (*s)) fatal ("Numeric argument required", (char *) 0, (char *) 0); s++; } if (c == 'b') { check = atoi (*xargv); if ((check < 1 || check > 3) && check != 5) fatal ("Invalid block check", (char *) 0, (char *) 0); } #ifdef DEBUG else if (c == 'E') { errorrate = atoi (*xargv); if (errorrate > 100) fatal ("Invalid error rate", (char *) 0, (char *) 0); } #endif /* DEBUG */ else if (c == 'w') { // maximum number of window slots to negotiate k.wslots_max = atoi (*xargv); if (k.wslots_max == 0) k.wslots_max = 1; if (k.wslots_max < 1 || k.wslots_max > P_WSLOTS) fatal ("Invalid no. of window slots", (char *) 0, (char *) 0); } else if (c == 'l') { int maxlen; // maximum packet lengths maxlen = atoi (*xargv); if (maxlen < 10 || maxlen > P_PKTLEN) fatal ("Invalid packet maxlen", (char *) 0, (char *) 0); k.p_maxlen = maxlen; } break; case 'h': /* Help */ case '?': usage (); case 'B': /* Force binary file transfer */ xmode = 1; /* So no automatic switching */ ftype = BINARY; break; case 'T': /* Force text file transfer */ xmode = 1; /* So no automatic switching */ ftype = TEXT; break; case 'R': /* Tell Kermit it's in remote mode */ remote = 1; break; case 'L': /* Tell Kermit it's in local mode */ remote = 0; break; case 'k': /* Keep incompletely received files */ keep = 1; break; case 'S': /* Synchronize a server by sending E-packet */ sync_server = 1; break; case 'p': /* Parity */ if (*(xp + 1)) fatal ("Invalid argument bundling", (char *) 0, (char *) 0); xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal ("Missing parity", (char *) 0, (char *) 0); switch (x = **xargv) { case 'e': /* Even */ case 'o': /* Odd */ case 'm': /* Mark */ case 's': parity = x; break; /* Space */ case 'n': parity = 0; break; /* None */ default: fatal ("Invalid parity '", *xargv, "'"); } break; #ifdef DEBUG case 'd': dbg++; break; #else case 'd': fprintf (stderr, "ignoring -d flag -- debugging code not present\n"); break; #endif /* DEBUG */ default: /* Anything else */ fatal ("Unknown command-line option ", *xargv, " type 'eksw -h' for help."); } c = *++xp; /* See if options are bundled */ } return (action); } int main (int argc, char **argv) { int loop; int status, rx_len = 0, xact; char c; UCHAR *inbuf; char ttyname[100]; long baud; #if 0 fprintf (stderr, "MAIN Options:\n"); fprintf (stderr, " IBUFLEN=%d\n", IBUFLEN); fprintf (stderr, " OBUFLEN=%d\n", OBUFLEN); fprintf (stderr, " IDATALEN=%d\n", IDATALEN); fprintf (stderr, " P_PKTLEN=%d\n", P_PKTLEN); fprintf (stderr, " P_WSLOTS=%d\n", P_WSLOTS); // fprintf (stderr, " F_SW\n"); // fprintf (stderr, " F_SSW\n"); // fprintf (stderr, " F_TSW\n"); #endif k.wslots_max = 1; // default to use one window slot k.p_maxlen = P_PKTLEN; parity = P_PARITY; /* Set this to desired parity */ status = X_OK; /* Initial kermit status */ if (argc == 1) { fprintf (stderr, "usage: eksw [tty baud] [options]\n"); exit (1); } if (argv[1][0] == '-') { strncpy (ttyname, "stdin", sizeof (ttyname) - 1); baud = 0; xargc = argc; xargv = argv; } else { strncpy (ttyname, argv[1], sizeof (ttyname) - 1); baud = atol (argv[2]); xargc = argc - 2; xargv = argv + 2; } xname = argv[0]; while (--xargc > 0) { /* Loop through command-line words */ xargv++; if (**xargv == '-') { /* Have dash */ c = *(*xargv + 1); /* Get the option letter */ xact = doarg (c); /* Go handle the option */ if (xact < 0) doexit (FAILURE); } else { /* No dash where expected */ fatal ("Malformed command-line option: '", *xargv, "'"); } } if (!action) /* Nothing to do, give usage message */ usage (); // fprintf(stderr,"dbg=%d\n",dbg); /* * THE REAL STUFF IS FROM HERE DOWN */ if (!devopen (ttyname, baud)) /* Open the communication device */ doexit (FAILURE); if (!devsettings ("dummy")) /* Perform any needed settings */ doexit (FAILURE); if (dbg == 1) { /* Open debug log if requested */ // fprintf(stderr,"opening debug.log without time stamps\n"); debug (DB_OPN, "debug.log", 0, 0); } if (dbg == 2) { /* Open debug log with time stamps */ // fprintf(stderr,"opening debug.log with time stamps\n"); debug (DB_OPNT, "debug.log", 0, 0); } debug (DB_MSG, "MAIN Initializing...", 0, 0); #ifdef DEBUG debug (DB_LOG, "MAIN SIMULATED ERROR RATE", 0, errorrate); if (errorrate) srand (seed); /* Init random error generator */ #endif /* DEBUG */ /* * Fill in parameters for this run */ k.send_pause_us = 1000000; k.send_pause_us = 0; k.baud = baud; k.xfermode = xmode; /* Text/binary automatic/manual */ k.remote = remote; /* Remote vs local */ k.binary = ftype; /* 0 = text, 1 = binary */ k.parity = parity; /* Communications parity */ if (check == 5) { k.bct = 3; /* Block check type 3 for all packets */ k.bcta3 = 1; } else { k.bct = check; /* Block check type */ k.bcta3 = 0; } k.ikeep = keep; /* Keep incompletely received files */ k.filelist = cmlist; /* List of files to send (if any) */ k.cancel = 0; /* Not canceled yet */ /* * Fill in the i/o pointers */ k.zinbuf = i_buf; /* File input buffer */ k.zinlen = IBUFLEN; /* File input buffer length */ k.zincnt = 0; /* File input buffer position */ k.obuf = o_buf; /* File output buffer */ k.obuflen = OBUFLEN; /* File output buffer length */ k.obufpos = 0; /* File output buffer position */ /* * Fill in function pointers */ k.rxd = readpkt; /* for reading packets */ k.txd = tx_data; /* for sending packets */ k.ixd = inchk; /* for checking connection */ k.openf = openfile; /* for opening files */ k.finfo = fileinfo; /* for getting file info */ k.readf = readfile; /* for reading files */ k.writef = writefile; /* for writing to output file */ k.closef = closefile; /* for closing files */ #ifdef DEBUG k.dbf = dbg ? dodebug : 0; /* for debugging */ #else k.dbf = 0; #endif /* DEBUG */ /* * Initialize Kermit protocol */ debug (DB_LOG, "MAIN action", 0, action); status = kermit (K_INIT, &k, 0, "eksw init", &r); debug (DB_LOG, "MAIN K_INIT kermit return status", 0, status); if (status == X_ERROR) doexit (FAILURE); if (sync_server) { // Send an E-pkt before starting protocol to reset a kermit server // The E-pkt will force an exit for simple send or receive kermits if (kermit (K_SYNC, &k, 0, "eksw sync", &r) == X_ERROR) doexit (FAILURE); } if (action == A_SEND) status = kermit (K_SEND, &k, 0, "eksw send", &r); if (action == A_GET) status = kermit (K_GET, &k, 0, "eksw get", &r); /* * Now we read a packet ourselves and call Kermit with it. Normally, * Kermit would read its own packets, but in the embedded context, the * device must be free to do other things while waiting for a packet * to arrive. So the real control program might dispatch to other * types of tasks, of which Kermit is only one. But in order to read * a packet into Kermit's internal buffer, we have to ask for a buffer * address and slot number. * * To interrupt a transfer in progress, set k.cancel to I_FILE to * interrupt only the current file, or to I_GROUP to cancel the * current file and all remaining files. To cancel the whole * status, call Kermit with K_ERROR. */ loop = 0; while (status != X_DONE) { loop++; /* * Here we block waiting for a packet to come in (unless readpkt * times out). Another possibility would be to call inchk() to see * if any bytes are waiting to be read, and if not, go do * something else for a while, then come back here and check * again. */ debug (DB_LOG, "MAIN -------------- loop", 0, loop); if (ok2rxd (&k)) { inbuf = k.ipktbuf; rx_len = k.rxd (&k, inbuf, P_PKTLEN); /* Try to read a packet */ debug (DB_LOG, "MAIN rx_len", 0, rx_len); debug (DB_HEX, "MHEX", inbuf, rx_len); if (rx_len < 1) { /* No data was read */ if (rx_len < 0) /* If there was a fatal error */ doexit (FAILURE); /* give up */ /* * This would be another place to dispatch to another task * while waiting for a Kermit packet to show up. */ } } /* * Handle the input * * For simplicity, kermit() ACKs the packet immediately after * verifying it was received correctly. If, afterwards, the * control program fails to handle the data correctly (e.g. can't * open file, can't write data, can't close file), then it tells * Kermit to send an Error packet next time through the loop. */ status = kermit (K_RUN, &k, rx_len, "", &r); debug (DB_LOG, "MAIN K_RUN return status", 0, status); switch (status) { case X_OK: /* * This shows how, after each packet, you get the protocol * state, file name, date, size, and bytes transferred so far. * These can be used in a file-transfer progress display, log, * etc. */ #if 0 debug (DB_LOG, "NAME", r.filename ? (uchar *) r.filename : (uchar *) "(NULL)", 0); debug (DB_LOG, "DATE", r.filedate ? (uchar *) r.filedate : (uchar *) "(NULL)", 0); debug (DB_LOG, "SIZE", 0, r.filesize); debug (DB_LOG, "STATE", 0, r.status); debug (DB_LOG, "SOFAR", 0, r.sofar); #endif /* * Maybe do other brief tasks here... */ continue; /* Keep looping */ case X_DONE: debug (DB_MSG, "MAIN X_DONE", 0, 0); break; /* Finished */ case X_ERROR: debug (DB_MSG, "MAIN error", 0, 0); doexit (FAILURE); /* Failed */ break; default: debug (DB_LOG, "MAIN unknown status=%d", 0, status); break; } } doexit (SUCCESS); return (0); }