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