Skip to content

Running a kdb+ daemon

Here’s a simple way to daemonize kdb+ on Linux. Supports redirecting stderr and stdout. It closes stdin and writes a pid to a pidfile.

Shell features

Remember shell features, e.g.

nohup q -p 5000 < /dev/null > /tmp/stdoe 2>&1&
echo $! > /tmp/pidfile

Sample use:

saturn:tmp> gcc daemonize.c -o daemonize
saturn:tmp> ./daemonize -e /tmp/stderr -o /tmp/stdout -p /tmp/pidfile
~/q/l64/q -p 5000
saturn:tmp> cat /tmp/pidfile
32139
saturn:tmp> q
KDB+ 2.4t 2007.05.04 Copyright (C) 1993-2007 Kx Systems
l64/ 4(8)core 3943MB niall saturn 127.0.0.1 prod 2012.01.01 niall

q)h:hopen `:localhost:5000
q)h"2+2"
4
q)h"0N!`hello"
`hello
q)\\
saturn:tmp> cat /tmp/stdout
`hello

The code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>

int open_or_die(char *path) {
    int file, flags = O_CREAT | O_WRONLY | O_APPEND;
    if ((NULL == path) || (-1 == (file = open (path, flags, 0666)))) {
            (void) fprintf (stderr, "Failed to open file \"%s\": %s\n", path,
                                    strerror(errno));
           exit (EXIT_FAILURE);
    }
    return file;
}

int main (int argc, char *argv[]) { 
    // Check for some args.
    if (8 > argc) {
        (void) puts(
            "Usage: daemonize options path [args]\n"
            "\t-e <filename>  Redirect stderr to file <filename>\n"
            "\t-o <filename>  Redirect stdout to file <filename>\n"
            "\t-p <filename>  Write pid to <filename>\n");
        exit (EXIT_FAILURE);
    }

    // Parse args.
    int option;
    char **command = NULL, *pid_filename, *stdout_filename,
               *stderr_filename = NULL;
    while (-1 != (option = getopt (argc, argv, "+e:o:p:"))) {
        switch (option) {
            case 'e': stderr_filename = optarg; break;
            case 'o': stdout_filename = optarg; break;
            case 'p': pid_filename = optarg; break;
            default: (void) fprintf (stderr, "Unknown option: -%c\n",
                                                        optopt);
        }
    }
       // Assume the command to daemonize is the rest of the arguments
    command = &argv[optind];    

    // Make a token attempt to see if we'll be able to exec the command.
    if (-1 == access (command[0], F_OK)) {
        (void) fprintf (stderr, "Can't access %s, exiting.", command[0]);
        exit (EXIT_FAILURE);
    }

    // Try to open some files for pid, stdin, stdout, stderr.
    FILE *pid_file = fopen (pid_filename, "w+");
    int stdin_file = open_or_die("/dev/null");
    int stderr_file = open_or_die(stderr_filename);
    int stdout_file = open_or_die(stdout_filename);

    // Nuke stdin and redirect stderr, stdout.
    close (STDIN_FILENO);
    dup2 (stdin_file, STDIN_FILENO);
    close (STDOUT_FILENO);
    dup2 (stdout_file, STDOUT_FILENO);
    close (STDERR_FILENO);
    dup2 (stderr_file, STDERR_FILENO);

    // Now daemonize..
    if (0 != daemon (0, 1))  {
        (void) fprintf (stderr, "Can't daemonize: %s\nExiting.",
                                   strerror(errno));
        exit (EXIT_FAILURE);
    }

    // Write the pid
    fprintf (pid_file, "%d\n", getpid ());
    fclose (pid_file);

    // And away we go..
    execvp (command[0], command);
}