Solved

daemon

Posted on 1999-01-05
12
608 Views
Last Modified: 2013-12-26
How to write a daemon application?

How to start it?

Thanks.

0
Comment
Question by:jonalee
  • 3
  • 3
  • 2
  • +3
12 Comments
 
LVL 84

Expert Comment

by:ozo
Comment Utility
what do you want this daemon to do?
0
 
LVL 2

Author Comment

by:jonalee
Comment Utility
I have write a Chess Server. I want this server run when I start the Machine (I run Red Hat 5.1)

Just like the Service application on Windows NT. It can run without any login.
0
 
LVL 2

Expert Comment

by:seedy
Comment Utility
>How to write a daemon application?
There are some basic things that a daemon should do - according to Richard Stevens -
1. fork and have the parent exit
2. call setsid to create a new session
3. Change the current working directory to the root directory
4. set the file creation mask to 0
5. close all unneeded file descriptor      

>How to start it?
you would want to write a cover shell script to start the daemon and place the shell script in appropriate rc directory (/etc/rc3.d, may be?).  Please see other scripts that are present under the rc diretcories to learn more.

0
 
LVL 2

Author Comment

by:jonalee
Comment Utility
Will you give me a small sample source code of the daemon application? (Very simple is OK)

You can send to me by using jonalee@online.sh.cn

Thanks.

0
 
LVL 2

Expert Comment

by:seedy
Comment Utility
Again, quoting from Richard Stevens and implementing my earlier comment:

pid_t pid;

if ( (pid = fork()) < 0)
    return(-1);
else if (pid != 0)
    exit(0);  /* parent goes bye-bye */
/* child continues */
setsid();    /* become session leader */
chdir("/");  /* change working directory */
umask(0);    /* clear our file creation mask */

/* Do what you want to do in the daemon here */

0
 

Expert Comment

by:sarum
Comment Utility
It is also important to close any file desciptors
that have been open by the shell (ie 0,1,2) and
yourself before the fork.

0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 

Expert Comment

by:sarum
Comment Utility
BTW - it is also important to use syslog for
errors not stderr. As you should have disconnect
from the standard file descriptors (see other
message).

0
 
LVL 4

Expert Comment

by:davidmwilliams
Comment Utility
 This should get you going ...
  This is all the code to start up a daemon and have it wait for socket requests. The 'process' routine is forked for each incoming connection, and that's where you need to add the bulk of your code.  You will also want to add some global data structures or shared memory or whatever to coordinate your chess app between all the different connections.
  The code also handles logging to a file, via the log function, and in a nice format (date, IP address, etc.) - though as Sarum said, logging to syslog is a good idea.
  Use WriteToFD to send messages back to the calling client.
  This is standard C and I know it compiles under Solaris and Linux.

  Let me know how you get on ...

#define __USE_BSD

#include <arpa/inet.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/termios.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <time.h>

#include <sys/param.h>
#include <unistd.h>

#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

void log (const char *format, ...);
int errexit (const char *format, ...);
void process (int ssock, char *Remote, int connections);
void DisplayHelpMessage (int port, int qlen);

extern int errno;
bool logging = false;
bool verbose = false;
char FileName [PATH_MAX];

