/*
  SisPM.c

  Controls the GEMBIRD Silver Shield PM USB outlet device

  (C) 2004-2011, Mondrian Nuessle, Computer Architecture Group, University of Mannheim, Germany
  (C) 2005, Andreas Neuper, Germany
  (C) 2010, Olivier Matheret, France, for the plannification part

  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


  nuessle@uni-mannheim.de
  aneuper@web.de

*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define __USE_XOPEN
#include <time.h>
#include <signal.h>
#include <usb.h>
#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <fcntl.h>

#include "sispm_ctl.h"
#include "socket.h"
#include "main.h"
#include "config.h"

#ifndef MSG_NOSIGNAL
#include <signal.h>
#endif

#define	BSIZE			 	 65536

char* homedir=0;
extern int errno;
int debug=0;
int verbose=1;


#ifndef WEBLESS
void daemonize()
{
  /* Our process ID and Session ID */
  pid_t pid, sid;

  /* Fork off the parent process */
  pid = fork();
  if (pid < 0)
    exit(EXIT_FAILURE);

  /* If we got a good PID, then
     we can exit the parent process. */
  if (pid > 0)
    exit(EXIT_SUCCESS);

  /* Change the file mode mask */
  umask(0);

  /* Create a new SID for the child process */
  sid = setsid();
  if (sid < 0)
    /* Log the failure */
    exit(EXIT_FAILURE);

  /* Change the current working directory */
  if ((chdir("/var/tmp")) < 0)
    /* Log the failure */
    exit(EXIT_FAILURE);

  /* Close out the standard file descriptors */
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
close(STDERR_FILENO);
}
#endif

