portable: configure: use config.h
[got-portable.git] / gotctl / gotctl.c
blob0fba1db7959d1da077ff54a97824b6ec0255df29
1 /*
2 * Copyright (c) 2022 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/queue.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
21 #include <err.h>
22 #include <event.h>
23 #include <imsg.h>
24 #include <limits.h>
25 #include <locale.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <getopt.h>
30 #include <unistd.h>
32 #include "got_error.h"
33 #include "got_version.h"
35 #include "got_lib_gitproto.h"
37 #include "gotd.h"
39 #ifndef nitems
40 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
41 #endif
43 #define GOTCTL_CMD_INFO "info"
44 #define GOTCTL_CMD_STOP "stop"
46 struct gotctl_cmd {
47 const char *cmd_name;
48 const struct got_error *(*cmd_main)(int, char *[], int);
49 void (*cmd_usage)(void);
52 __dead static void usage(int, int);
54 __dead static void usage_info(void);
55 __dead static void usage_stop(void);
57 static const struct got_error* cmd_info(int, char *[], int);
58 static const struct got_error* cmd_stop(int, char *[], int);
60 static const struct gotctl_cmd gotctl_commands[] = {
61 { "info", cmd_info, usage_info },
62 { "stop", cmd_stop, usage_stop },
65 __dead static void
66 usage_info(void)
68 fprintf(stderr, "usage: %s info\n", getprogname());
69 exit(1);
72 static const struct got_error *
73 show_info(struct imsg *imsg)
75 struct gotd_imsg_info info;
76 size_t datalen;
78 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
79 if (datalen != sizeof(info))
80 return got_error(GOT_ERR_PRIVSEP_LEN);
81 memcpy(&info, imsg->data, sizeof(info));
83 printf("gotd PID: %d\n", info.pid);
84 printf("verbosity: %d\n", info.verbosity);
85 printf("number of repositories: %d\n", info.nrepos);
86 printf("number of connected clients: %d\n", info.nclients);
87 return NULL;
90 static const struct got_error *
91 show_repo_info(struct imsg *imsg)
93 struct gotd_imsg_info_repo info;
94 size_t datalen;
96 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
97 if (datalen != sizeof(info))
98 return got_error(GOT_ERR_PRIVSEP_LEN);
99 memcpy(&info, imsg->data, sizeof(info));
101 printf("repository \"%s\", path %s\n", info.repo_name, info.repo_path);
102 return NULL;
105 static const struct got_error *
106 show_client_info(struct imsg *imsg)
108 struct gotd_imsg_info_client info;
109 size_t datalen;
111 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
112 if (datalen != sizeof(info))
113 return got_error(GOT_ERR_PRIVSEP_LEN);
114 memcpy(&info, imsg->data, sizeof(info));
116 printf("client UID %d, GID %d, ", info.euid, info.egid);
117 if (info.session_child_pid)
118 printf("session PID %ld, ", (long)info.session_child_pid);
119 if (info.repo_child_pid)
120 printf("repo PID %ld, ", (long)info.repo_child_pid);
121 if (info.is_writing)
122 printf("writing to %s\n", info.repo_name);
123 else
124 printf("reading from %s\n", info.repo_name);
126 return NULL;
129 static const struct got_error *
130 cmd_info(int argc, char *argv[], int gotd_sock)
132 const struct got_error *err;
133 struct imsgbuf ibuf;
134 struct imsg imsg;
136 imsg_init(&ibuf, gotd_sock);
138 if (imsg_compose(&ibuf, GOTD_IMSG_INFO, 0, 0, -1, NULL, 0) == -1)
139 return got_error_from_errno("imsg_compose INFO");
141 err = gotd_imsg_flush(&ibuf);
142 while (err == NULL) {
143 err = gotd_imsg_poll_recv(&imsg, &ibuf, 0);
144 if (err) {
145 if (err->code == GOT_ERR_EOF)
146 err = NULL;
147 break;
150 switch (imsg.hdr.type) {
151 case GOTD_IMSG_ERROR:
152 err = gotd_imsg_recv_error(NULL, &imsg);
153 break;
154 case GOTD_IMSG_INFO:
155 err = show_info(&imsg);
156 break;
157 case GOTD_IMSG_INFO_REPO:
158 err = show_repo_info(&imsg);
159 break;
160 case GOTD_IMSG_INFO_CLIENT:
161 err = show_client_info(&imsg);
162 break;
163 default:
164 err = got_error(GOT_ERR_PRIVSEP_MSG);
165 break;
168 imsg_free(&imsg);
171 imsg_clear(&ibuf);
172 return err;
175 __dead static void
176 usage_stop(void)
178 fprintf(stderr, "usage: %s stop\n", getprogname());
179 exit(1);
182 static const struct got_error *
183 cmd_stop(int argc, char *argv[], int gotd_sock)
185 const struct got_error *err;
186 struct imsgbuf ibuf;
187 struct imsg imsg;
189 imsg_init(&ibuf, gotd_sock);
191 if (imsg_compose(&ibuf, GOTD_IMSG_STOP, 0, 0, -1, NULL, 0) == -1)
192 return got_error_from_errno("imsg_compose STOP");
194 err = gotd_imsg_flush(&ibuf);
195 while (err == NULL) {
196 err = gotd_imsg_poll_recv(&imsg, &ibuf, 0);
197 if (err) {
198 if (err->code == GOT_ERR_EOF)
199 err = NULL;
200 break;
203 switch (imsg.hdr.type) {
204 case GOTD_IMSG_ERROR:
205 err = gotd_imsg_recv_error(NULL, &imsg);
206 break;
207 default:
208 err = got_error(GOT_ERR_PRIVSEP_MSG);
209 break;
212 imsg_free(&imsg);
215 imsg_clear(&ibuf);
216 return err;
219 static void
220 list_commands(FILE *fp)
222 size_t i;
224 fprintf(fp, "commands:");
225 for (i = 0; i < nitems(gotctl_commands); i++) {
226 const struct gotctl_cmd *cmd = &gotctl_commands[i];
227 fprintf(fp, " %s", cmd->cmd_name);
229 fputc('\n', fp);
232 __dead static void
233 usage(int hflag, int status)
235 FILE *fp = (status == 0) ? stdout : stderr;
237 fprintf(fp, "usage: %s [-hV] [-f path] command [arg ...]\n",
238 getprogname());
239 if (hflag)
240 list_commands(fp);
241 exit(status);
244 static const struct got_error *
245 apply_unveil(const char *unix_socket_path)
247 #ifdef PROFILE
248 if (unveil("gmon.out", "rwc") != 0)
249 return got_error_from_errno2("unveil", "gmon.out");
250 #endif
251 if (unveil(unix_socket_path, "w") != 0)
252 return got_error_from_errno2("unveil", unix_socket_path);
254 if (unveil(NULL, NULL) != 0)
255 return got_error_from_errno("unveil");
257 return NULL;
260 static int
261 connect_gotd(const char *socket_path)
263 const struct got_error *error = NULL;
264 int gotd_sock = -1;
265 struct sockaddr_un sun;
267 error = apply_unveil(socket_path);
268 if (error)
269 errx(1, "%s", error->msg);
271 #ifndef PROFILE
272 if (pledge("stdio unix", NULL) == -1)
273 err(1, "pledge");
274 #endif
275 if ((gotd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
276 err(1, "socket");
278 memset(&sun, 0, sizeof(sun));
279 sun.sun_family = AF_UNIX;
280 if (strlcpy(sun.sun_path, socket_path, sizeof(sun.sun_path)) >=
281 sizeof(sun.sun_path))
282 errx(1, "gotd socket path too long");
283 if (connect(gotd_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
284 err(1, "connect: %s", socket_path);
286 #ifndef PROFILE
287 if (pledge("stdio", NULL) == -1)
288 err(1, "pledge");
289 #endif
291 return gotd_sock;
295 main(int argc, char *argv[])
297 const struct gotctl_cmd *cmd;
298 int gotd_sock = -1, i;
299 int ch;
300 int hflag = 0, Vflag = 0;
301 static const struct option longopts[] = {
302 { "version", no_argument, NULL, 'V' },
303 { NULL, 0, NULL, 0 }
305 const char *socket_path = GOTD_UNIX_SOCKET;
307 setlocale(LC_CTYPE, "");
309 #ifndef PROFILE
310 if (pledge("stdio unix unveil", NULL) == -1)
311 err(1, "pledge");
312 #endif
314 while ((ch = getopt_long(argc, argv, "+hf:V", longopts, NULL)) != -1) {
315 switch (ch) {
316 case 'h':
317 hflag = 1;
318 break;
319 case 'f':
320 socket_path = optarg;
321 break;
322 case 'V':
323 Vflag = 1;
324 break;
325 default:
326 usage(hflag, 1);
327 /* NOTREACHED */
331 argc -= optind;
332 argv += optind;
333 optind = 1;
334 optreset = 1;
336 if (Vflag) {
337 got_version_print_str();
338 return 0;
341 if (argc <= 0)
342 usage(hflag, hflag ? 0 : 1);
344 for (i = 0; i < nitems(gotctl_commands); i++) {
345 const struct got_error *error;
347 cmd = &gotctl_commands[i];
349 if (strncmp(cmd->cmd_name, argv[0], strlen(argv[0])) != 0)
350 continue;
352 if (hflag)
353 cmd->cmd_usage();
355 gotd_sock = connect_gotd(socket_path);
356 if (gotd_sock == -1)
357 return 1;
358 error = cmd->cmd_main(argc, argv, gotd_sock);
359 close(gotd_sock);
360 if (error) {
361 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
362 return 1;
365 return 0;
368 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
369 list_commands(stderr);
370 return 1;