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