Table of Contents

Client-Server

This document proposes two codes. The first one is a code which can be started up as a daemon or not and write logs either in the standard output or in syslog. This daemon listen to a port (default 1091) wait for a data from a client (either the client below or from the telnet) and return a value! This is like a “skeleton” code which is a good starting point to create a specific application.

This daemon is also accompanied by its rc file which is normally located in /etc/init.d directory to do start/stop properly.

The client is not a daemon. This is a client able to communicate with the server by sending data and receives an answer. This client is in fact developed as a NAGIOS plug-in and returns 0, 1 or 2 as described in the NAGIOS plug-in guidlines.

References

Server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/*Libraries for communications*/
#include <signal.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
 
/*Libraries for the daemon*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
 
/*Library for syslog*/
#include <syslog.h>
 
/*library for options*/
#include <getopt.h>
 
volatile sig_atomic_t done;
 
int usage(FILE *std,char *appname){
 
	char *msg="Usage: %s [OPTION] \n\
	  -D run as daemon (--daemon)\n\
	  -s log handled by syslog (--syslog)\n\
	  -V version (--version)\n\
	  -p port (--port)\n\
	  -h help (--help)\n\
	  -v verbose (--verbose)\n";
 
	fprintf(std,msg,appname);
 
	return;
}
 
 
void inthandler(int Sig){
	done=1;
}
 
int CheckForData(int sockfd){
	struct timeval tv;
	fd_set read_fd;
	tv.tv_sec=0;
	tv.tv_usec=0;
	FD_ZERO(&read_fd);
	FD_SET(sockfd, &read_fd);
	if(select(sockfd+1, &read_fd, NULL, NULL, &tv) ==  -1){
		return 0;
	}
	if(FD_ISSET(sockfd,&read_fd)){
		return 1;
	}
	return 0;
}
 
