• C

C Programming: Creating Daemons

I am trying to create a Daemon in C, and simply bind a socket to port 9000. Once I run the code below, how can I verify if the Daemon is up and running?

I get the child process ID returned, but when I run $PS -A I do not see it listed.


int main( int argc, char *argv[] ) {

   pid_t process_id = 0;
   pid_t sid = 0;

   const char *protocolName = "tcp";
   const char *ipAddr = "127.0.0.1";
   int portNo = atoi(argv[4]); //argc = 5
   int sockfd, bindStatus;
   struct protoent *myProtocol = getprotobyname(protocolName);
   struct sockaddr_in sockIP4addr;
   struct sockaddr *mySockAddr;
   
   // Create child process
   process_id = fork();
   // fork() failure
   if (process_id < 0)
   {
      printf("fork failed!\n");
	  // return exit status
	  exit(1);
   }
   
   // PARENT PROCESS -- KILL IT
   if (process_id > 0)
   {
      printf("process_id of child process %d \n", process_id);
	  // return success in exit status
	  exit(0);
   }
   
   //unmask the file mode
   umask(0);
   
   // set new session
   sid = setsid();
   if(sid < 0)
   {
      // Return failure
	  exit(1);
   }
   
   // Change the current working directory to root
   chdir("/");
   
   // Close stdin. stdout and stderr
   close(STDIN_FILENO);
   close(STDOUT_FILENO);
   close(STDERR_FILENO);
   
   open("/dev/null", O_RDWR);
   dup(0);
   dup(0);

   // Open a log file in write mode
   int filedes = open("Log.txt", O_RDWR);

   sockIP4addr.sin_family = AF_INET;
   sockIP4addr.sin_port = htons(portNo);
   inet_pton(AF_INET, ipAddr, &sockIP4addr.sin_addr);

   sockfd = socket(AF_INET,SOCK_STREAM,myProtocol->p_proto);
   
   if(sockfd == -1)
      perror("socket() error");
   
   printf("Protocol name: %s \n", myProtocol->p_name);
   printf("Protocol num: %d \n", myProtocol->p_proto);
   
   mySockAddr = (struct sockaddr*)&sockIP4addr;
   bindStatus = bind(sockfd, mySockAddr, sizeof(sockIP4addr));
   
   if(bindStatus == -1) {
      perror("bind() error");
   }

}

Open in new window

LVL 8
pzozulkaAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

evilrixSenior Software Engineer (Avast)Commented:
Well, unless I am missing something it would appear to me that the child process has probably just finished. There isn't anything to keep it running is there? A child process doesn't just magically persist, your code has to continue to run for the child to run, too. Looking at your example it just opens a socket, prints some stuff and then ends.

Have you tried running it without forking to make sure it doesn't actually work as you expect?

Also, there is a lot more to implementing a daemon than just forking. You have to do a number of other things, such as close all IO file descriptors, change umask, create a session id and change cwd.

I suggest you read a few articles. The following look reasonable.
http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html
http://shahmirj.com/blog/beginners-guide-to-creating-a-daemon-in-linux
http://www.thegeekstuff.com/2012/02/c-daemon-process/
0
sarabandeCommented:
to add to before comment:

you probably want to add a while loop for accepting clients which try to connect to the port.

the while loop should be infinite. if you check for incoming requests by calling select with timeout, the daemon would be able to break the loop on request for example by checking a global mutex allocated in shared memory. alternatively, you could write a client that connects to the daemon and, once connected, forces it to end by sending an appropriate message.

Sara
0
pzozulkaAuthor Commented:
Thanks much to both for your responses.

In the original code I posted above, I believe I'm following all the instructions on how to create a Daemon just like the articles suggest.

Per your suggestions, I went forward to do more than just BIND. I created a client to try to connect to the Daemon, but am getting error message:

Connect() Error: Transport endpoint is already connected

Client
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define INET_ADDRSTRLEN 16;

