1 /* $NetBSD: conf.c,v 1.30 2000/05/20 02:20:18 lukem 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.30 2000/05/20 02:20:18 lukem 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 lreply(code, ""); 514 code = 0; 515 } 516 lreply(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 lreply(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 off_t b; 534 time_t now; 535 536 if (quietmessages) 537 return (0); 538 539 #define PUTC(x) putchar(x), b++ 540 541 if (EMPTYSTR(file)) 542 return(0); 543 if ((f = fopen(file, "r")) == NULL) 544 return (0); 545 lreply(code, ""); 546 547 b = 0; 548 for (; 549 (buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) { 550 if (len > 0) 551 if (buf[len - 1] == '\n') 552 buf[--len] = '\0'; 553 b += printf(" "); 554 555 for (p = buf; *p; p++) { 556 if (*p == '%') { 557 p++; 558 switch (*p) { 559 560 case 'c': 561 b += printf("%s", 562 curclass.classname ? 563 curclass.classname : "<unknown>"); 564 break; 565 566 case 'C': 567 if (getcwd(cwd, sizeof(cwd)-1) == NULL){ 568 syslog(LOG_WARNING, 569 "can't getcwd: %s", 570 strerror(errno)); 571 continue; 572 } 573 b += printf("%s", cwd); 574 break; 575 576 case 'E': 577 /* XXXX email address */ 578 break; 579 580 case 'L': 581 b += printf("%s", hostname); 582 break; 583 584 case 'M': 585 if (curclass.limit == -1) 586 b += printf("unlimited"); 587 else 588 b += printf("%d", 589 curclass.limit); 590 break; 591 592 case 'N': 593 if (connections > 0) 594 b += printf("%d", connections); 595 break; 596 597 case 'R': 598 b += printf("%s", remotehost); 599 break; 600 601 case 'T': 602 now = time(NULL); 603 b += printf("%.24s", ctime(&now)); 604 break; 605 606 case 'U': 607 b += printf("%s", 608 pw ? pw->pw_name : "<unknown>"); 609 break; 610 611 case '%': 612 PUTC('%'); 613 break; 614 615 } 616 } else { 617 PUTC(*p); 618 } 619 } 620 PUTC('\r'); 621 PUTC('\n'); 622 } 623 624 total_bytes += b; 625 total_bytes_out += b; 626 (void)fflush(stdout); 627 (void)fclose(f); 628 return (1); 629 } 630 631 /* 632 * Find s2 at the end of s1. If found, return a string up to (but 633 * not including) s2, otherwise returns NULL. 634 */ 635 static char * 636 strend(const char *s1, char *s2) 637 { 638 static char buf[MAXPATHLEN]; 639 640 char *start; 641 size_t l1, l2; 642 643 l1 = strlen(s1); 644 l2 = strlen(s2); 645 646 if (l2 >= l1) 647 return(NULL); 648 649 strlcpy(buf, s1, sizeof(buf)); 650 start = buf + (l1 - l2); 651 652 if (strcmp(start, s2) == 0) { 653 *start = '\0'; 654 return(buf); 655 } else 656 return(NULL); 657 } 658 659 static int 660 filetypematch(char *types, int mode) 661 { 662 for ( ; types[0] != '\0'; types++) 663 switch (*types) { 664 case 'd': 665 if (S_ISDIR(mode)) 666 return(1); 667 break; 668 case 'f': 669 if (S_ISREG(mode)) 670 return(1); 671 break; 672 } 673 return(0); 674 } 675 676 /* 677 * Look for a conversion. If we succeed, return a pointer to the 678 * command to execute for the conversion. 679 * 680 * The command is stored in a static array so there's no memory 681 * leak problems, and not too much to change in ftpd.c. This 682 * routine doesn't need to be re-entrant unless we start using a 683 * multi-threaded ftpd, and that's not likely for a while... 684 */ 685 char ** 686 do_conversion(const char *fname) 687 { 688 struct ftpconv *cp; 689 struct stat st; 690 int o_errno; 691 char *base = NULL; 692 char *cmd, *p, *lp, **argv; 693 StringList *sl; 694 695 o_errno = errno; 696 sl = NULL; 697 cmd = NULL; 698 for (cp = curclass.conversions; cp != NULL; cp = cp->next) { 699 if (cp->suffix == NULL) { 700 syslog(LOG_WARNING, 701 "cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!"); 702 continue; 703 } 704 if ((base = strend(fname, cp->suffix)) == NULL) 705 continue; 706 if (cp->types == NULL || cp->disable == NULL || 707 cp->command == NULL) 708 continue; 709 /* Is it enabled? */ 710 if (strcmp(cp->disable, ".") != 0 && 711 stat(cp->disable, &st) == 0) 712 continue; 713 /* Does the base exist? */ 714 if (stat(base, &st) < 0) 715 continue; 716 /* Is the file type ok */ 717 if (!filetypematch(cp->types, st.st_mode)) 718 continue; 719 break; /* "We have a winner!" */ 720 } 721 722 /* If we got through the list, no conversion */ 723 if (cp == NULL) 724 goto cleanup_do_conv; 725 726 /* Split up command into an argv */ 727 if ((sl = sl_init()) == NULL) 728 goto cleanup_do_conv; 729 cmd = xstrdup(cp->command); 730 p = cmd; 731 while (p) { 732 NEXTWORD(p, lp); 733 if (strcmp(lp, "%s") == 0) 734 lp = base; 735 if (sl_add(sl, xstrdup(lp)) == -1) 736 goto cleanup_do_conv; 737 } 738 739 if (sl_add(sl, NULL) == -1) 740 goto cleanup_do_conv; 741 argv = sl->sl_str; 742 free(cmd); 743 free(sl); 744 return(argv); 745 746 cleanup_do_conv: 747 if (sl) 748 sl_free(sl, 1); 749 free(cmd); 750 errno = o_errno; 751 return(NULL); 752 } 753 754 /* 755 * Convert the string `arg' to an int, which may have an optional SI suffix 756 * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise. 757 */ 758 int 759 strsuftoi(const char *arg) 760 { 761 char *cp; 762 long val; 763 764 if (!isdigit((unsigned char)arg[0])) 765 return (-1); 766 767 val = strtol(arg, &cp, 10); 768 if (cp != NULL) { 769 if (cp[0] != '\0' && cp[1] != '\0') 770 return (-1); 771 switch (tolower((unsigned char)cp[0])) { 772 case '\0': 773 case 'b': 774 break; 775 case 'k': 776 val <<= 10; 777 break; 778 case 'm': 779 val <<= 20; 780 break; 781 case 'g': 782 val <<= 30; 783 break; 784 default: 785 return (-1); 786 } 787 } 788 if (val < 0 || val > INT_MAX) 789 return (-1); 790 791 return (val); 792 } 793 794 /* 795 * Count the number of current connections, reading from 796 * /var/run/ftpd.pids-<class> 797 * Does a kill -0 on each pid in that file, and only counts 798 * processes that exist (or frees the slot if it doesn't). 799 * Adds getpid() to the first free slot. Truncates the file 800 * if possible. 801 */ 802 void 803 count_users(void) 804 { 805 char fn[MAXPATHLEN]; 806 int fd, i, last; 807 size_t count; 808 pid_t *pids, mypid; 809 struct stat sb; 810 811 (void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn)); 812 (void)strlcat(fn, curclass.classname, sizeof(fn)); 813 pids = NULL; 814 connections = 1; 815 816 if ((fd = open(fn, O_RDWR | O_CREAT | O_EXLOCK, 0600)) == -1) 817 return; 818 if (fstat(fd, &sb) == -1) 819 goto cleanup_count; 820 if ((pids = malloc(sb.st_size + sizeof(pid_t))) == NULL) 821 goto cleanup_count; 822 count = read(fd, pids, sb.st_size); 823 if (count < 0 || count != sb.st_size) 824 goto cleanup_count; 825 count /= sizeof(pid_t); 826 mypid = getpid(); 827 last = 0; 828 for (i = 0; i < count; i++) { 829 if (pids[i] == 0) 830 continue; 831 if (kill(pids[i], 0) == -1 && errno != EPERM) { 832 if (mypid != 0) { 833 pids[i] = mypid; 834 mypid = 0; 835 last = i; 836 } 837 } else { 838 connections++; 839 last = i; 840 } 841 } 842 if (mypid != 0) { 843 if (pids[last] != 0) 844 last++; 845 pids[last] = mypid; 846 } 847 count = (last + 1) * sizeof(pid_t); 848 if (lseek(fd, 0, SEEK_SET) == -1) 849 goto cleanup_count; 850 if (write(fd, pids, count) == -1) 851 goto cleanup_count; 852 (void)ftruncate(fd, count); 853 854 cleanup_count: 855 (void)flock(fd, LOCK_UN); 856 close(fd); 857 REASSIGN(pids, NULL); 858 } 859