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