int main( int argc, char *argv[] ) {

   socklen_t peer_addr_size;
   
   int conStatus;
   const char *protocolName = "tcp";
   const char *ipAddr = "127.0.0.1";
   int sockfd;
   struct protoent *myProtocol = getprotobyname(protocolName);
   struct sockaddr_in sockIP4addr;
   struct sockaddr *peerSockAddr;
   
   int max_input = 200;
   char buffer[max_input];
   size_t count = 200;
   ssize_t bytes_read;
   size_t blen;
   
   sockIP4addr.sin_family = AF_INET;
   sockIP4addr.sin_port = htons(atoi(argv[1]));
   inet_pton(AF_INET, ipAddr, &sockIP4addr.sin_addr);

   sockfd = socket(AF_INET,SOCK_STREAM,myProtocol->p_proto);
   
   if(sockfd == -1)
      perror("socket() error");
   
   printf("Protocol name: %s \n", myProtocol->p_name);
   printf("Protocol num: %d \n", myProtocol->p_proto);
   
   peer_addr_size = sizeof(struct sockaddr_in);
   peerSockAddr = (struct sockaddr*)&sockIP4addr;
   
   // Connect
   conStatus = connect(sockfd, peerSockAddr, peer_addr_size);
   
   if(conStatus == -1)
      perror("connect() error");
	  
   const char *b = "echo send test";
   blen = strlen(b);
   //while(1)
   //{
      if((write(sockfd, b, blen)) < 0)
	    perror("write() error");
		
      if(( bytes_read = read(sockfd, buffer, count)) == -1)
         perror("read() error");
	  else if( bytes_read == 0) {
	     exit(1);
      }
      else {
         buffer[bytes_read]='\0';
      }

	  printf("Bytes Read is: %d", bytes_read);
	  write(STDOUT_FILENO, buffer, strlen(buffer));
	  
   //}

}

Open in new window

Server
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <errno.h>

#define DAEMON_NAME "vdaemon"

void process() {
   syslog(LOG_NOTICE, "writing to my Syslog");
}

void communicate(const char *port) {
   FILE *fp=NULL;
   socklen_t peer_addr_size;

   const char *protocolName = "tcp";
   const char *ipAddr = "127.0.0.1";
   int portNo = atoi(port); //argc = 5
   int sockfd, csockfd, bindStatus, listenStatus, conStatus;
   int mybacklog = 50;
   int my_inet_addr_len = 16;
   struct protoent *myProtocol = getprotobyname(protocolName);
   struct sockaddr_in sockIP4addr, peerIP4addr;
   struct sockaddr *mySockAddr, *peerSockAddr;
   
   int max_input = 200;
   char buffer[max_input];
   size_t count = 200;
   ssize_t bytes_read;
   size_t blen;
   
   // Open a log file in write mode.
   fp = fopen ("Log.txt", "w+");
   fprintf(fp, "Logging info...\n");
   fflush(fp);
   
   sockIP4addr.sin_family = AF_INET;
   sockIP4addr.sin_port = htons(portNo);
   inet_pton(AF_INET, ipAddr, &sockIP4addr.sin_addr);

   // Create Socket
   sockfd = socket(AF_INET,SOCK_STREAM,myProtocol->p_proto);
   
   if(sockfd == -1)
      perror("socket() error");
   
   printf("Protocol name: %s \n", myProtocol->p_name);
   printf("Protocol num: %d \n", myProtocol->p_proto);
   
   mySockAddr = (struct sockaddr*)&sockIP4addr;
   
   // Bind Socket
   bindStatus = bind(sockfd, mySockAddr, sizeof(struct sockaddr_in));
   
   if(bindStatus == -1)
      fprintf(fp, "bind() error %s\n", strerror(errno));
    
	// Listen
   if (listen(sockfd, mybacklog) == -1)
      perror("listen() error");

   peer_addr_size = sizeof(struct sockaddr_in);
   peerSockAddr = (struct sockaddr*)&peerIP4addr;
   
   // Accept connections - peerSockAddr is filled in with RemoteAddr
   csockfd = accept(sockfd, peerSockAddr, &peer_addr_size);
   
   if(csockfd == -1)
      fprintf(fp, "accept() error %s\n", strerror(errno));
	  
   // Connect
   conStatus = connect(sockfd, peerSockAddr, peer_addr_size);
   
   if(conStatus == -1)
      fprintf(fp, "connect() error %s\n", strerror(errno));

   const char *b = "echo reply test";
   blen = strlen(b);
   while(1)
   {
      fprintf(fp, "Logging info while...\n");
      fflush(fp);
	  
      if(( bytes_read = read(sockfd, buffer, count)) == -1)
         fprintf(fp, "read() error %s\n", strerror(errno));
	  sleep(1);
      if((write(sockfd, b, blen)) < 0)
	     fprintf(fp, "write() error %s\n", strerror(errno));
   }
}

