summaryrefslogtreecommitdiff
path: root/src/system-posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/system-posix.c')
-rw-r--r--src/system-posix.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/system-posix.c b/src/system-posix.c
new file mode 100644
index 0000000..e36767a
--- /dev/null
+++ b/src/system-posix.c
@@ -0,0 +1,339 @@
+/* system-posix.c - System support functions.
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This file is part of Assuan.
+
+ Assuan is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ Assuan is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+/* Solaris 8 needs sys/types.h before time.h. */
+#include <sys/types.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "assuan-defs.h"
+#include "debug.h"
+
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
+
+
+
+assuan_fd_t
+assuan_fdopen (int fd)
+{
+ return dup (fd);
+}
+
+
+
+/* Sleep for the given number of microseconds. Default
+ implementation. */
+void
+__assuan_usleep (assuan_context_t ctx, unsigned int usec)
+{
+ if (! usec)
+ return;
+
+#ifdef HAVE_NANOSLEEP
+ {
+ struct timespec req;
+ struct timespec rem;
+
+ req.tv_sec = 0;
+ req.tv_nsec = usec * 1000;
+
+ while (nanosleep (&req, &rem) < 0 && errno == EINTR)
+ req = rem;
+ }
+#else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ select (0, NULL, NULL, NULL, &tv);
+ }
+#endif
+}
+
+
+
+/* Create a pipe with one inheritable end. Easy for Posix. */
+int
+__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
+{
+ return pipe (fd);
+}
+
+
+
+/* Close the given file descriptor, created with _assuan_pipe or one
+ of the socket functions. Easy for Posix. */
+int
+__assuan_close (assuan_context_t ctx, assuan_fd_t fd)
+{
+ return close (fd);
+}
+
+
+
+static ssize_t
+__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
+{
+ return read (fd, buffer, size);
+}
+
+
+
+static ssize_t
+__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
+ size_t size)
+{
+ return write (fd, buffer, size);
+}
+
+
+
+static int
+__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
+ int flags)
+{
+ int ret;
+
+ do
+ ret = recvmsg (fd, msg, flags);
+ while (ret == -1 && errno == EINTR);
+
+ return ret;
+}
+
+
+
+static int
+__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
+ int flags)
+{
+ int ret;
+
+ do
+ ret = sendmsg (fd, msg, flags);
+ while (ret == -1 && errno == EINTR);
+
+ return ret;
+}
+
+
+
+static int
+writen (int fd, const char *buffer, size_t length)
+{
+ while (length)
+ {
+ int nwritten = write (fd, buffer, length);
+
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1; /* write error */
+ }
+ length -= nwritten;
+ buffer += nwritten;
+ }
+ return 0; /* okay */
+}
+
+
+int
+__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
+ const char **argv,
+ assuan_fd_t fd_in, assuan_fd_t fd_out,
+ assuan_fd_t *fd_child_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, unsigned int flags)
+{
+ int pid;
+
+ pid = fork ();
+ if (pid < 0)
+ return -1;
+
+ if (pid == 0)
+ {
+ /* Child process (server side). */
+ int i;
+ int n;
+ char errbuf[512];
+ int *fdp;
+ int fdnul;
+
+ if (atfork)
+ atfork (atforkvalue, 0);
+
+ fdnul = open ("/dev/null", O_WRONLY);
+ if (fdnul == -1)
+ {
+ TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
+ "can't open `/dev/null': %s", strerror (errno));
+ _exit (4);
+ }
+
+ /* Dup handles to stdin/stdout. */
+ if (fd_out != STDOUT_FILENO)
+ {
+ if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out,
+ STDOUT_FILENO) == -1)
+ {
+ TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
+ "dup2 failed in child: %s", strerror (errno));
+ _exit (4);
+ }
+ }
+
+ if (fd_in != STDIN_FILENO)
+ {
+ if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in,
+ STDIN_FILENO) == -1)
+ {
+ TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
+ "dup2 failed in child: %s", strerror (errno));
+ _exit (4);
+ }
+ }
+
+ /* Dup stderr to /dev/null unless it is in the list of FDs to be
+ passed to the child. */
+ fdp = fd_child_list;
+ if (fdp)
+ {
+ for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
+ ;
+ }
+ if (!fdp || *fdp == -1)
+ {
+ if (dup2 (fdnul, STDERR_FILENO) == -1)
+ {
+ TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx,
+ "dup2(dev/null, 2) failed: %s", strerror (errno));
+ _exit (4);
+ }
+ }
+ close (fdnul);
+
+ /* Close all files which will not be duped and are not in the
+ fd_child_list. */
+ n = sysconf (_SC_OPEN_MAX);
+ if (n < 0)
+ n = MAX_OPEN_FDS;
+ for (i = 0; i < n; i++)
+ {
+ if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO)
+ continue;
+ fdp = fd_child_list;
+ if (fdp)
+ {
+ while (*fdp != -1 && *fdp != i)
+ fdp++;
+ }
+
+ if (!(fdp && *fdp != -1))
+ close (i);
+ }
+ gpg_err_set_errno (0);
+
+ if (! name)
+ {
+ /* No name and no args given, thus we don't do an exec
+ but continue the forked process. */
+ *argv = "server";
+
+ /* FIXME: Cleanup. */
+ return 0;
+ }
+
+ execv (name, (char *const *) argv);
+
+ /* oops - use the pipe to tell the parent about it */
+ snprintf (errbuf, sizeof(errbuf)-1,
+ "ERR %d can't exec `%s': %.50s\n",
+ _assuan_error (ctx, GPG_ERR_ASS_SERVER_START),
+ name, strerror (errno));
+ errbuf[sizeof(errbuf)-1] = 0;
+ writen (1, errbuf, strlen (errbuf));
+ _exit (4);
+ }
+
+ if (! name)
+ *argv = "client";
+
+ *r_pid = pid;
+
+ return 0;
+}
+
+
+
+/* FIXME: Add some sort of waitpid function that covers GPGME and
+ gpg-agent's use of assuan. */
+static pid_t
+__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait,
+ int *status, int options)
+{
+ /* We can't just release the PID, a waitpid is mandatory. But
+ NOWAIT in POSIX systems just means the caller already did the
+ waitpid for this child. */
+ if (! nowait)
+ return waitpid (pid, NULL, 0);
+ return 0;
+}
+
+
+
+int
+__assuan_socketpair (assuan_context_t ctx, int namespace, int style,
+ int protocol, assuan_fd_t filedes[2])
+{
+ return socketpair (namespace, style, protocol, filedes);
+}
+
+
+
+/* The default system hooks for assuan contexts. */
+struct assuan_system_hooks _assuan_system_hooks =
+ {
+ ASSUAN_SYSTEM_HOOKS_VERSION,
+ __assuan_usleep,
+ __assuan_pipe,
+ __assuan_close,
+ __assuan_read,
+ __assuan_write,
+ __assuan_recvmsg,
+ __assuan_sendmsg,
+ __assuan_spawn,
+ __assuan_waitpid,
+ __assuan_socketpair
+ };