On 7/14/06, David Jones <drj@pobox.com> wrote:
> Generally it's unsafe to longjmp out of a signal handler. Pretty
> much the only thing you can do in a signal handler portably and
> safely is to set a variable of type sig_atomic_t and return. Of
> course, your platform may provide more guarantees (I doubt it).
I thought so too. I've ditched the alarm/longjmp approach and am using
setsockopt to tell the socket to use given receive and send timeouts,
and also telling the socket not to linger ...
> What's your C platform?
uClibc/uClinux running on a MC68VZ328 (Motorola 68000 variant,
Dragonball @ 33 Mhz)
> Your problem is general and thorny. In the ideal world everything
> that might block (network, files, locks, threads, user input, alarms,
> etc) should be select(2)able, but sometimes you just can't get at the
> underlying fd.
I'd rather avoid select if I can. Code is thorny enough as it is :-)
> You may be able to make your select non-blocking before trying connect.
I tried this. It appears to work.
network.lua:
function net.ping(host, timeout)
local ret, err = socket.ping(host, timeout)
print('ret: ' .. tostring(ret) .. ' err: ' .. tostring(err))
end
net.ping('10.0.0.100', 3)
net.ping('www.google.com', 3)
net.ping('64.233.161.99', 10)
gives me
# lua ne*
ret: true err: nil
ret: nil err: bad IP address: Success
ret: nil err: ping write incomplete: Network is unreachable
using the below implementation of ping (adapted from busybox, adding
linger and timeouts)
#define DEFDATALEN 56
#define MAXIPLEN 60
#define MAXICMPLEN 76
static int pPing(lua_State *L)
{
struct in_addr hostaddr;
struct hostent *h;
struct sockaddr_in pingaddr;
struct icmp *pkt;
struct timeval timeout;
struct linger lingrr;
int pingsock, c, secs, ret;
const char *host;
char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
host = lua_tostring(L, 1);
secs = (int) lua_tonumber(L, 2);
if ((pingsock = socket(AF_INET, SOCK_RAW, 1)) < 0) /* 1 == ICMP */
goto notreachable;
memset(&pingaddr, 0, sizeof(struct sockaddr_in));
pingaddr.sin_family = AF_INET;
if (!(h = gethostbyname(host)))
goto notreachable;
memcpy(&(pingaddr.sin_addr), h->h_addr, sizeof(pingaddr.sin_addr));
pkt = (struct icmp *) packet;
memset(pkt, 0, sizeof(packet));
pkt->icmp_type = ICMP_ECHO;
pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
/* set timeout to user supplied value */
timeout.tv_sec = secs;
timeout.tv_usec = 0;
/* set socket to linger for user supplied time */
lingrr.l_onoff = 1;
lingrr.l_linger = secs;
if (setsockopt (pingsock, SOL_SOCKET, SO_LINGER, (char *)&lingrr,
sizeof lingrr) < 0)
goto notreachable;
if (setsockopt (pingsock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof timeout) < 0)
goto notreachable;
if (setsockopt (pingsock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof timeout) < 0)
goto notreachable;
c = sendto(pingsock, packet, sizeof(packet), 0,
(struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
if (c < 0 || c != sizeof(packet)) {
goto notreachable;
}
/* listen for replies */
while (1) {
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
(struct sockaddr *) &from, (socklen_t *)&fromlen)) < 0) {
if (errno == EINTR)
continue;
if (errno == ETIMEDOUT)
goto notreachable;
continue;
}
if (c >= 76) { /* ip + icmp */
struct iphdr *iphdr = (struct iphdr *) packet;
pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
if (pkt->icmp_type == ICMP_ECHOREPLY)
break;
}
}
close(pingsock);
lua_pushboolean(L, 1);
return 1;
notreachable:
ret = pusherror(L, NULL);
close(pingsock);
return ret;
}
static int in_cksum(unsigned short *buf, int sz)
{
int nleft = sz;
int sum = 0;
unsigned short *w = buf;
unsigned short ans = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char *) (&ans) = *(unsigned char *) w;
sum += ans;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
ans = ~sum;
return (ans);
}
Hope this helps someone later :-)
-- G.