Fix vconn_transact() bug introduced in commit 65ac65a6d2,
[sliver-openvswitch.git] / lib / vlog-socket.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  * 
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  * 
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  * 
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  * 
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include <config.h>
35 #include "vlog-socket.h"
36 #include <ctype.h>
37 #include <errno.h>
38 #include <sys/un.h>
39 #include <fcntl.h>
40 #include <poll.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 #include "daemon.h"
48 #include "fatal-signal.h"
49 #include "poll-loop.h"
50 #include "socket-util.h"
51 #include "timeval.h"
52 #include "util.h"
53
54 #ifndef SCM_CREDENTIALS
55 #include <time.h>
56 #endif
57
58 #define THIS_MODULE VLM_vlog_socket
59 #include "vlog.h"
60 \f
61 /* Server for Vlog control connection. */
62 struct vlog_server {
63     struct poll_waiter *waiter;
64     char *path;
65     int fd;
66 };
67
68 static void poll_server(int fd, short int events, void *server_);
69
70 /* Start listening for connections from clients and processing their
71  * requests.  'path' may be:
72  *
73  *      - NULL, in which case the default socket path is used.  (Only one
74  *        Vlog_server_socket per process can use the default path.)
75  *
76  *      - A name that does not start with '/', in which case it is appended to
77  *        the default socket path.
78  *
79  *      - An absolute path (starting with '/') that gives the exact name of
80  *        the Unix domain socket to listen on.
81  *
82  * Returns 0 if successful, otherwise a positive errno value.  If successful,
83  * sets '*serverp' to the new vlog_server, otherwise to NULL. */
84 int
85 vlog_server_listen(const char *path, struct vlog_server **serverp)
86 {
87     struct vlog_server *server = xmalloc(sizeof *server);
88
89     if (path && path[0] == '/') {
90         server->path = xstrdup(path);
91     } else {
92         server->path = xasprintf("/tmp/vlogs.%ld%s",
93                                  (long int) getpid(), path ? path : "");
94     }
95
96     server->fd = make_unix_socket(SOCK_DGRAM, true, true, server->path, NULL);
97     if (server->fd < 0) {
98         int fd = server->fd;
99         fprintf(stderr, "Could not initialize vlog configuration socket: %s\n",
100                 strerror(-server->fd));
101         free(server->path);
102         free(server);
103         if (serverp) {
104             *serverp = NULL; 
105         }
106         return fd;
107     }
108
109     server->waiter = poll_fd_callback(server->fd, POLLIN, poll_server, server);
110
111     if (serverp) {
112         *serverp = server; 
113     }
114     return 0;
115 }
116
117 /* Destroys 'server' and stops listening for connections. */
118 void
119 vlog_server_close(struct vlog_server *server)
120 {
121     if (server) {
122         poll_cancel(server->waiter);
123         close(server->fd);
124         unlink(server->path);
125         fatal_signal_remove_file_to_unlink(server->path);
126         free(server->path);
127         free(server);
128     }
129 }
130
131 static int
132 recv_with_creds(const struct vlog_server *server,
133                 char *cmd_buf, size_t cmd_buf_size,
134                 struct sockaddr_un *un, socklen_t *un_len)
135 {
136 #ifdef SCM_CREDENTIALS
137     /* Read a message and control messages from 'fd'.  */
138     char cred_buf[CMSG_SPACE(sizeof(struct ucred))];
139     ssize_t n;
140     struct iovec iov;
141     struct msghdr msg;
142     struct ucred* cred;
143     struct cmsghdr* cmsg;
144
145     iov.iov_base = cmd_buf;
146     iov.iov_len = cmd_buf_size - 1;
147
148     memset(&msg, 0, sizeof msg);
149     msg.msg_name = un;
150     msg.msg_namelen = sizeof *un;
151     msg.msg_iov = &iov;
152     msg.msg_iovlen = 1;
153     msg.msg_control = cred_buf;
154     msg.msg_controllen = sizeof cred_buf;
155
156     n = recvmsg(server->fd, &msg, 0);
157     *un_len = msg.msg_namelen;
158     if (n < 0) {
159         return errno;
160     }
161     cmd_buf[n] = '\0';
162
163     /* Ensure that the message has credentials ensuring that it was sent
164      * from the same user who started us, or by root. */
165     cred = NULL;
166     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
167          cmsg = CMSG_NXTHDR(&msg, cmsg)) {
168         if (cmsg->cmsg_level == SOL_SOCKET
169             && cmsg->cmsg_type == SCM_CREDENTIALS) {
170             cred = (struct ucred *) CMSG_DATA(cmsg);
171         } else if (cmsg->cmsg_level == SOL_SOCKET
172                    && cmsg->cmsg_type == SCM_RIGHTS) {
173             /* Anyone can send us fds.  If we don't close them, then that's
174              * a DoS: the sender can overflow our fd table. */
175             int* fds = (int *) CMSG_DATA(cmsg);
176             size_t n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof *fds;
177             size_t i;
178             for (i = 0; i < n_fds; i++) {
179                 close(fds[i]);
180             }
181         }
182     }
183     if (!cred) {
184         fprintf(stderr, "vlog: config message lacks credentials\n");
185         return -1;
186     } else if (cred->uid && cred->uid != getuid()) {
187         fprintf(stderr, "vlog: config message uid=%ld is not 0 or %ld\n",
188                 (long int) cred->uid, (long int) getuid());
189         return -1;
190     }
191
192     return 0;
193 #else /* !SCM_CREDENTIALS */
194     socklen_t len;
195     ssize_t n;
196     struct stat s;
197     time_t recent;
198
199     /* Receive a message. */
200     len = sizeof *un;
201     n = recvfrom(server->fd, cmd_buf, cmd_buf_size - 1, 0,
202                  (struct sockaddr *) un, &len);
203     *un_len = len;
204     if (n < 0) {
205         return errno;
206     }
207     cmd_buf[n] = '\0';
208
209     len -= offsetof(struct sockaddr_un, sun_path);
210     un->sun_path[len] = '\0';
211     if (stat(un->sun_path, &s) < 0) {
212         fprintf(stderr, "vlog: config message from inaccessible socket: %s\n",
213                 strerror(errno));
214         return -1;
215     }
216     if (!S_ISSOCK(s.st_mode)) {
217         fprintf(stderr, "vlog: config message not from a socket\n");
218         return -1;
219     }
220     recent = time_now() - 30;
221     if (s.st_atime < recent || s.st_ctime < recent || s.st_mtime < recent) {
222         fprintf(stderr, "vlog: config socket too old\n");
223         return -1;
224     }
225     if (s.st_uid && s.st_uid != getuid()) {
226         fprintf(stderr, "vlog: config message uid=%ld is not 0 or %ld\n",
227                 (long int) s.st_uid, (long int) getuid());
228         return -1;
229     }
230     return 0;
231 #endif /* !SCM_CREDENTIALS */
232 }
233
234 /* Processes incoming requests for 'server'. */
235 static void
236 poll_server(int fd UNUSED, short int events, void *server_)
237 {
238     struct vlog_server *server = server_;
239     for (;;) {
240         char cmd_buf[512];
241         struct sockaddr_un un;
242         socklen_t un_len;
243         char *reply;
244         int error;
245
246         error = recv_with_creds(server, cmd_buf, sizeof cmd_buf, &un, &un_len);
247         if (error > 0) {
248             if (error != EAGAIN && error != EWOULDBLOCK) {
249                 fprintf(stderr, "vlog: reading configuration socket: %s",
250                         strerror(errno));
251             }
252             break;
253         } else if (error < 0) {
254             continue;
255         }
256
257         /* Process message and send reply. */
258         if (!strncmp(cmd_buf, "set ", 4)) {
259             char *msg = vlog_set_levels_from_string(cmd_buf + 4);
260             reply = msg ? msg : xstrdup("ack");
261         } else if (!strcmp(cmd_buf, "list")) {
262             reply = vlog_get_levels();
263         } else if (!strcmp(cmd_buf, "reopen")) {
264             int error = vlog_reopen_log_file();
265             reply = (error
266                      ? xasprintf("could not reopen log file \"%s\": %s",
267                                  vlog_get_log_file(), strerror(error))
268                      : xstrdup("ack"));
269         } else {
270             reply = xstrdup("nak");
271         }
272         sendto(server->fd, reply, strlen(reply), 0,
273                (struct sockaddr*) &un, un_len);
274         free(reply);
275     }
276     server->waiter = poll_fd_callback(server->fd, POLLIN, poll_server, server);
277 }
278 \f
279 /* Client for Vlog control connection. */
280
281 struct vlog_client {
282     char *connect_path;
283     char *bind_path;
284     int fd;
285 };
286
287 /* Connects to a Vlog server socket.  'path' may be:
288  *
289  *      - A string that starts with a PID.  If a non-null, non-absolute name
290  *        was passed to Vlog_server_socket::listen(), then it must follow the
291  *        PID in 'path'.
292  *
293  *      - An absolute path (starting with '/') to a Vlog server socket or a
294  *        pidfile.  If it is a pidfile, the pidfile will be read and translated
295  *        into a Vlog server socket file name.
296  *
297  *      - A relative path, which is translated into a pidfile name and then
298  *        treated as above.
299  *
300  * Returns 0 if successful, otherwise a positive errno value.  If successful,
301  * sets '*clientp' to the new vlog_client, otherwise to NULL. */
302 int
303 vlog_client_connect(const char *path, struct vlog_client **clientp)
304 {
305     static int counter;
306     struct vlog_client *client;
307     struct stat s;
308     int error;
309
310     client = xmalloc(sizeof *client);
311     if (path[0] == '/') {
312         client->connect_path = xstrdup(path);
313     } else if (isdigit((unsigned char) path[0])) {
314         client->connect_path = xasprintf("/tmp/vlogs.%s", path);
315     } else {
316         client->connect_path = make_pidfile_name(path);
317     }
318     client->bind_path = NULL;
319
320     if (stat(client->connect_path, &s)) {
321         error = errno;
322         VLOG_WARN("could not stat \"%s\": %s",
323                   client->connect_path, strerror(error));
324         goto error;
325     } else if (S_ISREG(s.st_mode)) {
326         pid_t pid = read_pidfile(client->connect_path);
327         if (pid < 0) {
328             error = -pid;
329             VLOG_WARN("could not read pidfile \"%s\": %s",
330                       client->connect_path, strerror(error));
331             goto error;
332         }
333         free(client->connect_path);
334         client->connect_path = xasprintf("/tmp/vlogs.%ld", (long int) pid);
335     }
336     client->bind_path = xasprintf("/tmp/vlog.%ld.%d",
337                                   (long int) getpid(), counter++);
338     client->fd = make_unix_socket(SOCK_DGRAM, false, false,
339                                   client->bind_path, client->connect_path);
340     if (client->fd < 0) {
341         error = -client->fd;
342         goto error;
343     }
344     *clientp = client;
345     return 0;
346
347 error:
348     free(client->connect_path);
349     free(client->bind_path);
350     free(client);
351     *clientp = NULL;
352     return error;
353 }
354
355 /* Destroys 'client'. */
356 void
357 vlog_client_close(struct vlog_client *client)
358 {
359     if (client) {
360         unlink(client->bind_path);
361         fatal_signal_remove_file_to_unlink(client->bind_path);
362         free(client->bind_path);
363         free(client->connect_path);
364         close(client->fd);
365         free(client);
366     }
367 }
368
369 /* Sends 'request' to the server socket that 'client' is connected to.  Returns
370  * 0 if successful, otherwise a positive errno value. */
371 int
372 vlog_client_send(struct vlog_client *client, const char *request)
373 {
374 #ifdef SCM_CREDENTIALS
375     struct ucred cred;
376     struct iovec iov;
377     char buf[CMSG_SPACE(sizeof cred)];
378     struct msghdr msg;
379     struct cmsghdr* cmsg;
380     ssize_t nbytes;
381
382     cred.pid = getpid();
383     cred.uid = getuid();
384     cred.gid = getgid();
385
386     iov.iov_base = (void*) request;
387     iov.iov_len = strlen(request);
388
389     memset(&msg, 0, sizeof msg);
390     msg.msg_iov = &iov;
391     msg.msg_iovlen = 1;
392     msg.msg_control = buf;
393     msg.msg_controllen = sizeof buf;
394
395     cmsg = CMSG_FIRSTHDR(&msg);
396     cmsg->cmsg_level = SOL_SOCKET;
397     cmsg->cmsg_type = SCM_CREDENTIALS;
398     cmsg->cmsg_len = CMSG_LEN(sizeof cred);
399     memcpy(CMSG_DATA(cmsg), &cred, sizeof cred);
400     msg.msg_controllen = cmsg->cmsg_len;
401
402     nbytes = sendmsg(client->fd, &msg, 0);
403 #else /* !SCM_CREDENTIALS */
404     ssize_t nbytes = send(client->fd, request, strlen(request), 0);
405 #endif /* !SCM_CREDENTIALS */
406     if (nbytes > 0) {
407         return nbytes == strlen(request) ? 0 : ENOBUFS;
408     } else {
409         return errno;
410     }
411 }
412
413 /* Attempts to receive a response from the server socket that 'client' is
414  * connected to.  Returns 0 if successful, otherwise a positive errno value.
415  * If successful, sets '*reply' to the reply, which the caller must free,
416  * otherwise to NULL. */
417 int
418 vlog_client_recv(struct vlog_client *client, char **reply)
419 {
420     struct pollfd pfd;
421     int nfds;
422     char buffer[65536];
423     ssize_t nbytes;
424
425     *reply = NULL;
426
427     pfd.fd = client->fd;
428     pfd.events = POLLIN;
429     nfds = time_poll(&pfd, 1, 1000);
430     if (nfds == 0) {
431         return ETIMEDOUT;
432     } else if (nfds < 0) {
433         return -nfds;
434     }
435
436     nbytes = read(client->fd, buffer, sizeof buffer - 1);
437     if (nbytes < 0) {
438         return errno;
439     } else {
440         buffer[nbytes] = '\0';
441         *reply = xstrdup(buffer);
442         return 0;
443     }
444 }
445
446 /* Sends 'request' to the server socket and waits for a reply.  Returns 0 if
447  * successful, otherwise to a positive errno value.  If successful, sets
448  * '*reply' to the reply, which the caller must free, otherwise to NULL. */
449 int
450 vlog_client_transact(struct vlog_client *client,
451                      const char *request, char **reply)
452 {
453     int i;
454
455     /* Retry up to 3 times. */
456     for (i = 0; i < 3; ++i) {
457         int error = vlog_client_send(client, request);
458         if (error) {
459             *reply = NULL;
460             return error;
461         }
462         error = vlog_client_recv(client, reply);
463         if (error != ETIMEDOUT) {
464             return error;
465         }
466     }
467     *reply = NULL;
468     return ETIMEDOUT;
469 }
470
471 /* Returns the path of the server socket to which 'client' is connected.  The
472  * caller must not modify or free the returned string. */
473 const char *
474 vlog_client_target(const struct vlog_client *client)
475 {
476     return client->connect_path;
477 }