📄 fifo.c++
字号:
/* $Id: FIFO.c++,v 1.5 2005/05/30 14:52:33 tim 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. */boolHylaFAXServer::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. */intHylaFAXServer::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] = '\0'; 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] = '\0'; bp = &buf[0]; } else { triggerEvent(h, bp+sizeof (h)); bp += h.length; } } else { /* * Break up '\0'-separated records and strip * any trailing '\n' so that "echo mumble>FIFO" * works (i.e. echo appends a '\n' character). */ char* cp = strchr(bp, '\0'); /* * 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] = '\0'; bp = &buf[0]; cp = strchr(bp, '\0'); } if (cp > bp) { if (cp[-1] == '\n') { cp[-1] = '\0'; FIFOMessage(bp, &cp[-1]-bp); } else FIFOMessage(bp, cp-bp); } bp = cp+1; } } while (bp < &buf[cc]); } return (0);}voidHylaFAXServer::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. */boolHylaFAXServer::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. */boolHylaFAXServer::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. */boolHylaFAXServer::sendQueuerACK(fxStr& emsg, const char* fmt, ...){ va_list ap; va_start(ap, fmt); bool b = vsendQueuerACK(emsg, fmt, ap); va_end(ap); return (b);}boolHylaFAXServer::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. */boolHylaFAXServer::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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -