msync.c
c858e9b6
 /*
  * msync.c
dbdc4e5a
  * Copyright (C) 2008 KLab Inc. 
c858e9b6
  */
 #define _GNU_SOURCE
 #define _FILE_OFFSET_BITS 64
a0346c4a
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
c858e9b6
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <getopt.h>
 #include <netdb.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
a0346c4a
 #include "makuosan.h"
 
 excludeitem *exclude = NULL;
1f4b3de7
 
 void usage()
 {
a0346c4a
   printf("msync version %s (CLI for makuosan)\n", PACKAGE_VERSION);
   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");
   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
 }
 
 excludeitem *add_exclude(char *pattern)
 {
   excludeitem *item = malloc(sizeof(excludeitem));
   item->pattern = malloc(strlen(pattern) + 1);
   strcpy(item->pattern, pattern);
   item->prev    = NULL;
   item->next    = NULL;
   if(exclude){
     exclude->prev = item;
     item->next = exclude;
   }
   exclude = item;
   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));
   hint.ai_family = AF_INET;
   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
   }
   return(-1);
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)
 {
   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
       }
     }
c858e9b6
     switch(read(s, &d, 1)){
       case 0:
         return(p - buff);
       case -1:
         return(-1);
         break;
       default:
         if(d == '\r'){
           break;
         }
         if((d == '\n') && (p != buff)){
           return(p - buff);
         }
         *(p++) = d;
         break;
     }
f9cb6348
   }
c858e9b6
   return(-1); /* over flow */
f9cb6348
 }
 
 int wait_prompt(int s, char *passwd){
1f4b3de7
   int  r;
f9cb6348
   char buff[1024];
 
   while(r = readline(s, buff, sizeof(buff), 1, passwd)){
     if(r == -1){
       /* read error */
       return(-1);
     }
     if(r == -2){
       /* return prompt */
       return(1);
     }
     fprintf(stderr, "%s\n", buff);
   }
   return(0);
 } 
 
 int makuo(int s, char *c)
 {
c858e9b6
   int  r;
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);
   }
   r = wait_prompt(s, NULL);
   if(r == -1){
     fprintf(stderr, "error: can't read socket\n");
f9cb6348
     return(-1);
   }
c858e9b6
   return(r);
 }
 
a0346c4a
 int makuo_log(int s, int l)
c858e9b6
 {
   int r;
   char cmd[256];
   sprintf(cmd, "loglevel %d", l);
   r = makuo(s, cmd);
   if(r == 0){
     fprintf(stderr, "error: remote close\n");
     return(1);
   }
   if(r == -1){
     return(1);
   }
f9cb6348
   return(0);
 }
 
a0346c4a
 int makuo_exclude(int s)
 {
   int r;
   char cmd[1024];
   excludeitem *item;
   for(item=exclude;item;item=item->next){
     sprintf(cmd, "exclude add %s", item->pattern);
     r = makuo(s, cmd);
     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);
 }
 
 
 int makuo_exec(int s, char *cmd)
 { 
   int r = makuo(s, cmd);
   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);
 }
 
 int makuo_quit(int s)
c858e9b6
 { 
   int r = makuo(s, "quit");
a0346c4a
   close(s);
c858e9b6
   if(r == 0){
     return(0); /* success */
   }
   if(r == -1){
     return(1);
   }
   fprintf(stderr, "quit error?!\n");
   return(1);
 }
 
a0346c4a
 int exclude_from(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
   }
   while(r = readline(f, line, sizeof(line), 0, NULL)){
     if(r == -1){
       fprintf(stderr, "file read error: %s\n", filename);
c858e9b6
       close(f);
       return(1);
f9cb6348
     }
a0346c4a
     add_exclude(line);
f9cb6348
   }
a0346c4a
   close(f);
   return(0);
c858e9b6
 }
 
a0346c4a
 int fromfile(int s, char *filename)
c858e9b6
 {
a0346c4a
   int  f;
   int  r;
   char line[256];
c858e9b6
 
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 */
   while(r = readline(f, line, sizeof(line), 0, NULL)){
     if(r == -1){
       fprintf(stderr, "file read error: %s\n", filename);
       close(f);
       return(1);
     }
     if(makuo_exec(s, line)){
       close(f);
       return(1);
     }
   }
   close(f);
   return(makuo_quit(s));
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);
 }
 
c858e9b6
 void defaulttarget(char *target, int size)
