summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-11-08 03:19:58 +0000
committerDavid Robillard <d@drobilla.net>2015-11-08 03:19:58 +0000
commit36219bce05febf199e7ed6433d428c7821010620 (patch)
tree70e8c083111ca4c3dd2014a24085124244a817d4
parent9c2bb9e0e2bfd726eef5d7680bc674915817832f (diff)
downloadingen-36219bce05febf199e7ed6433d428c7821010620.tar.gz
ingen-36219bce05febf199e7ed6433d428c7821010620.tar.bz2
ingen-36219bce05febf199e7ed6433d428c7821010620.zip
Gracefully handle UNIX socket after a crash
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5814 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--src/server/SocketListener.cpp62
1 files changed, 59 insertions, 3 deletions
diff --git a/src/server/SocketListener.cpp b/src/server/SocketListener.cpp
index b00a5c79..462b2a37 100644
--- a/src/server/SocketListener.cpp
+++ b/src/server/SocketListener.cpp
@@ -16,8 +16,12 @@
#include <errno.h>
#include <poll.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <sstream>
+#include <string>
#include <thread>
#include "ingen/Configuration.hpp"
@@ -34,6 +38,26 @@
namespace Ingen {
namespace Server {
+static std::string
+get_link_target(const char* link_path)
+{
+ // Stat the link to get the required size for the target path
+ struct stat link_stat;
+ if (lstat(link_path, &link_stat)) {
+ return std::string();
+ }
+
+ // Allocate buffer and read link target
+ char* target = (char*)calloc(1, link_stat.st_size + 1);
+ if (readlink(link_path, target, link_stat.st_size) != -1) {
+ const std::string result(target);
+ free(target);
+ return result;
+ }
+
+ return std::string();
+}
+
void
SocketListener::ingen_listen(Engine* engine,
Raul::Socket* unix_sock,
@@ -41,15 +65,43 @@ SocketListener::ingen_listen(Engine* engine,
{
Ingen::World* world = engine->world();
- const std::string unix_path(world->conf().option("socket").ptr<char>());
+ const std::string link_path(world->conf().option("socket").ptr<char>());
+ const std::string unix_path(link_path + "." + std::to_string(getpid()));
- // Bind UNIX socket
+ // Bind UNIX socket and create PID-less symbolic link
const Raul::URI unix_uri(unix_scheme + unix_path);
+ bool make_link = true;
if (!unix_sock->bind(unix_uri) || !unix_sock->listen()) {
world->log().error("Failed to create UNIX socket\n");
unix_sock->close();
+ make_link = false;
} else {
- world->log().info(fmt("Listening on socket %1%\n") % unix_uri);
+ const std::string old_path = get_link_target(link_path.c_str());
+ if (!old_path.empty()) {
+ const std::string suffix = old_path.substr(old_path.find_last_of(".") + 1);
+ const pid_t pid = std::stoi(suffix);
+ if (!kill(pid, 0)) {
+ make_link = false;
+ world->log().warn(fmt("Another Ingen instance is running at %1% => %2%\n")
+ % link_path % old_path);
+ } else {
+ world->log().warn(fmt("Replacing old link %1% => %2%\n")
+ % link_path % old_path);
+ unlink(link_path.c_str());
+ }
+ }
+
+ if (make_link) {
+ if (!symlink(unix_path.c_str(), link_path.c_str())) {
+ world->log().info(fmt("Listening on %1%\n") %
+ (unix_scheme + link_path));
+ } else {
+ world->log().error(fmt("Failed to link %1% => %2% (%3%)\n")
+ % link_path % unix_path % strerror(errno));
+ }
+ } else {
+ world->log().info(fmt("Listening on %1%\n") % unix_uri);
+ }
}
// Bind TCP socket
@@ -109,6 +161,10 @@ SocketListener::ingen_listen(Engine* engine,
}
}
}
+
+ if (make_link) {
+ unlink(link_path.c_str());
+ }
}
} // namespace Server