//
// CRAM: CREATE Real-time Applications Manager
// See the copyright and acknowledgment of authors in the file COPYRIGHT
//
//// ProgramUtils.cpp -- Program class' socket I/O and thread mgmnt methods ////
//
//		This is the low-level implementation of the CRAM protocol, 
//		and could be swapped out for another form of getting call-backs 
//		(e.g., CORBA, XML/RPC, etc.)
//

#include "Program.h"

#include <signal.h>		// the kernel here needs all sorts of UNIX socket and networking code
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>

using namespace cram;

// IO loop thread function prototype
// I generally make thread functions external, although they could be static member functions 

extern "C" void * read_loop (void * inst);
extern "C" void * emergency_loop (void * inst);

#define STARTUP_FAILURE { 											\
		printf("\t[CRAM start-up failure]\n");						\
		mLog.log_msg(kLogFatal, "CRAM start-up failure");			\
		perror("Error starting CRAM program");						\
		mStatus = CRAM_FAILED;										\
		return(-1);													\
	}

////// initialize method; very paranoid
//
// Spawn 2 threads -- don't handle failure nicely;
// 	it's up to the manager to give you good socket addresses!
// Write pid, cmd_port, emergency_port to pid file in /tmp

int Program :: start_threads() {
//	printf("\t[Starting threads]\n"); fflush(stdout);
	try {
		mPid = (int) getpid();			// my ID is my pid
		
		if (spawn_l_thread())			// start listener thread
			STARTUP_FAILURE

#ifdef SPAWN_EMERGENCY_THREAD			// this is optional, though recommended
		if (spawn_e_thread())			// start emergency thread
			STARTUP_FAILURE
#endif
	} catch (int exc) 					// catch any error and fail
		STARTUP_FAILURE
		
#ifdef CREATE_PID_FILE
	try {								// create pid file in /tmp
		char pf_name[64];
		sprintf(pf_name, "%s%s.pid", CRAM_PID_DIR, mName);
		FILE * pid_file = fopen(pf_name, "w");
		if (pid_file == NULL)
			STARTUP_FAILURE				// write pid file info
		fprintf(pid_file, "%d\n%d\n%d\n", mPid, mLPort, mEPort);
		fclose(pid_file);
	} catch (int exc) 					// catch any error and fail
		STARTUP_FAILURE
#endif
	mStatus = CRAM_ON;
	return 0;
}

// Shut down -- kill the 2 listener threads
// don't actually exit; assume main() is waiting on the threads to die

void Program :: shut_down() {
		mLog.log_msg("CRAM program shutting down.");
		mLog.close();
		mStatus = CRAM_OFF;
}

////// Thread Management methods
// No exception handling at this level

// Start/restart the main command-listener thread on some port

int Program :: spawn_l_thread() {

	if (mLThread) 
		pthread_kill(mLThread, SIGINT);
									// CREATE THREAD on real_loop
	int err = pthread_create( & mLThread, NULL, read_loop, (void *) this);
									// don't handle failure nicely; 
									// it's up to the manager to give you good socket addresses!
	if ((err != 0) || (mLThread == 0)) {
		mLog.log_msg("Error starting CRAM program read thread; exiting.");
		perror("Error starting CRAM program read thread");
		return -1;
	}
	return 0;
}

// Spawn the emergency listener thread; only necessary once, since without it, we're lost...

int Program :: spawn_e_thread() {

	if (mEThread) {		// This should really never happen, having to re-spawn the emergency thread
		pthread_kill(mEThread, SIGINT);
		mLog.log_msg("Error trying to restart emergency thread; exiting.");
		exit(0);
	}
									// CREATE THREAD on emergency_loop
	int err = pthread_create( & mEThread, NULL, emergency_loop, (void *) this);
	
	if ((err !=0) || (mEThread == 0)) {
		mLog.log_msg("Error starting CRAM program emergency thread; exiting.");
		perror("Error starting CRAM program emergency thread");
		return -1;
	}
	return 0;
}


