LCOV - code coverage report
Current view: top level - src - system-posix.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 73 126 57.9 %
Date: 2016-12-21 11:14:47 Functions: 10 15 66.7 %

          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             :   };

Generated by: LCOV version 1.12