/*
 * common.c
 * Copyright (C) 2008 KLab Inc.
 */
#include "makuosan.h"

mopt moption;
mfile *mftop[2] = {NULL,NULL};
mhost *members  = NULL;
int loop_flag   = 1;
struct timeval curtime;
BF_KEY EncKey;

char *opcodestrlist[]={"PING ",
                       "EXIT ",
                       "SEND ",
                       "MD5  ",
                       "DSYNC",
                       "DEL  ",
                       "UNKNOWN"};

uint8_t opcodenumlist[]={MAKUO_OP_PING,
                         MAKUO_OP_EXIT,
                         MAKUO_OP_SEND,
                         MAKUO_OP_MD5,
                         MAKUO_OP_DSYNC,
                         MAKUO_OP_DEL,
                         MAKUO_OPCODE_MAX};

char *sstatestrlist[]={"SEND_STAT   ",
                       "SEND_OPEN   ",
                       "SEND_DATA   ",
                       "SEND_MARK   ",
                       "SEND_CLOSE  ",
                       "SEND_LAST   ",
                       "SEND_ERROR  ",
                       "SEND_BREAK  ",
                       "SEND_UNKNOWN"};

uint8_t sstatenumlist[]={MAKUO_SENDSTATE_STAT,
                         MAKUO_SENDSTATE_OPEN,
                         MAKUO_SENDSTATE_DATA,
                         MAKUO_SENDSTATE_MARK,
                         MAKUO_SENDSTATE_CLOSE,
                         MAKUO_SENDSTATE_LAST,
                         MAKUO_SENDSTATE_ERROR,
                         MAKUO_SENDSTATE_BREAK,
                         MAKUO_STATE_MAX};

char *rstatestrlist[] = {"RECV_NONE    ",
                         "RECV_UPDATE  ",
                         "RECV_SKIP    ",
                         "RECV_OPEN    ",
                         "RECV_MARK    ",
                         "RECV_CLOSE   ",
                         "RECV_IGNORE  ",
                         "RECV_READONLY",
                         "RECV_BREAK   ",
                         "RECV_MD5OK   ",
                         "RECV_MD5NG   ",
                         "RECV_DELETEOK",
                         "RECV_DELETENG",
                         "RECV_OPENERR ",
                         "RECV_READERR ", 
                         "RECV_WRITEERR", 
                         "RECV_CLOSEERR", 
                         "RECV_UNKNOWN"};

uint8_t rstatenumlist[]={MAKUO_RECVSTATE_NONE,
                         MAKUO_RECVSTATE_UPDATE,
                         MAKUO_RECVSTATE_SKIP,
                         MAKUO_RECVSTATE_OPEN,
                         MAKUO_RECVSTATE_MARK,
                         MAKUO_RECVSTATE_CLOSE,
                         MAKUO_RECVSTATE_IGNORE,
                         MAKUO_RECVSTATE_READONLY,
                         MAKUO_RECVSTATE_BREAK,
                         MAKUO_RECVSTATE_MD5OK,
                         MAKUO_RECVSTATE_MD5NG,
                         MAKUO_RECVSTATE_DELETEOK,
                         MAKUO_RECVSTATE_DELETENG,
                         MAKUO_RECVSTATE_OPENERROR,
                         MAKUO_RECVSTATE_READERROR,
                         MAKUO_RECVSTATE_WRITEERROR,
                         MAKUO_RECVSTATE_CLOSEERROR,
                         MAKUO_STATE_MAX};

char *strsstate(uint8_t n)
{
  int i;
  for(i=0;sstatenumlist[i] != MAKUO_STATE_MAX;i++){
    if(sstatenumlist[i] == n){
      break;
    }
  }
  return(sstatestrlist[i]);
}

char *strrstate(uint8_t n)
{
  int i;
  for(i=0;rstatenumlist[i] != MAKUO_STATE_MAX;i++){
    if(rstatenumlist[i] == n){
      break;
    }
  }
  return(rstatestrlist[i]);
}

char *strmstate(mdata *data)
{
  if(data->head.flags & MAKUO_FLAG_ACK){
    return(strrstate(data->head.nstate));
  }
  return(strsstate(data->head.nstate));
}

