1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /**************************************************************************** 9 10 Copyright (c) 1999,2000 WU-FTPD Development Group. 11 All rights reserved. 12 13 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994 14 The Regents of the University of California. 15 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis. 16 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc. 17 Portions Copyright (c) 1989 Massachusetts Institute of Technology. 18 Portions Copyright (c) 1998 Sendmail, Inc. 19 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman. 20 Portions Copyright (c) 1997 by Stan Barber. 21 Portions Copyright (c) 1997 by Kent Landfield. 22 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997 23 Free Software Foundation, Inc. 24 25 Use and distribution of this software and its source code are governed 26 by the terms and conditions of the WU-FTPD Software License ("LICENSE"). 27 28 If you did not receive a copy of the license, it may be obtained online 29 at http://www.wu-ftpd.org/license.html. 30 31 $Id: ftpcount.c,v 1.22 2000/07/01 18:17:39 wuftpd Exp $ 32 33 ****************************************************************************/ 34 #include "config.h" 35 36 #include <stdio.h> 37 #include <errno.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #ifdef HAVE_SYS_SYSLOG_H 41 #include <sys/syslog.h> 42 #endif 43 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H)) 44 #include <syslog.h> 45 #endif 46 #include <signal.h> 47 #include <time.h> 48 #include <ctype.h> 49 #include <limits.h> 50 51 #include <sys/types.h> 52 #include <sys/stat.h> 53 #include <sys/file.h> 54 #include <sys/param.h> 55 56 #ifdef HAVE_PATHS_H 57 #include <paths.h> 58 #endif 59 60 #if defined(VIRTUAL) && defined(INET6) 61 #include <netinet/in.h> 62 #endif 63 64 #include "pathnames.h" 65 #include "extensions.h" 66 67 #if defined(HAVE_FCNTL_H) 68 #include <fcntl.h> 69 #endif 70 71 #ifdef VIRTUAL 72 #define ARGS "Vv" 73 #else 74 #define ARGS "V" 75 #endif 76 77 struct c_list { 78 char *class; 79 struct c_list *next; 80 }; 81 82 #ifdef VIRTUAL 83 extern int read_servers_line(FILE *, char *, size_t, char *, size_t); 84 #endif 85 86 void print_copyright(void); 87 88 char *progname; 89 90 /*************************************************************************/ 91 /* FUNCTION : parse_time */ 92 /* PURPOSE : Check a single valid-time-string against the current time */ 93 /* and return whether or not a match occurs. */ 94 /* ARGUMENTS : a pointer to the time-string */ 95 /*************************************************************************/ 96 97 static int parsetime(char *whattime) 98 { 99 static char *days[] = 100 {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Wk"}; 101 time_t clock; 102 struct tm *curtime; 103 int wday, start, stop, ltime, validday, loop, match; 104 105 (void) time(&clock); 106 curtime = localtime(&clock); 107 wday = curtime->tm_wday; 108 validday = 0; 109 match = 1; 110 111 while (match && isalpha(*whattime) && isupper(*whattime)) { 112 match = 0; 113 for (loop = 0; loop < 8; loop++) { 114 if (strncmp(days[loop], whattime, 2) == 0) { 115 whattime += 2; 116 match = 1; 117 if ((wday == loop) || ((loop == 7) && wday && (wday < 6))) 118 validday = 1; 119 } 120 } 121 } 122 123 if (!validday) { 124 if (strncmp(whattime, "Any", 3) == 0) { 125 validday = 1; 126 whattime += 3; 127 } 128 else 129 return (0); 130 } 131 132 if (sscanf(whattime, "%d-%d", &start, &stop) == 2) { 133 ltime = curtime->tm_min + 100 * curtime->tm_hour; 134 if ((start < stop) && ((ltime >= start) && ltime < stop)) 135 return (1); 136 if ((start > stop) && ((ltime >= start) || ltime < stop)) 137 return (1); 138 } 139 else 140 return (1); 141 142 return (0); 143 } 144 145 /*************************************************************************/ 146 /* FUNCTION : validtime */ 147 /* PURPOSE : Break apart a set of valid time-strings and pass them to */ 148 /* parse_time, returning whether or not ANY matches occurred */ 149 /* ARGUMENTS : a pointer to the time-string */ 150 /*************************************************************************/ 151 152 static int validtime(char *ptr) 153 { 154 char *nextptr; 155 int good; 156 157 while (1) { 158 nextptr = strchr(ptr, '|'); 159 if (strchr(ptr, '|') == NULL) 160 return (parsetime(ptr)); 161 *nextptr = '\0'; 162 good = parsetime(ptr); 163 *nextptr++ = '|'; /* gotta restore the | or things get skipped! */ 164 if (good) 165 return (1); 166 ptr = nextptr; 167 } 168 } 169 170 static int acl_getlimit(char *aclbuf, char *class) 171 { 172 char *crptr, *ptr, linebuf[1024]; 173 int limit; 174 175 while (*aclbuf != '\0') { 176 if (strncasecmp(aclbuf, "limit", 5) == 0) { 177 for (crptr = aclbuf; *crptr++ != '\n';); 178 *--crptr = '\0'; 179 (void) strlcpy(linebuf, aclbuf, sizeof(linebuf)); 180 *crptr = '\n'; 181 (void) strtok(linebuf, " \t"); /* returns "limit" */ 182 if ((ptr = strtok(NULL, " \t")) && (strcmp(class, ptr) == 0)) { 183 if ((ptr = strtok(NULL, " \t"))) { 184 limit = atoi(ptr); /* returns limit <n> */ 185 if ((ptr = strtok(NULL, " \t")) && validtime(ptr)) 186 return (limit); 187 } 188 } 189 } 190 while (*aclbuf && *aclbuf++ != '\n'); 191 } 192 193 return (-1); 194 } 195 196 /*************************************************************************/ 197 /* FUNCTION : lock_fd */ 198 /* PURPOSE : Lock a file. */ 199 /* ARGUMENTS : File descriptor of file to lock. */ 200 /*************************************************************************/ 201 202 static void lock_fd(int fd) 203 { 204 #ifndef HAVE_FLOCK 205 struct flock arg; 206 #endif 207 208 #ifdef HAVE_FLOCK 209 while (flock(fd, LOCK_SH)) { 210 #ifndef NO_PID_SLEEP_MSGS 211 syslog(LOG_ERR, "sleeping: flock of pid file failed: %m"); 212 #endif 213 #else 214 arg.l_type = F_RDLCK; 215 arg.l_whence = arg.l_start = arg.l_len = 0; 216 while (-1 == fcntl(fd, F_SETLK, &arg)) { 217 #ifndef NO_PID_SLEEP_MSGS 218 syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %m"); 219 #endif 220 #endif /* HAVE_FLOCK */ 221 sleep(1); 222 } 223 #ifndef HAVE_FLOCK 224 #endif /* HAVE_FLOCK */ 225 } 226 227 /*************************************************************************/ 228 /* FUNCTION : unlock_fd */ 229 /* PURPOSE : Unlock a file locked by lock_fd. */ 230 /* ARGUMENTS : File descriptor of file to unlock. */ 231 /*************************************************************************/ 232 233 static void unlock_fd(int fd) 234 { 235 #ifndef HAVE_FLOCK 236 struct flock arg; 237 #endif 238 239 #ifdef HAVE_FLOCK 240 flock(fd, LOCK_UN); 241 #else 242 arg.l_type = F_UNLCK; 243 arg.l_whence = arg.l_start = arg.l_len = 0; 244 fcntl(fd, F_SETLK, &arg); 245 #endif /* HAVE_FLOCK */ 246 } 247 248 static int acl_countusers(char *class) 249 { 250 int i, j, n, count, pidfd; 251 pid_t procid; 252 char pidfile[MAXPATHLEN]; 253 char line[1024]; 254 FILE *ZeFile; 255 struct pidfile_header hdr; 256 struct stat pinfo; 257 unsigned char bits, *buf; 258 259 snprintf(pidfile, sizeof(pidfile), _PATH_PIDNAMES, class); 260 pidfd = open(pidfile, O_RDONLY); 261 if (pidfd == -1) { 262 return (0); 263 } 264 265 lock_fd(pidfd); 266 if (read(pidfd, (void *)&hdr, sizeof(hdr)) != sizeof(hdr)) { 267 unlock_fd(pidfd); 268 close(pidfd); 269 return (0); 270 } 271 if (strcmp(progname, "ftpcount") == 0) { 272 unlock_fd(pidfd); 273 close(pidfd); 274 return (hdr.count); 275 } 276 277 /* 278 * Printing the process information can take a long time, and while we 279 * hold the lock no users can join or leave this class. To minimize the 280 * problem, read the whole PID file into memory then release the lock. 281 */ 282 if (fstat(pidfd, &pinfo) != 0) { 283 unlock_fd(pidfd); 284 close(pidfd); 285 return (0); 286 } 287 if ((buf = malloc((size_t)pinfo.st_size)) == NULL) { 288 unlock_fd(pidfd); 289 close(pidfd); 290 return (0); 291 } 292 n = read(pidfd, buf, (size_t)pinfo.st_size); 293 unlock_fd(pidfd); 294 close(pidfd); 295 count = 0; 296 procid = 0; 297 for (i = 0; i < n; i++) { 298 if (buf[i] == 0) { 299 procid += CHAR_BIT; 300 } 301 else { 302 bits = 1; 303 for (j = 0; j < CHAR_BIT; j++) { 304 if (((buf[i] & bits) != 0) && 305 ((kill(procid, 0) == 0) || (errno == EPERM))) { 306 #if defined(SVR4) 307 #ifdef AIX 308 snprintf(line, sizeof(line), "/bin/ps %d", procid); 309 #elif defined(sun) 310 snprintf(line, sizeof(line), "/usr/ucb/ps auxww %ld", procid); 311 #else 312 #if defined (LINUX_BUT_NOT_REDHAT_6_0) 313 snprintf(line, sizeof(line), "/bin/ps axwww %d", procid); 314 #else 315 snprintf(line, sizeof(line), "/bin/ps -f -p %d", procid); 316 #endif 317 #endif 318 #elif defined(M_UNIX) 319 snprintf(line, sizeof(line), "/bin/ps -f -p %d", procid); 320 #else 321 snprintf(line, sizeof(line), "/bin/ps %d", procid); 322 #endif 323 ZeFile = popen(line, "r"); 324 fgets(line, sizeof(line), ZeFile); 325 line[0] = '\0'; 326 fgets(line, sizeof(line), ZeFile); 327 if (line[0] != '\0') { 328 size_t i; 329 for (i = strlen(line); (i > 0) && ((line[i - 1] == ' ') || (line[i - 1] == '\n')); --i) 330 line[i - 1] = '\0'; 331 printf("%s\n", line); 332 count++; 333 } 334 pclose(ZeFile); 335 } 336 bits <<= 1; 337 procid++; 338 } 339 } 340 } 341 free(buf); 342 return (count); 343 } 344 345 static void new_list(struct c_list **list) 346 { 347 struct c_list *cp, *tcp; 348 349 if (*list == NULL) { 350 *list = (struct c_list *) malloc(sizeof(struct c_list)); 351 if (*list == NULL) { 352 perror("malloc error in new_list"); 353 exit(1); 354 } 355 } 356 else { 357 cp = (*list)->next; 358 while (cp) { 359 if (cp->class) 360 free(cp->class); 361 tcp = cp; 362 cp = cp->next; 363 free(tcp); 364 } 365 } 366 (*list)->next = NULL; 367 } 368 369 static int add_list(char *class, struct c_list **list) 370 { 371 struct c_list *cp; 372 373 for (cp = (*list)->next; cp; cp = cp->next) { 374 if (!strcmp(cp->class, class)) 375 return (-1); 376 } 377 378 cp = (struct c_list *) malloc(sizeof(struct c_list)); 379 if (cp == NULL) { 380 perror("malloc error in add_list"); 381 exit(1); 382 } 383 384 cp->class = strdup(class); 385 if (cp->class == NULL) { 386 perror("malloc error in add_list"); 387 exit(1); 388 } 389 cp->next = (*list)->next; 390 (*list)->next = cp; 391 return (1); 392 } 393 394 static int display_info(char *ftpaccess, char *address) 395 { 396 FILE *accessfile; 397 char class[80], linebuf[1024], *aclbuf, *myaclbuf, *crptr; 398 int limit; 399 struct stat finfo; 400 static struct c_list *list = NULL; 401 402 if ((accessfile = fopen(ftpaccess, "r")) == NULL) { 403 if (errno != ENOENT) 404 fprintf(stderr, "%s: could not open access file %s: %s\n", 405 progname, ftpaccess, strerror(errno)); 406 return (1); 407 } 408 if (fstat(fileno(accessfile), &finfo) != 0) { 409 fprintf(stderr, "%s: could not fstat() access file %s: %s\n", 410 progname, ftpaccess, strerror(errno)); 411 fclose(accessfile); 412 return (1); 413 } 414 415 if (finfo.st_size == 0) { 416 printf("%s: no service classes defined, no usage count kept\n", progname); 417 fclose(accessfile); 418 return (0); 419 } 420 else { 421 if (!(aclbuf = (char *) malloc((size_t) finfo.st_size + 1))) { 422 fprintf(stderr, "%s: could not malloc aclbuf: %s\n", 423 progname, strerror(errno)); 424 fclose(accessfile); 425 return (1); 426 } 427 fread(aclbuf, (size_t) finfo.st_size, 1, accessfile); 428 fclose(accessfile); 429 *(aclbuf + (size_t) finfo.st_size) = '\0'; 430 } 431 432 (void) new_list(&list); 433 myaclbuf = aclbuf; 434 while (*myaclbuf != '\0') { 435 if (strncasecmp(myaclbuf, "class", 5) == 0) { 436 for (crptr = myaclbuf; *crptr++ != '\n';); 437 *--crptr = '\0'; 438 (void) strlcpy(linebuf, myaclbuf, sizeof(linebuf)); 439 *crptr = '\n'; 440 (void) strtok(linebuf, " \t"); /* returns "class" */ 441 /* returns class name */ 442 (void) strlcpy(class, strtok(NULL, " \t"), sizeof(class)); 443 if ((add_list(class, &list)) < 0) { 444 /* we have a class with multiple "class..." lines so, only 445 * display one count... */ 446 ; 447 } 448 else { 449 limit = acl_getlimit(myaclbuf, class); 450 #ifdef VIRTUAL 451 if (address != NULL) 452 printf("%s ", address); 453 #endif 454 if (strcmp(progname, "ftpcount")) { 455 printf("Service class %s: \n", class); 456 printf(" - %3d users ", acl_countusers(class)); 457 } 458 else { 459 printf("Service class %-20.20s - %3d users ", 460 class, acl_countusers(class)); 461 } 462 if (limit == -1) 463 printf("(no maximum)\n"); 464 else 465 printf("(%3d maximum)\n", limit); 466 } 467 } 468 while (*myaclbuf && *myaclbuf++ != '\n'); 469 } 470 free(aclbuf); 471 return (0); 472 } 473 474 int main(int argc, char **argv) 475 { 476 int c, exitval; 477 int virtual = 0; 478 #ifdef VIRTUAL 479 FILE *svrfp; 480 char *sp; 481 struct stat st; 482 char configdir[MAXPATHLEN]; 483 char accesspath[MAXPATHLEN]; 484 #ifdef INET6 485 char hostaddress[INET6_ADDRSTRLEN]; 486 #else 487 char hostaddress[32]; 488 #endif 489 #endif 490 491 if ((progname = strrchr(argv[0], '/'))) 492 ++progname; 493 else 494 progname = argv[0]; 495 496 if (argc > 1) { 497 while ((c = getopt(argc, argv, ARGS)) != EOF) { 498 switch (c) { 499 case 'V': 500 print_copyright(); 501 exit(0); 502 #ifdef VIRTUAL 503 case 'v': 504 virtual = 1; 505 break; 506 #endif 507 default: 508 fprintf(stderr, "usage: %s [-" ARGS "]\n", progname); 509 exit(1); 510 } 511 } 512 } 513 514 exitval = 0; 515 if ((virtual == 0) && (display_info(_PATH_FTPACCESS, NULL) != 0)) 516 exitval = 1; 517 518 #ifdef VIRTUAL 519 /* 520 * Deal with the ftpaccess files at the virtual domain directory locations 521 * specified in the ftpservers file. 522 */ 523 if (virtual && ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL)) { 524 while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress), 525 configdir, sizeof(configdir)) == 1) { 526 /* get rid of any trailing slash */ 527 sp = configdir + (strlen(configdir) - 1); 528 if (*sp == '/') 529 *sp = '\0'; 530 531 /* check to see that a valid directory value was supplied */ 532 if ((stat(configdir, &st) == 0) && 533 ((st.st_mode & S_IFMT) == S_IFDIR)) { 534 snprintf(accesspath, sizeof(accesspath), "%s/ftpaccess", 535 configdir); 536 if (display_info(accesspath, hostaddress) != 0) 537 exitval = 1; 538 } 539 } 540 fclose(svrfp); 541 } 542 #endif 543 return (exitval); 544 }