Blame src/PtyShell.cc

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