FIFO.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:10k
- /* $Id: FIFO.c++,v 1.1.1.1 2005/11/11 21:32:03 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 "port.h"
- #include "Sys.h"
- #include "config.h"
- #include "HylaFAXServer.h"
- #include "Dispatcher.h"
- /*
- * Support for communication with the HylaFAX queuer via FIFO's.
- */
- /*
- * Create the client FIFO and open it for use.
- */
- bool
- HylaFAXServer::initClientFIFO(fxStr& emsg)
- {
- clientFIFOName = fxStr::format(FAX_CLIENTDIR "/%u", getpid());
- if (Sys::mkfifo(clientFIFOName, 0622) < 0 && errno != EEXIST) {
- emsg = fxStr::format("Could not create %s: %s",
- (const char*) clientFIFOName, strerror(errno));
- return (false);
- }
- clientFd = Sys::open(clientFIFOName, CONFIG_OPENFIFO|O_NDELAY);
- if (clientFd == -1) {
- emsg = fxStr::format("Could not open FIFO file %s: %s",
- (const char*) clientFIFOName, strerror(errno));
- return (false);
- }
- if (!Sys::isFIFOFile(clientFd)) {
- emsg = clientFIFOName | " is not a FIFO special file";
- return (false);
- }
- // open should set O_NDELAY, but just to be sure...
- if (fcntl(clientFd, F_SETFL, fcntl(clientFd, F_GETFL, 0) | O_NDELAY) < 0)
- logError("initClientFIFO %s: fcntl: %m", (const char*) clientFIFOName);
- Dispatcher::instance().link(clientFd, Dispatcher::ReadMask, this);
- return (true);
- }
- /*
- * Respond to input on a FIFO file descriptor.
- */
- int
- HylaFAXServer::FIFOInput(int fd)
- {
- char buf[2048];
- int cc;
- while ((cc = Sys::read(fd, buf, sizeof (buf)-1)) > 0) {
- if (cc == sizeof(buf)-1)
- logWarning("FIFO Read full: %d", cc);
- buf[cc] = ' ';
- char* bp = &buf[0];
- do {
- if (bp[0] == '!') {
- /*
- * This is an event message from the scheduler
- * generated by a previously registered trigger.
- * Setup the unpacking work and dispatch it.
- */
- TriggerMsgHeader h;
- u_int left = &buf[cc]-bp;
- bool needy = false;
- if (left < sizeof(TriggerMsgHeader))
- {
- needy = true;
- } else
- {
- memcpy(&h, bp, sizeof (h)); // copy to align fields
- if (left < h.length)
- needy = true;
- }
- if (needy)
- {
- /*
- * Handle the case where the buffer was read full. This means that
- * we have a "partial" message at the end of buf, and the rest in
- * the FIFO.
- *
- * buf[n] is NEVER read from the file, it's always 1-past. If we
- * have reached buf[n], we know we are past the read data.
- *
- * We have (left) bytes of the message at the end of the buf.
- * We move them to the front, and read as much more as we can to
- * fill buf back up, leaving it in the same state as if this was
- * the initial read.
- */
- memmove(buf, bp, left);
- cc = Sys::read(fd, buf+left, (sizeof(buf)-1) - left) + left;
- buf[cc] = ' ';
- bp = &buf[0];
- } else {
- triggerEvent(h, bp+sizeof (h));
- bp += h.length;
- }
- } else {
- /*
- * Break up ' '-separated records and strip
- * any trailing 'n' so that "echo mumble>FIFO"
- * works (i.e. echo appends a 'n' character).
- */
- char* cp = strchr(bp, ' ');
- /*
- * Handle the case where the buffer was read full. This means that
- * we have a "partial" message at the end of buf, and the rest in
- * the FIFO.
- *
- * buf[n] is NEVER read from the file, it's always 1-past. If we
- * have reached buf[n], we know we are past the read data.
- *
- * We have (cp - bp) bytes of the message at the end of the buf.
- * We move them to the front, and read as much more as we can to
- * fill buf back up, leaving it in the same state as if this was
- * the initial read.
- */
- if (cp == &buf[sizeof(buf)-1])
- {
- memmove(buf, bp, cp-bp);
- cc = Sys::read(fd, buf+(cp-bp), (sizeof(buf)-1) - (cp-bp)) + (cp-bp);
- buf[cc] = ' ';
- bp = &buf[0];
- cp = strchr(bp, ' ');
- }
- if (cp > bp) {
- if (cp[-1] == 'n') {
- cp[-1] = ' ';
- FIFOMessage(bp, &cp[-1]-bp);
- } else
- FIFOMessage(bp, cp-bp);
- }
- bp = cp+1;
- }
- } while (bp < &buf[cc]);
- }
- return (0);
- }
- void
- HylaFAXServer::FIFOMessage(const char* cp, u_int)
- {
- if (IS(WAITFIFO)) {
- /*
- * Someone is waiting for a response
- * from the server. Stash the response
- * and notify them by marking the
- * response as arrived.
- */
- fifoResponse = cp;
- state &= ~S_WAITFIFO;
- return;
- }
- switch (cp[0]) {
- case 'H': // HELLO when queuer restarts
- if (faxqFd >= 0)
- Sys::close(faxqFd), faxqFd = -1;
- if (trigSpec != "") { // reload trigger
- fxStr emsg;
- (void) loadTrigger(emsg);
- }
- break;
- }
- }
- /*
- * Send a message to the central queuer process.
- */
- bool
- HylaFAXServer::sendQueuerMsg(fxStr& emsg, const fxStr& msg)
- {
- bool retry = false;
- again:
- if (faxqFd == -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);
- faxqFd = Sys::open(faxqFIFOName, O_WRONLY|O_NDELAY);
- } while (faxqFd == -1 && errno == ENXIO && ++tries < 5);
- #else
- faxqFd = Sys::open(faxqFIFOName, O_WRONLY|O_NDELAY);
- #endif
- if (faxqFd == -1) {
- emsg = fxStr::format("Unable to open scheduler FIFO: %s",
- strerror(errno));
- return (false);
- }
- /*
- * Turn off O_NDELAY so that write will block if FIFO is full.
- */
- if (fcntl(faxqFd, F_SETFL, fcntl(faxqFd, F_GETFL, 0) &~ O_NDELAY) < 0)
- logError("fcntl: %m");
- }
- ssize_t len = msg.length()+1;
- if (Sys::write(faxqFd, msg, len) != len) {
- if (errno == EBADF || errno == EPIPE) {
- /*
- * The queuer process is gone. Try again
- * once in case it has been restarted.
- */
- Sys::close(faxqFd), faxqFd = -1;
- if (!retry) {
- retry = true;
- goto again;
- }
- }
- emsg = fxStr::format("FIFO write failed: %s", strerror(errno));
- logError(emsg);
- return (false);
- } else
- return (true);
- }
- /*
- * Send a message to the central queuer process.
- */
- bool
- HylaFAXServer::sendQueuer(fxStr& emsg, const char* fmt ...)
- {
- va_list ap;
- va_start(ap, fmt);
- bool ok = sendQueuerMsg(emsg, fxStr::vformat(fmt, ap));
- va_end(ap);
- return (ok);
- }
- /*
- * Send a message to the central queuer process
- * and wait for a response on our client FIFO.
- */
- bool
- HylaFAXServer::sendQueuerACK(fxStr& emsg, const char* fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- bool b = vsendQueuerACK(emsg, fmt, ap);
- va_end(ap);
- return (b);
- }
- bool
- HylaFAXServer::vsendQueuerACK(fxStr& emsg, const char* fmt, va_list ap)
- {
- if (clientFd == -1) {
- emsg = "Bad server state, client FIFO is not open";
- return (false);
- }
- fxStr msg = fxStr::vformat(fmt, ap);
- if (msg.length() < 2) { // sanity check
- emsg = "Bad FIFO message, too short to be valid";
- return (false);
- }
- msg.insert(clientFIFOName | ":", 1); // insert FIFO name for reply
- bool ok = sendQueuerMsg(emsg, msg);
- if (ok) {
- Dispatcher& disp = Dispatcher::instance();
- for (state |= S_WAITFIFO; IS(WAITFIFO); disp.dispatch())
- ;
- if (fifoResponse.length() < 2) { // too short to be valid
- emsg = "Unexpected response from scheduler: "" |fifoResponse| """;
- ok = false;
- } else if (fifoResponse[0] == msg[0]) { // response to our request
- ok = (fifoResponse[1] == '*');
- if (!ok)
- emsg = "Unspecified reason (scheduler NAK'd request)";
- } else // user abort
- ok = false;
- }
- return (ok);
- }
- /*
- * Send a message to a modem process via the per-modem FIFO.
- */
- bool
- HylaFAXServer::sendModem(const char* modem, fxStr& emsg, const char* fmt ...)
- {
- fxStr fifoName(modem);
- canonDevID(fifoName); // convert pathname -> devid
- fifoName.insert("/" FAX_FIFO "."); // prepend /FIFO. string
- #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 fd;
- int tries = 0;
- do {
- if (tries > 0)
- sleep(1);
- fd = Sys::open(fifoName, O_WRONLY|O_NDELAY);
- } while (fd == -1 && errno == ENXIO && ++tries < 5);
- #else
- int fd = Sys::open(fifoName, O_WRONLY|O_NDELAY);
- #endif
- if (fd == -1) {
- emsg = fxStr::format("Unable to open %s: %s",
- (const char*) fifoName, strerror(errno));
- return (false);
- }
- va_list ap;
- va_start(ap, fmt);
- fxStr msg = fxStr::vformat(fmt, ap);
- va_end(ap);
- ssize_t len = msg.length()+1;
- if (Sys::write(fd, msg, len) != len) {
- emsg = fxStr::format("write to %s failed: %s",
- (const char*) fifoName, strerror(errno));
- logError(emsg);
- return (false);
- } else
- return (true);
- }