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 <sys/socket.h> 16#include <sys/un.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#ifdef __APPLE__ 45#include <sys/ioctl.h> 46#include <mach/mach_port.h> 47#include <IOKit/IOKitLib.h> 48#include <IOKit/IOMessage.h> 49#include <IOKit/serial/IOSerialKeys.h> 50#include <IOKit/serial/ioss.h> 51#include <IOKit/usb/IOUSBLib.h> 52#include <pthread.h> 53 54/* 55 * Constants... 56 */ 57 58#define SYSEVENT_CANSLEEP 0x1 /* Decide whether to allow sleep or not */ 59#define SYSEVENT_WILLSLEEP 0x2 /* Computer will go to sleep */ 60#define SYSEVENT_WOKE 0x4 /* Computer woke from sleep */ 61#define SYSEVENT_MODEMADDED 0x8 /* Modem was added */ 62#define SYSEVENT_MODEMREMOVED 0x10 /* Modem was removed */ 63 64 65/* 66 * Structures... 67 */ 68 69typedef struct threaddatastruct /*** Thread context data ****/ 70{ 71 sysevent_t sysevent; /* System event */ 72} threaddata_t; 73 74 75/* 76 * Globals... 77 */ 78 79static pthread_t sysEventThread = NULL; /* Thread to host a runloop */ 80static pthread_mutex_t sysEventThreadMutex = { 0 }; /* Coordinates access to shared gloabals */ 81static pthread_cond_t sysEventThreadCond = { 0 }; /* Thread initialization complete condition */ 82static CFRunLoopRef sysEventRunloop = NULL; /* The runloop. Access must be protected! */ 83static int sysEventPipes[2] = { -1, -1 }; /* Pipes for system event notifications */ 84sysevent_t sysevent; /* The system event */ 85 86static int clientEventFd = -1; /* Listening socket for client commands */ 87static const char clientSocketName[] = "/var/run/efax"; 88 /* Listener's domain socket name */ 89 90/* 91 * Prototypes... 92 */ 93 94static int sysEventMonitorUpdate(TFILE *f); 95static void *sysEventThreadEntry(); 96static void sysEventPowerNotifier(void *context, io_service_t service, natural_t messageType, void *messageArgument); 97static int clientEventUpdate(); 98static void deviceNotifier(void *context, io_iterator_t iterator); 99 100#endif /* __APPLE__ */ 101 102static int ttlock ( char *fname, int log ); 103static int ckfld ( char *field, int set, int get ); 104static int checktermio ( struct termios *t, TFILE *f ); 105static void tinit ( TFILE *f, int fd, int reverse, int hwfc ); 106static int ttlocked ( char *fname, int log ); 107static int ttunlock ( char *fname ); 108 109 110#ifndef CRTSCTS 111#define CRTSCTS 0 112#endif 113 114 115#if defined(__APPLE__) 116/* Send Mac OS X notification */ 117void notify(CFStringRef status, CFTypeRef value) 118{ 119 CFMutableDictionaryRef notification; 120 int i; 121 static CFNumberRef pid = NULL; 122 123 if (!pid) 124 { 125 i = getpid(); 126 pid = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i); 127 } 128 129 notification = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, 130 &kCFTypeDictionaryKeyCallBacks, 131 &kCFTypeDictionaryValueCallBacks); 132 133 CFDictionaryAddValue(notification, CFSTR("pid"), pid); 134 CFDictionaryAddValue(notification, CFSTR("status"), status); 135 136 if (value) 137 CFDictionaryAddValue(notification, CFSTR("value"), value); 138 139 CFNotificationCenterPostNotificationWithOptions( 140 CFNotificationCenterGetDistributedCenter(), 141 status, 142 CFSTR("com.apple.efax"), 143 notification, kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions); 144 145 CFRelease(notification); 146} 147#endif 148 149/* The milliseconds portion of the current time. If your system 150 does not provide gettimeofday(3) you can safely substitute a 151 dummy function that returns 0. This will cause the 152 milliseconds portion of time stamps to be printed as 0. */ 153 154int time_ms ( void ) 155{ 156 struct timeval tv ; 157 gettimeofday ( &tv, NULL ) ; 158 return (int) ( tv.tv_usec / 1000 ) ; 159} 160 161 162/* Process elapsed time in milliseconds. This is used for 163 ``virtual flow control'' only. */ 164 165long proc_ms ( void ) 166{ 167 struct timeval t ; 168 static int init=0 ; 169 static struct timeval s ; 170 if ( ! init ) { 171 gettimeofday ( &s, 0 ) ; 172 init = 1 ; 173 } 174 gettimeofday ( &t, 0 ) ; 175 return ( t.tv_sec - s.tv_sec ) * 1000 + ( t.tv_usec - s.tv_usec ) / 1000 ; 176} 177 178 179/* Wait for t millisecond (for systems without usleep). */ 180 181void msleep ( int t ) 182{ 183 struct timeval timeout ; 184 timeout.tv_sec = t / 1000 ; 185 timeout.tv_usec = ( t % 1000 ) * 1000 ; 186 if ( select ( 1 , 0 , 0 , 0 , &timeout ) < 0 ) 187 msg ("ES2select failed in msleep:") ; 188} 189 190 191/* Return number of characters ready to read or < 0 on error. t 192 is tenths of a second of idle time before timing out. If t is 193 negative, waits forever. 194 195 1: characters ready to read 196 0: timeout 197 -2: select() failed 198 -6: preparing to sleep 199 -7: woke / manual answer 200 -8: modem detected / cancel session 201 */ 202 203int tdata ( TFILE *f, int t ) 204{ 205 int n, err=0 ; 206 fd_set fds ; 207 int maxfd; 208 struct timeval timeout ; 209 static int modem_added = 0; 210 211 if (modem_added && f->modem_wait) 212 { 213 modem_added = 0; 214 return TDATA_MODEMADDED; 215 } 216 217 timeout.tv_sec = t / 10 ; 218 timeout.tv_usec = ( t % 10 ) * 100000 ; 219 220 FD_ZERO (&fds); 221 222 if (f->fd != -1) 223 FD_SET(f->fd, &fds); 224 225 maxfd = f->fd; 226 227#ifdef __APPLE__ 228 if ( sysEventPipes[0] != -1) 229 { 230 FD_SET(sysEventPipes[0], &fds); 231 232 if (sysEventPipes[0] > maxfd) 233 maxfd = sysEventPipes[0]; 234 } 235 236 if ( clientEventFd != -1) 237 { 238 FD_SET(clientEventFd, &fds); 239 240 if (clientEventFd > maxfd) 241 maxfd = clientEventFd; 242 } 243#endif 244 245 for (;;) 246 { 247 n = select ( maxfd + 1, &fds, 0, 0, t<0 ? 0 : &timeout ) ; 248 249 if (n == 0) 250 break; /* timeout */ 251 252 if (n < 0) 253 { 254 if (errno == EINTR) 255 { 256 if (f->signal) 257 { 258 int sig = f->signal; 259 f->signal = 0; 260 261 (f->onsig)(sig); 262 } 263 else 264 msg("Wselect() interrupted in tdata()"); 265 continue; 266 } 267 268 msg("Eselect() failed in tdata():"); 269 err = TDATA_SELECTERR; 270 break; 271 } 272 273 /* else n > 0 */ 274 275#ifdef __APPLE__ 276 if (sysEventPipes[0] != -1 && FD_ISSET(sysEventPipes[0], &fds)) 277 { 278 err = sysEventMonitorUpdate(f); 279 280 if (err == TDATA_MODEMADDED && !f->modem_wait) 281 { 282 /* If a modem was added but we're not interested just remember it for later... */ 283 modem_added = 1; 284 err = 0; 285 } 286 287 if (err) 288 break; 289 } 290 291 if (clientEventFd != -1 && FD_ISSET(clientEventFd, &fds)) 292 { 293 if ((err = clientEventUpdate()) != 0) 294 break; 295 } 296#endif 297 298 if (f->fd != -1 && FD_ISSET(f->fd, &fds)) 299 { 300 err = 1; 301 break; 302 } 303 } 304 305 return err ; 306} 307 308 309/* tundrflw is called only by the tgetc() macro when the buffer 310 is empty. t is maximum idle time before giving up. Returns 311 number of characters read or EOF on timeout or errors. */ 312 313int tundrflw ( TFILE *f, int t ) 314{ 315 int n ; 316 317 n = tdata ( f, t ) ; 318 319 if ( n > 0 ) 320 if ( ( n = read( f->fd, f->ibuf, IBUFSIZE ) ) < 0 ) 321 msg ( "ES2fax device read:" ) ; 322 323 f->iq = ( f->ip = f->ibuf ) + ( n > 0 ? n : 0 ) ; 324 325 return n > 0 ? n : EOF ; 326} 327 328 329/* tgetd returns the next data character after removing DLE 330 escapes, DLE-ETX terminators and fixing bit order. Evaluates 331 to the next character, EOF on error/timeout, or -2 on DLE-ETX. */ 332 333int tgetd ( TFILE *f, int t ) 334{ 335 int c ; 336 337 if ( ( c = tgetc(f,t) ) < 0 ) 338 c = EOF ; 339 else 340 if ( c != DLE ) 341 c = f->ibitorder[c] ; 342 else { /* escape sequence */ 343 c = tgetc(f,t) ; 344 if ( c == ETX ) 345 c = -2 ; 346 else 347 if ( c == DLE || c == SUB ) 348 c = f->ibitorder [ DLE ] ; 349 else 350 c = msg ( "W0invalid escape sequence (DLE-%s) in data", cname(c) ) ; 351 } 352 353 return c ; 354} 355 356/* Write buffer to modem. Returns 0 or EOF on error. */ 357 358int tput ( TFILE *f, const char *p, int n ) 359{ 360 int m=0 ; 361 362 while ( n > 0 && ( m = write( f->fd, p, n ) ) > 0 ) { 363 if ( m != n ) 364 msg ( "Wonly wrote %d of %d bytes", m, n ) ; 365 n -= m ; 366 p += m ; 367 } 368 369 if ( m < 0 ) 370 msg ( "ES2fax device write:" ) ; 371 372 return m ; 373} 374 375 376/* Compare current termios state with termios struct t. Returns 0 if equal, 377 1 otherwise. */ 378 379static int ckfld ( char *field, int set, int get ) 380{ 381 return set == get ? 382 0 : msg ( "W1 termios.%s is 0%08o, not 0%08o", field, get, set ) ; 383} 384 385static int checktermio ( struct termios *t, TFILE *f ) 386{ 387 struct termios s ; 388 int err=0 ; 389 s.c_iflag=s.c_oflag=s.c_lflag=s.c_cflag=s.c_cc[VMIN]=s.c_cc[VTIME]=0 ; 390 if ( tcgetattr ( f->fd , &s ) ) 391 err = msg ("ES2tcgetattr failed:") ; 392 393 if ( ! err ) return 394 ckfld ( "iflag" , t->c_iflag, s.c_iflag ) || 395 ckfld ( "oflag" , t->c_oflag , s.c_oflag ) || 396 ckfld ( "lflag" , t->c_lflag , s.c_lflag ) || 397 ckfld ( "cflag" , t->c_cflag & 0xFFFF , s.c_cflag & 0xFFFF) || 398 ckfld ( "START" , t->c_cc[VSTART] , s.c_cc[VSTART] ) || 399 ckfld ( "STOP" , t->c_cc[VSTOP] , s.c_cc[VSTOP] ) || 400 ckfld ( "MIN" , t->c_cc[VMIN] , s.c_cc[VMIN] ) || 401 ckfld ( "TIME" , t->c_cc[VTIME] , s.c_cc[VTIME] ) ; 402 return err ; 403} 404 405 406/* Set serial port mode. Sets raw, 8-bit, 19.2 kbps mode with no 407 flow control or as required. Break and parity errors are 408 ignored. CLOCAL means DCD is ignored since some modems 409 apparently drop it during the fax session. Flow control is 410 only used when sending. Returns 0 or 2 on error. */ 411 412int ttymode ( TFILE *f, enum ttymodes mode ) 413{ 414 int err=0, i ; 415 static struct termios t, oldt, *pt ; 416 static int saved=0 ; 417 418 if ( ! saved ) { 419 if ( tcgetattr ( f->fd, &oldt ) ) 420 err = msg ( "ES2tcgetattr on fd=%d failed:", f->fd ) ; 421 else 422 saved=1 ; 423 } 424 425 t.c_iflag = IGNBRK | IGNPAR ; 426 t.c_oflag = 0 ; 427 t.c_cflag = CS8 | CREAD | CLOCAL ; 428 t.c_lflag = 0 ; 429 430 for ( i=0 ; i<NCCS ; i++ ) t.c_cc[i] = 0 ; 431 432 t.c_cc[VMIN] = 1 ; 433 t.c_cc[VTIME] = 0 ; 434 t.c_cc[VSTOP] = XOFF; 435 t.c_cc[VSTART] = XON; 436 437 pt = &t ; 438 439 switch ( mode ) { 440 case VOICESEND : 441 t.c_iflag |= IXON ; 442 t.c_cflag |= f->hwfc ? CRTSCTS : 0 ; 443 case VOICECOMMAND : 444 cfsetospeed ( pt, B38400 ) ; 445 cfsetispeed ( pt, B38400 ) ; 446 break ; 447 case SEND : 448 t.c_iflag |= IXON ; 449 t.c_cflag |= f->hwfc ? CRTSCTS : 0 ; 450 case COMMAND : 451 cfsetospeed ( pt, B19200 ) ; 452 cfsetispeed ( pt, B19200 ) ; 453 break ; 454 case DROPDTR : 455 cfsetospeed ( pt, B0 ) ; 456 break ; 457 case ORIGINAL : 458 if ( saved ) pt = &oldt ; 459 break ; 460 default : 461 err = msg ("E2can't happen(ttymode)") ; 462 break ; 463 } 464 465 /* msg("lttymode: tcsetattr(%d, TCSADRAIN,...)", f->fd); */ 466 467 if ( ! err && tcsetattr ( f->fd, TCSADRAIN, pt ) ) 468 err = msg ( "ES2tcsetattr on fd=%d failed:", f->fd ) ; 469 470 if ( ! err && checktermio ( pt, f ) ) 471 msg ( "Wterminal mode not set properly" ) ; 472 473 tcflow ( f->fd, TCOON ) ; /* in case XON got lost */ 474 475 return err ; 476} 477 478 479/* Initialize TFILE data structure. Bit ordering: serial devices 480 transmit LS bit first. T.4/T.30 says MS bit is sent 481 first. `Normal' order therefore reverses bit order. */ 482 483static void tinit ( TFILE *f, int fd, int reverse, int hwfc ) 484{ 485 f->ip = f->iq = f->ibuf ; 486 f->obitorder = normalbits ; 487 f->ibitorder = reverse ? reversebits : normalbits ; 488 f->fd = fd ; 489 f->hwfc = hwfc ; 490 if ( ! normalbits[1] ) initbittab () ; 491} 492 493 494/* Open a serial fax device as a TFILE. Returns 0 if OK, 1 if 495 busy, 2 on error. */ 496 497int ttyopen ( TFILE *f, char *fname, int reverse, int hwfc ) 498{ 499 int flags, err=0 ; 500 501#if defined(__APPLE__) 502 int fd; 503 504 if ((fd = open(fname, O_RDWR | O_NONBLOCK, 0)) < 0 && errno == ENOENT) 505 { 506 /* 507 * Wait for a device added notification... 508 */ 509 510 /* Let tdata() know we're interested in modem add events... */ 511 f->modem_wait = 1; 512 513 /* Allow idle sleep while we're waiting for a modem... */ 514 waiting = 1; 515 516 do 517 { 518 msg("Iwaiting for modem"); 519 520 err = tdata(f, -1); 521 522 if (err == TDATA_MODEMADDED) 523 msg("Imodem detected..."); 524 525 } while ((fd = open(fname, O_RDWR | O_NONBLOCK, 0)) < 0 && errno == ENOENT); 526 527 /* We're no longer insterested in modem add or idle sleep events... */ 528 f->modem_wait = 0; 529 waiting = 0; 530 } 531 532 if (fd >= 0) 533 { 534 // clear the O_NONBLOCK flag on the port 535 fcntl(fd, F_SETFL, 0); 536 537 // Set exclusive open flag, returns EBUSY if somebody beat us to it. 538 if ((err = ioctl(fd, TIOCEXCL, 0)) != 0) 539 { 540 close(fd); 541 fd = -1; 542 err = 1; 543 } 544 else 545 { 546 /* 547 * Use a domain socket to receive commands from client applications. 548 */ 549 550 if ((clientEventFd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) 551 msg ( "W socket returned error %d - %s", (int)errno, strerror(errno)); 552 else 553 { 554 struct sockaddr_un laddr; 555 mode_t mask; 556 557 bzero(&laddr, sizeof(laddr)); 558 laddr.sun_family = AF_LOCAL; 559 strlcpy(laddr.sun_path, clientSocketName, sizeof(laddr.sun_path)); 560 unlink(laddr.sun_path); 561 mask = umask(0); 562 err = bind(clientEventFd, (struct sockaddr *)&laddr, SUN_LEN(&laddr)); 563 umask(mask); 564 565 if (err < 0) 566 { 567 msg ( "W bind returned error %d - %s", (int)errno, strerror(errno)); 568 close(clientEventFd); 569 clientEventFd = -1; 570 } 571 else 572 { 573 if (listen(clientEventFd, 2) < 0) 574 { 575 msg ( "W listen returned error %d - %s(%d)", (int)errno, strerror(errno)); 576 unlink(clientSocketName); 577 close(clientEventFd); 578 clientEventFd = -1; 579 } 580 } 581 } 582 err = 0; 583 } 584 } 585 586 tinit ( f, fd, reverse, hwfc ) ; 587 588#else 589 tinit ( f, open ( fname, O_RDWR | O_NDELAY | O_NOCTTY ), reverse, hwfc ) ; 590#endif 591 if ( f->fd < 0 ) { 592 if ( errno == EBUSY ) { 593 err = 1 ; 594 } else { 595 err = msg ( "ES2can't open serial port %s:", fname ) ; 596 } 597 } 598 599 if ( ! err ) { 600 if ( ( flags = fcntl( f->fd, F_GETFL, 0 ) ) < 0 || 601 fcntl( f->fd, F_SETFL, ( flags & ~O_NDELAY ) ) < 0 ) 602 err = msg ( "ES2fax device fcntl failed %s:", fname ) ; 603 } 604 605#ifdef TIOCSSOFTCAR 606 { 607 int arg = 1 ; 608 if ( ! err ) 609 if ( ioctl ( f->fd, TIOCSSOFTCAR, &arg ) ) 610 msg ("WS unable to set software carrier:" ) ; 611 } 612#endif 613 614 return err ; 615} 616 617/* Close a serial fax device. Returns 0 if OK. */ 618 619int ttyclose ( TFILE *f ) 620{ 621 /* 622 * Close the listener first so the next efax process can use the same domain socket... 623 */ 624 625 if ( clientEventFd != -1 ) { 626 unlink(clientSocketName); 627 close(clientEventFd); 628 clientEventFd = -1; 629 } 630 631 if ( f->fd != -1 ) { 632 close(f->fd); 633 f->fd = -1; 634 } 635 636 return 0 ; 637} 638 639 /* UUCP-style device locking using lock files */ 640 641/* Test for UUCP lock file & remove stale locks. Returns 0 on null file 642 name or if no longer locked, 1 if locked by another pid, 2 on error, 3 643 if locked by us. */ 644 645static int ttlocked ( char *fname, int log ) 646{ 647 int err=0, ipid ; 648 FILE *f ; 649 pid_t pid = 0 ; 650 char buf [ EFAX_PATH_MAX ] = "" ; 651 652 if ( fname && *fname == BINLKFLAG ) fname++ ; 653 654 if ( fname && ( f = fopen ( fname , "r" ) ) ) { 655 656 if ( fread ( buf, sizeof(char), EFAX_PATH_MAX-1, f ) == sizeof(pid_t) || 657 sscanf ( buf, "%d" , &ipid ) != 1 ) { 658 pid = * (pid_t *) buf ; 659 if ( log ) msg ("X+ read binary pid %d from %s", (int) pid, fname ) ; 660 } else { 661 char *p ; 662 pid = (pid_t) ipid ; 663 if ( log ) { 664 msg ( "X+ read HDB pid %d [", (int) pid ) ; 665 for ( p=buf ; *p ; p++ ) msg ( "X+ %s", cname ( *p ) ) ; 666 msg ( "X+ ] from %s", fname ) ; 667 } 668 } 669 670 if ( kill ( pid, 0 ) && errno == ESRCH ) { 671 if ( log ) msg ("X - stale" ) ; 672 if ( remove ( fname ) ) 673 err = msg ( "ES2can't remove stale lock %s from pid %d:", 674 fname, pid ) ; 675 else 676 err = msg ( "I0removed stale lock %s from pid %d", fname, pid ) ; 677 } else { 678 if ( pid != getpid() ) { 679 err = 1 ; 680 if ( log ) msg ( "X1 (not our pid)" ) ; 681 } else { 682 err = 3 ; 683 if ( log ) msg ( "X3 (our pid)" ) ; 684 } 685 } 686 fclose ( f ) ; 687 } 688 return err ; 689} 690 691 692/* Create UUCP (text or binary) lock file. Returns 0 on null 693 file name or if created, 1 if locked by another pid, 2 on 694 error, 3 if locked by us. */ 695 696static int ttlock ( char *fname, int log ) 697{ 698 int err=0, dirlen, bin=0 ; 699 FILE *f=0 ; 700 pid_t pid = getpid ( ) ; 701 char *p , buf [ EFAX_PATH_MAX ] = "" ; 702 703 if ( fname && *fname == BINLKFLAG ) { 704 fname++ ; 705 bin = 1 ; 706 } 707 708 err = ttlocked ( fname, log ) ; 709 710 if ( ! err ) { 711 dirlen = ( p = strrchr( fname , '/' ) ) ? p-fname+1 : strlen ( fname ) ; 712 snprintf ( buf , sizeof(buf), "%.*sTMP..%05d" , dirlen , fname , (int) pid ) ; 713 if ( ! ( f = fopen( buf, "w" ) ) ) 714 err = msg ( "ES2can't open pre-lock file %s:", buf ) ; 715 } 716 717 if ( ! err && f ) { 718 if ( bin ) { 719 if ( fwrite ( &pid, sizeof(pid_t), 1, f ) < 1 ) 720 err = msg ( "ES2can't write pre-lock file %s:", buf ) ; 721 } else { 722 if ( fprintf ( f, "%10d", (int) pid ) < 0 ) 723 err = msg ( "ES2can't write pre-lock file %s:", buf ) ; 724 } 725 } 726 727 if ( ! err && f ) { 728 if ( rename ( buf , fname ) == 0 ) { 729 chmod ( fname , 0444 ) ; 730 msg ( "Xcreated %s lock file %s", bin ? "binary" : "text", fname ) ; 731 } else { 732 err = ttlocked ( fname, log ) ; 733 if ( ! err ) 734 err = msg ( "ES2can't rename lock file %s to %s:", buf, fname ) ; 735 } 736 } 737 738 if ( f ) { 739 fclose ( f ) ; 740 if ( err ) remove ( buf ) ; 741 } 742 743 return err ; 744} 745 746 747/* Remove lock file. Returns 0 on null file name, doesn't exist, or was 748 removed, 1 if the lock is to another pid, 2 on errors. */ 749 750static int ttunlock ( char *fname ) 751{ 752 int err = 0 ; 753 754 if ( fname && *fname == BINLKFLAG ) fname++ ; 755 756 switch ( ttlocked ( fname, 1 ) ) { 757 case 0: break ; 758 case 1: err = msg ( "E1won't remove lock %s (not ours)" , fname ) ; break ; 759 case 2: err = 2 ; break ; 760 case 3: 761 if ( remove ( fname ) ) { 762 err = msg ( "ES2can't remove lock %s:", fname ) ; 763 } else { 764 err = msg ( "X0removed lock file %s", fname ) ; 765 } 766 break ; 767 default: 768 err = msg ( "E2can't happen (ttunlock)" ) ; 769 break ; 770 } 771 return err ; 772} 773 774 775/* Lock all lock files and possibly log attempt if log=1. 776 Returns 0 if all locks [already] applied, 1 if any are locked 777 to other pids, 2 on any errors. */ 778 779int lockall ( TFILE *f, char **lkfiles, int log ) 780{ 781 int err = 0 ; 782 char **p = lkfiles ; 783 784#if defined(__APPLE__) 785 msg("llockall: disallow premption (fd %d)", f->fd); 786 if (f->fd > 0) 787 { 788 int allowPremption = 0; 789 if ((err = ioctl(f->fd, IOSSPREEMPT, &allowPremption)) != 0) 790 err = 1; 791 } 792 793#endif /* __APPLE__ */ 794 795 while ( *p && ! err ) 796 if ( ( err = ttlock ( *p++, log ) ) == 3 ) err = 0 ; 797 return err ; 798} 799 800 801/* Remove all lock files. Returns 0 if all locks removed, 2 on 802 errors. */ 803 804int unlockall (TFILE *f, char **lkfiles ) 805{ 806 int err = 0, i ; 807 808 char **p = lkfiles ; 809 while ( *p ) 810 if ( ( i = ttunlock ( *p++ ) ) != 0 ) err = i ; 811 812#if defined(__APPLE__) 813 int allowPremption = 1; 814 msg("llockall: allow premption (fd %d)", f->fd); 815 ioctl(f->fd, IOSSPREEMPT, &allowPremption); 816#endif /* __APPLE__ */ 817 818 return err ; 819} 820 821/* Return basename of the argument or the whole thing if can't 822 find it. */ 823 824char *efaxbasename ( char *p ) 825{ 826 return strrchr ( p , '/' ) ? strrchr ( p , '/' ) + 1 : p ; 827} 828 829 830#ifdef __APPLE__ 831 832/* 833 * 'sysEventMonitorStart()' - Start system event notifications 834 */ 835 836void sysEventMonitorStart(void) 837{ 838 int flags; 839 840 pipe(sysEventPipes); 841 842 /* 843 * Set non-blocking mode on the descriptor we will be receiving notification events on. 844 */ 845 846 flags = fcntl(sysEventPipes[0], F_GETFL, 0); 847 fcntl(sysEventPipes[0], F_SETFL, flags | O_NONBLOCK); 848 849 /* 850 * Start the thread that runs the runloop... 851 */ 852 853 pthread_mutex_init(&sysEventThreadMutex, NULL); 854 pthread_cond_init(&sysEventThreadCond, NULL); 855 pthread_create(&sysEventThread, NULL, sysEventThreadEntry, NULL); 856} 857 858 859/* 860 * 'sysEventMonitorStop()' - Stop system event notifications 861 */ 862 863void sysEventMonitorStop(void) 864{ 865 CFRunLoopRef rl; /* The runloop */ 866 867 868 if (sysEventThread) 869 { 870 /* 871 * Make sure the thread has completed it's initialization and 872 * stored it's runloop reference in the shared global. 873 */ 874 875 pthread_mutex_lock(&sysEventThreadMutex); 876 877 if (sysEventRunloop == NULL) 878 pthread_cond_wait(&sysEventThreadCond, &sysEventThreadMutex); 879 880 rl = sysEventRunloop; 881 sysEventRunloop = NULL; 882 883 pthread_mutex_unlock(&sysEventThreadMutex); 884 885 if (rl) 886 CFRunLoopStop(rl); 887 888 pthread_join(sysEventThread, NULL); 889 pthread_mutex_destroy(&sysEventThreadMutex); 890 pthread_cond_destroy(&sysEventThreadCond); 891 } 892 893 if (sysEventPipes[0] >= 0) 894 { 895 close(sysEventPipes[0]); 896 close(sysEventPipes[1]); 897 898 sysEventPipes[0] = -1; 899 sysEventPipes[1] = -1; 900 } 901} 902 903 904/* 905 * 'sysEventMonitorUpdate()' - Handle power & network system events. 906 * 907 * Returns non-zero if a higher level event needs to be handeled. 908 */ 909 910static int sysEventMonitorUpdate(TFILE *f) 911{ 912 int err = 0; 913 914 /* 915 * Drain the event pipe... 916 */ 917 918 if (read((int)sysEventPipes[0], &sysevent, sizeof(sysevent)) == sizeof(sysevent)) 919 { 920 if ((sysevent.event & SYSEVENT_CANSLEEP)) 921 { 922 /* 923 * If we're waiting for the phone to ring allow the idle sleep, otherwise 924 * block it so we can finish the current session. 925 */ 926 if (waiting) 927 IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); 928 else 929 { 930 msg("Isleep canceled because of active job"); 931 IOCancelPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); 932 } 933 } 934 935 if ((sysevent.event & SYSEVENT_WILLSLEEP)) 936 { 937 /* 938 * If we're waiting return an error so answer can reset the modem and close the port, 939 * otherwise cancel the current session right here. 940 */ 941 if (waiting) 942 { 943 msg("Ipreparing to sleep..."); 944 err = TDATA_SLEEP; 945 } 946 else 947 { 948 msg("Iterminating to sleep or shutdown..."); 949 cleanup(6); 950 IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); 951 exit(6); 952 } 953 } 954 955 if ((sysevent.event & SYSEVENT_WOKE)) 956 { 957 IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); 958 if (waiting) 959 err = TDATA_WAKE; 960 } 961 962 if ((sysevent.event & SYSEVENT_MODEMADDED)) 963 err = TDATA_MODEMADDED; 964 965 if ((sysevent.event & SYSEVENT_MODEMREMOVED)) 966 { 967 msg("Imodem removed..."); 968 cleanup(4); 969 exit(4); 970 } 971 } 972 973 return err; 974} 975 976 977/* 978 * 'sysEventThreadEntry()' - A thread to run a runloop on. 979 * Receives power & network change notifications. 980 */ 981 982static void *sysEventThreadEntry() 983{ 984 io_object_t powerNotifierObj; /* Power notifier object */ 985 IONotificationPortRef powerNotifierPort; /* Power notifier port */ 986 CFRunLoopSourceRef powerRLS = NULL; /* Power runloop source */ 987 threaddata_t threadData; /* Thread context data for the runloop notifiers */ 988 IONotificationPortRef addNotification, /* Add notification port */ 989 removeNotification; /* Remove notification port */ 990 io_iterator_t addIterator, /* Add iterator */ 991 removeIterator; /* Remove iterator */ 992 mach_port_t masterPort; /* Master port */ 993 kern_return_t kr; /* Kernel error */ 994 CFMutableDictionaryRef classesToMatch; /* Dictionary to match */ 995 static const sysevent_t sysevent_modemadded = { SYSEVENT_MODEMADDED }; 996 /* Modem added event */ 997 static const sysevent_t sysevent_modemremoved = { SYSEVENT_MODEMREMOVED }; 998 /* Modem removed event */ 999 1000 bzero(&threadData, sizeof(threadData)); 1001 addNotification = \ 1002 removeNotification = NULL; 1003 addIterator = \ 1004 removeIterator = IO_OBJECT_NULL; 1005 1006 /* 1007 * Register for power state change notifications. 1008 */ 1009 1010 threadData.sysevent.powerKernelPort = IORegisterForSystemPower(&threadData, &powerNotifierPort, sysEventPowerNotifier, &powerNotifierObj); 1011 if (threadData.sysevent.powerKernelPort) 1012 { 1013 powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort); 1014 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode); 1015 } 1016 1017 /* 1018 * Register for IOKit serial device added & removed notifications. 1019 */ 1020 1021 kr = IOMasterPort(bootstrap_port, &masterPort); 1022 1023 if (kr == kIOReturnSuccess && masterPort != MACH_PORT_NULL) 1024 { 1025 if ((classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue)) != NULL) 1026 { 1027 CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType)); 1028 1029 /* 1030 * Each IOServiceAddMatchingNotification() call consumes a dictionary reference 1031 * so retain it for the second call below. 1032 */ 1033 1034 CFRetain(classesToMatch); 1035 1036 removeNotification = IONotificationPortCreate(masterPort); 1037 kr = IOServiceAddMatchingNotification( removeNotification, 1038 kIOTerminatedNotification, 1039 classesToMatch, 1040 &deviceNotifier, 1041 (void*)&sysevent_modemremoved, 1042 &removeIterator); 1043 1044 if (kr == kIOReturnSuccess && removeIterator != IO_OBJECT_NULL) 1045 { 1046 deviceNotifier((void*)&sysevent_modemremoved, removeIterator); 1047 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(removeNotification), kCFRunLoopDefaultMode); 1048 } 1049 1050 addNotification = IONotificationPortCreate(masterPort); 1051 kr = IOServiceAddMatchingNotification(addNotification, 1052 kIOMatchedNotification, 1053 classesToMatch, 1054 &deviceNotifier, 1055 (void*)&sysevent_modemadded, 1056 &addIterator); 1057 1058 if (kr == kIOReturnSuccess && addIterator != IO_OBJECT_NULL) 1059 { 1060 deviceNotifier((void*)&sysevent_modemadded, addIterator); 1061 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode); 1062 } 1063 } 1064 mach_port_deallocate(mach_task_self(), masterPort); 1065 } 1066 1067 /* 1068 * Store our runloop in a global so the main thread can 1069 * use it to stop us. 1070 */ 1071 1072 pthread_mutex_lock(&sysEventThreadMutex); 1073 1074 sysEventRunloop = CFRunLoopGetCurrent(); 1075 1076 pthread_cond_signal(&sysEventThreadCond); 1077 pthread_mutex_unlock(&sysEventThreadMutex); 1078 1079 /* 1080 * Disappear into the runloop until it's stopped by the main thread. 1081 */ 1082 1083 CFRunLoopRun(); 1084 1085 /* 1086 * Clean up before exiting. 1087 */ 1088 1089 if (addIterator != IO_OBJECT_NULL) 1090 IOObjectRelease(addIterator); 1091 1092 if (addNotification != NULL) 1093 IONotificationPortDestroy(addNotification); 1094 1095 if (removeIterator != IO_OBJECT_NULL) 1096 IOObjectRelease(removeIterator); 1097 1098 if (removeNotification != NULL) 1099 IONotificationPortDestroy(removeNotification); 1100 1101 if (powerRLS) 1102 { 1103 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode); 1104 CFRunLoopSourceInvalidate(powerRLS); 1105 } 1106 1107 if (threadData.sysevent.powerKernelPort) 1108 IODeregisterForSystemPower(&powerNotifierObj); 1109 1110 pthread_exit(NULL); 1111} 1112 1113 1114/* 1115 * 'sysEventPowerNotifier()' - . 1116 */ 1117 1118static void sysEventPowerNotifier(void *context, io_service_t service, natural_t messageType, void *messageArgument) 1119{ 1120 threaddata_t *threadData = (threaddata_t *)context; /* Thread context data */ 1121 (void)service; /* anti-compiler-warning-code */ 1122 1123 threadData->sysevent.event = 0; 1124 1125 switch (messageType) 1126 { 1127 case kIOMessageCanSystemPowerOff: 1128 case kIOMessageCanSystemSleep: 1129 threadData->sysevent.event = SYSEVENT_CANSLEEP; 1130 break; 1131 1132 case kIOMessageSystemWillRestart: 1133 case kIOMessageSystemWillPowerOff: 1134 case kIOMessageSystemWillSleep: 1135 threadData->sysevent.event = SYSEVENT_WILLSLEEP; 1136 break; 1137 1138 case kIOMessageSystemHasPoweredOn: 1139 threadData->sysevent.event = SYSEVENT_WOKE; 1140 break; 1141 1142 case kIOMessageSystemWillNotPowerOff: 1143 case kIOMessageSystemWillNotSleep: 1144 case kIOMessageSystemWillPowerOn: 1145 default: 1146 IOAllowPowerChange(threadData->sysevent.powerKernelPort, (long)messageArgument); 1147 break; 1148 } 1149 1150 if (threadData->sysevent.event) 1151 { 1152 /* 1153 * Send the event to the main thread. 1154 */ 1155 threadData->sysevent.powerNotificationID = (long)messageArgument; 1156 write((int)sysEventPipes[1], &threadData->sysevent, sizeof(threadData->sysevent)); 1157 } 1158} 1159 1160 1161/* 1162 * 'clientEventUpdate()' - Read process a command from an incoming client connection. 1163 */ 1164 1165static int clientEventUpdate() 1166{ 1167 int n, err = 0; 1168 int client_fd; 1169 fd_set client_fds ; 1170 struct timeval client_timeout ; 1171 struct sockaddr_un client_addr; 1172 socklen_t addrlen; 1173 char client_buf[255]; 1174 1175 /* 1176 * Accept the incomming connection request... 1177 */ 1178 1179 if ((client_fd = accept(clientEventFd, (struct sockaddr *)&client_addr, &addrlen)) < 0) 1180 msg ( "W0client accept error %d - %s", (int)errno, strerror(errno)); 1181 else 1182 { 1183 /* 1184 * Give the client 1 second to send us a command... 1185 */ 1186 1187 client_timeout.tv_sec = 1; 1188 client_timeout.tv_usec = 0 ; 1189 1190 FD_ZERO (&client_fds); 1191 FD_SET(client_fd, &client_fds); 1192 1193 n = select ( client_fd + 1, &client_fds, 0, 0, &client_timeout); 1194 1195 if (n <= 0) 1196 msg ( "W0client select error %d - %s", (int)errno, strerror(errno)); 1197 else /* (n > 0) */ 1198 { 1199 n = recv(client_fd, client_buf, sizeof(client_buf)-1, 0); 1200 if (n < 0) 1201 msg ( "W0client recv error %d - %s", (int)errno, strerror(errno)); 1202 else if (n > 0) 1203 { 1204 client_buf[n-1] = '\0'; 1205 1206 switch (client_buf[0]) 1207 { 1208 case 'a': /* Manual answer... */ 1209 msg ( "l manual answer"); 1210 manual_answer = 1; 1211 1212 if (answer_wait) /* Only return an error if we're waiting for activity... */ 1213 err = TDATA_MANANSWER; 1214 break; 1215 1216 case 'c': /* Cancel current session... */ 1217 msg ( "l cancel session"); 1218 err = TDATA_CANCEL; 1219 1220 /* 1221 * Close the listen socket so we're not interupped while cleaning up... 1222 */ 1223 1224 unlink(clientSocketName); 1225 close(clientEventFd); 1226 clientEventFd = -1; 1227 close(client_fd); 1228 1229#if defined(__APPLE__) 1230 notify(CFSTR("disconnecting"), NULL); 1231#endif 1232 exit ( cleanup ( 7 ) ) ; 1233 break; /* anti-compiler warning */ 1234 1235 default: 1236 msg ( "W unknown client command \"%s\"", client_buf); 1237 break; 1238 } 1239 } 1240 1241 close(client_fd); 1242 } 1243 } 1244 return err; 1245} 1246 1247 1248/* 1249 * 'deviceNotifier()' - Called when a serial or modem device is added or removed. 1250 */ 1251 1252static void deviceNotifier(void *context, io_iterator_t iterator) 1253{ 1254 int matched = false; /* Matched the right device? */ 1255 io_service_t obj; /* IOKit object */ 1256 CFTypeRef cfstr; /* CFString */ 1257 char bsdpath[PATH_MAX + 1]; /* BSD path ("/dev/<something>") */ 1258 1259 /* 1260 * Iterate over the devices looking for one that matches the modem to use 1261 * (always drain the iterator so we get future notifications). 1262 */ 1263 1264 while ((obj = IOIteratorNext(iterator)) != IO_OBJECT_NULL) 1265 { 1266 if (!matched && (cfstr = IORegistryEntryCreateCFProperty(obj, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0))) 1267 { 1268 CFStringGetCString(cfstr, bsdpath, sizeof(bsdpath), kCFStringEncodingUTF8); 1269 CFRelease(cfstr); 1270 matched = strcmp(bsdpath, faxfile) == 0; 1271 } 1272 1273 if (!matched && (cfstr = IORegistryEntryCreateCFProperty(obj, CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0))) 1274 { 1275 CFStringGetCString(cfstr, bsdpath, sizeof(bsdpath), kCFStringEncodingUTF8); 1276 CFRelease(cfstr); 1277 matched = strcmp(bsdpath, faxfile) == 0; 1278 } 1279 1280 IOObjectRelease(obj); 1281 } 1282 1283 /* 1284 * If we matched send the event to the main thread. 1285 */ 1286 1287 if (matched) 1288 write((int)sysEventPipes[1], (sysevent_t *)context, sizeof(sysevent)); 1289} 1290 1291#endif /* __APPLE__ */ 1292