2 * Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/types.h>
18 #include <sys/queue.h>
31 #include "got_compat.h"
33 #include "got_error.h"
34 #include "got_object.h"
35 #include "got_repository.h"
37 #include "got_lib_delta.h"
38 #include "got_lib_object.h"
39 #include "got_lib_privsep.h"
40 #include "got_lib_gitconfig.h"
42 static volatile sig_atomic_t sigint_received
;
45 catch_sigint(int signo
)
50 static const struct got_error
*
51 send_gitconfig_int(struct imsgbuf
*ibuf
, int value
)
53 if (imsg_compose(ibuf
, GOT_IMSG_GITCONFIG_INT_VAL
, 0, 0, -1,
54 &value
, sizeof(value
)) == -1)
55 return got_error_from_errno("imsg_compose GITCONFIG_INT_VAL");
57 return got_privsep_flush_imsg(ibuf
);
60 static const struct got_error
*
61 gitconfig_num_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
,
62 const char *section
, const char *tag
, int def
)
66 if (gitconfig
== NULL
)
67 return got_error(GOT_ERR_PRIVSEP_MSG
);
69 value
= got_gitconfig_get_num(gitconfig
, section
, tag
, def
);
70 return send_gitconfig_int(ibuf
, value
);
73 static const struct got_error
*
74 send_gitconfig_str(struct imsgbuf
*ibuf
, const char *value
)
76 size_t len
= value
? strlen(value
) : 0;
78 if (imsg_compose(ibuf
, GOT_IMSG_GITCONFIG_STR_VAL
, 0, 0, -1,
80 return got_error_from_errno("imsg_compose GITCONFIG_STR_VAL");
82 return got_privsep_flush_imsg(ibuf
);
85 static const struct got_error
*
86 send_gitconfig_pair(struct imsgbuf
*ibuf
, const char *key
, const char *val
)
89 size_t klen
= key
? strlen(key
) : 0;
90 size_t vlen
= val
? strlen(val
) : 0;
91 size_t tot
= sizeof(klen
) + sizeof(vlen
) + klen
+ vlen
;
93 if (tot
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
94 return got_error(GOT_ERR_NO_SPACE
);
96 wbuf
= imsg_create(ibuf
, GOT_IMSG_GITCONFIG_PAIR
, 0, 0, tot
);
98 return got_error_from_errno("imsg_create GITCONFIG_PAIR");
100 /* Keep in sync with got_imsg_gitconfig_pair */
101 if (imsg_add(wbuf
, &klen
, sizeof(klen
)) == -1)
102 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
103 if (imsg_add(wbuf
, &vlen
, sizeof(vlen
)) == -1)
104 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
105 if (imsg_add(wbuf
, key
, klen
) == -1)
106 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
107 if (imsg_add(wbuf
, val
, vlen
) == -1)
108 return got_error_from_errno("imsg_add GITCONFIG_PAIR");
111 imsg_close(ibuf
, wbuf
);
112 return got_privsep_flush_imsg(ibuf
);
115 static const struct got_error
*
116 gitconfig_str_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
,
117 const char *section
, const char *tag
)
121 if (gitconfig
== NULL
)
122 return got_error(GOT_ERR_PRIVSEP_MSG
);
124 value
= got_gitconfig_get_str(gitconfig
, section
, tag
);
125 return send_gitconfig_str(ibuf
, value
);
128 static const struct got_error
*
129 send_gitconfig_remotes(struct imsgbuf
*ibuf
, struct got_remote_repo
*remotes
,
132 const struct got_error
*err
= NULL
;
133 struct got_imsg_remotes iremotes
;
136 iremotes
.nremotes
= nremotes
;
137 if (imsg_compose(ibuf
, GOT_IMSG_GITCONFIG_REMOTES
, 0, 0, -1,
138 &iremotes
, sizeof(iremotes
)) == -1)
139 return got_error_from_errno("imsg_compose GITCONFIG_REMOTES");
141 err
= got_privsep_flush_imsg(ibuf
);
146 for (i
= 0; i
< nremotes
; i
++) {
147 struct got_imsg_remote iremote
;
148 size_t len
= sizeof(iremote
);
151 iremote
.mirror_references
= remotes
[i
].mirror_references
;
152 iremote
.name_len
= strlen(remotes
[i
].name
);
153 len
+= iremote
.name_len
;
154 iremote
.fetch_url_len
= strlen(remotes
[i
].fetch_url
);
155 len
+= iremote
.fetch_url_len
;
156 iremote
.send_url_len
= strlen(remotes
[i
].send_url
);
157 len
+= iremote
.send_url_len
;
159 wbuf
= imsg_create(ibuf
, GOT_IMSG_GITCONFIG_REMOTE
, 0, 0, len
);
161 return got_error_from_errno(
162 "imsg_create GITCONFIG_REMOTE");
164 if (imsg_add(wbuf
, &iremote
, sizeof(iremote
)) == -1)
165 return got_error_from_errno(
166 "imsg_add GITCONFIG_REMOTE");
168 if (imsg_add(wbuf
, remotes
[i
].name
, iremote
.name_len
) == -1)
169 return got_error_from_errno(
170 "imsg_add GITCONFIG_REMOTE");
171 if (imsg_add(wbuf
, remotes
[i
].fetch_url
, iremote
.fetch_url_len
) == -1)
172 return got_error_from_errno(
173 "imsg_add GITCONFIG_REMOTE");
174 if (imsg_add(wbuf
, remotes
[i
].send_url
, iremote
.send_url_len
) == -1)
175 return got_error_from_errno(
176 "imsg_add GITCONFIG_REMOTE");
179 imsg_close(ibuf
, wbuf
);
180 err
= got_privsep_flush_imsg(ibuf
);
189 get_boolean_val(char *val
)
191 return (strcasecmp(val
, "true") == 0 ||
192 strcasecmp(val
, "on") == 0 ||
193 strcasecmp(val
, "yes") == 0 ||
194 strcmp(val
, "1") == 0);
197 static const struct got_error
*
198 gitconfig_remotes_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
)
200 const struct got_error
*err
= NULL
;
201 struct got_gitconfig_list
*sections
;
202 struct got_gitconfig_list_node
*node
;
203 struct got_remote_repo
*remotes
= NULL
;
206 if (gitconfig
== NULL
)
207 return got_error(GOT_ERR_PRIVSEP_MSG
);
209 err
= got_gitconfig_get_section_list(§ions
, gitconfig
);
213 TAILQ_FOREACH(node
, §ions
->fields
, link
) {
214 if (strncasecmp("remote \"", node
->field
, 8) != 0)
220 err
= send_gitconfig_remotes(ibuf
, NULL
, 0);
224 remotes
= recallocarray(NULL
, 0, nremotes
, sizeof(*remotes
));
225 if (remotes
== NULL
) {
226 err
= got_error_from_errno("recallocarray");
231 TAILQ_FOREACH(node
, §ions
->fields
, link
) {
232 char *name
, *end
, *mirror
;
234 if (strncasecmp("remote \"", node
->field
, 8) != 0)
237 name
= strdup(node
->field
+ 8);
239 err
= got_error_from_errno("strdup");
242 end
= strrchr(name
, '"');
245 remotes
[i
].name
= name
;
247 remotes
[i
].fetch_url
= got_gitconfig_get_str(gitconfig
,
249 if (remotes
[i
].fetch_url
== NULL
) {
250 err
= got_error(GOT_ERR_GITCONFIG_SYNTAX
);
254 remotes
[i
].send_url
= got_gitconfig_get_str(gitconfig
,
255 node
->field
, "pushurl");
256 if (remotes
[i
].send_url
== NULL
)
257 remotes
[i
].send_url
= got_gitconfig_get_str(gitconfig
,
259 if (remotes
[i
].send_url
== NULL
) {
260 err
= got_error(GOT_ERR_GITCONFIG_SYNTAX
);
264 remotes
[i
].mirror_references
= 0;
265 mirror
= got_gitconfig_get_str(gitconfig
, node
->field
,
267 if (mirror
!= NULL
&& get_boolean_val(mirror
))
268 remotes
[i
].mirror_references
= 1;
273 err
= send_gitconfig_remotes(ibuf
, remotes
, nremotes
);
275 for (i
= 0; i
< nremotes
; i
++)
276 free(remotes
[i
].name
);
278 got_gitconfig_free_list(sections
);
282 static const struct got_error
*
283 gitconfig_owner_request(struct imsgbuf
*ibuf
, struct got_gitconfig
*gitconfig
)
287 if (gitconfig
== NULL
)
288 return got_error(GOT_ERR_PRIVSEP_MSG
);
290 value
= got_gitconfig_get_str(gitconfig
, "gotweb", "owner");
292 return send_gitconfig_str(ibuf
, value
);
293 value
= got_gitconfig_get_str(gitconfig
, "gitweb", "owner");
294 return send_gitconfig_str(ibuf
, value
);
297 static const struct got_error
*
298 gitconfig_extensions_request(struct imsgbuf
*ibuf
,
299 struct got_gitconfig
*gitconfig
)
301 const struct got_error
*err
= NULL
;
302 struct got_gitconfig_list
*tags
;
303 struct got_gitconfig_list_node
*node
;
307 if (gitconfig
== NULL
)
308 return got_error(GOT_ERR_PRIVSEP_MSG
);
310 tags
= got_gitconfig_get_tag_list(gitconfig
, "extensions");
312 return send_gitconfig_int(ibuf
, 0);
314 TAILQ_FOREACH(node
, &tags
->fields
, link
)
317 err
= send_gitconfig_int(ibuf
, nextensions
);
321 TAILQ_FOREACH(node
, &tags
->fields
, link
) {
322 val
= got_gitconfig_get_str(gitconfig
, "extensions",
324 err
= send_gitconfig_pair(ibuf
, node
->field
, val
);
329 got_gitconfig_free_list(tags
);
334 main(int argc
, char *argv
[])
336 const struct got_error
*err
= NULL
;
339 struct got_gitconfig
*gitconfig
= NULL
;
346 signal(SIGINT
, catch_sigint
);
348 imsg_init(&ibuf
, GOT_IMSG_FD_CHILD
);
351 /* revoke access to most system calls */
352 if (pledge("stdio recvfd", NULL
) == -1) {
353 err
= got_error_from_errno("pledge");
354 got_privsep_send_error(&ibuf
, err
);
358 /* revoke fs access */
359 if (landlock_no_fs() == -1) {
360 err
= got_error_from_errno("landlock_no_fs");
361 got_privsep_send_error(&ibuf
, err
);
364 if (cap_enter() == -1) {
365 err
= got_error_from_errno("cap_enter");
366 got_privsep_send_error(&ibuf
, err
);
374 memset(&imsg
, 0, sizeof(imsg
));
377 if (sigint_received
) {
378 err
= got_error(GOT_ERR_CANCELLED
);
382 err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0);
384 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
389 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
392 switch (imsg
.hdr
.type
) {
393 case GOT_IMSG_GITCONFIG_PARSE_REQUEST
:
394 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
396 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
400 err
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
405 got_gitconfig_close(gitconfig
);
406 err
= got_gitconfig_open(&gitconfig
, imsg
.fd
);
408 case GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST
:
409 err
= gitconfig_num_request(&ibuf
, gitconfig
, "core",
410 "repositoryformatversion", 0);
412 case GOT_IMSG_GITCONFIG_REPOSITORY_EXTENSIONS_REQUEST
:
413 err
= gitconfig_extensions_request(&ibuf
, gitconfig
);
415 case GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST
:
416 err
= gitconfig_str_request(&ibuf
, gitconfig
, "user",
419 case GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST
:
420 err
= gitconfig_str_request(&ibuf
, gitconfig
, "user",
423 case GOT_IMSG_GITCONFIG_REMOTES_REQUEST
:
424 err
= gitconfig_remotes_request(&ibuf
, gitconfig
);
426 case GOT_IMSG_GITCONFIG_OWNER_REQUEST
:
427 err
= gitconfig_owner_request(&ibuf
, gitconfig
);
430 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
435 if (close(imsg
.fd
) == -1 && err
== NULL
)
436 err
= got_error_from_errno("close");
446 if (!sigint_received
&& err
->code
!= GOT_ERR_PRIVSEP_PIPE
) {
447 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
448 got_privsep_send_error(&ibuf
, err
);
451 if (close(GOT_IMSG_FD_CHILD
) == -1 && err
== NULL
)
452 err
= got_error_from_errno("close");