passfd module, part 3.
[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 <unistd.h>
28
29 #include <sys/socket.h>
30 #if HAVE_SYS_UN_H
31 # include <sys/un.h>
32 #endif
33
34 #include "cloexec.h"
35
36 /* sendfd sends the file descriptor fd along the socket
37    to a process calling recvfd on the other end.
38
39    Return 0 on success, or -1 with errno set in case of error.
40 */
41 int
42 sendfd (int sock, int fd)
43 {
44   char send = 0;
45   struct iovec iov[1];
46   struct msghdr msg;
47
48   /* send at least one char */
49   iov[0].iov_base = &send;
50   iov[0].iov_len = 1;
51   msg.msg_iov = iov;
52   msg.msg_iovlen = 1;
53   msg.msg_name = 0;
54   msg.msg_namelen = 0;
55
56   {
57 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
58     struct cmsghdr *cmsg;
59     char buf[CMSG_SPACE (sizeof (fd))];
60
61     msg.msg_control = buf;
62     msg.msg_controllen = sizeof (buf);
63     cmsg = CMSG_FIRSTHDR (&msg);
64     cmsg->cmsg_level = SOL_SOCKET;
65     cmsg->cmsg_type = SCM_RIGHTS;
66     cmsg->cmsg_len = CMSG_LEN (sizeof (int));
67     /* Initialize the payload: */
68     memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
69     msg.msg_controllen = cmsg->cmsg_len;
70 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
71     msg.msg_accrights = &fd;
72     msg.msg_accrightslen = sizeof (fd);
73 #else
74     errno = ENOSYS;
75     return -1;
76 #endif
77   }
78
79   if (sendmsg (sock, &msg, 0) != iov[0].iov_len)
80     return -1;
81   return 0;
82 }
83
84 /* recvfd receives a file descriptor through the socket.
85    The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>).
86
87    Return 0 on success, or -1 with errno set in case of error.
88 */
89 int
90 recvfd (int sock, int flags)
91 {
92   char recv = 0;
93   struct iovec iov[1];
94   struct msghdr msg;
95
96   if ((flags & ~O_CLOEXEC) != 0)
97     {
98       errno = EINVAL;
99       return -1;
100     }
101
102   /* send at least one char */
103   iov[0].iov_base = &recv;
104   iov[0].iov_len = 1;
105   msg.msg_iov = iov;
106   msg.msg_iovlen = 1;
107   msg.msg_name = 0;
108   msg.msg_namelen = 0;
109
110   {
111 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
112     int fd;
113     struct cmsghdr *cmsg;
114     char buf[CMSG_SPACE (sizeof (fd))];
115     const int mone = -1;
116 # if HAVE_MSG_CMSG_CLOEXEC
117     int flags_recvmsg = (flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0);
118 # else
119     int flags_recvmsg = 0;
120 # endif
121
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 (int));
128     /* Initialize the payload: */
129     memcpy (CMSG_DATA (cmsg), &mone, sizeof (mone));
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 (int))
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 # if !HAVE_MSG_CMSG_CLOEXEC
148     /* set close-on-exec flag */
149     if (flags & O_CLOEXEC)
150       {
151         if (set_cloexec_flag (fd, true) < 0)
152           {
153             int saved_errno = errno;
154             (void) close (fd);
155             errno = saved_errno;
156             return -1;
157           }
158       }
159 # endif
160
161     return fd;
162
163 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
164     int fd;
165
166     msg.msg_accrights = &fd;
167     msg.msg_accrightslen = sizeof (fd);
168     if (recvmsg (sock, &msg, 0) < 0)
169       return -1;
170
171     /* set close-on-exec flag */
172     if (flags & O_CLOEXEC)
173       {
174         if (set_cloexec_flag (fd, true) < 0)
175           {
176             int saved_errno = errno;
177             (void) close (fd);
178             errno = saved_errno;
179             return -1;
180           }
181       }
182
183     return fd;
184
185 #else
186     errno = ENOSYS;
187     return -1;
188 #endif
189   }
190 }