2 efaxos.c - O/S-dependent routines
3 Copyright 1995, Ed Casas
13 #include <sys/types.h>
16 #include <sys/times.h>
20 #include <sys/select.h> /* for AIX */
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)
41 #include <sys/ioctl.h>
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. */
57 gettimeofday ( &tv, NULL ) ;
58 return (int) ( tv.tv_usec / 1000 ) ;
62 /* Process elapsed time in milliseconds. This is used for
63 ``virtual flow control'' only. */
69 static struct timeval s ;
71 gettimeofday ( &s, 0 ) ;
74 gettimeofday ( &t, 0 ) ;
75 return ( t.tv_sec - s.tv_sec ) * 1000 + ( t.tv_usec - s.tv_usec ) / 1000 ;
79 /* Wait for t millisecond (for systems without usleep). */
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:") ;
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. */
95 int tdata ( TFILE *f, int t )
100 struct timeval timeout ;
102 if ( f->fd < 0 ) msg ( "Ecan't happen (faxdata)" ) ;
104 timeout.tv_sec = t / 10 ;
105 timeout.tv_usec = ( t % 10 ) * 100000 ;
108 FD_SET ( f->fd, &fds ) ;
111 n = select ( f->fd + 1, &fds, 0, 0, t<0 ? 0 : &timeout ) ;
113 if ( errno == EINTR ) {
114 msg ( "W0 select() interrupted in tdata()" ) ;
116 err = msg ( "ES2 select() failed in tdata():" ) ;
119 } while ( n < 0 && ! err ) ;
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. */
129 int tundrflw ( TFILE *f, int t )
136 if ( ( n = read( f->fd, f->ibuf, IBUFSIZE ) ) < 0 )
137 msg ( "ES2fax device read:" ) ;
139 f->iq = ( f->ip = f->ibuf ) + ( n > 0 ? n : 0 ) ;
141 return n > 0 ? n : EOF ;
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. */
150 int tgetd ( TFILE *f, int t )
154 if ( ( c = tgetc(f,t) ) < 0 )
158 c = f->ibitorder[c] ;
159 else { /* escape sequence */
164 if ( c == DLE || c == SUB )
165 c = f->ibitorder [ DLE ] ;
167 c = msg ( "W0invalid escape sequence (DLE-%s) in data", cname(c) ) ;
174 /* Write buffer to modem. Returns 0 or EOF on error. */
176 int tput ( TFILE *f, uchar *p, int n )
180 while ( n > 0 && ( m = write( f->fd, p, n ) ) > 0 ) {
182 msg ( "Wonly wrote %d of %d bytes", m, n ) ;
188 msg ( "ES2fax device write:" ) ;
194 /* Compare current termios state with termios struct t. Returns 0 if equal,
197 static int ckfld ( char *field, int set, int get )
200 0 : msg ( "W1 termios.%s is 0%08o, not 0%08o", field, get, set ) ;
203 static int checktermio ( struct termios *t, TFILE *f )
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:") ;
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] ) ;
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. */
230 int ttymode ( TFILE *f, enum ttymodes mode )
233 static struct termios t, oldt, *pt ;
237 if ( tcgetattr ( f->fd, &oldt ) )
238 err = msg ( "ES2tcgetattr on fd=%d failed:", f->fd ) ;
243 t.c_iflag = IGNBRK | IGNPAR ;
245 t.c_cflag = CS8 | CREAD | CLOCAL ;
248 for ( i=0 ; i<NCCS ; i++ ) t.c_cc[i] = 0 ;
252 t.c_cc[VSTOP] = XOFF;
253 t.c_cc[VSTART] = XON;
260 t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
262 cfsetospeed ( pt, B38400 ) ;
263 cfsetispeed ( pt, B38400 ) ;
267 t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
269 cfsetospeed ( pt, B19200 ) ;
270 cfsetispeed ( pt, B19200 ) ;
273 cfsetospeed ( pt, B0 ) ;
276 if ( saved ) pt = &oldt ;
279 err = msg ("E2can't happen(ttymode)") ;
283 if ( ! err && tcsetattr ( f->fd, TCSADRAIN, pt ) )
284 err = msg ( "ES2tcsetattr on fd=%d failed:", f->fd ) ;
286 if ( ! err && checktermio ( pt, f ) )
287 msg ( "Wterminal mode not set properly" ) ;
289 tcflow ( f->fd, TCOON ) ; /* in case XON got lost */
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. */
299 static void tinit ( TFILE *f, int fd, int reverse, int hwfc )
301 f->ip = f->iq = f->ibuf ;
302 f->obitorder = normalbits ;
303 f->ibitorder = reverse ? reversebits : normalbits ;
306 if ( ! normalbits[1] ) initbittab () ;
310 /* Open a serial fax device as a TFILE. Returns 0 if OK, 1 if
313 int ttyopen ( TFILE *f, char *fname, int reverse, int hwfc )
317 tinit ( f, open ( fname, O_RDWR | O_NDELAY | O_NOCTTY ), reverse, hwfc ) ;
320 if ( errno == EBUSY ) {
323 err = msg ( "ES2can't open serial port %s:", fname ) ;
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 ) ;
337 if ( ioctl ( f->fd, TIOCSSOFTCAR, &arg ) )
338 msg ("WS unable to set software carrier:" ) ;
345 /* UUCP-style device locking using lock files */
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
351 static int ttlocked ( char *fname, int log )
356 char buf [ EFAX_PATH_MAX ] = "" ;
358 if ( fname && *fname == BINLKFLAG ) fname++ ;
360 if ( fname && ( f = fopen ( fname , "r" ) ) ) {
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 ) ;
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 ) ;
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:",
382 err = msg ( "I0removed stale lock %s from pid %d", fname, pid ) ;
384 if ( pid != getpid() ) {
386 if ( log ) msg ( "X1 (not our pid)" ) ;
389 if ( log ) msg ( "X3 (our pid)" ) ;
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. */
402 static int ttlock ( char *fname, int log )
404 int err=0, dirlen, bin=0 ;
406 pid_t pid = getpid ( ) ;
407 char *p , buf [ EFAX_PATH_MAX ] = "" ;
409 if ( fname && *fname == BINLKFLAG ) {
414 err = ttlocked ( fname, log ) ;
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 ) ;
425 if ( fwrite ( &pid, sizeof(pid_t), 1, f ) < 1 )
426 err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
428 if ( fprintf ( f, "%10d\n", (int) pid ) < 0 )
429 err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
434 if ( rename ( buf , fname ) == 0 ) {
435 chmod ( fname , 0444 ) ;
436 msg ( "Xcreated %s lock file %s", bin ? "binary" : "text", fname ) ;
438 err = ttlocked ( fname, log ) ;
440 err = msg ( "ES2can't rename lock file %s to %s:", buf, fname ) ;
446 if ( err ) unlink ( buf ) ;
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. */
456 static int ttunlock ( char *fname )
460 if ( fname && *fname == BINLKFLAG ) fname++ ;
462 switch ( ttlocked ( fname, 1 ) ) {
464 case 1: err = msg ( "E1won't remove lock %s (not ours)" , fname ) ; break ;
465 case 2: err = 2 ; break ;
467 if ( unlink ( fname ) ) {
468 err = msg ( "ES2can't remove lock %s:", fname ) ;
470 err = msg ( "X0removed lock file %s", fname ) ;
474 err = msg ( "E2can't happen (ttunlock)" ) ;
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. */
485 int lockall ( char **lkfiles, int log )
489 while ( *p && ! err )
490 if ( ( err = ttlock ( *p++, log ) ) == 3 ) err = 0 ;
495 /* Remove all lock files. Returns 0 if all locks removed, 2 on
498 int unlockall ( char **lkfiles )
503 if ( ( i = ttunlock ( *p++ ) ) != 0 ) err = i ;
507 /* Return basename of the argument or the whole thing if can't
510 char *efaxbasename ( char *p )
512 return strrchr ( p , '/' ) ? strrchr ( p , '/' ) + 1 : p ;