/*
 * SMS4IoT HA integration sms4iot gateway
 * Copyright notice:
 * SPDX-FileCopyrightText: 2026 Copyright Department C Incorporated
 * SPDX-License-Identifier: CC-BY-NC-4.0 https://creativecommons.org/licenses/by-nc/4.0/legalcode.en
 *
 * The above copyright notice shall be included in all copies or substantial portions of this software.
 * Note:sms4iot protocol and associated gateway architecture patented US11109197
 * Hey! Even a few poor developers deserve something for our work. Contact us for reasonable commercial licensing terms.
 *
 * based on ~/s4i3imqtt/tmp/s4i3imqtt.c
 * 2021 Copyright ZX Communications, Inc  All rights reserved
 * 2022-2025 Copyright Department C Inc. All rights reserved
 * sms4iot to mqtt
 * tossed together by RHLamb Jun 2022
 * updated to ASCON crypto July 2025
 * mqttsrvr<------->this<-udp->itdtlondonsrvr<->"the dns"<->sms4iotdevice
 * your responsibitiy | <-s4i encrypted-------------------------------->
 *                 sms4iotdevice keys
 */
#define VERSION "V0.007 s4igw (c) 2026 Department C Inc. All rights reserved" 
#define S4ICONFSERVER_DEFAULT "ns.5.x031.nl"
#define S4ICONFSERVERPORT_DEFAULT 54391
#define S4ICONFSERVER_SPACING_DEFAULT 5
#define LOGFILE_MAXSIZE_DEFAULT 5000000
#define LOGFILE_MAXNUMBER_DEFAULT 3

#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdarg.h>
#include <errno.h>
#include <pthread.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <dirent.h>

#pragma GCC diagnostic ignored "-Wformat-overflow"

#define DATA_BUFSIZE 8192
#define NARGS 20

static int debug=1; // 0:minimal 1:typical 2:lots

static char CURDIR[MAXPATHLEN];
static char MYCONFFILE[MAXPATHLEN];
static char LOGFILE[MAXPATHLEN];
static int LOGFILE_MAXSIZE=LOGFILE_MAXSIZE_DEFAULT;
static int LOGFILE_MAXNUMBER=LOGFILE_MAXNUMBER_DEFAULT;
static char MYDEVDIR[MAXPATHLEN];
static char MYDATADIR[MAXPATHLEN];
static char S4ICONFSERVER[256];
static int S4ICONFSERVERPORT=S4ICONFSERVERPORT_DEFAULT;
static int S4ICONFSERVER_SPACING=S4ICONFSERVER_SPACING_DEFAULT;

static FILE *flog=NULL;
static void _logit(const char *f,const char *format, ...);
#define mylog(...) _logit(__func__,__VA_ARGS__)

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(x,y) ((x)<(y)?(x):(y))
#define PTR232BIT(a) (uint32_t)(  ((uint32_t)(a)[0]<<24) | ((a)[1]<<16) | ((a)[2]<<8) | (a)[3] )
#define PTR232BITHOST(a) (uint32_t)(  ((uint32_t)(a)[3]<<24) | ((a)[2]<<16) | ((a)[1]<<8) | (a)[0] )
#define PTR216BIT(a) (uint16_t)(((a)[0]<<8)|(a)[1])
#define PTR216BITHOST(a) (uint16_t)(((a)[1]<<8)|(a)[0])
#define SET16BIT(a,b) { (a)[0] = b&0xFF; (a)[1] = (b>>8); }

static pthread_mutex_t mem_mutex=PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t devid_mutex=PTHREAD_MUTEX_INITIALIZER;
static int devid_mutex_inuse=0;
static pthread_mutex_t rlprintf_mutex=PTHREAD_MUTEX_INITIALIZER;
static uint mutexwait=0,mutexcnt=0;
static int _mymutex(pthread_mutex_t *t,const char *fnc,uint8_t sr)
{
  if(sr) {
    //printf("%s lock\n",fnc);
    mutexcnt++;
    if(pthread_mutex_trylock(t)) { // failed
      mutexwait++;
      if(pthread_mutex_lock(t)) { // wait for lock
        mylog("error %s-LOCK: %s\n",fnc,strerror(errno));
        return -1;
      }
    }
  } else {
    //printf("%s unlock\n",fnc);
    if(pthread_mutex_unlock(t)) {
      mylog("error %s-UNLOCK: %s\n",fnc,strerror(errno));
      return -1;
    }
  }
  return 0;
}
#define mymutex(a,b) _mymutex(a,__func__,b)

//#define TESTMYMALLOC
#ifdef TESTMYMALLOC
static char *mymemslots[32000];
#endif // TESTMYMALLOC
static uint32_t memmax=0,memtot=0;
static void *mymalloc(int n)
{
  uint8_t *p;

  if((p=(uint8_t *)malloc(n+4)) == NULL) {
    mylog("error: Failed malloc(%d) memtot:%u - just exit\n",n,memtot);
    exit(-1);
  }
  *(uint32_t *)p = n;

//mylog("%d %p\n",n,p);

  mymutex(&mem_mutex,1);
  memtot += n;
  if(memtot > memmax) memmax = memtot;
  mymutex(&mem_mutex,0);
#ifdef TESTMYMALLOC
  {
    int i;
    for(i=0;i<32000;i++) if(mymemslots[i] == 0) break;
    if(i < 32000) mymemslots[i] = (p+4);
  }
#endif // TESTMYMALLOC
  return (void *)(p+4);
}
static void myfree(void *q)
{
  uint32_t n;
  uint8_t *p;

  if((p = (uint8_t *)q) == NULL) {
    mylog("error: myfree failed: NULL argument\r\n");
    return;
  }
#ifdef TESTMYMALLOC
  {
    int i;
    for(i=0;i<32000;i++) if(mymemslots[i] == (char *)p) break;
    if(i < 32000) mymemslots[i] = NULL;
  }
#endif // TESTMYMALLOC
  p -= 4;
  n = *(uint32_t *)p;

  if(n == 0) mylog("error myfree %d %p\n",n,p);

  mymutex(&mem_mutex,1);
  memtot -= n;
  mymutex(&mem_mutex,0);

#ifdef TESTMYMALLOC
  memset(p,0,n+4);
#endif // TESTMYMALLOC
  
  memset(p,0,n+4);
  
  free(p);
}
static void mymemstat(void)
{
  mylog("memtot/memmax:%d/%d\n",memtot,memmax);
#ifdef TESTMYMALLOC
  {
    int i;
    for(i=0;i<32000;i++) {
      if(mymemslots[i]) {
        mylog("unfreed %d bytes p=%p\n",*(uint32_t *)(mymemslots[i]-4),mymemslots[i]);
      }
    }
  }
#endif // TESTMYMALLOC 
}

static char *mystrdup(char *str)
{
  int i;
  char *p;

  if(str == NULL) return (char *)0;
  i = strlen(str);
  p = (char *)mymalloc(i+1);
  if(p == NULL) return (char *)0;
  strcpy(p,str);
  return p;
}
static uint8_t *mymemdup(uint8_t *p,int n)
{
  uint8_t *q;

//mylog("%p %d\n",p,n);

  q = (uint8_t *)mymalloc(n+1);
  memcpy(q,p,n);
  q[n] = '\0'; // just in case
  return q;
}

static void _logit(const char *f,const char *format, ...)
{
  va_list args;
  
  if(mymutex(&rlprintf_mutex,1)) {
    printf("%s: inuse from %s. Dropping.\n",__func__,f?f:"NULL");
    return;
  }  
  {
    fprintf(stdout,"%s:",f);
    va_start(args,format);
    vfprintf(stdout,format,args);
    va_end(args);
    fflush(stdout);
  }
  if(flog) {
    fprintf(flog,"%s:",f);
    va_start(args,format);
    vfprintf(flog,format,args);
    va_end(args);
    fflush(flog);
  }
  mymutex(&rlprintf_mutex,0);
}
static void logfile_rollover(void)
{
  char buf[MAXPATHLEN],nowstr[50];
  struct stat st;
  if(flog == NULL) return;
  if(stat(LOGFILE,&st) == 0 && st.st_size > LOGFILE_MAXSIZE) {
    time_t ltmp;
    fclose(flog);
    time(&ltmp);
    strftime(nowstr,sizeof(nowstr),"%Y%m%d%H%M%S",gmtime(&ltmp));
    sprintf(buf,"%s.%s",LOGFILE,nowstr);
    rename(LOGFILE,buf);
    flog = fopen(LOGFILE,"a");
  }
  // prune old logfiles
  {
    DIR *dirp;
    struct dirent *dp;
    char *scandir,*p,oldest[MAXPATHLEN];
    int n,k;
    scandir = CURDIR;
    if((p=strrchr(LOGFILE,'/')) == NULL) return;
    p++;
    n = strlen(p);
    sprintf(oldest,"%s.30220101000000",p);
    k = 0;
    if((dirp = opendir(scandir)) == (DIR *)0) {
      mylog("Cannot open directory %s\n",scandir);
      return;
    }
    while((dp=readdir(dirp))) {
      if(strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0) continue;
      if(strncmp(dp->d_name,p,n)) continue;
      if(debug>1) mylog("Found |%s|\n",dp->d_name);
      if(strlen(dp->d_name) == n) continue;
      k++;
      if(strcmp(dp->d_name,oldest) < 0) strcpy(oldest,dp->d_name);
    }
    closedir(dirp);
    if(k > LOGFILE_MAXNUMBER) {
      sprintf(buf,"%s/%s",CURDIR,oldest);
      mylog("Removing |%s|\n",buf);
      unlink(buf);
    }
  }
}

