1 /* posix-io.c - Posix I/O functions
2 Copyright (C) 2000 Werner Koch (dd9jn)
3 Copyright (C) 2001, 2002 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 General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (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 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GPGME; if not, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
33 #include <sys/types.h>
44 void (*handler) (int,void*);
49 _gpgme_io_read (int fd, void *buffer, size_t count)
53 DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int) count);
56 nread = _gpgme_ath_read (fd, buffer, count);
58 while (nread == -1 && errno == EINTR );
59 DEBUG2 ("fd %d: got %d bytes\n", fd, nread);
61 _gpgme_debug (2, "fd %d: got `%.*s'\n", fd, nread, buffer);
67 _gpgme_io_write (int fd, const void *buffer, size_t count)
71 DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int) count);
72 _gpgme_debug (2, "fd %d: write `%.*s'\n", fd, (int) count, buffer);
75 nwritten = _gpgme_ath_write (fd, buffer, count);
77 while (nwritten == -1 && errno == EINTR);
78 DEBUG2 ("fd %d: wrote %d bytes\n", fd, (int) nwritten);
83 _gpgme_io_pipe (int filedes[2], int inherit_idx)
90 /* FIXME: Should get the old flags first. */
91 err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
102 _gpgme_io_close (int fd)
106 /* First call the notify handler. */
107 DEBUG1 ("closing fd %d", fd);
108 if (fd >= 0 && fd < DIM (notify_table))
110 if (notify_table[fd].handler)
112 notify_table[fd].handler (fd, notify_table[fd].value);
113 notify_table[fd].handler = NULL;
114 notify_table[fd].value = NULL;
117 /* Then do the close. */
123 _gpgme_io_set_close_notify (int fd, void (*handler)(int, void*), void *value)
127 if (fd < 0 || fd >= DIM (notify_table))
129 DEBUG1 ("set notification for fd %d", fd);
130 notify_table[fd].handler = handler;
131 notify_table[fd].value = value;
137 _gpgme_io_set_nonblocking (int fd)
141 flags = fcntl (fd, F_GETFL, 0);
145 return fcntl (fd, F_SETFL, flags);
149 /* Returns 0 on success, -1 on error. */
151 _gpgme_io_spawn (const char *path, char **argv,
152 struct spawn_fd_item_s *fd_child_list,
153 struct spawn_fd_item_s *fd_parent_list)
155 static int fixed_signals;
156 DEFINE_STATIC_LOCK (fixed_signals_lock);
161 LOCK (fixed_signals_lock);
164 struct sigaction act;
166 sigaction (SIGPIPE, NULL, &act);
167 if (act.sa_handler == SIG_DFL)
169 act.sa_handler = SIG_IGN;
170 sigemptyset (&act.sa_mask);
172 sigaction (SIGPIPE, &act, NULL);
176 UNLOCK (fixed_signals_lock);
184 /* Intermediate child to prevent zombie processes. */
185 if ((pid = fork ()) == 0)
189 int duped_stderr = 0;
191 /* First close all fds which will not be duped. */
192 for (i=0; fd_child_list[i].fd != -1; i++)
193 if (fd_child_list[i].dup_to == -1)
194 close (fd_child_list[i].fd);
196 /* And now dup and close the rest. */
197 for (i=0; fd_child_list[i].fd != -1; i++)
199 if (fd_child_list[i].dup_to != -1)
201 if (dup2 (fd_child_list[i].fd,
202 fd_child_list[i].dup_to) == -1)
204 DEBUG1 ("dup2 failed in child: %s\n", strerror (errno));
207 if (fd_child_list[i].dup_to == 0)
209 if (fd_child_list[i].dup_to == 2)
211 close (fd_child_list[i].fd);
215 if (!duped_stdin || !duped_stderr)
217 int fd = open ("/dev/null", O_RDWR);
220 DEBUG1 ("can't open `/dev/null': %s\n", strerror (errno));
223 /* Make sure that the process has a connected stdin. */
226 if (dup2 (fd, 0) == -1)
228 DEBUG1("dup2(/dev/null, 0) failed: %s\n",
234 if (dup2 (fd, 2) == -1)
236 DEBUG1 ("dup2(dev/null, 2) failed: %s\n", strerror (errno));
242 execv ( path, argv );
243 /* Hmm: in that case we could write a special status code to the
245 DEBUG1 ("exec of `%s' failed\n", path);
254 _gpgme_io_waitpid (pid, 1, &status, &signo);
258 /* .dup_to is not used in the parent list. */
259 for (i = 0; fd_parent_list[i].fd != -1; i++)
260 _gpgme_io_close (fd_parent_list[i].fd);
267 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
273 if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
275 if (WIFSIGNALED (status))
277 *r_status = 4; /* Need some value here. */
278 *r_signal = WTERMSIG (status);
280 else if (WIFEXITED (status))
281 *r_status = WEXITSTATUS (status);
283 *r_status = 4; /* Oops. */
291 _gpgme_io_kill (int pid, int hard)
293 return kill (pid, hard ? SIGKILL : SIGTERM);
298 * Select on the list of fds.
299 * Returns: -1 = error
300 * 0 = timeout or nothing to select
301 * >0 = number of signaled fds
304 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
308 int any, i, max_fd, n, count;
309 struct timeval timeout = { 1, 0 }; /* Use a 1s timeout. */
310 void *dbg_help = NULL;
318 DEBUG_BEGIN (dbg_help, 3, "gpgme:select on [ ");
320 for (i = 0; i < nfds; i++)
325 DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd);
326 else if (fds[i].for_read)
328 assert (!FD_ISSET (fds[i].fd, &readfds));
329 FD_SET (fds[i].fd, &readfds);
330 if (fds[i].fd > max_fd)
332 DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd);
335 else if (fds[i].for_write)
337 assert (!FD_ISSET (fds[i].fd, &writefds));
338 FD_SET (fds[i].fd, &writefds);
339 if (fds[i].fd > max_fd)
341 DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd);
346 DEBUG_END (dbg_help, "]");
352 count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL, &timeout);
354 while (count < 0 && errno == EINTR);
357 DEBUG1 ("_gpgme_io_select failed: %s\n", strerror (errno));
358 return -1; /* error */
361 DEBUG_BEGIN (dbg_help, 3, "select OK [ ");
362 if (DEBUG_ENABLED (dbg_help))
364 for (i = 0; i <= max_fd; i++)
366 if (FD_ISSET (i, &readfds))
367 DEBUG_ADD1 (dbg_help, "r%d ", i);
368 if (FD_ISSET (i, &writefds))
369 DEBUG_ADD1 (dbg_help, "w%d ", i);
371 DEBUG_END (dbg_help, "]");
374 /* n is used to optimize it a little bit. */
375 for (n = count, i = 0; i < nfds && n; i++)
379 else if (fds[i].for_read)
381 if (FD_ISSET (fds[i].fd, &readfds))
387 else if (fds[i].for_write)
389 if (FD_ISSET (fds[i].fd, &writefds))