/* * lftp - file transfer program * * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "trio.h" #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif #include #include #include "PtyShell.h" #include "lftp_pty.h" #include "SignalHook.h" #include "ArgV.h" #include "misc.h" int PtyShell::getfd() { if(fd!=-1 || error() || closed) return fd; int ptyfd,ttyfd; pid_t pid; int pipe0[2]; int pipe1[2]; if(use_pipes) { if(pipe(pipe0)<0) return -1; if(pipe(pipe1)<0) { close(pipe0[0]); close(pipe0[1]); return -1; } } const char *tty_name=open_pty(&ptyfd,&ttyfd); if(!tty_name) { if(!NonFatalError(errno)) error_text.vset(_("pseudo-tty allocation failed: "),strerror(errno),NULL); if(use_pipes) { close(pipe0[0]); close(pipe0[1]); close(pipe1[0]); close(pipe1[1]); } return -1; } struct termios tc; tcgetattr(ttyfd,&tc); tc.c_lflag=0; tc.c_oflag=0; tc.c_iflag=0; tc.c_cc[VMIN]=1; tc.c_cc[VTIME]=0; tcsetattr(ttyfd,TCSANOW,&tc); ProcWait::Signal(false); fflush(stderr); switch(pid=fork()) { case(0): /* child */ close(ptyfd); if(use_pipes) { close(pipe0[1]); close(pipe1[0]); dup2(pipe0[0],0); dup2(pipe1[1],1); if(pipe0[0]>2) close(pipe0[0]); if(pipe1[1]>2) close(pipe1[1]); } else { dup2(ttyfd,0); dup2(ttyfd,1); } dup2(ttyfd,2); if(ttyfd>2) close(ttyfd); /* start new session */ setsid(); /* make the pseudo-tty our controlling tty */ #ifdef TIOCSCTTY /* use ioctl if available. FD 2 is tty even if use_pipes==true */ ioctl(2, TIOCSCTTY, NULL); #else /* otherwise open the tty without O_NOCTTY flag */ ttyfd=open(tty_name, O_RDWR); if(ttyfd>=0) close(ttyfd); #endif SignalHook::RestoreAll(); kill(getpid(),SIGSTOP); if(oldcwd) { if(chdir(oldcwd)==-1) { fprintf(stderr,_("chdir(%s) failed: %s\n"),oldcwd.get(),strerror(errno)); fflush(stderr); _exit(1); } } /* force the messages to be in english */ putenv((char*)"LC_ALL=C"); putenv((char*)"LANG=C"); putenv((char*)"LANGUAGE=C"); if(a) execvp(a->a0(),a->GetVNonConst()); execl("/bin/sh","sh","-c",name.get(),NULL); fprintf(stderr,_("execl(/bin/sh) failed: %s\n"),strerror(errno)); fflush(stderr); _exit(1); case(-1): /* error */ close(ttyfd); close(ptyfd); if(use_pipes) { close(pipe0[0]); close(pipe0[1]); close(pipe1[0]); close(pipe1[1]); } goto out; } /* parent */ if(pg==0) pg=pid; close(ttyfd); fd=ptyfd; fcntl(fd,F_SETFD,FD_CLOEXEC); fcntl(fd,F_SETFL,O_NONBLOCK); if(use_pipes) { close(pipe0[0]); pipe_out=pipe0[1]; close(pipe1[1]); pipe_in=pipe1[0]; fcntl(pipe_in,F_SETFD,FD_CLOEXEC); fcntl(pipe_in,F_SETFL,O_NONBLOCK); fcntl(pipe_out,F_SETFD,FD_CLOEXEC); fcntl(pipe_out,F_SETFL,O_NONBLOCK); } oldcwd.set(0); int info; waitpid(pid,&info,WUNTRACED); w=new ProcWait(pid); out: ProcWait::Signal(true); return fd; } void PtyShell::Init() { xgetcwd_to(oldcwd); pg=0; closed=false; use_pipes=false; pipe_in=-1; pipe_out=-1; } void PtyShell::SetCwd(const char *cwd) { oldcwd.set(cwd); } PtyShell::PtyShell(const char *filter) : FDStream(-1,filter) { Init(); } PtyShell::PtyShell(ArgV *a1) : FDStream(-1,0), a(a1) { Init(); a->CombineTo(name); } PtyShell::~PtyShell() { if(fd!=-1) close(fd); if(pipe_in!=-1) close(pipe_in); if(pipe_out!=-1) close(pipe_out); if(w) { w->Kill(); w.borrow()->Auto(); } } bool PtyShell::Done() { if(w==0) return true; if(fd!=-1) { close(fd); fd=-1; closed=true; } if(w->GetState()!=w->RUNNING) return true; return false; } bool PtyShell::broken() { if(w==0) return false; if(fd==-1) return false; if(w->GetState()!=w->RUNNING) return true; // filter process terminated - pipe is broken return false; }