static uint8_t startshutdown=0;
static void mysighandler(int sig) {
  mylog("signal = %d\n",sig);
  startshutdown = 1;
  return; //exit(0);
}
static void blocksignals(int flg)
{
  if(flg) {
    signal(SIGCHLD,mysighandler);
    signal(SIGHUP,mysighandler);
    signal(SIGINT,mysighandler);
    signal(SIGQUIT,mysighandler);
    signal(SIGTERM,mysighandler);
    signal(SIGKILL,mysighandler); // cant block this one
    return;
  }
  signal(SIGCHLD,SIG_DFL);
  signal(SIGHUP,SIG_DFL);
  signal(SIGINT,SIG_DFL);
  signal(SIGQUIT,SIG_DFL);
  signal(SIGTERM,SIG_DFL);
  signal(SIGKILL,SIG_DFL);
}

static char *lookup_v4host(char *host)
{
  struct addrinfo hints,*res,*result;
  int err;
  static char addrstr[100];
  void *ptr;
  if(host == NULL) return NULL;
  memset(&hints,0,sizeof (hints));
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags |= AI_CANONNAME;  
  if((err=getaddrinfo(host,NULL,&hints,&result))) {
    mylog("getaddrinfo %s failed %s\n",host,gai_strerror(err));
    return NULL;
  }
  res = result;
  addrstr[0] = '\0';
  // pick v4 only for now
  while(res) {
    //inet_ntop(res->ai_family,res->ai_addr->sa_data,addrstr,sizeof(addrstr));
    switch(res->ai_family) {
    case AF_INET:
      ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
      inet_ntop(res->ai_family,ptr,addrstr,sizeof(addrstr));
      break;
    case AF_INET6:
      ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
      //inet_ntop(res->ai_family,ptr,addrstr,sizeof(addrstr));
      break;
    }
    //inet_ntop(res->ai_family,ptr,addrstr,sizeof(addrstr)); 
    //if(debug>1) mylog("IPv%d address: %s (%s)\n",res->ai_family == PF_INET6?6:4,addrstr,res->ai_canonname);
    res = res->ai_next;
  }
  freeaddrinfo(result);
  if(addrstr[0] == '\0') return NULL; // no IPv4 address found
  return addrstr;
}


