ModemServer.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:42k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: ModemServer.c++,v 1.13 2008/07/26 17:57:09 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1990-1996 Sam Leffler
  4.  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. #include "Sys.h"
  27. #include <ctype.h>
  28. #include <termios.h>
  29. #include <errno.h>
  30. #include <sys/param.h>
  31. #include <sys/time.h>
  32. #if HAS_MODEM_H
  33. #include <sys/modem.h>
  34. #else
  35. #include <sys/ioctl.h>
  36. #endif
  37. #if HAS_TERMIOX
  38. #include <sys/termiox.h>
  39. #endif
  40. #include <sys/file.h>
  41. #include "Dispatcher.h"
  42. #include "FaxTrace.h"
  43. #include "FaxMachineLog.h"
  44. #include "ModemServer.h"
  45. #include "UUCPLock.h"
  46. #include "Class0.h"
  47. #include "config.h"
  48. #ifndef O_NOCTTY
  49. #define O_NOCTTY 0 // no POSIX O_NOCTTY support
  50. #endif
  51. /*
  52.  * HylaFAX Modem Server.
  53.  */
  54. // map ClassModem::BaudRate to numeric value
  55. static const u_int baudRates[] = {
  56.     0, // BR0
  57.     300, // BR300
  58.     1200, // BR1200
  59.     2400, // BR2400
  60.     4800, // BR4800
  61.     9600, // BR9600
  62.     19200, // BR19200
  63.     38400, // BR38400
  64.     57600, // BR57600
  65.     76800, // BR76800
  66.     115200, // BR115200
  67. };
  68. ModemServer::ModemServer(const fxStr& devName, const fxStr& devID)
  69.     : modemDevice(devName)
  70.     , modemDevID(devID)
  71.     , configFile(fxStr(FAX_CONFIG) | "." | devID)
  72. {
  73.     state = BASE;
  74.     statusFile = NULL;
  75.     abortCall = false;
  76.     deduceComplain = true; // first failure causes complaint
  77.     changePriority = true;
  78.     delayConfig = false;
  79.     inputBuffered = true; // OSes buffer by default
  80.     modemFd = -1;
  81.     modem = NULL;
  82.     curRate = ClassModem::BR0; // unspecified baud rate
  83.     curParity = NONE; // default is 8-bit/no parity
  84.     curVMin = 127; // buffer input by default
  85.     curVTime = 1; // ditto
  86.     setupAttempts = 0;
  87.     rcvCC = rcvNext = rcvBit = gotByte = 0;
  88.     sawBlockEnd = false;
  89.     timeout = false;
  90.     log = NULL;
  91.     readyStateMsg = NULL;
  92. }
  93. ModemServer::~ModemServer()
  94. {
  95.     delete log;
  96.     delete modem;
  97.     if (statusFile)
  98. fclose(statusFile);
  99. }
  100. #include "faxApp.h"
  101. /*
  102.  * Initialize the server from command line arguments.
  103.  */
  104. void
  105. ModemServer::initialize(int argc, char** argv)
  106. {
  107.     for (GetoptIter iter(argc, argv, faxApp::getOpts()); iter.notDone(); iter++)
  108. switch (iter.option()) {
  109. case 'p':
  110.     changePriority = false;
  111.     break;
  112. case 'x':
  113.     tracingMask &= ~(FAXTRACE_MODEMIO|FAXTRACE_TIMEOUTS);
  114.     break;
  115. }
  116.     TIFFSetErrorHandler(NULL);
  117.     TIFFSetWarningHandler(NULL);
  118.     // setup server's status file
  119.     statusFile = Sys::fopen(FAX_STATUSDIR "/" | modemDevID, "w");
  120.     if (statusFile != NULL) {
  121. #if HAS_FCHMOD
  122. fchmod(fileno(statusFile), 0644);
  123. #else
  124. Sys::chmod(FAX_STATUSDIR "/" | modemDevID, 0644);
  125. #endif
  126. setServerStatus("Initializing server");
  127.     }
  128.     umask(077); // keep all temp files private
  129.     updateConfig(configFile); // read config file
  130. }
  131. /*
  132.  * Startup the server for the first time.
  133.  */
  134. void
  135. ModemServer::open()
  136. {
  137.     if (lockModem()) {
  138. bool modemReady = setupModem();
  139. unlockModem();
  140. if (!modemReady)
  141.     changeState(MODEMWAIT, pollModemWait);
  142. else
  143.     changeState(RUNNING, pollLockWait);
  144.     } else {
  145. traceServer("%s: Can not lock device.", (const char*) modemDevice);
  146. changeState(LOCKWAIT, pollLockWait);
  147.     }
  148. }
  149. /*
  150.  * Close down the server.
  151.  */
  152. void
  153. ModemServer::close()
  154. {
  155.     if (lockModem()) {
  156. if (modem)
  157.     modem->hangup();
  158. discardModem(true);
  159. unlockModem();
  160.     }
  161. }
  162. const char* ModemServer::stateNames[9] = {
  163.     "BASE",
  164.     "RUNNING",
  165.     "MODEMWAIT",
  166.     "LOCKWAIT",
  167.     "GETTYWAIT",
  168.     "SENDING",
  169.     "ANSWERING",
  170.     "RECEIVING",
  171.     "LISTENING"
  172. };
  173. const char* ModemServer::stateStatus[9] = {
  174.     "Initializing server and modem", // BASE
  175.     "Running and idle", // RUNNING
  176.     "Waiting for modem to come ready", // MODEMWAIT
  177.     "Waiting for modem to come free", // LOCKWAIT
  178.     "Waiting for login session to terminate", // GETTYWAIT
  179.     "Sending facsimile", // SENDING
  180.     "Answering the phone", // ANSWERING
  181.     "Receiving facsimile", // RECEIVING
  182.     "Listening to rings from modem", // LISTENING
  183. };
  184. /*
  185.  * Change the server's state and, optionally,
  186.  * start a timer running for timeout seconds.
  187.  */
  188. void
  189. ModemServer::changeState(ModemServerState s, long timeout, const char* msg)
  190. {
  191.     if (s != state) {
  192. if (timeout)
  193.     traceStatus(FAXTRACE_STATETRANS,
  194. "STATE CHANGE: %s -> %s (timeout %ld)",
  195. stateNames[state], stateNames[s], timeout);
  196. else
  197.     traceStatus(FAXTRACE_STATETRANS, "STATE CHANGE: %s -> %s",
  198. stateNames[state], stateNames[s]);
  199. state = s;
  200. if (changePriority)
  201.     setProcessPriority(state);
  202. if (modemFd >= 0)
  203.     setInputBuffering(state != RUNNING && state != SENDING &&
  204. state != ANSWERING && state != RECEIVING && state != LISTENING);
  205. if (state == RUNNING) {
  206.     fxStr statusmsg = msg ? fxStr(msg) : fxStr(stateStatus[state]);
  207.     if (readyStateMsg) statusmsg.append(readyStateMsg);
  208.     setServerStatus(statusmsg);
  209. } else {
  210.     setServerStatus(msg ? msg : stateStatus[state]);
  211. }
  212. switch (state) {
  213. case RUNNING:
  214.     notifyModemReady(); // notify surrogate
  215.     break;
  216. case MODEMWAIT:
  217.     setupAttempts = 0;
  218.     break;
  219. default:
  220.     break;
  221. }
  222.     } else if (s == MODEMWAIT && ++setupAttempts >= maxSetupAttempts) {
  223. traceStatus(FAXTRACE_SERVER,
  224.     "Unable to setup modem on %s; giving up after %d attempts",
  225.     (const char*) modemDevice, setupAttempts);
  226. notifyModemWedged();
  227.     }
  228.     /*
  229.      * Before we start any timer, make sure we stop the current one
  230.      */
  231.     Dispatcher::instance().stopTimer(this);
  232.     if (timeout)
  233. Dispatcher::instance().startTimer(timeout, 0, this);
  234. }
  235. #if HAS_SCHEDCTL
  236. #include <sys/schedctl.h>
  237. /*
  238.  * When low latency is required, use a nondegrading process
  239.  * priority; otherwise just remove any nondegrading priority.
  240.  * Note that we assign a high nondegrading priority when sending,
  241.  * answering the telephone, or receiving.  We assume that if the
  242.  * incoming call spawns a getty process that the priority will
  243.  * be reset in the child before the getty is exec'd.
  244.  */
  245. static const int schedCtlParams[9][2] = {
  246.     { NDPRI, 0 }, // BASE
  247.     { NDPRI, 0 }, // RUNNING
  248.     { NDPRI, 0 }, // MODEMWAIT
  249.     { NDPRI, 0 }, // LOCKWAIT
  250.     { NDPRI, 0 }, // GETTYWAIT
  251.     { NDPRI, NDPHIMIN }, // SENDING
  252.     { NDPRI, NDPHIMIN }, // ANSWERING
  253.     { NDPRI, NDPHIMIN }, // RECEIVING
  254.     { NDPRI, 0 }, // LISTENING
  255. };
  256. #elif HAS_PRIOCNTL
  257. extern "C" {
  258. #include <sys/priocntl.h>
  259. #ifdef HAS_FPPRIOCNTL
  260. #include <sys/fppriocntl.h>
  261. #else
  262. #include <sys/rtpriocntl.h>
  263. #endif
  264. #include <sys/tspriocntl.h>
  265. }
  266. static struct SchedInfo {
  267.     const char* clname; // scheduling class name
  268.     int params[3]; // scheduling class parameters
  269. } schedInfo[9] = {
  270.     { "TS", { TS_NOCHANGE, TS_NOCHANGE } }, // BASE
  271.     { "TS", { TS_NOCHANGE, TS_NOCHANGE } }, // RUNNING
  272.     { "TS", { TS_NOCHANGE, TS_NOCHANGE } }, // MODEMWAIT
  273.     { "TS", { TS_NOCHANGE, TS_NOCHANGE } }, // LOCKWAIT
  274.     { "TS", { TS_NOCHANGE, TS_NOCHANGE } }, // GETTYWAIT
  275. #ifdef HAS_FPPRIOCNTL // if still fails, set prio to 0
  276.     { "FP", { FP_NOCHANGE, FP_NOCHANGE, FP_TQDEF } },// SENDING
  277.     { "FP", { FP_NOCHANGE, FP_NOCHANGE, FP_TQDEF } },// ANSWERING
  278.     { "FP", { FP_NOCHANGE, FP_NOCHANGE, FP_TQDEF } },// RECEIVING
  279. #else
  280.     { "RT", { RT_NOCHANGE, RT_NOCHANGE, RT_NOCHANGE } },// SENDING
  281.     { "RT", { RT_NOCHANGE, RT_NOCHANGE, RT_NOCHANGE } },// ANSWERING
  282.     { "RT", { RT_NOCHANGE, RT_NOCHANGE, RT_NOCHANGE } },// RECEIVING
  283. #endif
  284.     { "TS", { TS_NOCHANGE, TS_NOCHANGE } }, // LISTENING
  285. };
  286. #elif HAS_RTPRIO
  287. /*
  288.  * On HP-UX, a real-time priority is between 0 (high)
  289.  * and 127 (low);  for now we use 120.
  290.  */
  291. #include <sys/rtprio.h>
  292. #ifndef RTPRIO_HIGH
  293. #define RTPRIO_HIGH 120
  294. #endif
  295. static const int rtprioParams[9] = {
  296.     RTPRIO_RTOFF, // BASE
  297.     RTPRIO_RTOFF, // RUNNING
  298.     RTPRIO_RTOFF, // MODEMWAIT
  299.     RTPRIO_RTOFF, // LOCKWAIT
  300.     RTPRIO_RTOFF, // GETTYWAIT
  301.     RTPRIO_HIGH, // SENDING
  302.     RTPRIO_HIGH, // ANSWERING
  303.     RTPRIO_HIGH, // RECEIVING
  304.     RTPRIO_RTOFF, // LISTENING
  305. };
  306. #elif HAS_POSIXSCHED
  307. /*
  308.  * In POSIX (i.e. Linux), a real time priority is
  309.  * between 1 (low) and 99 (high);
  310.  * for now we conservatively use 1.  Priority of 0 
  311.  * is without real-time application.
  312.  */
  313. #include <sched.h>
  314. static const struct SchedInfo {
  315.     int policy;
  316.     int priority;
  317. } sched_setschedulerParams[9] = {
  318.     {SCHED_OTHER, 0}, // BASE
  319.     {SCHED_OTHER, 0}, // RUNNING
  320.     {SCHED_OTHER, 0}, // MODEMWAIT
  321.     {SCHED_OTHER, 0}, // LOCKWAIT
  322.     {SCHED_OTHER, 0}, // GETTYWAIT
  323.     {SCHED_FIFO, RT_PRIORITY}, // SENDING
  324.     {SCHED_FIFO, RT_PRIORITY}, // ANSWERING
  325.     {SCHED_FIFO, RT_PRIORITY}, // RECEIVING
  326.     {SCHED_OTHER, 0}, // LISTENING
  327. };
  328. #endif
  329. void
  330. ModemServer::setProcessPriority(ModemServerState s)
  331. {
  332.     if (priorityScheduling) {
  333. #if HAS_SCHEDCTL
  334.     uid_t euid = geteuid();
  335.     if (seteuid(0) >= 0) { // must be done as root
  336. if (schedctl(schedCtlParams[s][0], 0, schedCtlParams[s][1]) < 0)
  337.     traceServer("schedctl: %m");
  338. if (seteuid(euid) < 0) // restore previous effective uid
  339.     traceServer("seteuid(%d): %m", euid);
  340.     } else
  341. traceServer("seteuid(root): %m");
  342. #elif HAS_PRIOCNTL
  343.     uid_t euid = geteuid();
  344.     if (seteuid(0) >= 0) { // must be done as root
  345. const SchedInfo& si = schedInfo[s];
  346. pcinfo_t pcinfo;
  347. strcpy(pcinfo.pc_clname, si.clname);
  348. if (priocntl((idtype_t)0, 0, PC_GETCID, (caddr_t)&pcinfo) >= 0) {
  349.     pcparms_t pcparms;
  350.     pcparms.pc_cid = pcinfo.pc_cid;
  351. #ifdef HAS_FPPRIOCNTL
  352.     if (streq(si.clname, "FP")) {
  353. fpparms_t* fpp = (fpparms_t*) pcparms.pc_clparms;
  354. fpp->fp_pri = si.params[0];
  355. fpp->fp_tqsecs = (ulong) si.params[1];
  356. fpp->fp_tqnsecs = si.params[2];
  357. #else
  358.     if (streq(si.clname, "RT")) {
  359. rtparms_t* rtp = (rtparms_t*) pcparms.pc_clparms;
  360. rtp->rt_pri = si.params[0];
  361. rtp->rt_tqsecs = (ulong) si.params[1];
  362. rtp->rt_tqnsecs = si.params[2];
  363. #endif
  364.     } else {
  365. tsparms_t* tsp = (tsparms_t*) pcparms.pc_clparms;
  366. tsp->ts_uprilim = si.params[0];
  367. tsp->ts_upri = si.params[1];
  368.     }
  369.     if (priocntl(P_PID, P_MYID, PC_SETPARMS, (caddr_t)&pcparms) < 0)
  370. traceServer("Unable to set %s scheduling parameters: %m",
  371.     si.clname);
  372. } else
  373.     traceServer("priocntl(%s): %m", si.clname);
  374. if (seteuid(euid) < 0) // restore previous effective uid
  375.     traceServer("setreuid(%d): %m", euid);
  376.     } else
  377. traceServer("setreuid(root): %m");
  378. #elif HAS_RTPRIO
  379.     uid_t euid = geteuid();
  380.     if (seteuid(0) >= 0) { // must be done as root
  381.         if(rtprio((pid_t) 0, rtprioParams[s]) < 0)
  382.            traceServer("rtprio: %m");
  383. if (seteuid(euid) < 0) // restore previous effective uid
  384.     traceServer("seteuid(%d): %m", euid);
  385.     } else
  386. traceServer("seteuid(root): %m");
  387. #elif HAS_POSIXSCHED
  388.     uid_t euid = geteuid();
  389.     if (seteuid(0) >= 0) { // must be done as root
  390. struct sched_param sp;
  391. sp.sched_priority = sched_setschedulerParams[s].priority;
  392. if (sched_setscheduler(0, sched_setschedulerParams[s].policy, &sp))
  393.     traceServer("sched_setscheduler: %m");
  394. if (sched_getparam(0, &sp))
  395.     traceServer("sched_getparam: %m");
  396. traceServer("sched policy=%d, priority=%d", sched_getscheduler(0), sp.sched_priority);
  397. if (seteuid(euid) < 0) // restore previous effective uid
  398.     traceServer("seteuid(%d): %m", euid);
  399.     } else
  400. traceServer("seteuid(root): %m");
  401. #endif
  402.     }
  403. }
  404. /*
  405.  * Record the server status in the status file.
  406.  */
  407. void
  408. ModemServer::setServerStatus(const char* fmt, ...)
  409. {
  410.     if (statusFile == NULL)
  411. return;
  412.     flock(fileno(statusFile), LOCK_EX);
  413.     rewind(statusFile);
  414.     va_list ap;
  415.     va_start(ap, fmt);
  416.     vfprintf(statusFile, fmt, ap);
  417.     va_end(ap);
  418.     fprintf(statusFile, "n");
  419.     fflush(statusFile);
  420.     int ignore = ftruncate(fileno(statusFile), ftell(statusFile));
  421.     flock(fileno(statusFile), LOCK_UN);
  422. }
  423. void
  424. ModemServer::resetConfig()
  425. {
  426.     if (modem)
  427. discardModem(true);
  428.     ServerConfig::resetConfig();
  429. }
  430. const fxStr& ModemServer::getConfigFile() const { return configFile; }
  431. /*
  432.  * Setup the modem; if needed.
  433.  */
  434. bool
  435. ModemServer::setupModem(bool isSend)
  436. {
  437.     if (!modem) {
  438. const char* dev = modemDevice;
  439. if (!openDevice(dev))
  440.     return (false);
  441. /*
  442.  * Deduce modem type and setup configuration info.
  443.  * The deduceComplain cruft is just to reduce the
  444.  * noise in the log file when probing for a modem.
  445.  */
  446. modem = deduceModem(isSend);
  447. if (!modem) {
  448.     discardModem(true);
  449.     if (deduceComplain) {
  450. traceServer("%s: Can not initialize modem.", dev);
  451. deduceComplain = false;
  452.     }
  453.     return (false);
  454. } else {
  455.     deduceComplain = true;
  456.     traceServer("MODEM "
  457. | modem->getManufacturer() | " "
  458. | modem->getModel() | "/"
  459. | modem->getRevision());
  460. }
  461.     } else {
  462. /*
  463.  * Reset the modem in case some other program
  464.  * went in and messed with the configuration.
  465.  *
  466.  * Sometimes a modem may get interrupted while in a
  467.  * "transmit" state such as AT+VTX (voice mode) or
  468.  * AT+FTM=146 (fax mode) or similar.  Now, the modem
  469.  * should be smart enough to return to command-mode
  470.  * after a short period of inactivity, but it's
  471.  * conceivable that some don't (and we've seen some
  472.  * that are this way).  Furthermore, it is likely
  473.  * possible to configure a modem in such a way so as
  474.  * to never provide for that short period of inactivity.
  475.  * Further complicating matters, some modems are not
  476.  * sensitive to DTR.
  477.  *
  478.  * So if our first reset attempt fails we send DLE+ETX 
  479.  * to the modem just in case we happen to have a modem 
  480.  * in this kind of state, waiting for DLE+ETX before 
  481.  * returning to command mode.  Then we retry the reset.
  482.  */
  483.         if (!(modem->reset())) {
  484.     sendDLEETX();
  485.     if (!(modem->reset()))
  486. return (false);
  487. }
  488.     }
  489.     /*
  490.      * Most modem-related parameters are dealt with
  491.      * in the modem driver.  The speaker volume is
  492.      * kept in the fax server because it often gets
  493.      * changed on the fly.  The phone number has no
  494.      * business in the modem class.
  495.      */
  496.     modem->setSpeakerVolume(speakerVolume);
  497.     return (true);
  498. }
  499. /*
  500.  * Ready the modem; if needed.
  501.  */
  502. bool
  503. ModemServer::readyModem()
  504. {
  505.     return (modem->ready());
  506. }
  507. /*
  508.  * Deduce the type of modem supplied to the server
  509.  * and return an instance of the appropriate modem
  510.  * driver class.
  511.  */
  512. ClassModem*
  513. ModemServer::deduceModem(bool isSend)
  514. {
  515.     ClassModem* modem = new Class0Modem(*this, *this);
  516.     if (modem) {
  517. if (modem->setupModem(isSend))
  518.     return modem;
  519. delete modem;
  520.     }
  521.     return (NULL);
  522. }
  523. fxStr
  524. ModemServer::getModemCapabilities() const
  525. {
  526.     return modem->getCapabilities();
  527. }
  528. /*
  529.  * Open the tty device associated with the modem
  530.  * and change the device file to be owned by us
  531.  * and with a protected file mode.
  532.  */
  533. bool
  534. ModemServer::openDevice(const char* dev)
  535. {
  536.     /*
  537.      * Temporarily become root to open the device.
  538.      * Routines that call setupModem *must* first
  539.      * lock the device with the usual effective uid.
  540.      */
  541.     uid_t euid = geteuid();
  542.     if (seteuid(0) < 0) {
  543.  traceServer("%s: seteuid root failed (%m)", dev);
  544.  return (false);
  545.     }
  546. #ifdef O_NDELAY
  547.     /*
  548.      * Open device w/ O_NDELAY to bypass modem
  549.      * control signals, then turn off the flag bit.
  550.      */
  551.     modemFd = Sys::open(dev, O_RDWR|O_NDELAY|O_NOCTTY);
  552.     if (modemFd < 0) {
  553. seteuid(euid);
  554. traceServer("%s: Can not open modem (%m)", dev);
  555. return (false);
  556.     }
  557.     /*
  558.      * Wait a second for "slower" modems
  559.      * such as the Nokia 6210 mobile.
  560.      */
  561.     (void) sleep(1);
  562.     int flags = fcntl(modemFd, F_GETFL, 0);
  563.     if (fcntl(modemFd, F_SETFL, flags &~ O_NDELAY) < 0) {
  564.  traceServer("%s: fcntl: %m", dev);
  565.  Sys::close(modemFd), modemFd = -1;
  566.  return (false);
  567.     }
  568. #else
  569.     startTimeout(3*1000);
  570.     modemFd = Sys::open(dev, O_RDWR);
  571.     stopTimeout("opening modem");
  572.     if (modemFd < 0) {
  573. seteuid(euid);
  574. traceServer((timeout ?
  575. "%s: Can not open modem (timed out)." :
  576. "%s: Can not open modem (%m)."),
  577.     dev, errno);
  578. return (false);
  579.     }
  580. #endif
  581.     /*
  582.      * NB: we stat and use the gid because passing -1
  583.      *     through the gid_t parameter in the prototype
  584.      *    causes it to get truncated to 65535.
  585.      */
  586.     struct stat sb;
  587.     (void) Sys::fstat(modemFd, sb);
  588. #if HAS_FCHOWN
  589.     if (fchown(modemFd, UUCPLock::getUUCPUid(), sb.st_gid) < 0)
  590. #else
  591.     if (Sys::chown(dev, UUCPLock::getUUCPUid(), sb.st_gid) < 0)
  592. #endif
  593. traceServer("%s: chown: %m", dev);
  594. #if HAS_FCHMOD
  595.     if (fchmod(modemFd, deviceMode) < 0)
  596. #else
  597.     if (Sys::chmod(dev, deviceMode) < 0)
  598. #endif
  599. traceServer("%s: chmod: %m", dev);
  600.     seteuid(euid);
  601.     return (true);
  602. }
  603. bool
  604. ModemServer::reopenDevice()
  605. {
  606.     if (modemFd >= 0)
  607. Sys::close(modemFd), modemFd = -1;
  608.     return openDevice(modemDevice);
  609. }
  610. /*
  611.  * Discard any handle on the modem.
  612.  */
  613. void
  614. ModemServer::discardModem(bool dropDTR)
  615. {
  616.     if (modemFd >= 0) {
  617. if (dropDTR)
  618.     (void) setDTR(false); // force hangup
  619. Sys::close(modemFd), modemFd = -1; // discard open file
  620. #ifdef sco5
  621. // do it again so DTR is really off (SCO Open Server 5 wierdness)
  622. modemFd = Sys::open(modemDevice, O_RDWR|O_NDELAY|O_NOCTTY);
  623. Sys::close(modemFd), modemFd = -1;
  624. #endif
  625.     }
  626.     delete modem, modem = NULL;
  627. }
  628. /*
  629.  * Start a session: a period of time during which
  630.  * carrier is raised and a peer is engaged.
  631.  */
  632. void
  633. ModemServer::beginSession(const fxStr& number)
  634. {
  635.     /*
  636.      * Obtain the next communication identifier by reading
  637.      * and updating the sequence number file.  If a problem
  638.      * occurs then session logging will not be done.
  639.      */
  640.     fxStr emsg;
  641.     u_long seqnum = Sequence::getNext(FAX_LOGDIR "/" FAX_SEQF, emsg);
  642.     if (seqnum == (u_long)-1)
  643.     {
  644. logError("Couldn't get next seqnum for session log: %s",
  645.  (const char*)emsg);
  646. return;
  647.     }
  648.     commid = fxStr::format(Sequence::format, seqnum);
  649.     fxStr file = FAX_LOGDIR "/c" | commid;
  650.     mode_t omask = umask(022);
  651.     int ftmp = Sys::open(file, O_RDWR|O_CREAT|O_EXCL, logMode);
  652.     umask(omask);
  653.     if (ftmp < 0)
  654.     {
  655. logError("Failed to open free sessionlog (seqnum=%u)", seqnum);
  656.     } else
  657.     {
  658. log =
  659.     new FaxMachineLog(ftmp, canonicalizePhoneNumber(number), commid);
  660.     }
  661. }
  662. /*
  663.  * Terminate a session.
  664.  */
  665. void
  666. ModemServer::endSession()
  667. {
  668.     delete log, log = NULL;
  669. }
  670. /*
  671.  * Return true if a request has been made to abort
  672.  * the current session.  This is true if a previous
  673.  * abort request was made or if an external abort
  674.  * message is dispatched during our processing.
  675.  */
  676. bool
  677. ModemServer::abortRequested()
  678. {
  679. #ifndef SERVERABORTBUG
  680.     if (!abortCall) {
  681. // poll for input so abort commands get processed
  682. long sec = 0;
  683. long usec = 0;
  684. while (Dispatcher::instance().dispatch(sec,usec) && !abortCall)
  685.     ;
  686.     }
  687. #endif
  688.     return (abortCall);
  689. }
  690. /*
  691.  * Request that a current session be aborted.
  692.  */
  693. void
  694. ModemServer::abortSession()
  695. {
  696.     abortCall = true;
  697.     traceServer("ABORT: job abort requested");
  698. }
  699. /*
  700.  * Dispatcher timer expired routine.  Perform the action
  701.  * associated with the server's state and, possible, transition
  702.  * to a new state.
  703.  */
  704. void
  705. ModemServer::timerExpired(long, long)
  706. {
  707.     switch (state) {
  708.     case RUNNING:
  709. /*
  710.  * Poll the lock file, see if it's lockable.
  711.  * If it's lockable, then no lock file exists.  Rinse. Repeat.
  712.  * If a lockfile exists, go to LOCKWAIT
  713.  */
  714. if (canLockModem()) {
  715.     bool ok = true;
  716.     if (pollLockPokeModem) {
  717. /*
  718.  * Poke the modem to make sure it's still there.
  719.  * If not, then mark it to be reset.
  720.  */
  721. lockModem();
  722. ok = modem->poke();
  723. unlockModem();
  724.     }
  725.     if (ok)
  726. Dispatcher::instance().startTimer(pollLockWait, 0, this);
  727.     else
  728. changeState(MODEMWAIT, pollModemWait);
  729. } else {
  730.     changeState(LOCKWAIT, pollLockWait);
  731. }
  732. break;
  733.     case MODEMWAIT:
  734.     case LOCKWAIT:
  735. /*
  736.  * Waiting for modem to start working.  Retry setup
  737.  * and either change state or restart the timer.
  738.  * Note that we unlock the modem before we change
  739.  * our state to RUNNING after a modem setup so that
  740.  * any callback doesn't find the modem locked (and
  741.  * so cause jobs to be requeued).
  742.  */
  743. if (lockModem()) {
  744.     bool modemReady = setupModem();
  745.     unlockModem();
  746.     if (modemReady)
  747. changeState(RUNNING, pollLockWait);
  748.     else
  749. changeState(MODEMWAIT, pollModemWait);
  750. } else
  751.     changeState(LOCKWAIT, pollLockWait);
  752. break;
  753.     default:
  754. traceServer("ModemServer::timerExpired() in an unexpected "
  755.     "state %d", state);
  756. break;
  757.     }
  758. }
  759. /*
  760.  * Modem support interfaces.  Note that the values
  761.  * returned when we don't have a handle on the modem
  762.  * are selected so that any imaged facsimile should
  763.  * still be sendable.
  764.  */
  765. bool ModemServer::modemReady() const
  766.     { return modem != NULL; }
  767. bool ModemServer::serverBusy() const
  768.     { return state != RUNNING; }
  769. bool ModemServer::modemWaitForRings(u_short rings, CallType& type, CallID& callid)
  770.     { return modem->waitForRings(rings, type, callid); }
  771. CallType ModemServer::modemAnswerCall(AnswerType atype, fxStr& emsg, const char* dialnumber)
  772.     { return modem->answerCall(atype, emsg, dialnumber); }
  773. void ModemServer::modemAnswerCallCmd(CallType ctype)
  774.     { modem->answerCallCmd(ctype); }
  775. void ModemServer::modemHangup() { modem->hangup(); }
  776. BaudRate ModemServer::getModemRate() const { return baudRates[curRate]; }
  777. /*
  778.  * Server configuration support.
  779.  */
  780. /*
  781.  * Read a configuration file.  Note that we suppress
  782.  * dial string rules setup while reading so that the
  783.  * order of related parameters is not important.  We
  784.  * also setup the local identifier from the fax number
  785.  * if nothing is specified in the config file (for
  786.  * backwards compatibility).
  787.  */
  788. void
  789. ModemServer::readConfig(const fxStr& filename)
  790. {
  791.     if (delayConfig)
  792.     {
  793.         /*
  794.         * We're recursively in here, likely from an Include statement.
  795.         * We don't want to do any of our delayConfig stuff...
  796.         */
  797.        ServerConfig::readConfig(filename);
  798.        return;
  799.     }
  800.     dialRulesFile = "";
  801.     delayConfig = true;
  802.     ServerConfig::readConfig(filename);
  803.     delayConfig = false;
  804.     if (dialRulesFile != "")
  805. setDialRules(dialRulesFile);
  806.     if (localIdentifier == "")
  807. setLocalIdentifier(canonicalizePhoneNumber(FAXNumber));
  808. }
  809. void ModemServer::vconfigError(const char* fmt, va_list ap)
  810.     { vtraceStatus(FAXTRACE_SERVER, fmt, ap); }
  811. void ModemServer::vconfigTrace(const char* fmt, va_list ap)
  812.     { vtraceStatus(FAXTRACE_CONFIG, fmt, ap); }
  813. void ModemServer::vdialrulesTrace(const char* fmt, va_list ap)
  814.     { vtraceStatus(FAXTRACE_DIALRULES, fmt, ap); }
  815. /*
  816.  * Setup the dial string rules.  Note that if we're
  817.  * reading the configuration file (as opposed to
  818.  * reconfiguring based on a FIFO message), then we
  819.  * suppress the actual setup so that other parameters
  820.  * such as the area code can be specified out of
  821.  * order in the configuration file.
  822.  */
  823. void
  824. ModemServer::setDialRules(const char* name)
  825. {
  826.     if (delayConfig) // delay during config setup
  827. dialRulesFile = name;
  828.     else
  829. ServerConfig::setDialRules(name);
  830. }
  831. /*
  832.  * Set the modem speaker volume and if a modem
  833.  * is setup, pass it into the modem driver to
  834.  * pass to the modem.
  835.  */
  836. void
  837. ModemServer::setModemSpeakerVolume(SpeakerVolume level)
  838. {
  839.     ServerConfig::setModemSpeakerVolume(level);
  840.     if (modem)
  841. modem->setSpeakerVolume(level);
  842. }
  843. /*
  844.  * Tracing support.
  845.  */
  846. void
  847. ModemServer::traceServer(const char* fmt ...)
  848. {
  849.     va_list ap;
  850.     va_start(ap, fmt);
  851.     vtraceStatus(FAXTRACE_SERVER, fmt, ap);
  852.     va_end(ap);
  853. }
  854. void
  855. ModemServer::traceProtocol(const char* fmt ...)
  856. {
  857.     va_list ap;
  858.     va_start(ap, fmt);
  859.     vtraceStatus(FAXTRACE_PROTOCOL, fmt, ap);
  860.     va_end(ap);
  861. }
  862. void
  863. ModemServer::traceModemOp(const char* fmt0 ...)
  864. {
  865.     va_list ap;
  866.     va_start(ap, fmt0);
  867.     fxStr fmt = fxStr::format("MODEM %s", fmt0);
  868.     vtraceStatus(FAXTRACE_MODEMOPS, fmt, ap);
  869.     va_end(ap);
  870. }
  871. void
  872. ModemServer::traceStatus(int kind, const char* fmt ...)
  873. {
  874.     va_list ap;
  875.     va_start(ap, fmt);
  876.     vtraceStatus(kind, fmt, ap);
  877.     va_end(ap);
  878. }
  879. extern void vlogInfo(const char* fmt, va_list ap);
  880. void
  881. ModemServer::vtraceStatus(int kind, const char* fmt, va_list ap)
  882. {
  883.     if (log) {
  884.         fxStr s = fxStr::vformat(fmt, ap);
  885.         if (kind == FAXTRACE_SERVER) { // always log server stuff
  886.             logInfo("%s", (const char*)s);
  887.         }
  888.         if (logTracingLevel & kind) {
  889.         log->log("%s", (const char*)s);
  890.         }
  891.     } else if (tracingLevel & kind) {
  892.     logInfo("%s", (const char*)fxStr::vformat(fmt, ap));
  893.     }
  894. }
  895. #include "StackBuffer.h"
  896. void
  897. ModemServer::traceModemIO(const char* dir, const u_char* data, u_int cc)
  898. {
  899.     if (log) {
  900. if ((logTracingLevel& FAXTRACE_MODEMIO) == 0)
  901.     return;
  902.     } else if ((tracingLevel & FAXTRACE_MODEMIO) == 0)
  903. return;
  904.     const char* hexdigits = "0123456789ABCDEF";
  905.     fxStackBuffer buf;
  906.     for (u_int i = 0; i < cc; i++) {
  907. u_char b = data[i];
  908. if (i > 0)
  909.     buf.put(' ');
  910. buf.put(hexdigits[b>>4]);
  911. buf.put(hexdigits[b&0xf]);
  912.     }
  913.     traceStatus(FAXTRACE_MODEMIO, "%s <%u:%.*s>",
  914. dir, cc, buf.getLength(), (const char*) buf);
  915. }
  916. /*
  917.  * Device manipulation.
  918.  */
  919. #ifndef B38400
  920. #define B38400 B19200
  921. #endif
  922. #ifndef B57600
  923. #define B57600 B38400
  924. #endif
  925. #ifndef B76800
  926. #define B76800 B57600
  927. #endif
  928. #ifndef B115200
  929. #define B115200 B76800
  930. #endif
  931. static speed_t termioBaud[] = {
  932.     B0, // BR0
  933.     B300, // BR300
  934.     B1200, // BR1200
  935.     B2400, // BR2400
  936.     B4800, // BR4800
  937.     B9600, // BR9600
  938.     B19200, // BR19200
  939.     B38400, // BR38400
  940.     B57600, // BR57600
  941.     B76800, // BR76800
  942.     B115200, // BR115200
  943. };
  944. #define NBAUDS (sizeof (termioBaud) / sizeof (termioBaud[0]))
  945. static const char* flowNames[] = { "NONE", "XON/XOFF", "RTS/CTS", };
  946. static const char* parityNames[] = {
  947.     "8 bits, no parity", // NONE
  948.     "7 bits, even parity", // EVEN
  949.     "7 bits, odd parity", // ODD
  950. };
  951. #if defined(CCTS_OFLOW) && defined(CRTS_IFLOW) && !defined(__NetBSD__) && !defined(__OpenBSD__)
  952. #undef CRTSCTS /* BSDi */
  953. #define CRTSCTS (CCTS_OFLOW|CRTS_IFLOW)
  954. #endif
  955. #if defined(CTSFLOW) && defined(RTSFLOW)
  956. #define CRTSCTS (CTSFLOW|RTSFLOW) /* SCO */
  957. #endif
  958. #if defined(CNEW_RTSCTS)
  959. #define CRTSCTS CNEW_RTSCTS /* IRIX 5.x */
  960. #endif
  961. #ifndef CRTSCTS
  962. #define CRTSCTS 0
  963. #endif
  964. void
  965. ModemServer::setFlow(termios& term, FlowControl iflow, FlowControl oflow)
  966. {
  967.     switch (iflow) {
  968.     case ClassModem::FLOW_NONE:
  969. term.c_iflag &= ~IXON;
  970. term.c_cflag &= ~CRTSCTS;
  971. break;
  972.     case ClassModem::FLOW_XONXOFF:
  973. term.c_iflag |= IXON;
  974. term.c_cflag &= ~CRTSCTS;
  975. break;
  976.     case ClassModem::FLOW_RTSCTS:
  977. term.c_iflag &= ~IXON;
  978. term.c_cflag |= CRTSCTS;
  979. break;
  980.     }
  981.     switch (oflow) {
  982.     case ClassModem::FLOW_NONE:
  983. term.c_iflag &= ~IXOFF;
  984. term.c_cflag &= ~CRTSCTS;
  985. break;
  986.     case ClassModem::FLOW_XONXOFF:
  987. term.c_iflag |= IXOFF;
  988. term.c_cflag &= ~CRTSCTS;
  989. break;
  990.     case ClassModem::FLOW_RTSCTS:
  991. term.c_iflag &= ~IXOFF;
  992. term.c_cflag |= CRTSCTS;
  993. break;
  994.     }
  995. }
  996. void
  997. ModemServer::setParity(termios& term, Parity parity)
  998. {
  999.     switch (parity) {
  1000.     case NONE: 
  1001. term.c_cflag &= ~(CSIZE | PARENB);
  1002. term.c_cflag |= CS8;
  1003. term.c_iflag &= ~(IGNPAR | ISTRIP);
  1004. break;
  1005.     case EVEN:
  1006. term.c_cflag &= ~(CSIZE | PARODD);
  1007. term.c_cflag |= CS7 | PARENB;
  1008. term.c_iflag |= IGNPAR | ISTRIP;
  1009. break;
  1010.     case ODD:
  1011. term.c_cflag &= ~CSIZE;
  1012. term.c_cflag |= CS7 | PARENB | PARODD;
  1013. term.c_iflag |= IGNPAR | ISTRIP;
  1014. break;
  1015.     }
  1016. }
  1017. bool
  1018. ModemServer::tcsetattr(int op, struct termios& term)
  1019. {
  1020.     bool ok;
  1021.     if (clocalAsRoot) {
  1022. /*
  1023.  * Gag, IRIX 5.2 and beyond permit only the super-user to
  1024.  * change the CLOCAL bit on a tty device with modem control.
  1025.  * However, since some versions have a bug in the UART driver
  1026.  * that causes RTS/CTS flow control to be silently turned off
  1027.  * when CLOCAL is set, for now we enable this from the
  1028.  * configuration file so that IRIX users can still disable it.
  1029.  */
  1030. uid_t euid = geteuid();
  1031. (void) seteuid(0);
  1032. ok = (::tcsetattr(modemFd, op, &term) == 0);
  1033. seteuid(euid);
  1034.     } else
  1035. ok = (::tcsetattr(modemFd, op, &term) == 0);
  1036.     if (!ok)
  1037. traceModemOp("tcsetattr: %m");
  1038.     return ok;
  1039. }
  1040. bool
  1041. ModemServer::tcgetattr(const char* method, struct termios& term)
  1042. {
  1043.     if (::tcgetattr(modemFd, &term) != 0) {
  1044. traceModemOp("%s::tcgetattr: %m", method);
  1045. return (false);
  1046.     } else
  1047. return (true);
  1048. }
  1049. /*
  1050.  * Set tty port baud rate and flow control.
  1051.  */
  1052. bool
  1053. ModemServer::setBaudRate(BaudRate rate, FlowControl iFlow, FlowControl oFlow)
  1054. {
  1055.     if (rate >= NBAUDS)
  1056. rate = NBAUDS-1;
  1057.     traceModemOp("set baud rate: %d baud, input flow %s, output flow %s",
  1058. baudRates[rate], flowNames[iFlow], flowNames[oFlow]);
  1059.     struct termios term;
  1060.     if (!tcgetattr("setBaudRate", term))
  1061. return (false);
  1062.     curRate = rate; // NB: for use elsewhere
  1063.     term.c_oflag = 0;
  1064.     term.c_lflag = 0;
  1065.     term.c_iflag &= IXON|IXOFF; // keep these bits
  1066.     term.c_cflag &= CRTSCTS; // and these bits
  1067.     setParity(term, curParity);
  1068.     term.c_cflag |= CLOCAL | CREAD;
  1069.     setFlow(term, iFlow, oFlow);
  1070.     cfsetospeed(&term, termioBaud[rate]);
  1071.     cfsetispeed(&term, termioBaud[rate]);
  1072.     term.c_cc[VMIN] = (cc_t) curVMin;
  1073.     term.c_cc[VTIME] = (cc_t) curVTime;
  1074.     flushModemInput();
  1075. #if HAS_TXCD
  1076.     /* 
  1077.      * From: Steve Williams <steve@geniers.cuug.ab.ca>
  1078.      *
  1079.      * Under AIX there is no easy way to determine if hardware
  1080.      * handshaking is already on the terminal Control Discipline
  1081.      * stack.  This is necessary to properly condition the tty
  1082.      * device for RTS/CTS flow control.  Rather than determine
  1083.      * the state of the device we just add/remove the Control
  1084.      * Discipline and ignore any errors.  Note that this is the
  1085.      * only place this must be done because it is the only
  1086.      * method through which the modem drivers enable/disable
  1087.      * RTS/CTS flow control.
  1088.      */
  1089.     if (iFlow == FaxModem::FLOW_RTSCTS) {
  1090. traceModemOp("add rts control discipline");
  1091. (void) ioctl(modemFd, TXADDCD, "rts"); // XXX check return
  1092.     } else {
  1093. traceModemOp("remove rts control discipline");
  1094. (void) ioctl(modemFd, TXDELCD, "rts"); // XXX check return
  1095.     }
  1096. #endif
  1097. #if HAS_TERMIOX
  1098.     /*
  1099.      * Some SVR4.2 systems require use of termiox
  1100.      * to setup hardware handshaking on a port.
  1101.      */
  1102.     struct termiox termx;
  1103.     if (ioctl(modemFd, TCGETX, &termx) >= 0) {
  1104. if (iFlow == FaxModem::FLOW_RTSCTS)
  1105.     termx.x_hflag |= RTSXOFF|CTSXON;
  1106. else
  1107.     termx.x_hflag &= ~(RTSXOFF|CTSXON);
  1108. if (ioctl(modemFd, TCSETX, &termx) < 0)
  1109.     traceModemOp("ioctl(TCSETX): %m");
  1110.     }
  1111. #endif
  1112.     return (tcsetattr(TCSANOW, term));
  1113. }
  1114. /*
  1115.  * Set tty port baud rate and leave flow control state unchanged.
  1116.  */
  1117. bool
  1118. ModemServer::setBaudRate(BaudRate rate)
  1119. {
  1120.     if (rate >= NBAUDS)
  1121. rate = NBAUDS-1;
  1122.     traceModemOp("set baud rate: %d baud (flow control unchanged)",
  1123. baudRates[rate]);
  1124.     struct termios term;
  1125.     if (!tcgetattr("setBaudRate", term))
  1126. return (false);
  1127.     curRate = rate; // NB: for use elsewhere
  1128.     term.c_oflag = 0;
  1129.     term.c_lflag = 0;
  1130.     term.c_iflag &= IXON|IXOFF; // keep these bits
  1131.     term.c_cflag &= CRTSCTS; // and these bits
  1132.     setParity(term, curParity);
  1133.     term.c_cflag |= CLOCAL | CREAD;
  1134.     cfsetospeed(&term, termioBaud[rate]);
  1135.     cfsetispeed(&term, termioBaud[rate]);
  1136.     term.c_cc[VMIN] = (cc_t) curVMin;
  1137.     term.c_cc[VTIME] = (cc_t) curVTime;
  1138.     flushModemInput();
  1139.     return (tcsetattr(TCSANOW, term));
  1140. }
  1141. /*
  1142.  * Set tty port parity and number of data bits
  1143.  */
  1144. bool
  1145. ModemServer::setParity(Parity parity)
  1146. {
  1147.     traceModemOp("set parity: %s", parityNames[parity]);
  1148.     struct termios term;
  1149.     if (!tcgetattr("setParity", term))
  1150. return (false);
  1151.     setParity(term, parity);
  1152.     flushModemInput();
  1153.     if (!tcsetattr(TCSANOW, term))
  1154. return (false);
  1155.     curParity = parity; // used above
  1156.     return (true);
  1157. }
  1158. /*
  1159.  * Manipulate DTR on tty port.
  1160.  *
  1161.  * On systems that support explicit DTR control this is done
  1162.  * with an ioctl.  Otherwise we assume that setting the baud
  1163.  * rate to zero causes DTR to be dropped (asserting DTR is
  1164.  * assumed to be implicit in setting a non-zero baud rate).
  1165.  *
  1166.  * NB: we use the explicit DTR manipulation ioctls because
  1167.  *     setting the baud rate to zero on some systems can cause
  1168.  *     strange side effects.
  1169.  */
  1170. bool
  1171. ModemServer::setDTR(bool onoff)
  1172. {
  1173.     traceModemOp("set DTR %s", onoff ? "ON" : "OFF");
  1174. #if defined(MCGETA) && defined(MCSETAF) && defined(MDTR)
  1175.     /*
  1176.      * HP-UX has a special way to manipulate DTR.
  1177.      */
  1178.     int mstat;
  1179.     if (ioctl(modemFd, MCGETA, &mstat) >= 0) {
  1180. if (onoff)
  1181.     mstat |= MDTR;
  1182. else
  1183.     mstat &= ~MDTR;
  1184. if (ioctl(modemFd, MCSETAF, &mstat) >= 0)
  1185.     return (true);
  1186.     }
  1187. #elif defined(TIOCMBIS)
  1188.     int mctl = TIOCM_DTR;
  1189.     /*
  1190.      * Happy days! Some systems passes the arg by value, while
  1191.      * others passes it by reference; is this progress?
  1192.      */
  1193. #ifdef CONFIG_TIOCMBISBYREF
  1194.     if (ioctl(modemFd, onoff ? TIOCMBIS : TIOCMBIC, (char *)&mctl) >= 0)
  1195. #else
  1196.     if (ioctl(modemFd, onoff ? TIOCMBIS : TIOCMBIC, (char *)mctl) >= 0)
  1197. #endif
  1198. return (true);
  1199.     /*
  1200.      * Sigh, Sun seems to support this ioctl only on *some*
  1201.      * devices (e.g. on-board duarts, but not the ALM-2 card);
  1202.      * so if the ioctl that should work fails, we fallback
  1203.      * on the usual way of doing things...
  1204.      */
  1205. #endif /* TIOCMBIS */
  1206.     return (onoff ? true : setBaudRate(ClassModem::BR0));
  1207. }
  1208. static const char* actNames[] = { "NOW", "DRAIN", "FLUSH" };
  1209. static u_int actCode[] = { TCSANOW, TCSADRAIN, TCSAFLUSH };
  1210. /*
  1211.  * Set tty modes so that the specified handling
  1212.  * is done on data being sent and received.  When
  1213.  * transmitting binary data, oFlow is FLOW_NONE to
  1214.  * disable the transmission of XON/XOFF by the host
  1215.  * to the modem.  When receiving binary data, iFlow
  1216.  * is FLOW_NONE to cause XON/XOFF from the modem
  1217.  * to not be interpreted.  In each case the opposite
  1218.  * XON/XOFF handling should be enabled so that any
  1219.  * XON/XOFF from/to the modem will be interpreted.
  1220.  */
  1221. bool
  1222. ModemServer::setXONXOFF(FlowControl iFlow, FlowControl oFlow, SetAction act)
  1223. {
  1224.     traceModemOp("set XON/XOFF/%s: input %s, output %s",
  1225. actNames[act],
  1226. iFlow == ClassModem::FLOW_NONE ? "ignored" : "interpreted",
  1227. oFlow == ClassModem::FLOW_NONE ? "disabled" : "generated"
  1228.     );
  1229.     struct termios term;
  1230.     if (!tcgetattr("setXONXOFF", term))
  1231. return (false);
  1232.     setFlow(term, iFlow, oFlow);
  1233.     if (act == ClassModem::ACT_FLUSH)
  1234. flushModemInput();
  1235.     return (tcsetattr(actCode[act], term));
  1236. }
  1237. #ifdef sgi
  1238. #include <sys/stropts.h>
  1239. #include <sys/z8530.h>
  1240. #endif
  1241. #ifdef sun
  1242. #include <sys/stropts.h>
  1243. #endif
  1244. /*
  1245.  * Setup process state either for minimum latency (no buffering)
  1246.  * or reduced latency (input may be buffered).  We fiddle with
  1247.  * the termio structure and, if required, the streams timer
  1248.  * that delays the delivery of input data from the UART module
  1249.  * upstream to the tty module.
  1250.  */
  1251. bool
  1252. ModemServer::setInputBuffering(bool on)
  1253. {
  1254.     if (on != inputBuffered) traceModemOp("input buffering %s", on ? "enabled" : "disabled");
  1255.     inputBuffered = on;
  1256. #ifdef SIOC_ITIMER
  1257.     /*
  1258.      * Silicon Graphics systems have a settable timer
  1259.      * that causes the UART driver to delay passing
  1260.      * data upstream to the tty module.  This can cause
  1261.      * anywhere from 20-30ms delay between input characters.
  1262.      * We set it to zero when input latency is critical.
  1263.      */
  1264.     strioctl str;
  1265.     str.ic_cmd = SIOC_ITIMER;
  1266.     str.ic_timout = (on ? 2 : 0); // 2 ticks = 20ms (usually)
  1267.     str.ic_len = 4;
  1268.     int arg = 0;
  1269.     str.ic_dp = (char*)&arg;
  1270.     if (ioctl(modemFd, I_STR, &str) < 0)
  1271. traceModemOp("setInputBuffer::ioctl(SIOC_ITIMER): %m");
  1272. #endif
  1273. #ifdef sun
  1274.     /*
  1275.      * SunOS has a timer similar to the SIOC_ITIMER described
  1276.      * above for input on the on-board serial ports, but it is
  1277.      * not generally accessible because it is controlled by a
  1278.      * stream control message (M_CTL w/ either MC_SERVICEDEF or
  1279.      * MC_SERVICEIMM) and you can not do a putmsg directly to
  1280.      * the UART module and the tty driver does not provide an
  1281.      * interface.  Also, the ALM-2 driver apparently also has
  1282.      * a timer, but does not provide the M_CTL interface that's
  1283.      * provided for the on-board ports.  All in all this means
  1284.      * that the only way to defeat the timer for the on-board
  1285.      * serial ports (and thereby provide enough control for the
  1286.      * fax server to work with Class 1 modems) is to implement
  1287.      * a streams module in the kernel that provides an interface
  1288.      * to the timer--which is what has been done.  In the case of
  1289.      * the ALM-2, however, you are just plain out of luck unless
  1290.      * you have source code.
  1291.      */
  1292.     static bool zsunbuf_push_tried = false;
  1293.     static bool zsunbuf_push_ok = false;
  1294.     if (on) { // pop zsunbuf if present to turn on buffering
  1295. char topmodule[FMNAMESZ+1];
  1296.         if (zsunbuf_push_ok && ioctl(modemFd, I_LOOK, topmodule) >= 0 &&
  1297.   streq(topmodule, "zsunbuf")) {
  1298.     if (ioctl(modemFd, I_POP, 0) < 0)
  1299. traceModemOp("pop zsunbuf failed: %m");
  1300. }
  1301.     } else { // push zsunbuf to turn off buffering
  1302.         if (!zsunbuf_push_tried) {
  1303.             zsunbuf_push_ok = (ioctl(modemFd, I_PUSH, "zsunbuf") >= 0);
  1304.             traceModemOp("initial push zsunbuf %s",
  1305.                 zsunbuf_push_ok ? "succeeded" : "failed");
  1306.             zsunbuf_push_tried = true;
  1307.         } else if (zsunbuf_push_ok) {
  1308.             if (ioctl(modemFd, I_PUSH, "zsunbuf") < 0)
  1309.                 traceModemOp("push zsunbuf failed: %m");
  1310.         }
  1311.     }
  1312. #endif
  1313.     struct termios term;
  1314.     (void) tcgetattr("setInputBuffering", term);
  1315.     if (on) {
  1316. curVMin = 127;
  1317. curVTime = 1;
  1318.     } else {
  1319. curVMin = 1;
  1320. curVTime = 0;
  1321.     }
  1322.     term.c_cc[VMIN] = (cc_t) curVMin;
  1323.     term.c_cc[VTIME] = (cc_t) curVTime;
  1324.     return (tcsetattr(TCSANOW, term));
  1325. }
  1326. bool
  1327. ModemServer::sendBreak(bool pause)
  1328. {
  1329.     traceModemOp("send break%s", pause ? " (pause)" : "");
  1330.     flushModemInput();
  1331.     if (pause) {
  1332. /*
  1333.  * NB: TCSBRK is supposed to wait for output to drain,
  1334.  * but some modems appear to lose data if we don't do this.
  1335.  */
  1336. (void) tcdrain(modemFd);
  1337.     }
  1338.     if (tcsendbreak(modemFd, 0) != 0) {
  1339. traceModemOp("tcsendbreak: %m");
  1340. return (false);
  1341.     } else
  1342. return (true);
  1343. }
  1344. void
  1345. ModemServer::startTimeout(long ms)
  1346. {
  1347.     timer.startTimeout(ms);
  1348.     timeout = false;
  1349. }
  1350. void
  1351. ModemServer::stopTimeout(const char* whichdir)
  1352. {
  1353.     timer.stopTimeout();
  1354.     timeout = timer.wasTimeout();
  1355.     if (timeout)
  1356. traceModemOp("TIMEOUT: %s", whichdir);
  1357. }
  1358. void
  1359. ModemServer::sendDLEETX()
  1360. {
  1361.     u_char buf[2];
  1362.     buf[0] = DLE;
  1363.     buf[1] = ETX;
  1364.     (void) putModem(buf, 2);
  1365. }
  1366. int
  1367. ModemServer::getModemLine(char rbuf[], u_int bufSize, long ms)
  1368. {
  1369.     int c;
  1370.     u_int cc = 0;
  1371.     if (ms) startTimeout(ms);
  1372.     do {
  1373. while ((c = getModemChar(0)) != EOF && c != 'n' && !timer.wasTimeout())
  1374.     if (c != '' && c != 'r' && cc < bufSize)
  1375. rbuf[cc++] = c;
  1376.     } while (!timer.wasTimeout() && cc == 0 && c != EOF);
  1377.     rbuf[cc] = '';
  1378.     if (ms) stopTimeout("reading line from modem");
  1379.     if (!timeout)
  1380. traceStatus(FAXTRACE_MODEMCOM, "--> [%d:%s]", cc, rbuf);
  1381.     return (cc);
  1382. }
  1383. int
  1384. ModemServer::getModemChar(long ms, bool isquery)
  1385. {
  1386.     if (rcvNext >= rcvCC) {
  1387. int n = 0;
  1388. if (isquery) {
  1389.     if (fcntl(modemFd, F_SETFL, fcntl(modemFd, F_GETFL, 0) | O_NONBLOCK)) {
  1390. traceStatus(FAXTRACE_MODEMCOM, "Can not set O_NONBLOCK: errno %u", errno);
  1391. return (EOF);
  1392.     }
  1393.     n = 5; // only read once
  1394. }
  1395. if (ms) startTimeout(ms);
  1396. do
  1397.     rcvCC = Sys::read(modemFd, (char*) rcvBuf, sizeof (rcvBuf));
  1398. while (n++ < 5 && rcvCC == 0);
  1399. if (ms) stopTimeout("reading from modem");
  1400. if (isquery) {
  1401.     if (fcntl(modemFd, F_SETFL, fcntl(modemFd, F_GETFL, 0) &~ O_NONBLOCK))
  1402. traceStatus(FAXTRACE_MODEMCOM, "Can not reset O_NONBLOCK: errno %u", errno);
  1403. }
  1404. if (rcvCC <= 0) {
  1405.     if (rcvCC < 0) {
  1406. if (errno != EINTR)
  1407.     if (!isquery || errno != EAGAIN)
  1408. traceStatus(FAXTRACE_MODEMCOM,
  1409.     "MODEM READ ERROR: errno %u", errno);
  1410.     }
  1411.     return (EOF);
  1412. } else
  1413.     traceModemIO("-->", rcvBuf, rcvCC);
  1414. rcvNext = 0;
  1415.     }
  1416.     return (rcvBuf[rcvNext++]);
  1417. }
  1418. int
  1419. ModemServer::getModemBit(long ms)
  1420. {
  1421.     /*
  1422.      * Return bytes bit-by-bit in MSB2LSB order.
  1423.      * getModemChar() returns them in LSB2MSB.
  1424.      */
  1425.     if (rcvBit < 1) {
  1426. rcvBit = 8;
  1427. gotByte = getModemChar(ms);
  1428. if (gotByte == 0x10) { // strip stuffed DLE
  1429.     gotByte = getModemChar(ms);
  1430.     if (gotByte == 0x03) sawBlockEnd = true; // DLE+ETX
  1431. }
  1432.     }
  1433.     // enable this to simulate a VERY noisy connection
  1434.     // if (((int) Sys::now() & 1) && ((random() % 10000)/10000.0) > 0.95) return (1);
  1435.     if (gotByte == EOF) return (EOF);
  1436.     else if (gotByte & (0x80 >> --rcvBit)) return (1);
  1437.     else return (0);
  1438. }
  1439. int
  1440. ModemServer::getLastByte()
  1441. {
  1442.     return gotByte;
  1443. }
  1444. bool
  1445. ModemServer::didBlockEnd()
  1446. {
  1447.     return sawBlockEnd;
  1448. }
  1449. void
  1450. ModemServer::resetBlock()
  1451. {
  1452.     sawBlockEnd = false;
  1453. }
  1454. void
  1455. ModemServer::modemFlushInput()
  1456. {
  1457.     traceModemOp("flush i/o");
  1458.     flushModemInput();
  1459.     if (tcflush(modemFd, TCIFLUSH) != 0)
  1460. traceModemOp("tcflush: %m");
  1461. }
  1462. bool
  1463. ModemServer::modemStopOutput()
  1464. {
  1465.     if (tcflow(modemFd, TCOOFF) != 0) {
  1466. traceModemOp("tcflow: %m");
  1467. return (false);
  1468.     } else
  1469. return (true);
  1470. }
  1471. void
  1472. ModemServer::flushModemInput()
  1473. {
  1474.     rcvCC = rcvNext = rcvBit = gotByte = 0;
  1475.     sawBlockEnd = false;
  1476. }
  1477. bool
  1478. ModemServer::putModem(const void* data, int n, long ms)
  1479. {
  1480.     traceStatus(FAXTRACE_MODEMCOM, "<-- data [%d]", n);
  1481.     return (putModem1(data, n, ms));
  1482. }
  1483. bool
  1484. ModemServer::putModem1(const void* data, int n, long ms)
  1485. {
  1486.     if (ms)
  1487. startTimeout(ms);
  1488.     else
  1489. timeout = false;
  1490.     int cc = Sys::write(modemFd, (const char*) data, n);
  1491.     if (ms)
  1492. stopTimeout("writing to modem");
  1493.     if (cc > 0) {
  1494. traceModemIO("<--", (const u_char*) data, cc);
  1495. n -= cc;
  1496.     }
  1497.     if (cc == -1) {
  1498. if (errno != EINTR)
  1499.     traceStatus(FAXTRACE_MODEMCOM, "MODEM WRITE ERROR: errno %u",
  1500. errno);
  1501.     } else if (n != 0)
  1502. traceStatus(FAXTRACE_MODEMCOM, "MODEM WRITE SHORT: sent %u, wrote %u",
  1503.     cc+n, cc);
  1504.     return (!timeout && n == 0);
  1505. }