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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: faxApp.c++,v 1.10 2008/08/07 02:17:18 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 "faxApp.h"
  27. #include "config.h"
  28. #include <errno.h>
  29. #include <pwd.h>
  30. #include <limits.h>
  31. #include <grp.h>
  32. #include <unistd.h>
  33. #if HAS_LOCALE
  34. extern "C" {
  35. #include <locale.h>
  36. }
  37. #endif
  38. #include "Sys.h"
  39. /*
  40.  * getopt Iterator Interface.
  41.  */
  42. extern int opterr, optind;
  43. extern char* optarg;
  44. GetoptIter::GetoptIter(int ac, char** av, const fxStr& s) : opts(s)
  45. {
  46.     argc = ac;
  47.     argv = av;
  48.     optind = 1;
  49.     opterr = 0;
  50.     c = Sys::getopt(argc, argv, opts);
  51. }
  52. GetoptIter::~GetoptIter() {}
  53. void GetoptIter::operator++() { c = Sys::getopt(argc, argv, opts); }
  54. void GetoptIter::operator++(int) { c = Sys::getopt(argc, argv, opts); }
  55. const char* GetoptIter::optArg() const { return optarg; }
  56. const char* GetoptIter::getArg()
  57.    { return optind < argc ? argv[optind] : ""; }
  58. const char* GetoptIter::nextArg()
  59.    { return optind < argc ? argv[optind++] : ""; }
  60. faxApp::faxApp()
  61. {
  62.     running = false;
  63.     faxqfifo = -1;
  64.     setLogFacility(LOG_FAX); // default
  65. #ifdef LC_CTYPE
  66.     setlocale(LC_CTYPE, ""); // for <ctype.h> calls
  67. #endif
  68. #ifdef LC_TIME
  69.     setlocale(LC_TIME, ""); // for strftime calls
  70. #endif
  71.     signal(SIGPIPE, fxSIGHANDLER(SIG_IGN)); // for FIFO writes
  72. }
  73. faxApp::~faxApp() {}
  74. void
  75. faxApp::initialize(int, char**)
  76. {
  77.     openFIFOs();
  78. }
  79. void faxApp::open(void) { running = true; }
  80. void
  81. faxApp::close(void)
  82. {
  83.     running = false;
  84.     if (faxqfifo != -1)
  85. Sys::close(faxqfifo);
  86. }
  87. fxStr faxApp::getopts;
  88. void faxApp::setOpts(const char* s) { getopts = s; }
  89. const fxStr& faxApp::getOpts() { return getopts; }
  90. void
  91. faxApp::fatal(const char* fmt ...)
  92. {
  93.     va_list ap;
  94.     va_start(ap, fmt);
  95.     vlogError(fmt, ap);
  96.     va_end(ap);
  97.     exit(-1);
  98. }
  99. /*
  100.  * FIFO-related support.
  101.  */
  102. const fxStr faxApp::fifoName = FAX_FIFO;
  103. /*
  104.  * Open the requisite FIFO special files.
  105.  */
  106. void
  107. faxApp::openFIFOs(void)
  108. {
  109. }
  110. void
  111. faxApp::closeFIFOs(void)
  112. {
  113. }
  114. /*
  115.  * Open the specified FIFO file.
  116.  */
  117. int
  118. faxApp::openFIFO(const char* fifoName, int mode, bool okToExist)
  119. {
  120.     if (Sys::mkfifo(fifoName, mode & 0777) < 0) {
  121. if (errno != EEXIST || !okToExist)
  122.     faxApp::fatal("Could not create %s: %m.", fifoName);
  123.     }
  124.     int fd = Sys::open(fifoName, CONFIG_OPENFIFO|O_NDELAY, 0);
  125.     if (fd == -1)
  126. faxApp::fatal("Could not open FIFO file %s: %m.", fifoName);
  127.     if (!Sys::isFIFOFile(fd))
  128. faxApp::fatal("%s is not a FIFO special file", fifoName);
  129.     // open should set O_NDELAY, but just to be sure...
  130.     if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY) < 0)
  131. logError("openFIFO %s: fcntl: %m", fifoName);
  132.     return (fd);
  133. }
  134. /*
  135.  * Respond to input on a FIFO file descriptor.
  136.  */
  137. int
  138. faxApp::FIFOInput(int fd)
  139. {
  140.     char buf[2048];
  141.     int n = 0;
  142.     while ((n = (Sys::read(fd, buf+n, sizeof (buf)-(n+1)) + n)) > 0) {
  143. buf[n] = '';
  144. /*
  145.  * Break up ''-separated records and strip
  146.  * any trailing 'n' so that "echo mumble>FIFO"
  147.  * works (i.e. echo appends a 'n' character).
  148.  */
  149. char* bp = &buf[0];
  150. bool done = false;
  151. do {
  152.     char* cp = strchr(bp, '');
  153.     /*
  154.      * If the read filled the buffer without emptying the FIFO 
  155.      * then we'll find ourselves at &buf[sizeof(buf)-1].  In 
  156.      * that case we need to wrap the existing message into the 
  157.      * next read buffer.
  158.      */
  159.     if (cp == &buf[sizeof(buf)-1]) {
  160. n = cp-bp;
  161. if (n == sizeof(buf)-1) {
  162.     // Is a single FIFO message realistically larger than the read buffer?  This appears to be a whole lot of garbage; ignore it all.
  163.     n = 0;
  164. }
  165. memmove(buf, bp, n);
  166. done = true;
  167.     } else {
  168. if (cp > bp) {
  169.     if (cp[-1] == 'n')
  170. cp[-1] = '';
  171.     FIFOMessage(bp);
  172.     bp = cp+1;
  173. }
  174. if (bp >= &buf[n]) {
  175.     done = true;
  176.     n = 0;
  177. }
  178.     }
  179. } while (!done);
  180.     }
  181. #ifdef FIFOSELECTBUG
  182.     /*
  183.      * Solaris 2.x botch (and some versions of IRIX 5.x).
  184.      *
  185.      * A client close of an open FIFO causes an M_HANGUP to be
  186.      * sent and results in the receiver's file descriptor being
  187.      * marked ``hung up''.  This in turn causes select to
  188.      * perpetually return true and if we're running as a realtime
  189.      * process, brings the system to a halt.  The workaround for
  190.      * Solaris 2.1 was to do a parallel reopen of the appropriate
  191.      * FIFO so that the original descriptor is recycled.  This
  192.      * apparently no longer works in Solaris 2.2 or later and we
  193.      * are forced to close and reopen both FIFO descriptors (noone
  194.      * appears capable of answering why this this is necessary and
  195.      * I personally don't care...)
  196.      */
  197.     closeFIFOs(); openFIFOs();
  198. #endif
  199.     return (0);
  200. }
  201. /*
  202.  * Process a message received through a FIFO.
  203.  */
  204. void
  205. faxApp::FIFOMessage(const char* cp)
  206. {
  207.     logError("Bad fifo message "%s"", cp);
  208. }
  209. /*
  210.  * Send a message to the central queuer process.
  211.  */
  212. bool
  213. faxApp::vsendQueuer(const char* fmt, va_list ap)
  214. {
  215.     if (faxqfifo == -1) {
  216. #ifdef FIFOSELECTBUG
  217. /*
  218.  * We try multiple times to open the appropriate FIFO
  219.  * file because the system has a kernel bug that forces
  220.  * the server to close+reopen the FIFO file descriptors
  221.  * for each message received on the FIFO (yech!).
  222.  */
  223. int tries = 0;
  224. do {
  225.     if (tries > 0)
  226. sleep(1);
  227.     faxqfifo = Sys::open(fifoName, O_WRONLY|O_NDELAY);
  228. } while (faxqfifo == -1 && errno == ENXIO && ++tries < 5);
  229. #else
  230. faxqfifo = Sys::open(fifoName, O_WRONLY|O_NDELAY);
  231. #endif
  232. if (faxqfifo == -1)
  233.     return (false);
  234. /*
  235.  * Turn off O_NDELAY so that write will block if FIFO is full.
  236.  */
  237. if (fcntl(faxqfifo, F_SETFL, fcntl(faxqfifo, F_GETFL, 0) &~ O_NDELAY) < 0)
  238.     logError("fcntl: %m");
  239.     }
  240.     fxStr msg = fxStr::vformat(fmt, ap);
  241.     u_int len = msg.length() + 1;
  242.     if (Sys::write(faxqfifo, (const char*)msg, len) != (ssize_t)len) {
  243. if (errno == EBADF || errno == EPIPE) // reader expired
  244.     Sys::close(faxqfifo), faxqfifo = -1;
  245. else
  246.     logError("FIFO write failed: %m"); 
  247. return (false);
  248.     } else
  249. return (true);
  250. }
  251. /*
  252.  * Send a message to the central queuer process.
  253.  */
  254. bool
  255. faxApp::sendQueuer(const char* fmt ...)
  256. {
  257.     va_list ap;
  258.     va_start(ap, fmt);
  259.     bool ok = vsendQueuer(fmt, ap);
  260.     va_end(ap);
  261.     return (ok);
  262. }
  263. /*
  264.  * Send a modem status message to the central queuer process.
  265.  */
  266. bool
  267. faxApp::sendModemStatus(const char* devid, const char* fmt0 ...)
  268. {
  269.     fxStr fmt = fxStr::format("+%s:%s", devid, fmt0);
  270.     va_list ap;
  271.     va_start(ap, fmt0);
  272.     bool ok = vsendQueuer(fmt, ap);
  273.     va_end(ap);
  274.     return (ok);
  275. }
  276. /*
  277.  * Send a job status message to the central queuer process.
  278.  */
  279. bool
  280. faxApp::sendJobStatus(const char* jobid, const char* fmt0 ...)
  281. {
  282.     fxStr fmt = fxStr::format("*%s:%s", jobid, fmt0);
  283.     va_list ap;
  284.     va_start(ap, fmt0);
  285.     bool ok = vsendQueuer(fmt, ap);
  286.     va_end(ap);
  287.     return (ok);
  288. }
  289. /*
  290.  * Send a receive status message to the central queuer process.
  291.  */
  292. bool
  293. faxApp::sendRecvStatus(const char* devid, const char* fmt0 ...)
  294. {
  295.     fxStr fmt = fxStr::format("@%s:%s", devid, fmt0);
  296.     va_list ap;
  297.     va_start(ap, fmt0);
  298.     bool ok = vsendQueuer(fmt, ap);
  299.     va_end(ap);
  300.     return (ok);
  301. }
  302. /*
  303.  * Miscellaneous stuff.
  304.  */
  305. /*
  306.  * Convert an identifier to the pathname for the
  307.  * device (required by the UUCP lock code).  This
  308.  * is done converting '_'s to '/'s and then prepending
  309.  * _PATH_DEV.  This is required for SVR4 systems
  310.  * which have their devices in subdirectories!
  311.  */
  312. fxStr
  313. faxApp::idToDev(const fxStr& id)
  314. {
  315.     fxStr dev(id);
  316.     u_int l;
  317.     while ((l = dev.next(0, '_')) < dev.length())
  318. dev[l] = '/';
  319.     if (dev[0] == '/') return (dev); // path + device
  320.     return (_PATH_DEV | dev);
  321. }
  322. fxStr
  323. faxApp::devToID(const fxStr& id)
  324. {
  325.     fxStr devID(id);
  326.     fxStr prefix(_PATH_DEV);
  327.     u_int l = prefix.length();
  328.     if (devID.length() > l && devID.head(l) == prefix)
  329. devID.remove(0, l);
  330.     while ((l = devID.next(0, '/')) < devID.length())
  331. devID[l] = '_';
  332.     return (devID);
  333. }
  334. /*
  335.  * Force the real uid+gid to be the same as
  336.  * the effective ones.  Must temporarily
  337.  * make the effective uid root in order to
  338.  * do the real id manipulations.
  339.  */
  340. void
  341. faxApp::setRealIDs(void)
  342. {
  343.     uid_t euid = geteuid();
  344.     if (seteuid(0) < 0)
  345. logError("seteuid(root): %m");
  346.     if (setgid(getegid()) < 0)
  347. logError("setgid: %m");
  348.     if (setuid(euid) < 0)
  349. logError("setuid: %m");
  350. }
  351. static void
  352. detachIO(void)
  353. {
  354.     endpwent(); // XXX some systems hold descriptors
  355.     closelog(); // XXX in case syslog has descriptor
  356.     int fd = Sys::open(_PATH_DEVNULL, O_RDWR);
  357.     if (fd == -1)
  358. printf("Could not open null device file %s.", _PATH_DEVNULL);
  359.     dup2(fd, STDIN_FILENO);
  360.     dup2(fd, STDOUT_FILENO);
  361.     dup2(fd, STDERR_FILENO);
  362.     for (fd = Sys::getOpenMax()-1; fd >= 0; fd--)
  363. if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
  364.     (void) Sys::close(fd);
  365. }
  366. const fxStr faxApp::quote = " '";
  367. const fxStr faxApp::enquote = "'";
  368. fxStr
  369. faxApp::quoted(const fxStr& s)
  370. {
  371.     fxStr q;
  372.     for (u_int i = 0; i < s.length(); i++) {
  373. if (s[i] == ''') q.append("'\'");
  374. q.append(s[i]);
  375.     }
  376.     return (q);
  377. }
  378. /*
  379.  * Run the specified shell command.  If changeIDs is
  380.  * true, we set the real uid+gid to the effective; this
  381.  * is so that programs like sendmail show an informative
  382.  * from address.
  383.  */
  384. bool
  385. faxApp::runCmd(const char* cmd, bool changeIDs, IOHandler* waiter)
  386. {
  387.     pid_t pid = fork();
  388.     switch (pid) {
  389.     case 0:
  390. if (changeIDs)
  391.     setRealIDs();
  392. detachIO();
  393. execl("/bin/sh", "sh", "-c", cmd, (char*) NULL);
  394. sleep(1); // XXX give parent time
  395. _exit(127);
  396.     case -1:
  397. logError("Can not fork for "%s"", cmd);
  398. return (false);
  399.     default:
  400. if (waiter == NULL)
  401. { int status = 0;
  402.   Sys::waitpid(pid, status);
  403.   if (status != 0) {
  404.     logError("Bad exit status %#o for '%s'", status, cmd);
  405.     return (false);
  406.   }
  407. } else
  408. {
  409.     Dispatcher::instance().startChild(pid, waiter);
  410. }
  411. return (true);
  412.     }
  413. }
  414. /*
  415.  * Setup server uid+gid.  Normally the server is started up
  416.  * by root and then sets its effective uid+gid to that of
  417.  * the ``fax'' user (the gid is used by hfaxd and should be
  418.  * the uid of the fax user).  This permits the server to
  419.  * switch to ``root'' whenever it's necessary (in order to
  420.  * gain access to a root-specific function such as starting
  421.  * a getty process).  Alternatively the server may be run
  422.  * setuid ``fax'' with the real uid of ``root'' (in order to
  423.  * do privileged operations).
  424.  */ 
  425. void
  426. faxApp::setupPermissions(void)
  427. {
  428.     if (getuid() != 0)
  429. faxApp::fatal("The fax server must run with real uid root.n");
  430.     uid_t euid = geteuid();
  431.     const passwd* pwd = getpwnam(FAX_USER);
  432.     if (!pwd)
  433. faxApp::fatal("No fax user "%s" defined on your system!n"
  434.     "This software is not installed properly!", FAX_USER);
  435.     if (euid == 0) {
  436. if (initgroups(pwd->pw_name, pwd->pw_gid) != 0)
  437.     faxApp::fatal("Can not setup permissions (supplementary groups)");
  438. if (setegid(pwd->pw_gid) < 0)
  439.     faxApp::fatal("Can not setup permissions (gid)");
  440. if (seteuid(pwd->pw_uid) < 0)
  441.     faxApp::fatal("Can not setup permissions (uid)");
  442.     } else {
  443. uid_t faxuid = pwd->pw_uid;
  444. setpwent();
  445. pwd = getpwuid(euid);
  446. if (!pwd)
  447.     faxApp::fatal("Can not figure out the identity of uid %u", euid);
  448. if (pwd->pw_uid != faxuid)
  449.     faxApp::fatal("Configuration error; "
  450. "the fax server must run as the fax user "%s".", FAX_USER);
  451.  (void) setegid(faxuid);
  452.     }
  453.     endpwent();
  454. }
  455. /*
  456.  * Break the association with the controlling tty if we can
  457.  * preserve it later with the POSIX O_NOCTTY mechanism.  Note
  458.  * that we do not use detachIO to close all the open file
  459.  * descriptors because many systems cache open descriptors within
  460.  * libraries for performance reasons and do not react well when
  461.  * you close them w/o telling them about it (and some don't react
  462.  * well even when you *DO* tell them).  Since we know we're called
  463.  * very early on from main in all our apps we just assume that
  464.  * we only need to remove the stdin+stdout+stderr before forking
  465.  * and starting a new session.
  466.  */
  467. void
  468. faxApp::detachFromTTY(void)
  469. {
  470. #ifdef O_NOCTTY
  471.     int fd = Sys::open(_PATH_DEVNULL, O_RDWR);
  472.     if (fd == -1)
  473. printf("Could not open null device file %s.", _PATH_DEVNULL);
  474.     dup2(fd, STDIN_FILENO);
  475.     dup2(fd, STDOUT_FILENO);
  476.     dup2(fd, STDERR_FILENO);
  477.     switch (fork()) {
  478.     case 0: break; // child, continue
  479.     case -1: _exit(1); // error
  480.     default: _exit(0); // parent, terminate
  481.     }
  482.     (void) setsid();
  483. #endif
  484. }
  485. /*
  486.  * Private version of fxassert for server processes.
  487.  * The default library routine sends the message to
  488.  * stderr and then calls abort().  This typically
  489.  * does not work for a server because stderr is not
  490.  * attached to anything (so the message is lost) and
  491.  * abort will not generate a core dump because the
  492.  * process has an effective uid and/or gid different
  493.  * from the real uid/gid.
  494.  *
  495.  * If (the undocumented configuration parameter)
  496.  * CONFIG_WAITONASSERT is set to a non-zero value
  497.  * then instead of dumping core the process will
  498.  * pause indefinitely so that a debugger can be
  499.  * attached.
  500.  */
  501. extern "C" void
  502. _fxassert(const char* msg, const char* file, int line)
  503. {
  504.     fprintf(stderr, "Assertion failed "%s", file "%s" line %d.n", 
  505. msg, file, line);
  506.     logError("Assertion failed "%s", file "%s" line %d.n", 
  507. msg, file, line);
  508. #if CONFIG_WAITONASSERT
  509.     for (;;) // wait for a debugger to attach
  510. pause();
  511. #else
  512.     faxApp::setRealIDs(); // reset so we get a core dump
  513.     abort();
  514. #endif
  515.     /*NOTREACHED*/
  516. }