Logo Search packages:      
Sourcecode: yate version File versions  Download package

extmodule.cpp

/**
 * extmodule.cpp
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Some parts of this code have been stolen shamelessly from app_agi.
 * I think that AGI is great idea.
 * 
 * External module handler
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2004 Null Team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <telengine.h>
#include <telephony.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>


using namespace TelEngine;

static Configuration s_cfg;
static ObjList s_chans;
static ObjList s_modules;
static Mutex s_mutex(true);

class ExtModReceiver;
class ExtModChan;

class ExtModSource : public ThreadedSource
{
public:
    ExtModSource(int fd, ExtModChan* chan);
    ~ExtModSource();
    virtual void run();
private:
    int m_fd;
    unsigned m_brate;
    unsigned m_total;
    ExtModChan* m_chan;
};

class ExtModConsumer : public DataConsumer
{
public:
    ExtModConsumer(int fd);
    ~ExtModConsumer();
    virtual void Consume(const DataBlock &data, unsigned long timestamp);
private:
    int m_fd;
    unsigned m_total;
};

class ExtModChan : public DataEndpoint
{
public:
    enum {
      NoChannel,
      DataNone,
      DataRead,
      DataWrite,
      DataBoth
    };
    static ExtModChan* build(const char *file, const char *args, int type);
    ~ExtModChan();
    virtual void disconnected(bool final, const char *reason);
    inline ExtModReceiver* receiver() const
      { return m_recv; }
    inline void setRecv(ExtModReceiver* recv)
      { m_recv = recv; }
    inline void setRunning(bool running)
      { m_running = running; }
    inline bool running() const
      { return m_running; }
private:
    ExtModChan(const char *file, const char *args, int type);
    ExtModReceiver *m_recv;
    int m_type;
    bool m_running;
};

class MsgHolder : public GenObject
{
public:
    MsgHolder(Message &msg);
    Message &m_msg;
    bool m_ret;
    String m_id;
    bool decode(const char *s);
};

class ExtModReceiver : public MessageReceiver, public Mutex
{
public:
    static ExtModReceiver* build(const char *script, const char *args,
      int ain = -1, int aout = -1, ExtModChan *chan = 0);
    static ExtModReceiver* find(const String &script);
    ~ExtModReceiver();
    virtual bool received(Message &msg, int id);
    void processLine(const char *line);
    bool outputLine(const char *line);
    void reportError(const char *line);
    bool start();
    void run();
    void cleanup();
    bool flush();
    void die(bool clearChan = true);
    inline void use()
      { m_use++; }
    bool unuse();
    inline const String &scriptFile() const
      { return m_script; }
    inline const String &commandArg() const
      { return m_args; }
private:
    ExtModReceiver(const char *script, const char *args,
      int ain = -1, int aout = -1, ExtModChan *chan = 0);
    bool create(const char *script, const char *args);
    bool m_dead;
    int m_use;
    pid_t m_pid;
    int m_in, m_out, m_ain, m_aout;
    ExtModChan *m_chan;
    String m_script, m_args;
    ObjList m_waiting;
    ObjList m_reenter;
    ObjList m_relays;
};

class ExtThread : public Thread
{
public:
    ExtThread(ExtModReceiver *receiver) : Thread("ExtModule"), m_receiver(receiver)
      { }
    virtual void run()
      { m_receiver->run(); }
    virtual void cleanup()
      { m_receiver->cleanup(); }
private:
    ExtModReceiver *m_receiver;
};

class ExtModHandler : public MessageHandler
{
public:
    ExtModHandler(const char *name) : MessageHandler(name) { }
    virtual bool received(Message &msg);
};

class ExtModCommand : public MessageHandler
{
public:
    ExtModCommand(const char *name) : MessageHandler(name) { }
    virtual bool received(Message &msg);
};

class ExtModulePlugin : public Plugin
{
public:
    ExtModulePlugin();
    ~ExtModulePlugin();
    virtual void initialize();
    virtual bool isBusy() const;
private:
    ExtModHandler *m_handler;
};


ExtModSource::ExtModSource(int fd, ExtModChan* chan)
    : m_fd(fd), m_brate(16000), m_total(0), m_chan(chan)
{
    Debug(DebugAll,"ExtModSource::ExtModSource(%d) [%p]",fd,this);
    if (m_fd >= 0) {
      chan->setRunning(true);
      start("ExtModSource");
    }
}

ExtModSource::~ExtModSource()
{
    Debug(DebugAll,"ExtModSource::~ExtModSource() [%p] total=%u",this,m_total);
    m_chan->setRunning(false);
    if (m_fd >= 0) {
      ::close(m_fd);
      m_fd = -1;
    }
}

void ExtModSource::run()
{
    DataBlock data(0,480);
    int r = 0;
    unsigned long long tpos = Time::now();
    do {
      r = ::read(m_fd,data.data(),data.length());
      if (r < 0) {
          if (errno == EINTR) {
            r = 1;
            continue;
          }
          break;
      }
      if (r < (int)data.length())
          data.assign(data.data(),r);
      long long dly = tpos - Time::now();
      if (dly > 0) {
          XDebug("ExtModSource",DebugAll,"Sleeping for %lld usec",dly);
          ::usleep((unsigned long)dly);
      }
      Forward(data,m_total);
      m_total += r;
      tpos += (r*1000000ULL/m_brate);
    } while (r > 0);
    Debug(DebugAll,"ExtModSource [%p] end of data total=%u",this,m_total);
    m_chan->setRunning(false);
}

ExtModConsumer::ExtModConsumer(int fd)
    : m_fd(fd), m_total(0)
{
    Debug(DebugAll,"ExtModConsumer::ExtModConsumer(%d) [%p]",fd,this);
}

ExtModConsumer::~ExtModConsumer()
{
    Debug(DebugAll,"ExtModConsumer::~ExtModConsumer() [%p] total=%u",this,m_total);
    if (m_fd >= 0) {
      ::close(m_fd);
      m_fd = -1;
    }
}

void ExtModConsumer::Consume(const DataBlock &data, unsigned long timestamp)
{
    if ((m_fd >= 0) && !data.null()) {
      ::write(m_fd,data.data(),data.length());
      m_total += data.length();
    }
}

ExtModChan* ExtModChan::build(const char *file, const char *args, int type)
{
    ExtModChan* chan = new ExtModChan(file,args,type);
    if (!chan->m_recv) {
      chan->destruct();
      return 0;
    }
    return chan;
}

ExtModChan::ExtModChan(const char *file, const char *args, int type)
    : DataEndpoint("ExtModule"), m_recv(0), m_type(type)
{
    Debug(DebugAll,"ExtModChan::ExtModChan(%d) [%p]",type,this);
    int wfifo[2] = { -1, -1 };
    int rfifo[2] = { -1, -1 };
    switch (m_type) {
      case DataWrite:
      case DataBoth:
          ::pipe(wfifo);
          setConsumer(new ExtModConsumer(wfifo[1]));
          getConsumer()->deref();
    }
    switch (m_type) {
      case DataRead:
      case DataBoth:
          ::pipe(rfifo);
          setSource(new ExtModSource(rfifo[0],this));
          getSource()->deref();
    }
    s_mutex.lock();
    s_chans.append(this);
    s_mutex.unlock();
    m_recv = ExtModReceiver::build(file,args,wfifo[0],rfifo[1],this);
}

ExtModChan::~ExtModChan()
{
    Debugger debug(DebugAll,"ExtModChan::~ExtModChan()"," [%p]",this);
    s_mutex.lock();
    s_chans.remove(this,false);
    s_mutex.unlock();
    setSource();
    setConsumer();
    if (m_recv)
      m_recv->die(false);
}

void ExtModChan::disconnected(bool final, const char *reason)
{
    Debugger debug("ExtModChan::disconnected()"," '%s' [%p]",reason,this);
}

MsgHolder::MsgHolder(Message &msg)
    : m_msg(msg), m_ret(false)
{
    // the address of this object should be unique
    m_id << (unsigned int)this << "." << (unsigned int)random();
}

bool MsgHolder::decode(const char *s)
{
    return (m_msg.decode(s,m_ret,m_id) == -2);
}

ExtModReceiver* ExtModReceiver::build(const char *script, const char *args,
                              int ain, int aout, ExtModChan *chan)
{
    ExtModReceiver* recv = new ExtModReceiver(script,args,ain,aout,chan);
    if (!recv->start()) {
      recv->destruct();
      return 0;
    }
    return recv;
}

ExtModReceiver* ExtModReceiver::find(const String &script)
{
    Lock lock(s_mutex);
    ObjList *l = &s_modules;
    for (; l; l=l->next()) {
      ExtModReceiver *r = static_cast<ExtModReceiver *>(l->get());
      if (r && (r->scriptFile() == script))
          return r;
    }
    return 0;
}

bool ExtModReceiver::unuse()
{
    int u = --m_use;
    if (!u)
      destruct();
    return (u <= 0);
}

ExtModReceiver::ExtModReceiver(const char *script, const char *args, int ain, int aout, ExtModChan *chan)
    : Mutex(true),
      m_dead(false), m_use(0), m_pid(-1),
      m_in(-1), m_out(-1), m_ain(ain), m_aout(aout),
      m_chan(chan), m_script(script), m_args(args)
{
    Debug(DebugAll,"ExtModReceiver::ExtModReceiver(\"%s\",\"%s\") [%p]",script,args,this);
    m_script.trimBlanks();
    m_args.trimBlanks();
    s_mutex.lock();
    s_modules.append(this);
    s_mutex.unlock();
}


ExtModReceiver::~ExtModReceiver()
{   
    Debug(DebugAll,"ExtModReceiver::~ExtModReceiver() [%p] pid=%d",this,m_pid);
    Lock lock(this);
    /* One destruction is plenty enough */
    m_use = -100;
    s_mutex.lock();
    s_modules.remove(this,false);
    s_mutex.unlock();
    die();
    if (m_pid > 0)
      Debug(DebugWarn,"ExtModReceiver::~ExtModReceiver() [%p] pid=%d",this,m_pid);
}