f9cb6348
 {
c858e9b6
   char *p = getenv("MSYNC_TARGET");
   strcpy(target, "tcp:127.0.0.1:5000");
   if(p && (strlen(p) < size)){
f9cb6348
     strcpy(target, p);
   }
 }
 
 int main(int argc, char *argv[])
 {
a0346c4a
   int i;
f9cb6348
   int r;
   int s;
 
a0346c4a
   if(argc == 1){
     usage();
     return(1);
   }
 
   /* makuo command */
   char cmd[1024];
   char mcmd[256];
   char mopt[256];
   strcpy(mcmd,"send");
   strcpy(mopt,"");
 
f9cb6348
   /* option */
a0346c4a
   int loopflag = 1;
f9cb6348
   int loglevel = 0;
   char scfile[256];
   char passwd[256];
   char target[256];
1f4b3de7
 
a0346c4a
   /* long option */
   struct option longopt[7];
   memset(longopt, 0, sizeof(longopt));
   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';
 
f9cb6348
   /* default */
1f4b3de7
   scfile[0] = 0;
f9cb6348
   passwd[0] = 0;
c858e9b6
   defaulttarget(target, sizeof(target));
1f4b3de7
 
a0346c4a
   while((r = getopt_long(argc, argv, "c:f:t:K:l:hvrn", longopt, NULL)) != -1){
1f4b3de7
     switch(r){
       case 'h':
         usage();
c858e9b6
         return(0);
 
a0346c4a
       case 'S':
         strcpy(mcmd, "status");
         loopflag = 0;
         break;
 
       case 'M':
         strcpy(mcmd, "members");
         loopflag = 0;
         break;
 
       case 'C':
         strcpy(mcmd, "check");
         break;
 
       case 'E':
         add_exclude(optarg);
         break;
 
       case 'F':
         if(exclude_from(optarg)){
           return(1);
         }
         break;
 
       case 'r':
         strcat(mopt," -r");
         break;
 
       case 'n':
         strcat(mopt," -n");
         break;
 
       case 't':
         strcat(mopt," -t ");
         strcat(mopt,optarg);
         break;
 
       case 'v':
         loglevel++;
         break;
 
c858e9b6
       case 'l':
         loglevel = atoi(optarg);
f9cb6348
         break;
c858e9b6
 
1f4b3de7
       case 'f':
c858e9b6
         if(strlen(optarg) < sizeof(scfile)){
           strcpy(scfile, optarg);
         }else{
           fprintf(stderr, "filename too long\n");
           return(1);
         }
1f4b3de7
         break;
c858e9b6
 
1f4b3de7
       case 'c':
c858e9b6
         if(strlen(optarg) < sizeof(target)){
           strcpy(target, optarg);
         }else{
           fprintf(stderr, "target too long\n");
           return(1);
         }
1f4b3de7
         break;
c858e9b6
 
f9cb6348
       case 'K':
         if(loadpass(optarg, passwd, sizeof(passwd)) == -1){
           return(1);
         }
         break;
 
a0346c4a
       case '?':
         usage();
         return(1);
         break;
     }
f9cb6348
   }
 
1f4b3de7
   s = connect_socket(target);
   if(s == -1){
c858e9b6
     fprintf(stderr, "can't connect %s\n", target);
1f4b3de7
     return(1);
   }
c858e9b6
 
f9cb6348
   r = wait_prompt(s, passwd);
c858e9b6
   if(r == 0){
     fprintf(stderr, "remote socket close\n");
     close(s);
     return(1);
   }
a0346c4a
 
c858e9b6
   if(r == -1){
     fprintf(stderr, "socket read error\n");
     close(s);
     return(1);
   }
f9cb6348
 
a0346c4a
   if(makuo_log(s, loglevel)){
     close(s);
     return(1);
   }
 
   if(makuo_exclude(s)){
c858e9b6
     close(s);
     return(1);
   }
 
   if(scfile[0]){
a0346c4a
     return(fromfile(s, scfile));
   }
 
   if(loopflag && (optind < argc)){
     for(i=optind;i<argc;i++){
       sprintf(cmd, "%s%s %s", mcmd, mopt, argv[i]);
       if(makuo_exec(s, cmd)){
         close(s);
         return(1);
       }
     }
c858e9b6
   }else{
a0346c4a
     sprintf(cmd, "%s%s", mcmd, mopt);
     for(i=optind;i<argc;i++){
       strcat(cmd, " ");
       strcat(cmd, argv[i]);
     }
     if(makuo_exec(s, cmd)){
       close(s);
       return(1);
     }
1f4b3de7
   }
a0346c4a
   return(makuo_quit(s));
1f4b3de7
 }
f9cb6348