Sometimes its easier to just write the code

Here’s a straightforward way to daemonize some primitive app. It supports redirecting stderr and stdout. It close stdin and writes a pid to a pidfile.

Use would look like this:

1
2
3
4
amandla> ./daemonize -e /tmp/stderr -o /tmp/stdout \ 
 -p /tmp/pidfile /opt/bin/crapapp 
amandla> cat /tmp/pidfile
32139

Where 32139 is the PID of crapapp and files in /tmp contain what you’d expect. There’s probably some existing way to do this easily but sometimes just writing the code is quicker and easier than uhh googling it. Deliver yesterday, program today, think tomorrow. Yeah, that’s how we do it. Here’s the code..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#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 flags) {
    int file;
    if ((NULL == path) || (-1 == (file = open(path, flags)))) {
        (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-i <filename> Redirect stdin from file <filename>\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, *stdin_filename, 
        *stdout_filename, *stderr_filename = NULL;
    while (-1 != (option = getopt(argc, argv, "+i:e:o:p:"))) {
        switch (option) {
        case 'i': stdin_filename = optarg; break;
        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(stdin_filename, 
        O_NONBLOCK | O_RDWR);
    int stderr_file = open_or_die(stderr_filename, 
        O_NONBLOCK | O_RDWR);
    int stdout_file = open_or_die(stdout_filename, 
        O_NONBLOCK | O_RDWR);
 
    // Redirect stdin, 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);
}

1 Comment »

  1. Chris Leary Said,

    July 23, 2009 @ 11:17 pm

    Check out python-daemon — it’s definitely one of those packages thats Done Right (TM): http://www.python.org/dev/peps/pep-3143/

Leave a Comment