mexec.c
c858e9b6
 /*
  * mexec.c
dbdc4e5a
  * Copyright (C) 2008 KLab Inc.
c858e9b6
  */
 
86badd32
 #include "makuosan.h"
 
642a6cb2
 char *command_list[]={"quit",
                       "exit",
                       "bye",
                       "send",
                       "sync",
                       "dsync",
                       "members",
                       "status",
                       "md5",
                       "check",
                       "echo",
                       "exclude",
                       "loglevel",
                       "help",
                       NULL};
86badd32
 
38f71e58
 mfile *mexec_with_dsync(mcomm *c, char *fn, int dryrun, int recurs, mhost *t)
 {
3b736b7c
   mfile *m = mfadd(MFSEND);
38f71e58
   if(!m){
d38d0833
 	  lprintf(0, "[error] %s: out of memorry\n", __func__);
3b736b7c
 	  cprintf(0, c, "error: out of memorry\n");
38f71e58
     return(m);
 	}
 
   strcpy(m->fn, ".");
   if(fn){
     if(*fn != '/'){
 	    strcat(m->fn, "/");
     }
 	  strcat(m->fn, fn);
   }
   
43a6533f
   strcpy((char *)(m->mdata.data), m->fn);
3b736b7c
   strcpy(m->cmdline, c->cmdline[0]);
38f71e58
 	m->mdata.head.reqid  = getrid();
 	m->mdata.head.szdata = strlen(m->fn);
 	m->mdata.head.opcode = MAKUO_OP_DSYNC;
   m->mdata.head.nstate = MAKUO_SENDSTATE_OPEN;
 	m->comm = c;
   if(dryrun){
     m->dryrun = 1;
     m->mdata.head.flags |= MAKUO_FLAG_DRYRUN;
   }
   if(recurs){
     m->recurs = 1;
     m->mdata.head.flags |= MAKUO_FLAG_RECURS;
   }
   m->initstate = 1;
 
   /*----- send to address set -----*/
   if(t){
     m->sendto = 1;
     memcpy(&(m->addr.sin_addr), &(t->ad), sizeof(m->addr.sin_addr));
   }
   return(m);
 }
 
86badd32
 int mexec_scan_cmd(int fd, char *buff)
 {
   int r;
   int size;
   char *cmd;
   fd_set fds;
   struct timeval tv;
 
   cmd = buff;
   size = strlen(buff);
   while(loop_flag && size){
     tv.tv_sec  = 1;
     tv.tv_usec = 0;
     FD_ZERO(&fds);
     FD_SET(fd,&fds);
     if(select(1024, NULL, &fds, NULL, &tv)<0)
       continue;
     if(FD_ISSET(fd,&fds)){
       r = write(fd, cmd, size);
       if(r == -1){
1151ee4a
         if(errno == EINTR){
           continue;
         }
d38d0833
         lprintf(0, "[error] %s: commend write error (%s) %s", __func__, strerror(errno), buff);
86badd32
         return(-1);
       }
       size -= r;
       cmd  += r;
     }
   }
   return(0);
 }
 
 int mexec_scan_echo(int fd, char *fmt, ...)
 {
   char buff[MAKUO_BUFFER_SIZE];
   char comm[MAKUO_BUFFER_SIZE];
   va_list arg;
   va_start(arg, fmt);
   vsprintf(buff, fmt, arg);
   va_end(arg);
   sprintf(comm, "echo %s\n", buff);
   mexec_scan_cmd(fd, comm);
   return(0);
 }
 
5cf3fe0f
 int mexec_scan_send(int fd, char *path, char *sendhost, int mode, gid_t gid)
86badd32
 {
   char buff[MAKUO_BUFFER_SIZE];
   char comm[MAKUO_BUFFER_SIZE];
 
   buff[0] = 0;
5cf3fe0f
   if((gid != -1) && (mode == MAKUO_MEXEC_SEND)){
     sprintf(buff, "-g %d ", gid);
   }
86badd32
   if(sendhost && *sendhost){
     strcat(buff, " -t ");
     strcat(buff, sendhost);
     strcat(buff, " ");
   }
   switch(mode){
     case MAKUO_MEXEC_SEND:
5cf3fe0f
       sprintf(comm, "send %s%s\r\n", buff, path);
86badd32
       break;
     case MAKUO_MEXEC_DRY:
       sprintf(comm, "send -n %s%s\r\n", buff, path);
       break;
     case MAKUO_MEXEC_MD5:
f9cb6348
       if(!is_reg(path)){
         return(0);
       }
5cf3fe0f
       sprintf(comm, "check %s%s\r\n", buff, path);
86badd32
       break;
   }
69d91074
   return(mexec_scan_cmd(fd, comm));
86badd32
 }
 
5cf3fe0f
 int mexec_scan_dir(int fd, char *base, char *sendhost, int mode, mcomm *c, int baseflag, gid_t gid)