char *stropcode(mdata *data)
{
  int i;
  for(i=0;opcodenumlist[i] != MAKUO_STATE_MAX;i++){
    if(opcodenumlist[i] == data->head.opcode){
      break;
    }
  }
  return(opcodestrlist[i]);
}

char *strackreq(mdata *data)
{
  char *ack = "ack";
  char *req = "req";
  if(data->head.flags & MAKUO_FLAG_ACK){
    return(ack);
  }
  return(req);
}

int md5sum(int fd, unsigned char *digest)
{
  int  rd;
  char buff[1024];
  MD5_CTX ctx;
  MD5_Init(&ctx);
  while(rd = read(fd, buff, sizeof(buff))){
    if(rd == -1){
      return(-1);
    }
    MD5_Update(&ctx, buff, rd);
  }
  MD5_Final(digest, &ctx);
  return(0);
}

/*
 *  タイムアウト時間が経過しているかどうかを判断する
 *   - 現在時刻がtfからmsec[ms]経過していれば1を返す
 *   - それ以外は0を返す
 */
int mtimeout(struct timeval *tf, uint32_t msec)
{
  struct timeval tv;
  struct timeval tr;
  
  if((tf->tv_sec == 0) && (tf->tv_usec == 0)){
    return(0);
  }
  tv.tv_sec  = msec / 1000;
  tv.tv_usec = (msec % 1000) * 1000;
  timeradd(tf, &tv, &tr);
  return(timercmp(&tr, &curtime, <));
}

/*
 *  現在時刻を取得する
 *  (といってもcurtimeをコピーするだけだけど)
 */
int mtimeget(struct timeval *tv)
{
  memcpy(tv, &curtime, sizeof(curtime));
  return(0);
}

/*
 *  ファイル名がexcludeリストにマッチするかどうかを調べる
 *   - マッチした場合はそのexcludeitem構造体のポインタを返す
 *   - マッチしない場合はNULLを返す
 */
excludeitem *mfnmatch(char *str, excludeitem *exclude)
{
  char *p;
  excludeitem *e;
  for(e=exclude;e;e=e->next){
    p=str;
    while(*p){
      if(strlen(p) < strlen(e->pattern)){
        break;
      }
      if(!fnmatch(e->pattern, p, FNM_PATHNAME)){
        return(e);
      }
      while(*p){
        if(*(p++) == '/'){
          break;
        }
      }
    }
  }
  return(NULL);
}

int isexclude(char *fn, excludeitem *exclude, int dir)
{
  char path[PATH_MAX];
  if(!strcmp(fn, ".")){
    fn = "";
  }
  if(!memcmp(fn, "./", 2)){
    fn += 2;
  }
  while(*fn == '/'){
    fn++;
  }
  if(dir){
    sprintf(path, "%s/%s/", moption.real_dir, fn);
  }else{
    sprintf(path, "%s/%s" , moption.real_dir, fn);
  }
  if(mfnmatch(path, exclude)){
    return(1);
  }
  return(0);
}

void fdprintf(int s, char *fmt, ...)
{
  char m[2048];
  va_list arg;
  if(s != -1){
    va_start(arg, fmt);
    vsprintf(m, fmt, arg);
    va_end(arg);
    write(s, m, strlen(m));
  }
}

void lprintf(int l, char *fmt, ...)
{
  va_list arg;
  struct timeval tv;
  char msg[2048];
  if(moption.loglevel >= l){
    va_start(arg, fmt);
    vsprintf(msg, fmt, arg);
    va_end(arg);
#ifdef MAKUO_DEBUG
    gettimeofday(&tv, NULL);
    fprintf(stderr, "%02d.%06d %s", tv.tv_sec % 60, tv.tv_usec, msg);
#else
    fprintf(stderr, "%s", msg);
#endif
    syslog(LOG_ERR, "%s: %s", moption.user_name, msg);
  }
}

