2 efaxos.c - O/S-dependent routines
3 Copyright 1995, Ed Casas
11 #include <sys/types.h>
14 #include <sys/times.h>
18 #include <sys/select.h> /* for AIX */
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)
39 #include <sys/ioctl.h>
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. */
55 gettimeofday ( &tv, NULL ) ;
56 return (int) ( tv.tv_usec / 1000 ) ;
60 /* Process elapsed time in milliseconds. This is used for
61 ``virtual flow control'' only. */
67 static struct timeval s ;
69 gettimeofday ( &s, 0 ) ;
72 gettimeofday ( &t, 0 ) ;
73 return ( t.tv_sec - s.tv_sec ) * 1000 + ( t.tv_usec - s.tv_usec ) / 1000 ;
77 /* Wait for t millisecond (for systems without usleep). */
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:") ;
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. */
93 int tdata ( TFILE *f, int t )
98 struct timeval timeout ;
100 if ( f->fd < 0 ) msg ( "Ecan't happen (faxdata)" ) ;
102 timeout.tv_sec = t / 10 ;
103 timeout.tv_usec = ( t % 10 ) * 100000 ;
106 FD_SET ( f->fd, &fds ) ;
109 n = select ( f->fd + 1, &fds, 0, 0, t<0 ? 0 : &timeout ) ;
111 if ( errno == EINTR ) {
112 msg ( "W0 select() interrupted in tdata()" ) ;
114 err = msg ( "ES2 select() failed in tdata():" ) ;
117 } while ( n < 0 && ! err ) ;
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. */
127 int tundrflw ( TFILE *f, int t )
134 if ( ( n = read( f->fd, f->ibuf, IBUFSIZE ) ) < 0 )
135 msg ( "ES2fax device read:" ) ;
137 f->iq = ( f->ip = f->ibuf ) + ( n > 0 ? n : 0 ) ;
139 return n > 0 ? n : EOF ;
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. */
147 int tgetd ( TFILE *f, int t )
151 if ( ( c = tgetc(f,t) ) < 0 )
155 c = f->ibitorder[c] ;
156 else { /* escape sequence */
161 if ( c == DLE || c == SUB )
162 c = f->ibitorder [ DLE ] ;
164 c = msg ( "W0invalid escape sequence (DLE-%s) in data", cname(c) ) ;
170 /* Write buffer to modem. Returns 0 or EOF on error. */
172 int tput ( TFILE *f, uchar *p, int n )
176 while ( n > 0 && ( m = write( f->fd, p, n ) ) > 0 ) {
178 msg ( "Wonly wrote %d of %d bytes", m, n ) ;
184 msg ( "ES2fax device write:" ) ;
190 /* Compare current termios state with termios struct t. Returns 0 if equal,
193 int ckfld ( char *field, int set, int get )
196 0 : msg ( "W1 termios.%s is 0%08o, not 0%08o", field, get, set ) ;
199 int checktermio ( struct termios *t, TFILE *f )
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:") ;
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] ) ;
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. */
226 int ttymode ( TFILE *f, enum ttymodes mode )
229 static struct termios t, oldt, *pt ;
233 if ( tcgetattr ( f->fd, &oldt ) )
234 err = msg ( "ES2tcgetattr on fd=%d failed:", f->fd ) ;
239 t.c_iflag = IGNBRK | IGNPAR ;
241 t.c_cflag = CS8 | CREAD | CLOCAL ;
244 for ( i=0 ; i<NCCS ; i++ ) t.c_cc[i] = 0 ;
248 t.c_cc[VSTOP] = XOFF;
249 t.c_cc[VSTART] = XON;
256 t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
258 cfsetospeed ( pt, B38400 ) ;
259 cfsetispeed ( pt, B38400 ) ;
263 t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
265 cfsetospeed ( pt, B19200 ) ;
266 cfsetispeed ( pt, B19200 ) ;
269 cfsetospeed ( pt, B0 ) ;
272 if ( saved ) pt = &oldt ;
275 err = msg ("E2can't happen(ttymode)") ;
279 if ( ! err && tcsetattr ( f->fd, TCSADRAIN, pt ) )
280 err = msg ( "ES2tcsetattr on fd=%d failed:", f->fd ) ;
282 if ( ! err && checktermio ( pt, f ) )
283 msg ( "Wterminal mode not set properly" ) ;
285 tcflow ( f->fd, TCOON ) ; /* in case XON got lost */
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. */
295 void tinit ( TFILE *f, int fd, int reverse, int hwfc )
297 f->ip = f->iq = f->ibuf ;
298 f->obitorder = normalbits ;
299 f->ibitorder = reverse ? reversebits : normalbits ;
302 if ( ! normalbits[1] ) initbittab () ;
306 /* Open a serial fax device as a TFILE. Returns 0 if OK, 1 if
309 int ttyopen ( TFILE *f, char *fname, int reverse, int hwfc )
313 tinit ( f, open ( fname, O_RDWR | O_NDELAY | O_NOCTTY ), reverse, hwfc ) ;
316 if ( errno == EBUSY ) {
319 err = msg ( "ES2can't open serial port %s:", fname ) ;
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 ) ;
333 if ( ioctl ( f->fd, TIOCSSOFTCAR, &arg ) )
334 msg ("WS unable to set software carrier:" ) ;
341 /* UUCP-style device locking using lock files */
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
347 int ttlocked ( char *fname, int log )
352 char buf [ EFAX_PATH_MAX ] = "" ;
354 if ( fname && *fname == BINLKFLAG ) fname++ ;
356 if ( fname && ( f = fopen ( fname , "r" ) ) ) {
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 ) ;
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 ) ;
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:",
378 err = msg ( "I0removed stale lock %s from pid %d", fname, pid ) ;
380 if ( pid != getpid() ) {
382 if ( log ) msg ( "X1 (not our pid)" ) ;
385 if ( log ) msg ( "X3 (our pid)" ) ;
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. */
398 int ttlock ( char *fname, int log )
400 int err=0, dirlen, bin=0 ;
402 pid_t pid = getpid ( ) ;
403 char *p , buf [ EFAX_PATH_MAX ] = "" ;
405 if ( fname && *fname == BINLKFLAG ) {
410 err = ttlocked ( fname, log ) ;
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 ) ;
421 if ( fwrite ( &pid, sizeof(pid_t), 1, f ) < 1 )
422 err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
424 if ( fprintf ( f, "%10d\n", (int) pid ) < 0 )
425 err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
430 if ( rename ( buf , fname ) == 0 ) {
431 chmod ( fname , 0444 ) ;
432 msg ( "Xcreated %s lock file %s", bin ? "binary" : "text", fname ) ;
434 err = ttlocked ( fname, log ) ;
436 err = msg ( "ES2can't rename lock file %s to %s:", buf, fname ) ;
442 if ( err ) remove ( buf ) ;
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. */
452 int ttunlock ( char *fname )
456 if ( fname && *fname == BINLKFLAG ) fname++ ;
458 switch ( ttlocked ( fname, 1 ) ) {
460 case 1: err = msg ( "E1won't remove lock %s (not ours)" , fname ) ; break ;
461 case 2: err = 2 ; break ;
463 if ( remove ( fname ) ) {
464 err = msg ( "ES2can't remove lock %s:", fname ) ;
466 err = msg ( "X0removed lock file %s", fname ) ;
470 err = msg ( "E2can't happen (ttunlock)" ) ;
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. */
481 int lockall ( char **lkfiles, int log )
485 while ( *p && ! err )
486 if ( ( err = ttlock ( *p++, log ) ) == 3 ) err = 0 ;
491 /* Remove all lock files. Returns 0 if all locks removed, 2 on
494 int unlockall ( char **lkfiles )
499 if ( ( i = ttunlock ( *p++ ) ) != 0 ) err = i ;
503 /* Return basename of the argument or the whole thing if can't
506 char *efaxbasename ( char *p )
508 return strrchr ( p , '/' ) ? strrchr ( p , '/' ) + 1 : p ;