86badd32
 {
69d91074
   int r;
86badd32
   DIR *d;
   struct dirent *dent;
   char path[PATH_MAX];
 
   d = opendir(base);
   if(!d){
     /* directory open error */
     mexec_scan_echo(fd, "directory open error %s", base);
   }else{
     while(dent=readdir(d)){
69d91074
       if(!loop_flag){
         return(1);
       }
       if(!strcmp(dent->d_name, ".")){
86badd32
         continue;
69d91074
       }
       if(!strcmp(dent->d_name, "..")){
86badd32
         continue;
69d91074
       }
86badd32
       if(baseflag){
         sprintf(path, "%s/%s", base, dent->d_name);
       }else{
         strcpy(path, dent->d_name);
       }
       space_escape(path);
69d91074
       if(r = mexec_scan_child(fd, path, sendhost, mode, c, gid)){
         return(r);
       }
86badd32
     }
     closedir(d);
   }
   return(0);
 }
 
5cf3fe0f
 int mexec_scan_child(int fd, char *base, char *sendhost, int mode, mcomm *c, gid_t gid)
86badd32
 {
69d91074
   int r;
86badd32
   char path[PATH_MAX];
   if(*base == 0){
     getcwd(path, PATH_MAX);
69d91074
     return(mexec_scan_dir(fd, path, sendhost, mode, c, 0, gid));
   }
   /*----- exclude -----*/
   sprintf(path, "%s/%s", moption.real_dir, base);
   if(!mfnmatch(path, c->exclude)){
     if(!is_dir(base)){
       return(mexec_scan_send(fd, base, sendhost, mode, gid));
     }else{
       /*----- exclude dir -----*/
       strcat(path, "/");
       if(mfnmatch(path, c->exclude)){
         return(0);
       }
       /*----- scan dir -----*/
       if(r = mexec_scan_dir(fd, base, sendhost, mode, c, 1, gid)){
         return(r);
       }
       if(loop_flag && (mode != MAKUO_MEXEC_MD5)){
         return(mexec_scan_send(fd, base, sendhost, mode, gid));
86badd32
       }
     }
   }
   return(0);
 }
 
5cf3fe0f
 int mexec_scan(mcomm *c, char *fn, mhost *h, int mode, gid_t gid)
86badd32
 {
   int pid;
   int p[2];
   char sendhost[256];
   char base[PATH_MAX];
 
   if(pipe(p) == -1){
8f9aeac1
     cprintf(0, c, "error: pipe error\n");    
d6e8005b
     lprintf(0, "[error] %s: %s pipe error\n", __func__, strerror(errno));    
86badd32
     return(-1);
   }
 
   base[0] = 0;
   sendhost[0] = 0;
   if(h)
     strcpy(sendhost, h->hostname);
   if(fn)
     strcpy(base, fn);
 
   pid=fork();
   if(pid == -1){
     close(p[0]);
     close(p[1]);
8f9aeac1
     cprintf(0, c, "error: fork error\n");
d6e8005b
     lprintf(0, "[error] %s: %s fork error\n", __func__, strerror(errno));
86badd32
     return(-1);
   }
   if(pid){
     /* parent */
     close(p[1]);
     c->cpid  = pid;
     c->fd[1] = p[0];
     return(0);
   }else{
     /* child */
     close(p[0]);
5cf3fe0f
     mexec_scan_child(p[1], base, sendhost, mode, c, gid);
86badd32
     close(p[1]);
     _exit(0);
   }
   return(0);
 }
 
 int mexec_close(mcomm *c, int n)
 {
642a6cb2
   int i;
86badd32
   mfile *m;
 
642a6cb2
   if(c->fd[n] != -1){
86badd32
     close(c->fd[n]);
642a6cb2
     for(i=0;i<MAX_COMM;i++){
       if(c == &(moption.comm[i])){
         break;
       }
     }
     if(n){
       lprintf(8, "%s: socket[%d] (backend)\n", __func__, i);
     }else{
       lprintf(1, "%s: socket[%d]\n", __func__, i);
     }
   }
86badd32
   c->fd[n]  = -1;
   c->size[n] = 0;
   if(!n){
f9cb6348
     c->authchk  = 0;
86badd32
     c->loglevel = 0;
     if(c->cpid){
       kill(c->cpid, SIGTERM);
       mexec_close(c, 1);
     }
     while(c->exclude){
       mexec_exclude_del(c, c->exclude);
     }
21e2082d
     for(m=mftop[MFSEND];m;m=m->next){
86badd32
       if(m->comm == c){
         m->comm = NULL;
642a6cb2
         lprintf(3, "%s: cancel> %s\n", __func__, m->cmdline);
86badd32
       }
     }
   }
   return(0);
 }
 
 int mexec_quit(mcomm *c, int n)
 {
   mexec_close(c, n);
   return(0);
 }
 
 int mexec_help(mcomm *c, int n)
 {
   cprintf(0, c, "COMMAND:\n");
   cprintf(0, c, "  quit\n");
8f9aeac1
   cprintf(0, c, "  exclude add PATTERN\n");
   cprintf(0, c, "  exclude del PATTERN\n");
86badd32
   cprintf(0, c, "  exclude list\n");
   cprintf(0, c, "  exclude clear\n");
3eaafa57
   cprintf(0, c, "  send  [-r] [-t host] [-n] [path]\n");
7ec57664
   cprintf(0, c, "  sync  [-r] [-t host] [-n] [path]\n");
3eaafa57
   cprintf(0, c, "  dsync [-r] [-t host] [-n] [path]\n");
   cprintf(0, c, "  check [-r] [-t host] [path]\n");
86badd32
   cprintf(0, c, "  loglevel num (0-9)\n");
   cprintf(0, c, "  members\n");
   cprintf(0, c, "  help\n");
   return(0);
 }
 
