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