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