7ec57664
 int mexec_send(mcomm *c, int n, int sync)
86badd32
 {
fc430629
   int e;
86badd32
   int i;
27b2005f
   int j;
86badd32
   ssize_t size;
   char *argv[9];
   char *fn = NULL;
   mfile *m = NULL;
810b92ac
   mhost *t = NULL;
   int dryrun = 0;
   int recurs = 0;
5cf3fe0f
   gid_t gid = -1;
86badd32
   int mode = MAKUO_MEXEC_SEND;
 
   if(moption.dontsend){
     cprintf(0, c, "error: this server can't send\n");
     return(0);
   }
7c8e6a17
   for(i=0;i<c->argc[n];i++){
86badd32
     argv[i] = c->parse[n][i];
7c8e6a17
   }
86badd32
   argv[i] = NULL;
7c8e6a17
 #ifdef HAVE_GETOPT_OPTRESET
   optind   = 1;
   optreset = 1;
 #else
86badd32
   optind = 0;
79ca8b92
 #endif
5cf3fe0f
   while((i=getopt(c->argc[n], argv, "g:t:nr")) != -1){
86badd32
     switch(i){
       case 'n':
810b92ac
         dryrun = 1;
86badd32
         mode = MAKUO_MEXEC_DRY;
         break;
       case 'r':
810b92ac
         recurs = 1;
86badd32
         break;
5cf3fe0f
       case 'g':
         if(*optarg >= '0' && *optarg <='9'){
           gid = atoi(optarg);
         }else{
43a6533f
           if(moption.gids){
             for(j=0;moption.gids[j];j++){
               if(!strcmp(optarg, moption.grnames[j])){
                 gid = moption.gids[j];
                 break;
               }
27b2005f
             }
5cf3fe0f
           }
         }
         break;
86badd32
       case 't':
27b2005f
         for(t=members;t;t=t->next){
           if(!strcmp(t->hostname, optarg)){
86badd32
             break;
27b2005f
           }
         }
810b92ac
         if(!t){
1ed29a26
           cprintf(0, c, "%s is not contained in members\n", optarg);
86badd32
           return(0);
         }
         break;
       case '?':
1ed29a26
         cprintf(0, c, "invalid option -- %c\n", optopt);
86badd32
         return(0); 
     }
   }
 
   while(optind < c->argc[n])
     fn = c->parse[n][optind++];
 
17b77ef8
   if(fn){
     int len;
     if(len = strlen(fn)){
       if(fn[len - 1] == '/'){
         fn[len - 1] = 0;
       }
     }
   }
 
86badd32
   /*----- directory scan -----*/
810b92ac
   if(recurs){
86badd32
     if(c->cpid){
       cprintf(0, c, "recursive process active now!\n");
       return(0);
     }
5cf3fe0f
     return(mexec_scan(c, fn, t, mode, gid));
86badd32
   }
38f71e58
 
86badd32
   /*----- help -----*/
   if(!fn){
7ec57664
     if(sync){
1ed29a26
       cprintf(0, c, "sync [-n] [-r] [-t host] [path]\n");
       cprintf(0, c, "  -n  # dryrun\n");
       cprintf(0, c, "  -r  # recursive\n");
       cprintf(0, c, "  -t  # target host\n");
7ec57664
     }else{
1ed29a26
       cprintf(0, c, "send [-n] [-r] [-t host] [path]\n");
       cprintf(0, c, "  -n  # dryrun\n");
       cprintf(0, c, "  -r  # recursive\n");
       cprintf(0, c, "  -D  # with delete\n");
       cprintf(0, c, "  -t  # target host\n");
7ec57664
     }
86badd32
     return(0);
   }
38f71e58
 
86badd32
   /*----- send file -----*/
3b736b7c
   m = mfadd(MFSEND);
86badd32
   if(!m){
d38d0833
 	  lprintf(0, "[error] %s: out of memorry\n", __func__);
3b736b7c
 	  cprintf(0, c, "error: out of memorry\n");
86badd32
     return(0);
 	}
57179b09
 
7ec57664
   /*----- send to address set -----*/
810b92ac
   if(t){
7ec57664
     m->sendto = 1;
810b92ac
     memcpy(&(m->addr.sin_addr), &(t->ad), sizeof(m->addr.sin_addr));
7ec57664
   }
 
 	strcpy(m->fn, fn);
3b736b7c
   strcpy(m->cmdline, c->cmdline[n]);
7ec57664
 	m->mdata.head.reqid  = getrid();
 	m->mdata.head.opcode = MAKUO_OP_SEND;
   m->mdata.head.nstate = MAKUO_SENDSTATE_STAT;
 	m->comm      = c;
810b92ac
   m->dryrun    = dryrun;
   m->recurs    = recurs;
7ec57664
   m->initstate = 1;
   if(m->dryrun){
     m->mdata.head.flags |= MAKUO_FLAG_DRYRUN;
   }
 
86badd32
 	if(lstat(fn, &m->fs) == -1){
fc430629
     e = errno;
     if(e == ENOENT){
7ec57664
       if(sync){
         m->mdata.head.flags |= MAKUO_FLAG_SYNC;
         return(0);
       }      
     }
fc430629
 	  cprintf(0, c, "error: %s %s\n", strerror(e), fn);
d6e8005b
 		lprintf(0, "[error] %s: %s %s\n", __func__, strerror(e), fn);
86badd32
 		mfdel(m);
     return(0);
 	}
57179b09
   
d93eefa2
   m->seqnomax  = m->fs.st_size / MAKUO_BUFFER_SIZE;
   if(m->fs.st_size % MAKUO_BUFFER_SIZE){
     m->seqnomax++; 
   }
86badd32
 
57179b09
   /*----- socket check -----*/
   if(S_ISSOCK(m->fs.st_mode)){
 	  cprintf(0, c, "skip: unix domain socket %s\n", fn);
 		mfdel(m);
     return(0);
   }
 
8f9aeac1
   /*----- owner check -----*/
   if(moption.ownmatch && (moption.uid != m->fs.st_uid)){
 	  cprintf(0, c, "skip: owner unmatch %s (%d != %d)\n", fn, moption.uid, m->fs.st_uid);
a80ad51a
 		lprintf(0, "%s: owner unmatch %s (%d != %d)\n", __func__, fn, moption.uid, m->fs.st_uid);
8f9aeac1
 		mfdel(m);
     return(0);
   }
 
86badd32
   /*----- readlink -----*/
   if(S_ISLNK(m->fs.st_mode)){
     size = readlink(m->fn, m->ln, PATH_MAX);
     if(size >= 0 && size < PATH_MAX){
       m->ln[size] = 0;
     }else{
8f9aeac1
 		  cprintf(0, c, "error: readlink error %s\n", fn);
d6e8005b
 		  lprintf(0, "[error] %s: readlink error %s\n", __func__, fn);
86badd32
 		  mfdel(m);
810b92ac
       return(0);
86badd32
     }
38f71e58
   }
 
27b2005f
   /*----- chgrp -----*/
7915da7c
   if((m->dryrun == 0) && (gid != -1)){
27b2005f
     if(m->fs.st_gid != gid){
       if(lchown(m->fn, -1, gid) == -1){
43a6533f
         lprintf(0, "%s: chgrp error (%s) [%d->%d] %s\n", __func__, strerror(errno),  m->fs.st_gid, gid, m->fn);
27b2005f
       }else{
         m->fs.st_gid = gid;
       }
     }
   }
86badd32
   return(0);
 }
 
