Programming with Rudiments using the Base Classes

Using the daemonprocess Class

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.

// Copyright (c) 2001  David Muse
// See the file COPYING for more information

#include <rudiments/daemonprocess.h>
#include <rudiments/permissions.h>
#include <rudiments/process.h>
#include <rudiments/file.h>
#include <rudiments/snooze.h>

#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

daemonprocess   *dmn;

// define a function to shut down the process cleanly
RETSIGTYPE      shutDown() {

        printf("%d: shutting down\n",process::getProcessId());

        // clean up
        delete dmn;
        file::remove("/tmp/dmn.pidfile");
        exit(0);
}

int main(int argc, const char **argv) {

        dmn=new daemonprocess();

        // set up signal handlers for clean shutdown
        dmn->handleShutDown((RETSIGTYPE *)shutDown);
        dmn->handleCrash((RETSIGTYPE *)shutDown);

        // change the user/group that the daemon is running as
        dmn->runAsUser("nobody");
        dmn->runAsGroup("nobody");

        // make sure that only one instance is running
        int    pid=dmn->checkForPidFile("/tmp/dmn.pidfile");
        if (pid>-1) {
                printf("Sorry, an instance of this daemon is already running with process id: %d\n",pid);
                delete dmn;
                exit(0);
        }

        // detach from the controlling terminal
        dmn->detach();

        // create a pid file which is used to make sure that only one instance
        // is running and can also be used to kill the process
        dmn->createPidFile("/tmp/dmn.pidfile",permissions::ownerReadWrite());

        if (!fork()) {
                for (;;) {
                        printf("%d: child looping...\n",
                                process::getProcessId());
                        snooze::macrosnooze(1);
                }
        }

        // loop, printing "looping..." once per second
        for (;;) {
                printf("%d: parent looping...\n",
                                process::getProcessId());
                snooze::macrosnooze(1);
        }
}
Using the file Class

...

// Copyright (c) 2003  David Muse
// See the file COPYING for more information

#include <rudiments/groupentry.h>
#include <rudiments/passwdentry.h>
#include <rudiments/file.h>
#include <rudiments/permissions.h>
#include <rudiments/datetime.h>
#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {


        // remove the file (in case it already exists)
        file::remove("testfile");


        // create a new file called "testfile" with rw-rw---- permissions
        // and initial contents "hello"
        file    fl;
        fl.create("testfile",permissions::evalPermString("rw-rw----"),"hello");

        printf("testfile:\n");

        // check for existence
        if (file::exists("testfile")) {
                printf("      exists\n");
        } else {
                printf("      does not exist\n");
        }

        // display the permissions of the file
        mode_t  mode=fl.getPermissions();
        printf("       permissions: %s\n",permissions::evalPermOctal(mode));


        // display the name of the user that owns the file
        uid_t   uid=fl.getOwnerUserId();
        char   *username;
        passwdentry::getName(uid,&username);
        printf("       user       : %s\n",username);
        delete[] username;


        // display the name of the group that owns the file
        gid_t   gid=fl.getOwnerGroupId();
        char   *groupname;
        groupentry::getName(gid,&groupname);
        printf("       group      : %s\n",groupname);
        delete[] groupname;


        // display the size of the file in bytes
        off64_t size=fl.getSize();
        printf("       size       : %ld\n",size);


        // display the size of the file in blocks
        blkcnt_t        blocks=fl.getBlockCount();
        printf("       blocks     : %ld\n",blocks);


        // display the file type
        printf("       is a socket: %d\n",fl.isSocket());
        printf("       is a symlink: %d\n",fl.isSymbolicLink());
        printf("       is a regular file: %d\n",fl.isRegularFile());
        printf("       is a block device: %d\n",fl.isBlockDevice());
        printf("       is a directory: %d\n",fl.isDirectory());
        printf("       is a character device: %d\n",fl.isCharacterDevice());
        printf("       is a fifo: %d\n",fl.isFifo());


        // display the last time the file was accessed
        time_t atime=fl.getLastAccessTime();
        char   *atimestr=datetime::getString(atime);
        printf("       last access      : %s\n",atimestr);
        delete[] atimestr;


        // display the last time the file was modified
        time_t mtime=fl.getLastModificationTime();
        char   *mtimestr=datetime::getString(mtime);
        printf("       last modification: %s\n",mtimestr);
        delete[] mtimestr;


        // display the last time the file was changed
        time_t ctime=fl.getLastChangeTime();
        char   *ctimestr=datetime::getString(ctime);
        printf("       last change      : %s\n",ctimestr);
        delete[] ctimestr;


        // display the device that the file resides on
        dev_t   dev=fl.getDevice();
        printf("       device           : %d\n",dev);


        // display the type of the device that the file resides on
        dev_t   devtype=fl.getDeviceType();
        printf("       device type      : %d\n",devtype);


        // display the file's first inode
        ino_t   inode=fl.getInode();
        printf("       inode            : %d\n",inode);


        // display the number of hard links to the file
        nlink_t nlink=fl.getNumberOfHardLinks();
        printf("       hard links : %ld\n",nlink);


        char   *path="/usr/local/firstworks/include/rudiments/file.h";
        char   *dirname=file::dirname(path);
        printf("dirname(%s)=%s\n",path,dirname);
        delete[] dirname;

        char   *basename=file::basename(path);
        printf("basename(%s)=%s\n",path,basename);
        delete[] basename;

        basename=file::basename(path,".h");
        printf("basename(%s,\".h\")=%s\n",path,basename);
        delete[] basename;

        printf("key=%d\n",file::generateKey("/",1));

        printf("maxLinks(%s)=%d\n",path,file::maxLinks(path));

        printf("canChangeOwner(%s)=%d\n",path,file::canChangeOwner(path));
}
Using the device Class

