Blame


1 8a35f56c 2022-07-16 thomas /*
2 8a35f56c 2022-07-16 thomas * Copyright (c) 2020-2021 Tracey Emery <tracey@traceyemery.net>
3 8a35f56c 2022-07-16 thomas * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
4 8a35f56c 2022-07-16 thomas *
5 8a35f56c 2022-07-16 thomas * Permission to use, copy, modify, and distribute this software for any
6 8a35f56c 2022-07-16 thomas * purpose with or without fee is hereby granted, provided that the above
7 8a35f56c 2022-07-16 thomas * copyright notice and this permission notice appear in all copies.
8 8a35f56c 2022-07-16 thomas *
9 8a35f56c 2022-07-16 thomas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 8a35f56c 2022-07-16 thomas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 8a35f56c 2022-07-16 thomas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 8a35f56c 2022-07-16 thomas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 8a35f56c 2022-07-16 thomas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 8a35f56c 2022-07-16 thomas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 8a35f56c 2022-07-16 thomas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 8a35f56c 2022-07-16 thomas */
17 8a35f56c 2022-07-16 thomas
18 8a35f56c 2022-07-16 thomas #include <sys/types.h>
19 8b925c6c 2022-07-16 thomas #include <sys/queue.h>
20 8a35f56c 2022-07-16 thomas #include <sys/time.h>
21 8a35f56c 2022-07-16 thomas #include <sys/uio.h>
22 8a35f56c 2022-07-16 thomas #include <sys/socket.h>
23 8a35f56c 2022-07-16 thomas
24 8a35f56c 2022-07-16 thomas #include <net/if.h>
25 8a35f56c 2022-07-16 thomas #include <netinet/in.h>
26 8a35f56c 2022-07-16 thomas
27 8a35f56c 2022-07-16 thomas #include <stdio.h>
28 8a35f56c 2022-07-16 thomas #include <stdlib.h>
29 8a35f56c 2022-07-16 thomas #include <termios.h>
30 8a35f56c 2022-07-16 thomas #include <unistd.h>
31 8a35f56c 2022-07-16 thomas #include <limits.h>
32 8a35f56c 2022-07-16 thomas #include <string.h>
33 8a35f56c 2022-07-16 thomas #include <event.h>
34 8a35f56c 2022-07-16 thomas #include <fcntl.h>
35 8a35f56c 2022-07-16 thomas #include <errno.h>
36 8a35f56c 2022-07-16 thomas
37 8a35f56c 2022-07-16 thomas #include "got_opentemp.h"
38 161663e7 2023-03-11 thomas #include "got_reference.h"
39 8a35f56c 2022-07-16 thomas
40 ff36aeea 2022-07-16 thomas #include "got_compat.h"
41 ff36aeea 2022-07-16 thomas
42 8a35f56c 2022-07-16 thomas #include "proc.h"
43 8a35f56c 2022-07-16 thomas #include "gotwebd.h"
44 8a35f56c 2022-07-16 thomas
45 8a35f56c 2022-07-16 thomas int
46 8a35f56c 2022-07-16 thomas config_init(struct gotwebd *env)
47 8a35f56c 2022-07-16 thomas {
48 8a35f56c 2022-07-16 thomas struct privsep *ps = env->gotwebd_ps;
49 8a35f56c 2022-07-16 thomas unsigned int what;
50 0b16f49b 2023-06-22 thomas
51 0b16f49b 2023-06-22 thomas strlcpy(env->httpd_chroot, D_HTTPD_CHROOT, sizeof(env->httpd_chroot));
52 8a35f56c 2022-07-16 thomas
53 8a35f56c 2022-07-16 thomas /* Global configuration. */
54 8a35f56c 2022-07-16 thomas if (privsep_process == PROC_GOTWEBD)
55 8a35f56c 2022-07-16 thomas env->prefork_gotwebd = GOTWEBD_NUMPROC;
56 8a35f56c 2022-07-16 thomas
57 8a35f56c 2022-07-16 thomas ps->ps_what[PROC_GOTWEBD] = CONFIG_ALL;
58 8a35f56c 2022-07-16 thomas ps->ps_what[PROC_SOCKS] = CONFIG_SOCKS;
59 8a35f56c 2022-07-16 thomas
60 8a35f56c 2022-07-16 thomas /* Other configuration. */
61 8a35f56c 2022-07-16 thomas what = ps->ps_what[privsep_process];
62 8a35f56c 2022-07-16 thomas if (what & CONFIG_SOCKS) {
63 8a35f56c 2022-07-16 thomas env->server_cnt = 0;
64 90d63d47 2022-08-16 thomas TAILQ_INIT(&env->servers);
65 90d63d47 2022-08-16 thomas TAILQ_INIT(&env->sockets);
66 8a35f56c 2022-07-16 thomas }
67 2a18388f 2023-05-17 thomas return 0;
68 8a35f56c 2022-07-16 thomas }
69 8a35f56c 2022-07-16 thomas
70 8a35f56c 2022-07-16 thomas int
71 8a35f56c 2022-07-16 thomas config_getcfg(struct gotwebd *env, struct imsg *imsg)
72 8a35f56c 2022-07-16 thomas {
73 8a35f56c 2022-07-16 thomas /* nothing to do but tell gotwebd configuration is done */
74 8a35f56c 2022-07-16 thomas if (privsep_process != PROC_GOTWEBD)
75 8a35f56c 2022-07-16 thomas proc_compose(env->gotwebd_ps, PROC_GOTWEBD,
76 8a35f56c 2022-07-16 thomas IMSG_CFG_DONE, NULL, 0);
77 8a35f56c 2022-07-16 thomas
78 2a18388f 2023-05-17 thomas return 0;
79 8a35f56c 2022-07-16 thomas }
80 8a35f56c 2022-07-16 thomas
81 8a35f56c 2022-07-16 thomas int
82 8a35f56c 2022-07-16 thomas config_setserver(struct gotwebd *env, struct server *srv)
83 8a35f56c 2022-07-16 thomas {
84 8a35f56c 2022-07-16 thomas struct server ssrv;
85 8a35f56c 2022-07-16 thomas struct privsep *ps = env->gotwebd_ps;
86 8a35f56c 2022-07-16 thomas
87 8a35f56c 2022-07-16 thomas memcpy(&ssrv, srv, sizeof(ssrv));
88 a004b24a 2022-09-06 thomas if (proc_compose(ps, PROC_SOCKS, IMSG_CFG_SRV, &ssrv, sizeof(ssrv))
89 a004b24a 2022-09-06 thomas == -1)
90 a004b24a 2022-09-06 thomas fatal("proc_compose");
91 2a18388f 2023-05-17 thomas return 0;
92 8a35f56c 2022-07-16 thomas }
93 8a35f56c 2022-07-16 thomas
94 8a35f56c 2022-07-16 thomas int
95 8a35f56c 2022-07-16 thomas config_getserver(struct gotwebd *env, struct imsg *imsg)
96 8a35f56c 2022-07-16 thomas {
97 8a35f56c 2022-07-16 thomas struct server *srv;
98 8a35f56c 2022-07-16 thomas uint8_t *p = imsg->data;
99 8a35f56c 2022-07-16 thomas
100 8a35f56c 2022-07-16 thomas IMSG_SIZE_CHECK(imsg, &srv);
101 8a35f56c 2022-07-16 thomas
102 8a35f56c 2022-07-16 thomas srv = calloc(1, sizeof(*srv));
103 8a35f56c 2022-07-16 thomas if (srv == NULL)
104 8a35f56c 2022-07-16 thomas fatalx("%s: calloc", __func__);
105 8a35f56c 2022-07-16 thomas
106 8a35f56c 2022-07-16 thomas if (IMSG_DATA_SIZE(imsg) != sizeof(*srv)) {
107 8a35f56c 2022-07-16 thomas log_debug("%s: imsg size error", __func__);
108 8a35f56c 2022-07-16 thomas free(srv);
109 8a35f56c 2022-07-16 thomas return 1;
110 8a35f56c 2022-07-16 thomas }
111 a004b24a 2022-09-06 thomas
112 a004b24a 2022-09-06 thomas memcpy(srv, p, sizeof(*srv));
113 a004b24a 2022-09-06 thomas srv->cached_repos = calloc(GOTWEBD_REPO_CACHESIZE,
114 a004b24a 2022-09-06 thomas sizeof(*srv->cached_repos));
115 a004b24a 2022-09-06 thomas if (srv->cached_repos == NULL)
116 a004b24a 2022-09-06 thomas fatal("%s: calloc", __func__);
117 a004b24a 2022-09-06 thomas srv->ncached_repos = 0;
118 8a35f56c 2022-07-16 thomas
119 8a35f56c 2022-07-16 thomas /* log server info */
120 8a35f56c 2022-07-16 thomas log_debug("%s: server=%s fcgi_socket=%s unix_socket=%s", __func__,
121 8a35f56c 2022-07-16 thomas srv->name, srv->fcgi_socket ? "yes" : "no", srv->unix_socket ?
122 8a35f56c 2022-07-16 thomas "yes" : "no");
123 8a35f56c 2022-07-16 thomas
124 90d63d47 2022-08-16 thomas TAILQ_INSERT_TAIL(&env->servers, srv, entry);
125 8a35f56c 2022-07-16 thomas
126 2a18388f 2023-05-17 thomas return 0;
127 8a35f56c 2022-07-16 thomas }
128 8a35f56c 2022-07-16 thomas
129 8a35f56c 2022-07-16 thomas int
130 8a35f56c 2022-07-16 thomas config_setsock(struct gotwebd *env, struct socket *sock)
131 8a35f56c 2022-07-16 thomas {
132 8a35f56c 2022-07-16 thomas struct privsep *ps = env->gotwebd_ps;
133 8a35f56c 2022-07-16 thomas struct socket_conf s;
134 8a35f56c 2022-07-16 thomas int id;
135 8a35f56c 2022-07-16 thomas int fd = -1, n, m;
136 8a35f56c 2022-07-16 thomas struct iovec iov[6];
137 8a35f56c 2022-07-16 thomas size_t c;
138 8a35f56c 2022-07-16 thomas unsigned int what;
139 8a35f56c 2022-07-16 thomas
140 8a35f56c 2022-07-16 thomas /* open listening sockets */
141 8a35f56c 2022-07-16 thomas if (sockets_privinit(env, sock) == -1)
142 8a35f56c 2022-07-16 thomas return -1;
143 8a35f56c 2022-07-16 thomas
144 8a35f56c 2022-07-16 thomas for (id = 0; id < PROC_MAX; id++) {
145 8a35f56c 2022-07-16 thomas what = ps->ps_what[id];
146 8a35f56c 2022-07-16 thomas
147 8a35f56c 2022-07-16 thomas if ((what & CONFIG_SOCKS) == 0 || id == privsep_process)
148 8a35f56c 2022-07-16 thomas continue;
149 8a35f56c 2022-07-16 thomas
150 8a35f56c 2022-07-16 thomas memcpy(&s, &sock->conf, sizeof(s));
151 8a35f56c 2022-07-16 thomas
152 8a35f56c 2022-07-16 thomas c = 0;
153 8a35f56c 2022-07-16 thomas iov[c].iov_base = &s;
154 8a35f56c 2022-07-16 thomas iov[c++].iov_len = sizeof(s);
155 8a35f56c 2022-07-16 thomas
156 8a35f56c 2022-07-16 thomas if (id == PROC_SOCKS) {
157 8a35f56c 2022-07-16 thomas /* XXX imsg code will close the fd after 1st call */
158 8a35f56c 2022-07-16 thomas n = -1;
159 8a35f56c 2022-07-16 thomas proc_range(ps, id, &n, &m);
160 8a35f56c 2022-07-16 thomas for (n = 0; n < m; n++) {
161 8a35f56c 2022-07-16 thomas if (sock->fd == -1)
162 8a35f56c 2022-07-16 thomas fd = -1;
163 8a35f56c 2022-07-16 thomas else if ((fd = dup(sock->fd)) == -1)
164 8a35f56c 2022-07-16 thomas return 1;
165 8a35f56c 2022-07-16 thomas if (proc_composev_imsg(ps, id, n, IMSG_CFG_SOCK,
166 8a35f56c 2022-07-16 thomas -1, fd, iov, c) != 0) {
167 8a35f56c 2022-07-16 thomas log_warn("%s: failed to compose "
168 8a35f56c 2022-07-16 thomas "IMSG_CFG_SOCK imsg",
169 8a35f56c 2022-07-16 thomas __func__);
170 8a35f56c 2022-07-16 thomas return 1;
171 8a35f56c 2022-07-16 thomas }
172 8a35f56c 2022-07-16 thomas if (proc_flush_imsg(ps, id, n) == -1) {
173 8a35f56c 2022-07-16 thomas log_warn("%s: failed to flush "
174 8a35f56c 2022-07-16 thomas "IMSG_CFG_SOCK imsg",
175 8a35f56c 2022-07-16 thomas __func__);
176 8a35f56c 2022-07-16 thomas return 1;
177 8a35f56c 2022-07-16 thomas }
178 8a35f56c 2022-07-16 thomas }
179 8a35f56c 2022-07-16 thomas }
180 8a35f56c 2022-07-16 thomas }
181 8a35f56c 2022-07-16 thomas
182 8a35f56c 2022-07-16 thomas /* Close socket early to prevent fd exhaustion in gotwebd. */
183 8a35f56c 2022-07-16 thomas if (sock->fd != -1) {
184 8a35f56c 2022-07-16 thomas close(sock->fd);
185 8a35f56c 2022-07-16 thomas sock->fd = -1;
186 8a35f56c 2022-07-16 thomas }
187 8a35f56c 2022-07-16 thomas
188 2a18388f 2023-05-17 thomas return 0;
189 8a35f56c 2022-07-16 thomas }
190 8a35f56c 2022-07-16 thomas
191 8a35f56c 2022-07-16 thomas int
192 8a35f56c 2022-07-16 thomas config_getsock(struct gotwebd *env, struct imsg *imsg)
193 8a35f56c 2022-07-16 thomas {
194 8a35f56c 2022-07-16 thomas struct socket *sock = NULL;
195 8a35f56c 2022-07-16 thomas struct socket_conf sock_conf;
196 8a35f56c 2022-07-16 thomas uint8_t *p = imsg->data;
197 8a35f56c 2022-07-16 thomas int i;
198 8a35f56c 2022-07-16 thomas
199 8a35f56c 2022-07-16 thomas IMSG_SIZE_CHECK(imsg, &sock_conf);
200 8a35f56c 2022-07-16 thomas memcpy(&sock_conf, p, sizeof(sock_conf));
201 8a35f56c 2022-07-16 thomas
202 8a35f56c 2022-07-16 thomas if (IMSG_DATA_SIZE(imsg) != sizeof(sock_conf)) {
203 8a35f56c 2022-07-16 thomas log_debug("%s: imsg size error", __func__);
204 8a35f56c 2022-07-16 thomas return 1;
205 8a35f56c 2022-07-16 thomas }
206 8a35f56c 2022-07-16 thomas
207 8a35f56c 2022-07-16 thomas /* create a new socket */
208 8a35f56c 2022-07-16 thomas if ((sock = calloc(1, sizeof(*sock))) == NULL) {
209 8a35f56c 2022-07-16 thomas if (imsg->fd != -1)
210 8a35f56c 2022-07-16 thomas close(imsg->fd);
211 8a35f56c 2022-07-16 thomas return 1;
212 8a35f56c 2022-07-16 thomas }
213 8a35f56c 2022-07-16 thomas
214 8a35f56c 2022-07-16 thomas memcpy(&sock->conf, &sock_conf, sizeof(sock->conf));
215 8a35f56c 2022-07-16 thomas sock->fd = imsg->fd;
216 8a35f56c 2022-07-16 thomas
217 90d63d47 2022-08-16 thomas TAILQ_INSERT_TAIL(&env->sockets, sock, entry);
218 8a35f56c 2022-07-16 thomas
219 8a35f56c 2022-07-16 thomas for (i = 0; i < PRIV_FDS__MAX; i++)
220 8a35f56c 2022-07-16 thomas sock->priv_fd[i] = -1;
221 8a35f56c 2022-07-16 thomas
222 e80b37ed 2022-07-22 thomas for (i = 0; i < GOTWEB_PACK_NUM_TEMPFILES; i++)
223 8a35f56c 2022-07-16 thomas sock->pack_fds[i] = -1;
224 8a35f56c 2022-07-16 thomas
225 8a35f56c 2022-07-16 thomas /* log new socket info */
226 720c2b05 2022-08-16 thomas log_debug("%s: name=%s id=%d server=%s af_type=%s socket_path=%s",
227 8a35f56c 2022-07-16 thomas __func__, sock->conf.name, sock->conf.id, sock->conf.srv_name,
228 720c2b05 2022-08-16 thomas sock->conf.af_type == AF_UNIX ? "unix" :
229 720c2b05 2022-08-16 thomas (sock->conf.af_type == AF_INET ? "inet" :
230 720c2b05 2022-08-16 thomas (sock->conf.af_type == AF_INET6 ? "inet6" : "unknown")),
231 102d840d 2023-06-22 thomas *sock->conf.unix_socket_name != '\0' ?
232 8a35f56c 2022-07-16 thomas sock->conf.unix_socket_name : "none");
233 8a35f56c 2022-07-16 thomas
234 2a18388f 2023-05-17 thomas return 0;
235 8a35f56c 2022-07-16 thomas }
236 8a35f56c 2022-07-16 thomas
237 8a35f56c 2022-07-16 thomas int
238 8a35f56c 2022-07-16 thomas config_setfd(struct gotwebd *env, struct socket *sock)
239 8a35f56c 2022-07-16 thomas {
240 8a35f56c 2022-07-16 thomas struct privsep *ps = env->gotwebd_ps;
241 8a35f56c 2022-07-16 thomas int id, s;
242 8a35f56c 2022-07-16 thomas int fd = -1, n, m, j;
243 8a35f56c 2022-07-16 thomas struct iovec iov[6];
244 8a35f56c 2022-07-16 thomas size_t c;
245 8a35f56c 2022-07-16 thomas unsigned int what;
246 8a35f56c 2022-07-16 thomas
247 8a35f56c 2022-07-16 thomas log_debug("%s: Allocating %d file descriptors",
248 e80b37ed 2022-07-22 thomas __func__, PRIV_FDS__MAX + GOTWEB_PACK_NUM_TEMPFILES);
249 8a35f56c 2022-07-16 thomas
250 e80b37ed 2022-07-22 thomas for (j = 0; j < PRIV_FDS__MAX + GOTWEB_PACK_NUM_TEMPFILES; j++) {
251 8a35f56c 2022-07-16 thomas for (id = 0; id < PROC_MAX; id++) {
252 8a35f56c 2022-07-16 thomas what = ps->ps_what[id];
253 8a35f56c 2022-07-16 thomas
254 8a35f56c 2022-07-16 thomas if ((what & CONFIG_SOCKS) == 0 || id == privsep_process)
255 8a35f56c 2022-07-16 thomas continue;
256 8a35f56c 2022-07-16 thomas
257 8a35f56c 2022-07-16 thomas s = sock->conf.id;
258 8a35f56c 2022-07-16 thomas c = 0;
259 8a35f56c 2022-07-16 thomas iov[c].iov_base = &s;
260 8a35f56c 2022-07-16 thomas iov[c++].iov_len = sizeof(s);
261 8a35f56c 2022-07-16 thomas
262 8a35f56c 2022-07-16 thomas if (id == PROC_SOCKS) {
263 8a35f56c 2022-07-16 thomas /*
264 8a35f56c 2022-07-16 thomas * XXX imsg code will close the fd
265 8a35f56c 2022-07-16 thomas * after 1st call
266 8a35f56c 2022-07-16 thomas */
267 8a35f56c 2022-07-16 thomas n = -1;
268 8a35f56c 2022-07-16 thomas proc_range(ps, id, &n, &m);
269 8a35f56c 2022-07-16 thomas for (n = 0; n < m; n++) {
270 8a35f56c 2022-07-16 thomas fd = got_opentempfd();
271 8a35f56c 2022-07-16 thomas if (fd == -1)
272 8a35f56c 2022-07-16 thomas return 1;
273 8a35f56c 2022-07-16 thomas if (proc_composev_imsg(ps, id, n,
274 8a35f56c 2022-07-16 thomas IMSG_CFG_FD, -1, fd, iov, c) != 0) {
275 8a35f56c 2022-07-16 thomas log_warn("%s: failed to compose "
276 8a35f56c 2022-07-16 thomas "IMSG_CFG_FD imsg",
277 8a35f56c 2022-07-16 thomas __func__);
278 8a35f56c 2022-07-16 thomas return 1;
279 8a35f56c 2022-07-16 thomas }
280 8a35f56c 2022-07-16 thomas if (proc_flush_imsg(ps, id, n) == -1) {
281 8a35f56c 2022-07-16 thomas log_warn("%s: failed to flush "
282 8a35f56c 2022-07-16 thomas "IMSG_CFG_FD imsg",
283 8a35f56c 2022-07-16 thomas __func__);
284 8a35f56c 2022-07-16 thomas return 1;
285 8a35f56c 2022-07-16 thomas }
286 8a35f56c 2022-07-16 thomas }
287 8a35f56c 2022-07-16 thomas }
288 8a35f56c 2022-07-16 thomas }
289 8a35f56c 2022-07-16 thomas
290 8a35f56c 2022-07-16 thomas /* Close fd early to prevent fd exhaustion in gotwebd. */
291 8a35f56c 2022-07-16 thomas if (fd != -1)
292 8a35f56c 2022-07-16 thomas close(fd);
293 8a35f56c 2022-07-16 thomas }
294 8a35f56c 2022-07-16 thomas return 0;
295 8a35f56c 2022-07-16 thomas }
296 8a35f56c 2022-07-16 thomas
297 8a35f56c 2022-07-16 thomas int
298 8a35f56c 2022-07-16 thomas config_getfd(struct gotwebd *env, struct imsg *imsg)
299 8a35f56c 2022-07-16 thomas {
300 8a35f56c 2022-07-16 thomas struct socket *sock;
301 8a35f56c 2022-07-16 thomas uint8_t *p = imsg->data;
302 8a35f56c 2022-07-16 thomas int sock_id, match = 0, i;
303 8a35f56c 2022-07-16 thomas
304 8a35f56c 2022-07-16 thomas IMSG_SIZE_CHECK(imsg, &sock_id);
305 8a35f56c 2022-07-16 thomas memcpy(&sock_id, p, sizeof(sock_id));
306 8a35f56c 2022-07-16 thomas
307 90d63d47 2022-08-16 thomas TAILQ_FOREACH(sock, &env->sockets, entry) {
308 e80b37ed 2022-07-22 thomas const int nfds = (GOTWEB_PACK_NUM_TEMPFILES + PRIV_FDS__MAX);
309 e80b37ed 2022-07-22 thomas for (i = 0; i < nfds; i++) {
310 8a35f56c 2022-07-16 thomas if (i < PRIV_FDS__MAX && sock->priv_fd[i] == -1) {
311 8a35f56c 2022-07-16 thomas log_debug("%s: assigning socket %d priv_fd %d",
312 8a35f56c 2022-07-16 thomas __func__, sock_id, imsg->fd);
313 8a35f56c 2022-07-16 thomas sock->priv_fd[i] = imsg->fd;
314 8a35f56c 2022-07-16 thomas match = 1;
315 8a35f56c 2022-07-16 thomas break;
316 8a35f56c 2022-07-16 thomas }
317 8a35f56c 2022-07-16 thomas if (sock->pack_fds[i - PRIV_FDS__MAX] == -1) {
318 8a35f56c 2022-07-16 thomas log_debug("%s: assigning socket %d pack_fd %d",
319 8a35f56c 2022-07-16 thomas __func__, sock_id, imsg->fd);
320 8a35f56c 2022-07-16 thomas sock->pack_fds[i - PRIV_FDS__MAX] = imsg->fd;
321 8a35f56c 2022-07-16 thomas match = 1;
322 8a35f56c 2022-07-16 thomas break;
323 8a35f56c 2022-07-16 thomas }
324 8a35f56c 2022-07-16 thomas }
325 8a35f56c 2022-07-16 thomas }
326 8a35f56c 2022-07-16 thomas
327 8a35f56c 2022-07-16 thomas if (match)
328 2a18388f 2023-05-17 thomas return 0;
329 8a35f56c 2022-07-16 thomas else
330 8a35f56c 2022-07-16 thomas return 1;
331 8a35f56c 2022-07-16 thomas }