// ---------------------------------------------------------------------------
int main (int argc, char *argv [])
{
  struct sockaddr_in sin, fsin;
  struct protoent *ppe;
  int sock, ssock;
  int alen;
  int port = 3000;
  int qlen = 5;
  int pid;
  int fd;
  char nowtime [26];
  char Remote [80];
  int connections = 0;
  FILE *logfp = NULL;

  FileName [0] = '\0';

// Process command line arguments.

  for (int i = 1; i < argc; i++)
  {
    if (strncmp (argv[i], "-p", 2) == 0)
      port = atoi (argv[i] + 2);
    else if (strncmp (argv[i], "-q", 2) == 0)
      qlen = atoi (argv[i] + 2);
    else if (strncmp (argv[i], "-l", 2) == 0)
      logging = true;
    else
    {
      DisplayHelpMessage (port, qlen);
      return 0;
    }
  }

// Print a welcome banner.

  printf ("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
          "    My daemon\n"
          "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n");

// Set up the socket.

  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = INADDR_ANY;
  sin.sin_port = htons (port);

  if ((ppe = getprotobyname ("tcp")) == 0)
    errexit ("Can't find tcp: %s\n", strerror (errno));

  if ((sock = socket (PF_INET, SOCK_STREAM, ppe->p_proto)) < 0)
    errexit ("Can't create socket: %s\n", strerror (errno));

  if (bind (sock, (struct sockaddr *) &sin, sizeof (sin)) < 0)
    errexit ("Can't bind to port: %s\n", strerror (errno));
  if (listen (sock, qlen) < 0)
    errexit ("Can't listen on port: %s\n", strerror (errno));

  log ("Listening on port %d (qlen = %d).\n", port, qlen);

// Set up the server safely.

// 1. Run the server in the background ...

  if ((pid = fork ()) < 0)
    errexit ("Error setting up server: %s\n", strerror (errno));

  if (pid)  // Non-zero is parent.
  {
    printf ("Server pid is %d.\n", pid);
    if (strlen (FileName) > 0)
      log ("Server pid is %d.\n\n", pid);
    exit (0);
  }

// 2. Detach from controlling tty ...

  fd = open ("/dev/tty", O_RDWR);
  ioctl (fd, TIOCNOTTY, 0);
  close (fd);

// 3. Miscellaneous commands ...

  umask (027);
  chdir ("/tmp");
  setpgrp (); //0, getpid ());

// Be the server !

  while (1)
  {
// Wait for connections. If one is made, then get a slave socket to
// process it, in a child process. This way the server remains free
// to accept more connections.

    alen = sizeof (fsin);
    ssock = accept (sock, (struct sockaddr *) &fsin, &alen);
    if (ssock < 0)
    {
      if (errno == EINTR)
        continue;
      else
        errexit ("accept: %s\n", strerror (errno));
    }
    strcpy (Remote, IPtoAddress (fsin.sin_addr));
    log ("%s: connect from %s\n",CurrentDateTime (nowtime), Remote);

    signal (SIGCHLD, reaper);
    connections++;
    if (fork () == 0)
    {
      process (ssock, Remote, connections);
      exit (0);
    }
    else
      close (ssock);
  }
}

// ---------------------------------------------------------------------------
void DisplayHelpMessage (int port, int qlen)
{
  printf ("\nMy daemon\n\n");
  printf ("\t\tSyntax:\tdaemon [flags]\n\n");
  printf ("\t-pPORT\t\tto specify the port to use.\n");
  printf ("\t\t\t(default = %d)\n", port);
  printf ("\t-qQLEN\t\tto specify the queue length.\n");
  printf ("\t\t\t(default = %d)\n", qlen);
  printf ("\t-l\t\tto perform logging.\n");
  printf ("\t-fFILENAME\tto specify a log file.\n");
  printf ("\t\t\t(default = stdout)\n");
  printf ("\t-v\t\tto specify verbose logging.\n");
  printf ("\t\t\t(default = off)\n\n");
}

// ---------------------------------------------------------------------------
char *CurrentDateTime (char *nowtime)
{
  time_t now;

  time (&now);
  strcpy (nowtime, ctime (&now));
  nowtime [strlen (nowtime) - 1] = '\0';

  return nowtime;
}

// ---------------------------------------------------------------------------
void WriteToFD (int filedesc, char *s)
{
  write (filedesc, s, strlen (s));
}

// ---------------------------------------------------------------------------
void log (const char *format, ...)
{
  va_list  args;
  FILE  *fp;

  if (logging)
  {
    fp = stdout;

    if (strlen (FileName) > 0)
    {
      if ((fp = fopen (FileName, "a")) < 0)
        fp = stdout;
    }

    va_start (args, format);
    vfprintf (fp, format, args);
    va_end (args);

    if (fp != stdout)
      fclose (fp);
  }
}

