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