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 "got_compat.h"
19 #include <sys/queue.h>
20 #include <sys/socket.h>
34 #include "got_error.h"
35 #include "got_object.h"
36 #include "got_version.h"
39 #include "got_lib_gitproto.h"
44 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
47 #define GOTCTL_CMD_INFO "info"
48 #define GOTCTL_CMD_STOP "stop"
52 const struct got_error
*(*cmd_main
)(int, char *[], int);
53 void (*cmd_usage
)(void);
56 __dead
static void usage(int, int);
58 __dead
static void usage_info(void);
59 __dead
static void usage_stop(void);
61 static const struct got_error
* cmd_info(int, char *[], int);
62 static const struct got_error
* cmd_stop(int, char *[], int);
64 static const struct gotctl_cmd gotctl_commands
[] = {
65 { "info", cmd_info
, usage_info
},
66 { "stop", cmd_stop
, usage_stop
},
72 fprintf(stderr
, "usage: %s info\n", getprogname());
76 static const struct got_error
*
77 show_info(struct imsg
*imsg
)
79 struct gotd_imsg_info info
;
82 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
83 if (datalen
!= sizeof(info
))
84 return got_error(GOT_ERR_PRIVSEP_LEN
);
85 memcpy(&info
, imsg
->data
, sizeof(info
));
87 printf("gotd PID: %d\n", info
.pid
);
88 printf("verbosity: %d\n", info
.verbosity
);
89 printf("number of repositories: %d\n", info
.nrepos
);
90 printf("number of connected clients: %d\n", info
.nclients
);
94 static const struct got_error
*
95 show_repo_info(struct imsg
*imsg
)
97 struct gotd_imsg_info_repo info
;
100 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
101 if (datalen
!= sizeof(info
))
102 return got_error(GOT_ERR_PRIVSEP_LEN
);
103 memcpy(&info
, imsg
->data
, sizeof(info
));
105 printf("repository \"%s\", path %s\n", info
.repo_name
, info
.repo_path
);
109 static const struct got_error
*
110 show_client_info(struct imsg
*imsg
)
112 struct gotd_imsg_info_client info
;
115 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
116 if (datalen
!= sizeof(info
))
117 return got_error(GOT_ERR_PRIVSEP_LEN
);
118 memcpy(&info
, imsg
->data
, sizeof(info
));
120 printf("client UID %d, GID %d, ", info
.euid
, info
.egid
);
121 if (info
.session_child_pid
)
122 printf("session PID %ld, ", (long)info
.session_child_pid
);
123 if (info
.repo_child_pid
)
124 printf("repo PID %ld, ", (long)info
.repo_child_pid
);
126 printf("writing to %s\n", info
.repo_name
);
128 printf("reading from %s\n", info
.repo_name
);
133 static const struct got_error
*
134 cmd_info(int argc
, char *argv
[], int gotd_sock
)
136 const struct got_error
*err
;
140 if (imsgbuf_init(&ibuf
, gotd_sock
) == -1)
141 return got_error_from_errno("imsgbuf_init");
142 imsgbuf_allow_fdpass(&ibuf
);
144 if (imsg_compose(&ibuf
, GOTD_IMSG_INFO
, 0, 0, -1, NULL
, 0) == -1) {
145 imsgbuf_clear(&ibuf
);
146 return got_error_from_errno("imsg_compose INFO");
149 err
= gotd_imsg_flush(&ibuf
);
150 while (err
== NULL
) {
151 err
= gotd_imsg_poll_recv(&imsg
, &ibuf
, 0);
153 if (err
->code
== GOT_ERR_EOF
)
158 switch (imsg
.hdr
.type
) {
159 case GOTD_IMSG_ERROR
:
160 err
= gotd_imsg_recv_error(NULL
, &imsg
);
163 err
= show_info(&imsg
);
165 case GOTD_IMSG_INFO_REPO
:
166 err
= show_repo_info(&imsg
);
168 case GOTD_IMSG_INFO_CLIENT
:
169 err
= show_client_info(&imsg
);
172 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
179 imsgbuf_clear(&ibuf
);
186 fprintf(stderr
, "usage: %s stop\n", getprogname());
190 static const struct got_error
*
191 cmd_stop(int argc
, char *argv
[], int gotd_sock
)
193 const struct got_error
*err
;
197 if (imsgbuf_init(&ibuf
, gotd_sock
) == -1)
198 return got_error_from_errno("imsgbuf_init");
199 imsgbuf_allow_fdpass(&ibuf
);
201 if (imsg_compose(&ibuf
, GOTD_IMSG_STOP
, 0, 0, -1, NULL
, 0) == -1) {
202 imsgbuf_clear(&ibuf
);
203 return got_error_from_errno("imsg_compose STOP");
206 err
= gotd_imsg_flush(&ibuf
);
207 while (err
== NULL
) {
208 err
= gotd_imsg_poll_recv(&imsg
, &ibuf
, 0);
210 if (err
->code
== GOT_ERR_EOF
)
215 switch (imsg
.hdr
.type
) {
216 case GOTD_IMSG_ERROR
:
217 err
= gotd_imsg_recv_error(NULL
, &imsg
);
220 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
227 imsgbuf_clear(&ibuf
);
232 list_commands(FILE *fp
)
236 fprintf(fp
, "commands:");
237 for (i
= 0; i
< nitems(gotctl_commands
); i
++) {
238 const struct gotctl_cmd
*cmd
= &gotctl_commands
[i
];
239 fprintf(fp
, " %s", cmd
->cmd_name
);
245 usage(int hflag
, int status
)
247 FILE *fp
= (status
== 0) ? stdout
: stderr
;
249 fprintf(fp
, "usage: %s [-hV] [-f path] command [arg ...]\n",
256 static const struct got_error
*
257 apply_unveil(const char *unix_socket_path
)
260 if (unveil("gmon.out", "rwc") != 0)
261 return got_error_from_errno2("unveil", "gmon.out");
263 if (unveil(unix_socket_path
, "w") != 0)
264 return got_error_from_errno2("unveil", unix_socket_path
);
266 if (unveil(NULL
, NULL
) != 0)
267 return got_error_from_errno("unveil");
273 connect_gotd(const char *socket_path
)
275 const struct got_error
*error
= NULL
;
277 struct sockaddr_un sun
;
279 error
= apply_unveil(socket_path
);
281 errx(1, "%s", error
->msg
);
284 if (pledge("stdio unix", NULL
) == -1)
287 if ((gotd_sock
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1)
290 memset(&sun
, 0, sizeof(sun
));
291 sun
.sun_family
= AF_UNIX
;
292 if (strlcpy(sun
.sun_path
, socket_path
, sizeof(sun
.sun_path
)) >=
293 sizeof(sun
.sun_path
))
294 errx(1, "gotd socket path too long");
295 if (connect(gotd_sock
, (struct sockaddr
*)&sun
, sizeof(sun
)) == -1)
296 err(1, "connect: %s", socket_path
);
299 if (pledge("stdio", NULL
) == -1)
307 main(int argc
, char *argv
[])
309 const struct gotctl_cmd
*cmd
;
310 int gotd_sock
= -1, i
;
312 int hflag
= 0, Vflag
= 0;
313 static const struct option longopts
[] = {
314 { "version", no_argument
, NULL
, 'V' },
317 const char *socket_path
= GOTD_UNIX_SOCKET
;
319 setlocale(LC_CTYPE
, "");
322 if (pledge("stdio unix unveil", NULL
) == -1)
326 while ((ch
= getopt_long(argc
, argv
, "+hf:V", longopts
, NULL
)) != -1) {
332 socket_path
= optarg
;
349 got_version_print_str();
354 usage(hflag
, hflag
? 0 : 1);
356 for (i
= 0; i
< nitems(gotctl_commands
); i
++) {
357 const struct got_error
*error
;
359 cmd
= &gotctl_commands
[i
];
361 if (strncmp(cmd
->cmd_name
, argv
[0], strlen(argv
[0])) != 0)
367 gotd_sock
= connect_gotd(socket_path
);
370 error
= cmd
->cmd_main(argc
, argv
, gotd_sock
);
373 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
380 fprintf(stderr
, "%s: unknown command '%s'\n", getprogname(), argv
[0]);
381 list_commands(stderr
);