void cprintf(int l, mcomm *c, char *fmt, ...)
{
  char m[2048];
  va_list arg;
  if(!c)
    return;
  if(c->fd[0] == -1)
    return;
  if(c->loglevel >= l){
    va_start(arg, fmt);
    vsprintf(m, fmt, arg);
    va_end(arg);
    write(c->fd[0], m, strlen(m));
    fsync(c->fd[0]);
  }
}

void mprintf(const char *func, mfile *m)
{
  lprintf(9, "%s: rid=%d init=%d wait=%d %s %s %s %s\n",
    func, 
    m->mdata.head.reqid, 
    m->initstate, m->sendwait, 
    inet_ntoa(m->addr.sin_addr), 
    stropcode(&(m->mdata)),
    strmstate(&(m->mdata)),
    m->fn);
}

int getrid()
{
  static int rid=0;
  return(rid++);
}

int workend(mcomm *c)
{
  if(c){
    if(c->working && !c->cpid){
      c->working = 0;
      if(moption.commpass && !c->authchk){
        cprintf(0, c, "password: \x1b]E");
      }else{
        cprintf(0,c,"> ");
        lprintf(1,"mexec: ======= separator =======\n");
      }
    }
  }
  return(0);
}

void mfdel(mfile *m)
{
  mfile *p;
  mfile *n;
  if(m){
    if(p = (mfile *)m->prev)
      p->next = m->next;
    if(n = (mfile *)m->next)
      n->prev = m->prev;
    if(mftop[0] == m)
      mftop[0] = n;
    if(mftop[1] == m)
      mftop[1] = n;
    free(m);
  }
}

mfile *mfnew()
{
  mfile *m;
  if(m = (mfile *)malloc(sizeof(mfile))){
    memset(m, 0, sizeof(mfile));
    m->mdata.head.maddr  = moption.maddr.sin_addr.s_addr;
    m->mdata.head.mport  = moption.maddr.sin_port;
    m->mdata.head.vproto = PROTOCOL_VERSION;
    m->fd   = -1;
    m->pipe = -1;
    m->retrycnt = MAKUO_SEND_RETRYCNT;
    memcpy(&(m->addr), &(moption.maddr), sizeof(m->addr));
  }
  return(m);
}

mfile *mfadd(int n)
{
  mfile *m;
  if(m = mfnew()){
    if(!mftop[n]){
      mftop[n] =m;
    }else{
      mfile *l;
      for(l=mftop[n];l->next;l=l->next);
      l->next = m;
      m->prev = l;
      m->next = NULL;
    }
  }
  return(m);
}

mfile *mfins(int n)
{
  mfile *m;
  if(m = mfnew()){
    if(mftop[n]){
      mftop[n]->prev = m;
      m->next = mftop[n];
    }
    mftop[n] =m;
  }
  return(m);
}

mhost *member_get(struct in_addr *addr)
{
  mhost *t;
  for(t=members;t;t=t->next){
    if(!memcmp(&t->ad, addr, sizeof(t->ad))){
      break;
    }
  }
  return(t); 
}

mhost *member_add(struct in_addr *addr, mdata *data)
{
  int f = 0;
  int l = 0;
  mping *p = NULL;
  mhost *t = member_get(addr);

  if(!t){
    f = 1;
    t = malloc(sizeof(mhost));
    if(!t){
      lprintf(0, "%s: out of memory\n", __func__);
      return(NULL);
    }
    memset(t, 0, sizeof(mhost));
    memcpy(&t->ad, addr, sizeof(t->ad));
    if(members){
      members->prev = t;
      t->next = members;
    }
    members = t;
    t->hostname[0] = 0;
  }
  if(data){
    if(data->head.opcode == MAKUO_OP_PING){
      p = (mping *)data->data;
      l = ntohs(p->hostnamelen);
      data->p = p->data;
      memcpy(t->hostname, data->p, l);
      t->hostname[l] = 0;
      data->p += l;
      l = ntohs(p->versionlen);
      memcpy(t->version,  data->p, l);
      t->version[l] = 0;
    }
  }
  if(f){
    lprintf(0, "%s: %s (%s)\n", __func__, inet_ntoa(t->ad), t->hostname);
  }
  mtimeget(&(t->lastrecv));
  return(t);
}