a0346c4a
 int mexec_check(mcomm *c, int n)
91eda4ea
 {
69b027c3
   int e;
91eda4ea
   int i;
   ssize_t size;
   char *argv[9];
   char *fn = NULL;
   mfile *m = NULL;
   mhost *t = NULL;
   mhash *h = NULL;
   int recursive = 0;
 
   for(i=0;i<c->argc[n];i++)
     argv[i] = c->parse[n][i];
   argv[i] = NULL;
5ba94378
 #ifdef HAVE_GETOPT_OPTRESET
   optind   = 1;
   optreset = 1;
 #else
91eda4ea
   optind = 0;
79ca8b92
 #endif
91eda4ea
   while((i=getopt(c->argc[n], argv, "t:r")) != -1){
     switch(i){
       case 'r':
         recursive = 1;
         break;
       case 't':
         for(t=members;t;t=t->next)
           if(!strcmp(t->hostname, optarg))
             break;
         if(!t){
1ed29a26
           cprintf(0, c, "%s is not contained in members\n", optarg);
91eda4ea
           return(0);
         }
         break;
       case '?':
1ed29a26
         cprintf(0, c, "invalid option -- %c\n", optopt);
91eda4ea
         return(0); 
     }
   }
 
   while(optind < c->argc[n])
     fn = c->parse[n][optind++];
 
   /*----- directory scan -----*/
   if(recursive){
     if(c->cpid){
       cprintf(0, c, "recursive process active now!\n");
       return(0);
     }
5cf3fe0f
     return(mexec_scan(c, fn, t, MAKUO_MEXEC_MD5, -1));
91eda4ea
   }
 
   /*----- help -----*/
   if(!fn){
1ed29a26
     cprintf(0, c,"usage: check [-t host] [-r] [path]\n");
     cprintf(0, c, "  -r  # dir recursive\n");
     cprintf(0, c, "  -t  # target host\n");
91eda4ea
     return(0);
   }
 
   /*----- create mfile -----*/
3b736b7c
   m = mfadd(MFSEND);
91eda4ea
   if(!m){
d6e8005b
 	  lprintf(0, "[error] %s: out of memorry\n", __func__);
91eda4ea
 	  cprintf(0, c, "error: out of memorry\n");
     return(0);
 	}
 	m->mdata.head.reqid  = getrid();
 	m->mdata.head.seqno  = 0;
 	m->mdata.head.opcode = MAKUO_OP_MD5;
   m->mdata.head.nstate = MAKUO_SENDSTATE_OPEN;
   m->initstate = 1;
 	m->comm      = c;
   m->sendto    = 0;
   m->dryrun    = 0;
   m->ln[0]     = 0;
 	strcpy(m->fn, fn);
3b736b7c
   strcpy(m->cmdline, c->cmdline[n]);
91eda4ea
 
   /*----- open -----*/
   m->fd = open(m->fn, O_RDONLY);
   if(m->fd == -1){
69b027c3
     e = errno;
d6e8005b
 	  lprintf(0, "[error] %s: %s %s\n", __func__, strerror(e), m->fn);
69b027c3
     cprintf(0, c, "error: %s %s\n", strerror(e), m->fn);
91eda4ea
     mfdel(m);
     return(0);
   }
 
   /*----- md5 -----*/
   h = (mhash *)m->mdata.data;
   h->fnlen = strlen(m->fn);
   memcpy(h->filename, m->fn, h->fnlen);
   m->mdata.head.szdata = sizeof(mhash) + h->fnlen;
   h->fnlen = htons(h->fnlen);
21e2082d
   MD5_Init(&(m->md5));
91eda4ea
 
   /*----- sendto address -----*/
   if(t){
     m->sendto = 1;
     memcpy(&(m->addr.sin_addr), &(t->ad), sizeof(m->addr.sin_addr));
   }
   return(0);
 }
 
