Fix the installation of manpages when using INSTALLROOT.
[tftp-hpa.git] / tftp / main.c
blob1b45005fc8eaa174fc7b22226f1befc0af64fd32
1 /* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */
2 /* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */
4 /*
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include "tftpsubs.h"
39 #ifndef lint
40 static const char *copyright UNUSED =
41 "@(#) Copyright (c) 1983, 1993\n\
42 The Regents of the University of California. All rights reserved.\n";
43 /* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */
44 /* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */
45 static const char *rcsid UNUSED =
46 "tftp-hpa $Id$";
47 #endif /* not lint */
49 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
52 * TFTP User Program -- Command Interface.
54 #include <sys/types.h>
55 #include <sys/socket.h>
56 #include <sys/file.h>
58 #include <netinet/in.h>
60 #include <arpa/inet.h>
62 #include <ctype.h>
63 #include <errno.h>
64 #include <netdb.h>
65 #include <setjmp.h>
66 #include <signal.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <unistd.h>
71 #include <fcntl.h>
72 #ifdef WITH_READLINE
73 #include <readline/readline.h>
74 #ifdef HAVE_READLINE_HISTORY_H
75 #include <readline/history.h>
76 #endif
77 #endif
79 #include "extern.h"
81 #define TIMEOUT 5 /* secs between rexmt's */
82 #define LBUFLEN 200 /* size of input buffer */
84 struct sockaddr_in peeraddr;
85 int f;
86 u_short port;
87 int trace;
88 int verbose;
89 int connected;
90 char mode[32];
91 #ifdef WITH_READLINE
92 char *line = NULL;
93 #else
94 char line[LBUFLEN];
95 #endif
96 int margc;
97 char *margv[20];
98 const char *prompt = "tftp> ";
99 sigjmp_buf toplevel;
100 void intr(int);
101 struct servent *sp;
103 void get (int, char **);
104 void help (int, char **);
105 void modecmd (int, char **);
106 void put (int, char **);
107 void quit (int, char **);
108 void setascii (int, char **);
109 void setbinary (int, char **);
110 void setpeer (int, char **);
111 void setrexmt (int, char **);
112 void settimeout (int, char **);
113 void settrace (int, char **);
114 void setverbose (int, char **);
115 void status (int, char **);
117 static void command (void);
119 static void getusage (char *);
120 static void makeargv (void);
121 static void putusage (char *);
122 static void settftpmode (const char *);
124 #define HELPINDENT (sizeof("connect"))
126 struct cmd {
127 const char *name;
128 const char *help;
129 void (*handler) (int, char **);
132 struct cmd cmdtab[] = {
133 { "connect",
134 "connect to remote tftp",
135 setpeer },
136 { "mode",
137 "set file transfer mode",
138 modecmd },
139 { "put",
140 "send file",
141 put },
142 { "get",
143 "receive file",
144 get },
145 { "quit",
146 "exit tftp",
147 quit },
148 { "verbose",
149 "toggle verbose mode",
150 setverbose },
151 { "trace",
152 "toggle packet tracing",
153 settrace },
154 { "status",
155 "show current status",
156 status },
157 { "binary",
158 "set mode to octet",
159 setbinary },
160 { "ascii",
161 "set mode to netascii",
162 setascii },
163 { "rexmt",
164 "set per-packet transmission timeout",
165 setrexmt },
166 { "timeout",
167 "set total retransmission timeout",
168 settimeout },
169 { "?",
170 "print help information",
171 help },
172 { "help",
173 "print help information",
174 help },
175 { 0, 0, 0 }
178 struct cmd *getcmd(char *);
179 char *tail(char *);
181 char *xstrdup(const char *);
184 main(int argc, char *argv[])
186 struct sockaddr_in s_in;
187 int c;
188 int pargc;
189 char **pargv;
191 while ((c = getopt(argc, argv, "Vv")) != -1) {
192 switch (c) {
193 case 'v':
194 verbose = 1;
195 break;
196 case 'V':
197 /* Print version and configuration to stdout and exit */
198 printf("%s\n", TFTP_CONFIG_STR);
199 exit(0);
200 default:
201 fprintf(stderr, "Usage: %s [-v] [host]\n", argv[0]);
202 exit(EX_USAGE);
206 pargc = argc - (optind-1);
207 pargv = argv + (optind-1);
209 sp = getservbyname("tftp", "udp");
210 if (sp == 0) {
211 /* Use canned values */
212 fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n");
213 sp = xmalloc(sizeof(struct servent));
214 sp->s_name = (char *)"tftp";
215 sp->s_aliases = NULL;
216 sp->s_port = htons(IPPORT_TFTP);
217 sp->s_proto = (char *)"udp";
218 exit(1);
220 f = socket(AF_INET, SOCK_DGRAM, 0);
221 if (f < 0) {
222 perror("tftp: socket");
223 exit(3);
225 bzero((char *)&s_in, sizeof (s_in));
226 s_in.sin_family = AF_INET;
227 if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
228 perror("tftp: bind");
229 exit(1);
231 strcpy(mode, "netascii");
232 bsd_signal(SIGINT, intr);
233 if (pargc > 1) {
234 if (sigsetjmp(toplevel,1) != 0)
235 exit(0);
236 setpeer(pargc, pargv);
238 if (sigsetjmp(toplevel,1) != 0)
239 (void)putchar('\n');
241 #ifdef WITH_READLINE
242 #ifdef HAVE_READLINE_HISTORY_H
243 using_history();
244 #endif
245 #endif
247 command();
249 return 0; /* Never reached */
252 char *hostname;
254 /* Called when a command is incomplete; modifies
255 the global variable "line" */
256 static void
257 getmoreargs(const char *partial, const char *mprompt)
259 #ifdef WITH_READLINE
260 char *eline;
261 int len, elen;
263 len = strlen(partial);
264 eline = readline(mprompt);
265 if (!eline)
266 exit(0); /* EOF */
268 elen = strlen(eline);
270 if (line)
271 free(line);
272 line = xmalloc(len+elen+1);
273 strcpy(line, partial);
274 strcpy(line+len, eline);
275 free(eline);
277 #ifdef HAVE_READLINE_HISTORY_H
278 add_history(line);
279 #endif
280 #else
281 int len = strlen(partial);
283 strcpy(line, partial);
284 fputs(mprompt, stdout);
285 if ( fgets(line+len, LBUFLEN-len, stdin) == 0 )
286 if ( feof(stdin) )
287 exit(0); /* EOF */
288 #endif
291 void
292 setpeer(int argc, char *argv[])
294 struct hostent *host;
296 if (argc < 2) {
297 getmoreargs("connect ", "(to) ");
298 makeargv();
299 argc = margc;
300 argv = margv;
302 if ((argc < 2) || (argc > 3)) {
303 printf("usage: %s host-name [port]\n", argv[0]);
304 return;
307 host = gethostbyname(argv[1]);
308 if (host == 0) {
309 connected = 0;
310 printf("%s: unknown host\n", argv[1]);
311 return;
313 peeraddr.sin_family = host->h_addrtype;
314 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
315 hostname = xstrdup(host->h_name);
317 port = sp->s_port;
318 if (argc == 3) {
319 struct servent *usp;
320 usp = getservbyname(argv[2], "udp");
321 if ( usp ) {
322 port = usp->s_port;
323 } else {
324 unsigned long myport;
325 char *ep;
326 myport = strtoul(argv[2], &ep, 10);
327 if (*ep || myport > 65535UL) {
328 printf("%s: bad port number\n", argv[2]);
329 connected = 0;
330 return;
332 port = htons((u_short)myport);
336 if (verbose) {
337 printf("Connected to %s (%s), port %u\n",
338 hostname, inet_ntoa(peeraddr.sin_addr),
339 (unsigned int)ntohs(port));
341 connected = 1;
344 struct modes {
345 const char *m_name;
346 const char *m_mode;
347 } modes[] = {
348 { "ascii", "netascii" },
349 { "netascii", "netascii" },
350 { "binary", "octet" },
351 { "image", "octet" },
352 { "octet", "octet" },
353 /* { "mail", "mail" }, */
354 { 0, 0 }
357 void
358 modecmd(int argc, char *argv[])
360 struct modes *p;
361 const char *sep;
363 if (argc < 2) {
364 printf("Using %s mode to transfer files.\n", mode);
365 return;
367 if (argc == 2) {
368 for (p = modes; p->m_name; p++)
369 if (strcmp(argv[1], p->m_name) == 0)
370 break;
371 if (p->m_name) {
372 settftpmode(p->m_mode);
373 return;
375 printf("%s: unknown mode\n", argv[1]);
376 /* drop through and print usage message */
379 printf("usage: %s [", argv[0]);
380 sep = " ";
381 for (p = modes; p->m_name; p++) {
382 printf("%s%s", sep, p->m_name);
383 if (*sep == ' ')
384 sep = " | ";
386 printf(" ]\n");
387 return;
390 void
391 setbinary(int argc, char *argv[])
393 (void)argc; (void)argv; /* Quiet unused warning */
394 settftpmode("octet");
397 void
398 setascii(int argc, char *argv[])
400 (void)argc; (void)argv; /* Quiet unused warning */
401 settftpmode("netascii");
404 static void
405 settftpmode(const char *newmode)
407 strcpy(mode, newmode);
408 if (verbose)
409 printf("mode set to %s\n", mode);
414 * Send file(s).
416 void
417 put(int argc, char *argv[])
419 int fd;
420 int n;
421 char *cp, *targ;
423 if (argc < 2) {
424 getmoreargs("send ", "(file) ");
425 makeargv();
426 argc = margc;
427 argv = margv;
429 if (argc < 2) {
430 putusage(argv[0]);
431 return;
433 targ = argv[argc - 1];
434 if (strchr(argv[argc - 1], ':')) {
435 struct hostent *hp;
437 for (n = 1; n < argc - 1; n++)
438 if (strchr(argv[n], ':')) {
439 putusage(argv[0]);
440 return;
442 cp = argv[argc - 1];
443 targ = strchr(cp, ':');
444 *targ++ = 0;
445 hp = gethostbyname(cp);
446 if (hp == NULL) {
447 fprintf(stderr, "tftp: %s: ", cp);
448 herror((char *)NULL);
449 return;
451 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
452 peeraddr.sin_family = hp->h_addrtype;
453 connected = 1;
454 hostname = xstrdup(hp->h_name);
456 if (!connected) {
457 printf("No target machine specified.\n");
458 return;
460 if (argc < 4) {
461 cp = argc == 2 ? tail(targ) : argv[1];
462 fd = open(cp, O_RDONLY);
463 if (fd < 0) {
464 fprintf(stderr, "tftp: "); perror(cp);
465 return;
467 if (verbose)
468 printf("putting %s to %s:%s [%s]\n",
469 cp, hostname, targ, mode);
470 peeraddr.sin_port = port;
471 tftp_sendfile(fd, targ, mode);
472 return;
474 /* this assumes the target is a directory */
475 /* on a remote unix system. hmmmm. */
476 cp = strchr(targ, '\0');
477 *cp++ = '/';
478 for (n = 1; n < argc - 1; n++) {
479 strcpy(cp, tail(argv[n]));
480 fd = open(argv[n], O_RDONLY);
481 if (fd < 0) {
482 fprintf(stderr, "tftp: "); perror(argv[n]);
483 continue;
485 if (verbose)
486 printf("putting %s to %s:%s [%s]\n",
487 argv[n], hostname, targ, mode);
488 peeraddr.sin_port = port;
489 tftp_sendfile(fd, targ, mode);
493 static void
494 putusage(char *s)
496 printf("usage: %s file ... host:target, or\n", s);
497 printf(" %s file ... target (when already connected)\n", s);
501 * Receive file(s).
503 void
504 get(int argc, char *argv[])
506 int fd;
507 int n;
508 char *cp;
509 char *src;
511 if (argc < 2) {
512 getmoreargs("get ", "(files) ");
513 makeargv();
514 argc = margc;
515 argv = margv;
517 if (argc < 2) {
518 getusage(argv[0]);
519 return;
521 if (!connected) {
522 for (n = 1; n < argc ; n++)
523 if (strchr(argv[n], ':') == 0) {
524 getusage(argv[0]);
525 return;
528 for (n = 1; n < argc ; n++) {
529 src = strchr(argv[n], ':');
530 if (src == NULL)
531 src = argv[n];
532 else {
533 struct hostent *hp;
535 *src++ = 0;
536 hp = gethostbyname(argv[n]);
537 if (hp == NULL) {
538 fprintf(stderr, "tftp: %s: ", argv[n]);
539 herror((char *)NULL);
540 continue;
542 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
543 hp->h_length);
544 peeraddr.sin_family = hp->h_addrtype;
545 connected = 1;
546 hostname = xstrdup(hp->h_name);
548 if (argc < 4) {
549 cp = argc == 3 ? argv[2] : tail(src);
550 fd = creat(cp, 0644);
551 if (fd < 0) {
552 fprintf(stderr, "tftp: "); perror(cp);
553 return;
555 if (verbose)
556 printf("getting from %s:%s to %s [%s]\n",
557 hostname, src, cp, mode);
558 peeraddr.sin_port = port;
559 tftp_recvfile(fd, src, mode);
560 break;
562 cp = tail(src); /* new .. jdg */
563 fd = creat(cp, 0644);
564 if (fd < 0) {
565 fprintf(stderr, "tftp: "); perror(cp);
566 continue;
568 if (verbose)
569 printf("getting from %s:%s to %s [%s]\n",
570 hostname, src, cp, mode);
571 peeraddr.sin_port = port;
572 tftp_recvfile(fd, src, mode);
576 static void
577 getusage(char *s)
579 printf("usage: %s host:file host:file ... file, or\n", s);
580 printf(" %s file file ... file if connected\n", s);
583 int rexmtval = TIMEOUT;
585 void
586 setrexmt(int argc, char *argv[])
588 int t;
590 if (argc < 2) {
591 getmoreargs("rexmt-timeout ", "(value) ");
592 makeargv();
593 argc = margc;
594 argv = margv;
596 if (argc != 2) {
597 printf("usage: %s value\n", argv[0]);
598 return;
600 t = atoi(argv[1]);
601 if (t < 0)
602 printf("%s: bad value\n", argv[1]);
603 else
604 rexmtval = t;
607 int maxtimeout = 5 * TIMEOUT;
609 void
610 settimeout(int argc, char *argv[])
612 int t;
614 if (argc < 2) {
615 getmoreargs("maximum-timeout ", "(value) ");
616 makeargv();
617 argc = margc;
618 argv = margv;
620 if (argc != 2) {
621 printf("usage: %s value\n", argv[0]);
622 return;
624 t = atoi(argv[1]);
625 if (t < 0)
626 printf("%s: bad value\n", argv[1]);
627 else
628 maxtimeout = t;
631 void
632 status(int argc, char *argv[])
634 (void)argc; (void)argv; /* Quiet unused warning */
635 if (connected)
636 printf("Connected to %s.\n", hostname);
637 else
638 printf("Not connected.\n");
639 printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
640 verbose ? "on" : "off", trace ? "on" : "off");
641 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
642 rexmtval, maxtimeout);
645 void
646 intr(int sig)
648 (void)sig; /* Quiet unused warning */
650 bsd_signal(SIGALRM, SIG_IGN);
651 alarm(0);
652 siglongjmp(toplevel, -1);
655 char *
656 tail(char *filename)
658 char *s;
660 while (*filename) {
661 s = strrchr(filename, '/');
662 if (s == NULL)
663 break;
664 if (s[1])
665 return (s + 1);
666 *s = '\0';
668 return (filename);
672 * Command parser.
674 static void
675 command(void)
677 struct cmd *c;
679 for (;;) {
680 #ifdef WITH_READLINE
681 if ( line )
682 free(line);
683 line = readline(prompt);
684 if ( !line )
685 exit(0); /* EOF */
686 #else
687 fputs(prompt, stdout);
688 if (fgets(line, LBUFLEN, stdin) == 0) {
689 if (feof(stdin)) {
690 exit(0);
691 } else {
692 continue;
695 #endif
696 if ((line[0] == 0) || (line[0] == '\n'))
697 continue;
698 #ifdef WITH_READLINE
699 #ifdef HAVE_READLINE_HISTORY_H
700 add_history(line);
701 #endif
702 #endif
703 makeargv();
704 if (margc == 0)
705 continue;
707 c = getcmd(margv[0]);
708 if (c == (struct cmd *)-1) {
709 printf("?Ambiguous command\n");
710 continue;
712 if (c == 0) {
713 printf("?Invalid command\n");
714 continue;
716 (*c->handler)(margc, margv);
720 struct cmd *
721 getcmd(char *name)
723 const char *p;
724 char *q;
725 struct cmd *c, *found;
726 int nmatches, longest;
728 longest = 0;
729 nmatches = 0;
730 found = 0;
731 for (c = cmdtab; (p = c->name) != NULL; c++) {
732 for (q = name; *q == *p++; q++)
733 if (*q == 0) /* exact match? */
734 return (c);
735 if (!*q) { /* the name was a prefix */
736 if (q - name > longest) {
737 longest = q - name;
738 nmatches = 1;
739 found = c;
740 } else if (q - name == longest)
741 nmatches++;
744 if (nmatches > 1)
745 return ((struct cmd *)-1);
746 return (found);
750 * Slice a string up into argc/argv.
752 static void
753 makeargv(void)
755 char *cp;
756 char **argp = margv;
758 margc = 0;
759 for (cp = line; *cp;) {
760 while (isspace(*cp))
761 cp++;
762 if (*cp == '\0')
763 break;
764 *argp++ = cp;
765 margc += 1;
766 while (*cp != '\0' && !isspace(*cp))
767 cp++;
768 if (*cp == '\0')
769 break;
770 *cp++ = '\0';
772 *argp++ = 0;
775 void
776 quit(int argc, char *argv[])
778 (void)argc; (void)argv; /* Quiet unused warning */
779 exit(0);
783 * Help command.
785 void
786 help(int argc, char *argv[])
788 struct cmd *c;
790 printf("%s\n", VERSION);
792 if (argc == 1) {
793 printf("Commands may be abbreviated. Commands are:\n\n");
794 for (c = cmdtab; c->name; c++)
795 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
796 return;
798 while (--argc > 0) {
799 char *arg;
800 arg = *++argv;
801 c = getcmd(arg);
802 if (c == (struct cmd *)-1)
803 printf("?Ambiguous help command %s\n", arg);
804 else if (c == (struct cmd *)0)
805 printf("?Invalid help command %s\n", arg);
806 else
807 printf("%s\n", c->help);
811 void
812 settrace(int argc, char *argv[])
814 (void)argc; (void)argv; /* Quiet unused warning */
816 trace = !trace;
817 printf("Packet tracing %s.\n", trace ? "on" : "off");
820 void
821 setverbose(int argc, char *argv[])
823 (void)argc; (void)argv; /* Quiet unused warning */
825 verbose = !verbose;
826 printf("Verbose mode %s.\n", verbose ? "on" : "off");