////// Thread function loops -- these two are the forked sub-threads for the listener and emergency loops

#define GIVE_UP perror(""); close(sock); 		\
				the_prog->mLThread = 0;			\
				the_prog->mLSocket = 0; 		\
				pthread_exit(NULL)

// This is the main read/eval/respond loop of all CRAM programs; it's forked as a separate thread

extern "C" void * read_loop (void * inst) {
	int sock, rval, wval;
	struct sockaddr_in sname, clientName = { 0 };
	in_addr_t ip;
	int addr_len = (int) sizeof(sname);
	char buffer[CRAM_MAX_MSG] = { 0 };				// the actual I/O buffer storage -- in the loop function
	Program * the_prog = (Program *) inst;			// the object pointer
	Logger * mLog = & (the_prog->mLog);
	
													// Create a socket to wait for requests
//	rval = the_prog->safe_open( & sock, the_prog->mLPort, & sname);
//	if (rval != the_prog->mLPort) {
//		mLog->log_msg(kLogFatal, "Safe open answered different port -- exp %d got %d", the_prog->mLPort, rval);	
//		GIVE_UP;
//	}
												// Create a socket to wait for requests
	try {
		mLog->log_msg("[Starting read loop for %s]", the_prog->mName);	
		sock = socket(PF_INET, SOCK_DGRAM, 0);
		if (sock < 0) {
			mLog->log_msg(kLogError, "Could not create read socket");	
			printf("Could not create read socket\n");
			GIVE_UP;
		}
		SET_IP_ADDR( (& sname), the_prog->mLPort, INADDR_ANY);
		if (bind(sock, (struct sockaddr *) & sname, sizeof(sname)) < 0) {
			mLog->log_msg(kLogError, "Service %s could not bind name to read socket %d", 
									the_prog->mName, the_prog->mLPort);	
			printf("Could not bind a name to the read socket\n");
			GIVE_UP;
		}
		if (the_prog->mReuseAddresses) {
			int yes = 1;
			if ((setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, & yes, sizeof(int)) < 0)
					|| (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, & yes, sizeof(int)) < 0)) {
				mLog->log_msg(kLogError, "Could not set read socket options");	
				printf("Could not set read socket options\n");
				GIVE_UP;
			}
		}
	} catch (int err) {
			mLog->log_msg(kLogError, "Listener socket setup exception");	
			printf("Service %s: read socket receive exception; exiting.", the_prog->mName);
			GIVE_UP;
	}
	the_prog->mLSocket = sock;

	rval = wval = 0;								// return val from safe receive
	int retry = 0;									// retry count
	int timeout = CRAM_SELECT_TIME;					// time out every X seconds
	if (mLog->mLevel > 3) mLog->log_msg("Start read loop on port %d sock %d", the_prog->mLPort, sock);	
	while (the_prog->mStatus != CRAM_OFF) {			// LOOP
		try { 
			while (rval == 0)				// do the safe receive for a command
											// be very careful blocking receive from mgr socket, get at least 12 bytes
				rval = the_prog->safe_recv(sock, buffer, CRAM_HEADER_SIZE, 
										(struct sockaddr *) & clientName, & addr_len, timeout, retry);
		} catch (int exc) {					// catch any error and fail
			mLog->log_msg(kLogFatal, "Socket receive exception");	
			printf("Service %s: Socket receive exception; exiting.", the_prog->mName);
			GIVE_UP;
		}
		if (rval > 0) {						// if receive succeeded
			the_prog->mLastLMsg = time(NULL);		// log last message receipt time
			ip = clientName.sin_addr.s_addr;
			the_prog->mMgrIP = ip;			// store the most recent sender
											// process the packet (response is # of bytes to answer back)
			wval = the_prog->process_request(ip, buffer);
			
			if (wval > 0) {					// if process_request() said there's a response, send it back...
				try { 
											// do the safe send of the response packet
					wval = the_prog->safe_send(sock, buffer, wval, (struct sockaddr *) & clientName); 
					
				} catch (int exc) {			// catch any error and fail
					mLog->log_msg(kLogError, "Socket write exception");	
					printf("Service %s: Socket write exception; exiting.", the_prog->mName);
					GIVE_UP;
				}
				if (wval < 0) {
					mLog->log_msg(kLogError, "Could not write listener response to socket: %d", errno);	
					perror("Could not write listener response to socket");
				}
			} else if (wval < 0) {			// if val < 0: error (val == 0 means no reply)
				mLog->log_msg(kLogError, "Error processing packet: %d", wval);
				perror("Could not process packet");
			}
			rval = 0;						// reset read counter
		}
	}								// end of endless loop
	printf("Service %s: read_loop exiting.", the_prog->mName);
	GIVE_UP;								// Never Reached (until exit time)
	return NULL;
}

