?
Solved

daemon

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

How to start it?

Thanks.

0
Comment
Question by:jonalee
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
  • 2
  • +3
12 Comments
 
LVL 84

Expert Comment

by:ozo
ID: 1295185
what do you want this daemon to do?
0
 
LVL 2

Author Comment

by:jonalee
ID: 1295186
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
ID: 1295187
>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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 2

Author Comment

by:jonalee
ID: 1295188
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
ID: 1295189
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
ID: 1295190
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
 

Expert Comment

by:sarum
ID: 1295191
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
ID: 1295192
 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
ID: 1295193
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 300 total points
ID: 1295194
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
ID: 1295195
 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
ID: 1295196
Can I ask why you preferred Ramshakal's solution to mine?  Thanks.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
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.
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …
Suggested Courses

765 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