#ifndef WEBLESS
void process(int out ,char *request, struct usb_device *dev, int devnum)
{
  char xbuffer[BSIZE+2];
  char filename[1024];
  char *ptr = NULL;
  FILE *in = NULL;
  long length = 0;
  long lastpos = 0;
  long remlen = 0;
  usb_dev_handle *udev;
  unsigned int id; //product id of current device
  char *retvalue = NULL;

  if (debug)
    fprintf(stderr,"\nRequested is (%s)\n",request);

  // extract the filename
  if (strchr(request,'\n') != NULL) {
    memset(filename, 0, 1023);
    strncpy(filename, strchr(request,' ')+1, strchr(request,'\n')-request);
    ptr = strchr(filename, ' ');
    if (ptr != NULL)
      *ptr = 0;
  }

  // avoid to read other directories, %-codes are not evalutated
  ptr = strrchr(filename,'/');
  if (ptr != NULL)
    ptr++;
  else
    ptr = filename;

  if (strlen(ptr) == 0)
    ptr="index.html";

  if (debug) {
    fprintf(stderr,"\nrequested filename(%s)\n", filename);
    fprintf(stderr,"resulting filename(%s)\n", ptr);
    fprintf(stderr,"change directory to (%s)\n", homedir);
  }

  if (chdir(homedir) != 0) {
    sprintf(xbuffer, "HTTP/1.1 4%02d Bad request\nServer: SisPM\nContent-Type: text/html\n\n"
            "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>4%02d Bad Defaults</title>\n"
            "</head><body>\n<h1>Bad Defaults</h1>\n<p>%s</p></body></html>\n\n",
            errno,errno,strerror(errno));
    send(out,xbuffer,strlen(xbuffer),0);
    return;
  }

  if (debug)
    fprintf(stderr,"\nopen file(%s)\n",ptr);

  in = fopen(ptr,"r");

  if (in == NULL) {
    sprintf(xbuffer, "HTTP/1.1 4%02d Bad request\nServer: SisPM\nContent-Type: text/html\n\n"
            "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>4%02d Bad Request</title>\n"
            "</head><body>\n<h1>Bad Request</h1>\n<p>%s</p></body></html>\n\n",
            errno,errno,strerror(errno));
    send(out,xbuffer,strlen(xbuffer),0);
    return;
  }

  /* get device-handle/-id */
  udev = get_handle(dev);
  id = get_id(dev);
  if (udev == NULL)
    fprintf(stderr, "No access to Gembird #%d USB device %s\n", devnum, dev->filename );
  else
    if (verbose)
      printf("Accessing Gembird #%d USB device %s\n", devnum, dev->filename );

  lastpos = ftell(in);
  retvalue = fgets(xbuffer, BSIZE-1, in);
  assert((retvalue != NULL) || feof(in));
  remlen = length = ftell(in) - lastpos;
  lastpos = ftell(in);

  while (!feof(in)) {
    char *mrk = xbuffer;
    char *ptr = xbuffer;
    /* search for:
     *	$$exec(0)?.1.:.2.$$	to execute command(#)
     *	$$stat(2)?.1.:.2.$$	to evaluate status(#)
     */
    for (mrk = ptr = xbuffer; (ptr-xbuffer) < length; ptr++) {
      if (*ptr=='$' && ptr[1]=='$' && (ptr[2]=='e' || ptr[2]=='s'|| ptr[2]=='o')) {
        /*
         * $$exec(1)?select:forget$$
         *   ^cmd    ^pos	 ^trm
         * ^ptr	 ^num	   ^neg
         */
        char *cmd=&ptr[2];
        char *num=strchr(cmd,'(');
        char *pos=strchr(num?num:cmd,'?');
        char *neg=strchr(pos?pos:cmd,':');
        char *trm=strchr(neg?neg:cmd,'$');
        if (debug) {
          fprintf(stderr,"%p\n%p\n%p\n%p\n%p\n%p\n",cmd,num,pos,neg,trm,ptr);
          fprintf(stderr,"%s%s%s%s%s%s",cmd,num,pos,neg,trm,ptr);
        }

	if (trm != NULL) {
          assert(("Command-Format: $$exec(#)?select:forget$$	ERROR at #",num!=NULL));
          assert(("Command-Format: $$exec(#)?select:forget$$	ERROR at ?",pos!=NULL));
          assert(("Command-Format: $$exec(#)?select:forget$$	ERROR at :",neg!=NULL));
          // if( (ptr=strchr(neg,'$')) == NULL ) ptr=cmd; else *ptr=0;
          // *pos=*neg=0;
          num++; pos++; neg++;
          send(out,mrk,ptr-mrk,0);
          remlen = remlen - (ptr - mrk);
          mrk=ptr;

          if (strncasecmp(cmd,"on(",3)==0) {
            if (debug)
              fprintf(stderr,"\nON(%s)\n",num);
            if (sispm_switch_on(udev,id,atoi(num)) !=0)
              send(out,pos,neg-pos-1,0);
            else
              send(out,neg,trm-neg,0);
          }
          else
            if (strncasecmp(cmd,"off(",4)==0) {
              assert(("Command-Format: $$exec(#)?select:forget$$	ERROR at final $",(trm[1]=='$')));
              if (debug)
                fprintf(stderr,"\nOFF(%s)\n",num);
              if (sispm_switch_off(udev,id,atoi(num)) !=0)
                send(out,pos,neg-pos-1,0);
              else
                send(out,neg,trm-neg,0);
            }
            else
              if (strncasecmp(cmd,"toggle(",7)==0) {
                assert(("Command-Format: $$exec(#)?select:forget$$	ERROR at final $",(trm[1]=='$')));
                if (debug)
                  fprintf(stderr,"\nTOGGLE(%s)\n",num);
                if (sispm_switch_getstatus(udev,id,atoi(num)) == 0) {
                  sispm_switch_on(udev,id,atoi(num));
                  send(out,pos,neg-pos-1,0);
                }
                else {
                  sispm_switch_off(udev,id,atoi(num));
                  send(out,neg,trm-neg,0);
                }
              }
              else
                if (strncasecmp(cmd,"status(",7)==0) {
                  assert(("Command-Format: $$exec(#)?select:forget$$	ERROR at final $",(trm[1]=='$')));
                  if (debug)
                    fprintf(stderr,"\nSTATUS(%s)\n",num);
                  if (sispm_switch_getstatus(udev,id,atoi(num)) != 0)
                    send(out,pos,neg-pos-1,0);
                  else
                    send(out,neg,trm-neg,0);
                }
                else {
                  // assert(("unknown but terminated command sequence",(trm[1]=='$')));
                  //fprintf(stderr,"\n<UNDERLINE>undefined sequence <B>%s</B></UNDERLINE>\n",cmd);
                  send(out,"$$",2,0);
                }
          remlen=remlen-(2+trm-mrk);
          mrk=ptr=&trm[2];
        }
      }
    }
    send(out, mrk, remlen, 0);
    memset(xbuffer, 0, BSIZE);
    retvalue = fgets(xbuffer, BSIZE-1, in);
    assert((retvalue != NULL) || feof(in));
    remlen = length = ftell(in) - lastpos;
    lastpos = ftell(in);
  }

  if (udev != NULL) {
    usb_close(udev);
    udev = NULL;
  }
  fclose(in);
  return;
}
#endif

