Skip to content

Commit ab45ca5

Browse files
Tim SmithMarkSymsCtx
authored andcommitted
CA-395554 Improve fairlocking reliability
Under some circumstances (when the listen queue is short enough) a process's blocking connect() to a UNIX-domain stream socket can return success even when the server side has not yet called accept(). To work around this, we have the server portion write a small fixed blob of data to each new connection and require the client to read it before it considers itself to have the lock; merely being connected successfully is no longer sufficient. Signed-off-by: Tim Smith <[email protected]>
1 parent be6922f commit ab45ca5

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

misc/fairlock/fairlock.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <sys/un.h>
66
#include <errno.h>
77
#include <syslog.h>
8+
#include <signal.h>
89

910
int main(int argc, char *argv[]) {
1011
struct sockaddr_un addr;
@@ -32,6 +33,11 @@ int main(int argc, char *argv[]) {
3233
fprintf(stderr, "listen(64) failed on socket %s: %s", argv[1], strerror(errno));
3334
exit(1);
3435
}
36+
/* We write 5 bytes to the connection when we get it from the client, but we do not
37+
* care if the client ever reads this. If they don't, we will get a SIGPIPE when we
38+
* close the socket, which we will ignore. */
39+
signal(SIGPIPE, SIG_IGN);
40+
3541
openlog("fairlock", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL2);
3642

3743
/* Now we have a socket, enter an endless loop of:
@@ -51,9 +57,15 @@ int main(int argc, char *argv[]) {
5157
while (1) {
5258
while ((fd = accept(sock, NULL, NULL)) > -1) {
5359
char buffer[128];
60+
ssize_t br;
5461

5562
syslog(LOG_INFO, "%s acquired\n", argv[1]);
56-
while (read(fd, buffer, sizeof(buffer)) > 0) {
63+
/* We do not care about the return code of this write() and will ignore any
64+
* SIGPIPE it might generate. The buffer is big enough that this will complete
65+
* even though the socket is blocking */
66+
write(fd, "LOCK", 5);
67+
while ((br = read(fd, buffer, sizeof(buffer)-1)) > 0) {
68+
buffer[br]='\0';
5769
buffer[127]='\0';
5870
syslog(LOG_INFO, "%s sent '%s'\n", argv[1], buffer);
5971
}

misc/fairlock/fairlock.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(self, name):
3636
self.name = name
3737
self.sockname = os.path.join(SOCKDIR, name)
3838
self.connected = False
39+
self.sock = None
3940

4041
def _ensure_service(self):
4142
service=f"fairlock@{self.name}.service"
@@ -52,18 +53,24 @@ def __enter__(self):
5253
raise FairlockDeadlock(f"Deadlock on Fairlock resource '{self.name}'")
5354

5455
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
56+
self.sock.setblocking(True)
5557
try:
56-
self.sock.connect(self.sockname)
58+
ret = self.sock.connect(self.sockname)
59+
# Merely being connected is not enough. Read a small blob of data.
60+
read = self.sock.recv(10)
5761
except (FileNotFoundError, ConnectionRefusedError):
5862
self._ensure_service()
59-
self.sock.connect(self.sockname)
63+
ret = self.sock.connect(self.sockname)
64+
# Merely being connected is not enough. Read a small blob of data.
65+
read = self.sock.recv(10)
6066

6167
self.sock.send(f'{os.getpid()} - {time.monotonic()}'.encode())
6268
self.connected = True
6369
return self
6470

6571
def __exit__(self, type, value, traceback):
6672
self.sock.close()
73+
self.sock = None
6774
self.connected = False
6875
return False
6976

mk/sm.spec.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,5 +237,14 @@ Manager and some other packages
237237
%{_unitdir}/[email protected]
238238
%{_libexecdir}/fairlock
239239

240+
%posttrans fairlock
241+
## On upgrade, shut down existing lock services so new ones will
242+
## be started. There should be no locks held during upgrade operations
243+
## so this is safe.
244+
if [ $1 -gt 1 ];
245+
then
246+
systemctl stop $(systemctl list-units fairlock@* --all --no-legend | cut -d' ' -f1)
247+
fi
248+
240249
%changelog
241250

0 commit comments

Comments
 (0)