void member_del(mhost *t)
{
  mhost *p;
  mhost *n;
  if(!t)
    return;
  lprintf(0, "%s: %s (%s)\n", __func__, inet_ntoa(t->ad), t->hostname);
  if(p = (mhost *)t->prev)
    p->next = t->next;
  if(n = (mhost *)t->next)
    n->prev = t->prev;
  if(members == t)
    members = n;
  free(t);
}

mmark *markalloc()
{
  mmark *mm = calloc(1, sizeof(mmark));
  return(mm);

}

void markfree(mmark *mm)
{
  free(mm);
}

mmark *addmark(mmark *mm, uint32_t l, uint32_t h)
{
  mmark *nn = markalloc();
  nn->l = l;
  nn->h = h;
  if(mm){
    nn->next = mm;
    nn->prev = mm->prev;
    mm->prev = nn;
    if(nn->prev){
      nn->prev->next = nn;
    }
  }
  return(nn);
}

mmark *delmark(mmark *mm)
{
  mmark *nn = NULL;
  if(mm){
    if(mm->prev){
      mm->prev->next = mm->next;
    }
    if(mm->next){
      mm->next->prev = mm->prev;
    }
    nn = mm->next;
    markfree(mm);
  }
  return(nn);
}

void seq_addmark(mfile *m, uint32_t l, uint32_t h)
{
  mmark *mm;
  if(!m){
    return;
  }
  if(h == l){
    return;
  }
  m->markcount += (h - l);
  m->mark = addmark(m->mark, l, h);

  int c=0;
  for(mm=m->mark;mm;mm=mm->next){
    c++;
  }
  lprintf(9, "%s: %06d->%06d (%d) markcount=%d marklist=%d %s\n", __func__, l, h, h - l, m->markcount, c, m->fn);
  return;
}

int seq_delmark(mfile *m, uint32_t seq)
{
  uint32_t l;
  uint32_t h;
  mmark  *mm;
  if(!m){
    return(0);
  }

  for(mm=m->mark;mm;mm=mm->next){
    l = mm->l;
    h = mm->h - 1;
    if(seq == l){
      mm->l++;
      if(mm->l == mm->h){
        if(mm == m->mark){
          m->mark = mm->next;
        }
        delmark(mm);
      }
      m->markcount--;
      return(1);
    }
    if(seq == h){
      mm->h--;
      if(mm->l == mm->h){
        if(mm == m->mark){
          m->mark = mm->next;
        }
        delmark(mm);
      }
      m->markcount--;
      return(1);
    }
    if((seq>l) && (seq<h)){
      if(mm == m->mark){
        m->mark = addmark(mm, mm->l, seq);
      }else{
        addmark(mm, mm->l, seq);
      }
      mm->l = seq + 1;
      m->markcount--;
      return(1);
    }
  }
  return(0);
}

void seq_setmark(mfile *m, uint32_t l, uint32_t h)
{
  mmark *mm;
  mmark *mn;

  if(!m){
    return;
  }
  mm = m->mark;
  mn = addmark(NULL, l, h);
  while(mm){
    if((mn->h < mm->l) || (mn->l > mm->h)){
      mm = mm->next;
      continue;
    }
    if(mn->l > mm->l){
      mn->l = mm->l;
    }
    if(mn->h < mm->h){
      mn->h = mm->h;
    }
    if(mm == m->mark){
      m->mark = mm->next;
    }
    mm = delmark(mm);
  }
  if(mn->next = m->mark){
    m->mark->prev = mn;
  }
  m->mark = mn;
  m->markcount = 0;
  for(mm=m->mark;mm;mm=mm->next){
    m->markcount += (mm->h - mm->l);
  }
  lprintf(9, "%s: %06d->%06d (%d) rid=%d %s\n", __func__, l, h, h - l, m->mdata.head.reqid, m->fn);
}

uint32_t seq_getmark(mfile *m)
{
  uint32_t seq;
  if(!m){
    return(0);
  }
  if(!m->mark){
    return(0);
  }
  seq = m->mark->l;
  m->markcount--;
  m->mark->l++;
  if(m->mark->l == m->mark->h){
    m->mark = delmark(m->mark);
  }  
  return(seq);
}

