Listing 2: Adam Zell's version of newping
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#define SVC_PORT "time"
#define DEF_TOUT 20 /* default timeout */
#define DEF_PORT 37 /* default port */
#define DEBUG 0X1 /* option flags */
#define VERBOSE 0X2
#define is_opt(x) (opts & (x))
#define add_opt(x) (opts |= (x))
typedef enum { /* use enum instead of multiple
#defines */
OK_RESPONSE = 0,
TOUT_CONNECT = 1,
TOUT_RESPONSE = 2,
REF_CONNECT = 3,
BAD_NET = 4,
BAD_HOST = 5,
BAD_USAGE = 6,
UNKNOWN_ERR = 255
} errtype;
static int hoststring(const char *);
static void die(errtype, const char *, const char *);
static void pexit(errtype);
static void noconnect(int); /* signal handlers */
static void noresponse(int);
/* all globals are volatile */
static volatile int timeout = DEF_TOUT, totsecs = 1, sckt = -1, opts;
static volatile const char *hostname;
int
main(int argc, char *argv[]) {
struct sockaddr_in phost;
struct sigaction phan;
int res;
const struct protoent *pent = getprotobyname("tcp");
const char *hname, *pname = strrchr(argv[0], '/');
if (pname) /* isolate name of program */
pname++;
else
pname = argv[0];
argc--;
argv++;
if (pent == NULL) /* tcp not available */
die(UNKNOWN_ERR, pname, "");
if (argv[0] && argv[0][0] == '-') { /* process options */
const char *p = argv[0] + 1;
if (*p == '\0') /* check for newping - */
die(BAD_USAGE, pname, "");
for (; *p; p++) {
if (*p == 'd')
add_opt(DEBUG);
else if (*p == 'v')
add_opt(VERBOSE);
else die(BAD_USAGE, pname, "");
}
argc--;
argv++;
}
if (argc == 0 || argc > 2)
die(BAD_USAGE, pname, "");
hostname = hname = argv[0]; /* get the host name */
if (argv[1]) { /* if non-NULL, convert to decimal */
timeout = atoi(argv[1]);
if (timeout <= 0)
die(BAD_USAGE, pname, "");
}
if (hoststring(hname)) { /* is it a string? */
const struct hostent *host = gethostbyname(hname);
if (host) { /* found it */
phost.sin_family = host->h_addrtype;
memcpy(&phost.sin_addr.s_addr, host->h_addr, host->h_length);
}
else
die(TOUT_CONNECT, pname, hname);
}
else { /* is in form 000.00.00.00 */
unsigned long ipaddr = inet_addr(hname);
if (ipaddr != INADDR_NONE) { /* found it */
phost.sin_family = AF_INET;
phost.sin_addr.s_addr = ipaddr;
}
else
die(TOUT_CONNECT, pname, hname);
}
if (is_opt(DEBUG))
printf("The IP address for '%s' is: %0lX\n", hname,
phost.sin_addr.s_addr);
phost.sin_port = DEF_PORT;
if (is_opt(DEBUG))
printf("Service %s recognized as port #%u\n",
SVC_PORT, phost.sin_port);
sckt = socket(PF_INET, SOCK_STREAM, pent->p_proto);
if (sckt < 0) { /* socket() returns -1 on failure */
perror("socket");
pexit(UNKNOWN_ERR);
}
if (is_opt(DEBUG))
printf("Socket open. Descriptor Number %d\n", sckt);
phan.sa_handler = &noconnect; /* set up sig handler using sigaction() */
sigemptyset(&phan.sa_mask); /* just block alarm signal */
phan.sa_flags = 0;
/* if defined, SA_RESTART automatically restarts interrupted system calls */
#ifdef SA_RESTART
phan.sa_flags = SA_RESTART;
#endif /* SA_RESTART */
sigaction(SIGALRM, &phan, NULL);
alarm(1);
res = connect(sckt, (struct sockaddr *)&phost, sizeof(phost));
while (res < 0) { /* connect returns -1 on failure */
if (errno != EINTR && errno != EISCONN)
perror("connect");
switch(errno) {
case EINTR: /* we were interrupted...try again */
case EISCONN:
close(sckt);
sckt = socket(PF_INET, SOCK_STREAM, pent->p_proto);
if (sckt < 0) {
perror("socket");
pexit(UNKNOWN_ERR);
}
break;
case ECONNRESET:
case ECONNREFUSED:
pexit(REF_CONNECT);
break;
case EHOSTUNREACH:
pexit(BAD_HOST);
break;
case ENETRESET:
case ENETDOWN:
case ENETUNREACH:
pexit(BAD_NET);
break;
default:
pexit(UNKNOWN_ERR);
break;
}
res = connect(sckt, (struct sockaddr *)&phost, sizeof(phost));
}
if (is_opt(DEBUG) && is_opt(VERBOSE))
printf("Connect made (returned with %d)\n", res);
phan.sa_handler = &noresponse; /* register 2nd phase handler */
sigaction(SIGALRM, &phan, NULL);
do {
char buf[32];
res = recv(sckt, buf, sizeof(buf), 0); /* recv returns -1 on failure */
if (res < 0 && errno != EINTR) { /* if EINTR, try again */
perror("recv");
switch(errno) {
case ECONNABORTED:
case ENOTCONN:
pexit(TOUT_CONNECT);
break;
case ECONNRESET:
case ENETRESET:
pexit(BAD_NET);
break;
default:
pexit(UNKNOWN_ERR);
break;
}
}
} while (res < 0);
if (is_opt(DEBUG) && is_opt(VERBOSE))
printf("Received something. Len = %d Total Elapsed Time: %d\n",
res, totsecs);
phan.sa_handler = SIG_IGN; /* just ignore signal now */
sigaction(SIGALRM, &phan, NULL);
printf("%s is alive (%d)\n", hname, totsecs);
pexit(OK_RESPONSE);
return 0;
}
/* is string in form of mach.net.com or 123.45.67? */
static int
hoststring(const char *p) {
for (; *p; p++) {
if (!isdigit(*p) && *p != '.')
break;
}
return (*p != '\0');
}
/* limited error output */
static void
die(errtype which, const char *p, const char *q) {
if (which == BAD_USAGE)
fprintf(stderr, "usage: %s [-dv] host [timeout]\n", p);
else if (which == TOUT_CONNECT)
fprintf(stderr, "%s: unknown host '%s'\n", p, q);
else
fprintf(stderr, "%s: unknown protocol TCP/IP\n", p);
exit(which);
}
static void
pexit(errtype which) {
if (sckt > -1) /* close socket if necessary */
close(sckt);
exit(which);
}
static void
noconnect(int which) {
alarm(1);
totsecs++;
if (is_opt(DEBUG) || is_opt(VERBOSE))
printf("No connection after %d seconds!\n", totsecs - 1);
if (totsecs > timeout) {
fprintf(stderr, "'%s' not acknowledging connect attempt\n", hostname);
pexit(TOUT_CONNECT);
}
}
static void
noresponse(int which) {
alarm(1);
totsecs++;
if (is_opt(DEBUG) || is_opt(VERBOSE))
printf("No connection after %d seconds!\n", totsecs - 1);
if (totsecs > timeout) {
fprintf(stderr, "'%s' not acknowledging connect attempt\n", hostname);
pexit(TOUT_RESPONSE);
}
}
/* end of file */
|