ftp://ftp.metalab.unc.edu/pub/Linux/apps/serialcomm/fax/efax-0.9.tar.gz
[efax.git] / efaxos.c
1 /* 
2                 efaxos.c - O/S-dependent routines
3                     Copyright 1995, Ed Casas
4 */
5
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sys/times.h>
15 #include <unistd.h>
16
17 #ifndef FD_SET
18 #include <sys/select.h>         /* for AIX */
19 #endif
20
21 #include "efaxlib.h"
22 #include "efaxmsg.h"
23 #include "efaxos.h"
24
25 #ifdef USE_TERMIO
26 #include <termio.h>
27 #include <sys/ioctl.h>
28 #define termios termio
29 #define tcgetattr(fd, pt) ioctl(fd, TCGETA, pt)
30 #define tcsetattr(fd, x, pt) ioctl(fd, TCSETAW, pt)
31 #define cfsetospeed(pt, b) ((pt)->c_cflag = ((pt)->c_cflag & ~CBAUD) | b)
32 #define cfsetispeed(pt, b)
33 #define tcdrain(fd)
34 #else
35 #include <termios.h>
36 #endif
37
38 #ifdef TIOCSSOFTCAR
39 #include <sys/ioctl.h>
40 #endif
41
42 #ifndef CRTSCTS
43 #define CRTSCTS 0
44 #endif
45
46
47 /* The milliseconds portion of the current time.  If your system
48    does not provide gettimeofday(3) you can safely substitute a
49    dummy function that returns 0.  This will cause the
50    milliseconds portion of time stamps to be printed as 0. */
51
52 int time_ms ( void )
53 {
54   struct timeval tv ;
55   gettimeofday ( &tv, NULL ) ;
56   return (int) ( tv.tv_usec / 1000 ) ;
57 }
58
59
60 /* Process elapsed time in milliseconds.  This is used for
61    ``virtual flow control'' only. */
62
63 long proc_ms ( void )
64 {
65   struct timeval t ;
66   static int init=0 ;
67   static struct timeval s ;
68   if ( ! init ) { 
69     gettimeofday ( &s, 0 ) ;
70     init = 1 ;
71   }
72   gettimeofday ( &t, 0 ) ;
73   return ( t.tv_sec - s.tv_sec ) * 1000 + ( t.tv_usec - s.tv_usec ) / 1000 ;
74 }
75
76
77 /* Wait for t millisecond (for systems without usleep). */
78
79 void msleep ( int t )
80 {
81   struct timeval timeout ;
82   timeout.tv_sec  = t / 1000 ; 
83   timeout.tv_usec = ( t % 1000 ) * 1000 ;
84   if ( select ( 1 , 0 , 0 , 0 , &timeout ) < 0 ) 
85     msg ("ES2select failed in msleep:") ;
86 }
87
88
89 /* Return number of characters ready to read or < 0 on error.  t
90    is tenths of a second of idle time before timing out.  If t is
91    negative, waits forever. */
92
93 int tdata ( TFILE *f, int t )
94 {
95   int n, err=0 ;
96   fd_set fds ;
97
98   struct timeval timeout ;
99
100   if ( f->fd < 0 ) msg ( "Ecan't happen (faxdata)" ) ;
101
102   timeout.tv_sec  = t / 10 ; 
103   timeout.tv_usec = ( t % 10 ) * 100000 ;
104
105   FD_ZERO ( &fds ) ;
106   FD_SET ( f->fd, &fds ) ;
107
108   do { 
109     n = select ( f->fd + 1, &fds, 0, 0, t<0 ? 0 : &timeout ) ;
110     if ( n < 0 ) {
111       if ( errno == EINTR ) {
112         msg ( "W0  select() interrupted in tdata()" ) ;
113       } else {
114         err = msg ( "ES2 select() failed in tdata():" ) ;
115       }
116     }
117   } while ( n < 0 && ! err ) ;
118
119   return n ;
120 }
121
122
123 /* tundrflw is called only by the tgetc() macro when the buffer
124    is empty.  t is maximum idle time before giving up. Returns
125    number of characters read or EOF on timeout or errors.  */
126
127 int tundrflw ( TFILE *f, int t )
128
129   int n ;
130
131   n = tdata ( f, t ) ;
132
133   if ( n > 0 )
134     if ( ( n = read( f->fd, f->ibuf, IBUFSIZE ) ) < 0 ) 
135       msg ( "ES2fax device read:" ) ;
136
137   f->iq = ( f->ip = f->ibuf ) + ( n > 0 ? n : 0 ) ;
138
139   return n > 0 ? n : EOF ;
140
141
142
143 /* tgetd returns the next data character after removing DLE
144    escapes, DLE-ETX terminators and fixing bit order. Evaluates
145    to the next character, EOF on error/timeout, or -2 on DLE-ETX.  */
146
147 int tgetd ( TFILE *f, int t )
148
149   int c ;
150
151   if ( ( c = tgetc(f,t) ) < 0 )
152     c = EOF ;
153   else
154     if ( c != DLE )
155       c = f->ibitorder[c] ;
156     else {                      /* escape sequence */
157       c = tgetc(f,t) ;
158       if ( c == ETX )
159         c = -2 ;
160       else
161         if ( c == DLE || c == SUB )
162           c = f->ibitorder [ DLE ] ;
163         else
164           c = msg ( "W0invalid escape sequence (DLE-%s) in data", cname(c) ) ;
165     }
166   
167   return c ;
168 }
169
170 /* Write buffer to modem.  Returns 0 or EOF on error. */
171
172 int tput ( TFILE *f, uchar *p, int n )
173 {
174   int m=0 ;
175
176   while ( n > 0 && ( m = write( f->fd, p, n ) ) > 0 ) {
177     if ( m != n )
178       msg ( "Wonly wrote %d of %d bytes", m, n ) ;
179     n -= m ;
180     p += m ;
181   }
182
183   if ( m < 0 )
184     msg ( "ES2fax device write:" ) ;
185
186   return m ;
187 }
188
189
190 /* Compare current termios state with termios struct t. Returns 0 if equal,
191    1 otherwise. */
192
193 int ckfld ( char *field, int set, int get )
194 {
195   return set == get ?
196     0 : msg ( "W1 termios.%s is 0%08o, not 0%08o", field, get, set ) ;
197 }
198
199 int checktermio ( struct termios *t, TFILE *f )
200 {
201   struct termios s ;
202   int err=0 ;
203   s.c_iflag=s.c_oflag=s.c_lflag=s.c_cflag=s.c_cc[VMIN]=s.c_cc[VTIME]=0 ;
204   if ( tcgetattr ( f->fd , &s ) ) 
205     err = msg ("ES2tcgetattr failed:") ;
206  
207  if ( ! err ) return 
208    ckfld ( "iflag" , t->c_iflag, s.c_iflag ) ||
209      ckfld ( "oflag" , t->c_oflag , s.c_oflag ) ||
210        ckfld ( "lflag" , t->c_lflag , s.c_lflag ) ||
211          ckfld ( "cflag" , t->c_cflag , s.c_cflag ) ||
212            ckfld ( "START" , t->c_cc[VSTART] , s.c_cc[VSTART] ) ||
213              ckfld ( "STOP" , t->c_cc[VSTOP] , s.c_cc[VSTOP] ) ||
214                ckfld ( "MIN" , t->c_cc[VMIN] , s.c_cc[VMIN] ) ||
215                  ckfld ( "TIME" , t->c_cc[VTIME] , s.c_cc[VTIME] ) ;
216   return err ;
217 }
218
219
220 /* Set serial port mode. Sets raw, 8-bit, 19.2 kbps mode with no
221    flow control or as required.  Break and parity errors are
222    ignored.  CLOCAL means DCD is ignored since some modems
223    apparently drop it during the fax session.  Flow control is
224    only used when sending.  Returns 0 or 2 on error. */
225
226 int ttymode ( TFILE *f, enum ttymodes mode )
227 {
228   int err=0, i ;         
229   static struct termios t, oldt, *pt ;
230   static int saved=0 ;
231
232   if ( ! saved ) {
233     if ( tcgetattr ( f->fd, &oldt ) ) 
234       err = msg ( "ES2tcgetattr on fd=%d failed:", f->fd ) ;
235     else
236       saved=1 ;
237   }
238
239   t.c_iflag = IGNBRK | IGNPAR ;
240   t.c_oflag = 0 ;
241   t.c_cflag = CS8 | CREAD | CLOCAL ;
242   t.c_lflag = 0 ;
243   
244   for ( i=0 ; i<NCCS ; i++ ) t.c_cc[i] = 0 ;
245
246   t.c_cc[VMIN]  = 1 ; 
247   t.c_cc[VTIME] = 0 ; 
248   t.c_cc[VSTOP] = XOFF;
249   t.c_cc[VSTART] = XON;
250
251   pt = &t ;
252
253   switch ( mode ) {
254   case VOICESEND :
255     t.c_iflag |= IXON ;
256     t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
257   case VOICECOMMAND :                                         
258     cfsetospeed ( pt, B38400 ) ; 
259     cfsetispeed ( pt, B38400 ) ; 
260     break ;
261   case SEND : 
262     t.c_iflag |= IXON ;
263     t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
264   case COMMAND :                                         
265     cfsetospeed ( pt, B19200 ) ; 
266     cfsetispeed ( pt, B19200 ) ; 
267     break ;
268   case DROPDTR : 
269     cfsetospeed ( pt, B0 ) ;                
270     break ;
271   case ORIGINAL :
272     if ( saved ) pt = &oldt ;
273     break ;
274   default : 
275     err = msg ("E2can't happen(ttymode)") ; 
276     break ;
277   }
278   
279   if ( ! err && tcsetattr ( f->fd, TCSADRAIN, pt ) )
280     err = msg ( "ES2tcsetattr on fd=%d failed:", f->fd ) ;
281
282   if ( ! err && checktermio ( pt, f ) ) 
283     msg ( "Wterminal mode not set properly" ) ;
284   
285   tcflow ( f->fd, TCOON ) ;     /* in case XON got lost */
286
287   return err ;
288 }
289
290
291 /* Initialize TFILE data structure. Bit ordering: serial devices
292    transmit LS bit first.  T.4/T.30 says MS bit is sent
293    first. `Normal' order therefore reverses bit order.  */
294
295 void tinit ( TFILE *f, int fd, int reverse, int hwfc )
296 {
297   f->ip = f->iq = f->ibuf ;
298   f->obitorder = normalbits ;
299   f->ibitorder = reverse ? reversebits : normalbits ;
300   f->fd = fd ;
301   f->hwfc = hwfc ;
302   if ( ! normalbits[1] ) initbittab () ;
303 }
304
305
306 /* Open a serial fax device as a TFILE.  Returns 0 if OK, 1 if
307    busy, 2 on error. */
308
309 int ttyopen ( TFILE *f, char *fname, int reverse, int hwfc )
310 {
311   int flags, err=0 ;
312
313   tinit ( f, open ( fname, O_RDWR | O_NDELAY | O_NOCTTY ), reverse, hwfc ) ;
314
315   if ( f->fd < 0 ) {
316     if ( errno == EBUSY ) {
317       err = 1 ; 
318     } else {
319       err = msg ( "ES2can't open serial port %s:", fname ) ;
320     }
321   }
322
323   if ( ! err ) {
324     if ( ( flags = fcntl( f->fd, F_GETFL, 0 ) ) < 0 ||
325         fcntl( f->fd, F_SETFL, ( flags & ~O_NDELAY ) ) < 0 )
326       err = msg ( "ES2fax device fcntl failed %s:", fname ) ;
327   }
328
329 #ifdef TIOCSSOFTCAR
330   { 
331     int arg = 1 ;
332     if ( ! err )
333       if ( ioctl ( f->fd, TIOCSSOFTCAR, &arg ) )
334         msg ("WS unable to set software carrier:" ) ;
335   }
336 #endif
337
338   return err ;
339 }
340
341         /* UUCP-style device locking using lock files */
342
343 /* Test for UUCP lock file & remove stale locks. Returns 0 on null file
344    name or if no longer locked, 1 if locked by another pid, 2 on error, 3
345    if locked by us. */
346
347 int ttlocked ( char *fname, int log )
348 {
349   int err=0, ipid ;
350   FILE *f ;
351   pid_t pid = 0 ;
352   char buf [ EFAX_PATH_MAX ] = "" ;
353
354   if ( fname && *fname == BINLKFLAG ) fname++ ;
355
356   if ( fname && ( f = fopen ( fname , "r" ) ) ) {
357
358     if ( fread ( buf, sizeof(char), EFAX_PATH_MAX-1, f )  == sizeof(pid_t) || 
359         sscanf ( buf, "%d" , &ipid ) != 1 ) {
360       pid = * (pid_t *) buf ;
361       if ( log ) msg ("X+ read binary pid %d from %s", (int) pid, fname ) ;
362     } else {
363       char *p ;
364       pid = (pid_t) ipid ;
365       if ( log ) {
366         msg ( "X+ read HDB pid %d [",  (int) pid ) ;
367         for ( p=buf ; *p ; p++ ) msg ( "X+ %s", cname ( *p ) ) ;
368         msg ( "X+ ] from %s", fname ) ;
369       }
370     }
371
372     if ( kill ( pid, 0 ) && errno == ESRCH ) {
373       if ( log ) msg ("X  - stale" ) ;
374       if ( remove ( fname ) ) 
375         err = msg ( "ES2can't remove stale lock %s from pid %d:", 
376                    fname, pid ) ;
377       else 
378         err = msg ( "I0removed stale lock %s from pid %d", fname, pid ) ;
379     } else { 
380       if ( pid != getpid() ) {
381         err = 1 ;
382         if ( log ) msg ( "X1  (not our pid)" ) ;
383       } else { 
384         err = 3 ; 
385         if ( log ) msg ( "X3  (our pid)" ) ;
386       }
387     }
388     fclose ( f ) ;
389   }
390   return err ;
391 }
392
393
394 /* Create UUCP (text or binary) lock file.  Returns 0 on null
395    file name or if created, 1 if locked by another pid, 2 on
396    error, 3 if locked by us. */
397
398 int ttlock ( char *fname, int log )
399 {
400   int err=0, dirlen, bin=0 ;    
401   FILE *f=0 ;    
402   pid_t pid = getpid ( ) ;
403   char *p , buf [ EFAX_PATH_MAX ] = "" ;
404
405   if ( fname && *fname == BINLKFLAG ) {
406     fname++ ; 
407     bin = 1 ;
408   }
409
410   err = ttlocked ( fname, log ) ;
411
412   if ( ! err ) {
413     dirlen = ( p = strrchr( fname , '/' ) ) ? p-fname+1 : strlen ( fname ) ;
414     sprintf ( buf , "%.*sTMP..%05d" , dirlen , fname , (int) pid ) ;
415     if ( ! ( f = fopen( buf, "w" ) ) ) 
416       err = msg ( "ES2can't open pre-lock file %s:", buf ) ;
417   }
418
419   if ( ! err && f ) {
420     if ( bin ) {
421       if ( fwrite ( &pid, sizeof(pid_t), 1, f ) < 1 ) 
422         err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
423     } else {
424       if ( fprintf ( f, "%10d\n", (int) pid ) < 0 )
425         err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
426     }
427   }
428
429   if ( ! err && f ) {
430     if ( rename ( buf , fname ) == 0 ) {
431       chmod ( fname , 0444 ) ;
432       msg ( "Xcreated %s lock file %s", bin ? "binary" : "text", fname ) ; 
433     } else {
434       err = ttlocked ( fname, log ) ;
435       if ( ! err )
436         err = msg ( "ES2can't rename lock file %s to %s:", buf, fname ) ;
437     }
438   }
439
440   if ( f ) { 
441     fclose ( f ) ; 
442     if ( err ) remove ( buf ) ; 
443   }
444
445   return err ;
446 }
447
448
449 /* Remove lock file.  Returns 0 on null file name, doesn't exist, or was
450    removed, 1 if the lock is to another pid, 2 on errors. */
451
452 int ttunlock ( char *fname )
453 {
454   int err = 0 ;
455
456   if ( fname && *fname == BINLKFLAG ) fname++ ;
457
458   switch ( ttlocked ( fname, 1 ) ) {
459   case 0: break ;
460   case 1: err = msg ( "E1won't remove lock %s (not ours)" , fname ) ; break ;
461   case 2: err = 2 ; break ; 
462   case 3:
463     if ( remove ( fname ) ) {
464       err = msg ( "ES2can't remove lock %s:", fname ) ;
465     } else { 
466       err = msg ( "X0removed lock file %s", fname ) ;
467     }
468     break ;
469   default:
470     err = msg ( "E2can't happen (ttunlock)" ) ;
471     break ;
472   }
473   return err ;
474 }
475
476
477 /* Lock all lock files and possibly log attempt if log=1.
478    Returns 0 if all locks [already] applied, 1 if any are locked
479    to other pids, 2 on any errors. */
480
481 int lockall ( char **lkfiles, int log )
482
483   int err = 0 ;
484   char **p = lkfiles ;
485   while ( *p && ! err ) 
486     if ( ( err = ttlock ( *p++, log ) ) == 3 ) err = 0 ; 
487   return err ; 
488 }
489
490
491 /* Remove all lock files.  Returns 0 if all locks removed, 2 on
492    errors. */
493
494 int unlockall ( char **lkfiles )
495
496   int err = 0, i ;
497   char **p = lkfiles ;
498   while ( *p ) 
499     if ( ( i = ttunlock ( *p++ ) ) != 0 ) err = i ; 
500   return err ; 
501 }
502
503 /* Return basename of the argument or the whole thing if can't
504    find it. */
505
506 char *efaxbasename ( char *p )
507 {
508   return strrchr ( p , '/' ) ? strrchr ( p , '/' ) + 1 : p ;
509 }
510