1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 1997, 1998 Public Flood Software
4  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5  * Copyright (c) 2001-2020 The ProFTPD Project team
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20  *
21  * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 /* Shows a count of "who" is online via proftpd.  Uses the scoreboard file. */
28 
29 #include "utils.h"
30 
31 #define MAX_CLASSES 100
32 struct scoreboard_class {
33    char *score_class;
34    unsigned long score_count;
35 };
36 
37 static char *config_filename = PR_CONFIG_FILE_PATH;
38 
check_scoreboard_file(void)39 static int check_scoreboard_file(void) {
40   struct stat st;
41 
42   if (stat(util_get_scoreboard(), &st) < 0)
43     return -1;
44 
45   return 0;
46 }
47 
48 static struct option_help {
49   char *long_opt, *short_opt, *desc;
50 } opts_help[] = {
51   { "--config",	"-c",	"specify full path to proftpd configuration file" },
52   { "--file",	"-f",	"specify full path to scoreboard file" },
53   { "--help",	"-h",	NULL },
54   { "--server",	"-S",	"show count only for specified ServerName" },
55   { NULL }
56 };
57 
58 #ifdef HAVE_GETOPT_LONG
59 static struct option opts[] = {
60   { "config",  1, NULL, 'c' },
61   { "file",    1, NULL, 'f' },
62   { "help",    0, NULL, 'h' },
63   { "server",  1, NULL, 'S' },
64   { NULL,      0, NULL, 0   }
65 };
66 #endif /* HAVE_GETOPT_LONG */
67 
show_usage(const char * progname,int exit_code)68 static void show_usage(const char *progname, int exit_code) {
69   struct option_help *h = NULL;
70 
71   printf("usage: %s [options]\n", progname);
72   for (h = opts_help; h->long_opt; h++) {
73 #ifdef HAVE_GETOPT_LONG
74     printf("  %s, %s\n", h->short_opt, h->long_opt);
75 #else /* HAVE_GETOPT_LONG */
76     printf("  %s\n", h->short_opt);
77 #endif
78     if (h->desc == NULL) {
79       printf("    display %s usage\n", progname);
80 
81     } else {
82       printf("    %s\n", h->desc);
83     }
84   }
85 
86   exit(exit_code);
87 }
88 
main(int argc,char ** argv)89 int main(int argc, char **argv) {
90   pr_scoreboard_entry_t *score = NULL;
91   pid_t oldpid = 0, mpid;
92   unsigned int count = 0, total = 0;
93   int c = 0, res = 0;
94   char *server_name = NULL;
95   struct scoreboard_class classes[MAX_CLASSES];
96   char *cp, *progname = *argv;
97   const char *cmdopts = "S:c:f:h";
98   register unsigned int i;
99 
100   memset(classes, 0, MAX_CLASSES * sizeof(struct scoreboard_class));
101 
102   cp = strrchr(progname, '/');
103   if (cp != NULL) {
104     progname = cp + 1;
105   }
106 
107   opterr = 0;
108   while ((c =
109 #ifdef HAVE_GETOPT_LONG
110 	 getopt_long(argc, argv, cmdopts, opts, NULL)
111 #else /* HAVE_GETOPT_LONG */
112 	 getopt(argc, argv, cmdopts)
113 #endif /* HAVE_GETOPT_LONG */
114 	 ) != -1) {
115     switch (c) {
116       case 'h':
117         show_usage(progname, 0);
118         break;
119 
120       case 'f':
121         util_set_scoreboard(optarg);
122         break;
123 
124       case 'c':
125         config_filename = strdup(optarg);
126         break;
127 
128       case 'S':
129         server_name = strdup(optarg);
130         break;
131 
132       case '?':
133         fprintf(stderr, "unknown option: %c\n", (char) optopt);
134         show_usage(progname, 1);
135         break;
136     }
137   }
138 
139   /* First attempt to check the supplied/default scoreboard path.  If this is
140    * incorrect, try the config file kludge.
141    */
142   if (check_scoreboard_file() < 0) {
143     char *path;
144 
145     path = util_scan_config(config_filename, "ScoreboardFile");
146     if (path != NULL) {
147       util_set_scoreboard(path);
148       free(path);
149     }
150 
151     if (check_scoreboard_file() < 0) {
152       fprintf(stderr, "%s: %s\n", util_get_scoreboard(), strerror(errno));
153       fprintf(stderr, "(Perhaps you need to specify the ScoreboardFile with -f, or change\n");
154       fprintf(stderr," the compile-time default directory?)\n");
155       exit(1);
156     }
157   }
158 
159   count = 0;
160   res = util_open_scoreboard(O_RDONLY);
161   if (res < 0) {
162     if (server_name != NULL) {
163       free(server_name);
164     }
165 
166     switch (res) {
167       case UTIL_SCORE_ERR_BAD_MAGIC:
168         fprintf(stderr, "error opening scoreboard: bad/corrupted file\n");
169         return 1;
170 
171       case UTIL_SCORE_ERR_OLDER_VERSION:
172         fprintf(stderr, "error opening scoreboard: bad version (too old)\n");
173         return 1;
174 
175       case UTIL_SCORE_ERR_NEWER_VERSION:
176         fprintf(stderr, "error opening scoreboard: bad version (too new)\n");
177         return 1;
178 
179       default:
180         fprintf(stderr, "error opening scoreboard: %s\n", strerror(errno));
181         return 1;
182     }
183   }
184 
185   mpid = util_scoreboard_get_daemon_pid();
186 
187   errno = 0;
188   while ((score = util_scoreboard_entry_read()) != NULL) {
189     if (errno) {
190       break;
191     }
192 
193     if (!count++ ||
194         oldpid != mpid) {
195       if (total > 0) {
196         printf("   -  %u user%s\n\n", total, total > 1 ? "s" : "");
197       }
198 
199       if (!mpid) {
200         printf("inetd FTP connections:\n");
201 
202       } else {
203         printf("Master proftpd process %u:\n", (unsigned int) mpid);
204       }
205 
206       if (server_name != NULL) {
207         printf("ProFTPD Server '%s'\n", server_name);
208       }
209 
210       oldpid = mpid;
211       total = 0;
212     }
213 
214     /* If a ServerName was given, skip unless the scoreboard entry matches. */
215     if (server_name != NULL &&
216         strcmp(server_name, score->sce_server_label) != 0) {
217       continue;
218     }
219 
220     for (i = 0; i != MAX_CLASSES; i++) {
221       if (classes[i].score_class == 0) {
222 	classes[i].score_class = strdup(score->sce_class);
223 	classes[i].score_count++;
224         break;
225       }
226 
227       if (strcasecmp(classes[i].score_class, score->sce_class) == 0) {
228 	classes[i].score_count++;
229 	break;
230       }
231     }
232 
233     total++;
234   }
235   util_close_scoreboard();
236 
237   /* Print out the total. */
238   if (total > 0) {
239     for (i = 0; i != MAX_CLASSES; i++) {
240       if (classes[i].score_class == NULL) {
241         break;
242       }
243 
244       printf("Service class %-20s - %3lu %s\n", classes[i].score_class,
245         classes[i].score_count, classes[i].score_count > 1 ? "users" : "user");
246 
247       /* Free up the memory, now that we're done with it. */
248       free(classes[i].score_class);
249       classes[i].score_class = NULL;
250     }
251 
252   } else {
253     if (!mpid) {
254       printf("inetd FTP connections:\n");
255 
256     } else {
257       printf("Master proftpd process %u:\n", (unsigned int) mpid);
258     }
259 
260     printf("0 users\n");
261   }
262 
263   if (server_name != NULL) {
264     free(server_name);
265   }
266 
267   return 0;
268 }
269