passfd: fix scoping bug
[gnulib.git] / lib / passfd.c
1 /* Copyright (C) 2011 Free Software Foundation, Inc.
2
3    This program is free software: you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 3 of the License, or
6    (at your option) any later version.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
15
16 #include <config.h>
17
18 /* Specification.  */
19 #include "passfd.h"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/uio.h>
28 #include <unistd.h>
29
30 #include <sys/socket.h>
31 #if HAVE_SYS_UN_H
32 # include <sys/un.h>
33 #endif
34
35 #include "cloexec.h"
36
37 #ifndef MSG_CMSG_CLOEXEC
38 # define MSG_CMSG_CLOEXEC 0
39 #endif
40
41 /* sendfd sends the file descriptor fd along the socket
42    to a process calling recvfd on the other end.
43
44    Return 0 on success, or -1 with errno set in case of error.
45 */
46 int
47 sendfd (int sock, int fd)
48 {
49   char send = 0;
50   struct iovec iov;
51   struct msghdr msg;
52 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
53   struct cmsghdr *cmsg;
54   char buf[CMSG_SPACE (sizeof fd)];
55 #endif
56
57   /* send at least one char */
58   memset (&msg, 0, sizeof msg);
59   iov.iov_base = &send;
60   iov.iov_len = 1;
61   msg.msg_iov = &iov;
62   msg.msg_iovlen = 1;
63   msg.msg_name = NULL;
64   msg.msg_namelen = 0;
65
66 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
67   msg.msg_control = buf;
68   msg.msg_controllen = sizeof buf;
69   cmsg = CMSG_FIRSTHDR (&msg);
70   cmsg->cmsg_level = SOL_SOCKET;
71   cmsg->cmsg_type = SCM_RIGHTS;
72   cmsg->cmsg_len = CMSG_LEN (sizeof fd);
73   /* Initialize the payload: */
74   memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);
75 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
76   msg.msg_accrights = &fd;
77   msg.msg_accrightslen = sizeof fd;
78 #else
79   errno = ENOSYS;
80   return -1;
81 #endif
82
83   if (sendmsg (sock, &msg, 0) != iov.iov_len)
84     return -1;
85   return 0;
86 }
87
88 /* recvfd receives a file descriptor through the socket.
89    The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>).
90
91    Return 0 on success, or -1 with errno set in case of error.
92 */
93 int
94 recvfd (int sock, int flags)
95 {
96   char recv = 0;
97   struct iovec iov;
98   struct msghdr msg;
99   int fd = -1;
100 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
101   struct cmsghdr *cmsg;
102   char buf[CMSG_SPACE (sizeof fd)];
103   int flags_recvmsg = flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0;
104 #endif
105
106   if ((flags & ~O_CLOEXEC) != 0)
107     {
108       errno = EINVAL;
109       return -1;
110     }
111
112   /* send at least one char */
113   memset (&msg, 0, sizeof msg);
114   iov.iov_base = &recv;
115   iov.iov_len = 1;
116   msg.msg_iov = &iov;
117   msg.msg_iovlen = 1;
118   msg.msg_name = NULL;
119   msg.msg_namelen = 0;
120
121 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
122   msg.msg_control = buf;
123   msg.msg_controllen = sizeof buf;
124   cmsg = CMSG_FIRSTHDR (&msg);
125   cmsg->cmsg_level = SOL_SOCKET;
126   cmsg->cmsg_type = SCM_RIGHTS;
127   cmsg->cmsg_len = CMSG_LEN (sizeof fd);
128   /* Initialize the payload: */
129   memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);
130   msg.msg_controllen = cmsg->cmsg_len;
131
132   if (recvmsg (sock, &msg, flags_recvmsg) < 0)
133     return -1;
134
135   cmsg = CMSG_FIRSTHDR (&msg);
136   /* be paranoiac */
137   if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof fd)
138       || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
139     {
140       /* fake errno: at end the file is not available */
141       errno = EACCES;
142       return -1;
143     }
144
145   memcpy (&fd, CMSG_DATA (cmsg), sizeof fd);
146
147   /* set close-on-exec flag */
148   if (!MSG_CMSG_CLOEXEC && (flags & O_CLOEXEC))
149     {
150       if (set_cloexec_flag (fd, true) < 0)
151         {
152           int saved_errno = errno;
153           (void) close (fd);
154           errno = saved_errno;
155           return -1;
156         }
157     }
158
159 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
160   msg.msg_accrights = &fd;
161   msg.msg_accrightslen = sizeof fd;
162   if (recvmsg (sock, &msg, 0) < 0)
163     return -1;
164
165   /* set close-on-exec flag */
166   if (flags & O_CLOEXEC)
167     {
168       if (set_cloexec_flag (fd, true) < 0)
169         {
170           int saved_errno = errno;
171           close (fd);
172           errno = saved_errno;
173           return -1;
174         }
175     }
176 #else
177   errno = ENOSYS;
178 #endif
179
180   return fd;
181 }