src/: +streamfer*
[nethome.git] / src / streamfer-client.C
diff --git a/src/streamfer-client.C b/src/streamfer-client.C
new file mode 100644 (file)
index 0000000..e998ffd
--- /dev/null
@@ -0,0 +1,107 @@
+#include "safeio.h"
+#include "socket.h"
+#include "stringf.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <climits>
+
+static string get_string(FILE *f,const char *fn,const char *what) {
+  char buf[PATH_MAX];
+  char *got=fgets(buf,sizeof(buf),f);
+  if (got!=buf)
+    fatal("Error reading %s from %s: %m",what,fn);
+  char *s(strchr(buf,'\n'));
+  if (!s)
+    fatal("Stored %s in %s is not newline-terminated",what,fn);
+  *s=0;
+  assert(!s[1]);
+  return buf;
+}
+
+// https://stackoverflow.com/a/8615450/2995591
+#include <glob.h> // glob(), globfree()
+#include <cstring> // memset()
+#include <vector>
+#include <string>
+
+int main(int argc,char **argv) {
+  if (argc!=1+3)
+    fatal("streamfer-client <host>:<port> <pattern> <last storage file>");
+  int server_fd(socket_connect(string(argv[1]),0/*retries*/));
+  string pattern(argv[2]);
+  write_safe(server_fd,pattern);
+  string last_found;
+  const char *last_stored_fn(argv[3]);
+  FILE *last_stored_f(fopen(last_stored_fn,"r"));
+  uint64_t offset(0);
+  if (!last_stored_f) {
+    if (errno!=ENOENT)
+      fatal("Error opening filename from %s: %m",last_stored_fn);
+  } else {
+    last_found=      get_string(last_stored_f,last_stored_fn,"filename") ;
+    string offsetstr(get_string(last_stored_f,last_stored_fn,"offset"  ));
+    char *end;
+    errno=0;
+    unsigned long ul(strtoul(offsetstr.c_str(),&end,0));
+    if (errno||(end&&*end))
+      fatal("Error converting offset from %s: %s",last_stored_fn,offsetstr.c_str());
+    offset=ul;
+    int gotint(fgetc(last_stored_f));
+    if (gotint!=EOF)
+      fatal("Stored filename in %s has excessive data after filename %s and offset %s",last_stored_fn,last_found.c_str(),offsetstr.c_str());
+    int err(fclose(last_stored_f));
+    assert(!err);
+  }
+  write_safe(server_fd,last_found);
+  write_safe(server_fd,offset);
+  string last_got;
+  read_safe(server_fd,last_got);
+  if (last_got.empty()) {
+    warning("No more files to transfer");
+    exit(EXIT_SUCCESS);
+  }
+  struct timespec mtim;
+  read_safe(server_fd,mtim);
+  int file_fd;
+  string file_name;
+  size_t slash(last_got.find_last_of('/'));
+  if (slash!=string::npos)
+    file_name=last_got.substr(slash+1);
+  else
+    file_name=last_got;
+  if (last_found==last_got) {
+    file_fd=open(file_name.c_str(),O_WRONLY);
+    if (file_fd==-1)
+      fatal("Error opening for write %s: %m",file_name.c_str());
+    off_t got(lseek(file_fd,offset,SEEK_SET));
+    if ((uint64_t)got!=offset)
+      fatal("Error seeking in %s to %zu, got: %zu",file_name.c_str(),(size_t)offset,(size_t)got);
+  } else {
+    offset=0;
+    file_fd=open(file_name.c_str(),O_WRONLY|O_CREAT,0644);
+    if (file_fd==-1)
+      fatal("Error creating %s: %m",file_name.c_str());
+  }
+  uint64_t transferred(transfer(server_fd,"server fd",file_fd,file_name.c_str()));
+  if (!transferred)
+    return EXIT_FAILURE;
+  offset+=transferred;
+  struct timespec mtim2[2];
+  mtim2[0]=mtim; // atime
+  mtim2[1]=mtim; // mtime
+  if (futimens(file_fd,mtim2))
+    fatal("Error setting timestamp of %s: %m",file_name.c_str());
+  if (close(file_fd))
+    fatal("Error closing %s: %m",file_name.c_str());
+  int last_stored_fd(open(last_stored_fn,O_WRONLY|O_CREAT|O_TRUNC,0644));
+  if (last_stored_fd==-1)
+    fatal("Error storing filename to %s: %m",last_stored_fn);
+  write_safe(last_stored_fd,last_got.c_str(),last_got.length());
+  write_safe(last_stored_fd,'\n');
+  string offsetstr(stringf("%zu",(size_t)offset));
+  write_safe(last_stored_fd,offsetstr.c_str(),offsetstr.length());
+  write_safe(last_stored_fd,'\n');
+  if (close(last_stored_fd))
+    fatal("Error closing %s: %m",last_stored_fn);
+  return EXIT_SUCCESS;
+}