IO-FDPass-1.3/0000755000000000000000000000000013774454422011514 5ustar rootrootIO-FDPass-1.3/FDPass.pm0000644000000000000000000001060613774453547013204 0ustar rootroot=head1 NAME IO::FDPass - pass a file descriptor over a socket =head1 SYNOPSIS use IO::FDPass; IO::FDPass::send fileno $socket, fileno $fh_to_pass or die "send failed: $!"; my $fd = IO::FDPass::recv fileno $socket; $fd >= 0 or die "recv failed: $!"; =head1 DESCRIPTION This small low-level module only has one purpose: pass a file descriptor to another process, using a (streaming) unix domain socket (on POSIX systems) or any (streaming) socket (on WIN32 systems). The ability to pass file descriptors on windows is currently the unique selling point of this module. Have I mentioned that it is really small, too? =head1 FUNCTIONS =over 4 =cut package IO::FDPass; BEGIN { $VERSION = 1.3; require XSLoader; XSLoader::load (__PACKAGE__, $VERSION); } =item $bool = IO::FDPass::send $socket_fd, $fd_to_pass Sends the file descriptor given by C<$fd_to_pass> over the socket C<$socket_fd>. Return true if it worked, false otherwise. Note that I parameters must be file descriptors, not handles. When used on non-blocking sockets, this function might fail with C<$!> set to C or equivalent, in which case you are free to try. It should succeed if called on a socket that indicates writability (e.g. via C). Example: receive a file descriptor from a blocking socket and convert it to a file handle. my $fd = IO::FDPass::recv fileno $socket; $fd >= 0 or die "unable to receive file handle: $!"; open my $fh, "+<&=$fd" or die "unable to convert file descriptor to handle: $!"; =back =head1 PORTABILITY NOTES This module has been tested on GNU/Linux x86 and amd64, NetBSD 6, OS X 10.5, Windows 2000 ActivePerl 5.10, Solaris 10, OpenBSD 4.4, 4.5, 4.8 and 5.0, DragonFly BSD, FreeBSD 7, 8 and 9, Windows 7 + ActivePerl 5.16.3 32 and 64 bit and Strawberry Perl 5.16.3 32 and 64 bit, and found to work, although ActivePerl 32 bit needed a newer MinGW version (that supports XP and higher). However, windows doesn't support asynchronous file descriptor passing, so the source process must still be around when the destination process wants to receive the file handle. Also, if the target process fails to fetch the handle for any reason (crashes, fails to call C etc.), the handle will leak, so never do that. Also, on windows, the receiving process must have the PROCESS_DUP_HANDLE access right on the sender process for this module to work. Cygwin is not supported at the moment, as file descriptor passing in cygwin is not supported, and cannot be rolled on your own as cygwin has no (working) method of opening a handle as fd. That is, it has one, but that one isn't exposed to programs, and only used for stdin/out/err. Sigh. =head1 OTHER MODULES At the time of this writing, the author of this module was aware of two other file descriptor passing modules on CPAN: L and L. The former hasn't seen any release for over a decade, isn't 64 bit clean and it's author didn't respond to my mail with the fix, so doesn't work on many 64 bit machines. It does, however, support a number of pre-standard unices, basically everything of relevance at the time it was written. The latter seems to have similar support for antique unices, and doesn't seem to suffer from 64 bit bugs, but inexplicably has a large perl part, doesn't support mixing data and file descriptors, and requires AnyEvent. Presumably that makes it much more user friendly than this module (skimming the manpage shows that a lot of thought has gone into it, and you are well advised to read it and maybe use it before trying a low-level module such as this one). In fact, the manpage discusses even more file descriptor passing modules on CPAN. Neither seems to support native win32 perls. =head1 AUTHOR Marc Lehmann http://home.schmorp.de/ =cut 1 IO-FDPass-1.3/Changes0000644000000000000000000000223113774453540013005 0ustar rootrootRevision history for Perl extension IO::FDPass 1.3 Mon Jan 4 00:46:36 CET 2021 - do not leak memory on unsuccessful recv (based on patch by Eric Wong). 1.2 Sun Sep 25 01:43:14 CEST 2016 - compatibility macros were still using C++ syntax, fortunately only affecting the solaris platform that apparently doesn't implement ipv6 (which defines those macros) (analyzed by Yary). 1.1 Mon Jul 6 19:40:36 CEST 2015 - added stability canary support. - seems to work on 5.6.2 too, so remove the use 5.008. - rewrite the cmsg recv check to rely on CMSG_FIRSTHDR instead of doing our own check. 1.0 Sun Apr 7 00:42:13 CEST 2013 - this release only updates the documentation. - better SYNOPSIS. - discuss other file descriptor passing modules more thoroughly. 0.2 Fri Apr 5 10:20:05 CEST 2013 - fix a problem when sending on non-blocking sockets on windows. - "improve" fd passing to be less blocking on windows, at the cost of leaking the handle on error. 0.1 Fri Apr 5 07:04:02 CEST 2013 - first release. 0.0 Fri Apr 5 06:09:40 CEST 2013 - ripped out of AnyEvent::Fork > 0.01. IO-FDPass-1.3/COPYING0000644000000000000000000000007610211640730012530 0ustar rootrootThis module is licensed under the same terms as perl itself. IO-FDPass-1.3/t/0000755000000000000000000000000013774454422011757 5ustar rootrootIO-FDPass-1.3/t/00_load.t0000644000000000000000000000017012127444366013355 0ustar rootrootBEGIN { $| = 1; print "1..1\n"; } END {print "not ok 1\n" unless $loaded;} use IO::FDPass; $loaded = 1; print "ok 1\n"; IO-FDPass-1.3/t/01_basic.t0000644000000000000000000000175612127450275013527 0ustar rootrootBEGIN { $| = 1; print "1..7\n"; } use Socket; use IO::FDPass; print "ok 1\n"; socketpair my $fh1, my $fh2, AF_UNIX, SOCK_STREAM, 0 or die "socketpair: $!"; socketpair my $fh3, my $fh4, AF_UNIX, SOCK_STREAM, 0 or die "socketpair: $!"; print "ok 2\n"; my $pid = fork; defined $pid or die "fork: $!"; unless ($pid) { close $fh3; my $fd = IO::FDPass::recv fileno $fh2; print $fd > 0 ? "" : "not ", "ok 4 # $fd\n"; open my $fh, "+<&=$fd" or die "open(fd) failed: $!"; sysread $fh, my $buf, 1 or die "sysread(child) failed: $!"; print $buf eq "4" ? "" : "not ", "ok 5 # $buf\n"; syswrite $fh, "3", 1 or die "syswrite(child) failed: $!"; exit; } print "ok 3\n"; IO::FDPass::send fileno $fh1, fileno $fh4 or die "send failed: $!"; close $fh4; syswrite $fh3, "4", 1 or die "syswrite(parent) failed: $!"; sysread $fh3, my $buf, 1 or die "sysread(parent) failed: $!"; print $buf eq "3" ? "" : "not ", "ok 6 # $buf\n"; print "ok 7\n"; IO-FDPass-1.3/Makefile.PL0000644000000000000000000000071312546537152013465 0ustar rootrootuse ExtUtils::MakeMaker; use Canary::Stability IO::FDPass => 1, 5.005; WriteMakefile( dist => { PREOP => 'pod2text FDPass.pm | tee README >$(DISTVNAME)/README; chmod -R u=rwX,go=rX . ;', COMPRESS => 'gzip -9v', SUFFIX => '.gz', }, NAME => "IO::FDPass", VERSION_FROM => "FDPass.pm", CONFIGURE_REQUIRES => { "ExtUtils::MakeMaker" => 6.52, "Canary::Stability" => 0, }, ); IO-FDPass-1.3/FDPass.xs0000644000000000000000000001056313774452767013227 0ustar rootroot#ifdef __sun #define _XOPEN_SOURCE 1 #define _XOPEN_SOURCE_EXTENDED 1 #define __EXTENSIONS__ 1 #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #if WIN32 /* perl probably did this already */ #include #elif __CYGWIN__ #include #include #include #define _open_osfhandle(h,m) cygwin_attach_handle_to_fd ("/dev/tcp", -1, (HANDLE)h, 1, GENERIC_READ | GENERIC_WRITE) typedef int SOCKET; #else #include // needed by broken bsds for NULL used in sys/uio.h #include #include /* send_fd/recv_fd taken from libptytty */ #include #include #include #ifndef CMSG_SPACE # define CMSG_SPACE(len) (sizeof (struct cmsghdr) + len) #endif #ifndef CMSG_LEN # define CMSG_LEN(len) (sizeof (struct cmsghdr) + len) #endif #endif #if defined(WIN32) /* the rub is this: win32 doesn't seem to have a way to query whether a socket */ /* is non-blocking or not. so we assume it is blocking, make it so if it isn't */ /* and reset it afterwards */ static int rw (int wr, int fd, char *buf, int len) { u_long nbio = 0; int got = 0; while (got != len) { int sze = wr ? send ((SOCKET)fd, buf, len - got, 0) /* we assume send and recv are macros with arguments */ : recv ((SOCKET)fd, buf, len - got, 0); /* to be on the safe side */ if (sze < 0) { if (errno == EAGAIN || errno == WSAEWOULDBLOCK) { ioctl (fd, FIONBIO, (void *)&nbio); nbio = 1; } else break; } else if (sze == 0) break; else got += sze; } if (nbio) ioctl (fd, FIONBIO, (void *)&nbio); return got == len; } #endif static int fd_send (int socket, int fd) { #if defined(WIN32) DWORD pid; HANDLE hdl; pid = GetCurrentProcessId (); if (!rw (1, socket, (char *)&pid, sizeof (pid))) return 0; errno = EBADF; if (!DuplicateHandle ((HANDLE)-1, (HANDLE)_get_osfhandle (fd), (HANDLE)-1, &hdl, 0, FALSE, DUPLICATE_SAME_ACCESS)) return 0; if (!rw (1, socket, (char *)&hdl, sizeof (hdl))) { CloseHandle (hdl); return 0; } return 1; #else void *buf = malloc (CMSG_SPACE (sizeof (int))); if (!buf) return 0; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; char data = 0; iov.iov_base = &data; iov.iov_len = 1; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = CMSG_SPACE (sizeof (int)); cmsg = CMSG_FIRSTHDR (&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN (sizeof (int)); *(int *)CMSG_DATA (cmsg) = fd; ssize_t result = sendmsg (socket, &msg, 0); free (buf); return result >= 0; #endif } static int fd_recv (int socket) { #if defined(WIN32) DWORD pid; HANDLE source, rhd, lhd; if (!rw (0, socket, (char *)&pid, sizeof (pid))) return -1; if (!rw (0, socket, (char *)&rhd, sizeof (rhd))) return -1; source = OpenProcess (PROCESS_DUP_HANDLE, FALSE, pid); errno = EACCES; if (!source) return -1; pid = DuplicateHandle (source, rhd, (HANDLE)-1, &lhd, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); CloseHandle (source); errno = EBADF; if (!pid) return -1; return _open_osfhandle ((intptr_t)lhd, 0); #else void *buf = malloc (CMSG_SPACE (sizeof (int))); if (!buf) return -1; struct msghdr msg; struct iovec iov; char data = 1; iov.iov_base = &data; iov.iov_len = 1; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = CMSG_SPACE (sizeof (int)); if (recvmsg (socket, &msg, 0) <= 0) { free (buf); return -1; } int fd = -1; errno = EDOM; struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg); if (data == 0 && cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len >= CMSG_LEN (sizeof (int))) fd = *(int *)CMSG_DATA (cmsg); free (buf); return fd; #endif } MODULE = IO::FDPass PACKAGE = IO::FDPass PREFIX = fd_ PROTOTYPES: DISABLE int fd_send (int socket, int fd) int fd_recv (int socket) IO-FDPass-1.3/MANIFEST0000644000000000000000000000040113774454422012640 0ustar rootrootREADME Changes MANIFEST COPYING Makefile.PL FDPass.pm FDPass.xs t/00_load.t t/01_basic.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) IO-FDPass-1.3/README0000644000000000000000000001102613774454422012374 0ustar rootrootNAME IO::FDPass - pass a file descriptor over a socket SYNOPSIS use IO::FDPass; IO::FDPass::send fileno $socket, fileno $fh_to_pass or die "send failed: $!"; my $fd = IO::FDPass::recv fileno $socket; $fd >= 0 or die "recv failed: $!"; DESCRIPTION This small low-level module only has one purpose: pass a file descriptor to another process, using a (streaming) unix domain socket (on POSIX systems) or any (streaming) socket (on WIN32 systems). The ability to pass file descriptors on windows is currently the unique selling point of this module. Have I mentioned that it is really small, too? FUNCTIONS $bool = IO::FDPass::send $socket_fd, $fd_to_pass Sends the file descriptor given by $fd_to_pass over the socket $socket_fd. Return true if it worked, false otherwise. Note that *both* parameters must be file descriptors, not handles. When used on non-blocking sockets, this function might fail with $! set to "EAGAIN" or equivalent, in which case you are free to try. It should succeed if called on a socket that indicates writability (e.g. via "select"). Example: pass a file handle over an open socket. IO::FDPass::send fileno $socket, fileno $fh or die "unable to pass file handle: $!"; $fd = IO::FDPass::recv $socket_fd Receive a file descriptor from the socket and return it if successful. On errors, return -1. Note that *both* $socket_fd and the returned file descriptor are, in fact, file descriptors, not handles. When used on non-blocking sockets, this function might fail with $! set to "EAGAIN" or equivalent, in which case you are free to try again. It should succeed if called on a socket that indicates readability (e.g. via "select"). Example: receive a file descriptor from a blocking socket and convert it to a file handle. my $fd = IO::FDPass::recv fileno $socket; $fd >= 0 or die "unable to receive file handle: $!"; open my $fh, "+<&=$fd" or die "unable to convert file descriptor to handle: $!"; PORTABILITY NOTES This module has been tested on GNU/Linux x86 and amd64, NetBSD 6, OS X 10.5, Windows 2000 ActivePerl 5.10, Solaris 10, OpenBSD 4.4, 4.5, 4.8 and 5.0, DragonFly BSD, FreeBSD 7, 8 and 9, Windows 7 + ActivePerl 5.16.3 32 and 64 bit and Strawberry Perl 5.16.3 32 and 64 bit, and found to work, although ActivePerl 32 bit needed a newer MinGW version (that supports XP and higher). However, windows doesn't support asynchronous file descriptor passing, so the source process must still be around when the destination process wants to receive the file handle. Also, if the target process fails to fetch the handle for any reason (crashes, fails to call "recv" etc.), the handle will leak, so never do that. Also, on windows, the receiving process must have the PROCESS_DUP_HANDLE access right on the sender process for this module to work. Cygwin is not supported at the moment, as file descriptor passing in cygwin is not supported, and cannot be rolled on your own as cygwin has no (working) method of opening a handle as fd. That is, it has one, but that one isn't exposed to programs, and only used for stdin/out/err. Sigh. OTHER MODULES At the time of this writing, the author of this module was aware of two other file descriptor passing modules on CPAN: File::FDPasser and AnyEvent::FDPasser. The former hasn't seen any release for over a decade, isn't 64 bit clean and it's author didn't respond to my mail with the fix, so doesn't work on many 64 bit machines. It does, however, support a number of pre-standard unices, basically everything of relevance at the time it was written. The latter seems to have similar support for antique unices, and doesn't seem to suffer from 64 bit bugs, but inexplicably has a large perl part, doesn't support mixing data and file descriptors, and requires AnyEvent. Presumably that makes it much more user friendly than this module (skimming the manpage shows that a lot of thought has gone into it, and you are well advised to read it and maybe use it before trying a low-level module such as this one). In fact, the manpage discusses even more file descriptor passing modules on CPAN. Neither seems to support native win32 perls. AUTHOR Marc Lehmann http://home.schmorp.de/ IO-FDPass-1.3/META.yml0000644000000000000000000000067513774454422012775 0ustar rootroot--- abstract: unknown author: - unknown build_requires: ExtUtils::MakeMaker: '0' configure_requires: Canary::Stability: '0' ExtUtils::MakeMaker: '6.52' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: IO-FDPass no_index: directory: - t - inc version: 1.3 IO-FDPass-1.3/META.json0000644000000000000000000000140413774454422013134 0ustar rootroot{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "IO-FDPass", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "Canary::Stability" : "0", "ExtUtils::MakeMaker" : "6.52" } } }, "release_status" : "stable", "version" : 1.3 }