int main(int argc, char **argv){
	/*char *appname=argv[0];*/
	char *appname="congiro_server";
	static int verbose_flag=0; /* Flag set by `--verbose'. */
	static int syslog_flag=0; /* Flag set by --syslog */
	int demonize=0;
	int port=1091;
	char data[255]={0};/*Miscellaneous use*/
 
	FILE *f_pid;
	char f_pid_name[FILENAME_MAX]="/var/run/";
	strcat(f_pid_name,appname);
	strcat(f_pid_name,".pid");
 
	static int c;
 
	while (1) {
		static struct option long_options[] =
		{
			/* These options set a flag. */
			{"verbose", no_argument,       &verbose_flag, 1},
			{"brief",   no_argument,       &verbose_flag, 0},
 
			/* These options don't set a flag.
			 * We distinguish them by their indices. */
			{"help",     no_argument,       0, 'h'},
			{"version",  no_argument,       0, 'V'},
			{"daemon",   no_argument, 0, 'D'},
			{"syslog",   no_argument, 0, 's'},
			{"port",  required_argument, 0, 'p'},
			{0, 0, 0, 0}
		};
 
		/* getopt_long stores the option index here. */
		int option_index = 0;
 
		c = getopt_long(argc, argv, "hVDvsp:",long_options, &option_index);
 
		/* Detect the end of the options. */
		if (c == -1)
			break;
 
		switch (c) {
			case 0:
				/* If this option set a flag, do nothing else now. */
				if (long_options[option_index].flag != 0)
					break;
 
				printf("option %s", long_options[option_index].name);
 
				if (optarg)
					printf(" with arg %s", optarg);
 
				printf("\n");
				break;
 
			case 'h':
				usage(stdout,appname);
				return EXIT_SUCCESS;
				break;
 
			case 'V':/*Version*/
				break;
 
			case 'p':/*Port*/
				port=atoi(optarg);
				break;
 
			case 'D':
				demonize=1;
				syslog_flag=1;
				break;
 
			case 'v':
				verbose_flag++;
				break;
 
			case 's':
				syslog_flag=1;
				break;
 
			default:
				abort();
		}
	}
 
	/* Open any logs here */
	if (syslog_flag == 1)
		syslog(LOG_NOTICE, " started by User %d", getuid());
	else
		printf("%s started by User %d\n",appname,getuid());
 
	/* verbosity?? */
	if(verbose_flag){
		if (syslog_flag == 1)
			syslog(LOG_NOTICE, " verbosity set to %i", verbose_flag);
		else
			printf("Verbosity set to %i\n",verbose_flag);
	}
 
	if(demonize){
		/*ONLY SYSLOG EXISTS HERE*/
 
		/* Our process ID and Session ID */
		pid_t pid, sid;
 
		/* Fork off the parent process */
		pid = fork();
		if (pid < 0) {
			exit(EXIT_FAILURE);
		}
 
		/* If we got a good PID, then
		 * we can exit the parent process. */
		if (pid > 0) {
			/*Write PID in /var/run/xxx.pid*/
			f_pid=fopen(f_pid_name,"w");
			if(f_pid != NULL){
				fprintf(f_pid,"%i\n",pid);
				fclose(f_pid);
			}else{
				syslog(LOG_NOTICE, " opening %s file failed!", f_pid_name);
			}
			exit(EXIT_SUCCESS);
		}
 
		/* Change the file mode mask */
		umask(0);
 
		/* Create a new SID for the child process */
		sid = setsid();
		if (sid < 0) {
			/* Log the failure */
			exit(EXIT_FAILURE);
		}
 
		/* Change the current working directory */
		if ((chdir("/")) < 0) {
			/* Log the failure */
			exit(EXIT_FAILURE);
		}
 
		/* Close out the standard file descriptors */
		close(STDIN_FILENO);
		close(STDOUT_FILENO);
		close(STDERR_FILENO);
	}
 
	/* Daemon-specific initialization goes here */
 
	int sockfd=0;
	int new_fd=0;
	struct sockaddr_in my_addr;
	struct sockaddr_in their_addr;
	socklen_t sin_size=0;
 
	if(signal(SIGINT,SIG_IGN) != SIG_IGN){
		signal(SIGINT,inthandler);
	}
 
	if(signal(SIGTERM,SIG_IGN) != SIG_IGN){
		signal(SIGTERM,inthandler);
	}
 
	my_addr.sin_family=AF_INET;
	my_addr.sin_addr.s_addr=INADDR_ANY;
	my_addr.sin_port=htons(port);
 
	memset(my_addr.sin_zero,0,sizeof my_addr.sin_zero);
 
	if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){
		if (syslog_flag == 1)
			syslog(LOG_NOTICE, " Unexpected error on socket()");
		else
			printf("Unexpected error on socket()\n");
 
		return EXIT_FAILURE;
	}
 
	if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1){
		if (syslog_flag == 1)
			syslog(LOG_NOTICE, " Unexpected error on bind()");
		else
			printf("Unexpected error on bind()\n");
 
		return EXIT_FAILURE;
	}
 
	if(listen(sockfd,4)==-1){
		if (syslog_flag == 1)
			syslog(LOG_NOTICE, " Unexpected error on listen()");
		else
			printf("Unexpected error on listen()\n");
 
		shutdown(sockfd,2);
		return EXIT_FAILURE;
	}
 
	/* The Big Loop */
	while(!done){
 
		/* Do some task here ... */
 
 
		/*ICI*/
 
		if(0!=CheckForData(sockfd)){
			sin_size=sizeof(struct sockaddr_in);
			if((new_fd=accept(sockfd,(struct sockaddr *)&their_addr,&sin_size)) == -1){
				if (syslog_flag == 1)
					syslog(LOG_NOTICE, " Unexpected error on accept()");
				else
					printf("Unexpected error on accept()\n");
				continue;
			}
 
			memset(data,0,sizeof data);
 
			if(recv(new_fd,data,sizeof data,0) == -1){
				if (syslog_flag == 1)
					syslog(LOG_NOTICE, " Unexpected error on recv()");
				else
					printf("Unexpected error on recv()\n");
			}else{
				if(syslog_flag == 1)
					syslog(LOG_NOTICE, " Received: %s", data);
				else
					printf("Received: %s\n",data);
			}
 
			memset(data,0,sizeof data);
 
			sprintf(data,"00");
			if(send(new_fd,data,sizeof data, 0) == -1){
				if (syslog_flag == 1)
					syslog(LOG_NOTICE, " Unexpected error on send()");
				else
					printf("Unexpected error on send()\n");
			}
 
			shutdown(new_fd,2);
		}
 
		sleep(5); /*wait 5 seconds*/
	}
 
	shutdown(sockfd,2);
	remove(f_pid_name);
 
	if (syslog_flag == 1)
		syslog(LOG_NOTICE, " User requested program to halt.");
	else
		printf("User requested program to halt.\n");
 
	exit(EXIT_SUCCESS);
}

Makefile

# Makefile for main atas
# Sylvain Bolay
#
# History:
# 09/03/04 -- created
#
# Please read the file README.makefile that came
# with your distribution for an overview
# on using this Makefile
 
######################################################
################## Miscellaneous #####################
######################################################
# The following variables apply to compilation
 
# Destination bin directory for executables.  Note: this
# directory *must* exist, or compilation will fail during
# linkage.
BINDIR = ./
 
