1 /*
2 * lftp - file transfer program
3 *
4 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include "trio.h"
23 #include <unistd.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <fcntl.h>
27 #include <utime.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <stddef.h>
31
32 #include "Filter.h"
33 #include "SignalHook.h"
34 #include "ArgV.h"
35 #include "misc.h"
36 #include "FileSet.h"
37 #include "ResMgr.h"
38 #include "log.h"
39
40 #ifndef O_BINARY
41 # define O_BINARY 0
42 #endif
43
FDStream(int new_fd,const char * new_name)44 FDStream::FDStream(int new_fd,const char *new_name)
45 : close_when_done(false), closed(false), fd(new_fd), name(new_name?expand_home_relative(new_name):0), status(0) {}
FDStream()46 FDStream::FDStream()
47 : close_when_done(false), closed(false), fd(-1), status(0) {}
48
MakeErrorText(int e)49 void FDStream::MakeErrorText(int e)
50 {
51 if(!e)
52 e=errno;
53 if(NonFatalError(e))
54 return; // not a serious error - can be retried
55 error_text.vset(name.get(),": ",strerror(e),NULL);
56 revert_backup();
57 }
SetCwd(const char * new_cwd)58 void FDStream::SetCwd(const char *new_cwd)
59 {
60 cwd.set(new_cwd);
61 }
DoCloseFD()62 void FDStream::DoCloseFD()
63 {
64 if(fd!=-1) {
65 if(close_when_done) {
66 close(fd);
67 Log::global->Format(11,"closed FD %d\n",fd);
68 }
69 fd=-1;
70 }
71 }
SetFD(int new_fd,bool c)72 void FDStream::SetFD(int new_fd,bool c)
73 {
74 DoCloseFD();
75 fd=new_fd;
76 close_when_done=c;
77 }
Done()78 bool FDStream::Done()
79 {
80 if(closed)
81 return true;
82 DoCloseFD();
83 closed=true;
84 return true;
85 }
~FDStream()86 FDStream::~FDStream()
87 {
88 DoCloseFD();
89 };
90
Parent(int * p)91 void OutputFilter::Parent(int *p)
92 {
93 close(p[0]);
94 SetFD(p[1],true);
95 }
96
Parent(int * p)97 void InputFilter::Parent(int *p)
98 {
99 close(p[1]);
100 SetFD(p[0],true);
101 }
Child(int * p)102 void OutputFilter::Child(int *p)
103 {
104 close(p[1]);
105 if(p[0]!=0)
106 {
107 dup2(p[0],0);
108 close(p[0]);
109 }
110 if(second_fd!=-1)
111 {
112 if(second_fd!=1)
113 {
114 dup2(second_fd,1);
115 close(second_fd);
116 }
117 int fl=fcntl(1,F_GETFL);
118 fcntl(1,F_SETFL,fl&~O_NONBLOCK);
119 }
120 }
Child(int * p)121 void InputFilter::Child(int *p)
122 {
123 close(p[0]);
124 if(p[1]!=1)
125 {
126 dup2(p[1],1);
127 close(p[1]);
128 }
129 if(second_fd!=-1)
130 {
131 if(second_fd!=0)
132 {
133 dup2(second_fd,0);
134 close(second_fd);
135 }
136 int fl=fcntl(0,F_GETFL);
137 fcntl(0,F_SETFL,fl&~O_NONBLOCK);
138 }
139 }
140
getfd()141 int OutputFilter::getfd()
142 {
143 if(fd!=-1 || error() || closed)
144 return fd;
145
146 if(second && second_fd==-1)
147 {
148 second_fd=second->getfd();
149 if(second_fd==-1)
150 {
151 if(second->error())
152 error_text.set(second->error_text);
153 return -1;
154 }
155 if(pg==0)
156 pg=second->GetProcGroup();
157 }
158
159 int p[2];
160 pid_t pid;
161
162 if(pipe(p)==-1)
163 {
164 if(NonFatalError(errno))
165 return -1;
166 error_text.vset(_("pipe() failed: "),strerror(errno),NULL);
167 return -1;
168 }
169
170 ProcWait::Signal(false);
171
172 bool had_pg=(pg!=0);
173
174 fflush(stdout);
175 fflush(stderr);
176 switch(pid=fork())
177 {
178 case(0): /* child */
179 setpgid(0,pg);
180 kill(getpid(),SIGSTOP);
181 SignalHook::RestoreAll();
182 Child(p);
183 if(stderr_to_stdout)
184 dup2(1,2);
185 if(stdout_to_null)
186 {
187 close(1);
188 int null=open("/dev/null",O_RDWR);
189 if(null==-1)
190 perror("open(\"/dev/null\")");
191 else if(null==0) {
192 if(dup(0)==-1)
193 perror("dup");
194 }
195 }
196 if(cwd)
197 {
198 if(chdir(cwd)==-1)
199 {
200 fprintf(stderr,_("chdir(%s) failed: %s\n"),cwd.get(),strerror(errno));
201 fflush(stderr);
202 _exit(1);
203 }
204 }
205 if(a)
206 {
207 execvp(a->a0(),a->GetVNonConst());
208 fprintf(stderr,_("execvp(%s) failed: %s\n"),a->a0(),strerror(errno));
209 }
210 else
211 {
212 execl("/bin/sh","sh","-c",name.get(),NULL);
213 fprintf(stderr,_("execl(/bin/sh) failed: %s\n"),strerror(errno));
214 }
215 fflush(stderr);
216 _exit(1);
217 case(-1): /* error */
218 close(p[0]);
219 close(p[1]);
220 goto out;
221 }
222
223 if(pg==0)
224 pg=pid;
225
226 /* parent */
227 Parent(p);
228
229 fcntl(fd,F_SETFD,FD_CLOEXEC);
230 fcntl(fd,F_SETFL,O_NONBLOCK);
231
232 // wait until the child stops.
233 int info;
234 waitpid(pid,&info,WUNTRACED);
235
236 w=new ProcWait(pid);
237
238 if(had_pg)
239 kill(pid,SIGCONT);
240 out:
241 ProcWait::Signal(true);
242 return fd;
243 }
244
Init()245 void OutputFilter::Init()
246 {
247 w=0;
248 second_fd=-1;
249 xgetcwd_to(cwd);
250 pg=0;
251 stderr_to_stdout=false;
252 stdout_to_null=false;
253 if(a)
254 a->CombineTo(name);
255 }
256
OutputFilter(const char * filter,int new_second_fd)257 OutputFilter::OutputFilter(const char *filter,int new_second_fd)
258 : FDStream(-1,filter), second(my_second), second_fd(new_second_fd)
259 {
260 Init();
261 }
262
OutputFilter(const char * filter,FDStream * new_second)263 OutputFilter::OutputFilter(const char *filter,FDStream *new_second)
264 : FDStream(-1,filter), my_second(new_second), second(my_second)
265 {
266 Init();
267 }
OutputFilter(const char * filter,const Ref<FDStream> & new_second)268 OutputFilter::OutputFilter(const char *filter,const Ref<FDStream>& new_second)
269 : FDStream(-1,filter), second(new_second)
270 {
271 Init();
272 }
OutputFilter(ArgV * a1,int new_second_fd)273 OutputFilter::OutputFilter(ArgV *a1,int new_second_fd)
274 : FDStream(-1,0), a(a1), second(my_second), second_fd(new_second_fd)
275 {
276 Init();
277 }
278
OutputFilter(ArgV * a1,FDStream * new_second)279 OutputFilter::OutputFilter(ArgV *a1,FDStream *new_second)
280 : FDStream(-1,0), a(a1), my_second(new_second), second(my_second)
281 {
282 Init();
283 }
OutputFilter(ArgV * a1,const Ref<FDStream> & new_second)284 OutputFilter::OutputFilter(ArgV *a1,const Ref<FDStream>& new_second)
285 : FDStream(-1,0), a(a1), second(new_second)
286 {
287 Init();
288 }
289
~OutputFilter()290 OutputFilter::~OutputFilter()
291 {
292 if(w)
293 w->Auto();
294 }
295
Done()296 bool OutputFilter::Done()
297 {
298 if(!FDStream::Done())
299 return false;
300 if(w==0)
301 return true;
302 if(w->GetState()!=w->RUNNING)
303 {
304 if(my_second)
305 return my_second->Done();
306 return true;
307 }
308 return false;
309 }
broken()310 bool OutputFilter::broken()
311 {
312 if(w==0)
313 return false;
314 if(fd==-1)
315 return false;
316 if(w->GetState()!=w->RUNNING)
317 return true; // filter process terminated - pipe is broken
318 return false;
319 }
Kill(int sig)320 void OutputFilter::Kill(int sig)
321 {
322 if(w)
323 w->Kill(sig);
324 if(second)
325 second->Kill(sig);
326 }
usesfd(int n_fd)327 bool OutputFilter::usesfd(int n_fd)
328 {
329 if(FDStream::usesfd(n_fd))
330 return true;
331 if(second_fd!=-1 && n_fd==second_fd)
332 return true;
333 if(second)
334 return second->usesfd(n_fd);
335 return n_fd<=2;
336 }
337
338
339 #define NO_MODE ((mode_t)-1)
340
setmtime(const FileTimestamp & ts)341 void FileStream::setmtime(const FileTimestamp &ts)
342 {
343 getfd(); // this might create the file... But can fail retriably. FIXME.
344
345 // skip the time update if the timestamp is already accurate enough.
346 struct stat st;
347 if(fstat(fd,&st)!=-1 && labs(st.st_mtime-ts)<=ts.ts_prec)
348 return;
349
350 struct utimbuf ut;
351 ut.actime=ut.modtime=ts;
352 utime(full_name,&ut);
353 }
FileStream(const char * fname,int new_mode)354 FileStream::FileStream(const char *fname,int new_mode)
355 : FDStream(-1,fname), mode(new_mode), create_mode(0664),
356 do_lock(ResMgr::QueryBool("file:use-lock",0)),
357 no_keep_backup(false),
358 old_file_mode(NO_MODE)
359 {
360 if(name[0]=='/')
361 full_name.set(name);
362 else
363 {
364 xgetcwd_to(cwd);
365 full_name.set(dir_file(cwd,name));
366 }
367 }
~FileStream()368 FileStream::~FileStream()
369 {
370 }
remove()371 void FileStream::remove()
372 {
373 ::remove(full_name);
374 }
remove_if_empty()375 void FileStream::remove_if_empty()
376 {
377 if(!full_name)
378 return;
379 struct stat st;
380 int res=stat(full_name,&st);
381 if(res!=-1 && st.st_size==0)
382 remove();
383 }
384
revert_backup()385 void FileStream::revert_backup()
386 {
387 if(backup_file) {
388 rename(backup_file,full_name);
389 backup_file.unset();
390 }
391 }
remove_backup()392 void FileStream::remove_backup()
393 {
394 if(backup_file && (no_keep_backup || !ResMgr::QueryBool("xfer:keep-backup",0))) {
395 ::remove(backup_file);
396 backup_file.unset();
397 }
398 if(old_file_mode!=NO_MODE)
399 chmod(full_name,old_file_mode);
400 }
401
getfd()402 int FileStream::getfd()
403 {
404 if(fd!=-1 || error() || closed)
405 return fd;
406
407 bool clobber=!(mode&O_EXCL);
408 bool truncate=(mode&O_TRUNC);
409
410 struct stat st;
411 if((!clobber || truncate) && stat(full_name,&st)!=-1 && st.st_size>0 && S_ISREG(st.st_mode))
412 {
413 if(!clobber)
414 {
415 error_text.vset(name.get(),": ",_("file already exists and xfer:clobber is unset"),NULL);
416 return -1;
417 }
418 if(truncate && ResMgr::QueryBool("xfer:make-backup",0))
419 {
420 /* rename old file if exists and size>0 */
421 xstring_ca suffix(xstrftime(ResMgr::Query("xfer:backup-suffix",0),SMTask::now));
422 backup_file.vset(full_name.get(),suffix.get(),NULL);
423 if(rename(full_name,backup_file)!=0)
424 backup_file.set(0);
425 else
426 create_mode=old_file_mode=st.st_mode;
427 }
428 }
429
430 int new_fd=open(full_name,mode|O_NONBLOCK|O_BINARY,create_mode);
431 if(new_fd==-1)
432 {
433 MakeErrorText();
434 return -1;
435 }
436 Log::global->Format(11,"opened FD %d (%s)\n",new_fd,full_name.get());
437 SetFD(new_fd,true);
438 fcntl(fd,F_SETFD,FD_CLOEXEC);
439 if(do_lock && !(mode&O_APPEND)) {
440 struct flock lk;
441 lk.l_type=((mode&3)==0)?F_RDLCK:F_WRLCK;
442 lk.l_whence=SEEK_SET;
443 lk.l_start=0;
444 lk.l_len=0;
445 if(fcntl(fd,F_SETLKW,&lk)==-1) {
446 MakeErrorText();
447 DoCloseFD();
448 return -1;
449 }
450 }
451 return fd;
452 }
453
can_seek()454 bool FileStream::can_seek()
455 {
456 if(mode&O_APPEND)
457 return false; // whatever we seek, the writes will go to end of file.
458 return true;
459 }
460
get_size()461 off_t FileStream::get_size()
462 {
463 struct stat st;
464 if(-1==(fd==-1?stat(full_name,&st):fstat(fd,&st)))
465 {
466 if(errno==ENOENT)
467 return 0; // assume non-existent files to be empty.
468 return -1;
469 }
470 return st.st_size;
471 }
472
473 #include "SMTask.h"
NonFatalError(int err)474 bool FDStream::NonFatalError(int err)
475 {
476 if(err==EDQUOT || err==ENOSPC)
477 {
478 struct stat st;
479 if(fd>=0 && fstat(fd,&st)!=-1 && st.st_nlink==0)
480 return false;
481 }
482 bool non_fatal=SMTask::NonFatalError(err);
483 if(non_fatal)
484 set_status(strerror(err));
485 else
486 clear_status();
487 return non_fatal;
488 }
489