msync.c
c858e9b6
 /*
  * msync.c
83cbbef8
  * Copyright (C) 2008-2012 KLab Inc. 
c858e9b6
  */
a0346c4a
 #include "makuosan.h"
 
05348f1d
 typedef struct msyncdata
 {
   int s;                /* */
   int loopflag;         /* */
   int loglevel;         /* */
   int sendflag;         /* */
   int delflag;          /* */
   int grpflag;          /* */
   char scfile[256];     /* */
   char passwd[256];     /* */
   char target[256];     /* */
   char mcmd[256];       /* */
   char mopt[256];       /* */
   char sopt[256];       /* */
   char path[PATH_MAX];  /* */
   excludeitem *exclude; /* */
 } msyncdata;
1f4b3de7
 
f5b6f77b
 void version()
1f4b3de7
 {
a0346c4a
   printf("msync version %s (CLI for makuosan)\n", PACKAGE_VERSION);
f5b6f77b
 }
 
 void usage()
 {
   version();
a0346c4a
   printf("usage: msync [OPTION] [FILENAME]\n");
   printf("\n");
   printf("  OPTION\n");
   printf("    --status            # show makuosan status\n");
   printf("    --members           # show makuosan members\n");
   printf("    --check             # file check use md5\n");
3eaafa57
   printf("    --delete            # \n");
7ec57664
   printf("    --sync              # \n");
a0346c4a
   printf("    --exclude=PATTERN   # \n"); 
   printf("    --exclude-from=FILE # \n");
   printf("\n");
   printf("    -l LOGLEVEL(0-9)    # log level select. default=0\n");
   printf("    -c MSYNC_TARGET     # \n");
   printf("    -f SCRIPT_FILE      # \n");
   printf("    -t HOSTNAME         # distnation hostname\n");
   printf("    -v                  # log level increment\n");
   printf("    -n                  # dry run\n");
   printf("    -r                  # recurse into directories\n");
1f4b3de7
   printf("\n");
a0346c4a
   printf("  MSYNC_TARGET\n");
   printf("    tcp:HOST:PORT  ex) tcp:127.0.0.1:5000\n");
   printf("    unix:SOCKET    ex) unix:makuosan.sock\n");
   printf("\n");
   printf("  SCRIPT_FILE\n");
   printf("    (It writes later)\n");
1f4b3de7
   printf("\n");
a0346c4a
 }
 
05348f1d
 excludeitem *add_exclude(msyncdata *md, char *pattern)
a0346c4a
 {
   excludeitem *item = malloc(sizeof(excludeitem));
   item->pattern = malloc(strlen(pattern) + 1);
   strcpy(item->pattern, pattern);
af8d11e0
   item->prev = NULL;
   item->next = NULL;
05348f1d
   if(md->exclude){
     md->exclude->prev = item;
     item->next = md->exclude;
a0346c4a
   }
05348f1d
   md->exclude = item;
a0346c4a
   return(item);
1f4b3de7
 }
 
c858e9b6
 int connect_socket_tcp(char *host, char *port)
1f4b3de7
 {
c858e9b6
   int s;
   struct addrinfo hint;
   struct addrinfo *res;
   if(!host || !port){
1f4b3de7
     return(-1);
   }
c858e9b6
   memset(&hint, 0, sizeof(struct addrinfo));
37d3fc2b
   hint. ai_family  = AF_INET;
   hint.ai_socktype = SOCK_STREAM;
   hint.ai_protocol = IPPROTO_TCP;
c858e9b6
   if(getaddrinfo(host, port, &hint, &res)){
1f4b3de7
     return(-1);
   }
c858e9b6
   if(!res){
1f4b3de7
     return(-1);
   }
c858e9b6
   s = socket(AF_INET, SOCK_STREAM, 0);
   if(s == -1){
     freeaddrinfo(res);
     return(-1);
1f4b3de7
   }
c858e9b6
   if(connect(s, res->ai_addr, res->ai_addrlen) == -1){
     freeaddrinfo(res);
1f4b3de7
     close(s);
     return(-1);
   }
c858e9b6
   freeaddrinfo(res);
1f4b3de7
   return(s);  
 }
 
c858e9b6
 int connect_socket_unix(char *path)