#ifdef FOOP
static char mytolower(char c)
{
  if(c >= 'A' && c <= 'Z') return c|0x20;
  return c;
}
static void strtolower(char *str)
{
  while(*str) {
    *str = mytolower(*str);
    str++;
  }
}
#endif // FOOP
static int cleanup(char *io)
{
  char *q,*p;

  /* rid trailing space and sr lf */
  for(q = io + strlen(io);q-- != io && (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n');) ;
  *(q+1) = '\0';
  /* rid leading space */
  for(q=io;*q == ' ' || *q == '\t';q++) ;
  for(p=io;*q;) *p++ = *q++;
  *p = '\0';
  return 0;
}
static int lparse(char *line,char *argv[],int maxargs,char delc)
{
  char *cp;
  int argc,qflag;

  if(line == NULL) return 0;

  if((cp = strchr(line,'\r')) != (char *)0) *cp = '\0';
  if((cp = strchr(line,'\n')) != (char *)0) *cp = '\0';

  for(argc=0;argc<maxargs;argc++) argv[argc] = (char *)0;

  for(argc=0;argc<maxargs;) {
    qflag = 0;
    while(*line == ' ' || *line == '\t') line++; /* whitespace */
    if(*line == '\0') break; /* done */
    if(*line == '"') { line++; qflag = 1; } /* quote */
    argv[argc++] = line;
    if(qflag) {                         /* quote */
      if((line = strchr(line,'"')) == (char *)0) return -1; /*error*/
      *line++ = '\0';
    } else {
      for(cp=line;*cp;cp++) {
        //if(*cp == ' ' || *cp == '\t' || *cp == delc) break;
	if(*cp == delc) break;
      }
      if(*cp) *cp++ = '\0'; /* non-zero */
      line = cp;
    }
  }
  return argc;
}
static int _rdump(unsigned char *ptr,int n,const char *fnc)
{
  int i,j1,j2; char buf[80]; static char htoas[]="0123456789ABCDEF";
  j1 = j2 = 0; /* gcc -W */
  for(i=0;i<n;i++,j1+=3,j2++) {
    if((i&0xf) == 0) {
      if(i) { buf[j2]='\0'; _logit(fnc,"%s|\n",buf); }
      j1=0; j2=51; memset(buf,' ',80); buf[50]='|';
    }
    buf[j1] = htoas[(ptr[i]&0xf0) >> 4]; buf[j1+1] = htoas[ptr[i]&0x0f];
    if(ptr[i] >= 0x20 && ptr[i] < 0x80) buf[j2]=ptr[i]; else buf[j2]='.';
  }
  buf[j2]='\0'; _logit(fnc,"%s|\n",buf);
  return 0;
}
#define rdump(a,b) _rdump(a,b,__func__)

static int hex2i(char c)
{
  if(c >= '0' && c <= '9') return (int)(c - '0');
  if(c >= 'A' && c <= 'F') return (int)((c - 'A') + 10);
  if(c >= 'a' && c <= 'f') return (int)((c - 'a') + 10);
  return -1;
}
#ifdef FOOP
static int ahex2bin(uint8_t *out,char *in)
{
  uint8_t *p;
  p = out;
  while(*in) {
    *p++ = hex2i(in[0])<<4 | hex2i(in[1]);
    in += 2;
  }
  return (int)(p - out);
}
#endif // FOOP
static int ahex2bin(uint8_t *out,int olen,char *in)
{
  uint8_t *p;
  int i,j;
  p = out;
  while(1) {
    if(*in == 0) break;
    if(*(in+1) == 0) return -1; // not even # of chars                          
    i = hex2i(in[0]);
    j = hex2i(in[1]);
    if(i < 0 || j < 0) return -1; // bad chars                                  
    in += 2;
    if(olen <= 0) return -1; // not enough space                                
    *p++ = (uint8_t) ((i<<4)|j); // make sure inplace ahex 2 bin works          
    olen--;
  }
  return (int)(p - out);
}
#ifdef FOOP
static int bin2ahex(char *out,uint8_t *in,int n)
{
  int i;
  for(i=0;i<n;i++) sprintf(&out[2*i],"%02x",in[i]);
  out[2*n] = '\0';
  return 0;
}
static char base64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static int base64decode(char *in,uint8_t *out)
{
  int i,n,len;
  char *c,p[4],*q;

  len = 0;
  n = strlen(in);
  q = in;
  while(n > 0) {
    int k;
    for(i=0;i<4;i++) {
      if((c=strchr(base64,*q)) == NULL) return -1;
      p[i] = c - base64;
      q++;
    }
    k = (p[2] == 64)?1:(p[3] == 64)?2:3;
    if(k != 3) {
      if(p[2] == 64) p[2] = 0;
      if(p[3] == 64) p[3] = 0;
    }
    *out++ = (p[0]<<2)|(p[1]>>4);
    *out++ = (p[1]<<4)|(p[2]>>2);
    *out++ = (p[2]<<6)|(p[3]);
    n -= 4;
    len += k;
  }
  return len;
}
static int base64encode(char *out,uint8_t *in,int n)
{
  int len;

  len = 0;
  while(n > 0) {
    *out++ = base64[ ((in[0]>>2)&0x3f) ];
    if(n > 2) {
      *out++ = base64[ ((in[0]<<4)&0x30) | ((in[1]>>4)&0x0f) ];
      *out++ = base64[ ((in[1]<<2)&0x3c) | ((in[2]>>6)&0x03) ];
      *out++ = base64[ in[2]&0x3f ];
    } else if(n == 2) {
      *out++ = base64[ ((in[0]<<4)&0x30) | ((in[1]>>4)&0x0f) ];
      *out++ = base64[ ((in[1]<<2)&0x3c) ];
      *out++ = '=';
    } else if(n == 1) {
      *out++ = base64[ ((in[0]<<4)&0x30) ];
      *out++ = '=';
      *out++ = '=';
    }
    len += 4;
    n -= 3;
    in += 3;
  }
  *out = '\0';
  return len;
}
#endif // FOOP
static void tconvert(char *out,uint32_t in,uint8_t dur)
{
  if(out == NULL) return; // 2007-04-05T14:30Z
  if(dur) {
    if(in > 86400) sprintf(out,"%dday %dhr",(in/86400),(in%86400)/3600);
    else sprintf(out,"%dhr %dmin",in/3600,(in%3600)/60);
  } else {
    struct tm tm;
    time_t tmp=in;
    localtime_r(&tmp,&tm);
    strftime(out,32,"%Y-%m-%dT%H:%M:%S%z",&tm); // ISO8601
  }
}
static void pytconvert(char *out,uint32_t in)
{
  if(out == NULL) return; // 2000-12-25 05:30:30
  struct tm tm;
  time_t tmp=in;
  localtime_r(&tmp,&tm);
  strftime(out,32,"%Y-%m-%d %H:%M:%S",&tm); // python datetime.datetime
}


// to avoid dependance on -lcrypto library - which changes often. put your own aes routines here
//#include "myaes.c"
#include "ascon.c"

// V3.0 |time(4)|R(16n)|hash(time+R+k2)(12)| = len=16n + 16
// R=aes(|time(4)|IE|...|IE|,k1)
enum {  SMV3_TYPE_NOP, SMV3_TYPE_NEWDEV,SMV3_TYPE_DATA,SMV5_TYPE_DATA,ASCON_TYPE_DATA };
enum {IE_NULL,
      IE_STATUS,   // req=|| resp=|internet connected yes/no(4)|ip(4)|mask(4)|gw(4)|dns(4)|ssid(32)|0(1)|
      IE_WIFISET,  // req=|ssid(32)|0(1)|pass(32)|0(1)| resp=|ssid(32)|bssid(6)|rssi(1)|
      IE_WIFISCAN, // req=|| resp=|number(1)|current yes/no(1)|rssi(1)|ssid(32)|0(1)|
      IE_LOGINSET, // req=|pass(32)|0(1)| resp=|ok yes/no(2)|
      IE_RELAY,    // req=|onoff(1)| resp=IE_STATUS
      IE_WIFISCANSSID,
      IE_SETMSG,  // req=|msg(32)|0(1)|
      IE_SENSORSTAT, // req=|| resp=|n(2)|d1(4)|...|dn(4)|
      IE_SETMSG2,  // req=|msg(64)|0(1)|
      IE_GETNEWID, // req=|mac(6)| note:but (id,dn,k1,k2)=special auto config set
      IE_STATUS2, // |statflg(4)|bssid(6)|rssi(2)|bssid(6)|rssi(2)|bssid(6)|rssi(2)|uptime(4)|vbat(4)|curssid(32)|
      IE_KEYEXCHANGE,
      IE_STREAMMSG,
      IE_GETNEWID2,
      IE_SENSORDATA,      
      IE_LAST // placeholder check    
};
#define DEV_RELAY_ON       0x00000001
#define DEV_INPIN_HIGH     0x00000002
#define DEV_BSSID_HISTORY  0x00000004
#define DEV_BUTTON_PUSHED  0x00000008

#define SMS4IOT_FRESHENESS 15 // if end-2-end age of response is less, then ok

typedef struct _devidcb {
  struct _devidcb *next;
  uint8_t inuse;
  uint8_t dflg;
  char *id;
  char *swver;
  uint32_t protoversion;
  uint8_t *v3_k2;
  uint8_t *v3_k1;
  char *v3_dmsg; // when v3_msg is null
  char *v3_msg; // current msg being sent
  char *nmsg; // queue of 1
  char *nmsg0,*nmsg1,msgfile[MAXPATHLEN];
  uint32_t ttime; // last tx time
  uint8_t txseq;
  uint32_t state;
  uint8_t *out;
  int n;
  int nk2time;
  int nk1time;
  int sendcnt;

  uint32_t lasttx,rtt;
  uint8_t rxack;
  uint8_t ack;
  uint32_t lastseen_uuid;
  uint32_t lastseen_time;
  
  int wroteconfig;

  char *hadata;
} devidcb;
static devidcb *dc0=NULL;
static devidcb *devid_find(char *devid,int create)
{
  devidcb *dc;

  if(devid && strlen(devid) > 20) return NULL;
  if(devid_mutex_inuse) return NULL;
 
  mymutex(&devid_mutex,1);
  devid_mutex_inuse = 1;
  for(dc=dc0;dc;dc=dc->next) {
    if(strcmp(dc->id,devid) == 0 && dc->dflg == 0) {
      mymutex(&devid_mutex,0);
      devid_mutex_inuse = 0;
      return dc;
    }
  }
  if(create == 0) {
    mymutex(&devid_mutex,0);
    devid_mutex_inuse = 0;
    return NULL;
  }
  dc = (devidcb *)mymalloc(sizeof(devidcb));
  memset(dc,0,sizeof(devidcb));
  dc->id = mystrdup(devid);
  
  dc->next = dc0;
  dc0 = dc;
  mymutex(&devid_mutex,0);
  devid_mutex_inuse = 0;
  return dc;
}

// update_devid_entry(dc->id,"temperature",llbuf);
static void update_devid_entry(devidcb *dc,char *name,char *data)
{
  if(name == NULL) {
    if(dc->hadata) {
      myfree(dc->hadata);
      dc->hadata = NULL;
    }
    return;
  }
  if(dc->hadata == NULL) {
    dc->hadata = mymalloc(2048);
    memset(dc->hadata,0,2048);
  }
  int i,j;
  j = i = strlen(dc->hadata);
  if(name) j += strlen(name);
  if(data) j += strlen(data);
  j += 2; // = \n
  if(j >= 2048) {
    return;
  }
  sprintf(dc->hadata + i,"%s=%s\n",name,data);
}

static char *readinfile(char *fname)
{
  struct stat myst;
  if(stat(fname,&myst)) return NULL;
  if(myst.st_size <= 0) return NULL;
  FILE *fstk;
  if((fstk=fopen(fname,"r")) == NULL) return NULL;
  uint8_t *p=mymalloc(myst.st_size + 8);
  size_t n=fread(p,1,myst.st_size,fstk);
  fclose(fstk);
  p[n] = '\0';
  return (char *)p;
}
static void readparsemsg(devidcb *dc)
{
  if(dc->nmsg) { // free last sent one
    myfree(dc->nmsg);
    dc->nmsg = NULL;
  }
  if(dc->nmsg0 == NULL) { // see if there is another out/msg
    sprintf(dc->msgfile,"%s/%s.out",MYDATADIR,dc->id); // 1st
    if((dc->nmsg0=readinfile(dc->msgfile)) == NULL) {
      sprintf(dc->msgfile,"%s/%s.msg",MYDATADIR,dc->id); // 2nd
      if((dc->nmsg0=readinfile(dc->msgfile)) == NULL) {	
      }
    }
    if(dc->nmsg0 == NULL) {
      dc->msgfile[0] = '\0';
      return;
    }
    dc->nmsg1 = dc->nmsg0; // will be NULL if no file found    
  }
  // fill in dc->nmsg
  char *r,*q,*lsp,*lsr,llbuf[128];
  r = dc->nmsg1;
  q = llbuf;
  q += sprintf(q,"setmsg2 '");
  lsp = NULL;
  while(*r && (int)(q-llbuf) <= 64) {
    if(*r == '\n') *r = ' ';
    if(*r == ' ') { lsp = q; lsr = r; }
    *q++ = *r++;
  }
  if(*r) {
    if(lsp) {
      q = lsp;
      dc->nmsg1 = lsr + 1;
    } else {
      dc->nmsg1 = r;
    }
    *q++ = '\'';
    *q++ = '\0';
    dc->nmsg = mystrdup(llbuf);
  } else { // last piece. we are done
    *q++ = '\'';
    *q++ = '\0';
    dc->nmsg = mystrdup(llbuf);
    myfree(dc->nmsg0);
    dc->nmsg0 = NULL;
    // remove commands from .out files. leave generic .msg
    if(strstr(dc->msgfile,".msg") == NULL) unlink(dc->msgfile);
    dc->msgfile[0] = '\0';
  }
}

#define SENSORDATA_TEMPERATURE 1
#define SENSORDATA_HUMIDITY 2
#define SENSORDATA_VBAT 3
#define SENSORDATA_NOP 4
#define SENSORDATA_TIMESTAMP 5
#define SENSORDATA_CURSSID 6
#define SENSORDATA_FLAGSM1 7
#define SENSORDATA_UPTIME 8
#define SENSORDATA_VERSION 9
#define SENSORDATA_POWER 10
#define SENSORDATA_CURRENT 11
#define SENSORDATA_VOLT 12
#define SENSORDATA_DEVNAME 13

static int conf_sock=-1;
static struct sockaddr_in conf_addr;
static int conffiot(void)
{
  int n;
  devidcb *dc;
  uint8_t *p,buf[DATA_BUFSIZE];

  char lbuf[MAXPATHLEN];
  char sid[64],f_status[1024];
  //char f_ssids[1024],f_sensors[1024];
  
  uint32_t id;
  FILE *fp;
  uint fromlen;
  struct sockaddr_in from;

  if((conf_sock=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
    mylog("Sender socket failed %s\n",strerror(errno));
    return -1;
  }
  if((p=(uint8_t *)lookup_v4host(S4ICONFSERVER)) == NULL) {
    mylog("Failed to lookup %s\n",S4ICONFSERVER);
    return -1;
  }
  memset(&conf_addr,0,sizeof(struct sockaddr_in));
  conf_addr.sin_family = AF_INET;
  conf_addr.sin_addr.s_addr = inet_addr((char *)p);
  conf_addr.sin_port = htons(S4ICONFSERVERPORT);
  mylog("Sender sock fd=%d\n",conf_sock);

  while(1) {
    fromlen = sizeof(from);
    n = recvfrom(conf_sock,buf,sizeof(buf),0,(struct sockaddr *)&from,&fromlen);

    if(n <= 0) { // error condition?
      if(n == 0) {
	mylog("error: Closing socket %d\n",conf_sock);
	close(conf_sock);
	goto endit;
      } else {
	mylog("error: read error %s\n",strerror(errno));
	if(errno == EAGAIN) mylog("More to read(fd=%d) EAGAIN\n",conf_sock);
      }
      continue;
    }

    //mylog("\n");
    if(debug>1) mylog("%d bytes from %s:%u\n",n,inet_ntoa(from.sin_addr),htons(from.sin_port));
    //rdump(buf,n); // |id(4)|data(n)|  data(n)=aes(|time(3)|txseq(1)|data|,k1) n=N*16
    //mylog("\n");

    id = PTR232BIT(buf);
    sprintf(sid,"%08X",id);

    if(n < 4 + 4 + 8) { // |id(4)| + ascon:|nonce(4)|ciphertext(0+)|tag(8)|
      mylog("error: UDP packet too short (%d bytes) id %s. dropped\n",n,sid);
      rdump(buf,n);
      continue;
    }
    
    if((dc=devid_find(sid,0)) == NULL) {
      mylog("error: Bad id %s\n",sid);
      continue;
    }
    
    mymutex(&mem_mutex,1);
    if(dc->inuse) {
      mymutex(&mem_mutex,0);
      mylog("error: %s In use by other thread. Skip\n",dc->id);
      continue;
    }
    dc->inuse = 3;
    mymutex(&mem_mutex,0);
    
    p = &buf[4];
    n -= 4;

    if(dc->protoversion == ASCON_TYPE_DATA) {
      if((n=ascondecryptinplace(p,(uint64_t)n,dc->v3_k1)) <= 0) {
        mylog("error: %s Bad decrypt\n",dc->id);
        dc->inuse = 0;
        continue;
      }
    } else {
      mylog("error: %s Cant decrypt:unsupported proto version=%d\n",dc->id,dc->protoversion);
      dc->inuse = 0;
      continue;
    }
    if(n < 6) { // was n <= 0
      mylog("error: %s Cant decrypt. n=%d\n",dc->id,n);
      dc->inuse = 0;
      continue;
    }

    time_t ltmp;
    time(&ltmp);
    uint32_t myts,mydf;
    if(dc->protoversion == ASCON_TYPE_DATA) {
      
      { // full-width diff of two Unix timestamps; masking to 16 bits wrapped every ~18.2h
        int64_t _d = (int64_t)ltmp - (int64_t)(*(uint32_t *)p);
        mydf = (uint32_t)(_d < 0 ? -_d : _d);
      }
      if(mydf > 600) {
	if(debug>0) mylog("error: %s time too far off. drop\n",dc->id);
	dc->inuse = 0;
	continue;
      }
      myts = (uint32_t)(ltmp - mydf);
      
      if(*(uint32_t *)p == dc->lastseen_uuid) {
	if(debug>0) mylog("%s stale. already seen (0x%08x) drop\n",dc->id,dc->lastseen_uuid);
	dc->inuse = 0;
	continue;
      }
      dc->lastseen_uuid = *(uint32_t *)p;
      
      dc->rxack = p[5]; // p[3];
      
      uint32_t lst=((*(uint32_t *)p)&0x0000FFFF);
      if((int)(lst - dc->lastseen_time) == 0) {
	if(debug>0) mylog("%s stale data. drop\n",dc->id);
	dc->inuse = 0;
	continue;
      }
      dc->lastseen_time = lst;
      
      if(dc->ack == p[4]) { // recv seq=p[2], we have already ack'd dc->ack.
	if(debug>0) mylog("%s seen packet %d. drop\n",dc->id,dc->ack);
	dc->inuse = 0;
	continue;
      }
      dc->ack = p[4];  // p[2] new data recieved!
            
      //if(debug>0) mylog("\n");
      
      if(mydf < SMS4IOT_FRESHENESS) {
        if(debug>1) mylog("%s Got fresh sensor data. nmsg=|%s|\n",dc->id,dc->nmsg?dc->nmsg:"NULL");
        dc->n = 0;
	/* 
	 * fresh sensor data indicates last exchange was good, so:
	 *  if no .out file, dc->v3_msg = dc->v3_dmsg (default) else
	 *  dc->v3_msg = contents of .out file and remove file.
	 *     OR
	 *  leave .out file in place until the response we get 
	 *  indicates change request sucessful. Then remove.
	 */
	readparsemsg(dc);
        if(dc->nmsg) dc->v3_msg = dc->nmsg; else dc->v3_msg = dc->v3_dmsg;
      }

      p += 6;
      n -= 6;

    } else {
      if(debug>0) mylog("error: %s unsupported proto\n",dc->id);
      dc->inuse = 0;
      continue;      
    }
    
    f_status[0] = '\0';
    //f_ssids[0] = '\0';
    //f_sensors[0] = '\0';

    while(n > 2) { // |ietype(2)|data|
      
      switch(*(uint16_t *)p) {
      case IE_STATUS: // data=|internet connected yes/no(4)|ip(4)|mask(4)|gw(4)|dns(4)|ssid(32)|0(1)|
	if(debug>0) mylog("%s IE_STATUS\n",dc->id);
	if(n < 20 + 32 + 1 + 2) { mylog("error: %s truncated IE_STATUS\n",dc->id); goto badpacket; }
	{
	  uint32_t stt=PTR232BITHOST(p+2);	  
	  if(debug>0) mylog("Local IP:%u.%u.%u.%u WiFi:%s Relay:%s\n",
		 p[6],p[7],p[8],p[9],p[22]?(char *)&p[22]:"",(stt&DEV_RELAY_ON)?"ON":"OFF");
	  
	}
	p += 20 + 32 + 1 + 2;
	n -= 20 + 32 + 1 + 2;
	break;

      case IE_STATUS2:
	if(debug>0) mylog("%s IE_STATUS2\n",dc->id);
	if(n < 8 + 4 + 8 + 8 + 8 + 4 + 4 + 32 + 2) { mylog("error: %s truncated IE_STATUS2\n",dc->id); goto badpacket; }
	// |ver(8)|statflg(4)|bssid(6)|rssi(2)|bssid(6)|rssi(2)|bssid(6)|rssi(2)|uptime(4)|vbat(4)|curssid(32)|
	{
	  uint32_t stt=PTR232BITHOST(p+2+8); // statflg(4)
	  char *qq,llbuf[34];
	  uint8_t *mr,i;
	  qq = f_status;
	  memcpy(llbuf,&p[2+8+4+8+8+8+4+4],32); // curssid(32)
	  llbuf[32] = '\0';
	  qq += sprintf(qq,"%.8s %s %s ssid:%s ", // %.8s limits ver to 8 chars
			p+2, // ver(8)
			(stt&DEV_RELAY_ON)?"ON":"OFF",
			(stt&DEV_INPIN_HIGH)?"H":"L",
			llbuf);
	  stt = *(uint32_t *)&p[2+8+4+8+8+8]; // uptime(4)
	  if(stt > 86400) sprintf(llbuf,"%d:days %d:hrs",(stt/86400),(stt%86400)/3600);
	  else sprintf(llbuf,"%d:hrs %d:mins",stt/3600,(stt%3600)/60);
	  qq += sprintf(qq,"uptime:%s\n",llbuf);

	  if((PTR232BITHOST(p+2+8)&DEV_BSSID_HISTORY)) {
	    mr = &p[2+8+4];
	    qq += sprintf(qq,"%u %02x%02x",*(uint32_t *)mr,mr[4],mr[5]);
	    mr += 4 + 2;
	    for(i=0;i<3;i++) {
	      qq += sprintf(qq," %02x:%02x:%02x:%02x:%02x:%02x",
			    mr[0],mr[1],mr[2],mr[3],mr[4],mr[5]);
	      mr += 6;
	    }
	    qq += sprintf(qq,"\n");
	  } else {
	    mr = &p[2+8+4];
	    for(i=0;i<3;i++) {
	      qq += sprintf(qq,"%02x:%02x:%02x:%02x:%02x:%02x %d\n",
			    mr[0],mr[1],mr[2],mr[3],mr[4],mr[5],*(int16_t *)&mr[6]);
	      mr += 8;
	    }
	  }
	  if(debug>0) mylog("%s\n",f_status);
	}
	p += 8+ 4 + 8 + 8 + 8 + 4 + 4 + 32 + 2;
	n -= 8+ 4 + 8 + 8 + 8 + 4 + 4 + 32 + 2;
	break;

      case IE_SENSORDATA: // |seqcnt|type|len|data|...|type|len|data|
	if(debug>0) mylog("%s:rx IE_SENSORDATA %db left. state:%d\n",dc->id,n,dc->state);
	/*
	 * output file for HA:
	 temperature=22.5
	 humidity=58
	 voltage=3.27
	 uptime=86400
	 relay=on
	 ssid=HomeWifi
	 dn=3.xl.nl
	*/
	char llbuf[256];
	p += 2;
	n -= 2;

	if(n < 1) { mylog("error: %s truncated sensordata\n",dc->id); goto badpacket; }
	int sn = (int)*p++;
	n--;
	while(sn > 0) {
	  int type,len;
	  if(n < 1) { mylog("error: %s truncated sensor type\n",dc->id); goto badpacket; }
	  if((*p)&0x80) {
	    int i = (int)((*p)&0x7F);
	    p++;
	    n--;
	    if(i == 1) {
	      if(n < 1) { mylog("%s error: truncated sensor type\n",dc->id); goto badpacket; }
	      type = (int)*p;
	      p++;
	      n--;
	    } else if(i == 2) {
	      if(n < 2) { mylog("%s error: truncated sensor type\n",dc->id); goto badpacket; }
	      type = (int)(((*p)<<8)|(*(p+1)));
	      p += 2;
	      n -= 2;
	    } else {
	      mylog("%s error bad type\n",__func__);
	      goto badpacket;
	    }
	  } else {
	    type = (int)(*p);
	    p++;
	    n--;
	  }
	  if(n < 1) { mylog("%s error: truncated sensor len\n",dc->id); goto badpacket; }
	  if((*p)&0x80) {
	    int i = (int)((*p)&0x7F);
	    p++;
	    n--;
	    if(i == 1) {
	      if(n < 1) { mylog("%s error: truncated sensor len\n",dc->id); goto badpacket; }
	      len = (int)*p;
	      p++;
	      n--;
	    } else if(i == 2) {
	      if(n < 2) { mylog("%s error: truncated sensor len\n",dc->id); goto badpacket; }
	      len = (int)(((*p)<<8)|(*(p+1)));
	      p += 2;
	      n -= 2;
	    } else {
	      mylog("%s error: bad length\n",__func__);
	      goto badpacket;
	    }
	  } else {
	    len = (int)(*p);
	    p++;
	    n--;
	  }
	  if(len < 0 || len > n) {
	    mylog("%s error: sensor len %d exceeds %d bytes remaining\n",dc->id,len,n);
	    goto badpacket;
	  }
	  if(len >= (int)sizeof(llbuf)) {
	    mylog("%s error: sensor len %d too large\n",dc->id,len);
	    goto badpacket;
	  }
	  {
	    int need = 0;
	    switch(type) {
	    case SENSORDATA_TEMPERATURE: case SENSORDATA_HUMIDITY: need = 1; break;
	    case SENSORDATA_VBAT: need = 2; break;
	    case SENSORDATA_TIMESTAMP: case SENSORDATA_FLAGSM1: case SENSORDATA_UPTIME:
	    case SENSORDATA_POWER: case SENSORDATA_CURRENT: case SENSORDATA_VOLT: need = 4; break;
	    }
	    if(len < need) {
	      mylog("%s error: sensor type %d len %d too short\n",dc->id,type,len);
	      goto badpacket;
	    }
	  }	  
	  switch(type) {
	  case SENSORDATA_TEMPERATURE:
	    sprintf(llbuf,"%d",(int)(*p) - 50);
	    update_devid_entry(dc,"temperature",llbuf);
	    //mqtt_devdata(dc,"temperature",llbuf);
	    //mylog(" Temp=%sC\n",llbuf);
	    break;
	  case SENSORDATA_HUMIDITY:
	    sprintf(llbuf,"%d",(int)(*p));
	    update_devid_entry(dc,"humidity",llbuf);
	    //mqtt_devdata(dc,"humidity",llbuf);
	    //mylog(" Humidity=%s%%\n",llbuf);
	    break;
	  case SENSORDATA_VBAT:
	    //sprintf(llbuf,"%d",*(int16_t *)p);
	    sprintf(llbuf,"%f", 2.02*((float)*(int16_t *)p)/1000.0); // 100kx2 /2 and 12db atten
	    update_devid_entry(dc,"adc",llbuf);
	    //mqtt_devdata(dc,"adc",llbuf);
	    //mylog(" V=%smV\n",llbuf);
	    break;	    
	  case SENSORDATA_TIMESTAMP:
	    {
	      time_t ts=(time_t)(*(uint32_t *)p);
	      sprintf(llbuf,"%s",ctime(&ts));
	    }
	    //update_devid_entry(dc->id,"timestamp",llbuf);	    
	    //mqtt_devdata(dc,"tstamp",llbuf);
	    //mylog(" TS=%s",llbuf);
	    break;
	  case SENSORDATA_CURSSID:
	    memcpy(llbuf,p,len);
	    llbuf[len] = '\0';
	    update_devid_entry(dc,"wifi",llbuf);
	    //mqtt_devdata(dc,"ssid",llbuf);
	    //mylog(" SSID=%s\n",llbuf);
	    break;
	  case SENSORDATA_FLAGSM1:
	    {
	      uint32_t stt=*(uint32_t *)p;
	      update_devid_entry(dc,"relay",(stt&DEV_RELAY_ON)?"on":"off");
	      //mqtt_devdata(dc,"state",(stt&DEV_RELAY_ON)?"ON":"OFF");
	      update_devid_entry(dc,"button",(stt&DEV_BUTTON_PUSHED)?"PUSHED":"RELEASED");
	      update_devid_entry(dc,"inpin",(stt&DEV_INPIN_HIGH)?"H":"L");
	      sprintf(llbuf,"%u",(stt>>24));
	      update_devid_entry(dc,"resets",llbuf);

#ifdef FOOP	      
	      FILE *fq;
	      sprintf(llbuf,"%s/%s.out",DEVTELDIR,dc->id);
	      if((fq=fopen(llbuf,"r"))) {
		char rlbuf[128];
		rlbuf[0] = '\0';
		fgets(rlbuf,sizeof(rlbuf),fq);
		cleanup(rlbuf);
		fclose(fq);
		// opposite of file - toggle
		if(strcmp(rlbuf,"ON") == 0) {
		  if((stt&DEV_RELAY_ON) == 0) {
		    unlink(llbuf);
		    mylog("%s: removed |%s|\n",dc->id,llbuf);
		  }
		} else {
		  if((stt&DEV_RELAY_ON)) {
		    unlink(llbuf);
		    mylog("%s: removed |%s|\n",dc->id,llbuf);
		  }
		}
	      }
	      if((stt&DEV_BUTTON_PUSHED) && dc->otherid[0]) {
		sprintf(llbuf,"%s/%s.out",DEVTELDIR,dc->otherid);
		fq = fopen(llbuf,"r");
		if(fq) {
		  fclose(fq);
		} else {
		  mylog("%s: Try to -> %s\n",dc->id,dc->otherid);
		  devidcb *dco;
		  if( (dco=devid_find(dc->otherid,0)) && (fq=fopen(llbuf,"w"))) {
		    // inverse logic due to toggle
		    fprintf(fq,"%s\n",(dco->laststt&DEV_RELAY_ON)?"ON":"OFF");
		    fclose(fq);
		    
		    dco->yowi = (dco->yown - 1); // skips current paragraph
		    doasendto(dco); // try to send immediately to other device
		    
		    mylog("%s: Created |%s|=%s\n",dc->id,llbuf,(dco->laststt&DEV_RELAY_ON)?"ON":"OFF");
		  }
		}
	      }
#endif // FOOP	  
	    }
	    break;	    
	  case SENSORDATA_UPTIME:
	    //sprintf(llbuf,"%u",*(uint32_t *)p);
	    tconvert(llbuf,*(uint32_t *)p,1); // hr min or day hr
	    //sprintf(llbuf,"%.5f",(double)(*(uint32_t *)p)/86400.0); // duration in fractions of a day
	    //mqtt_devdata(dc,"uptime",llbuf);
	    update_devid_entry(dc,"uptime",llbuf);
	    break;
	    
	  case SENSORDATA_VERSION:
	    memcpy(llbuf,p,len);
            llbuf[len] = '\0';
	    update_devid_entry(dc,"version",llbuf);
	    // save locally
	    if(dc->swver) {
	      if(strcmp(dc->swver,llbuf)) { // update device config data
		myfree(dc->swver);
		dc->swver = mystrdup(llbuf);
		
	      }
	    } else {
	      dc->swver = mystrdup(llbuf);
	      
	    }
	    break;

          case SENSORDATA_DEVNAME:
            memcpy(llbuf,p,len);
            llbuf[len] = '\0';
            //mqtt_devdata(dc,"devname",llbuf);
	    update_devid_entry(dc,"devname",llbuf);
            //mylog(" DEVNAME=%s\n",llbuf);
            break;
	    
         case SENSORDATA_POWER:
            sprintf(llbuf,"%.2f",*(float *)p);
	    //mqtt_devdata(dc,"power",llbuf);
	    update_devid_entry(dc,"power",llbuf);
            //mylog(" AC=%sW\n",llbuf);
            break;
          case SENSORDATA_CURRENT:
            sprintf(llbuf,"%.2f",*(float *)p);
	    //mqtt_devdata(dc,"current",llbuf);
	    update_devid_entry(dc,"current",llbuf);
            //mylog(" AC=%sA\n",llbuf);
            break;
          case SENSORDATA_VOLT:
            sprintf(llbuf,"%.2f",*(float *)p);
	    //mqtt_devdata(dc,"voltage",llbuf);
	    update_devid_entry(dc,"voltage",llbuf);
            //mylog(" AC=%sV\n",llbuf);
            break;
	    
	    
	  default:
	    mylog("error: Unk type=0x%x len=%d\n",type,len);
	    rdump(p,len);
	    break;
	  }	  
	  p += len;
	  n -= len;
	  sn--;
	}
	//sprintf(llbuf,"%u",(uint32_t)time(NULL));
	//tconvert(llbuf,(uint32_t)time(NULL),0);
	pytconvert(llbuf,(uint32_t)time(NULL));
	//mqtt_devdata(dc,"lastseen",llbuf);
	update_devid_entry(dc,"lastseen",llbuf);

	if(dc->hadata) {
	  if(debug>0) mylog("%s:HA=%s\n",dc->id,dc->hadata);
	  sprintf(lbuf,"%s/%s.tmp",MYDATADIR,dc->id);
	  if((fp=fopen(lbuf,"w"))) {
	    fprintf(fp,"%s",dc->hadata);
	    fclose(fp);
	    char *_tf=(char *)mymalloc(strlen(lbuf)+16);
	    sprintf(_tf,"%s/%s",MYDATADIR,dc->id);
	    rename(lbuf,_tf); // writes over old
	    myfree(_tf);
	  }
	  myfree(dc->hadata);
	  dc->hadata = NULL;
	}
	break;
		
#ifdef FOOP
      case IE_SENSORSTAT: // resp=|n(2)|d1(4)|...|dn(4)|
	if(debug>0) mylog("%s IE_SENSORSTAT\n",dc->id);
	p += 2; n -= 2;
	{
	  int k,i;
	  f_sensors[0] = '\0';
	  k = PTR216BITHOST(p); p += 2; n -= 2;
	  if(debug>0) mylog("m=%d\n",k);
	  for(i=0;i<k;i++) {
	    sprintf(&f_sensors[strlen(f_sensors)],"%d,",PTR232BITHOST(p));
	    if(debug>0) mylog("%d,",PTR232BITHOST(p));
	    if(mydf < SMS4IOT_FRESHENESS) {	
	      int vn;
	      char *fn;
	      vn = PTR232BITHOST(p);
	      if(vn > 0x7FFFFFF0) continue; // skip init-value
	      // bme680 temp,hum,pressure,res,iaq-acc,iaq,iaq-static,co2,voc, pm1.0,2.5,10 
	      switch(i) {
	      case 0: vn = 320 + (vn*9)/5; fn="bme680.temperature"; break; // x10 This is NOT iaqSensor.rawTemperature
	      case 1: fn="bme680.humidity"; break; // iaqSensor.humidity
	      case 2: vn = vn/100; fn="bme680.pressure"; break; // x100
	      case 3: fn="bme680.resistance"; break; // iaqSensor.pressure/100
	      case 4: fn="bme680.iaq_accuracy"; break; // iaqSensor.iaqAccuracy
	      case 5: fn="bme680.iaq"; break; // iaqSensor.iaq
	      case 6: fn="bme680.iaq_static"; break; // iaqSensor.staticIaq
	      case 7: fn="bme680.co2"; break; // iaqSensor.co2Equivalent
	      case 8: fn="bme680.voc"; break; // iaqSensor.breathVocEquivalent
	      default:
		if(k > 10) {
		  switch(i) {
		  case 9: fn="pms7003pm1d0"; break;
		  case 10:fn="pms7003pm2d5"; break;
		  case 11:fn="pms7003pm10"; break;
		  case 12: fn="sms4iot.cnt"; break;
		  default: fn=NULL; break;
		  }
		} else {
		  if(i == 9) { fn="sms4iot.cnt"; break; }
		  else fn=NULL; break;
		}
	      }
            }
	    p += 4;
	    n -= 4;
	  }
	  if(debug>0) mylog("%s\n",f_sensors);
	}
	break;
      case IE_WIFISET: // data=|ssid(32)|0(1)|bssid(6)|rssi(1)|
	if(debug>0) mylog("%s IE_WIFISET\n",dc->id);
	p[32+2] = '\0';
	if(debug>0) mylog("\"%s\",%02X:%02X:%02X:%02X:%02X:%02X,%d;\n",
		p+2,
		p[35],p[36],p[37],p[38],p[39],p[40],
		((int)p[41]) - 200
		);
	p += 40 + 2;
	n -= 40 + 2;
	break;
      case IE_WIFISCAN: // data=|number(1)|current yes/no(1)|rssi(1)|ssid(32)|0(1)|
	if(debug>0) mylog("%s IE_WIFISCAN\n",dc->id);
	p[35+2] = '\0'; // make sure its terminated
	if(p[2] == 0) { // first one. restart new list
	  f_ssids[0] = '\0';
	}
	sprintf(lbuf,"%s%s,%d;",p[3]?"*":"",&p[5],((int)p[4]) - 200);
	p += 36 + 2;
	n -= 36 + 2;
	if((sizeof(f_ssids) - strlen(f_ssids)) < (strlen(lbuf) + 4) ) {
	  mylog("wifi list too long\n");
	  break;
	}
	//mylog("ADD |%s|\n",lbuf);
	strcat(f_ssids,lbuf); // check for dups?
	if(debug>0) mylog("%s\n",f_ssids);
	break;
      case IE_WIFISCANSSID: // resp=|N(1)|ssid1|0(1)|ssid2|0(1)|..ssidN|0(1)|
	if(debug>0) mylog("%s IE_WIFISCANSSID\n",dc->id);
	{
	  int i,j,mn;
	  mn = (uint8_t)p[2];
	  p += 3;
	  n -= 3;
	  f_ssids[0] = '\0';
	  for(i=0;i<mn;i++) {
	    j = strlen(p);
	    if(debug>0) mylog("SSIDLIST |%s|\n",p);
	    sprintf(lbuf,"%s,-;",p);
	    strcat(f_ssids,lbuf);
	    p += (j+1);
	    n -= (j+1);
	  }
	}
	if(debug>0) mylog("%s\n",f_ssids);
	break;
#endif // FOOP

	
      case IE_NULL: // terminate
	if(debug>0) mylog("%s IE_NULL\n",dc->id);
	p += 2;
	n = 0; // n -= 2;
	break;

      default:
	mylog("error: %s Unknown IE=%u\n",dc->id,*(uint16_t *)p);
	n = 0;
	break;
      }
    }
 badpacket:
    dc->inuse = 0;
  }
 endit:
  mylog("Ending...\n");
  //conffiotrunning = 0;
  return -1;
}

static void devid_free(devidcb *dc)
{
  if(dc->id) myfree(dc->id);
  if(dc->swver) myfree(dc->swver);
  if(dc->hadata) myfree(dc->hadata);
  if(dc->v3_k2) myfree(dc->v3_k2);
  if(dc->v3_k1) myfree(dc->v3_k1);

  if(dc->v3_dmsg) myfree(dc->v3_dmsg);
  //if(dc->v3_msg) myfree(dc->v3_msg);
  if(dc->nmsg) myfree(dc->nmsg);
  if(dc->nmsg0) myfree(dc->nmsg0);
  if(dc->out) myfree(dc->out);
  myfree(dc);
}

/*
echo -n '10000110 5.X031.NL 4 CUHYPB6ZD0TU:7J9ZTRDRYQP40L34GG1VFWKB6GL/DAMJ*5NO$LY*PVUJ0IO5JR23C49WLQP05L:51E71UIMDHP3RQ3UL*0I'| sudo ./url2qr -

static const char BASE45_CHARSET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
 */
static uint8_t _C2I[256] = { // orig: static char _C2I[256]
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  36, 255,255,255,  37, 38,255,255, 255,255, 39, 40, 255, 41, 42, 43,
  0,   1,  2,  3,   4,  5,  6,  7,   8,  9, 44,255, 255,255,255,255, 
  
  255, 10, 11, 12,  13, 14, 15, 16,  17, 18, 19, 20,  21, 22, 23, 24, /* uppercase */
  25, 26, 27, 28,  29, 30, 31, 32,  33, 34, 35, 35, 255,255,255,255,
  255, 10, 11, 12,  13, 14, 15, 16,  17, 18, 19, 20,  21, 22, 23, 24, /* lowercase */
  //orig 25, 26, 27, 28,  29, 30, 31, 32,  33, 34, 35, 35, 255,255,255,255,
  25, 26, 27, 28,  29, 30, 31, 32,  33, 34, 35,255, 255,255,255,255,
  
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, 
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
  255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
};
static int base45_decode(unsigned char * dst,size_t *_max_dst_len,const char *src,size_t src_len) {
  size_t out_len = 0, max_dst_len;
  max_dst_len = _max_dst_len  ? *_max_dst_len : src_len;  
  if(dst == NULL && _max_dst_len == NULL) return -2;
  if(src == NULL) return -2;
  if(src_len == 0) src_len = strlen(src);
  for(int i=0;i<src_len;i+=3) {
    int x,a,b;
    if(src_len - i < 2) return -1;
    //if((255 == (a = _C2I[(int)src[i]])) || (255 == (b = _C2I[(int)src[i+1]]))) return -1;
    if((255 == (a = _C2I[(uint8_t)src[i]])) || (255 == (b = _C2I[(uint8_t)src[i+1]]))) return -1;
    x = a + 45 * b;
    if(src_len - i >= 3) {
      //if(255 == (a = _C2I[(int)src[i+2]])) return -1;
      if(255 == (a = _C2I[(uint8_t)src[i+2]])) return -1;
      x += a * 45 * 45;
      if(out_len < max_dst_len && dst) dst[out_len] = x / 256;
      out_len++;
      x %= 256;
    }
    if(out_len < max_dst_len && dst) dst[out_len] = x;
    out_len++;
  }
  if(_max_dst_len) *_max_dst_len = out_len;
  return 0;
}
static void devid_refresh(void)
{
  devidcb *dc;
  FILE *fp;
  char buf[MAXPATHLEN];
  uint8_t k1[64],k2[64];

  // scan MYDEVDIR=sms4iot/devices dir for devid/k2/k1
  DIR *dirp;
  struct dirent *dp;
  if((dirp=opendir(MYDEVDIR)) == (DIR *)0) {
    mylog("Cannot open %s directory\n",MYDEVDIR);
    return;
  }
  while((dp=readdir(dirp))) {
    if(strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0) continue;
    // check if dp->d_name is in the form of a device id
    sprintf(buf,"%s/%s",MYDEVDIR,dp->d_name);
    if((fp=fopen(buf,"r")) == NULL) {
      mylog("error: Cannot open %s\n",buf);
      continue;
    }
    while(fgets(buf,sizeof(buf),fp)) { // id fqdn proto k2|k1
      if(buf[0] == '#') continue; // comment
      char *p,*q,*fqdn,*proto,*defmsg=NULL;
      uint8_t k2k1[128];

      if(buf[strlen(buf) - 1] == 0x0A) buf[strlen(buf) - 1] = '\0';
      
      // devid
      q = buf;
      for(p=q;*p && *p != ' ';p++) ;
      if(*p == '\0') {
	mylog("error: Not enough args in %s\n",dp->d_name);
        continue;
      }
      *p++ = '\0';
      if(strcmp(q,dp->d_name)) { // redundant but check
        mylog("error: dev id and name dont match %s != %s\n",q,dp->d_name);
        continue;
      }
      // fqdn
      q = p;
      for(p=q;*p && *p != ' ';p++) ;
      if(*p == '\0') {
        mylog("error: Not enough args in %s\n",dp->d_name);
        continue;
      }
      *p++ = '\0';
      fqdn = q;
      //mylog("%s fqdn=|%s|\n",dp->d_name,fqdn);
      // proto
      q = p;
      for(p=q;*p && *p != ' ';p++) ;
      if(*p == '\0') {
        mylog("error: Not enough args B in %s\n",dp->d_name);
        continue;
      }
      *p++ = '\0';
      proto = q;
      if(atoi(proto) != ASCON_TYPE_DATA) {
        mylog("error: Unsupported protocol %s in %s\n",proto,dp->d_name);
        continue;
      }
      // k2k1 64 bytes base45 encoded
      q = p; // since base45, may start with " " space and have many in btwn
      size_t no = sizeof(k2k1);
      if(base45_decode(k2k1,&no,q,strlen(q)) || no != 64) {
	mylog("error: Failed to decode k2k1 in %s (%d bytes)\n",dp->d_name,no);
	mylog("error: %d|%s|\n",strlen(q),q);
	rdump(q,strlen(q));
        continue;
      }
      memcpy(k2,k2k1,32);
      memcpy(k1,k2k1+32,32);
      //rdump(k2k1,64);
      
      if((dc=devid_find(dp->d_name,1)) == NULL) {
	mylog("error: %s bad or in use.\n",dp->d_name);
	continue;
      }
      mymutex(&mem_mutex,1);
      if(dc->inuse) {
	mymutex(&mem_mutex,0);
	mylog("error: %s in use by other thread. Skip\n",dc->id);
	dc->dflg = 3; // we saw it. but deal with it on next refresh
	continue;
      }
      dc->inuse = 6;
      mymutex(&mem_mutex,0);
      
      dc->protoversion = atoi(proto);
      dc->dflg = 3; // mark temporaily as good here for drop below
      if(dc->v3_k2) { myfree(dc->v3_k2); dc->v3_k2 = NULL; }      
      dc->v3_k2 = mymemdup(k2,32);
      if(dc->v3_k1) { myfree(dc->v3_k1); dc->v3_k1 = NULL; }      
      dc->v3_k1 = mymemdup(k1,32);
      if(dc->v3_dmsg) {
	if(dc->v3_msg == dc->v3_dmsg) dc->v3_msg = NULL;
	myfree(dc->v3_dmsg); dc->v3_dmsg = NULL;
      }
      if(defmsg) dc->v3_dmsg = mystrdup(defmsg);
      else {
	char lbuf[64];
	sprintf(lbuf,"setmsg2 'hello there %s'",dc->id);
	dc->v3_dmsg = mystrdup(lbuf);
      }
      dc->inuse = 0;
    }
    fclose(fp);
  }
  closedir(dirp);

  // drop ones we did not find in conf file above 
  mymutex(&devid_mutex,1);
  devid_mutex_inuse = 1;
  devidcb *dcn,*dc1;
  dc = dc0; // // current head
  dc1 = NULL; // new head
  while(dc) {
    dcn = dc->next;
    if(dc->dflg != 3 && dc->inuse == 0) { // may have to skip if inuse
      devid_free(dc);
      dc = dcn;
      continue;
    }
    dc->dflg = 0; // reset to normal
    
    dc->next = dc1; // rebuild new head
    dc1 = dc;
    
    dc = dcn;
  }
  dc0 = dc1;
  mymutex(&devid_mutex,0);
  devid_mutex_inuse = 0;
  if(debug>1) mylog("Loading device files done\n");
}

static int devid_comm(devidcb *dc)
{
  int i,nd,nk1time,nk2time,n,nargs;
  uint8_t out[DATA_BUFSIZE];
  char *a0,*args[NARGS];

  if(dc->protoversion == ASCON_TYPE_DATA) {

    mylog("%s rack=%d txseq=%d\n",dc->id,dc->rxack,dc->txseq);
    if(dc->rxack == dc->txseq) {
      
      uint32_t tt=(uint32_t)time(NULL);
      dc->rtt = (int)(tt - dc->lasttx);
      dc->lasttx = tt;
      
      dc->txseq++;
    }
  } else {
    return 0; // unsuported proto
  }
  
  a0 = NULL;

  time_t ltmp;
  time(&ltmp);
  if((int)(ltmp - dc->ttime) < S4ICONFSERVER_SPACING) {
    mylog("error: %s Too frequent tx\n",dc->id);
    return 0;
  }
  if(dc->n) {
    if(debug>1) mylog("%s resend seq=%d\n",dc->id,dc->txseq);
    goto resend;
  }
  
  // setmsg "a message"  OR wifiset login pass
  if(dc->v3_msg == NULL) dc->v3_msg = dc->v3_dmsg; // just in case  
  a0 = mystrdup(dc->v3_msg); // working copy
  { char *p; for(p=a0;*p;p++) if(*p == '\'') *p = '"'; }
  if(debug>0) mylog("%s ===|%s|===\n",dc->id,a0);  
  if((nargs=lparse(a0,args,NARGS,' ')) < 1) {
    mylog("%s error: Not enough args %d\n",dc->id,nargs);
    goto endit;
  }
    
  memset(out,0,sizeof(out));
  nd = 0;
    
  *(uint32_t *)out = dc->protoversion;
  
  nd += 4;
  if(ahex2bin(&out[nd],sizeof(out)-nd,dc->id) != 4) {
    mylog("%s error: bad id\n",dc->id);
    goto endit;
  }
    
  nd += 4;

  nk2time = nd; // time nonce used for k2 scramble
  nd += 4;
  nk1time = nd; // timestamp+txseq used with k1 data()
  nd += 6;
  
  if(strcmp(args[0],"wait") == 0) { // do nothing but query for any data R
    if(debug>0) mylog("%s %s\n",dc->id,args[0]);
    n = 0;
    goto queryonly;
  } else if(strcmp(args[0],"status") == 0) {
    if(debug>0) mylog("%s %s\n",dc->id,args[0]);
    SET16BIT(&out[nd],IE_STATUS); // req=||
    n = 2;
  } else if(strcmp(args[0],"sensorstat") == 0) {
    if(debug>0) mylog("%s %s\n",dc->id,args[0]);
    SET16BIT(&out[nd],IE_SENSORSTAT); 
    n = 2;
  } else if(strcmp(args[0],"wifiset") == 0 && nargs > 2) { // .. ssid pass
    if(debug>0) mylog("%s %s (%s,%s)\n",dc->id,args[0],args[1],args[2]);
    SET16BIT(&out[nd],IE_WIFISET); // req=|ssid(32)|0(1)|pass(32)|0(1)|
    memcpy(&out[nd+2],args[1],min(32,strlen(args[1])));
    memcpy(&out[nd+2+32+1],args[2],min(32,strlen(args[2])));
    n = 2 + 32 + 1 + 32 + 1;
  } else if(strcmp(args[0],"loginset") == 0 && nargs > 1) { // .. login
    if(debug>0) mylog("%s %s (%s)\n",dc->id,args[0],args[1]);
    SET16BIT(&out[nd],IE_LOGINSET); // req=|pass(32)|0(1)|    
    memcpy(&out[nd+2],args[1],min(32,strlen(args[1])));
    n = 2 + 32 + 1;
  } else if(strcmp(args[0],"setmsg") == 0 && nargs > 1) { // .. msg
    if(debug>0) mylog("%s %s (%s)\n",dc->id,args[0],args[1]);
    SET16BIT(&out[nd],IE_SETMSG); // req=|msg(32)|0(1)|
    memcpy(&out[nd+2],args[1],min(32,strlen(args[1])));
    n = 2 + 32 + 1;
  } else if(strcmp(args[0],"setmsg2") == 0 && nargs > 1) { // .. msg
    if(debug>0) mylog("%s %s (%s)\n",dc->id,args[0],args[1]);
    SET16BIT(&out[nd],IE_SETMSG2); // req=|msg(64)|0(1)|
    memcpy(&out[nd+2],args[1],min(64,strlen(args[1])));
    n = 2 + 64 + 1;    
  } else if(strcmp(args[0],"wifiscan") == 0) {
    if(debug>0) mylog("%s %s\n",dc->id,args[0]);
    //SET16BIT(&out[nd],IE_WIFISCAN); // req=||
    SET16BIT(&out[nd],IE_WIFISCANSSID); // req=||    
    n = 2;
  } else if(strncmp(args[0],"relay",5) == 0) {
    if(debug>0) mylog("%s %s\n",dc->id,args[0]);
    SET16BIT(&out[nd],IE_RELAY); // req=|onoff(1)|
    out[nd+2] = (args[0])[5];
    n = 1 + 2;
  } else {
    dc->n = 0; // clear retransmit  
    if(debug>0) mylog("%s error: Unknown cmd(%d args): |%s|%s|%s|\n",dc->id,nargs,
		      args[0],args[1]?args[1]:"-",args[2]?args[2]:"-");
    goto endit;
  }

  *(uint32_t *)&out[nk1time] = (uint32_t)time(NULL);
  if(dc->protoversion == ASCON_TYPE_DATA) {
    out[nk1time+4] = dc->txseq;
    out[nk1time+5] = dc->ack;
    n += 6;
  } else {
    mylog("%s error: could not encode: unsupported prot version=%d\n",dc->id,dc->protoversion);
    goto endit;
  }
  
  SET16BIT(&out[nk1time+n],IE_NULL); // Terminate
  n += 2;

  if(debug>0) mylog("%s new send seq=%u\n",dc->id,dc->txseq);
  //rdump(&out[nk1time],n);

  //mylog("+++ %s:curkey=\n",__func__); rdump(curkey,32);

  if(dc->protoversion == ASCON_TYPE_DATA) {
    n = asconencryptinplace(&out[nk1time],n,dc->v3_k1);
  } else {
    mylog("%s error: could not encrypt: unsupported prot version=%d\n",dc->id,dc->protoversion);
    goto endit;
  }
  if(n <= 0) {
    mylog("%s error: could not encrypt\n",dc->id);
    goto endit;
  }

  //rdump(&out[nk1time],n);
  //mylog("send BB\n");
  
 queryonly:  
  dc->sendcnt = 0;
  dc->n = n;
  dc->nk2time = nk2time;
  dc->nk1time = nk1time;
  i = 4 + 4 + 4 + n + 12;
  //mylog("send BB i=%d\n",i);
  if(dc->out) myfree(dc->out);
  //dc->out = mymalloc(i+64); // add workspace
  //memcpy(dc->out,out,i);
  dc->out = mymemdup(out,i);

  //mylog("send CC\n");

 resend:
  n = dc->n;
  nk1time = dc->nk1time;
  nk2time = dc->nk2time;
  i = 4 + 4 + 4 + n + 12;
  //memset(out,0,sizeof(out));
  memcpy(out,dc->out,i); // sizeof out > dc->out since we use high out for hash workspace.

  if(dc->sendcnt++ >= 9) {
    // clear retransmit
    dc->n = 0;
    mylog("%s error: could not communicate (proto:%d)\n",dc->id,dc->protoversion);
    goto endit;
  }

  *(uint32_t *)&out[nk2time] = (uint32_t)time(NULL);
  memcpy(&out[nk1time+n],dc->v3_k2,32); // append hash to data sizeof out > dc->out
  if(dc->protoversion == ASCON_TYPE_DATA) {
    asconhash(&out[nk1time+n],&out[nk2time],n+4+32);
  } else {
    mylog("%s error: do not support proto:%d)\n",dc->id,dc->protoversion);
    goto endit;
  }
  //mylog("sendto len=%d\n",i);
  mylog("%s tx:proto=%d seq=%u ack=%u n=%d rtt=%ds\n",dc->id,dc->protoversion,dc->txseq,dc->ack,i,dc->rtt);
  //rdump(out,i);
  sendto(conf_sock,out,i,0,(struct sockaddr *)&conf_addr,sizeof(conf_addr));
  dc->ttime = (uint32_t)time(NULL);
  //sleep(3);
  // timeout?
  
 endit:
  if(a0) myfree(a0);
  return 0;
}

static void readconfigfile(void)
{
  FILE *fp;
  char buf[1024];

  LOGFILE_MAXSIZE = LOGFILE_MAXSIZE_DEFAULT;
  LOGFILE_MAXNUMBER = LOGFILE_MAXNUMBER_DEFAULT;
  strcpy(S4ICONFSERVER,S4ICONFSERVER_DEFAULT);
  S4ICONFSERVERPORT = S4ICONFSERVERPORT_DEFAULT;
  sprintf(MYDEVDIR,"%s/devices",CURDIR);
  sprintf(MYDATADIR,"%s/data",CURDIR);
  S4ICONFSERVER_SPACING = S4ICONFSERVER_SPACING_DEFAULT;
  if((fp=fopen(MYCONFFILE,"r"))) {
    int n;
    char *args[NARGS];
    while(fgets(buf,sizeof(buf),fp)) {
      if(buf[0] == '#' || buf[0] == ';') continue;
      n = lparse(buf,args,NARGS,':');
      if(n < 2) continue;
      if(strcmp(args[0],"S4ICONFSERVER") == 0) {
        strncpy(S4ICONFSERVER,args[1],sizeof(S4ICONFSERVER)-1);
	S4ICONFSERVER[sizeof(S4ICONFSERVER)-1] = '\0';
      } else if(strcmp(args[0],"S4ICONFSERVERPORT") == 0) {
        S4ICONFSERVERPORT = atoi(args[1]);
      } else if(strcmp(args[0],"S4ICONFSERVER_SPACING") == 0) {
	S4ICONFSERVER_SPACING = atoi(args[1]);
      } else if(strcmp(args[0],"debug") == 0) {
        debug = atoi(args[1]);
      } else if(strcmp(args[0],"LOGFILE_MAXSIZE") == 0) {
        LOGFILE_MAXSIZE = atoi(args[1]);
      } else if(strcmp(args[0],"LOGFILE_MAXNUMBER") == 0) {
        LOGFILE_MAXNUMBER = atoi(args[1]);
      } else if(strcmp(args[0],"MYDEVDIR") == 0) {
        strncpy(MYDEVDIR,args[1],sizeof(MYDEVDIR)-1);
	MYDEVDIR[sizeof(MYDEVDIR)-1] = '\0';
      } else if(strcmp(args[0],"MYDATADIR") == 0) {
        strncpy(MYDATADIR,args[1],sizeof(MYDATADIR)-1);
	MYDATADIR[sizeof(MYDATADIR)-1] = '\0';
      } else {
        mylog("Unknown conf item |%s|\n",args[0]);
      }
    }
    fclose(fp);
  } else if((fp=fopen(MYCONFFILE,"w"))) {
    fprintf(fp,"S4ICONFSERVER:%s\n",S4ICONFSERVER);
    fprintf(fp,"S4ICONFSERVERPORT:%d\n",S4ICONFSERVERPORT);
    fprintf(fp,"S4ICONFSERVER_SPACING:%d\n",S4ICONFSERVER_SPACING);
    fprintf(fp,"debug:%d\n",debug);
    fprintf(fp,"LOGFILE_MAXSIZE:%d\n",LOGFILE_MAXSIZE);
    fprintf(fp,"LOGFILE_MAXNUMBER:%d\n",LOGFILE_MAXNUMBER);
    fprintf(fp,"MYDEVDIR:%s\n",MYDEVDIR);
    fprintf(fp,"MYDATADIR:%s\n",MYDATADIR);
    fclose(fp);
  } else {
    mylog("warning: could not read or create config file |%s|\n",MYCONFFILE);
  }
}
int main(int argc,char *argv[])
{
  pthread_t ths[10];

  if(argc > 1) {
    printf("%s\n",VERSION);
    return 0;
  }
  char path[MAXPATHLEN],resolved_path[MAXPATHLEN],*p;
  strcpy(path,argv[0]);
  if(realpath(path,resolved_path) == NULL) {
    printf("error |%s|%s|\n",path,resolved_path);
    return -1;
  }
  
  sprintf(LOGFILE,"%s.log",resolved_path);
  sprintf(MYCONFFILE,"%s.conf",resolved_path);
  strcpy(CURDIR,resolved_path);
  if((p=strrchr(CURDIR,'/'))) *p = '\0';

  flog = fopen(LOGFILE,"a"); // start logging
  readconfigfile();

  mylog("S4ICONFSERVER:%s\n",S4ICONFSERVER);
  mylog("S4ICONFSERVERPORT:%d\n",S4ICONFSERVERPORT);
  mylog("debug:%d\n",debug);
  mylog("LOGFILE_MAXSIZE:%d\n",LOGFILE_MAXSIZE);
  mylog("LOGFILE_MAXNUMBER:%d\n",LOGFILE_MAXNUMBER);
  
  setsid();
  blocksignals(1);

#ifdef FOOP
  pid_t cpid;
  cpid = fork();
  if(cpid == -1) {
    mylog("error %s\n",strerror(errno));
    close(slocal);
    return -1;
  }
  if(cpid == 0) {
    sigignore( SIGCHLD );
    if(setsid() < 0) mylog("error setsid:%s\n",strerror(errno));
    mylog("%s running in background\n",argv[0]);
  } else {
    sleep(1); // give server a chance to startup
    mylog("Forked off pid=%d. Try query.\n",cpid);
    doclientquery(argc,argv);
    return 0;
  }
#endif // FOOP

  if(pthread_create(&ths[0],NULL,(void *)conffiot,NULL) != 0) {
    mylog("error: Could not create config thread: %s\n",strerror(errno));
    return -1;
  }
  mylog("%s\n",VERSION);

  // periodic poll
  devidcb *dc;
  uint8_t tck=0;
  while(1) {
    if(startshutdown) {
      mymutex(&devid_mutex,1); // wait+block all db operations before exit
      devid_mutex_inuse = 1;
      mylog("Shutdown or inactive so exit\n");
      break;
    }
    // scan infile for devid,k2,k1,default_message. send to gw. mqttlistener() may inject message.
    // responses from gw handled by conffiot().
    if((tck++ % 3) == 0) {
      devid_refresh(); // update devid info from file as needed
      if(debug>0) mymemstat();
      logfile_rollover();
    }
    for(dc=dc0;dc;dc=dc->next) {
      
      mymutex(&mem_mutex,1);
      if(dc->inuse) {
	mymutex(&mem_mutex,0);
	mylog("%s In use by other thread (%d). Skip\n",dc->id,dc->inuse);
	continue;
      }
      dc->inuse = 1;
      mymutex(&mem_mutex,0);
      
      devid_comm(dc); // send or resend to s4i device via gw until acked or timed out

      dc->inuse = 0;
    }
    sleep(S4ICONFSERVER_SPACING); // minimum 6sec spacing between requests
  }

  return 0;
}
