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