2 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
5 #include "cmogstored.h"
6 static Hash_table
*listeners
; /* yes, we'll scale to 10K listen sockets, L10K! */
9 union mog_sockaddr msa
;
14 static bool listener_cmp(const void *a
, const void *b
)
16 const struct listener
*la
= a
;
17 const struct listener
*lb
= b
;
19 return (la
->len
== lb
->len
) &&
20 (memcmp(&la
->msa
, &lb
->msa
, lb
->len
) == 0);
23 static size_t listener_hash(const void *x
, size_t tablesize
)
25 const struct listener
*l
= x
;
29 for (i
= 0; i
< l
->len
; i
++)
30 value
= (value
* 31 + l
->msa
.bytes
[i
]) % tablesize
;
35 static void register_listen_fd(int fd
)
40 struct mog_packaddr mpa
;
41 struct sockaddr
*sa
= &tmp
.msa
.sa
;
43 tmp
.len
= (socklen_t
)sizeof(tmp
.msa
);
44 if (getsockname(fd
, sa
, &tmp
.len
) != 0)
45 die_errno("getsockname(fd=%d) failed", fd
);
47 if (sa
->sa_family
!= AF_INET
&& sa
->sa_family
!= AF_INET6
)
48 die("invalid address family=%d (not AF_INET/AF_INET6)",
51 mog_packaddr_init(&mpa
, &tmp
.msa
, tmp
.len
);
53 mog_nameinfo(&mpa
, &ni
);
55 syslog(LOG_INFO
, "inherited %s%s on fd=%d", ni
.ni_host
, ni
.ni_serv
, fd
);
57 ins
= xmalloc(sizeof(*ins
));
61 switch (hash_insert_if_absent(listeners
, ins
, NULL
)) {
63 die("duplicate listener %s%s on fd=%d",
64 ni
.ni_host
, ni
.ni_serv
, fd
);
73 static void listeners_cleanup(void)
81 static bool listener_close_each(void *_l
, void *unused
)
83 struct listener
*l
= _l
;
85 syslog(LOG_INFO
, "closing unused inherited fd=%d", l
->fd
);
92 static void listeners_init(void)
94 if (listeners
) return;
95 listeners
= hash_initialize(3, NULL
, listener_hash
, listener_cmp
, free
);
96 mog_oom_if_null(listeners
);
97 atexit(listeners_cleanup
);
100 static unsigned long listen_env(const char *env
)
102 const char *e
= getenv(env
);
106 if (!e
) return ULONG_MAX
;
108 tmp
= strtoul(e
, &end
, 10);
109 if (errno
) die_errno("failed to parse %s: %s", env
, e
);
110 if (*end
) die("trailing byte in %s: %s", env
, e
);
115 /* systemd-style socket activation in the vein of sd_listen_fds(3) */
116 static void systemd_inherit_fds(void)
118 const int listen_fds_start
= 3; /* SD_LISTEN_FDS_START */
119 int fd
, listen_fds_end
;
120 unsigned long tmp
= listen_env("LISTEN_PID");
122 if (getpid() != (pid_t
)tmp
) goto out
;
124 tmp
= listen_env("LISTEN_FDS");
125 if (tmp
> INT_MAX
) die("LISTEN_FDS out of range: %lu", tmp
);
128 listen_fds_end
= listen_fds_start
+ (int)tmp
;
129 for (fd
= listen_fds_start
; fd
< listen_fds_end
; fd
++) {
130 if (mog_set_cloexec(fd
, true) == 0)
131 register_listen_fd(fd
);
133 die("inherited out %d of %lu LISTEN_FDS",
134 fd
- listen_fds_start
, tmp
);
137 unsetenv("LISTEN_FDS");
138 unsetenv("LISTEN_PID");
141 /* close all inherited listeners we do not need */
142 void mog_inherit_cleanup(void)
147 hash_do_for_each(listeners
, listener_close_each
, NULL
);
151 /* returns the FD belonging to the address if it was inherited */
152 int mog_inherit_get(struct sockaddr
*addr
, socklen_t len
)
162 memcpy(&l
.msa
.bytes
, addr
, len
);
164 in
= hash_delete(listeners
, &l
);
168 CHECK(int, 0, mog_set_cloexec(fd
, true));
174 void mog_inherit_init(void)
176 char *orig
= getenv("CMOGSTORED_FD");
183 systemd_inherit_fds();
194 fd
= strtoul(tip
, &end
, 10);
197 die("inherited fd=%lu is too large", fd
);
198 register_listen_fd((int)fd
);
200 die_errno("strtuol failed to parse: %s", tip
);
203 /* the end (or error) */
208 tip
= end
+ 1; /* more FDs to parse */
214 die("CMOGSTORED_FD contained invalid byte: %u", endbyte
);