/*
* Process structure:
* client (C wrapper) connects to server
+ * (including reading ack byte)
* if fails or garbage
* === acquire lock ===
* makes new listening socket
*
* monitor [fd0: null, fd[12]: syslgo]
* other fds: call(server-end)
+ * sends ack byte
* receives args, env, fds
* forks executor
*
struct sockaddr_un sun;
-int main(int argc, const char *const *argv) {
- script = process_opts(argc, argv);
+#define ACK_BYTE '\n'
+
+// Returns: call(client-end) fd, or -1 to mean "is garbage"
+// find_socket_path must have been called
+static int attempt_connect_existing(void) {
+ int r;
+ int fd = -1;
- find_socket_path();
FILLZERO(sun);
sun.sun_family = AF_UNIX;
assert(strlen(socket_path) <= sizeof(sun.sun_path));
strncpy(sun.sun_path, socket_path, sizeof(sun.sun_path));
bool isgarbage = check_garbage();
+ if (isgarbage) goto x_garbage;
- int client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (client_fd==-1) diee("socket() for client");
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd==-1) diee("socket() for client");
salen_t salen = sizeof(sun);
r = connect(client, (const struct sockaddr*)sun, salen);
if (r==-1) {
- if (errno==ECONNREFUSED || errno==ENOENT) {
-
+ if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbgae;
+ diee("connect() %s", socket_path);
+ }
+
+ for (;;) {
+ char ack;
+ sr = read(fd, &ack, 1);
+ if (sr == -1) {
+ if (errno==ECONNRESET) goto x_garbage;
+ if (errno==EINTR) continue;
+ diee("read() ack byte");
+ }
+ if (sr == 0) { goto x_garbage; }
+ if (ack != '\n') die("got ack byte 0x%02x, not '\n'", ack);
+ return fd;
+ }
+
+ x_garbage:
+ if (fd >= 0) close(fd);
+ return -1;
+}
+
+static int connect_or_spawn(void) {
+ int fd = connect_existing();
+ if (fd >= 0) return fd;
+
+ let acquire_lock();
+ fd = connect_existing();
+
+int main(int argc, const char *const *argv) {
+ script = process_opts(argc, argv);
+
+ find_socket_path();
+ int fd = connect_or_spawn();
}