aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--common.c41
-rw-r--r--common.h2
-rw-r--r--fishd.c159
3 files changed, 162 insertions, 40 deletions
diff --git a/common.c b/common.c
index 376b1620..ee9b2d9f 100644
--- a/common.c
+++ b/common.c
@@ -19,6 +19,8 @@ parts of fish.
#include <signal.h>
#include <locale.h>
#include <time.h>
+#include <sys/time.h>
+#include <fcntl.h>
#if HAVE_NCURSES_H
#include <ncurses.h>
@@ -52,6 +54,12 @@ parts of fish.
*/
#define ERROR_MAX_COUNT 1
+/**
+ The number of nanoseconds to wait between polls when attempting to acquire
+ a lockfile
+*/
+#define LOCKPOLLINTERVAL 1000000
+
struct termios shell_modes;
/**
@@ -1062,3 +1070,36 @@ wchar_t *unescape( wchar_t * in, int escape_special )
return in;
}
+/**
+ Attempt to acquire a lock based on a lockfile, waiting LOCKPOLLINTERVAL
+ nanoseconds between polls and timing out after timeout seconds,
+ thereafter forcibly attempting to obtain the lock. Returns the opened
+ lockfile's descriptor or -1 if not possible to open the file or on error.
+ Contains a race condition when the lockfile is on an NFS filesystem
+ according to Linux manpage open(2)
+*/
+int acquire_lock_file(const char *lockfile, const int timeout)
+{
+ int ret = -1;
+ struct timespec pollint;
+ struct timeval start, end;
+ double elapsed;
+
+ if (gettimeofday(&start, NULL) == 0) {
+ pollint.tv_sec = 0;
+ pollint.tv_nsec = LOCKPOLLINTERVAL;
+ while ((ret = open(lockfile, O_EXCL|O_CREAT|O_RDONLY)) == -1) {
+ if (gettimeofday(&end, NULL) != 0)
+ break;
+ elapsed = end.tv_sec + end.tv_usec/1000000.0 -
+ (start.tv_sec + start.tv_usec/1000000.0);
+ if (elapsed >= timeout) {
+ (void)unlink(lockfile);
+ ret = open(lockfile, O_EXCL|O_CREAT|O_RDONLY);
+ break;
+ }
+ (void)nanosleep(&pollint, NULL);
+ }
+ }
+ return ret;
+}
diff --git a/common.h b/common.h
index af835d60..9e2ea82e 100644
--- a/common.h
+++ b/common.h
@@ -246,3 +246,5 @@ wchar_t *unescape( wchar_t * in, int escape_special );
void block();
void unblock();
+
+int acquire_lock_file(const char *lockfile, const int timeout);
diff --git a/fishd.c b/fishd.c
index f22b37cc..8d26a109 100644
--- a/fishd.c
+++ b/fishd.c
@@ -51,6 +51,16 @@ down and save.
#define HOSTNAME_LEN 32
/**
+ The string to append to the socket name to name the lockfile
+*/
+#define LOCKPOSTFIX ".lock"
+
+/**
+ The timeout in seconds on the lockfile for critical section
+*/
+#define LOCKTIMEOUT 1
+
+/**
The list of connections to clients
*/
static connection_t *conn;
@@ -61,55 +71,112 @@ static connection_t *conn;
static int sock;
/**
- Connects to the fish socket
+ Constructs the fish socket filename
*/
-static int get_socket()
+static char *get_socket_filename(void)
{
- int s, len;
- struct sockaddr_un local;
char *name;
- char *dir = getenv( "FISHD_SOCKET_DIR" );
- char *uname = getenv( "USER" );
+ char *dir = getenv("FISHD_SOCKET_DIR");
+ char *uname = getenv("USER");
- if( !dir )
+ if (dir == NULL)
dir = "/tmp";
- if( uname==0 )
- {
+
+ if (uname == NULL) {
struct passwd *pw;
- pw = getpwuid( getuid() );
- uname = strdup( pw->pw_name );
+ pw = getpwuid(getuid());
+ uname = strdup(pw->pw_name);
}
-
- name = malloc( strlen(dir)+ strlen(uname)+ strlen(SOCK_FILENAME) + 2 );
- strcpy( name, dir );
- strcat( name, "/" );
- strcat( name, SOCK_FILENAME );
- strcat( name, uname );
- if( strlen( name ) >= UNIX_PATH_MAX )
- {
- debug( 1, L"Filename too long: '%s'", name );
- exit(1);
+ name = malloc(strlen(dir)+ strlen(uname)+ strlen(SOCK_FILENAME) + 2);
+ if (name == NULL) {
+ perror("get_socket_filename");
+ exit(EXIT_FAILURE);
+ }
+ strcpy(name, dir);
+ strcat(name, "/");
+ strcat(name, SOCK_FILENAME);
+ strcat(name, uname);
+
+ if (strlen(name) >= UNIX_PATH_MAX) {
+ debug(1, L"Filename too long: '%s'", name);
+ exit(EXIT_FAILURE);
}
+ return name;
+}
+
+/**
+ Acquire the lock file for the socket
+ Returns an fd that should be closed to unlock
+*/
+int acquire_socket_lock_file(const char *sock_name, char **lockfile)
+{
+ int fd;
+ int len = strlen(sock_name);
+
+ *lockfile = malloc(len + strlen(LOCKPOSTFIX) + 1);
+ if (*lockfile == NULL) {
+ perror("get_socket");
+ exit(EXIT_FAILURE);
+ }
+ (void)strcpy(*lockfile, sock_name);
+ (void)strcpy(*lockfile + len, LOCKPOSTFIX);
+ if ((fd = acquire_lock_file(*lockfile, LOCKTIMEOUT)) == -1) {
+ fprintf(stderr, "Unable to obtain lockfile %s, exiting",
+ *lockfile);
+ exit(EXIT_FAILURE);
+ }
+ return fd;
+}
+
+/**
+ Connects to the fish socket and starts listening for connections
+*/
+static int get_socket(void)
+{
+ int s, len, doexit = 0;
+ int exitcode = EXIT_FAILURE;
+ struct sockaddr_un local;
+ char *lockfile;
+ char *sock_name = get_socket_filename();
+ int fd_lockfile;
+
+ /**
+ Start critical section protected by lock file
+ */
+ fd_lockfile = acquire_socket_lock_file(sock_name, &lockfile);
+ debug(1, L"Acquired lockfile %s", lockfile);
- debug( 1, L"Connect to socket at %s", name );
+ local.sun_family = AF_UNIX;
+ strcpy(local.sun_path, sock_name);
+ len = strlen(local.sun_path) + sizeof(local.sun_family);
+
+ debug(1, L"Connect to socket at %s", sock_name);
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
- exit(1);
+ doexit = 1;
+ goto unlock;
}
-
- local.sun_family = AF_UNIX;
- strcpy(local.sun_path, name );
- unlink(local.sun_path);
- len = strlen(local.sun_path) + sizeof(local.sun_family);
- free( name );
+ /**
+ First check whether the socket has been opened by another fishd;
+ if so, exit with success status
+ */
+ if (connect(s, (struct sockaddr *)&local, len) == 0) {
+ debug(1, L"Socket already exists, exiting");
+ doexit = 1;
+ exitcode = 0;
+ goto unlock;
+ }
+ unlink(local.sun_path);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
- exit(1);
+ doexit = 1;
+ goto unlock;
}
+
/*
if( setsockopt( s, SOL_SOCKET, SO_PASSCRED, &on, sizeof( on ) ) )
{
@@ -118,14 +185,30 @@ static int get_socket()
}
*/
- if( fcntl( s, F_SETFL, O_NONBLOCK ) != 0 )
- {
+ if( fcntl( s, F_SETFL, O_NONBLOCK ) != 0 ) {
wperror( L"fcntl" );
- close( s );
- return -1;
-
+ close( s );
+ doexit = 1;
+ } else if (listen(s, 64) == -1) {
+ wperror(L"listen");
+ doexit = 1;
}
+
+unlock:
+ (void)close(fd_lockfile);
+ (void)unlink(lockfile);
+ debug(1, L"Released lockfile %s", lockfile);
+ /**
+ End critical section protected by lock file
+ */
+
+ free(lockfile);
+ free(sock_name);
+
+ if (doexit)
+ exit(exitcode);
+
return s;
}
@@ -277,12 +360,8 @@ static void save()
static void init()
{
program_name=L"fishd";
+
sock = get_socket();
- if (listen(sock, 64) == -1)
- {
- wperror(L"listen");
- exit(1);
- }
daemonize();