...

// Copyright (c) 2003  David Muse
// See the file COPYING for more information

#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {

        // FIXME: implement this...
}
Using the serialport Class

...

// Copyright (c) 2003  David Muse
// See the file COPYING for more information

#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {

        // FIXME: implement this...
}
Using the shmfile Class

...

// Copyright (c) 2003  David Muse
// See the file COPYING for more information

#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {

        // FIXME: implement this...
}
Using the Server Classes

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

// Copyright (c) 2001  David Muse
// See the file COPYING for more information

#include <rudiments/daemonprocess.h>
#include <rudiments/permissions.h>
#include <rudiments/inetserversocket.h>
#include <rudiments/file.h>
#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

class myserver : public daemonprocess, public inetserversocket {
        public:
                        myserver() : daemonprocess(), inetserversocket() {}
                void  listen();
};


void    myserver::listen() {


        // make sure that only one instance is running
        int    pid=checkForPidFile("/tmp/svr.pidfile");
        if (pid>-1) {
                printf("Sorry, an instance of this server is already running with process id: %d\n",pid);
                return;
        }


        // detach from the controlling terminal
        //detach();


        // create a pid file which is used to make sure that only one instance
        // is running and can also be used to kill the process
        createPidFile("/tmp/svr.pidfile",permissions::ownerReadWrite());


        // listen on inet socket port 8000
        if (!inetserversocket::listen(NULL,8000,15)) {
                printf("couldn't listen on port 8000\n");
        }


        // loop...
        for (;;) {

                // accept a client connection
                filedescriptor  *clientsock=accept();

                // read 5 bytes from the client and display it
                char  buffer[6];
                buffer[5]=(char)NULL;
                clientsock->read((char *)buffer,5);
                printf("%s\n",buffer);

                // write "hello" back to the client
                clientsock->write("hello",5);

                // close the socket and clean up
                clientsock->close();
                delete clientsock;
        }
}


myserver        *mysvr;

// define a function to shut down the process cleanly
RETSIGTYPE      shutDown() {
        printf("shutting down\n");
        mysvr->close();
        delete mysvr;
        file::remove("/tmp/svr.pidfile");
        exit(0);
}


