chiark / gitweb /
Release 1.2.3.
[preload-hacks] / uopen.c
1 /* -*-c-*-
2  *
3  * Allow open(2) to work on Unix-domain sockets
4  *
5  * (c) 2008 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the preload-hacks package.
11  *
12  * Preload-hacks are free software; you can redistribute it and/or modify
13  * them under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or (at
15  * your option) any later version.
16  *
17  * Preload-hacks are distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with preload-hacks; if not, write to the Free Software Foundation, Inc.,
24  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 #define _GNU_SOURCE
28 #undef sun
29
30 /*----- Header files ------------------------------------------------------*/
31
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include <unistd.h>
38 #include <dlfcn.h>
39 #include <fcntl.h>
40
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/un.h>
44
45 #ifndef SUN_LEN
46 #  define SUN_LEN (sun) \
47         (strlen((sun)->sun_path) + offsetof(struct sockaddr_un, sun_path))
48 #endif
49
50 /*----- Import the real versions of functions -----------------------------*/
51
52 /* The list of functions to immport. */
53 #define IMPORTS(_)                                                      \
54   _(open, int, (const char *, int, ...))                                \
55   _(open64, int, (const char *, int, ...))                              \
56   _(fopen, FILE *, (const char *, const char *))                        \
57   _(freopen, FILE *, (const char *, const char *, FILE *))
58
59 /* Function pointers to set up. */
60 #define DECL(imp, ret, args) static ret (*real_##imp) args;
61 IMPORTS(DECL)
62 #undef DECL
63
64 /* Import the system calls. */
65 static void import(void)
66 {
67 #define IMPORT(imp, ret, args)                                          \
68     real_##imp = (ret (*)args)dlsym(RTLD_NEXT, #imp);
69   IMPORTS(IMPORT)
70 #undef IMPORT
71 }
72 /*----- Utilities ---------------------------------------------------------*/
73
74 /* Socket address casts */
75 #define SA(sa) ((struct sockaddr *)(sa))
76
77 /* Preservation of error status */
78 #define PRESERVING_ERRNO(body) do {                                     \
79   int _err = errno; { body } errno = _err;                              \
80 } while (0)
81
82 /*----- Connecting to Unix-domain sockets ---------------------------------*/
83
84 /* If FN refers to a Unix-domain socket, connect to it, stash the socket
85  * (or -1 on error) in *FDP, and return 1.  Otherwise return 0.
86  */
87 static int maybe_connect(const char *fn, int *fdp)
88 {
89   int fd;
90   struct sockaddr_un sun;
91   struct stat st;
92
93   if (stat(fn, &st) || !S_ISSOCK(st.st_mode))
94     return (0);
95   *fdp = -1;
96   if (strlen(fn) >= sizeof(sun.sun_path)) {
97     errno = EINVAL;
98     return (1);
99   }
100   strncpy(sun.sun_path, fn, sizeof(sun.sun_path));
101   sun.sun_family = AF_UNIX;
102   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0 ||
103       connect(fd, SA(&sun), SUN_LEN(&sun))) {
104     PRESERVING_ERRNO( if (fd >= 0) close(fd); );
105     return (1);
106   }
107   *fdp = fd;
108   return (1);
109 }
110
111 /*----- Intercepted system calls ------------------------------------------*/
112
113 /* Create an open(2)-like function OPEN. */
114 #define OPEN_VENEER(open)                                               \
115   int open(const char *fn, int how, ...)                                \
116   {                                                                     \
117     int mode = 0;                                                       \
118     int fd;                                                             \
119     va_list ap;                                                         \
120                                                                         \
121     PRESERVING_ERRNO({                                                  \
122       if (how & O_CREAT)                                                \
123         { va_start(ap, how); mode = va_arg(ap, int); va_end(ap); }      \
124       if (!maybe_connect(fn, &fd)) fd = real_##open(fn, how, mode);     \
125       if (fd < 0) return (-1);                                          \
126     });                                                                 \
127     return (fd);                                                        \
128   }
129
130 /* open(2) and open64(2). */
131 OPEN_VENEER(open)
132 OPEN_VENEER(open64)
133
134 FILE *fopen(const char *fn, const char *how)
135 {
136   int fd;
137   FILE *fp = 0;
138
139   PRESERVING_ERRNO({
140     if (!maybe_connect(fn, &fd))
141       fp = real_fopen(fn, how);
142     else if (fd >= 0 && (fp = fdopen(fd, how)) == 0)
143       PRESERVING_ERRNO( close(fd); );
144   });
145   return (fp);
146 }
147
148 FILE *freopen(const char *fn, const char *how, FILE *fp)
149 {
150   int fd;
151
152   PRESERVING_ERRNO({
153     if (!maybe_connect(fn, &fd))
154       fp = real_freopen(fn, how, fp);
155     else if (fd >= 0) {
156       if (fflush(fp) || dup2(fd, fileno(fp))) fp = 0;
157       PRESERVING_ERRNO( close(fd); );
158     }
159   });
160   return (fp);
161 }
162
163 /*----- Initialization ----------------------------------------------------*/
164
165 static void setup(void) __attribute__((constructor));
166 static void setup(void)
167 {
168   PRESERVING_ERRNO({
169     import();
170   });
171 }
172
173 /*----- That's all, folks -------------------------------------------------*/