12 // https://stackoverflow.com/a/8615450/2995591
13 #include <glob.h> // glob(),globfree()
14 #include <cstring> // memset()
18 static std::vector<std::string> cxxglob(const std::string pattern) {
20 memset(&glob_result,0,sizeof(glob_result));
21 int return_value=glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
23 fatal("glob() failed with return_value %s",return_value);
24 vector<string> filenames;
25 filenames.reserve(glob_result.gl_pathc);
26 for (size_t i = 0; i < glob_result.gl_pathc; ++i)
27 filenames.push_back(string(glob_result.gl_pathv[i]));
28 globfree(&glob_result);
33 static bool fd_is_open(const char *execname,const char *fn) {
34 const char slashproc[]("/proc");
35 DIR *dir(opendir(slashproc));
37 fatal("Cannot opendir %s: %m",slashproc);
41 const struct dirent *de=readdir(dir);
44 fatal("Cannot readdir %s: %m",slashproc);
47 if (!isdigit(de->d_name[0]))
51 ssize_t got(readlinkat(dirfd(dir),stringf("%s/exe",de->d_name).c_str(),buf,sizeof(buf)));
52 if (got==-1||got==sizeof(buf))
55 char *s=strrchr(buf,'/');
58 if (strcmp(s+1,execname)!=0)
61 string procpidfd(stringf("/proc/%s/fd",de->d_name));
62 DIR *fddir(opendir(procpidfd.c_str()));
64 fatal("Cannot opendir %s: %m",procpidfd.c_str());
67 const struct dirent *de=readdir(fddir);
70 fatal("Cannot readdir %s: %m",procpidfd.c_str());
73 if (!isdigit(de->d_name[0]))
76 ssize_t got(readlinkat(dirfd(fddir),de->d_name,buf,sizeof(buf)));
77 if (got==-1||got==sizeof(buf))
80 if (strcmp(buf,fn)==0) {
86 fatal("Cannot closedir %s: %m",procpidfd.c_str());
91 fatal("Cannot closedir %s: %m",slashproc);
95 int main(int argc,char **argv) {
96 static struct sigaction sigchld;
97 sigchld.sa_handler=SIG_DFL;
98 sigchld.sa_flags=SA_NOCLDWAIT;
99 int err(sigaction(SIGCHLD,&sigchld,nullptr));
102 if (argc!=1+2&&argc!=1+3)
103 fatal("streamfer-server [<listen-host>:]<listen-port> <prefix> [follow-fd-of-executable-basename]");
105 if (argc>=1+2&&*argv[2])
107 const char *execname(nullptr);
110 int listen_fd(socket_bind(argv[1]));
113 client_fd=socket_accept(listen_fd,[&](int client_fd,string addr) {
114 warning("%d:%s",client_fd,addr.c_str());
120 int err(close(client_fd));
123 err=close(listen_fd);
126 string pattern(read_safe_string(client_fd));
127 std::vector<std::string> matched(cxxglob(pattern));
128 for (size_t ix=0;ix<matched.size()-1;++ix) {
129 const std::string &a(matched[ix ]);
130 const std::string &b(matched[ix+1]);
131 int err(strcmp(a.c_str(),b.c_str()));
133 fatal("glob: strcmp(\"%s\",\"%s\")=%d",a.c_str(),b.c_str(),err);
135 string last(read_safe_string(client_fd));
136 size_t lastix(SIZE_MAX);
137 for (size_t ix=0;ix<matched.size();++ix) {
138 const std::string &member(matched[ix]);
139 if (strcmp(last.c_str(),member.c_str())>0)
140 assert(lastix==SIZE_MAX);
141 else if (lastix==SIZE_MAX)
144 if (lastix==SIZE_MAX)
145 fatal("Requested too new file");
147 read_safe(client_fd,offset);
148 struct timespec mtim;
149 read_safe(client_fd,mtim);
153 for (;lastix<matched.size();file_fd=-1,++lastix) {
154 fnp=&matched[lastix];
155 const string &fn(*fnp);
156 file_fd=open(fn.c_str(),O_RDONLY);
159 fatal("Cannot open %s: %m",fn.c_str());
162 int err(fstat(file_fd,&statbuf));
164 if (offset<(uint64_t)statbuf.st_size)
166 static const struct timespec mtim_zero{};
167 if (memcmp(&mtim,&mtim_zero,sizeof(mtim))!=0&&memcmp(&mtim,&statbuf.st_mtim,sizeof(mtim))!=0)
169 if (offset>(uint64_t)statbuf.st_size)
170 warning("File %s has transferred %zu < %zu which is its size",fn.c_str(),(size_t)offset,(size_t)statbuf.st_size);
171 if (lastix==matched.size()-1&&execname)
179 write_safe(client_fd,empty);
180 fatal("No more files to transfer");
182 const string &fn(*fnp);
183 const char *fn_canon(nullptr);
184 if (!prefix.empty()||execname) {
185 fn_canon=realpath(fn.c_str(),nullptr);
187 fatal("realpath %s: %m",fn.c_str());
191 if (!prefix.empty()&&strncmp(prefix.c_str(),fn_canon,prefix.length())!=0)
192 fatal("prefix=\"%s\" realpath=\"%s\"",prefix.c_str(),fn_canon);
193 warning("%s @%zu",fn.c_str(),(size_t)offset);
194 write_safe(client_fd,fn);
195 write_safe(client_fd,statbuf.st_mtim);
196 off_t got(lseek(file_fd,offset,SEEK_SET));
197 assert((uint64_t)got==offset);
200 fds.events=POLLIN|POLLPRI|POLLRDHUP;
202 transfer(file_fd,fn.c_str(),client_fd,"client fd");
203 if (!fn_canon||!fd_is_open(execname,fn_canon))
205 int err(poll(&fds,1,1000/*ms*/));
207 fatal("poll client fd: %m");
209 fatal("poll client fd: revents=0x%x",fds.revents);