bool ExtModReceiver::start()
{
    if (m_pid < 0) {
      ExtThread *ext = new ExtThread(this);
      ext->startup();
      while (m_pid < 0)
          Thread::yield();
    }
    return (m_pid > 0);
}


bool ExtModReceiver::flush()
{
    /* Make sure we release all pending messages and not accept new ones */
    if (!Engine::exiting())
      m_relays.clear();
    else {
      ObjList *p = &m_relays;
      for (; p; p=p->next())
          p->setDelete(false);
    }
    if (m_waiting.get()) {
      DDebug(DebugAll,"ExtModReceiver releasing pending messages [%p]",this);
      m_waiting.clear();
      Thread::yield();
      return true;
    }
    return false;
}

void ExtModReceiver::die(bool clearChan)
{
#ifdef DEBUG
    Debugger debug(DebugAll,"ExtModReceiver::die()"," pid=%d dead=%s [%p]",
      m_pid,m_dead ? "yes" : "no",this);
#else
    Debug(DebugAll,"ExtModReceiver::die() pid=%d dead=%s [%p]",
      m_pid,m_dead ? "yes" : "no",this);
#endif
    if (m_dead)
      return;
    m_dead = true;
    use();
    flush();

    ExtModChan *chan = m_chan;
    m_chan = 0;
    if (chan)
      chan->setRecv(0);

    /* Give the external script a chance to die gracefully */
    if (m_out != -1) {
      ::close(m_out);
      m_out = -1;
    }
    if (m_pid > 0) {
      Debug(DebugAll,"ExtModReceiver::die() waiting for pid=%d to die",m_pid);
      for (int i=0; i<100; i++) {
          Thread::yield();
          if (m_pid <= 0)
            break;
      }
    }
    if (m_pid > 0)
      Debug(DebugInfo,"ExtModReceiver::die() pid=%d did not exit?",m_pid);

    /* Now terminate the process and close its stdout pipe */
    if (m_in != -1) {
      ::close(m_in);
      m_in = -1;
    }
    if (m_pid > 0)
      ::kill(m_pid,SIGTERM);
    if (chan && clearChan)
          chan->disconnect();
    unuse();
}

