#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <signal.h>
#include <linux/wait.h>
#include <sys/syscall.h>
#include <unistd.h>
#define die_sys(format) Perl_croak(aTHX_ format, strerror(errno))
static SV* S_io_fdopen(pTHX_ int fd, const char* classname, char type) {
PerlIO* pio = PerlIO_fdopen(fd, "r");
GV* gv = newGVgen(classname ? classname : "Linux::FD::Pid");
SV* ret = newRV_noinc((SV*)gv);
IO* io = GvIOn(gv);
IoTYPE(io) = type;
IoIFP(io) = pio;
IoOFP(io) = pio;
if (classname) {
HV* stash = gv_stashpv(classname, FALSE);
sv_bless(ret, stash);
}
return ret;
}
#define io_fdopen(fd, classname, type) S_io_fdopen(aTHX_ fd, classname, type)
static char S_get_flags(pTHX_ int fd) {
struct stat info;
if (fstat(fd, &info) != -1) {
if (S_ISSOCK(info.st_mode))
return 's';
else if (S_ISFIFO(info.st_mode))
return '|';
}
int flags = fcntl(fd, F_GETFL);
if (flags & O_APPEND)
return 'a';
switch (flags & 3) {
case O_RDONLY:
return '<';
case O_WRONLY:
return '>';
case O_RDWR:
return '+';
default:
close(fd);
Perl_croak(aTHX_ "Unknown mode on descriptor");
}
}
#define get_flags(fd) S_get_flags(aTHX_ fd)
typedef struct { const char* key; unsigned long value; } map[];
#define PIDFD_NONBLOCK O_NONBLOCK
static map pid_flags = {
{ "non-blocking", PIDFD_NONBLOCK },
};
static UV S_get_pid_flag(pTHX_ SV* flag_name) {
int i;
for (i = 0; i < sizeof pid_flags / sizeof *pid_flags; ++i)
if (strEQ(SvPV_nolen(flag_name), pid_flags[i].key))
return pid_flags[i].value;
Perl_croak(aTHX_ "No such flag '%s' known", SvPV_nolen(flag_name));
}
#define get_pid_flag(name) S_get_pid_flag(aTHX_ name)
#define get_fd(self) PerlIO_fileno(IoOFP(sv_2io(SvRV(self))))
MODULE = Linux::FD::Pid PACKAGE = Linux::FD::Pid
PROTOTYPES: DISABLED
BOOT:
load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("IO::Handle"), NULL);
av_push(get_av("Linux::FD::Pid::ISA" , GV_ADD), newSVpvs("IO::Handle"));
SV*
new(classname, pid, ...)
const char* classname;
int pid;
PREINIT:
int pidfd;
int i, flags = 0;
CODE:
for (i = 2; i < items; i++)
flags |= get_pid_flag(ST(i));
pidfd = syscall(__NR_pidfd_open, pid, 0);
if (pidfd < 0)
die_sys("Couldn't open pidfd: %s");
RETVAL = io_fdopen(pidfd, classname, '<');
OUTPUT:
RETVAL
void
send(file_handle, signal)
SV* file_handle;
SV* signal;
PREINIT:
int fd, ret;
CODE:
fd = get_fd(file_handle);
int signo = (SvIOK(signal) || looks_like_number(signal)) && SvIV(signal) ? SvIV(signal) : whichsig(SvPV_nolen(signal));
ret = syscall(__NR_pidfd_send_signal, fd, signo, NULL, 0);
if (ret < 0)
die_sys("Couldn't send signal: %s");
int
wait(file_handle, flags = WEXITED)
SV* file_handle;
int flags;
PREINIT:
int fd, wait_result;
siginfo_t info;
CODE:
fd = get_fd(file_handle);
wait_result = waitid(P_PIDFD, fd, &info, flags);
if (wait_result != 0) {
if (errno == EAGAIN)
XSRETURN_UNDEF;
else
die_sys("Can't wait pid: %s");
}
if (info.si_signo == 0)
XSRETURN_UNDEF;
if (info.si_code == CLD_EXITED)
RETVAL = info.si_status << 8;
else
RETVAL = info.si_status;
OUTPUT:
RETVAL
SV*
get_handle(file_handle, fd)
SV* file_handle;
int fd;
PREINIT:
int pidfd, newfd;
CODE:
pidfd = get_fd(file_handle);
newfd = syscall(__NR_pidfd_getfd, pidfd, fd, 0);
if (newfd < 0)
die_sys("Can't get file descriptor: %s");
RETVAL = io_fdopen(newfd, NULL, get_flags(newfd));
OUTPUT:
RETVAL