Blame src/PtyShell.cc

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
}