lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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.