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>
33 #include "got_error.h"
34 #include "got_version.h"
36 #include "got_lib_gitproto.h"
41 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
44 #define GOTCTL_CMD_INFO "info"
45 #define GOTCTL_CMD_STOP "stop"
49 const struct got_error *(*cmd_main)(int, char *[], int);
50 void (*cmd_usage)(void);
53 __dead static void usage(int, int);
55 __dead static void usage_info(void);
56 __dead static void usage_stop(void);
58 static const struct got_error* cmd_info(int, char *[], int);
59 static const struct got_error* cmd_stop(int, char *[], int);
61 static const struct gotctl_cmd gotctl_commands[] = {
62 { "info", cmd_info, usage_info },
63 { "stop", cmd_stop, usage_stop },
69 fprintf(stderr, "usage: %s info\n", getprogname());
73 static const struct got_error *
74 show_info(struct imsg *imsg)
76 struct gotd_imsg_info info;
79 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
80 if (datalen != sizeof(info))
81 return got_error(GOT_ERR_PRIVSEP_LEN);
82 memcpy(&info, imsg->data, sizeof(info));
84 printf("gotd PID: %d\n", info.pid);
85 printf("verbosity: %d\n", info.verbosity);
86 printf("number of repositories: %d\n", info.nrepos);
87 printf("number of connected clients: %d\n", info.nclients);
91 static const struct got_error *
92 show_repo_info(struct imsg *imsg)
94 struct gotd_imsg_info_repo info;
97 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
98 if (datalen != sizeof(info))
99 return got_error(GOT_ERR_PRIVSEP_LEN);
100 memcpy(&info, imsg->data, sizeof(info));
102 printf("repository \"%s\", path %s\n", info.repo_name, info.repo_path);
107 get_state_name(enum gotd_client_state state)
109 static char unknown_state[64];
112 case GOTD_STATE_EXPECT_LIST_REFS:
114 case GOTD_STATE_EXPECT_CAPABILITIES:
115 return "expect-capabilities";
116 case GOTD_STATE_EXPECT_WANT:
117 return "expect-want";
118 case GOTD_STATE_EXPECT_REF_UPDATE:
119 return "expect-ref-update";
120 case GOTD_STATE_EXPECT_MORE_REF_UPDATES:
121 return "expect-more-ref-updates";
122 case GOTD_STATE_EXPECT_HAVE:
123 return "expect-have";
124 case GOTD_STATE_EXPECT_PACKFILE:
125 return "expect-packfile";
126 case GOTD_STATE_EXPECT_DONE:
127 return "expect-done";
128 case GOTD_STATE_DONE:
132 snprintf(unknown_state, sizeof(unknown_state),
133 "unknown state %d", state);
134 return unknown_state;
137 static const struct got_error *
138 show_client_info(struct imsg *imsg)
140 struct gotd_imsg_info_client info;
143 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
144 if (datalen != sizeof(info))
145 return got_error(GOT_ERR_PRIVSEP_LEN);
146 memcpy(&info, imsg->data, sizeof(info));
148 printf("client UID %d, GID %d, protocol state '%s', ",
149 info.euid, info.egid, get_state_name(info.state));
150 if (info.session_child_pid)
151 printf("session PID %ld, ", (long)info.session_child_pid);
152 if (info.repo_child_pid)
153 printf("repo PID %ld, ", (long)info.repo_child_pid);
155 printf("writing to %s\n", info.repo_name);
157 printf("reading from %s\n", info.repo_name);
162 static const struct got_error *
163 cmd_info(int argc, char *argv[], int gotd_sock)
165 const struct got_error *err;
169 imsg_init(&ibuf, gotd_sock);
171 if (imsg_compose(&ibuf, GOTD_IMSG_INFO, 0, 0, -1, NULL, 0) == -1)
172 return got_error_from_errno("imsg_compose INFO");
174 err = gotd_imsg_flush(&ibuf);
175 while (err == NULL) {
176 err = gotd_imsg_poll_recv(&imsg, &ibuf, 0);
178 if (err->code == GOT_ERR_EOF)
183 switch (imsg.hdr.type) {
184 case GOTD_IMSG_ERROR:
185 err = gotd_imsg_recv_error(NULL, &imsg);
188 err = show_info(&imsg);
190 case GOTD_IMSG_INFO_REPO:
191 err = show_repo_info(&imsg);
193 case GOTD_IMSG_INFO_CLIENT:
194 err = show_client_info(&imsg);
197 err = got_error(GOT_ERR_PRIVSEP_MSG);
211 fprintf(stderr, "usage: %s stop\n", getprogname());
215 static const struct got_error *
216 cmd_stop(int argc, char *argv[], int gotd_sock)
218 const struct got_error *err;
222 imsg_init(&ibuf, gotd_sock);
224 if (imsg_compose(&ibuf, GOTD_IMSG_STOP, 0, 0, -1, NULL, 0) == -1)
225 return got_error_from_errno("imsg_compose STOP");
227 err = gotd_imsg_flush(&ibuf);
228 while (err == NULL) {
229 err = gotd_imsg_poll_recv(&imsg, &ibuf, 0);
231 if (err->code == GOT_ERR_EOF)
236 switch (imsg.hdr.type) {
237 case GOTD_IMSG_ERROR:
238 err = gotd_imsg_recv_error(NULL, &imsg);
241 err = got_error(GOT_ERR_PRIVSEP_MSG);
253 list_commands(FILE *fp)
257 fprintf(fp, "commands:");
258 for (i = 0; i < nitems(gotctl_commands); i++) {
259 const struct gotctl_cmd *cmd = &gotctl_commands[i];
260 fprintf(fp, " %s", cmd->cmd_name);
266 usage(int hflag, int status)
268 FILE *fp = (status == 0) ? stdout : stderr;
270 fprintf(fp, "usage: %s [-hV] [-f path] command [arg ...]\n",
277 static const struct got_error *
278 apply_unveil(const char *unix_socket_path)
281 if (unveil("gmon.out", "rwc") != 0)
282 return got_error_from_errno2("unveil", "gmon.out");
284 if (unveil(unix_socket_path, "w") != 0)
285 return got_error_from_errno2("unveil", unix_socket_path);
287 if (unveil(NULL, NULL) != 0)
288 return got_error_from_errno("unveil");
294 connect_gotd(const char *socket_path)
296 const struct got_error *error = NULL;
298 struct sockaddr_un sun;
300 error = apply_unveil(socket_path);
302 errx(1, "%s", error->msg);
305 if (pledge("stdio unix", NULL) == -1)
308 if ((gotd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
311 memset(&sun, 0, sizeof(sun));
312 sun.sun_family = AF_UNIX;
313 if (strlcpy(sun.sun_path, socket_path, sizeof(sun.sun_path)) >=
314 sizeof(sun.sun_path))
315 errx(1, "gotd socket path too long");
316 if (connect(gotd_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
317 err(1, "connect: %s", socket_path);
320 if (pledge("stdio", NULL) == -1)
328 main(int argc, char *argv[])
330 const struct gotctl_cmd *cmd;
331 int gotd_sock = -1, i;
333 int hflag = 0, Vflag = 0;
334 static const struct option longopts[] = {
335 { "version", no_argument, NULL, 'V' },
338 const char *socket_path = GOTD_UNIX_SOCKET;
340 setlocale(LC_CTYPE, "");
343 if (pledge("stdio unix unveil", NULL) == -1)
347 while ((ch = getopt_long(argc, argv, "+hf:V", longopts, NULL)) != -1) {
353 socket_path = optarg;
370 got_version_print_str();
375 usage(hflag, hflag ? 0 : 1);
377 for (i = 0; i < nitems(gotctl_commands); i++) {
378 const struct got_error *error;
380 cmd = &gotctl_commands[i];
382 if (strncmp(cmd->cmd_name, argv[0], strlen(argv[0])) != 0)
388 gotd_sock = connect_gotd(socket_path);
391 error = cmd->cmd_main(argc, argv, gotd_sock);
394 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
401 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
402 list_commands(stderr);