######################################################
############### Sequential Code Variables ############
######################################################
# Edit the following when compiling.
 
# C compiler for sequential code
LCC = gcc
WCC = i586-mingw32msvc-gcc
 
CFLAGS= -O3
 
############################################################
#/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\#
############################################################
#                                                          #
# You shouldn't need to change anything beyond this point. #
#                                                          #
############################################################
#/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\#
############################################################
 
######################################################
################# common object files ################
######################################################
linux_obj = \
	linux_main.o
 
win32_obj = \
	win32_main.o
 
######################################################
#################### targets #########################
######################################################
 
################## sequential targets ###############
linux : $(linux_obj)
	$(LCC) -lm -o congiro_server $(linux_obj)
 
win32 : $(win32_obj)
	$(WCC) -lm -o congiro_server.exe $(win32_obj)
 
######################################################
############## implicit rules for object files #######
######################################################
 
############## main rules
linux_main.o : main.c
	$(LCC) -c $(CFLAGS) main.c -o linux_main.o
 
win32_main.o : main.c
	$(WCC) -c $(CFLAGS) main.c -o win32_main.o
 
######################################################
############# cleanup rules ##########################
######################################################
clean: 
	-$(RM) *.o
 
######################################################
############## dependencies ##########################
######################################################
linux_main.o : main.c
win32_main.o : main.c

RC file

#! /bin/sh
### BEGIN INIT INFO
# Provides:          congiro
# Required-Start:    $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Init script for the congiro_server daemon
# Description:       This file should be used to start stop the congiro_server and be 
#                    placed in /etc/init.d.
#		     This is based on the /etc/init.d/skeleton file provided by Debian
### END INIT INFO
 
# Author: Sylvain Bolay <sylvain.bolay@irovision.ch>
 
# Do NOT "set -e"
 
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Daemon which checks RS485 clients and report status in case of failure!"
NAME=congiro_server
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS="--daemon --port 9001"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
 
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
 
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
 
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
 
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
 
#
# Function that starts the daemon/service
#
do_start()
{
	# Return
	#   0 if daemon has been started
	#   1 if daemon was already running
	#   2 if daemon could not be started
	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
		|| return 1
	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
		$DAEMON_ARGS \
		|| return 2
	# Add code here, if necessary, that waits for the process to be ready
	# to handle requests from services started subsequently which depend
	# on this one.  As a last resort, sleep for some time.
}
 
#
# Function that stops the daemon/service
#
do_stop()
{
	# Return
	#   0 if daemon has been stopped
	#   1 if daemon was already stopped
	#   2 if daemon could not be stopped
	#   other if a failure occurred
	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
	RETVAL="$?"
	[ "$RETVAL" = 2 ] && return 2
	# Wait for children to finish too if this is a daemon that forks
	# and if the daemon is only ever run from this initscript.
	# If the above conditions are not satisfied then add some other code
	# that waits for the process to drop all resources that could be
	# needed by services started subsequently.  A last resort is to
	# sleep for some time.
	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
	[ "$?" = 2 ] && return 2
	# Many daemons don't delete their pidfiles when they exit.
	rm -f $PIDFILE
	return "$RETVAL"
}
 
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
	#
	# If the daemon can reload its configuration without
	# restarting (for example, when it is sent a SIGHUP),
	# then implement that here.
	#
	start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
	return 0
}
 
case "$1" in
  start)
	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
	do_start
	case "$?" in
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
	esac
	;;
  stop)
	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
	do_stop
	case "$?" in
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
	esac
	;;
  #reload|force-reload)
	#
	# If do_reload() is not implemented then leave this commented out
	# and leave 'force-reload' as an alias for 'restart'.
	#
	#log_daemon_msg "Reloading $DESC" "$NAME"
	#do_reload
	#log_end_msg $?
	#;;
  restart|force-reload)
	#
	# If the "reload" option is implemented then remove the
	# 'force-reload' alias
	#
	log_daemon_msg "Restarting $DESC" "$NAME"
	do_stop
	case "$?" in
	  0|1)
		do_start
		case "$?" in
			0) log_end_msg 0 ;;
			1) log_end_msg 1 ;; # Old process is still running
			*) log_end_msg 1 ;; # Failed to start
		esac
		;;
	  *)
	  	# Failed to stop
		log_end_msg 1
		;;
	esac
	;;
  *)
	#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
	echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
	exit 3
	;;
esac
 
:

Client

