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