void clr_hoststate(mfile *m)
{
  int i;
  mhost *t;
  for(t=members;t;t=t->next){
    for(i=0;i<MAKUO_PARALLEL_MAX;i++){
      if(t->mflist[i] == m){
        t->mflist[i] = NULL;
        t->state[i]  = 0;
      }
    }
  }
}

void dump_hoststate(mfile *m, char *func)
{
  mhost   *t;
  uint8_t *r;
  for(t=members;t;t=t->next){
    if(r=get_hoststate(t,m)){
      lprintf(9,"%s: %s from %s %s\n", func, strrstate(*r), t->hostname, m->fn);
    }
  }
}

uint8_t *get_hoststate(mhost *t, mfile *m)
{
  int i;
  int r;
  if(!t || !m){
    return(NULL);
  }
  r = -1;
  for(i=0;i<MAKUO_PARALLEL_MAX;i++){
    if(t->mflist[i] == m){
      return(&(t->state[i]));
    }else{
      if((r == -1) && !(t->mflist[i])){
        r = i;
      }
    }
  }
  if(r != -1){
    t->mflist[r] = m;
    return(&(t->state[r]));
  }
  return(NULL);
}

uint8_t *set_hoststate(mhost *t, mfile *m, uint8_t state)
{
  uint8_t *s;
  if(s = get_hoststate(t,m)){
    *s = state;
  }
  return(s);
}

int ack_clear(mfile *m, int state)
{
  uint8_t *s;
  mhost   *t;
  for(t=members;t;t=t->next){
    if(!m->sendto){
      if(s = get_hoststate(t, m)){
        if(state == -1 || *s == state){
          *s = MAKUO_RECVSTATE_NONE;
        }
      }
    }else{
      if(!memcmp(&(m->addr.sin_addr), &(t->ad), sizeof(m->addr.sin_addr))){
        if(s = get_hoststate(t, m)){
          if(state == -1 || *s == state){
            *s = MAKUO_RECVSTATE_NONE;
            return(1);
          }else{
            return(0);
          }
        }
      }
    }
  }
  if(m->sendto)
    return(-1);
  return(0);
}

/* 指定したステータスを持つ応答メッセージがあるかをチェックする
 * 引数  :
 *      m: 送信対象のファイルオブジェクト
 *  state: 検索するステータス
 *
 * 戻り値:
 *      0: 見つからなかった
 *      1: 見つかった
 *     -1: ホスト指定転送なのにホストオブジェクトが見つからない
*/
int ack_check(mfile *m, int state)
{
  uint8_t *s;
  mhost   *t;
  for(t=members;t;t=t->next){
    if(!m->sendto){
      s = get_hoststate(t,m);
      if(!s){
        lprintf(0,"%s: can't get state area host=%s fn=%s\n", 
          __func__,
          t->hostname, 
          m->fn);
      }else{
        if(*s == state){
          return(1);
        }
      }
    }else{
      if(!memcmp(&(m->addr.sin_addr), &(t->ad), sizeof(m->addr.sin_addr))){
        s = get_hoststate(t,m);
        if(!s){
          lprintf(0,"%s: can't get state area host=%s fn=%s\n", 
            __func__,
            t->hostname, 
            m->fn);
        }else{
          if(*s == state){
            return(1);
          }else{
            return(0);
          }
        }
      }
    }
  }
  if(m->sendto)
    return(-1);
  return(0);
}

int linkcmp(mfile *m)
{
  ssize_t size;
  char ln[PATH_MAX];
  if(m){
    size = readlink(m->fn, ln, PATH_MAX);
    if(size != -1 && size < PATH_MAX){
      ln[size] = 0;
      if(!strcmp(m->ln, ln)){
        return(MAKUO_RECVSTATE_SKIP);
      }
    }
  }
  return(MAKUO_RECVSTATE_UPDATE);
}