bool ExtModReceiver::received(Message &msg, int id)
{
    lock();
    /* Check if we are no longer running or the message was generated
       by ourselves - avoid reentrance */
    if ((m_pid <= 0) || (m_in == -1) || (m_out == -1) || m_reenter.find(&msg)) {
      unlock();
      return false;
    }

    use();
    MsgHolder h(msg);
    if (outputLine(msg.encode(h.m_id))) {
      m_waiting.append(&h)->setDelete(false);
      DDebug(DebugAll,"ExtMod [%p] queued message '%s' [%p]",this,msg.c_str(),&msg);
    }
    unlock();
    for (;;) {
      Thread::yield();
      lock();
        bool ok = !m_waiting.find(&h);
      unlock();
      if (ok)
          break;
    }
    DDebug(DebugAll,"ExtMod [%p] message '%s' [%p] returning %s",this,msg.c_str(),&msg, h.m_ret ? "true" : "false");
    unuse();
    return h.m_ret;
}

bool ExtModReceiver::create(const char *script, const char *args)
{
    String tmp(script);
    int pid;
    int ext2yate[2];
    int yate2ext[2];
    int x;
    if (script[0] != '/') {
      tmp = s_cfg.getValue("general","scripts_dir","scripts/") + tmp;
    }
    script = tmp.c_str();
    if (::pipe(ext2yate)) {
      Debug(DebugWarn, "Unable to create ext->yate pipe: %s",strerror(errno));
      return false;
    }
    if (pipe(yate2ext)) {
      Debug(DebugWarn, "unable to create yate->ext pipe: %s", strerror(errno));
      ::close(ext2yate[0]);
      ::close(ext2yate[1]);
      return false;
    }
    pid = ::fork();
    if (pid < 0) {
      Debug(DebugWarn, "Failed to fork(): %s", strerror(errno));
      ::close(yate2ext[0]);
      ::close(yate2ext[1]);
      ::close(ext2yate[0]);
      ::close(ext2yate[1]);
      return false;
    }
    if (!pid) {
      /* In child - terminate all other threads if needed */
      Thread::preExec();
      /* Try to immunize child from ^C and ^\ */
      ::signal(SIGINT,SIG_IGN);
      ::signal(SIGQUIT,SIG_IGN);
      /* And restore default handlers for other signals */
      ::signal(SIGTERM,SIG_DFL);
      ::signal(SIGHUP,SIG_DFL);
      /* Redirect stdin and out */
      ::dup2(yate2ext[0], STDIN_FILENO);
      ::dup2(ext2yate[1], STDOUT_FILENO);
      /* Set audio in/out handlers */
      if (m_ain != -1)
          ::dup2(m_ain, STDERR_FILENO+1);
      else
          ::close(STDERR_FILENO+1);
      if (m_aout != -1)
          ::dup2(m_aout, STDERR_FILENO+2);
      else
          ::close(STDERR_FILENO+2);
      /* Close everything but stdin/out/err/audio */
      for (x=STDERR_FILENO+3;x<1024;x++) 
          ::close(x);
      /* Execute script */
      if (debugAt(DebugInfo))
          ::fprintf(stderr, "Execing '%s' '%s'\n", script, args);
        ::execl(script, script, args, (char *)NULL);
      ::fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
      /* Shit happened. Die as quick and brutal as possible */
      ::_exit(1);
    }
    Debug(DebugInfo,"Launched External Script %s", script);
    m_in = ext2yate[0];
    m_out = yate2ext[1];

    /* close what we're not using in the parent */
    close(ext2yate[1]);
    close(yate2ext[0]);
    m_pid = pid;
    return true;
}

