cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / inherit.c
blob74e2734a0e3e260323143362271a952087d4aee1
1 /*
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>
4 */
5 #include "cmogstored.h"
6 static Hash_table *listeners; /* yes, we'll scale to 10K listen sockets, L10K! */
8 struct listener {
9 union mog_sockaddr msa;
10 socklen_t len;
11 int fd;
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;
26 size_t value = 0;
27 socklen_t i;
29 for (i = 0; i < l->len; i++)
30 value = (value * 31 + l->msa.bytes[i]) % tablesize;
32 return value;
35 static void register_listen_fd(int fd)
37 struct listener tmp;
38 struct listener *ins;
39 struct mog_ni ni;
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)",
49 (int)sa->sa_family);
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));
58 *ins = tmp;
59 ins->fd = fd;
61 switch (hash_insert_if_absent(listeners, ins, NULL)) {
62 case 0:
63 die("duplicate listener %s%s on fd=%d",
64 ni.ni_host, ni.ni_serv, fd);
65 break;
66 case 1: /* success */
67 return;
68 default:
69 mog_oom();
73 static void listeners_cleanup(void)
75 if (!listeners)
76 return;
77 hash_free(listeners);
78 listeners = NULL;
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);
86 mog_close(l->fd);
87 l->fd = -1;
89 return true;
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);
103 unsigned long tmp;
104 char *end;
106 if (!e) return ULONG_MAX;
107 errno = 0;
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);
112 return tmp;
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);
127 listeners_init();
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);
132 else
133 die("inherited out %d of %lu LISTEN_FDS",
134 fd - listen_fds_start, tmp);
136 out:
137 unsetenv("LISTEN_FDS");
138 unsetenv("LISTEN_PID");
141 /* close all inherited listeners we do not need */
142 void mog_inherit_cleanup(void)
144 if (!listeners)
145 return;
147 hash_do_for_each(listeners, listener_close_each, NULL);
148 listeners_cleanup();
151 /* returns the FD belonging to the address if it was inherited */
152 int mog_inherit_get(struct sockaddr *addr, socklen_t len)
154 struct listener l;
155 struct listener *in;
156 int fd = -1;
158 if (!listeners)
159 return fd;
161 l.len = len;
162 memcpy(&l.msa.bytes, addr, len);
164 in = hash_delete(listeners, &l);
165 if (in) {
166 fd = in->fd;
167 free(in);
168 CHECK(int, 0, mog_set_cloexec(fd, true));
171 return fd;
174 void mog_inherit_init(void)
176 char *orig = getenv("CMOGSTORED_FD");
177 char *fds;
178 char *tip;
179 char *end;
180 unsigned long fd;
181 unsigned endbyte;
183 systemd_inherit_fds();
185 if (orig == NULL)
186 return;
188 listeners_init();
189 fds = xstrdup(orig);
190 tip = fds;
192 for (;;) {
193 errno = 0;
194 fd = strtoul(tip, &end, 10);
195 if (errno == 0) {
196 if (fd > INT_MAX)
197 die("inherited fd=%lu is too large", fd);
198 register_listen_fd((int)fd);
199 } else {
200 die_errno("strtuol failed to parse: %s", tip);
203 /* the end (or error) */
204 endbyte = *end;
205 if (endbyte != ',')
206 break;
208 tip = end + 1; /* more FDs to parse */
211 free(fds);
213 if (endbyte != '\0')
214 die("CMOGSTORED_FD contained invalid byte: %u", endbyte);