int statcmp(struct stat *s1, struct stat *s2)
{
  if(s1->st_mtime != s2->st_mtime)
    return(MAKUO_RECVSTATE_UPDATE);
  if(!geteuid() || !getegid()){
    if(s1->st_uid != s2->st_uid){
      return(MAKUO_RECVSTATE_UPDATE);
    }
    if(s1->st_gid != s2->st_gid){
      return(MAKUO_RECVSTATE_UPDATE);
    }
  }
  if((S_ISDIR(s1->st_mode)) && (S_ISDIR(s2->st_mode))){
    if(s1->st_mode != s2->st_mode)
      return(MAKUO_RECVSTATE_UPDATE);
    return(MAKUO_RECVSTATE_SKIP);
  }
  if((S_ISREG(s1->st_mode)) && (S_ISREG(s2->st_mode))){
    if(s1->st_mode != s2->st_mode)
      return(MAKUO_RECVSTATE_UPDATE);
    if(s1->st_size != s2->st_size)
      return(MAKUO_RECVSTATE_UPDATE);
    return(MAKUO_RECVSTATE_SKIP);
  }
  if(s1->st_mode == s2->st_mode){
    if(s1->st_rdev == s2->st_rdev){
      return(MAKUO_RECVSTATE_SKIP);
    }
  }
  return(MAKUO_RECVSTATE_UPDATE);
} 

int is_dir(char *path)
{
  struct stat mstat;
  if(!lstat(path,&mstat))
    return(S_ISDIR(mstat.st_mode));
  return(0);
}

int is_reg(char *path)
{
  struct stat mstat;
  if(!lstat(path,&mstat))
    return(S_ISREG(mstat.st_mode));
  return(0);
}

int set_guid(uid_t uid, gid_t gid, gid_t *gids)
{
  size_t num;

  /*----- setgids -----*/
  if(gids){
    for(num=0;gids[num];num++);
    if(num){
      if(setgroups(num, gids) == -1){
        return(-1);
      }
    }
  }else{
    if(gid != getegid()){
      if(setgroups(1, &gid) == -1){
        return(-1);
      }
    }
  }

  /*----- setgid -----*/
  if(gid != getegid()){
    if(setegid(gid) == -1){
      return(-1);
    }
  }
  /*----- setuid -----*/
  if(uid != geteuid()){
    if(seteuid(uid) == -1){
      return(-1);
    }
  }  
  return(0);
}

int set_gids(char *groups)
{
  char *p;
  size_t num;
  struct group *g;
  char buff[1024];

  num = 0;
  strcpy(buff, groups);
  p = strtok(buff,",");
  while(p){
    p = strtok(NULL,",");
    num++;
  }
  if(moption.gids){
    free(moption.gids);
  }
  moption.gids = malloc(sizeof(gid_t) * (num + 1));
 
  num = 0; 
  strcpy(buff, groups);
  p = strtok(buff,",");
  while(p){
    if(*p >= '0' && *p <= '9'){
      moption.gids[num] = atoi(p);
      if(g = getgrgid(moption.gids[num])){
        strcpy(moption.grnames[num], g->gr_name);
      }
    }else{
      if(g = getgrnam(p)){
        moption.gids[num] = g->gr_gid;
        strcpy(moption.grnames[num], p);
      }
    }
    p = strtok(NULL,",");
    num++;
  }
  moption.gids[num] = 0;
  return(0);
}

void set_filestat(char *path, uid_t uid, gid_t gid, mode_t mode)
{
  struct stat fs;
  if(lstat(path, &fs) == -1){
    return;
  }
  if(fs.st_uid != uid){
    lchown(path, uid, -1);
  }
  if(fs.st_gid != gid){
    lchown(path, -1, gid);
  }
  chmod(path, mode & 07777);
}

void mtempname(char *base, char *fn, char *tn)
{
  struct stat    st;
  struct timeval tv;
  char path[PATH_MAX];
  do{
    gettimeofday(&tv, NULL);
    sprintf(tn, "%s.makuo%03u%03u", fn, getrid() % 1000,  (int)tv.tv_usec);
    sprintf(path, "%s/%s", base, tn);
  }while(lstat(tn, &st) != -1);
}

