Blame


1 ce1bfad9 2024-03-30 thomas /*
2 ce1bfad9 2024-03-30 thomas * Copyright (c) 2024 Stefan Sperling <stsp@openbsd.org>
3 ce1bfad9 2024-03-30 thomas *
4 ce1bfad9 2024-03-30 thomas * Permission to use, copy, modify, and distribute this software for any
5 ce1bfad9 2024-03-30 thomas * purpose with or without fee is hereby granted, provided that the above
6 ce1bfad9 2024-03-30 thomas * copyright notice and this permission notice appear in all copies.
7 ce1bfad9 2024-03-30 thomas *
8 ce1bfad9 2024-03-30 thomas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 ce1bfad9 2024-03-30 thomas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 ce1bfad9 2024-03-30 thomas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ce1bfad9 2024-03-30 thomas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 ce1bfad9 2024-03-30 thomas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ce1bfad9 2024-03-30 thomas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 ce1bfad9 2024-03-30 thomas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 ce1bfad9 2024-03-30 thomas */
16 ce1bfad9 2024-03-30 thomas
17 b2ce1dae 2024-03-30 thomas #include "got_compat.h"
18 b2ce1dae 2024-03-30 thomas
19 ce1bfad9 2024-03-30 thomas #include <sys/types.h>
20 ce1bfad9 2024-03-30 thomas #include <sys/queue.h>
21 ce1bfad9 2024-03-30 thomas #include <sys/socket.h>
22 ce1bfad9 2024-03-30 thomas #include <sys/wait.h>
23 ce1bfad9 2024-03-30 thomas
24 ce1bfad9 2024-03-30 thomas #include <errno.h>
25 ce1bfad9 2024-03-30 thomas #include <event.h>
26 ce1bfad9 2024-03-30 thomas #include <limits.h>
27 ce1bfad9 2024-03-30 thomas #include <signal.h>
28 ce1bfad9 2024-03-30 thomas #include <stdio.h>
29 ce1bfad9 2024-03-30 thomas #include <stdlib.h>
30 ce1bfad9 2024-03-30 thomas #include <string.h>
31 ce1bfad9 2024-03-30 thomas #include <imsg.h>
32 ce1bfad9 2024-03-30 thomas #include <unistd.h>
33 ce1bfad9 2024-03-30 thomas
34 ce1bfad9 2024-03-30 thomas #include "got_error.h"
35 ce1bfad9 2024-03-30 thomas #include "got_path.h"
36 ce1bfad9 2024-03-30 thomas
37 ce1bfad9 2024-03-30 thomas #include "gotd.h"
38 ce1bfad9 2024-03-30 thomas #include "log.h"
39 ce1bfad9 2024-03-30 thomas #include "notify.h"
40 ce1bfad9 2024-03-30 thomas
41 ce1bfad9 2024-03-30 thomas #ifndef nitems
42 ce1bfad9 2024-03-30 thomas #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
43 ce1bfad9 2024-03-30 thomas #endif
44 ce1bfad9 2024-03-30 thomas
45 ce1bfad9 2024-03-30 thomas static struct gotd_notify {
46 ce1bfad9 2024-03-30 thomas pid_t pid;
47 ce1bfad9 2024-03-30 thomas const char *title;
48 ce1bfad9 2024-03-30 thomas struct gotd_imsgev parent_iev;
49 ce1bfad9 2024-03-30 thomas struct gotd_repolist *repos;
50 ce1bfad9 2024-03-30 thomas const char *default_sender;
51 ce1bfad9 2024-03-30 thomas } gotd_notify;
52 ce1bfad9 2024-03-30 thomas
53 ce1bfad9 2024-03-30 thomas struct gotd_notify_session {
54 ce1bfad9 2024-03-30 thomas STAILQ_ENTRY(gotd_notify_session) entry;
55 ce1bfad9 2024-03-30 thomas uint32_t id;
56 ce1bfad9 2024-03-30 thomas struct gotd_imsgev iev;
57 ce1bfad9 2024-03-30 thomas };
58 ce1bfad9 2024-03-30 thomas STAILQ_HEAD(gotd_notify_sessions, gotd_notify_session);
59 ce1bfad9 2024-03-30 thomas
60 ce1bfad9 2024-03-30 thomas static struct gotd_notify_sessions gotd_notify_sessions[GOTD_CLIENT_TABLE_SIZE];
61 ce1bfad9 2024-03-30 thomas static SIPHASH_KEY sessions_hash_key;
62 ce1bfad9 2024-03-30 thomas
63 ce1bfad9 2024-03-30 thomas static void gotd_notify_shutdown(void);
64 ce1bfad9 2024-03-30 thomas
65 ce1bfad9 2024-03-30 thomas static uint64_t
66 ce1bfad9 2024-03-30 thomas session_hash(uint32_t session_id)
67 ce1bfad9 2024-03-30 thomas {
68 ce1bfad9 2024-03-30 thomas return SipHash24(&sessions_hash_key, &session_id, sizeof(session_id));
69 ce1bfad9 2024-03-30 thomas }
70 ce1bfad9 2024-03-30 thomas
71 ce1bfad9 2024-03-30 thomas static void
72 ce1bfad9 2024-03-30 thomas add_session(struct gotd_notify_session *session)
73 ce1bfad9 2024-03-30 thomas {
74 ce1bfad9 2024-03-30 thomas uint64_t slot;
75 ce1bfad9 2024-03-30 thomas
76 ce1bfad9 2024-03-30 thomas slot = session_hash(session->id) % nitems(gotd_notify_sessions);
77 ce1bfad9 2024-03-30 thomas STAILQ_INSERT_HEAD(&gotd_notify_sessions[slot], session, entry);
78 ce1bfad9 2024-03-30 thomas }
79 ce1bfad9 2024-03-30 thomas
80 ce1bfad9 2024-03-30 thomas static struct gotd_notify_session *
81 ce1bfad9 2024-03-30 thomas find_session(uint32_t session_id)
82 ce1bfad9 2024-03-30 thomas {
83 ce1bfad9 2024-03-30 thomas uint64_t slot;
84 ce1bfad9 2024-03-30 thomas struct gotd_notify_session *s;
85 ce1bfad9 2024-03-30 thomas
86 ce1bfad9 2024-03-30 thomas slot = session_hash(session_id) % nitems(gotd_notify_sessions);
87 ce1bfad9 2024-03-30 thomas STAILQ_FOREACH(s, &gotd_notify_sessions[slot], entry) {
88 ce1bfad9 2024-03-30 thomas if (s->id == session_id)
89 ce1bfad9 2024-03-30 thomas return s;
90 ce1bfad9 2024-03-30 thomas }
91 ce1bfad9 2024-03-30 thomas
92 ce1bfad9 2024-03-30 thomas return NULL;
93 ce1bfad9 2024-03-30 thomas }
94 ce1bfad9 2024-03-30 thomas
95 ce1bfad9 2024-03-30 thomas static struct gotd_notify_session *
96 ce1bfad9 2024-03-30 thomas find_session_by_fd(int fd)
97 ce1bfad9 2024-03-30 thomas {
98 ce1bfad9 2024-03-30 thomas uint64_t slot;
99 ce1bfad9 2024-03-30 thomas struct gotd_notify_session *s;
100 ce1bfad9 2024-03-30 thomas
101 ce1bfad9 2024-03-30 thomas for (slot = 0; slot < nitems(gotd_notify_sessions); slot++) {
102 ce1bfad9 2024-03-30 thomas STAILQ_FOREACH(s, &gotd_notify_sessions[slot], entry) {
103 ce1bfad9 2024-03-30 thomas if (s->iev.ibuf.fd == fd)
104 ce1bfad9 2024-03-30 thomas return s;
105 ce1bfad9 2024-03-30 thomas }
106 ce1bfad9 2024-03-30 thomas }
107 ce1bfad9 2024-03-30 thomas
108 ce1bfad9 2024-03-30 thomas return NULL;
109 ce1bfad9 2024-03-30 thomas }
110 ce1bfad9 2024-03-30 thomas
111 ce1bfad9 2024-03-30 thomas static void
112 ce1bfad9 2024-03-30 thomas remove_session(struct gotd_notify_session *session)
113 ce1bfad9 2024-03-30 thomas {
114 ce1bfad9 2024-03-30 thomas uint64_t slot;
115 ce1bfad9 2024-03-30 thomas
116 ce1bfad9 2024-03-30 thomas slot = session_hash(session->id) % nitems(gotd_notify_sessions);
117 ce1bfad9 2024-03-30 thomas STAILQ_REMOVE(&gotd_notify_sessions[slot], session,
118 ce1bfad9 2024-03-30 thomas gotd_notify_session, entry);
119 c02f62d5 2024-04-29 thomas close(session->iev.ibuf.fd);
120 ce1bfad9 2024-03-30 thomas free(session);
121 ce1bfad9 2024-03-30 thomas }
122 ce1bfad9 2024-03-30 thomas
123 ce1bfad9 2024-03-30 thomas static uint32_t
124 ce1bfad9 2024-03-30 thomas get_session_id(void)
125 ce1bfad9 2024-03-30 thomas {
126 ce1bfad9 2024-03-30 thomas int duplicate = 0;
127 ce1bfad9 2024-03-30 thomas uint32_t id;
128 ce1bfad9 2024-03-30 thomas
129 ce1bfad9 2024-03-30 thomas do {
130 ce1bfad9 2024-03-30 thomas id = arc4random();
131 ce1bfad9 2024-03-30 thomas duplicate = (find_session(id) != NULL);
132 ce1bfad9 2024-03-30 thomas } while (duplicate || id == 0);
133 ce1bfad9 2024-03-30 thomas
134 ce1bfad9 2024-03-30 thomas return id;
135 ce1bfad9 2024-03-30 thomas }
136 ce1bfad9 2024-03-30 thomas
137 ce1bfad9 2024-03-30 thomas static void
138 ce1bfad9 2024-03-30 thomas gotd_notify_sighdlr(int sig, short event, void *arg)
139 ce1bfad9 2024-03-30 thomas {
140 ce1bfad9 2024-03-30 thomas /*
141 ce1bfad9 2024-03-30 thomas * Normal signal handler rules don't apply because libevent
142 ce1bfad9 2024-03-30 thomas * decouples for us.
143 ce1bfad9 2024-03-30 thomas */
144 ce1bfad9 2024-03-30 thomas
145 ce1bfad9 2024-03-30 thomas switch (sig) {
146 ce1bfad9 2024-03-30 thomas case SIGHUP:
147 ce1bfad9 2024-03-30 thomas log_info("%s: ignoring SIGHUP", __func__);
148 ce1bfad9 2024-03-30 thomas break;
149 ce1bfad9 2024-03-30 thomas case SIGUSR1:
150 ce1bfad9 2024-03-30 thomas log_info("%s: ignoring SIGUSR1", __func__);
151 ce1bfad9 2024-03-30 thomas break;
152 ce1bfad9 2024-03-30 thomas case SIGTERM:
153 ce1bfad9 2024-03-30 thomas case SIGINT:
154 ce1bfad9 2024-03-30 thomas gotd_notify_shutdown();
155 ce1bfad9 2024-03-30 thomas /* NOTREACHED */
156 ce1bfad9 2024-03-30 thomas break;
157 ce1bfad9 2024-03-30 thomas default:
158 ce1bfad9 2024-03-30 thomas fatalx("unexpected signal");
159 ce1bfad9 2024-03-30 thomas }
160 ce1bfad9 2024-03-30 thomas }
161 ce1bfad9 2024-03-30 thomas
162 ce1bfad9 2024-03-30 thomas static void
163 f0b0cfba 2024-04-25 thomas.ad run_notification_helper(const char *prog, const char **argv, int fd,
164 f0b0cfba 2024-04-25 thomas.ad const char *user, const char *pass)
165 ce1bfad9 2024-03-30 thomas {
166 ce1bfad9 2024-03-30 thomas const struct got_error *err = NULL;
167 ce1bfad9 2024-03-30 thomas pid_t pid;
168 ce1bfad9 2024-03-30 thomas int child_status;
169 ce1bfad9 2024-03-30 thomas
170 ce1bfad9 2024-03-30 thomas pid = fork();
171 ce1bfad9 2024-03-30 thomas if (pid == -1) {
172 ce1bfad9 2024-03-30 thomas err = got_error_from_errno("fork");
173 ce1bfad9 2024-03-30 thomas log_warn("%s", err->msg);
174 ce1bfad9 2024-03-30 thomas return;
175 ce1bfad9 2024-03-30 thomas } else if (pid == 0) {
176 ce1bfad9 2024-03-30 thomas signal(SIGQUIT, SIG_DFL);
177 ce1bfad9 2024-03-30 thomas signal(SIGINT, SIG_DFL);
178 ce1bfad9 2024-03-30 thomas signal(SIGCHLD, SIG_DFL);
179 ce1bfad9 2024-03-30 thomas
180 ce1bfad9 2024-03-30 thomas if (dup2(fd, STDIN_FILENO) == -1) {
181 ce1bfad9 2024-03-30 thomas fprintf(stderr, "%s: dup2: %s\n", getprogname(),
182 ce1bfad9 2024-03-30 thomas strerror(errno));
183 ce1bfad9 2024-03-30 thomas _exit(1);
184 ce1bfad9 2024-03-30 thomas }
185 ce1bfad9 2024-03-30 thomas
186 ce1bfad9 2024-03-30 thomas closefrom(STDERR_FILENO + 1);
187 f0b0cfba 2024-04-25 thomas.ad
188 f0b0cfba 2024-04-25 thomas.ad if (user != NULL && pass != NULL) {
189 f0b0cfba 2024-04-25 thomas.ad setenv("GOT_NOTIFY_HTTP_USER", user, 1);
190 f0b0cfba 2024-04-25 thomas.ad setenv("GOT_NOTIFY_HTTP_PASS", pass, 1);
191 f0b0cfba 2024-04-25 thomas.ad }
192 ce1bfad9 2024-03-30 thomas
193 ce1bfad9 2024-03-30 thomas if (execv(prog, (char *const *)argv) == -1) {
194 ce1bfad9 2024-03-30 thomas fprintf(stderr, "%s: exec %s: %s\n", getprogname(),
195 ce1bfad9 2024-03-30 thomas prog, strerror(errno));
196 ce1bfad9 2024-03-30 thomas _exit(1);
197 ce1bfad9 2024-03-30 thomas }
198 ce1bfad9 2024-03-30 thomas
199 ce1bfad9 2024-03-30 thomas /* not reached */
200 ce1bfad9 2024-03-30 thomas }
201 ce1bfad9 2024-03-30 thomas
202 ce1bfad9 2024-03-30 thomas if (waitpid(pid, &child_status, 0) == -1) {
203 ce1bfad9 2024-03-30 thomas err = got_error_from_errno("waitpid");
204 ce1bfad9 2024-03-30 thomas goto done;
205 ce1bfad9 2024-03-30 thomas }
206 ce1bfad9 2024-03-30 thomas
207 ce1bfad9 2024-03-30 thomas if (!WIFEXITED(child_status)) {
208 ce1bfad9 2024-03-30 thomas err = got_error(GOT_ERR_PRIVSEP_DIED);
209 ce1bfad9 2024-03-30 thomas goto done;
210 ce1bfad9 2024-03-30 thomas }
211 ce1bfad9 2024-03-30 thomas
212 ce1bfad9 2024-03-30 thomas if (WEXITSTATUS(child_status) != 0)
213 ce1bfad9 2024-03-30 thomas err = got_error(GOT_ERR_PRIVSEP_EXIT);
214 ce1bfad9 2024-03-30 thomas done:
215 ce1bfad9 2024-03-30 thomas if (err)
216 ce1bfad9 2024-03-30 thomas log_warnx("%s: child %s pid %d: %s", gotd_notify.title,
217 ce1bfad9 2024-03-30 thomas prog, pid, err->msg);
218 ce1bfad9 2024-03-30 thomas }
219 ce1bfad9 2024-03-30 thomas
220 ce1bfad9 2024-03-30 thomas static void
221 ce1bfad9 2024-03-30 thomas notify_email(struct gotd_notification_target *target, const char *subject_line,
222 ce1bfad9 2024-03-30 thomas int fd)
223 ce1bfad9 2024-03-30 thomas {
224 ce1bfad9 2024-03-30 thomas const char *argv[13];
225 ce1bfad9 2024-03-30 thomas int i = 0;
226 ce1bfad9 2024-03-30 thomas
227 ce1bfad9 2024-03-30 thomas argv[i++] = GOTD_PATH_PROG_NOTIFY_EMAIL;
228 ce1bfad9 2024-03-30 thomas
229 ce1bfad9 2024-03-30 thomas argv[i++] = "-f";
230 ce1bfad9 2024-03-30 thomas if (target->conf.email.sender)
231 ce1bfad9 2024-03-30 thomas argv[i++] = target->conf.email.sender;
232 ce1bfad9 2024-03-30 thomas else
233 ce1bfad9 2024-03-30 thomas argv[i++] = gotd_notify.default_sender;
234 ce1bfad9 2024-03-30 thomas
235 ce1bfad9 2024-03-30 thomas if (target->conf.email.responder) {
236 ce1bfad9 2024-03-30 thomas argv[i++] = "-r";
237 ce1bfad9 2024-03-30 thomas argv[i++] = target->conf.email.responder;
238 ce1bfad9 2024-03-30 thomas }
239 ce1bfad9 2024-03-30 thomas
240 ce1bfad9 2024-03-30 thomas if (target->conf.email.hostname) {
241 ce1bfad9 2024-03-30 thomas argv[i++] = "-h";
242 ce1bfad9 2024-03-30 thomas argv[i++] = target->conf.email.hostname;
243 ce1bfad9 2024-03-30 thomas }
244 c50aaf5d 2024-04-25 thomas.ad
245 ce1bfad9 2024-03-30 thomas if (target->conf.email.port) {
246 ce1bfad9 2024-03-30 thomas argv[i++] = "-p";
247 ce1bfad9 2024-03-30 thomas argv[i++] = target->conf.email.port;
248 ce1bfad9 2024-03-30 thomas }
249 ce1bfad9 2024-03-30 thomas
250 ce1bfad9 2024-03-30 thomas argv[i++] = "-s";
251 ce1bfad9 2024-03-30 thomas argv[i++] = subject_line;
252 ce1bfad9 2024-03-30 thomas
253 ce1bfad9 2024-03-30 thomas argv[i++] = target->conf.email.recipient;
254 ce1bfad9 2024-03-30 thomas
255 ce1bfad9 2024-03-30 thomas argv[i] = NULL;
256 ce1bfad9 2024-03-30 thomas
257 f0b0cfba 2024-04-25 thomas.ad run_notification_helper(GOTD_PATH_PROG_NOTIFY_EMAIL, argv, fd,
258 f0b0cfba 2024-04-25 thomas.ad NULL, NULL);
259 ce1bfad9 2024-03-30 thomas }
260 ce1bfad9 2024-03-30 thomas
261 ce1bfad9 2024-03-30 thomas static void
262 bdf7ef6f 2024-04-29 thomas notify_http(struct gotd_notification_target *target, const char *repo,
263 bdf7ef6f 2024-04-29 thomas const char *username, int fd)
264 ce1bfad9 2024-03-30 thomas {
265 bdf7ef6f 2024-04-29 thomas const char *argv[12];
266 94a3f4e9 2024-03-30 thomas int argc = 0;
267 ce1bfad9 2024-03-30 thomas
268 94a3f4e9 2024-03-30 thomas argv[argc++] = GOTD_PATH_PROG_NOTIFY_HTTP;
269 94a3f4e9 2024-03-30 thomas if (target->conf.http.tls)
270 94a3f4e9 2024-03-30 thomas argv[argc++] = "-c";
271 94a3f4e9 2024-03-30 thomas
272 23022bc0 2024-04-25 thomas.ad argv[argc++] = "-r";
273 23022bc0 2024-04-25 thomas.ad argv[argc++] = repo;
274 94a3f4e9 2024-03-30 thomas argv[argc++] = "-h";
275 94a3f4e9 2024-03-30 thomas argv[argc++] = target->conf.http.hostname;
276 94a3f4e9 2024-03-30 thomas argv[argc++] = "-p";
277 94a3f4e9 2024-03-30 thomas argv[argc++] = target->conf.http.port;
278 bdf7ef6f 2024-04-29 thomas argv[argc++] = "-u";
279 bdf7ef6f 2024-04-29 thomas argv[argc++] = username;
280 94a3f4e9 2024-03-30 thomas
281 94a3f4e9 2024-03-30 thomas argv[argc++] = target->conf.http.path;
282 94a3f4e9 2024-03-30 thomas
283 94a3f4e9 2024-03-30 thomas argv[argc] = NULL;
284 94a3f4e9 2024-03-30 thomas
285 f0b0cfba 2024-04-25 thomas.ad run_notification_helper(GOTD_PATH_PROG_NOTIFY_HTTP, argv, fd,
286 f0b0cfba 2024-04-25 thomas.ad target->conf.http.user, target->conf.http.password);
287 ce1bfad9 2024-03-30 thomas }
288 ce1bfad9 2024-03-30 thomas
289 ce1bfad9 2024-03-30 thomas static const struct got_error *
290 ce1bfad9 2024-03-30 thomas send_notification(struct imsg *imsg, struct gotd_imsgev *iev)
291 ce1bfad9 2024-03-30 thomas {
292 ce1bfad9 2024-03-30 thomas const struct got_error *err = NULL;
293 ce1bfad9 2024-03-30 thomas struct gotd_imsg_notify inotify;
294 ce1bfad9 2024-03-30 thomas size_t datalen;
295 ce1bfad9 2024-03-30 thomas struct gotd_repo *repo;
296 ce1bfad9 2024-03-30 thomas struct gotd_notification_target *target;
297 ce1bfad9 2024-03-30 thomas int fd;
298 bdf7ef6f 2024-04-29 thomas char *username = NULL;
299 ce1bfad9 2024-03-30 thomas
300 ce1bfad9 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
301 bdf7ef6f 2024-04-29 thomas if (datalen < sizeof(inotify))
302 ce1bfad9 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
303 ce1bfad9 2024-03-30 thomas
304 bdf7ef6f 2024-04-29 thomas memcpy(&inotify, imsg->data, sizeof(inotify));
305 bdf7ef6f 2024-04-29 thomas if (datalen != sizeof(inotify) + inotify.username_len)
306 bdf7ef6f 2024-04-29 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
307 ce1bfad9 2024-03-30 thomas
308 ce1bfad9 2024-03-30 thomas repo = gotd_find_repo_by_name(inotify.repo_name, gotd_notify.repos);
309 ce1bfad9 2024-03-30 thomas if (repo == NULL)
310 ce1bfad9 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_MSG);
311 ce1bfad9 2024-03-30 thomas
312 ce1bfad9 2024-03-30 thomas fd = imsg_get_fd(imsg);
313 ce1bfad9 2024-03-30 thomas if (fd == -1)
314 ce1bfad9 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_NO_FD);
315 ce1bfad9 2024-03-30 thomas
316 bdf7ef6f 2024-04-29 thomas username = strndup(imsg->data + sizeof(inotify), inotify.username_len);
317 bdf7ef6f 2024-04-29 thomas if (username == NULL)
318 bdf7ef6f 2024-04-29 thomas return got_error_from_errno("strndup");
319 bdf7ef6f 2024-04-29 thomas
320 ce1bfad9 2024-03-30 thomas if (lseek(fd, 0, SEEK_SET) == -1) {
321 ce1bfad9 2024-03-30 thomas err = got_error_from_errno("lseek");
322 ce1bfad9 2024-03-30 thomas goto done;
323 ce1bfad9 2024-03-30 thomas }
324 ce1bfad9 2024-03-30 thomas
325 bdf7ef6f 2024-04-29 thomas
326 ce1bfad9 2024-03-30 thomas STAILQ_FOREACH(target, &repo->notification_targets, entry) {
327 ce1bfad9 2024-03-30 thomas switch (target->type) {
328 ce1bfad9 2024-03-30 thomas case GOTD_NOTIFICATION_VIA_EMAIL:
329 ce1bfad9 2024-03-30 thomas notify_email(target, inotify.subject_line, fd);
330 ce1bfad9 2024-03-30 thomas break;
331 ce1bfad9 2024-03-30 thomas case GOTD_NOTIFICATION_VIA_HTTP:
332 bdf7ef6f 2024-04-29 thomas notify_http(target, repo->name, username, fd);
333 ce1bfad9 2024-03-30 thomas break;
334 ce1bfad9 2024-03-30 thomas }
335 ce1bfad9 2024-03-30 thomas }
336 ce1bfad9 2024-03-30 thomas
337 ce1bfad9 2024-03-30 thomas if (gotd_imsg_compose_event(iev, GOTD_IMSG_NOTIFICATION_SENT,
338 ce1bfad9 2024-03-30 thomas PROC_NOTIFY, -1, NULL, 0) == -1) {
339 ce1bfad9 2024-03-30 thomas err = got_error_from_errno("imsg compose NOTIFY");
340 ce1bfad9 2024-03-30 thomas goto done;
341 ce1bfad9 2024-03-30 thomas }
342 ce1bfad9 2024-03-30 thomas done:
343 ce1bfad9 2024-03-30 thomas close(fd);
344 bdf7ef6f 2024-04-29 thomas free(username);
345 ce1bfad9 2024-03-30 thomas return err;
346 ce1bfad9 2024-03-30 thomas }
347 ce1bfad9 2024-03-30 thomas
348 ce1bfad9 2024-03-30 thomas static void
349 ce1bfad9 2024-03-30 thomas notify_dispatch_session(int fd, short event, void *arg)
350 ce1bfad9 2024-03-30 thomas {
351 ce1bfad9 2024-03-30 thomas struct gotd_imsgev *iev = arg;
352 ce1bfad9 2024-03-30 thomas struct imsgbuf *ibuf = &iev->ibuf;
353 ce1bfad9 2024-03-30 thomas ssize_t n;
354 ce1bfad9 2024-03-30 thomas int shut = 0;
355 ce1bfad9 2024-03-30 thomas struct imsg imsg;
356 ce1bfad9 2024-03-30 thomas
357 ce1bfad9 2024-03-30 thomas if (event & EV_READ) {
358 ce1bfad9 2024-03-30 thomas if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
359 ce1bfad9 2024-03-30 thomas fatal("imsg_read error");
360 ce1bfad9 2024-03-30 thomas if (n == 0) {
361 ce1bfad9 2024-03-30 thomas /* Connection closed. */
362 ce1bfad9 2024-03-30 thomas shut = 1;
363 ce1bfad9 2024-03-30 thomas goto done;
364 ce1bfad9 2024-03-30 thomas }
365 ce1bfad9 2024-03-30 thomas }
366 ce1bfad9 2024-03-30 thomas
367 ce1bfad9 2024-03-30 thomas if (event & EV_WRITE) {
368 ce1bfad9 2024-03-30 thomas n = msgbuf_write(&ibuf->w);
369 7ede118f 2024-04-29 thomas if (n == -1 && errno != EAGAIN && errno != EPIPE)
370 ce1bfad9 2024-03-30 thomas fatal("msgbuf_write");
371 7ede118f 2024-04-29 thomas if (n == 0 || (n == -1 && errno == EPIPE)) {
372 ce1bfad9 2024-03-30 thomas /* Connection closed. */
373 ce1bfad9 2024-03-30 thomas shut = 1;
374 ce1bfad9 2024-03-30 thomas goto done;
375 ce1bfad9 2024-03-30 thomas }
376 ce1bfad9 2024-03-30 thomas }
377 ce1bfad9 2024-03-30 thomas
378 ce1bfad9 2024-03-30 thomas for (;;) {
379 ce1bfad9 2024-03-30 thomas const struct got_error *err = NULL;
380 ce1bfad9 2024-03-30 thomas
381 ce1bfad9 2024-03-30 thomas if ((n = imsg_get(ibuf, &imsg)) == -1)
382 ce1bfad9 2024-03-30 thomas fatal("%s: imsg_get error", __func__);
383 ce1bfad9 2024-03-30 thomas if (n == 0) /* No more messages. */
384 ce1bfad9 2024-03-30 thomas break;
385 ce1bfad9 2024-03-30 thomas
386 ce1bfad9 2024-03-30 thomas switch (imsg.hdr.type) {
387 ce1bfad9 2024-03-30 thomas case GOTD_IMSG_NOTIFY:
388 ce1bfad9 2024-03-30 thomas err = send_notification(&imsg, iev);
389 ce1bfad9 2024-03-30 thomas break;
390 ce1bfad9 2024-03-30 thomas default:
391 ce1bfad9 2024-03-30 thomas log_debug("unexpected imsg %d", imsg.hdr.type);
392 ce1bfad9 2024-03-30 thomas break;
393 ce1bfad9 2024-03-30 thomas }
394 ce1bfad9 2024-03-30 thomas imsg_free(&imsg);
395 ce1bfad9 2024-03-30 thomas
396 ce1bfad9 2024-03-30 thomas if (err)
397 ce1bfad9 2024-03-30 thomas log_warnx("%s: %s", __func__, err->msg);
398 ce1bfad9 2024-03-30 thomas }
399 ce1bfad9 2024-03-30 thomas done:
400 ce1bfad9 2024-03-30 thomas if (!shut) {
401 ce1bfad9 2024-03-30 thomas gotd_imsg_event_add(iev);
402 ce1bfad9 2024-03-30 thomas } else {
403 ce1bfad9 2024-03-30 thomas struct gotd_notify_session *session;
404 ce1bfad9 2024-03-30 thomas
405 ce1bfad9 2024-03-30 thomas /* This pipe is dead. Remove its event handler */
406 ce1bfad9 2024-03-30 thomas event_del(&iev->ev);
407 ce1bfad9 2024-03-30 thomas imsg_clear(&iev->ibuf);
408 ce1bfad9 2024-03-30 thomas
409 ce1bfad9 2024-03-30 thomas session = find_session_by_fd(fd);
410 ce1bfad9 2024-03-30 thomas if (session)
411 ce1bfad9 2024-03-30 thomas remove_session(session);
412 ce1bfad9 2024-03-30 thomas }
413 ce1bfad9 2024-03-30 thomas }
414 ce1bfad9 2024-03-30 thomas
415 ce1bfad9 2024-03-30 thomas static const struct got_error *
416 ce1bfad9 2024-03-30 thomas recv_session(struct imsg *imsg)
417 ce1bfad9 2024-03-30 thomas {
418 ce1bfad9 2024-03-30 thomas struct gotd_notify_session *session;
419 ce1bfad9 2024-03-30 thomas size_t datalen;
420 ce1bfad9 2024-03-30 thomas int fd;
421 ce1bfad9 2024-03-30 thomas
422 ce1bfad9 2024-03-30 thomas datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
423 ce1bfad9 2024-03-30 thomas if (datalen != 0)
424 ce1bfad9 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_LEN);
425 ce1bfad9 2024-03-30 thomas
426 ce1bfad9 2024-03-30 thomas fd = imsg_get_fd(imsg);
427 ce1bfad9 2024-03-30 thomas if (fd == -1)
428 ce1bfad9 2024-03-30 thomas return got_error(GOT_ERR_PRIVSEP_NO_FD);
429 ce1bfad9 2024-03-30 thomas
430 ce1bfad9 2024-03-30 thomas session = calloc(1, sizeof(*session));
431 ce1bfad9 2024-03-30 thomas if (session == NULL)
432 ce1bfad9 2024-03-30 thomas return got_error_from_errno("calloc");
433 ce1bfad9 2024-03-30 thomas
434 ce1bfad9 2024-03-30 thomas session->id = get_session_id();
435 ce1bfad9 2024-03-30 thomas imsg_init(&session->iev.ibuf, fd);
436 ce1bfad9 2024-03-30 thomas session->iev.handler = notify_dispatch_session;
437 ce1bfad9 2024-03-30 thomas session->iev.events = EV_READ;
438 ce1bfad9 2024-03-30 thomas session->iev.handler_arg = NULL;
439 ce1bfad9 2024-03-30 thomas event_set(&session->iev.ev, session->iev.ibuf.fd, EV_READ,
440 ce1bfad9 2024-03-30 thomas notify_dispatch_session, &session->iev);
441 ce1bfad9 2024-03-30 thomas gotd_imsg_event_add(&session->iev);
442 ce1bfad9 2024-03-30 thomas add_session(session);
443 ce1bfad9 2024-03-30 thomas
444 ce1bfad9 2024-03-30 thomas return NULL;
445 ce1bfad9 2024-03-30 thomas }
446 ce1bfad9 2024-03-30 thomas
447 ce1bfad9 2024-03-30 thomas static void
448 ce1bfad9 2024-03-30 thomas notify_dispatch(int fd, short event, void *arg)
449 ce1bfad9 2024-03-30 thomas {
450 ce1bfad9 2024-03-30 thomas struct gotd_imsgev *iev = arg;
451 ce1bfad9 2024-03-30 thomas struct imsgbuf *ibuf = &iev->ibuf;
452 ce1bfad9 2024-03-30 thomas ssize_t n;
453 ce1bfad9 2024-03-30 thomas int shut = 0;
454 ce1bfad9 2024-03-30 thomas struct imsg imsg;
455 ce1bfad9 2024-03-30 thomas
456 ce1bfad9 2024-03-30 thomas if (event & EV_READ) {
457 ce1bfad9 2024-03-30 thomas if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
458 ce1bfad9 2024-03-30 thomas fatal("imsg_read error");
459 ce1bfad9 2024-03-30 thomas if (n == 0) {
460 ce1bfad9 2024-03-30 thomas /* Connection closed. */
461 ce1bfad9 2024-03-30 thomas shut = 1;
462 ce1bfad9 2024-03-30 thomas goto done;
463 ce1bfad9 2024-03-30 thomas }
464 ce1bfad9 2024-03-30 thomas }
465 ce1bfad9 2024-03-30 thomas
466 ce1bfad9 2024-03-30 thomas if (event & EV_WRITE) {
467 ce1bfad9 2024-03-30 thomas n = msgbuf_write(&ibuf->w);
468 6d3ee6a5 2024-04-29 thomas if (n == -1 && errno != EAGAIN)
469 ce1bfad9 2024-03-30 thomas fatal("msgbuf_write");
470 6d3ee6a5 2024-04-29 thomas if (n == 0) {
471 ce1bfad9 2024-03-30 thomas /* Connection closed. */
472 ce1bfad9 2024-03-30 thomas shut = 1;
473 ce1bfad9 2024-03-30 thomas goto done;
474 ce1bfad9 2024-03-30 thomas }
475 ce1bfad9 2024-03-30 thomas }
476 ce1bfad9 2024-03-30 thomas
477 ce1bfad9 2024-03-30 thomas for (;;) {
478 ce1bfad9 2024-03-30 thomas const struct got_error *err = NULL;
479 ce1bfad9 2024-03-30 thomas
480 ce1bfad9 2024-03-30 thomas if ((n = imsg_get(ibuf, &imsg)) == -1)
481 ce1bfad9 2024-03-30 thomas fatal("%s: imsg_get error", __func__);
482 ce1bfad9 2024-03-30 thomas if (n == 0) /* No more messages. */
483 ce1bfad9 2024-03-30 thomas break;
484 ce1bfad9 2024-03-30 thomas
485 ce1bfad9 2024-03-30 thomas switch (imsg.hdr.type) {
486 ce1bfad9 2024-03-30 thomas case GOTD_IMSG_CONNECT_SESSION:
487 ce1bfad9 2024-03-30 thomas err = recv_session(&imsg);
488 ce1bfad9 2024-03-30 thomas break;
489 ce1bfad9 2024-03-30 thomas default:
490 ce1bfad9 2024-03-30 thomas log_debug("unexpected imsg %d", imsg.hdr.type);
491 ce1bfad9 2024-03-30 thomas break;
492 ce1bfad9 2024-03-30 thomas }
493 ce1bfad9 2024-03-30 thomas imsg_free(&imsg);
494 ce1bfad9 2024-03-30 thomas
495 ce1bfad9 2024-03-30 thomas if (err)
496 ce1bfad9 2024-03-30 thomas log_warnx("%s: %s", __func__, err->msg);
497 ce1bfad9 2024-03-30 thomas }
498 ce1bfad9 2024-03-30 thomas done:
499 ce1bfad9 2024-03-30 thomas if (!shut) {
500 ce1bfad9 2024-03-30 thomas gotd_imsg_event_add(iev);
501 ce1bfad9 2024-03-30 thomas } else {
502 ce1bfad9 2024-03-30 thomas /* This pipe is dead. Remove its event handler */
503 ce1bfad9 2024-03-30 thomas event_del(&iev->ev);
504 ce1bfad9 2024-03-30 thomas event_loopexit(NULL);
505 ce1bfad9 2024-03-30 thomas }
506 ce1bfad9 2024-03-30 thomas
507 ce1bfad9 2024-03-30 thomas }
508 ce1bfad9 2024-03-30 thomas
509 ce1bfad9 2024-03-30 thomas void
510 ce1bfad9 2024-03-30 thomas notify_main(const char *title, struct gotd_repolist *repos,
511 ce1bfad9 2024-03-30 thomas const char *default_sender)
512 ce1bfad9 2024-03-30 thomas {
513 ce1bfad9 2024-03-30 thomas const struct got_error *err = NULL;
514 ce1bfad9 2024-03-30 thomas struct event evsigint, evsigterm, evsighup, evsigusr1;
515 ce1bfad9 2024-03-30 thomas
516 ce1bfad9 2024-03-30 thomas arc4random_buf(&sessions_hash_key, sizeof(sessions_hash_key));
517 ce1bfad9 2024-03-30 thomas
518 ce1bfad9 2024-03-30 thomas gotd_notify.title = title;
519 ce1bfad9 2024-03-30 thomas gotd_notify.repos = repos;
520 ce1bfad9 2024-03-30 thomas gotd_notify.default_sender = default_sender;
521 ce1bfad9 2024-03-30 thomas gotd_notify.pid = getpid();
522 ce1bfad9 2024-03-30 thomas
523 ce1bfad9 2024-03-30 thomas signal_set(&evsigint, SIGINT, gotd_notify_sighdlr, NULL);
524 ce1bfad9 2024-03-30 thomas signal_set(&evsigterm, SIGTERM, gotd_notify_sighdlr, NULL);
525 ce1bfad9 2024-03-30 thomas signal_set(&evsighup, SIGHUP, gotd_notify_sighdlr, NULL);
526 ce1bfad9 2024-03-30 thomas signal_set(&evsigusr1, SIGUSR1, gotd_notify_sighdlr, NULL);
527 ce1bfad9 2024-03-30 thomas signal(SIGPIPE, SIG_IGN);
528 ce1bfad9 2024-03-30 thomas
529 ce1bfad9 2024-03-30 thomas signal_add(&evsigint, NULL);
530 ce1bfad9 2024-03-30 thomas signal_add(&evsigterm, NULL);
531 ce1bfad9 2024-03-30 thomas signal_add(&evsighup, NULL);
532 ce1bfad9 2024-03-30 thomas signal_add(&evsigusr1, NULL);
533 ce1bfad9 2024-03-30 thomas
534 ce1bfad9 2024-03-30 thomas imsg_init(&gotd_notify.parent_iev.ibuf, GOTD_FILENO_MSG_PIPE);
535 ce1bfad9 2024-03-30 thomas gotd_notify.parent_iev.handler = notify_dispatch;
536 ce1bfad9 2024-03-30 thomas gotd_notify.parent_iev.events = EV_READ;
537 ce1bfad9 2024-03-30 thomas gotd_notify.parent_iev.handler_arg = NULL;
538 ce1bfad9 2024-03-30 thomas event_set(&gotd_notify.parent_iev.ev, gotd_notify.parent_iev.ibuf.fd,
539 ce1bfad9 2024-03-30 thomas EV_READ, notify_dispatch, &gotd_notify.parent_iev);
540 ce1bfad9 2024-03-30 thomas gotd_imsg_event_add(&gotd_notify.parent_iev);
541 ce1bfad9 2024-03-30 thomas
542 ce1bfad9 2024-03-30 thomas event_dispatch();
543 ce1bfad9 2024-03-30 thomas
544 ce1bfad9 2024-03-30 thomas if (err)
545 ce1bfad9 2024-03-30 thomas log_warnx("%s: %s", title, err->msg);
546 ce1bfad9 2024-03-30 thomas gotd_notify_shutdown();
547 ce1bfad9 2024-03-30 thomas }
548 ce1bfad9 2024-03-30 thomas
549 ce1bfad9 2024-03-30 thomas void
550 ce1bfad9 2024-03-30 thomas gotd_notify_shutdown(void)
551 ce1bfad9 2024-03-30 thomas {
552 b1a47061 2024-03-30 thomas log_debug("%s: shutting down", gotd_notify.title);
553 ce1bfad9 2024-03-30 thomas exit(0);
554 ce1bfad9 2024-03-30 thomas }