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