3eaafa57
 int mexec_dsync(mcomm *c, int n)
91eda4ea
 {
3eaafa57
   int i;
   ssize_t size;
   char *argv[9];
   char *fn = NULL;
   mhost *t = NULL;
   int  recurs = 0;
   int  dryrun = 0;
 
   if(moption.dontsend){
     cprintf(0, c, "error: this server can't send\n");
     return(0);
   }
   for(i=0;i<c->argc[n];i++)
     argv[i] = c->parse[n][i];
   argv[i] = NULL;
5ba94378
 #ifdef HAVE_GETOPT_OPTRESET
   optind   = 1;
   optreset = 1;
 #else
3eaafa57
   optind = 0;
79ca8b92
 #endif
3eaafa57
   while((i=getopt(c->argc[n], argv, "t:nr")) != -1){
     switch(i){
       case 'n':
         dryrun = 1;
         break;
       case 'r':
         recurs = 1;
         break;
       case 't':
         for(t=members;t;t=t->next)
           if(!strcmp(t->hostname, optarg))
             break;
         if(!t){
1ed29a26
           cprintf(0, c, "%s is not contained in members\n", optarg);
3eaafa57
           return(0);
         }
         break;
       case '?':
1ed29a26
         cprintf(0, c, "invalid option -- %c\n", optopt);
3eaafa57
         return(0); 
     }
   }
 
806dbe3b
   while(optind < c->argc[n]){
3eaafa57
     fn = c->parse[n][optind++];
806dbe3b
   }
3eaafa57
 
17b77ef8
   if(fn){
     int len;
     if(len = strlen(fn)){
       if(fn[len - 1] == '/'){
         fn[len - 1] = 0;
       }
     }
   }
 
3eaafa57
   /*----- help -----*/
   if(c->argc[n]<2){
1ed29a26
     cprintf(0, c, "dsync [-r] [-t host] [-n] [path]\n");
     cprintf(0, c, "  -r  # recursive\n");
     cprintf(0, c, "  -t  # target host\n");
     cprintf(0, c, "  -n  # dryrun\n");
3eaafa57
     return(0);
   }
 
38f71e58
   mexec_with_dsync(c, fn, dryrun, recurs, t);
91eda4ea
   return(0);
 }
 