void ExtModReceiver::cleanup()
{
#ifdef DEBUG
    Debugger debug(DebugAll,"ExtModReceiver::cleanup()"," [%p]",this);
#endif
    /* We must call waitpid from here - same thread we started the child */
    if (m_pid > 0) {
      /* No thread switching if possible */
      if (m_out != -1) {
          ::close(m_out);
          m_out = -1;
      }
      Thread::yield();
      int w = ::waitpid(m_pid, 0, WNOHANG);
      if (w == 0) {
          Debug(DebugWarn, "Process %d has not exited on closing stdin - we'll kill it",m_pid);
          ::kill(m_pid,SIGTERM);
          Thread::yield();
          w = ::waitpid(m_pid, 0, WNOHANG);
      }
      if (w == 0)
          Debug(DebugWarn, "Process %d has still not exited yet?",m_pid);
      else if (w < 0)
          Debug(DebugMild, "Failed waitpid on %d: %s",m_pid,strerror(errno));
      m_pid = 0;
    }
    unuse();
}

void ExtModReceiver::run()
{
    if (!create(m_script.safe(),m_args.safe())) {
      m_pid = 0;
      return;
    }
    use();
    char buffer[1024];
    int posinbuf = 0;
    DDebug(DebugAll,"ExtModReceiver::run() entering loop [%p]",this);
    for (;;) {
      use();
      int readsize = (m_in >= 0) ? ::read(m_in,buffer+posinbuf,sizeof(buffer)-posinbuf-1) : 0;
      DDebug(DebugAll,"ExtModReceiver::run() read %d",readsize);
      if (unuse())
          return;
      if (!readsize) {
          lock();
          Debug("ExtModule",DebugInfo,"Read EOF on %d [%p]",m_in,this);
          if (m_in != -1) {
            ::close(m_in);
            m_in = -1;
          }
          flush();
          unlock();
          if (m_chan && m_chan->running())
            ::usleep(1000000);
          break;
      }
      else if (readsize < 0) {
          Debug("ExtModule",DebugWarn,"Read error %d on %d [%p]",errno,m_in,this);
          break;
      }
      int totalsize = readsize + posinbuf;
      buffer[totalsize]=0;
      for (;;) {
          char *eoline = ::strchr(buffer,'\n');
          if (!eoline && ((int)::strlen(buffer) < totalsize))
            eoline=buffer+::strlen(buffer);
          if (!eoline)
            break;
          *eoline=0;
          use();
          if (buffer[0])
            processLine(buffer);
          if (unuse())
            return;
          totalsize -= eoline-buffer+1;
          ::memmove(buffer,eoline+1,totalsize+1);
      }
      posinbuf = totalsize;
    }
}