// This is the emergency loop of all CRAM programs; it's forked as a separate thread
// The socket handling is more careful and read here is more paranoid than the listener loop

#define EGIVE_UP perror(""); close(sock); 		\
				the_prog->mEThread = 0;			\
				the_prog->mESocket = 0; 		\
				pthread_exit(NULL)

extern "C" void * emergency_loop (void * inst) {
	int sock, rval, wval;
	struct sockaddr_in sname, clientName = { 0 };
	in_addr_t ip;
	int addr_len = (int) sizeof(clientName);
	char buffer[CRAM_MAX_MSG] = { 0 };			// the actual I/O buffer storage -- in the loop function
	CRAMPacketHeader * pkt = (CRAMPacketHeader *) buffer;
	Program * the_prog = (Program *) inst;		// the object pointer
	Logger * mLog = & (the_prog->mLog);
												// Create a socket to wait for requests
//	rval = the_prog->safe_open( & sock, the_prog->_e_port, & sname);
//	if (rval != the_prog->_e_port) {
//		mLog->log_msg(kLogFatal, "Safe open answered different port -- exp %d got %d", the_prog->_e_port, rval);	
//		GIVE_UP;
//	}

	try {
		mLog->log_msg("[Starting emergency loop for %s]", the_prog->mName);	
		sock = socket(PF_INET, SOCK_DGRAM, 0);
		if (sock < 0) {
			mLog->log_msg(kLogError, "Could not create emergency socket");	
			printf("Could not create emergency socket\n");
			EGIVE_UP;
		}
		SET_IP_ADDR( (& sname), the_prog->mEPort, INADDR_ANY);
		if (bind(sock, (struct sockaddr *) & sname, sizeof(sname)) < 0) {
			mLog->log_msg(kLogError, "Could not bind name to emergency socket");	
			printf("Could not bind a name to the emergency socket\n");
			EGIVE_UP;
		}
		if (the_prog->mReuseAddresses) {
			int yes = 1;
			if ((setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, & yes, sizeof(int)) < 0)
					|| (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, & yes, sizeof(int)) < 0)) {
				mLog->log_msg(kLogError, "Could not set emergency socket options");	
				printf("Could not set emergency socket options\n");
				EGIVE_UP;
			}
		}
	} catch (int err) {
			mLog->log_msg(kLogError, "Emergency socket setup exception");	
			printf("Service %s: emergency socket receive exception; exiting.", the_prog->mName);
			EGIVE_UP;
	}
	the_prog->mESocket = sock;
	rval = wval = 0;						// return val form safe receive
	int retry = 0;							// retry count
	int timeout = CRAM_SELECT_TIME;			// time out every X seconds
	if (mLog->mLevel > 3) mLog->log_msg("Start emergency loop on port %d sock %d", the_prog->mEPort, sock);	
	while (the_prog->mStatus != CRAM_OFF) {				// LOOP
do_over: 
		try { 
			while (rval == 0)				// very careful blocking receive from mgr socket, get at least 8 bytes
				rval = the_prog->safe_recv(sock, buffer, CRAM_HEADER_SIZE, 
								(struct sockaddr *) & clientName, & addr_len, timeout, retry);
		} catch (int exc) {					// catch any error and fail
			mLog->log_msg(kLogError, "Emergency socket receive exception");	
			printf("Service %s: emergency socket receive exception; exiting.", the_prog->mName);
			EGIVE_UP;
		}
		if (rval > 0) {						// if receive succeeded
			ip = clientName.sin_addr.s_addr;
											// Handle the packet locally here, rather than through proces_packet()
			int reply = the_prog->check_header(pkt, CRAM_HEADER_SIZE);	// check packet header
			if (reply != CRAM_AOK)			// on error, return packet header with error flag in the size field
				goto do_over;				// ignore bogus or partial packets	
											// process the packet (response is # of bytes to answer back)
			wval = the_prog->handle_packet(clientName.sin_addr.s_addr, pkt);
			
			if (wval > 0) {					// if process_request() said there's a response, send it back...
				try { 
					wval = the_prog->safe_send(sock, buffer, wval, (struct sockaddr *) & clientName); 
				} catch (int exc) {			// catch any error and fail
					mLog->log_msg(kLogError, "Socket write exception in emergency loop");	
					printf("Service %s: Socket write exception in emergency loop; exiting.", the_prog->mName);
					EGIVE_UP;
				}
				if (wval < 0) {
					mLog->log_msg(kLogError, "Could not write emergency response to socket: %d", errno);	
					perror("Could not write emergency response to socket");
				}
			} else if (wval < 0) {			// if val < 0: error (val == 0 means no reply)
				mLog->log_msg(kLogError, "Error processing emergency packet: %d", wval);
				perror("Could not process emergency packet");
			}
			rval = 0;						// reset read counter
		}
	}								// end of endless loop
	printf("Service %s: emergency loop exiting.", the_prog->mName);
	EGIVE_UP;								// Never Reached (until exit time)
	return NULL;
}


