Set the close-on-exec flag for CAPS client descriptors.
[dragonfly.git] / lib / libcaps / caps_client.c
1 /*
2  * CAPS_CLIENT.C
3  *
4  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $DragonFly: src/lib/libcaps/Attic/caps_client.c,v 1.2 2003/11/24 21:32:33 dillon Exp $
29  */
30 #include "defs.h"
31
32 static lwkt_msg_t caps_client_kev(struct kevent *kev);
33
34 static int caps_client_putport(lwkt_port_t port, lwkt_msg_t msg);
35 static void *caps_client_waitport(lwkt_port_t port, lwkt_msg_t msg);
36 static void caps_client_replyport(lwkt_port_t port, lwkt_msg_t msg);
37
38 /*
39  * Connect to a remote service message port.
40  *
41  * Use the standard lwkt_*() messaging functions to send messages and wait
42  * for replies.
43  *
44  * This code will eventually be replaced by a kernel API
45  */
46 caps_port_t
47 caps_client(const char *name, uid_t uid, int flags)
48 {
49     struct sockaddr_un sunix;
50     struct kevent kev;
51     caps_port_t port;
52     int len;
53     int error;
54
55     /*
56      * If uid is -1 first try our current uid, then try uid 0
57      */
58     if (uid == (uid_t)-1) {
59         uid = getuid();
60         if ((port = caps_client(name, uid, flags)) != NULL)
61             return(port);
62         uid = 0;
63     }
64
65     /*
66      * Allocate a port to handle incoming messages
67      */
68     port = caps_mkport(CAPT_CLIENT, 
69                         caps_client_putport,
70                         caps_client_waitport,
71                         caps_client_replyport);
72
73     /*
74      * Construct the unix domain socket.  If uid is -1 attempt to connect
75      * to the services UID for this process (use VARSYM variable?). XXX
76      */
77
78     if ((port->cfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
79         goto failed;
80     bzero(&sunix, sizeof(sunix));
81     sunix.sun_family = AF_UNIX;
82     if (uid == 0) {
83         snprintf(sunix.sun_path, sizeof(sunix.sun_path), 
84                     CAPS_PATH1, name);
85     } else {
86         snprintf(sunix.sun_path, sizeof(sunix.sun_path),
87                     CAPS_PATH2, (int)uid, name);
88     }
89     len = strlen(sunix.sun_path);
90
91     error = connect(port->cfd, (void *)&sunix,
92                     offsetof(struct sockaddr_un, sun_path[len+1]));
93     if (error)
94         goto failed;
95
96     if ((port->kqfd = kqueue()) < 0)
97         goto failed;
98
99     /*
100      * The server expects a cred immediately.
101      */
102     {
103         struct msghdr msghdr;
104         struct caps_creds_cmsg cmsg;
105
106         bzero(&msghdr, sizeof(msghdr));
107         bzero(&cmsg, sizeof(cmsg));
108         msghdr.msg_control = &cmsg;
109         msghdr.msg_controllen = sizeof(cmsg);
110         cmsg.cmsg.cmsg_len = sizeof(cmsg);
111         cmsg.cmsg.cmsg_level = SOL_SOCKET;
112         cmsg.cmsg.cmsg_type = SCM_CREDS;
113         if (sendmsg(port->cfd, &msghdr, 0) < 0) {
114             perror("sendmsg");
115             goto failed;
116         }
117     }
118     fcntl(port->cfd, F_SETFL, O_NONBLOCK);
119     fcntl(port->cfd, F_SETFD, FD_CLOEXEC);
120     EV_SET(&kev, port->cfd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, port);
121     if (kevent(port->kqfd, &kev, 1, NULL, 0, NULL) < 0)
122         goto failed;
123     DBPRINTF(("Successfully created port %s\n", sunix.sun_path));
124     return(port);
125 failed:
126     caps_close(port);
127     return(NULL);
128 }
129
130 /*
131  * Service kqueue events on a client port.
132  */
133 static lwkt_msg_t
134 caps_client_kev(struct kevent *kev)
135 {
136     caps_port_t port = kev->udata;
137     lwkt_msg_t msg;
138
139     if (port->type == CAPT_CLIENT) {
140         if (kev->filter == EVFILT_WRITE)
141                 caps_kev_write(port, NULL);
142         if (kev->filter == EVFILT_READ) {
143                 msg = caps_kev_read(port);
144         } else {
145                 msg = NULL;
146         }
147     } else {
148         assert(0);
149         msg = NULL;
150     }
151     return(msg);
152 }
153
154 /*
155  * You can only reply to messages received on a services port.  You cannot
156  * send a message to a services port (where would we send it?)
157  */
158 static
159 int
160 caps_client_putport(lwkt_port_t port, lwkt_msg_t msg)
161 {
162     msg->ms_flags &= ~(MSGF_DONE | MSGF_REPLY);
163     caps_kev_write((caps_port_t)port, msg);
164     return(EASYNC);
165 }
166
167 /*
168  * Temporary hack until LWKT threading is integrated, because until then
169  * we can't wait on a normal LWKT port (the client's reply port)
170  */
171 void *
172 caps_client_waitreply(caps_port_t port, lwkt_msg_t msg)
173 {
174     return(caps_client_waitport(&port->lport, msg));
175 }
176
177 /*
178  * Wait for a new message to arrive.  At the moment we expect only replies
179  * to previously sent messagse to be received.
180  */
181 static
182 void *
183 caps_client_waitport(lwkt_port_t lport, lwkt_msg_t wmsg)
184 {
185     caps_port_t port;
186     lwkt_msg_t msg;
187
188     /*
189      * Wait for any message or a particular message.  If the message
190      * is available dequeue and return it.
191      */
192     if (wmsg == NULL)
193         wmsg = TAILQ_FIRST(&lport->mp_msgq);
194     if (wmsg && (wmsg->ms_flags & MSGF_DONE)) {
195         if (wmsg->ms_flags & MSGF_QUEUED) {
196             TAILQ_REMOVE(&lport->mp_msgq, wmsg, ms_node);
197             wmsg->ms_flags &= ~MSGF_QUEUED;
198         }
199         return(wmsg);
200     }
201
202     /*
203      * Wait for any message or a particular message which is not yet
204      * available.
205      */
206     port = (caps_port_t)lport;
207     for (;;) {
208         struct kevent kev;
209
210         if (kevent(port->kqfd, NULL, 0, &kev, 1, NULL) <= 0) {
211             if (errno == EINTR)
212                 continue;
213             msg = NULL;
214             break;
215         }
216         msg = caps_client_kev(&kev);
217         if (msg != NULL) {
218             assert(msg->ms_flags & MSGF_DONE);
219             /*
220              * Return the message we are looking for
221              */
222             if (msg == wmsg || wmsg == NULL) {
223                 break;
224             }
225             /*
226              * Or save it for later retrieval
227              */
228             TAILQ_INSERT_TAIL(&lport->mp_msgq, msg, ms_node);
229             msg->ms_flags |= MSGF_QUEUED;
230         }
231     }
232     return(msg);
233 }
234
235 /*
236  * A message's reply port is set to the port representing the connection
237  * the message came in on, not set to the services port.  There should be
238  * no replies made to the services port.
239  */
240 static
241 void
242 caps_client_replyport(lwkt_port_t port, lwkt_msg_t msg)
243 {
244     assert(0);
245 }
246