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

server.cpp

// Copyright (C) 2000-2001 Open Source Telecom Corporation.
//  
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#ifdef  __FreeBSD__
#define getopt(a, b, c) getopt()
#include <unistd.h>
#undef getopt
#endif

#include <cc++/config.h>
#include <cc++/process.h>
#include <cc++/url.h>
#include <getopt.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <cerrno>
#include "server.h"
#include <iomanip>

#ifdef      DLL_SERVER
#define     PLUGIN_SUBDIR     ".libs/"
#else
#define     PLUGIN_SUBDIR     ""
#endif

#ifdef      _POSIX_MEMLOCK
#include <sys/mman.h>
#endif

#ifdef      CCXX_NAMESPACES
namespace ost {
using namespace std;
#endif

ThreadLock cachelock;
bool running = false;
unsigned numbering = 0;
int mainpid;
int tgipipe[2];
bool restart_server = false;
bool upflag = false;
char service[65] = "";
static int *tgipids = NULL;
static Resolver *resolver = NULL;
static char **restart_argv;
static char **cmds;
static char *inits[128];
static unsigned icount = 0;

static int pidfile(char *fpath)
{
      int fd;
      pid_t pid;
      time_t now;
      struct stat ino;
      int len, status;
      int mask;
      char buffer[65];

      for(;;)
      {
            fd = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0660);
            if(fd > 0)
            {
                  pid = getpid();
                  snprintf(buffer, sizeof(buffer), "%d\n", pid);
                  write(fd, buffer, strlen(buffer));
                  close(fd);
                  return 0;
            }
            if(fd < 0 && errno != EEXIST)
                  return -1;

            fd = open(fpath, O_RDONLY);
            if(fd < 0)
            {
                  if(errno == ENOENT)
                        continue;
                  return -1;
            }
            sleep(2);
            status = read(fd, buffer, sizeof(buffer) - 1);
            if(status < 1)
            {
                  close(fd);
                  continue;
            }

            buffer[status] = 0;
            pid = atoi(buffer);
            if(pid)
            {
                  if(pid == getpid())
                  {
                        status = -1;
                        errno = 0;
                  }
                  else
                        status = kill(pid, 0);

                  if(!status || (errno == EPERM))
                  {
                        close(fd);
                        return pid;
                  }
            }
            close(fd);
            unlink(fpath);
      }
}

#ifndef     HAVE_SETENV
static void setenv(char *name, const char *value, int overwrite)
{
      char strbuf[256];

      snprintf(strbuf, sizeof(strbuf), "%s=%s", name, value);
      putenv(strbuf);
}
#endif

static void purge(const char *path)
{
      if(!isDir(path))
            return;

      char fullpath[256];
      char *ex[4];
      int pid;
      Dir dir(path);
      char *name;

      while(NULL != (name = (char *)dir.getName()))
      {
            if(!stricmp(name, "."))
                  continue;

            if(!stricmp(name, ".."))
                  continue;

            pid = fork();
            if(pid < 0)
                  return;

            if(pid)
            {
                  waitpid(pid, NULL, 0);
                  continue;
            }

            snprintf(fullpath, sizeof(fullpath), "%s/%s", path, name);
            ex[0] = "rm";
            ex[1] = "-rf";
            ex[2] = fullpath;
            ex[3] = NULL;

            exit(execvp("rm", ex));
      }
}
            
static void rt(void)
{
      int min, max;
      int pages = keythreads.getPages();
      int pri = keythreads.getPriority();
      int policy = keythreads.getPolicy();
      char memory[1024 * pages];

      memset(memory, sizeof(memory), 0);

#ifdef      _POSIX_PRIORITY_SCHEDULING
      struct sched_param p;

      sched_getparam(0, &p);
      pri += p.sched_priority;
      if(!policy)
            policy = sched_getscheduler(0);

      min = sched_get_priority_min(policy);
      max = sched_get_priority_max(policy);
      if(pri < min)
            pri = min;
      if(pri > max)
            pri = max;
      p.sched_priority = pri;
      sched_setscheduler(getpid(), policy, &p);
#else
      nice(-pri);
#endif

#ifdef      MCI_CURRENT
      if(pages)
            mlockall(MCI_CURRENT | MCI_FUTURE);
#endif
}

#ifdef      HAVE_TGI

static RETSIGTYPE tgiusr1(int signo)
{
}