////// Lowest-level Socket utilities

// Safe socket IO

// Safe_open creates and binds to a socket with exception handling
// Answer the port # (if the port passed in is 0, it is guessed)

int Program :: safe_open(int * ssoc, unsigned short port, struct sockaddr_in * scln) {
	int sock;
	
	try {
		mLog.log_msg("[Safe socket open for port %d]", port);	
		sock = socket(PF_INET, SOCK_DGRAM, 0);		// socket call
		if (sock < 0) {
			mLog.log_msg(kLogError, "Could not create socket");	
			return -1;
		}
		SET_IP_ADDR(scln, port, INADDR_ANY);		// set up address
													// bind call
		if (bind(sock, (struct sockaddr *) & scln, sizeof(struct sockaddr_in)) < 0) {
			mLog.log_msg(kLogError, "Could not bind name to listener socket");	
			return -1;
		}
		if (mReuseAddresses) {
			int yes = 1;								// setsockopt calls
			if ((setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, & yes, sizeof(int)) < 0)
					|| (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, & yes, sizeof(int)) < 0)) {
				mLog.log_msg(kLogError, "Could not set socket options");	
				return -1;
			}
		}
	} catch (int err) {
			mLog.log_msg(kLogError, "Socket receive exception");	
			return -1;
	}
	* ssoc = sock;
	return scln->sin_port;			// answer the port used
}

// Safe_send retries multiple writes to a socket; returns bytes sent or error code
// It does no exception handling; call it with in try{}catch{}

int Program :: safe_send(int ssoc, char * buf, unsigned ssiz, struct sockaddr * scln) {
	int val;
	unsigned ssent = 0;	
	unsigned to_ssend = ssiz;	
	while (ssent < ssiz) {					// loop retrying partial sends
		if (mLog.mLevel > 3) printf("Calling sendto %d bytes\n", to_ssend);	
		
		val = sendto(ssoc, (& buf[ssent]), to_ssend, 0, scln, sizeof(struct sockaddr_in));
		
		if (val < 0) {	
			mLog.log_msg(kLogError, "%s: safe-sending to socket: %d", mName, val);
			perror("Could not write to socket");
			return -1;
		} else {
			ssent += val;
			to_ssend -= val;
		}
	}
	return ssent;
}