// ---------------------------------------------------------------------------
int errexit (const char *format, ...)
{
  va_list args;
  char errstr [255];

  va_start (args, format);
  vsprintf (errstr, format, args);
  log (errstr);
  va_end (args);

  exit (1);
}

// ---------------------------------------------------------------------------
const char *IPtoAddress (struct in_addr ipA)
{
  unsigned long hostname;
  struct hostent *ip;

  hostname = inet_addr (inet_ntoa (ipA));

  if ((ip = gethostbyaddr ((char *) &hostname, sizeof (long), AF_INET))<0)
    return "unknown";
  else
    return ip->h_name;
}

// ---------------------------------------------------------------------------
void reaper (int sig)
// The reaper cleans up zombie children processes.
// In Unix, when a child process terminates it sends a message back to
// the parent process.  Unless this message is handled, the child process
// will wait around forever taking up resources.
{
  int status;

  wait3 (&status, WNOHANG, (struct rusage *) 0);
}

// ---------------------------------------------------------------------------
void process (int ssock, char *Remote, int connections)
// This function handles the processing of a socket connection.
{
// Do your stuff here !
}
0
 

Expert Comment

by:sarum
Comment Utility
Another point do you wish this to be a demon started
by inetd when ever a client connects to your 'well-known'
port. If so then you should not close stdin and stdout
as inetd will have duped the sockets on to these
fd for you. Is this the sort of infor you are intrested
in?

0
 
LVL 1

Accepted Solution

by:
ramshakal earned 100 total points
Comment Utility
Here I am giving a short of code example  for writing daemon.


#include <...>
/* include all the needed file  */

int sockfd;
int newsockfd;            /* connected socket descriptor */
long timevar;            /* contains time      */
char *ctime();            /* declare time formatting routine */
struct sockaddr_in client_addr, serv_addr;
/* declare other variable neded in your application */

main(int argc,char **argv)
{
      int clilen,childpid;            /* listen socket descriptor */

      char *readbufptr=(char *)malloc(512);
      char *writebufptr=(char *)malloc(512);

      bzero( (char *) &serv_addr, sizeof(serv_addr) );
                              /* fill serv_addr with null bytes */
      bzero( (char *) &client_addr, sizeof(client_addr) );
                              /* clear out address structure         */
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = htons(SERV_TCP_PORT);
      serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

      fd=fopen("logfile","a");
      
      if ( (sockfd=socket(AF_INET, SOCK_STREAM,0)) < 0)
      {
            perror("server: could  not open stream socket\n");
            exit(1);
      }
      
      if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
      {
            perror("server: could not bind local address\n");
            exit(1);
      }
      if (listen(sockfd,1) <0 )
      {
            perror("server: listen error\n");
            exit(1);
      }

      daemon_test();   /* make this server as daemon */
      
      for(;;)
      {
            /*
             * concurrent server.
             * wait for connection from a client process.
             */
            clilen=sizeof(client_addr);
      
            /* this call will block until new connection arrives.
             * then it will return the address of connecting client,
             * and a new socket descriptor,s, for that connection
             * and a child server to handle each one
             */
            if((newsockfd=accept(sockfd, (struct sockaddr *) &client_addr,&clilen)) < 0 )
            {
                  fprintf(fd,"server: accept error");
                  exit(1);
            }
            if ( (childpid = fork()) < 0 )
            {
                  fprintf(fd,"server: fork error");
                  exit(1);
            }
            if (childpid == 0)        /* in child process */
            {
                  server();  /* start your server to support your CHESS appl */
                  exit(0);
            }
            if (childpid > 0)
            {
                  close(newsockfd);        /* in parent daemon process */
            }
      }
}