static void tgi(int cfd)
{
      int argc;
      char *argv[65];
      char *user;
      char buffer[PIPE_BUF / 2];
      int vpid, pool, stat;
      int count = keythreads.getGateways();
      tgipids = new int[count];
      sigset_t sigs;
      struct sigaction act;
      tgicmd_t cmd;
      TGI *tgi;
      int status;
      char *cp;
      int len, fd;
      char **arg;
      const char *prefix;

      if(canModify(keypaths.getRunfiles()))
      {
            strcpy(buffer, keypaths.getRunfiles());
            strcat(buffer, "/bayonne");
            setenv("SERVER_CONTROL", buffer, 1);
            strcpy(buffer, keypaths.getRunfiles());
            strcat(buffer, "/bayonne.ctrl");
      }
      else
      {
            sprintf(buffer, "%s/.bayonne", getenv("HOME"));
            setenv("SERVER_CONTROL", buffer, 1);
            sprintf(buffer, "%s/.bayonne.ctrl", getenv("HOME"));
      }

      pipe(tgipipe);

      for(pool = 0; pool < count; ++pool)
      {
#ifdef      __FreeBSD__
            tgipids[pool] = vfork();
#else
            tgipids[pool] = fork();
#endif
            if(!tgipids[pool])
                  break;
      }
      
      if(pool == count)
      {
            close(tgipipe[0]);
            return;
      }

      close(tgipipe[1]);

      arg = cmds;
      while(*arg)
      {     
            len = strlen(*arg);
            memset(*arg, 0, len);
            ++arg;
      }
      strcpy(cmds[0], "[tgi]");
      keyserver.setGid();
      keyserver.setUid();
      sigemptyset(&sigs);
      sigaddset(&sigs, SIGINT);
      signal(SIGINT, SIG_DFL);
      sigprocmask(SIG_UNBLOCK, &sigs, NULL);

      act.sa_handler = &tgiusr1;
      act.sa_flags = SA_RESTART;
      sigemptyset(&act.sa_mask);
      sigaction(SIGUSR1, &act, NULL);     

      slog(Slog::levelNotice) << "tgi: initialized; uid=" << getuid() << " pid=" << getpid() << endl;
      nice(-keythreads.priGateway());

      while(cfd > -1 && cfd < 3)
            cfd = dup(cfd);
      if(cfd < 0)
            slog(Slog::levelError) << "tgi: fifo failed; buffer=" << buffer << "; failure=" << strerror(errno) << endl;
      else
            slog(Slog::levelDebug) << "tgi: buffer=" << buffer << "; cfd=" << cfd << "; time=" << count * 10 << endl;

      while(read(tgipipe[0], &cmd, sizeof(cmd)) == sizeof(cmd))
      {
            argc = 0;
            slog(Slog::levelDebug) << "tgi: cmd=" << cmd.cmd << endl;
            tgi = getInterp(cmd.cmd);
            if(tgi)
            {
                  if(cmd.mode != TGI_EXEC_DETACH)
                        sprintf(buffer, "wait %d %d\n", cmd.port, getpid());
                  status = tgi->parse(cfd, cmd.port, cmd.cmd);
                  if(cmd.mode != TGI_EXEC_DETACH)
                        sprintf(buffer, "exit %d %d\n", cmd.port, status);
                  continue;
            }
            vpid = vfork();
            if(vpid)
            {
                  if(cmd.mode != TGI_EXEC_DETACH)
                  {
                        sprintf(buffer, "wait %d %d\n", cmd.port, vpid);
                        write(cfd, buffer, strlen(buffer));
                        if(keythreads.getAudit())
                              slog(Slog::levelDebug) << "thread: gateway waiting on pid " << vpid << endl;
                  }
                  else
                  {
                        if(keythreads.getAudit())
                              slog(Slog::levelDebug) << "thread: gateway detached pid " << vpid << endl;
                  }

                  if(cmd.mode != TGI_EXEC_DETACH)
                  {
#ifdef      __FreeBSD__
                        wait4(vpid, &stat, 0, NULL);
#else
                        waitpid(vpid, &stat, 0);
#endif
                        if(keythreads.getAudit())
                              slog(Slog::levelDebug) << "thread: gateway exiting pid " << vpid << endl;
                        sprintf(buffer, "exit %d %d\n", cmd.port, WEXITSTATUS(stat));
                        write(cfd, buffer, strlen(buffer));
                  }
                  else
#ifdef      __FreeBSD__
                        wait4(vpid, &stat, 0, NULL);
#else
                        waitpid(vpid, &stat, 0);
#endif
                  continue;
            }

            if(cmd.mode == TGI_EXEC_DETACH)
            {
#ifndef     COMMON_PROCESS_ATTACH
                  ::close(0);
                  ::close(1);
                        ::close(2);
#endif
                  Process::detach();
#ifndef     COMMON_PROCESS_ATTACH
                  ::open("/dev/null", O_RDWR);
                  ::open("/dev/null", O_RDWR);
                  ::open("/dev/null", O_RDWR);
#endif
                  strcpy(cmds[0], "-detach");
            }
            else
                  sprintf(cmds[0], "tgi/%03d", cmd.port);

            user = NULL;
            sprintf(buffer, "%d", cmd.port);
            setenv("PORT_NUMBER", buffer, 1);
            argv[argc++] = strtok(cmd.cmd, " \t");
            while(NULL != (argv[argc] = strtok(NULL, " \t")))
            {
                  /* Used for standard TGI execution */

                  if(!strnicmp(argv[argc], "user=", 5))
                  {
                        user = argv[argc] + 5;
                        setenv("PORT_USER", user, 1);
                        continue;
                  }

                  if(!strnicmp(argv[argc], "digits=", 7))
                  {
                        setenv("PORT_DIGITS", argv[argc] + 7, 1);
                        continue;
                  }

                  if(!strnicmp(argv[argc], "dnid=", 5))
                  {
                        setenv("PORT_DNID", argv[argc] + 5, 1);
                        continue;
                  }

                  if(!strnicmp(argv[argc], "clid=", 5))
                  {
                        setenv("PORT_CLID", argv[argc] + 5, 1);
                        continue;
                  }

                  if(!strnicmp(argv[argc], "query=", 6))
                  {
                        setenv("PORT_QUERY", argv[argc] + 6, 1);
                        continue;
                  }

                  // Added for TTS audio format selection

                  if(!strnicmp(argv[argc], "format=", 6))
                  {
                        setenv("TTS_FORMAT", argv[argc] + 6, 1);
                        continue;
                  }

                  // Added for TTS audio file type and ASR filename

                  if(!strnicmp(argv[argc], "audio=", 6))
                  {
                        if(cmd.mode == TGI_EXEC_AUDIO)
                        {
                              remove(argv[argc] + 6);
                              setenv("TTS_AUDIO", argv[argc] + 6, 1);
                        }
                        else
                              setenv("ASR_AUDIO", argv[argc] + 6, 1);
                        setenv("TEMP_AUDIO", argv[argc] + 6, 1);
                        continue;
                  }

                  // Added for TTS/ASR language specification

                  if(!strnicmp(argv[argc], "language=", 9))
                  {
                        setenv("TTS_LANGUAGE", argv[argc] + 9, 1); 
                        setenv("ASR_LANGUAGE", argv[argc] + 9, 1);
                        continue;
                  }

                  // Added for TTS/ASR voice domain selection

                  if(!strnicmp(argv[argc], "voice=", 6))
                  {
                        setenv("TTS_VOICE", argv[argc] + 6, 1);
                        setenv("ASR_VOICE", argv[argc] + 6, 1);
                        continue;
                  }

                  // Added for domain vocabulary selection

                  if(!strnicmp(argv[argc], "domain=", 7))
                  {
                        setenv("ASR_DOMAIN", argv[argc] + 7, 1);
                        continue;
                  }

                  // Added for ASR event trapping

                  if(!strnicmp(argv[argc], "asrevt=", 7))
                  {
                        setenv("ASR_EVENTS", argv[argc] + 7, 1);
                        continue;
                  }

                  // Added for passing TTS phrases

                  if(!strnicmp(argv[argc], "phrase=", 7))
                  {
                        while(NULL != (cp = strchr(argv[argc], '+')))
                              *cp = ' ';
                        while(NULL != (cp = strchr(argv[argc], ',')))
                              *cp = ' ';
                        setenv("TTS_PHRASE", argv[argc] + 7, 1);
                        continue;
                  }

                  // Added for passing TTS source if file based

                  if(!strnicmp(argv[argc], "source=", 7))
                  {
                        setenv("TTS_SOURCE", argv[argc] + 7, 1);
                        continue;
                  } 

                  // Added for url helpers and such
                  if(!strnicmp(argv[argc], "href=", 5))
                  {
                        setenv("URL_HREF", argv[argc] + 5, 1);
                        continue;
                  }

                  if(!strnicmp(argv[argc], "base=", 5))
                  {
                        setenv("URL_BASE", argv[argc] + 5, 1);
                        continue;
                  }

                  if(!strnicmp(argv[argc], "url=", 4))
                  {
                        setenv("URL_SOURCE", argv[argc] + 4, 1);
                        continue;
                  }

                  // Added for passing TTS cache path

                  if(!strnicmp(argv[argc], "cache=", 6))
                  {
                        setenv("TTS_CACHE", argv[argc] + 6, 1);
                        continue;
                  }

                  ++argc;
            }
            argv[argc] = NULL;
//          slog.close();
            close(0);
            close(1);
//          close(2);
            open("/dev/null", O_RDWR);
            dup2(cfd, 1);
//          dup2(cfd, 2);
            close(cfd);

            if(!stricmp(*argv, "-log"))
            {
                  snprintf(buffer, sizeof(buffer), "%s/%s",
                        keypaths.getLast("logpath"),
                        urlDecode(argv[1]));
                  fd = open(buffer, O_CREAT | O_WRONLY | O_APPEND, 0660);
                  if(fd < 0)
                        exit(-1);
                  snprintf(buffer, sizeof(buffer), "%s\n",
                        urlDecode(argv[2]));
                  write(fd, buffer, strlen(buffer));
                  close(fd);
                  exit(0);
            }

            if(!stricmp(*argv, "-append"))
                  argv[2] = urlDecode(argv[2]);

            if(!stricmp(*argv, "-copy"))
            {
                  argv[2] = urlDecode(argv[2]);
                  remove(argv[2]);
                  *argv = "-append";
            }

            if(!stricmp(*argv, "-append"))
            {
                  argv[1] = urlDecode(argv[1]);
                  execvp(keypaths.getLast("sox"), argv);
                  exit(-1);
            }

#ifdef      USER_HOSTING
            if(user)
                  prefix = keyusers.getLast(user);
            else
                  prefix = NULL;

            if(!prefix)
                  prefix = getenv("SERVER_LIBEXEC");
#else
            prefix = getenv("SERVER_LIBEXEC");
#endif

            sprintf(buffer, "%s/%s", prefix, *argv);
            getInterp(buffer, argv);
            slog(Slog::levelDebug) << "tgi: exec " << buffer << endl;
            execvp(buffer, argv);
            slog(Slog::levelError) << "tgi: exec failed; " << buffer << endl;
            exit(-1);
      }
      exit(SIGINT);
}

