--- /dev/null
- ? xasprintf("%.*s/.%s.~lock~", slash - file_name, file_name,
- slash + 1)
+ /* Copyright (c) 2008, 2009 Nicira Networks
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "lockfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "coverage.h"
+#include "hash.h"
+#include "hmap.h"
+#include "timeval.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_lockfile
+#include "vlog.h"
+
+struct lockfile {
+ struct hmap_node hmap_node;
+ char *name;
+ dev_t device;
+ ino_t inode;
+ int fd;
+};
+
+/* Lock table.
+ *
+ * We have to do this stupid dance because POSIX says that closing *any* file
+ * descriptor for a file on which a process holds a lock drops *all* locks on
+ * that file. That means that we can't afford to open a lockfile more than
+ * once. */
+static struct hmap lock_table = HMAP_INITIALIZER(&lock_table);
+
+static void lockfile_unhash(struct lockfile *);
+static int lockfile_try_lock(const char *name, bool block,
+ struct lockfile **lockfilep);
+
+/* Returns the name of the lockfile that would be created for locking a file
+ * named 'file_name'. The caller is responsible for freeing the returned
+ * name, with free(), when it is no longer needed. */
+char *
+lockfile_name(const char *file_name)
+{
+ const char *slash = strrchr(file_name, '/');
+ return (slash
++ ? xasprintf("%.*s/.%s.~lock~",
++ (int) (slash - file_name), file_name, slash + 1)
+ : xasprintf(".%s.~lock~", file_name));
+}
+
+/* Locks the configuration file against modification by other processes and
+ * re-reads it from disk.
+ *
+ * The 'timeout' specifies the maximum number of milliseconds to wait for the
+ * config file to become free. Use 0 to avoid waiting or INT_MAX to wait
+ * forever.
+ *
+ * Returns 0 on success, otherwise a positive errno value. On success,
+ * '*lockfilep' is set to point to a new "struct lockfile *" that may be
+ * unlocked with lockfile_unlock(). On failure, '*lockfilep' is set to
+ * NULL. */
+int
+lockfile_lock(const char *file, int timeout, struct lockfile **lockfilep)
+{
+ /* Only exclusive ("write") locks are supported. This is not a problem
+ * because the Open vSwitch code that currently uses lock files does so in
+ * stylized ways such that any number of readers may access a file while it
+ * is being written. */
+ long long int start, elapsed;
+ char *lock_name;
+ int error;
+
+ COVERAGE_INC(lockfile_lock);
+
+ lock_name = lockfile_name(file);
+ time_refresh();
+ start = time_msec();
+
+ do {
+ error = lockfile_try_lock(lock_name, timeout > 0, lockfilep);
+ time_refresh();
+ elapsed = time_msec() - start;
+ } while (error == EINTR && (timeout == INT_MAX || elapsed < timeout));
+
+ if (!error) {
+ if (elapsed) {
+ VLOG_WARN("%s: waited %lld ms for lock file",
+ lock_name, elapsed);
+ }
+ } else if (error == EINTR) {
+ COVERAGE_INC(lockfile_timeout);
+ VLOG_WARN("%s: giving up on lock file after %lld ms",
+ lock_name, elapsed);
+ error = ETIMEDOUT;
+ } else {
+ COVERAGE_INC(lockfile_error);
+ if (error == EACCES) {
+ error = EAGAIN;
+ }
+ VLOG_WARN("%s: failed to lock file "
+ "(after %lld ms, with %d-ms timeout): %s",
+ lock_name, elapsed, timeout, strerror(error));
+ }
+
+ free(lock_name);
+ return error;
+}
+
+/* Unlocks 'lockfile', which must have been created by a call to
+ * lockfile_lock(), and frees 'lockfile'. */
+void
+lockfile_unlock(struct lockfile *lockfile)
+{
+ if (lockfile) {
+ COVERAGE_INC(lockfile_unlock);
+ lockfile_unhash(lockfile);
+ free(lockfile->name);
+ free(lockfile);
+ }
+}
+
+/* Marks all the currently locked lockfiles as no longer locked. It makes
+ * sense to call this function after fork(), because a child created by fork()
+ * does not hold its parents' locks. */
+void
+lockfile_postfork(void)
+{
+ struct lockfile *lockfile;
+
+ HMAP_FOR_EACH (lockfile, struct lockfile, hmap_node, &lock_table) {
+ if (lockfile->fd >= 0) {
+ VLOG_WARN("%s: child does not inherit lock", lockfile->name);
+ lockfile_unhash(lockfile);
+ }
+ }
+}
+\f
+static uint32_t
+lockfile_hash(dev_t device, ino_t inode)
+{
+ return hash_bytes(&device, sizeof device,
+ hash_bytes(&inode, sizeof inode, 0));
+}
+
+static struct lockfile *
+lockfile_find(dev_t device, ino_t inode)
+{
+ struct lockfile *lockfile;
+
+ HMAP_FOR_EACH_WITH_HASH (lockfile, struct lockfile, hmap_node,
+ lockfile_hash(device, inode), &lock_table) {
+ if (lockfile->device == device && lockfile->inode == inode) {
+ return lockfile;
+ }
+ }
+ return NULL;
+}
+
+static void
+lockfile_unhash(struct lockfile *lockfile)
+{
+ if (lockfile->fd >= 0) {
+ close(lockfile->fd);
+ lockfile->fd = -1;
+ hmap_remove(&lock_table, &lockfile->hmap_node);
+ }
+}
+
+static struct lockfile *
+lockfile_register(const char *name, dev_t device, ino_t inode, int fd)
+{
+ struct lockfile *lockfile;
+
+ lockfile = lockfile_find(device, inode);
+ if (lockfile) {
+ VLOG_ERR("%s: lock file disappeared and reappeared!", name);
+ lockfile_unhash(lockfile);
+ }
+
+ lockfile = xmalloc(sizeof *lockfile);
+ lockfile->name = xstrdup(name);
+ lockfile->device = device;
+ lockfile->inode = inode;
+ lockfile->fd = fd;
+ hmap_insert(&lock_table, &lockfile->hmap_node,
+ lockfile_hash(device, inode));
+ return lockfile;
+}
+
+static int
+lockfile_try_lock(const char *name, bool block, struct lockfile **lockfilep)
+{
+ struct flock l;
+ struct stat s;
+ int error;
+ int fd;
+
+ *lockfilep = NULL;
+
+ /* Open the lock file, first creating it if necessary. */
+ for (;;) {
+ /* Check whether we've already got a lock on that file. */
+ if (!stat(name, &s)) {
+ if (lockfile_find(s.st_dev, s.st_ino)) {
+ return EDEADLK;
+ }
+ } else if (errno != ENOENT) {
+ VLOG_WARN("%s: failed to stat lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ /* Try to open an existing lock file. */
+ fd = open(name, O_RDWR);
+ if (fd >= 0) {
+ break;
+ } else if (errno != ENOENT) {
+ VLOG_WARN("%s: failed to open lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ /* Try to create a new lock file. */
+ VLOG_INFO("%s: lock file does not exist, creating", name);
+ fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ break;
+ } else if (errno != EEXIST) {
+ VLOG_WARN("%s: failed to create lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ /* Someone else created the lock file. Try again. */
+ }
+
+ /* Get the inode and device number for the lock table. */
+ if (fstat(fd, &s)) {
+ VLOG_ERR("%s: failed to fstat lock file: %s", name, strerror(errno));
+ close(fd);
+ return errno;
+ }
+
+ /* Try to lock the file. */
+ memset(&l, 0, sizeof l);
+ l.l_type = F_WRLCK;
+ l.l_whence = SEEK_SET;
+ l.l_start = 0;
+ l.l_len = 0;
+
+ time_disable_restart();
+ error = fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
+ time_enable_restart();
+
+ if (!error) {
+ *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd);
+ } else {
+ close(fd);
+ }
+ return error;
+}
+
--- /dev/null
- error = tcp_open_active(suffix, 0, &sin, &fd);
+/*
+ * Copyright (c) 2008, 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "stream.h"
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "packets.h"
+#include "socket-util.h"
+#include "util.h"
+#include "stream-provider.h"
+#include "stream-fd.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_stream_tcp
+
+/* Active TCP. */
+
+static int
+new_tcp_stream(const char *name, int fd, int connect_status,
+ const struct sockaddr_in *remote, struct stream **streamp)
+{
+ struct sockaddr_in local;
+ socklen_t local_len = sizeof local;
+ int on = 1;
+ int retval;
+
+ /* Get the local IP and port information */
+ retval = getsockname(fd, (struct sockaddr *)&local, &local_len);
+ if (retval) {
+ memset(&local, 0, sizeof local);
+ }
+
+ retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on);
+ if (retval) {
+ VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno));
+ close(fd);
+ return errno;
+ }
+
+ retval = new_fd_stream(name, fd, connect_status, NULL, streamp);
+ if (!retval) {
+ struct stream *stream = *streamp;
+ stream_set_remote_ip(stream, remote->sin_addr.s_addr);
+ stream_set_remote_port(stream, remote->sin_port);
+ stream_set_local_ip(stream, local.sin_addr.s_addr);
+ stream_set_local_port(stream, local.sin_port);
+ }
+ return retval;
+}
+
+static int
+tcp_open(const char *name, char *suffix, struct stream **streamp)
+{
+ struct sockaddr_in sin;
+ int fd, error;
+
- fd = tcp_open_passive(suffix, 0);
++ error = inet_open_active(SOCK_STREAM, suffix, 0, &sin, &fd);
+ if (fd >= 0) {
+ return new_tcp_stream(name, fd, error, &sin, streamp);
+ } else {
+ VLOG_ERR("%s: connect: %s", name, strerror(error));
+ return error;
+ }
+}
+
+struct stream_class tcp_stream_class = {
+ "tcp", /* name */
+ tcp_open, /* open */
+ NULL, /* close */
+ NULL, /* connect */
+ NULL, /* recv */
+ NULL, /* send */
+ NULL, /* wait */
+};
+\f
+/* Passive TCP. */
+
+static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
+ struct stream **streamp);
+
+static int
+ptcp_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp)
+{
+ int fd;
+
++ fd = inet_open_passive(SOCK_STREAM, suffix, 0);
+ if (fd < 0) {
+ return -fd;
+ } else {
+ return new_fd_pstream("ptcp", fd, ptcp_accept, NULL, pstreamp);
+ }
+}
+
+static int
+ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
+ struct stream **streamp)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *) sa;
+ char name[128];
+
+ if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) {
+ sprintf(name, "tcp:"IP_FMT, IP_ARGS(&sin->sin_addr));
+ sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port));
+ } else {
+ strcpy(name, "tcp");
+ }
+ return new_tcp_stream(name, fd, 0, sin, streamp);
+}
+
+struct pstream_class ptcp_pstream_class = {
+ "ptcp",
+ ptcp_open,
+ NULL,
+ NULL,
+ NULL
+};
+
/* Time at which to die with SIGALRM (if not TIME_MIN). */
static time_t deadline = TIME_MIN;
- static void setup_timer(void);
- static void setup_signal(int flags);
+ static void set_up_timer(void);
++static void set_up_signal(int flags);
static void sigalrm_handler(int);
static void refresh_if_ticked(void);
static time_t time_add(time_t, time_t);
gettimeofday(&now, NULL);
tick = false;
- setup_signal(SA_RESTART);
- setup_timer();
- /* Set up signal handler. */
++ set_up_signal(SA_RESTART);
++ set_up_timer();
+}
+
+static void
- setup_signal(int flags)
++set_up_signal(int flags)
+{
+ struct sigaction sa;
+
memset(&sa, 0, sizeof sa);
sa.sa_handler = sigalrm_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL)) {
ovs_fatal(errno, "sigaction(SIGALRM) failed");
}
+}
- /* Set up periodic signal. */
- set_up_timer();
+/* Remove SA_RESTART from the flags for SIGALRM, so that any system call that
+ * is interrupted by the periodic timer interrupt will return EINTR instead of
+ * continuing after the signal handler returns.
+ *
+ * time_disable_restart() and time_enable_restart() may be usefully wrapped
+ * around function calls that might otherwise block forever unless interrupted
+ * by a signal, e.g.:
+ *
+ * time_disable_restart();
+ * fcntl(fd, F_SETLKW, &lock);
+ * time_enable_restart();
+ */
+void
+time_disable_restart(void)
+{
- setup_signal(0);
++ set_up_signal(0);
+}
+
+/* Add SA_RESTART to the flags for SIGALRM, so that any system call that
+ * is interrupted by the periodic timer interrupt will continue after the
+ * signal handler returns instead of returning EINTR. */
+void
+time_enable_restart(void)
+{
- setup_signal(SA_RESTART);
++ set_up_signal(SA_RESTART);
}
static void