xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 61427)
122499Sdist /*
2*61427Sbostic  * Copyright (c) 1985, 1988, 1990, 1992, 1993
3*61427Sbostic  *	The Regents of the University of California.  All rights reserved.
433738Sbostic  *
542666Sbostic  * %sccs.include.redist.c%
622499Sdist  */
722499Sdist 
810275Ssam #ifndef lint
9*61427Sbostic static char copyright[] =
10*61427Sbostic "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993\n\
11*61427Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1233738Sbostic #endif /* not lint */
1310275Ssam 
1422499Sdist #ifndef lint
15*61427Sbostic static char sccsid[] = "@(#)ftpd.c	8.1 (Berkeley) 06/04/93";
1633738Sbostic #endif /* not lint */
1722499Sdist 
1810275Ssam /*
1910275Ssam  * FTP server.
2010275Ssam  */
2110303Ssam #include <sys/param.h>
2210275Ssam #include <sys/stat.h>
2310275Ssam #include <sys/ioctl.h>
2410275Ssam #include <sys/socket.h>
2513595Ssam #include <sys/wait.h>
2610275Ssam 
2710275Ssam #include <netinet/in.h>
2844339Skarels #include <netinet/in_systm.h>
2944339Skarels #include <netinet/ip.h>
3010275Ssam 
3136933Skarels #define	FTP_NAMES
3213034Ssam #include <arpa/ftp.h>
3313211Sroot #include <arpa/inet.h>
3426044Sminshall #include <arpa/telnet.h>
3513034Ssam 
3610275Ssam #include <signal.h>
3746669Sbostic #include <dirent.h>
3846669Sbostic #include <fcntl.h>
3946669Sbostic #include <time.h>
4010275Ssam #include <pwd.h>
4110275Ssam #include <setjmp.h>
4210275Ssam #include <netdb.h>
4310423Ssam #include <errno.h>
4426493Sminshall #include <syslog.h>
4546669Sbostic #include <unistd.h>
4646669Sbostic #include <stdio.h>
4746669Sbostic #include <ctype.h>
4846669Sbostic #include <stdlib.h>
4946669Sbostic #include <string.h>
5037459Skarels #include "pathnames.h"
5155258Sandrew #include "extern.h"
5210275Ssam 
5355258Sandrew #if __STDC__
5455258Sandrew #include <stdarg.h>
5555258Sandrew #else
5655258Sandrew #include <varargs.h>
5755258Sandrew #endif
5855258Sandrew 
5955258Sandrew extern	off_t restart_point;
6010275Ssam extern	char *home;		/* pointer to home directory for glob */
6126044Sminshall extern	char cbuf[];
6255258Sandrew extern	char version[];
6310275Ssam 
6410275Ssam struct	sockaddr_in ctrl_addr;
6510275Ssam struct	sockaddr_in data_source;
6610275Ssam struct	sockaddr_in data_dest;
6710275Ssam struct	sockaddr_in his_addr;
6836933Skarels struct	sockaddr_in pasv_addr;
6910275Ssam 
7010275Ssam int	data;
7126044Sminshall jmp_buf	errcatch, urgcatch;
7210275Ssam int	logged_in;
7310275Ssam struct	passwd *pw;
7410275Ssam int	debug;
7526493Sminshall int	timeout = 900;    /* timeout after 15 minutes of inactivity */
7636933Skarels int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
7711757Ssam int	logging;
7810275Ssam int	guest;
7910275Ssam int	type;
8010275Ssam int	form;
8110275Ssam int	stru;			/* avoid C keyword */
8210275Ssam int	mode;
8310321Ssam int	usedefault = 1;		/* for data transfers */
8436304Skarels int	pdata = -1;		/* for passive mode */
8526044Sminshall int	transflag;
8636933Skarels off_t	file_size;
8736933Skarels off_t	byte_count;
8836933Skarels #if !defined(CMASK) || CMASK == 0
8936933Skarels #undef CMASK
9036933Skarels #define CMASK 027
9136933Skarels #endif
9236933Skarels int	defumask = CMASK;		/* default umask value */
9326044Sminshall char	tmpline[7];
9436276Sbostic char	hostname[MAXHOSTNAMELEN];
9536276Sbostic char	remotehost[MAXHOSTNAMELEN];
9610275Ssam 
9711653Ssam /*
9811653Ssam  * Timeout intervals for retrying connections
9911653Ssam  * to hosts that don't accept PORT cmds.  This
10011653Ssam  * is a kludge, but given the problems with TCP...
10111653Ssam  */
10211653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
10311653Ssam #define	SWAITINT	5	/* interval between retries */
10411653Ssam 
10511653Ssam int	swaitmax = SWAITMAX;
10611653Ssam int	swaitint = SWAITINT;
10711653Ssam 
10836620Srick #ifdef SETPROCTITLE
10936620Srick char	**Argv = NULL;		/* pointer to argument vector */
11036620Srick char	*LastArgv = NULL;	/* end of argv */
11136933Skarels char	proctitle[BUFSIZ];	/* initial part of title */
11236620Srick #endif /* SETPROCTITLE */
11336620Srick 
11455258Sandrew #define MAXLINE         256
11555258Sandrew 
11655258Sandrew #define LOGCMD(cmd, file) \
11755258Sandrew 	if (logging > 1) \
11855258Sandrew 	    syslog(LOG_INFO,"%s %s%s", cmd, \
11955258Sandrew 		*(file) == '/' ? "" : curdir(), file);
12055258Sandrew #define LOGCMD2(cmd, file1, file2) \
12155258Sandrew 	 if (logging > 1) \
12255258Sandrew 	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
12355258Sandrew 		*(file1) == '/' ? "" : curdir(), file1, \
12455258Sandrew 		*(file2) == '/' ? "" : curdir(), file2);
12555258Sandrew #define LOGBYTES(cmd, file, cnt) \
12655258Sandrew 	if (logging > 1) { \
12755258Sandrew 		if (cnt == (off_t)-1) \
12855258Sandrew 		    syslog(LOG_INFO,"%s %s%s", cmd, \
12955258Sandrew 			*(file) == '/' ? "" : curdir(), file); \
13055258Sandrew 		else \
13155258Sandrew 		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
13255258Sandrew 			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
13355258Sandrew 	}
13455258Sandrew 
13555258Sandrew static void	 ack __P((char *));
13655258Sandrew static void	 myoob __P((int));
13755258Sandrew static int	 checkuser __P((char *));
13855258Sandrew static FILE	*dataconn __P((char *, off_t, char *));
13955258Sandrew static void	 dolog __P((struct sockaddr_in *));
14055258Sandrew static char	*curdir __P((void));
14155258Sandrew static void	 end_login __P((void));
14255258Sandrew static FILE	*getdatasock __P((char *));
14355258Sandrew static char	*gunique __P((char *));
14455258Sandrew static void	 lostconn __P((int));
14555258Sandrew static int	 receive_data __P((FILE *, FILE *));
14655258Sandrew static void	 send_data __P((FILE *, FILE *, off_t));
14755258Sandrew static struct passwd *
14855258Sandrew 		 sgetpwnam __P((char *));
14955258Sandrew static char	*sgetsave __P((char *));
15055258Sandrew 
15155258Sandrew static char *
15255258Sandrew curdir()
15355258Sandrew {
15455258Sandrew 	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
15555258Sandrew 
15655258Sandrew 	if (getcwd(path, sizeof(path)-2) == NULL)
15755258Sandrew 		return ("");
15855258Sandrew 	if (path[1] != '\0')		/* special case for root dir. */
15955258Sandrew 		strcat(path, "/");
16055258Sandrew 	/* For guest account, skip / since it's chrooted */
16155258Sandrew 	return (guest ? path+1 : path);
16255258Sandrew }
16355258Sandrew 
16455258Sandrew int
16536620Srick main(argc, argv, envp)
16610275Ssam 	int argc;
16710275Ssam 	char *argv[];
16836620Srick 	char **envp;
16910275Ssam {
17044339Skarels 	int addrlen, on = 1, tos;
17155258Sandrew 	char *cp, line[MAXLINE];
17255258Sandrew 	FILE *fd;
17310275Ssam 
17445028Skarels 	/*
17545028Skarels 	 * LOG_NDELAY sets up the logging connection immediately,
17645028Skarels 	 * necessary for anonymous ftp's that chroot and can't do it later.
17745028Skarels 	 */
17855258Sandrew 	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
17916339Skarels 	addrlen = sizeof (his_addr);
18036304Skarels 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
18126493Sminshall 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
18210275Ssam 		exit(1);
18310275Ssam 	}
18416339Skarels 	addrlen = sizeof (ctrl_addr);
18536304Skarels 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
18626493Sminshall 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
18716339Skarels 		exit(1);
18816339Skarels 	}
18944339Skarels #ifdef IP_TOS
19044339Skarels 	tos = IPTOS_LOWDELAY;
19144339Skarels 	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
19244339Skarels 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
19344339Skarels #endif
19416339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
19510275Ssam 	debug = 0;
19636620Srick #ifdef SETPROCTITLE
19736620Srick 	/*
19836620Srick 	 *  Save start and extent of argv for setproctitle.
19936620Srick 	 */
20036620Srick 	Argv = argv;
20136620Srick 	while (*envp)
20236620Srick 		envp++;
20336620Srick 	LastArgv = envp[-1] + strlen(envp[-1]);
20436620Srick #endif /* SETPROCTITLE */
20536620Srick 
20610275Ssam 	argc--, argv++;
20710275Ssam 	while (argc > 0 && *argv[0] == '-') {
20810275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
20910275Ssam 
21011653Ssam 		case 'v':
21111653Ssam 			debug = 1;
21211653Ssam 			break;
21311653Ssam 
21410275Ssam 		case 'd':
21510275Ssam 			debug = 1;
21610275Ssam 			break;
21710275Ssam 
21811757Ssam 		case 'l':
21955258Sandrew 			logging++;	/* > 1 == extra logging */
22011757Ssam 			break;
22111757Ssam 
22211653Ssam 		case 't':
22311653Ssam 			timeout = atoi(++cp);
22436933Skarels 			if (maxtimeout < timeout)
22536933Skarels 				maxtimeout = timeout;
22611653Ssam 			goto nextopt;
22711653Ssam 
22836933Skarels 		case 'T':
22936933Skarels 			maxtimeout = atoi(++cp);
23036933Skarels 			if (timeout > maxtimeout)
23136933Skarels 				timeout = maxtimeout;
23236933Skarels 			goto nextopt;
23336933Skarels 
23436933Skarels 		case 'u':
23536933Skarels 		    {
23636933Skarels 			int val = 0;
23736933Skarels 
23836933Skarels 			while (*++cp && *cp >= '0' && *cp <= '9')
23936933Skarels 				val = val*8 + *cp - '0';
24036933Skarels 			if (*cp)
24136933Skarels 				fprintf(stderr, "ftpd: Bad value for -u\n");
24236933Skarels 			else
24336933Skarels 				defumask = val;
24436933Skarels 			goto nextopt;
24536933Skarels 		    }
24636933Skarels 
24710275Ssam 		default:
24816339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
24916339Skarels 			     *cp);
25010275Ssam 			break;
25110275Ssam 		}
25211653Ssam nextopt:
25310275Ssam 		argc--, argv++;
25410275Ssam 	}
25537459Skarels 	(void) freopen(_PATH_DEVNULL, "w", stderr);
25626493Sminshall 	(void) signal(SIGPIPE, lostconn);
25726493Sminshall 	(void) signal(SIGCHLD, SIG_IGN);
25835691Sbostic 	if ((int)signal(SIGURG, myoob) < 0)
25926493Sminshall 		syslog(LOG_ERR, "signal: %m");
26035691Sbostic 
26138134Srick 	/* Try to handle urgent data inline */
26227750Sminshall #ifdef SO_OOBINLINE
26336276Sbostic 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
26427750Sminshall 		syslog(LOG_ERR, "setsockopt: %m");
26536276Sbostic #endif
26638134Srick 
26736933Skarels #ifdef	F_SETOWN
26836304Skarels 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
26936304Skarels 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
27036933Skarels #endif
27116760Slepreau 	dolog(&his_addr);
27216339Skarels 	/*
27316339Skarels 	 * Set up default state
27416339Skarels 	 */
27516339Skarels 	data = -1;
27616339Skarels 	type = TYPE_A;
27716339Skarels 	form = FORM_N;
27816339Skarels 	stru = STRU_F;
27916339Skarels 	mode = MODE_S;
28026044Sminshall 	tmpline[0] = '\0';
28155258Sandrew 
28255258Sandrew 	/* If logins are disabled, print out the message. */
28355258Sandrew 	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
28455258Sandrew 		while (fgets(line, sizeof (line), fd) != NULL) {
28560087Sbostic 			if ((cp = strchr(line, '\n')) != NULL)
28655258Sandrew 				*cp = '\0';
28755258Sandrew 			lreply(530, "%s", line);
28855258Sandrew 		}
28955258Sandrew 		(void) fflush(stdout);
29055258Sandrew 		(void) fclose(fd);
29155258Sandrew 		reply(530, "System not available.");
29255258Sandrew 		exit(0);
29355258Sandrew 	}
29455258Sandrew 	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
29555258Sandrew 		while (fgets(line, sizeof (line), fd) != NULL) {
29660087Sbostic 			if ((cp = strchr(line, '\n')) != NULL)
29755258Sandrew 				*cp = '\0';
29855258Sandrew 			lreply(220, "%s", line);
29955258Sandrew 		}
30055258Sandrew 		(void) fflush(stdout);
30155258Sandrew 		(void) fclose(fd);
30255258Sandrew 		/* reply(220,) must follow */
30355258Sandrew 	}
30426493Sminshall 	(void) gethostname(hostname, sizeof (hostname));
30536276Sbostic 	reply(220, "%s FTP server (%s) ready.", hostname, version);
30636304Skarels 	(void) setjmp(errcatch);
30736304Skarels 	for (;;)
30826493Sminshall 		(void) yyparse();
30936620Srick 	/* NOTREACHED */
31010275Ssam }
31110419Ssam 
31255258Sandrew static void
31355258Sandrew lostconn(signo)
31455258Sandrew 	int signo;
31510275Ssam {
31614089Ssam 	if (debug)
31726493Sminshall 		syslog(LOG_DEBUG, "lost connection");
31814089Ssam 	dologout(-1);
31910275Ssam }
32010275Ssam 
32135672Sbostic static char ttyline[20];
32235672Sbostic 
32336185Sbostic /*
32436185Sbostic  * Helper function for sgetpwnam().
32536185Sbostic  */
32655258Sandrew static char *
32736185Sbostic sgetsave(s)
32836185Sbostic 	char *s;
32936185Sbostic {
33036185Sbostic 	char *new = malloc((unsigned) strlen(s) + 1);
33136933Skarels 
33236185Sbostic 	if (new == NULL) {
33336620Srick 		perror_reply(421, "Local resource failure: malloc");
33436185Sbostic 		dologout(1);
33536620Srick 		/* NOTREACHED */
33636185Sbostic 	}
33736185Sbostic 	(void) strcpy(new, s);
33836185Sbostic 	return (new);
33936185Sbostic }
34036185Sbostic 
34136185Sbostic /*
34236185Sbostic  * Save the result of a getpwnam.  Used for USER command, since
34336185Sbostic  * the data returned must not be clobbered by any other command
34436185Sbostic  * (e.g., globbing).
34536185Sbostic  */
34655258Sandrew static struct passwd *
34736185Sbostic sgetpwnam(name)
34836185Sbostic 	char *name;
34936185Sbostic {
35036185Sbostic 	static struct passwd save;
35136185Sbostic 	register struct passwd *p;
35236185Sbostic 	char *sgetsave();
35336185Sbostic 
35436185Sbostic 	if ((p = getpwnam(name)) == NULL)
35536185Sbostic 		return (p);
35636185Sbostic 	if (save.pw_name) {
35736185Sbostic 		free(save.pw_name);
35836185Sbostic 		free(save.pw_passwd);
35936185Sbostic 		free(save.pw_gecos);
36036185Sbostic 		free(save.pw_dir);
36136185Sbostic 		free(save.pw_shell);
36236185Sbostic 	}
36336185Sbostic 	save = *p;
36436185Sbostic 	save.pw_name = sgetsave(p->pw_name);
36536185Sbostic 	save.pw_passwd = sgetsave(p->pw_passwd);
36636185Sbostic 	save.pw_gecos = sgetsave(p->pw_gecos);
36736185Sbostic 	save.pw_dir = sgetsave(p->pw_dir);
36836185Sbostic 	save.pw_shell = sgetsave(p->pw_shell);
36936185Sbostic 	return (&save);
37036185Sbostic }
37136185Sbostic 
37255258Sandrew static int login_attempts;	/* number of failed login attempts */
37355258Sandrew static int askpasswd;		/* had user command, ask for passwd */
37455258Sandrew static char curname[10];	/* current USER name */
37536304Skarels 
37636304Skarels /*
37736304Skarels  * USER command.
37842327Sbostic  * Sets global passwd pointer pw if named account exists and is acceptable;
37942327Sbostic  * sets askpasswd if a PASS command is expected.  If logged in previously,
38042327Sbostic  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
38142327Sbostic  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
38242327Sbostic  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
38342327Sbostic  * requesting login privileges.  Disallow anyone who does not have a standard
38442327Sbostic  * shell as returned by getusershell().  Disallow anyone mentioned in the file
38542327Sbostic  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
38636304Skarels  */
38755258Sandrew void
38836304Skarels user(name)
38936304Skarels 	char *name;
39036304Skarels {
39136304Skarels 	register char *cp;
39236304Skarels 	char *shell;
39336304Skarels 
39436304Skarels 	if (logged_in) {
39536304Skarels 		if (guest) {
39636304Skarels 			reply(530, "Can't change user from guest login.");
39736304Skarels 			return;
39836304Skarels 		}
39936304Skarels 		end_login();
40036304Skarels 	}
40136304Skarels 
40236304Skarels 	guest = 0;
40336304Skarels 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
40440183Smckusick 		if (checkuser("ftp") || checkuser("anonymous"))
40540155Smckusick 			reply(530, "User %s access denied.", name);
40640155Smckusick 		else if ((pw = sgetpwnam("ftp")) != NULL) {
40736304Skarels 			guest = 1;
40836304Skarels 			askpasswd = 1;
40955258Sandrew 			reply(331,
41055258Sandrew 			    "Guest login ok, type your name as password.");
41136933Skarels 		} else
41236304Skarels 			reply(530, "User %s unknown.", name);
41355258Sandrew 		if (!askpasswd && logging)
41455258Sandrew 			syslog(LOG_NOTICE,
41555258Sandrew 			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
41636304Skarels 		return;
41736304Skarels 	}
41836304Skarels 	if (pw = sgetpwnam(name)) {
41936304Skarels 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
42037459Skarels 			shell = _PATH_BSHELL;
42136304Skarels 		while ((cp = getusershell()) != NULL)
42236304Skarels 			if (strcmp(cp, shell) == 0)
42336304Skarels 				break;
42436304Skarels 		endusershell();
42555258Sandrew 
42640183Smckusick 		if (cp == NULL || checkuser(name)) {
42736304Skarels 			reply(530, "User %s access denied.", name);
42836933Skarels 			if (logging)
42936933Skarels 				syslog(LOG_NOTICE,
43036933Skarels 				    "FTP LOGIN REFUSED FROM %s, %s",
43136933Skarels 				    remotehost, name);
43236304Skarels 			pw = (struct passwd *) NULL;
43336304Skarels 			return;
43436304Skarels 		}
43536304Skarels 	}
43655258Sandrew 	if (logging)
43755258Sandrew 		strncpy(curname, name, sizeof(curname)-1);
43836304Skarels 	reply(331, "Password required for %s.", name);
43936304Skarels 	askpasswd = 1;
44036304Skarels 	/*
44136304Skarels 	 * Delay before reading passwd after first failed
44236304Skarels 	 * attempt to slow down passwd-guessing programs.
44336304Skarels 	 */
44436304Skarels 	if (login_attempts)
44536304Skarels 		sleep((unsigned) login_attempts);
44636304Skarels }
44736304Skarels 
44836304Skarels /*
44940183Smckusick  * Check if a user is in the file _PATH_FTPUSERS
45040183Smckusick  */
45155258Sandrew static int
45240183Smckusick checkuser(name)
45340183Smckusick 	char *name;
45440183Smckusick {
45542327Sbostic 	register FILE *fd;
45642327Sbostic 	register char *p;
45742327Sbostic 	char line[BUFSIZ];
45855258Sandrew 	int found = 0;
45940183Smckusick 
46040183Smckusick 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
46142327Sbostic 		while (fgets(line, sizeof(line), fd) != NULL)
46260087Sbostic 			if ((p = strchr(line, '\n')) != NULL) {
46342327Sbostic 				*p = '\0';
46442327Sbostic 				if (line[0] == '#')
46542327Sbostic 					continue;
46655258Sandrew 				if (strcmp(p, name) == 0) {
46755258Sandrew 					found = 1;
46855258Sandrew 					break;
46955258Sandrew 				}
47042327Sbostic 			}
47140183Smckusick 		(void) fclose(fd);
47240183Smckusick 	}
47355258Sandrew 	return (found);
47440183Smckusick }
47540183Smckusick 
47640183Smckusick /*
47736304Skarels  * Terminate login as previous user, if any, resetting state;
47836304Skarels  * used when USER command is given or login fails.
47936304Skarels  */
48055258Sandrew static void
48136304Skarels end_login()
48236304Skarels {
48336304Skarels 
48436304Skarels 	(void) seteuid((uid_t)0);
48536304Skarels 	if (logged_in)
48636304Skarels 		logwtmp(ttyline, "", "");
48736304Skarels 	pw = NULL;
48836304Skarels 	logged_in = 0;
48936304Skarels 	guest = 0;
49036304Skarels }
49136304Skarels 
49255258Sandrew void
49310275Ssam pass(passwd)
49410275Ssam 	char *passwd;
49510275Ssam {
49636304Skarels 	char *xpasswd, *salt;
49755258Sandrew 	FILE *fd;
49810275Ssam 
49936304Skarels 	if (logged_in || askpasswd == 0) {
50010275Ssam 		reply(503, "Login with USER first.");
50110275Ssam 		return;
50210275Ssam 	}
50336304Skarels 	askpasswd = 0;
50410275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
50536304Skarels 		if (pw == NULL)
50636304Skarels 			salt = "xx";
50736304Skarels 		else
50836304Skarels 			salt = pw->pw_passwd;
50936304Skarels 		xpasswd = crypt(passwd, salt);
51016760Slepreau 		/* The strcmp does not catch null passwords! */
51136304Skarels 		if (pw == NULL || *pw->pw_passwd == '\0' ||
51236304Skarels 		    strcmp(xpasswd, pw->pw_passwd)) {
51310275Ssam 			reply(530, "Login incorrect.");
51455258Sandrew 			if (logging)
51555258Sandrew 				syslog(LOG_NOTICE,
51655258Sandrew 				    "FTP LOGIN FAILED FROM %s, %s",
51755258Sandrew 				    remotehost, curname);
51810275Ssam 			pw = NULL;
51936304Skarels 			if (login_attempts++ >= 5) {
52036933Skarels 				syslog(LOG_NOTICE,
52136304Skarels 				    "repeated login failures from %s",
52236304Skarels 				    remotehost);
52336304Skarels 				exit(0);
52436304Skarels 			}
52510275Ssam 			return;
52610275Ssam 		}
52710275Ssam 	}
52836304Skarels 	login_attempts = 0;		/* this time successful */
52955258Sandrew 	if (setegid((gid_t)pw->pw_gid) < 0) {
53055258Sandrew 		reply(550, "Can't set gid.");
53155258Sandrew 		return;
53255258Sandrew 	}
53336304Skarels 	(void) initgroups(pw->pw_name, pw->pw_gid);
53416033Sralph 
53536192Sbostic 	/* open wtmp before chroot */
53636192Sbostic 	(void)sprintf(ttyline, "ftp%d", getpid());
53736192Sbostic 	logwtmp(ttyline, pw->pw_name, remotehost);
53836192Sbostic 	logged_in = 1;
53936192Sbostic 
54036446Sbostic 	if (guest) {
54136620Srick 		/*
54236933Skarels 		 * We MUST do a chdir() after the chroot. Otherwise
54336933Skarels 		 * the old current directory will be accessible as "."
54436933Skarels 		 * outside the new root!
54536620Srick 		 */
54636620Srick 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
54736446Sbostic 			reply(550, "Can't set guest privileges.");
54836446Sbostic 			goto bad;
54936446Sbostic 		}
55036933Skarels 	} else if (chdir(pw->pw_dir) < 0) {
55136620Srick 		if (chdir("/") < 0) {
55236446Sbostic 			reply(530, "User %s: can't change directory to %s.",
55336446Sbostic 			    pw->pw_name, pw->pw_dir);
55436446Sbostic 			goto bad;
55536933Skarels 		} else
55636446Sbostic 			lreply(230, "No directory! Logging in with home=/");
55736620Srick 	}
55836304Skarels 	if (seteuid((uid_t)pw->pw_uid) < 0) {
55936304Skarels 		reply(550, "Can't set uid.");
56036304Skarels 		goto bad;
56136304Skarels 	}
56255258Sandrew 	/*
56355258Sandrew 	 * Display a login message, if it exists.
56455258Sandrew 	 * N.B. reply(230,) must follow the message.
56555258Sandrew 	 */
56655258Sandrew 	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
56755258Sandrew 		char *cp, line[MAXLINE];
56855258Sandrew 
56955258Sandrew 		while (fgets(line, sizeof (line), fd) != NULL) {
57060087Sbostic 			if ((cp = strchr(line, '\n')) != NULL)
57155258Sandrew 				*cp = '\0';
57255258Sandrew 			lreply(230, "%s", line);
57355258Sandrew 		}
57455258Sandrew 		(void) fflush(stdout);
57555258Sandrew 		(void) fclose(fd);
57655258Sandrew 	}
57736550Sbostic 	if (guest) {
57836192Sbostic 		reply(230, "Guest login ok, access restrictions apply.");
57936933Skarels #ifdef SETPROCTITLE
58036933Skarels 		sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
58136933Skarels 		    sizeof(proctitle) - sizeof(remotehost) -
58236933Skarels 		    sizeof(": anonymous/"), passwd);
58336933Skarels 		setproctitle(proctitle);
58436933Skarels #endif /* SETPROCTITLE */
58536933Skarels 		if (logging)
58636933Skarels 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
58736933Skarels 			    remotehost, passwd);
58852998Sbostic 		home = "/";		/* guest home dir for globbing */
58936933Skarels 	} else {
59010275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
59136933Skarels #ifdef SETPROCTITLE
59236933Skarels 		sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
59336933Skarels 		setproctitle(proctitle);
59436933Skarels #endif /* SETPROCTITLE */
59536933Skarels 		if (logging)
59655258Sandrew 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
59736933Skarels 			    remotehost, pw->pw_name);
59852998Sbostic 		home = pw->pw_dir;	/* home dir for globbing */
59936550Sbostic 	}
60036933Skarels 	(void) umask(defumask);
60110303Ssam 	return;
60210303Ssam bad:
60336304Skarels 	/* Forget all about it... */
60436304Skarels 	end_login();
60510275Ssam }
60610275Ssam 
60755258Sandrew void
60810275Ssam retrieve(cmd, name)
60910275Ssam 	char *cmd, *name;
61010275Ssam {
61110275Ssam 	FILE *fin, *dout;
61210275Ssam 	struct stat st;
61336620Srick 	int (*closefunc)();
61410275Ssam 
61536557Sbostic 	if (cmd == 0) {
61636446Sbostic 		fin = fopen(name, "r"), closefunc = fclose;
61736557Sbostic 		st.st_size = 0;
61836557Sbostic 	} else {
61910275Ssam 		char line[BUFSIZ];
62010275Ssam 
62126493Sminshall 		(void) sprintf(line, cmd, name), name = line;
62236304Skarels 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
62336557Sbostic 		st.st_size = -1;
62436933Skarels 		st.st_blksize = BUFSIZ;
62510275Ssam 	}
62610275Ssam 	if (fin == NULL) {
62755258Sandrew 		if (errno != 0) {
62836304Skarels 			perror_reply(550, name);
62955258Sandrew 			if (cmd == 0) {
63055258Sandrew 				LOGCMD("get", name);
63155258Sandrew 			}
63255258Sandrew 		}
63310275Ssam 		return;
63410275Ssam 	}
63555258Sandrew 	byte_count = -1;
63610275Ssam 	if (cmd == 0 &&
63736933Skarels 	    (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
63810275Ssam 		reply(550, "%s: not a plain file.", name);
63910275Ssam 		goto done;
64010275Ssam 	}
64137459Skarels 	if (restart_point) {
64237459Skarels 		if (type == TYPE_A) {
64337459Skarels 			register int i, n, c;
64437459Skarels 
64537459Skarels 			n = restart_point;
64637459Skarels 			i = 0;
64737459Skarels 			while (i++ < n) {
64837459Skarels 				if ((c=getc(fin)) == EOF) {
64937459Skarels 					perror_reply(550, name);
65037459Skarels 					goto done;
65137459Skarels 				}
65237459Skarels 				if (c == '\n')
65337459Skarels 					i++;
65452998Sbostic 			}
65537459Skarels 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
65637459Skarels 			perror_reply(550, name);
65737459Skarels 			goto done;
65837459Skarels 		}
65937459Skarels 	}
66010275Ssam 	dout = dataconn(name, st.st_size, "w");
66110275Ssam 	if (dout == NULL)
66210275Ssam 		goto done;
66336620Srick 	send_data(fin, dout, st.st_blksize);
66426493Sminshall 	(void) fclose(dout);
66526044Sminshall 	data = -1;
66626044Sminshall 	pdata = -1;
66710275Ssam done:
66855258Sandrew 	if (cmd == 0)
66955258Sandrew 		LOGBYTES("get", name, byte_count);
67010275Ssam 	(*closefunc)(fin);
67110275Ssam }
67210275Ssam 
67355258Sandrew void
67436304Skarels store(name, mode, unique)
67510275Ssam 	char *name, *mode;
67636304Skarels 	int unique;
67710275Ssam {
67810275Ssam 	FILE *fout, *din;
67936446Sbostic 	struct stat st;
68036620Srick 	int (*closefunc)();
68110275Ssam 
68236446Sbostic 	if (unique && stat(name, &st) == 0 &&
68355258Sandrew 	    (name = gunique(name)) == NULL) {
68455258Sandrew 		LOGCMD(*mode == 'w' ? "put" : "append", name);
68536446Sbostic 		return;
68655258Sandrew 	}
68710303Ssam 
68837459Skarels 	if (restart_point)
68937459Skarels 		mode = "r+w";
69036620Srick 	fout = fopen(name, mode);
69136620Srick 	closefunc = fclose;
69210275Ssam 	if (fout == NULL) {
69336304Skarels 		perror_reply(553, name);
69455258Sandrew 		LOGCMD(*mode == 'w' ? "put" : "append", name);
69510275Ssam 		return;
69610275Ssam 	}
69755258Sandrew 	byte_count = -1;
69837459Skarels 	if (restart_point) {
69937459Skarels 		if (type == TYPE_A) {
70037459Skarels 			register int i, n, c;
70137459Skarels 
70237459Skarels 			n = restart_point;
70337459Skarels 			i = 0;
70437459Skarels 			while (i++ < n) {
70537459Skarels 				if ((c=getc(fout)) == EOF) {
70637459Skarels 					perror_reply(550, name);
70737459Skarels 					goto done;
70837459Skarels 				}
70937459Skarels 				if (c == '\n')
71037459Skarels 					i++;
71152998Sbostic 			}
71237459Skarels 			/*
71337459Skarels 			 * We must do this seek to "current" position
71437459Skarels 			 * because we are changing from reading to
71537459Skarels 			 * writing.
71637459Skarels 			 */
71737459Skarels 			if (fseek(fout, 0L, L_INCR) < 0) {
71837459Skarels 				perror_reply(550, name);
71937459Skarels 				goto done;
72037459Skarels 			}
72137459Skarels 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
72237459Skarels 			perror_reply(550, name);
72337459Skarels 			goto done;
72437459Skarels 		}
72537459Skarels 	}
72636304Skarels 	din = dataconn(name, (off_t)-1, "r");
72710275Ssam 	if (din == NULL)
72810275Ssam 		goto done;
72936620Srick 	if (receive_data(din, fout) == 0) {
73036620Srick 		if (unique)
73136304Skarels 			reply(226, "Transfer complete (unique file name:%s).",
73236933Skarels 			    name);
73336304Skarels 		else
73436304Skarels 			reply(226, "Transfer complete.");
73526044Sminshall 	}
73626493Sminshall 	(void) fclose(din);
73726044Sminshall 	data = -1;
73826044Sminshall 	pdata = -1;
73910275Ssam done:
74055258Sandrew 	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
74110275Ssam 	(*closefunc)(fout);
74210275Ssam }
74310275Ssam 
74455258Sandrew static FILE *
74510275Ssam getdatasock(mode)
74610275Ssam 	char *mode;
74710275Ssam {
74837459Skarels 	int s, on = 1, tries;
74955258Sandrew 	int t;
75010275Ssam 
75110275Ssam 	if (data >= 0)
75210275Ssam 		return (fdopen(data, mode));
75350391Skarels 	(void) seteuid((uid_t)0);
75413247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
75510602Ssam 	if (s < 0)
75650391Skarels 		goto bad;
75737459Skarels 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
75837459Skarels 	    (char *) &on, sizeof (on)) < 0)
75910602Ssam 		goto bad;
76013152Ssam 	/* anchor socket to avoid multi-homing problems */
76113152Ssam 	data_source.sin_family = AF_INET;
76213152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
76337459Skarels 	for (tries = 1; ; tries++) {
76437459Skarels 		if (bind(s, (struct sockaddr *)&data_source,
76537459Skarels 		    sizeof (data_source)) >= 0)
76637459Skarels 			break;
76737459Skarels 		if (errno != EADDRINUSE || tries > 10)
76837459Skarels 			goto bad;
76937459Skarels 		sleep(tries);
77037459Skarels 	}
77136304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
77244339Skarels #ifdef IP_TOS
77344339Skarels 	on = IPTOS_THROUGHPUT;
77444339Skarels 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
77544339Skarels 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
77644339Skarels #endif
77710275Ssam 	return (fdopen(s, mode));
77810602Ssam bad:
77955258Sandrew 	/* Return the real value of errno (close may change it) */
78055258Sandrew 	t = errno;
78136304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
78226493Sminshall 	(void) close(s);
78355258Sandrew 	errno = t;
78410602Ssam 	return (NULL);
78510275Ssam }
78610275Ssam 
78755258Sandrew static FILE *
78810275Ssam dataconn(name, size, mode)
78910275Ssam 	char *name;
79011653Ssam 	off_t size;
79110275Ssam 	char *mode;
79210275Ssam {
79310275Ssam 	char sizebuf[32];
79410275Ssam 	FILE *file;
79544339Skarels 	int retry = 0, tos;
79610275Ssam 
79736933Skarels 	file_size = size;
79836933Skarels 	byte_count = 0;
79936304Skarels 	if (size != (off_t) -1)
80055258Sandrew 		(void) sprintf (sizebuf, " (%qd bytes)", size);
80110275Ssam 	else
80210275Ssam 		(void) strcpy(sizebuf, "");
80336304Skarels 	if (pdata >= 0) {
80426044Sminshall 		struct sockaddr_in from;
80526044Sminshall 		int s, fromlen = sizeof(from);
80626044Sminshall 
80736304Skarels 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
80826044Sminshall 		if (s < 0) {
80926044Sminshall 			reply(425, "Can't open data connection.");
81026044Sminshall 			(void) close(pdata);
81126044Sminshall 			pdata = -1;
81226044Sminshall 			return(NULL);
81326044Sminshall 		}
81426044Sminshall 		(void) close(pdata);
81526044Sminshall 		pdata = s;
81644339Skarels #ifdef IP_TOS
81744339Skarels 		tos = IPTOS_LOWDELAY;
81844339Skarels 		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
81944339Skarels 		    sizeof(int));
82044339Skarels #endif
82155258Sandrew 		reply(150, "Opening %s mode data connection for '%s'%s.",
82236235Skarels 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
82326044Sminshall 		return(fdopen(pdata, mode));
82426044Sminshall 	}
82510275Ssam 	if (data >= 0) {
82655258Sandrew 		reply(125, "Using existing data connection for '%s'%s.",
82710275Ssam 		    name, sizebuf);
82810321Ssam 		usedefault = 1;
82910275Ssam 		return (fdopen(data, mode));
83010275Ssam 	}
83110566Ssam 	if (usedefault)
83210422Ssam 		data_dest = his_addr;
83310422Ssam 	usedefault = 1;
83410275Ssam 	file = getdatasock(mode);
83510275Ssam 	if (file == NULL) {
83610275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
83713247Ssam 		    inet_ntoa(data_source.sin_addr),
83842412Sbostic 		    ntohs(data_source.sin_port), strerror(errno));
83910275Ssam 		return (NULL);
84010275Ssam 	}
84110275Ssam 	data = fileno(file);
84236304Skarels 	while (connect(data, (struct sockaddr *)&data_dest,
84336304Skarels 	    sizeof (data_dest)) < 0) {
84411653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
84526493Sminshall 			sleep((unsigned) swaitint);
84611653Ssam 			retry += swaitint;
84711653Ssam 			continue;
84811653Ssam 		}
84936304Skarels 		perror_reply(425, "Can't build data connection");
85010275Ssam 		(void) fclose(file);
85110275Ssam 		data = -1;
85210275Ssam 		return (NULL);
85310275Ssam 	}
85455258Sandrew 	reply(150, "Opening %s mode data connection for '%s'%s.",
85536235Skarels 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
85610275Ssam 	return (file);
85710275Ssam }
85810275Ssam 
85910275Ssam /*
86055258Sandrew  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
86155258Sandrew  * encapsulation of the data subject * to Mode, Structure, and Type.
86210275Ssam  *
86310275Ssam  * NB: Form isn't handled.
86410275Ssam  */
86555258Sandrew static void
86636446Sbostic send_data(instr, outstr, blksize)
86710275Ssam 	FILE *instr, *outstr;
86836446Sbostic 	off_t blksize;
86910275Ssam {
87036446Sbostic 	register int c, cnt;
87136446Sbostic 	register char *buf;
87236446Sbostic 	int netfd, filefd;
87310275Ssam 
87426044Sminshall 	transflag++;
87526044Sminshall 	if (setjmp(urgcatch)) {
87626044Sminshall 		transflag = 0;
87736620Srick 		return;
87826044Sminshall 	}
87910275Ssam 	switch (type) {
88010275Ssam 
88110275Ssam 	case TYPE_A:
88210275Ssam 		while ((c = getc(instr)) != EOF) {
88336933Skarels 			byte_count++;
88411220Ssam 			if (c == '\n') {
88536933Skarels 				if (ferror(outstr))
88636620Srick 					goto data_err;
88727750Sminshall 				(void) putc('\r', outstr);
88811220Ssam 			}
88927750Sminshall 			(void) putc(c, outstr);
89010275Ssam 		}
89136933Skarels 		fflush(outstr);
89226044Sminshall 		transflag = 0;
89336933Skarels 		if (ferror(instr))
89436933Skarels 			goto file_err;
89536933Skarels 		if (ferror(outstr))
89636620Srick 			goto data_err;
89736620Srick 		reply(226, "Transfer complete.");
89836620Srick 		return;
89936446Sbostic 
90010275Ssam 	case TYPE_I:
90110275Ssam 	case TYPE_L:
90236446Sbostic 		if ((buf = malloc((u_int)blksize)) == NULL) {
90336446Sbostic 			transflag = 0;
90436933Skarels 			perror_reply(451, "Local resource failure: malloc");
90536933Skarels 			return;
90636446Sbostic 		}
90710275Ssam 		netfd = fileno(outstr);
90810275Ssam 		filefd = fileno(instr);
90936933Skarels 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
91036620Srick 		    write(netfd, buf, cnt) == cnt)
91136933Skarels 			byte_count += cnt;
91226044Sminshall 		transflag = 0;
91336446Sbostic 		(void)free(buf);
91436933Skarels 		if (cnt != 0) {
91536933Skarels 			if (cnt < 0)
91636933Skarels 				goto file_err;
91736620Srick 			goto data_err;
91836933Skarels 		}
91936620Srick 		reply(226, "Transfer complete.");
92036620Srick 		return;
92136620Srick 	default:
92236620Srick 		transflag = 0;
92336620Srick 		reply(550, "Unimplemented TYPE %d in send_data", type);
92436620Srick 		return;
92510275Ssam 	}
92636620Srick 
92736620Srick data_err:
92826044Sminshall 	transflag = 0;
92936933Skarels 	perror_reply(426, "Data connection");
93036933Skarels 	return;
93136933Skarels 
93236933Skarels file_err:
93336933Skarels 	transflag = 0;
93436933Skarels 	perror_reply(551, "Error on input file");
93510275Ssam }
93610275Ssam 
93710275Ssam /*
93855258Sandrew  * Transfer data from peer to "outstr" using the appropriate encapulation of
93955258Sandrew  * the data subject to Mode, Structure, and Type.
94010275Ssam  *
94110275Ssam  * N.B.: Form isn't handled.
94210275Ssam  */
94355258Sandrew static int
94410275Ssam receive_data(instr, outstr)
94510275Ssam 	FILE *instr, *outstr;
94610275Ssam {
94710275Ssam 	register int c;
94838134Srick 	int cnt, bare_lfs = 0;
94910275Ssam 	char buf[BUFSIZ];
95010275Ssam 
95126044Sminshall 	transflag++;
95226044Sminshall 	if (setjmp(urgcatch)) {
95326044Sminshall 		transflag = 0;
95436933Skarels 		return (-1);
95526044Sminshall 	}
95610275Ssam 	switch (type) {
95710275Ssam 
95810275Ssam 	case TYPE_I:
95910275Ssam 	case TYPE_L:
96026044Sminshall 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
96136620Srick 			if (write(fileno(outstr), buf, cnt) != cnt)
96236933Skarels 				goto file_err;
96336933Skarels 			byte_count += cnt;
96426044Sminshall 		}
96536933Skarels 		if (cnt < 0)
96636620Srick 			goto data_err;
96726044Sminshall 		transflag = 0;
96836933Skarels 		return (0);
96910275Ssam 
97010275Ssam 	case TYPE_E:
97127106Smckusick 		reply(553, "TYPE E not implemented.");
97226044Sminshall 		transflag = 0;
97327106Smckusick 		return (-1);
97410275Ssam 
97510275Ssam 	case TYPE_A:
97610275Ssam 		while ((c = getc(instr)) != EOF) {
97736933Skarels 			byte_count++;
97838134Srick 			if (c == '\n')
97938134Srick 				bare_lfs++;
98027750Sminshall 			while (c == '\r') {
98136933Skarels 				if (ferror(outstr))
98236620Srick 					goto data_err;
98336933Skarels 				if ((c = getc(instr)) != '\n') {
98427750Sminshall 					(void) putc ('\r', outstr);
98536933Skarels 					if (c == '\0' || c == EOF)
98636933Skarels 						goto contin2;
98736933Skarels 				}
98810275Ssam 			}
98936933Skarels 			(void) putc(c, outstr);
99036933Skarels 	contin2:	;
99110275Ssam 		}
99236620Srick 		fflush(outstr);
99336933Skarels 		if (ferror(instr))
99436620Srick 			goto data_err;
99536933Skarels 		if (ferror(outstr))
99636933Skarels 			goto file_err;
99726044Sminshall 		transflag = 0;
99838134Srick 		if (bare_lfs) {
99938134Srick 			lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
100038134Srick 			printf("   File may not have transferred correctly.\r\n");
100138134Srick 		}
100210275Ssam 		return (0);
100336620Srick 	default:
100436620Srick 		reply(550, "Unimplemented TYPE %d in receive_data", type);
100536620Srick 		transflag = 0;
100636933Skarels 		return (-1);
100710275Ssam 	}
100836620Srick 
100936620Srick data_err:
101026044Sminshall 	transflag = 0;
101136933Skarels 	perror_reply(426, "Data Connection");
101236933Skarels 	return (-1);
101336933Skarels 
101436933Skarels file_err:
101536933Skarels 	transflag = 0;
101636933Skarels 	perror_reply(452, "Error writing file");
101736933Skarels 	return (-1);
101810275Ssam }
101910275Ssam 
102055258Sandrew void
102136933Skarels statfilecmd(filename)
102236933Skarels 	char *filename;
102336933Skarels {
102436933Skarels 	char line[BUFSIZ];
102536933Skarels 	FILE *fin;
102636933Skarels 	int c;
102736933Skarels 
102855258Sandrew 	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
102936933Skarels 	fin = ftpd_popen(line, "r");
103036933Skarels 	lreply(211, "status of %s:", filename);
103136933Skarels 	while ((c = getc(fin)) != EOF) {
103236933Skarels 		if (c == '\n') {
103336933Skarels 			if (ferror(stdout)){
103436933Skarels 				perror_reply(421, "control connection");
103536933Skarels 				(void) ftpd_pclose(fin);
103636933Skarels 				dologout(1);
103736933Skarels 				/* NOTREACHED */
103836933Skarels 			}
103936933Skarels 			if (ferror(fin)) {
104036933Skarels 				perror_reply(551, filename);
104136933Skarels 				(void) ftpd_pclose(fin);
104236933Skarels 				return;
104336933Skarels 			}
104436933Skarels 			(void) putc('\r', stdout);
104536933Skarels 		}
104636933Skarels 		(void) putc(c, stdout);
104736933Skarels 	}
104836933Skarels 	(void) ftpd_pclose(fin);
104936933Skarels 	reply(211, "End of Status");
105036933Skarels }
105136933Skarels 
105255258Sandrew void
105336933Skarels statcmd()
105436933Skarels {
105536933Skarels 	struct sockaddr_in *sin;
105636933Skarels 	u_char *a, *p;
105736933Skarels 
105836933Skarels 	lreply(211, "%s FTP server status:", hostname, version);
105936933Skarels 	printf("     %s\r\n", version);
106036933Skarels 	printf("     Connected to %s", remotehost);
106138134Srick 	if (!isdigit(remotehost[0]))
106236933Skarels 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
106336933Skarels 	printf("\r\n");
106436933Skarels 	if (logged_in) {
106536933Skarels 		if (guest)
106636933Skarels 			printf("     Logged in anonymously\r\n");
106736933Skarels 		else
106836933Skarels 			printf("     Logged in as %s\r\n", pw->pw_name);
106936933Skarels 	} else if (askpasswd)
107036933Skarels 		printf("     Waiting for password\r\n");
107136933Skarels 	else
107236933Skarels 		printf("     Waiting for user name\r\n");
107336933Skarels 	printf("     TYPE: %s", typenames[type]);
107436933Skarels 	if (type == TYPE_A || type == TYPE_E)
107536933Skarels 		printf(", FORM: %s", formnames[form]);
107636933Skarels 	if (type == TYPE_L)
107736933Skarels #if NBBY == 8
107836933Skarels 		printf(" %d", NBBY);
107936933Skarels #else
108036933Skarels 		printf(" %d", bytesize);	/* need definition! */
108136933Skarels #endif
108236933Skarels 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
108336933Skarels 	    strunames[stru], modenames[mode]);
108436933Skarels 	if (data != -1)
108536933Skarels 		printf("     Data connection open\r\n");
108636933Skarels 	else if (pdata != -1) {
108736933Skarels 		printf("     in Passive mode");
108836933Skarels 		sin = &pasv_addr;
108936933Skarels 		goto printaddr;
109036933Skarels 	} else if (usedefault == 0) {
109136933Skarels 		printf("     PORT");
109236933Skarels 		sin = &data_dest;
109336933Skarels printaddr:
109436933Skarels 		a = (u_char *) &sin->sin_addr;
109536933Skarels 		p = (u_char *) &sin->sin_port;
109636933Skarels #define UC(b) (((int) b) & 0xff)
109736933Skarels 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
109836933Skarels 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
109936933Skarels #undef UC
110036933Skarels 	} else
110136933Skarels 		printf("     No data connection\r\n");
110236933Skarels 	reply(211, "End of status");
110336933Skarels }
110436933Skarels 
110555258Sandrew void
110610275Ssam fatal(s)
110710275Ssam 	char *s;
110810275Ssam {
110910275Ssam 	reply(451, "Error in server: %s\n", s);
111010275Ssam 	reply(221, "Closing connection due to server error.");
111113247Ssam 	dologout(0);
111236620Srick 	/* NOTREACHED */
111310275Ssam }
111410275Ssam 
111555258Sandrew void
111655258Sandrew #if __STDC__
111755258Sandrew reply(int n, const char *fmt, ...)
111855258Sandrew #else
111955258Sandrew reply(n, fmt, va_alist)
112010275Ssam 	int n;
112136446Sbostic 	char *fmt;
112255258Sandrew         va_dcl
112355258Sandrew #endif
112410275Ssam {
112555258Sandrew 	va_list ap;
112655258Sandrew #if __STDC__
112755258Sandrew 	va_start(ap, fmt);
112855258Sandrew #else
112955258Sandrew 	va_start(ap);
113055258Sandrew #endif
113155258Sandrew 	(void)printf("%d ", n);
113255258Sandrew 	(void)vprintf(fmt, ap);
113355258Sandrew 	(void)printf("\r\n");
113436435Sbostic 	(void)fflush(stdout);
113510275Ssam 	if (debug) {
113626493Sminshall 		syslog(LOG_DEBUG, "<--- %d ", n);
113755258Sandrew 		vsyslog(LOG_DEBUG, fmt, ap);
113855258Sandrew 	}
113910275Ssam }
114010275Ssam 
114155258Sandrew void
114255258Sandrew #if __STDC__
114355258Sandrew lreply(int n, const char *fmt, ...)
114455258Sandrew #else
114555258Sandrew lreply(n, fmt, va_alist)
114610275Ssam 	int n;
114736446Sbostic 	char *fmt;
114855258Sandrew         va_dcl
114955258Sandrew #endif
115010275Ssam {
115155258Sandrew 	va_list ap;
115255258Sandrew #if __STDC__
115355258Sandrew 	va_start(ap, fmt);
115455258Sandrew #else
115555258Sandrew 	va_start(ap);
115655258Sandrew #endif
115755258Sandrew 	(void)printf("%d- ", n);
115855258Sandrew 	(void)vprintf(fmt, ap);
115955258Sandrew 	(void)printf("\r\n");
116036435Sbostic 	(void)fflush(stdout);
116136446Sbostic 	if (debug) {
116236446Sbostic 		syslog(LOG_DEBUG, "<--- %d- ", n);
116355258Sandrew 		vsyslog(LOG_DEBUG, fmt, ap);
116436446Sbostic 	}
116510275Ssam }
116610275Ssam 
116755258Sandrew static void
116810275Ssam ack(s)
116910275Ssam 	char *s;
117010275Ssam {
117127106Smckusick 	reply(250, "%s command successful.", s);
117210275Ssam }
117310275Ssam 
117455258Sandrew void
117510275Ssam nack(s)
117610275Ssam 	char *s;
117710275Ssam {
117810275Ssam 	reply(502, "%s command not implemented.", s);
117910275Ssam }
118010275Ssam 
118136304Skarels /* ARGSUSED */
118255258Sandrew char *
118326493Sminshall yyerror(s)
118426493Sminshall 	char *s;
118510275Ssam {
118626044Sminshall 	char *cp;
118726044Sminshall 
118860087Sbostic 	if (cp = strchr(cbuf,'\n'))
118936551Sbostic 		*cp = '\0';
119036933Skarels 	reply(500, "'%s': command not understood.", cbuf);
119110275Ssam }
119210275Ssam 
119355258Sandrew void
119410275Ssam delete(name)
119510275Ssam 	char *name;
119610275Ssam {
119710275Ssam 	struct stat st;
119810275Ssam 
119955258Sandrew 	LOGCMD("delete", name);
120010275Ssam 	if (stat(name, &st) < 0) {
120136304Skarels 		perror_reply(550, name);
120210275Ssam 		return;
120310275Ssam 	}
120410275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
120510275Ssam 		if (rmdir(name) < 0) {
120636304Skarels 			perror_reply(550, name);
120710275Ssam 			return;
120810275Ssam 		}
120910275Ssam 		goto done;
121010275Ssam 	}
121110275Ssam 	if (unlink(name) < 0) {
121236304Skarels 		perror_reply(550, name);
121310275Ssam 		return;
121410275Ssam 	}
121510275Ssam done:
121610275Ssam 	ack("DELE");
121710275Ssam }
121810275Ssam 
121955258Sandrew void
122010275Ssam cwd(path)
122110275Ssam 	char *path;
122210275Ssam {
122336620Srick 	if (chdir(path) < 0)
122436304Skarels 		perror_reply(550, path);
122536620Srick 	else
122636620Srick 		ack("CWD");
122710275Ssam }
122810275Ssam 
122955258Sandrew void
123010303Ssam makedir(name)
123110275Ssam 	char *name;
123210275Ssam {
123355258Sandrew 	LOGCMD("mkdir", name);
123436276Sbostic 	if (mkdir(name, 0777) < 0)
123536304Skarels 		perror_reply(550, name);
123636276Sbostic 	else
123736276Sbostic 		reply(257, "MKD command successful.");
123810275Ssam }
123910275Ssam 
124055258Sandrew void
124110303Ssam removedir(name)
124210275Ssam 	char *name;
124310275Ssam {
124455258Sandrew 	LOGCMD("rmdir", name);
124536620Srick 	if (rmdir(name) < 0)
124636304Skarels 		perror_reply(550, name);
124736620Srick 	else
124836620Srick 		ack("RMD");
124910275Ssam }
125010275Ssam 
125155258Sandrew void
125210303Ssam pwd()
125310275Ssam {
125410303Ssam 	char path[MAXPATHLEN + 1];
125510275Ssam 
125636620Srick 	if (getwd(path) == (char *)NULL)
125727106Smckusick 		reply(550, "%s.", path);
125836620Srick 	else
125936620Srick 		reply(257, "\"%s\" is current directory.", path);
126010275Ssam }
126110275Ssam 
126210275Ssam char *
126310275Ssam renamefrom(name)
126410275Ssam 	char *name;
126510275Ssam {
126610275Ssam 	struct stat st;
126710275Ssam 
126810275Ssam 	if (stat(name, &st) < 0) {
126936304Skarels 		perror_reply(550, name);
127010275Ssam 		return ((char *)0);
127110275Ssam 	}
127210303Ssam 	reply(350, "File exists, ready for destination name");
127310275Ssam 	return (name);
127410275Ssam }
127510275Ssam 
127655258Sandrew void
127710275Ssam renamecmd(from, to)
127810275Ssam 	char *from, *to;
127910275Ssam {
128055258Sandrew 	LOGCMD2("rename", from, to);
128136620Srick 	if (rename(from, to) < 0)
128236304Skarels 		perror_reply(550, "rename");
128336620Srick 	else
128436620Srick 		ack("RNTO");
128510275Ssam }
128610275Ssam 
128755258Sandrew static void
128810275Ssam dolog(sin)
128910275Ssam 	struct sockaddr_in *sin;
129010275Ssam {
129136304Skarels 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
129210275Ssam 		sizeof (struct in_addr), AF_INET);
129310275Ssam 
129436304Skarels 	if (hp)
129526493Sminshall 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
129636304Skarels 	else
129726493Sminshall 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
129813247Ssam 		    sizeof (remotehost));
129936620Srick #ifdef SETPROCTITLE
130036933Skarels 	sprintf(proctitle, "%s: connected", remotehost);
130136933Skarels 	setproctitle(proctitle);
130236620Srick #endif /* SETPROCTITLE */
130336933Skarels 
130452998Sbostic 	if (logging)
130552998Sbostic 		syslog(LOG_INFO, "connection from %s", remotehost);
130610275Ssam }
130710695Ssam 
130810695Ssam /*
130913247Ssam  * Record logout in wtmp file
131013247Ssam  * and exit with supplied status.
131113247Ssam  */
131255258Sandrew void
131313247Ssam dologout(status)
131413247Ssam 	int status;
131513247Ssam {
131617580Ssam 	if (logged_in) {
131736304Skarels 		(void) seteuid((uid_t)0);
131835672Sbostic 		logwtmp(ttyline, "", "");
131913247Ssam 	}
132014436Ssam 	/* beware of flushing buffers after a SIGPIPE */
132114436Ssam 	_exit(status);
132213247Ssam }
132313247Ssam 
132455258Sandrew static void
132555258Sandrew myoob(signo)
132655258Sandrew 	int signo;
132726044Sminshall {
132827750Sminshall 	char *cp;
132926044Sminshall 
133027750Sminshall 	/* only process if transfer occurring */
133136304Skarels 	if (!transflag)
133226044Sminshall 		return;
133327750Sminshall 	cp = tmpline;
133427750Sminshall 	if (getline(cp, 7, stdin) == NULL) {
133536304Skarels 		reply(221, "You could at least say goodbye.");
133627750Sminshall 		dologout(0);
133726044Sminshall 	}
133826044Sminshall 	upper(cp);
133936933Skarels 	if (strcmp(cp, "ABOR\r\n") == 0) {
134036933Skarels 		tmpline[0] = '\0';
134136933Skarels 		reply(426, "Transfer aborted. Data connection closed.");
134236933Skarels 		reply(226, "Abort successful");
134336933Skarels 		longjmp(urgcatch, 1);
134436933Skarels 	}
134536933Skarels 	if (strcmp(cp, "STAT\r\n") == 0) {
134636933Skarels 		if (file_size != (off_t) -1)
134755258Sandrew 			reply(213, "Status: %qd of %qd bytes transferred",
134836933Skarels 			    byte_count, file_size);
134936933Skarels 		else
135055258Sandrew 			reply(213, "Status: %qd bytes transferred", byte_count);
135136933Skarels 	}
135226044Sminshall }
135326044Sminshall 
135427106Smckusick /*
135536620Srick  * Note: a response of 425 is not mentioned as a possible response to
135652998Sbostic  *	the PASV command in RFC959. However, it has been blessed as
135752998Sbostic  *	a legitimate response by Jon Postel in a telephone conversation
135836620Srick  *	with Rick Adams on 25 Jan 89.
135927106Smckusick  */
136055258Sandrew void
136126044Sminshall passive()
136226044Sminshall {
136326044Sminshall 	int len;
136426044Sminshall 	register char *p, *a;
136526044Sminshall 
136626044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
136726044Sminshall 	if (pdata < 0) {
136836620Srick 		perror_reply(425, "Can't open passive connection");
136926044Sminshall 		return;
137026044Sminshall 	}
137136933Skarels 	pasv_addr = ctrl_addr;
137236933Skarels 	pasv_addr.sin_port = 0;
137336304Skarels 	(void) seteuid((uid_t)0);
137436933Skarels 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
137536304Skarels 		(void) seteuid((uid_t)pw->pw_uid);
137636620Srick 		goto pasv_error;
137726044Sminshall 	}
137836304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
137936933Skarels 	len = sizeof(pasv_addr);
138036933Skarels 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
138136620Srick 		goto pasv_error;
138236620Srick 	if (listen(pdata, 1) < 0)
138336620Srick 		goto pasv_error;
138436933Skarels 	a = (char *) &pasv_addr.sin_addr;
138536933Skarels 	p = (char *) &pasv_addr.sin_port;
138626044Sminshall 
138726044Sminshall #define UC(b) (((int) b) & 0xff)
138826044Sminshall 
138926044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
139026044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
139136620Srick 	return;
139236620Srick 
139336620Srick pasv_error:
139436620Srick 	(void) close(pdata);
139536620Srick 	pdata = -1;
139636620Srick 	perror_reply(425, "Can't open passive connection");
139736620Srick 	return;
139826044Sminshall }
139926044Sminshall 
140036304Skarels /*
140136304Skarels  * Generate unique name for file with basename "local".
140236304Skarels  * The file named "local" is already known to exist.
140336304Skarels  * Generates failure reply on error.
140436304Skarels  */
140555258Sandrew static char *
140626044Sminshall gunique(local)
140726044Sminshall 	char *local;
140826044Sminshall {
140926044Sminshall 	static char new[MAXPATHLEN];
141036304Skarels 	struct stat st;
141155258Sandrew 	int count;
141255258Sandrew 	char *cp;
141326044Sminshall 
141460087Sbostic 	cp = strrchr(local, '/');
141536304Skarels 	if (cp)
141626044Sminshall 		*cp = '\0';
141736620Srick 	if (stat(cp ? local : ".", &st) < 0) {
141836933Skarels 		perror_reply(553, cp ? local : ".");
141926044Sminshall 		return((char *) 0);
142026044Sminshall 	}
142136620Srick 	if (cp)
142236620Srick 		*cp = '/';
142326044Sminshall 	(void) strcpy(new, local);
142426044Sminshall 	cp = new + strlen(new);
142526044Sminshall 	*cp++ = '.';
142636304Skarels 	for (count = 1; count < 100; count++) {
142755258Sandrew 		(void)sprintf(cp, "%d", count);
142836304Skarels 		if (stat(new, &st) < 0)
142936304Skarels 			return(new);
143026044Sminshall 	}
143136304Skarels 	reply(452, "Unique file name cannot be created.");
143255258Sandrew 	return(NULL);
143326044Sminshall }
143436304Skarels 
143536304Skarels /*
143636304Skarels  * Format and send reply containing system error number.
143736304Skarels  */
143855258Sandrew void
143936304Skarels perror_reply(code, string)
144036304Skarels 	int code;
144136304Skarels 	char *string;
144236304Skarels {
144342412Sbostic 	reply(code, "%s: %s.", string, strerror(errno));
144436304Skarels }
144536620Srick 
144636620Srick static char *onefile[] = {
144736620Srick 	"",
144836620Srick 	0
144936620Srick };
145036620Srick 
145155258Sandrew void
145236620Srick send_file_list(whichfiles)
145336620Srick 	char *whichfiles;
145436620Srick {
145536620Srick 	struct stat st;
145636620Srick 	DIR *dirp = NULL;
145746669Sbostic 	struct dirent *dir;
145836620Srick 	FILE *dout = NULL;
145936620Srick 	register char **dirlist, *dirname;
146037459Skarels 	int simple = 0;
146136620Srick 
146236620Srick 	if (strpbrk(whichfiles, "~{[*?") != NULL) {
146355258Sandrew 		extern char *globerr;
146436933Skarels 
146536620Srick 		globerr = NULL;
146646669Sbostic 		dirlist = ftpglob(whichfiles);
146736620Srick 		if (globerr != NULL) {
146836620Srick 			reply(550, globerr);
146936620Srick 			return;
147036620Srick 		} else if (dirlist == NULL) {
147136620Srick 			errno = ENOENT;
147236620Srick 			perror_reply(550, whichfiles);
147336620Srick 			return;
147436620Srick 		}
147536620Srick 	} else {
147636620Srick 		onefile[0] = whichfiles;
147736620Srick 		dirlist = onefile;
147837459Skarels 		simple = 1;
147936620Srick 	}
148036933Skarels 
148136933Skarels 	if (setjmp(urgcatch)) {
148236933Skarels 		transflag = 0;
148336933Skarels 		return;
148436933Skarels 	}
148536620Srick 	while (dirname = *dirlist++) {
148636620Srick 		if (stat(dirname, &st) < 0) {
148736933Skarels 			/*
148836933Skarels 			 * If user typed "ls -l", etc, and the client
148936933Skarels 			 * used NLST, do what the user meant.
149036933Skarels 			 */
149136933Skarels 			if (dirname[0] == '-' && *dirlist == NULL &&
149236933Skarels 			    transflag == 0) {
149336933Skarels 				retrieve("/bin/ls %s", dirname);
149436933Skarels 				return;
149536933Skarels 			}
149636620Srick 			perror_reply(550, whichfiles);
149736620Srick 			if (dout != NULL) {
149836620Srick 				(void) fclose(dout);
149936933Skarels 				transflag = 0;
150036620Srick 				data = -1;
150136620Srick 				pdata = -1;
150236620Srick 			}
150336620Srick 			return;
150436620Srick 		}
150536933Skarels 
150636620Srick 		if ((st.st_mode&S_IFMT) == S_IFREG) {
150736620Srick 			if (dout == NULL) {
150837459Skarels 				dout = dataconn("file list", (off_t)-1, "w");
150936620Srick 				if (dout == NULL)
151036620Srick 					return;
151136933Skarels 				transflag++;
151236620Srick 			}
151338158Srick 			fprintf(dout, "%s%s\n", dirname,
151438158Srick 				type == TYPE_A ? "\r" : "");
151536933Skarels 			byte_count += strlen(dirname) + 1;
151636620Srick 			continue;
151736933Skarels 		} else if ((st.st_mode&S_IFMT) != S_IFDIR)
151836620Srick 			continue;
151936620Srick 
152036620Srick 		if ((dirp = opendir(dirname)) == NULL)
152136620Srick 			continue;
152236620Srick 
152336620Srick 		while ((dir = readdir(dirp)) != NULL) {
152436933Skarels 			char nbuf[MAXPATHLEN];
152536620Srick 
152636620Srick 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
152736620Srick 				continue;
152836933Skarels 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
152936933Skarels 			    dir->d_namlen == 2)
153036620Srick 				continue;
153136620Srick 
153236933Skarels 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
153336933Skarels 
153436620Srick 			/*
153536933Skarels 			 * We have to do a stat to insure it's
153636933Skarels 			 * not a directory or special file.
153736620Srick 			 */
153837459Skarels 			if (simple || (stat(nbuf, &st) == 0 &&
153937459Skarels 			    (st.st_mode&S_IFMT) == S_IFREG)) {
154036620Srick 				if (dout == NULL) {
154137459Skarels 					dout = dataconn("file list", (off_t)-1,
154236620Srick 						"w");
154336620Srick 					if (dout == NULL)
154436620Srick 						return;
154536933Skarels 					transflag++;
154636620Srick 				}
154736620Srick 				if (nbuf[0] == '.' && nbuf[1] == '/')
154838158Srick 					fprintf(dout, "%s%s\n", &nbuf[2],
154938158Srick 						type == TYPE_A ? "\r" : "");
155036620Srick 				else
155138158Srick 					fprintf(dout, "%s%s\n", nbuf,
155238158Srick 						type == TYPE_A ? "\r" : "");
155336933Skarels 				byte_count += strlen(nbuf) + 1;
155436620Srick 			}
155536620Srick 		}
155636620Srick 		(void) closedir(dirp);
155736620Srick 	}
155836620Srick 
155936933Skarels 	if (dout == NULL)
156036933Skarels 		reply(550, "No files found.");
156136933Skarels 	else if (ferror(dout) != 0)
156236933Skarels 		perror_reply(550, "Data connection");
156336933Skarels 	else
156436620Srick 		reply(226, "Transfer complete.");
156536620Srick 
156636933Skarels 	transflag = 0;
156736933Skarels 	if (dout != NULL)
156836620Srick 		(void) fclose(dout);
156936620Srick 	data = -1;
157036620Srick 	pdata = -1;
157136620Srick }
157236620Srick 
157336620Srick #ifdef SETPROCTITLE
157436620Srick /*
157555258Sandrew  * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
157655258Sandrew  * Warning, since this is usually started from inetd.conf, it often doesn't
157755258Sandrew  * have much of an environment or arglist to overwrite.
157836620Srick  */
157955258Sandrew void
158055258Sandrew #if __STDC__
158155258Sandrew setproctitle(const char *fmt, ...)
158255258Sandrew #else
158355258Sandrew setproctitle(fmt, va_alist)
158455258Sandrew 	char *fmt;
158555258Sandrew         va_dcl
158655258Sandrew #endif
158736620Srick {
158836620Srick 	register char *p, *bp, ch;
158936620Srick 	register int i;
159055258Sandrew 	va_list ap;
159136620Srick 	char buf[BUFSIZ];
159255258Sandrew #if __STDC__
159355258Sandrew 	va_start(ap, fmt);
159455258Sandrew #else
159555258Sandrew 	va_start(ap);
159655258Sandrew #endif
159755258Sandrew 	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
159836620Srick 
159936620Srick 	/* make ps print our process name */
160036620Srick 	p = Argv[0];
160136620Srick 	*p++ = '-';
160236620Srick 
160336620Srick 	i = strlen(buf);
160436620Srick 	if (i > LastArgv - p - 2) {
160536620Srick 		i = LastArgv - p - 2;
160636620Srick 		buf[i] = '\0';
160736620Srick 	}
160836620Srick 	bp = buf;
160936620Srick 	while (ch = *bp++)
161036620Srick 		if (ch != '\n' && ch != '\r')
161136620Srick 			*p++ = ch;
161236620Srick 	while (p < LastArgv)
161336620Srick 		*p++ = ' ';
161436620Srick }
161536620Srick #endif /* SETPROCTITLE */
1616