Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions clientserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include "rsync.h"
#include "itypes.h"
#include "ifuncs.h"
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif

extern int quiet;
extern int dry_run;
Expand Down Expand Up @@ -1722,8 +1725,10 @@ static void become_daemon(void)
}
}

/* Inetd supplies a connected IP stream on stdin. Other launchers may use a
* local socket for process I/O, which must not select inetd mode. */
/* Inetd supplies a connected IP stream on stdin. Socket activation may also
* supply a connected Unix stream whose local endpoint has a name. Other
* launchers may use an unnamed local socket for process I/O, which must not
* select inetd mode. */
static int is_inetd_socket(int fd)
{
int type;
Expand All @@ -1736,11 +1741,38 @@ static int is_inetd_socket(int fd)
|| getpeername(fd, (struct sockaddr *)&peer, &peer_len) != 0)
return 0;

return peer.ss_family == AF_INET
if (peer.ss_family == AF_INET
#ifdef INET6
|| peer.ss_family == AF_INET6
|| peer.ss_family == AF_INET6
#endif
;
)
return 1;

#ifdef HAVE_SYS_UN_H
if (peer.ss_family == AF_UNIX) {
struct sockaddr_storage local = {0};
struct sockaddr_un *local_un = (struct sockaddr_un *)&local;
socklen_t local_len = sizeof local;
size_t path_len, i;

if (getsockname(fd, (struct sockaddr *)&local, &local_len) != 0
|| local.ss_family != AF_UNIX)
return 0;

if (local_len <= (socklen_t)offsetof(struct sockaddr_un, sun_path))
return 0;
path_len = (size_t)local_len - offsetof(struct sockaddr_un, sun_path);
if (path_len > sizeof local_un->sun_path)
path_len = sizeof local_un->sun_path;
for (i = 0; i < path_len; i++) {
if (local_un->sun_path[i] != '\0')
return 1;
}
return 0;
}
#endif

return 0;
}

int daemon_main(void)
Expand Down
84 changes: 84 additions & 0 deletions testsuite/daemon-stdin-unix-listener_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""A named Unix listener on stdin selects inetd-style daemon mode."""

import os
import socket
import subprocess
import sys
import tempfile

from rsyncfns import (
SCRATCHDIR, claim_free_port, rmtree, rsync_argv, test_fail,
test_skipped, write_daemon_conf,
)

if not hasattr(socket, 'AF_UNIX'):
test_skipped('Unix-domain sockets are unavailable')
if sys.platform == 'cygwin':
test_skipped('Cygwin cannot pass a named Unix listener as daemon stdin')

base = SCRATCHDIR / 'daemon-stdin-unix-listener'
rmtree(base)
module = base / 'module'
module.mkdir(parents=True)
conf = write_daemon_conf([
('module', {'path': str(module), 'read only': 'yes'}),
])
port = claim_free_port(12980)


def check_listener(address, label):
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
child = None
proc = None
try:
try:
listener.bind(address)
listener.listen(1)
client.connect(address)
child, _ = listener.accept()
except (OSError, ValueError) as e:
test_skipped(f'{label} Unix listener is unavailable: {e}')

proc = subprocess.Popen(
rsync_argv('--daemon', '--no-detach', f'--config={conf}',
f'--port={port}'),
stdin=child.fileno(), stdout=child.fileno(),
stderr=subprocess.PIPE, close_fds=True,
)
child.close()
child = None

client.settimeout(2)
try:
greeting = client.recv(256)
except socket.timeout:
test_fail(f'{label}: daemon did not greet the accepted Unix connection')
if not greeting.startswith(b'@RSYNCD:'):
test_fail(f'{label}: unexpected daemon greeting: {greeting!r}')
finally:
if child is not None:
child.close()
client.close()
listener.close()
if proc is not None:
try:
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.terminate()
try:
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=2)


with tempfile.TemporaryDirectory(prefix='rsync-listener-') as tmpdir:
check_listener(f'{tmpdir}/rsyncd.sock', 'filesystem')

if sys.platform.startswith('linux'):
# A leading NUL selects Linux's abstract Unix namespace.
check_listener(f'\0rsync-stdin-listener-{os.getpid()}', 'abstract')

print('named Unix stdin listeners select inetd-style daemon mode')
1 change: 1 addition & 0 deletions testsuite/skiplist/cygwin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ daemon-module-chdir-symlink
daemon-module-private-parent
daemon-secrets-file-symlink
daemon-stdin-local-socket # Cygwin Python has no AF_UNIX socketpair
daemon-stdin-unix-listener # Cygwin cannot pass a named AF_UNIX listener as daemon stdin
devices
dir-sgid
early-input-symlink
Expand Down
Loading