From FreeBSD:
[dragonfly.git] / lib / libcaps / caps_service.c
1 /*
2  * CAPS_SERVICE.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_service.c,v 1.1 2003/11/24 21:15:58 dillon Exp $
29  */
30
31 #include "defs.h"
32
33 static lwkt_msg_t caps_service_kev(struct kevent *kev);
34
35 static int caps_service_putport(lwkt_port_t port, lwkt_msg_t msg);
36 static void *caps_service_waitport(lwkt_port_t port, lwkt_msg_t msg);
37 static void caps_service_replyport(lwkt_port_t port, lwkt_msg_t msg);
38
39 static int caps_remote_putport(lwkt_port_t port, lwkt_msg_t msg);
40 static void *caps_remote_waitport(lwkt_port_t port, lwkt_msg_t msg);
41 static void caps_remote_replyport(lwkt_port_t port, lwkt_msg_t msg);
42
43 /*
44  * Create a message port rendezvous at the specified service name.
45  *
46  * Use the standard lwkt_*() messaging functions to retrieve and reply to
47  * messages sent to your port.
48  *
49  * This code will eventually be replaced by a kernel API
50  */
51 caps_port_t
52 caps_service(const char *name, gid_t gid, mode_t modes, int flags)
53 {
54     struct sockaddr_un sunix;
55     struct kevent kev;
56     caps_port_t port;
57     mode_t omask;
58     int len;
59     int error;
60     uid_t uid;
61
62     /*
63      * Allocate a port to handle incoming messages
64      */
65     port = caps_mkport(CAPT_SERVICE, 
66                         caps_service_putport,
67                         caps_service_waitport,
68                         caps_service_replyport);
69     uid = getuid();
70
71     /*
72      * Construct the unix domain socket.
73      */
74     if ((port->lfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
75         goto failed;
76     bzero(&sunix, sizeof(sunix));
77     sunix.sun_family = AF_UNIX;
78     if (uid == 0) {
79         snprintf(sunix.sun_path, sizeof(sunix.sun_path), 
80                     CAPS_PATH1, name);
81     } else {
82         snprintf(sunix.sun_path, sizeof(sunix.sun_path),
83                     CAPS_PATH2, (int)uid, name);
84     }
85     len = strlen(sunix.sun_path);
86
87     /*
88      * If CAPS_EXCL is set do not allow duplicate registrations.
89      */
90     if (flags & CAPS_EXCL) {
91         error = connect(port->lfd, (void *)&sunix,
92                 offsetof(struct sockaddr_un, sun_path[len+1]));
93         if (error == 0) {
94             errno = EEXIST;
95             goto failed;
96         }
97     }
98
99     /*
100      * Bind and listen on the port.  Note: if the unix domain file
101      * is on a read-only filesystem the remove may fail.
102      */
103     remove(sunix.sun_path);     /* XXX use create/rename for atomicy */
104     omask = umask(~modes & 0x0777);
105     error = bind(port->lfd, (void *)&sunix,
106                 offsetof(struct sockaddr_un, sun_path[len+1]));
107     umask(omask);
108     if (error < 0)
109         goto failed;
110     fcntl(port->lfd, F_SETFL, O_NONBLOCK);
111     if ((error = listen(port->lfd, 128)) < 0)
112         goto failed;
113     if ((port->kqfd = kqueue()) < 0)
114         goto failed;
115
116     /*
117      * Use kqueue to get accept events on the listen descriptor
118      */
119     EV_SET(&kev, port->lfd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, port);
120     if (kevent(port->kqfd, &kev, 1, NULL, 0, NULL) < 0)
121         goto failed;
122     DBPRINTF(("Successfully created port %s\n", sunix.sun_path));
123     return(port);
124 failed:
125     caps_close(port);
126     return(NULL);
127 }
128
129 /*
130  * Service kqueue events on a services port.  Note that ports representing
131  * individual client connections aggregate their events onto the main services
132  * port.
133  */
134 static lwkt_msg_t
135 caps_service_kev(struct kevent *kev)
136 {
137     caps_port_t port = kev->udata;
138     lwkt_msg_t msg;
139
140     if (port->type == CAPT_SERVICE && kev->filter == EVFILT_READ) {
141         /*
142          * Accept a new connection on the master service port
143          */
144         int fd = accept(port->lfd, NULL, 0);
145         caps_port_t rport;
146
147         if (fd >= 0) {
148             rport = caps_mkport(CAPT_REMOTE,
149                             caps_remote_putport,
150                             caps_remote_waitport,
151                             caps_remote_replyport);
152             rport->flags |= CAPPF_WAITCRED | CAPPF_ONLIST;
153             rport->cfd = fd;
154             rport->server = port;
155             fcntl(port->cfd, F_SETFL, O_NONBLOCK);
156             /* note: use rport's mp_refs (1) to indicate ONLIST */
157             TAILQ_INSERT_TAIL(&port->clist, rport, centry);
158             EV_SET(kev, fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, rport);
159             if (kevent(port->kqfd, kev, 1, NULL, 0, NULL) < 0)
160                 caps_shutdown(rport);
161             else
162                 DBPRINTF(("accepted %d\n", fd));
163         }
164         msg = NULL;
165     } else if (port->type == CAPT_REMOTE && kev->filter == EVFILT_READ) {
166         msg = caps_kev_read(port);
167     } else if (port->type == CAPT_REMOTE && kev->filter == EVFILT_WRITE) {
168         caps_kev_write(port, NULL);
169         msg = NULL;
170     } else {
171         fprintf(stderr, "bad port: type %d flags %d\n", port->type, kev->filter);
172         assert(0);
173         msg = NULL;
174     }
175     return(msg);
176 }
177
178
179 /*
180  * You can only reply to messages received on a services port.  You cannot
181  * send a message to a services port (where would we send it?)
182  */
183 static
184 int
185 caps_service_putport(lwkt_port_t port, lwkt_msg_t msg)
186 {
187     return(EINVAL);
188 }
189
190 /*
191  * Wait for a new message to arrive.  Since messages are not replied to
192  * services port msg must be NULL.
193  */
194 static
195 void *
196 caps_service_waitport(lwkt_port_t lport, lwkt_msg_t msg)
197 {
198     caps_port_t port;
199
200     if (msg)
201         return(NULL);
202     if ((msg = TAILQ_FIRST(&lport->mp_msgq)) != NULL) {
203         TAILQ_REMOVE(&lport->mp_msgq, msg, ms_node);
204         return(msg);
205     }
206     port = (caps_port_t)lport;
207     do {
208         struct kevent kev;
209         while (kevent(port->kqfd, NULL, 0, &kev, 1, NULL) > 0) {
210             if ((msg = (lwkt_msg_t)caps_service_kev(&kev)) != NULL)
211                 return(msg);
212         }
213     } while (errno == EINTR);
214     return(NULL);
215 }
216
217 /*
218  * A message's reply port is set to the port representing the connection
219  * the message came in on, not set to the services port.  There should be
220  * no replies made to the services port.
221  */
222 static
223 void
224 caps_service_replyport(lwkt_port_t port, lwkt_msg_t msg)
225 {
226     assert(0);
227 }
228
229 /*
230  * You can only reply to messages received on a services remote port. 
231  * You cannot send a message to a services remote port.   XXX future 
232  * feature should allow the sending of unsolicited messagse to the remote
233  * client.
234  */
235 static
236 int
237 caps_remote_putport(lwkt_port_t port, lwkt_msg_t msg)
238 {
239     return(EINVAL);
240 }
241
242 /*
243  * Wait for a new message to arrive.  Message waiting is done on the main
244  * services port, not on the per-client remote port.  XXX as a future feature
245  * we could allow waiting for a message from a particular client.
246  */
247 static
248 void *
249 caps_remote_waitport(lwkt_port_t lport, lwkt_msg_t msg)
250 {
251     assert(0);
252     return(NULL);
253 }
254
255 /*
256  * Messages received on the main services port are replied back to a
257  * port representing the particular connection the message actually
258  * came in on.  If the port has been shutdown due to an error we have
259  * to destroy it when the last message reference goes away.
260  */
261 static
262 void
263 caps_remote_replyport(lwkt_port_t lport, lwkt_msg_t msg)
264 {
265     assert(lport->mp_refs > 0);
266     --lport->mp_refs;
267     if (lport->mp_flags & CAPPF_SHUTDOWN) {
268         if (lport->mp_refs == 0) {
269             ++lport->mp_refs;
270             caps_close((caps_port_t)lport);
271         }
272         free(msg);
273     } else {
274         msg->ms_flags |= MSGF_DONE | MSGF_REPLY;
275         caps_kev_write((caps_port_t)lport, msg);
276     }
277 }
278