e998ffd016d4a454306b427733a0e5e70acfda3b
[nethome.git] / src / streamfer-client.C
1 #include "safeio.h"
2 #include "socket.h"
3 #include "stringf.h"
4 #include <fcntl.h>
5 #include <sys/stat.h>
6 #include <climits>
7
8 static string get_string(FILE *f,const char *fn,const char *what) {
9   char buf[PATH_MAX];
10   char *got=fgets(buf,sizeof(buf),f);
11   if (got!=buf)
12     fatal("Error reading %s from %s: %m",what,fn);
13   char *s(strchr(buf,'\n'));
14   if (!s)
15     fatal("Stored %s in %s is not newline-terminated",what,fn);
16   *s=0;
17   assert(!s[1]);
18   return buf;
19 }
20
21 // https://stackoverflow.com/a/8615450/2995591
22 #include <glob.h> // glob(), globfree()
23 #include <cstring> // memset()
24 #include <vector>
25 #include <string>
26
27 int main(int argc,char **argv) {
28   if (argc!=1+3)
29     fatal("streamfer-client <host>:<port> <pattern> <last storage file>");
30   int server_fd(socket_connect(string(argv[1]),0/*retries*/));
31   string pattern(argv[2]);
32   write_safe(server_fd,pattern);
33   string last_found;
34   const char *last_stored_fn(argv[3]);
35   FILE *last_stored_f(fopen(last_stored_fn,"r"));
36   uint64_t offset(0);
37   if (!last_stored_f) {
38     if (errno!=ENOENT)
39       fatal("Error opening filename from %s: %m",last_stored_fn);
40   } else {
41     last_found=      get_string(last_stored_f,last_stored_fn,"filename") ;
42     string offsetstr(get_string(last_stored_f,last_stored_fn,"offset"  ));
43     char *end;
44     errno=0;
45     unsigned long ul(strtoul(offsetstr.c_str(),&end,0));
46     if (errno||(end&&*end))
47       fatal("Error converting offset from %s: %s",last_stored_fn,offsetstr.c_str());
48     offset=ul;
49     int gotint(fgetc(last_stored_f));
50     if (gotint!=EOF)
51       fatal("Stored filename in %s has excessive data after filename %s and offset %s",last_stored_fn,last_found.c_str(),offsetstr.c_str());
52     int err(fclose(last_stored_f));
53     assert(!err);
54   }
55   write_safe(server_fd,last_found);
56   write_safe(server_fd,offset);
57   string last_got;
58   read_safe(server_fd,last_got);
59   if (last_got.empty()) {
60     warning("No more files to transfer");
61     exit(EXIT_SUCCESS);
62   }
63   struct timespec mtim;
64   read_safe(server_fd,mtim);
65   int file_fd;
66   string file_name;
67   size_t slash(last_got.find_last_of('/'));
68   if (slash!=string::npos)
69     file_name=last_got.substr(slash+1);
70   else
71     file_name=last_got;
72   if (last_found==last_got) {
73     file_fd=open(file_name.c_str(),O_WRONLY);
74     if (file_fd==-1)
75       fatal("Error opening for write %s: %m",file_name.c_str());
76     off_t got(lseek(file_fd,offset,SEEK_SET));
77     if ((uint64_t)got!=offset)
78       fatal("Error seeking in %s to %zu, got: %zu",file_name.c_str(),(size_t)offset,(size_t)got);
79   } else {
80     offset=0;
81     file_fd=open(file_name.c_str(),O_WRONLY|O_CREAT,0644);
82     if (file_fd==-1)
83       fatal("Error creating %s: %m",file_name.c_str());
84   }
85   uint64_t transferred(transfer(server_fd,"server fd",file_fd,file_name.c_str()));
86   if (!transferred)
87     return EXIT_FAILURE;
88   offset+=transferred;
89   struct timespec mtim2[2];
90   mtim2[0]=mtim; // atime
91   mtim2[1]=mtim; // mtime
92   if (futimens(file_fd,mtim2))
93     fatal("Error setting timestamp of %s: %m",file_name.c_str());
94   if (close(file_fd))
95     fatal("Error closing %s: %m",file_name.c_str());
96   int last_stored_fd(open(last_stored_fn,O_WRONLY|O_CREAT|O_TRUNC,0644));
97   if (last_stored_fd==-1)
98     fatal("Error storing filename to %s: %m",last_stored_fn);
99   write_safe(last_stored_fd,last_got.c_str(),last_got.length());
100   write_safe(last_stored_fd,'\n');
101   string offsetstr(stringf("%zu",(size_t)offset));
102   write_safe(last_stored_fd,offsetstr.c_str(),offsetstr.length());
103   write_safe(last_stored_fd,'\n');
104   if (close(last_stored_fd))
105     fatal("Error closing %s: %m",last_stored_fn);
106   return EXIT_SUCCESS;
107 }