Example Programs

This example shows how to use gotcha to wrap the open and fopen libc calls. This example is self-contained, though in typical gotcha workflows the gotcha calls would be in a separate library from the application.

The example logs the parameters and return result of every open and fopen call to stderr.

 1#include <stdio.h>
 2#include <sys/types.h>
 3#include <sys/stat.h>
 4#include <fcntl.h>
 5
 6#include "gotcha/gotcha.h"
 7
 8typedef int (*open_fptr)(const char *pathname, int flags, mode_t mode);
 9typedef FILE* (*fopen_fptr)(const char *pathname, const char *mode);
10
11static gotcha_wrappee_handle_t open_handle;
12static gotcha_wrappee_handle_t fopen_handle;
13
14static int open_wrapper(const char *pathname, int flags, mode_t mode) {
15  open_fptr open_wrappee = (open_fptr) gotcha_get_wrappee(open_handle);
16  int result = open_wrappee(pathname, flags, mode);
17  fprintf(stderr, "open(%s, %d, %u) = %d\n",
18          pathname, flags, (unsigned int) mode, result);
19  return result;
20}
21
22static FILE *fopen_wrapper(const char *path, const char *mode) {
23  fopen_fptr fopen_wrappee = (fopen_fptr) gotcha_get_wrappee(fopen_handle);
24  FILE *result = fopen_wrappee(path, mode);
25  fprintf(stderr, "fopen(%s, %s) = %p\n",
26          path, mode, result);
27  return result;
28}
29
30static gotcha_binding_t bindings[] = {
31  { "open", open_wrapper, &open_handle },
32  { "fopen", fopen_wrapper, &fopen_handle }
33};
34
35int main(int argc, char *argv[]) {
36  gotcha_wrap(bindings, 2, "demotool");
37
38  open("/dev/null", O_RDONLY);
39  open("/dev/null", O_WRONLY | O_CREAT | O_EXCL);
40  fopen("/dev/random", "r");
41  fopen("/does/not/exist", "w");
42
43  return 0;
44}

The fundamental data structure in the Gotcha API is the gotcha_binding_t table, which is shown in lines 29-32. This table states that calls to open should be rerouted to call open_wrapper, and similarly for fopen and fopen_wrapper. The original open and fopen functions will still be accessible via the handles open_handle and fopen_handle.

The binding table is passed to Gotcha on line 36, which specifies there are two entries in the table and that these are part of the “demotool” tool. The open_handle and fopen_handle variables are updated by this call to gotcha_wrap and can now be used to look up function pointers to the original open and fopen calls.

The subsequent callsites to open and fopen on lines 37-40 are redirected to respectively call open_wrapper and fopen_wrapper on lines 14-20 and 22-27. Each of these functions looks up the original open and fopen functions using the gotcha_get_wrappee API call and the open_handle and fopen_handle on lines 15 and 23.

The wrappers call then call the underlying functions open and fopen functions on lines 16 and 24. The print the parameters and results of these calls on lines 17 and 25 and return.

Note that this example skips proper error handling for brevity. The call to gotcha_wrap could have failed to find instances of fopen and open in the process, which would have led to an error return. The calls to fprintf on lines 17 and 25 are stomping on the value of errno, which could be set in the open and fopen calls on lines 16 and 24.