2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
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.
19 #include <sys/types.h>
20 #include <sys/socket.h>
34 #include <readline/readline.h>
35 #include <readline/history.h>
39 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
53 struct tls_config *tlsconf;
57 struct evbuffer *dirbuf;
63 #define ASSERT_EMPTYBUF() assert(EVBUFFER_LENGTH(buf) == 0)
67 read_line(const char *prompt)
72 if ((line = readline(prompt)) == NULL)
74 /* XXX: trim spaces? */
85 read_line(const char *prompt)
87 char *ch, *line = NULL;
94 linelen = getline(&line, &linesize, stdin);
98 if ((ch = strchr(line, '\n')) != NULL)
107 fprintf(stderr, "usage: %s [-c] host[:port] [path]\n",
109 fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE VERSION "\n");
120 while (EVBUFFER_LENGTH(evb) != 0) {
121 buf = EVBUFFER_DATA(evb);
122 nbytes = EVBUFFER_LENGTH(evb);
125 r = write(sock, buf, nbytes);
126 if (r == 0 || r == -1)
129 r = tls_write(ctx, buf, nbytes);
130 if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
133 errx(1, "tls: %s", tls_error(ctx));
136 evbuffer_drain(evb, r);
141 mustread(void *d, size_t len)
147 r = read(sock, d, len);
148 if (r == 0 || r == -1)
151 r = tls_read(ctx, d, len);
152 if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
155 errx(1, "tls: %s", tls_error(ctx));
169 mustread(&len, sizeof(len));
171 if (len < HEADERSIZE)
172 errx(1, "read message of invalid length %d", len);
174 len -= 4; /* skip the length just read */
177 l = MIN(len, sizeof(tmp));
180 evbuffer_add(buf, tmp, l);
185 np_read64(struct evbuffer *buf)
189 evbuffer_remove(buf, &n, sizeof(n));
194 np_read32(struct evbuffer *buf)
198 evbuffer_remove(buf, &n, sizeof(n));
203 np_read16(struct evbuffer *buf)
207 evbuffer_remove(buf, &n, sizeof(n));
212 np_read8(struct evbuffer *buf)
216 evbuffer_remove(buf, &n, sizeof(n));
221 np_readstr(struct evbuffer *buf)
226 len = np_read16(buf);
227 assert(EVBUFFER_LENGTH(buf) >= len);
229 if ((str = calloc(1, len+1)) == NULL)
231 evbuffer_remove(buf, str, len);
236 np_read_qid(struct evbuffer *buf, struct qid *qid)
238 assert(EVBUFFER_LENGTH(buf) >= QIDSIZE);
240 qid->type = np_read8(buf);
241 qid->vers = np_read32(buf);
242 qid->path = np_read64(buf);
257 err = np_readstr(buf);
258 errx(1, "expected %s, got error %s",
259 pp_msg_type(type), err);
262 errx(1, "expected %s, got msg type %s",
263 pp_msg_type(type), pp_msg_type(t));
267 expect2(uint8_t type, uint16_t tag)
277 errx(1, "expected tag 0x%x, got 0x%x", tag, t);
285 tversion(VERSION9P, MSIZE9P);
288 expect2(Rversion, NOTAG);
290 msize = np_read32(buf);
291 version = np_readstr(buf);
294 errx(1, "got unexpected msize: %d", msize);
295 if (strcmp(version, VERSION9P))
296 errx(1, "unexpected 9p version: %s", version);
303 do_attach(const char *path)
310 if ((user = getenv("USER")) == NULL)
313 tattach(PWDFID, NOFID, user, path);
316 expect2(Rattach, iota_tag);
317 np_read_qid(buf, &qid);
323 do_open(uint32_t fid, uint8_t mode)
331 expect2(Ropen, iota_tag);
333 np_read_qid(buf, &qid);
334 iounit = np_read32(buf);
342 do_clunk(uint32_t fid)
347 expect2(Rclunk, iota_tag);
353 dup_fid(int fid, int nfid)
357 twalk(fid, nfid, NULL, 0);
360 expect2(Rwalk, iota_tag);
362 nwqid = np_read16(buf);
369 do_tls_connect(const char *host, const char *port)
373 if ((tlsconf = tls_config_new()) == NULL)
374 fatalx("tls_config_new");
375 tls_config_insecure_noverifycert(tlsconf);
376 tls_config_insecure_noverifyname(tlsconf);
377 if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
378 fatalx("can't load certs (%s, %s)", crtpath, keypath);
380 if ((ctx = tls_client()) == NULL)
382 if (tls_configure(ctx, tlsconf) == -1)
383 fatalx("tls_configure: %s", tls_error(ctx));
385 if (tls_connect(ctx, host, port) == -1)
386 fatalx("can't connect to %s:%s: %s", host, port,
389 for (handshake = 0; !handshake;) {
390 switch (tls_handshake(ctx)) {
392 fatalx("tls_handshake: %s", tls_error(ctx));
401 do_ctxt_connect(const char *host, const char *port)
403 struct addrinfo hints, *res, *res0;
404 int error, saved_errno;
405 const char *cause = NULL;
407 memset(&hints, 0, sizeof(hints));
408 hints.ai_family = AF_UNSPEC;
409 hints.ai_socktype = SOCK_STREAM;
410 error = getaddrinfo(host, port, &hints, &res0);
412 errx(1, "%s", gai_strerror(error));
415 for (res = res0; res != NULL; res = res->ai_next) {
416 sock = socket(res->ai_family, res->ai_socktype,
423 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
441 do_connect(const char *connspec, const char *path)
446 host = xstrdup(connspec);
447 if ((colon = strchr(host, ':')) != NULL) {
453 printf("connecting to %s:%s...", host, port);
457 do_tls_connect(host, port);
459 do_ctxt_connect(host, port);
470 cmd_bell(int argc, const char **argv)
475 puts("bell mode enabled");
477 puts("bell mode disabled");
484 if (!strcmp(*argv, "on")) {
486 puts("bell mode enabled");
490 if (!strcmp(*argv, "off")) {
492 puts("bell mode disabled");
497 printf("bell [on | off]\n");
501 cmd_bye(int argc, const char **argv)
508 cmd_lcd(int argc, const char **argv)
513 printf("lcd takes only one argument\n");
520 if (argc == 0 && (dir = getenv("HOME")) == NULL) {
521 printf("HOME is not defined\n");
525 if (chdir(dir) == -1)
526 printf("cd: %s: %s\n", dir, strerror(errno));
530 cmd_lpwd(int argc, const char **argv)
534 if (getcwd(path, sizeof(path)) == NULL) {
535 printf("lpwd: %s\n", strerror(errno));
539 printf("%s\n", path);
543 cmd_ls(int argc, const char **argv)
549 printf("ls don't take arguments (yet)\n");
556 evbuffer_drain(dirbuf, EVBUFFER_LENGTH(dirbuf));
559 tread(1, off, BUFSIZ);
562 expect2(Rread, iota_tag);
564 len = np_read32(buf);
568 evbuffer_add_buffer(dirbuf, buf);
574 while (EVBUFFER_LENGTH(dirbuf) != 0) {
580 size = np_read16(dirbuf);
581 assert(size <= EVBUFFER_LENGTH(dirbuf));
583 np_read16(dirbuf); /* skip type */
584 np_read32(dirbuf); /* skip dev */
586 np_read_qid(dirbuf, &qid);
587 printf("%s ", pp_qid_type(qid.type));
589 np_read32(dirbuf); /* skip mode */
590 np_read32(dirbuf); /* skip atime */
591 np_read32(dirbuf); /* skip mtime */
593 len = np_read64(dirbuf);
594 printf("%llu ", (unsigned long long)len);
596 name = np_readstr(dirbuf);
597 printf("%s\n", name);
600 free(np_readstr(dirbuf)); /* skip uid */
601 free(np_readstr(dirbuf)); /* skip gid */
602 free(np_readstr(dirbuf)); /* skip muid */
609 cmd_verbose(int argc, const char **argv)
612 log_setverbose(!log_getverbose());
613 if (log_getverbose())
614 puts("verbose mode enabled");
616 puts("verbose mode disabled");
623 if (!strcmp(*argv, "on")) {
625 puts("verbose mode enabled");
629 if (!strcmp(*argv, "off")) {
631 puts("verbose mode disabled");
636 printf("verbose [on | off]\n");
640 excmd(int argc, const char **argv)
644 void (*fn)(int, const char **);
652 {"verbose", cmd_verbose},
658 for (i = 0; i < nitems(cmds); ++i) {
659 if (!strcmp(cmds[i].name, *argv)) {
660 cmds[i].fn(argc-1, argv+1);
665 log_warnx("unknown command %s", *argv);
669 main(int argc, char **argv)
673 log_init(1, LOG_DAEMON);
675 log_procinit(getprogname());
677 while ((ch = getopt(argc, argv, "C:cK:")) != -1) {
698 if ((evb = evbuffer_new()) == NULL)
699 fatal("evbuffer_new");
701 if ((buf = evbuffer_new()) == NULL)
702 fatal("evbuffer_new");
704 if ((dirbuf = evbuffer_new()) == NULL)
705 fatal("evbuferr_new");
707 do_connect(argv[0], argv[1]);
709 /* cmd_ls(0, NULL); */
713 char *line, *argv[16] = {0}, **ap;
715 if ((line = read_line("kamiftp> ")) == NULL)
718 for (argc = 0, ap = argv; ap < &argv[15] &&
719 (*ap = strsep(&line, " \t")) != NULL;) {
723 excmd(argc, (const char **)argv);