manual page distributed with 4.1BSD
[unix-history] / usr / src / usr.bin / tftp / main.c
CommitLineData
524aa063 1#ifndef lint
fc945f1a 2static char sccsid[] = "@(#)main.c 4.9 (Berkeley) %G%";
524aa063 3#endif
c3353d83
SL
4
5/*
6 * TFTP User Program -- Command Interface.
7 */
8#include <sys/types.h>
c3353d83 9#include <sys/socket.h>
61ad9979 10#include <sys/file.h>
de3b21e8
SL
11
12#include <netinet/in.h>
13
c3353d83
SL
14#include <signal.h>
15#include <stdio.h>
16#include <errno.h>
17#include <setjmp.h>
18#include <ctype.h>
4d5e33bd 19#include <netdb.h>
c3353d83 20
61ad9979
SL
21#define TIMEOUT 5 /* secs between rexmt's */
22
fc945f1a 23struct sockaddr_in sin = { AF_INET };
c3353d83 24int f;
c3353d83 25int trace;
c3353d83
SL
26int connected;
27char mode[32];
28char line[200];
29int margc;
30char *margv[20];
31char *prompt = "tftp";
32jmp_buf toplevel;
33int intr();
4d5e33bd 34struct servent *sp;
c3353d83 35
fc945f1a 36int quit(), help(), settrace(), status();
61ad9979 37int get(), put(), setpeer(), setmode(), setrexmt(), settimeout();
c3353d83
SL
38
39#define HELPINDENT (sizeof("connect"))
40
41struct cmd {
42 char *name;
43 char *help;
44 int (*handler)();
45};
46
c3353d83
SL
47char thelp[] = "toggle packet tracing";
48char chelp[] = "connect to remote tftp";
49char qhelp[] = "exit tftp";
50char hhelp[] = "print help information";
51char shelp[] = "send file";
52char rhelp[] = "receive file";
53char mhelp[] = "set file transfer mode";
54char sthelp[] = "show current status";
61ad9979
SL
55char xhelp[] = "set per-packet retransmission timeout";
56char ihelp[] = "set total retransmission timeout";
c3353d83
SL
57
58struct cmd cmdtab[] = {
59 { "connect", chelp, setpeer },
60 { "mode", mhelp, setmode },
61 { "put", shelp, put },
62 { "get", rhelp, get },
63 { "quit", qhelp, quit },
c3353d83
SL
64 { "trace", thelp, settrace },
65 { "status", sthelp, status },
61ad9979
SL
66 { "rexmt", xhelp, setrexmt },
67 { "timeout", ihelp, settimeout },
c3353d83
SL
68 { "?", hhelp, help },
69 0
70};
71
72struct cmd *getcmd();
73char *tail();
74char *index();
75char *rindex();
76
77main(argc, argv)
78 char *argv[];
79{
61ad9979
SL
80 int top;
81
4d5e33bd
SL
82 sp = getservbyname("tftp", "udp");
83 if (sp == 0) {
84 fprintf(stderr, "tftp: udp/tftp: unknown service\n");
85 exit(1);
86 }
85b11450 87 f = socket(AF_INET, SOCK_DGRAM, 0, 0);
c3353d83 88 if (f < 0) {
61ad9979 89 perror("tftp: socket");
c3353d83
SL
90 exit(3);
91 }
61ad9979
SL
92 if (bind(f, &sin, sizeof (sin)) < 0) {
93 perror("tftp: bind");
94 exit(1);
95 }
fc945f1a 96 strcpy(mode, "octet");
61ad9979 97 signal(SIGINT, intr);
c3353d83
SL
98 if (argc > 1) {
99 if (setjmp(toplevel) != 0)
100 exit(0);
101 setpeer(argc, argv);
102 }
61ad9979 103 top = setjmp(toplevel) == 0;
c3353d83 104 for (;;)
61ad9979 105 command(top);
c3353d83
SL
106}
107
4d5e33bd
SL
108char *hostname;
109char hnamebuf[32];
c3353d83
SL
110
111setpeer(argc, argv)
112 int argc;
113 char *argv[];
114{
115 register int c;
4d5e33bd 116 struct hostent *host;
c3353d83
SL
117
118 if (argc < 2) {
119 strcpy(line, "Connect ");
120 printf("(to) ");
121 gets(&line[strlen(line)]);
122 makeargv();
123 argc = margc;
124 argv = margv;
125 }
126 if (argc > 3) {
127 printf("usage: %s host-name [port]\n", argv[0]);
128 return;
129 }
4d5e33bd
SL
130 host = gethostbyname(argv[1]);
131 if (host) {
de3b21e8 132 sin.sin_family = host->h_addrtype;
4d5e33bd
SL
133 bcopy(host->h_addr, &sin.sin_addr, host->h_length);
134 hostname = host->h_name;
135 } else {
de3b21e8 136 sin.sin_family = AF_INET;
4d5e33bd
SL
137 sin.sin_addr.s_addr = inet_addr(argv[1]);
138 if (sin.sin_addr.s_addr == -1) {
139 connected = 0;
140 printf("%s: unknown host\n", argv[1]);
141 return;
142 }
143 strcpy(hnamebuf, argv[1]);
144 hostname = hnamebuf;
c3353d83 145 }
4d5e33bd 146 sin.sin_port = sp->s_port;
c3353d83
SL
147 if (argc == 3) {
148 sin.sin_port = atoi(argv[2]);
149 if (sin.sin_port < 0) {
150 printf("%s: bad port number\n", argv[2]);
151 connected = 0;
152 return;
153 }
26d9d18d 154 sin.sin_port = htons((u_short)sin.sin_port);
4d5e33bd 155 }
c3353d83
SL
156 connected = 1;
157}
158
159struct modes {
160 char *m_name;
161 char *m_mode;
162} modes[] = {
fc945f1a
SL
163 { "binary", "octet" },
164 { "image", "octet" },
165 { "octet", "octet" },
c3353d83
SL
166 { 0, 0 }
167};
168
169setmode(argc, argv)
170 char *argv[];
171{
172 register struct modes *p;
173
174 if (argc > 2) {
175 char *sep;
176
177 printf("usage: %s [", argv[0]);
178 sep = " ";
179 for (p = modes; p->m_name; p++) {
180 printf("%s%s", sep, p->m_name);
181 if (*sep == ' ')
182 sep = " | ";
183 }
184 printf(" ]\n");
185 return;
186 }
187 if (argc < 2) {
188 printf("Using %s mode to transfer files.\n", mode);
189 return;
190 }
191 for (p = modes; p->m_name; p++)
192 if (strcmp(argv[1], p->m_name) == 0)
193 break;
194 if (p->m_name)
195 strcpy(mode, p->m_mode);
196 else
197 printf("%s: unknown mode\n", argv[1]);
198}
199
200/*
201 * Send file(s).
202 */
203put(argc, argv)
204 char *argv[];
205{
206 int fd;
207 register int n, addr;
208 register char *cp, *targ;
209
210 if (argc < 2) {
211 strcpy(line, "send ");
212 printf("(file) ");
213 gets(&line[strlen(line)]);
214 makeargv();
215 argc = margc;
216 argv = margv;
217 }
218 if (argc < 2) {
219 putusage(argv[0]);
220 return;
221 }
222 targ = argv[argc - 1];
223 if (index(argv[argc - 1], ':')) {
4d5e33bd
SL
224 char *cp;
225 struct hostent *hp;
c3353d83
SL
226
227 for (n = 1; n < argc - 1; n++)
228 if (index(argv[n], ':')) {
229 putusage(argv[0]);
230 return;
231 }
4d5e33bd
SL
232 cp = argv[argc - 1];
233 targ = index(cp, ':');
c3353d83 234 *targ++ = 0;
4d5e33bd
SL
235 hp = gethostbyname(cp);
236 if (hp == 0) {
237 printf("%s: Unknown host.\n", cp);
c3353d83
SL
238 return;
239 }
de3b21e8 240 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
4d5e33bd 241 sin.sin_family = hp->h_addrtype;
c3353d83 242 connected = 1;
4d5e33bd 243 hostname = hp->h_name;
c3353d83
SL
244 }
245 if (!connected) {
246 printf("No target machine specified.\n");
247 return;
248 }
c3353d83
SL
249 if (argc < 4) {
250 cp = argc == 2 ? tail(targ) : argv[1];
61ad9979 251 fd = open(cp, O_RDONLY);
c3353d83 252 if (fd < 0) {
61ad9979 253 fprintf(stderr, "tftp: "); perror(cp);
c3353d83
SL
254 return;
255 }
fc945f1a 256 (void) sendfile(fd, targ);
c3353d83
SL
257 return;
258 }
259 cp = index(targ, '\0');
260 *cp++ = '/';
261 for (n = 1; n < argc - 1; n++) {
262 strcpy(cp, tail(argv[n]));
61ad9979 263 fd = open(argv[n], O_RDONLY);
c3353d83 264 if (fd < 0) {
61ad9979 265 fprintf(stderr, "tftp: "); perror(argv[n]);
c3353d83
SL
266 continue;
267 }
fc945f1a 268 (void) sendfile(fd, targ);
c3353d83
SL
269 }
270}
271
272putusage(s)
273 char *s;
274{
275 printf("usage: %s file ... host:target, or\n", s);
276 printf(" %s file ... target (when already connected)\n", s);
277}
278
279/*
280 * Receive file(s).
281 */
282get(argc, argv)
283 char *argv[];
284{
285 int fd;
286 register int n, addr;
287 register char *cp;
288 char *src;
289
290 if (argc < 2) {
291 strcpy(line, "get ");
292 printf("(files) ");
293 gets(&line[strlen(line)]);
294 makeargv();
295 argc = margc;
296 argv = margv;
297 }
298 if (argc < 2) {
299 getusage(argv[0]);
300 return;
301 }
302 if (!connected)
303 for (n = 1; n < argc - 1; n++)
304 if (index(argv[n], ':') == 0) {
305 getusage(argv[0]);
306 return;
307 }
c3353d83
SL
308 for (n = 1; argc == 2 || n < argc - 1; n++) {
309 src = index(argv[n], ':');
310 if (src == NULL)
311 src = argv[n];
312 else {
4d5e33bd
SL
313 struct hostent *hp;
314
c3353d83 315 *src++ = 0;
4d5e33bd
SL
316 hp = gethostbyname(argv[n]);
317 if (hp == 0) {
c3353d83
SL
318 printf("%s: Unknown host.\n", argv[n]);
319 continue;
320 }
de3b21e8 321 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
4d5e33bd 322 sin.sin_family = hp->h_addrtype;
c3353d83 323 connected = 1;
4d5e33bd 324 hostname = hp->h_name;
c3353d83
SL
325 }
326 if (argc < 4) {
327 cp = argc == 3 ? argv[2] : tail(src);
328 fd = creat(cp, 0644);
329 if (fd < 0) {
61ad9979 330 fprintf(stderr, "tftp: "); perror(cp);
c3353d83
SL
331 return;
332 }
fc945f1a 333 (void) recvfile(fd, src);
c3353d83
SL
334 break;
335 }
336 cp = index(argv[argc - 1], '\0');
337 *cp++ = '/';
338 strcpy(cp, tail(src));
339 fd = creat(src, 0644);
340 if (fd < 0) {
61ad9979 341 fprintf(stderr, "tftp: "); perror(src);
c3353d83
SL
342 continue;
343 }
fc945f1a 344 (void) recvfile(fd, src);
c3353d83
SL
345 }
346}
347
348getusage(s)
349{
350 printf("usage: %s host:file host:file ... file, or\n", s);
351 printf(" %s file file ... file if connected\n", s);
352}
353
61ad9979
SL
354int rexmtval = TIMEOUT;
355
356setrexmt(argc, argv)
357 char *argv[];
358{
359 int t;
360
361 if (argc < 2) {
362 strcpy(line, "Rexmt-timeout ");
363 printf("(value) ");
364 gets(&line[strlen(line)]);
365 makeargv();
366 argc = margc;
367 argv = margv;
368 }
369 if (argc != 2) {
370 printf("usage: %s value\n", argv[0]);
371 return;
372 }
373 t = atoi(argv[1]);
374 if (t < 0)
375 printf("%s: bad value\n", t);
376 else
377 rexmtval = t;
378}
379
380int maxtimeout = 5 * TIMEOUT;
381
382settimeout(argc, argv)
383 char *argv[];
384{
385 int t;
386
387 if (argc < 2) {
388 strcpy(line, "Maximum-timeout ");
389 printf("(value) ");
390 gets(&line[strlen(line)]);
391 makeargv();
392 argc = margc;
393 argv = margv;
394 }
395 if (argc != 2) {
396 printf("usage: %s value\n", argv[0]);
397 return;
398 }
399 t = atoi(argv[1]);
400 if (t < 0)
401 printf("%s: bad value\n", t);
402 else
403 maxtimeout = t;
404}
405
c3353d83
SL
406status(argc, argv)
407 char *argv[];
408{
409 if (connected)
4d5e33bd 410 printf("Connected to %s.\n", hostname);
c3353d83
SL
411 else
412 printf("Not connected.\n");
fc945f1a 413 printf("Mode: %s Tracing: %s\n", mode, trace ? "on" : "off");
61ad9979
SL
414 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
415 rexmtval, maxtimeout);
c3353d83
SL
416}
417
418intr()
419{
61ad9979 420
b09ddebf 421 alarm(0);
c3353d83
SL
422 longjmp(toplevel, -1);
423}
424
425char *
426tail(filename)
427 char *filename;
428{
429 register char *s;
430
431 while (*filename) {
432 s = rindex(filename, '/');
433 if (s == NULL)
434 break;
435 if (s[1])
436 return (s + 1);
437 *s = '\0';
438 }
439 return (filename);
440}
441
442/*
443 * Command parser.
444 */
445command(top)
446 int top;
447{
448 register struct cmd *c;
449
450 if (!top)
451 putchar('\n');
c3353d83
SL
452 for (;;) {
453 printf("%s> ", prompt);
454 if (gets(line) == 0)
61ad9979 455 continue;
c3353d83 456 if (line[0] == 0)
61ad9979 457 continue;
c3353d83
SL
458 makeargv();
459 c = getcmd(margv[0]);
460 if (c == (struct cmd *)-1) {
461 printf("?Ambiguous command\n");
462 continue;
463 }
464 if (c == 0) {
465 printf("?Invalid command\n");
466 continue;
467 }
468 (*c->handler)(margc, margv);
c3353d83 469 }
c3353d83
SL
470}
471
472struct cmd *
473getcmd(name)
474 register char *name;
475{
476 register char *p, *q;
477 register struct cmd *c, *found;
478 register int nmatches, longest;
479
480 longest = 0;
481 nmatches = 0;
482 found = 0;
483 for (c = cmdtab; p = c->name; c++) {
484 for (q = name; *q == *p++; q++)
485 if (*q == 0) /* exact match? */
486 return (c);
487 if (!*q) { /* the name was a prefix */
488 if (q - name > longest) {
489 longest = q - name;
490 nmatches = 1;
491 found = c;
492 } else if (q - name == longest)
493 nmatches++;
494 }
495 }
496 if (nmatches > 1)
497 return ((struct cmd *)-1);
498 return (found);
499}
500
501/*
502 * Slice a string up into argc/argv.
503 */
504makeargv()
505{
506 register char *cp;
507 register char **argp = margv;
508
509 margc = 0;
510 for (cp = line; *cp;) {
511 while (isspace(*cp))
512 cp++;
513 if (*cp == '\0')
514 break;
515 *argp++ = cp;
516 margc += 1;
517 while (*cp != '\0' && !isspace(*cp))
518 cp++;
519 if (*cp == '\0')
520 break;
521 *cp++ = '\0';
522 }
523 *argp++ = 0;
524}
525
526/*VARARGS*/
527quit()
528{
529 exit(0);
530}
531
532/*
533 * Help command.
c3353d83
SL
534 */
535help(argc, argv)
536 int argc;
537 char *argv[];
538{
539 register struct cmd *c;
540
541 if (argc == 1) {
542 printf("Commands may be abbreviated. Commands are:\n\n");
543 for (c = cmdtab; c->name; c++)
544 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
545 return;
546 }
547 while (--argc > 0) {
548 register char *arg;
549 arg = *++argv;
550 c = getcmd(arg);
551 if (c == (struct cmd *)-1)
552 printf("?Ambiguous help command %s\n", arg);
553 else if (c == (struct cmd *)0)
554 printf("?Invalid help command %s\n", arg);
555 else
556 printf("%s\n", c->help);
557 }
558}
559
c3353d83
SL
560/*VARARGS*/
561settrace()
562{
563 trace = !trace;
564 printf("Packet tracing %s.\n", trace ? "on" : "off");
565}