daemon_test()
{
      int childpid;
      
      /*
       * Diassociate from a control terminal and process group
       * ensure that process cannot reacquire a new control terminal.
       */
      if(setpgrp(0,getpid())==-1)
      {
            perror("can't change process group\n");
            fprintf(fd,"cannot change process group\n");
            exit(1);
      }
      
      /* fork (and let the parent exit,) so that
       * process cannot become process group leader
       * and hence cannot acquire control treminal
       */
      
      if((childpid=fork()) < 0 )
      {
            perror("can't fork first child\n");
            fprintf(fd,"server: unable to fork first child in daemon\n");
            exit(1);
      }
      if (childpid > 0 )
      {
            exit(0);      /*      parent process comes here       */
      }
      if (childpid==0)
      {

      /*
       * First child process daemon comes here.
       */

      /*
       * close stdin and stderr any open files descriptors
       */
      fclose(stdin);
      fclose(stderr);
      /*
       * Immune from pgrp leader death.
       */
      signal(SIGHUP,SIG_IGN);      
      
      /* set SIGCLD to SIG_IGN, in order to prevent
       * the accumulation of zombies as each child terminates.
       * This means the daemon does not have to make wait calls
       * to clean them up
       */
      signal(SIGCLD,SIG_IGN);

      return;
      }
      
}


server()
{
      char *hostname;            /* point to remote hosts name string.      */
      char **p;
      struct hostent *hp;
      char *readbufptr=(char *)malloc(512);
      char *writebufptr=(char *)malloc(512);
      
      close(sockfd);            /* close the listen socket inherited        */

/*
      fprintf(fd,"from Netid/Hostid : %s\n",inet_ntoa(client_addr.sin_addr));
        fprintf(fd,"PORT no. : %u \n",ntohs(client_addr.sin_port));
*/


      hp=gethostbyaddr( (char *) &client_addr.sin_addr, sizeof(struct in_addr), client_addr.sin_family );

      if (hp==NULL)
      {
            fprintf(fd,"info unavailable for remote host\n");
            hostname = (char *)inet_ntoa(client_addr.sin_addr);
      }
      else
      {
            hostname = hp->h_name;            

            for (p = hp->h_addr_list; *p != 0; p++)
            {
                   struct in_addr in;
                         char **q;
 
                         (void) memcpy(&client_addr.sin_addr.s_addr, *p, sizeof (in.s_addr));
                         (void) fprintf(fd,"net-id = %s and name = %s ", inet_ntoa(client_addr.sin_addr), hp->h_name);
                         for (q = hp->h_aliases; *q != 0; q++)
                             (void) fprintf(fd," aliases  %s", *q);
               }
      }

      
      /*
       * Log a startup message
         */

      time( &timevar);
      /*
       * the port number must be converted first to host byte order
       */


      fprintf(fd,"\nAccept connection from %s host, %u port, at %s\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port),ctime(&timevar) );


            read_from_sock(newsockfd,readbufptr);
            serv_eval_buff(readbufptr,writebufptr,client_addr);
            write_to_sock(newsockfd, writebufptr,strlen(writebufptr));
            
            sleep(2);      /*simulates the processing of request */
            
            close(newsockfd);
            /*
             * Log a finishing message
             */
            time (&timevar);

            fprintf(fd,"Completed '%s' host, %u port, at %s\n", hostname, ntohs(client_addr.sin_port), ctime(&timevar) );

           /**********************
         WRITE CODE TO SUPPORT YOUR CHESS
        **************************/

}

0
 
LVL 4

Expert Comment

by:davidmwilliams
Comment Utility
 Ramshakal's code doesn't provide as much functionality as my program to you did ...
  As a further note, I know what I wrote works well, because I've used it as the basis for several daemon processes of my own.
0
 
LVL 4

Expert Comment

by:davidmwilliams
Comment Utility
Can I ask why you preferred Ramshakal's solution to mine?  Thanks.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
configuration management 2 99
repeatEnd java challenge 42 83
move a line in eclipse 3 60
fizzArray  challenge 1 47
In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now