Home | History | Annotate | Download | only in in.ftpd
      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 }
    545