xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcount.c (revision 2425:9274196fea31)
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 
parsetime(char * whattime)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 
validtime(char * ptr)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 
acl_getlimit(char * aclbuf,char * class)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 
lock_fd(int fd)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