Archive for June, 2009

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);
}

Comments (1)

Stacktraces for c++

I must be getting old. I felt the need for stacktraces in some c++ code I’m running that ehhh occasionally has trouble dealing with life and decides to blow up rather ignominiously in the depths of some 3rd party library. This is entirely un-portable and frankly, my dear, I don’t have a ham. Linux and g++. Deal with it. If it works anywhere else, its a miracle and you can send me a check. If it doesn’t work, you get to keep the pieces.

So, roughly speaking, when our test program blows up we want to see it dump a somewhat readable stacktrace with unmangled (demangled?!) c++ names.. not the blah blah that looks random junk but is in fact encoded facts about the name.. like so:

1
2
3
4
5
6
7
8
9
10
amandla> ./test 
stacktrace_exception::stacktrace_exception()
three()
foo::call_three()
two()
one()
main()
__libc_start_main()
./test [0x401619]
amandla>

where this is the test 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
#include <iostream>
#include <cstdlib>
#include <stacktrace_exception.hpp>
 
void three() throw(stacktrace_exception)
{
        stacktrace_exception e;
        throw e;
}
 
struct foo 
{
        int call_three() 
        {
                three();
        }
};
 
void two() throw(stacktrace_exception)
{
        foo f;  
        f.call_three();
}
 
void one() throw(stacktrace_exception)
{
        two();
}
 
int main()
{
        using namespace std;
        try {
                one();
        }
        catch (stacktrace_exception& e) {
                cout << e.what() << endl;
        }
        return EXIT_SUCCESS;
}

On my Ubuntu system, there are some rather useful functions in /usr/include/c++/4.3/cxxabi.h that deal with demangling names. You have to love code that contains “magic placehoder classes”. But, I digress. Without further ado, here is the header:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef _STACKTRACE_EXCEPTION_HPP
#define _STACKTRACE_EXCEPTION_HPP
 
#include <string>
#include <exception>
#include <execinfo.h>
#include <cxxabi.h>
 
static const int max_stacktrace_depth = 256;
 
class stacktrace_exception : public std::exception {
public:
        explicit stacktrace_exception();
        ~stacktrace_exception() throw();
        virtual const char* what() const throw();
 
private:
        std::string trace;
};
 
#endif

And the implementation.. with some comments to describe the more shameful aspects..

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
#include <cstdlib>
#include <cstring>
#include <stacktrace_exception.hpp>
 
stacktrace_exception::stacktrace_exception() 
{
    // Unfortunately we have to commit to a 
    // maximum stack trace depth. *sigh*
    void *buffer[max_stacktrace_depth];
 
    // Walk the stack and fill in a backtrace 
    // in our buffer.
    int number_of_addresses 
        = backtrace(buffer, max_stacktrace_depth);
 
    // Get the symbols for the stack trace.
    char **strings 
        = backtrace_symbols(buffer, number_of_addresses);
 
    // Since we're in c++ we should attempt to 
    // demangle the entries.
    for (int i = 0; i < number_of_addresses; ++i) {
	// Take a guess at the maximum width of a 
        // name - template names can be wide. 
	size_t max_name_width = 1024;
 
	// Malloc up some space; we use the C functions 
        // because of the code we're interfacing with. 
	char *demangled_name 
            = static_cast<char*>(malloc(max_name_width));
 
	// Begin and end of the mangled name.
	char *begin = 0, *end = 0;
 
        // Find the the parentheses and address offset 
        // surrounding the name.
	for (char *j = strings[i]; *j; ++j) {
	    if (*j == '(')
		begin = j;
	    else if (*j == '+') 
		end = j;
	}
 
	// If we found it one..
	if (0 != begin && 0 != end) {
	    // Isolate the name.
	    *begin++ = '\0'; *end = '\0';
 
	    int ignore;
	    char *returned_demangled_name 
            = abi::__cxa_demangle(begin, demangled_name, 
                                 &max_name_width, &ignore);
	    if (0 != returned_demangled_name){
		// We need to do this in case 
                // things got realloc()'d on us.
		demangled_name = returned_demangled_name;
	    }
	    else {
		// Demangling failed, just do our best 
                // with the existing name.
		std::strncpy(demangled_name, begin, max_name_width);
		std::strncat(demangled_name, "()", max_name_width);
		demangled_name[max_name_width-1] = '\0';
	    }
	    trace.append(demangled_name);
	}
	else
	    trace.append(strings[i]);
	trace.append("\n"); 
	free(demangled_name);
    }
    free(strings);
}
 
stacktrace_exception::~stacktrace_exception() throw ()
{
}
 
const char* stacktrace_exception::what() const throw()
{
    return trace.c_str();   
}

Now the astute reader will be cringing at the memory being allocated. We could be in a situation where the glorious edifice of our program is crumbling around our ears due to memory being exhausted, and we should dump the backtrace symbols to stderr via a call to backtrace_symbols_fd(). Such an extension is left as an exercise to the reader.

I’ve long maintained that c++ is a language for consenting adults; any time its used you know someone is going to get fucked. However, its hard to ignore, its generally useful and there are some fine libraries that are best dealt with from their native language. Such is life. Even if it does take “worse is better” to new and dizzying depths.

Leave a Comment