bool ExtModReceiver::outputLine(const char *line)
{
    DDebug("ExtModReceiver",DebugAll,"outputLine '%s'", line);
    if (m_out < 0)
      return false;
    ::write(m_out,line,::strlen(line));
    char nl = '\n';
    ::write(m_out,&nl,sizeof(nl));
    return true;
}

void ExtModReceiver::reportError(const char *line)
{
    Debug("ExtModReceiver",DebugWarn,"Error: '%s'", line);
    outputLine("Error in: " + String(line));
}

static bool startSkip(String &s, const char *keyword)
{
    if (s.startsWith(keyword,false)) {
        s >> keyword;
        return true;
    }
    return false;
}

void ExtModReceiver::processLine(const char *line)
{
    DDebug("ExtModReceiver",DebugAll,"processLine '%s'", line);
    {
      Lock mylock(this);
      ObjList *p = &m_waiting;
      for (; p; p=p->next()) {
          MsgHolder *msg = static_cast<MsgHolder *>(p->get());
          if (msg && msg->decode(line)) {
            DDebug("ExtModReceiver",DebugInfo,"Matched message");
            p->remove(false);
            return;
          }
      }
    }
    String id(line);
    if (startSkip(id,"%%>install:")) {
      int prio = 100;
      id >> prio >> ":";
      bool ok = true;
      ObjList *p = &m_relays;
      for (; p; p=p->next()) {
          MessageRelay *r = static_cast<MessageRelay *>(p->get());
          if (r && (*r == id)) {
            ok = false;
            break;
          }
      }
      if (ok) {
          MessageRelay *r = new MessageRelay(id,this,0,prio);
          m_relays.append(r);
          Engine::install(r);
      }
      Debug("ExtModReceiver",DebugAll,"Install '%s', prio %d %s", id.c_str(),prio,ok ? "ok" : "failed");
      String out("%%<install:");
      out << prio << ":" << id << ":" << ok;
      outputLine(out);
      return;
    }
    else if (startSkip(id,"%%>uninstall:")) {
      int prio = 0;
      bool ok = false;
      ObjList *p = &m_relays;
      for (; p; p=p->next()) {
          MessageRelay *r = static_cast<MessageRelay *>(p->get());
          if (r && (*r == id)) {
            prio = r->priority();
            p->remove();
            ok = true;
            break;
          }
      }
      Debug("ExtModReceiver",DebugAll,"Uninstall '%s' %s", id.c_str(),ok ? "ok" : "failed");
      String out("%%<uninstall:");
      out << prio << ":" << id << ":" << ok;
      outputLine(out);
      return;
    }
    else {
      Message m("");
      if (m.decode(line,id) == -2) {
          DDebug("ExtModReceiver",DebugAll,"Created message [%p]",this);
          lock();
          m.userData(m_chan);
          /* Copy the user data pointer from waiting message with same id */
          ObjList *p = &m_waiting;
          for (; p; p=p->next()) {
            MsgHolder *h = static_cast<MsgHolder *>(p->get());
            if (h && (h->m_id == id)) {
                void *ud = h->m_msg.userData();
                Debug("ExtModReceiver",DebugAll,"Copying data pointer %p from %p",ud,&(h->m_msg));
                m.userData(ud);
                break;
            }
          }
          /* Temporary add to the reenter list to avoid reentrance */
          m_reenter.append(&m)->setDelete(false);
          unlock();
          String ret(m.encode(Engine::dispatch(m),id));
          lock();
          outputLine(ret);
          m_reenter.remove(&m,false);
          unlock();
          return;
      }
    }
    reportError(line);
}

