1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
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:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
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
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.
35 #include "vlog-socket.h"
43 #include <sys/socket.h>
45 #include <sys/types.h>
48 #include "fatal-signal.h"
49 #include "poll-loop.h"
50 #include "socket-util.h"
54 #ifndef SCM_CREDENTIALS
58 #define THIS_MODULE VLM_vlog_socket
61 /* Server for Vlog control connection. */
63 struct poll_waiter *waiter;
68 static void poll_server(int fd, short int events, void *server_);
70 /* Start listening for connections from clients and processing their
71 * requests. 'path' may be:
73 * - NULL, in which case the default socket path is used. (Only one
74 * Vlog_server_socket per process can use the default path.)
76 * - A name that does not start with '/', in which case it is appended to
77 * the default socket path.
79 * - An absolute path (starting with '/') that gives the exact name of
80 * the Unix domain socket to listen on.
82 * A program that (optionally) daemonizes itself should call this function
83 * *after* daemonization, so that the socket name contains the pid of the
84 * daemon instead of the pid of the program that exited. (Otherwise, "vlogconf
85 * --target <program>.pid" will fail.)
87 * Returns 0 if successful, otherwise a positive errno value. If successful,
88 * sets '*serverp' to the new vlog_server, otherwise to NULL. */
90 vlog_server_listen(const char *path, struct vlog_server **serverp)
92 struct vlog_server *server = xmalloc(sizeof *server);
94 if (path && path[0] == '/') {
95 server->path = xstrdup(path);
97 server->path = xasprintf("/tmp/vlogs.%ld%s",
98 (long int) getpid(), path ? path : "");
101 server->fd = make_unix_socket(SOCK_DGRAM, true, true, server->path, NULL);
102 if (server->fd < 0) {
104 fprintf(stderr, "Could not initialize vlog configuration socket: %s\n",
105 strerror(-server->fd));
114 server->waiter = poll_fd_callback(server->fd, POLLIN, poll_server, server);
122 /* Destroys 'server' and stops listening for connections. */
124 vlog_server_close(struct vlog_server *server)
127 poll_cancel(server->waiter);
129 unlink(server->path);
130 fatal_signal_remove_file_to_unlink(server->path);
137 recv_with_creds(const struct vlog_server *server,
138 char *cmd_buf, size_t cmd_buf_size,
139 struct sockaddr_un *un, socklen_t *un_len)
141 #ifdef SCM_CREDENTIALS
142 /* Read a message and control messages from 'fd'. */
143 char cred_buf[CMSG_SPACE(sizeof(struct ucred))];
148 struct cmsghdr* cmsg;
150 iov.iov_base = cmd_buf;
151 iov.iov_len = cmd_buf_size - 1;
153 memset(&msg, 0, sizeof msg);
155 msg.msg_namelen = sizeof *un;
158 msg.msg_control = cred_buf;
159 msg.msg_controllen = sizeof cred_buf;
161 n = recvmsg(server->fd, &msg, 0);
162 *un_len = msg.msg_namelen;
168 /* Ensure that the message has credentials ensuring that it was sent
169 * from the same user who started us, or by root. */
171 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
172 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
173 if (cmsg->cmsg_level == SOL_SOCKET
174 && cmsg->cmsg_type == SCM_CREDENTIALS) {
175 cred = (struct ucred *) CMSG_DATA(cmsg);
176 } else if (cmsg->cmsg_level == SOL_SOCKET
177 && cmsg->cmsg_type == SCM_RIGHTS) {
178 /* Anyone can send us fds. If we don't close them, then that's
179 * a DoS: the sender can overflow our fd table. */
180 int* fds = (int *) CMSG_DATA(cmsg);
181 size_t n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof *fds;
183 for (i = 0; i < n_fds; i++) {
189 fprintf(stderr, "vlog: config message lacks credentials\n");
191 } else if (cred->uid && cred->uid != getuid()) {
192 fprintf(stderr, "vlog: config message uid=%ld is not 0 or %ld\n",
193 (long int) cred->uid, (long int) getuid());
198 #else /* !SCM_CREDENTIALS */
204 /* Receive a message. */
206 n = recvfrom(server->fd, cmd_buf, cmd_buf_size - 1, 0,
207 (struct sockaddr *) un, &len);
214 len -= offsetof(struct sockaddr_un, sun_path);
215 un->sun_path[len] = '\0';
216 if (stat(un->sun_path, &s) < 0) {
217 fprintf(stderr, "vlog: config message from inaccessible socket: %s\n",
221 if (!S_ISSOCK(s.st_mode)) {
222 fprintf(stderr, "vlog: config message not from a socket\n");
225 recent = time_now() - 30;
226 if (s.st_atime < recent || s.st_ctime < recent || s.st_mtime < recent) {
227 fprintf(stderr, "vlog: config socket too old\n");
230 if (s.st_uid && s.st_uid != getuid()) {
231 fprintf(stderr, "vlog: config message uid=%ld is not 0 or %ld\n",
232 (long int) s.st_uid, (long int) getuid());
236 #endif /* !SCM_CREDENTIALS */
239 /* Processes incoming requests for 'server'. */
241 poll_server(int fd UNUSED, short int events, void *server_)
243 struct vlog_server *server = server_;
246 struct sockaddr_un un;
251 error = recv_with_creds(server, cmd_buf, sizeof cmd_buf, &un, &un_len);
253 if (error != EAGAIN && error != EWOULDBLOCK) {
254 fprintf(stderr, "vlog: reading configuration socket: %s",
258 } else if (error < 0) {
262 /* Process message and send reply. */
263 if (!strncmp(cmd_buf, "set ", 4)) {
264 char *msg = vlog_set_levels_from_string(cmd_buf + 4);
265 reply = msg ? msg : xstrdup("ack");
266 } else if (!strcmp(cmd_buf, "list")) {
267 reply = vlog_get_levels();
268 } else if (!strcmp(cmd_buf, "reopen")) {
269 int error = vlog_reopen_log_file();
271 ? xasprintf("could not reopen log file \"%s\": %s",
272 vlog_get_log_file(), strerror(error))
275 reply = xstrdup("nak");
277 sendto(server->fd, reply, strlen(reply), 0,
278 (struct sockaddr*) &un, un_len);
281 server->waiter = poll_fd_callback(server->fd, POLLIN, poll_server, server);
284 /* Client for Vlog control connection. */
292 /* Connects to a Vlog server socket. 'path' may be:
294 * - A string that starts with a PID. If a non-null, non-absolute name
295 * was passed to Vlog_server_socket::listen(), then it must follow the
298 * - An absolute path (starting with '/') to a Vlog server socket or a
299 * pidfile. If it is a pidfile, the pidfile will be read and translated
300 * into a Vlog server socket file name.
302 * - A relative path, which is translated into a pidfile name and then
305 * Returns 0 if successful, otherwise a positive errno value. If successful,
306 * sets '*clientp' to the new vlog_client, otherwise to NULL. */
308 vlog_client_connect(const char *path, struct vlog_client **clientp)
311 struct vlog_client *client;
315 client = xmalloc(sizeof *client);
316 if (path[0] == '/') {
317 client->connect_path = xstrdup(path);
318 } else if (isdigit((unsigned char) path[0])) {
319 client->connect_path = xasprintf("/tmp/vlogs.%s", path);
321 client->connect_path = make_pidfile_name(path);
323 client->bind_path = NULL;
325 if (stat(client->connect_path, &s)) {
327 VLOG_WARN("could not stat \"%s\": %s",
328 client->connect_path, strerror(error));
330 } else if (S_ISREG(s.st_mode)) {
331 pid_t pid = read_pidfile(client->connect_path);
334 VLOG_WARN("could not read pidfile \"%s\": %s",
335 client->connect_path, strerror(error));
338 free(client->connect_path);
339 client->connect_path = xasprintf("/tmp/vlogs.%ld", (long int) pid);
341 client->bind_path = xasprintf("/tmp/vlog.%ld.%d",
342 (long int) getpid(), counter++);
343 client->fd = make_unix_socket(SOCK_DGRAM, false, false,
344 client->bind_path, client->connect_path);
345 if (client->fd < 0) {
353 free(client->connect_path);
354 free(client->bind_path);
360 /* Destroys 'client'. */
362 vlog_client_close(struct vlog_client *client)
365 unlink(client->bind_path);
366 fatal_signal_remove_file_to_unlink(client->bind_path);
367 free(client->bind_path);
368 free(client->connect_path);
374 /* Sends 'request' to the server socket that 'client' is connected to. Returns
375 * 0 if successful, otherwise a positive errno value. */
377 vlog_client_send(struct vlog_client *client, const char *request)
379 #ifdef SCM_CREDENTIALS
382 char buf[CMSG_SPACE(sizeof cred)];
384 struct cmsghdr* cmsg;
391 iov.iov_base = (void*) request;
392 iov.iov_len = strlen(request);
394 memset(&msg, 0, sizeof msg);
397 msg.msg_control = buf;
398 msg.msg_controllen = sizeof buf;
400 cmsg = CMSG_FIRSTHDR(&msg);
401 cmsg->cmsg_level = SOL_SOCKET;
402 cmsg->cmsg_type = SCM_CREDENTIALS;
403 cmsg->cmsg_len = CMSG_LEN(sizeof cred);
404 memcpy(CMSG_DATA(cmsg), &cred, sizeof cred);
405 msg.msg_controllen = cmsg->cmsg_len;
407 nbytes = sendmsg(client->fd, &msg, 0);
408 #else /* !SCM_CREDENTIALS */
409 ssize_t nbytes = send(client->fd, request, strlen(request), 0);
410 #endif /* !SCM_CREDENTIALS */
412 return nbytes == strlen(request) ? 0 : ENOBUFS;
418 /* Attempts to receive a response from the server socket that 'client' is
419 * connected to. Returns 0 if successful, otherwise a positive errno value.
420 * If successful, sets '*reply' to the reply, which the caller must free,
421 * otherwise to NULL. */
423 vlog_client_recv(struct vlog_client *client, char **reply)
434 nfds = time_poll(&pfd, 1, 1000);
437 } else if (nfds < 0) {
441 nbytes = read(client->fd, buffer, sizeof buffer - 1);
445 buffer[nbytes] = '\0';
446 *reply = xstrdup(buffer);
451 /* Sends 'request' to the server socket and waits for a reply. Returns 0 if
452 * successful, otherwise to a positive errno value. If successful, sets
453 * '*reply' to the reply, which the caller must free, otherwise to NULL. */
455 vlog_client_transact(struct vlog_client *client,
456 const char *request, char **reply)
460 /* Retry up to 3 times. */
461 for (i = 0; i < 3; ++i) {
462 int error = vlog_client_send(client, request);
467 error = vlog_client_recv(client, reply);
468 if (error != ETIMEDOUT) {
476 /* Returns the path of the server socket to which 'client' is connected. The
477 * caller must not modify or free the returned string. */
479 vlog_client_target(const struct vlog_client *client)
481 return client->connect_path;