5
|
|
|
|
|
|
|
#ifndef SCHMORP_PERL_H_
#define SCHMORP_PERL_H_
/* WARNING
* This header file is a shared resource between many modules.
* perl header files MUST already be included.
*/
#include <signal.h>
#include <errno.h>
#if defined(WIN32 ) || defined(_MINIX)
# define SCHMORP_H_PREFER_SELECT 1
#endif
#if !SCHMORP_H_PREFER_SELECT
# include <poll.h>
#endif
/* useful stuff, used by schmorp mostly */
#include "patchlevel.h"
#define PERL_VERSION_ATLEAST(a,b,c) \
(PERL_REVISION > (a) \
|| (PERL_REVISION == (a) \
&& (PERL_VERSION > (b) \
|| (PERL_VERSION == (b) && PERL_SUBVERSION >= (c)))))
#ifndef PERL_MAGIC_ext
# define PERL_MAGIC_ext '~'
#endif
#if !PERL_VERSION_ATLEAST (5,6,0)
# ifndef PL_ppaddr
# define PL_ppaddr ppaddr
# endif
# ifndef call_sv
# define call_sv perl_call_sv
# endif
# ifndef get_sv
# define get_sv perl_get_sv
# endif
# ifndef get_cv
# define get_cv perl_get_cv
# endif
# ifndef IS_PADGV
# define IS_PADGV(v) 0
# endif
# ifndef IS_PADCONST
# define IS_PADCONST(v) 0
# endif
#endif
/* use NV for 32 bit perls as it allows larger offsets */
#if IVSIZE >= 8
typedef IV VAL64;
# define SvVAL64(sv) SvIV (sv)
# define newSVval64(i64) newSViv (i64)
#else
typedef NV VAL64;
# define SvVAL64(sv) SvNV (sv)
# define newSVval64(i64) newSVnv (i64)
#endif
/* typemap for the above */
/*
VAL64 T_VAL64
INPUT
T_VAL64
$var = ($type)SvVAL64 ($arg);
OUTPUT
T_VAL64
$arg = newSVval64 ($var);
*/
/* 5.11 */
#ifndef CxHASARGS
# define CxHASARGS(cx) (cx)->blk_sub.hasargs
#endif
/* 5.10.0 */
#ifndef SvREFCNT_inc_NN
# define SvREFCNT_inc_NN(sv) SvREFCNT_inc (sv)
#endif
/* 5.8.8 */
#ifndef GV_NOTQUAL
# define GV_NOTQUAL 0
#endif
#ifndef newSV
# define newSV(l) NEWSV(0,l)
#endif
#ifndef CvISXSUB_on
# define CvISXSUB_on(cv) (void)cv
#endif
#ifndef CvISXSUB
# define CvISXSUB(cv) (CvXSUB (cv) ? TRUE : FALSE)
#endif
#ifndef Newx
# define Newx(ptr,nitems,type) New (0,ptr,nitems,type)
#endif
/* 5.8.7 */
#ifndef SvRV_set
# define SvRV_set(s,v) SvRV(s) = (v)
#endif
static int
s_signum (SV *sig)
{
#ifndef SIG_SIZE
/* kudos to Slaven Rezic for the idea */
static char sig_size [] = { SIG_NUM };
# define SIG_SIZE (sizeof (sig_size) + 1)
#endif
dTHX;
int signum;
SvGETMAGIC (sig);
for (signum = 1; signum < SIG_SIZE; ++signum)
if (strEQ (SvPV_nolen (sig), PL_sig_name [signum]))
return signum;
signum = SvIV (sig);
if (signum > 0 && signum < SIG_SIZE)
return signum;
return -1;
}
static int
s_signum_croak (SV *sig)
{
int signum = s_signum (sig);
if (signum < 0)
{
dTHX;
croak ("%s: invalid signal name or number", SvPV_nolen (sig));
}
return signum;
}
static int
s_fileno (SV *fh, int wr)
{
dTHX;
SvGETMAGIC (fh);
if (SvROK (fh))
{
fh = SvRV (fh);
SvGETMAGIC (fh);
}
if (SvTYPE (fh) == SVt_PVGV)
return PerlIO_fileno (wr ? IoOFP (sv_2io (fh)) : IoIFP (sv_2io (fh)));
if (SvOK (fh) && (SvIV (fh) >= 0) && (SvIV (fh) < 0x7fffffffL))
return SvIV (fh);
return -1;
}
static int
s_fileno_croak (SV *fh, int wr)
{
int fd = s_fileno (fh, wr);
if (fd < 0)
{
dTHX;
croak ("%s: illegal fh argument, either not an OS file or read/write mode mismatch", SvPV_nolen (fh));
}
return fd;
}
static SV *
s_get_cv (SV *cb_sv)
{
dTHX;
HV *st;
GV *gvp;
return (SV *)sv_2cv (cb_sv, &st, &gvp, 0);
}
static SV *
s_get_cv_croak (SV *cb_sv)
{
SV *cv = s_get_cv (cb_sv);
if (!cv)
{
dTHX;
croak ("%s: callback must be a CODE reference or another callable object", SvPV_nolen (cb_sv));
}
return cv;
}
/*****************************************************************************/
/* gensub: simple closure generation utility */
#define S_GENSUB_ARG CvXSUBANY (cv).any_ptr
/* create a closure from XS, returns a code reference */
/* the arg can be accessed via GENSUB_ARG from the callback */
/* the callback must use dXSARGS/XSRETURN */
static SV *
s_gensub (pTHX_ void (*xsub)(pTHX_ CV *), void *arg)
{
CV *cv = (CV *)newSV (0);
sv_upgrade ((SV *)cv, SVt_PVCV);
CvANON_on (cv);
CvISXSUB_on (cv);
CvXSUB (cv) = xsub;
S_GENSUB_ARG = arg;
return newRV_noinc ((SV *)cv);
}
/*****************************************************************************/
/* portable pipe/socketpair */
#if defined(USE_SOCKETS_AS_HANDLES) || PERL_VERSION_ATLEAST(5,18,0)
# define S_TO_HANDLE(x) ((HANDLE)win32_get_osfhandle (x))
#else
# define S_TO_HANDLE(x) ((HANDLE)x)
#endif
#ifdef _WIN32
/* taken almost verbatim from libev's ev_win32.c */
/* oh, the humanity! */
static int
s_pipe (int filedes [2])
{
dTHX;
struct sockaddr_in addr = { 0 };
int addr_size = sizeof (addr);
struct sockaddr_in adr2;
int adr2_size = sizeof (adr2);
SOCKET listener;
SOCKET sock [2] = { -1, -1 };
if ((listener = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
return -1;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
addr.sin_port = 0;
if (bind (listener, (struct sockaddr *)&addr, addr_size))
goto fail;
if (getsockname (listener, (struct sockaddr *)&addr, &addr_size))
goto fail;
if (listen (listener, 1))
goto fail;
if ((sock [0] = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
goto fail;
if (connect (sock [0], (struct sockaddr *)&addr, addr_size))
goto fail;
if ((sock [1] = accept (listener, 0, 0)) < 0)
goto fail;
/* windows vista returns fantasy port numbers for getpeername.
* example for two interconnected tcp sockets:
*
* (Socket::unpack_sockaddr_in getsockname $sock0)[0] == 53364
* (Socket::unpack_sockaddr_in getpeername $sock0)[0] == 53363
* (Socket::unpack_sockaddr_in getsockname $sock1)[0] == 53363
* (Socket::unpack_sockaddr_in getpeername $sock1)[0] == 53365
*
* wow! tridirectional sockets!
*
* this way of checking ports seems to work:
*/
if (getpeername (sock [0], (struct sockaddr *)&addr, &addr_size))
goto fail;
if (getsockname (sock [1], (struct sockaddr *)&adr2, &adr2_size))
goto fail;
errno = WSAEINVAL;
if (addr_size != adr2_size
|| addr.sin_addr.s_addr != adr2.sin_addr.s_addr /* just to be sure, I mean, it's windows */
|| addr.sin_port != adr2.sin_port)
goto fail;
closesocket (listener);
#if defined(USE_SOCKETS_AS_HANDLES) || PERL_VERSION_ATLEAST(5,18,0)
/* when select isn't winsocket, we also expect socket, connect, accept etc.
* to work on fds */
filedes [0] = sock [0];
filedes [1] = sock [1];
#else
filedes [0] = _open_osfhandle (sock [0], 0);
filedes [1] = _open_osfhandle (sock [1], 0);
#endif
return 0;
fail:
closesocket (listener);
if (sock [0] != INVALID_SOCKET) closesocket (sock [0]);
if (sock [1] != INVALID_SOCKET) closesocket (sock [1]);
return -1;
}
#define s_socketpair(domain,type,protocol,filedes) s_pipe (filedes)
static int
s_fd_blocking (int fd, int blocking)
{
u_long nonblocking = !blocking;
return ioctlsocket ((SOCKET)S_TO_HANDLE (fd), FIONBIO, &nonblocking);
}
#define s_fd_prepare(fd) s_fd_blocking (fd, 0)
#else
#define s_socketpair(domain,type,protocol,filedes) socketpair (domain, type, protocol, filedes)
#define s_pipe(filedes) pipe (filedes)
static int
s_fd_blocking (int fd, int blocking)
{
return fcntl (fd, F_SETFL, blocking ? 0 : O_NONBLOCK);
}
static int
s_fd_prepare (int fd)
{
return s_fd_blocking (fd, 0)
|| fcntl (fd, F_SETFD, FD_CLOEXEC);
}
#endif
#if HAVE_EVENTFD
# include <sys/eventfd.h>
#else
# if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 7))
# define SCHMORP_H_HAVE_EVENTFD 1
/* our minimum requirement is glibc 2.7 which has the stub, but not the header */
# include <stdint.h>
# ifdef __cplusplus
extern "C" {
# endif
int eventfd (unsigned int initval, int flags);
# ifdef __cplusplus
}
# endif
# else
# define eventfd(initval,flags) -1
# endif
#endif
typedef struct {
int fd[2]; /* read, write fd, might be equal */
int len; /* write length (1 pipe/socket, 8 eventfd) */
} s_epipe;
static int
s_epipe_new (s_epipe *epp)
{
s_epipe ep;
ep.fd [0] = ep.fd [1] = eventfd (0, 0);
if (ep.fd [0] >= 0)
{
s_fd_prepare (ep.fd [0]);
ep.len = 8;
}
else
{
if (s_pipe (ep.fd))
return -1;
if (s_fd_prepare (ep.fd [0])
|| s_fd_prepare (ep.fd [1]))
{
dTHX;
close (ep.fd [0]);
close (ep.fd [1]);
return -1;
}
ep.len = 1;
}
*epp = ep;
return 0;
}
static void
s_epipe_destroy (s_epipe *epp)
{
dTHX;
close (epp->fd [0]);
if (epp->fd [1] != epp->fd [0])
close (epp->fd [1]);
epp->len = 0;
}
static void
s_epipe_signal (s_epipe *epp)
{
#ifdef _WIN32
/* perl overrides send with a function that crashes in other threads.
* unfortunately, it overrides it with an argument-less macro, so
* there is no way to force usage of the real send function.
* incompetent windows programmers - is this redundant?
*/
DWORD dummy;
WriteFile (S_TO_HANDLE (epp->fd [1]), (LPCVOID)&dummy, 1, &dummy, 0);
#else
static uint64_t counter = 1;
/* some modules accept fd's from outside, support eventfd here */
if (write (epp->fd [1], &counter, epp->len) < 0
&& errno == EINVAL
&& epp->len != 8)
write (epp->fd [1], &counter, (epp->len = 8));
#endif
}
static void
s_epipe_drain (s_epipe *epp)
{
dTHX;
char buf [9];
#ifdef _WIN32
recv (epp->fd [0], buf, sizeof (buf), 0);
#else
read (epp->fd [0], buf, sizeof (buf));
#endif
}
/* like new, but dups over old */
static int
s_epipe_renew (s_epipe *epp)
{
dTHX;
s_epipe epn;
if (epp->fd [1] != epp->fd [0])
close (epp->fd [1]);
if (s_epipe_new (&epn))
return -1;
if (epp->len)
{
if (dup2 (epn.fd [0], epp->fd [0]) < 0)
croak ("unable to dup over old event pipe"); /* should not croak */
close (epn.fd [0]);
if (epn.fd [0] == epn.fd [1])
epn.fd [1] = epp->fd [0];
epn.fd [0] = epp->fd [0];
}
*epp = epn;
return 0;
}
#define s_epipe_fd(epp) ((epp)->fd [0])
static int
s_epipe_wait (s_epipe *epp)
{
dTHX;
#if SCHMORP_H_PREFER_SELECT
fd_set rfd;
int fd = s_epipe_fd (epp);
FD_ZERO (&rfd);
FD_SET (fd, &rfd);
return PerlSock_select (fd + 1, &rfd, 0, 0, 0);
#else
/* poll is preferable on posix systems */
struct pollfd pfd;
pfd.fd = s_epipe_fd (epp);
pfd.events = POLLIN;
return poll (&pfd, 1, -1);
#endif
}
#endif
|