xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 55258)
122499Sdist /*
2*55258Sandrew  * Copyright (c) 1985, 1988, 1990, 1992 Regents of the University of California.
333738Sbostic  * All rights reserved.
433738Sbostic  *
542666Sbostic  * %sccs.include.redist.c%
622499Sdist  */
722499Sdist 
810275Ssam #ifndef lint
922499Sdist char copyright[] =
10*55258Sandrew "@(#) Copyright (c) 1985, 1988, 1990, 1992 Regents of the University of California.\n\
1122499Sdist  All rights reserved.\n";
1233738Sbostic #endif /* not lint */
1310275Ssam 
1422499Sdist #ifndef lint
15*55258Sandrew static char sccsid[] = "@(#)ftpd.c	5.42 (Berkeley) 07/15/92";
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"
51*55258Sandrew #include "extern.h"
5210275Ssam 
53*55258Sandrew #if __STDC__
54*55258Sandrew #include <stdarg.h>
55*55258Sandrew #else
56*55258Sandrew #include <varargs.h>
57*55258Sandrew #endif
58*55258Sandrew 
59*55258Sandrew extern	off_t restart_point;
6010275Ssam extern	char *home;		/* pointer to home directory for glob */
6126044Sminshall extern	char cbuf[];
62*55258Sandrew 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 
114*55258Sandrew #define MAXLINE         256
115*55258Sandrew 
116*55258Sandrew #define LOGCMD(cmd, file) \
117*55258Sandrew 	if (logging > 1) \
118*55258Sandrew 	    syslog(LOG_INFO,"%s %s%s", cmd, \
119*55258Sandrew 		*(file) == '/' ? "" : curdir(), file);
120*55258Sandrew #define LOGCMD2(cmd, file1, file2) \
121*55258Sandrew 	 if (logging > 1) \
122*55258Sandrew 	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
123*55258Sandrew 		*(file1) == '/' ? "" : curdir(), file1, \
124*55258Sandrew 		*(file2) == '/' ? "" : curdir(), file2);
125*55258Sandrew #define LOGBYTES(cmd, file, cnt) \
126*55258Sandrew 	if (logging > 1) { \
127*55258Sandrew 		if (cnt == (off_t)-1) \
128*55258Sandrew 		    syslog(LOG_INFO,"%s %s%s", cmd, \
129*55258Sandrew 			*(file) == '/' ? "" : curdir(), file); \
130*55258Sandrew 		else \
131*55258Sandrew 		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
132*55258Sandrew 			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
133*55258Sandrew 	}
134*55258Sandrew 
135*55258Sandrew static void	 ack __P((char *));
136*55258Sandrew static void	 myoob __P((int));
137*55258Sandrew static int	 checkuser __P((char *));
138*55258Sandrew static FILE	*dataconn __P((char *, off_t, char *));
139*55258Sandrew static void	 dolog __P((struct sockaddr_in *));
140*55258Sandrew static char	*curdir __P((void));
141*55258Sandrew static void	 end_login __P((void));
142*55258Sandrew static FILE	*getdatasock __P((char *));
143*55258Sandrew static char	*gunique __P((char *));
144*55258Sandrew static void	 lostconn __P((int));
145*55258Sandrew static void	 pass __P((char *));
146*55258Sandrew static int	 receive_data __P((FILE *, FILE *));
147*55258Sandrew static void	 send_data __P((FILE *, FILE *, off_t));
148*55258Sandrew static struct passwd *
149*55258Sandrew 		 sgetpwnam __P((char *));
150*55258Sandrew static char	*sgetsave __P((char *));
151*55258Sandrew static void	 user __P((char *));
152*55258Sandrew 
153*55258Sandrew static char *
154*55258Sandrew curdir()
155*55258Sandrew {
156*55258Sandrew 	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
157*55258Sandrew 
158*55258Sandrew 	if (getcwd(path, sizeof(path)-2) == NULL)
159*55258Sandrew 		return ("");
160*55258Sandrew 	if (path[1] != '\0')		/* special case for root dir. */
161*55258Sandrew 		strcat(path, "/");
162*55258Sandrew 	/* For guest account, skip / since it's chrooted */
163*55258Sandrew 	return (guest ? path+1 : path);
164*55258Sandrew }
165*55258Sandrew 
166*55258Sandrew int
16736620Srick main(argc, argv, envp)
16810275Ssam 	int argc;
16910275Ssam 	char *argv[];
17036620Srick 	char **envp;
17110275Ssam {
17244339Skarels 	int addrlen, on = 1, tos;
173*55258Sandrew 	char *cp, line[MAXLINE];
174*55258Sandrew 	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 	 */
180*55258Sandrew 	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
18116339Skarels 	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 	}
18616339Skarels 	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 
20810275Ssam 	argc--, argv++;
20910275Ssam 	while (argc > 0 && *argv[0] == '-') {
21010275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
21110275Ssam 
21211653Ssam 		case 'v':
21311653Ssam 			debug = 1;
21411653Ssam 			break;
21511653Ssam 
21610275Ssam 		case 'd':
21710275Ssam 			debug = 1;
21810275Ssam 			break;
21910275Ssam 
22011757Ssam 		case 'l':
221*55258Sandrew 			logging++;	/* > 1 == extra logging */
22211757Ssam 			break;
22311757Ssam 
22411653Ssam 		case 't':
22511653Ssam 			timeout = atoi(++cp);
22636933Skarels 			if (maxtimeout < timeout)
22736933Skarels 				maxtimeout = timeout;
22811653Ssam 			goto nextopt;
22911653Ssam 
23036933Skarels 		case 'T':
23136933Skarels 			maxtimeout = atoi(++cp);
23236933Skarels 			if (timeout > maxtimeout)
23336933Skarels 				timeout = maxtimeout;
23436933Skarels 			goto nextopt;
23536933Skarels 
23636933Skarels 		case 'u':
23736933Skarels 		    {
23836933Skarels 			int val = 0;
23936933Skarels 
24036933Skarels 			while (*++cp && *cp >= '0' && *cp <= '9')
24136933Skarels 				val = val*8 + *cp - '0';
24236933Skarels 			if (*cp)
24336933Skarels 				fprintf(stderr, "ftpd: Bad value for -u\n");
24436933Skarels 			else
24536933Skarels 				defumask = val;
24636933Skarels 			goto nextopt;
24736933Skarels 		    }
24836933Skarels 
24910275Ssam 		default:
25016339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
25116339Skarels 			     *cp);
25210275Ssam 			break;
25310275Ssam 		}
25411653Ssam nextopt:
25510275Ssam 		argc--, argv++;
25610275Ssam 	}
25737459Skarels 	(void) freopen(_PATH_DEVNULL, "w", stderr);
25826493Sminshall 	(void) signal(SIGPIPE, lostconn);
25926493Sminshall 	(void) signal(SIGCHLD, SIG_IGN);
26035691Sbostic 	if ((int)signal(SIGURG, myoob) < 0)
26126493Sminshall 		syslog(LOG_ERR, "signal: %m");
26235691Sbostic 
26338134Srick 	/* Try to handle urgent data inline */
26427750Sminshall #ifdef SO_OOBINLINE
26536276Sbostic 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
26627750Sminshall 		syslog(LOG_ERR, "setsockopt: %m");
26736276Sbostic #endif
26838134Srick 
26936933Skarels #ifdef	F_SETOWN
27036304Skarels 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
27136304Skarels 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
27236933Skarels #endif
27316760Slepreau 	dolog(&his_addr);
27416339Skarels 	/*
27516339Skarels 	 * Set up default state
27616339Skarels 	 */
27716339Skarels 	data = -1;
27816339Skarels 	type = TYPE_A;
27916339Skarels 	form = FORM_N;
28016339Skarels 	stru = STRU_F;
28116339Skarels 	mode = MODE_S;
28226044Sminshall 	tmpline[0] = '\0';
283*55258Sandrew 
284*55258Sandrew 	/* If logins are disabled, print out the message. */
285*55258Sandrew 	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
286*55258Sandrew 		while (fgets(line, sizeof (line), fd) != NULL) {
287*55258Sandrew 			if ((cp = index(line, '\n')) != NULL)
288*55258Sandrew 				*cp = '\0';
289*55258Sandrew 			lreply(530, "%s", line);
290*55258Sandrew 		}
291*55258Sandrew 		(void) fflush(stdout);
292*55258Sandrew 		(void) fclose(fd);
293*55258Sandrew 		reply(530, "System not available.");
294*55258Sandrew 		exit(0);
295*55258Sandrew 	}
296*55258Sandrew 	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
297*55258Sandrew 		while (fgets(line, sizeof (line), fd) != NULL) {
298*55258Sandrew 			if ((cp = index(line, '\n')) != NULL)
299*55258Sandrew 				*cp = '\0';
300*55258Sandrew 			lreply(220, "%s", line);
301*55258Sandrew 		}
302*55258Sandrew 		(void) fflush(stdout);
303*55258Sandrew 		(void) fclose(fd);
304*55258Sandrew 		/* reply(220,) must follow */
305*55258Sandrew 	}
30626493Sminshall 	(void) gethostname(hostname, sizeof (hostname));
30736276Sbostic 	reply(220, "%s FTP server (%s) ready.", hostname, version);
30836304Skarels 	(void) setjmp(errcatch);
30936304Skarels 	for (;;)
31026493Sminshall 		(void) yyparse();
31136620Srick 	/* NOTREACHED */
31210275Ssam }
31310419Ssam 
314*55258Sandrew static void
315*55258Sandrew lostconn(signo)
316*55258Sandrew 	int signo;
31710275Ssam {
31814089Ssam 	if (debug)
31926493Sminshall 		syslog(LOG_DEBUG, "lost connection");
32014089Ssam 	dologout(-1);
32110275Ssam }
32210275Ssam 
32335672Sbostic static char ttyline[20];
32435672Sbostic 
32536185Sbostic /*
32636185Sbostic  * Helper function for sgetpwnam().
32736185Sbostic  */
328*55258Sandrew static char *
32936185Sbostic sgetsave(s)
33036185Sbostic 	char *s;
33136185Sbostic {
33236185Sbostic 	char *new = malloc((unsigned) strlen(s) + 1);
33336933Skarels 
33436185Sbostic 	if (new == NULL) {
33536620Srick 		perror_reply(421, "Local resource failure: malloc");
33636185Sbostic 		dologout(1);
33736620Srick 		/* NOTREACHED */
33836185Sbostic 	}
33936185Sbostic 	(void) strcpy(new, s);
34036185Sbostic 	return (new);
34136185Sbostic }
34236185Sbostic 
34336185Sbostic /*
34436185Sbostic  * Save the result of a getpwnam.  Used for USER command, since
34536185Sbostic  * the data returned must not be clobbered by any other command
34636185Sbostic  * (e.g., globbing).
34736185Sbostic  */
348*55258Sandrew static struct passwd *
34936185Sbostic sgetpwnam(name)
35036185Sbostic 	char *name;
35136185Sbostic {
35236185Sbostic 	static struct passwd save;
35336185Sbostic 	register struct passwd *p;
35436185Sbostic 	char *sgetsave();
35536185Sbostic 
35636185Sbostic 	if ((p = getpwnam(name)) == NULL)
35736185Sbostic 		return (p);
35836185Sbostic 	if (save.pw_name) {
35936185Sbostic 		free(save.pw_name);
36036185Sbostic 		free(save.pw_passwd);
36136185Sbostic 		free(save.pw_gecos);
36236185Sbostic 		free(save.pw_dir);
36336185Sbostic 		free(save.pw_shell);
36436185Sbostic 	}
36536185Sbostic 	save = *p;
36636185Sbostic 	save.pw_name = sgetsave(p->pw_name);
36736185Sbostic 	save.pw_passwd = sgetsave(p->pw_passwd);
36836185Sbostic 	save.pw_gecos = sgetsave(p->pw_gecos);
36936185Sbostic 	save.pw_dir = sgetsave(p->pw_dir);
37036185Sbostic 	save.pw_shell = sgetsave(p->pw_shell);
37136185Sbostic 	return (&save);
37236185Sbostic }
37336185Sbostic 
374*55258Sandrew static int login_attempts;	/* number of failed login attempts */
375*55258Sandrew static int askpasswd;		/* had user command, ask for passwd */
376*55258Sandrew static char curname[10];	/* current USER name */
37736304Skarels 
37836304Skarels /*
37936304Skarels  * USER command.
38042327Sbostic  * Sets global passwd pointer pw if named account exists and is acceptable;
38142327Sbostic  * sets askpasswd if a PASS command is expected.  If logged in previously,
38242327Sbostic  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
38342327Sbostic  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
38442327Sbostic  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
38542327Sbostic  * requesting login privileges.  Disallow anyone who does not have a standard
38642327Sbostic  * shell as returned by getusershell().  Disallow anyone mentioned in the file
38742327Sbostic  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
38836304Skarels  */
389*55258Sandrew void
39036304Skarels user(name)
39136304Skarels 	char *name;
39236304Skarels {
39336304Skarels 	register char *cp;
39436304Skarels 	char *shell;
39536304Skarels 
39636304Skarels 	if (logged_in) {
39736304Skarels 		if (guest) {
39836304Skarels 			reply(530, "Can't change user from guest login.");
39936304Skarels 			return;
40036304Skarels 		}
40136304Skarels 		end_login();
40236304Skarels 	}
40336304Skarels 
40436304Skarels 	guest = 0;
40536304Skarels 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
40640183Smckusick 		if (checkuser("ftp") || checkuser("anonymous"))
40740155Smckusick 			reply(530, "User %s access denied.", name);
40840155Smckusick 		else if ((pw = sgetpwnam("ftp")) != NULL) {
40936304Skarels 			guest = 1;
41036304Skarels 			askpasswd = 1;
411*55258Sandrew 			reply(331,
412*55258Sandrew 			    "Guest login ok, type your name as password.");
41336933Skarels 		} else
41436304Skarels 			reply(530, "User %s unknown.", name);
415*55258Sandrew 		if (!askpasswd && logging)
416*55258Sandrew 			syslog(LOG_NOTICE,
417*55258Sandrew 			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
41836304Skarels 		return;
41936304Skarels 	}
42036304Skarels 	if (pw = sgetpwnam(name)) {
42136304Skarels 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
42237459Skarels 			shell = _PATH_BSHELL;
42336304Skarels 		while ((cp = getusershell()) != NULL)
42436304Skarels 			if (strcmp(cp, shell) == 0)
42536304Skarels 				break;
42636304Skarels 		endusershell();
427*55258Sandrew 
42840183Smckusick 		if (cp == NULL || checkuser(name)) {
42936304Skarels 			reply(530, "User %s access denied.", name);
43036933Skarels 			if (logging)
43136933Skarels 				syslog(LOG_NOTICE,
43236933Skarels 				    "FTP LOGIN REFUSED FROM %s, %s",
43336933Skarels 				    remotehost, name);
43436304Skarels 			pw = (struct passwd *) NULL;
43536304Skarels 			return;
43636304Skarels 		}
43736304Skarels 	}
438*55258Sandrew 	if (logging)
439*55258Sandrew 		strncpy(curname, name, sizeof(curname)-1);
44036304Skarels 	reply(331, "Password required for %s.", name);
44136304Skarels 	askpasswd = 1;
44236304Skarels 	/*
44336304Skarels 	 * Delay before reading passwd after first failed
44436304Skarels 	 * attempt to slow down passwd-guessing programs.
44536304Skarels 	 */
44636304Skarels 	if (login_attempts)
44736304Skarels 		sleep((unsigned) login_attempts);
44836304Skarels }
44936304Skarels 
45036304Skarels /*
45140183Smckusick  * Check if a user is in the file _PATH_FTPUSERS
45240183Smckusick  */
453*55258Sandrew static int
45440183Smckusick checkuser(name)
45540183Smckusick 	char *name;
45640183Smckusick {
45742327Sbostic 	register FILE *fd;
45842327Sbostic 	register char *p;
45942327Sbostic 	char line[BUFSIZ];
460*55258Sandrew 	int found = 0;
46140183Smckusick 
46240183Smckusick 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
46342327Sbostic 		while (fgets(line, sizeof(line), fd) != NULL)
46442327Sbostic 			if ((p = index(line, '\n')) != NULL) {
46542327Sbostic 				*p = '\0';
46642327Sbostic 				if (line[0] == '#')
46742327Sbostic 					continue;
468*55258Sandrew 				if (strcmp(p, name) == 0) {
469*55258Sandrew 					found = 1;
470*55258Sandrew 					break;
471*55258Sandrew 				}
47242327Sbostic 			}
47340183Smckusick 		(void) fclose(fd);
47440183Smckusick 	}
475*55258Sandrew 	return (found);
47640183Smckusick }
47740183Smckusick 
47840183Smckusick /*
47936304Skarels  * Terminate login as previous user, if any, resetting state;
48036304Skarels  * used when USER command is given or login fails.
48136304Skarels  */
482*55258Sandrew static void
48336304Skarels end_login()
48436304Skarels {
48536304Skarels 
48636304Skarels 	(void) seteuid((uid_t)0);
48736304Skarels 	if (logged_in)
48836304Skarels 		logwtmp(ttyline, "", "");
48936304Skarels 	pw = NULL;
49036304Skarels 	logged_in = 0;
49136304Skarels 	guest = 0;
49236304Skarels }
49336304Skarels 
494*55258Sandrew void
49510275Ssam pass(passwd)
49610275Ssam 	char *passwd;
49710275Ssam {
49836304Skarels 	char *xpasswd, *salt;
499*55258Sandrew 	FILE *fd;
50010275Ssam 
50136304Skarels 	if (logged_in || askpasswd == 0) {
50210275Ssam 		reply(503, "Login with USER first.");
50310275Ssam 		return;
50410275Ssam 	}
50536304Skarels 	askpasswd = 0;
50610275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
50736304Skarels 		if (pw == NULL)
50836304Skarels 			salt = "xx";
50936304Skarels 		else
51036304Skarels 			salt = pw->pw_passwd;
51136304Skarels 		xpasswd = crypt(passwd, salt);
51216760Slepreau 		/* The strcmp does not catch null passwords! */
51336304Skarels 		if (pw == NULL || *pw->pw_passwd == '\0' ||
51436304Skarels 		    strcmp(xpasswd, pw->pw_passwd)) {
51510275Ssam 			reply(530, "Login incorrect.");
516*55258Sandrew 			if (logging)
517*55258Sandrew 				syslog(LOG_NOTICE,
518*55258Sandrew 				    "FTP LOGIN FAILED FROM %s, %s",
519*55258Sandrew 				    remotehost, curname);
52010275Ssam 			pw = NULL;
52136304Skarels 			if (login_attempts++ >= 5) {
52236933Skarels 				syslog(LOG_NOTICE,
52336304Skarels 				    "repeated login failures from %s",
52436304Skarels 				    remotehost);
52536304Skarels 				exit(0);
52636304Skarels 			}
52710275Ssam 			return;
52810275Ssam 		}
52910275Ssam 	}
53036304Skarels 	login_attempts = 0;		/* this time successful */
531*55258Sandrew 	if (setegid((gid_t)pw->pw_gid) < 0) {
532*55258Sandrew 		reply(550, "Can't set gid.");
533*55258Sandrew 		return;
534*55258Sandrew 	}
53536304Skarels 	(void) initgroups(pw->pw_name, pw->pw_gid);
53616033Sralph 
53736192Sbostic 	/* open wtmp before chroot */
53836192Sbostic 	(void)sprintf(ttyline, "ftp%d", getpid());
53936192Sbostic 	logwtmp(ttyline, pw->pw_name, remotehost);
54036192Sbostic 	logged_in = 1;
54136192Sbostic 
54236446Sbostic 	if (guest) {
54336620Srick 		/*
54436933Skarels 		 * We MUST do a chdir() after the chroot. Otherwise
54536933Skarels 		 * the old current directory will be accessible as "."
54636933Skarels 		 * outside the new root!
54736620Srick 		 */
54836620Srick 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
54936446Sbostic 			reply(550, "Can't set guest privileges.");
55036446Sbostic 			goto bad;
55136446Sbostic 		}
55236933Skarels 	} else if (chdir(pw->pw_dir) < 0) {
55336620Srick 		if (chdir("/") < 0) {
55436446Sbostic 			reply(530, "User %s: can't change directory to %s.",
55536446Sbostic 			    pw->pw_name, pw->pw_dir);
55636446Sbostic 			goto bad;
55736933Skarels 		} else
55836446Sbostic 			lreply(230, "No directory! Logging in with home=/");
55936620Srick 	}
56036304Skarels 	if (seteuid((uid_t)pw->pw_uid) < 0) {
56136304Skarels 		reply(550, "Can't set uid.");
56236304Skarels 		goto bad;
56336304Skarels 	}
564*55258Sandrew 	/*
565*55258Sandrew 	 * Display a login message, if it exists.
566*55258Sandrew 	 * N.B. reply(230,) must follow the message.
567*55258Sandrew 	 */
568*55258Sandrew 	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
569*55258Sandrew 		char *cp, line[MAXLINE];
570*55258Sandrew 
571*55258Sandrew 		while (fgets(line, sizeof (line), fd) != NULL) {
572*55258Sandrew 			if ((cp = index(line, '\n')) != NULL)
573*55258Sandrew 				*cp = '\0';
574*55258Sandrew 			lreply(230, "%s", line);
575*55258Sandrew 		}
576*55258Sandrew 		(void) fflush(stdout);
577*55258Sandrew 		(void) fclose(fd);
578*55258Sandrew 	}
57936550Sbostic 	if (guest) {
58036192Sbostic 		reply(230, "Guest login ok, access restrictions apply.");
58136933Skarels #ifdef SETPROCTITLE
58236933Skarels 		sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
58336933Skarels 		    sizeof(proctitle) - sizeof(remotehost) -
58436933Skarels 		    sizeof(": anonymous/"), passwd);
58536933Skarels 		setproctitle(proctitle);
58636933Skarels #endif /* SETPROCTITLE */
58736933Skarels 		if (logging)
58836933Skarels 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
58936933Skarels 			    remotehost, passwd);
59052998Sbostic 		home = "/";		/* guest home dir for globbing */
59136933Skarels 	} else {
59210275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
59336933Skarels #ifdef SETPROCTITLE
59436933Skarels 		sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
59536933Skarels 		setproctitle(proctitle);
59636933Skarels #endif /* SETPROCTITLE */
59736933Skarels 		if (logging)
598*55258Sandrew 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
59936933Skarels 			    remotehost, pw->pw_name);
60052998Sbostic 		home = pw->pw_dir;	/* home dir for globbing */
60136550Sbostic 	}
60236933Skarels 	(void) umask(defumask);
60310303Ssam 	return;
60410303Ssam bad:
60536304Skarels 	/* Forget all about it... */
60636304Skarels 	end_login();
60710275Ssam }
60810275Ssam 
609*55258Sandrew void
61010275Ssam retrieve(cmd, name)
61110275Ssam 	char *cmd, *name;
61210275Ssam {
61310275Ssam 	FILE *fin, *dout;
61410275Ssam 	struct stat st;
61536620Srick 	int (*closefunc)();
61610275Ssam 
61736557Sbostic 	if (cmd == 0) {
61836446Sbostic 		fin = fopen(name, "r"), closefunc = fclose;
61936557Sbostic 		st.st_size = 0;
62036557Sbostic 	} else {
62110275Ssam 		char line[BUFSIZ];
62210275Ssam 
62326493Sminshall 		(void) sprintf(line, cmd, name), name = line;
62436304Skarels 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
62536557Sbostic 		st.st_size = -1;
62636933Skarels 		st.st_blksize = BUFSIZ;
62710275Ssam 	}
62810275Ssam 	if (fin == NULL) {
629*55258Sandrew 		if (errno != 0) {
63036304Skarels 			perror_reply(550, name);
631*55258Sandrew 			if (cmd == 0) {
632*55258Sandrew 				LOGCMD("get", name);
633*55258Sandrew 			}
634*55258Sandrew 		}
63510275Ssam 		return;
63610275Ssam 	}
637*55258Sandrew 	byte_count = -1;
63810275Ssam 	if (cmd == 0 &&
63936933Skarels 	    (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
64010275Ssam 		reply(550, "%s: not a plain file.", name);
64110275Ssam 		goto done;
64210275Ssam 	}
64337459Skarels 	if (restart_point) {
64437459Skarels 		if (type == TYPE_A) {
64537459Skarels 			register int i, n, c;
64637459Skarels 
64737459Skarels 			n = restart_point;
64837459Skarels 			i = 0;
64937459Skarels 			while (i++ < n) {
65037459Skarels 				if ((c=getc(fin)) == EOF) {
65137459Skarels 					perror_reply(550, name);
65237459Skarels 					goto done;
65337459Skarels 				}
65437459Skarels 				if (c == '\n')
65537459Skarels 					i++;
65652998Sbostic 			}
65737459Skarels 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
65837459Skarels 			perror_reply(550, name);
65937459Skarels 			goto done;
66037459Skarels 		}
66137459Skarels 	}
66210275Ssam 	dout = dataconn(name, st.st_size, "w");
66310275Ssam 	if (dout == NULL)
66410275Ssam 		goto done;
66536620Srick 	send_data(fin, dout, st.st_blksize);
66626493Sminshall 	(void) fclose(dout);
66726044Sminshall 	data = -1;
66826044Sminshall 	pdata = -1;
66910275Ssam done:
670*55258Sandrew 	if (cmd == 0)
671*55258Sandrew 		LOGBYTES("get", name, byte_count);
67210275Ssam 	(*closefunc)(fin);
67310275Ssam }
67410275Ssam 
675*55258Sandrew void
67636304Skarels store(name, mode, unique)
67710275Ssam 	char *name, *mode;
67836304Skarels 	int unique;
67910275Ssam {
68010275Ssam 	FILE *fout, *din;
68136446Sbostic 	struct stat st;
68236620Srick 	int (*closefunc)();
68310275Ssam 
68436446Sbostic 	if (unique && stat(name, &st) == 0 &&
685*55258Sandrew 	    (name = gunique(name)) == NULL) {
686*55258Sandrew 		LOGCMD(*mode == 'w' ? "put" : "append", name);
68736446Sbostic 		return;
688*55258Sandrew 	}
68910303Ssam 
69037459Skarels 	if (restart_point)
69137459Skarels 		mode = "r+w";
69236620Srick 	fout = fopen(name, mode);
69336620Srick 	closefunc = fclose;
69410275Ssam 	if (fout == NULL) {
69536304Skarels 		perror_reply(553, name);
696*55258Sandrew 		LOGCMD(*mode == 'w' ? "put" : "append", name);
69710275Ssam 		return;
69810275Ssam 	}
699*55258Sandrew 	byte_count = -1;
70037459Skarels 	if (restart_point) {
70137459Skarels 		if (type == TYPE_A) {
70237459Skarels 			register int i, n, c;
70337459Skarels 
70437459Skarels 			n = restart_point;
70537459Skarels 			i = 0;
70637459Skarels 			while (i++ < n) {
70737459Skarels 				if ((c=getc(fout)) == EOF) {
70837459Skarels 					perror_reply(550, name);
70937459Skarels 					goto done;
71037459Skarels 				}
71137459Skarels 				if (c == '\n')
71237459Skarels 					i++;
71352998Sbostic 			}
71437459Skarels 			/*
71537459Skarels 			 * We must do this seek to "current" position
71637459Skarels 			 * because we are changing from reading to
71737459Skarels 			 * writing.
71837459Skarels 			 */
71937459Skarels 			if (fseek(fout, 0L, L_INCR) < 0) {
72037459Skarels 				perror_reply(550, name);
72137459Skarels 				goto done;
72237459Skarels 			}
72337459Skarels 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
72437459Skarels 			perror_reply(550, name);
72537459Skarels 			goto done;
72637459Skarels 		}
72737459Skarels 	}
72836304Skarels 	din = dataconn(name, (off_t)-1, "r");
72910275Ssam 	if (din == NULL)
73010275Ssam 		goto done;
73136620Srick 	if (receive_data(din, fout) == 0) {
73236620Srick 		if (unique)
73336304Skarels 			reply(226, "Transfer complete (unique file name:%s).",
73436933Skarels 			    name);
73536304Skarels 		else
73636304Skarels 			reply(226, "Transfer complete.");
73726044Sminshall 	}
73826493Sminshall 	(void) fclose(din);
73926044Sminshall 	data = -1;
74026044Sminshall 	pdata = -1;
74110275Ssam done:
742*55258Sandrew 	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
74310275Ssam 	(*closefunc)(fout);
74410275Ssam }
74510275Ssam 
746*55258Sandrew static FILE *
74710275Ssam getdatasock(mode)
74810275Ssam 	char *mode;
74910275Ssam {
75037459Skarels 	int s, on = 1, tries;
751*55258Sandrew 	int t;
75210275Ssam 
75310275Ssam 	if (data >= 0)
75410275Ssam 		return (fdopen(data, mode));
75550391Skarels 	(void) seteuid((uid_t)0);
75613247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
75710602Ssam 	if (s < 0)
75850391Skarels 		goto bad;
75937459Skarels 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
76037459Skarels 	    (char *) &on, sizeof (on)) < 0)
76110602Ssam 		goto bad;
76213152Ssam 	/* anchor socket to avoid multi-homing problems */
76313152Ssam 	data_source.sin_family = AF_INET;
76413152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
76537459Skarels 	for (tries = 1; ; tries++) {
76637459Skarels 		if (bind(s, (struct sockaddr *)&data_source,
76737459Skarels 		    sizeof (data_source)) >= 0)
76837459Skarels 			break;
76937459Skarels 		if (errno != EADDRINUSE || tries > 10)
77037459Skarels 			goto bad;
77137459Skarels 		sleep(tries);
77237459Skarels 	}
77336304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
77444339Skarels #ifdef IP_TOS
77544339Skarels 	on = IPTOS_THROUGHPUT;
77644339Skarels 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
77744339Skarels 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
77844339Skarels #endif
77910275Ssam 	return (fdopen(s, mode));
78010602Ssam bad:
781*55258Sandrew 	/* Return the real value of errno (close may change it) */
782*55258Sandrew 	t = errno;
78336304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
78426493Sminshall 	(void) close(s);
785*55258Sandrew 	errno = t;
78610602Ssam 	return (NULL);
78710275Ssam }
78810275Ssam 
789*55258Sandrew static FILE *
79010275Ssam dataconn(name, size, mode)
79110275Ssam 	char *name;
79211653Ssam 	off_t size;
79310275Ssam 	char *mode;
79410275Ssam {
79510275Ssam 	char sizebuf[32];
79610275Ssam 	FILE *file;
79744339Skarels 	int retry = 0, tos;
79810275Ssam 
79936933Skarels 	file_size = size;
80036933Skarels 	byte_count = 0;
80136304Skarels 	if (size != (off_t) -1)
802*55258Sandrew 		(void) sprintf (sizebuf, " (%qd bytes)", size);
80310275Ssam 	else
80410275Ssam 		(void) strcpy(sizebuf, "");
80536304Skarels 	if (pdata >= 0) {
80626044Sminshall 		struct sockaddr_in from;
80726044Sminshall 		int s, fromlen = sizeof(from);
80826044Sminshall 
80936304Skarels 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
81026044Sminshall 		if (s < 0) {
81126044Sminshall 			reply(425, "Can't open data connection.");
81226044Sminshall 			(void) close(pdata);
81326044Sminshall 			pdata = -1;
81426044Sminshall 			return(NULL);
81526044Sminshall 		}
81626044Sminshall 		(void) close(pdata);
81726044Sminshall 		pdata = s;
81844339Skarels #ifdef IP_TOS
81944339Skarels 		tos = IPTOS_LOWDELAY;
82044339Skarels 		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
82144339Skarels 		    sizeof(int));
82244339Skarels #endif
823*55258Sandrew 		reply(150, "Opening %s mode data connection for '%s'%s.",
82436235Skarels 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
82526044Sminshall 		return(fdopen(pdata, mode));
82626044Sminshall 	}
82710275Ssam 	if (data >= 0) {
828*55258Sandrew 		reply(125, "Using existing data connection for '%s'%s.",
82910275Ssam 		    name, sizebuf);
83010321Ssam 		usedefault = 1;
83110275Ssam 		return (fdopen(data, mode));
83210275Ssam 	}
83310566Ssam 	if (usedefault)
83410422Ssam 		data_dest = his_addr;
83510422Ssam 	usedefault = 1;
83610275Ssam 	file = getdatasock(mode);
83710275Ssam 	if (file == NULL) {
83810275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
83913247Ssam 		    inet_ntoa(data_source.sin_addr),
84042412Sbostic 		    ntohs(data_source.sin_port), strerror(errno));
84110275Ssam 		return (NULL);
84210275Ssam 	}
84310275Ssam 	data = fileno(file);
84436304Skarels 	while (connect(data, (struct sockaddr *)&data_dest,
84536304Skarels 	    sizeof (data_dest)) < 0) {
84611653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
84726493Sminshall 			sleep((unsigned) swaitint);
84811653Ssam 			retry += swaitint;
84911653Ssam 			continue;
85011653Ssam 		}
85136304Skarels 		perror_reply(425, "Can't build data connection");
85210275Ssam 		(void) fclose(file);
85310275Ssam 		data = -1;
85410275Ssam 		return (NULL);
85510275Ssam 	}
856*55258Sandrew 	reply(150, "Opening %s mode data connection for '%s'%s.",
85736235Skarels 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
85810275Ssam 	return (file);
85910275Ssam }
86010275Ssam 
86110275Ssam /*
862*55258Sandrew  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
863*55258Sandrew  * encapsulation of the data subject * to Mode, Structure, and Type.
86410275Ssam  *
86510275Ssam  * NB: Form isn't handled.
86610275Ssam  */
867*55258Sandrew static void
86836446Sbostic send_data(instr, outstr, blksize)
86910275Ssam 	FILE *instr, *outstr;
87036446Sbostic 	off_t blksize;
87110275Ssam {
87236446Sbostic 	register int c, cnt;
87336446Sbostic 	register char *buf;
87436446Sbostic 	int netfd, filefd;
87510275Ssam 
87626044Sminshall 	transflag++;
87726044Sminshall 	if (setjmp(urgcatch)) {
87826044Sminshall 		transflag = 0;
87936620Srick 		return;
88026044Sminshall 	}
88110275Ssam 	switch (type) {
88210275Ssam 
88310275Ssam 	case TYPE_A:
88410275Ssam 		while ((c = getc(instr)) != EOF) {
88536933Skarels 			byte_count++;
88611220Ssam 			if (c == '\n') {
88736933Skarels 				if (ferror(outstr))
88836620Srick 					goto data_err;
88927750Sminshall 				(void) putc('\r', outstr);
89011220Ssam 			}
89127750Sminshall 			(void) putc(c, outstr);
89210275Ssam 		}
89336933Skarels 		fflush(outstr);
89426044Sminshall 		transflag = 0;
89536933Skarels 		if (ferror(instr))
89636933Skarels 			goto file_err;
89736933Skarels 		if (ferror(outstr))
89836620Srick 			goto data_err;
89936620Srick 		reply(226, "Transfer complete.");
90036620Srick 		return;
90136446Sbostic 
90210275Ssam 	case TYPE_I:
90310275Ssam 	case TYPE_L:
90436446Sbostic 		if ((buf = malloc((u_int)blksize)) == NULL) {
90536446Sbostic 			transflag = 0;
90636933Skarels 			perror_reply(451, "Local resource failure: malloc");
90736933Skarels 			return;
90836446Sbostic 		}
90910275Ssam 		netfd = fileno(outstr);
91010275Ssam 		filefd = fileno(instr);
91136933Skarels 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
91236620Srick 		    write(netfd, buf, cnt) == cnt)
91336933Skarels 			byte_count += cnt;
91426044Sminshall 		transflag = 0;
91536446Sbostic 		(void)free(buf);
91636933Skarels 		if (cnt != 0) {
91736933Skarels 			if (cnt < 0)
91836933Skarels 				goto file_err;
91936620Srick 			goto data_err;
92036933Skarels 		}
92136620Srick 		reply(226, "Transfer complete.");
92236620Srick 		return;
92336620Srick 	default:
92436620Srick 		transflag = 0;
92536620Srick 		reply(550, "Unimplemented TYPE %d in send_data", type);
92636620Srick 		return;
92710275Ssam 	}
92836620Srick 
92936620Srick data_err:
93026044Sminshall 	transflag = 0;
93136933Skarels 	perror_reply(426, "Data connection");
93236933Skarels 	return;
93336933Skarels 
93436933Skarels file_err:
93536933Skarels 	transflag = 0;
93636933Skarels 	perror_reply(551, "Error on input file");
93710275Ssam }
93810275Ssam 
93910275Ssam /*
940*55258Sandrew  * Transfer data from peer to "outstr" using the appropriate encapulation of
941*55258Sandrew  * the data subject to Mode, Structure, and Type.
94210275Ssam  *
94310275Ssam  * N.B.: Form isn't handled.
94410275Ssam  */
945*55258Sandrew static int
94610275Ssam receive_data(instr, outstr)
94710275Ssam 	FILE *instr, *outstr;
94810275Ssam {
94910275Ssam 	register int c;
95038134Srick 	int cnt, bare_lfs = 0;
95110275Ssam 	char buf[BUFSIZ];
95210275Ssam 
95326044Sminshall 	transflag++;
95426044Sminshall 	if (setjmp(urgcatch)) {
95526044Sminshall 		transflag = 0;
95636933Skarels 		return (-1);
95726044Sminshall 	}
95810275Ssam 	switch (type) {
95910275Ssam 
96010275Ssam 	case TYPE_I:
96110275Ssam 	case TYPE_L:
96226044Sminshall 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
96336620Srick 			if (write(fileno(outstr), buf, cnt) != cnt)
96436933Skarels 				goto file_err;
96536933Skarels 			byte_count += cnt;
96626044Sminshall 		}
96736933Skarels 		if (cnt < 0)
96836620Srick 			goto data_err;
96926044Sminshall 		transflag = 0;
97036933Skarels 		return (0);
97110275Ssam 
97210275Ssam 	case TYPE_E:
97327106Smckusick 		reply(553, "TYPE E not implemented.");
97426044Sminshall 		transflag = 0;
97527106Smckusick 		return (-1);
97610275Ssam 
97710275Ssam 	case TYPE_A:
97810275Ssam 		while ((c = getc(instr)) != EOF) {
97936933Skarels 			byte_count++;
98038134Srick 			if (c == '\n')
98138134Srick 				bare_lfs++;
98227750Sminshall 			while (c == '\r') {
98336933Skarels 				if (ferror(outstr))
98436620Srick 					goto data_err;
98536933Skarels 				if ((c = getc(instr)) != '\n') {
98627750Sminshall 					(void) putc ('\r', outstr);
98736933Skarels 					if (c == '\0' || c == EOF)
98836933Skarels 						goto contin2;
98936933Skarels 				}
99010275Ssam 			}
99136933Skarels 			(void) putc(c, outstr);
99236933Skarels 	contin2:	;
99310275Ssam 		}
99436620Srick 		fflush(outstr);
99536933Skarels 		if (ferror(instr))
99636620Srick 			goto data_err;
99736933Skarels 		if (ferror(outstr))
99836933Skarels 			goto file_err;
99926044Sminshall 		transflag = 0;
100038134Srick 		if (bare_lfs) {
100138134Srick 			lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
100238134Srick 			printf("   File may not have transferred correctly.\r\n");
100338134Srick 		}
100410275Ssam 		return (0);
100536620Srick 	default:
100636620Srick 		reply(550, "Unimplemented TYPE %d in receive_data", type);
100736620Srick 		transflag = 0;
100836933Skarels 		return (-1);
100910275Ssam 	}
101036620Srick 
101136620Srick data_err:
101226044Sminshall 	transflag = 0;
101336933Skarels 	perror_reply(426, "Data Connection");
101436933Skarels 	return (-1);
101536933Skarels 
101636933Skarels file_err:
101736933Skarels 	transflag = 0;
101836933Skarels 	perror_reply(452, "Error writing file");
101936933Skarels 	return (-1);
102010275Ssam }
102110275Ssam 
1022*55258Sandrew void
102336933Skarels statfilecmd(filename)
102436933Skarels 	char *filename;
102536933Skarels {
102636933Skarels 	char line[BUFSIZ];
102736933Skarels 	FILE *fin;
102836933Skarels 	int c;
102936933Skarels 
1030*55258Sandrew 	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
103136933Skarels 	fin = ftpd_popen(line, "r");
103236933Skarels 	lreply(211, "status of %s:", filename);
103336933Skarels 	while ((c = getc(fin)) != EOF) {
103436933Skarels 		if (c == '\n') {
103536933Skarels 			if (ferror(stdout)){
103636933Skarels 				perror_reply(421, "control connection");
103736933Skarels 				(void) ftpd_pclose(fin);
103836933Skarels 				dologout(1);
103936933Skarels 				/* NOTREACHED */
104036933Skarels 			}
104136933Skarels 			if (ferror(fin)) {
104236933Skarels 				perror_reply(551, filename);
104336933Skarels 				(void) ftpd_pclose(fin);
104436933Skarels 				return;
104536933Skarels 			}
104636933Skarels 			(void) putc('\r', stdout);
104736933Skarels 		}
104836933Skarels 		(void) putc(c, stdout);
104936933Skarels 	}
105036933Skarels 	(void) ftpd_pclose(fin);
105136933Skarels 	reply(211, "End of Status");
105236933Skarels }
105336933Skarels 
1054*55258Sandrew void
105536933Skarels statcmd()
105636933Skarels {
105736933Skarels 	struct sockaddr_in *sin;
105836933Skarels 	u_char *a, *p;
105936933Skarels 
106036933Skarels 	lreply(211, "%s FTP server status:", hostname, version);
106136933Skarels 	printf("     %s\r\n", version);
106236933Skarels 	printf("     Connected to %s", remotehost);
106338134Srick 	if (!isdigit(remotehost[0]))
106436933Skarels 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
106536933Skarels 	printf("\r\n");
106636933Skarels 	if (logged_in) {
106736933Skarels 		if (guest)
106836933Skarels 			printf("     Logged in anonymously\r\n");
106936933Skarels 		else
107036933Skarels 			printf("     Logged in as %s\r\n", pw->pw_name);
107136933Skarels 	} else if (askpasswd)
107236933Skarels 		printf("     Waiting for password\r\n");
107336933Skarels 	else
107436933Skarels 		printf("     Waiting for user name\r\n");
107536933Skarels 	printf("     TYPE: %s", typenames[type]);
107636933Skarels 	if (type == TYPE_A || type == TYPE_E)
107736933Skarels 		printf(", FORM: %s", formnames[form]);
107836933Skarels 	if (type == TYPE_L)
107936933Skarels #if NBBY == 8
108036933Skarels 		printf(" %d", NBBY);
108136933Skarels #else
108236933Skarels 		printf(" %d", bytesize);	/* need definition! */
108336933Skarels #endif
108436933Skarels 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
108536933Skarels 	    strunames[stru], modenames[mode]);
108636933Skarels 	if (data != -1)
108736933Skarels 		printf("     Data connection open\r\n");
108836933Skarels 	else if (pdata != -1) {
108936933Skarels 		printf("     in Passive mode");
109036933Skarels 		sin = &pasv_addr;
109136933Skarels 		goto printaddr;
109236933Skarels 	} else if (usedefault == 0) {
109336933Skarels 		printf("     PORT");
109436933Skarels 		sin = &data_dest;
109536933Skarels printaddr:
109636933Skarels 		a = (u_char *) &sin->sin_addr;
109736933Skarels 		p = (u_char *) &sin->sin_port;
109836933Skarels #define UC(b) (((int) b) & 0xff)
109936933Skarels 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
110036933Skarels 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
110136933Skarels #undef UC
110236933Skarels 	} else
110336933Skarels 		printf("     No data connection\r\n");
110436933Skarels 	reply(211, "End of status");
110536933Skarels }
110636933Skarels 
1107*55258Sandrew void
110810275Ssam fatal(s)
110910275Ssam 	char *s;
111010275Ssam {
111110275Ssam 	reply(451, "Error in server: %s\n", s);
111210275Ssam 	reply(221, "Closing connection due to server error.");
111313247Ssam 	dologout(0);
111436620Srick 	/* NOTREACHED */
111510275Ssam }
111610275Ssam 
1117*55258Sandrew void
1118*55258Sandrew #if __STDC__
1119*55258Sandrew reply(int n, const char *fmt, ...)
1120*55258Sandrew #else
1121*55258Sandrew reply(n, fmt, va_alist)
112210275Ssam 	int n;
112336446Sbostic 	char *fmt;
1124*55258Sandrew         va_dcl
1125*55258Sandrew #endif
112610275Ssam {
1127*55258Sandrew 	va_list ap;
1128*55258Sandrew #if __STDC__
1129*55258Sandrew 	va_start(ap, fmt);
1130*55258Sandrew #else
1131*55258Sandrew 	va_start(ap);
1132*55258Sandrew #endif
1133*55258Sandrew 	(void)printf("%d ", n);
1134*55258Sandrew 	(void)vprintf(fmt, ap);
1135*55258Sandrew 	(void)printf("\r\n");
113636435Sbostic 	(void)fflush(stdout);
113710275Ssam 	if (debug) {
113826493Sminshall 		syslog(LOG_DEBUG, "<--- %d ", n);
1139*55258Sandrew 		vsyslog(LOG_DEBUG, fmt, ap);
1140*55258Sandrew 	}
114110275Ssam }
114210275Ssam 
1143*55258Sandrew void
1144*55258Sandrew #if __STDC__
1145*55258Sandrew lreply(int n, const char *fmt, ...)
1146*55258Sandrew #else
1147*55258Sandrew lreply(n, fmt, va_alist)
114810275Ssam 	int n;
114936446Sbostic 	char *fmt;
1150*55258Sandrew         va_dcl
1151*55258Sandrew #endif
115210275Ssam {
1153*55258Sandrew 	va_list ap;
1154*55258Sandrew #if __STDC__
1155*55258Sandrew 	va_start(ap, fmt);
1156*55258Sandrew #else
1157*55258Sandrew 	va_start(ap);
1158*55258Sandrew #endif
1159*55258Sandrew 	(void)printf("%d- ", n);
1160*55258Sandrew 	(void)vprintf(fmt, ap);
1161*55258Sandrew 	(void)printf("\r\n");
116236435Sbostic 	(void)fflush(stdout);
116336446Sbostic 	if (debug) {
116436446Sbostic 		syslog(LOG_DEBUG, "<--- %d- ", n);
1165*55258Sandrew 		vsyslog(LOG_DEBUG, fmt, ap);
116636446Sbostic 	}
116710275Ssam }
116810275Ssam 
1169*55258Sandrew static void
117010275Ssam ack(s)
117110275Ssam 	char *s;
117210275Ssam {
117327106Smckusick 	reply(250, "%s command successful.", s);
117410275Ssam }
117510275Ssam 
1176*55258Sandrew void
117710275Ssam nack(s)
117810275Ssam 	char *s;
117910275Ssam {
118010275Ssam 	reply(502, "%s command not implemented.", s);
118110275Ssam }
118210275Ssam 
118336304Skarels /* ARGSUSED */
1184*55258Sandrew char *
118526493Sminshall yyerror(s)
118626493Sminshall 	char *s;
118710275Ssam {
118826044Sminshall 	char *cp;
118926044Sminshall 
119036551Sbostic 	if (cp = index(cbuf,'\n'))
119136551Sbostic 		*cp = '\0';
119236933Skarels 	reply(500, "'%s': command not understood.", cbuf);
119310275Ssam }
119410275Ssam 
1195*55258Sandrew void
119610275Ssam delete(name)
119710275Ssam 	char *name;
119810275Ssam {
119910275Ssam 	struct stat st;
120010275Ssam 
1201*55258Sandrew 	LOGCMD("delete", name);
120210275Ssam 	if (stat(name, &st) < 0) {
120336304Skarels 		perror_reply(550, name);
120410275Ssam 		return;
120510275Ssam 	}
120610275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
120710275Ssam 		if (rmdir(name) < 0) {
120836304Skarels 			perror_reply(550, name);
120910275Ssam 			return;
121010275Ssam 		}
121110275Ssam 		goto done;
121210275Ssam 	}
121310275Ssam 	if (unlink(name) < 0) {
121436304Skarels 		perror_reply(550, name);
121510275Ssam 		return;
121610275Ssam 	}
121710275Ssam done:
121810275Ssam 	ack("DELE");
121910275Ssam }
122010275Ssam 
1221*55258Sandrew void
122210275Ssam cwd(path)
122310275Ssam 	char *path;
122410275Ssam {
122536620Srick 	if (chdir(path) < 0)
122636304Skarels 		perror_reply(550, path);
122736620Srick 	else
122836620Srick 		ack("CWD");
122910275Ssam }
123010275Ssam 
1231*55258Sandrew void
123210303Ssam makedir(name)
123310275Ssam 	char *name;
123410275Ssam {
1235*55258Sandrew 	LOGCMD("mkdir", name);
123636276Sbostic 	if (mkdir(name, 0777) < 0)
123736304Skarels 		perror_reply(550, name);
123836276Sbostic 	else
123936276Sbostic 		reply(257, "MKD command successful.");
124010275Ssam }
124110275Ssam 
1242*55258Sandrew void
124310303Ssam removedir(name)
124410275Ssam 	char *name;
124510275Ssam {
1246*55258Sandrew 	LOGCMD("rmdir", name);
124736620Srick 	if (rmdir(name) < 0)
124836304Skarels 		perror_reply(550, name);
124936620Srick 	else
125036620Srick 		ack("RMD");
125110275Ssam }
125210275Ssam 
1253*55258Sandrew void
125410303Ssam pwd()
125510275Ssam {
125610303Ssam 	char path[MAXPATHLEN + 1];
125710275Ssam 
125836620Srick 	if (getwd(path) == (char *)NULL)
125927106Smckusick 		reply(550, "%s.", path);
126036620Srick 	else
126136620Srick 		reply(257, "\"%s\" is current directory.", path);
126210275Ssam }
126310275Ssam 
126410275Ssam char *
126510275Ssam renamefrom(name)
126610275Ssam 	char *name;
126710275Ssam {
126810275Ssam 	struct stat st;
126910275Ssam 
127010275Ssam 	if (stat(name, &st) < 0) {
127136304Skarels 		perror_reply(550, name);
127210275Ssam 		return ((char *)0);
127310275Ssam 	}
127410303Ssam 	reply(350, "File exists, ready for destination name");
127510275Ssam 	return (name);
127610275Ssam }
127710275Ssam 
1278*55258Sandrew void
127910275Ssam renamecmd(from, to)
128010275Ssam 	char *from, *to;
128110275Ssam {
1282*55258Sandrew 	LOGCMD2("rename", from, to);
128336620Srick 	if (rename(from, to) < 0)
128436304Skarels 		perror_reply(550, "rename");
128536620Srick 	else
128636620Srick 		ack("RNTO");
128710275Ssam }
128810275Ssam 
1289*55258Sandrew static void
129010275Ssam dolog(sin)
129110275Ssam 	struct sockaddr_in *sin;
129210275Ssam {
129336304Skarels 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
129410275Ssam 		sizeof (struct in_addr), AF_INET);
129510275Ssam 
129636304Skarels 	if (hp)
129726493Sminshall 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
129836304Skarels 	else
129926493Sminshall 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
130013247Ssam 		    sizeof (remotehost));
130136620Srick #ifdef SETPROCTITLE
130236933Skarels 	sprintf(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  */
1314*55258Sandrew void
131513247Ssam dologout(status)
131613247Ssam 	int status;
131713247Ssam {
131817580Ssam 	if (logged_in) {
131936304Skarels 		(void) seteuid((uid_t)0);
132035672Sbostic 		logwtmp(ttyline, "", "");
132113247Ssam 	}
132214436Ssam 	/* beware of flushing buffers after a SIGPIPE */
132314436Ssam 	_exit(status);
132413247Ssam }
132513247Ssam 
1326*55258Sandrew static void
1327*55258Sandrew myoob(signo)
1328*55258Sandrew 	int signo;
132926044Sminshall {
133027750Sminshall 	char *cp;
133126044Sminshall 
133227750Sminshall 	/* only process if transfer occurring */
133336304Skarels 	if (!transflag)
133426044Sminshall 		return;
133527750Sminshall 	cp = tmpline;
133627750Sminshall 	if (getline(cp, 7, stdin) == NULL) {
133736304Skarels 		reply(221, "You could at least say goodbye.");
133827750Sminshall 		dologout(0);
133926044Sminshall 	}
134026044Sminshall 	upper(cp);
134136933Skarels 	if (strcmp(cp, "ABOR\r\n") == 0) {
134236933Skarels 		tmpline[0] = '\0';
134336933Skarels 		reply(426, "Transfer aborted. Data connection closed.");
134436933Skarels 		reply(226, "Abort successful");
134536933Skarels 		longjmp(urgcatch, 1);
134636933Skarels 	}
134736933Skarels 	if (strcmp(cp, "STAT\r\n") == 0) {
134836933Skarels 		if (file_size != (off_t) -1)
1349*55258Sandrew 			reply(213, "Status: %qd of %qd bytes transferred",
135036933Skarels 			    byte_count, file_size);
135136933Skarels 		else
1352*55258Sandrew 			reply(213, "Status: %qd bytes transferred", byte_count);
135336933Skarels 	}
135426044Sminshall }
135526044Sminshall 
135627106Smckusick /*
135736620Srick  * Note: a response of 425 is not mentioned as a possible response to
135852998Sbostic  *	the PASV command in RFC959. However, it has been blessed as
135952998Sbostic  *	a legitimate response by Jon Postel in a telephone conversation
136036620Srick  *	with Rick Adams on 25 Jan 89.
136127106Smckusick  */
1362*55258Sandrew void
136326044Sminshall passive()
136426044Sminshall {
136526044Sminshall 	int len;
136626044Sminshall 	register char *p, *a;
136726044Sminshall 
136826044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
136926044Sminshall 	if (pdata < 0) {
137036620Srick 		perror_reply(425, "Can't open passive connection");
137126044Sminshall 		return;
137226044Sminshall 	}
137336933Skarels 	pasv_addr = ctrl_addr;
137436933Skarels 	pasv_addr.sin_port = 0;
137536304Skarels 	(void) seteuid((uid_t)0);
137636933Skarels 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
137736304Skarels 		(void) seteuid((uid_t)pw->pw_uid);
137836620Srick 		goto pasv_error;
137926044Sminshall 	}
138036304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
138136933Skarels 	len = sizeof(pasv_addr);
138236933Skarels 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
138336620Srick 		goto pasv_error;
138436620Srick 	if (listen(pdata, 1) < 0)
138536620Srick 		goto pasv_error;
138636933Skarels 	a = (char *) &pasv_addr.sin_addr;
138736933Skarels 	p = (char *) &pasv_addr.sin_port;
138826044Sminshall 
138926044Sminshall #define UC(b) (((int) b) & 0xff)
139026044Sminshall 
139126044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
139226044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
139336620Srick 	return;
139436620Srick 
139536620Srick pasv_error:
139636620Srick 	(void) close(pdata);
139736620Srick 	pdata = -1;
139836620Srick 	perror_reply(425, "Can't open passive connection");
139936620Srick 	return;
140026044Sminshall }
140126044Sminshall 
140236304Skarels /*
140336304Skarels  * Generate unique name for file with basename "local".
140436304Skarels  * The file named "local" is already known to exist.
140536304Skarels  * Generates failure reply on error.
140636304Skarels  */
1407*55258Sandrew static char *
140826044Sminshall gunique(local)
140926044Sminshall 	char *local;
141026044Sminshall {
141126044Sminshall 	static char new[MAXPATHLEN];
141236304Skarels 	struct stat st;
1413*55258Sandrew 	int count;
1414*55258Sandrew 	char *cp;
141526044Sminshall 
1416*55258Sandrew 	cp = rindex(local, '/');
141736304Skarels 	if (cp)
141826044Sminshall 		*cp = '\0';
141936620Srick 	if (stat(cp ? local : ".", &st) < 0) {
142036933Skarels 		perror_reply(553, cp ? local : ".");
142126044Sminshall 		return((char *) 0);
142226044Sminshall 	}
142336620Srick 	if (cp)
142436620Srick 		*cp = '/';
142526044Sminshall 	(void) strcpy(new, local);
142626044Sminshall 	cp = new + strlen(new);
142726044Sminshall 	*cp++ = '.';
142836304Skarels 	for (count = 1; count < 100; count++) {
1429*55258Sandrew 		(void)sprintf(cp, "%d", count);
143036304Skarels 		if (stat(new, &st) < 0)
143136304Skarels 			return(new);
143226044Sminshall 	}
143336304Skarels 	reply(452, "Unique file name cannot be created.");
1434*55258Sandrew 	return(NULL);
143526044Sminshall }
143636304Skarels 
143736304Skarels /*
143836304Skarels  * Format and send reply containing system error number.
143936304Skarels  */
1440*55258Sandrew void
144136304Skarels perror_reply(code, string)
144236304Skarels 	int code;
144336304Skarels 	char *string;
144436304Skarels {
144542412Sbostic 	reply(code, "%s: %s.", string, strerror(errno));
144636304Skarels }
144736620Srick 
144836620Srick static char *onefile[] = {
144936620Srick 	"",
145036620Srick 	0
145136620Srick };
145236620Srick 
1453*55258Sandrew void
145436620Srick send_file_list(whichfiles)
145536620Srick 	char *whichfiles;
145636620Srick {
145736620Srick 	struct stat st;
145836620Srick 	DIR *dirp = NULL;
145946669Sbostic 	struct dirent *dir;
146036620Srick 	FILE *dout = NULL;
146136620Srick 	register char **dirlist, *dirname;
146237459Skarels 	int simple = 0;
146336620Srick 
146436620Srick 	if (strpbrk(whichfiles, "~{[*?") != NULL) {
1465*55258Sandrew 		extern char *globerr;
146636933Skarels 
146736620Srick 		globerr = NULL;
146846669Sbostic 		dirlist = ftpglob(whichfiles);
146936620Srick 		if (globerr != NULL) {
147036620Srick 			reply(550, globerr);
147136620Srick 			return;
147236620Srick 		} else if (dirlist == NULL) {
147336620Srick 			errno = ENOENT;
147436620Srick 			perror_reply(550, whichfiles);
147536620Srick 			return;
147636620Srick 		}
147736620Srick 	} else {
147836620Srick 		onefile[0] = whichfiles;
147936620Srick 		dirlist = onefile;
148037459Skarels 		simple = 1;
148136620Srick 	}
148236933Skarels 
148336933Skarels 	if (setjmp(urgcatch)) {
148436933Skarels 		transflag = 0;
148536933Skarels 		return;
148636933Skarels 	}
148736620Srick 	while (dirname = *dirlist++) {
148836620Srick 		if (stat(dirname, &st) < 0) {
148936933Skarels 			/*
149036933Skarels 			 * If user typed "ls -l", etc, and the client
149136933Skarels 			 * used NLST, do what the user meant.
149236933Skarels 			 */
149336933Skarels 			if (dirname[0] == '-' && *dirlist == NULL &&
149436933Skarels 			    transflag == 0) {
149536933Skarels 				retrieve("/bin/ls %s", dirname);
149636933Skarels 				return;
149736933Skarels 			}
149836620Srick 			perror_reply(550, whichfiles);
149936620Srick 			if (dout != NULL) {
150036620Srick 				(void) fclose(dout);
150136933Skarels 				transflag = 0;
150236620Srick 				data = -1;
150336620Srick 				pdata = -1;
150436620Srick 			}
150536620Srick 			return;
150636620Srick 		}
150736933Skarels 
150836620Srick 		if ((st.st_mode&S_IFMT) == S_IFREG) {
150936620Srick 			if (dout == NULL) {
151037459Skarels 				dout = dataconn("file list", (off_t)-1, "w");
151136620Srick 				if (dout == NULL)
151236620Srick 					return;
151336933Skarels 				transflag++;
151436620Srick 			}
151538158Srick 			fprintf(dout, "%s%s\n", dirname,
151638158Srick 				type == TYPE_A ? "\r" : "");
151736933Skarels 			byte_count += strlen(dirname) + 1;
151836620Srick 			continue;
151936933Skarels 		} else if ((st.st_mode&S_IFMT) != S_IFDIR)
152036620Srick 			continue;
152136620Srick 
152236620Srick 		if ((dirp = opendir(dirname)) == NULL)
152336620Srick 			continue;
152436620Srick 
152536620Srick 		while ((dir = readdir(dirp)) != NULL) {
152636933Skarels 			char nbuf[MAXPATHLEN];
152736620Srick 
152836620Srick 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
152936620Srick 				continue;
153036933Skarels 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
153136933Skarels 			    dir->d_namlen == 2)
153236620Srick 				continue;
153336620Srick 
153436933Skarels 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
153536933Skarels 
153636620Srick 			/*
153736933Skarels 			 * We have to do a stat to insure it's
153836933Skarels 			 * not a directory or special file.
153936620Srick 			 */
154037459Skarels 			if (simple || (stat(nbuf, &st) == 0 &&
154137459Skarels 			    (st.st_mode&S_IFMT) == S_IFREG)) {
154236620Srick 				if (dout == NULL) {
154337459Skarels 					dout = dataconn("file list", (off_t)-1,
154436620Srick 						"w");
154536620Srick 					if (dout == NULL)
154636620Srick 						return;
154736933Skarels 					transflag++;
154836620Srick 				}
154936620Srick 				if (nbuf[0] == '.' && nbuf[1] == '/')
155038158Srick 					fprintf(dout, "%s%s\n", &nbuf[2],
155138158Srick 						type == TYPE_A ? "\r" : "");
155236620Srick 				else
155338158Srick 					fprintf(dout, "%s%s\n", nbuf,
155438158Srick 						type == TYPE_A ? "\r" : "");
155536933Skarels 				byte_count += strlen(nbuf) + 1;
155636620Srick 			}
155736620Srick 		}
155836620Srick 		(void) closedir(dirp);
155936620Srick 	}
156036620Srick 
156136933Skarels 	if (dout == NULL)
156236933Skarels 		reply(550, "No files found.");
156336933Skarels 	else if (ferror(dout) != 0)
156436933Skarels 		perror_reply(550, "Data connection");
156536933Skarels 	else
156636620Srick 		reply(226, "Transfer complete.");
156736620Srick 
156836933Skarels 	transflag = 0;
156936933Skarels 	if (dout != NULL)
157036620Srick 		(void) fclose(dout);
157136620Srick 	data = -1;
157236620Srick 	pdata = -1;
157336620Srick }
157436620Srick 
157536620Srick #ifdef SETPROCTITLE
157636620Srick /*
1577*55258Sandrew  * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
1578*55258Sandrew  * Warning, since this is usually started from inetd.conf, it often doesn't
1579*55258Sandrew  * have much of an environment or arglist to overwrite.
158036620Srick  */
1581*55258Sandrew void
1582*55258Sandrew #if __STDC__
1583*55258Sandrew setproctitle(const char *fmt, ...)
1584*55258Sandrew #else
1585*55258Sandrew setproctitle(fmt, va_alist)
1586*55258Sandrew 	char *fmt;
1587*55258Sandrew         va_dcl
1588*55258Sandrew #endif
158936620Srick {
159036620Srick 	register char *p, *bp, ch;
159136620Srick 	register int i;
1592*55258Sandrew 	va_list ap;
159336620Srick 	char buf[BUFSIZ];
1594*55258Sandrew #if __STDC__
1595*55258Sandrew 	va_start(ap, fmt);
1596*55258Sandrew #else
1597*55258Sandrew 	va_start(ap);
1598*55258Sandrew #endif
1599*55258Sandrew 	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
160036620Srick 
160136620Srick 	/* make ps print our process name */
160236620Srick 	p = Argv[0];
160336620Srick 	*p++ = '-';
160436620Srick 
160536620Srick 	i = strlen(buf);
160636620Srick 	if (i > LastArgv - p - 2) {
160736620Srick 		i = LastArgv - p - 2;
160836620Srick 		buf[i] = '\0';
160936620Srick 	}
161036620Srick 	bp = buf;
161136620Srick 	while (ch = *bp++)
161236620Srick 		if (ch != '\n' && ch != '\r')
161336620Srick 			*p++ = ch;
161436620Srick 	while (p < LastArgv)
161536620Srick 		*p++ = ' ';
161636620Srick }
161736620Srick #endif /* SETPROCTITLE */
1618