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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: Trigger.c++,v 1.7 2008/04/26 22:34:28 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1995-1996 Sam Leffler
  4.  * Copyright (c) 1995-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 "Trigger.h"
  28. #include "TriggerRef.h"
  29. #include "HylaClient.h"
  30. #include "Sys.h"
  31. #include "Job.h"
  32. #include "Modem.h"
  33. #include "StackBuffer.h"
  34. #include "config.h"
  35. #include <ctype.h>
  36. #include <errno.h>
  37. /*
  38.  * Trigger Support.
  39.  */
  40. #define EventClass(e) (e>>4)
  41. u_int Trigger::tidFree[TRIGGER_MAXWDS];
  42. u_int Trigger::tidRotor = 0;
  43. Trigger* Trigger::triggers[TRIGGER_MAXTID];
  44. QLink Trigger::wildcards[TRIGGER_MAXEVENT>>4];
  45. Trigger::Trigger(trid_t t, const fxStr& fifoName) : tid(t)
  46. {
  47.     refs = 0;
  48.     interests[JOB_BASE>>4]   = 0;
  49.     interests[SEND_BASE>>4]  = 0;
  50.     interests[RECV_BASE>>4]  = 0;
  51.     interests[MODEM_BASE>>4] = 0;
  52.     client = &HylaClient::getClient(fifoName);
  53.     client->inc();
  54.     triggers[tid] = this; // register in master table
  55. }
  56. Trigger::~Trigger()
  57. {
  58.     if (refs > 0)
  59. purgeWildRefs();
  60.     if (refs != 0) // NB: should be no other references
  61. logError("Trigger %u deleted with %u references", tid, refs);
  62.     /*
  63.      * Remove references to this trigger from the master
  64.      * table and clear the tid from the allocation bitmap.
  65.      * We release the reference on the client and if this
  66.      * is the last reference to the client we purge it.
  67.      */
  68.     triggers[tid] = NULL;
  69.     // NB: we mimic the logic below to avoid byte-order issues
  70.     u_int i = tid / TRIGGER_BPW;
  71.     u_int b = tid % TRIGGER_BPW;
  72.     ((u_char*) &tidFree[i])[b>>3] &= ~(1<<(b&7));
  73.     client->dec();
  74. }
  75. /*
  76.  * Remove all references to this that appear
  77.  * on a reference list (used for wildcards).
  78.  */
  79. void
  80. Trigger::purgeWildRefs()
  81. {
  82.     for (u_int i = 0; i < EventClass(TRIGGER_MAXEVENT) && refs > 0; i++) {
  83. QLink& w = wildcards[i];
  84. if (!w.isEmpty())
  85.     TriggerRef::purge(w, this);
  86.     }
  87. }
  88. /*
  89.  * Delete all the triggers associated
  90.  * with the specified client.
  91.  */
  92. void
  93. Trigger::purgeClient(HylaClient* hc)
  94. {
  95.     u_int refs = hc->refs;
  96.     for (u_int w = 0; w < TRIGGER_MAXWDS; w++) {
  97. if (Trigger::tidFree[w] != 0) {
  98.     u_int n = TRIGGER_BPW-1; 
  99.     u_int i = w*TRIGGER_BPW;
  100.     do {
  101. Trigger* t = triggers[i++];
  102. if (t && t->client == hc) {
  103.     if (t->cancel())
  104. delete t;
  105.     if (--refs == 0) // found all triggers
  106. return;
  107.     // check if this block is now empty
  108.     if (Trigger::tidFree[w] == 0)
  109. break;
  110. }
  111.     } while (--n);
  112. }
  113.     }
  114.     logError("Lost trigger for %s", (const char*) hc->fifoName);
  115. }
  116. /*
  117.  * Create a trigger for the client process listening on
  118.  * the specified FIFO.  The interests are given by the
  119.  * specification string.
  120.  */
  121. void
  122. Trigger::create(const fxStr& fifoName, const char* spec)
  123. {
  124.     trid_t tid = tidNextFree();
  125.     if (tid == TRIGGER_MAXTID) {
  126. HylaClient::getClient(fifoName).send("T!", 3);
  127. logError("TRIGGER: tid table overflow");
  128.     } else {
  129. Trigger* tr = new Trigger(tid, fifoName);
  130. if (!tr->parse(spec)) {
  131.     tr->send("T!");
  132.     delete tr;
  133. } else
  134.     tr->send("T*%u", tid);
  135.     }
  136. }
  137. /*
  138.  * Return a free trigger id if one is available.
  139.  * If the table is full return TRIGGER_MAXTID
  140.  */
  141. trid_t
  142. Trigger::tidNextFree()
  143. {
  144.     u_int r = tidRotor;
  145.     if (tidFree[r] == (u_int) -1) {
  146. /*
  147.  * This word is full, move the rotor
  148.  * forward looking for a word with space.
  149.  */
  150. r = (r+1) % TRIGGER_MAXWDS;
  151. while (r != tidRotor && tidFree[r] == (u_int) -1)
  152.     r = (r+1) % TRIGGER_MAXWDS;
  153. if (r == tidRotor)
  154.     return (TRIGGER_MAXTID);
  155. tidRotor = r;
  156.     }
  157.     static u_char ffc[256] = {
  158.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  159.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  160.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  161.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6,
  162.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  163.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  164.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  165.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7,
  166.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  167.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  168.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  169.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6,
  170.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  171.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  172.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  173.         0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8,
  174.     };
  175.     // locate free id in bitmask
  176.     u_char* bp = (u_char*) &tidFree[r];
  177.     r *= TRIGGER_BPW;
  178.     if (bp[0] != 0xff) { bp += 0; r +=  0; } else
  179.     if (bp[1] != 0xff) { bp += 1; r +=  8; } else
  180.     if (bp[2] != 0xff) { bp += 2; r += 16; } else
  181.             { bp += 3, r += 24; }
  182.     u_char b = ffc[bp[0]];
  183.     bp[0] |= 1<<b;
  184.     return (r+b);
  185. }
  186. /*
  187.  * Parse a trigger specification.  Syntax is:
  188.  *
  189.  *   [<class>['<'id'>'][<mask>|'*']]*
  190.  *
  191.  * <class> defines a class of events and is one of:
  192.  *
  193.  *   J job-related events
  194.  *   S send-related events
  195.  *   R receive-related events
  196.  *   M modem-related events
  197.  *
  198.  * <mask> is a 4-hex-digit mask of trigger events as defined
  199.  * in the Trigger class.  Bit 0 corresponds to <class>_BASE,
  200.  * bit 1 to <class>_BASE+1, etc.  If '*' is specified then any
  201.  * event in the class is matched.
  202.  *
  203.  * An <id> can be used to restrict matches to a specific
  204.  * job or modem.  Eventually this will need to be generalized
  205.  * for job groups.
  206.  *
  207.  * Thus an example specification that would catch any event
  208.  * for the modem on ttyf2 would be ``M<ttyf2>*'', and to be
  209.  * notified when job 1932 is requeued or completes one would
  210.  * use ``J<1932>4c60''.
  211.  */
  212. bool
  213. Trigger::parse(const char* spec0)
  214. {
  215.     const char* spec = spec0;
  216.     while (*spec) {
  217. u_int base;
  218. switch (spec[0]) {
  219. case 'J': base = JOB_BASE; break;
  220. case 'S': base = SEND_BASE; break;
  221. case 'R': base = RECV_BASE; break;
  222. case 'M': base = MODEM_BASE; break;
  223. default: spec++; continue;
  224. }
  225. const char* cp = spec+1;
  226. fxStr id;
  227. if (cp[0] == '<') {
  228.     const char* tp;
  229.     for (tp = ++cp; *tp != '>'; tp++)
  230. if (*tp == '') { // XXX syntax error
  231.     syntaxError(spec0, "missing '>'");
  232.     return (false);
  233. }
  234.     id = fxStr(cp, tp-cp);
  235.     cp = tp+1;
  236. }
  237. u_short& m = interests[base>>4];
  238. if (m != 0) {
  239.     syntaxError(spec0, "interests conflict");
  240.     return (false);
  241. }
  242. if (*cp == '*') {
  243.     m = 0xffff;
  244.     cp++;
  245. } else {
  246.     u_int v = 0;
  247.     for (u_int i = 0; i < 4; i++) {
  248. int c = *cp++;
  249. if (isxdigit(c)) {
  250.     u_int bits = isdigit(c) ? c-'0' :
  251. (islower(c) ? 10+(c-'a') : 10+(c-'A'));
  252.     v = (v<<4) | bits;
  253. } else {
  254.     syntaxError(spec0, "non-hex event mask");
  255.     return (false);
  256. }
  257.     }
  258.     m = v;
  259. }
  260. TriggerRef* tr = new TriggerRef(*this);
  261. /*
  262.  * Place the trigger reference on the appropriate list.
  263.  * If no modem or job is specified then we match any
  264.  * job/modem by placing the reference on the wildcards list.
  265.  * Otherwise we locate the job or modem and hook this
  266.  * reference to the list that hangs off the object.
  267.  */
  268. if (id == "") {
  269.     tr->insert(wildcards[EventClass(base)]);
  270. } else if (base == MODEM_BASE || base == RECV_BASE) {
  271.     /*
  272.      * NB: you can install a trigger on a modem before
  273.      *     the modem ``exists''; maybe this is bad?
  274.      */
  275.     tr->insert(Modem::getModemByID(id).triggers);
  276. } else if (base == JOB_BASE || base == SEND_BASE) {
  277.     Job* job = Job::getJobByID(id);
  278.     if (!job) {
  279. logError("TRIGGER: job %s does not exist", (const char*) id);
  280. return (false);
  281.     }
  282.     tr->insert(job->triggers);
  283. }
  284. spec = cp;
  285.     }
  286.     return (true);
  287. }
  288. void
  289. Trigger::syntaxError(const char* spec, const char* msg)
  290. {
  291.     logError("TRIGGER: syntax error, %s in "%s"", msg, spec);
  292. }
  293. /*
  294.  * Cancel (delete) the trigger with the specified id.
  295.  */
  296. bool
  297. Trigger::cancel(const char* cp)
  298. {
  299.     trid_t tid = (trid_t) strtoul(cp, NULL, 10);
  300.     if (tid < TRIGGER_MAXTID) {
  301. Trigger* t = triggers[tid];
  302. if (t) {
  303.     if (t->cancel())
  304. delete t;
  305.     return (true);
  306. }
  307.     }
  308.     return (false);
  309. }
  310. /*
  311.  * Cancel a trigger.  Because a trigger may have references
  312.  * scattered many places this can be hard.  If all the
  313.  * references are on the wildcard lists then we can just
  314.  * remove those and delete it.  Otherwise have to just
  315.  * clear all the interests (so no future events will be
  316.  * posted to the client) and wait for the object holding
  317.  * the reference to go away.
  318.  */
  319. bool
  320. Trigger::cancel()
  321. {
  322.     purgeWildRefs(); // wildcard references
  323.     if (refs == 0)
  324. return (true);
  325.     if (interests[MODEM_BASE>>4] != 0) {
  326. /*
  327.  * Must explicitly search and purge references
  328.  * associated with a modem since modems are too
  329.  * long-lived to wait for them to be deleted.
  330.  */
  331. for (ModemIter iter(Modem::list); iter.notDone(); iter++) {
  332.     TriggerRef::purge(iter.modem().triggers, this);
  333.     if (refs == 0)
  334. return (true);
  335. }
  336.     }
  337.     // clear interests so no more messages are sent
  338.     memset(interests, 0, sizeof (interests));
  339.     return (false);
  340. }
  341. /*
  342.  * Printf-like interface to send a trigger message.
  343.  */
  344. void
  345. Trigger::send(const char* fmt ...)
  346. {
  347.     va_list ap;
  348.     va_start(ap, fmt);
  349.     fxStr msg(fxStr::vformat(fmt, ap));
  350.     (void) client->send(msg, msg.length()+1);
  351.     va_end(ap);
  352. }
  353. /*
  354.  * Post an event to interested parties.
  355.  */
  356. void
  357. Trigger::post(TriggerEvent e, const QLink& tl, const QLink& any, fxStackBuffer& msg)
  358. {
  359.     TriggerMsgHeader& hdr = *((TriggerMsgHeader*) &msg[0]);
  360.     hdr.length = (u_short) msg.getLength();
  361.     hdr.event = (u_short) e;
  362.     hdr.tstamp = Sys::now();
  363.     u_short mask = 1<<(e&15);
  364.     const QLink* ql;
  365.     for (ql = tl.next; ql != &tl; ql = ql->next) {
  366. TriggerRef* tr = (TriggerRef*) ql;
  367. if (tr->ref.interests[e>>4] & mask) {
  368.     HylaClient& client = *tr->ref.client;
  369.     hdr.seqnum = client.getSeqnum();
  370.     client.send(msg, hdr.length);
  371. }
  372.     }
  373.     for (ql = any.next; ql != &any; ql = ql->next) {
  374. TriggerRef* tr = (TriggerRef*) ql;
  375. if (tr->ref.interests[e>>4] & mask) {
  376.     HylaClient& client = *tr->ref.client;
  377.     hdr.seqnum = client.getSeqnum();
  378.     client.send(msg, hdr.length);
  379. }
  380.     }
  381. }
  382. static TriggerMsgHeader hdr = { '!', ' ' };
  383. void
  384. Trigger::post(TriggerEvent e, const Job& job, const char* info)
  385. {
  386.     const QLink& any = wildcards[EventClass(e)];
  387.     if (!job.triggers.isEmpty() || !any.isEmpty()) {
  388. fxStackBuffer msg;
  389. msg.put((const char*) &hdr, sizeof (hdr));
  390. job.encode(msg);
  391. if (info)
  392.     msg.put(info);
  393. post(e, job.triggers, any, msg);
  394.     }
  395.     hook(e, job.jobid, info);
  396. }
  397. void
  398. Trigger::post(TriggerEvent e, const Modem& modem, const char* info)
  399. {
  400.     const QLink& any = wildcards[EventClass(e)];
  401.     if (!modem.triggers.isEmpty() || !any.isEmpty()) {
  402. fxStackBuffer msg;
  403. msg.put((const char*) &hdr, sizeof (hdr));
  404. modem.encode(msg);
  405. if (info)
  406.     msg.put(info);
  407. post(e, modem.triggers, any, msg);
  408.     }
  409.     hook(e, modem.devID, info);
  410. }
  411. u_short Trigger::hookInterests[TRIGGER_MAXEVENT>>4];
  412. fxStr Trigger::hookCmd;
  413. void
  414. Trigger::hook (TriggerEvent e, const char* id, const char* info)
  415. {
  416.     static const char* classNames[4] = { "JOB", "SEND", "RECV", "MODEM" };
  417.     u_short mask = 1<<(e&15);
  418.     if (hookInterests[e>>4] & mask)
  419.     {
  420. fxStr cmd = fxStr::format("%s %s 0x%04x "%s"",
  421. (const char*)hookCmd, classNames[EventClass(e)], mask, id);
  422. if (info)
  423. cmd.append(fxStr::format(" "%s"", info));
  424. logInfo("HOOK CMD %s", (const char*)cmd);
  425. faxApp::runCmd(cmd);
  426.     }
  427. }
  428. bool
  429. Trigger::setTriggerHook (const char* prg, const char* spec)
  430. {
  431.     TriggerEvent hookTID = tidNextFree();
  432.     if (hookTID == TRIGGER_MAXTID) {
  433. logError("TRIGGER: tid table overflow for EVENT");
  434. return false;
  435.     }
  436.     Trigger* event = new Trigger(hookTID, "/dev/null");
  437.     if (!event->parse(spec))
  438.     {
  439.         logWarning("TRIGGER EVENT: Couldn't parse spec: %s", spec);
  440.     }
  441.     memcpy(hookInterests, event->interests, sizeof(hookInterests));
  442.     event->cancel();
  443.     delete event;
  444.     hookCmd = prg;
  445.     return (true);
  446. }