ce4e92384ac79f86c79b8d41e9bbb7b785b8e09a
[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 static void last_stored_write(const char *last_stored_fn,const string &filename,uint64_t offset) {
22   int last_stored_fd(open(last_stored_fn,O_WRONLY|O_CREAT|O_TRUNC,0644));
23   if (last_stored_fd==-1)
24     fatal("Error storing filename to %s: %m",last_stored_fn);
25   write_safe(last_stored_fd,filename.c_str(),filename.length());
26   write_safe(last_stored_fd,'\n');
27   string offsetstr(stringf("%zu",(size_t)offset));
28   write_safe(last_stored_fd,offsetstr.c_str(),offsetstr.length());
29   write_safe(last_stored_fd,'\n');
30   if (close(last_stored_fd))
31     fatal("Error closing %s: %m",last_stored_fn);
32 }
33
34 static string my_basename(const string &path) {
35   size_t slash(path.find_last_of('/'));
36   if (slash!=string::npos)
37     return path.substr(slash+1);
38   return path;
39 }
40
41 int main(int argc,char **argv) {
42   if (argc!=1+3)
43     fatal("streamfer-client <host>:<port> <pattern> <last storage file>");
44   int server_fd(socket_connect(string(argv[1]),0/*retries*/));
45   string pattern(argv[2]);
46   write_safe(server_fd,pattern);
47   string last_found;
48   const char *last_stored_fn(argv[3]);
49   FILE *last_stored_f(fopen(last_stored_fn,"r"));
50   uint64_t offset(0);
51   if (!last_stored_f) {
52     if (errno!=ENOENT)
53       fatal("Error opening filename from %s: %m",last_stored_fn);
54   } else {
55     last_found=      get_string(last_stored_f,last_stored_fn,"filename") ;
56     string offsetstr(get_string(last_stored_f,last_stored_fn,"offset"  ));
57     char *end;
58     errno=0;
59     unsigned long ul(strtoul(offsetstr.c_str(),&end,0));
60     if (errno||(end&&*end))
61       fatal("Error converting offset from %s: %s",last_stored_fn,offsetstr.c_str());
62     offset=ul;
63     int gotint(fgetc(last_stored_f));
64     if (gotint!=EOF)
65       fatal("Stored filename in %s has excessive data after filename %s and offset %s",last_stored_fn,last_found.c_str(),offsetstr.c_str());
66     int err(fclose(last_stored_f));
67     assert(!err);
68   }
69   write_safe(server_fd,last_found);
70   write_safe(server_fd,offset);
71   { struct stat statbuf;
72     string last_found_basename(my_basename(last_found));
73     if (stat(last_found_basename.c_str(),&statbuf)) {
74       static const struct timespec mtim_zero{};
75       write_safe(server_fd,mtim_zero);
76     } else
77       write_safe(server_fd,statbuf.st_mtim);
78   }
79   string last_got;
80   try {
81     read_safe(server_fd,last_got);
82   } catch (SafeIOError) {
83     fatal("SafeIOError reading from the server %s",argv[1]);
84   }
85   if (last_got.empty()) {
86     warning("No more files to transfer");
87     exit(EXIT_SUCCESS);
88   }
89   struct timespec mtim;
90   read_safe(server_fd,mtim);
91   int file_fd;
92   string file_name(my_basename(last_got));;
93   if (last_found==last_got) {
94     file_fd=open(file_name.c_str(),O_WRONLY);
95     if (file_fd==-1)
96       fatal("Error opening for write %s: %m",file_name.c_str());
97     off_t got(lseek(file_fd,offset,SEEK_SET));
98     if ((uint64_t)got!=offset)
99       fatal("Error seeking in %s to %zu, got: %zu",file_name.c_str(),(size_t)offset,(size_t)got);
100   } else {
101     offset=0;
102     file_fd=open(file_name.c_str(),O_WRONLY|O_CREAT,0644);
103     if (file_fd==-1)
104       fatal("Error creating %s: %m",file_name.c_str());
105   }
106   last_stored_write(last_stored_fn,last_got,offset);
107   uint64_t transferred(transfer(server_fd,"server fd",file_fd,file_name.c_str()));
108   if (!transferred) {
109     struct stat statbuf;
110     int err(fstat(file_fd,&statbuf));
111     assert(!err);
112     if (memcmp(&mtim,&statbuf.st_mtim,sizeof(mtim))==0)
113       return EXIT_FAILURE;
114   }
115   offset+=transferred;
116   struct timespec mtim2[2];
117   mtim2[0]=mtim; // atime
118   mtim2[1]=mtim; // mtime
119   if (futimens(file_fd,mtim2))
120     fatal("Error setting timestamp of %s: %m",file_name.c_str());
121   if (close(file_fd))
122     fatal("Error closing %s: %m",file_name.c_str());
123   last_stored_write(last_stored_fn,last_got,offset);
124   return EXIT_SUCCESS;
125 }