void print_disclaimer(char*name)
{
  fprintf(stderr, "\nSiS PM Control for Linux 3.1\n\n"
	 "(C) 2004-2011 by Mondrian Nuessle, (C) 2005, 2006 by Andreas Neuper.\n"
	 "(C) 2010 by Olivier Matheret for the plannification part\n"
	 "This program is free software.\n"
	 "%s comes with ABSOLUTELY NO WARRANTY; for details \n"
	 "see the file INSTALL. This is free software, and you are welcome\n"
	 "to redistribute it under certain conditions; see the file INSTALL\n"
	 "for details.\n\n", name );
  return;
}

void print_usage(char* name)
{
  print_disclaimer(name);
  fprintf(stderr,"\n"
          "sispmctl -s\n"
          "sispmctl [-q] [-n] [-d 0...] [-D ...] -b <on|off>\n"
          "sispmctl [-q] [-n] [-d 0...] [-D ...] -[o|f|t|g|m] 1..4|all\n"
          "sispmctl [-q] [-n] [-d 0...] [-D ...] -[a|A] 1..4|all [--Aat '...'] [--Aafter ...] [--Ado <on|off>] ... [--Aloop ...]\n"
          "   'v'   - print version & copyright\n"
          "   'h'   - print this usage information\n"
          "   's'   - scan for supported GEMBIRD devices\n"
          "   'b'   - switch buzzer on or off\n"
          "   'o'   - switch outlet(s) on\n"
          "   'f'   - switch outlet(s) off\n"
          "   't'   - toggle outlet(s) on/off\n"
          "   'g'   - get status of outlet(s)\n"
          "   'm'   - get power supply status outlet(s) on/off\n"
          "   'd'   - apply to device 'n'\n"
          "   'D'   - apply to device with given serial number\n"
          "   'n'   - show result numerically\n"
          "   'q'   - quiet mode, no explanations - but errors\n"
          "   'a'   - get plannification for outlet\n"
          "   'A'   - set plannification for outlet\n"
          "           '-A<num>'        - select outlet\n"
          "           '--Aat \"date\"'   - sets an event time as a date '%%Y-%%m-%%d %%H:%%M'\n"
          "           '--Aafter N'     - sets an event time as N minutes after the previous one\n"
          "           '--Ado <on|off>' - sets the current event's action\n"
          "           '--Aloop N'      - loops to 1st event's action after N minutes\n\n"
#ifndef WEBLESS
          "Webinterface features:\n"
          "sispmctl [-q] [-i <ip>] [-p <#port>] [-u <path>] -l\n"
          "   'l'   - start port listener\n"
          "   'i'   - bind socket on interface with given IP (dotted decimal, i.e. 192.168.1.1)\n"
          "   'p'   - port number for listener (%d)\n"
          "   'u'   - repository for web pages (default=%s)\n\n"
	  ,listenport, homedir
#endif
          );

#ifdef WEBLESS
  fprintf(stderr,"Note: This build was compiled without web-interface features.\n\n");
#endif
}

