chiark / gitweb /
fishdescriptor: donate: some bugfixes
[chiark-utils.git] / fishdescriptor / donate.c
1 /*
2  * Copyright (C) 2009 Citrix Ltd.
3  * Copyright (C) 2017 Citrix Ltd.
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdlib.h>
20 #include <errno.h>
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25
26 static int fishdescriptor_sendmsg_fds(int carrier,
27                                       int nfds, const int fds[]) {
28   /* return convention: like a syscall */
29   struct msghdr msg = { 0 };
30   struct cmsghdr *cmsg;
31   size_t spaceneeded = nfds * sizeof(fds[0]);
32   char control[CMSG_SPACE(spaceneeded)];
33   struct iovec iov;
34   int r;
35
36   iov.iov_base = &nfds;
37   iov.iov_len  = sizeof(nfds);
38
39   /* compose the message */
40   msg.msg_iov = &iov;
41   msg.msg_iovlen = 1;
42   msg.msg_control = control;
43   msg.msg_controllen = sizeof(control);
44
45   /* attach open fd */
46   cmsg = CMSG_FIRSTHDR(&msg);
47   cmsg->cmsg_level = SOL_SOCKET;
48   cmsg->cmsg_type = SCM_RIGHTS;
49   cmsg->cmsg_len = CMSG_LEN(spaceneeded);
50   memcpy(CMSG_DATA(cmsg), fds, spaceneeded);
51
52   msg.msg_controllen = cmsg->cmsg_len;
53     
54   r = sendmsg(carrier, &msg, 0);
55   if (r < 0) return -1;
56
57   return 0;
58 }
59
60 int fishdescriptor_donate(const char *path, const int *fds) {
61   /* return convention: returns errno value */
62   /* within function: r is like syscall return, errno value is in errno */
63   int r;
64   int esave = errno;
65   int carrier=-1;
66
67   carrier = socket(AF_UNIX, SOCK_STREAM, 0);
68   if (carrier < 0) goto out;
69
70   struct sockaddr_un suna;
71   memset(&suna,0,sizeof(suna));
72   suna.sun_family = AF_UNIX;
73   if (strlen(path) >= sizeof(suna.sun_path)) { errno = E2BIG; goto out; }
74   strcpy(suna.sun_path, path);
75
76   r = connect(carrier, (const struct sockaddr*)&suna, sizeof(suna));
77   if (r) goto out;
78
79   int nfds;
80   for (nfds=0; fds[nfds] > 0; nfds++);
81
82   r = fishdescriptor_sendmsg_fds(carrier, nfds, fds);
83   if (r) goto out;
84
85   r = 0;
86  out:
87   if (carrier >= 0) close(carrier);
88   r = !r ? 0 : !errno ? EIO : errno;
89   esave = errno;
90   return r;
91 }