/*
static void tgisignal(void)
{
      int count = keythreads.getGateways();
      int pid;

      if(!tgipids)
            return;

      for(pid = 0; pid < count; ++pid)
            kill(tgipids[pid], SIGUSR1);
}
*/

#endif
            
static RETSIGTYPE final(int sig)
{
      int count = keythreads.getGateways();
      int i;
      const char *cp;

      if(debug)
            if(debug->debugFinal(sig))
                  return;

      if(getpid() != mainpid)
      {
            kill(mainpid, sig);
#ifdef      COMMON_THREAD_SLEEP
            Thread::sleep(~0);
#else
            ccxx_sleep(~0);
#endif
      }
      signal(SIGINT, SIG_IGN);
      signal(SIGABRT, SIG_IGN);
      signal(SIGTERM, SIG_IGN);
      signal(SIGQUIT, SIG_IGN);

#ifdef      NODE_SERVICES
      network.stop();
#endif
      scheduler.stop();
      stopServers();

      if(resolver)
            delete resolver;

      Trunk::sync();    

      if(driver)
            driver->stop();

      if(sig)
            slog(Slog::levelWarning) << "exiting: reason=" << sig << endl;
      else
            slog(Slog::levelNotice) << "normal shutdown" << endl;

      if(tgipids)
      {
            for(i = 0; i < count; ++i)
            {
                  kill(tgipids[i], SIGINT);
                  waitpid(tgipids[i], NULL, 0);
            }
            tgipids = NULL;
      }

      if(restart_server)
            execvp(*restart_argv, restart_argv);

      cp = keypaths.getLast("pidfile");
      if(cp)
            remove(cp);

      cp = keypaths.getLast("ctrlfile");
      if(cp)
            remove(cp);

        cp = keypaths.getLast("pktfile");
        if(cp)
                remove(cp);

      cp = keypaths.getLast("nodefile");
      if(cp)
            remove(cp);

      purge(keypaths.getLast("tmpfs"));
      purge(keypaths.getLast("tmp"));
      exit(sig);
}