// Safe_recv times-out after a given period, has several levels of retry, 
// answers the # of bytes read.
// It does no exception handling; call it with in try{}catch{}

int Program :: safe_recv(int ssoc, char * buf, unsigned ssiz, struct sockaddr * scln, int * sadr, int stimeo, int srtry) {
	int ss_ret = 0, sr_ret = 0;		
	CRAMPacketHeader * pkt = (CRAMPacketHeader *) buf;
	
#ifdef USE_SELECT						// If we're compiled to use the select() call
	fd_set sfds;			
	struct timeval stv;			
	do {								// loop retrying partial select/reads
		FD_ZERO( & sfds);							// set up file descriptor bitmask
		FD_SET(ssoc, & sfds);		
		stv.tv_sec = stimeo;						// set up timeout struct
		stv.tv_usec = 0;		
		
		if (mLog.mLevel > 3) mLog.log_msg("Calling select");	
		
		ss_ret = select(ssoc + 1, & sfds, NULL, NULL, & stv);	// do select() with timeout
		
		if (mLog.mLevel > 3) mLog.log_msg("Select returned %d", ss_ret);	
		if ((ss_ret > 0) && ( ! FD_ISSET(ssoc, & sfds))) {		// select returns 1, is my bit set?
			mLog.log_msg(kLogError, "%s: select returned wrong bit in safe_recv", mName);
			return 0;											// should never happen	
		}
	} while((ss_ret == 0) && (srtry--));						// answer 0 means timeout
#else
	ss_ret = 1;							// if no select, just block on the recvfrom
#endif	
	if (ss_ret > 0) {								// if we got something
		if (mLog.mLevel > 3) mLog.log_msg("Calling recvfrom");	
		
		sr_ret = recvfrom(ssoc, buf, CRAM_MEDIUM_MSG, 0, scln, sadr);	// try recv-from
		
		if (mLog.mLevel > 3) mLog.log_msg("Select recvfrom %d", sr_ret);	
		
		if (sr_ret < 0) {							// if it fails...	
			mLog.log_msg(kLogError, "Recv IO error %d", sr_ret);
			perror("Recv IO error");
			return -1;	
		} else {									//else check read size
			if (pkt->size < ssiz) {					// if we got less than the minimum, give up
				mLog.log_msg(kLogError, "Recv size error: %d", pkt->size);
				return -1;
			}	
		}		
	} else if (ss_ret == 0) {						// if we timed out too many times
		return 0;
	} else if (ss_ret < 0) {						// select failed badly
		mLog.log_msg(kLogError, "Select error %d", ss_ret);	
		perror("Select error");
		return -1;	
	}
	return sr_ret;	
}

////// Packet processing utilities

// Check the buffer header; answer 0 or -1 on error

int Program :: check_header(CRAMPacketHeader * pkt_header, unsigned short min_size) {
	int mag = pkt_header->magic;
	if ((mag & 0xffffff00) != CRAM_PACKET_MAGIC) {		// Check header fields, ignore packet on any error
		printf("%s: Bad packet magic number: %x\n", mName, mag);
		mLog.log_msg(kLogError, "Bad packet magic number: %x", mag);
		pkt_header->magic = CRAM_PACKET_MAGIC;
		pkt_header->size = CRAM_HEADER_ERROR;		// If header bad -- set error flag in size
		return CRAM_HEADER_ERROR;
	}
	if (pkt_header->size < min_size) {
		printf("%s: Packet too small: %d\n", mName, pkt_header->size);
		mLog.log_msg(kLogWarning, "Packet too small: %d", pkt_header->size);
		pkt_header->size = CRAM_HEADER_ERROR;
		return CRAM_HEADER_ERROR;
	}
	return CRAM_AOK;
}

