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