1
/* posix-io.c - Posix I/O functions
2
Copyright (C) 2000 Werner Koch (dd9jn)
3
Copyright (C) 2001, 2002, 2004, 2005, 2007 g10 Code GmbH
5
This file is part of GPGME.
7
GPGME is free software; you can redistribute it and/or modify it
8
under the terms of the GNU Lesser General Public License as
9
published by the Free Software Foundation; either version 2.1 of
10
the License, or (at your option) any later version.
12
GPGME is distributed in the hope that it will be useful, but
13
WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
Lesser General Public License for more details.
17
You should have received a copy of the GNU Lesser General Public
18
License along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
34
#include <sys/types.h>
40
#include <sys/resource.h>
51
_gpgme_io_subsystem_init (void)
55
sigaction (SIGPIPE, NULL, &act);
56
if (act.sa_handler == SIG_DFL)
58
act.sa_handler = SIG_IGN;
59
sigemptyset (&act.sa_mask);
61
sigaction (SIGPIPE, &act, NULL);
66
/* Write the printable version of FD to the buffer BUF of length
67
BUFLEN. The printable version is the representation on the command
68
line that the child process expects. */
70
_gpgme_io_fd2str (char *buf, int buflen, int fd)
72
return snprintf (buf, buflen, "%d", fd);
78
_gpgme_close_notify_handler_t handler;
84
_gpgme_io_read (int fd, void *buffer, size_t count)
87
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
88
"buffer=%p, count=%u", buffer, count);
92
nread = _gpgme_ath_read (fd, buffer, count);
94
while (nread == -1 && errno == EINTR);
96
TRACE_LOGBUF (buffer, nread);
97
return TRACE_SYSRES (nread);
102
_gpgme_io_write (int fd, const void *buffer, size_t count)
105
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
106
"buffer=%p, count=%u", buffer, count);
107
TRACE_LOGBUF (buffer, count);
111
nwritten = _gpgme_ath_write (fd, buffer, count);
113
while (nwritten == -1 && errno == EINTR);
115
return TRACE_SYSRES (nwritten);
120
_gpgme_io_pipe (int filedes[2], int inherit_idx)
124
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
125
"inherit_idx=%i (GPGME uses it for %s)",
126
inherit_idx, inherit_idx ? "reading" : "writing");
128
err = pipe (filedes);
130
return TRACE_SYSRES (err);
132
/* FIXME: Should get the old flags first. */
133
err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
142
return TRACE_SYSRES (err);
144
return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
149
_gpgme_io_close (int fd)
153
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
158
return TRACE_SYSRES (-1);
161
/* First call the notify handler. */
162
if (fd >= 0 && fd < (int) DIM (notify_table))
164
if (notify_table[fd].handler)
166
TRACE_LOG2 ("invoking close handler %p/%p",
167
notify_table[fd].handler, notify_table[fd].value);
168
notify_table[fd].handler (fd, notify_table[fd].value);
169
notify_table[fd].handler = NULL;
170
notify_table[fd].value = NULL;
173
/* Then do the close. */
175
return TRACE_SYSRES (res);
180
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
183
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
184
"close_handler=%p/%p", handler, value);
188
if (fd < 0 || fd >= (int) DIM (notify_table))
191
return TRACE_SYSRES (-1);
193
notify_table[fd].handler = handler;
194
notify_table[fd].value = value;
195
return TRACE_SYSRES (0);
200
_gpgme_io_set_nonblocking (int fd)
204
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
206
flags = fcntl (fd, F_GETFL, 0);
208
return TRACE_SYSRES (-1);
210
res = fcntl (fd, F_SETFL, flags);
211
return TRACE_SYSRES (res);
225
rc = getrlimit (RLIMIT_NOFILE, &rl);
228
source = "RLIMIT_NOFILE";
237
rc = getrlimit (RLIMIT_OFILE, &rl);
240
source = "RLIMIT_OFILE";
249
scres = sysconf (_SC_OPEN_MAX);
252
source = "_SC_OPEN_MAX";
265
#if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
266
&& !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
267
#warning "No known way to get the maximum number of file descriptors."
271
source = "arbitrary";
272
/* Arbitrary limit. */
276
TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%i (%s)", fds, source);
282
_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
288
if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
290
if (WIFSIGNALED (status))
292
*r_status = 4; /* Need some value here. */
293
*r_signal = WTERMSIG (status);
295
else if (WIFEXITED (status))
296
*r_status = WEXITSTATUS (status);
298
*r_status = 4; /* Oops. */
305
/* Returns 0 on success, -1 on error. */
307
_gpgme_io_spawn (const char *path, char *const argv[],
308
struct spawn_fd_item_s *fd_list, pid_t *r_pid)
314
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
319
TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
322
for (i = 0; fd_list[i].fd != -1; i++)
323
if (fd_list[i].dup_to == -1)
324
TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
326
TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
330
return TRACE_SYSRES (-1);
334
/* Intermediate child to prevent zombie processes. */
335
if ((pid = fork ()) == 0)
337
int max_fds = get_max_fds ();
344
/* First close all fds which will not be inherited. */
345
for (fd = 0; fd < max_fds; fd++)
347
for (i = 0; fd_list[i].fd != -1; i++)
348
if (fd_list[i].fd == fd)
350
if (fd_list[i].fd == -1)
354
/* And now dup and close those to be duplicated. */
355
for (i = 0; fd_list[i].fd != -1; i++)
360
if (fd_list[i].dup_to != -1)
361
child_fd = fd_list[i].dup_to;
363
child_fd = fd_list[i].fd;
367
else if (child_fd == 2)
370
if (fd_list[i].dup_to == -1)
373
res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
377
/* FIXME: The debug file descriptor is not
378
dup'ed anyway, so we can't see this. */
379
TRACE_LOG1 ("dup2 failed in child: %s\n",
385
close (fd_list[i].fd);
388
if (! seen_stdin || ! seen_stderr)
390
fd = open ("/dev/null", O_RDWR);
394
/* FIXME: The debug file descriptor is not dup'ed
395
anyway, so we can't see this. */
396
TRACE_LOG1 ("can't open `/dev/null': %s\n",
401
/* Make sure that the process has a connected stdin. */
402
if (! seen_stdin && fd != 0)
404
if (dup2 (fd, 0) == -1)
407
/* FIXME: The debug file descriptor is not dup'ed
408
anyway, so we can't see this. */
409
TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
415
if (! seen_stderr && fd != 2)
416
if (dup2 (fd, 2) == -1)
419
/* FIXME: The debug file descriptor is not dup'ed
420
anyway, so we can't see this. */
421
TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
426
if (fd != 0 && fd != 2)
430
execv (path, (char *const *) argv);
431
/* Hmm: in that case we could write a special status code to the
434
/* FIXME: The debug file descriptor is not dup'ed anyway, so
435
we can't see this. */
436
TRACE_LOG1 ("exec of `%s' failed\n", path);
447
TRACE_LOG1 ("waiting for child process pid=%i", pid);
448
_gpgme_io_waitpid (pid, 1, &status, &signo);
450
return TRACE_SYSRES (-1);
452
for (i = 0; fd_list[i].fd != -1; i++)
454
_gpgme_io_close (fd_list[i].fd);
455
/* No handle translation. */
456
fd_list[i].peer_name = fd_list[i].fd;
462
return TRACE_SYSRES (0);
466
/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
467
nothing to select, > 0 = number of signaled fds. */
469
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
478
/* Use a 1s timeout. */
479
struct timeval timeout = { 1, 0 };
480
void *dbg_help = NULL;
481
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
482
"nfds=%u, nonblock=%u", nfds, nonblock);
490
TRACE_SEQ (dbg_help, "select on [ ");
493
for (i = 0; i < nfds; i++)
499
assert (!FD_ISSET (fds[i].fd, &readfds));
500
FD_SET (fds[i].fd, &readfds);
501
if (fds[i].fd > max_fd)
503
TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
506
else if (fds[i].for_write)
508
assert (!FD_ISSET (fds[i].fd, &writefds));
509
FD_SET (fds[i].fd, &writefds);
510
if (fds[i].fd > max_fd)
512
TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
517
TRACE_END (dbg_help, "]");
519
return TRACE_SYSRES (0);
523
count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
526
while (count < 0 && errno == EINTR);
528
return TRACE_SYSRES (-1);
530
TRACE_SEQ (dbg_help, "select OK [ ");
531
if (TRACE_ENABLED (dbg_help))
533
for (i = 0; i <= max_fd; i++)
535
if (FD_ISSET (i, &readfds))
536
TRACE_ADD1 (dbg_help, "r0x%x ", i);
537
if (FD_ISSET (i, &writefds))
538
TRACE_ADD1 (dbg_help, "w0x%x ", i);
540
TRACE_END (dbg_help, "]");
543
/* The variable N is used to optimize it a little bit. */
544
for (n = count, i = 0; i < nfds && n; i++)
548
else if (fds[i].for_read)
550
if (FD_ISSET (fds[i].fd, &readfds))
556
else if (fds[i].for_write)
558
if (FD_ISSET (fds[i].fd, &writefds))
565
return TRACE_SYSRES (count);
570
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
575
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
576
"msg=%p, flags=%i", msg, flags);
580
while (iov < msg->msg_iov + msg->msg_iovlen)
582
nread += iov->iov_len;
586
TRACE_LOG1 ("about to receive %d bytes", nread);
590
nread = _gpgme_ath_recvmsg (fd, msg, flags);
592
while (nread == -1 && errno == EINTR);
601
int len = nr > iov->iov_len ? iov->iov_len : nr;
602
TRACE_LOGBUF (msg->msg_iov->iov_base, len);
608
return TRACE_SYSRES (nread);
613
_gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
617
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
618
"msg=%p, flags=%i", msg, flags);
622
while (iov < msg->msg_iov + msg->msg_iovlen)
624
nwritten += iov->iov_len;
628
TRACE_LOG1 ("about to receive %d bytes", nwritten);
632
int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
633
TRACE_LOGBUF (msg->msg_iov->iov_base, len);
640
nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
642
while (nwritten == -1 && errno == EINTR);
643
return TRACE_SYSRES (nwritten);
648
_gpgme_io_dup (int fd)
650
int new_fd = dup (fd);
652
TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);