1 /* $NetBSD: conf.c,v 1.32 2000/07/09 02:24:30 sommerfeld Exp $ */ 2 3 /*- 4 * Copyright (c) 1997-2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge and Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __RCSID("$NetBSD: conf.c,v 1.32 2000/07/09 02:24:30 sommerfeld Exp $"); 42 #endif /* not lint */ 43 44 #include <sys/types.h> 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 48 #include <ctype.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <glob.h> 52 #include <setjmp.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <stringlist.h> 58 #include <syslog.h> 59 #include <time.h> 60 #include <unistd.h> 61 #include <util.h> 62 63 #ifdef KERBEROS5 64 #include <krb5/krb5.h> 65 #endif 66 67 #include "extern.h" 68 #include "pathnames.h" 69 70 static char *strend(const char *, char *); 71 static int filetypematch(char *, int); 72 73 74 /* 75 * Initialise curclass to an `empty' state 76 */ 77 void 78 init_curclass(void) 79 { 80 struct ftpconv *conv, *cnext; 81 82 for (conv = curclass.conversions; conv != NULL; conv = cnext) { 83 REASSIGN(conv->suffix, NULL); 84 REASSIGN(conv->types, NULL); 85 REASSIGN(conv->disable, NULL); 86 REASSIGN(conv->command, NULL); 87 cnext = conv->next; 88 free(conv); 89 } 90 91 curclass.checkportcmd = 0; 92 REASSIGN(curclass.classname, NULL); 93 curclass.conversions = NULL; 94 REASSIGN(curclass.display, NULL); 95 curclass.limit = -1; /* unlimited connections */ 96 REASSIGN(curclass.limitfile, NULL); 97 curclass.maxrateget = 0; 98 curclass.maxrateput = 0; 99 curclass.maxtimeout = 7200; /* 2 hours */ 100 curclass.modify = 1; 101 REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG)); 102 REASSIGN(curclass.notify, NULL); 103 curclass.passive = 1; 104 curclass.portmin = 0; 105 curclass.portmax = 0; 106 curclass.rateget = 0; 107 curclass.rateput = 0; 108 curclass.timeout = 900; /* 15 minutes */ 109 curclass.umask = 027; 110 curclass.upload = 1; 111 } 112 113 /* 114 * Parse the configuration file, looking for the named class, and 115 * define curclass to contain the appropriate settings. 116 */ 117 void 118 parse_conf(const char *findclass) 119 { 120 FILE *f; 121 char *buf, *p; 122 size_t len; 123 int none, match, rate; 124 char *endp; 125 char *class, *word, *arg, *template; 126 const char *infile; 127 size_t line; 128 unsigned int timeout; 129 struct ftpconv *conv, *cnext; 130 131 init_curclass(); 132 REASSIGN(curclass.classname, xstrdup(findclass)); 133 if (strcasecmp(findclass, "guest") == 0) { 134 curclass.modify = 0; 135 curclass.umask = 0707; 136 } 137 138 infile = conffilename(_PATH_FTPDCONF); 139 if ((f = fopen(infile, "r")) == NULL) 140 return; 141 142 line = 0; 143 template = NULL; 144 for (; 145 (buf = fparseln(f, &len, &line, NULL, FPARSELN_UNESCCOMM | 146 FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL; 147 free(buf)) { 148 none = match = 0; 149 p = buf; 150 if (len < 1) 151 continue; 152 if (p[len - 1] == '\n') 153 p[--len] = '\0'; 154 if (EMPTYSTR(p)) 155 continue; 156 157 NEXTWORD(p, word); 158 NEXTWORD(p, class); 159 NEXTWORD(p, arg); 160 if (EMPTYSTR(word) || EMPTYSTR(class)) 161 continue; 162 if (strcasecmp(class, "none") == 0) 163 none = 1; 164 if (! (strcasecmp(class, findclass) == 0 || 165 (template != NULL && strcasecmp(class, template) == 0) || 166 none || 167 strcasecmp(class, "all") == 0) ) 168 continue; 169 170 if (strcasecmp(word, "checkportcmd") == 0) { 171 if (none || 172 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) 173 curclass.checkportcmd = 0; 174 else 175 curclass.checkportcmd = 1; 176 177 } else if (strcasecmp(word, "classtype") == 0) { 178 if (!none && !EMPTYSTR(arg)) { 179 if (strcasecmp(arg, "GUEST") == 0) 180 curclass.type = CLASS_GUEST; 181 else if (strcasecmp(arg, "CHROOT") == 0) 182 curclass.type = CLASS_CHROOT; 183 else if (strcasecmp(arg, "REAL") == 0) 184 curclass.type = CLASS_REAL; 185 else { 186 syslog(LOG_WARNING, 187 "%s line %d: unknown class type `%s'", 188 infile, (int)line, arg); 189 continue; 190 } 191 } 192 193 } else if (strcasecmp(word, "conversion") == 0) { 194 char *suffix, *types, *disable, *convcmd; 195 196 if (EMPTYSTR(arg)) { 197 syslog(LOG_WARNING, 198 "%s line %d: %s requires a suffix", 199 infile, (int)line, word); 200 continue; /* need a suffix */ 201 } 202 NEXTWORD(p, types); 203 NEXTWORD(p, disable); 204 convcmd = p; 205 if (convcmd) 206 convcmd += strspn(convcmd, " \t"); 207 suffix = xstrdup(arg); 208 if (none || EMPTYSTR(types) || 209 EMPTYSTR(disable) || EMPTYSTR(convcmd)) { 210 types = NULL; 211 disable = NULL; 212 convcmd = NULL; 213 } else { 214 types = xstrdup(types); 215 disable = xstrdup(disable); 216 convcmd = xstrdup(convcmd); 217 } 218 for (conv = curclass.conversions; conv != NULL; 219 conv = conv->next) { 220 if (strcmp(conv->suffix, suffix) == 0) 221 break; 222 } 223 if (conv == NULL) { 224 conv = (struct ftpconv *) 225 calloc(1, sizeof(struct ftpconv)); 226 if (conv == NULL) { 227 syslog(LOG_WARNING, "can't malloc"); 228 continue; 229 } 230 conv->next = NULL; 231 for (cnext = curclass.conversions; 232 cnext != NULL; cnext = cnext->next) 233 if (cnext->next == NULL) 234 break; 235 if (cnext != NULL) 236 cnext->next = conv; 237 else 238 curclass.conversions = conv; 239 } 240 REASSIGN(conv->suffix, suffix); 241 REASSIGN(conv->types, types); 242 REASSIGN(conv->disable, disable); 243 REASSIGN(conv->command, convcmd); 244 245 } else if (strcasecmp(word, "display") == 0) { 246 if (none || EMPTYSTR(arg)) 247 arg = NULL; 248 else 249 arg = xstrdup(arg); 250 REASSIGN(curclass.display, arg); 251 252 } else if (strcasecmp(word, "limit") == 0) { 253 int limit; 254 255 if (none || EMPTYSTR(arg)) 256 continue; 257 limit = (int)strtol(arg, &endp, 10); 258 if (*endp != 0) { 259 syslog(LOG_WARNING, 260 "%s line %d: invalid limit %s", 261 infile, (int)line, arg); 262 continue; 263 } 264 curclass.limit = limit; 265 REASSIGN(curclass.limitfile, 266 EMPTYSTR(p) ? NULL : xstrdup(p)); 267 268 } else if (strcasecmp(word, "maxtimeout") == 0) { 269 if (none || EMPTYSTR(arg)) 270 continue; 271 timeout = (unsigned int)strtoul(arg, &endp, 10); 272 if (*endp != 0) { 273 syslog(LOG_WARNING, 274 "%s line %d: invalid maxtimeout %s", 275 infile, (int)line, arg); 276 continue; 277 } 278 if (timeout < 30) { 279 syslog(LOG_WARNING, 280 "%s line %d: maxtimeout %d < 30 seconds", 281 infile, (int)line, timeout); 282 continue; 283 } 284 if (timeout < curclass.timeout) { 285 syslog(LOG_WARNING, 286 "%s line %d: maxtimeout %d < timeout (%d)", 287 infile, (int)line, timeout, 288 curclass.timeout); 289 continue; 290 } 291 curclass.maxtimeout = timeout; 292 293 } else if (strcasecmp(word, "modify") == 0) { 294 if (none || 295 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) 296 curclass.modify = 0; 297 else 298 curclass.modify = 1; 299 300 } else if (strcasecmp(word, "motd") == 0) { 301 if (none || EMPTYSTR(arg)) 302 arg = NULL; 303 else 304 arg = xstrdup(arg); 305 REASSIGN(curclass.motd, arg); 306 307 308 } else if (strcasecmp(word, "notify") == 0) { 309 if (none || EMPTYSTR(arg)) 310 arg = NULL; 311 else 312 arg = xstrdup(arg); 313 REASSIGN(curclass.notify, arg); 314 315 } else if (strcasecmp(word, "passive") == 0) { 316 if (none || 317 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) 318 curclass.passive = 0; 319 else 320 curclass.passive = 1; 321 322 } else if (strcasecmp(word, "portrange") == 0) { 323 int minport, maxport; 324 char *min, *max; 325 326 if (none) { 327 curclass.portmin = 0; 328 curclass.portmax = 0; 329 continue; 330 } 331 if (EMPTYSTR(arg)) 332 continue; 333 min = arg; 334 NEXTWORD(p, max); 335 if (EMPTYSTR(max)) { 336 syslog(LOG_WARNING, 337 "%s line %d: missing maxport argument", 338 infile, (int)line); 339 continue; 340 } 341 minport = (int)strtol(min, &endp, 10); 342 if (*endp != 0 || minport < IPPORT_RESERVED || 343 minport > IPPORT_ANONMAX) { 344 syslog(LOG_WARNING, 345 "%s line %d: invalid minport %s", 346 infile, (int)line, min); 347 continue; 348 } 349 maxport = (int)strtol(max, &endp, 10); 350 if (*endp != 0 || maxport < IPPORT_RESERVED || 351 maxport > IPPORT_ANONMAX) { 352 syslog(LOG_WARNING, 353 "%s line %d: invalid maxport %s", 354 infile, (int)line, max); 355 continue; 356 } 357 if (minport >= maxport) { 358 syslog(LOG_WARNING, 359 "%s line %d: minport %d >= maxport %d", 360 infile, (int)line, minport, maxport); 361 continue; 362 } 363 curclass.portmin = minport; 364 curclass.portmax = maxport; 365 366 } else if (strcasecmp(word, "rateget") == 0) { 367 if (none || EMPTYSTR(arg)) 368 continue; 369 rate = strsuftoi(arg); 370 if (rate == -1) { 371 syslog(LOG_WARNING, 372 "%s line %d: invalid rateget %s", 373 infile, (int)line, arg); 374 continue; 375 } 376 curclass.maxrateget = rate; 377 curclass.rateget = rate; 378 379 } else if (strcasecmp(word, "rateput") == 0) { 380 if (none || EMPTYSTR(arg)) 381 continue; 382 rate = strsuftoi(arg); 383 if (rate == -1) { 384 syslog(LOG_WARNING, 385 "%s line %d: invalid rateput %s", 386 infile, (int)line, arg); 387 continue; 388 } 389 curclass.maxrateput = rate; 390 curclass.rateput = rate; 391 392 } else if (strcasecmp(word, "timeout") == 0) { 393 if (none || EMPTYSTR(arg)) 394 continue; 395 timeout = (unsigned int)strtoul(arg, &endp, 10); 396 if (*endp != 0) { 397 syslog(LOG_WARNING, 398 "%s line %d: invalid timeout %s", 399 infile, (int)line, arg); 400 continue; 401 } 402 if (timeout < 30) { 403 syslog(LOG_WARNING, 404 "%s line %d: timeout %d < 30 seconds", 405 infile, (int)line, timeout); 406 continue; 407 } 408 if (timeout > curclass.maxtimeout) { 409 syslog(LOG_WARNING, 410 "%s line %d: timeout %d > maxtimeout (%d)", 411 infile, (int)line, timeout, 412 curclass.maxtimeout); 413 continue; 414 } 415 curclass.timeout = timeout; 416 417 } else if (strcasecmp(word, "template") == 0) { 418 if (none) 419 continue; 420 REASSIGN(template, EMPTYSTR(arg) ? NULL : xstrdup(arg)); 421 422 } else if (strcasecmp(word, "umask") == 0) { 423 mode_t umask; 424 425 if (none || EMPTYSTR(arg)) 426 continue; 427 umask = (mode_t)strtoul(arg, &endp, 8); 428 if (*endp != 0 || umask > 0777) { 429 syslog(LOG_WARNING, 430 "%s line %d: invalid umask %s", 431 infile, (int)line, arg); 432 continue; 433 } 434 curclass.umask = umask; 435 436 } else if (strcasecmp(word, "upload") == 0) { 437 if (none || 438 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) { 439 curclass.modify = 0; 440 curclass.upload = 0; 441 } else 442 curclass.upload = 1; 443 444 } else { 445 syslog(LOG_WARNING, 446 "%s line %d: unknown directive '%s'", 447 infile, (int)line, word); 448 continue; 449 } 450 } 451 REASSIGN(template, NULL); 452 fclose(f); 453 } 454 455 /* 456 * Show file listed in curclass.display first time in, and list all the 457 * files named in curclass.notify in the current directory. Send back 458 * responses with the prefix `code' + "-". 459 */ 460 void 461 show_chdir_messages(int code) 462 { 463 static StringList *slist = NULL; 464 465 struct stat st; 466 struct tm *t; 467 glob_t gl; 468 time_t now, then; 469 int age; 470 char cwd[MAXPATHLEN]; 471 char *cp, **rlist; 472 473 if (quietmessages) 474 return; 475 476 /* Setup list for directory cache */ 477 if (slist == NULL) 478 slist = sl_init(); 479 if (slist == NULL) { 480 syslog(LOG_WARNING, "can't allocate memory for stringlist"); 481 return; 482 } 483 484 /* Check if this directory has already been visited */ 485 if (getcwd(cwd, sizeof(cwd) - 1) == NULL) { 486 syslog(LOG_WARNING, "can't getcwd: %s", strerror(errno)); 487 return; 488 } 489 if (sl_find(slist, cwd) != NULL) 490 return; 491 492 cp = xstrdup(cwd); 493 if (sl_add(slist, cp) == -1) 494 syslog(LOG_WARNING, "can't add `%s' to stringlist", cp); 495 496 /* First check for a display file */ 497 (void)format_file(curclass.display, code); 498 499 /* Now see if there are any notify files */ 500 if (EMPTYSTR(curclass.notify)) 501 return; 502 503 if (glob(curclass.notify, 0, NULL, &gl) != 0 || gl.gl_matchc == 0) 504 return; 505 time(&now); 506 for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) { 507 if (stat(*rlist, &st) != 0) 508 continue; 509 if (!S_ISREG(st.st_mode)) 510 continue; 511 then = st.st_mtime; 512 if (code != 0) { 513 reply(-code, "%s", ""); 514 code = 0; 515 } 516 reply(-code, "Please read the file %s", *rlist); 517 t = localtime(&now); 518 age = 365 * t->tm_year + t->tm_yday; 519 t = localtime(&then); 520 age -= 365 * t->tm_year + t->tm_yday; 521 reply(-code, " it was last modified on %.24s - %d day%s ago", 522 ctime(&then), age, PLURAL(age)); 523 } 524 globfree(&gl); 525 } 526 527 int 528 format_file(const char *file, int code) 529 { 530 FILE *f; 531 char *buf, *p, *cwd; 532 size_t len; 533 time_t now; 534 535 if (quietmessages) 536 return (0); 537 538 if (EMPTYSTR(file)) 539 return(0); 540 if ((f = fopen(file, "r")) == NULL) 541 return (0); 542 reply(-code, "%s", ""); 543 544 for (; 545 (buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) { 546 if (len > 0) 547 if (buf[len - 1] == '\n') 548 buf[--len] = '\0'; 549 cprintf(stdout, " "); 550 551 for (p = buf; *p; p++) { 552 if (*p == '%') { 553 p++; 554 switch (*p) { 555 556 case 'c': 557 cprintf(stdout, "%s", 558 curclass.classname ? 559 curclass.classname : "<unknown>"); 560 break; 561 562 case 'C': 563 if (getcwd(cwd, sizeof(cwd)-1) == NULL){ 564 syslog(LOG_WARNING, 565 "can't getcwd: %s", 566 strerror(errno)); 567 continue; 568 } 569 cprintf(stdout, "%s", cwd); 570 break; 571 572 case 'E': 573 /* XXXX email address */ 574 break; 575 576 case 'L': 577 cprintf(stdout, "%s", hostname); 578 break; 579 580 case 'M': 581 if (curclass.limit == -1) 582 cprintf(stdout, "unlimited"); 583 else 584 cprintf(stdout, "%d", 585 curclass.limit); 586 break; 587 588 case 'N': 589 if (connections > 0) 590 cprintf(stdout, "%d", 591 connections); 592 break; 593 594 case 'R': 595 cprintf(stdout, "%s", remotehost); 596 break; 597 598 case 'T': 599 now = time(NULL); 600 cprintf(stdout, "%.24s", ctime(&now)); 601 break; 602 603 case 'U': 604 cprintf(stdout, "%s", 605 pw ? pw->pw_name : "<unknown>"); 606 break; 607 608 case '%': 609 CPUTC('%', stdout); 610 break; 611 612 } 613 } else 614 CPUTC(*p, stdout); 615 } 616 cprintf(stdout, "\r\n"); 617 } 618 619 (void)fflush(stdout); 620 (void)fclose(f); 621 return (1); 622 } 623 624 /* 625 * Find s2 at the end of s1. If found, return a string up to (but 626 * not including) s2, otherwise returns NULL. 627 */ 628 static char * 629 strend(const char *s1, char *s2) 630 { 631 static char buf[MAXPATHLEN]; 632 633 char *start; 634 size_t l1, l2; 635 636 l1 = strlen(s1); 637 l2 = strlen(s2); 638 639 if (l2 >= l1) 640 return(NULL); 641 642 strlcpy(buf, s1, sizeof(buf)); 643 start = buf + (l1 - l2); 644 645 if (strcmp(start, s2) == 0) { 646 *start = '\0'; 647 return(buf); 648 } else 649 return(NULL); 650 } 651 652 static int 653 filetypematch(char *types, int mode) 654 { 655 for ( ; types[0] != '\0'; types++) 656 switch (*types) { 657 case 'd': 658 if (S_ISDIR(mode)) 659 return(1); 660 break; 661 case 'f': 662 if (S_ISREG(mode)) 663 return(1); 664 break; 665 } 666 return(0); 667 } 668 669 /* 670 * Look for a conversion. If we succeed, return a pointer to the 671 * command to execute for the conversion. 672 * 673 * The command is stored in a static array so there's no memory 674 * leak problems, and not too much to change in ftpd.c. This 675 * routine doesn't need to be re-entrant unless we start using a 676 * multi-threaded ftpd, and that's not likely for a while... 677 */ 678 char ** 679 do_conversion(const char *fname) 680 { 681 struct ftpconv *cp; 682 struct stat st; 683 int o_errno; 684 char *base = NULL; 685 char *cmd, *p, *lp, **argv; 686 StringList *sl; 687 688 o_errno = errno; 689 sl = NULL; 690 cmd = NULL; 691 for (cp = curclass.conversions; cp != NULL; cp = cp->next) { 692 if (cp->suffix == NULL) { 693 syslog(LOG_WARNING, 694 "cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!"); 695 continue; 696 } 697 if ((base = strend(fname, cp->suffix)) == NULL) 698 continue; 699 if (cp->types == NULL || cp->disable == NULL || 700 cp->command == NULL) 701 continue; 702 /* Is it enabled? */ 703 if (strcmp(cp->disable, ".") != 0 && 704 stat(cp->disable, &st) == 0) 705 continue; 706 /* Does the base exist? */ 707 if (stat(base, &st) < 0) 708 continue; 709 /* Is the file type ok */ 710 if (!filetypematch(cp->types, st.st_mode)) 711 continue; 712 break; /* "We have a winner!" */ 713 } 714 715 /* If we got through the list, no conversion */ 716 if (cp == NULL) 717 goto cleanup_do_conv; 718 719 /* Split up command into an argv */ 720 if ((sl = sl_init()) == NULL) 721 goto cleanup_do_conv; 722 cmd = xstrdup(cp->command); 723 p = cmd; 724 while (p) { 725 NEXTWORD(p, lp); 726 if (strcmp(lp, "%s") == 0) 727 lp = base; 728 if (sl_add(sl, xstrdup(lp)) == -1) 729 goto cleanup_do_conv; 730 } 731 732 if (sl_add(sl, NULL) == -1) 733 goto cleanup_do_conv; 734 argv = sl->sl_str; 735 free(cmd); 736 free(sl); 737 return(argv); 738 739 cleanup_do_conv: 740 if (sl) 741 sl_free(sl, 1); 742 free(cmd); 743 errno = o_errno; 744 return(NULL); 745 } 746 747 /* 748 * Convert the string `arg' to an int, which may have an optional SI suffix 749 * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise. 750 */ 751 int 752 strsuftoi(const char *arg) 753 { 754 char *cp; 755 long val; 756 757 if (!isdigit((unsigned char)arg[0])) 758 return (-1); 759 760 val = strtol(arg, &cp, 10); 761 if (cp != NULL) { 762 if (cp[0] != '\0' && cp[1] != '\0') 763 return (-1); 764 switch (tolower((unsigned char)cp[0])) { 765 case '\0': 766 case 'b': 767 break; 768 case 'k': 769 val <<= 10; 770 break; 771 case 'm': 772 val <<= 20; 773 break; 774 case 'g': 775 val <<= 30; 776 break; 777 default: 778 return (-1); 779 } 780 } 781 if (val < 0 || val > INT_MAX) 782 return (-1); 783 784 return (val); 785 } 786 787 /* 788 * Count the number of current connections, reading from 789 * /var/run/ftpd.pids-<class> 790 * Does a kill -0 on each pid in that file, and only counts 791 * processes that exist (or frees the slot if it doesn't). 792 * Adds getpid() to the first free slot. Truncates the file 793 * if possible. 794 */ 795 void 796 count_users(void) 797 { 798 char fn[MAXPATHLEN]; 799 int fd, i, last; 800 size_t count; 801 pid_t *pids, mypid; 802 struct stat sb; 803 804 (void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn)); 805 (void)strlcat(fn, curclass.classname, sizeof(fn)); 806 pids = NULL; 807 connections = 1; 808 809 if ((fd = open(fn, O_RDWR | O_CREAT | O_EXLOCK, 0600)) == -1) 810 return; 811 if (fstat(fd, &sb) == -1) 812 goto cleanup_count; 813 if ((pids = malloc(sb.st_size + sizeof(pid_t))) == NULL) 814 goto cleanup_count; 815 count = read(fd, pids, sb.st_size); 816 if (count < 0 || count != sb.st_size) 817 goto cleanup_count; 818 count /= sizeof(pid_t); 819 mypid = getpid(); 820 last = 0; 821 for (i = 0; i < count; i++) { 822 if (pids[i] == 0) 823 continue; 824 if (kill(pids[i], 0) == -1 && errno != EPERM) { 825 if (mypid != 0) { 826 pids[i] = mypid; 827 mypid = 0; 828 last = i; 829 } 830 } else { 831 connections++; 832 last = i; 833 } 834 } 835 if (mypid != 0) { 836 if (pids[last] != 0) 837 last++; 838 pids[last] = mypid; 839 } 840 count = (last + 1) * sizeof(pid_t); 841 if (lseek(fd, 0, SEEK_SET) == -1) 842 goto cleanup_count; 843 if (write(fd, pids, count) == -1) 844 goto cleanup_count; 845 (void)ftruncate(fd, count); 846 847 cleanup_count: 848 (void)flock(fd, LOCK_UN); 849 close(fd); 850 REASSIGN(pids, NULL); 851 } 852