Link to home
Start Free TrialLog in
Avatar of jonalee
jonalee

asked on

daemon

How to write a daemon application?

How to start it?

Thanks.

Avatar of ozo
ozo
Flag of United States of America image

what do you want this daemon to do?
Avatar of jonalee
jonalee

ASKER

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.
>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.

Avatar of jonalee

ASKER

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.

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 */

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.

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).

 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 !
}
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?

ASKER CERTIFIED SOLUTION
Avatar of ramshakal
ramshakal

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
 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.
Can I ask why you preferred Ramshakal's solution to mine?  Thanks.