#ifndef WEBLESS
const char *show_header(int type)
{
  char *content = NULL;
  static char buffer[1024];
  time_t now;
  int http = 100 * (type>>4);

  switch(type&0xf) {
    case 1: content="\nContent-Type: text/html"; break;
    case 2: content="\nContent-Type: text/plain"; break;
    case 3: content="\nContent-Type: image/gif"; break;
    case 4: content="\nContent-Type: image/jpg"; break;
    default: content=""; break;
  }

  now = time(NULL);
  sprintf(buffer, "HTTP/1.0 %d Answer\nServer: sispm\nConnection: close%s\nDate: %s\n\n", http, content, ctime(&now));

  return(buffer);
}



const char *answer(char*in)
{
  char *ptr = NULL, *end = NULL;
  int type = 0;

  static char out[MAXANSWER+2];
  memset(out,0,MAXANSWER);

  if (strncasecmp("GET ",in,4) == 0) {
    type = 1;
    ptr = &in[4];
  } else
    if (strncasecmp("POST ",in,5) == 0) {
      type = 1;
      ptr = &in[5];
    } else {
      type = 1;
      ptr = &in[0];
    }

  end = strchr(ptr,' ');
  assert((end-ptr<1024,"filename buffer is defined to 1024 chars only"));
  if (strncasecmp("/switch",ptr,6) == 0)
    ptr += 6;

  strcpy(out,show_header((4<<4)+1));

  strcat(out,"\n\n<HTML><HEAD><TITLE>TEST</TITLE></HEAD><BODY><H1>TEST</H1></BODY></HTML>\n");
  strcat(out,show_header((4<<4)+1));
  return(out);
}
#endif