int main( int argc, char *argv[] ) {

   //Set our Logging Mask and open the Log
   /*setlogmask(LOG_UPTO(LOG_NOTICE));
   openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
   
   syslog(LOG_INFO, "Entering Daemon");
   */

   pid_t process_id = 0, sid = 0;
   
   // Create child process
   process_id = fork();
   
   // fork() failure
   if (process_id < 0)
   {
      printf("fork failed!\n");
	  // return exit status
	  exit(1);
   }
   
   // PARENT PROCESS -- KILL IT
   if (process_id > 0)
   {
      printf("process_id of child process %d \n", process_id);
	  // return success in exit status
	  exit(0);
   }
   
   //unmask the file mode
   umask(0);
   
   // set new session
   sid = setsid();
   if(sid < 0)
   {
      // Return failure
	  exit(1);
   }
   
   // Change the current working directory to root
   chdir("/home/users1/pz951772");
   
   // Close stdin. stdout and stderr
   close(STDIN_FILENO);
   close(STDOUT_FILENO);
   close(STDERR_FILENO);
   

   
   //-------------------
   //Main Process
   //-------------------
   
   communicate(argv[4]);
   
   //Close the log
   closelog();
   
}

Open in new window

0
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

sarabandeCommented:
you need to put the accept call into the infinite loop. for each client connected you would get a new socket which then can be used to read from or send to. the latter mostly was done by a new thread, one for each client connected. alternatively you may have a thread-pool where single tasks like "read from client socket with timeout" were performed by a thread from a pool. the latter design would allow more clients to connect but is a much more complex design.

a single-threaded solution would add each new client socket to an array of sockets. then when select for the accept call returns with timeout or when a new connection was added, the server would perform a loop thru all client sockets trying to read from each socket (always with timeout).

while (true)
{
      int r = select(1, &acceptfs, 0, 0, timeout);
      if (r > 0)
      {
             csockfd = accept(sockfd, peerSockAddr, &peer_addr_size);
             if (csockfd > 0)
             {
                clientsockets.pushback(csockfd); // put socket to vector
             }
       }
       for (int n = 0; n < (int)clientsockets.size(); ++n)
       {
              FD_ZERO(&readfs);
              FD_SET(clientsockets[n], &readfs);
              r =    select(1, &readfs, 0, 0, timeout);
              if (r > 0)
              {
                     // handle message from client
                     ...
              }
       }

Open in new window


Sara
0
sarabandeCommented:
note, your code didn't work cause the accept call would use the socket where you had made the bind and listen call. the read and send would use the socket returned from accept call. you may not use the same socket for both operations.

Sara
0
pzozulkaAuthor Commented:
Can you explain what you mean by "you can't use the same socket for both operations"? BY BOTH, what two things are you referring to?

All I'm trying to do is only once connect from client to server. And have server echo something back to client just so I know it's working. So I do need to accept a socket and then use read and write once on that socket.

I should be able to do that.
0
sarabandeCommented:
you have one socket where the server accepts new clients.

struct fd_set acceptfs = { 0 };
FD_ZERO(&acceptfs);
FD_SET(sockfd, &acceptfs);
std::vector<int> clientsockets;
while (true)
{
      int r = select(1, &acceptfs, 0, 0, timeout);
      if (r > 0)
      {
             csockfd = accept(sockfd, peerSockAddr, &peer_addr_size);

Open in new window


for any new client accepted the return of the accept call returns a new socket handle. you have to use that socket handle for any communication (read, send) between the server and the specific client. your code already uses two variables sockfd, csockfd for storing socket handles, but you need one socket for each client connection.

as told the mostly used design is to have a new thread for each socket. but as you can see in the code snippet i posted, it is possible to handle all sockets by one thread as well.

Sara
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.