Blame src/attach.h

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2013 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
#ifndef ATTACH_H
Packit 8f70b4
#define ATTACH_H
Packit 8f70b4
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h> // for mkdir()
Packit 8f70b4
#include <stdio.h>
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include <locale.h>
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
#include <sys/un.h>
Packit 8f70b4
#if HAVE_SYS_SOCKET_H
Packit 8f70b4
# include <sys/socket.h>
Packit 8f70b4
#elif HAVE_WS2TCPIP_H
Packit 8f70b4
# include <ws2tcpip.h>
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include "SMTask.h"
Packit 8f70b4
#include "Error.h"
Packit 8f70b4
#include "SignalHook.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "passfd.h"
Packit 8f70b4
Packit 8f70b4
#ifndef SUN_LEN
Packit 8f70b4
#define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
class AcceptTermFD : public SMTask
Packit 8f70b4
{
Packit 8f70b4
   int sock;
Packit 8f70b4
   int a_sock;
Packit 8f70b4
   int recv_i;
Packit 8f70b4
   int fds[3];
Packit 8f70b4
   bool accepted;
Packit 8f70b4
   bool detached;
Packit 8f70b4
public:
Packit 8f70b4
   AcceptTermFD() : sock(-1), a_sock(-1), recv_i(0), accepted(false), detached(false) {
Packit 8f70b4
      do_listen();
Packit 8f70b4
   }
Packit 8f70b4
   ~AcceptTermFD() {
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 close(fds[i]);
Packit 8f70b4
      if(sock!=-1) {
Packit 8f70b4
	 close(sock);
Packit 8f70b4
	 unlink(get_sock_path());
Packit 8f70b4
      }
Packit 8f70b4
      if(a_sock!=-1)
Packit 8f70b4
	 close(a_sock);
Packit 8f70b4
   }
Packit 8f70b4
   int Do() {
Packit 8f70b4
      int m=STALL;
Packit 8f70b4
      if(detached)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(accepted) {
Packit 8f70b4
	 char buf;
Packit 8f70b4
	 int res=read(a_sock,&buf,1);
Packit 8f70b4
	 if(res==-1 && E_RETRY(errno)) {
Packit 8f70b4
	    Block(a_sock,POLLIN);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(res<=0) {
Packit 8f70b4
	    detached=true;
Packit 8f70b4
	    close(a_sock);
Packit 8f70b4
	    a_sock=-1;
Packit 8f70b4
	    raise(SIGHUP);
Packit 8f70b4
	 }
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      if(a_sock==-1) {
Packit 8f70b4
	 if(sock==-1)
Packit 8f70b4
	    do_listen();
Packit 8f70b4
	 if(sock==-1) {
Packit 8f70b4
	    TimeoutS(1);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!Ready(sock,POLLIN)) {
Packit 8f70b4
	    Block(sock,POLLIN);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 struct sockaddr_un sun_addr;
Packit 8f70b4
	 socklen_t sa_len=sizeof(sun_addr);
Packit 8f70b4
	 a_sock=accept(sock,(sockaddr*)&sun_addr,&sa_len);
Packit 8f70b4
	 if(a_sock==-1 && E_RETRY(errno)) {
Packit 8f70b4
	    Block(sock,POLLIN);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(a_sock==-1) {
Packit 8f70b4
	    perror("accept");
Packit 8f70b4
	    do_listen();
Packit 8f70b4
	    TimeoutS(1);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 close(sock);
Packit 8f70b4
	 sock=-1;
Packit 8f70b4
	 int fl=fcntl(a_sock,F_GETFL);
Packit 8f70b4
	 fcntl(a_sock,F_SETFL,fl|O_NONBLOCK);
Packit 8f70b4
	 fcntl(a_sock,F_SETFD,FD_CLOEXEC);
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      while(recv_i<=2) {
Packit 8f70b4
	 int fd=recvfd(a_sock,0);
Packit 8f70b4
	 if(fd==-1 && E_RETRY(errno)) {
Packit 8f70b4
	    Block(a_sock,POLLIN);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(fd==-1)
Packit 8f70b4
	 {
Packit 8f70b4
	    perror("recvfd");
Packit 8f70b4
	    do_listen();
Packit 8f70b4
	    TimeoutS(1);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 fcntl(fd,F_SETFD,FD_CLOEXEC);
Packit 8f70b4
	 fds[recv_i]=fd;
Packit 8f70b4
	 ++recv_i;
Packit 8f70b4
      }
Packit 8f70b4
      printf(_("[%u] Attached to terminal %s. %s\n"),(unsigned)getpid(),ttyname(fds[1]),now.IsoDateTime());
Packit 8f70b4
      fflush(stdout);
Packit 8f70b4
      fflush(stderr);
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 dup2(fds[i],i);
Packit 8f70b4
	 if(fds[i]>=recv_i)
Packit 8f70b4
	    close(fds[i]);
Packit 8f70b4
      }
Packit 8f70b4
      close(sock);
Packit 8f70b4
      sock=-1;
Packit 8f70b4
      unlink(get_sock_path());
Packit 8f70b4
      accepted=true;
Packit 8f70b4
      printf(_("[%u] Attached to terminal.\n"),(unsigned)getpid());
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   static xstring& get_sock_path(int pid=0) {
Packit 8f70b4
      if(!pid)
Packit 8f70b4
	 pid=getpid();
Packit 8f70b4
      const char *home=get_lftp_data_dir();
Packit 8f70b4
      mkdir(xstring::format("%s/bg",home),0700);
Packit 8f70b4
      return xstring::format("%s/bg/%s-%d",home,get_nodename(),pid);
Packit 8f70b4
   }
Packit 8f70b4
   void do_listen() {
Packit 8f70b4
      const char *path=get_sock_path();
Packit 8f70b4
      unlink(path);
Packit 8f70b4
      if(sock>=0)
Packit 8f70b4
	 close(sock);
Packit 8f70b4
      if(a_sock>=0) {
Packit 8f70b4
	 close(a_sock);
Packit 8f70b4
	 a_sock=-1;
Packit 8f70b4
      }
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 close(fds[i]);
Packit 8f70b4
      recv_i=0;
Packit 8f70b4
      accepted=false;
Packit 8f70b4
      detached=false;
Packit 8f70b4
      sock=socket(AF_UNIX,SOCK_STREAM,0);
Packit 8f70b4
      if(sock!=-1) {
Packit 8f70b4
	 int fl=fcntl(sock,F_GETFL);
Packit 8f70b4
	 fcntl(sock,F_SETFL,fl|O_NONBLOCK);
Packit 8f70b4
	 fcntl(sock,F_SETFD,FD_CLOEXEC);
Packit 8f70b4
	 struct sockaddr_un sun_addr;
Packit 8f70b4
	 memset(&sun_addr,0,sizeof(sun_addr));
Packit 8f70b4
	 sun_addr.sun_family=AF_UNIX;
Packit 8f70b4
	 strncpy(sun_addr.sun_path,path,sizeof(sun_addr.sun_path));
Packit 8f70b4
	 if(bind(sock,(sockaddr*)&sun_addr,SUN_LEN(&sun_addr))==-1) {
Packit 8f70b4
	    perror("bind");
Packit 8f70b4
	    close(sock);
Packit 8f70b4
	    sock=-1;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(sock>=0)
Packit 8f70b4
	    listen(sock,1);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   bool Accepted() { return accepted; }
Packit 8f70b4
   void Detach() { do_listen(); }
Packit 8f70b4
   bool Detached() { return detached; }
Packit 8f70b4
};
Packit 8f70b4
Packit 8f70b4
class SendTermFD : public SMTask
Packit 8f70b4
{
Packit 8f70b4
   static pid_t pass_pid;
Packit 8f70b4
   static void pass_sig(int s) {
Packit 8f70b4
      kill(pass_pid,s);
Packit 8f70b4
   }
Packit 8f70b4
   Ref<Error> error;
Packit 8f70b4
   pid_t pid;
Packit 8f70b4
   int sock;
Packit 8f70b4
   bool connected;
Packit 8f70b4
   bool sent;
Packit 8f70b4
   int send_i;
Packit 8f70b4
   bool detached;
Packit 8f70b4
public:
Packit 8f70b4
   SendTermFD(pid_t p) : pid(p), sock(-1), connected(false), sent(false), send_i(0), detached(false) {}
Packit 8f70b4
   ~SendTermFD() {
Packit 8f70b4
      if(sock>=0)
Packit 8f70b4
	 close(sock);
Packit 8f70b4
   }
Packit 8f70b4
   int Do() {
Packit 8f70b4
      int m=STALL;
Packit 8f70b4
      if(error || detached)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(sent) {
Packit 8f70b4
	 char buf;
Packit 8f70b4
	 int res=read(sock,&buf,1);
Packit 8f70b4
	 if(res==-1 && E_RETRY(errno)) {
Packit 8f70b4
	    Block(sock,POLLIN);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(res<=0) {
Packit 8f70b4
	    detached=true;
Packit 8f70b4
	    close(sock);
Packit 8f70b4
	    sock=-1;
Packit 8f70b4
	    SignalHook::DoCount(SIGINT);
Packit 8f70b4
	    SignalHook::Restore(SIGQUIT);
Packit 8f70b4
	    SignalHook::DoCount(SIGTSTP);
Packit 8f70b4
	    SignalHook::Restore(SIGWINCH);
Packit 8f70b4
	 }
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(sock==-1) {
Packit 8f70b4
	 sock=socket(AF_UNIX,SOCK_STREAM,0);
Packit 8f70b4
	 if(sock==-1) {
Packit 8f70b4
	    if(NonFatalError(errno))
Packit 8f70b4
	    {
Packit 8f70b4
	       TimeoutS(1);
Packit 8f70b4
	       return m;
Packit 8f70b4
	    }
Packit 8f70b4
	    error=Error::Fatal(xstring::format("socket(): %s",strerror(errno)));
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 int fl=fcntl(sock,F_GETFL);
Packit 8f70b4
	 fcntl(sock,F_SETFL,fl|O_NONBLOCK);
Packit 8f70b4
	 fcntl(sock,F_SETFD,FD_CLOEXEC);
Packit 8f70b4
	 connected=false;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(!connected) {
Packit 8f70b4
	 struct sockaddr_un sun_addr;
Packit 8f70b4
	 memset(&sun_addr,0,sizeof(sun_addr));
Packit 8f70b4
	 sun_addr.sun_family=AF_UNIX;
Packit 8f70b4
	 const char *path=AcceptTermFD::get_sock_path(pid);
Packit 8f70b4
	 strncpy(sun_addr.sun_path,path,sizeof(sun_addr.sun_path));
Packit 8f70b4
	 int res=connect(sock,(sockaddr*)&sun_addr,SUN_LEN(&sun_addr));
Packit 8f70b4
	 if(res==-1 && !NonFatalError(errno)) {
Packit 8f70b4
	    error=Error::Fatal(xstring::format("connect(%s): %s",path,strerror(errno)));
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(res==-1) {
Packit 8f70b4
	    Block(sock,POLLOUT);
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
	 connected=true;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      while(send_i<=2) {
Packit 8f70b4
	 if(sendfd(sock,send_i)<0) {
Packit 8f70b4
	    if(NonFatalError(errno)) {
Packit 8f70b4
	       Block(sock,POLLOUT);
Packit 8f70b4
	       return m;
Packit 8f70b4
	    }
Packit 8f70b4
	    error=Error::Fatal(xstring::format("sendfd: %s",strerror(errno)));
Packit 8f70b4
	    close(sock);
Packit 8f70b4
	    sock=-1;
Packit 8f70b4
	    return 0;
Packit 8f70b4
	 }
Packit 8f70b4
	 ++send_i;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      sent=true;
Packit 8f70b4
      pass_pid=pid;
Packit 8f70b4
      if(isatty(0)) {
Packit 8f70b4
	 SignalHook::Handle(SIGINT,pass_sig);
Packit 8f70b4
	 SignalHook::Handle(SIGQUIT,pass_sig);
Packit 8f70b4
	 SignalHook::Handle(SIGTSTP,pass_sig);
Packit 8f70b4
	 SignalHook::Handle(SIGWINCH,pass_sig);
Packit 8f70b4
      }
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   bool Done() { return error || detached; }
Packit 8f70b4
   bool Failed() { return error; }
Packit 8f70b4
   const char *ErrorText() { return error->Text(); }
Packit 8f70b4
};
Packit 8f70b4
#endif