1 /*
2  * Changes for use with CM11A copyright 1996, 1997 - 2003 Daniel B. Suthers,
3  * Pleasanton Ca, 94588 USA
4  * E-mail dbs@tanj.com
5  *
6  */
7 
8 /*
9  * Copyright 1986 by Larry Campbell, 73 Concord Street, Maynard MA 01754 USA
10  * (maynard!campbell).
11  *
12  * John Chmielewski (tesla!jlc until 9/1/86, then rogue!jlc) assisted
13  * by doing the System V port and adding some nice features.  Thanks!
14  */
15 
16 /*
17  *   This program is free software: you can redistribute it and/or modify
18  *   it under the terms of the GNU General Public License as published by
19  *   the Free Software Foundation, either version 3 of the License, or
20  *   (at your option) any later version.
21  *
22  *   This program is distributed in the hope that it will be useful,
23  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  *   GNU General Public License for more details.
26  *
27  *   You should have received a copy of the GNU General Public License
28  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
29  *
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <setjmp.h>
36 #if    (defined(SCO) || defined (SOLARIS) || defined (ATTSVR4) || defined(OPENBSD) || defined(NETBSD))
37 #include <errno.h>
38 #else
39 #include <sys/errno.h>
40 #endif
41 #include <syslog.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include "x10.h"
45 #if (defined(LINUX) || defined(SOLARIS) || defined(FREEBSD) || defined(DARWIN) || defined(SYSV) || defined(OPENBSD) || defined(NETBSD))
46 #include <string.h>    /* char *strerror(); */
47 #endif
48 
49 #include <time.h>
50 
51 #include "process.h"
52 #include "local.h"
53 
54 extern int verbose;
55 extern int i_am_relay, i_am_aux;
56 extern int tty;
57 
58 unsigned alarm();
59 void sigtimer( int );
60 #ifdef HAS_ITIMER
61 #include <sys/time.h>
62 #include <unistd.h>
63 struct itimerval iold, icurrent;
64 #endif
65 int xread ( int, unsigned char *, int, int );
66 
67 /*
68  * xread(fd, buf, count, timeout)
69  *
70  *	Timed read.  Works just like read(2) but gives up after
71  *	timeout seconds, returning whatever's been read so far.
72  */
73 /*  NOTE:  The CM11A will pop out a poll message every second when it wants
74  *  to send an update.  Alarm(1) will go off when the next second starts.
75  *  This may be any amount of time up to 1 second.  This makes a normal
76  *  alarm(1) unsuitable.  Thus the use of setitimer.  Other OSes will have
77  *  similar capabilities that assure a 1 second alarm is actually one second.
78  */
79 
80 jmp_buf jb;
81 
82 
xread(fd,buf,count,timeout)83 int xread(fd, buf, count, timeout)
84 int fd, count, timeout;
85 unsigned char *buf;
86 {
87     static int total;	/* so setjump won't clobber it */
88     extern int i_am_relay;
89     int counter;
90     unsigned char *cbuf;
91     char RCSID[]= "@(#) $Id: xread.c,v 1.10 2003/03/17 01:40:32 dbs Exp dbs $\n";
92 
93     display(RCSID);
94 
95     if (verbose && (! i_am_relay) ) {
96     fprintf(stderr, "xread() called, count=%d, timeout = %d\n", count, timeout);
97     fflush(stderr);
98     }
99 
100     total = 0;
101 
102     if (setjmp(jb))
103     {
104 	if (verbose && (! i_am_relay) ) {
105 	    fprintf(stderr, "xread() returning %d after timeout\n", total);
106 	    fflush(stderr);
107 	}
108 	(void) alarm(0);
109 	(void) signal(SIGALRM, SIG_IGN);
110 	return (total);
111     }
112 
113     /* The compiler would complain that
114      *  "argument `buf' and count might be clobbered by `longjmp'"
115      * This is bacause we change them after the setjmp.
116      * To prevent this, we use copies of the arguements.
117      */
118     cbuf = buf;
119     counter = count;
120 
121     (void)alarm(0);
122     (void) signal(SIGALRM, sigtimer);
123 #ifdef HAS_ITIMER
124     icurrent.it_interval.tv_sec = 0;
125     icurrent.it_interval.tv_usec = 0;
126     icurrent.it_value.tv_sec = timeout;
127     icurrent.it_value.tv_usec = 30;
128     if (setitimer(ITIMER_REAL, &icurrent, &iold) == -1)
129 	if( !i_am_relay )
130 	    perror ("setitimer");
131 #else
132     (void) alarm((unsigned) timeout);
133 #endif
134 
135 
136     errno = 0;
137     while (counter--) {			/* loop till all characters are read */
138 	int i;
139 	if ((i = read(fd, (char *)cbuf, 1)) < 1) {
140 	    if (i == 0) {
141 		/* loop around on EOF */
142 		counter++;	/* reverse the decrement in the while loop. */
143 		if( errno == EINTR)  /* alarm was triggered */
144 		{
145 		    break;
146 		}
147 		continue;
148 	    }
149 	    if( (i < 0) && (i_am_relay != 1) )
150 	        perror("read");
151 	    else
152 	       if( (i < 0) && (i_am_relay == 1) )
153 	       {
154 	           syslog(LOG_ERR,"Relay Xread read error");
155 	           syslog(LOG_ERR, "%s", strerror(errno));
156 	       }
157 	    (void) alarm(0);
158 	    (void) signal(SIGALRM, SIG_IGN);
159 	    if (verbose) {
160 	        fprintf(stderr, "xread() returning %d bytes after error\n", total);
161 		fflush(stderr);
162 	    }
163 	    return (total);
164 	}
165 	cbuf++;
166 	total++;
167     }
168     (void) alarm(0);
169     (void) signal(SIGALRM, SIG_IGN);
170     if (verbose && (i_am_relay != 1) ) {
171         if ( total == 0 )
172            fprintf(stderr, "xread() returning 0 bytes\n");
173         else
174            fprintf(stderr, "xread() returning %d byte(s). The first is 0x%02x\n", total, buf[0] );
175 	fflush(stderr);
176     }
177     return (total);
178 }
179 
sigtimer(int signo)180 void sigtimer( int signo )
181 {
182     if (verbose && (! i_am_relay)  ){
183         fprintf(stderr, "Alarm - timeout\n");
184         fflush(stderr);
185     }
186     (void) signal(SIGALRM, sigtimer);
187     errno = EINTR;
188     return;
189     /* the jmp temporarily disabled (version 1.34)
190             ... It was causing problems when triggered under linux 2.2.20
191             using USB.  The second trap would not behave properly.
192             I don't know why.
193     */
194 
195     /* longjmp(jb, 1); */
196 }
197 
198 
199 
200 /* expect xread sends a code to the spool file that specifys how many
201  * incoming characters are to be discarded.
202  * The code is 3 0xff followed by (the number of bytes to expect +127)
203  */
exread(fd,buf,count,timeout)204 int exread(fd, buf, count, timeout)
205 int fd;
206 char *buf;
207 int count;
208 int timeout;
209 {
210     unsigned char lbuf[160];
211     extern int sptty;
212 
213     lbuf[1] = lbuf[2] = lbuf[0] = 0xff;
214     lbuf[3] = count+127;
215     if ( (!i_am_relay || i_am_aux) && write(sptty, (char *)lbuf , 4) != 4 )
216     {
217        /* if(  i_am_relay )
218        {
219 	   syslog (LOG_ERR, "exread(): write failed\n");
220        }
221        else */
222 
223        if ( !i_am_relay && !i_am_aux )
224        {
225 	   fprintf (stderr, "exread(): write failed\n");
226        }
227    }
228 
229 
230     return(xread( fd, (unsigned char *)buf, count, timeout));
231 }
232 
233 
234 
235 
is_blocking(int tty)236 int is_blocking ( int tty )
237 {
238     int flags;
239 
240     flags = fcntl(tty, F_GETFL);
241 
242     if ( flags & O_NONBLOCK )
243        return 0;
244 
245     return 1;
246 }
247 
file_flags(void)248 int file_flags ( void )
249 {
250    return fcntl(tty, F_GETFL);
251 }
252 
setblocking(int fd,int code)253 int setblocking ( int fd, int code )
254 {
255    int flags;
256 
257    flags = fcntl(fd, F_GETFL);
258    if ( code == 0 )
259       flags |= O_NONBLOCK;
260    else
261       flags &= ~O_NONBLOCK;
262    fcntl(fd, F_SETFL, flags);
263 
264    return flags;
265 }
266 
sxread_isr_1(int signo)267 void sxread_isr_1( int signo )
268 {
269    return;
270 }
271 
272 
273 #if defined(HASSIGACT)
274 /*--------------------------------------------------------+
275  |  An interruptable read() like xread(), but from a      |
276  |  serial port file descriptor.  Needs sigaction()       |
277  +--------------------------------------------------------*/
sxread(int fd,unsigned char * buffer,int size,int timeout)278 int sxread ( int fd, unsigned char *buffer, int size, int timeout )
279 {
280    extern void cleanup_files();
281    extern void cleanup_aux(void);
282    int count = 0, nread = 0, passes = 0;
283    struct sigaction act;
284 
285    if ( fd == TTY_DUMMY ) {
286       sleep(timeout);
287       return 0;
288    }
289 
290    errno = 0;
291    setblocking(fd, 1);
292 
293    sigaction(SIGALRM, NULL, &act);
294 
295    act.sa_handler = sxread_isr_1;
296    act.sa_flags &= ~SA_RESTART;
297 
298    sigaction(SIGALRM, &act, NULL);
299 
300    alarm(timeout);
301    while ( nread < size && passes++ < 100 ) {
302       count = read(fd, (char *)(buffer + nread), size - nread);
303       if ( count < 0 )
304          break;
305       nread += count;
306    }
307    alarm(0);
308 
309    act.sa_handler = SIG_IGN;
310    act.sa_flags |= SA_RESTART;
311    sigaction(SIGALRM, &act, NULL);
312 
313    if ( count >= 0 || (errno == EINTR) )
314       return nread;
315 
316    syslog(LOG_ERR, "sxread() failed, error = %s\n", strerror(errno));
317 
318    if ( i_am_aux )
319       cleanup_aux();
320 
321    cleanup_files();
322 
323    exit(1);
324 
325    return -1;
326 }
327 
328 
329 #elif defined(HASSIGINT)
330 /*--------------------------------------------------------+
331  |  An interruptable read() like xread(), but from a      |
332  |  serial port file descriptor.  Needs siginterrupt()    |
333  +--------------------------------------------------------*/
sxread(int fd,unsigned char * buffer,int size,int timeout)334 int sxread ( int fd, unsigned char *buffer, int size, int timeout )
335 {
336    extern void cleanup_files();
337    extern void cleanup_aux(void);
338    int count = 0, nread = 0, passes = 0;
339 
340    if ( fd == TTY_DUMMY ) {
341       sleep(timeout);
342       return 0;
343    }
344 
345    errno = 0;
346    setblocking(fd, 1);
347    siginterrupt(SIGALRM, 1);
348    signal(SIGALRM, sxread_isr_1);
349    alarm(timeout);
350    while ( size > nread && passes++ < 100 ) {
351       count = read(fd, (char *)(buffer + nread), size - nread);
352       if ( count < 0 )
353          break;
354       nread += count;
355    }
356    alarm(0);
357    siginterrupt(SIGALRM, 0);
358 
359    if ( count >= 0 || (count < 0 && errno == EINTR) )
360       return nread;
361 
362    syslog(LOG_ERR, "sxread() failed, error = %s\n", strerror(errno));
363 
364    if ( i_am_aux )
365       cleanup_aux();
366 
367    cleanup_files();
368 
369    exit(1);
370 
371    return -1;
372 }
373 
374 
375 #elif defined(SIGKLUGE)
376 
377 static int s_fd;
sxread_isr_2(int signo)378 void sxread_isr_2( int signo )
379 {
380    setblocking(s_fd, 0);
381    return;
382 }
383 
384 /*--------------------------------------------------------+
385  |  An interruptable read() like xread(), but from a      |
386  |  serial port file descriptor.  This one uses a kluge   |
387  |  whereby the port is changed from blocking to non-     |
388  |  blocking after the alarm timeout.                     |
389  +--------------------------------------------------------*/
sxread(int fd,unsigned char * buffer,int size,int timeout)390 int sxread ( int fd, unsigned char *buffer, int size, int timeout )
391 {
392    extern void cleanup_files();
393    extern void cleanup_aux(void);
394    int count = 0, nread = 0, passes = 0;
395 
396    if ( fd == TTY_DUMMY ) {
397       sleep(timeout);
398       return 0;
399    }
400 
401    s_fd = fd;
402    alarm(0);
403    signal(SIGALRM, sxread_isr_2);
404    alarm(timeout);
405    while ( size > nread && passes++ < 100 ) {
406       errno = 0;
407       setblocking(fd, 1);
408       count = read(fd, (char *)(buffer + nread), size - nread);
409       if ( count < 0 ) {
410          if ( errno == EINTR || errno == EAGAIN ) {
411             break;
412          }
413          else {
414             continue;
415          }
416       }
417       nread += count;
418    }
419    alarm(0);
420    setblocking(fd, 1);
421 
422    if ( count >= 0 || (errno == EINTR || errno == EAGAIN) )
423       return nread;
424 
425    syslog(LOG_ERR, "sxread() failed, error = %s\n", strerror(errno));
426 
427    if ( i_am_aux )
428       cleanup_aux();
429 
430    cleanup_files();
431 
432    exit(1);
433 
434    return -1;
435 }
436 
437 #else
438 /*--------------------------------------------------------+
439  |  This version of sxread polls until the characters are |
440  |  read or timeout.  Timeout is not very accurate - can  |
441  |  take twice as long as specified with older kernels.   |
442  +--------------------------------------------------------*/
sxread(int fd,unsigned char * buffer,int size,int timeout)443 int sxread ( int fd, unsigned char *buffer, int size, int timeout )
444 {
445    extern void cleanup_files();
446    extern void cleanup_aux();
447    int count = 0, nread = 0, passes = 0;
448 
449    if ( fd == TTY_DUMMY ) {
450       sleep(timeout);
451       return 0;
452    }
453 
454    setblocking(fd, 0);
455    passes = 100 * timeout;
456    while ( size > nread && --passes > 0 ) {
457       errno = 0;
458       count = read(fd, (char *)(buffer + nread), size - nread);
459       if ( count <= 0 ) {
460          millisleep(10);
461          continue;
462       }
463       nread += count;
464    }
465    setblocking(fd, 1);
466 
467    if ( count >= 0 || errno == EAGAIN )
468       return nread;
469 
470    syslog(LOG_ERR, "sxread() failed, error = %s\n", strerror(errno));
471 
472    if ( i_am_aux )
473       cleanup_aux();
474 
475    cleanup_files();
476 
477    exit(1);
478 
479    return -1;
480 }
481 
482 #endif
483 
484 
485 #if 0
486 int timeout_flag = 0;
487 
488 void xread_sigtimer ( int signo )
489 {
490    timeout_flag = 1;
491    errno = EINTR;
492    return;
493 }
494 
495 /*
496  * An experimental version of xread()
497  */
498 int xread ( int fd, unsigned char *buffer, int count, int timeout )
499 {
500    int  nread, ntot = 0;
501 
502    if ( fd == TTY_DUMMY ) {
503       sleep(timeout);
504       return 0;
505    }
506 
507    if ( verbose && (! i_am_relay) ) {
508       fprintf(stderr, "xread() called, count=%d, timeout = %d\n", count, timeout);
509       fflush(stderr);
510    }
511 
512    timeout_flag = 0;
513 
514    (void)alarm(0);
515    (void) signal(SIGALRM, xread_sigtimer);
516 #ifdef HAS_ITIMER
517    icurrent.it_interval.tv_sec = 0;
518    icurrent.it_interval.tv_usec = 0;
519    icurrent.it_value.tv_sec = timeout;
520    icurrent.it_value.tv_usec = 30;
521    if ( setitimer(ITIMER_REAL, &icurrent, &iold) == -1 ) {
522       if( !i_am_relay )
523           perror ("setitimer");
524    }
525 #else
526     (void) alarm((unsigned) timeout);
527 #endif
528 
529    errno = 0;
530    while ( ntot < count && timeout_flag == 0 ) {
531       nread = read(fd, (char *)(buffer + ntot), count - ntot);
532       if ( nread < 0 )  {
533          syslog(LOG_ERR, "xread: read() error %s\n", strerror(errno));
534          if ( verbose && !i_am_relay ) {
535 	    fprintf(stderr, "xread() returning %d bytes after error\n", ntot);
536             fflush(stderr);
537 	 }
538          alarm(0);
539          signal(SIGALRM, SIG_IGN);
540          return ntot;
541       }
542       ntot += nread;
543 
544       if ( nread == 0 )
545          millisleep(10);
546    }
547 
548    if ( verbose && !i_am_relay ) {
549       if ( ntot == 0 )
550          fprintf(stderr, "xread() returning 0 bytes\n");
551       else
552          fprintf(stderr, "xread() returning %d byte(s). The first is 0x%02x\n", ntot, buffer[0]);
553       fflush(stderr);
554    }
555    alarm(0);
556    signal(SIGALRM, SIG_IGN);
557 
558    return ntot;
559 }
560 #endif
561 
562 
563 #if 0
564 /*
565  * This is an alternate experimental version of xread which does not use
566  * a signal for timeout.
567  */
568 int xread ( int fd, unsigned char *buffer, int count, int timeout )
569 {
570    int  nread, ntot = 0;
571    long maxpasses, passes = 0;
572    time_t tquit;
573 
574    if ( fd == TTY_DUMMY ) {
575       sleep(timeout);
576       return 0;
577    }
578 
579    if ( verbose && (! i_am_relay) ) {
580       fprintf(stderr, "xread() called, count=%d, timeout = %d\n", count, timeout);
581       fflush(stderr);
582    }
583 
584    maxpasses = timeout * 100;                     /* at nominal 10 msec/pass */
585 
586    /* System clock is checked since the timing resolution with older kernels */
587    /* is such that the timout interval could otherwise be twice as long as   */
588    /* specified */
589 
590    tquit = time(NULL) + (time_t)(timeout + 1);
591 
592    errno = 0;
593    while ( ntot < count && passes < maxpasses && time(NULL) < tquit ) {
594       nread = read(fd, (char *)(buffer + ntot), count - ntot);
595       if ( nread < 0 )  {
596          syslog(LOG_ERR, "xread: read() error %s\n", strerror(errno));
597          if ( verbose ) {
598 	    fprintf(stderr, "xread() returning %d bytes after error\n", ntot);
599             fflush(stderr);
600 	 }
601          return ntot;
602       }
603       ntot += nread;
604       passes++;
605       if ( nread == 0 )
606          millisleep(10);
607    }
608 
609    if ( verbose && (i_am_relay != 1) ) {
610       if ( ntot == 0 )
611          fprintf(stderr, "xread() returning 0 bytes\n");
612       else
613          fprintf(stderr, "xread() returning %d byte(s). The first is 0x%02x\n", ntot, buffer[0]);
614       fflush(stderr);
615    }
616 
617    return ntot;
618 }
619 
620 #endif
621 
622 
623 
624 
625 
626 
627 
628 
629 
630 
631 
632