bool ExtModHandler::received(Message &msg)
{
    String dest(msg.getValue("callto"));
    if (dest.null())
      return false;
    Regexp r("^external/\\([^/]*\\)/\\([^ ]*\\)\\(.*\\)$");
    if (!dest.matches(r))
      return false;
    DataEndpoint *dd = static_cast<DataEndpoint *>(msg.userData());
    String t = dest.matchString(1);
    int typ = 0;
    if (t == "nochan")
      typ = ExtModChan::NoChannel;
    else if (t == "nodata")
      typ = ExtModChan::DataNone;
    else if (t == "play")
      typ = ExtModChan::DataRead;
    else if (t == "record")
      typ = ExtModChan::DataWrite;
    else if (t == "playrec")
      typ = ExtModChan::DataBoth;
    else {
      Debug(DebugGoOn,"Invalid ExtModule method '%s', use 'nochan', 'nodata', 'play', 'record' or 'playrec'",
          t.c_str());
      return false;
    }
    if (typ == ExtModChan::NoChannel) {
      ExtModReceiver *r = ExtModReceiver::build(dest.matchString(2).c_str(),
                                      dest.matchString(3).trimBlanks().c_str());
      return r ? r->received(msg,1) : false;
    }
    if (typ != ExtModChan::DataNone && !dd) {
      Debug(DebugGoOn,"ExtMod '%s' call found but no data channel!",t.c_str());
      return false;
    }
    ExtModChan *em = ExtModChan::build(dest.matchString(2).c_str(),
                               dest.matchString(3).c_str(),typ);
    if (!em) {
      Debug(DebugGoOn,"Failed to create ExtMod for '%s'",dest.matchString(2).c_str());
      return false;
    }
    if (!(em->receiver() && em->receiver()->received(msg,1))) {
      Debug(DebugWarn,"ExtMod '%s' did not handle call message",dest.matchString(2).c_str());
      em->deref();
      return false;
    }
    if (dd && dd->connect(em))
      em->deref();
    return true;
}

bool ExtModCommand::received(Message &msg)
{
    String line(msg.getValue("line"));
    if (!line.startsWith("external",true))
      return false;
    line >> "external";
    line.trimBlanks();
    if (line.null()) {
      msg.retValue() = "";
      int n = 0;
      Lock lock(s_mutex);
      ObjList *l = &s_modules;
      for (; l; l=l->next()) {
          ExtModReceiver *r = static_cast<ExtModReceiver *>(l->get());
          if (r)
            msg.retValue() << ++n << ". " << r->scriptFile() << " " << r->commandArg() << "\n";
      }
      return true;
    }
    int blank = line.find(' ');
    if (blank <= 0) {
      if (line == "stop")
          return false;
      ExtModReceiver *r = ExtModReceiver::build(line,0);
      msg.retValue() = r ? "External start attempt\n" : "External command failed!\n";
      return true;
    }
    if (line.startSkip("stop")) {
      if (line.null())
          return false;
      ExtModReceiver *r = ExtModReceiver::find(line);
      if (r) {
          r->destruct();
          msg.retValue() = "External command stopped\n";
      }
      else
          msg.retValue() = "External not running\n";
      return true;
    }
    ExtModReceiver *r = ExtModReceiver::build(line.substr(0,blank),line.substr(blank+1));
    msg.retValue() = r ? "External start attempt\n" : "External command failed\n";
    return true;
}

ExtModulePlugin::ExtModulePlugin()
    : m_handler(0)
{
    Output("Loaded module ExtModule");
}

ExtModulePlugin::~ExtModulePlugin()
{
    Output("Unloading module ExtModule");
    s_mutex.lock();
    s_modules.clear();
    // the receivers destroyed above should also clear chans but better be sure
    s_chans.clear();
    s_mutex.unlock();
}

bool ExtModulePlugin::isBusy() const
{
    Lock lock(s_mutex);
    return (s_chans.count() != 0);
}

void ExtModulePlugin::initialize()
{
    Output("Initializing module ExtModule");
    s_cfg = Engine::configFile("extmodule");
    s_cfg.load();
    if (!m_handler) {
      m_handler = new ExtModHandler("call.execute");
      Engine::install(m_handler);
      Engine::install(new ExtModCommand("engine.command"));
      NamedList *list = s_cfg.getSection("scripts");
      if (list)
      {
          unsigned int len = list->length();
          for (unsigned int i=0; i<len; i++) {
            NamedString *n = list->getParam(i);
            if (n)
                ExtModReceiver::build(n->name(),*n);
          }
      }
    }
}

INIT_PLUGIN(ExtModulePlugin);

/* vi: set ts=8 sw=4 sts=4 noet: */

Generated by  Doxygen 1.6.0   Back to index