static void initial(int argc, char **argv, int cfd)
{
      static struct option long_options[] = {
            {"background", 0, 0, 'D'},
            {"foreground", 0, 0, 'F'},
            {"daemon", 0, 0, 'D'},
            {"help", 0, 0, 'h'},
            {"priority", 1, 0, 'p'},
            {"port", 1, 0, 'P'},
            {"driver", 1, 0, 'd'},
            {"node", 1, 0, 'n'},
            {"test", 0, 0, 't'},
            {"trace", 0, 0, 'T'},
            {"thread", 0, 0, 'A'},
            {"version", 0, 0, 'V'},
            {"voice", 1, 0, 'v'},
            {"language", 1, 0, 'l'},
            {"gui", 0, 0, 'G'},
            {"display", 1, 0, 'X'},
            {"startup", 1, 0, 'S'},
            {"debug", 0, 0, 'x'},
            {"demo", 0, 0, 'c'},
            {"init", 0, 0, 'I'},
            {"alt", 1, 0, 'a'},
            {"groups", 0, 0, 'g'},
            {"tts", 1, 0, 'Y'},
            {"prefix", 1, 0, 'z'},
            {0, 0, 0, 0}};

      static bool daemon = false;
      static bool usage = false;
      static bool gui = false;
      static bool pcount = 0;
      static bool mcount = 0;
      static unsigned plen = 0;

      TrunkGroup *grp;
      ScriptSymbol *globals = Trunk::getGlobals();
      struct utsname uts;
      TrunkGroup *policy;
      restart_argv = argv;
      char *envDriver = getenv("BAYONNE_DRIVER");
      Mixer *mixer;
      fstream mix;
      char prefix[256], libpath[256];
      const char *cp;
      char *pp, *tok;
      sigset_t sigs;
      int opt, opt_index;
      unsigned ports, id;
      bool test = false;
      bool schedule = true;
      unsigned count;
      ofstream php;
      const char *embed, *name;
      bool gdump = false;
      bool pref = false;
      const char *alt = NULL;
      char *sp = NULL;

      cmds = argv;
      mainpid = getpid();
      sigemptyset(&sigs);
      sigaddset(&sigs, SIGTERM);
      sigaddset(&sigs, SIGQUIT);
      sigaddset(&sigs, SIGINT);
      pthread_sigmask(SIG_BLOCK, &sigs, NULL);
      slog.level(Slog::levelNotice);

      strncpy(prefix, argv[0], sizeof(prefix));
      pp = strrchr(prefix, '/');
        if(pp)
        {
            *pp = 0;
                chdir(prefix);
        }

      uname(&uts);

      getcwd(prefix, sizeof(prefix) - 1);
      pp = strrchr(prefix, '/');

      snprintf(libpath, sizeof(libpath), "%s/%s", 
            keypaths.getLast("libpath"), VERPATH);
      keypaths.setValue("modlibpath", libpath);

      if(envDriver)
            plugins.setValue("driver", envDriver);

      while(EOF != (opt = getopt_long(argc, argv, "z:123cthVxP:GTDFX:d:p:d:n:a:AS:s:v:l:gY:I", long_options, &opt_index)))
            switch(opt)
            {
            case 'z':
                  sp = optarg;
                  break;
            case '1':
                  numbering = 1;
                  break;
            case '2':
                  numbering = 2;
                  break;
            case '3':
                  numbering = 3;
                  break;
            case 'x':
                  slog.level(Slog::levelDebug);
                  break;
            case 'I':
                  pref = true;
                  break;
            case 'g':
                  gdump = true;
                  break;
            case 'v':
                  setenv("BAYONNE_VOICE", optarg, 1);
                  break;
            case 'Y':
                  plugins.setValue("tts", optarg);
                  break;
            case 'l':
                  setenv("BAYONNE_LANGUAGE", optarg, 1);
                  break;
            case 'X':
                  setenv("DISPLAY", optarg, 1);
            case 'G':
                  gui = true;
                  plugins.setValue("debug", "gui");
                  break;            
            case 'P':
                  plugins.setValue("debug", "tcpmon");
                  setenv("TCPMON", optarg, 1);
                  break;      
            case 'S':
                  inits[icount++] = optarg;
                  break;
            case 'V':
                  cout << VERPATH << endl;
                  exit(0);
            case 'A':
                  keythreads.setValue("audit", "1");
                  slog.level(Slog::levelDebug);
                  break;
            case 'T':
                  daemon = false;
                  slog.level(Slog::levelDebug);
                  plugins.setValue("debug", "trace");
                  break;
            case 'a':
                  alt = optarg;
            case 't':
                  schedule = false;
                  test = true;
                  daemon = false;
                  slog.level(Slog::levelDebug);
#if defined(have_montecarlo_h) || defined(HAVE_MONTECARLO_H)
                  strcpy(pp, "/drivers/pika/" PLUGIN_SUBDIR "pika.ivr");
#elif defined(HAVE_DIALOGIC_SDK)
                  strcpy(pp, "/drivers/dialogic/" PLUGIN_SUBDIR "dialogic.ivr");
#elif defined(HAVE_VPBAPI_H)
                  strcpy(pp, "/drivers/vpb/" PLUGIN_SUBDIR "vpb.ivr");
#elif defined(HAVE_CAPI20_H)
                  strcpy(pp, "/drivers/capi20/" PLUGIN_SUBDIR "capi20.ivr");
#elif defined(HAVE_LINUX_TELEPHONY_H)
                  strcpy(pp, "/drivers/phonedev/" PLUGIN_SUBDIR "phonedev.ivr");
#else
                  strcpy(pp, "/drivers/dummy/" PLUGIN_SUBDIR "dummy.ivr");
#endif
                  if(envDriver)
                        sprintf(pp, "/drivers/%s/%s%s.ivr",
                              envDriver, PLUGIN_SUBDIR, envDriver);
                  plugins.setValue("driver", prefix);
                  strcpy(pp, "/modules/translators/" PLUGIN_SUBDIR "english.tts");
                  plugins.setValue("languages", prefix);
                  plugins.setValue("switch", "");
#ifdef      HAVE_FLITE
                  strcpy(pp, "/modules/flite/" PLUGIN_SUBDIR "flite.tts");
                  plugins.setValue("tts", prefix);
#endif

#ifdef      HAVE_POSTGRES
                  strcpy(pp, "/modules/postgres/" PLUGIN_SUBDIR "postgres.sql");
                  plugins.setValue("sql", prefix);
#endif

#ifdef      COMMON_XML_PARSING
                  strcpy(pp, "/modules/xml/" PLUGIN_SUBDIR "bayonne.xml");
                  plugins.setValue("xml", prefix);
                  strcpy(pp, "/modules/xml" PLUGIN_SUBDIR);
                  keypaths.setValue("modlibpath", prefix);
#endif
//                strcpy(pp, "/modules/perl/" PLUGIN_SUBDIR "perl.tgi");
//                plugins.setValue("tgi", prefix);
                  strcpy(pp, "/modules/protocols/"  PLUGIN_SUBDIR "info.mod");
                  plugins.setValue("modules", prefix);
                  plugins.setValue("auditing", "");
                  if(gui)
                        strcpy(pp, "/modules/gui/" PLUGIN_SUBDIR "gui.dbg");
                  else
                        strcpy(pp, "/server/" PLUGIN_SUBDIR "test.dbg");
                  plugins.setValue("debug", prefix);
                  keypaths.setValue("cache", "cache");
                  keypaths.setValue("spool", "spool");
                  strcpy(pp, "/data/script");
                  keypaths.setValue("scripts", prefix);
                  strcpy(pp, "/data");
                  keypaths.setValue("prompts", prefix);
                  if(alt)
                        cp = strrchr(alt, '/');
                  else
                        cp = "/alt";
                  if(!cp)
                        cp = alt;
                  else
                        ++cp;
                  sprintf(pp, "/data/%s", cp);
                  keypaths.setValue("libexec", prefix);
                  keypaths.setValue("altprompts", prefix);
                  keypaths.setValue("altscripts", prefix);
                  keypaths.setValue("datafiles", "../var");
                  break;
            case 'n':
                  keyserver.setValue("node", optarg);
                  break;
            case 'c':
                  optarg = "dummy";
                  daemon = false;
            case 'd':
                  if(*optarg != '/')
                        envDriver = optarg;

                  if(test && *optarg != '/')
                  {
                        sprintf(pp, "/drivers/%s/%s%s.ivr",
                              optarg, PLUGIN_SUBDIR, optarg);
                        optarg = prefix;
                  } 
                  plugins.setValue("driver", optarg);
                  break;
            case 'p':
                  keythreads.setValue("priority", optarg);
                  break;
            case 'D':
                  daemon = true;
                  break;
            case 'F':
                  daemon = false;
                  break;
            default:
            case 'h':
                  usage = true;
            }

      if(optind < argc)
      {
            if(test && !gui)
            {
                  strcpy(pp, "/modules/auditing/trace.dbg");
                  plugins.setValue("debug", prefix);
            }
            keyserver.setValue("default", argv[optind++]);
            schedule = false;
      }

      if(usage || optind < argc)
      {
            clog << "use: bayonne [-options] [defscript]" << endl;
            exit(-1);
      }

      endKeydata();
      if(sp)
            keypaths.setPrefix(sp);

      keyserver.setGid();

      if(!getuid())
      {
            umask(003);
            if(!isDir(keypaths.getRunfiles()))
                  mkdir(keypaths.getRunfiles(), 0770);
      }
      mkdir(keypaths.getDatafiles(), 0750);
      chdir(keypaths.getDatafiles());
      purge("temp");
      purge(keypaths.getSpool());
      purge(keypaths.getCache());
      purge(keypaths.getLast("tmpfs"));
      purge(keypaths.getLast("tmp"));

      mkdir(keypaths.getLast("tmpfs"), 0770);
      mkdir(keypaths.getLast("tmp"), 0770);
      mkdir("temp", 0770);
      mkdir(keypaths.getCache(), 0770);
      mkdir("users", 0770);
      symlink(keypaths.getCache(), "cache");
      mkdir(keypaths.getSpool(), 0770);
      symlink(keypaths.getSpool(), "spool");

      if(daemon)
      {
#ifndef     COMMON_PROCESS_ATTACH
            ::close(0);
            ::close(1);
            ::close(2);
#endif
            Process::detach();
#ifndef     COMMON_PROCESS_ATTACH
            ::open("/dev/null", O_RDWR);
            ::open("/dev/null", O_RDWR);
            ::open("/dev/null", O_RDWR);
#endif
            mainpid = getpid();
            slog.open("bayonne", Slog::classDaemon);
            slog(Slog::levelNotice) << "daemon mode started" << endl;
      }
      else if(getppid() == 1)
      {
            close(0);
            close(1);
            close(2);
            open("/dev/null", O_RDWR);
            open("/dev/null", O_RDWR);
            open("/dev/null", O_RDWR);
            slog.open("bayonne", Slog::classDaemon);
            slog(Slog::levelNotice) << "daemon init started" << endl;
      }

      if(canModify(keypaths.getRunfiles()))
            snprintf(prefix, sizeof(prefix), "%s/bayonne.pid",
                  keypaths.getRunfiles());
      else
            snprintf(prefix, sizeof(prefix), "%s/.bayonne.pid",
                  Process::getEnv("HOME"));

      keypaths.setValue("pidfile", prefix);

      switch(pidfile(prefix))
      {
      case -1:
            slog(Slog::levelWarning) << "server: cannot create pidfile " << prefix << endl;
      case 0:
            break;
      default:
            slog(Slog::levelCritical) << "server: another instance running; cannot continue" << endl;
            final(-1);
      }

      setenv("SERVER_PLATFORM", plugins.getDriverName(), 1);
      setenv("SERVER_LIBEXEC", keypaths.getLibexec(), 1);
      setenv("SERVER_SOFTWARE", "bayonne", 1);
      setenv("SERVER_PROTOCOL", "2.2", 1);
      setenv("SERVER_VERSION", VERPATH, 1);
      setenv("SERVER_TOKEN", keyserver.getToken(), 1);

      strcpy(prefix, keyserver.getPrefix());
      strcat(prefix, "/bayonne");
      if(isDir(prefix))
      {
            strcat(prefix, ":");
            strcat(prefix, keypaths.getTgipath());
            setenv("PATH", prefix, 1);
      }
      else
            setenv("PATH", keypaths.getTgipath(), 1);

      slog(Slog::levelInfo) << "SERVER VERSION " << VERPATH << "; ";
      slog() << uts.machine << " ";
      slog() << uts.sysname << " " << uts.release << endl;
      slog(Slog::levelInfo) << "TGI VERSION 2.2";
      slog() << "; driver=" << plugins.getDriverName();
      slog() << "; prefix=" << prefix;
      slog() << "; etc=" << keypaths.getLast("etc") << endl;

      slog(Slog::levelDebug) << "Loading TGI plugins..." << endl;
      try
      {
            plugins.loadTGI();
      }
      catch(DSO *dso)
      {
            slog(Slog::levelCritical) << dso->getError() << endl;
            final(-1);
      }
#ifdef      HAVE_TGI
      tgi(cfd);
      sleep(1);
#endif
      rt();

#ifdef      SCRIPT_IF_OVERRIDE
      addConditional("voice", &Trunk::hasVoice);
      addConditional("alt", &Trunk::hasAltVoice);
      addConditional("altvoice", &Trunk::hasAltVoice);
      addConditional("sys", &Trunk::hasSysVoice);
      addConditional("sysvoice", &Trunk::hasSysVoice);
      addConditional("app", &Trunk::hasAppVoice);
      addConditional("appvoice", &Trunk::hasAppVoice);
      addConditional("group", &Trunk::hasGroup);
      addConditional("policy", &Trunk::hasGroup);
      addConditional("plugin", &Trunk::hasPlugin);
      addConditional("node", &Trunk::isNode);
      addConditional("service", &Trunk::isService);
      addConditional("dtmf", &Trunk::ifDTMF);
      addConditional("feature", &Trunk::ifFeature);
      addConditional("ext", &Trunk::isExtension);
      addConditional("extension", &Trunk::isExtension);
      addConditional("station", &Trunk::isStation);
      addConditional("virtual", &Trunk::isVirtual);
      addConditional("user", &Trunk::isActiveUser);
      addConditional("dnd", &Trunk::isDnd);
      addConditional("hunt", &Trunk::isHunt);
#endif

      try
      {
            slog(Slog::levelDebug) << "Loading DSO plugin images..." << endl;
            plugins.loadManagers();
            if(!test)
                  plugins.loadExtensions();
            plugins.loadDebug();
            plugins.loadMonitor();
            plugins.loadDriver();
            plugins.loadSwitch();
            plugins.loadTTS();
            plugins.loadSQL();
            plugins.loadModules();
            plugins.loadPreload();
            plugins.loadTranslators();
            plugins.loadAuditing();
#ifdef      XML_SCRIPTS
            plugins.loadXML();
#endif
            if(!(driver->getCaps() & Driver::capSwitch))
                  driver->getImage();
            else if(numbering)
                  driver->setExtNumbering(numbering);
      }
      catch(Driver *drv)
      {
            slog(Slog::levelCritical) << "multiple drivers loaded or driver failure" << endl;
            final(-1);
      }
      catch(Socket *sock)
      {
            slog(Slog::levelCritical) << "socket interface binding failure" << endl;
            final(-1);
      }
      catch(Dir *dir)
      {
            slog(Slog::levelCritical) << keypaths.getScriptFiles(); 
            slog() << ": no script directory" << endl;
            final(-1);
      }
      catch(DSO *dso)
      {
            slog(Slog::levelCritical) << dso->getError() << endl;
            final(-1);
      }
      catch(InetAddress *in)
      {
            slog(Slog::levelCritical) << "protocol resolver failed" << endl;
            final(-1);
      }
      catch(Thread *)
      {
            slog(Slog::levelCritical) << "service failure" << endl;
            final(-1);
      }
      catch(...)
      {
            slog(Slog::levelCritical) << "unknown failure" << endl;
            final(-1);
      }

      if(!driver)
      {
            slog(Slog::levelCritical) << "no driver loaded" << endl;
            final(-1);
      }

      keyserver.loadGroups(test);

      if(gdump)
      {
            for(id = 0; id < driver->getTrunkCount(); ++id)
            {
                  grp = driver->getTrunkGroup(id);
                  if(!grp)
                        continue;
                  cout << "port " << id << " group=" << grp->getLast("name") << endl;
            }
            for(id = 0; id < MAX_SPANS; ++id)
            {
                  grp = driver->getSpanGroup(id);
                  if(!grp)
                        continue;
                  cout << "span " << id << " group=" << grp->getLast("name") << endl;
            }
            for(id = 0; id < MAX_CARDS; ++id)
            {
                  grp = driver->getCardGroup(id);
                  if(!grp)
                        continue;
                  cout << "card " << id << " group=" << grp->getLast("name") << endl;
            }
            exit(0);
      }
      
      sync();
      ports = driver->start();
      if(ports)
            slog(Slog::levelNotice) << "driver started " << ports << " port(s)" << endl;
      else
      {
            slog(Slog::levelCritical) << "no trunk ports activated" << endl;
            final(-1);
      }
      policy = getGroup(NULL);
      embed = keypaths.getLast("embed");
      if(!embed)
            embed = "php";
      if(!*embed)
            embed = NULL;

        cp = keypaths.getLast("php");
        if(cp && !test)
      {
                  php.open(cp, ios::out);
            chmod(cp, 0660);
      }
        if(php.is_open())
        {
            if(embed)
                        php << "<?" << embed << endl;

            php << "# This file is automatically generated, do not edit" << endl;

                  php << "$BAYONNE_DRIVER=\"" 
                << plugins.getDriverName() <<"\";" << endl;
            php << "$BAYONNE_HOME=\""
                << getenv("HOME") << "\";" << endl;
            php << "$BAYONNE_LIBPATH=\""
                << keypaths.getLibpath() << "\";" << endl;
            php << "$BAYONNE_LIBEXEC=\"" 
                << getenv("SERVER_LIBEXEC") << "\";" << endl;
            php << "$BAYONNE_NODE=\""
                << keyserver.getNode() << "\";" << endl;
            php << "$BAYONNE_TOKEN=\""
                << keyserver.getToken() << "\";" << endl;
            php << "$BAYONNE_DATAFILES=\""
                << keypaths.getDatafiles() << "\";" << endl;
            php << "$BAYONNE_RUNFILES=\""
                << keypaths.getRunfiles() << "\";" << endl;
            php << "$BAYONNE_PROMPTS=\""
                << keypaths.getPromptFiles() << "\";" << endl;
            php << "$BAYONNE_SCRIPTS=\""
                << keypaths.getScriptFiles() << "\";" << endl;
            strcpy(prefix, policy->getLast("groups"));
            pp = strtok_r(prefix, " \t\n,;", &tok);
            while(pp)
            {
                  php << "$BAYONNE_POLICY[" << pcount << "]=\"";
                  php << pp << "\";" << endl;
                  ++pcount;
                  pp = strtok_r(NULL, " \t\n,;", &tok);
            }
            php << "$BAYONNE_POLICIES="
                << pcount << ";" << endl;
            php << "$BAYONNE_PORTS="
                << ports << ";" << endl;
                  php << "$BAYONNE_VERSION=\"" VERPATH "\";" << endl;
            php << "$BAYONNE_MIXERS="
                << driver->getMixers() << ";" << endl;
            while(mcount < driver->getMixers())
            {
                  mixer = driver->getMixer(mcount);
                  php << "$MIXER_GROUPS[" << mcount << "]=";
                        php << mixer->getGroups() << ";" << endl;
                  php << "$MIXER_MEMBERS[" << mcount << "]=";
                  php << mixer->getMembers() << ";" << endl;
                  ++mcount;
            }

            if(embed)
                        php << "?>" << endl;
                  php.close();
            chown(cp, keyserver.getUid(), keyserver.getGid());
      }

      if(keythreads.getResolver())
            resolver = new Resolver();

      startServers();

#ifdef      NODE_SERVICES
      network.start();
#endif

      if(canModify(keypaths.getRunfiles()))
      {
            strcpy(prefix, keypaths.getRunfiles());
            strcat(prefix, "/bayonne.mixer");
      }
      else
            sprintf(prefix, "%s/.bayonne.mixer", getenv("HOME"));

      remove(prefix);
      mix.open(prefix, ios::out);
      if(mix.is_open())
      {
            chmod(prefix, 0640);
            chown(prefix, keyserver.getUid(), keyserver.getGid());
            count = 0;
            while(count < driver->getMixers())
            {
                  mixer = driver->getMixer(count);
                  if(!mixer)
                        break;

                  mix << count++ << " " << mixer->getGroups() << " " << mixer->getMembers() << endl;
            }
            mix.close();
      }
      else
            slog(Slog::levelWarning) << "startup: mixer open failed" << endl;

      signal(SIGTERM, final);
      signal(SIGQUIT, final);
      signal(SIGINT, final);
      signal(SIGABRT, final);
      pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
      slog("bayonne", Slog::classDaemon);
      if(keyserver.getLast("config"))
            slog(Slog::levelNotice) << "normal startup; " << keyserver.getLast("config") << endl;
      else
            slog(Slog::levelNotice) << "normal startup" << endl;

      if(policy->getLast("groups"))
            strcpy(prefix, policy->getLast("groups"));
      else
            strcpy(prefix, "*");

      char pbuf[256];
      pbuf[0] = 0;
      pp = strtok_r(prefix, " ,;\t\n", &tok);
      while(pp && plen < sizeof(pbuf))
      {
            if(plen)
                  pbuf[plen++] = ',';
            strncpy(pbuf + plen, pp, sizeof(pbuf) - plen);
            pbuf[sizeof(pbuf) - 1] = 0;
            plen = strlen(pbuf);
            pp = strtok_r(NULL, " ,;\t\n", &tok);
      }

      globals->setConst(SYM_POLICIES, pbuf);

      sprintf(prefix, "%d", driver->getTrunkCount());
      globals->setConst(SYM_PORTS, prefix);
      globals->setConst(SYM_USER, keyserver.getLast("user"));
      globals->setConst(SYM_VERSION, VERPATH);
      globals->setConst(SYM_SERVER, "bayonne");
      globals->setConst(SYM_NODE, keyserver.getNode());
      globals->setConst(SYM_SCRIPTS, keypaths.getScriptFiles());
      globals->setConst(SYM_PROMPTS, keypaths.getPromptFiles());
      globals->setConst(SYM_RELEASE, "1");

      Trunk::initPassword();
      Trunk::initLines();

      Dir users("users");

      while(NULL != (name = users.getName()))
      {
            if(*name == '.')
                  continue;

            Trunk::load(name);
      }

      if(isDir("lines"))
      {
            Dir lines("lines");
      
            while(NULL != (name = lines.getName()))
            {
                  if(*name == '.')
                        continue;

                  Trunk::loadPref(name, "line", "lines");
            }
      }

      if(isDir("hunting"))
      {
            Dir hunt("hunting");

            while(NULL != (name = hunt.getName()))
            {
                  if(*name == '.')
                        continue;

                  Trunk::loadPref(name, "hunt", "hunting");
            }
      }

      Trunk::sync(pref);
      new KeyTones();
      running = true;
}

