Blame


1 92eb0426 2024-03-30 thomas /*
2 92eb0426 2024-03-30 thomas * Copyright (c) 2022, 2023 Stefan Sperling <stsp@openbsd.org>
3 92eb0426 2024-03-30 thomas *
4 92eb0426 2024-03-30 thomas * Permission to use, copy, modify, and distribute this software for any
5 92eb0426 2024-03-30 thomas * purpose with or without fee is hereby granted, provided that the above
6 92eb0426 2024-03-30 thomas * copyright notice and this permission notice appear in all copies.
7 92eb0426 2024-03-30 thomas *
8 92eb0426 2024-03-30 thomas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 92eb0426 2024-03-30 thomas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 92eb0426 2024-03-30 thomas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 92eb0426 2024-03-30 thomas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 92eb0426 2024-03-30 thomas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 92eb0426 2024-03-30 thomas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 92eb0426 2024-03-30 thomas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 92eb0426 2024-03-30 thomas */
16 92eb0426 2024-03-30 thomas
17 92eb0426 2024-03-30 thomas #include <sys/types.h>
18 92eb0426 2024-03-30 thomas #include <sys/queue.h>
19 92eb0426 2024-03-30 thomas #include <sys/tree.h>
20 92eb0426 2024-03-30 thomas #include <sys/socket.h>
21 92eb0426 2024-03-30 thomas #include <sys/stat.h>
22 92eb0426 2024-03-30 thomas #include <sys/uio.h>
23 92eb0426 2024-03-30 thomas
24 92eb0426 2024-03-30 thomas #include <errno.h>
25 92eb0426 2024-03-30 thomas #include <event.h>
26 92eb0426 2024-03-30 thomas #include <limits.h>
27 92eb0426 2024-03-30 thomas #include <sha1.h>
28 92eb0426 2024-03-30 thomas #include <sha2.h>
29 92eb0426 2024-03-30 thomas #include <signal.h>
30 92eb0426 2024-03-30 thomas #include <stdint.h>
31 92eb0426 2024-03-30 thomas #include <stdio.h>
32 92eb0426 2024-03-30 thomas #include <stdlib.h>
33 92eb0426 2024-03-30 thomas #include <string.h>
34 92eb0426 2024-03-30 thomas #include <imsg.h>
35 92eb0426 2024-03-30 thomas #include <unistd.h>
36 92eb0426 2024-03-30 thomas
37 92eb0426 2024-03-30 thomas #include "got_error.h"
38 92eb0426 2024-03-30 thomas #include "got_repository.h"
39 92eb0426 2024-03-30 thomas #include "got_object.h"
40 92eb0426 2024-03-30 thomas #include "got_path.h"
41 92eb0426 2024-03-30 thomas #include "got_reference.h"
42 92eb0426 2024-03-30 thomas #include "got_opentemp.h"
43 92eb0426 2024-03-30 thomas
44 92eb0426 2024-03-30 thomas #include "got_lib_hash.h"
45 92eb0426 2024-03-30 thomas #include "got_lib_delta.h"
46 92eb0426 2024-03-30 thomas #include "got_lib_object.h"
47 92eb0426 2024-03-30 thomas #include "got_lib_object_cache.h"
48 92eb0426 2024-03-30 thomas #include "got_lib_pack.h"
49 92eb0426 2024-03-30 thomas #include "got_lib_repository.h"
50 92eb0426 2024-03-30 thomas #include "got_lib_gitproto.h"
51 92eb0426 2024-03-30 thomas
52 92eb0426 2024-03-30 thomas #include "gotd.h"
53 92eb0426 2024-03-30 thomas #include "log.h"
54 92eb0426 2024-03-30 thomas #include "session_read.h"
55 92eb0426 2024-03-30 thomas
56 92eb0426 2024-03-30 thomas enum gotd_session_read_state {
57 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_LIST_REFS,
58 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_CAPABILITIES,
59 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_WANT,
60 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_HAVE,
61 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_DONE,
62 92eb0426 2024-03-30 thomas GOTD_STATE_DONE,
63 92eb0426 2024-03-30 thomas };
64 92eb0426 2024-03-30 thomas
65 92eb0426 2024-03-30 thomas static struct gotd_session_read {
66 92eb0426 2024-03-30 thomas pid_t pid;
67 92eb0426 2024-03-30 thomas const char *title;
68 92eb0426 2024-03-30 thomas struct got_repository *repo;
69 92eb0426 2024-03-30 thomas struct gotd_repo *repo_cfg;
70 92eb0426 2024-03-30 thomas int *pack_fds;
71 92eb0426 2024-03-30 thomas int *temp_fds;
72 92eb0426 2024-03-30 thomas struct gotd_imsgev parent_iev;
73 92eb0426 2024-03-30 thomas struct gotd_imsgev notifier_iev;
74 92eb0426 2024-03-30 thomas struct timeval request_timeout;
75 92eb0426 2024-03-30 thomas enum gotd_session_read_state state;
76 92eb0426 2024-03-30 thomas struct gotd_imsgev repo_child_iev;
77 92eb0426 2024-03-30 thomas } gotd_session;
78 92eb0426 2024-03-30 thomas
79 92eb0426 2024-03-30 thomas static struct gotd_session_client {
80 92eb0426 2024-03-30 thomas struct gotd_client_capability *capabilities;
81 92eb0426 2024-03-30 thomas size_t ncapa_alloc;
82 92eb0426 2024-03-30 thomas size_t ncapabilities;
83 92eb0426 2024-03-30 thomas uint32_t id;
84 92eb0426 2024-03-30 thomas int fd;
85 92eb0426 2024-03-30 thomas int delta_cache_fd;
86 92eb0426 2024-03-30 thomas struct gotd_imsgev iev;
87 92eb0426 2024-03-30 thomas struct event tmo;
88 92eb0426 2024-03-30 thomas uid_t euid;
89 92eb0426 2024-03-30 thomas gid_t egid;
90 92eb0426 2024-03-30 thomas char *username;
91 92eb0426 2024-03-30 thomas char *packfile_path;
92 92eb0426 2024-03-30 thomas char *packidx_path;
93 92eb0426 2024-03-30 thomas int nref_updates;
94 92eb0426 2024-03-30 thomas int accept_flush_pkt;
95 92eb0426 2024-03-30 thomas int flush_disconnect;
96 92eb0426 2024-03-30 thomas } gotd_session_client;
97 92eb0426 2024-03-30 thomas
98 92eb0426 2024-03-30 thomas static void session_read_shutdown(void);
99 92eb0426 2024-03-30 thomas
100 92eb0426 2024-03-30 thomas static void
101 92eb0426 2024-03-30 thomas disconnect(struct gotd_session_client *client)
102 92eb0426 2024-03-30 thomas {
103 92eb0426 2024-03-30 thomas log_debug("uid %d: disconnecting", client->euid);
104 92eb0426 2024-03-30 thomas
105 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&gotd_session.parent_iev,
106 92eb0426 2024-03-30 thomas GOTD_IMSG_DISCONNECT, PROC_SESSION_READ, -1, NULL, 0) == -1)
107 92eb0426 2024-03-30 thomas log_warn("imsg compose DISCONNECT");
108 92eb0426 2024-03-30 thomas
109 92eb0426 2024-03-30 thomas imsg_clear(&gotd_session.repo_child_iev.ibuf);
110 92eb0426 2024-03-30 thomas event_del(&gotd_session.repo_child_iev.ev);
111 92eb0426 2024-03-30 thomas evtimer_del(&client->tmo);
112 92eb0426 2024-03-30 thomas close(client->fd);
113 92eb0426 2024-03-30 thomas if (client->delta_cache_fd != -1)
114 92eb0426 2024-03-30 thomas close(client->delta_cache_fd);
115 92eb0426 2024-03-30 thomas if (client->packfile_path) {
116 92eb0426 2024-03-30 thomas if (unlink(client->packfile_path) == -1 && errno != ENOENT)
117 92eb0426 2024-03-30 thomas log_warn("unlink %s: ", client->packfile_path);
118 92eb0426 2024-03-30 thomas free(client->packfile_path);
119 92eb0426 2024-03-30 thomas }
120 92eb0426 2024-03-30 thomas if (client->packidx_path) {
121 92eb0426 2024-03-30 thomas if (unlink(client->packidx_path) == -1 && errno != ENOENT)
122 92eb0426 2024-03-30 thomas log_warn("unlink %s: ", client->packidx_path);
123 92eb0426 2024-03-30 thomas free(client->packidx_path);
124 92eb0426 2024-03-30 thomas }
125 92eb0426 2024-03-30 thomas free(client->capabilities);
126 92eb0426 2024-03-30 thomas
127 92eb0426 2024-03-30 thomas session_read_shutdown();
128 92eb0426 2024-03-30 thomas }
129 92eb0426 2024-03-30 thomas
130 92eb0426 2024-03-30 thomas static void
131 92eb0426 2024-03-30 thomas disconnect_on_error(struct gotd_session_client *client,
132 92eb0426 2024-03-30 thomas const struct got_error *err)
133 92eb0426 2024-03-30 thomas {
134 92eb0426 2024-03-30 thomas struct imsgbuf ibuf;
135 92eb0426 2024-03-30 thomas
136 92eb0426 2024-03-30 thomas if (err->code != GOT_ERR_EOF) {
137 92eb0426 2024-03-30 thomas log_warnx("uid %d: %s", client->euid, err->msg);
138 92eb0426 2024-03-30 thomas imsg_init(&ibuf, client->fd);
139 92eb0426 2024-03-30 thomas gotd_imsg_send_error(&ibuf, 0, PROC_SESSION_READ, err);
140 92eb0426 2024-03-30 thomas imsg_clear(&ibuf);
141 92eb0426 2024-03-30 thomas }
142 92eb0426 2024-03-30 thomas
143 92eb0426 2024-03-30 thomas disconnect(client);
144 92eb0426 2024-03-30 thomas }
145 92eb0426 2024-03-30 thomas
146 92eb0426 2024-03-30 thomas static void
147 92eb0426 2024-03-30 thomas gotd_request_timeout(int fd, short events, void *arg)
148 92eb0426 2024-03-30 thomas {
149 92eb0426 2024-03-30 thomas struct gotd_session_client *client = arg;
150 92eb0426 2024-03-30 thomas
151 92eb0426 2024-03-30 thomas log_debug("disconnecting uid %d due to timeout", client->euid);
152 92eb0426 2024-03-30 thomas disconnect(client);
153 92eb0426 2024-03-30 thomas }
154 92eb0426 2024-03-30 thomas
155 92eb0426 2024-03-30 thomas static void
156 92eb0426 2024-03-30 thomas session_read_sighdlr(int sig, short event, void *arg)
157 92eb0426 2024-03-30 thomas {
158 92eb0426 2024-03-30 thomas /*
159 92eb0426 2024-03-30 thomas * Normal signal handler rules don't apply because libevent
160 92eb0426 2024-03-30 thomas * decouples for us.
161 92eb0426 2024-03-30 thomas */
162 92eb0426 2024-03-30 thomas
163 92eb0426 2024-03-30 thomas switch (sig) {
164 92eb0426 2024-03-30 thomas case SIGHUP:
165 92eb0426 2024-03-30 thomas log_info("%s: ignoring SIGHUP", __func__);
166 92eb0426 2024-03-30 thomas break;
167 92eb0426 2024-03-30 thomas case SIGUSR1:
168 92eb0426 2024-03-30 thomas log_info("%s: ignoring SIGUSR1", __func__);
169 92eb0426 2024-03-30 thomas break;
170 92eb0426 2024-03-30 thomas case SIGTERM:
171 92eb0426 2024-03-30 thomas case SIGINT:
172 92eb0426 2024-03-30 thomas session_read_shutdown();
173 92eb0426 2024-03-30 thomas /* NOTREACHED */
174 92eb0426 2024-03-30 thomas break;
175 92eb0426 2024-03-30 thomas default:
176 92eb0426 2024-03-30 thomas fatalx("unexpected signal");
177 92eb0426 2024-03-30 thomas }
178 92eb0426 2024-03-30 thomas }
179 92eb0426 2024-03-30 thomas
180 92eb0426 2024-03-30 thomas static const struct got_error *
181 92eb0426 2024-03-30 thomas recv_packfile_done(struct imsg *imsg)
182 92eb0426 2024-03-30 thomas {
183 92eb0426 2024-03-30 thomas size_t datalen;
184 92eb0426 2024-03-30 thomas
185 92eb0426 2024-03-30 thomas log_debug("packfile-done received");
186 92eb0426 2024-03-30 thomas
187 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
188 92eb0426 2024-03-30 thomas if (datalen != 0)
189 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
190 92eb0426 2024-03-30 thomas
191 92eb0426 2024-03-30 thomas return NULL;
192 92eb0426 2024-03-30 thomas }
193 92eb0426 2024-03-30 thomas
194 92eb0426 2024-03-30 thomas static void
195 92eb0426 2024-03-30 thomas session_dispatch_repo_child(int fd, short event, void *arg)
196 92eb0426 2024-03-30 thomas {
197 92eb0426 2024-03-30 thomas struct gotd_imsgev *iev = arg;
198 92eb0426 2024-03-30 thomas struct imsgbuf *ibuf = &iev->ibuf;
199 92eb0426 2024-03-30 thomas struct gotd_session_client *client = &gotd_session_client;
200 92eb0426 2024-03-30 thomas ssize_t n;
201 92eb0426 2024-03-30 thomas int shut = 0;
202 92eb0426 2024-03-30 thomas struct imsg imsg;
203 92eb0426 2024-03-30 thomas
204 92eb0426 2024-03-30 thomas if (event & EV_READ) {
205 92eb0426 2024-03-30 thomas if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
206 92eb0426 2024-03-30 thomas fatal("imsg_read error");
207 92eb0426 2024-03-30 thomas if (n == 0) {
208 92eb0426 2024-03-30 thomas /* Connection closed. */
209 92eb0426 2024-03-30 thomas shut = 1;
210 92eb0426 2024-03-30 thomas goto done;
211 92eb0426 2024-03-30 thomas }
212 92eb0426 2024-03-30 thomas }
213 92eb0426 2024-03-30 thomas
214 92eb0426 2024-03-30 thomas if (event & EV_WRITE) {
215 92eb0426 2024-03-30 thomas n = msgbuf_write(&ibuf->w);
216 92eb0426 2024-03-30 thomas if (n == -1 && errno != EAGAIN)
217 92eb0426 2024-03-30 thomas fatal("msgbuf_write");
218 92eb0426 2024-03-30 thomas if (n == 0) {
219 92eb0426 2024-03-30 thomas /* Connection closed. */
220 92eb0426 2024-03-30 thomas shut = 1;
221 92eb0426 2024-03-30 thomas goto done;
222 92eb0426 2024-03-30 thomas }
223 92eb0426 2024-03-30 thomas }
224 92eb0426 2024-03-30 thomas
225 92eb0426 2024-03-30 thomas for (;;) {
226 92eb0426 2024-03-30 thomas const struct got_error *err = NULL;
227 92eb0426 2024-03-30 thomas uint32_t client_id = 0;
228 92eb0426 2024-03-30 thomas int do_disconnect = 0;
229 92eb0426 2024-03-30 thomas
230 92eb0426 2024-03-30 thomas if ((n = imsg_get(ibuf, &imsg)) == -1)
231 92eb0426 2024-03-30 thomas fatal("%s: imsg_get error", __func__);
232 92eb0426 2024-03-30 thomas if (n == 0) /* No more messages. */
233 92eb0426 2024-03-30 thomas break;
234 92eb0426 2024-03-30 thomas
235 92eb0426 2024-03-30 thomas switch (imsg.hdr.type) {
236 92eb0426 2024-03-30 thomas case GOTD_IMSG_ERROR:
237 92eb0426 2024-03-30 thomas do_disconnect = 1;
238 92eb0426 2024-03-30 thomas err = gotd_imsg_recv_error(&client_id, &imsg);
239 92eb0426 2024-03-30 thomas break;
240 92eb0426 2024-03-30 thomas case GOTD_IMSG_PACKFILE_DONE:
241 92eb0426 2024-03-30 thomas do_disconnect = 1;
242 92eb0426 2024-03-30 thomas err = recv_packfile_done(&imsg);
243 92eb0426 2024-03-30 thomas break;
244 92eb0426 2024-03-30 thomas default:
245 92eb0426 2024-03-30 thomas log_debug("unexpected imsg %d", imsg.hdr.type);
246 92eb0426 2024-03-30 thomas break;
247 92eb0426 2024-03-30 thomas }
248 92eb0426 2024-03-30 thomas
249 92eb0426 2024-03-30 thomas if (do_disconnect) {
250 92eb0426 2024-03-30 thomas if (err)
251 92eb0426 2024-03-30 thomas disconnect_on_error(client, err);
252 92eb0426 2024-03-30 thomas else
253 92eb0426 2024-03-30 thomas disconnect(client);
254 92eb0426 2024-03-30 thomas } else {
255 92eb0426 2024-03-30 thomas if (err)
256 92eb0426 2024-03-30 thomas log_warnx("uid %d: %s", client->euid, err->msg);
257 92eb0426 2024-03-30 thomas }
258 92eb0426 2024-03-30 thomas imsg_free(&imsg);
259 92eb0426 2024-03-30 thomas }
260 92eb0426 2024-03-30 thomas done:
261 92eb0426 2024-03-30 thomas if (!shut) {
262 92eb0426 2024-03-30 thomas gotd_imsg_event_add(iev);
263 92eb0426 2024-03-30 thomas } else {
264 92eb0426 2024-03-30 thomas /* This pipe is dead. Remove its event handler */
265 92eb0426 2024-03-30 thomas event_del(&iev->ev);
266 92eb0426 2024-03-30 thomas event_loopexit(NULL);
267 92eb0426 2024-03-30 thomas }
268 92eb0426 2024-03-30 thomas }
269 92eb0426 2024-03-30 thomas
270 92eb0426 2024-03-30 thomas static const struct got_error *
271 92eb0426 2024-03-30 thomas recv_capabilities(struct gotd_session_client *client, struct imsg *imsg)
272 92eb0426 2024-03-30 thomas {
273 92eb0426 2024-03-30 thomas struct gotd_imsg_capabilities icapas;
274 92eb0426 2024-03-30 thomas size_t datalen;
275 92eb0426 2024-03-30 thomas
276 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
277 92eb0426 2024-03-30 thomas if (datalen != sizeof(icapas))
278 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
279 92eb0426 2024-03-30 thomas memcpy(&icapas, imsg->data, sizeof(icapas));
280 92eb0426 2024-03-30 thomas
281 92eb0426 2024-03-30 thomas client->ncapa_alloc = icapas.ncapabilities;
282 92eb0426 2024-03-30 thomas client->capabilities = calloc(client->ncapa_alloc,
283 92eb0426 2024-03-30 thomas sizeof(*client->capabilities));
284 92eb0426 2024-03-30 thomas if (client->capabilities == NULL) {
285 92eb0426 2024-03-30 thomas client->ncapa_alloc = 0;
286 92eb0426 2024-03-30 thomas return got_error_from_errno("calloc");
287 92eb0426 2024-03-30 thomas }
288 92eb0426 2024-03-30 thomas
289 92eb0426 2024-03-30 thomas log_debug("expecting %zu capabilities from uid %d",
290 92eb0426 2024-03-30 thomas client->ncapa_alloc, client->euid);
291 92eb0426 2024-03-30 thomas return NULL;
292 92eb0426 2024-03-30 thomas }
293 92eb0426 2024-03-30 thomas
294 92eb0426 2024-03-30 thomas static const struct got_error *
295 92eb0426 2024-03-30 thomas recv_capability(struct gotd_session_client *client, struct imsg *imsg)
296 92eb0426 2024-03-30 thomas {
297 92eb0426 2024-03-30 thomas struct gotd_imsg_capability icapa;
298 92eb0426 2024-03-30 thomas struct gotd_client_capability *capa;
299 92eb0426 2024-03-30 thomas size_t datalen;
300 92eb0426 2024-03-30 thomas char *key, *value = NULL;
301 92eb0426 2024-03-30 thomas
302 92eb0426 2024-03-30 thomas if (client->capabilities == NULL ||
303 92eb0426 2024-03-30 thomas client->ncapabilities >= client->ncapa_alloc) {
304 92eb0426 2024-03-30 thomas return got_error_msg(GOT_ERR_BAD_REQUEST,
305 92eb0426 2024-03-30 thomas "unexpected capability received");
306 92eb0426 2024-03-30 thomas }
307 92eb0426 2024-03-30 thomas
308 92eb0426 2024-03-30 thomas memset(&icapa, 0, sizeof(icapa));
309 92eb0426 2024-03-30 thomas
310 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
311 92eb0426 2024-03-30 thomas if (datalen < sizeof(icapa))
312 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
313 92eb0426 2024-03-30 thomas memcpy(&icapa, imsg->data, sizeof(icapa));
314 92eb0426 2024-03-30 thomas
315 92eb0426 2024-03-30 thomas if (datalen != sizeof(icapa) + icapa.key_len + icapa.value_len)
316 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
317 92eb0426 2024-03-30 thomas
318 92eb0426 2024-03-30 thomas key = strndup(imsg->data + sizeof(icapa), icapa.key_len);
319 92eb0426 2024-03-30 thomas if (key == NULL)
320 92eb0426 2024-03-30 thomas return got_error_from_errno("strndup");
321 92eb0426 2024-03-30 thomas if (icapa.value_len > 0) {
322 92eb0426 2024-03-30 thomas value = strndup(imsg->data + sizeof(icapa) + icapa.key_len,
323 92eb0426 2024-03-30 thomas icapa.value_len);
324 92eb0426 2024-03-30 thomas if (value == NULL) {
325 92eb0426 2024-03-30 thomas free(key);
326 92eb0426 2024-03-30 thomas return got_error_from_errno("strndup");
327 92eb0426 2024-03-30 thomas }
328 92eb0426 2024-03-30 thomas }
329 92eb0426 2024-03-30 thomas
330 92eb0426 2024-03-30 thomas capa = &client->capabilities[client->ncapabilities++];
331 92eb0426 2024-03-30 thomas capa->key = key;
332 92eb0426 2024-03-30 thomas capa->value = value;
333 92eb0426 2024-03-30 thomas
334 92eb0426 2024-03-30 thomas if (value)
335 92eb0426 2024-03-30 thomas log_debug("uid %d: capability %s=%s", client->euid, key, value);
336 92eb0426 2024-03-30 thomas else
337 92eb0426 2024-03-30 thomas log_debug("uid %d: capability %s", client->euid, key);
338 92eb0426 2024-03-30 thomas
339 92eb0426 2024-03-30 thomas return NULL;
340 92eb0426 2024-03-30 thomas }
341 92eb0426 2024-03-30 thomas
342 92eb0426 2024-03-30 thomas static const struct got_error *
343 92eb0426 2024-03-30 thomas forward_want(struct gotd_session_client *client, struct imsg *imsg)
344 92eb0426 2024-03-30 thomas {
345 92eb0426 2024-03-30 thomas struct gotd_imsg_want ireq;
346 92eb0426 2024-03-30 thomas struct gotd_imsg_want iwant;
347 92eb0426 2024-03-30 thomas size_t datalen;
348 92eb0426 2024-03-30 thomas
349 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
350 92eb0426 2024-03-30 thomas if (datalen != sizeof(ireq))
351 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
352 92eb0426 2024-03-30 thomas
353 92eb0426 2024-03-30 thomas memcpy(&ireq, imsg->data, datalen);
354 92eb0426 2024-03-30 thomas
355 92eb0426 2024-03-30 thomas memset(&iwant, 0, sizeof(iwant));
356 92eb0426 2024-03-30 thomas memcpy(iwant.object_id, ireq.object_id, SHA1_DIGEST_LENGTH);
357 92eb0426 2024-03-30 thomas
358 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&gotd_session.repo_child_iev,
359 92eb0426 2024-03-30 thomas GOTD_IMSG_WANT, PROC_SESSION_READ, -1,
360 92eb0426 2024-03-30 thomas &iwant, sizeof(iwant)) == -1)
361 92eb0426 2024-03-30 thomas return got_error_from_errno("imsg compose WANT");
362 92eb0426 2024-03-30 thomas
363 92eb0426 2024-03-30 thomas return NULL;
364 92eb0426 2024-03-30 thomas }
365 92eb0426 2024-03-30 thomas
366 92eb0426 2024-03-30 thomas static const struct got_error *
367 92eb0426 2024-03-30 thomas forward_have(struct gotd_session_client *client, struct imsg *imsg)
368 92eb0426 2024-03-30 thomas {
369 92eb0426 2024-03-30 thomas struct gotd_imsg_have ireq;
370 92eb0426 2024-03-30 thomas struct gotd_imsg_have ihave;
371 92eb0426 2024-03-30 thomas size_t datalen;
372 92eb0426 2024-03-30 thomas
373 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
374 92eb0426 2024-03-30 thomas if (datalen != sizeof(ireq))
375 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
376 92eb0426 2024-03-30 thomas
377 92eb0426 2024-03-30 thomas memcpy(&ireq, imsg->data, datalen);
378 92eb0426 2024-03-30 thomas
379 92eb0426 2024-03-30 thomas memset(&ihave, 0, sizeof(ihave));
380 92eb0426 2024-03-30 thomas memcpy(ihave.object_id, ireq.object_id, SHA1_DIGEST_LENGTH);
381 92eb0426 2024-03-30 thomas
382 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&gotd_session.repo_child_iev,
383 92eb0426 2024-03-30 thomas GOTD_IMSG_HAVE, PROC_SESSION_READ, -1,
384 92eb0426 2024-03-30 thomas &ihave, sizeof(ihave)) == -1)
385 92eb0426 2024-03-30 thomas return got_error_from_errno("imsg compose HAVE");
386 92eb0426 2024-03-30 thomas
387 92eb0426 2024-03-30 thomas return NULL;
388 92eb0426 2024-03-30 thomas }
389 92eb0426 2024-03-30 thomas
390 92eb0426 2024-03-30 thomas static int
391 92eb0426 2024-03-30 thomas client_has_capability(struct gotd_session_client *client, const char *capastr)
392 92eb0426 2024-03-30 thomas {
393 92eb0426 2024-03-30 thomas struct gotd_client_capability *capa;
394 92eb0426 2024-03-30 thomas size_t i;
395 92eb0426 2024-03-30 thomas
396 92eb0426 2024-03-30 thomas if (client->ncapabilities == 0)
397 92eb0426 2024-03-30 thomas return 0;
398 92eb0426 2024-03-30 thomas
399 92eb0426 2024-03-30 thomas for (i = 0; i < client->ncapabilities; i++) {
400 92eb0426 2024-03-30 thomas capa = &client->capabilities[i];
401 92eb0426 2024-03-30 thomas if (strcmp(capa->key, capastr) == 0)
402 92eb0426 2024-03-30 thomas return 1;
403 92eb0426 2024-03-30 thomas }
404 92eb0426 2024-03-30 thomas
405 92eb0426 2024-03-30 thomas return 0;
406 92eb0426 2024-03-30 thomas }
407 92eb0426 2024-03-30 thomas
408 92eb0426 2024-03-30 thomas static const struct got_error *
409 92eb0426 2024-03-30 thomas send_packfile(struct gotd_session_client *client)
410 92eb0426 2024-03-30 thomas {
411 92eb0426 2024-03-30 thomas const struct got_error *err = NULL;
412 92eb0426 2024-03-30 thomas struct gotd_imsg_send_packfile ipack;
413 92eb0426 2024-03-30 thomas int pipe[2];
414 92eb0426 2024-03-30 thomas
415 92eb0426 2024-03-30 thomas if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1)
416 92eb0426 2024-03-30 thomas return got_error_from_errno("socketpair");
417 92eb0426 2024-03-30 thomas
418 92eb0426 2024-03-30 thomas memset(&ipack, 0, sizeof(ipack));
419 92eb0426 2024-03-30 thomas
420 92eb0426 2024-03-30 thomas if (client_has_capability(client, GOT_CAPA_SIDE_BAND_64K))
421 92eb0426 2024-03-30 thomas ipack.report_progress = 1;
422 92eb0426 2024-03-30 thomas
423 92eb0426 2024-03-30 thomas client->delta_cache_fd = got_opentempfd();
424 92eb0426 2024-03-30 thomas if (client->delta_cache_fd == -1)
425 92eb0426 2024-03-30 thomas return got_error_from_errno("got_opentempfd");
426 92eb0426 2024-03-30 thomas
427 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&gotd_session.repo_child_iev,
428 92eb0426 2024-03-30 thomas GOTD_IMSG_SEND_PACKFILE, PROC_GOTD, client->delta_cache_fd,
429 92eb0426 2024-03-30 thomas &ipack, sizeof(ipack)) == -1) {
430 92eb0426 2024-03-30 thomas err = got_error_from_errno("imsg compose SEND_PACKFILE");
431 92eb0426 2024-03-30 thomas close(pipe[0]);
432 92eb0426 2024-03-30 thomas close(pipe[1]);
433 92eb0426 2024-03-30 thomas return err;
434 92eb0426 2024-03-30 thomas }
435 92eb0426 2024-03-30 thomas
436 92eb0426 2024-03-30 thomas /* Send pack pipe end 0 to repo child process. */
437 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&gotd_session.repo_child_iev,
438 92eb0426 2024-03-30 thomas GOTD_IMSG_PACKFILE_PIPE, PROC_GOTD, pipe[0], NULL, 0) == -1) {
439 92eb0426 2024-03-30 thomas err = got_error_from_errno("imsg compose PACKFILE_PIPE");
440 92eb0426 2024-03-30 thomas close(pipe[1]);
441 92eb0426 2024-03-30 thomas return err;
442 92eb0426 2024-03-30 thomas }
443 92eb0426 2024-03-30 thomas
444 92eb0426 2024-03-30 thomas /* Send pack pipe end 1 to gotsh(1) (expects just an fd, no data). */
445 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&client->iev,
446 92eb0426 2024-03-30 thomas GOTD_IMSG_PACKFILE_PIPE, PROC_GOTD, pipe[1], NULL, 0) == -1)
447 92eb0426 2024-03-30 thomas err = got_error_from_errno("imsg compose PACKFILE_PIPE");
448 92eb0426 2024-03-30 thomas
449 92eb0426 2024-03-30 thomas return err;
450 92eb0426 2024-03-30 thomas }
451 92eb0426 2024-03-30 thomas
452 92eb0426 2024-03-30 thomas static void
453 92eb0426 2024-03-30 thomas session_dispatch_client(int fd, short events, void *arg)
454 92eb0426 2024-03-30 thomas {
455 92eb0426 2024-03-30 thomas struct gotd_imsgev *iev = arg;
456 92eb0426 2024-03-30 thomas struct imsgbuf *ibuf = &iev->ibuf;
457 92eb0426 2024-03-30 thomas struct gotd_session_client *client = &gotd_session_client;
458 92eb0426 2024-03-30 thomas const struct got_error *err = NULL;
459 92eb0426 2024-03-30 thomas struct imsg imsg;
460 92eb0426 2024-03-30 thomas ssize_t n;
461 92eb0426 2024-03-30 thomas
462 92eb0426 2024-03-30 thomas if (events & EV_WRITE) {
463 92eb0426 2024-03-30 thomas while (ibuf->w.queued) {
464 92eb0426 2024-03-30 thomas n = msgbuf_write(&ibuf->w);
465 92eb0426 2024-03-30 thomas if (n == -1 && errno == EPIPE) {
466 92eb0426 2024-03-30 thomas /*
467 92eb0426 2024-03-30 thomas * The client has closed its socket.
468 92eb0426 2024-03-30 thomas * This can happen when Git clients are
469 92eb0426 2024-03-30 thomas * done sending pack file data.
470 92eb0426 2024-03-30 thomas */
471 92eb0426 2024-03-30 thomas msgbuf_clear(&ibuf->w);
472 92eb0426 2024-03-30 thomas continue;
473 92eb0426 2024-03-30 thomas } else if (n == -1 && errno != EAGAIN) {
474 92eb0426 2024-03-30 thomas err = got_error_from_errno("imsg_flush");
475 92eb0426 2024-03-30 thomas disconnect_on_error(client, err);
476 92eb0426 2024-03-30 thomas return;
477 92eb0426 2024-03-30 thomas }
478 92eb0426 2024-03-30 thomas if (n == 0) {
479 92eb0426 2024-03-30 thomas /* Connection closed. */
480 92eb0426 2024-03-30 thomas err = got_error(GOT_ERR_EOF);
481 92eb0426 2024-03-30 thomas disconnect_on_error(client, err);
482 92eb0426 2024-03-30 thomas return;
483 92eb0426 2024-03-30 thomas }
484 92eb0426 2024-03-30 thomas }
485 92eb0426 2024-03-30 thomas
486 92eb0426 2024-03-30 thomas if (client->flush_disconnect) {
487 92eb0426 2024-03-30 thomas disconnect(client);
488 92eb0426 2024-03-30 thomas return;
489 92eb0426 2024-03-30 thomas }
490 92eb0426 2024-03-30 thomas }
491 92eb0426 2024-03-30 thomas
492 92eb0426 2024-03-30 thomas if ((events & EV_READ) == 0)
493 92eb0426 2024-03-30 thomas return;
494 92eb0426 2024-03-30 thomas
495 92eb0426 2024-03-30 thomas memset(&imsg, 0, sizeof(imsg));
496 92eb0426 2024-03-30 thomas
497 92eb0426 2024-03-30 thomas while (err == NULL) {
498 92eb0426 2024-03-30 thomas err = gotd_imsg_recv(&imsg, ibuf, 0);
499 92eb0426 2024-03-30 thomas if (err) {
500 92eb0426 2024-03-30 thomas if (err->code == GOT_ERR_PRIVSEP_READ)
501 92eb0426 2024-03-30 thomas err = NULL;
502 92eb0426 2024-03-30 thomas else if (err->code == GOT_ERR_EOF &&
503 92eb0426 2024-03-30 thomas gotd_session.state ==
504 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_CAPABILITIES) {
505 92eb0426 2024-03-30 thomas /*
506 92eb0426 2024-03-30 thomas * The client has closed its socket before
507 92eb0426 2024-03-30 thomas * sending its capability announcement.
508 92eb0426 2024-03-30 thomas * This can happen when Git clients have
509 92eb0426 2024-03-30 thomas * no ref-updates to send.
510 92eb0426 2024-03-30 thomas */
511 92eb0426 2024-03-30 thomas disconnect_on_error(client, err);
512 92eb0426 2024-03-30 thomas return;
513 92eb0426 2024-03-30 thomas }
514 92eb0426 2024-03-30 thomas break;
515 92eb0426 2024-03-30 thomas }
516 92eb0426 2024-03-30 thomas
517 92eb0426 2024-03-30 thomas evtimer_del(&client->tmo);
518 92eb0426 2024-03-30 thomas
519 92eb0426 2024-03-30 thomas switch (imsg.hdr.type) {
520 92eb0426 2024-03-30 thomas case GOTD_IMSG_CAPABILITIES:
521 92eb0426 2024-03-30 thomas if (gotd_session.state !=
522 92eb0426 2024-03-30 thomas GOTD_STATE_EXPECT_CAPABILITIES) {
523 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
524 92eb0426 2024-03-30 thomas "unexpected capabilities received");
525 92eb0426 2024-03-30 thomas break;
526 92eb0426 2024-03-30 thomas }
527 92eb0426 2024-03-30 thomas log_debug("receiving capabilities from uid %d",
528 92eb0426 2024-03-30 thomas client->euid);
529 92eb0426 2024-03-30 thomas err = recv_capabilities(client, &imsg);
530 92eb0426 2024-03-30 thomas break;
531 92eb0426 2024-03-30 thomas case GOTD_IMSG_CAPABILITY:
532 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_CAPABILITIES) {
533 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
534 92eb0426 2024-03-30 thomas "unexpected capability received");
535 92eb0426 2024-03-30 thomas break;
536 92eb0426 2024-03-30 thomas }
537 92eb0426 2024-03-30 thomas err = recv_capability(client, &imsg);
538 92eb0426 2024-03-30 thomas if (err || client->ncapabilities < client->ncapa_alloc)
539 92eb0426 2024-03-30 thomas break;
540 92eb0426 2024-03-30 thomas gotd_session.state = GOTD_STATE_EXPECT_WANT;
541 92eb0426 2024-03-30 thomas client->accept_flush_pkt = 1;
542 92eb0426 2024-03-30 thomas log_debug("uid %d: expecting want-lines", client->euid);
543 92eb0426 2024-03-30 thomas break;
544 92eb0426 2024-03-30 thomas case GOTD_IMSG_WANT:
545 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_WANT) {
546 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
547 92eb0426 2024-03-30 thomas "unexpected want-line received");
548 92eb0426 2024-03-30 thomas break;
549 92eb0426 2024-03-30 thomas }
550 92eb0426 2024-03-30 thomas log_debug("received want-line from uid %d",
551 92eb0426 2024-03-30 thomas client->euid);
552 92eb0426 2024-03-30 thomas client->accept_flush_pkt = 1;
553 92eb0426 2024-03-30 thomas err = forward_want(client, &imsg);
554 92eb0426 2024-03-30 thomas break;
555 92eb0426 2024-03-30 thomas case GOTD_IMSG_HAVE:
556 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_HAVE) {
557 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
558 92eb0426 2024-03-30 thomas "unexpected have-line received");
559 92eb0426 2024-03-30 thomas break;
560 92eb0426 2024-03-30 thomas }
561 92eb0426 2024-03-30 thomas log_debug("received have-line from uid %d",
562 92eb0426 2024-03-30 thomas client->euid);
563 92eb0426 2024-03-30 thomas err = forward_have(client, &imsg);
564 92eb0426 2024-03-30 thomas if (err)
565 92eb0426 2024-03-30 thomas break;
566 92eb0426 2024-03-30 thomas client->accept_flush_pkt = 1;
567 92eb0426 2024-03-30 thomas break;
568 92eb0426 2024-03-30 thomas case GOTD_IMSG_FLUSH:
569 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_WANT &&
570 92eb0426 2024-03-30 thomas gotd_session.state != GOTD_STATE_EXPECT_HAVE &&
571 92eb0426 2024-03-30 thomas gotd_session.state != GOTD_STATE_EXPECT_DONE) {
572 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
573 92eb0426 2024-03-30 thomas "unexpected flush-pkt received");
574 92eb0426 2024-03-30 thomas break;
575 92eb0426 2024-03-30 thomas }
576 92eb0426 2024-03-30 thomas if (!client->accept_flush_pkt) {
577 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
578 92eb0426 2024-03-30 thomas "unexpected flush-pkt received");
579 92eb0426 2024-03-30 thomas break;
580 92eb0426 2024-03-30 thomas }
581 92eb0426 2024-03-30 thomas
582 92eb0426 2024-03-30 thomas /*
583 92eb0426 2024-03-30 thomas * Accept just one flush packet at a time.
584 92eb0426 2024-03-30 thomas * Future client state transitions will set this flag
585 92eb0426 2024-03-30 thomas * again if another flush packet is expected.
586 92eb0426 2024-03-30 thomas */
587 92eb0426 2024-03-30 thomas client->accept_flush_pkt = 0;
588 92eb0426 2024-03-30 thomas
589 92eb0426 2024-03-30 thomas log_debug("received flush-pkt from uid %d",
590 92eb0426 2024-03-30 thomas client->euid);
591 92eb0426 2024-03-30 thomas if (gotd_session.state == GOTD_STATE_EXPECT_WANT) {
592 92eb0426 2024-03-30 thomas gotd_session.state = GOTD_STATE_EXPECT_HAVE;
593 92eb0426 2024-03-30 thomas log_debug("uid %d: expecting have-lines",
594 92eb0426 2024-03-30 thomas client->euid);
595 92eb0426 2024-03-30 thomas } else if (gotd_session.state == GOTD_STATE_EXPECT_HAVE) {
596 92eb0426 2024-03-30 thomas gotd_session.state = GOTD_STATE_EXPECT_DONE;
597 92eb0426 2024-03-30 thomas client->accept_flush_pkt = 1;
598 92eb0426 2024-03-30 thomas log_debug("uid %d: expecting 'done'",
599 92eb0426 2024-03-30 thomas client->euid);
600 92eb0426 2024-03-30 thomas } else if (gotd_session.state != GOTD_STATE_EXPECT_DONE) {
601 92eb0426 2024-03-30 thomas /* should not happen, see above */
602 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
603 92eb0426 2024-03-30 thomas "unexpected client state");
604 92eb0426 2024-03-30 thomas break;
605 92eb0426 2024-03-30 thomas }
606 92eb0426 2024-03-30 thomas break;
607 92eb0426 2024-03-30 thomas case GOTD_IMSG_DONE:
608 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_HAVE &&
609 92eb0426 2024-03-30 thomas gotd_session.state != GOTD_STATE_EXPECT_DONE) {
610 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_BAD_REQUEST,
611 92eb0426 2024-03-30 thomas "unexpected flush-pkt received");
612 92eb0426 2024-03-30 thomas break;
613 92eb0426 2024-03-30 thomas }
614 92eb0426 2024-03-30 thomas log_debug("received 'done' from uid %d", client->euid);
615 92eb0426 2024-03-30 thomas gotd_session.state = GOTD_STATE_DONE;
616 92eb0426 2024-03-30 thomas client->accept_flush_pkt = 1;
617 92eb0426 2024-03-30 thomas err = send_packfile(client);
618 92eb0426 2024-03-30 thomas break;
619 92eb0426 2024-03-30 thomas default:
620 92eb0426 2024-03-30 thomas log_debug("unexpected imsg %d", imsg.hdr.type);
621 92eb0426 2024-03-30 thomas err = got_error(GOT_ERR_PRIVSEP_MSG);
622 92eb0426 2024-03-30 thomas break;
623 92eb0426 2024-03-30 thomas }
624 92eb0426 2024-03-30 thomas
625 92eb0426 2024-03-30 thomas imsg_free(&imsg);
626 92eb0426 2024-03-30 thomas }
627 92eb0426 2024-03-30 thomas
628 92eb0426 2024-03-30 thomas if (err) {
629 92eb0426 2024-03-30 thomas if (err->code != GOT_ERR_EOF)
630 92eb0426 2024-03-30 thomas disconnect_on_error(client, err);
631 92eb0426 2024-03-30 thomas } else {
632 92eb0426 2024-03-30 thomas gotd_imsg_event_add(iev);
633 92eb0426 2024-03-30 thomas evtimer_add(&client->tmo, &gotd_session.request_timeout);
634 92eb0426 2024-03-30 thomas }
635 92eb0426 2024-03-30 thomas }
636 92eb0426 2024-03-30 thomas
637 92eb0426 2024-03-30 thomas static const struct got_error *
638 92eb0426 2024-03-30 thomas list_refs_request(void)
639 92eb0426 2024-03-30 thomas {
640 92eb0426 2024-03-30 thomas static const struct got_error *err;
641 92eb0426 2024-03-30 thomas struct gotd_session_client *client = &gotd_session_client;
642 92eb0426 2024-03-30 thomas struct gotd_imsgev *iev = &gotd_session.repo_child_iev;
643 92eb0426 2024-03-30 thomas int fd;
644 92eb0426 2024-03-30 thomas
645 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_LIST_REFS)
646 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_MSG);
647 92eb0426 2024-03-30 thomas
648 92eb0426 2024-03-30 thomas fd = dup(client->fd);
649 92eb0426 2024-03-30 thomas if (fd == -1)
650 92eb0426 2024-03-30 thomas return got_error_from_errno("dup");
651 92eb0426 2024-03-30 thomas
652 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(iev, GOTD_IMSG_LIST_REFS_INTERNAL,
653 92eb0426 2024-03-30 thomas PROC_SESSION_READ, fd, NULL, 0) == -1) {
654 92eb0426 2024-03-30 thomas err = got_error_from_errno("imsg compose LIST_REFS_INTERNAL");
655 92eb0426 2024-03-30 thomas close(fd);
656 92eb0426 2024-03-30 thomas return err;
657 92eb0426 2024-03-30 thomas }
658 92eb0426 2024-03-30 thomas
659 92eb0426 2024-03-30 thomas gotd_session.state = GOTD_STATE_EXPECT_CAPABILITIES;
660 92eb0426 2024-03-30 thomas log_debug("uid %d: expecting capabilities", client->euid);
661 92eb0426 2024-03-30 thomas return NULL;
662 92eb0426 2024-03-30 thomas }
663 92eb0426 2024-03-30 thomas
664 92eb0426 2024-03-30 thomas static const struct got_error *
665 92eb0426 2024-03-30 thomas recv_connect(struct imsg *imsg)
666 92eb0426 2024-03-30 thomas {
667 92eb0426 2024-03-30 thomas struct gotd_session_client *client = &gotd_session_client;
668 92eb0426 2024-03-30 thomas struct gotd_imsg_connect iconnect;
669 92eb0426 2024-03-30 thomas size_t datalen;
670 92eb0426 2024-03-30 thomas
671 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_LIST_REFS)
672 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_MSG);
673 92eb0426 2024-03-30 thomas
674 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
675 92eb0426 2024-03-30 thomas if (datalen < sizeof(iconnect))
676 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
677 92eb0426 2024-03-30 thomas memcpy(&iconnect, imsg->data, sizeof(iconnect));
678 92eb0426 2024-03-30 thomas if (iconnect.username_len == 0 ||
679 92eb0426 2024-03-30 thomas datalen != sizeof(iconnect) + iconnect.username_len)
680 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
681 92eb0426 2024-03-30 thomas
682 92eb0426 2024-03-30 thomas client->euid = iconnect.euid;
683 92eb0426 2024-03-30 thomas client->egid = iconnect.egid;
684 92eb0426 2024-03-30 thomas client->fd = imsg_get_fd(imsg);
685 92eb0426 2024-03-30 thomas if (client->fd == -1)
686 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_NO_FD);
687 92eb0426 2024-03-30 thomas
688 92eb0426 2024-03-30 thomas client->username = strndup(imsg->data + sizeof(iconnect),
689 92eb0426 2024-03-30 thomas iconnect.username_len);
690 92eb0426 2024-03-30 thomas if (client->username == NULL)
691 92eb0426 2024-03-30 thomas return got_error_from_errno("strndup");
692 92eb0426 2024-03-30 thomas
693 92eb0426 2024-03-30 thomas imsg_init(&client->iev.ibuf, client->fd);
694 92eb0426 2024-03-30 thomas client->iev.handler = session_dispatch_client;
695 92eb0426 2024-03-30 thomas client->iev.events = EV_READ;
696 92eb0426 2024-03-30 thomas client->iev.handler_arg = NULL;
697 92eb0426 2024-03-30 thomas event_set(&client->iev.ev, client->iev.ibuf.fd, EV_READ,
698 92eb0426 2024-03-30 thomas session_dispatch_client, &client->iev);
699 92eb0426 2024-03-30 thomas gotd_imsg_event_add(&client->iev);
700 92eb0426 2024-03-30 thomas evtimer_set(&client->tmo, gotd_request_timeout, client);
701 92eb0426 2024-03-30 thomas
702 92eb0426 2024-03-30 thomas return NULL;
703 92eb0426 2024-03-30 thomas }
704 92eb0426 2024-03-30 thomas
705 92eb0426 2024-03-30 thomas static const struct got_error *
706 92eb0426 2024-03-30 thomas recv_repo_child(struct imsg *imsg)
707 92eb0426 2024-03-30 thomas {
708 92eb0426 2024-03-30 thomas struct gotd_imsg_connect_repo_child ichild;
709 92eb0426 2024-03-30 thomas struct gotd_session_client *client = &gotd_session_client;
710 92eb0426 2024-03-30 thomas size_t datalen;
711 92eb0426 2024-03-30 thomas int fd;
712 92eb0426 2024-03-30 thomas
713 92eb0426 2024-03-30 thomas if (gotd_session.state != GOTD_STATE_EXPECT_LIST_REFS)
714 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_MSG);
715 92eb0426 2024-03-30 thomas
716 92eb0426 2024-03-30 thomas /* We should already have received a pipe to the listener. */
717 92eb0426 2024-03-30 thomas if (client->fd == -1)
718 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_MSG);
719 92eb0426 2024-03-30 thomas
720 92eb0426 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
721 92eb0426 2024-03-30 thomas if (datalen != sizeof(ichild))
722 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
723 92eb0426 2024-03-30 thomas
724 92eb0426 2024-03-30 thomas memcpy(&ichild, imsg->data, sizeof(ichild));
725 92eb0426 2024-03-30 thomas
726 92eb0426 2024-03-30 thomas if (ichild.proc_id != PROC_REPO_READ)
727 92eb0426 2024-03-30 thomas return got_error_msg(GOT_ERR_PRIVSEP_MSG,
728 92eb0426 2024-03-30 thomas "bad child process type");
729 92eb0426 2024-03-30 thomas
730 92eb0426 2024-03-30 thomas fd = imsg_get_fd(imsg);
731 92eb0426 2024-03-30 thomas if (fd == -1)
732 92eb0426 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_NO_FD);
733 92eb0426 2024-03-30 thomas
734 92eb0426 2024-03-30 thomas imsg_init(&gotd_session.repo_child_iev.ibuf, fd);
735 92eb0426 2024-03-30 thomas gotd_session.repo_child_iev.handler = session_dispatch_repo_child;
736 92eb0426 2024-03-30 thomas gotd_session.repo_child_iev.events = EV_READ;
737 92eb0426 2024-03-30 thomas gotd_session.repo_child_iev.handler_arg = NULL;
738 92eb0426 2024-03-30 thomas event_set(&gotd_session.repo_child_iev.ev,
739 92eb0426 2024-03-30 thomas gotd_session.repo_child_iev.ibuf.fd, EV_READ,
740 92eb0426 2024-03-30 thomas session_dispatch_repo_child, &gotd_session.repo_child_iev);
741 92eb0426 2024-03-30 thomas gotd_imsg_event_add(&gotd_session.repo_child_iev);
742 92eb0426 2024-03-30 thomas
743 92eb0426 2024-03-30 thomas /* The "recvfd" pledge promise is no longer needed. */
744 92eb0426 2024-03-30 thomas if (pledge("stdio rpath wpath cpath sendfd fattr flock", NULL) == -1)
745 92eb0426 2024-03-30 thomas fatal("pledge");
746 92eb0426 2024-03-30 thomas
747 92eb0426 2024-03-30 thomas return NULL;
748 92eb0426 2024-03-30 thomas }
749 92eb0426 2024-03-30 thomas
750 92eb0426 2024-03-30 thomas static void
751 92eb0426 2024-03-30 thomas session_dispatch(int fd, short event, void *arg)
752 92eb0426 2024-03-30 thomas {
753 92eb0426 2024-03-30 thomas struct gotd_imsgev *iev = arg;
754 92eb0426 2024-03-30 thomas struct imsgbuf *ibuf = &iev->ibuf;
755 92eb0426 2024-03-30 thomas struct gotd_session_client *client = &gotd_session_client;
756 92eb0426 2024-03-30 thomas ssize_t n;
757 92eb0426 2024-03-30 thomas int shut = 0;
758 92eb0426 2024-03-30 thomas struct imsg imsg;
759 92eb0426 2024-03-30 thomas
760 92eb0426 2024-03-30 thomas if (event & EV_READ) {
761 92eb0426 2024-03-30 thomas if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
762 92eb0426 2024-03-30 thomas fatal("imsg_read error");
763 92eb0426 2024-03-30 thomas if (n == 0) {
764 92eb0426 2024-03-30 thomas /* Connection closed. */
765 92eb0426 2024-03-30 thomas shut = 1;
766 92eb0426 2024-03-30 thomas goto done;
767 92eb0426 2024-03-30 thomas }
768 92eb0426 2024-03-30 thomas }
769 92eb0426 2024-03-30 thomas
770 92eb0426 2024-03-30 thomas if (event & EV_WRITE) {
771 92eb0426 2024-03-30 thomas n = msgbuf_write(&ibuf->w);
772 92eb0426 2024-03-30 thomas if (n == -1 && errno != EAGAIN)
773 92eb0426 2024-03-30 thomas fatal("msgbuf_write");
774 92eb0426 2024-03-30 thomas if (n == 0) {
775 92eb0426 2024-03-30 thomas /* Connection closed. */
776 92eb0426 2024-03-30 thomas shut = 1;
777 92eb0426 2024-03-30 thomas goto done;
778 92eb0426 2024-03-30 thomas }
779 92eb0426 2024-03-30 thomas }
780 92eb0426 2024-03-30 thomas
781 92eb0426 2024-03-30 thomas for (;;) {
782 92eb0426 2024-03-30 thomas const struct got_error *err = NULL;
783 92eb0426 2024-03-30 thomas uint32_t client_id = 0;
784 92eb0426 2024-03-30 thomas int do_disconnect = 0, do_list_refs = 0;
785 92eb0426 2024-03-30 thomas
786 92eb0426 2024-03-30 thomas if ((n = imsg_get(ibuf, &imsg)) == -1)
787 92eb0426 2024-03-30 thomas fatal("%s: imsg_get error", __func__);
788 92eb0426 2024-03-30 thomas if (n == 0) /* No more messages. */
789 92eb0426 2024-03-30 thomas break;
790 92eb0426 2024-03-30 thomas
791 92eb0426 2024-03-30 thomas switch (imsg.hdr.type) {
792 92eb0426 2024-03-30 thomas case GOTD_IMSG_ERROR:
793 92eb0426 2024-03-30 thomas do_disconnect = 1;
794 92eb0426 2024-03-30 thomas err = gotd_imsg_recv_error(&client_id, &imsg);
795 92eb0426 2024-03-30 thomas break;
796 92eb0426 2024-03-30 thomas case GOTD_IMSG_CONNECT:
797 92eb0426 2024-03-30 thomas err = recv_connect(&imsg);
798 92eb0426 2024-03-30 thomas break;
799 92eb0426 2024-03-30 thomas case GOTD_IMSG_DISCONNECT:
800 92eb0426 2024-03-30 thomas do_disconnect = 1;
801 92eb0426 2024-03-30 thomas break;
802 92eb0426 2024-03-30 thomas case GOTD_IMSG_CONNECT_REPO_CHILD:
803 92eb0426 2024-03-30 thomas err = recv_repo_child(&imsg);
804 92eb0426 2024-03-30 thomas if (err)
805 92eb0426 2024-03-30 thomas break;
806 92eb0426 2024-03-30 thomas do_list_refs = 1;
807 92eb0426 2024-03-30 thomas break;
808 92eb0426 2024-03-30 thomas default:
809 92eb0426 2024-03-30 thomas log_debug("unexpected imsg %d", imsg.hdr.type);
810 92eb0426 2024-03-30 thomas break;
811 92eb0426 2024-03-30 thomas }
812 92eb0426 2024-03-30 thomas imsg_free(&imsg);
813 92eb0426 2024-03-30 thomas
814 92eb0426 2024-03-30 thomas if (do_disconnect) {
815 92eb0426 2024-03-30 thomas if (err)
816 92eb0426 2024-03-30 thomas disconnect_on_error(client, err);
817 92eb0426 2024-03-30 thomas else
818 92eb0426 2024-03-30 thomas disconnect(client);
819 92eb0426 2024-03-30 thomas } else if (do_list_refs)
820 92eb0426 2024-03-30 thomas err = list_refs_request();
821 92eb0426 2024-03-30 thomas
822 92eb0426 2024-03-30 thomas if (err)
823 92eb0426 2024-03-30 thomas log_warnx("uid %d: %s", client->euid, err->msg);
824 92eb0426 2024-03-30 thomas }
825 92eb0426 2024-03-30 thomas done:
826 92eb0426 2024-03-30 thomas if (!shut) {
827 92eb0426 2024-03-30 thomas gotd_imsg_event_add(iev);
828 92eb0426 2024-03-30 thomas } else {
829 92eb0426 2024-03-30 thomas /* This pipe is dead. Remove its event handler */
830 92eb0426 2024-03-30 thomas event_del(&iev->ev);
831 92eb0426 2024-03-30 thomas event_loopexit(NULL);
832 92eb0426 2024-03-30 thomas }
833 92eb0426 2024-03-30 thomas }
834 92eb0426 2024-03-30 thomas
835 92eb0426 2024-03-30 thomas void
836 92eb0426 2024-03-30 thomas session_read_main(const char *title, const char *repo_path,
837 92eb0426 2024-03-30 thomas int *pack_fds, int *temp_fds, struct timeval *request_timeout,
838 92eb0426 2024-03-30 thomas struct gotd_repo *repo_cfg)
839 92eb0426 2024-03-30 thomas {
840 92eb0426 2024-03-30 thomas const struct got_error *err = NULL;
841 92eb0426 2024-03-30 thomas struct event evsigint, evsigterm, evsighup, evsigusr1;
842 92eb0426 2024-03-30 thomas
843 92eb0426 2024-03-30 thomas gotd_session.title = title;
844 92eb0426 2024-03-30 thomas gotd_session.pid = getpid();
845 92eb0426 2024-03-30 thomas gotd_session.pack_fds = pack_fds;
846 92eb0426 2024-03-30 thomas gotd_session.temp_fds = temp_fds;
847 92eb0426 2024-03-30 thomas memcpy(&gotd_session.request_timeout, request_timeout,
848 92eb0426 2024-03-30 thomas sizeof(gotd_session.request_timeout));
849 92eb0426 2024-03-30 thomas gotd_session.repo_cfg = repo_cfg;
850 92eb0426 2024-03-30 thomas
851 92eb0426 2024-03-30 thomas imsg_init(&gotd_session.notifier_iev.ibuf, -1);
852 92eb0426 2024-03-30 thomas
853 92eb0426 2024-03-30 thomas err = got_repo_open(&gotd_session.repo, repo_path, NULL, pack_fds);
854 92eb0426 2024-03-30 thomas if (err)
855 92eb0426 2024-03-30 thomas goto done;
856 92eb0426 2024-03-30 thomas if (!got_repo_is_bare(gotd_session.repo)) {
857 92eb0426 2024-03-30 thomas err = got_error_msg(GOT_ERR_NOT_GIT_REPO,
858 92eb0426 2024-03-30 thomas "bare git repository required");
859 92eb0426 2024-03-30 thomas goto done;
860 92eb0426 2024-03-30 thomas }
861 92eb0426 2024-03-30 thomas
862 92eb0426 2024-03-30 thomas got_repo_temp_fds_set(gotd_session.repo, temp_fds);
863 92eb0426 2024-03-30 thomas
864 92eb0426 2024-03-30 thomas signal_set(&evsigint, SIGINT, session_read_sighdlr, NULL);
865 92eb0426 2024-03-30 thomas signal_set(&evsigterm, SIGTERM, session_read_sighdlr, NULL);
866 92eb0426 2024-03-30 thomas signal_set(&evsighup, SIGHUP, session_read_sighdlr, NULL);
867 92eb0426 2024-03-30 thomas signal_set(&evsigusr1, SIGUSR1, session_read_sighdlr, NULL);
868 92eb0426 2024-03-30 thomas signal(SIGPIPE, SIG_IGN);
869 92eb0426 2024-03-30 thomas
870 92eb0426 2024-03-30 thomas signal_add(&evsigint, NULL);
871 92eb0426 2024-03-30 thomas signal_add(&evsigterm, NULL);
872 92eb0426 2024-03-30 thomas signal_add(&evsighup, NULL);
873 92eb0426 2024-03-30 thomas signal_add(&evsigusr1, NULL);
874 92eb0426 2024-03-30 thomas
875 92eb0426 2024-03-30 thomas gotd_session.state = GOTD_STATE_EXPECT_LIST_REFS;
876 92eb0426 2024-03-30 thomas
877 92eb0426 2024-03-30 thomas gotd_session_client.fd = -1;
878 92eb0426 2024-03-30 thomas gotd_session_client.nref_updates = -1;
879 92eb0426 2024-03-30 thomas gotd_session_client.delta_cache_fd = -1;
880 92eb0426 2024-03-30 thomas gotd_session_client.accept_flush_pkt = 1;
881 92eb0426 2024-03-30 thomas
882 92eb0426 2024-03-30 thomas imsg_init(&gotd_session.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
883 92eb0426 2024-03-30 thomas gotd_session.parent_iev.handler = session_dispatch;
884 92eb0426 2024-03-30 thomas gotd_session.parent_iev.events = EV_READ;
885 92eb0426 2024-03-30 thomas gotd_session.parent_iev.handler_arg = NULL;
886 92eb0426 2024-03-30 thomas event_set(&gotd_session.parent_iev.ev, gotd_session.parent_iev.ibuf.fd,
887 92eb0426 2024-03-30 thomas EV_READ, session_dispatch, &gotd_session.parent_iev);
888 92eb0426 2024-03-30 thomas if (gotd_imsg_compose_event(&gotd_session.parent_iev,
889 92eb0426 2024-03-30 thomas GOTD_IMSG_CLIENT_SESSION_READY, PROC_SESSION_READ,
890 92eb0426 2024-03-30 thomas -1, NULL, 0) == -1) {
891 92eb0426 2024-03-30 thomas err = got_error_from_errno("imsg compose CLIENT_SESSION_READY");
892 92eb0426 2024-03-30 thomas goto done;
893 92eb0426 2024-03-30 thomas }
894 92eb0426 2024-03-30 thomas
895 92eb0426 2024-03-30 thomas event_dispatch();
896 92eb0426 2024-03-30 thomas done:
897 92eb0426 2024-03-30 thomas if (err)
898 92eb0426 2024-03-30 thomas log_warnx("%s: %s", title, err->msg);
899 92eb0426 2024-03-30 thomas session_read_shutdown();
900 92eb0426 2024-03-30 thomas }
901 92eb0426 2024-03-30 thomas
902 92eb0426 2024-03-30 thomas static void
903 92eb0426 2024-03-30 thomas session_read_shutdown(void)
904 92eb0426 2024-03-30 thomas {
905 b1a47061 2024-03-30 thomas log_debug("%s: shutting down", gotd_session.title);
906 92eb0426 2024-03-30 thomas
907 92eb0426 2024-03-30 thomas if (gotd_session.repo)
908 92eb0426 2024-03-30 thomas got_repo_close(gotd_session.repo);
909 92eb0426 2024-03-30 thomas got_repo_pack_fds_close(gotd_session.pack_fds);
910 92eb0426 2024-03-30 thomas got_repo_temp_fds_close(gotd_session.temp_fds);
911 92eb0426 2024-03-30 thomas free(gotd_session_client.username);
912 92eb0426 2024-03-30 thomas exit(0);
913 92eb0426 2024-03-30 thomas }