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