void loader(const char *path, const char *ext)
{
      char buffer[256];
      Dir dir(path);
      const char *name;
      const char *tail;

      while(NULL != (name = dir.getName()))
      {
            tail = strrchr(name, '.');
            if(!tail)
                  continue;

            if(stricmp(tail, ext))
                  continue;

            snprintf(buffer, sizeof(buffer), "%s/%s", path, name);
            new DSO(path);
      }
}

void check(void)
{
      Dir dir(keypaths.getCache());
      const char *entry;
      char path[128];
      time_t now;
      struct stat ino;

      slog(Slog::levelDebug) << "server: checking cache..." << endl;

      time(&now);

      while(NULL != (entry = dir.getName()))
      {
            if(*entry == '.')
                  continue;

            snprintf(path, sizeof(path), "cache/%s", entry);
            if(stat(path, &ino))
                  continue;

            if(now - ino.st_mtime >= 3600)
            {
                  cachelock.writeLock();
                  remove(path);
                  cachelock.unlock();
            }
      }
}

static int fd = -1;
static int so = -1;

void control(const char *cmd)
{
      if(fd < 0)
            return;

      write(fd, cmd, strlen(cmd));
}

#define     PROTOCOL_VERSION 2

static void packetio(void)
{
        bool rts;
        char packet[512];
        char *p = packet + 1;
        char reply[2];
        struct sockaddr_un caddr;
        socklen_t clen = sizeof(caddr);
        int len = ::recvfrom(so, packet, sizeof(packet), 0, (struct sockaddr *)&caddr, &clen);

        if(len < 0)
        {
                slog(Slog::levelWarning) << "packet: invalid read" << endl;
                return;
        }

        while(isspace(*p))
                ++p;
        rts = fifo.command(p);
        if(!packet[0])
                return;

        reply[0] = packet[0];
        if(rts)
                reply[1] = '1';
        else
                reply[0] = '0';

        ::sendto(so, reply, 2, 0, (struct sockaddr *)&caddr, len);
}