// Read arguments from the given packet, performing appropriate checking 
// (works like scanf, also checks packet footer)
// Formats: "s" = string arg; "d" = short arg, "i" = int arg
// Example scan_packet(pkt, "ds", (short *), (char *))

int Program :: scan_packet(CRAMPacketHeader * pkt, char * format, ...) {
	va_list args;
	short * arg_short;
	int * arg_int, footer;
	char * arg_str, * buf_str;
	unsigned offset = CRAM_HEADER_SIZE;
	unsigned num_args = strlen(format);
	int arg_len;
	char * buffer = (char *) pkt;

	va_start(args, format);
	for (unsigned i = 0; i < num_args; i++) {
		switch (format[i]) {
		case 's':								// read a null-terminated string arg
			arg_str = va_arg(args, char *);
			buf_str = (char *) & buffer[offset];
			arg_len = safe_strlen(buf_str, (pkt->size - offset));		// use my safe strlen here
			if (arg_len > 0) {					// OK: copy string
				memcpy((void *) arg_str, (void *) buf_str, arg_len);
				arg_str[arg_len] = '\0';
				offset += (arg_len + 1);
			} else if (arg_len == 0) { 			// empty
				offset++;
				break;
			} else								// -1 strlen = non-terminated packet
				return CRAM_ARGUMENT_ERROR;
			break;
		case 'd':								// read a short int (16-bit) arg
			arg_short = va_arg(args, short *);
			memcpy((void *) arg_short, (void *) ( & buffer[offset]), 2);
			offset += 2;
			break;
		case 'i':								// read an int-32 arg
			arg_int = va_arg(args, int *);
			memcpy((void *) arg_int, (void *) ( & buffer[offset]), 4);
			offset += 4;
			break;
		default:
			printf("%s: Bad packet argument type: %c\n", mName, format[i]);
			mLog.log_msg(kLogError, "Bad packet argument type: %c", format[i]);
			return CRAM_ARGUMENT_ERROR;
			break;
		}
	}
	va_end(args);								// now check the packet footer
	memcpy((void *) & footer, (void *) & (buffer[offset]), 4);
	if (footer != CRAM_PACKET_MAGIC) {			// packet ends with magic number
	//	printf("Error reading footer from packet\n");
		mLog.log_msg(kLogError, "Error reading footer from formatted packet");
	//	return CRAM_ARGUMENT_ERROR;
	}
	return CRAM_AOK;
}

// Print arguments onto the given packet (works like printf)
// Formats: "s" = string arg; "d" = short arg, "i" = int arg
// Example print_packet(pkt, "ds", (short), (char *))

int Program :: print_packet(CRAMPacketHeader * pkt, char * format, ...) {
	va_list args;
	short arg_short;
	int arg_int, footer;
	char * arg_str, * buf_str;
	unsigned offset = CRAM_HEADER_SIZE;
	unsigned num_args = strlen(format);
	int arg_len;
	char * buffer = (char *) pkt;

	va_start(args, format);
	for (unsigned i = 0; i < num_args; i++) {
		switch (format[i]) {
		case 's':						// write a null-terminated string arg
			arg_str = va_arg(args, char *);
			buf_str = (char *) & buffer[offset];
			arg_len = strlen(arg_str);
			if (arg_len > 0) {					// OK: copy string
				sprintf(buf_str, "%s", arg_str);
				offset += arg_len;
				buf_str[offset] = '\0';
				offset++;
			}
			break;
		case 'd':						// write a short int (16-bit) arg
			arg_int = va_arg(args, int);
			arg_short = (short) arg_int;
			memcpy((void *) ( & buffer[offset]), (void *) & arg_short, 2);
			offset += 2;
			break;
		case 'i':						// read an int-32 arg
			arg_int = va_arg(args, int);
			memcpy((void *) ( & buffer[offset]), (void *) & arg_int, 4);
			offset += 4;
			break;
		default:
			printf("%s: Bad packet argument type: %c\n", mName, format[i]);
			mLog.log_msg(kLogError, "Bad packet argument type: %c", format[i]);
			return CRAM_ARGUMENT_ERROR;
			break;
		}
	}
	va_end(args);
	footer = CRAM_PACKET_MAGIC;		// write trailing magic number
	memcpy((void *) & (buffer[offset]), (void *) & footer, 4);
	return offset + 4;
}

