* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vlog-socket.h"
+#include <ctype.h>
#include <errno.h>
#include <sys/un.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "daemon.h"
#include "fatal-signal.h"
#include "poll-loop.h"
+#include "socket-util.h"
+#include "timeval.h"
#include "util.h"
-#include "vlog.h"
#ifndef SCM_CREDENTIALS
#include <time.h>
#endif
-static int make_unix_socket(bool nonblock, bool passcred,
- const char *bind_path, const char *connect_path);
+#define THIS_MODULE VLM_vlog_socket
+#include "vlog.h"
\f
/* Server for Vlog control connection. */
struct vlog_server {
* - An absolute path (starting with '/') that gives the exact name of
* the Unix domain socket to listen on.
*
+ * A program that (optionally) daemonizes itself should call this function
+ * *after* daemonization, so that the socket name contains the pid of the
+ * daemon instead of the pid of the program that exited. (Otherwise, "vlogconf
+ * --target <program>.pid" will fail.)
+ *
* Returns 0 if successful, otherwise a positive errno value. If successful,
* sets '*serverp' to the new vlog_server, otherwise to NULL. */
int
(long int) getpid(), path ? path : "");
}
- server->fd = make_unix_socket(true, true, server->path, NULL);
+ server->fd = make_unix_socket(SOCK_DGRAM, true, true, server->path, NULL);
if (server->fd < 0) {
int fd = server->fd;
- free(server->path);
- free(server);
fprintf(stderr, "Could not initialize vlog configuration socket: %s\n",
strerror(-server->fd));
+ free(server->path);
+ free(server);
if (serverp) {
*serverp = NULL;
}
fprintf(stderr, "vlog: config message not from a socket\n");
return -1;
}
- recent = time(0) - 30;
+ recent = time_now() - 30;
if (s.st_atime < recent || s.st_ctime < recent || s.st_mtime < recent) {
fprintf(stderr, "vlog: config socket too old\n");
return -1;
reply = msg ? msg : xstrdup("ack");
} else if (!strcmp(cmd_buf, "list")) {
reply = vlog_get_levels();
+ } else if (!strcmp(cmd_buf, "reopen")) {
+ int error = vlog_reopen_log_file();
+ reply = (error
+ ? xasprintf("could not reopen log file \"%s\": %s",
+ vlog_get_log_file(), strerror(error))
+ : xstrdup("ack"));
} else {
reply = xstrdup("nak");
}
int fd;
};
-/* Connects to a Vlog server socket. If 'path' does not start with '/', then
- * it start with a PID as a string. If a non-null, non-absolute name was
- * passed to Vlog_server_socket::listen(), then it must follow the PID in
- * 'path'. If 'path' starts with '/', then it must be an absolute path that
- * gives the exact name of the Unix domain socket to connect to.
+/* Connects to a Vlog server socket. 'path' may be:
+ *
+ * - A string that starts with a PID. If a non-null, non-absolute name
+ * was passed to Vlog_server_socket::listen(), then it must follow the
+ * PID in 'path'.
+ *
+ * - An absolute path (starting with '/') to a Vlog server socket or a
+ * pidfile. If it is a pidfile, the pidfile will be read and translated
+ * into a Vlog server socket file name.
+ *
+ * - A relative path, which is translated into a pidfile name and then
+ * treated as above.
*
* Returns 0 if successful, otherwise a positive errno value. If successful,
* sets '*clientp' to the new vlog_client, otherwise to NULL. */
int
vlog_client_connect(const char *path, struct vlog_client **clientp)
{
+ static int counter;
struct vlog_client *client;
- int fd;
+ struct stat s;
+ int error;
client = xmalloc(sizeof *client);
- client->connect_path = (path[0] == '/'
- ? xstrdup(path)
- : xasprintf("/tmp/vlogs.%s", path));
-
- client->bind_path = xasprintf("/tmp/vlog.%ld", (long int) getpid());
- fd = make_unix_socket(false, false,
- client->bind_path, client->connect_path);
-
- if (fd >= 0) {
- client->fd = fd;
- *clientp = client;
- return 0;
+ if (path[0] == '/') {
+ client->connect_path = xstrdup(path);
+ } else if (isdigit((unsigned char) path[0])) {
+ client->connect_path = xasprintf("/tmp/vlogs.%s", path);
} else {
+ client->connect_path = make_pidfile_name(path);
+ }
+ client->bind_path = NULL;
+
+ if (stat(client->connect_path, &s)) {
+ error = errno;
+ VLOG_WARN("could not stat \"%s\": %s",
+ client->connect_path, strerror(error));
+ goto error;
+ } else if (S_ISREG(s.st_mode)) {
+ pid_t pid = read_pidfile(client->connect_path);
+ if (pid < 0) {
+ error = -pid;
+ VLOG_WARN("could not read pidfile \"%s\": %s",
+ client->connect_path, strerror(error));
+ goto error;
+ }
free(client->connect_path);
- free(client->bind_path);
- free(client);
- *clientp = NULL;
- return errno;
+ client->connect_path = xasprintf("/tmp/vlogs.%ld", (long int) pid);
+ }
+ client->bind_path = xasprintf("/tmp/vlog.%ld.%d",
+ (long int) getpid(), counter++);
+ client->fd = make_unix_socket(SOCK_DGRAM, false, false,
+ client->bind_path, client->connect_path);
+ if (client->fd < 0) {
+ error = -client->fd;
+ goto error;
}
+ *clientp = client;
+ return 0;
+
+error:
+ free(client->connect_path);
+ free(client->bind_path);
+ free(client);
+ *clientp = NULL;
+ return error;
}
/* Destroys 'client'. */
pfd.fd = client->fd;
pfd.events = POLLIN;
- nfds = poll(&pfd, 1, 1000);
+ nfds = time_poll(&pfd, 1, 1000);
if (nfds == 0) {
return ETIMEDOUT;
} else if (nfds < 0) {
- return errno;
+ return -nfds;
}
nbytes = read(client->fd, buffer, sizeof buffer - 1);
{
return client->connect_path;
}
-\f
-/* Helper functions. */
-
-/* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in
- * '*un_len' the size of the sockaddr_un. */
-static void
-make_sockaddr_un(const char *name, struct sockaddr_un* un, socklen_t *un_len)
-{
- un->sun_family = AF_UNIX;
- strncpy(un->sun_path, name, sizeof un->sun_path);
- un->sun_path[sizeof un->sun_path - 1] = '\0';
- *un_len = (offsetof(struct sockaddr_un, sun_path)
- + strlen (un->sun_path) + 1);
-}
-
-/* Creates a Unix domain datagram socket that is bound to '*bind_path' (if
- * 'bind_path' is non-null) and connected to '*connect_path' (if 'connect_path'
- * is non-null). If 'nonblock' is true, the socket is made non-blocking. If
- * 'passcred' is true, the socket is configured to receive SCM_CREDENTIALS
- * control messages.
- *
- * Returns the socket's fd if successful, otherwise a negative errno value. */
-static int
-make_unix_socket(bool nonblock, bool passcred UNUSED,
- const char *bind_path, const char *connect_path)
-{
- int error;
- int fd;
-
- fd = socket(PF_UNIX, SOCK_DGRAM, 0);
- if (fd < 0) {
- return -errno;
- }
-
- if (nonblock) {
- int flags = fcntl(fd, F_GETFL, 0);
- if (flags == -1) {
- goto error;
- }
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
- goto error;
- }
- }
-
- if (bind_path) {
- struct sockaddr_un un;
- socklen_t un_len;
- make_sockaddr_un(bind_path, &un, &un_len);
- if (unlink(un.sun_path) && errno != ENOENT) {
- fprintf(stderr, "unlinking \"%s\": %s\n",
- un.sun_path, strerror(errno));
- }
- fatal_signal_add_file_to_unlink(bind_path);
- if (bind(fd, (struct sockaddr*) &un, un_len)
- || fchmod(fd, S_IRWXU)) {
- goto error;
- }
- }
-
- if (connect_path) {
- struct sockaddr_un un;
- socklen_t un_len;
- make_sockaddr_un(connect_path, &un, &un_len);
- if (connect(fd, (struct sockaddr*) &un, un_len)) {
- goto error;
- }
- }
-
-#ifdef SCM_CREDENTIALS
- if (passcred) {
- int enable = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable))) {
- goto error;
- }
- }
-#endif
-
- return fd;
-
-error:
- if (bind_path) {
- fatal_signal_remove_file_to_unlink(bind_path);
- }
- error = errno;
- close(fd);
- return -error;
-}