date and time created 86/01/05 18:46:22 by sam
[unix-history] / usr / src / libexec / ftpd / ftpd.c
CommitLineData
f644bb55
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
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
c6d3d85f 13#ifndef lint
f644bb55
DF
14static char sccsid[] = "@(#)ftpd.c 5.1 (Berkeley) %G%";
15#endif not lint
c6d3d85f
SL
16
17/*
18 * FTP server.
19 */
aa159ba8 20#include <sys/param.h>
c6d3d85f
SL
21#include <sys/stat.h>
22#include <sys/ioctl.h>
23#include <sys/socket.h>
bb16805b 24#include <sys/file.h>
c8b8c458 25#include <sys/wait.h>
c6d3d85f
SL
26
27#include <netinet/in.h>
28
23eeaca6 29#include <arpa/ftp.h>
1668a723 30#include <arpa/inet.h>
23eeaca6 31
c6d3d85f
SL
32#include <stdio.h>
33#include <signal.h>
c6d3d85f
SL
34#include <pwd.h>
35#include <setjmp.h>
36#include <netdb.h>
28e19fb7 37#include <errno.h>
c6d3d85f 38
0597ed04
SL
39/*
40 * File containing login names
41 * NOT to be used on this machine.
42 * Commonly used to disallow uucp.
43 */
44#define FTPUSERS "/etc/ftpusers"
45
c6d3d85f
SL
46extern int errno;
47extern char *sys_errlist[];
48extern char *crypt();
49extern char version[];
50extern char *home; /* pointer to home directory for glob */
51extern FILE *popen(), *fopen();
52extern int pclose(), fclose();
53
54struct sockaddr_in ctrl_addr;
55struct sockaddr_in data_source;
56struct sockaddr_in data_dest;
57struct sockaddr_in his_addr;
58
59struct hostent *hp;
60
61int data;
62jmp_buf errcatch;
63int logged_in;
64struct passwd *pw;
65int debug;
5ac6fc46 66int timeout;
9072bd8a 67int logging;
c6d3d85f 68int guest;
5c50e19b 69int wtmp;
c6d3d85f
SL
70int type;
71int form;
72int stru; /* avoid C keyword */
73int mode;
8643b66e 74int usedefault = 1; /* for data transfers */
c6d3d85f 75char hostname[32];
bb16805b 76char remotehost[32];
c6d3d85f 77
5ac6fc46
SL
78/*
79 * Timeout intervals for retrying connections
80 * to hosts that don't accept PORT cmds. This
81 * is a kludge, but given the problems with TCP...
82 */
83#define SWAITMAX 90 /* wait at most 90 seconds */
84#define SWAITINT 5 /* interval between retries */
85
86int swaitmax = SWAITMAX;
87int swaitint = SWAITINT;
88
c6d3d85f 89int lostconn();
3cb22fa5 90int reapchild();
c6d3d85f 91FILE *getdatasock(), *dataconn();
c6d3d85f
SL
92
93main(argc, argv)
94 int argc;
95 char *argv[];
96{
a3dc4d0e 97 int options = 0, addrlen;
c6d3d85f
SL
98 char *cp;
99
a3dc4d0e
MK
100 addrlen = sizeof (his_addr);
101 if (getpeername(0, &his_addr, &addrlen) < 0) {
102 fprintf(stderr, "%s: ", argv[0]);
103 perror("getpeername");
c6d3d85f
SL
104 exit(1);
105 }
a3dc4d0e
MK
106 addrlen = sizeof (ctrl_addr);
107 if (getsockname(0, &ctrl_addr, &addrlen) < 0) {
108 fprintf(stderr, "%s: ", argv[0]);
109 perror("getsockname");
110 exit(1);
111 }
112 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
c6d3d85f
SL
113 debug = 0;
114 argc--, argv++;
115 while (argc > 0 && *argv[0] == '-') {
116 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
117
5ac6fc46
SL
118 case 'v':
119 debug = 1;
120 break;
121
c6d3d85f
SL
122 case 'd':
123 debug = 1;
124 options |= SO_DEBUG;
125 break;
126
9072bd8a
SL
127 case 'l':
128 logging = 1;
129 break;
130
5ac6fc46
SL
131 case 't':
132 timeout = atoi(++cp);
133 goto nextopt;
134 break;
135
c6d3d85f 136 default:
a3dc4d0e
MK
137 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
138 *cp);
c6d3d85f
SL
139 break;
140 }
5ac6fc46 141nextopt:
c6d3d85f
SL
142 argc--, argv++;
143 }
a3dc4d0e
MK
144 signal(SIGPIPE, lostconn);
145 signal(SIGCHLD, SIG_IGN);
2584c5be 146 dolog(&his_addr);
a3dc4d0e
MK
147 /* do telnet option negotiation here */
148 /*
149 * Set up default state
150 */
151 logged_in = 0;
152 data = -1;
153 type = TYPE_A;
154 form = FORM_N;
155 stru = STRU_F;
156 mode = MODE_S;
157 gethostname(hostname, sizeof (hostname));
158 reply(220, "%s FTP server (%s) ready.",
159 hostname, version);
c6d3d85f 160 for (;;) {
a3dc4d0e
MK
161 setjmp(errcatch);
162 yyparse();
c6d3d85f
SL
163 }
164}
165
3cb22fa5
SL
166reapchild()
167{
168 union wait status;
169
170 while (wait3(&status, WNOHANG, 0) > 0)
171 ;
172}
173
c6d3d85f
SL
174lostconn()
175{
176
407329f7
SL
177 if (debug)
178 fprintf(stderr, "Lost connection.\n");
179 dologout(-1);
c6d3d85f
SL
180}
181
182pass(passwd)
183 char *passwd;
184{
aa159ba8
SL
185 char *xpasswd, *savestr();
186 static struct passwd save;
c6d3d85f
SL
187
188 if (logged_in || pw == NULL) {
189 reply(503, "Login with USER first.");
190 return;
191 }
192 if (!guest) { /* "ftp" is only account allowed no password */
193 xpasswd = crypt(passwd, pw->pw_passwd);
2584c5be
JL
194 /* The strcmp does not catch null passwords! */
195 if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
c6d3d85f
SL
196 reply(530, "Login incorrect.");
197 pw = NULL;
198 return;
199 }
200 }
aa159ba8 201 setegid(pw->pw_gid);
c6d3d85f
SL
202 initgroups(pw->pw_name, pw->pw_gid);
203 if (chdir(pw->pw_dir)) {
c5822543 204 reply(550, "User %s: can't change directory to %s.",
c6d3d85f 205 pw->pw_name, pw->pw_dir);
aa159ba8 206 goto bad;
c6d3d85f 207 }
5c50e19b 208
2584c5be
JL
209 /* grab wtmp before chroot */
210 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
aa159ba8 211 if (guest && chroot(pw->pw_dir) < 0) {
c6d3d85f 212 reply(550, "Can't set guest privileges.");
2584c5be
JL
213 if (wtmp >= 0) {
214 (void) close(wtmp);
215 wtmp = -1;
216 }
aa159ba8 217 goto bad;
c6d3d85f
SL
218 }
219 if (!guest)
220 reply(230, "User %s logged in.", pw->pw_name);
221 else
222 reply(230, "Guest login ok, access restrictions apply.");
223 logged_in = 1;
bb16805b 224 dologin(pw);
aa159ba8
SL
225 seteuid(pw->pw_uid);
226 /*
227 * Save everything so globbing doesn't
228 * clobber the fields.
229 */
230 save = *pw;
231 save.pw_name = savestr(pw->pw_name);
232 save.pw_passwd = savestr(pw->pw_passwd);
233 save.pw_comment = savestr(pw->pw_comment);
234 save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
235 save.pw_dir = savestr(pw->pw_dir);
236 save.pw_shell = savestr(pw->pw_shell);
237 pw = &save;
238 home = pw->pw_dir; /* home dir for globbing */
239 return;
240bad:
241 seteuid(0);
242 pw = NULL;
243}
244
245char *
246savestr(s)
247 char *s;
248{
249 char *malloc();
250 char *new = malloc(strlen(s) + 1);
251
252 if (new != NULL)
253 strcpy(new, s);
df2da992 254 return (new);
c6d3d85f
SL
255}
256
257retrieve(cmd, name)
258 char *cmd, *name;
259{
260 FILE *fin, *dout;
261 struct stat st;
262 int (*closefunc)();
263
264 if (cmd == 0) {
6fcbb8a1
SL
265#ifdef notdef
266 /* no remote command execution -- it's a security hole */
5ac6fc46 267 if (*name == '|')
c6d3d85f
SL
268 fin = popen(name + 1, "r"), closefunc = pclose;
269 else
6fcbb8a1 270#endif
c6d3d85f
SL
271 fin = fopen(name, "r"), closefunc = fclose;
272 } else {
273 char line[BUFSIZ];
274
5c81b68e 275 sprintf(line, cmd, name), name = line;
c6d3d85f
SL
276 fin = popen(line, "r"), closefunc = pclose;
277 }
278 if (fin == NULL) {
edcef58b
SL
279 if (errno != 0)
280 reply(550, "%s: %s.", name, sys_errlist[errno]);
c6d3d85f
SL
281 return;
282 }
283 st.st_size = 0;
284 if (cmd == 0 &&
285 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
286 reply(550, "%s: not a plain file.", name);
287 goto done;
288 }
289 dout = dataconn(name, st.st_size, "w");
290 if (dout == NULL)
291 goto done;
aa159ba8 292 if (send_data(fin, dout) || ferror(dout))
c6d3d85f
SL
293 reply(550, "%s: %s.", name, sys_errlist[errno]);
294 else
295 reply(226, "Transfer complete.");
aa159ba8 296 fclose(dout), data = -1;
c6d3d85f
SL
297done:
298 (*closefunc)(fin);
299}
300
301store(name, mode)
302 char *name, *mode;
303{
304 FILE *fout, *din;
aa159ba8 305 int (*closefunc)(), dochown = 0;
c6d3d85f 306
6fcbb8a1
SL
307#ifdef notdef
308 /* no remote command execution -- it's a security hole */
5ac6fc46 309 if (name[0] == '|')
c6d3d85f 310 fout = popen(&name[1], "w"), closefunc = pclose;
6fcbb8a1
SL
311 else
312#endif
313 {
aa159ba8
SL
314 struct stat st;
315
316 if (stat(name, &st) < 0)
317 dochown++;
c6d3d85f 318 fout = fopen(name, mode), closefunc = fclose;
aa159ba8 319 }
c6d3d85f
SL
320 if (fout == NULL) {
321 reply(550, "%s: %s.", name, sys_errlist[errno]);
322 return;
323 }
5ac6fc46 324 din = dataconn(name, (off_t)-1, "r");
c6d3d85f
SL
325 if (din == NULL)
326 goto done;
aa159ba8 327 if (receive_data(din, fout) || ferror(fout))
c6d3d85f
SL
328 reply(550, "%s: %s.", name, sys_errlist[errno]);
329 else
330 reply(226, "Transfer complete.");
331 fclose(din), data = -1;
332done:
aa159ba8
SL
333 if (dochown)
334 (void) chown(name, pw->pw_uid, -1);
c6d3d85f
SL
335 (*closefunc)(fout);
336}
337
338FILE *
339getdatasock(mode)
340 char *mode;
341{
3083a381 342 int s, on = 1;
c6d3d85f
SL
343
344 if (data >= 0)
345 return (fdopen(data, mode));
bb16805b 346 s = socket(AF_INET, SOCK_STREAM, 0);
58846728 347 if (s < 0)
c6d3d85f 348 return (NULL);
c6d3d85f 349 seteuid(0);
3083a381 350 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
58846728 351 goto bad;
edcef58b
SL
352 /* anchor socket to avoid multi-homing problems */
353 data_source.sin_family = AF_INET;
354 data_source.sin_addr = ctrl_addr.sin_addr;
58846728
SL
355 if (bind(s, &data_source, sizeof (data_source), 0) < 0)
356 goto bad;
0a11c3ae 357 seteuid(pw->pw_uid);
c6d3d85f 358 return (fdopen(s, mode));
58846728
SL
359bad:
360 seteuid(pw->pw_uid);
361 close(s);
362 return (NULL);
c6d3d85f
SL
363}
364
365FILE *
366dataconn(name, size, mode)
367 char *name;
5ac6fc46 368 off_t size;
c6d3d85f
SL
369 char *mode;
370{
371 char sizebuf[32];
372 FILE *file;
5ac6fc46 373 int retry = 0;
c6d3d85f
SL
374
375 if (size >= 0)
5ac6fc46 376 sprintf (sizebuf, " (%ld bytes)", size);
c6d3d85f
SL
377 else
378 (void) strcpy(sizebuf, "");
379 if (data >= 0) {
380 reply(125, "Using existing data connection for %s%s.",
381 name, sizebuf);
8643b66e 382 usedefault = 1;
c6d3d85f
SL
383 return (fdopen(data, mode));
384 }
afa6f79b 385 if (usedefault)
5c81b68e 386 data_dest = his_addr;
5c81b68e 387 usedefault = 1;
c6d3d85f
SL
388 file = getdatasock(mode);
389 if (file == NULL) {
390 reply(425, "Can't create data socket (%s,%d): %s.",
bb16805b 391 inet_ntoa(data_source.sin_addr),
c6d3d85f
SL
392 ntohs(data_source.sin_port),
393 sys_errlist[errno]);
394 return (NULL);
395 }
58846728 396 reply(150, "Opening data connection for %s (%s,%d)%s.",
bb16805b 397 name, inet_ntoa(data_dest.sin_addr.s_addr),
58846728 398 ntohs(data_dest.sin_port), sizebuf);
c6d3d85f 399 data = fileno(file);
5ac6fc46
SL
400 while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
401 if (errno == EADDRINUSE && retry < swaitmax) {
402 sleep(swaitint);
403 retry += swaitint;
404 continue;
405 }
c6d3d85f
SL
406 reply(425, "Can't build data connection: %s.",
407 sys_errlist[errno]);
408 (void) fclose(file);
409 data = -1;
410 return (NULL);
411 }
412 return (file);
413}
414
415/*
416 * Tranfer the contents of "instr" to
417 * "outstr" peer using the appropriate
418 * encapulation of the date subject
419 * to Mode, Structure, and Type.
420 *
421 * NB: Form isn't handled.
422 */
423send_data(instr, outstr)
424 FILE *instr, *outstr;
425{
426 register int c;
427 int netfd, filefd, cnt;
428 char buf[BUFSIZ];
429
430 switch (type) {
431
432 case TYPE_A:
433 while ((c = getc(instr)) != EOF) {
d33c618b
SL
434 if (c == '\n') {
435 if (ferror (outstr))
436 return (1);
c6d3d85f 437 putc('\r', outstr);
d33c618b
SL
438 }
439 putc(c, outstr);
440 if (c == '\r')
441 putc ('\0', outstr);
c6d3d85f 442 }
d33c618b
SL
443 if (ferror (instr) || ferror (outstr))
444 return (1);
c6d3d85f
SL
445 return (0);
446
447 case TYPE_I:
448 case TYPE_L:
449 netfd = fileno(outstr);
450 filefd = fileno(instr);
451
aa159ba8 452 while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
c6d3d85f
SL
453 if (write(netfd, buf, cnt) < 0)
454 return (1);
455 return (cnt < 0);
456 }
457 reply(504,"Unimplemented TYPE %d in send_data", type);
458 return (1);
459}
460
461/*
462 * Transfer data from peer to
463 * "outstr" using the appropriate
464 * encapulation of the data subject
465 * to Mode, Structure, and Type.
466 *
467 * N.B.: Form isn't handled.
468 */
469receive_data(instr, outstr)
470 FILE *instr, *outstr;
471{
472 register int c;
d33c618b 473 int cnt;
c6d3d85f
SL
474 char buf[BUFSIZ];
475
476
477 switch (type) {
478
479 case TYPE_I:
480 case TYPE_L:
2e4e0338
SL
481 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
482 if (write(fileno(outstr), buf, cnt) < 0)
c6d3d85f
SL
483 return (1);
484 return (cnt < 0);
485
486 case TYPE_E:
487 reply(504, "TYPE E not implemented.");
488 return (1);
489
490 case TYPE_A:
c6d3d85f 491 while ((c = getc(instr)) != EOF) {
c6d3d85f 492 if (c == '\r') {
d33c618b
SL
493 if (ferror (outstr))
494 return (1);
495 if ((c = getc(instr)) != '\n')
496 putc ('\r', outstr);
497 if (c == '\0')
498 continue;
c6d3d85f 499 }
d33c618b 500 putc (c, outstr);
c6d3d85f 501 }
d33c618b
SL
502 if (ferror (instr) || ferror (outstr))
503 return (1);
c6d3d85f
SL
504 return (0);
505 }
506 fatal("Unknown type in receive_data.");
507 /*NOTREACHED*/
508}
509
510fatal(s)
511 char *s;
512{
513 reply(451, "Error in server: %s\n", s);
514 reply(221, "Closing connection due to server error.");
bb16805b 515 dologout(0);
c6d3d85f
SL
516}
517
518reply(n, s, args)
519 int n;
520 char *s;
521{
522
523 printf("%d ", n);
524 _doprnt(s, &args, stdout);
525 printf("\r\n");
526 fflush(stdout);
527 if (debug) {
528 fprintf(stderr, "<--- %d ", n);
529 _doprnt(s, &args, stderr);
530 fprintf(stderr, "\n");
531 fflush(stderr);
532 }
533}
534
535lreply(n, s, args)
536 int n;
537 char *s;
538{
539 printf("%d-", n);
540 _doprnt(s, &args, stdout);
541 printf("\r\n");
542 fflush(stdout);
543 if (debug) {
544 fprintf(stderr, "<--- %d-", n);
545 _doprnt(s, &args, stderr);
546 fprintf(stderr, "\n");
547 }
548}
549
550replystr(s)
551 char *s;
552{
553 printf("%s\r\n", s);
554 fflush(stdout);
555 if (debug)
556 fprintf(stderr, "<--- %s\n", s);
557}
558
559ack(s)
560 char *s;
561{
562 reply(200, "%s command okay.", s);
563}
564
565nack(s)
566 char *s;
567{
568 reply(502, "%s command not implemented.", s);
569}
570
571yyerror()
572{
573 reply(500, "Command not understood.");
574}
575
576delete(name)
577 char *name;
578{
579 struct stat st;
580
581 if (stat(name, &st) < 0) {
582 reply(550, "%s: %s.", name, sys_errlist[errno]);
583 return;
584 }
585 if ((st.st_mode&S_IFMT) == S_IFDIR) {
586 if (rmdir(name) < 0) {
587 reply(550, "%s: %s.", name, sys_errlist[errno]);
588 return;
589 }
590 goto done;
591 }
592 if (unlink(name) < 0) {
593 reply(550, "%s: %s.", name, sys_errlist[errno]);
594 return;
595 }
596done:
597 ack("DELE");
598}
599
600cwd(path)
601 char *path;
602{
603
604 if (chdir(path) < 0) {
605 reply(550, "%s: %s.", path, sys_errlist[errno]);
606 return;
607 }
608 ack("CWD");
609}
610
aa159ba8 611makedir(name)
c6d3d85f
SL
612 char *name;
613{
aa159ba8
SL
614 struct stat st;
615 int dochown = stat(name, &st) < 0;
c6d3d85f
SL
616
617 if (mkdir(name, 0777) < 0) {
618 reply(550, "%s: %s.", name, sys_errlist[errno]);
619 return;
620 }
aa159ba8
SL
621 if (dochown)
622 (void) chown(name, pw->pw_uid, -1);
c6d3d85f
SL
623 ack("MKDIR");
624}
625
aa159ba8 626removedir(name)
c6d3d85f
SL
627 char *name;
628{
629
630 if (rmdir(name) < 0) {
631 reply(550, "%s: %s.", name, sys_errlist[errno]);
632 return;
633 }
634 ack("RMDIR");
635}
636
aa159ba8 637pwd()
c6d3d85f 638{
aa159ba8 639 char path[MAXPATHLEN + 1];
c6d3d85f
SL
640
641 if (getwd(path) == NULL) {
642 reply(451, "%s.", path);
643 return;
644 }
645 reply(251, "\"%s\" is current directory.", path);
646}
647
648char *
649renamefrom(name)
650 char *name;
651{
652 struct stat st;
653
654 if (stat(name, &st) < 0) {
655 reply(550, "%s: %s.", name, sys_errlist[errno]);
656 return ((char *)0);
657 }
aa159ba8 658 reply(350, "File exists, ready for destination name");
c6d3d85f
SL
659 return (name);
660}
661
662renamecmd(from, to)
663 char *from, *to;
664{
665
666 if (rename(from, to) < 0) {
667 reply(550, "rename: %s.", sys_errlist[errno]);
668 return;
669 }
670 ack("RNTO");
671}
672
c6d3d85f
SL
673dolog(sin)
674 struct sockaddr_in *sin;
675{
676 struct hostent *hp = gethostbyaddr(&sin->sin_addr,
677 sizeof (struct in_addr), AF_INET);
c6d3d85f
SL
678 time_t t;
679
bb16805b
SL
680 if (hp) {
681 strncpy(remotehost, hp->h_name, sizeof (remotehost));
682 endhostent();
683 } else
684 strncpy(remotehost, inet_ntoa(sin->sin_addr),
685 sizeof (remotehost));
686 if (!logging)
687 return;
c6d3d85f 688 t = time(0);
9072bd8a 689 fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
c6d3d85f
SL
690 fflush(stderr);
691}
0597ed04 692
bb16805b
SL
693#include <utmp.h>
694
695#define SCPYN(a, b) strncpy(a, b, sizeof (a))
696struct utmp utmp;
697
698/*
699 * Record login in wtmp file.
700 */
701dologin(pw)
702 struct passwd *pw;
703{
bb16805b
SL
704 char line[32];
705
bb16805b
SL
706 if (wtmp >= 0) {
707 /* hack, but must be unique and no tty line */
708 sprintf(line, "ftp%d", getpid());
709 SCPYN(utmp.ut_line, line);
710 SCPYN(utmp.ut_name, pw->pw_name);
711 SCPYN(utmp.ut_host, remotehost);
712 utmp.ut_time = time(0);
713 (void) write(wtmp, (char *)&utmp, sizeof (utmp));
2584c5be
JL
714 if (!guest) { /* anon must hang on */
715 (void) close(wtmp);
716 wtmp = -1;
717 }
bb16805b
SL
718 }
719}
720
721/*
722 * Record logout in wtmp file
723 * and exit with supplied status.
724 */
725dologout(status)
726 int status;
727{
a3dc4d0e 728
632ff766
SL
729 if (logged_in) {
730 (void) seteuid(0);
731 if (wtmp < 0)
732 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
733 if (wtmp >= 0) {
734 SCPYN(utmp.ut_name, "");
735 SCPYN(utmp.ut_host, "");
736 utmp.ut_time = time(0);
737 (void) write(wtmp, (char *)&utmp, sizeof (utmp));
738 (void) close(wtmp);
739 }
bb16805b 740 }
930b478b
SL
741 /* beware of flushing buffers after a SIGPIPE */
742 _exit(status);
bb16805b
SL
743}
744
0597ed04
SL
745/*
746 * Special version of popen which avoids
747 * call to shell. This insures noone may
748 * create a pipe to a hidden program as a side
749 * effect of a list or dir command.
750 */
751#define tst(a,b) (*mode == 'r'? (b) : (a))
752#define RDR 0
753#define WTR 1
754static int popen_pid[5];
755
756static char *
757nextarg(cpp)
758 char *cpp;
759{
760 register char *cp = cpp;
761
762 if (cp == 0)
763 return (cp);
764 while (*cp && *cp != ' ' && *cp != '\t')
765 cp++;
766 if (*cp == ' ' || *cp == '\t') {
767 *cp++ = '\0';
768 while (*cp == ' ' || *cp == '\t')
769 cp++;
770 }
771 if (cp == cpp)
772 return ((char *)0);
773 return (cp);
774}
775
776FILE *
777popen(cmd, mode)
778 char *cmd, *mode;
779{
1668a723 780 int p[2], ac, gac;
0597ed04 781 register myside, hisside, pid;
1668a723 782 char *av[20], *gav[512];
0597ed04
SL
783 register char *cp;
784
785 if (pipe(p) < 0)
786 return (NULL);
787 cp = cmd, ac = 0;
1668a723 788 /* break up string into pieces */
0597ed04
SL
789 do {
790 av[ac++] = cp;
791 cp = nextarg(cp);
1668a723 792 } while (cp && *cp && ac < 20);
0597ed04 793 av[ac] = (char *)0;
1668a723
BJ
794 gav[0] = av[0];
795 /* glob each piece */
796 for (gac = ac = 1; av[ac] != NULL; ac++) {
797 char **pop;
6aff9cb0 798 extern char **glob(), **copyblk();
1668a723
BJ
799
800 pop = glob(av[ac]);
6aff9cb0
SL
801 if (pop == (char **)NULL) { /* globbing failed */
802 char *vv[2];
803
804 vv[0] = av[ac];
805 vv[1] = 0;
806 pop = copyblk(vv);
1668a723 807 }
6aff9cb0
SL
808 av[ac] = (char *)pop; /* save to free later */
809 while (*pop && gac < 512)
810 gav[gac++] = *pop++;
9072bd8a 811 }
1668a723 812 gav[gac] = (char *)0;
0597ed04
SL
813 myside = tst(p[WTR], p[RDR]);
814 hisside = tst(p[RDR], p[WTR]);
815 if ((pid = fork()) == 0) {
816 /* myside and hisside reverse roles in child */
817 close(myside);
818 dup2(hisside, tst(0, 1));
819 close(hisside);
1668a723 820 execv(gav[0], gav);
0597ed04
SL
821 _exit(1);
822 }
1668a723
BJ
823 for (ac = 1; av[ac] != NULL; ac++)
824 blkfree((char **)av[ac]);
0597ed04
SL
825 if (pid == -1)
826 return (NULL);
827 popen_pid[myside] = pid;
828 close(hisside);
829 return (fdopen(myside, mode));
830}
831
832pclose(ptr)
833 FILE *ptr;
834{
835 register f, r, (*hstat)(), (*istat)(), (*qstat)();
836 int status;
837
838 f = fileno(ptr);
839 fclose(ptr);
840 istat = signal(SIGINT, SIG_IGN);
841 qstat = signal(SIGQUIT, SIG_IGN);
842 hstat = signal(SIGHUP, SIG_IGN);
843 while ((r = wait(&status)) != popen_pid[f] && r != -1)
844 ;
845 if (r == -1)
846 status = -1;
847 signal(SIGINT, istat);
848 signal(SIGQUIT, qstat);
849 signal(SIGHUP, hstat);
850 return (status);
851}
852
853/*
854 * Check user requesting login priviledges.
855 * Disallow anyone mentioned in the file FTPUSERS
856 * to allow people such as uucp to be avoided.
857 */
858checkuser(name)
859 register char *name;
860{
861 char line[BUFSIZ], *index();
862 FILE *fd;
863 int found = 0;
864
865 fd = fopen(FTPUSERS, "r");
866 if (fd == NULL)
867 return (1);
868 while (fgets(line, sizeof (line), fd) != NULL) {
869 register char *cp = index(line, '\n');
870
871 if (cp)
872 *cp = '\0';
873 if (strcmp(line, name) == 0) {
874 found++;
875 break;
876 }
877 }
878 fclose(fd);
879 return (!found);
880}