xref: /netbsd-src/external/bsd/ekermit/dist/main.c (revision 29e96a293cce6645949e0442c3d00c61ca902b9e)
1 /*  Embedded Kermit demo, main program.  */
2 
3 /*
4   Author: Frank da Cruz, the Kermit Project, Columbia University, New York.
5   Copyright (C) 1995, 2011,
6   Trustees of Columbia University in the City of New York.
7   All rights reserved.
8 
9   Redistribution and use in source and binary forms, with or without
10   modification, are permitted provided that the following conditions are met:
11 
12   * Redistributions of source code must retain the above copyright notice,
13     this list of conditions and the following disclaimer.
14 
15   * Redistributions in binary form must reproduce the above copyright notice,
16     this list of conditions and the following disclaimer in the documentation
17     and/or other materials provided with the distribution.
18 
19   * Neither the name of Columbia University nor the names of its contributors
20     may be used to endorse or promote products derived from this software
21     without specific prior written permission.
22 
23   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33   POSSIBILITY OF SUCH DAMAGE.
34 */
35 
36 /*
37   This is a demo/test framework, to be replaced by a real control program.
38   It includes a simple Unix-style command-line parser to allow setup and
39   testing of the Kermit module.  Skip past all this to where it says REAL
40   STUFF to see the real stuff.  ANSI C required.  Note: order of the
41   following includes is important.
42 */
43 #include "cdefs.h"      /* Data types for all modules */
44 #include "debug.h"	/* Debugging */
45 #include "platform.h"	/* Platform-specific includes and definitions */
46 #include "kermit.h"	/* Kermit symbols and data structures */
47 #ifdef __linux
48 #include <errno.h>
49 #endif /* __linux */
50 
51 /*
52   Sample prototypes for i/o functions.
53   The functions are defined in a platform-specific i/o module.
54   The function names are unknown to the Kermit module.
55   The names can be changed but not their calling conventions.
56   The following prototypes are keyed to unixio.c.
57 */
58 int devopen(char *);			/* Communications device/path */
59 int devsettings(char *);
60 int devrestore(void);
61 int devclose(void);
62 int pktmode(short);
63 
64 int readpkt(struct k_data *, UCHAR *, int); /* Communications i/o functions */
65 int tx_data(struct k_data *, UCHAR *, int);
66 int inchk(struct k_data *);
67 
68 int openfile(struct k_data *, UCHAR *, int); /* File i/o functions */
69 int writefile(struct k_data *, UCHAR *, int);
70 int readfile(struct k_data *);
71 int closefile(struct k_data *, UCHAR, int);
72 ULONG fileinfo(struct k_data *, UCHAR *, UCHAR *, int, short *, short);
73 
74 /* External data */
75 
76 extern UCHAR o_buf[];                   /* Must be defined in io.c */
77 extern UCHAR i_buf[];                   /* Must be defined in io.c */
78 extern int errno;
79 
80 /* Data global to this module */
81 
82 struct k_data k;                        /* Kermit data structure */
83 struct k_response r;                    /* Kermit response structure */
84 
85 char **xargv;				/* Global pointer to arg vector */
86 UCHAR **cmlist = (UCHAR **)0;		/* Pointer to file list */
87 char * xname = "ek";			/* Default program name */
88 
89 int xargc;				/* Global argument count */
90 int nfils = 0;				/* Number of files in file list */
91 int action = 0;				/* Send or Receive */
92 int xmode = 0;				/* File-transfer mode */
93 int ftype = 1;				/* Global file type 0=text 1=binary*/
94 int keep = 0;				/* Keep incompletely received files */
95 int db = 0;				/* Debugging */
96 short fmode = -1;			/* Transfer mode for this file */
97 int parity = 0;				/* Parity */
98 #ifdef F_CRC
99 int check = 3;				/* Block check */
100 #else
101 int check = 1;
102 #endif /* F_CRC */
103 int remote = 1;				/* 1 = Remote, 0 = Local */
104 #ifdef DEBUG
105 int errorrate = 0;			/* Simulated error rate */
106 int seed = 1234;			/* Random number generator seed */
107 #endif /* DEBUG */
108 
109 void
doexit(int status)110 doexit(int status) {
111     devrestore();                       /* Restore device */
112     devclose();                         /* Close device */
113     exit(status);                       /* Exit with indicated status */
114 }
115 
116 void
usage()117 usage() {
118     fprintf(stderr,"E-Kermit %s\n",VERSION);
119     fprintf(stderr,"Usage: %s <options>\n",xname);
120     fprintf(stderr,"Options:\n");
121     fprintf(stderr," -r           Receive files\n");
122 #ifndef RECVONLY
123     fprintf(stderr," -s <files>   Send files\n");
124 #endif /* RECVONLY */
125     fprintf(stderr," -p [neoms]   Parity: none, even, odd, mark, space\n");
126 #ifdef F_CRC
127     fprintf(stderr," -b [1235]    Block check type: 1, 2, 3, or 5\n");
128 #endif /* F_CRC */
129     fprintf(stderr," -k           Keep incompletely received files\n");
130     fprintf(stderr," -B           Force binary mode\n");
131     fprintf(stderr," -T           Force text mode\n");
132     fprintf(stderr," -R           Remote mode (vs local)\n");
133     fprintf(stderr," -L           Local mode (vs remote)\n");
134 #ifdef DEBUG
135     fprintf(stderr," -E <number>  Simulated error rate (0-100)\n");
136     fprintf(stderr," -d           Create debug.log\n");
137 #endif /* DEBUG */
138     fprintf(stderr," -h           Help (this message)\n");
139     doexit(FAILURE);
140 }
141 
142 void
fatal(char * msg1,char * msg2,char * msg3)143 fatal(char *msg1, char *msg2, char *msg3) { /* Not to be called except */
144     if (msg1) {				    /* from this module */
145 	fprintf(stderr,"%s: %s",xname,msg1);
146 	if (msg2) fprintf(stderr,"%s",msg2);
147 	if (msg3) fprintf(stderr,"%s",msg3);
148 	fprintf(stderr,"\n");
149     }
150     doexit(FAILURE);
151 }
152 
153 /* Simple user interface for testing */
154 
155 int
doarg(char c)156 doarg(char c) {				/* Command-line option parser */
157     int x;				/* Parses one option with its arg(s) */
158     char *xp, *s;
159     struct stat statbuf;
160 
161     xp = *xargv+1;			/* Pointer for bundled args */
162     while (c) {
163 #ifdef DEBUG
164 	if (errorrate) seed += (int)c;
165 #endif /* DEBUG) */
166 	switch (c) {
167 	  case 'r':			/* Receive */
168 	    if (action) fatal("Conflicting actions",(char *)0,(char *)0);
169 	    action = A_RECV;
170 	    break;
171 
172 #ifndef RECVONLY
173 	  case 's':			/* Send */
174 	    if (action)
175 	      fatal("Conflicting actions",(char *)0,(char *)0);
176 	    if (*(xp+1))
177 	      fatal("Invalid argument bundling after -s",(char *)0,(char *)0);
178 	    nfils = 0;			/* Initialize file counter, flag */
179 	    cmlist = (UCHAR **)(xargv+1); /* Remember this pointer */
180 	    while (--xargc > 0) {	/* Traverse the list */
181 		xargv++;
182 		s = *xargv;
183 #ifdef DEBUG
184 		if (errorrate) seed += (int)*s;
185 #endif /* DEBUG) */
186 		if (**xargv == '-')
187 		  break;
188 		errno = 0;
189 		x = stat(s,&statbuf);
190 		if (x < 0)
191 		  fatal("File '",s,"' not found");
192 		if (access(s,4) < 0)
193 		  fatal("File '",s,"' not accessible");
194 		nfils++;
195 	    }
196 	    xargc++, xargv--;		/* Adjust argv/argc */
197 	    if (nfils < 1)
198 	      fatal("Missing filename for -s",(char *)0,(char *)0);
199 	    action = A_SEND;
200 	    break;
201 #endif /* RECVONLY */
202 
203 #ifdef F_CRC
204 	  case 'b':			/* Block-check type */
205 #endif /* F_CRC */
206 #ifdef DEBUG
207 	  case 'E':			/* Simulated error rate */
208 #endif /* DEBUG */
209 	    if (*(xp+1))
210 	      fatal("Invalid argument bundling",(char *)0,(char *)0);
211 	    xargv++, xargc--;
212 	    if ((xargc < 1) || (**xargv == '-'))
213 	      fatal("Missing option argument",(char *)0,(char *)0);
214 	    s = *xargv;
215 	    while (*s) {
216 		if (!isdigit(*s))
217 		  fatal("Numeric argument required",(char *)0,(char *)0);
218 		s++;
219 	    }
220 	    if (c == 'b') {
221 		check = atoi(*xargv);
222 		if (check < 1 || check > 5 || check == 4)
223 		  fatal("Invalid block check",(char *)0,(char *)0);
224 #ifdef DEBUG
225 	    } else if (c == 'E') {
226 		errorrate = atoi(*xargv);
227 		if (errorrate > 100)
228 		  fatal("Invalid error rate",(char *)0,(char *)0);
229 #endif /* DEBUG */
230 	    }
231 	    break;
232 
233 	  case 'h':			/* Help */
234 	  case '?':
235 	    usage();
236 
237 	  case 'B':			/* Force binary file transfer */
238 	    xmode = 1;			/* So no automatic switching */
239 	    ftype = BINARY;
240 	    break;
241 
242 	  case 'T':			/* Force text file transfer */
243 	    xmode = 1;			/* So no automatic switching */
244 	    ftype = TEXT;
245 	    break;
246 
247 	  case 'R':			/* Tell Kermit it's in remote mode */
248 	    remote = 1;
249 	    break;
250 
251 	  case 'L':			/* Tell Kermit it's in local mode */
252 	    remote = 0;
253 	    break;
254 
255 	  case 'k':			/* Keep incompletely received files */
256 	    keep = 1;
257 	    break;
258 
259 	  case 'p':			/* Parity */
260 	    if (*(xp+1))
261 	      fatal("Invalid argument bundling",(char *)0,(char *)0);
262 	    xargv++, xargc--;
263 	    if ((xargc < 1) || (**xargv == '-'))
264 	      fatal("Missing parity",(char *)0,(char *)0);
265 	    switch(x = **xargv) {
266 	      case 'e':			/* Even */
267 	      case 'o':			/* Odd */
268 	      case 'm':			/* Mark */
269 	      case 's': parity = x; break; /* Space */
270 	      case 'n': parity = 0; break; /* None */
271 	      default:  fatal("Invalid parity '", *xargv, "'");
272 	    }
273 	    break;
274 
275 #ifdef DEBUG
276 	  case 'd':
277 	    db++;
278 	    break;
279 #endif /* DEBUG */
280 
281 	  default:			/* Anything else */
282 	    fatal("Unknown command-line option ",
283 		  *xargv,
284 		  " type 'ek -h' for help."
285 		  );
286         }
287 	c = *++xp;			/* See if options are bundled */
288     }
289     return(action);
290 }
291 
292 int
main(int argc,char ** argv)293 main(int argc, char ** argv) {
294     int status, rx_len, i, x;
295     char c;
296     UCHAR *inbuf;
297     short r_slot;
298 
299     parity = P_PARITY;                  /* Set this to desired parity */
300     status = X_OK;                      /* Initial kermit status */
301 
302     xargc = argc;
303     xargv = argv;
304     xname = argv[0];
305 
306     while (--xargc > 0) {		/* Loop through command-line words */
307 	xargv++;
308 	if (**xargv == '-') {		/* Have dash */
309 	    c = *(*xargv+1);		/* Get the option letter */
310 	    x = doarg(c);		/* Go handle the option */
311 	    if (x < 0) doexit(FAILURE);
312     	} else {			/* No dash where expected */
313 	    fatal("Malformed command-line option: '",*xargv,"'");
314 	}
315     }
316     if (!action)			/* Nothing to do, give usage message */
317       usage();
318 
319 #ifdef DEBUG
320     debug(DB_LOG,"SIMULATED ERROR RATE:",0,errorrate);
321     if (errorrate) srand(seed);		/* Init random error generator */
322 #endif /* DEBUG */
323 
324 /* THE REAL STUFF IS FROM HERE DOWN */
325 
326     if (!devopen("dummy"))		/* Open the communication device */
327       doexit(FAILURE);
328     if (!devsettings("dummy"))		/* Perform any needed settings */
329       doexit(FAILURE);
330     if (db)				/* Open debug log if requested */
331       debug(DB_OPN,"debug.log",0,0);
332 
333     debug(DB_MSG,"Initializing...",0,0);
334 
335 /*  Fill in parameters for this run */
336 
337     k.xfermode = xmode;			/* Text/binary automatic/manual  */
338     k.remote = remote;			/* Remote vs local */
339     k.binary = ftype;			/* 0 = text, 1 = binary */
340     k.parity = parity;                  /* Communications parity */
341     k.bct = (check == 5) ? 3 : check;	/* Block check type */
342     k.ikeep = keep;			/* Keep incompletely received files */
343     k.filelist = cmlist;		/* List of files to send (if any) */
344     k.cancel = 0;			/* Not canceled yet */
345 
346 /*  Fill in the i/o pointers  */
347 
348     k.zinbuf = i_buf;			/* File input buffer */
349     k.zinlen = IBUFLEN;			/* File input buffer length */
350     k.zincnt = 0;			/* File input buffer position */
351     k.obuf = o_buf;			/* File output buffer */
352     k.obuflen = OBUFLEN;		/* File output buffer length */
353     k.obufpos = 0;			/* File output buffer position */
354 
355 /* Fill in function pointers */
356 
357     k.rxd    = readpkt;			/* for reading packets */
358     k.txd    = tx_data;			/* for sending packets */
359     k.ixd    = inchk;			/* for checking connection */
360     k.openf  = openfile;                /* for opening files */
361     k.finfo  = fileinfo;                /* for getting file info */
362     k.readf  = readfile;		/* for reading files */
363     k.writef = writefile;               /* for writing to output file */
364     k.closef = closefile;               /* for closing files */
365 #ifdef DEBUG
366     k.dbf    = db ? dodebug : 0;	/* for debugging */
367 #else
368     k.dbf    = 0;
369 #endif /* DEBUG */
370     /* Force Type 3 Block Check (16-bit CRC) on all packets, or not */
371     k.bctf   = (check == 5) ? 1 : 0;
372 
373 /* Initialize Kermit protocol */
374 
375     status = kermit(K_INIT, &k, 0, 0, "", &r);
376 #ifdef DEBUG
377     debug(DB_LOG,"init status:",0,status);
378     debug(DB_LOG,"version:",k.version,0);
379 #endif /* DEBUG */
380     if (status == X_ERROR)
381       doexit(FAILURE);
382     if (action == A_SEND)
383       status = kermit(K_SEND, &k, 0, 0, "", &r);
384 /*
385   Now we read a packet ourselves and call Kermit with it.  Normally, Kermit
386   would read its own packets, but in the embedded context, the device must be
387   free to do other things while waiting for a packet to arrive.  So the real
388   control program might dispatch to other types of tasks, of which Kermit is
389   only one.  But in order to read a packet into Kermit's internal buffer, we
390   have to ask for a buffer address and slot number.
391 
392   To interrupt a transfer in progress, set k.cancel to I_FILE to interrupt
393   only the current file, or to I_GROUP to cancel the current file and all
394   remaining files.  To cancel the whole operation in such a way that the
395   both Kermits return an error status, call Kermit with K_ERROR.
396 */
397     while (status != X_DONE) {
398 /*
399   Here we block waiting for a packet to come in (unless readpkt times out).
400   Another possibility would be to call inchk() to see if any bytes are waiting
401   to be read, and if not, go do something else for a while, then come back
402   here and check again.
403 */
404         inbuf = getrslot(&k,&r_slot);	/* Allocate a window slot */
405         rx_len = k.rxd(&k,inbuf,P_PKTLEN); /* Try to read a packet */
406         debug(DB_PKT,"main packet",&(k.ipktbuf[0][r_slot]),rx_len);
407 /*
408   For simplicity, kermit() ACKs the packet immediately after verifying it was
409   received correctly.  If, afterwards, the control program fails to handle the
410   data correctly (e.g. can't open file, can't write data, can't close file),
411   then it tells Kermit to send an Error packet next time through the loop.
412 */
413         if (rx_len < 1) {               /* No data was read */
414             freerslot(&k,r_slot);	/* So free the window slot */
415             if (rx_len < 0)             /* If there was a fatal error */
416               doexit(FAILURE);          /* give up */
417 
418 	    /* This would be another place to dispatch to another task */
419 	    /* while waiting for a Kermit packet to show up. */
420 
421         }
422         /* Handle the input */
423 
424         switch (status = kermit(K_RUN, &k, r_slot, rx_len, "", &r)) {
425 	  case X_OK:
426 #ifdef DEBUG
427 /*
428   This shows how, after each packet, you get the protocol state, file name,
429   date, size, and bytes transferred so far.  These can be used in a
430   file-transfer progress display, log, etc.
431 */
432 	    debug(DB_LOG,"NAME",r.filename ? (char *)r.filename : "(NULL)",0);
433 	    debug(DB_LOG,"DATE",r.filedate ? (char *)r.filedate : "(NULL)",0);
434 	    debug(DB_LOG,"SIZE",0,r.filesize);
435 	    debug(DB_LOG,"STATE",0,r.status);
436 	    debug(DB_LOG,"SOFAR",0,r.sofar);
437 #endif /* DEBUG */
438 	    /* Maybe do other brief tasks here... */
439 	    continue;			/* Keep looping */
440 	  case X_DONE:
441 	    break;			/* Finished */
442 	  case X_ERROR:
443 	    doexit(FAILURE);		/* Failed */
444 	}
445     }
446     doexit(SUCCESS);
447 }
448