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 }