xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 68976)
122499Sdist /*
266711Spendry  * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
361427Sbostic  *	The Regents of the University of California.  All rights reserved.
433738Sbostic  *
542666Sbostic  * %sccs.include.redist.c%
622499Sdist  */
722499Sdist 
810275Ssam #ifndef lint
961427Sbostic static char copyright[] =
1066711Spendry "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
1161427Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1233738Sbostic #endif /* not lint */
1310275Ssam 
1422499Sdist #ifndef lint
15*68976Sbostic static char sccsid[] = "@(#)ftpd.c	8.5 (Berkeley) 04/28/95";
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 
3666711Spendry #include <ctype.h>
3746669Sbostic #include <dirent.h>
3866711Spendry #include <err.h>
3966711Spendry #include <errno.h>
4046669Sbostic #include <fcntl.h>
4166711Spendry #include <glob.h>
4266711Spendry #include <limits.h>
4366711Spendry #include <netdb.h>
4410275Ssam #include <pwd.h>
4510275Ssam #include <setjmp.h>
4666711Spendry #include <signal.h>
4746669Sbostic #include <stdio.h>
4846669Sbostic #include <stdlib.h>
4946669Sbostic #include <string.h>
5066711Spendry #include <syslog.h>
5166711Spendry #include <time.h>
5266711Spendry #include <unistd.h>
5366711Spendry 
5437459Skarels #include "pathnames.h"
5555258Sandrew #include "extern.h"
5610275Ssam 
5755258Sandrew #if __STDC__
5855258Sandrew #include <stdarg.h>
5955258Sandrew #else
6055258Sandrew #include <varargs.h>
6155258Sandrew #endif
6255258Sandrew 
6366711Spendry static char version[] = "Version 6.00";
6466711Spendry 
6555258Sandrew extern	off_t restart_point;
6626044Sminshall extern	char cbuf[];
6710275Ssam 
6810275Ssam struct	sockaddr_in ctrl_addr;
6910275Ssam struct	sockaddr_in data_source;
7010275Ssam struct	sockaddr_in data_dest;
7110275Ssam struct	sockaddr_in his_addr;
7236933Skarels struct	sockaddr_in pasv_addr;
7310275Ssam 
7410275Ssam int	data;
7526044Sminshall jmp_buf	errcatch, urgcatch;
7610275Ssam int	logged_in;
7710275Ssam struct	passwd *pw;
7810275Ssam int	debug;
7926493Sminshall int	timeout = 900;    /* timeout after 15 minutes of inactivity */
8036933Skarels int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
8111757Ssam int	logging;
8210275Ssam int	guest;
8310275Ssam int	type;
8410275Ssam int	form;
8510275Ssam int	stru;			/* avoid C keyword */
8610275Ssam int	mode;
8710321Ssam int	usedefault = 1;		/* for data transfers */
8836304Skarels int	pdata = -1;		/* for passive mode */
8966711Spendry sig_atomic_t transflag;
9036933Skarels off_t	file_size;
9136933Skarels off_t	byte_count;
9236933Skarels #if !defined(CMASK) || CMASK == 0
9336933Skarels #undef CMASK
9436933Skarels #define CMASK 027
9536933Skarels #endif
9636933Skarels int	defumask = CMASK;		/* default umask value */
9726044Sminshall char	tmpline[7];
9836276Sbostic char	hostname[MAXHOSTNAMELEN];
9936276Sbostic char	remotehost[MAXHOSTNAMELEN];
10010275Ssam 
10111653Ssam /*
10211653Ssam  * Timeout intervals for retrying connections
10311653Ssam  * to hosts that don't accept PORT cmds.  This
10411653Ssam  * is a kludge, but given the problems with TCP...
10511653Ssam  */
10611653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
10711653Ssam #define	SWAITINT	5	/* interval between retries */
10811653Ssam 
10911653Ssam int	swaitmax = SWAITMAX;
11011653Ssam int	swaitint = SWAITINT;
11111653Ssam 
11236620Srick #ifdef SETPROCTITLE
11336620Srick char	**Argv = NULL;		/* pointer to argument vector */
11436620Srick char	*LastArgv = NULL;	/* end of argv */
11566711Spendry char	proctitle[LINE_MAX];	/* initial part of title */
11636620Srick #endif /* SETPROCTITLE */
11736620Srick 
11855258Sandrew #define LOGCMD(cmd, file) \
11955258Sandrew 	if (logging > 1) \
12055258Sandrew 	    syslog(LOG_INFO,"%s %s%s", cmd, \
12155258Sandrew 		*(file) == '/' ? "" : curdir(), file);
12255258Sandrew #define LOGCMD2(cmd, file1, file2) \
12355258Sandrew 	 if (logging > 1) \
12455258Sandrew 	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
12555258Sandrew 		*(file1) == '/' ? "" : curdir(), file1, \
12655258Sandrew 		*(file2) == '/' ? "" : curdir(), file2);
12755258Sandrew #define LOGBYTES(cmd, file, cnt) \
12855258Sandrew 	if (logging > 1) { \
12955258Sandrew 		if (cnt == (off_t)-1) \
13055258Sandrew 		    syslog(LOG_INFO,"%s %s%s", cmd, \
13155258Sandrew 			*(file) == '/' ? "" : curdir(), file); \
13255258Sandrew 		else \
13355258Sandrew 		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
13455258Sandrew 			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
13555258Sandrew 	}
13655258Sandrew 
13755258Sandrew static void	 ack __P((char *));
13855258Sandrew static void	 myoob __P((int));
13955258Sandrew static int	 checkuser __P((char *));
14055258Sandrew static FILE	*dataconn __P((char *, off_t, char *));
14155258Sandrew static void	 dolog __P((struct sockaddr_in *));
14255258Sandrew static char	*curdir __P((void));
14355258Sandrew static void	 end_login __P((void));
14455258Sandrew static FILE	*getdatasock __P((char *));
14555258Sandrew static char	*gunique __P((char *));
14655258Sandrew static void	 lostconn __P((int));
14755258Sandrew static int	 receive_data __P((FILE *, FILE *));
14855258Sandrew static void	 send_data __P((FILE *, FILE *, off_t));
14955258Sandrew static struct passwd *
15055258Sandrew 		 sgetpwnam __P((char *));
15155258Sandrew static char	*sgetsave __P((char *));
15255258Sandrew 
15355258Sandrew static char *
curdir()15455258Sandrew curdir()
15555258Sandrew {
15655258Sandrew 	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
15755258Sandrew 
15855258Sandrew 	if (getcwd(path, sizeof(path)-2) == NULL)
15955258Sandrew 		return ("");
16055258Sandrew 	if (path[1] != '\0')		/* special case for root dir. */
16155258Sandrew 		strcat(path, "/");
16255258Sandrew 	/* For guest account, skip / since it's chrooted */
16355258Sandrew 	return (guest ? path+1 : path);
16455258Sandrew }
16555258Sandrew 
16655258Sandrew int
main(argc,argv,envp)16736620Srick main(argc, argv, envp)
16810275Ssam 	int argc;
16910275Ssam 	char *argv[];
17036620Srick 	char **envp;
17110275Ssam {
17266711Spendry 	int addrlen, ch, on = 1, tos;
17366711Spendry 	char *cp, line[LINE_MAX];
17455258Sandrew 	FILE *fd;
17510275Ssam 
17645028Skarels 	/*
17745028Skarels 	 * LOG_NDELAY sets up the logging connection immediately,
17845028Skarels 	 * necessary for anonymous ftp's that chroot and can't do it later.
17945028Skarels 	 */
18055258Sandrew 	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
18166711Spendry 	addrlen = sizeof(his_addr);
18236304Skarels 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
18326493Sminshall 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
18410275Ssam 		exit(1);
18510275Ssam 	}
18666711Spendry 	addrlen = sizeof(ctrl_addr);
18736304Skarels 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
18826493Sminshall 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
18916339Skarels 		exit(1);
19016339Skarels 	}
19144339Skarels #ifdef IP_TOS
19244339Skarels 	tos = IPTOS_LOWDELAY;
19344339Skarels 	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
19444339Skarels 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
19544339Skarels #endif
19616339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
19710275Ssam 	debug = 0;
19836620Srick #ifdef SETPROCTITLE
19936620Srick 	/*
20036620Srick 	 *  Save start and extent of argv for setproctitle.
20136620Srick 	 */
20236620Srick 	Argv = argv;
20336620Srick 	while (*envp)
20436620Srick 		envp++;
20536620Srick 	LastArgv = envp[-1] + strlen(envp[-1]);
20636620Srick #endif /* SETPROCTITLE */
20736620Srick 
20866711Spendry 	while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) {
20966711Spendry 		switch (ch) {
21010275Ssam 		case 'd':
21110275Ssam 			debug = 1;
21210275Ssam 			break;
21310275Ssam 
21411757Ssam 		case 'l':
21555258Sandrew 			logging++;	/* > 1 == extra logging */
21611757Ssam 			break;
21711757Ssam 
21811653Ssam 		case 't':
21966711Spendry 			timeout = atoi(optarg);
22036933Skarels 			if (maxtimeout < timeout)
22136933Skarels 				maxtimeout = timeout;
22266711Spendry 			break;
22311653Ssam 
22436933Skarels 		case 'T':
22566711Spendry 			maxtimeout = atoi(optarg);
22636933Skarels 			if (timeout > maxtimeout)
22736933Skarels 				timeout = maxtimeout;
22866711Spendry 			break;
22936933Skarels 
23036933Skarels 		case 'u':
23136933Skarels 		    {
23266711Spendry 			long val = 0;
23336933Skarels 
23466711Spendry 			val = strtol(optarg, &optarg, 8);
23566711Spendry 			if (*optarg != '\0' || val < 0)
23666711Spendry 				warnx("bad value for -u");
23736933Skarels 			else
23836933Skarels 				defumask = val;
23966711Spendry 			break;
24036933Skarels 		    }
24136933Skarels 
24266711Spendry 		case 'v':
24366711Spendry 			debug = 1;
24466711Spendry 			break;
24566711Spendry 
24610275Ssam 		default:
24766711Spendry 			warnx("unknown flag -%c ignored", optopt);
24810275Ssam 			break;
24910275Ssam 		}
25010275Ssam 	}
25137459Skarels 	(void) freopen(_PATH_DEVNULL, "w", stderr);
25226493Sminshall 	(void) signal(SIGPIPE, lostconn);
25326493Sminshall 	(void) signal(SIGCHLD, SIG_IGN);
25435691Sbostic 	if ((int)signal(SIGURG, myoob) < 0)
25526493Sminshall 		syslog(LOG_ERR, "signal: %m");
25635691Sbostic 
25738134Srick 	/* Try to handle urgent data inline */
25827750Sminshall #ifdef SO_OOBINLINE
25936276Sbostic 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
26027750Sminshall 		syslog(LOG_ERR, "setsockopt: %m");
26136276Sbostic #endif
26238134Srick 
26336933Skarels #ifdef	F_SETOWN
26436304Skarels 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
26536304Skarels 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
26636933Skarels #endif
26716760Slepreau 	dolog(&his_addr);
26816339Skarels 	/*
26916339Skarels 	 * Set up default state
27016339Skarels 	 */
27116339Skarels 	data = -1;
27216339Skarels 	type = TYPE_A;
27316339Skarels 	form = FORM_N;
27416339Skarels 	stru = STRU_F;
27516339Skarels 	mode = MODE_S;
27626044Sminshall 	tmpline[0] = '\0';
27755258Sandrew 
27855258Sandrew 	/* If logins are disabled, print out the message. */
27955258Sandrew 	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
28066711Spendry 		while (fgets(line, sizeof(line), fd) != NULL) {
28160087Sbostic 			if ((cp = strchr(line, '\n')) != NULL)
28255258Sandrew 				*cp = '\0';
28355258Sandrew 			lreply(530, "%s", line);
28455258Sandrew 		}
28555258Sandrew 		(void) fflush(stdout);
28655258Sandrew 		(void) fclose(fd);
28755258Sandrew 		reply(530, "System not available.");
28855258Sandrew 		exit(0);
28955258Sandrew 	}
29055258Sandrew 	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
29166711Spendry 		while (fgets(line, sizeof(line), fd) != NULL) {
29260087Sbostic 			if ((cp = strchr(line, '\n')) != NULL)
29355258Sandrew 				*cp = '\0';
29455258Sandrew 			lreply(220, "%s", line);
29555258Sandrew 		}
29655258Sandrew 		(void) fflush(stdout);
29755258Sandrew 		(void) fclose(fd);
29855258Sandrew 		/* reply(220,) must follow */
29955258Sandrew 	}
30066711Spendry 	(void) gethostname(hostname, sizeof(hostname));
30136276Sbostic 	reply(220, "%s FTP server (%s) ready.", hostname, version);
30236304Skarels 	(void) setjmp(errcatch);
30336304Skarels 	for (;;)
30426493Sminshall 		(void) yyparse();
30536620Srick 	/* NOTREACHED */
30610275Ssam }
30710419Ssam 
30855258Sandrew static void
lostconn(signo)30955258Sandrew lostconn(signo)
31055258Sandrew 	int signo;
31110275Ssam {
31266711Spendry 
31314089Ssam 	if (debug)
31426493Sminshall 		syslog(LOG_DEBUG, "lost connection");
31514089Ssam 	dologout(-1);
31610275Ssam }
31710275Ssam 
31835672Sbostic static char ttyline[20];
31935672Sbostic 
32036185Sbostic /*
32136185Sbostic  * Helper function for sgetpwnam().
32236185Sbostic  */
32355258Sandrew static char *
sgetsave(s)32436185Sbostic sgetsave(s)
32536185Sbostic 	char *s;
32636185Sbostic {
32736185Sbostic 	char *new = malloc((unsigned) strlen(s) + 1);
32836933Skarels 
32936185Sbostic 	if (new == NULL) {
33036620Srick 		perror_reply(421, "Local resource failure: malloc");
33136185Sbostic 		dologout(1);
33236620Srick 		/* NOTREACHED */
33336185Sbostic 	}
33436185Sbostic 	(void) strcpy(new, s);
33536185Sbostic 	return (new);
33636185Sbostic }
33736185Sbostic 
33836185Sbostic /*
33936185Sbostic  * Save the result of a getpwnam.  Used for USER command, since
34036185Sbostic  * the data returned must not be clobbered by any other command
34136185Sbostic  * (e.g., globbing).
34236185Sbostic  */
34355258Sandrew static struct passwd *
sgetpwnam(name)34436185Sbostic sgetpwnam(name)
34536185Sbostic 	char *name;
34636185Sbostic {
34736185Sbostic 	static struct passwd save;
34866711Spendry 	struct passwd *p;
34936185Sbostic 
35036185Sbostic 	if ((p = getpwnam(name)) == NULL)
35136185Sbostic 		return (p);
35236185Sbostic 	if (save.pw_name) {
35336185Sbostic 		free(save.pw_name);
35436185Sbostic 		free(save.pw_passwd);
35536185Sbostic 		free(save.pw_gecos);
35636185Sbostic 		free(save.pw_dir);
35736185Sbostic 		free(save.pw_shell);
35836185Sbostic 	}
35936185Sbostic 	save = *p;
36036185Sbostic 	save.pw_name = sgetsave(p->pw_name);
36136185Sbostic 	save.pw_passwd = sgetsave(p->pw_passwd);
36236185Sbostic 	save.pw_gecos = sgetsave(p->pw_gecos);
36336185Sbostic 	save.pw_dir = sgetsave(p->pw_dir);
36436185Sbostic 	save.pw_shell = sgetsave(p->pw_shell);
36536185Sbostic 	return (&save);
36636185Sbostic }
36736185Sbostic 
36855258Sandrew static int login_attempts;	/* number of failed login attempts */
36955258Sandrew static int askpasswd;		/* had user command, ask for passwd */
37055258Sandrew static char curname[10];	/* current USER name */
37136304Skarels 
37236304Skarels /*
37336304Skarels  * USER command.
37442327Sbostic  * Sets global passwd pointer pw if named account exists and is acceptable;
37542327Sbostic  * sets askpasswd if a PASS command is expected.  If logged in previously,
37642327Sbostic  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
37742327Sbostic  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
37842327Sbostic  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
37942327Sbostic  * requesting login privileges.  Disallow anyone who does not have a standard
38042327Sbostic  * shell as returned by getusershell().  Disallow anyone mentioned in the file
38142327Sbostic  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
38236304Skarels  */
38355258Sandrew void
user(name)38436304Skarels user(name)
38536304Skarels 	char *name;
38636304Skarels {
38766711Spendry 	char *cp, *shell;
38836304Skarels 
38936304Skarels 	if (logged_in) {
39036304Skarels 		if (guest) {
39136304Skarels 			reply(530, "Can't change user from guest login.");
39236304Skarels 			return;
39336304Skarels 		}
39436304Skarels 		end_login();
39536304Skarels 	}
39636304Skarels 
39736304Skarels 	guest = 0;
39836304Skarels 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
39940183Smckusick 		if (checkuser("ftp") || checkuser("anonymous"))
40040155Smckusick 			reply(530, "User %s access denied.", name);
40140155Smckusick 		else if ((pw = sgetpwnam("ftp")) != NULL) {
40236304Skarels 			guest = 1;
40336304Skarels 			askpasswd = 1;
40455258Sandrew 			reply(331,
40555258Sandrew 			    "Guest login ok, type your name as password.");
40636933Skarels 		} else
40736304Skarels 			reply(530, "User %s unknown.", name);
40855258Sandrew 		if (!askpasswd && logging)
40955258Sandrew 			syslog(LOG_NOTICE,
41055258Sandrew 			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
41136304Skarels 		return;
41236304Skarels 	}
41336304Skarels 	if (pw = sgetpwnam(name)) {
41436304Skarels 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
41537459Skarels 			shell = _PATH_BSHELL;
41636304Skarels 		while ((cp = getusershell()) != NULL)
41736304Skarels 			if (strcmp(cp, shell) == 0)
41836304Skarels 				break;
41936304Skarels 		endusershell();
42055258Sandrew 
42140183Smckusick 		if (cp == NULL || checkuser(name)) {
42236304Skarels 			reply(530, "User %s access denied.", name);
42336933Skarels 			if (logging)
42436933Skarels 				syslog(LOG_NOTICE,
42536933Skarels 				    "FTP LOGIN REFUSED FROM %s, %s",
42636933Skarels 				    remotehost, name);
42736304Skarels 			pw = (struct passwd *) NULL;
42836304Skarels 			return;
42936304Skarels 		}
43036304Skarels 	}
43155258Sandrew 	if (logging)
43255258Sandrew 		strncpy(curname, name, sizeof(curname)-1);
43336304Skarels 	reply(331, "Password required for %s.", name);
43436304Skarels 	askpasswd = 1;
43536304Skarels 	/*
43636304Skarels 	 * Delay before reading passwd after first failed
43736304Skarels 	 * attempt to slow down passwd-guessing programs.
43836304Skarels 	 */
43936304Skarels 	if (login_attempts)
44036304Skarels 		sleep((unsigned) login_attempts);
44136304Skarels }
44236304Skarels 
44336304Skarels /*
44440183Smckusick  * Check if a user is in the file _PATH_FTPUSERS
44540183Smckusick  */
44655258Sandrew static int
checkuser(name)44740183Smckusick checkuser(name)
44840183Smckusick 	char *name;
44940183Smckusick {
45066711Spendry 	FILE *fd;
45155258Sandrew 	int found = 0;
45266711Spendry 	char *p, line[BUFSIZ];
45340183Smckusick 
45440183Smckusick 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
45542327Sbostic 		while (fgets(line, sizeof(line), fd) != NULL)
45660087Sbostic 			if ((p = strchr(line, '\n')) != NULL) {
45742327Sbostic 				*p = '\0';
45842327Sbostic 				if (line[0] == '#')
45942327Sbostic 					continue;
460*68976Sbostic 				if (strcmp(line, name) == 0) {
46155258Sandrew 					found = 1;
46255258Sandrew 					break;
46355258Sandrew 				}
46442327Sbostic 			}
46540183Smckusick 		(void) fclose(fd);
46640183Smckusick 	}
46755258Sandrew 	return (found);
46840183Smckusick }
46940183Smckusick 
47040183Smckusick /*
47136304Skarels  * Terminate login as previous user, if any, resetting state;
47236304Skarels  * used when USER command is given or login fails.
47336304Skarels  */
47455258Sandrew static void
end_login()47536304Skarels end_login()
47636304Skarels {
47736304Skarels 
47836304Skarels 	(void) seteuid((uid_t)0);
47936304Skarels 	if (logged_in)
48036304Skarels 		logwtmp(ttyline, "", "");
48136304Skarels 	pw = NULL;
48236304Skarels 	logged_in = 0;
48336304Skarels 	guest = 0;
48436304Skarels }
48536304Skarels 
48655258Sandrew void
pass(passwd)48710275Ssam pass(passwd)
48810275Ssam 	char *passwd;
48910275Ssam {
49066711Spendry 	char *salt, *xpasswd;
49155258Sandrew 	FILE *fd;
49210275Ssam 
49336304Skarels 	if (logged_in || askpasswd == 0) {
49410275Ssam 		reply(503, "Login with USER first.");
49510275Ssam 		return;
49610275Ssam 	}
49736304Skarels 	askpasswd = 0;
49810275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
49936304Skarels 		if (pw == NULL)
50036304Skarels 			salt = "xx";
50136304Skarels 		else
50236304Skarels 			salt = pw->pw_passwd;
50336304Skarels 		xpasswd = crypt(passwd, salt);
50416760Slepreau 		/* The strcmp does not catch null passwords! */
50536304Skarels 		if (pw == NULL || *pw->pw_passwd == '\0' ||
50636304Skarels 		    strcmp(xpasswd, pw->pw_passwd)) {
50710275Ssam 			reply(530, "Login incorrect.");
50855258Sandrew 			if (logging)
50955258Sandrew 				syslog(LOG_NOTICE,
51055258Sandrew 				    "FTP LOGIN FAILED FROM %s, %s",
51155258Sandrew 				    remotehost, curname);
51210275Ssam 			pw = NULL;
51336304Skarels 			if (login_attempts++ >= 5) {
51436933Skarels 				syslog(LOG_NOTICE,
51536304Skarels 				    "repeated login failures from %s",
51636304Skarels 				    remotehost);
51736304Skarels 				exit(0);
51836304Skarels 			}
51910275Ssam 			return;
52010275Ssam 		}
52110275Ssam 	}
52236304Skarels 	login_attempts = 0;		/* this time successful */
52355258Sandrew 	if (setegid((gid_t)pw->pw_gid) < 0) {
52455258Sandrew 		reply(550, "Can't set gid.");
52555258Sandrew 		return;
52655258Sandrew 	}
52736304Skarels 	(void) initgroups(pw->pw_name, pw->pw_gid);
52816033Sralph 
52936192Sbostic 	/* open wtmp before chroot */
53036192Sbostic 	(void)sprintf(ttyline, "ftp%d", getpid());
53136192Sbostic 	logwtmp(ttyline, pw->pw_name, remotehost);
53236192Sbostic 	logged_in = 1;
53336192Sbostic 
53436446Sbostic 	if (guest) {
53536620Srick 		/*
53636933Skarels 		 * We MUST do a chdir() after the chroot. Otherwise
53736933Skarels 		 * the old current directory will be accessible as "."
53836933Skarels 		 * outside the new root!
53936620Srick 		 */
54036620Srick 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
54136446Sbostic 			reply(550, "Can't set guest privileges.");
54236446Sbostic 			goto bad;
54336446Sbostic 		}
54436933Skarels 	} else if (chdir(pw->pw_dir) < 0) {
54536620Srick 		if (chdir("/") < 0) {
54636446Sbostic 			reply(530, "User %s: can't change directory to %s.",
54736446Sbostic 			    pw->pw_name, pw->pw_dir);
54836446Sbostic 			goto bad;
54936933Skarels 		} else
55036446Sbostic 			lreply(230, "No directory! Logging in with home=/");
55136620Srick 	}
55236304Skarels 	if (seteuid((uid_t)pw->pw_uid) < 0) {
55336304Skarels 		reply(550, "Can't set uid.");
55436304Skarels 		goto bad;
55536304Skarels 	}
55655258Sandrew 	/*
55755258Sandrew 	 * Display a login message, if it exists.
55855258Sandrew 	 * N.B. reply(230,) must follow the message.
55955258Sandrew 	 */
56055258Sandrew 	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
56166711Spendry 		char *cp, line[LINE_MAX];
56255258Sandrew 
56366711Spendry 		while (fgets(line, sizeof(line), fd) != NULL) {
56460087Sbostic 			if ((cp = strchr(line, '\n')) != NULL)
56555258Sandrew 				*cp = '\0';
56655258Sandrew 			lreply(230, "%s", line);
56755258Sandrew 		}
56855258Sandrew 		(void) fflush(stdout);
56955258Sandrew 		(void) fclose(fd);
57055258Sandrew 	}
57136550Sbostic 	if (guest) {
57236192Sbostic 		reply(230, "Guest login ok, access restrictions apply.");
57336933Skarels #ifdef SETPROCTITLE
57466711Spendry 		snprintf(proctitle, sizeof(proctitle),
57566711Spendry 		    "%s: anonymous/%.*s", remotehost,
57636933Skarels 		    sizeof(proctitle) - sizeof(remotehost) -
57736933Skarels 		    sizeof(": anonymous/"), passwd);
57836933Skarels 		setproctitle(proctitle);
57936933Skarels #endif /* SETPROCTITLE */
58036933Skarels 		if (logging)
58136933Skarels 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
58236933Skarels 			    remotehost, passwd);
58336933Skarels 	} else {
58410275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
58536933Skarels #ifdef SETPROCTITLE
58666711Spendry 		snprintf(proctitle, sizeof(proctitle),
58766711Spendry 		    "%s: %s", remotehost, pw->pw_name);
58836933Skarels 		setproctitle(proctitle);
58936933Skarels #endif /* SETPROCTITLE */
59036933Skarels 		if (logging)
59155258Sandrew 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
59236933Skarels 			    remotehost, pw->pw_name);
59336550Sbostic 	}
59436933Skarels 	(void) umask(defumask);
59510303Ssam 	return;
59610303Ssam bad:
59736304Skarels 	/* Forget all about it... */
59836304Skarels 	end_login();
59910275Ssam }
60010275Ssam 
60155258Sandrew void
retrieve(cmd,name)60210275Ssam retrieve(cmd, name)
60310275Ssam 	char *cmd, *name;
60410275Ssam {
60510275Ssam 	FILE *fin, *dout;
60610275Ssam 	struct stat st;
60766711Spendry 	int (*closefunc) __P((FILE *));
60810275Ssam 
60936557Sbostic 	if (cmd == 0) {
61036446Sbostic 		fin = fopen(name, "r"), closefunc = fclose;
61136557Sbostic 		st.st_size = 0;
61236557Sbostic 	} else {
61310275Ssam 		char line[BUFSIZ];
61410275Ssam 
61526493Sminshall 		(void) sprintf(line, cmd, name), name = line;
61636304Skarels 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
61736557Sbostic 		st.st_size = -1;
61836933Skarels 		st.st_blksize = BUFSIZ;
61910275Ssam 	}
62010275Ssam 	if (fin == NULL) {
62155258Sandrew 		if (errno != 0) {
62236304Skarels 			perror_reply(550, name);
62355258Sandrew 			if (cmd == 0) {
62455258Sandrew 				LOGCMD("get", name);
62555258Sandrew 			}
62655258Sandrew 		}
62710275Ssam 		return;
62810275Ssam 	}
62955258Sandrew 	byte_count = -1;
63066711Spendry 	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
63110275Ssam 		reply(550, "%s: not a plain file.", name);
63210275Ssam 		goto done;
63310275Ssam 	}
63437459Skarels 	if (restart_point) {
63537459Skarels 		if (type == TYPE_A) {
63666711Spendry 			off_t i, n;
63766711Spendry 			int c;
63837459Skarels 
63937459Skarels 			n = restart_point;
64037459Skarels 			i = 0;
64137459Skarels 			while (i++ < n) {
64237459Skarels 				if ((c=getc(fin)) == EOF) {
64337459Skarels 					perror_reply(550, name);
64437459Skarels 					goto done;
64537459Skarels 				}
64637459Skarels 				if (c == '\n')
64737459Skarels 					i++;
64852998Sbostic 			}
64937459Skarels 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
65037459Skarels 			perror_reply(550, name);
65137459Skarels 			goto done;
65237459Skarels 		}
65337459Skarels 	}
65410275Ssam 	dout = dataconn(name, st.st_size, "w");
65510275Ssam 	if (dout == NULL)
65610275Ssam 		goto done;
65736620Srick 	send_data(fin, dout, st.st_blksize);
65826493Sminshall 	(void) fclose(dout);
65926044Sminshall 	data = -1;
66026044Sminshall 	pdata = -1;
66110275Ssam done:
66255258Sandrew 	if (cmd == 0)
66355258Sandrew 		LOGBYTES("get", name, byte_count);
66410275Ssam 	(*closefunc)(fin);
66510275Ssam }
66610275Ssam 
66755258Sandrew void
store(name,mode,unique)66836304Skarels store(name, mode, unique)
66910275Ssam 	char *name, *mode;
67036304Skarels 	int unique;
67110275Ssam {
67210275Ssam 	FILE *fout, *din;
67336446Sbostic 	struct stat st;
67466711Spendry 	int (*closefunc) __P((FILE *));
67510275Ssam 
67636446Sbostic 	if (unique && stat(name, &st) == 0 &&
67755258Sandrew 	    (name = gunique(name)) == NULL) {
67855258Sandrew 		LOGCMD(*mode == 'w' ? "put" : "append", name);
67936446Sbostic 		return;
68055258Sandrew 	}
68110303Ssam 
68237459Skarels 	if (restart_point)
68366711Spendry 		mode = "r+";
68436620Srick 	fout = fopen(name, mode);
68536620Srick 	closefunc = fclose;
68610275Ssam 	if (fout == NULL) {
68736304Skarels 		perror_reply(553, name);
68855258Sandrew 		LOGCMD(*mode == 'w' ? "put" : "append", name);
68910275Ssam 		return;
69010275Ssam 	}
69155258Sandrew 	byte_count = -1;
69237459Skarels 	if (restart_point) {
69337459Skarels 		if (type == TYPE_A) {
69466711Spendry 			off_t i, n;
69566711Spendry 			int c;
69637459Skarels 
69737459Skarels 			n = restart_point;
69837459Skarels 			i = 0;
69937459Skarels 			while (i++ < n) {
70037459Skarels 				if ((c=getc(fout)) == EOF) {
70137459Skarels 					perror_reply(550, name);
70237459Skarels 					goto done;
70337459Skarels 				}
70437459Skarels 				if (c == '\n')
70537459Skarels 					i++;
70652998Sbostic 			}
70737459Skarels 			/*
70837459Skarels 			 * We must do this seek to "current" position
70937459Skarels 			 * because we are changing from reading to
71037459Skarels 			 * writing.
71137459Skarels 			 */
71237459Skarels 			if (fseek(fout, 0L, L_INCR) < 0) {
71337459Skarels 				perror_reply(550, name);
71437459Skarels 				goto done;
71537459Skarels 			}
71637459Skarels 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
71737459Skarels 			perror_reply(550, name);
71837459Skarels 			goto done;
71937459Skarels 		}
72037459Skarels 	}
72136304Skarels 	din = dataconn(name, (off_t)-1, "r");
72210275Ssam 	if (din == NULL)
72310275Ssam 		goto done;
72436620Srick 	if (receive_data(din, fout) == 0) {
72536620Srick 		if (unique)
72636304Skarels 			reply(226, "Transfer complete (unique file name:%s).",
72736933Skarels 			    name);
72836304Skarels 		else
72936304Skarels 			reply(226, "Transfer complete.");
73026044Sminshall 	}
73126493Sminshall 	(void) fclose(din);
73226044Sminshall 	data = -1;
73326044Sminshall 	pdata = -1;
73410275Ssam done:
73555258Sandrew 	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
73610275Ssam 	(*closefunc)(fout);
73710275Ssam }
73810275Ssam 
73955258Sandrew static FILE *
getdatasock(mode)74010275Ssam getdatasock(mode)
74110275Ssam 	char *mode;
74210275Ssam {
74366711Spendry 	int on = 1, s, t, tries;
74410275Ssam 
74510275Ssam 	if (data >= 0)
74610275Ssam 		return (fdopen(data, mode));
74750391Skarels 	(void) seteuid((uid_t)0);
74813247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
74910602Ssam 	if (s < 0)
75050391Skarels 		goto bad;
75137459Skarels 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
75266711Spendry 	    (char *) &on, sizeof(on)) < 0)
75310602Ssam 		goto bad;
75413152Ssam 	/* anchor socket to avoid multi-homing problems */
75513152Ssam 	data_source.sin_family = AF_INET;
75613152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
75737459Skarels 	for (tries = 1; ; tries++) {
75837459Skarels 		if (bind(s, (struct sockaddr *)&data_source,
75966711Spendry 		    sizeof(data_source)) >= 0)
76037459Skarels 			break;
76137459Skarels 		if (errno != EADDRINUSE || tries > 10)
76237459Skarels 			goto bad;
76337459Skarels 		sleep(tries);
76437459Skarels 	}
76536304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
76644339Skarels #ifdef IP_TOS
76744339Skarels 	on = IPTOS_THROUGHPUT;
76844339Skarels 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
76944339Skarels 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
77044339Skarels #endif
77110275Ssam 	return (fdopen(s, mode));
77210602Ssam bad:
77355258Sandrew 	/* Return the real value of errno (close may change it) */
77455258Sandrew 	t = errno;
77536304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
77626493Sminshall 	(void) close(s);
77755258Sandrew 	errno = t;
77810602Ssam 	return (NULL);
77910275Ssam }
78010275Ssam 
78155258Sandrew static FILE *
dataconn(name,size,mode)78210275Ssam dataconn(name, size, mode)
78310275Ssam 	char *name;
78411653Ssam 	off_t size;
78510275Ssam 	char *mode;
78610275Ssam {
78710275Ssam 	char sizebuf[32];
78810275Ssam 	FILE *file;
78944339Skarels 	int retry = 0, tos;
79010275Ssam 
79136933Skarels 	file_size = size;
79236933Skarels 	byte_count = 0;
79336304Skarels 	if (size != (off_t) -1)
79466711Spendry 		(void) sprintf(sizebuf, " (%qd bytes)", size);
79510275Ssam 	else
79610275Ssam 		(void) strcpy(sizebuf, "");
79736304Skarels 	if (pdata >= 0) {
79826044Sminshall 		struct sockaddr_in from;
79926044Sminshall 		int s, fromlen = sizeof(from);
80026044Sminshall 
80136304Skarels 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
80226044Sminshall 		if (s < 0) {
80326044Sminshall 			reply(425, "Can't open data connection.");
80426044Sminshall 			(void) close(pdata);
80526044Sminshall 			pdata = -1;
80666711Spendry 			return (NULL);
80726044Sminshall 		}
80826044Sminshall 		(void) close(pdata);
80926044Sminshall 		pdata = s;
81044339Skarels #ifdef IP_TOS
81144339Skarels 		tos = IPTOS_LOWDELAY;
81244339Skarels 		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
81344339Skarels 		    sizeof(int));
81444339Skarels #endif
81555258Sandrew 		reply(150, "Opening %s mode data connection for '%s'%s.",
81636235Skarels 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
81766711Spendry 		return (fdopen(pdata, mode));
81826044Sminshall 	}
81910275Ssam 	if (data >= 0) {
82055258Sandrew 		reply(125, "Using existing data connection for '%s'%s.",
82110275Ssam 		    name, sizebuf);
82210321Ssam 		usedefault = 1;
82310275Ssam 		return (fdopen(data, mode));
82410275Ssam 	}
82510566Ssam 	if (usedefault)
82610422Ssam 		data_dest = his_addr;
82710422Ssam 	usedefault = 1;
82810275Ssam 	file = getdatasock(mode);
82910275Ssam 	if (file == NULL) {
83010275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
83113247Ssam 		    inet_ntoa(data_source.sin_addr),
83242412Sbostic 		    ntohs(data_source.sin_port), strerror(errno));
83310275Ssam 		return (NULL);
83410275Ssam 	}
83510275Ssam 	data = fileno(file);
83636304Skarels 	while (connect(data, (struct sockaddr *)&data_dest,
83766711Spendry 	    sizeof(data_dest)) < 0) {
83811653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
83926493Sminshall 			sleep((unsigned) swaitint);
84011653Ssam 			retry += swaitint;
84111653Ssam 			continue;
84211653Ssam 		}
84336304Skarels 		perror_reply(425, "Can't build data connection");
84410275Ssam 		(void) fclose(file);
84510275Ssam 		data = -1;
84610275Ssam 		return (NULL);
84710275Ssam 	}
84855258Sandrew 	reply(150, "Opening %s mode data connection for '%s'%s.",
84936235Skarels 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
85010275Ssam 	return (file);
85110275Ssam }
85210275Ssam 
85310275Ssam /*
85455258Sandrew  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
85555258Sandrew  * encapsulation of the data subject * to Mode, Structure, and Type.
85610275Ssam  *
85710275Ssam  * NB: Form isn't handled.
85810275Ssam  */
85955258Sandrew static void
send_data(instr,outstr,blksize)86036446Sbostic send_data(instr, outstr, blksize)
86110275Ssam 	FILE *instr, *outstr;
86236446Sbostic 	off_t blksize;
86310275Ssam {
86466711Spendry 	int c, cnt, filefd, netfd;
86566711Spendry 	char *buf;
86610275Ssam 
86726044Sminshall 	transflag++;
86826044Sminshall 	if (setjmp(urgcatch)) {
86926044Sminshall 		transflag = 0;
87036620Srick 		return;
87126044Sminshall 	}
87210275Ssam 	switch (type) {
87310275Ssam 
87410275Ssam 	case TYPE_A:
87510275Ssam 		while ((c = getc(instr)) != EOF) {
87636933Skarels 			byte_count++;
87711220Ssam 			if (c == '\n') {
87836933Skarels 				if (ferror(outstr))
87936620Srick 					goto data_err;
88027750Sminshall 				(void) putc('\r', outstr);
88111220Ssam 			}
88227750Sminshall 			(void) putc(c, outstr);
88310275Ssam 		}
88436933Skarels 		fflush(outstr);
88526044Sminshall 		transflag = 0;
88636933Skarels 		if (ferror(instr))
88736933Skarels 			goto file_err;
88836933Skarels 		if (ferror(outstr))
88936620Srick 			goto data_err;
89036620Srick 		reply(226, "Transfer complete.");
89136620Srick 		return;
89236446Sbostic 
89310275Ssam 	case TYPE_I:
89410275Ssam 	case TYPE_L:
89536446Sbostic 		if ((buf = malloc((u_int)blksize)) == NULL) {
89636446Sbostic 			transflag = 0;
89736933Skarels 			perror_reply(451, "Local resource failure: malloc");
89836933Skarels 			return;
89936446Sbostic 		}
90010275Ssam 		netfd = fileno(outstr);
90110275Ssam 		filefd = fileno(instr);
90236933Skarels 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
90336620Srick 		    write(netfd, buf, cnt) == cnt)
90436933Skarels 			byte_count += cnt;
90526044Sminshall 		transflag = 0;
90636446Sbostic 		(void)free(buf);
90736933Skarels 		if (cnt != 0) {
90836933Skarels 			if (cnt < 0)
90936933Skarels 				goto file_err;
91036620Srick 			goto data_err;
91136933Skarels 		}
91236620Srick 		reply(226, "Transfer complete.");
91336620Srick 		return;
91436620Srick 	default:
91536620Srick 		transflag = 0;
91636620Srick 		reply(550, "Unimplemented TYPE %d in send_data", type);
91736620Srick 		return;
91810275Ssam 	}
91936620Srick 
92036620Srick data_err:
92126044Sminshall 	transflag = 0;
92236933Skarels 	perror_reply(426, "Data connection");
92336933Skarels 	return;
92436933Skarels 
92536933Skarels file_err:
92636933Skarels 	transflag = 0;
92736933Skarels 	perror_reply(551, "Error on input file");
92810275Ssam }
92910275Ssam 
93010275Ssam /*
93155258Sandrew  * Transfer data from peer to "outstr" using the appropriate encapulation of
93255258Sandrew  * the data subject to Mode, Structure, and Type.
93310275Ssam  *
93410275Ssam  * N.B.: Form isn't handled.
93510275Ssam  */
93655258Sandrew static int
receive_data(instr,outstr)93710275Ssam receive_data(instr, outstr)
93810275Ssam 	FILE *instr, *outstr;
93910275Ssam {
94066711Spendry 	int c;
94138134Srick 	int cnt, bare_lfs = 0;
94210275Ssam 	char buf[BUFSIZ];
94310275Ssam 
94426044Sminshall 	transflag++;
94526044Sminshall 	if (setjmp(urgcatch)) {
94626044Sminshall 		transflag = 0;
94736933Skarels 		return (-1);
94826044Sminshall 	}
94910275Ssam 	switch (type) {
95010275Ssam 
95110275Ssam 	case TYPE_I:
95210275Ssam 	case TYPE_L:
95366711Spendry 		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
95436620Srick 			if (write(fileno(outstr), buf, cnt) != cnt)
95536933Skarels 				goto file_err;
95636933Skarels 			byte_count += cnt;
95726044Sminshall 		}
95836933Skarels 		if (cnt < 0)
95936620Srick 			goto data_err;
96026044Sminshall 		transflag = 0;
96136933Skarels 		return (0);
96210275Ssam 
96310275Ssam 	case TYPE_E:
96427106Smckusick 		reply(553, "TYPE E not implemented.");
96526044Sminshall 		transflag = 0;
96627106Smckusick 		return (-1);
96710275Ssam 
96810275Ssam 	case TYPE_A:
96910275Ssam 		while ((c = getc(instr)) != EOF) {
97036933Skarels 			byte_count++;
97138134Srick 			if (c == '\n')
97238134Srick 				bare_lfs++;
97327750Sminshall 			while (c == '\r') {
97436933Skarels 				if (ferror(outstr))
97536620Srick 					goto data_err;
97636933Skarels 				if ((c = getc(instr)) != '\n') {
97727750Sminshall 					(void) putc ('\r', outstr);
97836933Skarels 					if (c == '\0' || c == EOF)
97936933Skarels 						goto contin2;
98036933Skarels 				}
98110275Ssam 			}
98236933Skarels 			(void) putc(c, outstr);
98336933Skarels 	contin2:	;
98410275Ssam 		}
98536620Srick 		fflush(outstr);
98636933Skarels 		if (ferror(instr))
98736620Srick 			goto data_err;
98836933Skarels 		if (ferror(outstr))
98936933Skarels 			goto file_err;
99026044Sminshall 		transflag = 0;
99138134Srick 		if (bare_lfs) {
99266842Sbostic 			lreply(226,
99366842Sbostic 		"WARNING! %d bare linefeeds received in ASCII mode",
99466842Sbostic 			    bare_lfs);
99566842Sbostic 		(void)printf("   File may not have transferred correctly.\r\n");
99638134Srick 		}
99710275Ssam 		return (0);
99836620Srick 	default:
99936620Srick 		reply(550, "Unimplemented TYPE %d in receive_data", type);
100036620Srick 		transflag = 0;
100136933Skarels 		return (-1);
100210275Ssam 	}
100336620Srick 
100436620Srick data_err:
100526044Sminshall 	transflag = 0;
100636933Skarels 	perror_reply(426, "Data Connection");
100736933Skarels 	return (-1);
100836933Skarels 
100936933Skarels file_err:
101036933Skarels 	transflag = 0;
101136933Skarels 	perror_reply(452, "Error writing file");
101236933Skarels 	return (-1);
101310275Ssam }
101410275Ssam 
101555258Sandrew void
statfilecmd(filename)101636933Skarels statfilecmd(filename)
101736933Skarels 	char *filename;
101836933Skarels {
101936933Skarels 	FILE *fin;
102036933Skarels 	int c;
102166711Spendry 	char line[LINE_MAX];
102236933Skarels 
102355258Sandrew 	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
102436933Skarels 	fin = ftpd_popen(line, "r");
102536933Skarels 	lreply(211, "status of %s:", filename);
102636933Skarels 	while ((c = getc(fin)) != EOF) {
102736933Skarels 		if (c == '\n') {
102836933Skarels 			if (ferror(stdout)){
102936933Skarels 				perror_reply(421, "control connection");
103036933Skarels 				(void) ftpd_pclose(fin);
103136933Skarels 				dologout(1);
103236933Skarels 				/* NOTREACHED */
103336933Skarels 			}
103436933Skarels 			if (ferror(fin)) {
103536933Skarels 				perror_reply(551, filename);
103636933Skarels 				(void) ftpd_pclose(fin);
103736933Skarels 				return;
103836933Skarels 			}
103936933Skarels 			(void) putc('\r', stdout);
104036933Skarels 		}
104136933Skarels 		(void) putc(c, stdout);
104236933Skarels 	}
104336933Skarels 	(void) ftpd_pclose(fin);
104436933Skarels 	reply(211, "End of Status");
104536933Skarels }
104636933Skarels 
104755258Sandrew void
statcmd()104836933Skarels statcmd()
104936933Skarels {
105036933Skarels 	struct sockaddr_in *sin;
105136933Skarels 	u_char *a, *p;
105236933Skarels 
105336933Skarels 	lreply(211, "%s FTP server status:", hostname, version);
105436933Skarels 	printf("     %s\r\n", version);
105536933Skarels 	printf("     Connected to %s", remotehost);
105638134Srick 	if (!isdigit(remotehost[0]))
105736933Skarels 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
105836933Skarels 	printf("\r\n");
105936933Skarels 	if (logged_in) {
106036933Skarels 		if (guest)
106136933Skarels 			printf("     Logged in anonymously\r\n");
106236933Skarels 		else
106336933Skarels 			printf("     Logged in as %s\r\n", pw->pw_name);
106436933Skarels 	} else if (askpasswd)
106536933Skarels 		printf("     Waiting for password\r\n");
106636933Skarels 	else
106736933Skarels 		printf("     Waiting for user name\r\n");
106836933Skarels 	printf("     TYPE: %s", typenames[type]);
106936933Skarels 	if (type == TYPE_A || type == TYPE_E)
107036933Skarels 		printf(", FORM: %s", formnames[form]);
107136933Skarels 	if (type == TYPE_L)
107236933Skarels #if NBBY == 8
107336933Skarels 		printf(" %d", NBBY);
107436933Skarels #else
107536933Skarels 		printf(" %d", bytesize);	/* need definition! */
107636933Skarels #endif
107736933Skarels 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
107836933Skarels 	    strunames[stru], modenames[mode]);
107936933Skarels 	if (data != -1)
108036933Skarels 		printf("     Data connection open\r\n");
108136933Skarels 	else if (pdata != -1) {
108236933Skarels 		printf("     in Passive mode");
108336933Skarels 		sin = &pasv_addr;
108436933Skarels 		goto printaddr;
108536933Skarels 	} else if (usedefault == 0) {
108636933Skarels 		printf("     PORT");
108736933Skarels 		sin = &data_dest;
108836933Skarels printaddr:
108936933Skarels 		a = (u_char *) &sin->sin_addr;
109036933Skarels 		p = (u_char *) &sin->sin_port;
109136933Skarels #define UC(b) (((int) b) & 0xff)
109236933Skarels 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
109336933Skarels 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
109436933Skarels #undef UC
109536933Skarels 	} else
109636933Skarels 		printf("     No data connection\r\n");
109736933Skarels 	reply(211, "End of status");
109836933Skarels }
109936933Skarels 
110055258Sandrew void
fatal(s)110110275Ssam fatal(s)
110210275Ssam 	char *s;
110310275Ssam {
110466711Spendry 
110510275Ssam 	reply(451, "Error in server: %s\n", s);
110610275Ssam 	reply(221, "Closing connection due to server error.");
110713247Ssam 	dologout(0);
110836620Srick 	/* NOTREACHED */
110910275Ssam }
111010275Ssam 
111155258Sandrew void
111255258Sandrew #if __STDC__
reply(int n,const char * fmt,...)111355258Sandrew reply(int n, const char *fmt, ...)
111455258Sandrew #else
111555258Sandrew reply(n, fmt, va_alist)
111610275Ssam 	int n;
111736446Sbostic 	char *fmt;
111855258Sandrew         va_dcl
111955258Sandrew #endif
112010275Ssam {
112155258Sandrew 	va_list ap;
112255258Sandrew #if __STDC__
112355258Sandrew 	va_start(ap, fmt);
112455258Sandrew #else
112555258Sandrew 	va_start(ap);
112655258Sandrew #endif
112755258Sandrew 	(void)printf("%d ", n);
112855258Sandrew 	(void)vprintf(fmt, ap);
112955258Sandrew 	(void)printf("\r\n");
113036435Sbostic 	(void)fflush(stdout);
113110275Ssam 	if (debug) {
113226493Sminshall 		syslog(LOG_DEBUG, "<--- %d ", n);
113355258Sandrew 		vsyslog(LOG_DEBUG, fmt, ap);
113455258Sandrew 	}
113510275Ssam }
113610275Ssam 
113755258Sandrew void
113855258Sandrew #if __STDC__
lreply(int n,const char * fmt,...)113955258Sandrew lreply(int n, const char *fmt, ...)
114055258Sandrew #else
114155258Sandrew lreply(n, fmt, va_alist)
114210275Ssam 	int n;
114336446Sbostic 	char *fmt;
114455258Sandrew         va_dcl
114555258Sandrew #endif
114610275Ssam {
114755258Sandrew 	va_list ap;
114855258Sandrew #if __STDC__
114955258Sandrew 	va_start(ap, fmt);
115055258Sandrew #else
115155258Sandrew 	va_start(ap);
115255258Sandrew #endif
115355258Sandrew 	(void)printf("%d- ", n);
115455258Sandrew 	(void)vprintf(fmt, ap);
115555258Sandrew 	(void)printf("\r\n");
115636435Sbostic 	(void)fflush(stdout);
115736446Sbostic 	if (debug) {
115836446Sbostic 		syslog(LOG_DEBUG, "<--- %d- ", n);
115955258Sandrew 		vsyslog(LOG_DEBUG, fmt, ap);
116036446Sbostic 	}
116110275Ssam }
116210275Ssam 
116355258Sandrew static void
ack(s)116410275Ssam ack(s)
116510275Ssam 	char *s;
116610275Ssam {
116766711Spendry 
116827106Smckusick 	reply(250, "%s command successful.", s);
116910275Ssam }
117010275Ssam 
117155258Sandrew void
nack(s)117210275Ssam nack(s)
117310275Ssam 	char *s;
117410275Ssam {
117566711Spendry 
117610275Ssam 	reply(502, "%s command not implemented.", s);
117710275Ssam }
117810275Ssam 
117936304Skarels /* ARGSUSED */
118066711Spendry void
yyerror(s)118126493Sminshall yyerror(s)
118226493Sminshall 	char *s;
118310275Ssam {
118426044Sminshall 	char *cp;
118526044Sminshall 
118660087Sbostic 	if (cp = strchr(cbuf,'\n'))
118736551Sbostic 		*cp = '\0';
118836933Skarels 	reply(500, "'%s': command not understood.", cbuf);
118910275Ssam }
119010275Ssam 
119155258Sandrew void
delete(name)119210275Ssam delete(name)
119310275Ssam 	char *name;
119410275Ssam {
119510275Ssam 	struct stat st;
119610275Ssam 
119755258Sandrew 	LOGCMD("delete", name);
119810275Ssam 	if (stat(name, &st) < 0) {
119936304Skarels 		perror_reply(550, name);
120010275Ssam 		return;
120110275Ssam 	}
120210275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
120310275Ssam 		if (rmdir(name) < 0) {
120436304Skarels 			perror_reply(550, name);
120510275Ssam 			return;
120610275Ssam 		}
120710275Ssam 		goto done;
120810275Ssam 	}
120910275Ssam 	if (unlink(name) < 0) {
121036304Skarels 		perror_reply(550, name);
121110275Ssam 		return;
121210275Ssam 	}
121310275Ssam done:
121410275Ssam 	ack("DELE");
121510275Ssam }
121610275Ssam 
121755258Sandrew void
cwd(path)121810275Ssam cwd(path)
121910275Ssam 	char *path;
122010275Ssam {
122166711Spendry 
122236620Srick 	if (chdir(path) < 0)
122336304Skarels 		perror_reply(550, path);
122436620Srick 	else
122536620Srick 		ack("CWD");
122610275Ssam }
122710275Ssam 
122855258Sandrew void
makedir(name)122910303Ssam makedir(name)
123010275Ssam 	char *name;
123110275Ssam {
123266711Spendry 
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
removedir(name)124110303Ssam removedir(name)
124210275Ssam 	char *name;
124310275Ssam {
124466711Spendry 
124555258Sandrew 	LOGCMD("rmdir", name);
124636620Srick 	if (rmdir(name) < 0)
124736304Skarels 		perror_reply(550, name);
124836620Srick 	else
124936620Srick 		ack("RMD");
125010275Ssam }
125110275Ssam 
125255258Sandrew void
pwd()125310303Ssam pwd()
125410275Ssam {
125510303Ssam 	char path[MAXPATHLEN + 1];
125610275Ssam 
125736620Srick 	if (getwd(path) == (char *)NULL)
125827106Smckusick 		reply(550, "%s.", path);
125936620Srick 	else
126036620Srick 		reply(257, "\"%s\" is current directory.", path);
126110275Ssam }
126210275Ssam 
126310275Ssam char *
renamefrom(name)126410275Ssam renamefrom(name)
126510275Ssam 	char *name;
126610275Ssam {
126710275Ssam 	struct stat st;
126810275Ssam 
126910275Ssam 	if (stat(name, &st) < 0) {
127036304Skarels 		perror_reply(550, name);
127110275Ssam 		return ((char *)0);
127210275Ssam 	}
127310303Ssam 	reply(350, "File exists, ready for destination name");
127410275Ssam 	return (name);
127510275Ssam }
127610275Ssam 
127755258Sandrew void
renamecmd(from,to)127810275Ssam renamecmd(from, to)
127910275Ssam 	char *from, *to;
128010275Ssam {
128166711Spendry 
128255258Sandrew 	LOGCMD2("rename", from, to);
128336620Srick 	if (rename(from, to) < 0)
128436304Skarels 		perror_reply(550, "rename");
128536620Srick 	else
128636620Srick 		ack("RNTO");
128710275Ssam }
128810275Ssam 
128955258Sandrew static void
dolog(sin)129010275Ssam dolog(sin)
129110275Ssam 	struct sockaddr_in *sin;
129210275Ssam {
129336304Skarels 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
129466711Spendry 		sizeof(struct in_addr), AF_INET);
129510275Ssam 
129636304Skarels 	if (hp)
129766711Spendry 		(void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
129836304Skarels 	else
129926493Sminshall 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
130066711Spendry 		    sizeof(remotehost));
130136620Srick #ifdef SETPROCTITLE
130266711Spendry 	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
130336933Skarels 	setproctitle(proctitle);
130436620Srick #endif /* SETPROCTITLE */
130536933Skarels 
130652998Sbostic 	if (logging)
130752998Sbostic 		syslog(LOG_INFO, "connection from %s", remotehost);
130810275Ssam }
130910695Ssam 
131010695Ssam /*
131113247Ssam  * Record logout in wtmp file
131213247Ssam  * and exit with supplied status.
131313247Ssam  */
131455258Sandrew void
dologout(status)131513247Ssam dologout(status)
131613247Ssam 	int status;
131713247Ssam {
131866711Spendry 
131917580Ssam 	if (logged_in) {
132036304Skarels 		(void) seteuid((uid_t)0);
132135672Sbostic 		logwtmp(ttyline, "", "");
132213247Ssam 	}
132314436Ssam 	/* beware of flushing buffers after a SIGPIPE */
132414436Ssam 	_exit(status);
132513247Ssam }
132613247Ssam 
132755258Sandrew static void
myoob(signo)132855258Sandrew myoob(signo)
132955258Sandrew 	int signo;
133026044Sminshall {
133127750Sminshall 	char *cp;
133226044Sminshall 
133327750Sminshall 	/* only process if transfer occurring */
133436304Skarels 	if (!transflag)
133526044Sminshall 		return;
133627750Sminshall 	cp = tmpline;
133727750Sminshall 	if (getline(cp, 7, stdin) == NULL) {
133836304Skarels 		reply(221, "You could at least say goodbye.");
133927750Sminshall 		dologout(0);
134026044Sminshall 	}
134126044Sminshall 	upper(cp);
134236933Skarels 	if (strcmp(cp, "ABOR\r\n") == 0) {
134336933Skarels 		tmpline[0] = '\0';
134436933Skarels 		reply(426, "Transfer aborted. Data connection closed.");
134536933Skarels 		reply(226, "Abort successful");
134636933Skarels 		longjmp(urgcatch, 1);
134736933Skarels 	}
134836933Skarels 	if (strcmp(cp, "STAT\r\n") == 0) {
134936933Skarels 		if (file_size != (off_t) -1)
135055258Sandrew 			reply(213, "Status: %qd of %qd bytes transferred",
135136933Skarels 			    byte_count, file_size);
135236933Skarels 		else
135355258Sandrew 			reply(213, "Status: %qd bytes transferred", byte_count);
135436933Skarels 	}
135526044Sminshall }
135626044Sminshall 
135727106Smckusick /*
135836620Srick  * Note: a response of 425 is not mentioned as a possible response to
135952998Sbostic  *	the PASV command in RFC959. However, it has been blessed as
136052998Sbostic  *	a legitimate response by Jon Postel in a telephone conversation
136136620Srick  *	with Rick Adams on 25 Jan 89.
136227106Smckusick  */
136355258Sandrew void
passive()136426044Sminshall passive()
136526044Sminshall {
136626044Sminshall 	int len;
136766711Spendry 	char *p, *a;
136826044Sminshall 
136926044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
137026044Sminshall 	if (pdata < 0) {
137136620Srick 		perror_reply(425, "Can't open passive connection");
137226044Sminshall 		return;
137326044Sminshall 	}
137436933Skarels 	pasv_addr = ctrl_addr;
137536933Skarels 	pasv_addr.sin_port = 0;
137636304Skarels 	(void) seteuid((uid_t)0);
137736933Skarels 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
137836304Skarels 		(void) seteuid((uid_t)pw->pw_uid);
137936620Srick 		goto pasv_error;
138026044Sminshall 	}
138136304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
138236933Skarels 	len = sizeof(pasv_addr);
138336933Skarels 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
138436620Srick 		goto pasv_error;
138536620Srick 	if (listen(pdata, 1) < 0)
138636620Srick 		goto pasv_error;
138736933Skarels 	a = (char *) &pasv_addr.sin_addr;
138836933Skarels 	p = (char *) &pasv_addr.sin_port;
138926044Sminshall 
139026044Sminshall #define UC(b) (((int) b) & 0xff)
139126044Sminshall 
139226044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
139326044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
139436620Srick 	return;
139536620Srick 
139636620Srick pasv_error:
139736620Srick 	(void) close(pdata);
139836620Srick 	pdata = -1;
139936620Srick 	perror_reply(425, "Can't open passive connection");
140036620Srick 	return;
140126044Sminshall }
140226044Sminshall 
140336304Skarels /*
140436304Skarels  * Generate unique name for file with basename "local".
140536304Skarels  * The file named "local" is already known to exist.
140636304Skarels  * Generates failure reply on error.
140736304Skarels  */
140855258Sandrew static char *
gunique(local)140926044Sminshall gunique(local)
141026044Sminshall 	char *local;
141126044Sminshall {
141226044Sminshall 	static char new[MAXPATHLEN];
141336304Skarels 	struct stat st;
141455258Sandrew 	int count;
141555258Sandrew 	char *cp;
141626044Sminshall 
141760087Sbostic 	cp = strrchr(local, '/');
141836304Skarels 	if (cp)
141926044Sminshall 		*cp = '\0';
142036620Srick 	if (stat(cp ? local : ".", &st) < 0) {
142136933Skarels 		perror_reply(553, cp ? local : ".");
142266711Spendry 		return ((char *) 0);
142326044Sminshall 	}
142436620Srick 	if (cp)
142536620Srick 		*cp = '/';
142626044Sminshall 	(void) strcpy(new, local);
142726044Sminshall 	cp = new + strlen(new);
142826044Sminshall 	*cp++ = '.';
142936304Skarels 	for (count = 1; count < 100; count++) {
143055258Sandrew 		(void)sprintf(cp, "%d", count);
143136304Skarels 		if (stat(new, &st) < 0)
143266711Spendry 			return (new);
143326044Sminshall 	}
143436304Skarels 	reply(452, "Unique file name cannot be created.");
143566711Spendry 	return (NULL);
143626044Sminshall }
143736304Skarels 
143836304Skarels /*
143936304Skarels  * Format and send reply containing system error number.
144036304Skarels  */
144155258Sandrew void
perror_reply(code,string)144236304Skarels perror_reply(code, string)
144336304Skarels 	int code;
144436304Skarels 	char *string;
144536304Skarels {
144666711Spendry 
144742412Sbostic 	reply(code, "%s: %s.", string, strerror(errno));
144836304Skarels }
144936620Srick 
145036620Srick static char *onefile[] = {
145136620Srick 	"",
145236620Srick 	0
145336620Srick };
145436620Srick 
145555258Sandrew void
send_file_list(whichf)145666711Spendry send_file_list(whichf)
145766711Spendry 	char *whichf;
145836620Srick {
145936620Srick 	struct stat st;
146036620Srick 	DIR *dirp = NULL;
146146669Sbostic 	struct dirent *dir;
146236620Srick 	FILE *dout = NULL;
146366711Spendry 	char **dirlist, *dirname;
146437459Skarels 	int simple = 0;
146566711Spendry 	int freeglob = 0;
146666711Spendry 	glob_t gl;
146736620Srick 
146866711Spendry 	if (strpbrk(whichf, "~{[*?") != NULL) {
146966726Spendry 		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
147036933Skarels 
147166711Spendry 		memset(&gl, 0, sizeof(gl));
147266711Spendry 		freeglob = 1;
147366726Spendry 		if (glob(whichf, flags, 0, &gl)) {
147466711Spendry 			reply(550, "not found");
147566711Spendry 			goto out;
147666711Spendry 		} else if (gl.gl_pathc == 0) {
147736620Srick 			errno = ENOENT;
147866711Spendry 			perror_reply(550, whichf);
147966711Spendry 			goto out;
148036620Srick 		}
148166711Spendry 		dirlist = gl.gl_pathv;
148236620Srick 	} else {
148366711Spendry 		onefile[0] = whichf;
148436620Srick 		dirlist = onefile;
148537459Skarels 		simple = 1;
148636620Srick 	}
148736933Skarels 
148836933Skarels 	if (setjmp(urgcatch)) {
148936933Skarels 		transflag = 0;
149066711Spendry 		goto out;
149136933Skarels 	}
149236620Srick 	while (dirname = *dirlist++) {
149336620Srick 		if (stat(dirname, &st) < 0) {
149436933Skarels 			/*
149536933Skarels 			 * If user typed "ls -l", etc, and the client
149636933Skarels 			 * used NLST, do what the user meant.
149736933Skarels 			 */
149836933Skarels 			if (dirname[0] == '-' && *dirlist == NULL &&
149936933Skarels 			    transflag == 0) {
150036933Skarels 				retrieve("/bin/ls %s", dirname);
150166711Spendry 				goto out;
150236933Skarels 			}
150366711Spendry 			perror_reply(550, whichf);
150436620Srick 			if (dout != NULL) {
150536620Srick 				(void) fclose(dout);
150636933Skarels 				transflag = 0;
150736620Srick 				data = -1;
150836620Srick 				pdata = -1;
150936620Srick 			}
151066711Spendry 			goto out;
151136620Srick 		}
151236933Skarels 
151366711Spendry 		if (S_ISREG(st.st_mode)) {
151436620Srick 			if (dout == NULL) {
151537459Skarels 				dout = dataconn("file list", (off_t)-1, "w");
151636620Srick 				if (dout == NULL)
151766711Spendry 					goto out;
151836933Skarels 				transflag++;
151936620Srick 			}
152038158Srick 			fprintf(dout, "%s%s\n", dirname,
152138158Srick 				type == TYPE_A ? "\r" : "");
152236933Skarels 			byte_count += strlen(dirname) + 1;
152336620Srick 			continue;
152466711Spendry 		} else if (!S_ISDIR(st.st_mode))
152536620Srick 			continue;
152636620Srick 
152736620Srick 		if ((dirp = opendir(dirname)) == NULL)
152836620Srick 			continue;
152936620Srick 
153036620Srick 		while ((dir = readdir(dirp)) != NULL) {
153136933Skarels 			char nbuf[MAXPATHLEN];
153236620Srick 
153336620Srick 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
153436620Srick 				continue;
153536933Skarels 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
153636933Skarels 			    dir->d_namlen == 2)
153736620Srick 				continue;
153836620Srick 
153936933Skarels 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
154036933Skarels 
154136620Srick 			/*
154236933Skarels 			 * We have to do a stat to insure it's
154336933Skarels 			 * not a directory or special file.
154436620Srick 			 */
154537459Skarels 			if (simple || (stat(nbuf, &st) == 0 &&
154666711Spendry 			    S_ISREG(st.st_mode))) {
154736620Srick 				if (dout == NULL) {
154837459Skarels 					dout = dataconn("file list", (off_t)-1,
154936620Srick 						"w");
155036620Srick 					if (dout == NULL)
155166711Spendry 						goto out;
155236933Skarels 					transflag++;
155336620Srick 				}
155436620Srick 				if (nbuf[0] == '.' && nbuf[1] == '/')
155538158Srick 					fprintf(dout, "%s%s\n", &nbuf[2],
155638158Srick 						type == TYPE_A ? "\r" : "");
155736620Srick 				else
155838158Srick 					fprintf(dout, "%s%s\n", nbuf,
155938158Srick 						type == TYPE_A ? "\r" : "");
156036933Skarels 				byte_count += strlen(nbuf) + 1;
156136620Srick 			}
156236620Srick 		}
156336620Srick 		(void) closedir(dirp);
156436620Srick 	}
156536620Srick 
156636933Skarels 	if (dout == NULL)
156736933Skarels 		reply(550, "No files found.");
156836933Skarels 	else if (ferror(dout) != 0)
156936933Skarels 		perror_reply(550, "Data connection");
157036933Skarels 	else
157136620Srick 		reply(226, "Transfer complete.");
157236620Srick 
157336933Skarels 	transflag = 0;
157436933Skarels 	if (dout != NULL)
157536620Srick 		(void) fclose(dout);
157636620Srick 	data = -1;
157736620Srick 	pdata = -1;
157866711Spendry out:
157966711Spendry 	if (freeglob) {
158066711Spendry 		freeglob = 0;
158166711Spendry 		globfree(&gl);
158266711Spendry 	}
158336620Srick }
158436620Srick 
158536620Srick #ifdef SETPROCTITLE
158636620Srick /*
158755258Sandrew  * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
158855258Sandrew  * Warning, since this is usually started from inetd.conf, it often doesn't
158955258Sandrew  * have much of an environment or arglist to overwrite.
159036620Srick  */
159155258Sandrew void
159255258Sandrew #if __STDC__
setproctitle(const char * fmt,...)159355258Sandrew setproctitle(const char *fmt, ...)
159455258Sandrew #else
159555258Sandrew setproctitle(fmt, va_alist)
159655258Sandrew 	char *fmt;
159755258Sandrew         va_dcl
159855258Sandrew #endif
159936620Srick {
160066711Spendry 	int i;
160155258Sandrew 	va_list ap;
160266711Spendry 	char *p, *bp, ch;
160366711Spendry 	char buf[LINE_MAX];
160466711Spendry 
160555258Sandrew #if __STDC__
160655258Sandrew 	va_start(ap, fmt);
160755258Sandrew #else
160855258Sandrew 	va_start(ap);
160955258Sandrew #endif
161055258Sandrew 	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
161136620Srick 
161236620Srick 	/* make ps print our process name */
161336620Srick 	p = Argv[0];
161436620Srick 	*p++ = '-';
161536620Srick 
161636620Srick 	i = strlen(buf);
161736620Srick 	if (i > LastArgv - p - 2) {
161836620Srick 		i = LastArgv - p - 2;
161936620Srick 		buf[i] = '\0';
162036620Srick 	}
162136620Srick 	bp = buf;
162236620Srick 	while (ch = *bp++)
162336620Srick 		if (ch != '\n' && ch != '\r')
162436620Srick 			*p++ = ch;
162536620Srick 	while (p < LastArgv)
162636620Srick 		*p++ = ' ';
162736620Srick }
162836620Srick #endif /* SETPROCTITLE */
1629