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 110 doexit(int status) { 111 devrestore(); /* Restore device */ 112 devclose(); /* Close device */ 113 exit(status); /* Exit with indicated status */ 114 } 115 116 void 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 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 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 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