/*
* 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