Here is some sample code for a daemon process that writes "hello" every 2 seconds. Most daemons don't write anything to the console, but this one does for purposes of demonstration.
#include <rudiments/daemonprocess.h> #include <iostream.h> #include <unistd.h> #include <stdlib.h> class mydaemon: public daemonprocess { public: void init(); void loop(); }; void mydaemon::init() { runAsUser("nobody"); runAsGroup("nobody"); detach(); } void mydaemon::loop() { for (;;) { cout << "hello" << endl; sleep(2); } } mydaemon *md; void shutDown() { delete md; exit(0); } main() { md=new mydaemon; md->handleShutDown((void *)shutDown); md->init(); md->loop(); }Using the Error Handler Class
The errorhandler class provides simple error handling methods to any class. To use the errorhandler class, your class should inherit from it. Whenever your class needs to generate an error, it can call the clearError() methods and set the value of the protected "errorstr" strstream. When a program needs to get the error message, it can call the public getError() method.
The following program implements a file-handler class that generates errors using methods from the errorhandler class.
#include <rudiments/errorhandler.h> #include <rudiments/permissions.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <iostream.h> class fileclass : public errorhandler { public: int openFile(const char *filename); ssize_t writeToFile(const char *data); ssize_t readFromFile(char *data, size_t length); int closeFile(); private: int fd; }; int fileclass::openFile(const char *filename) { if (!(fd=open(filename,O_RDWR|O_CREAT,permissions::ownerReadWrite()))) { clearError(); *errorstr << "error: open() failed" << ends; return 0; } return 1; } ssize_t fileclass::writeToFile(const char *data) { size_t length=strlen(data); ssize_t returnlength=0; if ((returnlength=write(fd,(const void *)data,length))!=length) { clearError(); *errorstr << "error: write() failed" << ends; } return returnlength; } ssize_t fileclass::readFromFile(char *data, size_t length) { ssize_t returnlength=0; if ((returnlength=read(fd,(void *)data,length))!=length) { clearError(); *errorstr << "error: read() failed" << ends; } return returnlength; } int fileclass::closeFile() { if (close(fd)==-1) { clearError(); *errorstr << "error: close() failed" << ends; return 0; } return 1; } main() { fileclass fc; if (!fc.openFile("myfile")) { cerr << fc.getError() << endl; } if (fc.writeToFile("hello")!=5) { cerr << fc.getError() << endl; } if (!fc.closeFile()) { cerr << fc.getError() << endl; } if (!fc.openFile("myfile")) { cerr << fc.getError() << endl; } char data[6]; data[5]=(char)NULL; if (fc.readFromFile(data,5)!=5) { cerr << fc.getError() << endl; } cout << data << endl; if (!fc.closeFile()) { cerr << fc.getError() << endl; } }Using the File Descriptor Class
...
Using the File Class...
Using the Data Transport Classes...
Using the Server Socket ClassesDaemons are commonly used to serve data to clients on the same machine or over a network. Below is an example combining the daemon and listener classes. This server listens on unix and inet sockets for a client connection, receives a string from the client and writes the same string back to the client.
#include <rudiments/daemonprocess.h> #include <rudiments/listener.h> #include <rudiments/unixserversocket.h> #include <rudiments/inetserversocket.h> #include <rudiments/transport.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> class myserver: public daemonprocess, public listener { public: myserver(); ~myserver(); void loop(); void session(); private: unixserversocket inetsocket; inetserversocket unixsocket; transport *sock; }; myserver::myserver() : daemonprocess(), listener() { // run as a different user/group runAsUser("nobody"); runAsGroup("nobody"); // detach from the controlling tty detach(); // open, bind and listen on an inet port and unix socket inetsocket.listenOnSocket(NULL,8040,15); unixsocket.listenOnSocket("/tmp/mysocket",S_IRUSR|S_IWUSR,15); // add the sockets to the pool addFileDescriptor(inetsocket.getFileDescriptor()); addFileDescriptor(unixsocket.getFileDescriptor()); } myserver::~myserver() { inetsocket.close(); unixsocket.close(); } void myserver::loop() { for (;;) { // accept connections, with no timeout int fd=waitForData(-1,-1); if (fd==inetsocket.getFileDescriptor()) { sock=inetsocket.acceptClientConnection(); } else if (fd==unixsocket.getFileDescriptor()) { sock=unixsocket.acceptClientConnection(); } // fork to handle the new connection if (!fork()) { // close the duplicated server socket, // engage in a session with the client, // close the client socket and exit if (fd==inetsocket.getFileDescriptor()) { inetsocket.close(); } else if (fd==unixsocket.getFileDescriptor()) { unixsocket.close(); } session(); sock->close(); _exit(0); } // close the client socket, but leave the server // socket open to accept more connections if (fd==inetsocket.getFileDescriptor()) { inetsocket.close(); } else if (fd==unixsocket.getFileDescriptor()) { unixsocket.close(); } } } void myserver::session() { // read a string from the client short bytes; sock->read(&bytes); char *string=new char[bytes+1]; sock->read(string,bytes); // write the same string back to the client sock->write(bytes); sock->write(string,bytes); // clean up delete[] string; } myserver *ms; void shutDown() { delete ms; exit(0); } main() { ms=new myserver(); ms->handleShutDown((void *)shutDown); ms->loop(); }
Notice that this server listens on both inet and unix ports. Inet ports allow clients and servers to talk across a network. Unix ports allow clients and servers on the same machine to talk through a pipe. Though clients and servers on the same machine could talk over inet ports, unix ports are much faster and use fewer system resources.
Using the Client Socket ClassesHere's the code for a client that can talk to the server above. This client sends a string to the server, reads what the server sends back and prints it out. It does this once over an inet port and once over a unix port.
#include <rudiments/inetclientsocket.h> #include <rudiments/unixclientsocket.h> #include <iostream.h> class myclient { public: void inetSession(char *host, int port, char *message); void unixSession(char *socket, char *message); private: void session(transport *sock, char *message); inetclientsocket inetsock; unixclientsocket unixsock; }; void myclient::inetSession(char *host, int port, char *message) { inetsock.connectToServer(host,port,0,1); session(&inetsock,message); } void myclient::unixSession(char *socket, char *message) { unixsock.connectToServer(socket,0,1); session(&unixsock,message); } void myclient::session(transport *sock, char *message) { // write a message to the server sock->write((short)strlen(message)); sock->write(message); // read a message back from the server short bytes; sock->read(&bytes); char *string=new char[bytes+1]; sock->read(string,bytes); // write out the message we got from the server cout << string << endl; // clean up delete[] string; } main() { // declare an instance of our client myclient mc; // connect to a known inet host/port and unix socket and // send a message over each connection mc.inetSession("localhost",8040,"message over inet port"); mc.unixSession("/tmp/mysocket","message over unix port"); }Using the Complex Socket Initialization methods of the Server Classes
Setting up a server to listen on a socket is actually a multi-step process. The listenOnSocket() methods simplify this process but some applications may require a more flexible interface. If you need to set socket options or perform additional actions between the steps of socket initialization, you can use the Complex Socket Inititalization methods of the server classes.
Below is an alternative implementation of the myserver constructor in which some socket options are set.
myserver::myserver() : daemonprocess(), listener() { // run as a different user/group runAsUser("nobody"); runAsGroup("nobody"); // detach from the controlling tty detach(); // initialize the ports inetsocket.initInetServerSocket(NULL,8040); unixsocket.initUnixServerSocket("/tmp/mysocket",S_IRUSR|S_IWUSR,15); // set some socket options inetsocket.lingerOnClose(10); inetsocket.reuseAddresses(); unixsocket.lingerOnClose(10); // bind to the ports inetsocket.bind(); unixsocket.bind(); // listen on the ports inetsocket.listen(15); unixsocket.listen(15); // add sockets to the pool addFileDescriptor(inetsocket.getFileDescriptor()); addFileDescriptor(unixsocket.getFileDescriptor()); }