void parse_command_line(int argc, char* argv[], int count, struct usb_device*dev[], char *usbdevsn[])
{
  int numeric=0;
  int c;
  int i,j;
  int result;
  int from=1, upto=4;
  int status;
  int devnum=0;
  usb_dev_handle *udev=NULL;
  usb_dev_handle *sudev=NULL; //scan device
  unsigned int id=0; //product id of current device
  char* onoff[] = { "off", "on", "0", "1" };
#ifndef WEBLESS
  char* bindaddr=0;
#endif
  unsigned int outlet;
  struct plannif plan;
  plannif_reset(&plan);

#ifdef BINDADDR
  if (strlen(BINDADDR) != 0)
    bindaddr=BINDADDR;
#endif

  while( (c=getopt(argc, argv,"i:o:f:t:a:A:b:g:m:lqvhnsd:D:u:p:")) != -1 )
  {
    if ((c != 'h') && (c != 'v') && (count == 0)) {
      fprintf(stderr, "No GEMBIRD SiS-PM found. Check USB connections, please!\n");
      if (udev != NULL) {
        usb_close(udev);
        udev = NULL;
      }
      exit(1);
    }
    if( c=='o' || c=='f' || c=='g' || c=='t' || c=='a' || c=='A' || c=='m')
    {
      if((strncmp(optarg,"all",strlen("all"))==0)
         || (atoi(optarg)==7) )
      { //use all outlets
        from=1;
        upto=4;
      } else
      {
		    from = upto = atoi(optarg);
      }
      if (from<1 || upto>4)
      {
        fprintf(stderr,"Invalid outlet number given: %s\n"
          "Expected: 1, 2, 3, 4, or all.\nTerminating.\n",optarg);
        print_disclaimer( argv[0] );
        exit(-6);
      }
    }
    else
    {
      from = upto = 0;
    }
    if( c=='o' || c=='f' || c=='g' || c=='b' || c=='t' || c=='a' || c=='A' || c=='m') //we need a device handle for these commands
    {
      /* get device-handle/-id if it wasn't done already */
      if(udev==NULL){
        udev = get_handle(dev[devnum]);
        id = get_id(dev[devnum]);
        if(udev==NULL)
          fprintf(stderr, "No access to Gembird #%d USB device %s\n",
            devnum, dev[devnum]->filename );
        else
          if(verbose) printf("Accessing Gembird #%d USB device %s\n",
		       devnum, dev[devnum]->filename );
      }
    }

#ifdef WEBLESS
    if (c=='l' || c=='i' || c=='p' || c=='u' )
    {
      fprintf(stderr,"Application was compiled without web-interface. Feature not available.\n");
      exit(-100);
    }
#endif

    // using first available device
    id = get_id(dev[devnum]);
    if (((id == PRODUCT_ID_MSISPM_OLD) || (id == PRODUCT_ID_MSISPM_FLASH)) && (from != upto))
      from = upto = 1;
    for (i=from; i <= upto; i++) {
      switch(c) {
        case 's':
          for (status=0; status<count; status++) {
            if (numeric == 0)
              printf("Gembird #%d\nUSB information:  bus %s, device %s\n", status,
                     dev[status]->bus->dirname, dev[status]->filename);
            else
              printf("%d %s %s\n", status,
                     dev[status]->bus->dirname, dev[status]->filename);
            id = get_id(dev[status]);
            if ((id == PRODUCT_ID_SISPM) || (id == PRODUCT_ID_SISPM_FLASH_NEW))
              if (numeric == 0)
                printf("device type:      4-socket SiS-PM\n");
              else
                printf("4\n");
            else
              if (numeric == 0)
                printf("device type:      1-socket mSiS-PM.\n");
              else
                printf("1\n");
            sudev = get_handle(dev[status]);
            id = get_id(dev[status]);
            if(sudev==NULL)
              fprintf(stderr, "No access to Gembird #%d USB device %s\n",
                      status, dev[status]->filename );

            if (numeric == 0)
              printf("serial number:    %s\n",get_serial(sudev));
            else
              printf("%s\n",get_serial(sudev));
            usb_close(sudev);
            sudev = NULL;
            printf("\n");
          }
          break;
      // select device...
      // replace previous (first is default) device by selected one
      case 'd': // by id
        if (udev != NULL) {
          usb_close(udev);
          udev = NULL;
        }
        devnum = atoi(optarg);
        if ((devnum < 0) || (devnum >= count)) {
          fprintf(stderr, "Invalid number or given device not found.\nTerminating\n");
          if (udev != NULL) {
            usb_close(udev);
            udev = NULL;
          }
          exit(-8);
        }
        break;
      case 'D': // by serial number
        for (j=0; j < count; j++) {
          if (debug)
            fprintf(stderr, "now comparing %s and %s\n", usbdevsn[j], optarg);
          if (strcasecmp(usbdevsn[j], optarg) == 0) {
            if (udev != NULL) {
              usb_close(udev);
              udev = NULL;
            }
            devnum = j;
            break;
          }
        }
        if (devnum != j) {
          fprintf(stderr, "No device with serial number %s found.\nTerminating\n",optarg);
          if (udev != NULL) {
            usb_close(udev);
            udev = NULL;
          }
          exit(-8);
        }
        break;
      	case 'o':
          outlet=check_outlet_number(id, i);
          sispm_switch_on(udev,id,outlet);
          if(verbose) printf("Switched outlet %d %s\n",i,onoff[1+numeric]);
          break;
      	case 'f':
	        outlet=check_outlet_number(id, i);
	        sispm_switch_off(udev,id,outlet);
	        if(verbose) printf("Switched outlet %d %s\n",i,onoff[0+numeric]);
	        break;
      	case 't':
	        outlet=check_outlet_number(id, i);
	        result=sispm_switch_toggle(udev,id,outlet);
	        if(verbose) printf("Toggled outlet %d %s\n",i,onoff[result]);
	        break;
      	case 'A':
	      {
          time_t date, lastEventTime;
          struct tm *timeStamp_tm;
          struct plannif plan;
          int opt, lastAction = 0;
          ulong loop = 0;
          int optindsave = optind;
          int actionNo=0;

          outlet=check_outlet_number(id, i);

          time( &date );
					timeStamp_tm = localtime(&date);
          lastEventTime = ((ulong)(date/60))*60; // round to previous minute
          plannif_reset (&plan);
          plan.socket = outlet;
          plan.timeStamp = date;
          plan.actions[0].switchOn = 0;

          const struct option opts[] = {
            { "Ado", 1, NULL, 'd' }, { "Aafter", 1, NULL, 'a' }, { "Aat", 1, NULL, '@' },
            { "Aloop", 1, NULL, 'l' }, { NULL, 0, 0, 0 }
          };

          // scan long options and store in plan+loop variables
          while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF) {
            if (opt == 'l') {
              loop = atol(optarg);
              continue;
            }
            if (actionNo+1 >= sizeof(plan.actions)/sizeof(struct plannifAction)) { // last event is reserved for loop or stop
              fprintf(stderr,"Too many plannification events\nTerminating\n");
              exit(-7);
            }
            switch (opt) {
            case 'd':
              plan.actions[actionNo+1].switchOn = (strcmp(optarg, "on") == 0 ? 1 : 0);
              break;
            case 'a':
              plan.actions[actionNo].timeForNext = atol(optarg);
              break;
            case '@':
              {
                struct tm tm;
                time_t time4next;
                bzero (&tm, sizeof(tm));
                tm.tm_isdst = timeStamp_tm->tm_isdst;
                strptime(optarg, "%Y-%m-%d %H:%M", &tm);
                time4next = mktime(&tm);
                if (time4next > lastEventTime)
                  plan.actions[actionNo].timeForNext = (time4next - lastEventTime) / 60;
                else
                  plan.actions[actionNo].timeForNext = 0;
                break;
              }
            case '?':
            default:
                fprintf(stderr,"Unknown Option: %s\nTerminating\n",argv[optind-1]);
                exit(-7);
                break;
            }
            if (plan.actions[actionNo].timeForNext == 0) {
              fprintf(stderr,"Incorrect Date: %s\nTerminating\n",optarg);
              exit(-7);
            }

            if (plan.actions[actionNo].timeForNext != -1 && plan.actions[actionNo+1].switchOn != -1) {
              lastEventTime += 60 * plan.actions[actionNo].timeForNext;
              actionNo++;
            }
          }

          // compute the value to set in the last row, according to loop
          while (plan.actions[lastAction].timeForNext != -1) {
            if (loop && (lastAction > 0)) { // we ignore the first time for the loop calculation
              if (loop <= plan.actions[lastAction].timeForNext) {
                printf ("error : the loop period is too short\n");
                exit(1);
              }
              loop -= plan.actions[lastAction].timeForNext;
            }
            lastAction++;
          }
          if (lastAction >= 1)
            plan.actions[lastAction].timeForNext = loop;

          // let's go, and check
          usb_command_setplannif(udev, &plan);
	        if(verbose) {
            plannif_reset (&plan);
	          usb_command_getplannif(udev,outlet,&plan);
	          plannif_display(&plan, 0, NULL);
          }

          if (i<upto)
            optind = optindsave; // reset for next device if needed

          break;
	      }
      	case 'a':
	        outlet=check_outlet_number(id, i);
          struct plannif plan;
          plannif_reset (&plan);
	        usb_command_getplannif(udev,outlet,&plan);
          plannif_display(&plan, verbose, argv[0]);
	        break;
      	case 'g':
	        outlet=check_outlet_number(id, i);
	        result=sispm_switch_getstatus(udev,id,outlet);
	        if(verbose) printf("Status of outlet %d:\t",i);
	        printf("%s\n",onoff[ result +numeric]);
	        break;
      	case 'm':
    	    outlet=check_outlet_number(id, i);
	        result=sispm_get_power_supply_status(udev,id,outlet);
	        if(verbose) printf("Power supply status is:\t");
	        printf("%s\n",onoff[ result +numeric]); //take bit 1, which gives the relais status
	        break;
#ifndef WEBLESS
  	    case 'p':
	        listenport=atoi(optarg);
	        if(verbose) printf("Server will listen on port %d.\n",listenport);
	        break;
      	case 'u':
	        homedir=strdup(optarg);
	        if(verbose) printf("Web pages come from \"%s\".\n",homedir);
	        break;
            case 'i':
      	    bindaddr=optarg;
	        if (verbose) printf("Web server will bind on interface with IP %s\n",bindaddr);
	        break;
        case 'l':
          {
                int* s;
                if (verbose)
                  printf("Server goes to listen mode now.\n");
                if ((s = socket_init(bindaddr)) != NULL) {
                  daemonize();
                  while(1)
                    l_listen(s,dev[devnum],devnum);
                }
                else
                  exit(EXIT_FAILURE);
            break;
          }
#endif
      	case 'q':
	        verbose=1-verbose;
    	    break;
      	case 'n':
	        numeric=2-numeric;
	        break;
        case 'b':
    	    if (strncmp(optarg,"on",strlen("on"))==0)
          {
                 sispm_buzzer_on(udev);
		        if(verbose) printf("Turned buzzer %s\n",onoff[1+numeric]);
    	    } else
      	    if (strncmp(optarg,"off",strlen("off"))==0)
	          {
	            sispm_buzzer_off(udev);
          		if(verbose) printf("Turned buzzer %s\n",onoff[0+numeric]);
      	    }
    	    break;
        case 'v':
            print_disclaimer( argv[0] );
    	    break;
        case 'h':
            print_usage( argv[0] );
            break;
      	default:
            fprintf(stderr,"Unknown Option: %c(%x)\nTerminating\n",c,c);
            exit(-7);
      }
    } // loop through devices
  } // loop through options

  if (udev!=NULL) {
    usb_close(udev);
    udev = NULL;
  }
  return;
}


