1 /* Sample system-dependent communications i/o routines for embedded Kermit. */
2
3 /*
4 Author: Frank da Cruz.
5 Copyright (C) 1995, 2011.
6 Trustees of Columbia University in the City of New York.
7 All rights reserved.
8 See kermit.c for license.
9 */
10
11 /*
12 The sample i/o routines for UNIX that provide packet i/o
13 functions on the console (login) device.
14 Copy this file, rename it appropriately, and replace the contents
15 of each routine appropriately for your platform.
16
17 Device i/o:
18
19 int devopen() Communications device - open
20 int pktmode() Communications device - enter/exit packet mode
21 int readpkt() Communications device - read a packet
22 int tx_data() Communications device - send data
23 int devclose() Communications device - close
24 int inchk() Communications device - check if bytes are ready to read
25
26 File i/o:
27
28 int openfile() File - open for input or output
29 ULONG fileinfo() Get input file modtime and size
30 int readfile() Input file - read data
31 int writefile() Output file - write data
32 int closefile() Input or output file - close
33
34 Full definitions below, prototypes in kermit.h.
35
36 These routines must handle speed setting, parity, flow control, file i/o,
37 and similar items without the kermit() routine knowing anything about it.
38 If parity is in effect, these routines must add it to outbound characters
39 and strip it from inbound characters.
40 */
41 #include <stdio.h>
42 #include <sys/stat.h>
43 #include <time.h>
44 #include <errno.h>
45 #ifndef O_WRONLY
46 #include <sys/file.h>
47 #ifdef X_OK
48 #undef X_OK
49 #endif /* X_OK */
50 #endif /* O_WRONLY */
51
52 #include "cdefs.h"
53 #include "debug.h"
54 #include "platform.h"
55 #include "kermit.h"
56
57 UCHAR o_buf[OBUFLEN+8]; /* File output buffer */
58 UCHAR i_buf[IBUFLEN+8]; /* File output buffer */
59
60 /*
61 In this example, the output file is unbuffered to ensure that every
62 output byte is commited. The input file, however, is buffered for speed.
63 This is just one of many possible implmentation choices, invisible to the
64 Kermit protocol module.
65 */
66 static int ttyfd, ofile = -1; /* File descriptors */
67 static FILE * ifile = (FILE *)0; /* and pointers */
68
69 /* Debugging */
70
71 #ifdef DEBUG
72 static FILE * dp = (FILE *)0; /* Debug log */
73 static int xdebug = 0; /* Debugging on/off */
74
75 void
dodebug(int fc,UCHAR * label,UCHAR * sval,long nval)76 dodebug(int fc, UCHAR * label, UCHAR * sval, long nval) {
77
78 if (fc != DB_OPN && !xdebug)
79 return;
80 if (!label)
81 label = "";
82
83 switch (fc) { /* Function code */
84 case DB_OPN: /* Open debug log */
85 if (dp) fclose(dp);
86 if (!*label) label = "debug.log";
87 dp = fopen(label,"w");
88 if (!dp) {
89 dp = stderr;
90 } else {
91 setbuf(dp,(char *)0);
92 }
93 xdebug = 1;
94 fprintf(dp,"DEBUG LOG OPEN\n");
95 return;
96 case DB_MSG: /* Write a message */
97 if (dp) fprintf(dp,"%s\n",label);
98 return;
99 case DB_CHR: /* Write label and character */
100 if (dp) fprintf(dp,"%s=[%c]\n",label,(char)nval);
101 return;
102 case DB_PKT: /* Log a packet */
103 /* (fill in later, fall thru for now...) */
104 case DB_LOG: /* Write label and string or number */
105 if (sval && dp)
106 fprintf(dp,"%s[%s]\n",label,sval);
107 else
108 fprintf(dp,"%s=%ld\n",label,nval);
109 return;
110 case DB_CLS: /* Close debug log */
111 if (dp) {
112 fclose(dp);
113 dp = (FILE *)0;
114 }
115 xdebug = 0;
116 }
117 }
118 #endif /* DEBUG */
119
120 /* D E V O P E N -- Open communications device */
121 /*
122
123 Call with: string pointer to device name. This routine should get the
124 current device settings and save them so devclose() can restore them.
125 It should open the device. If the device is a serial port, devopen()
126 set the speed, stop bits, flow control, etc.
127 Returns: 0 on failure, 1 on success.
128 */
129 int
devopen(char * device)130 devopen(char *device) {
131 ttyfd = 0;
132 return(1);
133 }
134
135 /* P K T M O D E -- Put communications device into or out of packet mode */
136 /*
137 Call with: 0 to put in normal (cooked) mode, 1 to put in packet (raw) mode.
138 For a "dumb i/o device" like an i/o port that does not have a login attached
139 to it, this routine can usually be a no-op.
140 Returns: 0 on failure, 1 on success.
141 */
142 int
pktmode(short on)143 pktmode(short on) {
144 if (ttyfd < 0) /* Device must be open */
145 return(0);
146 system(on ? "stty raw -echo" : "stty sane"); /* Crude but effective */
147 return(1);
148 }
149
150
151 /* D E V S E T T I N G S */
152
153 int
devsettings(char * s)154 devsettings(char * s) {
155 /* Get current device settings, save them for devrestore() */
156 /* Parse string s, do whatever it says, e.g. "9600;8N1" */
157 if (!pktmode(ON)) /* And put device in packet mode */
158 return(0);
159 return(1);
160 }
161
162 /* D E V R E S T O R E */
163
164 int
devrestore(void)165 devrestore(void) {
166 /* Put device back as we found it */
167 pktmode(OFF);
168 return(1);
169 }
170
171
172 /* D E V C L O S E -- Closes the current open communications device */
173 /*
174 Call with: nothing
175 Closes the device and puts it back the way it was found by devopen().
176 Returns: 0 on failure, 1 on success.
177 */
178 int
devclose(void)179 devclose(void) {
180 ttyfd = -1;
181 return(1);
182 }
183
184 /* I N C H K -- Check if input waiting */
185
186 /*
187 Check if input is waiting to be read, needed for sliding windows. This
188 sample version simply looks in the stdin buffer (which is not portable
189 even among different Unixes). If your platform does not provide a way to
190 look at the device input buffer without blocking and without actually
191 reading from it, make this routine return -1. On success, returns the
192 numbers of characters waiting to be read, i.e. that can be safely read
193 without blocking.
194 */
195 int
inchk(struct k_data * k)196 inchk(struct k_data * k) {
197 #ifdef _IO_file_flags /* Linux */
198 if (ttyfd < 0) /* Device must be open */
199 return(0);
200 return((int) ((stdin->_IO_read_end) - (stdin->_IO_read_ptr)));
201 #else
202 #ifdef AIX /* AIX */
203 if (ttyfd < 0)
204 return(0);
205 return(stdin->_cnt);
206 #else
207 #ifdef SunOS /* Solaris and SunOS */
208 if (ttyfd < 0)
209 return(0);
210 return(stdin->_cnt);
211 #else
212 #ifdef HPUX /* HPUX */
213 if (ttyfd < 0)
214 return(0);
215 return(stdin->__cnt);
216 #else
217 return(-1);
218 #endif /* HPUX */
219 #endif /* SunOS */
220 #endif /* AIX */
221 #endif /* _IO_file_flags */
222 }
223
224 /* R E A D P K T -- Read a Kermit packet from the communications device */
225 /*
226 Call with:
227 k - Kermit struct pointer
228 p - pointer to read buffer
229 len - length of read buffer
230
231 When reading a packet, this function looks for start of Kermit packet
232 (k->r_soh), then reads everything between it and the end of the packet
233 (k->r_eom) into the indicated buffer. Returns the number of bytes read, or:
234 0 - timeout or other possibly correctable error;
235 -1 - fatal error, such as loss of connection, or no buffer to read into.
236 */
237
238 int
readpkt(struct k_data * k,UCHAR * p,int len,int fc)239 readpkt(struct k_data * k, UCHAR *p, int len, int fc) {
240 int x, n, max;
241 short flag;
242 UCHAR c;
243 /*
244 Timeout not implemented in this sample.
245 It should not be needed. All non-embedded Kermits that are capable of
246 making connections are also capable of timing out, and only one Kermit
247 needs to time out. NOTE: This simple example waits for SOH and then
248 reads everything up to the negotiated packet terminator. A more robust
249 version might be driven by the value of the packet-length field.
250 */
251 #ifdef DEBUG
252 char * p2;
253 #endif /* DEBUG */
254
255 #ifdef F_CTRLC
256 short ccn;
257 ccn = 0;
258 #endif /* F_CTRLC */
259
260 if (ttyfd < 0 || !p) { /* Device not open or no buffer */
261 debug(DB_MSG,"readpkt FAIL",0,0);
262 return(-1);
263 }
264 flag = n = 0; /* Init local variables */
265
266 #ifdef DEBUG
267 p2 = p;
268 #endif /* DEBUG */
269
270 while (1) {
271 x = getchar(); /* Replace this with real i/o */
272 c = (k->parity) ? x & 0x7f : x & 0xff; /* Strip parity */
273
274 #ifdef F_CTRLC
275 /* In remote mode only: three consecutive ^C's to quit */
276 if (k->remote && c == (UCHAR) 3) {
277 if (++ccn > 2) {
278 debug(DB_MSG,"readpkt ^C^C^C",0,0);
279 return(-1);
280 }
281 } else {
282 ccn = 0;
283 }
284 #endif /* F_CTRLC */
285
286 if (!flag && c != k->r_soh) /* No start of packet yet */
287 continue; /* so discard these bytes. */
288 if (c == k->r_soh) { /* Start of packet */
289 flag = 1; /* Remember */
290 continue; /* But discard. */
291 } else if (c == k->r_eom /* Packet terminator */
292 || c == '\012' /* 1.3: For HyperTerminal */
293 ) {
294 #ifdef DEBUG
295 *p = NUL; /* Terminate for printing */
296 debug(DB_PKT,"RPKT",p2,n);
297 #endif /* DEBUG */
298 return(n);
299 } else { /* Contents of packet */
300 if (n++ > k->r_maxlen) /* Check length */
301 return(0);
302 else
303 *p++ = x & 0xff;
304 }
305 }
306 debug(DB_MSG,"READPKT FAIL (end)",0,0);
307 return(-1);
308 }
309
310 /* T X _ D A T A -- Writes n bytes of data to communication device. */
311 /*
312 Call with:
313 k = pointer to Kermit struct.
314 p = pointer to data to transmit.
315 n = length.
316 Returns:
317 X_OK on success.
318 X_ERROR on failure to write - i/o error.
319 */
320 int
tx_data(struct k_data * k,UCHAR * p,int n)321 tx_data(struct k_data * k, UCHAR *p, int n) {
322 int x;
323 int max;
324
325 max = 10; /* Loop breaker */
326
327 while (n > 0) { /* Keep trying till done */
328 x = write(ttyfd,p,n);
329 debug(DB_MSG,"tx_data write",0,x);
330 if (x < 0 || --max < 1) /* Errors are fatal */
331 return(X_ERROR);
332 n -= x;
333 p += x;
334 }
335 return(X_OK); /* Success */
336 }
337
338 /* O P E N F I L E -- Open output file */
339 /*
340 Call with:
341 Pointer to filename.
342 Size in bytes.
343 Creation date in format yyyymmdd hh:mm:ss, e.g. 19950208 14:00:00
344 Mode: 1 = read, 2 = create, 3 = append.
345 Returns:
346 X_OK on success.
347 X_ERROR on failure, including rejection based on name, size, or date.
348 */
349 int
openfile(struct k_data * k,UCHAR * s,int mode)350 openfile(struct k_data * k, UCHAR * s, int mode) {
351
352 switch (mode) {
353 case 1: /* Read */
354 if (!(ifile = fopen(s,"r"))) {
355 debug(DB_LOG,"openfile read error",s,0);
356 return(X_ERROR);
357 }
358 k->s_first = 1; /* Set up for getkpt */
359 k->zinbuf[0] = '\0'; /* Initialize buffer */
360 k->zinptr = k->zinbuf; /* Set up buffer pointer */
361 k->zincnt = 0; /* and count */
362 debug(DB_LOG,"openfile read ok",s,0);
363 return(X_OK);
364
365 case 2: /* Write (create) */
366 ofile = creat(s,0644);
367 if (ofile < 0) {
368 debug(DB_LOG,"openfile write error",s,0);
369 return(X_ERROR);
370 }
371 debug(DB_LOG,"openfile write ok",s,0);
372 return(X_OK);
373
374 #ifdef COMMENT
375 case 3: /* Append (not used) */
376 ofile = open(s,O_WRONLY|O_APPEND);
377 if (ofile < 0) {
378 debug(DB_LOG,"openfile append error",s,0);
379 return(X_ERROR);
380 }
381 debug(DB_LOG,"openfile append ok",s,0);
382 return(X_OK);
383 #endif /* COMMENT */
384
385 default:
386 return(X_ERROR);
387 }
388 }
389
390 /* F I L E I N F O -- Get info about existing file */
391 /*
392 Call with:
393 Pointer to filename
394 Pointer to buffer for date-time string
395 Length of date-time string buffer (must be at least 18 bytes)
396 Pointer to int file type:
397 0: Prevailing type is text.
398 1: Prevailing type is binary.
399 Transfer mode (0 = auto, 1 = manual):
400 0: Figure out whether file is text or binary and return type.
401 1: (nonzero) Don't try to figure out file type.
402 Returns:
403 X_ERROR on failure.
404 0L or greater on success == file length.
405 Date-time string set to yyyymmdd hh:mm:ss modtime of file.
406 If date can't be determined, first byte of buffer is set to NUL.
407 Type set to 0 (text) or 1 (binary) if mode == 0.
408 */
409 #ifdef F_SCAN
410 #define SCANBUF 1024
411 #define SCANSIZ 49152
412 #endif /* F_SCAN */
413
414 ULONG
fileinfo(struct k_data * k,UCHAR * filename,UCHAR * buf,int buflen,short * type,short mode)415 fileinfo(struct k_data * k,
416 UCHAR * filename, UCHAR * buf, int buflen, short * type, short mode) {
417 struct stat statbuf;
418 struct tm * timestamp, * localtime();
419
420 #ifdef F_SCAN
421 FILE * fp; /* File scan pointer */
422 char inbuf[SCANBUF]; /* and buffer */
423 #endif /* F_SCAN */
424
425 if (!buf)
426 return(X_ERROR);
427 buf[0] = '\0';
428 if (buflen < 18)
429 return(X_ERROR);
430 if (stat(filename,&statbuf) < 0)
431 return(X_ERROR);
432 timestamp = localtime(&(statbuf.st_mtime));
433 sprintf(buf,"%04d%02d%02d %02d:%02d:%02d",
434 timestamp->tm_year + 1900,
435 timestamp->tm_mon + 1,
436 timestamp->tm_mday,
437 timestamp->tm_hour,
438 timestamp->tm_min,
439 timestamp->tm_sec
440 );
441 #ifdef F_SCAN
442 /*
443 Here we determine if the file is text or binary if the transfer mode is
444 not forced. This is an extremely crude sample, which diagnoses any file
445 that contains a control character other than HT, LF, FF, or CR as binary.
446 A more thorough content analysis can be done that accounts for various
447 character sets as well as various forms of Unicode (UTF-8, UTF-16, etc).
448 Or the diagnosis could be based wholly or in part on the filename.
449 etc etc. Or the implementation could skip this entirely by not defining
450 F_SCAN and/or by always calling this routine with type set to -1.
451 */
452 if (!mode) { /* File type determination requested */
453 int isbinary = 1;
454 fp = fopen(filename,"r"); /* Open the file for scanning */
455 if (fp) {
456 int n = 0, count = 0;
457 char c, * p;
458
459 debug(DB_LOG,"fileinfo scan ",filename,0);
460
461 isbinary = 0;
462 while (count < SCANSIZ && !isbinary) { /* Scan this much */
463 n = fread(inbuf,1,SCANBUF,fp);
464 if (n == EOF || n == 0)
465 break;
466 count += n;
467 p = inbuf;
468 while (n--) {
469 c = *p++;
470 if (c < 32 || c == 127) {
471 if (c != 9 && /* Tab */
472 c != 10 && /* LF */
473 c != 12 && /* FF */
474 c != 13) { /* CR */
475 isbinary = 1;
476 debug(DB_MSG,"fileinfo BINARY",0,0);
477 break;
478 }
479 }
480 }
481 }
482 fclose(fp);
483 *type = isbinary;
484 }
485 }
486 #endif /* F_SCAN */
487
488 return((ULONG)(statbuf.st_size));
489 }
490
491
492 /* R E A D F I L E -- Read data from a file */
493
494 int
readfile(struct k_data * k)495 readfile(struct k_data * k) {
496 if (!k->zinptr) {
497 #ifdef DEBUG
498 fprintf(dp,"readfile ZINPTR NOT SET\n");
499 #endif /* DEBUG */
500 return(X_ERROR);
501 }
502 if (k->zincnt < 1) { /* Nothing in buffer - must refill */
503 if (k->binary) { /* Binary - just read raw buffers */
504 k->dummy = 0;
505 k->zincnt = fread(k->zinbuf, 1, k->zinlen, ifile);
506 debug(DB_LOG,"readfile binary ok zincnt",0,k->zincnt);
507
508 } else { /* Text mode needs LF/CRLF handling */
509 int c; /* Current character */
510 for (k->zincnt = 0; (k->zincnt < (k->zinlen - 2)); (k->zincnt)++) {
511 if ((c = getc(ifile)) == EOF)
512 break;
513 if (c == '\n') /* Have newline? */
514 k->zinbuf[(k->zincnt)++] = '\r'; /* Insert CR */
515 k->zinbuf[k->zincnt] = c;
516 }
517 #ifdef DEBUG
518 k->zinbuf[k->zincnt] = '\0';
519 debug(DB_LOG,"readfile text ok zincnt",0,k->zincnt);
520 #endif /* DEBUG */
521 }
522 k->zinbuf[k->zincnt] = '\0'; /* Terminate. */
523 if (k->zincnt == 0) /* Check for EOF */
524 return(-1);
525 k->zinptr = k->zinbuf; /* Not EOF - reset pointer */
526 }
527 (k->zincnt)--; /* Return first byte. */
528
529 debug(DB_LOG,"readfile exit zincnt",0,k->zincnt);
530 debug(DB_LOG,"readfile exit zinptr",0,k->zinptr);
531 return(*(k->zinptr)++ & 0xff);
532 }
533
534
535 /* W R I T E F I L E -- Write data to file */
536 /*
537 Call with:
538 Kermit struct
539 String pointer
540 Length
541 Returns:
542 X_OK on success
543 X_ERROR on failure, such as i/o error, space used up, etc
544 */
545 int
writefile(struct k_data * k,UCHAR * s,int n)546 writefile(struct k_data * k, UCHAR * s, int n) {
547 int rc;
548 rc = X_OK;
549
550 debug(DB_LOG,"writefile binary",0,k->binary);
551
552 if (k->binary) { /* Binary mode, just write it */
553 if (write(ofile,s,n) != n)
554 rc = X_ERROR;
555 } else { /* Text mode, skip CRs */
556 UCHAR * p, * q;
557 int i;
558 q = s;
559
560 while (1) {
561 for (p = q, i = 0; ((*p) && (*p != (UCHAR)13)); p++, i++) ;
562 if (i > 0)
563 if (write(ofile,q,i) != i)
564 rc = X_ERROR;
565 if (!*p) break;
566 q = p+1;
567 }
568 }
569 return(rc);
570 }
571
572 /* C L O S E F I L E -- Close output file */
573 /*
574 Mode = 1 for input file, mode = 2 or 3 for output file.
575
576 For output files, the character c is the character (if any) from the Z
577 packet data field. If it is D, it means the file transfer was canceled
578 in midstream by the sender, and the file is therefore incomplete. This
579 routine should check for that and decide what to do. It should be
580 harmless to call this routine for a file that that is not open.
581 */
582 int
closefile(struct k_data * k,UCHAR c,int mode)583 closefile(struct k_data * k, UCHAR c, int mode) {
584 int rc = X_OK; /* Return code */
585
586 switch (mode) {
587 case 1: /* Closing input file */
588 if (!ifile) /* If not not open */
589 break; /* do nothing but succeed */
590 debug(DB_LOG,"closefile (input)",k->filename,0);
591 if (fclose(ifile) < 0)
592 rc = X_ERROR;
593 break;
594 case 2: /* Closing output file */
595 case 3:
596 if (ofile < 0) /* If not open */
597 break; /* do nothing but succeed */
598 debug(DB_LOG,"closefile (output) name",k->filename,0);
599 debug(DB_LOG,"closefile (output) keep",0,k->ikeep);
600 if (close(ofile) < 0) { /* Try to close */
601 rc = X_ERROR;
602 } else if ((k->ikeep == 0) && /* Don't keep incomplete files */
603 (c == 'D')) { /* This file was incomplete */
604 if (k->filename) {
605 debug(DB_LOG,"deleting incomplete",k->filename,0);
606 unlink(k->filename); /* Delete it. */
607 }
608 }
609 break;
610 default:
611 rc = X_ERROR;
612 }
613 return(rc);
614 }
615
616 #ifdef DEBUG
xerror()617 int xerror() {
618 unsigned int x;
619 extern int errorrate; /* Fix this - NO EXTERNS */
620 if (!errorrate)
621 return(0);
622 x = rand() % 100; /* Fix this - NO C LIBRARY */
623 debug(DB_LOG,"RANDOM",0,x);
624 debug(DB_LOG,"ERROR",0,(x < errorrate));
625 return(x < errorrate);
626 }
627 #endif /* DEBUG */
628