Use a lock file and flock() to serialize server start, avoids problems

when running a bunch of tmux from cron at the same time. Based on a diff
from Tim Ruehsen.
This commit is contained in:
Nicholas Marriott 2012-03-09 09:57:40 +00:00
parent 18012f5b18
commit ac9ebc29a2
3 changed files with 46 additions and 15 deletions

View File

@ -39,6 +39,7 @@ int client_exitval;
enum msgtype client_exittype; enum msgtype client_exittype;
int client_attached; int client_attached;
int client_get_lock(char *);
int client_connect(char *, int); int client_connect(char *, int);
void client_send_identify(int); void client_send_identify(int);
void client_send_environ(void); void client_send_environ(void);
@ -49,13 +50,38 @@ void client_callback(int, short, void *);
int client_dispatch_attached(void); int client_dispatch_attached(void);
int client_dispatch_wait(void *); int client_dispatch_wait(void *);
/*
* Get server create lock. If already held then server start is happening in
* another client, so block until the lock is released and return -1 to
* retry. Ignore other errors - just continue and start the server without the
* lock.
*/
int
client_get_lock(char *lockfile)
{
int lockfd;
if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1)
fatal("open failed");
if (flock(lockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) {
while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR)
/* nothing */;
close(lockfd);
return (-1);
}
return (lockfd);
}
/* Connect client to server. */ /* Connect client to server. */
int int
client_connect(char *path, int start_server) client_connect(char *path, int start_server)
{ {
struct sockaddr_un sa; struct sockaddr_un sa;
size_t size; size_t size;
int fd; int fd, lockfd;
char *lockfile;
memset(&sa, 0, sizeof sa); memset(&sa, 0, sizeof sa);
sa.sun_family = AF_UNIX; sa.sun_family = AF_UNIX;
@ -65,24 +91,25 @@ client_connect(char *path, int start_server)
return (-1); return (-1);
} }
retry:
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
fatal("socket failed"); fatal("socket failed");
if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
if (errno != ECONNREFUSED && errno != ENOENT)
goto failed;
if (!start_server) if (!start_server)
goto failed; goto failed;
switch (errno) { close(fd);
case ECONNREFUSED:
if (unlink(path) != 0) xasprintf(&lockfile, "%s.lock", path);
goto failed; if ((lockfd = client_get_lock(lockfile)) == -1)
/* FALLTHROUGH */ goto retry;
case ENOENT: if (unlink(path) != 0 && errno != ENOENT)
if ((fd = server_start()) == -1) return (-1);
goto failed; fd = server_start(lockfd, lockfile);
break; xfree(lockfile);
default: close(lockfd);
goto failed;
}
} }
setblocking(fd, 0); setblocking(fd, 0);

View File

@ -104,7 +104,7 @@ server_create_socket(void)
/* Fork new server. */ /* Fork new server. */
int int
server_start(void) server_start(int lockfd, char *lockfile)
{ {
struct window_pane *wp; struct window_pane *wp;
int pair[2]; int pair[2];
@ -161,6 +161,10 @@ server_start(void)
server_fd = server_create_socket(); server_fd = server_create_socket();
server_client_create(pair[1]); server_client_create(pair[1]);
unlink(lockfile);
xfree(lockfile);
close(lockfd);
if (access(SYSTEM_CFG, R_OK) == 0) if (access(SYSTEM_CFG, R_OK) == 0)
load_cfg(SYSTEM_CFG, NULL, &cfg_causes); load_cfg(SYSTEM_CFG, NULL, &cfg_causes);
else if (errno != ENOENT) { else if (errno != ENOENT) {

2
tmux.h
View File

@ -1687,7 +1687,7 @@ const char *key_string_lookup_key(int);
extern struct clients clients; extern struct clients clients;
extern struct clients dead_clients; extern struct clients dead_clients;
extern struct paste_stack global_buffers; extern struct paste_stack global_buffers;
int server_start(void); int server_start(int, char *);
void server_update_socket(void); void server_update_socket(void);
/* server-client.c */ /* server-client.c */