1f4b3de7
 {
c858e9b6
   int s;
1f4b3de7
   struct sockaddr_un sa;
c858e9b6
   if(!path){
     return(-1);
   }
   if(strlen(path) >= sizeof(sa.sun_path)){
 	  return(-1);
   }
   s = socket(AF_UNIX, SOCK_STREAM, 0);
   if(s == -1){
     return(-1);
   }
   sa.sun_family = AF_UNIX;
   strcpy(sa.sun_path,path);
   if(connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1){
     close(s);
     return(-1);
1f4b3de7
   }
   return(s);
 }
 
 int connect_socket(char *target)
 {
c858e9b6
   char *h;
   char *p;
1f4b3de7
   char buff[256];
 
   strcpy(buff, target);
   p = strtok(buff,":");
   if(!p){
     usage();
c858e9b6
     exit(1);
1f4b3de7
   }
   if(!strcmp(p, "tcp")){
c858e9b6
     h = strtok(NULL,":");
     p = strtok(NULL,":");
     return(connect_socket_tcp(h,p));
1f4b3de7
   }
   if(!strcmp(p, "unix")){
c858e9b6
     p = strtok(NULL,":");
     return(connect_socket_unix(p));
1f4b3de7
   }
d38d0833
   return(connect_socket_unix(buff));
f9cb6348
 }
1f4b3de7
 
f9cb6348
 int writeline(int s, char *buff)
1f4b3de7
 {
f9cb6348
   int r; 
   int clen;
   int size;
 
   clen = strlen(buff);
   size = clen;
   while(size){
     r = write(s, buff + clen - size, size);
     if(r == -1){
       return(-1);
     }
     size -= r;
   }
   return(0);
 }
 
c858e9b6
 int check_prompt(int s, char *buff, char *passwd)
 {
   if(!strcmp(buff, "> ")){
     return(1);
   }
   if(!strcmp(buff, "password: \x1b]E") && passwd){
     writeline(s, passwd);
     writeline(s, "\r\n");
     return(2);
   }
   return(0);
 }
 
f9cb6348
 int readline(int s, char *buff, int size, int prompt, char *passwd)
 {
c3144016
   int   r = 0;
f9cb6348
   char  d = 0;
   char *p = buff;
c858e9b6
 
   while(p < buff + size){
     *p = 0;
     if(prompt){
       switch(check_prompt(s, buff, passwd)){
         case 1:
           return(-2);
         case 2:
           p = buff;
           continue;
f9cb6348
       }
     }
c3144016
     r = read(s, &d, 1);
     if(r == 0){
       return(p - buff);
     }
     if(r == -1){
       return(-1);
     }
     if(d == '\n'){
       if(p != buff){
c858e9b6
         return(p - buff);
c3144016
       }
     }else{
       if(d != '\r'){
c858e9b6
         *(p++) = d;
c3144016
       }
c858e9b6
     }
f9cb6348
   }
c858e9b6
   return(-1); /* over flow */
f9cb6348
 }
 
05348f1d
 int wait_prompt(int s, char *passwd, int view, int *line){
1f4b3de7
   int  r;
f31a5410
   char buff[8192];
1b4c64d4
   while((r = readline(s, buff, sizeof(buff), 1, passwd))){
f9cb6348
     if(r == -1){
       /* read error */
05348f1d
       r = -1;
       break;
f9cb6348
     }
     if(r == -2){
       /* return prompt */
05348f1d
       r = 1;
       break;
f9cb6348
     }
c2cdbd80
     if(!strcmp(buff, "alive")){
05348f1d
       continue;
     }
     if(line){
       (*line)++;
     }
     if(view){
       if(!memcmp(buff, "error:", 6)){
         fprintf(stderr, "%s\n", buff);
       }else{
         fprintf(stdout, "%s\n", buff);
       }
f31a5410
     }
f9cb6348
   }
05348f1d
   return(r);
f9cb6348
 } 
 
05348f1d
 int makuo(int s, char *c, int view)