int main(int argc, char** argv)
{
  struct usb_bus *bus;
  struct usb_device *dev, *usbdev[MAXGEMBIRD], *usbdevtemp;
  char *usbdevsn[MAXGEMBIRD];
  int count=0, found = 0, i=1;

#ifndef MSG_NOSIGNAL
  (void) signal(SIGPIPE, SIG_IGN);
#endif

  memset(usbdev,0,sizeof(usbdev));

  usb_init();
  usb_find_busses();
  usb_find_devices();

#ifndef WEBLESS
  if (strlen(WEBDIR)==0)
    homedir=HOMEDIR;
  else
    homedir=WEBDIR;
#endif

  // initialize by setting device pointers to zero
  for (count=0; count < MAXGEMBIRD; count++)
    usbdev[count]=NULL; count=0;

  //first search for GEMBIRD (m)SiS-PM devices
  for (bus = usb_busses; bus; bus = bus->next) {
    for (dev = bus->devices; dev; dev = dev->next) {
      if ((dev->descriptor.idVendor == VENDOR_ID) && ((dev->descriptor.idProduct == PRODUCT_ID_SISPM) ||
                                                      (dev->descriptor.idProduct == PRODUCT_ID_MSISPM_OLD) ||
                                                      (dev->descriptor.idProduct == PRODUCT_ID_MSISPM_FLASH) ||
                                                      (dev->descriptor.idProduct == PRODUCT_ID_SISPM_FLASH_NEW))) {
	usbdev[count++] = dev;
      }
      if (count == MAXGEMBIRD) {
        fprintf(stderr,"%d devices found. Please recompile if you need to support more devices!\n",count);
        break;
      }
    }
  }

  /* bubble sort them first, thnx Ingo Flaschenberger */
  if (count > 1) {
    do {
      found = 0;
      for (i=1; i< count; i++) {
        if (usbdev[i]->devnum < usbdev[i-1]->devnum) {
          usbdevtemp = usbdev[i];
          usbdev[i] = usbdev[i-1];
          usbdev[i-1] = usbdevtemp;
          found = 1;
        }
      }
    } while (found != 0);
  }

  /* get serial number of each device */
  for (i=0; i < count; i++) {
    usb_dev_handle *sudev = NULL;

    sudev = get_handle(usbdev[i]);
    if (sudev == NULL) {
      fprintf(stderr, "No access to Gembird #%d USB device %s\n",
              i, usbdev[i]->filename );
      usbdevsn[i] = malloc(5);
      usbdevsn[i][0] = '#';
      usbdevsn[i][1] = '0'+i;
      usbdevsn[i][2] = '\0';
    }
    else {
      usbdevsn[i] = strdup(get_serial(sudev));
      usb_close(sudev);
      sudev = NULL;
    }
  }

  /* do the real work here */
  if (argc <= 1)
    print_usage(argv[0]);
  else
    parse_command_line(argc,argv,count,usbdev,usbdevsn);

  return 0;
}
