/*
 * isup v0.3.1
 * Is a machine up?
 * Try connecting, if ECONNREFUSED, machine up, so return 0, else non-zero.
 * Alan Ford <ajf101@ecs.soton.ac.uk> 03/04/2003
 *
 * v0.2, with output, 20/04/2003
 * v0.3, with long options and -a (is available), 13/08/2003
 * v0.3.1, will accept service names as well as port numbers, 13/08/2003
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>

#define DEFAULT_PORT 7

static int verbose_flag, available_flag;

void display_help(char *argv[]);

int main (int argc, char *argv[]) {
	int status, c, args_left, port;
	struct servent *s;

	while (1) {
		static struct option long_options[] = {
			{"verbose", no_argument, 0, 'v'},
			{"available", no_argument, 0, 'a'},
			{"help", no_argument, 0, 'h'},
			{0, 0, 0, 0}
		};

		int option_index = 0;

		c = getopt_long(argc, argv, "ahv", long_options, &option_index);

		if (c == -1)
			break;

		switch (c) {
			case 'a':
				available_flag = 1;
				break;
			
			case 'v':
				verbose_flag = 1;
				break;

			default:
				display_help(argv);
		}
	}

	args_left = argc - optind;

	if (args_left < 1)
		display_help(argv);

	if (args_left > 1) {
		port = atoi(argv[optind + 1]);
		if (!port) {
			s = getservbyname(argv[optind + 1], "tcp");
			if (!s) {
				if (verbose_flag)
					printf("%s is unavailable: unknown port\n", argv[optind]);
				exit(-3);
			}
			// Convert from network to host byte order
			port = ntohs(s->s_port); 
		}
	} else {
		port = DEFAULT_PORT;
	}
	
	if (verbose_flag) {
		status = testconn(argv[optind], port);
		if (status == 0)
			printf("%s is available\n", argv[optind]);
		else if (status == -2)
			printf("%s is unavailable: %s\n", argv[optind], hstrerror(h_errno));
		else
			printf("%s is unavailable: %s (error %d)\n", argv[optind], strerror(errno), errno);
	} else
		status = testconn(argv[optind], port);
	
	exit(status);
}

int testconn(char *host, int port) {
	int sock_fd, rc;
	struct sockaddr_in servAddr;
	struct hostent *h;

	h = gethostbyname(host);
	if (h == NULL)
		return(-2);

	sock_fd = socket(AF_INET, SOCK_STREAM, 0);

	if (sock_fd < 0)
		return(-1);

	servAddr.sin_family = h->h_addrtype;
	servAddr.sin_port = htons(port);
	memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);

	rc = connect(sock_fd, (struct sockaddr *)&servAddr, sizeof(struct sockaddr));
	if (rc == 0) {
		close(sock_fd);
		return(0);
	}

	if (errno == ECONNREFUSED && !available_flag)
		return(0);

	return(errno);
}

void display_help(char *argv[]) {
	printf("isup 0.3.1 (2003-08-13)\n");
	printf("Usage:\t%s [-v] [-a] host [port]\n", argv[0]);
	printf("Returns non-zero if machine is down.\n\n");
	printf("\t-v, --verbose\tAlso print output.\n");
	printf("\t-a, --available\tOnly return success if connection is available.\n");
	printf("\t-h, --help\tThis usage information.\n\n");
	printf("Report bugs to Alan Ford <ajf101@ecs.soton.ac.uk>\n");
	exit(-1);
}

