Trigger.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:13k
- /* $Id: Trigger.c++,v 1.7 2008/04/26 22:34:28 faxguy Exp $ */
- /*
- * Copyright (c) 1995-1996 Sam Leffler
- * Copyright (c) 1995-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 "Trigger.h"
- #include "TriggerRef.h"
- #include "HylaClient.h"
- #include "Sys.h"
- #include "Job.h"
- #include "Modem.h"
- #include "StackBuffer.h"
- #include "config.h"
- #include <ctype.h>
- #include <errno.h>
- /*
- * Trigger Support.
- */
- #define EventClass(e) (e>>4)
- u_int Trigger::tidFree[TRIGGER_MAXWDS];
- u_int Trigger::tidRotor = 0;
- Trigger* Trigger::triggers[TRIGGER_MAXTID];
- QLink Trigger::wildcards[TRIGGER_MAXEVENT>>4];
- Trigger::Trigger(trid_t t, const fxStr& fifoName) : tid(t)
- {
- refs = 0;
- interests[JOB_BASE>>4] = 0;
- interests[SEND_BASE>>4] = 0;
- interests[RECV_BASE>>4] = 0;
- interests[MODEM_BASE>>4] = 0;
- client = &HylaClient::getClient(fifoName);
- client->inc();
- triggers[tid] = this; // register in master table
- }
- Trigger::~Trigger()
- {
- if (refs > 0)
- purgeWildRefs();
- if (refs != 0) // NB: should be no other references
- logError("Trigger %u deleted with %u references", tid, refs);
- /*
- * Remove references to this trigger from the master
- * table and clear the tid from the allocation bitmap.
- * We release the reference on the client and if this
- * is the last reference to the client we purge it.
- */
- triggers[tid] = NULL;
- // NB: we mimic the logic below to avoid byte-order issues
- u_int i = tid / TRIGGER_BPW;
- u_int b = tid % TRIGGER_BPW;
- ((u_char*) &tidFree[i])[b>>3] &= ~(1<<(b&7));
- client->dec();
- }
- /*
- * Remove all references to this that appear
- * on a reference list (used for wildcards).
- */
- void
- Trigger::purgeWildRefs()
- {
- for (u_int i = 0; i < EventClass(TRIGGER_MAXEVENT) && refs > 0; i++) {
- QLink& w = wildcards[i];
- if (!w.isEmpty())
- TriggerRef::purge(w, this);
- }
- }
- /*
- * Delete all the triggers associated
- * with the specified client.
- */
- void
- Trigger::purgeClient(HylaClient* hc)
- {
- u_int refs = hc->refs;
- for (u_int w = 0; w < TRIGGER_MAXWDS; w++) {
- if (Trigger::tidFree[w] != 0) {
- u_int n = TRIGGER_BPW-1;
- u_int i = w*TRIGGER_BPW;
- do {
- Trigger* t = triggers[i++];
- if (t && t->client == hc) {
- if (t->cancel())
- delete t;
- if (--refs == 0) // found all triggers
- return;
- // check if this block is now empty
- if (Trigger::tidFree[w] == 0)
- break;
- }
- } while (--n);
- }
- }
- logError("Lost trigger for %s", (const char*) hc->fifoName);
- }
- /*
- * Create a trigger for the client process listening on
- * the specified FIFO. The interests are given by the
- * specification string.
- */
- void
- Trigger::create(const fxStr& fifoName, const char* spec)
- {
- trid_t tid = tidNextFree();
- if (tid == TRIGGER_MAXTID) {
- HylaClient::getClient(fifoName).send("T!", 3);
- logError("TRIGGER: tid table overflow");
- } else {
- Trigger* tr = new Trigger(tid, fifoName);
- if (!tr->parse(spec)) {
- tr->send("T!");
- delete tr;
- } else
- tr->send("T*%u", tid);
- }
- }
- /*
- * Return a free trigger id if one is available.
- * If the table is full return TRIGGER_MAXTID
- */
- trid_t
- Trigger::tidNextFree()
- {
- u_int r = tidRotor;
- if (tidFree[r] == (u_int) -1) {
- /*
- * This word is full, move the rotor
- * forward looking for a word with space.
- */
- r = (r+1) % TRIGGER_MAXWDS;
- while (r != tidRotor && tidFree[r] == (u_int) -1)
- r = (r+1) % TRIGGER_MAXWDS;
- if (r == tidRotor)
- return (TRIGGER_MAXTID);
- tidRotor = r;
- }
- static u_char ffc[256] = {
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
- 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8,
- };
- // locate free id in bitmask
- u_char* bp = (u_char*) &tidFree[r];
- r *= TRIGGER_BPW;
- if (bp[0] != 0xff) { bp += 0; r += 0; } else
- if (bp[1] != 0xff) { bp += 1; r += 8; } else
- if (bp[2] != 0xff) { bp += 2; r += 16; } else
- { bp += 3, r += 24; }
- u_char b = ffc[bp[0]];
- bp[0] |= 1<<b;
- return (r+b);
- }
- /*
- * Parse a trigger specification. Syntax is:
- *
- * [<class>['<'id'>'][<mask>|'*']]*
- *
- * <class> defines a class of events and is one of:
- *
- * J job-related events
- * S send-related events
- * R receive-related events
- * M modem-related events
- *
- * <mask> is a 4-hex-digit mask of trigger events as defined
- * in the Trigger class. Bit 0 corresponds to <class>_BASE,
- * bit 1 to <class>_BASE+1, etc. If '*' is specified then any
- * event in the class is matched.
- *
- * An <id> can be used to restrict matches to a specific
- * job or modem. Eventually this will need to be generalized
- * for job groups.
- *
- * Thus an example specification that would catch any event
- * for the modem on ttyf2 would be ``M<ttyf2>*'', and to be
- * notified when job 1932 is requeued or completes one would
- * use ``J<1932>4c60''.
- */
- bool
- Trigger::parse(const char* spec0)
- {
- const char* spec = spec0;
- while (*spec) {
- u_int base;
- switch (spec[0]) {
- case 'J': base = JOB_BASE; break;
- case 'S': base = SEND_BASE; break;
- case 'R': base = RECV_BASE; break;
- case 'M': base = MODEM_BASE; break;
- default: spec++; continue;
- }
- const char* cp = spec+1;
- fxStr id;
- if (cp[0] == '<') {
- const char* tp;
- for (tp = ++cp; *tp != '>'; tp++)
- if (*tp == ' ') { // XXX syntax error
- syntaxError(spec0, "missing '>'");
- return (false);
- }
- id = fxStr(cp, tp-cp);
- cp = tp+1;
- }
- u_short& m = interests[base>>4];
- if (m != 0) {
- syntaxError(spec0, "interests conflict");
- return (false);
- }
- if (*cp == '*') {
- m = 0xffff;
- cp++;
- } else {
- u_int v = 0;
- for (u_int i = 0; i < 4; i++) {
- int c = *cp++;
- if (isxdigit(c)) {
- u_int bits = isdigit(c) ? c-'0' :
- (islower(c) ? 10+(c-'a') : 10+(c-'A'));
- v = (v<<4) | bits;
- } else {
- syntaxError(spec0, "non-hex event mask");
- return (false);
- }
- }
- m = v;
- }
- TriggerRef* tr = new TriggerRef(*this);
- /*
- * Place the trigger reference on the appropriate list.
- * If no modem or job is specified then we match any
- * job/modem by placing the reference on the wildcards list.
- * Otherwise we locate the job or modem and hook this
- * reference to the list that hangs off the object.
- */
- if (id == "") {
- tr->insert(wildcards[EventClass(base)]);
- } else if (base == MODEM_BASE || base == RECV_BASE) {
- /*
- * NB: you can install a trigger on a modem before
- * the modem ``exists''; maybe this is bad?
- */
- tr->insert(Modem::getModemByID(id).triggers);
- } else if (base == JOB_BASE || base == SEND_BASE) {
- Job* job = Job::getJobByID(id);
- if (!job) {
- logError("TRIGGER: job %s does not exist", (const char*) id);
- return (false);
- }
- tr->insert(job->triggers);
- }
- spec = cp;
- }
- return (true);
- }
- void
- Trigger::syntaxError(const char* spec, const char* msg)
- {
- logError("TRIGGER: syntax error, %s in "%s"", msg, spec);
- }
- /*
- * Cancel (delete) the trigger with the specified id.
- */
- bool
- Trigger::cancel(const char* cp)
- {
- trid_t tid = (trid_t) strtoul(cp, NULL, 10);
- if (tid < TRIGGER_MAXTID) {
- Trigger* t = triggers[tid];
- if (t) {
- if (t->cancel())
- delete t;
- return (true);
- }
- }
- return (false);
- }
- /*
- * Cancel a trigger. Because a trigger may have references
- * scattered many places this can be hard. If all the
- * references are on the wildcard lists then we can just
- * remove those and delete it. Otherwise have to just
- * clear all the interests (so no future events will be
- * posted to the client) and wait for the object holding
- * the reference to go away.
- */
- bool
- Trigger::cancel()
- {
- purgeWildRefs(); // wildcard references
- if (refs == 0)
- return (true);
- if (interests[MODEM_BASE>>4] != 0) {
- /*
- * Must explicitly search and purge references
- * associated with a modem since modems are too
- * long-lived to wait for them to be deleted.
- */
- for (ModemIter iter(Modem::list); iter.notDone(); iter++) {
- TriggerRef::purge(iter.modem().triggers, this);
- if (refs == 0)
- return (true);
- }
- }
- // clear interests so no more messages are sent
- memset(interests, 0, sizeof (interests));
- return (false);
- }
- /*
- * Printf-like interface to send a trigger message.
- */
- void
- Trigger::send(const char* fmt ...)
- {
- va_list ap;
- va_start(ap, fmt);
- fxStr msg(fxStr::vformat(fmt, ap));
- (void) client->send(msg, msg.length()+1);
- va_end(ap);
- }
- /*
- * Post an event to interested parties.
- */
- void
- Trigger::post(TriggerEvent e, const QLink& tl, const QLink& any, fxStackBuffer& msg)
- {
- TriggerMsgHeader& hdr = *((TriggerMsgHeader*) &msg[0]);
- hdr.length = (u_short) msg.getLength();
- hdr.event = (u_short) e;
- hdr.tstamp = Sys::now();
- u_short mask = 1<<(e&15);
- const QLink* ql;
- for (ql = tl.next; ql != &tl; ql = ql->next) {
- TriggerRef* tr = (TriggerRef*) ql;
- if (tr->ref.interests[e>>4] & mask) {
- HylaClient& client = *tr->ref.client;
- hdr.seqnum = client.getSeqnum();
- client.send(msg, hdr.length);
- }
- }
- for (ql = any.next; ql != &any; ql = ql->next) {
- TriggerRef* tr = (TriggerRef*) ql;
- if (tr->ref.interests[e>>4] & mask) {
- HylaClient& client = *tr->ref.client;
- hdr.seqnum = client.getSeqnum();
- client.send(msg, hdr.length);
- }
- }
- }
- static TriggerMsgHeader hdr = { '!', ' ' };
- void
- Trigger::post(TriggerEvent e, const Job& job, const char* info)
- {
- const QLink& any = wildcards[EventClass(e)];
- if (!job.triggers.isEmpty() || !any.isEmpty()) {
- fxStackBuffer msg;
- msg.put((const char*) &hdr, sizeof (hdr));
- job.encode(msg);
- if (info)
- msg.put(info);
- post(e, job.triggers, any, msg);
- }
- hook(e, job.jobid, info);
- }
- void
- Trigger::post(TriggerEvent e, const Modem& modem, const char* info)
- {
- const QLink& any = wildcards[EventClass(e)];
- if (!modem.triggers.isEmpty() || !any.isEmpty()) {
- fxStackBuffer msg;
- msg.put((const char*) &hdr, sizeof (hdr));
- modem.encode(msg);
- if (info)
- msg.put(info);
- post(e, modem.triggers, any, msg);
- }
- hook(e, modem.devID, info);
- }
- u_short Trigger::hookInterests[TRIGGER_MAXEVENT>>4];
- fxStr Trigger::hookCmd;
- void
- Trigger::hook (TriggerEvent e, const char* id, const char* info)
- {
- static const char* classNames[4] = { "JOB", "SEND", "RECV", "MODEM" };
- u_short mask = 1<<(e&15);
- if (hookInterests[e>>4] & mask)
- {
- fxStr cmd = fxStr::format("%s %s 0x%04x "%s"",
- (const char*)hookCmd, classNames[EventClass(e)], mask, id);
- if (info)
- cmd.append(fxStr::format(" "%s"", info));
- logInfo("HOOK CMD %s", (const char*)cmd);
- faxApp::runCmd(cmd);
- }
- }
- bool
- Trigger::setTriggerHook (const char* prg, const char* spec)
- {
- TriggerEvent hookTID = tidNextFree();
- if (hookTID == TRIGGER_MAXTID) {
- logError("TRIGGER: tid table overflow for EVENT");
- return false;
- }
- Trigger* event = new Trigger(hookTID, "/dev/null");
- if (!event->parse(spec))
- {
- logWarning("TRIGGER EVENT: Couldn't parse spec: %s", spec);
- }
- memcpy(hookInterests, event->interests, sizeof(hookInterests));
- event->cancel();
- delete event;
- hookCmd = prg;
- return (true);
- }