int mremove(char *base, char *name)
{
  DIR *d;
  struct dirent *dent;
  char path[PATH_MAX];
  if(!base){
    strcpy(path,name);
  }else{
    sprintf(path, "%s/%s", base, name);
  }
  if(is_dir(path)){
    if(d = opendir(path)){
      while(dent=readdir(d)){
        if(!strcmp(dent->d_name, "."))
          continue;
        if(!strcmp(dent->d_name, ".."))
          continue;
        mremove(path, dent->d_name);
      }
      closedir(d);
    }
  }
  return(remove(path));
}

int mrename(char *base, char *oldname, char *newname)
{
  char oldpath[PATH_MAX];
  char newpath[PATH_MAX];
  char tmppath[PATH_MAX];
  sprintf(oldpath,"%s/%s", base, oldname);
  sprintf(newpath,"%s/%s", base, newname);
  mtempname(base, newname, tmppath);
  rename(newpath,tmppath);
  if(rename(oldpath,newpath) == -1){
    rename(tmppath, newpath);
    return(-1);
  }
  mremove(NULL,tmppath);
  return(0);
}

int mcreatedir(char *base, char *name, mode_t mode)
{
  char *p;
  char buff[PATH_MAX];
  char path[PATH_MAX];
  strcpy(buff, name);
  strcpy(path, base);
  p = strtok(buff, "/");
  while(p){
    strcat(path, "/");
    strcat(path, p);
    if(p = strtok(NULL,"/")){
      if(!is_dir(path)){
        remove(path);
        if(mkdir(path,mode) == -1){
          return(-1);
        }
      }
    }
  }
  return(0);
}

int mcreatenode(char *base, char *name, mode_t mode, dev_t dev)
{
  int r = -1;
  mode_t u = umask(0);
  char path[PATH_MAX];
  if(!mcreatedir(base, name, 0755)){
    sprintf(path,"%s/%s",base,name);
    r = mknod(path, mode, dev);
  }
  umask(u);
  return(r);
}

int mcreatefile(char *base, char *name, mode_t mode)
{
  int fd = -1;
  mode_t u = umask(0);
  char path[PATH_MAX];
  if(!mcreatedir(base,name,0755)){
    sprintf(path,"%s/%s",base,name);
    fd = open(path, O_RDWR | O_CREAT | O_TRUNC, mode & 0xFFF);
  }
  umask(u);
  return(fd);
}

int mcreatelink(char *base, char *name, char *link)
{
  char path[PATH_MAX];
  if(!mcreatedir(base,name,0755)){
    sprintf(path,"%s/%s",base,name);
    return(symlink(link,path));
  }
  return(-1);
}

int space_escape(char *str)
{
  int  r = 0;
  char buff[PATH_MAX];
  char *s = str;
  char *d = buff;
  
  while(*s){
    if(*s == ' '){
      r++;
      *(d++) = '\\';
    }
    *(d++) = *(s++);
  }
  *d = 0;
  strcpy(str, buff);
  return(r);
}

void chexit()
{
  struct utsname uts;
  char cwd[PATH_MAX];
  if(moption.chroot){
    if(uname(&uts) == -1){
      return;
    }
    if(strcmp("Linux", uts.sysname)){
      return;
    }
    /*----- chroot exit(linux only) -----*/
    mtempname("",".MAKUOWORK",cwd);
    mkdir(cwd,0700);
    chroot(cwd);
    rmdir(cwd);
    chdir("..");
    getcwd(cwd,PATH_MAX);
    while(strcmp("/", cwd)){
      chdir("..");
      getcwd(cwd,PATH_MAX);
    }
    chroot(".");
  }
  return;
}

void restoreguid()
{
  if(getuid() != geteuid())
    seteuid(getuid());
  if(getgid() != getegid())
    setegid(getgid());
}

mfile *mkreq(mdata *data, struct sockaddr_in *addr, uint8_t state)
{
  mfile *a;
  if(a = mfins(0)){
    a->mdata.head.opcode = data->head.opcode;
    a->mdata.head.reqid  = data->head.reqid;
    a->mdata.head.seqno  = data->head.seqno;
    a->mdata.head.nstate = state;
    memcpy(&(a->addr), addr, sizeof(a->addr));
  }
  return(a);
}