// safe strlen (essentially str_n_len) returns null is string shorter than max

int Program :: safe_strlen(char * src, unsigned max) {
	char * ptr = src;
	unsigned i = max;
	while ( *ptr++  &&  i-- ) 		// loop to null character or max_len (whichever comes first)
		;
	if (i)
		return (ptr - src - 1);
	else
		return -1;
}

// Answer a random available port number

unsigned short Program :: guess_port(unsigned short avoidThisOne) {
	int sock, namelen;
	struct sockaddr_in name;
					// Create a socket to test
	sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		mLog.log_msg(kLogError, "Could not create socket (%d)", sock);	
		perror("Could not create socket");
		return 0;
	}
	SET_IP_ADDR( (& name), 0, INADDR_ANY)
	if (bind(sock, (struct sockaddr *) & name, sizeof(name)) != 0) {
		mLog.log_msg(kLogError, "Could not bind to socket (%d)", sock);	
		perror("Could not bind to socket");
		close(sock);
		return 0;
	}
	getsockname(sock, (struct sockaddr *) & name, & namelen);
	close(sock);
						// if the above bind failed, just try a few...
	if ((name.sin_port == 0) || (name.sin_port == avoidThisOne)) {
		srandom((unsigned long) time(NULL));		// set random seed
		int port = 10000 + (int) ((float)random() / (float)RAND_MAX * 20000.0);
incr:   while (test_port(port) != CRAM_AOK)
			port++;
		if (port == avoidThisOne) {
			port++;
			goto incr;
		}
		return port;
	} else {
		return (name.sin_port);
	}
}

// Test the given port, answer CRAM_AOK or not

int Program :: test_port(unsigned short port) {
	int sock;
	struct sockaddr_in name;
					// Create a socket to test
	sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		mLog.log_msg(kLogError, "Could not create socket (%d)", sock);	
		perror("Could not create socket");
		return CRAM_IO_ERROR;
	}
	SET_IP_ADDR( (& name), port, INADDR_ANY)
	if (bind(sock, (struct sockaddr *) & name, sizeof(name)) < 0) {
		mLog.log_msg(kLogError, "Could not bind to socket (%d)", sock);	
		perror("Could not bind to socket");
		close(sock);
		return CRAM_IO_ERROR;
	}
	close(sock);
	return CRAM_AOK;
}

// Get a string for the given value

char * Program :: flag_string(int flag) {

		switch (flag) {
			case CRAM_HEADER_ERROR:				return("Header error");
			case CRAM_ARGUMENT_ERROR:			return("Argument error");
			case CRAM_COMMAND_ERROR:			return("Command ID error");
			case CRAM_PROCESSING_ERROR:			return("Processing error");
			case CRAM_IO_ERROR:					return("IO error");
			case CRAM_NO_SERVICE_ERROR:			return("No such service");
			case CRAM_OTHER_ERROR:				return("Unknown error");
			
			case CRAM_AOK:						return("Status AOK");
			case CRAM_WARN:						return("Status Warning");
			case CRAM_ERROR:					return("Status Error");
			
			case CRAM_INIT:						return("Init State");
			case CRAM_ON:						return("On State");
			case CRAM_SUSPENDED:				return("Suspended State");
			case CRAM_OFF:						return("Off State");
			case CRAM_CONFUSED:					return("Confused State");
			case CRAM_ZOMBIE:					return("Zombie State");
			case CRAM_UNKNOWN:					return("Snknown State");
		}
		return NULL;
}

