passfd: allow compilation on mingw
[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 #ifndef MSG_CMSG_CLOEXEC
37 # define MSG_CMSG_CLOEXEC 0
38 #endif
39
40 #if HAVE_SENDMSG
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 #else
88 int
89 sendfd (int sock _GL_UNUSED, int fd _GL_UNUSED)
90 {
91   errno = ENOSYS;
92   return -1;
93 }
94 #endif
95
96
97 #if HAVE_RECVMSG
98 /* recvfd receives a file descriptor through the socket.
99    The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>).
100
101    Return 0 on success, or -1 with errno set in case of error.
102 */
103 int
104 recvfd (int sock, int flags)
105 {
106   char recv = 0;
107   struct iovec iov;
108   struct msghdr msg;
109   int fd = -1;
110 # if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
111   struct cmsghdr *cmsg;
112   char buf[CMSG_SPACE (sizeof fd)];
113   int flags_recvmsg = flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0;
114 # endif
115
116   if ((flags & ~O_CLOEXEC) != 0)
117     {
118       errno = EINVAL;
119       return -1;
120     }
121
122   /* send at least one char */
123   memset (&msg, 0, sizeof msg);
124   iov.iov_base = &recv;
125   iov.iov_len = 1;
126   msg.msg_iov = &iov;
127   msg.msg_iovlen = 1;
128   msg.msg_name = NULL;
129   msg.msg_namelen = 0;
130
131 # if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
132   msg.msg_control = buf;
133   msg.msg_controllen = sizeof buf;
134   cmsg = CMSG_FIRSTHDR (&msg);
135   cmsg->cmsg_level = SOL_SOCKET;
136   cmsg->cmsg_type = SCM_RIGHTS;
137   cmsg->cmsg_len = CMSG_LEN (sizeof fd);
138   /* Initialize the payload: */
139   memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);
140   msg.msg_controllen = cmsg->cmsg_len;
141
142   if (recvmsg (sock, &msg, flags_recvmsg) < 0)
143     return -1;
144
145   cmsg = CMSG_FIRSTHDR (&msg);
146   /* be paranoiac */
147   if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof fd)
148       || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
149     {
150       /* fake errno: at end the file is not available */
151       errno = EACCES;
152       return -1;
153     }
154
155   memcpy (&fd, CMSG_DATA (cmsg), sizeof fd);
156
157   /* set close-on-exec flag */
158   if (!MSG_CMSG_CLOEXEC && (flags & O_CLOEXEC))
159     {
160       if (set_cloexec_flag (fd, true) < 0)
161         {
162           int saved_errno = errno;
163           (void) close (fd);
164           errno = saved_errno;
165           return -1;
166         }
167     }
168
169 # elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
170   msg.msg_accrights = &fd;
171   msg.msg_accrightslen = sizeof fd;
172   if (recvmsg (sock, &msg, 0) < 0)
173     return -1;
174
175   /* set close-on-exec flag */
176   if (flags & O_CLOEXEC)
177     {
178       if (set_cloexec_flag (fd, true) < 0)
179         {
180           int saved_errno = errno;
181           close (fd);
182           errno = saved_errno;
183           return -1;
184         }
185     }
186 # else
187   errno = ENOSYS;
188 # endif
189
190   return fd;
191 }
192 #else
193 int
194 recvfd (int sock _GL_UNUSED, int flags _GL_UNUSED)
195 {
196   errno = ENOSYS;
197   return -1;
198 }
199 #endif