f9cb6348
 {
c858e9b6
   int  r;
05348f1d
   int  line = 1;
f9cb6348
   char buff[256];
c858e9b6
   if(sizeof(buff) < strlen(c) + 2){
     fprintf(stderr, "error: command too long\n");
     return(-1);
   }
f9cb6348
   sprintf(buff, "%s\r\n", c);
   if(writeline(s, buff) == -1){
c858e9b6
     fprintf(stderr, "error: can't write socket\n");
     return(-1);
   }
05348f1d
   r = wait_prompt(s, NULL, view, &line);
c858e9b6
   if(r == -1){
     fprintf(stderr, "error: can't read socket\n");
f9cb6348
     return(-1);
   }
05348f1d
   if(r == 0){
     return(0);
   }
   return(line);
 }
 
 void makuo_aliveon(msyncdata *md)
 {
   int r;
   char cmd[256];
   struct timeval tv;
   sprintf(cmd, "alive on");
   r = makuo(md->s, cmd, 0);
   if(r == 0){
     exit(1);
   }
   if(r == -1){
     exit(1);
   }
   if(r == 1){
     tv.tv_sec  = 30;
     tv.tv_usec = 0;
   }else{
     tv.tv_sec  = 0;
     tv.tv_usec = 0;
   }
   setsockopt(md->s, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
c858e9b6
 }
 
05348f1d
 void makuo_log(msyncdata *md)
c858e9b6
 {
   int r;
   char cmd[256];
05348f1d
   sprintf(cmd, "loglevel %d", md->loglevel);
   r = makuo(md->s, cmd, 0);
c858e9b6
   if(r == 0){
     fprintf(stderr, "error: remote close\n");
05348f1d
     exit(1);
c858e9b6
   }
   if(r == -1){
05348f1d
     exit(1);
c858e9b6
   }
f9cb6348
 }
 
05348f1d
 void makuo_exclude(msyncdata *md)
a0346c4a
 {
   int r;
   char cmd[1024];
   excludeitem *item;
05348f1d
   for(item=md->exclude;item;item=item->next){
a0346c4a
     sprintf(cmd, "exclude add %s", item->pattern);
05348f1d
     r = makuo(md->s, cmd, 0);
a0346c4a
     if(r == 0){
       fprintf(stderr, "error: makuosan remote close. (%s)\n", cmd);
05348f1d
       exit(1);
a0346c4a
     }
     if(r == -1){
       fprintf(stderr, "error: makuosan socket error. (%s)\n", cmd);
05348f1d
       exit(1);
a0346c4a
     }
   }
 }
 
 int makuo_exec(int s, char *cmd)
 { 
05348f1d
   int r = makuo(s, cmd, 1);
a0346c4a
   if(r == 0){
     fprintf(stderr, "error: makuosan remote close. (%s)\n", cmd);
     return(1);
   }
   if(r == -1){
     fprintf(stderr, "error: makuosan socket error. (%s)\n", cmd);
     return(1);
   }
   return(0);
 }
 
05348f1d
 void makuo_send(msyncdata *md)
 {
   char cmd[1024];
   if(md->delflag){
     sprintf(cmd, "dsync%s %s", md->mopt, md->path);
     if(makuo_exec(md->s, cmd)){
       close(md->s);
       exit(1);
     }
   }
   if(md->sendflag){
     sprintf(cmd, "%s%s%s %s", md->mcmd, md->mopt, md->sopt, md->path);
   }else{
     sprintf(cmd, "%s%s %s", md->mcmd, md->mopt, md->path);
   }
   if(makuo_exec(md->s, cmd)){
     close(md->s);
     exit(1);
   }
 }
 
 int makuo_quit(msyncdata *md)
c858e9b6
 { 
05348f1d
   int r = makuo(md->s, "quit", 0);
   close(md->s);
c858e9b6
   if(r == 0){
     return(0); /* success */
   }
   if(r == -1){
     return(1);
   }
   fprintf(stderr, "quit error?!\n");
   return(1);
 }
 
05348f1d
 int exclude_from(msyncdata *md, char *filename)
f9cb6348
 {
1f4b3de7
   int  f;
f9cb6348
   int  r;
   char line[256];
1f4b3de7
 
f9cb6348
   if(!strcmp(filename, "-")){
c858e9b6
     f = dup(0);
f9cb6348
   }else{
     f = open(filename, O_RDONLY);
c858e9b6
   }
   if(f == -1){
     fprintf(stderr,"can't open: %s\n", filename);
     return(1);
f9cb6348
   }
1b4c64d4
   while((r = readline(f, line, sizeof(line), 0, NULL))){
f9cb6348
     if(r == -1){
       fprintf(stderr, "file read error: %s\n", filename);
c858e9b6
       close(f);
       return(1);
f9cb6348
     }
50cd0ac2
     if((*line != '\r') && (*line != '\n') && (*line !=0)){
05348f1d
       add_exclude(md, line);
50cd0ac2
     }
f9cb6348
   }
a0346c4a
   close(f);
   return(0);
c858e9b6
 }
 
05348f1d
 int makuo_file(msyncdata *md)
c858e9b6
 {
a0346c4a
   int  f;
   int  r;
   char line[256];
05348f1d
   char *filename;
c858e9b6
 
05348f1d
   filename = md->scfile;
   if(!strlen(filename)){
     return(0);
   }
a0346c4a
   if(!strcmp(filename, "-")){
     /* f = stdin */
     f = dup(0);
   }else{
     f = open(filename, O_RDONLY);
c858e9b6
   }
a0346c4a
   if(f == -1){
     fprintf(stderr,"can't open: %s\n", filename);
c858e9b6
     return(1);
   }
 
a0346c4a
   /* command read loop */
1b4c64d4
   while((r = readline(f, line, sizeof(line), 0, NULL))){
a0346c4a
     if(r == -1){
       fprintf(stderr, "file read error: %s\n", filename);
05348f1d
       break;
a0346c4a
     }
05348f1d
     if(makuo_exec(md->s, line)){
a0346c4a
       close(f);
05348f1d
       exit(1);
a0346c4a
     }
   }
   close(f);
05348f1d
   return(1);
f9cb6348
 }
 
 int loadpass(char *filename, char *passwd, int size)
 {
   int f;
 
   f = open(filename, O_RDONLY);
   if(f == -1){
     fprintf(stderr, "file open error %s\n", filename);
     return(-1);
   }
   if(readline(f, passwd, size, 0, NULL) == -1){
     fprintf(stderr, "file read error %s\n", filename);
     close(f);
     return(-1);
   }
   close(f);
   return(0);
 }
 
c2cdbd80
 void get_envopt(msyncdata *md)
f9cb6348
 {
c2cdbd80
   char *p;
1b4c64d4
   if((p = getenv("MSYNC_TARGET"))){
c2cdbd80
     if(strlen(p) < sizeof(md->target)){
       strcpy(md->target, p);
     }else{
1b4c64d4
       fprintf(stderr, "MSYNC_TARGET too long. %s\n", p);
c2cdbd80
       exit(1);
     }
f9cb6348
   }
 }
 
05348f1d
 struct option *optinit()
f9cb6348
 {
f5b6f77b
   static struct option longopt[10];
a0346c4a
   longopt[0].name    = "help";
   longopt[0].has_arg = 0;
   longopt[0].flag    = NULL;
   longopt[0].val     = 'h';
   longopt[1].name    = "status";
   longopt[1].has_arg = 0;
   longopt[1].flag    = NULL;
   longopt[1].val     = 'S';
   longopt[2].name    = "members";
   longopt[2].has_arg = 0;
   longopt[2].flag    = NULL;
   longopt[2].val     = 'M';
   longopt[3].name    = "check";
   longopt[3].has_arg = 0;
   longopt[3].flag    = NULL;
   longopt[3].val     = 'C';
   longopt[4].name    = "exclude";
   longopt[4].has_arg = 1;
   longopt[4].flag    = NULL;
   longopt[4].val     = 'E';
   longopt[5].name    = "exclude-from";
   longopt[5].has_arg = 1;
   longopt[5].flag    = NULL;
   longopt[5].val     = 'F';
   longopt[6].name    = "delete";
   longopt[6].has_arg = 0;
   longopt[6].flag    = NULL;
   longopt[6].val     = 'D';
7ec57664
   longopt[7].name    = "sync";
   longopt[7].has_arg = 0;
   longopt[7].flag    = NULL;
   longopt[7].val     = 'd';
f5b6f77b
   longopt[8].name    = "version";
05348f1d
   longopt[8].has_arg = 0;
   longopt[8].flag    = NULL;
f5b6f77b
   longopt[8].val     = 'V';
   longopt[9].name    = NULL;
   longopt[9].has_arg = 0;
   longopt[9].flag    = NULL;
   longopt[9].val     = 0;
05348f1d
   return(longopt);
 }
a0346c4a
 
05348f1d
 void parse_opt(int argc, char *argv[], struct option *opt, msyncdata *md)
 {
   int r;
f5b6f77b
   while((r = getopt_long(argc, argv, "g:c:f:t:K:l:hvrnV", opt, NULL)) != -1){
1f4b3de7
     switch(r){
       case 'h':
         usage();
05348f1d
         exit(0);
c858e9b6
 
f5b6f77b
       case 'V':
         version();
         exit(0);
 
3eaafa57
       case 'D':
05348f1d
         md->delflag = 1;
3eaafa57
         break;
 
7ec57664
       case 'd':
05348f1d
         strcpy(md->mcmd, "sync");
7ec57664
         break;
 
a0346c4a
       case 'S':
05348f1d
         strcpy(md->mcmd, "status");
         md->loopflag = 0;
         md->sendflag = 0;
a0346c4a
         break;
 
       case 'M':
05348f1d
         strcpy(md->mcmd, "members");
         md->loopflag = 0;
         md->sendflag = 0;
a0346c4a
         break;
 
       case 'C':
05348f1d
         strcpy(md->mcmd, "check");
         md->sendflag = 0;
a0346c4a
         break;
 
       case 'E':
05348f1d
         add_exclude(md, optarg);
a0346c4a
         break;
 
       case 'F':
05348f1d
         if(exclude_from(md, optarg)){
           exit(1);
a0346c4a
         }
         break;
 
       case 'r':
05348f1d
         strcat(md->mopt," -r");
a0346c4a
         break;
 
       case 'n':
05348f1d
         strcat(md->mopt," -n");
a0346c4a
         break;
 
       case 't':
05348f1d
         strcat(md->mopt," -t ");
         strcat(md->mopt,optarg);
a0346c4a
         break;
 
5cf3fe0f
       case 'g':
05348f1d
         md->grpflag = 1;
         strcat(md->sopt," -g ");
         strcat(md->sopt,optarg);
5cf3fe0f
         break;
 
a0346c4a
       case 'v':
05348f1d
         md->loglevel++;
a0346c4a
         break;
 
c858e9b6
       case 'l':
05348f1d
         md->loglevel = atoi(optarg);
f9cb6348
         break;
c858e9b6
 
1f4b3de7
       case 'f':
05348f1d
         if(strlen(optarg) < sizeof(md->scfile)){
           strcpy(md->scfile, optarg);
c858e9b6
         }else{
           fprintf(stderr, "filename too long\n");
05348f1d
           exit(1);
c858e9b6
         }
1f4b3de7
         break;
c858e9b6
 
1f4b3de7
       case 'c':
05348f1d
         if(strlen(optarg) < sizeof(md->target)){
           strcpy(md->target, optarg);
c858e9b6
         }else{
           fprintf(stderr, "target too long\n");
05348f1d
           exit(1);
c858e9b6
         }
1f4b3de7
         break;
c858e9b6
 
f9cb6348
       case 'K':
05348f1d
         if(loadpass(optarg, md->passwd, sizeof(md->passwd)) == -1){
           exit(1);
f9cb6348
         }
         break;
 
a0346c4a
       case '?':
         usage();
05348f1d
         exit(1);
a0346c4a
         break;
     }
f9cb6348
   }
05348f1d
   if(md->delflag && !md->sendflag){
3eaafa57
     usage();
05348f1d
     exit(1);
5cf3fe0f
   }
05348f1d
   if(md->grpflag && !md->sendflag){
5cf3fe0f
     usage();
05348f1d
     exit(1);
3eaafa57
   }
05348f1d
   if(argc == optind){
     md->loopflag = 0;
1f4b3de7
   }
05348f1d
 }
c858e9b6
 
05348f1d
 int connect_wait(msyncdata *md)
 {
   int r;
   r = wait_prompt(md->s, md->passwd, 0, NULL);
c858e9b6
   if(r == 0){
     fprintf(stderr, "remote socket close\n");
     return(1);
   }
   if(r == -1){
     fprintf(stderr, "socket read error\n");
     return(1);
   }
05348f1d
   return(0);
 }
f9cb6348
 
05348f1d
 void connect_target(msyncdata *md)
 {
   struct timeval tv;
   md->s = connect_socket(md->target);
   if(md->s == -1){
     fprintf(stderr, "can't connect %s\n", md->target);
     exit(1);
a0346c4a
   }
05348f1d
   tv.tv_sec  = 5;
   tv.tv_usec = 0;
   setsockopt(md->s, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
   if(connect_wait(md)){
     close(md->s);
     exit(1);
   } 
 }
a0346c4a
 
05348f1d
 void msync_init(msyncdata *md)
 {
   memset(md, 0, sizeof(msyncdata));
   md->loopflag = 1;
   md->sendflag = 1;
   strcpy(md->mcmd, "send");
c2cdbd80
   strcpy(md->target, "tcp:127.0.0.1:5000");
05348f1d
 }
c858e9b6
 
05348f1d
 int main(int argc, char *argv[])
 {
   int i;
   msyncdata md;
 
   if(argc == 1){
     usage();
     exit(1);
a0346c4a
   }
 
05348f1d
   msync_init(&md);
c2cdbd80
   get_envopt(&md);
05348f1d
   parse_opt(argc, argv, optinit(), &md);
   connect_target(&md);
 
   makuo_aliveon(&md);
   makuo_log(&md);
   makuo_exclude(&md);
 
   if(!makuo_file(&md)){
     if(!md.loopflag){
       md.path[0] = 0;
       makuo_send(&md);
     }else{
3eaafa57
       for(i=optind;i<argc;i++){
05348f1d
         strcpy(md.path, argv[i]);
         makuo_send(&md);
3eaafa57
       }
     }
1f4b3de7
   }
05348f1d
   return(makuo_quit(&md));
1f4b3de7
 }
f9cb6348