faxApp.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:14k
- /* $Id: faxApp.c++,v 1.10 2008/08/07 02:17:18 faxguy Exp $ */
- /*
- * Copyright (c) 1990-1996 Sam Leffler
- * Copyright (c) 1991-1996 Silicon Graphics, Inc.
- * HylaFAX is a trademark of Silicon Graphics
- *
- * Permission to use, copy, modify, distribute, and sell this software and
- * its documentation for any purpose is hereby granted without fee, provided
- * that (i) the above copyright notices and this permission notice appear in
- * all copies of the software and related documentation, and (ii) the names of
- * Sam Leffler and Silicon Graphics may not be used in any advertising or
- * publicity relating to the software without the specific, prior written
- * permission of Sam Leffler and Silicon Graphics.
- *
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
- *
- * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
- * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
- * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
- #include "faxApp.h"
- #include "config.h"
- #include <errno.h>
- #include <pwd.h>
- #include <limits.h>
- #include <grp.h>
- #include <unistd.h>
- #if HAS_LOCALE
- extern "C" {
- #include <locale.h>
- }
- #endif
- #include "Sys.h"
- /*
- * getopt Iterator Interface.
- */
- extern int opterr, optind;
- extern char* optarg;
- GetoptIter::GetoptIter(int ac, char** av, const fxStr& s) : opts(s)
- {
- argc = ac;
- argv = av;
- optind = 1;
- opterr = 0;
- c = Sys::getopt(argc, argv, opts);
- }
- GetoptIter::~GetoptIter() {}
- void GetoptIter::operator++() { c = Sys::getopt(argc, argv, opts); }
- void GetoptIter::operator++(int) { c = Sys::getopt(argc, argv, opts); }
- const char* GetoptIter::optArg() const { return optarg; }
- const char* GetoptIter::getArg()
- { return optind < argc ? argv[optind] : ""; }
- const char* GetoptIter::nextArg()
- { return optind < argc ? argv[optind++] : ""; }
- faxApp::faxApp()
- {
- running = false;
- faxqfifo = -1;
- setLogFacility(LOG_FAX); // default
- #ifdef LC_CTYPE
- setlocale(LC_CTYPE, ""); // for <ctype.h> calls
- #endif
- #ifdef LC_TIME
- setlocale(LC_TIME, ""); // for strftime calls
- #endif
- signal(SIGPIPE, fxSIGHANDLER(SIG_IGN)); // for FIFO writes
- }
- faxApp::~faxApp() {}
- void
- faxApp::initialize(int, char**)
- {
- openFIFOs();
- }
- void faxApp::open(void) { running = true; }
- void
- faxApp::close(void)
- {
- running = false;
- if (faxqfifo != -1)
- Sys::close(faxqfifo);
- }
- fxStr faxApp::getopts;
- void faxApp::setOpts(const char* s) { getopts = s; }
- const fxStr& faxApp::getOpts() { return getopts; }
- void
- faxApp::fatal(const char* fmt ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vlogError(fmt, ap);
- va_end(ap);
- exit(-1);
- }
- /*
- * FIFO-related support.
- */
- const fxStr faxApp::fifoName = FAX_FIFO;
- /*
- * Open the requisite FIFO special files.
- */
- void
- faxApp::openFIFOs(void)
- {
- }
- void
- faxApp::closeFIFOs(void)
- {
- }
- /*
- * Open the specified FIFO file.
- */
- int
- faxApp::openFIFO(const char* fifoName, int mode, bool okToExist)
- {
- if (Sys::mkfifo(fifoName, mode & 0777) < 0) {
- if (errno != EEXIST || !okToExist)
- faxApp::fatal("Could not create %s: %m.", fifoName);
- }
- int fd = Sys::open(fifoName, CONFIG_OPENFIFO|O_NDELAY, 0);
- if (fd == -1)
- faxApp::fatal("Could not open FIFO file %s: %m.", fifoName);
- if (!Sys::isFIFOFile(fd))
- faxApp::fatal("%s is not a FIFO special file", fifoName);
- // open should set O_NDELAY, but just to be sure...
- if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY) < 0)
- logError("openFIFO %s: fcntl: %m", fifoName);
- return (fd);
- }
- /*
- * Respond to input on a FIFO file descriptor.
- */
- int
- faxApp::FIFOInput(int fd)
- {
- char buf[2048];
- int n = 0;
- while ((n = (Sys::read(fd, buf+n, sizeof (buf)-(n+1)) + n)) > 0) {
- buf[n] = ' ';
- /*
- * Break up ' '-separated records and strip
- * any trailing 'n' so that "echo mumble>FIFO"
- * works (i.e. echo appends a 'n' character).
- */
- char* bp = &buf[0];
- bool done = false;
- do {
- char* cp = strchr(bp, ' ');
- /*
- * If the read filled the buffer without emptying the FIFO
- * then we'll find ourselves at &buf[sizeof(buf)-1]. In
- * that case we need to wrap the existing message into the
- * next read buffer.
- */
- if (cp == &buf[sizeof(buf)-1]) {
- n = cp-bp;
- if (n == sizeof(buf)-1) {
- // Is a single FIFO message realistically larger than the read buffer? This appears to be a whole lot of garbage; ignore it all.
- n = 0;
- }
- memmove(buf, bp, n);
- done = true;
- } else {
- if (cp > bp) {
- if (cp[-1] == 'n')
- cp[-1] = ' ';
- FIFOMessage(bp);
- bp = cp+1;
- }
- if (bp >= &buf[n]) {
- done = true;
- n = 0;
- }
- }
- } while (!done);
- }
- #ifdef FIFOSELECTBUG
- /*
- * Solaris 2.x botch (and some versions of IRIX 5.x).
- *
- * A client close of an open FIFO causes an M_HANGUP to be
- * sent and results in the receiver's file descriptor being
- * marked ``hung up''. This in turn causes select to
- * perpetually return true and if we're running as a realtime
- * process, brings the system to a halt. The workaround for
- * Solaris 2.1 was to do a parallel reopen of the appropriate
- * FIFO so that the original descriptor is recycled. This
- * apparently no longer works in Solaris 2.2 or later and we
- * are forced to close and reopen both FIFO descriptors (noone
- * appears capable of answering why this this is necessary and
- * I personally don't care...)
- */
- closeFIFOs(); openFIFOs();
- #endif
- return (0);
- }
- /*
- * Process a message received through a FIFO.
- */
- void
- faxApp::FIFOMessage(const char* cp)
- {
- logError("Bad fifo message "%s"", cp);
- }
- /*
- * Send a message to the central queuer process.
- */
- bool
- faxApp::vsendQueuer(const char* fmt, va_list ap)
- {
- if (faxqfifo == -1) {
- #ifdef FIFOSELECTBUG
- /*
- * We try multiple times to open the appropriate FIFO
- * file because the system has a kernel bug that forces
- * the server to close+reopen the FIFO file descriptors
- * for each message received on the FIFO (yech!).
- */
- int tries = 0;
- do {
- if (tries > 0)
- sleep(1);
- faxqfifo = Sys::open(fifoName, O_WRONLY|O_NDELAY);
- } while (faxqfifo == -1 && errno == ENXIO && ++tries < 5);
- #else
- faxqfifo = Sys::open(fifoName, O_WRONLY|O_NDELAY);
- #endif
- if (faxqfifo == -1)
- return (false);
- /*
- * Turn off O_NDELAY so that write will block if FIFO is full.
- */
- if (fcntl(faxqfifo, F_SETFL, fcntl(faxqfifo, F_GETFL, 0) &~ O_NDELAY) < 0)
- logError("fcntl: %m");
- }
- fxStr msg = fxStr::vformat(fmt, ap);
- u_int len = msg.length() + 1;
- if (Sys::write(faxqfifo, (const char*)msg, len) != (ssize_t)len) {
- if (errno == EBADF || errno == EPIPE) // reader expired
- Sys::close(faxqfifo), faxqfifo = -1;
- else
- logError("FIFO write failed: %m");
- return (false);
- } else
- return (true);
- }
- /*
- * Send a message to the central queuer process.
- */
- bool
- faxApp::sendQueuer(const char* fmt ...)
- {
- va_list ap;
- va_start(ap, fmt);
- bool ok = vsendQueuer(fmt, ap);
- va_end(ap);
- return (ok);
- }
- /*
- * Send a modem status message to the central queuer process.
- */
- bool
- faxApp::sendModemStatus(const char* devid, const char* fmt0 ...)
- {
- fxStr fmt = fxStr::format("+%s:%s", devid, fmt0);
- va_list ap;
- va_start(ap, fmt0);
- bool ok = vsendQueuer(fmt, ap);
- va_end(ap);
- return (ok);
- }
- /*
- * Send a job status message to the central queuer process.
- */
- bool
- faxApp::sendJobStatus(const char* jobid, const char* fmt0 ...)
- {
- fxStr fmt = fxStr::format("*%s:%s", jobid, fmt0);
- va_list ap;
- va_start(ap, fmt0);
- bool ok = vsendQueuer(fmt, ap);
- va_end(ap);
- return (ok);
- }
- /*
- * Send a receive status message to the central queuer process.
- */
- bool
- faxApp::sendRecvStatus(const char* devid, const char* fmt0 ...)
- {
- fxStr fmt = fxStr::format("@%s:%s", devid, fmt0);
- va_list ap;
- va_start(ap, fmt0);
- bool ok = vsendQueuer(fmt, ap);
- va_end(ap);
- return (ok);
- }
- /*
- * Miscellaneous stuff.
- */
- /*
- * Convert an identifier to the pathname for the
- * device (required by the UUCP lock code). This
- * is done converting '_'s to '/'s and then prepending
- * _PATH_DEV. This is required for SVR4 systems
- * which have their devices in subdirectories!
- */
- fxStr
- faxApp::idToDev(const fxStr& id)
- {
- fxStr dev(id);
- u_int l;
- while ((l = dev.next(0, '_')) < dev.length())
- dev[l] = '/';
- if (dev[0] == '/') return (dev); // path + device
- return (_PATH_DEV | dev);
- }
- fxStr
- faxApp::devToID(const fxStr& id)
- {
- fxStr devID(id);
- fxStr prefix(_PATH_DEV);
- u_int l = prefix.length();
- if (devID.length() > l && devID.head(l) == prefix)
- devID.remove(0, l);
- while ((l = devID.next(0, '/')) < devID.length())
- devID[l] = '_';
- return (devID);
- }
- /*
- * Force the real uid+gid to be the same as
- * the effective ones. Must temporarily
- * make the effective uid root in order to
- * do the real id manipulations.
- */
- void
- faxApp::setRealIDs(void)
- {
- uid_t euid = geteuid();
- if (seteuid(0) < 0)
- logError("seteuid(root): %m");
- if (setgid(getegid()) < 0)
- logError("setgid: %m");
- if (setuid(euid) < 0)
- logError("setuid: %m");
- }
- static void
- detachIO(void)
- {
- endpwent(); // XXX some systems hold descriptors
- closelog(); // XXX in case syslog has descriptor
- int fd = Sys::open(_PATH_DEVNULL, O_RDWR);
- if (fd == -1)
- printf("Could not open null device file %s.", _PATH_DEVNULL);
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- for (fd = Sys::getOpenMax()-1; fd >= 0; fd--)
- if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
- (void) Sys::close(fd);
- }
- const fxStr faxApp::quote = " '";
- const fxStr faxApp::enquote = "'";
- fxStr
- faxApp::quoted(const fxStr& s)
- {
- fxStr q;
- for (u_int i = 0; i < s.length(); i++) {
- if (s[i] == ''') q.append("'\'");
- q.append(s[i]);
- }
- return (q);
- }
- /*
- * Run the specified shell command. If changeIDs is
- * true, we set the real uid+gid to the effective; this
- * is so that programs like sendmail show an informative
- * from address.
- */
- bool
- faxApp::runCmd(const char* cmd, bool changeIDs, IOHandler* waiter)
- {
- pid_t pid = fork();
- switch (pid) {
- case 0:
- if (changeIDs)
- setRealIDs();
- detachIO();
- execl("/bin/sh", "sh", "-c", cmd, (char*) NULL);
- sleep(1); // XXX give parent time
- _exit(127);
- case -1:
- logError("Can not fork for "%s"", cmd);
- return (false);
- default:
- if (waiter == NULL)
- { int status = 0;
- Sys::waitpid(pid, status);
- if (status != 0) {
- logError("Bad exit status %#o for '%s'", status, cmd);
- return (false);
- }
- } else
- {
- Dispatcher::instance().startChild(pid, waiter);
- }
- return (true);
- }
- }
- /*
- * Setup server uid+gid. Normally the server is started up
- * by root and then sets its effective uid+gid to that of
- * the ``fax'' user (the gid is used by hfaxd and should be
- * the uid of the fax user). This permits the server to
- * switch to ``root'' whenever it's necessary (in order to
- * gain access to a root-specific function such as starting
- * a getty process). Alternatively the server may be run
- * setuid ``fax'' with the real uid of ``root'' (in order to
- * do privileged operations).
- */
- void
- faxApp::setupPermissions(void)
- {
- if (getuid() != 0)
- faxApp::fatal("The fax server must run with real uid root.n");
- uid_t euid = geteuid();
- const passwd* pwd = getpwnam(FAX_USER);
- if (!pwd)
- faxApp::fatal("No fax user "%s" defined on your system!n"
- "This software is not installed properly!", FAX_USER);
- if (euid == 0) {
- if (initgroups(pwd->pw_name, pwd->pw_gid) != 0)
- faxApp::fatal("Can not setup permissions (supplementary groups)");
- if (setegid(pwd->pw_gid) < 0)
- faxApp::fatal("Can not setup permissions (gid)");
- if (seteuid(pwd->pw_uid) < 0)
- faxApp::fatal("Can not setup permissions (uid)");
- } else {
- uid_t faxuid = pwd->pw_uid;
- setpwent();
- pwd = getpwuid(euid);
- if (!pwd)
- faxApp::fatal("Can not figure out the identity of uid %u", euid);
- if (pwd->pw_uid != faxuid)
- faxApp::fatal("Configuration error; "
- "the fax server must run as the fax user "%s".", FAX_USER);
- (void) setegid(faxuid);
- }
- endpwent();
- }
- /*
- * Break the association with the controlling tty if we can
- * preserve it later with the POSIX O_NOCTTY mechanism. Note
- * that we do not use detachIO to close all the open file
- * descriptors because many systems cache open descriptors within
- * libraries for performance reasons and do not react well when
- * you close them w/o telling them about it (and some don't react
- * well even when you *DO* tell them). Since we know we're called
- * very early on from main in all our apps we just assume that
- * we only need to remove the stdin+stdout+stderr before forking
- * and starting a new session.
- */
- void
- faxApp::detachFromTTY(void)
- {
- #ifdef O_NOCTTY
- int fd = Sys::open(_PATH_DEVNULL, O_RDWR);
- if (fd == -1)
- printf("Could not open null device file %s.", _PATH_DEVNULL);
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- switch (fork()) {
- case 0: break; // child, continue
- case -1: _exit(1); // error
- default: _exit(0); // parent, terminate
- }
- (void) setsid();
- #endif
- }
- /*
- * Private version of fxassert for server processes.
- * The default library routine sends the message to
- * stderr and then calls abort(). This typically
- * does not work for a server because stderr is not
- * attached to anything (so the message is lost) and
- * abort will not generate a core dump because the
- * process has an effective uid and/or gid different
- * from the real uid/gid.
- *
- * If (the undocumented configuration parameter)
- * CONFIG_WAITONASSERT is set to a non-zero value
- * then instead of dumping core the process will
- * pause indefinitely so that a debugger can be
- * attached.
- */
- extern "C" void
- _fxassert(const char* msg, const char* file, int line)
- {
- fprintf(stderr, "Assertion failed "%s", file "%s" line %d.n",
- msg, file, line);
- logError("Assertion failed "%s", file "%s" line %d.n",
- msg, file, line);
- #if CONFIG_WAITONASSERT
- for (;;) // wait for a debugger to attach
- pause();
- #else
- faxApp::setRealIDs(); // reset so we get a core dump
- abort();
- #endif
- /*NOTREACHED*/
- }