int main(int argc, const char **argv) {

        mysvr=new myserver();

        // set up signal handlers for clean shutdown
        mysvr->handleShutDown((RETSIGTYPE *)shutDown);
        mysvr->handleCrash((RETSIGTYPE *)shutDown);

        mysvr->listen();
}

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 Classes

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

// Copyright (c) 2001  David Muse
// See the file COPYING for more information

#include <rudiments/inetclientsocket.h>
#include <rudiments/error.h>
#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {

        // create an inet socket client
        inetclientsocket        clnt;

        // connect to a server on localhost, listening on port 8000
        if (clnt.connect("localhost",8000,-1,-1,1,1)<0) {
                printf("connect failed: %s\n",error::getErrorString());
                exit(1);
        }

        // write "hello" to the server
        clnt.write("hello",5);

        // read 10 bytes from the server and display them
        char   buffer[11];
        int    sizeread=clnt.read(buffer,10);
        buffer[sizeread]=(char)NULL;
        printf("%s\n",buffer);

        // close the connection to the server
        clnt.close();
}
// Copyright (c) 2001  David Muse
// See the file COPYING for more information

#include <rudiments/unixclientsocket.h>
#include <rudiments/error.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {

        // create a unix socket client
        unixclientsocket        clnt;

        // connect to a server listening on /tmp/lsnr.socket
        if (clnt.connect("/tmp/lsnr.socket",-1,-1,1,1)<0) {
                printf("connect failed: %s\n",error::getErrorString());
                exit(0);
        }

        // write "hello" to the server
        clnt.write("hello",5);

        // read 10 bytes from the server and display them
        char   buffer[11];
        int    sizeread=clnt.read(buffer,10);
        buffer[sizeread]=(char)NULL;
        printf("%s\n",buffer);

        // close the connection to the server
        clnt.close();
}
Using the Complex Initialization methods of the Server Classes

Setting up a server to listen on a socket is actually a multi-step process. The listen() 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 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.initialize(NULL,8040);
        unixsocket.initialize("/tmp/mysocket",S_IRUSR|S_IWUSR);

        // 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);
        addFileDescriptor(&unixsocket);
}
Using the listener Class

...

// Copyright (c) 2001  David Muse
// See the file COPYING for more information

#include <rudiments/listener.h>
#include <rudiments/inetserversocket.h>
#include <rudiments/unixserversocket.h>
#include <rudiments/permissions.h>
#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif

int main(int argc, const char **argv) {


        // listen on inet socket port 1800
        inetserversocket        inetsock;
        if (!inetsock.listen(NULL,8000,15)) {
                printf("couldn't listen on inet socket\n");
        }


        // listen on unix socket "/tmp/lsnr.socket"
        unixserversocket        unixsock;
        if (!unixsock.listen("/tmp/lsnr.socket",0000,15)) {
                printf("couldn't listen on unix socket\n");
        }


        // create a listener and add the 2 sockets to it
        listener        pool;
        pool.addFileDescriptor(&inetsock);
        pool.addFileDescriptor(&unixsock);


        // loop...
        for (;;) {

                // wait for a client to connect to one of the sockets
                pool.waitForNonBlockingRead(-1,-1);
                filedescriptor  *fd=NULL;
                pool.getReadyList()->getDataByIndex(0,&fd);

                // figure out which socket the client connected to
                filedescriptor  *clientsock;
                if (fd==&inetsock) {
                        clientsock=inetsock.accept();
                        printf("inetsock: ");
                } else if (fd==&unixsock) {
                        clientsock=unixsock.accept();
                        printf("unixsock: ");
                } else {
                        printf("error or timeout waiting...\n");
                        continue;
                }


                // read 5 bytes from the client and display it
                char  buffer[6];
                buffer[5]=(char)NULL;
                clientsock->read(buffer,5);
                printf("%s\n",buffer);


                // write "hello" back to the client
                clientsock->write("hello",5);


                // close the socket and clean up
                clientsock->close();
                delete clientsock;
        }
}