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