Line data Source code
1 : /* system-posix.c - System support functions.
2 : Copyright (C) 2009, 2010 Free Software Foundation, Inc.
3 :
4 : This file is part of Assuan.
5 :
6 : Assuan is free software; you can redistribute it and/or modify it
7 : under the terms of the GNU Lesser General Public License as
8 : published by the Free Software Foundation; either version 2.1 of
9 : the License, or (at your option) any later version.
10 :
11 : Assuan is distributed in the hope that it will be useful, but
12 : WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : Lesser General Public License for more details.
15 :
16 : You should have received a copy of the GNU Lesser General Public
17 : License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 :
25 : #include <stdlib.h>
26 : #include <errno.h>
27 : /* Solaris 8 needs sys/types.h before time.h. */
28 : #include <sys/types.h>
29 : #include <time.h>
30 : #include <fcntl.h>
31 : #include <sys/wait.h>
32 :
33 : #include "assuan-defs.h"
34 : #include "debug.h"
35 :
36 : #ifdef _POSIX_OPEN_MAX
37 : #define MAX_OPEN_FDS _POSIX_OPEN_MAX
38 : #else
39 : #define MAX_OPEN_FDS 20
40 : #endif
41 :
42 :
43 :
44 :
45 : assuan_fd_t
46 0 : assuan_fdopen (int fd)
47 : {
48 0 : return dup (fd);
49 : }
50 :
51 :
52 :
53 : /* Sleep for the given number of microseconds. Default
54 : implementation. */
55 : void
56 0 : __assuan_usleep (assuan_context_t ctx, unsigned int usec)
57 : {
58 0 : if (! usec)
59 0 : return;
60 :
61 : #ifdef HAVE_NANOSLEEP
62 : {
63 : struct timespec req;
64 : struct timespec rem;
65 :
66 0 : req.tv_sec = 0;
67 0 : req.tv_nsec = usec * 1000;
68 :
69 0 : while (nanosleep (&req, &rem) < 0 && errno == EINTR)
70 0 : req = rem;
71 : }
72 : #else
73 : {
74 : struct timeval tv;
75 :
76 : tv.tv_sec = usec / 1000000;
77 : tv.tv_usec = usec % 1000000;
78 : select (0, NULL, NULL, NULL, &tv);
79 : }
80 : #endif
81 : }
82 :
83 :
84 :
85 : /* Create a pipe with one inheritable end. Easy for Posix. */
86 : int
87 2 : __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
88 : {
89 2 : return pipe (fd);
90 : }
91 :
92 :
93 :
94 : /* Close the given file descriptor, created with _assuan_pipe or one
95 : of the socket functions. Easy for Posix. */
96 : int
97 11 : __assuan_close (assuan_context_t ctx, assuan_fd_t fd)
98 : {
99 11 : return close (fd);
100 : }
101 :
102 :
103 :
104 : ssize_t
105 5 : __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
106 : {
107 5 : return read (fd, buffer, size);
108 : }
109 :
110 :
111 :
112 : ssize_t
113 12 : __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
114 : size_t size)
115 : {
116 12 : return write (fd, buffer, size);
117 : }
118 :
119 :
120 :
121 : int
122 32 : __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
123 : int flags)
124 : {
125 : int ret;
126 :
127 : do
128 32 : ret = recvmsg (fd, msg, flags);
129 32 : while (ret == -1 && errno == EINTR);
130 :
131 32 : return ret;
132 : }
133 :
134 :
135 :
136 : int
137 59 : __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
138 : int flags)
139 : {
140 : int ret;
141 :
142 : do
143 59 : ret = sendmsg (fd, msg, flags);
144 59 : while (ret == -1 && errno == EINTR);
145 :
146 59 : return ret;
147 : }
148 :
149 :
150 :
151 : static int
152 0 : writen (int fd, const char *buffer, size_t length)
153 : {
154 0 : while (length)
155 : {
156 0 : int nwritten = write (fd, buffer, length);
157 :
158 0 : if (nwritten < 0)
159 : {
160 0 : if (errno == EINTR)
161 0 : continue;
162 0 : return -1; /* write error */
163 : }
164 0 : length -= nwritten;
165 0 : buffer += nwritten;
166 : }
167 0 : return 0; /* okay */
168 : }
169 :
170 :
171 : int
172 2 : __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
173 : const char **argv,
174 : assuan_fd_t fd_in, assuan_fd_t fd_out,
175 : assuan_fd_t *fd_child_list,
176 : void (*atfork) (void *opaque, int reserved),
177 : void *atforkvalue, unsigned int flags)
178 : {
179 : int pid;
180 :
181 2 : pid = fork ();
182 4 : if (pid < 0)
183 0 : return -1;
184 :
185 4 : if (pid == 0)
186 : {
187 : /* Child process (server side). */
188 : int i;
189 : int n;
190 : char errbuf[512];
191 : int *fdp;
192 : int fdnul;
193 :
194 2 : if (atfork)
195 2 : atfork (atforkvalue, 0);
196 :
197 2 : fdnul = open ("/dev/null", O_WRONLY);
198 2 : if (fdnul == -1)
199 : {
200 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
201 : "can't open `/dev/null': %s", strerror (errno));
202 0 : _exit (4);
203 : }
204 :
205 : /* Dup handles to stdin/stdout. */
206 2 : if (fd_out != STDOUT_FILENO)
207 : {
208 2 : if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out,
209 : STDOUT_FILENO) == -1)
210 : {
211 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
212 : "dup2 failed in child: %s", strerror (errno));
213 0 : _exit (4);
214 : }
215 : }
216 :
217 2 : if (fd_in != STDIN_FILENO)
218 : {
219 2 : if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in,
220 : STDIN_FILENO) == -1)
221 : {
222 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
223 : "dup2 failed in child: %s", strerror (errno));
224 0 : _exit (4);
225 : }
226 : }
227 :
228 : /* Dup stderr to /dev/null unless it is in the list of FDs to be
229 : passed to the child. */
230 2 : fdp = fd_child_list;
231 2 : if (fdp)
232 : {
233 2 : for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
234 : ;
235 : }
236 2 : if (!fdp || *fdp == -1)
237 : {
238 0 : if (dup2 (fdnul, STDERR_FILENO) == -1)
239 : {
240 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx,
241 : "dup2(dev/null, 2) failed: %s", strerror (errno));
242 0 : _exit (4);
243 : }
244 : }
245 2 : close (fdnul);
246 :
247 : /* Close all files which will not be duped and are not in the
248 : fd_child_list. */
249 2 : n = sysconf (_SC_OPEN_MAX);
250 2 : if (n < 0)
251 0 : n = MAX_OPEN_FDS;
252 512002 : for (i = 0; i < n; i++)
253 : {
254 512000 : if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO)
255 6 : continue;
256 511994 : fdp = fd_child_list;
257 511994 : if (fdp)
258 : {
259 1791977 : while (*fdp != -1 && *fdp != i)
260 767989 : fdp++;
261 : }
262 :
263 511994 : if (!(fdp && *fdp != -1))
264 511993 : close (i);
265 : }
266 2 : gpg_err_set_errno (0);
267 :
268 2 : if (! name)
269 : {
270 : /* No name and no args given, thus we don't do an exec
271 : but continue the forked process. */
272 1 : *argv = "server";
273 :
274 : /* FIXME: Cleanup. */
275 1 : return 0;
276 : }
277 :
278 1 : execv (name, (char *const *) argv);
279 :
280 : /* oops - use the pipe to tell the parent about it */
281 0 : snprintf (errbuf, sizeof(errbuf)-1,
282 : "ERR %d can't exec `%s': %.50s\n",
283 : _assuan_error (ctx, GPG_ERR_ASS_SERVER_START),
284 1 : name, strerror (errno));
285 0 : errbuf[sizeof(errbuf)-1] = 0;
286 0 : writen (1, errbuf, strlen (errbuf));
287 0 : _exit (4);
288 : }
289 :
290 2 : if (! name)
291 1 : *argv = "client";
292 :
293 2 : *r_pid = pid;
294 :
295 2 : return 0;
296 : }
297 :
298 :
299 :
300 : /* FIXME: Add some sort of waitpid function that covers GPGME and
301 : gpg-agent's use of assuan. */
302 : pid_t
303 3 : __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait,
304 : int *status, int options)
305 : {
306 : /* We can't just release the PID, a waitpid is mandatory. But
307 : NOWAIT in POSIX systems just means the caller already did the
308 : waitpid for this child. */
309 3 : if (! nowait)
310 3 : return waitpid (pid, NULL, 0);
311 0 : return 0;
312 : }
313 :
314 :
315 :
316 : int
317 1 : __assuan_socketpair (assuan_context_t ctx, int namespace, int style,
318 : int protocol, assuan_fd_t filedes[2])
319 : {
320 1 : return socketpair (namespace, style, protocol, filedes);
321 : }
322 :
323 :
324 : int
325 0 : __assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol)
326 : {
327 0 : return socket (namespace, style, protocol);
328 : }
329 :
330 :
331 : int
332 0 : __assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr,
333 : socklen_t length)
334 : {
335 0 : return connect (sock, addr, length);
336 : }
337 :
338 :
339 :
340 : /* The default system hooks for assuan contexts. */
341 : struct assuan_system_hooks _assuan_system_hooks =
342 : {
343 : ASSUAN_SYSTEM_HOOKS_VERSION,
344 : __assuan_usleep,
345 : __assuan_pipe,
346 : __assuan_close,
347 : __assuan_read,
348 : __assuan_write,
349 : __assuan_recvmsg,
350 : __assuan_sendmsg,
351 : __assuan_spawn,
352 : __assuan_waitpid,
353 : __assuan_socketpair,
354 : __assuan_socket,
355 : __assuan_connect
356 : };
|