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 : #ifdef HAVE_STDINT_H
28 : # include <stdint.h>
29 : #endif
30 : /* Solaris 8 needs sys/types.h before time.h. */
31 : #include <sys/types.h>
32 : #include <time.h>
33 : #include <fcntl.h>
34 : #include <sys/wait.h>
35 : #ifdef HAVE_GETRLIMIT
36 : # include <sys/time.h>
37 : # include <sys/resource.h>
38 : #endif /*HAVE_GETRLIMIT*/
39 : #if __linux__
40 : # include <dirent.h>
41 : #endif /*__linux__ */
42 :
43 :
44 : #include "assuan-defs.h"
45 : #include "debug.h"
46 :
47 :
48 :
49 :
50 : assuan_fd_t
51 0 : assuan_fdopen (int fd)
52 : {
53 0 : return dup (fd);
54 : }
55 :
56 :
57 :
58 : /* Sleep for the given number of microseconds. Default
59 : implementation. */
60 : void
61 0 : __assuan_usleep (assuan_context_t ctx, unsigned int usec)
62 : {
63 0 : if (! usec)
64 0 : return;
65 :
66 : #ifdef HAVE_NANOSLEEP
67 : {
68 : struct timespec req;
69 : struct timespec rem;
70 :
71 0 : req.tv_sec = 0;
72 0 : req.tv_nsec = usec * 1000;
73 :
74 0 : while (nanosleep (&req, &rem) < 0 && errno == EINTR)
75 0 : req = rem;
76 : }
77 : #else
78 : {
79 : struct timeval tv;
80 :
81 : tv.tv_sec = usec / 1000000;
82 : tv.tv_usec = usec % 1000000;
83 : select (0, NULL, NULL, NULL, &tv);
84 : }
85 : #endif
86 : }
87 :
88 :
89 :
90 : /* Create a pipe with one inheritable end. Easy for Posix. */
91 : int
92 2 : __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
93 : {
94 2 : return pipe (fd);
95 : }
96 :
97 :
98 :
99 : /* Close the given file descriptor, created with _assuan_pipe or one
100 : of the socket functions. Easy for Posix. */
101 : int
102 11 : __assuan_close (assuan_context_t ctx, assuan_fd_t fd)
103 : {
104 11 : return close (fd);
105 : }
106 :
107 :
108 :
109 : ssize_t
110 5 : __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
111 : {
112 5 : return read (fd, buffer, size);
113 : }
114 :
115 :
116 :
117 : ssize_t
118 12 : __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
119 : size_t size)
120 : {
121 12 : return write (fd, buffer, size);
122 : }
123 :
124 :
125 :
126 : int
127 32 : __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
128 : int flags)
129 : {
130 : int ret;
131 :
132 : do
133 32 : ret = recvmsg (fd, msg, flags);
134 32 : while (ret == -1 && errno == EINTR);
135 :
136 32 : return ret;
137 : }
138 :
139 :
140 :
141 : int
142 59 : __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
143 : int flags)
144 : {
145 : int ret;
146 :
147 : do
148 59 : ret = sendmsg (fd, msg, flags);
149 59 : while (ret == -1 && errno == EINTR);
150 :
151 59 : return ret;
152 : }
153 :
154 :
155 :
156 : static int
157 0 : writen (int fd, const char *buffer, size_t length)
158 : {
159 0 : while (length)
160 : {
161 0 : int nwritten = write (fd, buffer, length);
162 :
163 0 : if (nwritten < 0)
164 : {
165 0 : if (errno == EINTR)
166 0 : continue;
167 0 : return -1; /* write error */
168 : }
169 0 : length -= nwritten;
170 0 : buffer += nwritten;
171 : }
172 0 : return 0; /* okay */
173 : }
174 :
175 :
176 : /* Return the maximum number of currently allowed open file
177 : * descriptors. */
178 : static int
179 2 : get_max_fds (void)
180 : {
181 2 : int max_fds = -1;
182 :
183 : #ifdef HAVE_GETRLIMIT
184 : struct rlimit rl;
185 :
186 : /* Under Linux we can figure out the highest used file descriptor by
187 : * reading /proc/PID/fd. This is in the common cases much faster
188 : * than for example doing 4096 close calls where almost all of them
189 : * will fail. We use the same code in GnuPG and measured this: On a
190 : * system with a limit of 4096 files and only 8 files open with the
191 : * highest number being 10, we speedup close_all_fds from 125ms to
192 : * 0.4ms including the readdir.
193 : *
194 : * Another option would be to close the file descriptors as returned
195 : * from reading that directory - however then we need to snapshot
196 : * that list before starting to close them. */
197 : #ifdef __linux__
198 : {
199 2 : DIR *dir = NULL;
200 : struct dirent *dir_entry;
201 : const char *s;
202 : int x;
203 :
204 2 : dir = opendir ("/proc/self/fd");
205 2 : if (dir)
206 : {
207 26 : while ((dir_entry = readdir (dir)))
208 : {
209 22 : s = dir_entry->d_name;
210 22 : if ( *s < '0' || *s > '9')
211 4 : continue;
212 18 : x = atoi (s);
213 18 : if (x > max_fds)
214 18 : max_fds = x;
215 : }
216 2 : closedir (dir);
217 : }
218 2 : if (max_fds != -1)
219 2 : return max_fds + 1;
220 : }
221 : #endif /* __linux__ */
222 :
223 : # ifdef RLIMIT_NOFILE
224 0 : if (!getrlimit (RLIMIT_NOFILE, &rl))
225 0 : max_fds = rl.rlim_max;
226 : # endif
227 :
228 : # ifdef RLIMIT_OFILE
229 0 : if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl))
230 0 : max_fds = rl.rlim_max;
231 :
232 : # endif
233 : #endif /*HAVE_GETRLIMIT*/
234 :
235 : #ifdef _SC_OPEN_MAX
236 0 : if (max_fds == -1)
237 : {
238 0 : long int scres = sysconf (_SC_OPEN_MAX);
239 0 : if (scres >= 0)
240 0 : max_fds = scres;
241 : }
242 : #endif
243 :
244 : #ifdef _POSIX_OPEN_MAX
245 0 : if (max_fds == -1)
246 0 : max_fds = _POSIX_OPEN_MAX;
247 : #endif
248 :
249 : #ifdef OPEN_MAX
250 : if (max_fds == -1)
251 : max_fds = OPEN_MAX;
252 : #endif
253 :
254 0 : if (max_fds == -1)
255 0 : max_fds = 256; /* Arbitrary limit. */
256 :
257 : /* AIX returns INT32_MAX instead of a proper value. We assume that
258 : this is always an error and use a more reasonable limit. */
259 : #ifdef INT32_MAX
260 0 : if (max_fds == INT32_MAX)
261 0 : max_fds = 256;
262 : #endif
263 :
264 0 : return max_fds;
265 : }
266 :
267 :
268 : int
269 2 : __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
270 : const char **argv,
271 : assuan_fd_t fd_in, assuan_fd_t fd_out,
272 : assuan_fd_t *fd_child_list,
273 : void (*atfork) (void *opaque, int reserved),
274 : void *atforkvalue, unsigned int flags)
275 : {
276 : int pid;
277 :
278 2 : pid = fork ();
279 4 : if (pid < 0)
280 0 : return -1;
281 :
282 4 : if (pid == 0)
283 : {
284 : /* Child process (server side). */
285 : int i;
286 : int n;
287 : char errbuf[512];
288 : int *fdp;
289 : int fdnul;
290 :
291 2 : if (atfork)
292 2 : atfork (atforkvalue, 0);
293 :
294 2 : fdnul = open ("/dev/null", O_WRONLY);
295 2 : if (fdnul == -1)
296 : {
297 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
298 : "can't open `/dev/null': %s", strerror (errno));
299 0 : _exit (4);
300 : }
301 :
302 : /* Dup handles to stdin/stdout. */
303 2 : if (fd_out != STDOUT_FILENO)
304 : {
305 2 : if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out,
306 : STDOUT_FILENO) == -1)
307 : {
308 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
309 : "dup2 failed in child: %s", strerror (errno));
310 0 : _exit (4);
311 : }
312 : }
313 :
314 2 : if (fd_in != STDIN_FILENO)
315 : {
316 2 : if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in,
317 : STDIN_FILENO) == -1)
318 : {
319 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
320 : "dup2 failed in child: %s", strerror (errno));
321 0 : _exit (4);
322 : }
323 : }
324 :
325 : /* Dup stderr to /dev/null unless it is in the list of FDs to be
326 : passed to the child. */
327 2 : fdp = fd_child_list;
328 2 : if (fdp)
329 : {
330 2 : for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
331 : ;
332 : }
333 2 : if (!fdp || *fdp == -1)
334 : {
335 0 : if (dup2 (fdnul, STDERR_FILENO) == -1)
336 : {
337 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx,
338 : "dup2(dev/null, 2) failed: %s", strerror (errno));
339 0 : _exit (4);
340 : }
341 : }
342 2 : close (fdnul);
343 :
344 : /* Close all files which will not be duped and are not in the
345 : fd_child_list. */
346 2 : n = get_max_fds ();
347 62 : for (i = 0; i < n; i++)
348 : {
349 60 : if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO)
350 6 : continue;
351 54 : fdp = fd_child_list;
352 54 : if (fdp)
353 : {
354 187 : while (*fdp != -1 && *fdp != i)
355 79 : fdp++;
356 : }
357 :
358 54 : if (!(fdp && *fdp != -1))
359 53 : close (i);
360 : }
361 2 : gpg_err_set_errno (0);
362 :
363 2 : if (! name)
364 : {
365 : /* No name and no args given, thus we don't do an exec
366 : but continue the forked process. */
367 1 : *argv = "server";
368 :
369 : /* FIXME: Cleanup. */
370 1 : return 0;
371 : }
372 :
373 1 : execv (name, (char *const *) argv);
374 :
375 : /* oops - use the pipe to tell the parent about it */
376 0 : snprintf (errbuf, sizeof(errbuf)-1,
377 : "ERR %d can't exec `%s': %.50s\n",
378 : _assuan_error (ctx, GPG_ERR_ASS_SERVER_START),
379 1 : name, strerror (errno));
380 0 : errbuf[sizeof(errbuf)-1] = 0;
381 0 : writen (1, errbuf, strlen (errbuf));
382 0 : _exit (4);
383 : }
384 :
385 2 : if (! name)
386 1 : *argv = "client";
387 :
388 2 : *r_pid = pid;
389 :
390 2 : return 0;
391 : }
392 :
393 :
394 :
395 : /* FIXME: Add some sort of waitpid function that covers GPGME and
396 : gpg-agent's use of assuan. */
397 : pid_t
398 3 : __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait,
399 : int *status, int options)
400 : {
401 : /* We can't just release the PID, a waitpid is mandatory. But
402 : NOWAIT in POSIX systems just means the caller already did the
403 : waitpid for this child. */
404 3 : if (! nowait)
405 3 : return waitpid (pid, NULL, 0);
406 0 : return 0;
407 : }
408 :
409 :
410 :
411 : int
412 1 : __assuan_socketpair (assuan_context_t ctx, int namespace, int style,
413 : int protocol, assuan_fd_t filedes[2])
414 : {
415 1 : return socketpair (namespace, style, protocol, filedes);
416 : }
417 :
418 :
419 : int
420 0 : __assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol)
421 : {
422 0 : return socket (namespace, style, protocol);
423 : }
424 :
425 :
426 : int
427 0 : __assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr,
428 : socklen_t length)
429 : {
430 0 : return connect (sock, addr, length);
431 : }
432 :
433 :
434 :
435 : /* The default system hooks for assuan contexts. */
436 : struct assuan_system_hooks _assuan_system_hooks =
437 : {
438 : ASSUAN_SYSTEM_HOOKS_VERSION,
439 : __assuan_usleep,
440 : __assuan_pipe,
441 : __assuan_close,
442 : __assuan_read,
443 : __assuan_write,
444 : __assuan_recvmsg,
445 : __assuan_sendmsg,
446 : __assuan_spawn,
447 : __assuan_waitpid,
448 : __assuan_socketpair,
449 : __assuan_socket,
450 : __assuan_connect
451 : };
|