extern "C" int main(int argc, char **argv)
{
      static char buffer[PIPE_BUF / 2];
      static char packet[256];
      struct sockaddr_un server_addr;
      char *p;
      const char *etc;
      int bpos = 0, len;
#ifdef      HAVE_POLL
      struct pollfd pfd[2];
#else
      struct timeval tv;
      int maxfd;
      fd_set sfd;
#endif
      TimerPort timer;
      timeout_t step;
      unsigned ic = 0;
      unsigned seccount = 0, mincount = 0, minhour = 0;
      ifstream init;
#ifndef NODE_SERVICES
        statnode_t node;
        static char nodepath[256];
        int nodes;
        TrunkGroup *grp;
#endif

        if(canModify(keypaths.getRunfiles()))
        {
                snprintf(buffer, sizeof(buffer), "%s/bayonne.ctrl",
                        keypaths.getRunfiles());
                snprintf(packet, sizeof(packet), "%s/bayonne.pkt",
                        keypaths.getRunfiles());

#ifndef NODE_SERVICES
                snprintf(nodepath, sizeof(nodepath), "%s/bayonne.nodes",
                        keypaths.getRunfiles());
#endif
        }
        else
        {
                snprintf(buffer, sizeof(buffer), "%s/.bayonne.ctrl",
                        getenv("HOME"));
                snprintf(packet, sizeof(packet), "%s/.bayonne.pkt",
                        getenv("HOME"));

#ifndef NODE_SERVICES
                snprintf(nodepath, sizeof(nodepath), "%s/.bayonne.nodes",
                        keypaths.getRunfiles());
#endif
        }

      keypaths.setValue("ctrlfile", buffer);
      keypaths.setValue("pktfile", packet);

      if(!getuid())
            umask(003);
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sun_family = AF_UNIX;
        strncpy(server_addr.sun_path, packet, sizeof(server_addr.sun_path));

#ifdef  __SUN_LEN
        len = sizeof(server_addr.sun_len) + strlen(server_addr.sun_path)
                        + sizeof(addr.sun_family) + 1;

        addr.sun_len = len;
#else
        len = strlen(server_addr.sun_path) + sizeof(server_addr.sun_family) + 1;
#endif
        remove(packet);
        so = socket(AF_UNIX, SOCK_DGRAM, 0);
        if(so > -1)
        {
                if(bind(so, (struct sockaddr *)&server_addr, len))
                {
                        slog(Slog::levelError) << "packet interface failed;" << endl;
                        so = -1;
                        remove(packet);
                }
        }

      remove(buffer);
      mkfifo(buffer, 0660);
      fd = open(buffer, O_RDWR);

      initial(argc, argv, fd);

      if(debug->debugTest())
            final(0);

      slog(Slog::levelDebug) << "fifo: path=" << buffer << endl;
      if(fd < 0)
      {
            slog(Slog::levelWarning) << "fifo: open failed" << endl;
            sleep((unsigned)(-1));
      }

      chown(buffer, keyserver.getUid(), keyserver.getGid());
#ifndef NODE_SERVICES
        nodes = creat(nodepath, 0640);
        chown(nodepath, keyserver.getUid(), keyserver.getGid());
        grp = getGroup();
#endif

      scheduler.initial();

      while(ic < icount)
            fifo.command(inits[ic++]);
            
        etc = keypaths.getLast("etc");
        etc = strchr(etc + 1, '/');
        if(etc)
                ++etc;
        else
                etc = "";

        strcpy(buffer, keypaths.getLast("etc"));
        if(*etc)
                strcat(buffer, "startup.conf");
        else
                strcat(buffer, "bayonne.init");
        if(!canAccess(buffer))
        {
                strcpy(buffer, keypaths.getLast("etc"));
                strcat(buffer, "bayonne.init");
        }

      init.open(buffer);
      if(init.is_open())
      {
            while(!init.eof())
            {
                  init.getline(buffer, sizeof(buffer));
                  p = buffer + strlen(buffer) - 1;
                  while(isspace(*p) && p >= buffer)
                  {
                        *(p--) = 0;
                  }
                  p = buffer;
                  while(isspace(*p))
                        ++p;

                  if(!isalpha(*p))
                        continue;

                  fifo.command(p);
            }
            init.close();
      }

      timer.setTimer(1000);
      for(;;)
      {
            Thread::yield();
#ifdef      HAVE_POLL
            pfd[0].fd = fd;
            pfd[0].events = POLLIN | POLLRDNORM;
            pfd[0].revents = 0;
            if(so > -1)
            {
                      pfd[1].fd = so;
                    pfd[1].events = POLLIN | POLLRDNORM;
                  pfd[1].revents = 0;
            }
#else
            maxfd = fd;
            FD_ZERO(&sfd);
            FD_SET(fd, &sfd);
            if(so > -1)
                  FD_SET(so, &sfd);
#endif
            step = timer.getTimer();
            if(!step)
            {
                  if(driver)
                        driver->secTick();
                  timer.setTimer(1000);
#ifndef NODE_SERVICES
                        if(!(seccount % 10) && nodes > -1)
                        {
                                lseek(nodes, 0l, SEEK_SET);
                                memset(&node, 0, sizeof(node));
                                node.version = PROTOCOL_VERSION;
                                node.buddies = 0;
                                strncpy(node.name, keyserver.getNode(),
                                        sizeof(node.name));
                                node.ports = driver->getTrunkCount();
                                node.calls = grp->getStat(STAT_SYS_ACTIVITY);
                                driver->getStatus(node.stat);
                        time(&node.update);
                                ::write(nodes, &node, sizeof(node));
                        }
#endif

                  if(++seccount >= 60)
                  {
                        scheduler.sync();
                        rpclog.expire();
                        seccount = 0;
                        if(++mincount >= 10)
                        {
                              check();
                              mincount = 0;
                        }
                        if(++minhour >= 60)
                        {
                              Trunk::sync();
                              minhour = 0;
                        }
                  }     
                  continue;
            }

#ifdef      HAVE_POLL
            len = 1;
            if(so > -1)
                  ++len;
            poll(pfd, len, step);
            if(so > -1 && pfd[1].revents & POLLIN)
                  packetio();
            if(pfd[0].revents & POLLIN)
                {
#else
            tv.tv_sec = step / 1000;
            tv.tv_usec = (step % 1000) * 1000;
            select(maxfd + 1, &sfd, NULL, &sfd, &tv);

            if(so > -1 && FD_ISSET(so, &sfd))
                  packetio();

            if(FD_ISSET(fd, &sfd))
            {
#endif
                        bpos = 0;
                        for(;;)
                        {
                                read(fd, &buffer[bpos], 1);
                                if(buffer[bpos] == '\n')
                                        break;
                                if(buffer[bpos] != '\r' && bpos < sizeof(buffer))
                                        ++bpos;
                        }
                        buffer[bpos] = 0;
                  p = buffer + strlen(buffer) - 1;
                  while(p > buffer)
                  {
                        if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
                              *(p--) = 0;
                        else
                              break;
                  }
                  p = buffer;
                  while(*p == ' ' || *p == '\t')
                        ++p;
                  if(!*p)
                        continue;
                  fifo.command(p);
            }
      }
      close(fd);
      final(0);
}

#ifdef      CCXX_NAMESPACES
};
#endif

Generated by  Doxygen 1.6.0   Back to index