1 /*
2 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
11 The Regents of the University of California. All rights reserved.\n";
12 #endif /* not lint */
13
14 #ifndef lint
15 static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 04/28/95";
16 #endif /* not lint */
17
18 /*
19 * FTP server.
20 */
21 #include <sys/param.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25 #include <sys/wait.h>
26
27 #include <netinet/in.h>
28 #include <netinet/in_systm.h>
29 #include <netinet/ip.h>
30
31 #define FTP_NAMES
32 #include <arpa/ftp.h>
33 #include <arpa/inet.h>
34 #include <arpa/telnet.h>
35
36 #include <ctype.h>
37 #include <dirent.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <glob.h>
42 #include <limits.h>
43 #include <netdb.h>
44 #include <pwd.h>
45 #include <setjmp.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <time.h>
52 #include <unistd.h>
53
54 #include "pathnames.h"
55 #include "extern.h"
56
57 #if __STDC__
58 #include <stdarg.h>
59 #else
60 #include <varargs.h>
61 #endif
62
63 static char version[] = "Version 6.00";
64
65 extern off_t restart_point;
66 extern char cbuf[];
67
68 struct sockaddr_in ctrl_addr;
69 struct sockaddr_in data_source;
70 struct sockaddr_in data_dest;
71 struct sockaddr_in his_addr;
72 struct sockaddr_in pasv_addr;
73
74 int data;
75 jmp_buf errcatch, urgcatch;
76 int logged_in;
77 struct passwd *pw;
78 int debug;
79 int timeout = 900; /* timeout after 15 minutes of inactivity */
80 int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
81 int logging;
82 int guest;
83 int type;
84 int form;
85 int stru; /* avoid C keyword */
86 int mode;
87 int usedefault = 1; /* for data transfers */
88 int pdata = -1; /* for passive mode */
89 sig_atomic_t transflag;
90 off_t file_size;
91 off_t byte_count;
92 #if !defined(CMASK) || CMASK == 0
93 #undef CMASK
94 #define CMASK 027
95 #endif
96 int defumask = CMASK; /* default umask value */
97 char tmpline[7];
98 char hostname[MAXHOSTNAMELEN];
99 char remotehost[MAXHOSTNAMELEN];
100
101 /*
102 * Timeout intervals for retrying connections
103 * to hosts that don't accept PORT cmds. This
104 * is a kludge, but given the problems with TCP...
105 */
106 #define SWAITMAX 90 /* wait at most 90 seconds */
107 #define SWAITINT 5 /* interval between retries */
108
109 int swaitmax = SWAITMAX;
110 int swaitint = SWAITINT;
111
112 #ifdef SETPROCTITLE
113 char **Argv = NULL; /* pointer to argument vector */
114 char *LastArgv = NULL; /* end of argv */
115 char proctitle[LINE_MAX]; /* initial part of title */
116 #endif /* SETPROCTITLE */
117
118 #define LOGCMD(cmd, file) \
119 if (logging > 1) \
120 syslog(LOG_INFO,"%s %s%s", cmd, \
121 *(file) == '/' ? "" : curdir(), file);
122 #define LOGCMD2(cmd, file1, file2) \
123 if (logging > 1) \
124 syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
125 *(file1) == '/' ? "" : curdir(), file1, \
126 *(file2) == '/' ? "" : curdir(), file2);
127 #define LOGBYTES(cmd, file, cnt) \
128 if (logging > 1) { \
129 if (cnt == (off_t)-1) \
130 syslog(LOG_INFO,"%s %s%s", cmd, \
131 *(file) == '/' ? "" : curdir(), file); \
132 else \
133 syslog(LOG_INFO, "%s %s%s = %qd bytes", \
134 cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
135 }
136
137 static void ack __P((char *));
138 static void myoob __P((int));
139 static int checkuser __P((char *));
140 static FILE *dataconn __P((char *, off_t, char *));
141 static void dolog __P((struct sockaddr_in *));
142 static char *curdir __P((void));
143 static void end_login __P((void));
144 static FILE *getdatasock __P((char *));
145 static char *gunique __P((char *));
146 static void lostconn __P((int));
147 static int receive_data __P((FILE *, FILE *));
148 static void send_data __P((FILE *, FILE *, off_t));
149 static struct passwd *
150 sgetpwnam __P((char *));
151 static char *sgetsave __P((char *));
152
153 static char *
curdir()154 curdir()
155 {
156 static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */
157
158 if (getcwd(path, sizeof(path)-2) == NULL)
159 return ("");
160 if (path[1] != '\0') /* special case for root dir. */
161 strcat(path, "/");
162 /* For guest account, skip / since it's chrooted */
163 return (guest ? path+1 : path);
164 }
165
166 int
main(argc,argv,envp)167 main(argc, argv, envp)
168 int argc;
169 char *argv[];
170 char **envp;
171 {
172 int addrlen, ch, on = 1, tos;
173 char *cp, line[LINE_MAX];
174 FILE *fd;
175
176 /*
177 * LOG_NDELAY sets up the logging connection immediately,
178 * necessary for anonymous ftp's that chroot and can't do it later.
179 */
180 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
181 addrlen = sizeof(his_addr);
182 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
183 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
184 exit(1);
185 }
186 addrlen = sizeof(ctrl_addr);
187 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
188 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
189 exit(1);
190 }
191 #ifdef IP_TOS
192 tos = IPTOS_LOWDELAY;
193 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
194 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
195 #endif
196 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
197 debug = 0;
198 #ifdef SETPROCTITLE
199 /*
200 * Save start and extent of argv for setproctitle.
201 */
202 Argv = argv;
203 while (*envp)
204 envp++;
205 LastArgv = envp[-1] + strlen(envp[-1]);
206 #endif /* SETPROCTITLE */
207
208 while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) {
209 switch (ch) {
210 case 'd':
211 debug = 1;
212 break;
213
214 case 'l':
215 logging++; /* > 1 == extra logging */
216 break;
217
218 case 't':
219 timeout = atoi(optarg);
220 if (maxtimeout < timeout)
221 maxtimeout = timeout;
222 break;
223
224 case 'T':
225 maxtimeout = atoi(optarg);
226 if (timeout > maxtimeout)
227 timeout = maxtimeout;
228 break;
229
230 case 'u':
231 {
232 long val = 0;
233
234 val = strtol(optarg, &optarg, 8);
235 if (*optarg != '\0' || val < 0)
236 warnx("bad value for -u");
237 else
238 defumask = val;
239 break;
240 }
241
242 case 'v':
243 debug = 1;
244 break;
245
246 default:
247 warnx("unknown flag -%c ignored", optopt);
248 break;
249 }
250 }
251 (void) freopen(_PATH_DEVNULL, "w", stderr);
252 (void) signal(SIGPIPE, lostconn);
253 (void) signal(SIGCHLD, SIG_IGN);
254 if ((int)signal(SIGURG, myoob) < 0)
255 syslog(LOG_ERR, "signal: %m");
256
257 /* Try to handle urgent data inline */
258 #ifdef SO_OOBINLINE
259 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
260 syslog(LOG_ERR, "setsockopt: %m");
261 #endif
262
263 #ifdef F_SETOWN
264 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
265 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
266 #endif
267 dolog(&his_addr);
268 /*
269 * Set up default state
270 */
271 data = -1;
272 type = TYPE_A;
273 form = FORM_N;
274 stru = STRU_F;
275 mode = MODE_S;
276 tmpline[0] = '\0';
277
278 /* If logins are disabled, print out the message. */
279 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
280 while (fgets(line, sizeof(line), fd) != NULL) {
281 if ((cp = strchr(line, '\n')) != NULL)
282 *cp = '\0';
283 lreply(530, "%s", line);
284 }
285 (void) fflush(stdout);
286 (void) fclose(fd);
287 reply(530, "System not available.");
288 exit(0);
289 }
290 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
291 while (fgets(line, sizeof(line), fd) != NULL) {
292 if ((cp = strchr(line, '\n')) != NULL)
293 *cp = '\0';
294 lreply(220, "%s", line);
295 }
296 (void) fflush(stdout);
297 (void) fclose(fd);
298 /* reply(220,) must follow */
299 }
300 (void) gethostname(hostname, sizeof(hostname));
301 reply(220, "%s FTP server (%s) ready.", hostname, version);
302 (void) setjmp(errcatch);
303 for (;;)
304 (void) yyparse();
305 /* NOTREACHED */
306 }
307
308 static void
lostconn(signo)309 lostconn(signo)
310 int signo;
311 {
312
313 if (debug)
314 syslog(LOG_DEBUG, "lost connection");
315 dologout(-1);
316 }
317
318 static char ttyline[20];
319
320 /*
321 * Helper function for sgetpwnam().
322 */
323 static char *
sgetsave(s)324 sgetsave(s)
325 char *s;
326 {
327 char *new = malloc((unsigned) strlen(s) + 1);
328
329 if (new == NULL) {
330 perror_reply(421, "Local resource failure: malloc");
331 dologout(1);
332 /* NOTREACHED */
333 }
334 (void) strcpy(new, s);
335 return (new);
336 }
337
338 /*
339 * Save the result of a getpwnam. Used for USER command, since
340 * the data returned must not be clobbered by any other command
341 * (e.g., globbing).
342 */
343 static struct passwd *
sgetpwnam(name)344 sgetpwnam(name)
345 char *name;
346 {
347 static struct passwd save;
348 struct passwd *p;
349
350 if ((p = getpwnam(name)) == NULL)
351 return (p);
352 if (save.pw_name) {
353 free(save.pw_name);
354 free(save.pw_passwd);
355 free(save.pw_gecos);
356 free(save.pw_dir);
357 free(save.pw_shell);
358 }
359 save = *p;
360 save.pw_name = sgetsave(p->pw_name);
361 save.pw_passwd = sgetsave(p->pw_passwd);
362 save.pw_gecos = sgetsave(p->pw_gecos);
363 save.pw_dir = sgetsave(p->pw_dir);
364 save.pw_shell = sgetsave(p->pw_shell);
365 return (&save);
366 }
367
368 static int login_attempts; /* number of failed login attempts */
369 static int askpasswd; /* had user command, ask for passwd */
370 static char curname[10]; /* current USER name */
371
372 /*
373 * USER command.
374 * Sets global passwd pointer pw if named account exists and is acceptable;
375 * sets askpasswd if a PASS command is expected. If logged in previously,
376 * need to reset state. If name is "ftp" or "anonymous", the name is not in
377 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
378 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
379 * requesting login privileges. Disallow anyone who does not have a standard
380 * shell as returned by getusershell(). Disallow anyone mentioned in the file
381 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
382 */
383 void
user(name)384 user(name)
385 char *name;
386 {
387 char *cp, *shell;
388
389 if (logged_in) {
390 if (guest) {
391 reply(530, "Can't change user from guest login.");
392 return;
393 }
394 end_login();
395 }
396
397 guest = 0;
398 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
399 if (checkuser("ftp") || checkuser("anonymous"))
400 reply(530, "User %s access denied.", name);
401 else if ((pw = sgetpwnam("ftp")) != NULL) {
402 guest = 1;
403 askpasswd = 1;
404 reply(331,
405 "Guest login ok, type your name as password.");
406 } else
407 reply(530, "User %s unknown.", name);
408 if (!askpasswd && logging)
409 syslog(LOG_NOTICE,
410 "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
411 return;
412 }
413 if (pw = sgetpwnam(name)) {
414 if ((shell = pw->pw_shell) == NULL || *shell == 0)
415 shell = _PATH_BSHELL;
416 while ((cp = getusershell()) != NULL)
417 if (strcmp(cp, shell) == 0)
418 break;
419 endusershell();
420
421 if (cp == NULL || checkuser(name)) {
422 reply(530, "User %s access denied.", name);
423 if (logging)
424 syslog(LOG_NOTICE,
425 "FTP LOGIN REFUSED FROM %s, %s",
426 remotehost, name);
427 pw = (struct passwd *) NULL;
428 return;
429 }
430 }
431 if (logging)
432 strncpy(curname, name, sizeof(curname)-1);
433 reply(331, "Password required for %s.", name);
434 askpasswd = 1;
435 /*
436 * Delay before reading passwd after first failed
437 * attempt to slow down passwd-guessing programs.
438 */
439 if (login_attempts)
440 sleep((unsigned) login_attempts);
441 }
442
443 /*
444 * Check if a user is in the file _PATH_FTPUSERS
445 */
446 static int
checkuser(name)447 checkuser(name)
448 char *name;
449 {
450 FILE *fd;
451 int found = 0;
452 char *p, line[BUFSIZ];
453
454 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
455 while (fgets(line, sizeof(line), fd) != NULL)
456 if ((p = strchr(line, '\n')) != NULL) {
457 *p = '\0';
458 if (line[0] == '#')
459 continue;
460 if (strcmp(line, name) == 0) {
461 found = 1;
462 break;
463 }
464 }
465 (void) fclose(fd);
466 }
467 return (found);
468 }
469
470 /*
471 * Terminate login as previous user, if any, resetting state;
472 * used when USER command is given or login fails.
473 */
474 static void
end_login()475 end_login()
476 {
477
478 (void) seteuid((uid_t)0);
479 if (logged_in)
480 logwtmp(ttyline, "", "");
481 pw = NULL;
482 logged_in = 0;
483 guest = 0;
484 }
485
486 void
pass(passwd)487 pass(passwd)
488 char *passwd;
489 {
490 char *salt, *xpasswd;
491 FILE *fd;
492
493 if (logged_in || askpasswd == 0) {
494 reply(503, "Login with USER first.");
495 return;
496 }
497 askpasswd = 0;
498 if (!guest) { /* "ftp" is only account allowed no password */
499 if (pw == NULL)
500 salt = "xx";
501 else
502 salt = pw->pw_passwd;
503 xpasswd = crypt(passwd, salt);
504 /* The strcmp does not catch null passwords! */
505 if (pw == NULL || *pw->pw_passwd == '\0' ||
506 strcmp(xpasswd, pw->pw_passwd)) {
507 reply(530, "Login incorrect.");
508 if (logging)
509 syslog(LOG_NOTICE,
510 "FTP LOGIN FAILED FROM %s, %s",
511 remotehost, curname);
512 pw = NULL;
513 if (login_attempts++ >= 5) {
514 syslog(LOG_NOTICE,
515 "repeated login failures from %s",
516 remotehost);
517 exit(0);
518 }
519 return;
520 }
521 }
522 login_attempts = 0; /* this time successful */
523 if (setegid((gid_t)pw->pw_gid) < 0) {
524 reply(550, "Can't set gid.");
525 return;
526 }
527 (void) initgroups(pw->pw_name, pw->pw_gid);
528
529 /* open wtmp before chroot */
530 (void)sprintf(ttyline, "ftp%d", getpid());
531 logwtmp(ttyline, pw->pw_name, remotehost);
532 logged_in = 1;
533
534 if (guest) {
535 /*
536 * We MUST do a chdir() after the chroot. Otherwise
537 * the old current directory will be accessible as "."
538 * outside the new root!
539 */
540 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
541 reply(550, "Can't set guest privileges.");
542 goto bad;
543 }
544 } else if (chdir(pw->pw_dir) < 0) {
545 if (chdir("/") < 0) {
546 reply(530, "User %s: can't change directory to %s.",
547 pw->pw_name, pw->pw_dir);
548 goto bad;
549 } else
550 lreply(230, "No directory! Logging in with home=/");
551 }
552 if (seteuid((uid_t)pw->pw_uid) < 0) {
553 reply(550, "Can't set uid.");
554 goto bad;
555 }
556 /*
557 * Display a login message, if it exists.
558 * N.B. reply(230,) must follow the message.
559 */
560 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
561 char *cp, line[LINE_MAX];
562
563 while (fgets(line, sizeof(line), fd) != NULL) {
564 if ((cp = strchr(line, '\n')) != NULL)
565 *cp = '\0';
566 lreply(230, "%s", line);
567 }
568 (void) fflush(stdout);
569 (void) fclose(fd);
570 }
571 if (guest) {
572 reply(230, "Guest login ok, access restrictions apply.");
573 #ifdef SETPROCTITLE
574 snprintf(proctitle, sizeof(proctitle),
575 "%s: anonymous/%.*s", remotehost,
576 sizeof(proctitle) - sizeof(remotehost) -
577 sizeof(": anonymous/"), passwd);
578 setproctitle(proctitle);
579 #endif /* SETPROCTITLE */
580 if (logging)
581 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
582 remotehost, passwd);
583 } else {
584 reply(230, "User %s logged in.", pw->pw_name);
585 #ifdef SETPROCTITLE
586 snprintf(proctitle, sizeof(proctitle),
587 "%s: %s", remotehost, pw->pw_name);
588 setproctitle(proctitle);
589 #endif /* SETPROCTITLE */
590 if (logging)
591 syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
592 remotehost, pw->pw_name);
593 }
594 (void) umask(defumask);
595 return;
596 bad:
597 /* Forget all about it... */
598 end_login();
599 }
600
601 void
retrieve(cmd,name)602 retrieve(cmd, name)
603 char *cmd, *name;
604 {
605 FILE *fin, *dout;
606 struct stat st;
607 int (*closefunc) __P((FILE *));
608
609 if (cmd == 0) {
610 fin = fopen(name, "r"), closefunc = fclose;
611 st.st_size = 0;
612 } else {
613 char line[BUFSIZ];
614
615 (void) sprintf(line, cmd, name), name = line;
616 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
617 st.st_size = -1;
618 st.st_blksize = BUFSIZ;
619 }
620 if (fin == NULL) {
621 if (errno != 0) {
622 perror_reply(550, name);
623 if (cmd == 0) {
624 LOGCMD("get", name);
625 }
626 }
627 return;
628 }
629 byte_count = -1;
630 if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
631 reply(550, "%s: not a plain file.", name);
632 goto done;
633 }
634 if (restart_point) {
635 if (type == TYPE_A) {
636 off_t i, n;
637 int c;
638
639 n = restart_point;
640 i = 0;
641 while (i++ < n) {
642 if ((c=getc(fin)) == EOF) {
643 perror_reply(550, name);
644 goto done;
645 }
646 if (c == '\n')
647 i++;
648 }
649 } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
650 perror_reply(550, name);
651 goto done;
652 }
653 }
654 dout = dataconn(name, st.st_size, "w");
655 if (dout == NULL)
656 goto done;
657 send_data(fin, dout, st.st_blksize);
658 (void) fclose(dout);
659 data = -1;
660 pdata = -1;
661 done:
662 if (cmd == 0)
663 LOGBYTES("get", name, byte_count);
664 (*closefunc)(fin);
665 }
666
667 void
store(name,mode,unique)668 store(name, mode, unique)
669 char *name, *mode;
670 int unique;
671 {
672 FILE *fout, *din;
673 struct stat st;
674 int (*closefunc) __P((FILE *));
675
676 if (unique && stat(name, &st) == 0 &&
677 (name = gunique(name)) == NULL) {
678 LOGCMD(*mode == 'w' ? "put" : "append", name);
679 return;
680 }
681
682 if (restart_point)
683 mode = "r+";
684 fout = fopen(name, mode);
685 closefunc = fclose;
686 if (fout == NULL) {
687 perror_reply(553, name);
688 LOGCMD(*mode == 'w' ? "put" : "append", name);
689 return;
690 }
691 byte_count = -1;
692 if (restart_point) {
693 if (type == TYPE_A) {
694 off_t i, n;
695 int c;
696
697 n = restart_point;
698 i = 0;
699 while (i++ < n) {
700 if ((c=getc(fout)) == EOF) {
701 perror_reply(550, name);
702 goto done;
703 }
704 if (c == '\n')
705 i++;
706 }
707 /*
708 * We must do this seek to "current" position
709 * because we are changing from reading to
710 * writing.
711 */
712 if (fseek(fout, 0L, L_INCR) < 0) {
713 perror_reply(550, name);
714 goto done;
715 }
716 } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
717 perror_reply(550, name);
718 goto done;
719 }
720 }
721 din = dataconn(name, (off_t)-1, "r");
722 if (din == NULL)
723 goto done;
724 if (receive_data(din, fout) == 0) {
725 if (unique)
726 reply(226, "Transfer complete (unique file name:%s).",
727 name);
728 else
729 reply(226, "Transfer complete.");
730 }
731 (void) fclose(din);
732 data = -1;
733 pdata = -1;
734 done:
735 LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
736 (*closefunc)(fout);
737 }
738
739 static FILE *
getdatasock(mode)740 getdatasock(mode)
741 char *mode;
742 {
743 int on = 1, s, t, tries;
744
745 if (data >= 0)
746 return (fdopen(data, mode));
747 (void) seteuid((uid_t)0);
748 s = socket(AF_INET, SOCK_STREAM, 0);
749 if (s < 0)
750 goto bad;
751 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
752 (char *) &on, sizeof(on)) < 0)
753 goto bad;
754 /* anchor socket to avoid multi-homing problems */
755 data_source.sin_family = AF_INET;
756 data_source.sin_addr = ctrl_addr.sin_addr;
757 for (tries = 1; ; tries++) {
758 if (bind(s, (struct sockaddr *)&data_source,
759 sizeof(data_source)) >= 0)
760 break;
761 if (errno != EADDRINUSE || tries > 10)
762 goto bad;
763 sleep(tries);
764 }
765 (void) seteuid((uid_t)pw->pw_uid);
766 #ifdef IP_TOS
767 on = IPTOS_THROUGHPUT;
768 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
769 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
770 #endif
771 return (fdopen(s, mode));
772 bad:
773 /* Return the real value of errno (close may change it) */
774 t = errno;
775 (void) seteuid((uid_t)pw->pw_uid);
776 (void) close(s);
777 errno = t;
778 return (NULL);
779 }
780
781 static FILE *
dataconn(name,size,mode)782 dataconn(name, size, mode)
783 char *name;
784 off_t size;
785 char *mode;
786 {
787 char sizebuf[32];
788 FILE *file;
789 int retry = 0, tos;
790
791 file_size = size;
792 byte_count = 0;
793 if (size != (off_t) -1)
794 (void) sprintf(sizebuf, " (%qd bytes)", size);
795 else
796 (void) strcpy(sizebuf, "");
797 if (pdata >= 0) {
798 struct sockaddr_in from;
799 int s, fromlen = sizeof(from);
800
801 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
802 if (s < 0) {
803 reply(425, "Can't open data connection.");
804 (void) close(pdata);
805 pdata = -1;
806 return (NULL);
807 }
808 (void) close(pdata);
809 pdata = s;
810 #ifdef IP_TOS
811 tos = IPTOS_LOWDELAY;
812 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
813 sizeof(int));
814 #endif
815 reply(150, "Opening %s mode data connection for '%s'%s.",
816 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
817 return (fdopen(pdata, mode));
818 }
819 if (data >= 0) {
820 reply(125, "Using existing data connection for '%s'%s.",
821 name, sizebuf);
822 usedefault = 1;
823 return (fdopen(data, mode));
824 }
825 if (usedefault)
826 data_dest = his_addr;
827 usedefault = 1;
828 file = getdatasock(mode);
829 if (file == NULL) {
830 reply(425, "Can't create data socket (%s,%d): %s.",
831 inet_ntoa(data_source.sin_addr),
832 ntohs(data_source.sin_port), strerror(errno));
833 return (NULL);
834 }
835 data = fileno(file);
836 while (connect(data, (struct sockaddr *)&data_dest,
837 sizeof(data_dest)) < 0) {
838 if (errno == EADDRINUSE && retry < swaitmax) {
839 sleep((unsigned) swaitint);
840 retry += swaitint;
841 continue;
842 }
843 perror_reply(425, "Can't build data connection");
844 (void) fclose(file);
845 data = -1;
846 return (NULL);
847 }
848 reply(150, "Opening %s mode data connection for '%s'%s.",
849 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
850 return (file);
851 }
852
853 /*
854 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
855 * encapsulation of the data subject * to Mode, Structure, and Type.
856 *
857 * NB: Form isn't handled.
858 */
859 static void
send_data(instr,outstr,blksize)860 send_data(instr, outstr, blksize)
861 FILE *instr, *outstr;
862 off_t blksize;
863 {
864 int c, cnt, filefd, netfd;
865 char *buf;
866
867 transflag++;
868 if (setjmp(urgcatch)) {
869 transflag = 0;
870 return;
871 }
872 switch (type) {
873
874 case TYPE_A:
875 while ((c = getc(instr)) != EOF) {
876 byte_count++;
877 if (c == '\n') {
878 if (ferror(outstr))
879 goto data_err;
880 (void) putc('\r', outstr);
881 }
882 (void) putc(c, outstr);
883 }
884 fflush(outstr);
885 transflag = 0;
886 if (ferror(instr))
887 goto file_err;
888 if (ferror(outstr))
889 goto data_err;
890 reply(226, "Transfer complete.");
891 return;
892
893 case TYPE_I:
894 case TYPE_L:
895 if ((buf = malloc((u_int)blksize)) == NULL) {
896 transflag = 0;
897 perror_reply(451, "Local resource failure: malloc");
898 return;
899 }
900 netfd = fileno(outstr);
901 filefd = fileno(instr);
902 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
903 write(netfd, buf, cnt) == cnt)
904 byte_count += cnt;
905 transflag = 0;
906 (void)free(buf);
907 if (cnt != 0) {
908 if (cnt < 0)
909 goto file_err;
910 goto data_err;
911 }
912 reply(226, "Transfer complete.");
913 return;
914 default:
915 transflag = 0;
916 reply(550, "Unimplemented TYPE %d in send_data", type);
917 return;
918 }
919
920 data_err:
921 transflag = 0;
922 perror_reply(426, "Data connection");
923 return;
924
925 file_err:
926 transflag = 0;
927 perror_reply(551, "Error on input file");
928 }
929
930 /*
931 * Transfer data from peer to "outstr" using the appropriate encapulation of
932 * the data subject to Mode, Structure, and Type.
933 *
934 * N.B.: Form isn't handled.
935 */
936 static int
receive_data(instr,outstr)937 receive_data(instr, outstr)
938 FILE *instr, *outstr;
939 {
940 int c;
941 int cnt, bare_lfs = 0;
942 char buf[BUFSIZ];
943
944 transflag++;
945 if (setjmp(urgcatch)) {
946 transflag = 0;
947 return (-1);
948 }
949 switch (type) {
950
951 case TYPE_I:
952 case TYPE_L:
953 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
954 if (write(fileno(outstr), buf, cnt) != cnt)
955 goto file_err;
956 byte_count += cnt;
957 }
958 if (cnt < 0)
959 goto data_err;
960 transflag = 0;
961 return (0);
962
963 case TYPE_E:
964 reply(553, "TYPE E not implemented.");
965 transflag = 0;
966 return (-1);
967
968 case TYPE_A:
969 while ((c = getc(instr)) != EOF) {
970 byte_count++;
971 if (c == '\n')
972 bare_lfs++;
973 while (c == '\r') {
974 if (ferror(outstr))
975 goto data_err;
976 if ((c = getc(instr)) != '\n') {
977 (void) putc ('\r', outstr);
978 if (c == '\0' || c == EOF)
979 goto contin2;
980 }
981 }
982 (void) putc(c, outstr);
983 contin2: ;
984 }
985 fflush(outstr);
986 if (ferror(instr))
987 goto data_err;
988 if (ferror(outstr))
989 goto file_err;
990 transflag = 0;
991 if (bare_lfs) {
992 lreply(226,
993 "WARNING! %d bare linefeeds received in ASCII mode",
994 bare_lfs);
995 (void)printf(" File may not have transferred correctly.\r\n");
996 }
997 return (0);
998 default:
999 reply(550, "Unimplemented TYPE %d in receive_data", type);
1000 transflag = 0;
1001 return (-1);
1002 }
1003
1004 data_err:
1005 transflag = 0;
1006 perror_reply(426, "Data Connection");
1007 return (-1);
1008
1009 file_err:
1010 transflag = 0;
1011 perror_reply(452, "Error writing file");
1012 return (-1);
1013 }
1014
1015 void
statfilecmd(filename)1016 statfilecmd(filename)
1017 char *filename;
1018 {
1019 FILE *fin;
1020 int c;
1021 char line[LINE_MAX];
1022
1023 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1024 fin = ftpd_popen(line, "r");
1025 lreply(211, "status of %s:", filename);
1026 while ((c = getc(fin)) != EOF) {
1027 if (c == '\n') {
1028 if (ferror(stdout)){
1029 perror_reply(421, "control connection");
1030 (void) ftpd_pclose(fin);
1031 dologout(1);
1032 /* NOTREACHED */
1033 }
1034 if (ferror(fin)) {
1035 perror_reply(551, filename);
1036 (void) ftpd_pclose(fin);
1037 return;
1038 }
1039 (void) putc('\r', stdout);
1040 }
1041 (void) putc(c, stdout);
1042 }
1043 (void) ftpd_pclose(fin);
1044 reply(211, "End of Status");
1045 }
1046
1047 void
statcmd()1048 statcmd()
1049 {
1050 struct sockaddr_in *sin;
1051 u_char *a, *p;
1052
1053 lreply(211, "%s FTP server status:", hostname, version);
1054 printf(" %s\r\n", version);
1055 printf(" Connected to %s", remotehost);
1056 if (!isdigit(remotehost[0]))
1057 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1058 printf("\r\n");
1059 if (logged_in) {
1060 if (guest)
1061 printf(" Logged in anonymously\r\n");
1062 else
1063 printf(" Logged in as %s\r\n", pw->pw_name);
1064 } else if (askpasswd)
1065 printf(" Waiting for password\r\n");
1066 else
1067 printf(" Waiting for user name\r\n");
1068 printf(" TYPE: %s", typenames[type]);
1069 if (type == TYPE_A || type == TYPE_E)
1070 printf(", FORM: %s", formnames[form]);
1071 if (type == TYPE_L)
1072 #if NBBY == 8
1073 printf(" %d", NBBY);
1074 #else
1075 printf(" %d", bytesize); /* need definition! */
1076 #endif
1077 printf("; STRUcture: %s; transfer MODE: %s\r\n",
1078 strunames[stru], modenames[mode]);
1079 if (data != -1)
1080 printf(" Data connection open\r\n");
1081 else if (pdata != -1) {
1082 printf(" in Passive mode");
1083 sin = &pasv_addr;
1084 goto printaddr;
1085 } else if (usedefault == 0) {
1086 printf(" PORT");
1087 sin = &data_dest;
1088 printaddr:
1089 a = (u_char *) &sin->sin_addr;
1090 p = (u_char *) &sin->sin_port;
1091 #define UC(b) (((int) b) & 0xff)
1092 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1093 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1094 #undef UC
1095 } else
1096 printf(" No data connection\r\n");
1097 reply(211, "End of status");
1098 }
1099
1100 void
fatal(s)1101 fatal(s)
1102 char *s;
1103 {
1104
1105 reply(451, "Error in server: %s\n", s);
1106 reply(221, "Closing connection due to server error.");
1107 dologout(0);
1108 /* NOTREACHED */
1109 }
1110
1111 void
1112 #if __STDC__
reply(int n,const char * fmt,...)1113 reply(int n, const char *fmt, ...)
1114 #else
1115 reply(n, fmt, va_alist)
1116 int n;
1117 char *fmt;
1118 va_dcl
1119 #endif
1120 {
1121 va_list ap;
1122 #if __STDC__
1123 va_start(ap, fmt);
1124 #else
1125 va_start(ap);
1126 #endif
1127 (void)printf("%d ", n);
1128 (void)vprintf(fmt, ap);
1129 (void)printf("\r\n");
1130 (void)fflush(stdout);
1131 if (debug) {
1132 syslog(LOG_DEBUG, "<--- %d ", n);
1133 vsyslog(LOG_DEBUG, fmt, ap);
1134 }
1135 }
1136
1137 void
1138 #if __STDC__
lreply(int n,const char * fmt,...)1139 lreply(int n, const char *fmt, ...)
1140 #else
1141 lreply(n, fmt, va_alist)
1142 int n;
1143 char *fmt;
1144 va_dcl
1145 #endif
1146 {
1147 va_list ap;
1148 #if __STDC__
1149 va_start(ap, fmt);
1150 #else
1151 va_start(ap);
1152 #endif
1153 (void)printf("%d- ", n);
1154 (void)vprintf(fmt, ap);
1155 (void)printf("\r\n");
1156 (void)fflush(stdout);
1157 if (debug) {
1158 syslog(LOG_DEBUG, "<--- %d- ", n);
1159 vsyslog(LOG_DEBUG, fmt, ap);
1160 }
1161 }
1162
1163 static void
ack(s)1164 ack(s)
1165 char *s;
1166 {
1167
1168 reply(250, "%s command successful.", s);
1169 }
1170
1171 void
nack(s)1172 nack(s)
1173 char *s;
1174 {
1175
1176 reply(502, "%s command not implemented.", s);
1177 }
1178
1179 /* ARGSUSED */
1180 void
yyerror(s)1181 yyerror(s)
1182 char *s;
1183 {
1184 char *cp;
1185
1186 if (cp = strchr(cbuf,'\n'))
1187 *cp = '\0';
1188 reply(500, "'%s': command not understood.", cbuf);
1189 }
1190
1191 void
delete(name)1192 delete(name)
1193 char *name;
1194 {
1195 struct stat st;
1196
1197 LOGCMD("delete", name);
1198 if (stat(name, &st) < 0) {
1199 perror_reply(550, name);
1200 return;
1201 }
1202 if ((st.st_mode&S_IFMT) == S_IFDIR) {
1203 if (rmdir(name) < 0) {
1204 perror_reply(550, name);
1205 return;
1206 }
1207 goto done;
1208 }
1209 if (unlink(name) < 0) {
1210 perror_reply(550, name);
1211 return;
1212 }
1213 done:
1214 ack("DELE");
1215 }
1216
1217 void
cwd(path)1218 cwd(path)
1219 char *path;
1220 {
1221
1222 if (chdir(path) < 0)
1223 perror_reply(550, path);
1224 else
1225 ack("CWD");
1226 }
1227
1228 void
makedir(name)1229 makedir(name)
1230 char *name;
1231 {
1232
1233 LOGCMD("mkdir", name);
1234 if (mkdir(name, 0777) < 0)
1235 perror_reply(550, name);
1236 else
1237 reply(257, "MKD command successful.");
1238 }
1239
1240 void
removedir(name)1241 removedir(name)
1242 char *name;
1243 {
1244
1245 LOGCMD("rmdir", name);
1246 if (rmdir(name) < 0)
1247 perror_reply(550, name);
1248 else
1249 ack("RMD");
1250 }
1251
1252 void
pwd()1253 pwd()
1254 {
1255 char path[MAXPATHLEN + 1];
1256
1257 if (getwd(path) == (char *)NULL)
1258 reply(550, "%s.", path);
1259 else
1260 reply(257, "\"%s\" is current directory.", path);
1261 }
1262
1263 char *
renamefrom(name)1264 renamefrom(name)
1265 char *name;
1266 {
1267 struct stat st;
1268
1269 if (stat(name, &st) < 0) {
1270 perror_reply(550, name);
1271 return ((char *)0);
1272 }
1273 reply(350, "File exists, ready for destination name");
1274 return (name);
1275 }
1276
1277 void
renamecmd(from,to)1278 renamecmd(from, to)
1279 char *from, *to;
1280 {
1281
1282 LOGCMD2("rename", from, to);
1283 if (rename(from, to) < 0)
1284 perror_reply(550, "rename");
1285 else
1286 ack("RNTO");
1287 }
1288
1289 static void
dolog(sin)1290 dolog(sin)
1291 struct sockaddr_in *sin;
1292 {
1293 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1294 sizeof(struct in_addr), AF_INET);
1295
1296 if (hp)
1297 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1298 else
1299 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1300 sizeof(remotehost));
1301 #ifdef SETPROCTITLE
1302 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1303 setproctitle(proctitle);
1304 #endif /* SETPROCTITLE */
1305
1306 if (logging)
1307 syslog(LOG_INFO, "connection from %s", remotehost);
1308 }
1309
1310 /*
1311 * Record logout in wtmp file
1312 * and exit with supplied status.
1313 */
1314 void
dologout(status)1315 dologout(status)
1316 int status;
1317 {
1318
1319 if (logged_in) {
1320 (void) seteuid((uid_t)0);
1321 logwtmp(ttyline, "", "");
1322 }
1323 /* beware of flushing buffers after a SIGPIPE */
1324 _exit(status);
1325 }
1326
1327 static void
myoob(signo)1328 myoob(signo)
1329 int signo;
1330 {
1331 char *cp;
1332
1333 /* only process if transfer occurring */
1334 if (!transflag)
1335 return;
1336 cp = tmpline;
1337 if (getline(cp, 7, stdin) == NULL) {
1338 reply(221, "You could at least say goodbye.");
1339 dologout(0);
1340 }
1341 upper(cp);
1342 if (strcmp(cp, "ABOR\r\n") == 0) {
1343 tmpline[0] = '\0';
1344 reply(426, "Transfer aborted. Data connection closed.");
1345 reply(226, "Abort successful");
1346 longjmp(urgcatch, 1);
1347 }
1348 if (strcmp(cp, "STAT\r\n") == 0) {
1349 if (file_size != (off_t) -1)
1350 reply(213, "Status: %qd of %qd bytes transferred",
1351 byte_count, file_size);
1352 else
1353 reply(213, "Status: %qd bytes transferred", byte_count);
1354 }
1355 }
1356
1357 /*
1358 * Note: a response of 425 is not mentioned as a possible response to
1359 * the PASV command in RFC959. However, it has been blessed as
1360 * a legitimate response by Jon Postel in a telephone conversation
1361 * with Rick Adams on 25 Jan 89.
1362 */
1363 void
passive()1364 passive()
1365 {
1366 int len;
1367 char *p, *a;
1368
1369 pdata = socket(AF_INET, SOCK_STREAM, 0);
1370 if (pdata < 0) {
1371 perror_reply(425, "Can't open passive connection");
1372 return;
1373 }
1374 pasv_addr = ctrl_addr;
1375 pasv_addr.sin_port = 0;
1376 (void) seteuid((uid_t)0);
1377 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1378 (void) seteuid((uid_t)pw->pw_uid);
1379 goto pasv_error;
1380 }
1381 (void) seteuid((uid_t)pw->pw_uid);
1382 len = sizeof(pasv_addr);
1383 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1384 goto pasv_error;
1385 if (listen(pdata, 1) < 0)
1386 goto pasv_error;
1387 a = (char *) &pasv_addr.sin_addr;
1388 p = (char *) &pasv_addr.sin_port;
1389
1390 #define UC(b) (((int) b) & 0xff)
1391
1392 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1393 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1394 return;
1395
1396 pasv_error:
1397 (void) close(pdata);
1398 pdata = -1;
1399 perror_reply(425, "Can't open passive connection");
1400 return;
1401 }
1402
1403 /*
1404 * Generate unique name for file with basename "local".
1405 * The file named "local" is already known to exist.
1406 * Generates failure reply on error.
1407 */
1408 static char *
gunique(local)1409 gunique(local)
1410 char *local;
1411 {
1412 static char new[MAXPATHLEN];
1413 struct stat st;
1414 int count;
1415 char *cp;
1416
1417 cp = strrchr(local, '/');
1418 if (cp)
1419 *cp = '\0';
1420 if (stat(cp ? local : ".", &st) < 0) {
1421 perror_reply(553, cp ? local : ".");
1422 return ((char *) 0);
1423 }
1424 if (cp)
1425 *cp = '/';
1426 (void) strcpy(new, local);
1427 cp = new + strlen(new);
1428 *cp++ = '.';
1429 for (count = 1; count < 100; count++) {
1430 (void)sprintf(cp, "%d", count);
1431 if (stat(new, &st) < 0)
1432 return (new);
1433 }
1434 reply(452, "Unique file name cannot be created.");
1435 return (NULL);
1436 }
1437
1438 /*
1439 * Format and send reply containing system error number.
1440 */
1441 void
perror_reply(code,string)1442 perror_reply(code, string)
1443 int code;
1444 char *string;
1445 {
1446
1447 reply(code, "%s: %s.", string, strerror(errno));
1448 }
1449
1450 static char *onefile[] = {
1451 "",
1452 0
1453 };
1454
1455 void
send_file_list(whichf)1456 send_file_list(whichf)
1457 char *whichf;
1458 {
1459 struct stat st;
1460 DIR *dirp = NULL;
1461 struct dirent *dir;
1462 FILE *dout = NULL;
1463 char **dirlist, *dirname;
1464 int simple = 0;
1465 int freeglob = 0;
1466 glob_t gl;
1467
1468 if (strpbrk(whichf, "~{[*?") != NULL) {
1469 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1470
1471 memset(&gl, 0, sizeof(gl));
1472 freeglob = 1;
1473 if (glob(whichf, flags, 0, &gl)) {
1474 reply(550, "not found");
1475 goto out;
1476 } else if (gl.gl_pathc == 0) {
1477 errno = ENOENT;
1478 perror_reply(550, whichf);
1479 goto out;
1480 }
1481 dirlist = gl.gl_pathv;
1482 } else {
1483 onefile[0] = whichf;
1484 dirlist = onefile;
1485 simple = 1;
1486 }
1487
1488 if (setjmp(urgcatch)) {
1489 transflag = 0;
1490 goto out;
1491 }
1492 while (dirname = *dirlist++) {
1493 if (stat(dirname, &st) < 0) {
1494 /*
1495 * If user typed "ls -l", etc, and the client
1496 * used NLST, do what the user meant.
1497 */
1498 if (dirname[0] == '-' && *dirlist == NULL &&
1499 transflag == 0) {
1500 retrieve("/bin/ls %s", dirname);
1501 goto out;
1502 }
1503 perror_reply(550, whichf);
1504 if (dout != NULL) {
1505 (void) fclose(dout);
1506 transflag = 0;
1507 data = -1;
1508 pdata = -1;
1509 }
1510 goto out;
1511 }
1512
1513 if (S_ISREG(st.st_mode)) {
1514 if (dout == NULL) {
1515 dout = dataconn("file list", (off_t)-1, "w");
1516 if (dout == NULL)
1517 goto out;
1518 transflag++;
1519 }
1520 fprintf(dout, "%s%s\n", dirname,
1521 type == TYPE_A ? "\r" : "");
1522 byte_count += strlen(dirname) + 1;
1523 continue;
1524 } else if (!S_ISDIR(st.st_mode))
1525 continue;
1526
1527 if ((dirp = opendir(dirname)) == NULL)
1528 continue;
1529
1530 while ((dir = readdir(dirp)) != NULL) {
1531 char nbuf[MAXPATHLEN];
1532
1533 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1534 continue;
1535 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1536 dir->d_namlen == 2)
1537 continue;
1538
1539 sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1540
1541 /*
1542 * We have to do a stat to insure it's
1543 * not a directory or special file.
1544 */
1545 if (simple || (stat(nbuf, &st) == 0 &&
1546 S_ISREG(st.st_mode))) {
1547 if (dout == NULL) {
1548 dout = dataconn("file list", (off_t)-1,
1549 "w");
1550 if (dout == NULL)
1551 goto out;
1552 transflag++;
1553 }
1554 if (nbuf[0] == '.' && nbuf[1] == '/')
1555 fprintf(dout, "%s%s\n", &nbuf[2],
1556 type == TYPE_A ? "\r" : "");
1557 else
1558 fprintf(dout, "%s%s\n", nbuf,
1559 type == TYPE_A ? "\r" : "");
1560 byte_count += strlen(nbuf) + 1;
1561 }
1562 }
1563 (void) closedir(dirp);
1564 }
1565
1566 if (dout == NULL)
1567 reply(550, "No files found.");
1568 else if (ferror(dout) != 0)
1569 perror_reply(550, "Data connection");
1570 else
1571 reply(226, "Transfer complete.");
1572
1573 transflag = 0;
1574 if (dout != NULL)
1575 (void) fclose(dout);
1576 data = -1;
1577 pdata = -1;
1578 out:
1579 if (freeglob) {
1580 freeglob = 0;
1581 globfree(&gl);
1582 }
1583 }
1584
1585 #ifdef SETPROCTITLE
1586 /*
1587 * Clobber argv so ps will show what we're doing. (Stolen from sendmail.)
1588 * Warning, since this is usually started from inetd.conf, it often doesn't
1589 * have much of an environment or arglist to overwrite.
1590 */
1591 void
1592 #if __STDC__
setproctitle(const char * fmt,...)1593 setproctitle(const char *fmt, ...)
1594 #else
1595 setproctitle(fmt, va_alist)
1596 char *fmt;
1597 va_dcl
1598 #endif
1599 {
1600 int i;
1601 va_list ap;
1602 char *p, *bp, ch;
1603 char buf[LINE_MAX];
1604
1605 #if __STDC__
1606 va_start(ap, fmt);
1607 #else
1608 va_start(ap);
1609 #endif
1610 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1611
1612 /* make ps print our process name */
1613 p = Argv[0];
1614 *p++ = '-';
1615
1616 i = strlen(buf);
1617 if (i > LastArgv - p - 2) {
1618 i = LastArgv - p - 2;
1619 buf[i] = '\0';
1620 }
1621 bp = buf;
1622 while (ch = *bp++)
1623 if (ch != '\n' && ch != '\r')
1624 *p++ = ch;
1625 while (p < LastArgv)
1626 *p++ = ' ';
1627 }
1628 #endif /* SETPROCTITLE */
1629