Blame


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