hammer2 - Fix critical bulkfree bug, refactor hammer2_io
[dragonfly.git] / usr.bin / tftp / main.c
blob1e7c618acb750e1329ea0832a5de64f0c416c061
1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * @(#) Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved.
30 * @(#)main.c 8.1 (Berkeley) 6/6/93
31 * $FreeBSD: src/usr.bin/tftp/main.c,v 1.8.2.3 2002/05/14 22:08:07 bsd Exp $
34 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
37 * TFTP User Program -- Command Interface.
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/file.h>
44 #include <netinet/in.h>
46 #include <arpa/inet.h>
48 #include <ctype.h>
49 #include <err.h>
50 #include <histedit.h>
51 #include <netdb.h>
52 #include <setjmp.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
59 #include "extern.h"
61 #define MAXLINE 200
62 #define TIMEOUT 5 /* secs between rexmt's */
64 struct sockaddr_storage peeraddr;
65 int f;
66 int trace;
67 int verbose;
68 int connected;
69 char mode[32];
70 char line[MAXLINE];
71 int margc;
72 #define MAX_MARGV 20
73 char *margv[MAX_MARGV];
74 jmp_buf toplevel;
75 volatile int txrx_error;
76 void intr(int);
78 void get(int, char **);
79 void help(int, char **);
80 void modecmd(int, char **);
81 void put(int, char **);
82 void quit(int, char **);
83 void setascii(int, char **);
84 void setbinary(int, char **);
85 void setpeer0(char *, const char *);
86 void setpeer(int, char **);
87 void setrexmt(int, char **);
88 void settimeout(int, char **);
89 void settrace(int, char **);
90 void setverbose(int, char **);
91 void status(int, char **);
93 static void command(void) __dead2;
94 static const char *command_prompt(void);
96 static void getusage(char *);
97 static void makeargv(void);
98 static void putusage(char *);
99 static void settftpmode(const char *);
101 #define HELPINDENT (sizeof("connect"))
103 struct cmd {
104 const char *name;
105 const char *help;
106 void (*handler)(int, char **);
109 const char vhelp[] = "toggle verbose mode";
110 const char thelp[] = "toggle packet tracing";
111 const char chelp[] = "connect to remote tftp";
112 const char qhelp[] = "exit tftp";
113 const char hhelp[] = "print help information";
114 const char shelp[] = "send file";
115 const char rhelp[] = "receive file";
116 const char mhelp[] = "set file transfer mode";
117 const char sthelp[] = "show current status";
118 const char xhelp[] = "set per-packet retransmission timeout";
119 const char ihelp[] = "set total retransmission timeout";
120 const char ashelp[] = "set mode to netascii";
121 const char bnhelp[] = "set mode to octet";
123 struct cmd cmdtab[] = {
124 { "connect", chelp, setpeer },
125 { "mode", mhelp, modecmd },
126 { "put", shelp, put },
127 { "get", rhelp, get },
128 { "quit", qhelp, quit },
129 { "verbose", vhelp, setverbose },
130 { "trace", thelp, settrace },
131 { "status", sthelp, status },
132 { "binary", bnhelp, setbinary },
133 { "ascii", ashelp, setascii },
134 { "rexmt", xhelp, setrexmt },
135 { "timeout", ihelp, settimeout },
136 { "?", hhelp, help },
137 { .name = NULL }
140 struct cmd *getcmd(char *);
141 char *tail(char *);
144 main(int argc, char **argv)
146 f = -1;
147 strcpy(mode, "netascii");
148 signal(SIGINT, intr);
149 if (argc > 1) {
150 if (setjmp(toplevel) != 0)
151 exit(txrx_error);
152 setpeer(argc, argv);
154 if (setjmp(toplevel) != 0)
155 (void)putchar('\n');
156 command();
159 char hostname[MAXHOSTNAMELEN];
161 void
162 setpeer0(char *host, const char *port)
164 struct addrinfo hints, *res0, *res;
165 int error;
166 struct sockaddr_storage ss;
167 const char *cause = "unknown";
169 if (connected) {
170 close(f);
171 f = -1;
173 connected = 0;
175 memset(&hints, 0, sizeof(hints));
176 hints.ai_family = PF_UNSPEC;
177 hints.ai_socktype = SOCK_DGRAM;
178 hints.ai_protocol = IPPROTO_UDP;
179 hints.ai_flags = AI_CANONNAME;
180 if (!port)
181 port = "tftp";
182 error = getaddrinfo(host, port, &hints, &res0);
183 if (error) {
184 warnx("%s", gai_strerror(error));
185 return;
188 for (res = res0; res; res = res->ai_next) {
189 if (res->ai_addrlen > sizeof(peeraddr))
190 continue;
191 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
192 if (f < 0) {
193 cause = "socket";
194 continue;
197 memset(&ss, 0, sizeof(ss));
198 ss.ss_family = res->ai_family;
199 ss.ss_len = res->ai_addrlen;
200 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
201 cause = "bind";
202 close(f);
203 f = -1;
204 continue;
207 break;
210 if (f < 0)
211 warn("%s", cause);
212 else {
213 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
214 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
215 if (res->ai_canonname) {
216 (void) strncpy(hostname, res->ai_canonname,
217 sizeof(hostname));
218 } else
219 (void) strncpy(hostname, host, sizeof(hostname));
220 hostname[sizeof(hostname)-1] = 0;
221 connected = 1;
224 freeaddrinfo(res0);
227 void
228 setpeer(int argc, char **argv)
231 if (argc < 2) {
232 strcpy(line, "Connect ");
233 printf("(to) ");
234 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
235 makeargv();
236 argc = margc;
237 argv = margv;
239 if ((argc < 2) || (argc > 3)) {
240 printf("usage: %s host-name [port]\n", argv[0]);
241 return;
243 if (argc == 3)
244 setpeer0(argv[1], NULL);
245 else
246 setpeer0(argv[1], argv[2]);
249 struct modes {
250 const char *m_name;
251 const char *m_mode;
252 } modes[] = {
253 { "ascii", "netascii" },
254 { "netascii", "netascii" },
255 { "binary", "octet" },
256 { "image", "octet" },
257 { "octet", "octet" },
258 /* { "mail", "mail" }, */
259 { NULL, NULL }
262 void
263 modecmd(int argc, char **argv)
265 struct modes *p;
266 const char *sep;
268 if (argc < 2) {
269 printf("Using %s mode to transfer files.\n", mode);
270 return;
272 if (argc == 2) {
273 for (p = modes; p->m_name; p++)
274 if (strcmp(argv[1], p->m_name) == 0)
275 break;
276 if (p->m_name) {
277 settftpmode(p->m_mode);
278 return;
280 printf("%s: unknown mode\n", argv[1]);
281 /* drop through and print usage message */
284 printf("usage: %s [", argv[0]);
285 sep = " ";
286 for (p = modes; p->m_name; p++) {
287 printf("%s%s", sep, p->m_name);
288 if (*sep == ' ')
289 sep = " | ";
291 printf(" ]\n");
292 return;
295 void
296 setbinary(int argc __unused, char **argv __unused)
299 settftpmode("octet");
302 void
303 setascii(int argc __unused, char **argv __unused)
306 settftpmode("netascii");
309 static void
310 settftpmode(const char *newmode)
312 strcpy(mode, newmode);
313 if (verbose)
314 printf("mode set to %s\n", mode);
319 * Send file(s).
321 void
322 put(int argc, char **argv)
324 int fd;
325 int n;
326 char *cp, *targ;
328 if (argc < 2) {
329 strcpy(line, "send ");
330 printf("(file) ");
331 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
332 makeargv();
333 argc = margc;
334 argv = margv;
336 if (argc < 2) {
337 putusage(argv[0]);
338 return;
340 targ = argv[argc - 1];
341 if (strrchr(argv[argc - 1], ':')) {
342 for (n = 1; n < argc - 1; n++)
343 if (strchr(argv[n], ':')) {
344 putusage(argv[0]);
345 return;
347 cp = argv[argc - 1];
348 targ = strrchr(cp, ':');
349 *targ++ = 0;
350 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
351 cp[strlen(cp) - 1] = '\0';
352 cp++;
354 setpeer0(cp, NULL);
356 if (!connected) {
357 printf("No target machine specified.\n");
358 return;
360 if (argc < 4) {
361 cp = argc == 2 ? tail(targ) : argv[1];
362 fd = open(cp, O_RDONLY);
363 if (fd < 0) {
364 warn("%s", cp);
365 return;
367 if (verbose)
368 printf("putting %s to %s:%s [%s]\n",
369 cp, hostname, targ, mode);
370 xmitfile(fd, targ, mode);
371 return;
373 /* this assumes the target is a directory */
374 /* on a remote unix system. hmmmm. */
375 cp = strchr(targ, '\0');
376 *cp++ = '/';
377 for (n = 1; n < argc - 1; n++) {
378 strcpy(cp, tail(argv[n]));
379 fd = open(argv[n], O_RDONLY);
380 if (fd < 0) {
381 warn("%s", argv[n]);
382 continue;
384 if (verbose)
385 printf("putting %s to %s:%s [%s]\n",
386 argv[n], hostname, targ, mode);
387 xmitfile(fd, targ, mode);
391 static void
392 putusage(char *s)
394 printf("usage: %s file ... host:target, or\n", s);
395 printf(" %s file ... target (when already connected)\n", s);
399 * Receive file(s).
401 void
402 get(int argc, char **argv)
404 int fd;
405 int n;
406 char *cp;
407 char *src;
409 if (argc < 2) {
410 strcpy(line, "get ");
411 printf("(files) ");
412 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
413 makeargv();
414 argc = margc;
415 argv = margv;
417 if (argc < 2) {
418 getusage(argv[0]);
419 return;
421 if (!connected) {
422 for (n = 1; n < argc ; n++)
423 if (strrchr(argv[n], ':') == 0) {
424 getusage(argv[0]);
425 return;
428 for (n = 1; n < argc ; n++) {
429 src = strrchr(argv[n], ':');
430 if (src == NULL)
431 src = argv[n];
432 else {
433 *src++ = 0;
434 cp = argv[n];
435 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
436 cp[strlen(cp) - 1] = '\0';
437 cp++;
439 setpeer0(cp, NULL);
440 if (!connected)
441 continue;
443 if (argc < 4) {
444 cp = argc == 3 ? argv[2] : tail(src);
445 fd = creat(cp, 0644);
446 if (fd < 0) {
447 warn("%s", cp);
448 return;
450 if (verbose)
451 printf("getting from %s:%s to %s [%s]\n",
452 hostname, src, cp, mode);
453 recvfile(fd, src, mode);
454 break;
456 cp = tail(src); /* new .. jdg */
457 fd = creat(cp, 0644);
458 if (fd < 0) {
459 warn("%s", cp);
460 continue;
462 if (verbose)
463 printf("getting from %s:%s to %s [%s]\n",
464 hostname, src, cp, mode);
465 recvfile(fd, src, mode);
469 static void
470 getusage(char *s)
472 printf("usage: %s host:file host:file ... file, or\n", s);
473 printf(" %s file file ... file if connected\n", s);
476 int rexmtval = TIMEOUT;
478 void
479 setrexmt(int argc, char **argv)
481 int t;
483 if (argc < 2) {
484 strcpy(line, "Rexmt-timeout ");
485 printf("(value) ");
486 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
487 makeargv();
488 argc = margc;
489 argv = margv;
491 if (argc != 2) {
492 printf("usage: %s value\n", argv[0]);
493 return;
495 t = atoi(argv[1]);
496 if (t < 0)
497 printf("%s: bad value\n", argv[1]);
498 else
499 rexmtval = t;
502 int maxtimeout = 5 * TIMEOUT;
504 void
505 settimeout(int argc, char **argv)
507 int t;
509 if (argc < 2) {
510 strcpy(line, "Maximum-timeout ");
511 printf("(value) ");
512 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
513 makeargv();
514 argc = margc;
515 argv = margv;
517 if (argc != 2) {
518 printf("usage: %s value\n", argv[0]);
519 return;
521 t = atoi(argv[1]);
522 if (t < 0)
523 printf("%s: bad value\n", argv[1]);
524 else
525 maxtimeout = t;
528 void
529 status(int argc __unused, char **argv __unused)
531 if (connected)
532 printf("Connected to %s.\n", hostname);
533 else
534 printf("Not connected.\n");
535 printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
536 verbose ? "on" : "off", trace ? "on" : "off");
537 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
538 rexmtval, maxtimeout);
541 void
542 intr(int signo __unused)
544 signal(SIGALRM, SIG_IGN);
545 alarm(0);
546 longjmp(toplevel, -1);
549 char *
550 tail(char *filename)
552 char *s;
554 while (*filename) {
555 s = strrchr(filename, '/');
556 if (s == NULL)
557 break;
558 if (s[1])
559 return (s + 1);
560 *s = '\0';
562 return (filename);
565 static const char *
566 command_prompt(void)
568 return ("tftp> ");
572 * Command parser.
574 static void
575 command(void)
577 HistEvent he;
578 struct cmd *c;
579 static EditLine *el;
580 static History *hist;
581 const char *bp;
582 char *cp;
583 int len, num, vrbose;
585 vrbose = isatty(0);
586 if (vrbose) {
587 el = el_init("tftp", stdin, stdout, stderr);
588 hist = history_init();
589 history(hist, &he, H_SETSIZE, 100);
590 el_set(el, EL_HIST, history, hist);
591 el_set(el, EL_EDITOR, "emacs");
592 el_set(el, EL_PROMPT, command_prompt);
593 el_set(el, EL_SIGNAL, 1);
594 el_source(el, NULL);
596 for (;;) {
597 if (vrbose) {
598 if ((bp = el_gets(el, &num)) == NULL || num == 0)
599 exit(txrx_error);
600 len = (num > MAXLINE) ? MAXLINE : num;
601 memcpy(line, bp, len);
602 line[len] = '\0';
603 history(hist, &he, H_ENTER, bp);
604 } else {
605 if (fgets(line, sizeof line , stdin) == 0) {
606 if (feof(stdin)) {
607 exit(txrx_error);
608 } else {
609 continue;
613 if ((cp = strchr(line, '\n')))
614 *cp = '\0';
615 if (line[0] == 0)
616 continue;
617 makeargv();
618 if (margc == 0)
619 continue;
620 c = getcmd(margv[0]);
621 if (c == (struct cmd *)-1) {
622 printf("?Ambiguous command\n");
623 continue;
625 if (c == NULL) {
626 printf("?Invalid command\n");
627 continue;
629 (*c->handler)(margc, margv);
633 struct cmd *
634 getcmd(char *name)
636 const char *p;
637 char *q;
638 struct cmd *c, *found;
639 int nmatches, longest;
641 longest = 0;
642 nmatches = 0;
643 found = NULL;
644 for (c = cmdtab; (p = c->name) != NULL; c++) {
645 for (q = name; *q == *p++; q++)
646 if (*q == 0) /* exact match? */
647 return (c);
648 if (!*q) { /* the name was a prefix */
649 if (q - name > longest) {
650 longest = q - name;
651 nmatches = 1;
652 found = c;
653 } else if (q - name == longest)
654 nmatches++;
657 if (nmatches > 1)
658 return ((struct cmd *)-1);
659 return (found);
663 * Slice a string up into argc/argv.
665 static void
666 makeargv(void)
668 char *cp;
669 char **argp = margv;
671 margc = 0;
672 if ((cp = strchr(line, '\n')))
673 *cp = '\0';
674 for (cp = line; margc < MAX_MARGV -1 && *cp;) {
675 while (isspace(*cp))
676 cp++;
677 if (*cp == '\0')
678 break;
679 *argp++ = cp;
680 margc += 1;
681 while (*cp != '\0' && !isspace(*cp))
682 cp++;
683 if (*cp == '\0')
684 break;
685 *cp++ = '\0';
687 *argp++ = NULL;
690 void
691 quit(int argc __unused, char **argv __unused)
694 exit(txrx_error);
698 * Help command.
700 void
701 help(int argc, char **argv)
703 struct cmd *c;
705 if (argc == 1) {
706 printf("Commands may be abbreviated. Commands are:\n\n");
707 for (c = cmdtab; c->name; c++)
708 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
709 return;
711 while (--argc > 0) {
712 char *arg;
713 arg = *++argv;
714 c = getcmd(arg);
715 if (c == (struct cmd *)-1)
716 printf("?Ambiguous help command %s\n", arg);
717 else if (c == NULL)
718 printf("?Invalid help command %s\n", arg);
719 else
720 printf("%s\n", c->help);
724 void
725 settrace(int argc __unused, char **argv __unused)
727 trace = !trace;
728 printf("Packet tracing %s.\n", trace ? "on" : "off");
731 void
732 setverbose(int argc __unused, char **argv __unused)
734 verbose = !verbose;
735 printf("Verbose mode %s.\n", verbose ? "on" : "off");