/* Sylvain Bolay (sylvain.bolay@irovision.ch)
 * Jan 14, 2009
 * Plugin for Nagios 2 witch is used to query the congIRO server daemon
 * Follows this guidline: http://nagiosplug.sourceforge.net/developer-guidelines.html
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
 
#include <getopt.h>
 
#define NAGIOS_OK 0
#define NAGIOS_WARNING 1
#define NAGIOS_CRITICAL 2
#define NAGIOS_UNKNOWN 3
#define NAGIOS_OTHERS 4 /*This is for garbage errors, and is not from the nagios documentation*/
 
int Random(int n){
	return (int)(n * (rand() / (1.0 + (double)RAND_MAX)));
}
 
int usage(FILE *std,char *appname){
 
	char *msg="Usage: %s [OPTION]... -H hostname \n\
	  -D detector [0..32] (--detector)\n\
	  -V version (--version)\n\
	  -h help (--help)\n\
	  -t timeout (--timeout)\n\
	  -w warning threshold (--warning)\n\
	  -c critical threshold (--critical)\n\
	  -p port (--port)\n\
	  -H hostname (--hostname)\n\
	  -v verbose (--verbose)\n";
 
	fprintf(std,msg,appname);
 
	return;
}
 
int main(int argc, char *argv[]){
	int sockfd=0;
	char data[255]={0};
	struct hostent *he;
	struct sockaddr_in their_addr;
	char *hostname=NULL;
	char *appname=argv[0];
	int port=1091;
 
	int detector=0;
	int verbose=0;
 
	/* Flag set by `--verbose'. */
	static int verbose_flag;
 
	static int c;
 
	int status=0;
	int alarmFound=0;
 
	srand(time(NULL));
 
	if(argc < 2){
		usage(stderr,appname);
		return NAGIOS_UNKNOWN;/*UNKNOWN*/
	}
 
 
	while (1) {
		static struct option long_options[] =
		{
			/* These options set a flag. */
			{"verbose", no_argument,       &verbose_flag, 1},
			{"brief",   no_argument,       &verbose_flag, 0},
 
			/* These options don't set a flag.
			 * We distinguish them by their indices. */
			{"help",     no_argument,       0, 'h'},
			{"version",  no_argument,       0, 'V'},
			{"timeout",  required_argument, 0, 't'},
			{"warning",  required_argument, 0, 'w'},
			{"critical", required_argument, 0, 'c'},
			{"hostname", required_argument, 0, 'H'},
			{"detector", required_argument, 0, 'D'},
			{"port",     required_argument, 0, 'p'},
			{0, 0, 0, 0}
		};
 
		/* getopt_long stores the option index here. */
		int option_index = 0;
 
		c = getopt_long(argc, argv, "hVt:w:c:H:D:p:v",long_options, &option_index);
 
		/* Detect the end of the options. */
		if (c == -1)
			break;
 
		switch (c) {
			case 0:
				/* If this option set a flag, do nothing else now. */
				if (long_options[option_index].flag != 0)
					break;
 
				printf ("option %s", long_options[option_index].name);
 
				if (optarg)
					printf (" with arg %s", optarg);
 
				printf ("\n");
				break;
 
			case 'h':
				usage(stdout,appname);
				return NAGIOS_OTHERS;/*OTHERS*/
				break;
 
			case 'V':
				break;
 
			case 't':
				break;
 
			case 'w':
				break;
 
			case 'c':
				break;
 
			case 'p':
				port=atoi(optarg);
				break;
 
			case 'H':
				hostname=optarg;
				if(verbose)
					printf("hostname: %s\n",hostname);
				break;
 
			case 'D':
				detector=atoi(optarg);
				if(verbose)
					printf("Detector: %i\n",detector);
				break;
 
			case 'v':
				verbose++;
				break;
 
			default:
				abort();
		}
	}
 
	if(detector<0 || detector>=33){
		printf("Detector number out of range!\n");
		return NAGIOS_UNKNOWN;/*UNKNOWN*/
	}
 
	if(hostname==NULL){
		usage(stderr,appname);
		return NAGIOS_OTHERS;/*OTHERS*/
	}
 
	if((he= gethostbyname(hostname)) == NULL){
		printf("Error with gethostbyname()\n");
		return NAGIOS_OTHERS;/*OTHERS*/
	}
 
	if((sockfd = socket(AF_INET,SOCK_STREAM, 0)) == -1){
		printf("Error with socket()\n");
		return NAGIOS_UNKNOWN;/*UNKNOWN*/
	}
 
	their_addr.sin_family = AF_INET;
	their_addr.sin_port = htons(port);
	their_addr.sin_addr = *((struct in_addr *)he->h_addr);
	memset(their_addr.sin_zero,0,sizeof their_addr.sin_zero);
 
	if(connect(sockfd,(struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1){
		/*printf("Error with connect()\n");*/
		printf("Not able to establish a communication with the server!\n");
		return NAGIOS_UNKNOWN;/*UNKNOWN*/
	}
 
	sprintf(data,"%i",detector);
 
	if(send(sockfd, data, sizeof data, 0) == -1){
		printf("Error with send()\n");
		return NAGIOS_UNKNOWN;/*UNKNOWN*/
	}
 
	/*data initialisation*/
	memset(data,0,sizeof data);
 
	if(recv(sockfd,data,sizeof data, 0) == -1){
		printf("Error with recv()\n");
		return NAGIOS_UNKNOWN;/*UNKNOWN*/
	}
 
	if(verbose)
		printf("Received: %s\n",data);
 
	/*Test for alarm status	*/
	status=0;
	switch(data[0]){
		case '0':
			/*OK*/
			printf("Detector %i OK\n",detector);
			return NAGIOS_OK;
			break;
		case '1':
			/*Warning*/
			status=NAGIOS_WARNING;
			printf("WARNING: ");
			break;
		case '2':
			/*Alarme*/
			status=NAGIOS_CRITICAL;
			printf("ALARME: ");
			break;
		default:
			printf("Undefined status (%c)!",data[0]);
			return NAGIOS_WARNING;/*WARNING*/
	}
 
	/* Test for temperature
	 * XXX1
	*/
	switch (data[1]){
		case '1':/*ONLY*/
		case '3':
		case '5':
		case '7':
		case '9':
		case 'B':
		case 'D':
		case 'F':
			/*Alerte temperature*/
			alarmFound=1;
			printf(" -- Temperature threshold reached");
			break;
	}
 
	/* Test for contact1
	 * XX1X
	 */
	switch (data[1]){
		case '2':/*ONLY*/
		case '3':
		case '6':
		case '7':
		case 'A':
		case 'B':
		case 'E':
		case 'F':
			/*Alerte contact 1*/
			alarmFound=1;
			printf(" -- Contact 1 is open!");
			break;
	}
 
	/* Test for contact2	
	 * X1XX
	 */
	switch (data[1]){
		case '4':/*ONLY*/
		case '5':
		case '6':
		case '7':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
			/*Alerte contact 2*/
			alarmFound=1;
			printf(" -- Contact 2 is open!");
			break;
	}
 
	/* Test for not responding
	 * 1XXX
	 */
	switch (data[1]){
		case '8':/*ONLY*/
		case '9':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
			/*Alerte detector not responding*/
			alarmFound=1;
			printf(" -- Detector not responding!");
			break;
	}
 
	if(!alarmFound)
		printf(" -- Unknown reason!");
 
	printf("\n");
 
	shutdown(sockfd,2);
	return status;
}

Makefile

# Makefile for main atas
# Sylvain Bolay
#
# History:
# 09/03/04 -- created
#
# Please read the file README.makefile that came
# with your distribution for an overview
# on using this Makefile
 
######################################################
################## Miscellaneous #####################
######################################################
# The following variables apply to compilation
 
# Destination bin directory for executables.  Note: this
# directory *must* exist, or compilation will fail during
# linkage.
BINDIR = ./
 
######################################################
############### Sequential Code Variables ############
######################################################
# Edit the following when compiling.
 
# C compiler for sequential code
LCC = gcc
WCC = i586-mingw32msvc-gcc
 
CFLAGS= -O3
 
############################################################
#/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\#
############################################################
#                                                          #
# You shouldn't need to change anything beyond this point. #
#                                                          #
############################################################
#/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\#
############################################################
 
######################################################
################# common object files ################
######################################################
linux_obj = \
	linux_main.o
 
win32_obj = \
	win32_main.o
 
######################################################
#################### targets #########################
######################################################
 
################## sequential targets ###############
linux : $(linux_obj)
	$(LCC) -lm -o congiro_client $(linux_obj)
 
win32 : $(win32_obj)
	$(WCC) -lm -o congiro_client.exe $(win32_obj)
 
######################################################
############## implicit rules for object files #######
######################################################
############## main rules
linux_main.o : main.c
	$(LCC) -c $(CFLAGS) main.c -o linux_main.o
 
win32_main.o : main.c
	$(WCC) -c $(CFLAGS) main.c -o win32_main.o
 
######################################################
############# cleanup rules ##########################
######################################################
clean: 
	-$(RM) *.o
 
######################################################
############## dependencies ##########################
######################################################
linux_main.o : main.c
win32_main.o : main.c