passfd module, part 2, tweaks.
[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 <stddef.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include <sys/socket.h>
29 #if HAVE_SYS_UN_H
30 # include <sys/un.h>
31 #endif
32
33 /* sendfd sends the file descriptor fd along the socket
34    to a process calling recvfd on the other end.
35
36    Return 0 on success, or -1 with errno set in case of error.
37 */
38 int
39 sendfd (int sock, int fd)
40 {
41   char send = 0;
42   struct iovec iov[1];
43   struct msghdr msg;
44
45   /* send at least one char */
46   iov[0].iov_base = &send;
47   iov[0].iov_len = 1;
48   msg.msg_iov = iov;
49   msg.msg_iovlen = 1;
50   msg.msg_name = 0;
51   msg.msg_namelen = 0;
52
53   {
54 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
55     struct cmsghdr *cmsg;
56     char buf[CMSG_SPACE (sizeof (fd))];
57
58     msg.msg_control = buf;
59     msg.msg_controllen = sizeof (buf);
60     cmsg = CMSG_FIRSTHDR (&msg);
61     cmsg->cmsg_level = SOL_SOCKET;
62     cmsg->cmsg_type = SCM_RIGHTS;
63     cmsg->cmsg_len = CMSG_LEN (sizeof (int));
64     /* Initialize the payload: */
65     memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
66     msg.msg_controllen = cmsg->cmsg_len;
67 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
68     msg.msg_accrights = &fd;
69     msg.msg_accrightslen = sizeof (fd);
70 #else
71     errno = ENOSYS;
72     return -1;
73 #endif
74   }
75
76   if (sendmsg (sock, &msg, 0) != iov[0].iov_len)
77     return -1;
78   return 0;
79 }
80
81 /* recvfd receives a file descriptor through the socket.
82
83    Return 0 on success, or -1 with errno set in case of error.
84 */
85 int
86 recvfd (int sock)
87 {
88   char recv = 0;
89   struct iovec iov[1];
90   struct msghdr msg;
91
92   /* send at least one char */
93   iov[0].iov_base = &recv;
94   iov[0].iov_len = 1;
95   msg.msg_iov = iov;
96   msg.msg_iovlen = 1;
97   msg.msg_name = 0;
98   msg.msg_namelen = 0;
99
100   {
101 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
102     int fd;
103     struct cmsghdr *cmsg;
104     char buf[CMSG_SPACE (sizeof (fd))];
105     const int mone = -1;
106
107     msg.msg_control = buf;
108     msg.msg_controllen = sizeof (buf);
109     cmsg = CMSG_FIRSTHDR (&msg);
110     cmsg->cmsg_level = SOL_SOCKET;
111     cmsg->cmsg_type = SCM_RIGHTS;
112     cmsg->cmsg_len = CMSG_LEN (sizeof (int));
113     /* Initialize the payload: */
114     memcpy (CMSG_DATA (cmsg), &mone, sizeof (mone));
115     msg.msg_controllen = cmsg->cmsg_len;
116
117     if (recvmsg (sock, &msg, 0) < 0)
118       return -1;
119
120     cmsg = CMSG_FIRSTHDR (&msg);
121     /* be paranoiac */
122     if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof (int))
123         || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
124       {
125         /* fake errno: at end the file is not available */
126         errno = EACCES;
127         return -1;
128       }
129
130     memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
131     return fd;
132 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
133     int fd;
134
135     msg.msg_accrights = &fd;
136     msg.msg_accrightslen = sizeof (fd);
137     if (recvmsg (sock, &msg, 0) < 0)
138       return -1;
139     return fd;
140 #else
141     errno = ENOSYS;
142     return -1;
143 #endif
144   }
145 }