NEXT UP previous
Next: Socket Server Additions

The Daemon Listing

The following program shows the complete listing of the example socket server shown earlier, modified to operate as a robust daemon process. You will notice that some changes have been made to the original server code itself, as well as the addition of the setup_daemon() function. All of these will be explained in detail after the listing:

/****************************************************************

THE SOCKET SERVER DAEMON
*****************************************************************/

	#include <stdio.h>
	#include <string.h> 
	#include <signal.h> 
	#include <fcntl.h> 
	#include <setjmp.h>
	#include <sys/param.h>
	#include "socklib.h"

	/* Server's communication port number */ 
	#define PORT 2121

	/* Values for the next_action parameter to fatal() */ 
	#define NA_EXIT 0

	/* Global environment store for setjmp/longjmp */ 
	jmp_buf env;

/****************************************************************

MAIN - A very simple file tranfer daemon, which sets up a concurrent
socket server on the host machine on port number PORT to transfer 
any files from the current directory on request.
*****************************************************************/

	main (void)
	{
		SOCKET *sp;
		int sd;

		/* Rearrange operating environment for daemon working */
		setup_daemon();
		/* Set SIGCHLD for no zombies */
		signal (SIGCHLD, SIG_IGN);

		/* Create server's socket */
		if ((sp = sopen())==0)
			/* Exit server if this fails */ 
			fatal("sopen()", NA_EXIT);

		/* Setup the NA_RESTART (post error) entry point */ 
		setjmp(env);

		/* Repeatedly accept and service client requests */
		for (;;)
		{
			/* Connect to client or restart on failure */ 
			if ((sd = sserver(sp, PORT, S_DELAY))==-1)
				fatal("sserver()", NA_RESTART);

			/* Fork() a child process */
			switch (fork())
			{
				/* Deal with client request in child */
				case 0:
					do_service(sd);

				/* Deal with fork() failure */ 
				case -1:
					close(sd);

					/* Restart for next client */ 
					fatal("fork()", NA_RESTART);

			}
			/* In parent, close client and loop for next connection */ 
			close(sd)
		}
	}





/********************************************************************

SETUP_DAEMON - This function is responsible for nodifying the current 
working environment of the process to that required by a daenon. 
i.e.  it closes unnecessary file descriptors, disassociates itself 
from the current controlling terninal, session and process group and 
modifies the current file creation mask.
*********************************************************************/


	setup_daemon (void) 
	{
		int i;

		/* Close all open file descriptors */
		for (i = 0; i<NOFILE; ++i)
	   		close(i);

		/* 1st fork() call */
		switch (fork())
		{
			/* Error in 1st forkO */
			case -1:
				fatal("setup_daemon(), 1st fork()", NA_EXIT);

			/* Exit 1st parent */ 
			default:
				exit (0);

			/* Continue in 1st child (2nd parent) */ 
			case 0:
				/* Start new session exit() on error */ 
				if (setsid()==-1)
					fatal("setup_daemon(), setsid()", NA_EXIT);

				/* 2nd fork() call */
				switch (fork())
				{
					/* Error in 2nd fork() */
					case -1:
						fatal("setup_daemon(), 2nd fork()", NA_EXIT);

					/* Exit 2nd parent */ 
					default:
						exit (0);

					/* Continue in 2nd child */ 
					case 0:
						/* Reset file creation mask for ERROR.LOG */ 
						umask(0);
						/* and return with daemon set up */ 
						return;
				}
		}
	}




/***************************************************************

FATAL - This routine is called when an error condition is 
returned to the daemon, usually from one of the system calls 
it makes. The function first prints the message pointed to by 
<text> and then either terminates the process or restarts 
it with longjmp(), depending on the value of the <next_action> 
parameter.
****************************************************************/

	fatal(char *text, int next_action)
	{
		FILE *fp;

		/* Open the error log file */ 
		if ((fp = fopen("ERROR.LOG", "a"))==0)
			/* exit() on failure here, with error unreported... */
			exit(l)

		/* Write the error message */ 
		fprintf(fp, "Error in %s\n", text); 
		fclose(fp);

		if (next_action==NA_RESTART)
			/* Restart calling process */ 
			longjmp(env, 1);
		else
			/* Terminate calling process */ 
			exit(1);
	}






/******************************************************************

DO_SERVICE - This function is called in the child process that is 
created when a client makes a successful connection. Its task is 
to service the client's request and supervise the required file 
transfer. The parameter <sd> is the socket descriptor to 
use for the communication.
*******************************************************************/

	do_service(int sd)
	{
		int i, fd;
		char *name;
		char c, namlen;

		/* Get length of file name */ 
		if (read(sd, &namlen, 1)!=1)
			fatal("namlen read()", NA_EXIT);
                 
		/* Space for name + " open()" */ 
		if ((name = (char *)malloc(namlen+8))==0)
			fatal("malloc()", NA_EXIT);

		/* Get required file name into name[] */ 
		for (i = 0; i<namlen; ++i)
			if (read(sd, &name[i], 1)!=1) 
				fatal("file name read()", NA_EXIT);

		/* Make name[] a string */ 
		name[i] = '\0';

		/* Check name[] is in current directory */ 
		if (strchr(name,  "/"))
			fatal("illegal file name", NA_EXIT);

		/* Open specified file */ 
		if ((fd = open(name, O_RDONLY))==-1)
			fatal(strcat(name, " open()"), NA_EXIT);

		/* Transfer contents to client */ 
		while ((i = read(fd, &c, 1))!=O)
			if (i==-1)
				fatal("file read()", NA_EXIT); 
			else 
				if (write(sd, &c, 1)!=1)
					fatal("write()", NA_EXIT);

		/* Terminate child and with it the connection to this client */
		exit (0);
	}

NEXT UP previous
Next: Socket Server Additions