int snprint_sockaddr(char *buffer, size_t buflen, struct sockaddr *addr) {
char tmp[256];
int port;
if (addr->sa_family == AF_INET) {
if (!inet_ntop(addr->sa_family, &((struct sockaddr_in*)addr)->sin_addr, tmp, sizeof(tmp)))
snprintf(tmp, sizeof(tmp), "(invalid?)");
port= ntohs(((struct sockaddr_in*)addr)->sin_port);
return snprintf(buffer, buflen, "inet %s:%d", tmp, port);
}
#ifdef AF_INET6
else if (addr->sa_family == AF_INET6) {
if (!inet_ntop(addr->sa_family, &((struct sockaddr_in6*)addr)->sin6_addr, tmp, sizeof(tmp)))
snprintf(tmp, sizeof(tmp), "(invalid?)");
port= ntohs(((struct sockaddr_in6*)addr)->sin6_port);
return snprintf(buffer, buflen, "inet6 [%s]:%d", tmp, port);
}
#endif
#ifdef AF_UNIX
else if (addr->sa_family == AF_UNIX) {
return snprintf(buffer, buflen, "unix %s", ((struct sockaddr_un*)addr)->sun_path);
}
#endif
return snprintf(buffer, buflen, "(unsupported address family %d)", (int)addr->sa_family);
}
int parse_signal(SV *name_sv) {
char *name;
if (looks_like_number(name_sv))
return SvIV(name_sv);
name= SvPV_nolen(name_sv);
if (!strcmp(name, "SIGKILL")) return SIGKILL;
if (!strcmp(name, "SIGTERM")) return SIGTERM;
if (!strcmp(name, "SIGUSR1")) return SIGUSR1;
if (!strcmp(name, "SIGUSR2")) return SIGUSR2;
if (!strcmp(name, "SIGALRM")) return SIGALRM;
if (!strcmp(name, "SIGABRT")) return SIGABRT;
if (!strcmp(name, "SIGINT" )) return SIGINT;
if (!strcmp(name, "SIGHUP" )) return SIGHUP;
croak("Unimplemented signal name %s", name);
}
int fileno_from_sv(SV *sv) {
PerlIO *io;
GV *gv;
SV *rv;
if (!SvOK(sv)) // undef
return -1;
if (!SvROK(sv)) // scalar, is it only digits?
return looks_like_number(sv)? SvIV(sv) : -1;
// is it a globref?
rv= SvRV(sv);
if (SvTYPE(rv) == SVt_PVGV) {
io= IoIFP(GvIOp((GV*) rv));
return PerlIO_fileno(io);
}
return -1;
}
int snprint_fd_table(char *buf, size_t sizeof_buf, int max_fd) {
struct stat statbuf;
struct sockaddr_storage addr;
size_t len= 0;
int i, j, n_closed;
len= snprintf(buf, sizeof_buf, "File descriptors {\n");
for (i= 0; i < max_fd; i++) {
socklen_t addr_len= sizeof(addr);
char * bufpos= buf + len;
size_t avail= sizeof_buf > len? sizeof_buf - len : 0;
if (fstat(i, &statbuf) < 0) {
// Find the next valid fd
for (j= i+1; j < max_fd; j++)
if (fstat(j, &statbuf) == 0)
break;
if (j - i >= 2)
len += snprintf(bufpos, avail, "%4d-%d: (closed)\n", i, j-1);
else
len += snprintf(bufpos, avail, "%4d: (closed)\n", i);
i= j;
}
else if (!S_ISSOCK(statbuf.st_mode)) {
char pathbuf[64];
char linkbuf[256];
int got;
snprintf(pathbuf, sizeof(pathbuf), "/proc/%d/fd/%d", getpid(), i);
pathbuf[sizeof(pathbuf)-1]= '\0';
got= readlink(pathbuf, linkbuf, sizeof(linkbuf));
if (got > 0 && got < sizeof(linkbuf)) {
linkbuf[got]= '\0';
len += snprintf(bufpos, avail, "%4d: %s\n", i, linkbuf);
} else {
len += snprintf(bufpos, avail, "%4d: (not a socket, no proc/fd?)\n", i);
}
}
else {
if (getsockname(i, (struct sockaddr*) &addr, &addr_len) < 0) {
len += snprintf(bufpos, avail, "%4d: (getsockname failed)", i);
}
else if (addr.ss_family == AF_INET) {
char addr_str[INET6_ADDRSTRLEN];
struct sockaddr_in *sin= (struct sockaddr_in*) &addr;
inet_ntop(AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str));
len += snprintf(bufpos, avail, "%4d: inet [%s]:%d", i, addr_str, ntohs(sin->sin_port));
}
else if (addr.ss_family == AF_UNIX) {
struct sockaddr_un *s_un= (struct sockaddr_un*) &addr;
char *p;
// sanitize socket name, which will be random bytes if anonymous
for (p= s_un->sun_path; *p; p++)
if (*p <= 0x20 || *p >= 0x7F)
*p= '?';
len += snprintf(bufpos, avail, "%4d: unix [%s]", i, s_un->sun_path);
}
else {
len += snprintf(bufpos, avail, "%4d: ? socket family %d", i, addr.ss_family);
}
bufpos= buf + len;
avail= sizeof_buf > len? sizeof_buf - len : 0;
// Is it connected to anything?
if (getpeername(i, (struct sockaddr*) &addr, &addr_len) == 0) {
if (addr.ss_family == AF_INET) {
char addr_str[INET6_ADDRSTRLEN];
struct sockaddr_in *sin= (struct sockaddr_in*) &addr;
inet_ntop(AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str));
len += snprintf(bufpos, avail, " -> [%s]:%d\n", addr_str, ntohs(sin->sin_port));
}
else if (addr.ss_family == AF_UNIX) {
struct sockaddr_un *s_un= (struct sockaddr_un*) &addr;
char *p;
// sanitize socket name, which will be random bytes if anonymous
for (p= s_un->sun_path; *p; p++)
if (*p <= 0x20 || *p >= 0x7F)
*p= '?';
len += snprintf(bufpos, avail, " -> unix [%s]\n", s_un->sun_path);
}
else {
len += snprintf(bufpos, avail, " -> socket family %d\n", addr.ss_family);
}
}
else {
len++;
if (avail > 0)
bufpos[0]= '\n';
}
}
}
// Did it all fit in the buffer, including NUL terminator?
if (len + 3 <= sizeof_buf) {
buf[len++]= '}';
buf[len++]= '\n';
buf[len ]= '\0';
}
else { // overwrite last 2 chars to end with newline and NUL
if (sizeof_buf > 1) buf[sizeof_buf-2]= '\n';
if (sizeof_buf > 0) buf[sizeof_buf-1]= '\0';
len= sizeof_buf-1;
}
return len;
}
#if 0
// neat idea, but no real need for it right now
int get_fd_table(AV *out, int max_fd) {
struct stat statbuf;
size_t len= 0;
int i, j, k, n_closed;
pid_t pid= 0;
for (i= 0; i < max_fd; i++) {
if (fstat(i, &statbuf) < 0)
continue;
else if (!S_ISSOCK(statbuf.st_mode)) {
char pathbuf[64];
char linkbuf[256];
int got;
if (!pid) pid= getpid();
// Prefer whatever /proc/sel/fd/N says.
snprintf(pathbuf, sizeof(pathbuf), "/proc/%d/fd/%d", pid, i);
pathbuf[sizeof(pathbuf)-1]= '\0';
got= readlink(pathbuf, linkbuf, sizeof(linkbuf));
if (got > 0 && got <= sizeof(linkbuf)) {
sv= newSVpvn(linkbuf, got);
}
// for systems without /prod/self/fd, give a simple approximation
else if (S_ISREG(statbuf.st_mode)) {
sv= newSVpvs("file");
} else if (S_ISDIR(statbuf.st_mode)) {
sv= newSVpvs("dir");
} else if (S_ISCHR(statbuf.st_mode)) {
sv= newSVpvs("chardevice");
} else if (S_ISBLK(statbuf.st_mode)) {
sv= newSVpvs("blockdev");
} else if (S_ISFIFO(statbuf.st_mode)) {
sv= newSVpvs("pipe");
} else {
sv= newSVpvs("unknown");
}
}
else {
SV *sname= NULL, *pname= NULL;
int protocol= -1, family= -1;
const char *clname;
struct sockaddr_storage addr;
socklen_t addr_len= sizeof(addr);
if (getsockname(i, (struct sockaddr*) &addr, &addr_len) == 0) {
sname= newSVpvn((char*) &addr, addr_len);
family= addr.ss_family;
}
addr_len= sizeof(addr);
if (getpeername(i, (struct sockaddr*) &addr, &addr_len) == 0) {
pname= newSVpvn((char*) &addr, addr_len);
family= addr.ss_family;
}
if (family == -1) {
int len = sizeof(family);
if (getsockopt(i, SOL_SOCKET, SO_FAMILY, &family, &len) == -1) {
perror("getsockopt SO_FAMILY");
family= -1;
}
}
if (protocol == -1) {
int len = sizeof(family);
if (getsockopt(i, SOL_SOCKET, SO_PROTOCOL, &protocol, &len) == -1) {
perror("getsockopt SO_PROTOCOL");
protocol= -1;
}
}
if (family == AF_INET) {
clname= (protocol == SOCK_STREAM)? "IO::SocketAlarm::FdInfo::TCP"
: (protocol == SOCK_DGRAM)? "IO::SocketAlarm::FdInfo::UDP"
: "IO::SocketAlarm::FdInfo::INET";
}
#ifdef AF_INET6
else if (family == AF_INET6) {
clname= (protocol == SOCK_STREAM)? "IO::SocketAlarm::FdInfo::TCP6"
: (protocol == SOCK_DGRAM)? "IO::SocketAlarm::FdInfo::UDP6"
: "IO::SocketAlarm::FdInfo::INET6";
}
#endif
#ifdef AF_UNIX
else if (family == AF_UNIX) {
clname= (protocol == SOCK_STREAM)? "IO::SocketAlarm::FdInfo::UNIX"
: (protocol == SOCK_DGRAM)? "IO::SocketAlarm::FdInfo::UNIX_DGRAM"
: (protocol == SOCK_SEQPACKET)? "IO::SocketAlarm::FdInfo::UNIX_SEQPACKET"
: "IO::SocketAlarm::FdInfo::UNIX";
}
#endif
else clname= "IO::SocketAlarm::FdInfo";
}
}
}
#endif
// This loads now_ts with the current clock time if it was not already initialized.
// Use tv_nsec == -1 as an indicator of being uninitialized.
bool lazy_build_now_ts(struct timespec *now_ts) {
if (now_ts->tv_nsec == -1) {
if (clock_gettime(CLOCK_MONOTONIC, now_ts) != 0) {
perror("clock_gettime(CLOCK_MONOTONIC)");
now_ts->tv_nsec= -1; // ensure remains undefined
return false; // kind of a serious error... but this runs from the background thread, so can't call 'croak'
}
}
return true;
}