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
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
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>
44 #include <netinet/in.h>
46 #include <arpa/inet.h>
62 #define TIMEOUT 5 /* secs between rexmt's */
64 struct sockaddr_storage peeraddr
;
73 char *margv
[MAX_MARGV
];
75 volatile int txrx_error
;
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"))
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
},
140 struct cmd
*getcmd(char *);
144 main(int argc
, char **argv
)
147 strcpy(mode
, "netascii");
148 signal(SIGINT
, intr
);
150 if (setjmp(toplevel
) != 0)
154 if (setjmp(toplevel
) != 0)
159 char hostname
[MAXHOSTNAMELEN
];
162 setpeer0(char *host
, const char *port
)
164 struct addrinfo hints
, *res0
, *res
;
166 struct sockaddr_storage ss
;
167 const char *cause
= "unknown";
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
;
182 error
= getaddrinfo(host
, port
, &hints
, &res0
);
184 warnx("%s", gai_strerror(error
));
188 for (res
= res0
; res
; res
= res
->ai_next
) {
189 if (res
->ai_addrlen
> sizeof(peeraddr
))
191 f
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
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) {
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
,
219 (void) strncpy(hostname
, host
, sizeof(hostname
));
220 hostname
[sizeof(hostname
)-1] = 0;
228 setpeer(int argc
, char **argv
)
232 strcpy(line
, "Connect ");
234 fgets(&line
[strlen(line
)], sizeof line
- strlen(line
), stdin
);
239 if ((argc
< 2) || (argc
> 3)) {
240 printf("usage: %s host-name [port]\n", argv
[0]);
244 setpeer0(argv
[1], NULL
);
246 setpeer0(argv
[1], argv
[2]);
253 { "ascii", "netascii" },
254 { "netascii", "netascii" },
255 { "binary", "octet" },
256 { "image", "octet" },
257 { "octet", "octet" },
258 /* { "mail", "mail" }, */
263 modecmd(int argc
, char **argv
)
269 printf("Using %s mode to transfer files.\n", mode
);
273 for (p
= modes
; p
->m_name
; p
++)
274 if (strcmp(argv
[1], p
->m_name
) == 0)
277 settftpmode(p
->m_mode
);
280 printf("%s: unknown mode\n", argv
[1]);
281 /* drop through and print usage message */
284 printf("usage: %s [", argv
[0]);
286 for (p
= modes
; p
->m_name
; p
++) {
287 printf("%s%s", sep
, p
->m_name
);
296 setbinary(int argc __unused
, char **argv __unused
)
299 settftpmode("octet");
303 setascii(int argc __unused
, char **argv __unused
)
306 settftpmode("netascii");
310 settftpmode(const char *newmode
)
312 strcpy(mode
, newmode
);
314 printf("mode set to %s\n", mode
);
322 put(int argc
, char **argv
)
329 strcpy(line
, "send ");
331 fgets(&line
[strlen(line
)], sizeof line
- strlen(line
), stdin
);
340 targ
= argv
[argc
- 1];
341 if (strrchr(argv
[argc
- 1], ':')) {
342 for (n
= 1; n
< argc
- 1; n
++)
343 if (strchr(argv
[n
], ':')) {
348 targ
= strrchr(cp
, ':');
350 if (cp
[0] == '[' && cp
[strlen(cp
) - 1] == ']') {
351 cp
[strlen(cp
) - 1] = '\0';
357 printf("No target machine specified.\n");
361 cp
= argc
== 2 ? tail(targ
) : argv
[1];
362 fd
= open(cp
, O_RDONLY
);
368 printf("putting %s to %s:%s [%s]\n",
369 cp
, hostname
, targ
, mode
);
370 xmitfile(fd
, targ
, mode
);
373 /* this assumes the target is a directory */
374 /* on a remote unix system. hmmmm. */
375 cp
= strchr(targ
, '\0');
377 for (n
= 1; n
< argc
- 1; n
++) {
378 strcpy(cp
, tail(argv
[n
]));
379 fd
= open(argv
[n
], O_RDONLY
);
385 printf("putting %s to %s:%s [%s]\n",
386 argv
[n
], hostname
, targ
, mode
);
387 xmitfile(fd
, targ
, mode
);
394 printf("usage: %s file ... host:target, or\n", s
);
395 printf(" %s file ... target (when already connected)\n", s
);
402 get(int argc
, char **argv
)
410 strcpy(line
, "get ");
412 fgets(&line
[strlen(line
)], sizeof line
- strlen(line
), stdin
);
422 for (n
= 1; n
< argc
; n
++)
423 if (strrchr(argv
[n
], ':') == 0) {
428 for (n
= 1; n
< argc
; n
++) {
429 src
= strrchr(argv
[n
], ':');
435 if (cp
[0] == '[' && cp
[strlen(cp
) - 1] == ']') {
436 cp
[strlen(cp
) - 1] = '\0';
444 cp
= argc
== 3 ? argv
[2] : tail(src
);
445 fd
= creat(cp
, 0644);
451 printf("getting from %s:%s to %s [%s]\n",
452 hostname
, src
, cp
, mode
);
453 recvfile(fd
, src
, mode
);
456 cp
= tail(src
); /* new .. jdg */
457 fd
= creat(cp
, 0644);
463 printf("getting from %s:%s to %s [%s]\n",
464 hostname
, src
, cp
, mode
);
465 recvfile(fd
, src
, mode
);
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
;
479 setrexmt(int argc
, char **argv
)
484 strcpy(line
, "Rexmt-timeout ");
486 fgets(&line
[strlen(line
)], sizeof line
- strlen(line
), stdin
);
492 printf("usage: %s value\n", argv
[0]);
497 printf("%s: bad value\n", argv
[1]);
502 int maxtimeout
= 5 * TIMEOUT
;
505 settimeout(int argc
, char **argv
)
510 strcpy(line
, "Maximum-timeout ");
512 fgets(&line
[strlen(line
)], sizeof line
- strlen(line
), stdin
);
518 printf("usage: %s value\n", argv
[0]);
523 printf("%s: bad value\n", argv
[1]);
529 status(int argc __unused
, char **argv __unused
)
532 printf("Connected to %s.\n", hostname
);
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
);
542 intr(int signo __unused
)
544 signal(SIGALRM
, SIG_IGN
);
546 longjmp(toplevel
, -1);
555 s
= strrchr(filename
, '/');
580 static History
*hist
;
583 int len
, num
, 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);
598 if ((bp
= el_gets(el
, &num
)) == NULL
|| num
== 0)
600 len
= (num
> MAXLINE
) ? MAXLINE
: num
;
601 memcpy(line
, bp
, len
);
603 history(hist
, &he
, H_ENTER
, bp
);
605 if (fgets(line
, sizeof line
, stdin
) == 0) {
613 if ((cp
= strchr(line
, '\n')))
620 c
= getcmd(margv
[0]);
621 if (c
== (struct cmd
*)-1) {
622 printf("?Ambiguous command\n");
626 printf("?Invalid command\n");
629 (*c
->handler
)(margc
, margv
);
638 struct cmd
*c
, *found
;
639 int nmatches
, longest
;
644 for (c
= cmdtab
; (p
= c
->name
) != NULL
; c
++) {
645 for (q
= name
; *q
== *p
++; q
++)
646 if (*q
== 0) /* exact match? */
648 if (!*q
) { /* the name was a prefix */
649 if (q
- name
> longest
) {
653 } else if (q
- name
== longest
)
658 return ((struct cmd
*)-1);
663 * Slice a string up into argc/argv.
672 if ((cp
= strchr(line
, '\n')))
674 for (cp
= line
; margc
< MAX_MARGV
-1 && *cp
;) {
681 while (*cp
!= '\0' && !isspace(*cp
))
691 quit(int argc __unused
, char **argv __unused
)
701 help(int argc
, char **argv
)
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
);
715 if (c
== (struct cmd
*)-1)
716 printf("?Ambiguous help command %s\n", arg
);
718 printf("?Invalid help command %s\n", arg
);
720 printf("%s\n", c
->help
);
725 settrace(int argc __unused
, char **argv __unused
)
728 printf("Packet tracing %s.\n", trace
? "on" : "off");
732 setverbose(int argc __unused
, char **argv __unused
)
735 printf("Verbose mode %s.\n", verbose
? "on" : "off");