mfile *mkack(mdata *data, struct sockaddr_in *addr, uint8_t state)
{
  mfile *a;
  for(a=mftop[0];a;a=a->next){
    if((a->mdata.head.flags & MAKUO_FLAG_ACK)      &&
       (a->mdata.head.opcode == data->head.opcode) &&
       (a->mdata.head.reqid  == data->head.reqid)  &&
       (a->mdata.head.seqno  == data->head.seqno)  &&
       (a->mdata.head.nstate == state)             &&
       (!memcmp(&(a->addr), addr, sizeof(a->addr)))){
      return(a);
    }
  }
  if(a = mfins(0)){
    a->mdata.head.flags |= MAKUO_FLAG_ACK;
    a->mdata.head.opcode = data->head.opcode;
    a->mdata.head.reqid  = data->head.reqid;
    a->mdata.head.seqno  = data->head.seqno;
    a->mdata.head.ostate = data->head.ostate;
    a->mdata.head.nstate = state;
    a->mdata.head.error  = data->head.error;
    memcpy(&(a->addr), addr, sizeof(a->addr));
  }
  return(a);
}

int atomic_read(int fd, char *buff, int size)
{
  int r;

  while(size){
    r = read(fd, buff, size);
    if(r == -1){
      if(errno == EINTR){
        continue;
      }
      return(-1);
    }
    if(r == 0){
      return(1);
    }
    size -= r;
    buff += r;
  }
  return(0);
}

int data_safeget(mdata *data, void *buff, size_t size)
{
  if(data->p + size > data->data + data->head.szdata){
    return(-1);
  }
  memcpy(buff, data->p, size);
  data->p += size;
  return(0);
}

int data_safeget16(mdata *data, uint16_t *buff)
{
  int r = data_safeget(data, buff, sizeof(uint16_t));
  if(!r){
    *buff = ntohs(*buff);
  }
  return(r);
}

int data_safeget32(mdata *data, uint32_t *buff)
{
  int r = data_safeget(data, buff, sizeof(uint32_t));
  if(!r){
    *buff = ntohl(*buff);
  }
  return(r);
}

int data_safeset(mdata *data, void *buff, size_t size)
{
  if(data->head.szdata + size > MAKUO_BUFFER_SIZE){
    return(-1);
  }
  memcpy(data->data + data->head.szdata, buff, size);
  data->head.szdata += size;
  return(0);
}

int data_safeset16(mdata *data, uint16_t val)
{
  if(data->head.szdata + sizeof(uint16_t) > MAKUO_BUFFER_SIZE){
    return(-1);
  }
  *(uint16_t *)(data->data + data->head.szdata) = htons(val);
  data->head.szdata += sizeof(uint16_t);
  return(0);
}

int data_safeset32(mdata *data, uint32_t val)
{
  if(data->head.szdata + sizeof(uint32_t) > MAKUO_BUFFER_SIZE){
    return(-1);
  }
  *(uint32_t *)(data->data + data->head.szdata) = htonl(val);
  data->head.szdata += sizeof(uint32_t);
  return(0);
}

excludeitem *exclude_add(excludeitem *exclude, char *pattern)
{
  excludeitem *e = malloc(sizeof(excludeitem));
  e->prev = NULL;
  e->next = NULL;
  if(exclude){
    e->next = exclude;
    e->prev = exclude->prev;
    exclude->prev = e;
    if(e->prev){
      e->prev->next = e;
    }
  }
  e->pattern = malloc(strlen(pattern)+1);
  strcpy(e->pattern, pattern);
  return(e);
}

excludeitem *exclude_del(excludeitem *e)
{
  excludeitem *r = NULL;
  excludeitem *p = NULL;
  excludeitem *n = NULL;

  if(!e){
    return(NULL);
  }
  p = e->prev;
  n = e->next;
  if(p){
    p->next=n;
  }
  if(n){
    n->prev=p;
    r = n;
  }
  free(e->pattern);
  e->pattern = NULL;
  e->prev = NULL;
  e->next = NULL;
  free(e);
  return(r);
}