86badd32
 int mexec_members(mcomm *c, int n)
 {
e8508211
   int i, j;
a227852e
   int counter = 0;
   int namelen = 0;
   int addrlen = 0;
3b736b7c
   int statcnt = 0;
e8508211
   mhost *t;
   mhost **pt;
a227852e
   char form[256];
e8508211
 
   /* count */
 	for(t=members;t;t=t->next){
a227852e
     counter++;
     if(namelen < strlen(t->hostname)){
       namelen = strlen(t->hostname);
     }
     if(addrlen < strlen(inet_ntoa(t->ad))){
       addrlen = strlen(inet_ntoa(t->ad));
     }
86badd32
   }
a227852e
 
e8508211
   /* set */
   t  = members;
a227852e
   pt = malloc(sizeof(mhost *) * counter);
   for(i=0;i<counter;i++){
e8508211
     pt[i] = t;
     t = t->next;
   }
a227852e
 
e8508211
   /* sort */
a227852e
   for(i=0;i<counter;i++){
     for(j=i+1;j<counter;j++){
       if(strcmp(pt[i]->hostname, pt[j]->hostname) > 0){
e8508211
         t = pt[i];
         pt[i] = pt[j];
         pt[j] = t;
       }
     }
   }
a227852e
 
e8508211
   /* view */
3b736b7c
 #ifdef MAKUO_DEBUG
   sprintf(form, "%%-%ds %%-%ds (Ver%%s) STATE_AREA(%%d/%%d)\n", namelen, addrlen);
   for(i=0;i<counter;i++){
     statcnt = 0;
     for(j=0;j<MAKUO_HOSTSTATE_SIZE;j++){
       if(pt[i]->mflist[j]){
         statcnt++;
       }
     }
     cprintf(0, c, form, pt[i]->hostname, inet_ntoa(pt[i]->ad), pt[i]->version, statcnt, MAKUO_HOSTSTATE_SIZE);
   }
 #else
a227852e
   sprintf(form, "%%-%ds %%-%ds (Ver%%s)\n", namelen, addrlen);
   for(i=0;i<counter;i++){
     cprintf(0, c, form, pt[i]->hostname, inet_ntoa(pt[i]->ad), pt[i]->version);
e8508211
   }
3b736b7c
 #endif
a227852e
   cprintf(0, c, "Total: %d members\n", counter);
e8508211
   free(pt);
86badd32
   return(0);
 }
 
 int mexec_echo(mcomm *c, int n)
 {
   int i;
   cprintf(0, c, "%s", c->parse[n][1]);
   for(i=2;i<8;i++){
     if(c->parse[n][i][0]){
       cprintf(0, c, " %s", c->parse[n][i]);
     }
   }
   cprintf(0, c, "\n");
   return(0);
 }
 
 int mexec_loglevel(mcomm *c, int n)
 {
   c->loglevel=atoi(c->parse[n][1]);
   return(0);
 }
 
 int mexec_exclude_add(mcomm *c, char *pattern)
 {
5cf3fe0f
   c->exclude = exclude_add(c->exclude, pattern);
86badd32
   return(0);
 }
 
 int mexec_exclude_del(mcomm *c, excludeitem *e)
 {
5cf3fe0f
   excludeitem *d = exclude_del(e);
   if(e == c->exclude){
     c->exclude = d;
   }
86badd32
   return(0);
 }
 
 int mexec_exclude(mcomm *c, int n)
 {
   excludeitem *e;
   switch(c->argc[n]){
     case 2:
       if(!strcmp("list", c->parse[n][1])){
         for(e=c->exclude;e;e=e->next){
           cprintf(0,c,"%s\n", e->pattern);
         }
         return(0);
       }
       if(!strcmp("clear", c->parse[n][1])){
         while(c->exclude){
           mexec_exclude_del(c, c->exclude);
         }
         return(0);
       }
       break;
 
     case 3:
       if(!strcmp("add", c->parse[n][1])){
         for(e=c->exclude;e;e=e->next){
           if(!strcmp(e->pattern, c->parse[n][2])){
             break;
           }
         }
         if(!e){
           mexec_exclude_add(c, c->parse[n][2]);
           return(0);
         }
       }
       if(!strcmp("del", c->parse[n][1])){
         for(e=c->exclude;e;e=e->next){
           if(!strcmp(e->pattern, c->parse[n][2])){
             mexec_exclude_del(c, e);
             return(0);
           }
         }
         cprintf(0,c,"pattern not found %s\n", c->parse[n][2]);
       }
       break;
   }
8f9aeac1
   cprintf(0,c,"usage: exclude add PATTERN\n");
   cprintf(0,c,"       exclude del PATTERN\n");
86badd32
   cprintf(0,c,"       exclude list\n");
   cprintf(0,c,"       exclude clear\n");
   return(0);
 }
 
 int mexec_status(mcomm *c, int n)
 {
31e550c9
   int i;
86badd32
   int count;
   mfile  *m;
5f0d41cd
   struct tm *t;
 
642a6cb2
   /*----- pid -----*/
   cprintf(0, c, "process: %d\n", getpid());
 
   /*----- version -----*/
13671329
   cprintf(0,c,"version: %s\n", PACKAGE_VERSION);
642a6cb2
 
   /*----- basedir -----*/
f9cb6348
   if(moption.chroot){
13671329
     cprintf(0, c, "chroot : %s/\n", moption.real_dir);
f9cb6348
   }else{
13671329
     cprintf(0, c, "basedir: %s/\n", moption.base_dir);
f9cb6348
   }
3b736b7c
 
07238671
   /*----- mfalloc -----*/
   count = 0;
   for(m=mftop[MFSEND];m;m=m->next){
     count++;
   }
   for(m=mftop[MFRECV];m;m=m->next){
     count++;
   }
   for(m=mfreeobj;m;m=m->next){
     count++;
   }
   cprintf(0, c, "mfalloc: %d\n", count);
 
3b736b7c
   /*----- command -----*/
   count = 0;
   for(i=0;i<MAX_COMM;i++){
     if(moption.comm[i].working && (c != &(moption.comm[i]))){
       count++;
     }
   }
   cprintf(0, c, "command: %d\n", count);
   for(i=0;i<MAX_COMM;i++){
     if(moption.comm[i].working && (c != &(moption.comm[i]))){
       cprintf(0, c, "  %d> %s\n", i, moption.comm[i].cmdline[0]);
     }
   }
 
   /*----- send -----*/
86badd32
   count = 0;
3b736b7c
   for(m=mftop[MFSEND];m;m=m->next){
86badd32
     count++;
481bf811
   }
13671329
   cprintf(0,c,"send op: %d\n", count);
3b736b7c
   for(m=mftop[MFSEND];m;m=m->next){
481bf811
     uint32_t snow = m->seqnonow;
     uint32_t smax = m->seqnomax;
     if(snow > smax){
       snow = smax;
5f0d41cd
     }
ba2caa89
     cprintf(0, c, "  (%s) %s %s %s (%u:%u/%u) rid=%d flags=%x\n",
43a6533f
       strackreq(&(m->mdata)), 
17b77ef8
       stropcode(&(m->mdata)), 
       strmstate(&(m->mdata)), 
       m->fn, 
       m->markcount,
       snow, 
       smax,
ba2caa89
       m->mdata.head.reqid,
       m->mdata.head.flags);
5f0d41cd
   }
86badd32
 
3b736b7c
   /*----- recv -----*/
86badd32
   count = 0;
3b736b7c
   for(m=mftop[MFRECV];m;m=m->next)
86badd32
     count++;
13671329
   cprintf(0, c, "recv op: %d\n", count);
3b736b7c
   for(m=mftop[MFRECV];m;m=m->next){
5f0d41cd
     t = localtime(&(m->lastrecv.tv_sec));
17b77ef8
     cprintf(0, c, "  %s %s %02d:%02d:%02d %s (%d/%d) mark=%d rid=%d\n",
       stropcode(&(m->mdata)), 
       strrstate(m->mdata.head.nstate), 
806dbe3b
       t->tm_hour, t->tm_min, t->tm_sec, 
       m->fn, 
17b77ef8
       m->recvcount,
       m->seqnomax, 
       m->markcount,
       m->mdata.head.reqid); 
5f0d41cd
   }
86badd32
   return(0);
 }
 
 int mexec_password(char *password)
 {
   unsigned char digest[16];
   MD5_CTX ctx;
   MD5_Init(&ctx);
   MD5_Update(&ctx, password, strlen(password));
   MD5_Final(digest, &ctx);
   if(!memcmp(moption.password[0], digest, 16)){ 
     return(1);
   }
   return(0);
 }
 
 int mexec_parse(mcomm *c, int n)
 {
   int i;
   int j;
   int l;
   char *p;
   char cmd[MAKUO_BUFFER_SIZE];
 
   c->argc[n] = 0;
   p = c->cmdline[n];
   for(i=0;i<8;i++)
     c->parse[n][i][0]=0;
   for(i=0;i<c->size[n];i++){
     *p = c->readbuff[n][i];
     if(c->readbuff[n][i] == '\r')
       *p = 0;
     if(c->readbuff[n][i] == '\n'){
       *p = 0;
       break;
     }
     p++;
   }
   c->check[n] = 0;
   if(i == c->size[n])
     return(-1);
   i++;
   memmove(c->readbuff[n], c->readbuff[n] + i, MAKUO_BUFFER_SIZE - i);
   if(c->size[n] -= i)
     c->check[n] = 1;
   if(moption.commpass && !c->authchk){
     c->authchk = mexec_password(c->cmdline[n]);
     c->cmdline[n][0]=0;
     cprintf(0, c,"\r");
f9cb6348
     if(!c->authchk){
       cprintf(0, c, "sorry.\n");
       mexec_close(c, n);
       return(-1);
     }
86badd32
   }else{
     strcpy(cmd, c->cmdline[n]);
     p=strtok(cmd, " ");
     for(j=0;j<8;j++){
       if(!p)
         break;
       strcpy(c->parse[n][j], p);
       if(j){
         if(l = strlen(c->parse[n][j-1])){
           if(c->parse[n][j-1][l-1] == '\\'){
             c->parse[n][j-1][l-1] = 0;
             strcat(c->parse[n][j-1], " ");
             strcat(c->parse[n][j-1], p);
             c->parse[n][j][0] = 0;
             j--;
           }
         }
       }
       p = strtok(NULL, " ");
     }
     c->argc[n] = j;
   }
   for(i=0;command_list[i];i++)
     if(!strcmp(c->parse[n][0], command_list[i]))
       break;
   return(i);
 }
 
 int mexec(mcomm *c, int n)
 {
   int r;
f9cb6348
   int size   = MAKUO_BUFFER_SIZE - c->size[n];
86badd32
   char *buff = c->readbuff[n] + c->size[n];
f9cb6348
   mfile *m   = NULL;
   int count  = 0;
86badd32
 
   if(n == 0 && c->working){
     c->size[n] = 0;
     r = read(c->fd[n], buff, size);
     if(r>0){
     }else{
       if(r == -1){
d38d0833
         lprintf(0, "[error] %s: read error n=%d\n", __func__, n);
86badd32
       }
       mexec_close(c, n);
     }
     return(-1);
   }
91eda4ea
 
dbdc4e5a
   if(n == 1){
d38d0833
     for(m=mftop[MFSEND];m;m=m->next){
dbdc4e5a
       if(m->comm == c){
810b92ac
         count++;
         if(count == MAKUO_PARALLEL_MAX){
86badd32
           return(-1);
dbdc4e5a
         }
       }
     }
   }
91eda4ea
 
86badd32
   if(!size){
d38d0833
     lprintf(0, "[error] %s: buffer over fllow n=%d\n", __func__, n);
86badd32
     mexec_close(c, n);
     return(-1);
   }
 
   if(!c->check[n]){
     r = read(c->fd[n], buff, size);
     if(r > 0){
       c->size[n] += r;
     }else{
       if(r < 0){
8f9aeac1
         lprintf(0, "%s: read error n=%d\n", __func__, n);
86badd32
       }
       mexec_close(c, n);
       return(-1);
     }
   }
 
   if((r = mexec_parse(c, n)) == -1)
     return(-1); 
 
   if(!command_list[r]){
     if(c->parse[n][0][0]){
       cprintf(0, c, "mexec: command error '%s'\n", c->parse[n][0]);
     }
     if(moption.commpass && !c->authchk){
f9cb6348
       cprintf(0,c,"password: \x1b]E");
86badd32
     }else{
       cprintf(0, c, "> ");
     }
   }else{
8f5ac56e
     lprintf(1 + n * 7, "%s: %s\n", __func__, c->cmdline[n]);
86badd32
     c->working = 1;
 
     if(!strcmp("help",command_list[r]))
       return(mexec_help(c,n));
 
     if(!strcmp("quit",command_list[r]))
       return(mexec_quit(c,n));
 
     if(!strcmp("exit",command_list[r]))
       return(mexec_quit(c,n));
 
     if(!strcmp("bye",command_list[r]))
       return(mexec_quit(c,n));
 
     if(!strcmp("send",command_list[r]))
7ec57664
       return(mexec_send(c,n,0));
 
     if(!strcmp("sync",command_list[r]))
       return(mexec_send(c,n,1));
86badd32
 
     if(!strcmp("md5",command_list[r]))
a0346c4a
       return(mexec_check(c,n));
 
     if(!strcmp("check",command_list[r]))
       return(mexec_check(c,n));
86badd32
 
3eaafa57
     if(!strcmp("dsync",command_list[r]))
       return(mexec_dsync(c,n));
91eda4ea
 
86badd32
     if(!strcmp("members",command_list[r]))
       return(mexec_members(c,n));
 
     if(!strcmp("echo",command_list[r]))
       return(mexec_echo(c,n));
 
     if(!strcmp("loglevel",command_list[r]))
       return(mexec_loglevel(c,n));
 
     if(!strcmp("exclude",command_list[r]))
       return(mexec_exclude(c,n));
 
     if(!strcmp("status",command_list[r]))
       return(mexec_status(c,n));
 
     c->working = 0;
   }
   return(r);
 }