chiark / gitweb /
It seems to work!
[preload-hacks] / uopen.c
1 #define _GNU_SOURCE
2 #undef sun
3
4 #include <errno.h>
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <string.h>
8
9 #include <unistd.h>
10 #include <dlfcn.h>
11 #include <fcntl.h>
12
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/un.h>
16
17 #define IMPORTS(_)                                                      \
18   _(open, int, (const char *, int, ...))                                \
19   _(open64, int, (const char *, int, ...))                              \
20   _(fopen, FILE *, (const char *, const char *))                        \
21   _(freopen, FILE *, (const char *, const char *, FILE *))
22
23 #define DECL(imp, ret, args) static ret (*real_##imp) args;
24 IMPORTS(DECL)
25 #undef DECL
26
27 static void setup(void) __attribute__((constructor));
28 static void import(void)
29 {
30 #define IMPORT(imp, ret, args)                                          \
31     real_##imp = (ret (*)args)dlsym(RTLD_NEXT, #imp);
32   IMPORTS(IMPORT)
33 #undef IMPORT
34 }
35
36 #define SA(sa) ((struct sockaddr *)(sa))
37
38 #define PRESERVING_ERRNO(body) do {                                     \
39   int _err = errno; { body } errno = _err;                              \
40 } while (0)
41
42 static int maybe_connect(const char *fn, int *fdp)
43 {
44   int fd;
45   struct sockaddr_un sun;
46   struct stat st;
47
48   if (stat(fn, &st) || !S_ISSOCK(st.st_mode))
49     return (0);
50   *fdp = -1;
51   if (strlen(fn) >= sizeof(sun.sun_path)) {
52     errno = EINVAL;
53     return (1);
54   }
55   strncpy(sun.sun_path, fn, sizeof(sun.sun_path));
56   sun.sun_family = AF_UNIX;
57   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0 ||
58       connect(fd, SA(&sun), SUN_LEN(&sun))) {
59     PRESERVING_ERRNO( if (fd >= 0) close(fd); );
60     return (1);
61   }
62   *fdp = fd;
63   return (1);
64 }
65
66 #define OPEN_VENEER(open)                                               \
67   int open(const char *fn, int how, ...)                                \
68   {                                                                     \
69     int mode = 0;                                                       \
70     int fd;                                                             \
71     va_list ap;                                                         \
72                                                                         \
73     PRESERVING_ERRNO({                                                  \
74       if (how & O_CREAT)                                                \
75         { va_start(ap, how); mode = va_arg(ap, int); va_end(ap); }      \
76       if (!maybe_connect(fn, &fd)) fd = real_##open(fn, how, mode);     \
77       if (fd < 0) return (-1);                                          \
78     });                                                                 \
79     return (fd);                                                        \
80   }
81 OPEN_VENEER(open)
82 OPEN_VENEER(open64)
83
84 FILE *fopen(const char *fn, const char *how)
85 {
86   int fd;
87   FILE *fp = 0;
88
89   PRESERVING_ERRNO({
90     if (!maybe_connect(fn, &fd))
91       fp = real_fopen(fn, how);
92     else if (fd >= 0 && (fp = fdopen(fd, how)) == 0)
93       PRESERVING_ERRNO( close(fd); );
94   });
95   return (fp);
96 }
97
98 FILE *freopen(const char *fn, const char *how, FILE *fp)
99 {
100   int fd;
101
102   PRESERVING_ERRNO({
103     if (!maybe_connect(fn, &fd))
104       fp = real_freopen(fn, how, fp);
105     else if (fd >= 0) {
106       if (fflush(fp) || dup2(fd, fileno(fp))) fp = 0;
107       PRESERVING_ERRNO( close(fd); );
108     }
109   });
110   return (fp);
111 }
112
113 static void setup(void)
114 {
115   PRESERVING_ERRNO({
116     import();
117   });
118 }