1 /*
2 * control-proto.c - control protocol implementation
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2001, 2002, 2003 Stefan Jahn <stefan@lkcc.org>
6 *
7 * This 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 3, or (at your option)
10 * any later version.
11 *
12 * This software 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 package. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <time.h>
27 #include "networking-headers.h"
28 #include "unused.h"
29
30 #ifndef __MINGW32__
31 # include <sys/types.h>
32 #endif
33
34 #if HAVE_LIBKSTAT
35 # include <kstat.h>
36 # include <sys/sysinfo.h>
37 #elif HAVE_SYS_PARAM_H && HAVE_SYS_PSTAT_H
38 # include <sys/param.h>
39 # include <sys/pstat.h>
40 # define HAVE_PSTAT 1
41 #elif HAVE_SYS_SYSGET_H && HAVE_SYS_SYSINFO_H
42 # include <sys/sysget.h>
43 # include <sys/sysinfo.h>
44 # define HAVE_SYSGET 1
45 #elif HAVE_HOST_STATISTICS
46 # include <mach/mach_init.h>
47 # include <mach/mach_host.h>
48 #endif
49
50 #if HAVE_TIMES
51 # include <sys/times.h>
52 #endif
53
54 #ifdef HAVE_CRYPT_H
55 # include <crypt.h>
56 #endif
57
58 #include "libserveez.h"
59 #include "control-proto.h"
60
61 #if ENABLE_HTTP_PROTO
62 # include "http-server/http-cache.h"
63 #endif
64
65 /*
66 * The one and only...
67 */
68 char *control_protocol_password;
69
70 /*
71 * The control server instance configuration.
72 */
73 ctrl_config_t ctrl_config =
74 {
75 0 /* nothing */
76 };
77
78 /*
79 * Definition of the configuration items processed by the configuration
80 * language.
81 */
82 svz_key_value_pair_t ctrl_config_prototype [] =
83 {
84 SVZ_REGISTER_END ()
85 };
86
87 /*
88 * Definition of the control protocol server.
89 */
90 svz_servertype_t ctrl_server_definition =
91 {
92 "control protocol server", /* long server description */
93 "control", /* short server description */
94 NULL, /* global initializer */
95 ctrl_init, /* instance initializer */
96 ctrl_detect_proto, /* protocol detection routine */
97 ctrl_connect_socket, /* connection routine */
98 ctrl_finalize, /* instance finalization routine */
99 NULL, /* global finalizer */
100 ctrl_info_client, /* client info */
101 ctrl_info_server, /* server info */
102 NULL, /* server timer */
103 NULL, /* server reset callback */
104 NULL, /* handle request callback */
105 SVZ_CONFIG_DEFINE ("control", ctrl_config, ctrl_config_prototype)
106 };
107
108 /*
109 * Within the ‘ctrl_idle’ function this structure gets filled with
110 * the appropriate data.
111 */
112 cpu_state_t cpu_state;
113
114 /*
115 * Server instance initializer. This is currently used for binding the
116 * server to a given port configuration.
117 */
118 int
ctrl_init(UNUSED svz_server_t * server)119 ctrl_init (UNUSED svz_server_t *server)
120 {
121 return 0;
122 }
123
124 /*
125 * Server instance finalizer.
126 */
127 int
ctrl_finalize(UNUSED svz_server_t * server)128 ctrl_finalize (UNUSED svz_server_t *server)
129 {
130 return 0;
131 }
132
133 /*
134 * Server info callback.
135 */
136 char *
ctrl_info_server(UNUSED svz_server_t * server)137 ctrl_info_server (UNUSED svz_server_t *server)
138 {
139 static char info[128];
140
141 sprintf (info, " nothing to be configured, yet");
142 return info;
143 }
144
145 /*
146 * Client info callback.
147 */
148 char *
ctrl_info_client(UNUSED svz_server_t * server,UNUSED svz_socket_t * sock)149 ctrl_info_client (UNUSED svz_server_t *server,
150 UNUSED svz_socket_t *sock)
151 {
152 static char info[128];
153
154 sprintf (info, "This is a control connection client.");
155 return info;
156 }
157
158 /*
159 * This function gets called for new sockets which are not yet
160 * identified. It returns a non-zero value when the content in
161 * the receive buffer looks like the control protocol.
162 */
163 int
ctrl_detect_proto(UNUSED svz_server_t * server,svz_socket_t * sock)164 ctrl_detect_proto (UNUSED svz_server_t *server, svz_socket_t *sock)
165 {
166 int ret = 0;
167
168 /* accept both CRLF and CR */
169 if (sock->recv_buffer_fill >= 2 &&
170 sock->recv_buffer[0] == '\r' &&
171 sock->recv_buffer[1] == '\n')
172 {
173 ret = 2;
174 }
175 else if (sock->recv_buffer_fill >= 1 && sock->recv_buffer[0] == '\n')
176 {
177 ret = 1;
178 }
179
180 /* control protocol detected */
181 if (ret)
182 {
183 if (ret < sock->recv_buffer_fill)
184 {
185 memmove (sock->recv_buffer,
186 sock->recv_buffer + ret,
187 sock->recv_buffer_fill - ret);
188 }
189 sock->recv_buffer_fill -= ret;
190 #if ENABLE_DEBUG
191 svz_log (SVZ_LOG_DEBUG, "control protocol client detected\n");
192 #endif
193 return -1;
194 }
195
196 return 0;
197 }
198
199 /*
200 * Format string for system business output on different systems.
201 */
202 #ifdef HAVE_LIBKSTAT
203 # define CPU_FORMAT \
204 "user %ld.%01ld%%, sys %ld.%01ld%%, wait %ld.%01ld%%, idle %ld.%01ld%%"
205 #elif HAVE_PROC_STAT
206 # define CPU_FORMAT \
207 "user %ld.%01ld%%, nice %ld.%01ld%%, sys %ld.%01ld%%, idle %ld.%01ld%%"
208 #elif HAVE_PSTAT
209 # define CPU_FORMAT \
210 "user %ld.%01ld%%, nice %ld.%01ld%%, sys %ld.%01ld%%, idle %ld.%01ld%%"
211 #elif HAVE_SYSGET
212 # define CPU_FORMAT \
213 "user %ld.%01ld%%, sys %ld.%01ld%%, wait %ld.%01ld%%, idle %ld.%01ld%%"
214 #elif HAVE_HOST_STATISTICS
215 # define CPU_FORMAT \
216 "user %ld.%01ld%%, sys %ld.%01ld%%, idle %ld.%01ld%%, nice %ld.%01ld%%"
217 #else
218 # define CPU_FORMAT "no cpu info available"
219 #endif
220
221 /*
222 * When ctrl_detect_proto has identified a client connection being
223 * a control protocol connection you have to call the following
224 * routine.
225 */
226 int
ctrl_connect_socket(UNUSED svz_server_t * server,svz_socket_t * sock)227 ctrl_connect_socket (UNUSED svz_server_t *server, svz_socket_t *sock)
228 {
229 svz_sock_resize_buffers (sock, CTRL_SEND_BUFSIZE, CTRL_RECV_BUFSIZE);
230 sock->check_request = svz_sock_check_request;
231 sock->handle_request = ctrl_handle_request;
232 sock->boundary = CTRL_PACKET_DELIMITER;
233 sock->boundary_size = CTRL_PACKET_DELIMITER_LEN;
234 sock->idle_func = ctrl_idle;
235 sock->idle_counter = CTRL_LOAD_UPDATE;
236
237 #if HAVE_PROC_STAT
238 cpu_state.cpufile = CPU_FILE_NAME;
239 cpu_state.cpuline = CPU_LINE_FORMAT;
240 #elif HAVE_LIBKSTAT /* not HAVE_PROC_STAT */
241
242 #else /* neither HAVE_PROC_STAT nor HAVE_LIBKSTAT */
243 strcpy (cpu_state.info, CPU_FORMAT);
244 #endif
245
246 cpu_state.cpuinfoline = CPU_FORMAT;
247
248 /* send welcome message */
249 svz_sock_printf (sock, "%s", CTRL_PASSWD);
250
251 return 0;
252 }
253
254 /*
255 * Quit command. If the client sends this command the control protocol
256 * connection will be closed immediately.
257 */
258 int
ctrl_quit(UNUSED svz_socket_t * sock,int flag,UNUSED char * arg)259 ctrl_quit (UNUSED svz_socket_t *sock, int flag, UNUSED char *arg)
260 {
261 return flag;
262 }
263
264 /*
265 * Help screen. Here you will get all the available commands of the
266 * control protocol. These depend on the features the current version
267 * of Serveez implements.
268 */
269 int
ctrl_help(svz_socket_t * sock,int flag,UNUSED char * arg)270 ctrl_help (svz_socket_t *sock, int flag, UNUSED char *arg)
271 {
272 svz_sock_printf (sock,
273 "\r\n available commands:\r\n"
274 " * help - this help screen\r\n"
275 " * quit - quit this control connection\r\n"
276 " * restart ident - restart the ident coserver\r\n"
277 " * restart reverse dns - restart reverse DNS lookup coserver\r\n"
278 " * restart dns - restart the DNS lookup coserver\r\n"
279 " * killall - shutdown all client connections\r\n"
280 " * kill id NUM - shutdown connection NUM\r\n"
281 " * stat - general statistics\r\n"
282 " * stat SERVER - SERVER's statistic\r\n"
283 " * stat coserver - coserver statistics\r\n"
284 " * stat con - connection statistics\r\n"
285 " * stat id NUM - NUM's connection info\r\n"
286 " * stat all - server and coserver state\r\n"
287 #if ENABLE_HTTP_PROTO
288 " * stat cache - http cache statistics\r\n"
289 " * kill cache - free all http cache entries\r\n"
290 #endif /* ENABLE_HTTP_PROTO */
291 "\r\n");
292
293 return flag;
294 }
295
296 /*
297 * ID's connection info. This function displays a given socket id's
298 * socket structure.
299 */
300 int
ctrl_stat_id(svz_socket_t * sock,int flag,char * arg)301 ctrl_stat_id (svz_socket_t *sock, int flag, char *arg)
302 {
303 size_t n;
304 int id;
305 svz_socket_t *xsock;
306 char proto[128];
307 svz_server_t *server;
308 svz_coserver_t *coserver;
309
310 /* Find the appropriate client or server connection. */
311 id = atoi (arg);
312 if ((xsock = svz_sock_find (id, -1)) == NULL)
313 {
314 svz_sock_printf (sock, "no such connection: %d\r\n", id);
315 return flag;
316 }
317
318 svz_sock_printf (sock, "\r\nconnection id %d (version %d) "
319 "statistics\r\n\r\n",
320 id, xsock->version);
321
322 /*
323 * Process general socket structure's flags. Uppercase words refer
324 * to set bits and lowercase to unset bits.
325 */
326 svz_sock_printf (sock,
327 " flags : %s %s %s %s %s %s %s\r\n"
328 " %s %s %s %s %s %s %s\r\n",
329 xsock->flags & SVZ_SOFLG_INBUF ? "INBUF" : "inbuf",
330 xsock->flags & SVZ_SOFLG_OUTBUF ? "OUTBUF" : "outbuf",
331 xsock->flags & SVZ_SOFLG_CONNECTED ? "CONNECTED" : "connected",
332 xsock->flags & SVZ_SOFLG_LISTENING ? "LISTENING" : "listening",
333 xsock->flags & SVZ_SOFLG_KILLED ? "KILLED" : "killed",
334 xsock->flags & SVZ_SOFLG_NOFLOOD ? "flood" : "FLOOD",
335 xsock->flags & SVZ_SOFLG_CONNECTING ? "CONNECTING" : "connecting",
336 xsock->flags & SVZ_SOFLG_INITED ? "INITED" : "inited",
337 xsock->flags & SVZ_SOFLG_COSERVER ? "COSERVER" : "coserver",
338 xsock->flags & SVZ_SOFLG_PIPE ? "PIPE" : "pipe",
339 xsock->flags & SVZ_SOFLG_FILE ? "FILE" : "file",
340 xsock->flags & SVZ_SOFLG_SOCK ? "SOCK" : "sock",
341 xsock->flags & SVZ_SOFLG_ENQUEUED ? "ENQUEUED" : "enqueued",
342 xsock->flags & SVZ_SOFLG_PRIORITY ? "PRIORITY" : "priority");
343
344 svz_sock_printf (sock, " protocol : ");
345
346 /* process connection type server flags */
347 if (xsock->flags & SVZ_SOFLG_LISTENING)
348 {
349 svz_array_t *servers;
350
351 /* a listening server */
352 strcpy (proto, "server: ");
353 if (xsock->proto & SVZ_PROTO_TCP)
354 strcat (proto, "tcp ");
355 if (xsock->proto & SVZ_PROTO_UDP)
356 strcat (proto, "udp ");
357 if (xsock->proto & SVZ_PROTO_ICMP)
358 strcat (proto, "icmp ");
359 if (xsock->proto & SVZ_PROTO_PIPE)
360 strcat (proto, "pipe ");
361 if (xsock->proto & SVZ_PROTO_RAW)
362 strcat (proto, "raw ");
363
364 svz_sock_printf (sock, "%s\r\n", proto);
365 servers = svz_sock_servers (xsock);
366 svz_array_foreach (servers, server, n)
367 {
368 svz_sock_printf (sock, " %d. %s (%s)\r\n",
369 n + 1, server->name, server->description);
370 }
371 svz_array_destroy (servers);
372 }
373 /* process client info */
374 else
375 {
376 /* usual client */
377 if ((server = svz_server_find (xsock->cfg)) != NULL)
378 {
379 char *info;
380 svz_sock_printf (sock, "%s client\r\n", server->name);
381 if (server->info_client &&
382 (info = server->info_client (server, xsock)) != NULL)
383 {
384 svz_sock_printf (sock, " %s\r\n", info);
385 }
386 }
387 /* coserver */
388 else if (xsock->flags & SVZ_SOFLG_COSERVER)
389 {
390 coserver = xsock->data;
391 svz_sock_printf (sock, "internal %s coserver\r\n",
392 svz_coserver_type_name (coserver));
393 }
394 /* unidentified */
395 else
396 {
397 svz_sock_printf (sock, "not yet identified\r\n");
398 }
399 }
400
401 /* print all previously collected statistics of this connection */
402 if (xsock->flags & SVZ_SOFLG_SOCK)
403 svz_sock_printf (sock, " sock fd : %d\r\n", xsock->sock_desc);
404 if (xsock->flags & SVZ_SOFLG_FILE)
405 svz_sock_printf (sock, " file fd : %d\r\n", xsock->file_desc);
406 if (xsock->flags & SVZ_SOFLG_PIPE)
407 svz_sock_printf (sock, " pipe fd : %d (recv), %d (send)\r\n",
408 xsock->pipe_desc[SVZ_READ],
409 xsock->pipe_desc[SVZ_WRITE]);
410
411 if (xsock->flags & SVZ_SOFLG_PIPE)
412 {
413 if (xsock->send_pipe)
414 svz_sock_printf (sock, " foreign : %s\r\n", xsock->send_pipe);
415 if (xsock->recv_pipe)
416 svz_sock_printf (sock, " local : %s\r\n", xsock->recv_pipe);
417 }
418 if (xsock->flags & SVZ_SOFLG_SOCK)
419 {
420 char buf[64];
421
422 svz_sock_printf (sock, " foreign : %s\r\n",
423 SVZ_PP_ADDR_PORT (buf, xsock->remote_addr,
424 xsock->remote_port));
425 svz_sock_printf (sock, " local : %s\r\n",
426 SVZ_PP_ADDR_PORT (buf, xsock->local_addr,
427 xsock->local_port));
428 }
429
430 svz_sock_printf (sock,
431 " sendbuf : %d (size), %d (fill), %s (last send)\r\n"
432 " recvbuf : %d (size), %d (fill), %s (last recv)\r\n"
433 " idle : %d\r\n"
434 #if ENABLE_FLOOD_PROTECTION
435 " flood : %d (points), %d (limit)\r\n"
436 #endif /* ENABLE_FLOOD_PROTECTION */
437 " avail : %s\r\n\r\n",
438 xsock->send_buffer_size,
439 xsock->send_buffer_fill,
440 svz_time (xsock->last_send),
441 xsock->recv_buffer_size,
442 xsock->recv_buffer_fill,
443 svz_time (xsock->last_recv),
444 xsock->idle_counter,
445 #if ENABLE_FLOOD_PROTECTION
446 xsock->flood_points,
447 xsock->flood_limit,
448 #endif /* ENABLE_FLOOD_PROTECTION */
449 xsock->unavailable ? "no" : "yes");
450
451 return flag;
452 }
453
454 static char *
uptime(long diff)455 uptime (long diff)
456 {
457 #define TEXTSIZE 64
458 #define SET_TEXT(fmt,...) snprintf (text, TEXTSIZE, fmt, __VA_ARGS__)
459
460 static char text[TEXTSIZE];
461 long sec, min, hour, day, old;
462
463 old = diff;
464 sec = diff % 60;
465 diff /= 60;
466 min = diff % 60;
467 diff /= 60;
468 hour = diff % 24;
469 diff /= 24;
470 day = diff;
471
472 if (old < 60)
473 SET_TEXT ("%ld sec", sec);
474 else if (old < 60 * 60)
475 SET_TEXT ("%ld min", min);
476 else if (old < 60 * 60 * 24)
477 SET_TEXT ("%ld hours, %ld min", hour, min);
478 else
479 SET_TEXT ("%ld days, %ld:%02ld", day, hour, min);
480
481 return text;
482
483 #undef SET_TEXT
484 #undef TEXTSIZE
485 }
486
487 /*
488 * General statistics about Serveez. Here we display all the information
489 * we could get from the system and the process itself.
490 * Furthermore we check if the command is something about a certain
491 * server and give information about it if so.
492 */
493 int
ctrl_stat(svz_socket_t * sock,int flag,char * arg)494 ctrl_stat (svz_socket_t *sock, int flag, char *arg)
495 {
496 svz_server_t *server;
497 char *p;
498 long ut = svz_uptime ();
499
500 /* find an appropriate server instance */
501 p = arg;
502 while (*p && *p != '\r' && *p != '\n')
503 p++;
504 if (*p)
505 *p = '\0';
506 if ((server = svz_server_get (arg)) != NULL)
507 {
508 svz_sock_printf (sock, "\r\n%s (%s):\r\n",
509 server->description, server->name);
510 if (server->info_server && (p = server->info_server (server)) != NULL)
511 {
512 svz_sock_printf (sock, "%s\r\n", p);
513 }
514 svz_sock_printf (sock, "\r\n");
515 return flag;
516 }
517
518 /* print a standard output */
519 svz_sock_printf (sock,
520 "\r\nThis is %s running since %s.\r\n",
521 PACKAGE_STRING, svz_time (time (NULL) - ut));
522
523 /* display compile time feature list */
524 svz_sock_printf (sock, "Features : FOO"
525 #ifdef ENABLE_HTTP_PROTO
526 " HTTP"
527 #endif /* ENABLE_HTTP_PROTO */
528 #ifdef ENABLE_IRC_PROTO
529 " IRC"
530 #endif /* ENABLE_IRC_PROTO */
531 " CTRL"
532 #if ENABLE_SNTP_PROTO
533 " SNTP"
534 #endif /* ENABLE_SNTP_PROTO */
535 #if ENABLE_GNUTELLA
536 " NUT"
537 #endif /* ENABLE_GNUTELLA */
538 #if ENABLE_TUNNEL
539 " TUNNEL"
540 #endif /* ENABLE_TUNNEL */
541 #if ENABLE_FAKEIDENT
542 " IDENTD"
543 #endif /* ENABLE_FAKEIDENT */
544 #if ENABLE_PROG_SERVER
545 " PROG"
546 #endif /* ENABLE_PROG_SERVER */
547 "\r\n");
548
549 /* second feature line */
550 svz_sock_printf (sock, " "
551 " IDENT"
552 " REVERSE-DNS"
553 " DNS"
554 #ifdef ENABLE_FLOOD_PROTECTION
555 " FLOOD"
556 #endif /* ENABLE_FLOOD_PROTECTION */
557 #ifdef ENABLE_DEBUG
558 " DEBUG"
559 #endif /* ENABLE_DEBUG */
560 #if defined (__MINGW32__) || defined (__CYGWIN__)
561 " WIN32"
562 #endif /* __MINGW32__, __CYGWIN__ */
563 "\r\n");
564
565 /* display system and process information */
566 svz_sock_printf (sock, "Os : %s\r\n", svz_sys_version ());
567 svz_sock_printf (sock, "Sys-Load : %s\r\n", cpu_state.info);
568 svz_sock_printf (sock, "Proc-Load : %s\r\n", cpu_state.pinfo);
569
570 /* show general state */
571 svz_sock_printf (sock, "\r\n * %d connected sockets (hard limit is %d)\r\n",
572 svz_sock_nconnections (), SVZ_RUNPARM (MAX_SOCKETS));
573 svz_sock_printf (sock, " * uptime is %s\r\n", uptime (ut));
574 #if ENABLE_DEBUG
575 {
576 size_t cur[2];
577
578 svz_get_curalloc (cur);
579 svz_sock_printf (sock, " * %d bytes of memory in %d blocks allocated\r\n",
580 cur[0], cur[1]);
581 }
582 #endif /* ENABLE_DEBUG */
583 svz_sock_printf (sock, "\r\n");
584
585 return flag;
586 }
587
588 static int
stat_con_internal(svz_socket_t * sock,void * closure)589 stat_con_internal (svz_socket_t *sock, void *closure)
590 {
591 svz_socket_t *to = closure;
592 char *id;
593 char linet[64];
594 char rinet[64];
595 svz_server_t *server;
596
597 if (sock->flags & SVZ_SOFLG_LISTENING)
598 id = "Listener";
599 else if (sock->flags & SVZ_SOFLG_COSERVER)
600 id = "Co-Server";
601 else if ((server = svz_server_find (sock->cfg)) != NULL)
602 id = server->name;
603 else
604 id = "None";
605
606 svz_sock_printf (to,
607 "%-16s %4d %6d %6d "
608 "%-20s %-20s" /* FIXME: IPv4 */
609 "\r\n", id,
610 sock->id, sock->recv_buffer_fill,
611 sock->send_buffer_fill,
612 SVZ_PP_ADDR_PORT (linet, sock->local_addr, sock->local_port),
613 SVZ_PP_ADDR_PORT (rinet, sock->remote_addr, sock->remote_port));
614 return 0;
615 }
616
617 /*
618 * Connection statistics. This function displays basic information about
619 * each socket structure currently within the socket list.
620 */
621 int
ctrl_stat_con(svz_socket_t * sock,int flag,UNUSED char * arg)622 ctrl_stat_con (svz_socket_t *sock, int flag, UNUSED char *arg)
623 {
624 svz_sock_printf (sock, "\r\n%s",
625 "Proto Id RecvQ SendQ "
626 "Local Foreign\r\n");
627 svz_foreach_socket (stat_con_internal, sock);
628 svz_sock_printf (sock, "\r\n");
629
630 return flag;
631 }
632
633 #if ENABLE_HTTP_PROTO
634 /*
635 * HTTP cache statistics. The following displayed information is a
636 * visual representation of the http cache structures.
637 */
638 int
ctrl_stat_cache(svz_socket_t * sock,int flag,UNUSED char * arg)639 ctrl_stat_cache (svz_socket_t *sock, int flag, UNUSED char *arg)
640 {
641 int n, total, files;
642 char *p;
643 http_cache_entry_t *cache;
644
645 svz_sock_printf (sock, "\r\n%s",
646 "File "
647 "Size Usage Hits Recent Ready\r\n");
648
649 files = total = n = 0;
650 /* go through each cache entry */
651 for (cache = http_cache_first; cache; cache = cache->next, n++)
652 {
653 files++;
654 total += cache->size;
655 p = cache->file;
656 p += strlen (cache->file);
657 while (*p != '/' && *p != '\\' && p != cache->file) p--;
658 if (p != cache->file) p++;
659 svz_sock_printf (sock, "%-30s %6d %6d %5d %6d %-5s\r\n", p,
660 cache->size, cache->usage, cache->hits, n,
661 cache->ready ? "Yes" : "No");
662 }
663
664 /* print cache summary */
665 svz_sock_printf (sock, "\r\nTotal : %d byte in %d cache entries\r\n\r\n",
666 total, files);
667
668 return flag;
669 }
670
671 /*
672 * Free all HTTP cache entries.
673 */
674 int
ctrl_kill_cache(svz_socket_t * sock,int flag,UNUSED char * arg)675 ctrl_kill_cache (svz_socket_t *sock, int flag, UNUSED char *arg)
676 {
677 svz_sock_printf (sock, "%d HTTP cache entries reinitialized.\r\n",
678 http_cache_entries);
679 http_free_cache ();
680 http_alloc_cache (http_cache_entries);
681 return flag;
682 }
683 #endif /* ENABLE_HTTP_PROTO */
684
685 static int
stat_coservers_internal(const svz_coserver_t * coserver,void * closure)686 stat_coservers_internal (const svz_coserver_t *coserver,
687 void *closure)
688 {
689 svz_socket_t *sock = closure;
690
691 svz_sock_printf (sock, "\r\ninternal %s coserver:\r\n",
692 svz_coserver_type_name (coserver));
693 svz_sock_printf (sock,
694 " socket id : %d\r\n"
695 " %s %d\r\n"
696 " requests : %d\r\n",
697 coserver->sock->id,
698 #ifndef __MINGW32__
699 "process id :", coserver->pid,
700 #else /* __MINGW32__ */
701 "thread id :", coserver->tid,
702 #endif /* __MINGW32__ */
703 coserver->busy);
704 return 0;
705 }
706
707 /*
708 * Show all Co-Server instances statistics.
709 */
710 int
ctrl_stat_coservers(svz_socket_t * sock,int flag,UNUSED char * arg)711 ctrl_stat_coservers (svz_socket_t *sock, int flag, UNUSED char *arg)
712 {
713 /* go through all internal coserver instances */
714 svz_foreach_coserver (stat_coservers_internal, sock);
715 svz_sock_printf (sock, "\r\n");
716 return flag;
717 }
718
719 static void
stat_all_internal(svz_server_t * server,void * closure)720 stat_all_internal (svz_server_t *server, void *closure)
721 {
722 svz_socket_t *sock = closure;
723
724 svz_sock_printf (sock, "\r\n%s (%s):\r\n",
725 server->description, server->name);
726 if (server->info_server)
727 svz_sock_printf (sock, "%s\r\n", server->info_server (server));
728 }
729
730 /*
731 * Server and Co-Server instance statistics.
732 */
733 int
ctrl_stat_all(svz_socket_t * sock,int flag,char * arg)734 ctrl_stat_all (svz_socket_t *sock, int flag, char *arg)
735 {
736 /* go through all server instances */
737 svz_foreach_server (stat_all_internal, sock);
738
739 /* show coserver statistics */
740 ctrl_stat_coservers (sock, flag, arg);
741
742 return flag;
743 }
744
745 /*
746 * Shutdown a specified network connection. This might even be used to
747 * kill your own (the control client's) connection, coservers and servers.
748 * So you want to be *very* careful with this command.
749 */
750 int
ctrl_kill_id(svz_socket_t * sock,int flag,char * arg)751 ctrl_kill_id (svz_socket_t *sock, int flag, char *arg)
752 {
753 int id;
754 svz_socket_t *xsock;
755
756 id = atoi (arg);
757 if ((xsock = svz_sock_find (id, -1)) == NULL)
758 {
759 svz_sock_printf (sock, "no such connection: %d\r\n", id);
760 return flag;
761 }
762
763 svz_sock_schedule_for_shutdown (xsock);
764 svz_sock_printf (sock, "scheduled socket id %d for shutdown\r\n", id);
765 return flag;
766 }
767
768 struct killall_closure
769 {
770 svz_socket_t *to;
771 int n;
772 };
773
774 static int
killall_internal(svz_socket_t * sock,void * closure)775 killall_internal (svz_socket_t *sock, void *closure)
776 {
777 struct killall_closure *x = closure;
778
779 if (x->to != sock && !(sock->flags & (SVZ_SOFLG_LISTENING
780 | SVZ_SOFLG_COSERVER
781 | SVZ_SOFLG_PRIORITY)))
782 {
783 svz_sock_schedule_for_shutdown (sock);
784 (x->n)++;
785 }
786 return 0;
787 }
788
789 /*
790 * Shutdown all network connections except listening, control connections,
791 * coservers and sockets with the priority flag set.
792 */
793 int
ctrl_killall(svz_socket_t * sock,int flag,UNUSED char * arg)794 ctrl_killall (svz_socket_t *sock, int flag, UNUSED char *arg)
795 {
796 struct killall_closure closure = { sock, 0 };
797
798 svz_foreach_socket (killall_internal, &closure);
799 svz_sock_printf (sock, "killed %d network connections\r\n", closure.n);
800
801 return flag;
802 }
803
804 struct restart_closure
805 {
806 svz_socket_t *sock;
807 int type;
808 };
809
810 static int
restart_coservers_internal(const svz_coserver_t * coserver,void * closure)811 restart_coservers_internal (const svz_coserver_t *coserver,
812 void *closure)
813 {
814 struct restart_closure *x = closure;
815 svz_socket_t *sock = x->sock;
816 int type = x->type;
817
818 if (coserver->type != type)
819 return 0;
820
821 svz_coserver_destroy (type);
822 svz_coserver_create (type);
823 svz_sock_printf (sock, "internal %s coserver restarted\r\n",
824 svz_coserver_type_name (coserver));
825 return -1;
826 }
827
828 /*
829 * Restart coservers.
830 */
831 int
ctrl_restart(svz_socket_t * sock,int type,UNUSED char * arg)832 ctrl_restart (svz_socket_t *sock, int type, UNUSED char *arg)
833 {
834 struct restart_closure closure = { sock, type };
835 svz_coserver_t *coserver;
836
837 /* find an appropriate coserver to kill */
838 if (0 > svz_foreach_coserver (restart_coservers_internal, &closure))
839 return 0;
840
841 /* start a new internal coserver if there has none found */
842 coserver = svz_coserver_create (type);
843 svz_sock_printf (sock, "internal %s coserver invoked\r\n",
844 svz_coserver_type_name (coserver));
845 return 0;
846 }
847
848 /*
849 * This structure defines the calling conventions for the various
850 * control protocol commands.
851 */
852 struct
853 {
854 char *command; /* the complete command string */
855 int (*func)(svz_socket_t *, int, char *); /* callback routine */
856 int flag; /* second argument */
857 }
858 ctrl[] =
859 {
860 { CTRL_CMD_HELP, ctrl_help, 0 },
861 { CTRL_CMD_QUIT, ctrl_quit, -1 },
862 { CTRL_CMD_EXIT, ctrl_quit, -1 },
863 { CTRL_CMD_BYE, ctrl_quit, -1 },
864 { CTRL_CMD_STAT_COSERVER, ctrl_stat_coservers, 0 },
865 { CTRL_CMD_STAT_CON, ctrl_stat_con, 0 },
866 { CTRL_CMD_STAT_ID, ctrl_stat_id, 0 },
867 { CTRL_CMD_STAT_ALL, ctrl_stat_all, 0 },
868 #if ENABLE_HTTP_PROTO
869 { CTRL_CMD_STAT_CACHE, ctrl_stat_cache, 0 },
870 { CTRL_CMD_KILL_CACHE, ctrl_kill_cache, 0 },
871 #endif
872 { CTRL_CMD_STAT, ctrl_stat, 0 },
873 { CTRL_CMD_KILLALL, ctrl_killall, 0 },
874 { CTRL_CMD_KILL_ID, ctrl_kill_id, 0 },
875 { CTRL_CMD_RESTART_RDNS, ctrl_restart, SVZ_COSERVER_REVERSE_DNS },
876 { CTRL_CMD_RESTART_IDENT, ctrl_restart, SVZ_COSERVER_IDENT },
877 { CTRL_CMD_RESTART_DNS, ctrl_restart, SVZ_COSERVER_DNS },
878 { NULL, NULL, 0 }
879 };
880
881 /*
882 * The ctrl_handle_request routine gets called by the check_request
883 * routine of the control protocol.
884 */
885 int
ctrl_handle_request(svz_socket_t * sock,char * request,int len)886 ctrl_handle_request (svz_socket_t *sock, char *request, int len)
887 {
888 static char last_request[CTRL_RECV_BUFSIZE];
889 static int last_len;
890 int n;
891 int ret = 0;
892 int l;
893
894 /* search through if there is an input line */
895 while (request[len - 1] == '\r' || request[len - 1] == '\n')
896 len--;
897
898 /* password given? */
899 if (!(sock->userflags & CTRL_FLAG_PASSED))
900 {
901 /*
902 * check here the control protocol password
903 */
904 if (len <= 2) return -1;
905 #if defined HAVE_CRYPT
906 request[len] = '\0';
907 if (control_protocol_password == NULL ||
908 !strcmp (crypt (request, control_protocol_password),
909 control_protocol_password))
910 #else
911 if (control_protocol_password == NULL ||
912 (!memcmp (request, control_protocol_password, len) &&
913 (unsigned) len >= strlen (control_protocol_password)))
914 #endif
915 {
916 sock->userflags |= CTRL_FLAG_PASSED;
917 svz_sock_printf (sock, "Login ok.\r\n%s", CTRL_PROMPT);
918 }
919 else return -1;
920 }
921 /* yes, already logged in */
922 else if (len > 0)
923 {
924 /* repeat last command */
925 if (!memcmp (request, "/\r\n", 3))
926 {
927 memcpy (request, last_request, len = last_len);
928 }
929 /* go through all commands */
930 n = 0;
931 while (ctrl[n].command != NULL)
932 {
933 l = strlen (ctrl[n].command);
934 if (!memcmp (request, ctrl[n].command, l))
935 {
936 /* save this command for repetition */
937 memcpy (last_request, request, last_len = len);
938
939 /* execute valid command and give the prompt */
940 ret = ctrl[n].func (sock, ctrl[n].flag, &request[l + 1]);
941 svz_sock_printf (sock, "%s", CTRL_PROMPT);
942 return ret;
943 }
944 n++;
945 }
946 l = 0;
947 while (l < len && request[l] >= ' ') l++;
948 request[l] = 0;
949 svz_sock_printf (sock, "no such command: %s\r\n", request);
950 svz_sock_printf (sock, "%s", CTRL_PROMPT);
951 }
952 else
953 {
954 svz_sock_printf (sock, "%s", CTRL_PROMPT);
955 }
956 return ret;
957 }
958
959 /*
960 * Depending on the systems this routine gets the cpu load.
961 * Returns -1 if an error occurred.
962 * Linux -- /proc/stat
963 * HP-Unix -- ‘pstat_getdynamic’
964 * Solaris -- ‘kstat_read’
965 * Irix -- ‘sysget’
966 * MacOS -- ‘host_statistics’
967 */
968 static int
ctrl_get_cpu_state(void)969 ctrl_get_cpu_state (void)
970 {
971 int n;
972
973 #if HAVE_LIBKSTAT
974 static kstat_ctl_t *kc;
975 static kstat_t *ksp = NULL;
976 static cpu_stat_t cs;
977 #elif HAVE_PROC_STAT
978 FILE *f;
979 static char stat[STAT_BUFFER_SIZE];
980 #elif HAVE_PSTAT
981 struct pst_dynamic stats;
982 #elif HAVE_SYSGET
983 struct sysinfo_cpu info;
984 sgt_cookie_t cookie;
985 #elif HAVE_HOST_STATISTICS
986 host_cpu_load_info_data_t info;
987 mach_msg_type_number_t count;
988 #endif
989
990 #if HAVE_TIMES
991 struct tms proc_tms;
992 #endif
993
994 n = (cpu_state.index + 1) & 1;
995
996 #if HAVE_TIMES
997 cpu_state.ptotal[n] = times (&proc_tms);
998 cpu_state.proc[n][0] = proc_tms.tms_utime;
999 cpu_state.proc[n][1] = proc_tms.tms_stime;
1000 cpu_state.proc[n][2] = proc_tms.tms_cutime;
1001 cpu_state.proc[n][3] = proc_tms.tms_cstime;
1002 #else /* not HAVE_TIMES */
1003 cpu_state.ptotal[n] = cpu_state.ptotal[cpu_state.index] +
1004 (CLOCKS_PER_SEC * CTRL_LOAD_UPDATE);
1005 cpu_state.proc[n][0] = clock ();
1006 #endif /* not HAVE_TIMES */
1007
1008 #if HAVE_LIBKSTAT /* Solaris */
1009
1010 if (ksp == NULL)
1011 {
1012 kc = kstat_open ();
1013
1014 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next)
1015 if (strncmp (ksp->ks_name, "cpu_stat", 8) == 0)
1016 break;
1017 }
1018 else
1019 {
1020 if (kstat_read (kc, ksp, &cs) == -1)
1021 {
1022 snprintf (cpu_state.info, STAT_BUFFER_SIZE,
1023 "kstat_read(2) failed");
1024 return -1;
1025 }
1026
1027 cpu_state.cpu[n][0] = cs.cpu_sysinfo.cpu[CPU_USER];
1028 cpu_state.cpu[n][1] = cs.cpu_sysinfo.cpu[CPU_KERNEL];
1029 cpu_state.cpu[n][2] = cs.cpu_sysinfo.cpu[CPU_WAIT];
1030 cpu_state.cpu[n][3] = cs.cpu_sysinfo.cpu[CPU_IDLE];
1031 }
1032
1033 #elif HAVE_PROC_STAT /* Linux */
1034
1035 /* open the statistics file */
1036 if ((f = svz_fopen (cpu_state.cpufile, "r")) == NULL)
1037 {
1038 snprintf (cpu_state.info, STAT_BUFFER_SIZE,
1039 "%s not available", cpu_state.cpufile);
1040 return -1;
1041 }
1042
1043 /* find the appropriate cpu statistics line */
1044 while (fgets (stat, STAT_BUFFER_SIZE, f))
1045 {
1046 if (4 == sscanf (stat, cpu_state.cpuline,
1047 &cpu_state.cpu[n][0],
1048 &cpu_state.cpu[n][1],
1049 &cpu_state.cpu[n][2],
1050 &cpu_state.cpu[n][3]))
1051 {
1052 svz_fclose (f);
1053 return 0;
1054 }
1055 }
1056
1057 /* cpu line not found */
1058 snprintf (cpu_state.info, STAT_BUFFER_SIZE,
1059 "cpu line not found in %s", cpu_state.cpufile);
1060 svz_fclose (f);
1061
1062 #elif HAVE_PSTAT /* HP Unix */
1063
1064 pstat_getdynamic (&stats, sizeof (struct pst_dynamic), 1, 0);
1065
1066 cpu_state.cpu[n][0] = stats.psd_cpu_time[0];
1067 cpu_state.cpu[n][1] = stats.psd_cpu_time[1];
1068 cpu_state.cpu[n][2] = stats.psd_cpu_time[2];
1069 cpu_state.cpu[n][3] = stats.psd_cpu_time[3];
1070
1071 #elif HAVE_SYSGET /* Irix */
1072
1073 SGT_COOKIE_INIT (&cookie);
1074 sysget (SGT_SINFO_CPU, (char *) &info, sizeof (info), SGT_READ, &cookie);
1075
1076 cpu_state.cpu[n][0] = info.cpu[CPU_USER];
1077 cpu_state.cpu[n][1] = info.cpu[CPU_KERNEL];
1078 cpu_state.cpu[n][2] = info.cpu[CPU_WAIT];
1079 cpu_state.cpu[n][3] = info.cpu[CPU_IDLE];
1080
1081 #elif HAVE_HOST_STATISTICS /* MacOS */
1082
1083 host_statistics (mach_host_self (),
1084 HOST_CPU_LOAD_INFO, (host_info_t) &info, &count);
1085
1086 cpu_state.cpu[n][0] = info.cpu_ticks[CPU_STATE_USER];
1087 cpu_state.cpu[n][1] = info.cpu_ticks[CPU_STATE_SYSTEM];
1088 cpu_state.cpu[n][2] = info.cpu_ticks[CPU_STATE_IDLE];
1089 cpu_state.cpu[n][3] = info.cpu_ticks[CPU_STATE_NICE];
1090
1091 #endif
1092 return 0;
1093 }
1094
1095 /*
1096 * Within the CTRL_IDLE function the server gets the CPU
1097 * load. This procedure differs on different platforms.
1098 */
1099
1100 #define PROC_DIFF(x) (c->proc[n][x] - c->proc[old][x])
1101 #define CPU_DIFF(x) (c->cpu[n][x] - c->cpu[old][x])
1102
1103 int
ctrl_idle(svz_socket_t * sock)1104 ctrl_idle (svz_socket_t *sock)
1105 {
1106 int n, old, ret;
1107 unsigned long all;
1108 cpu_state_t *c = &cpu_state;
1109
1110 old = c->index;
1111 n = (c->index + 1) & 1;
1112
1113 /* get status of the cpu and process */
1114 ret = ctrl_get_cpu_state ();
1115
1116 /* calculate process specific info */
1117 all = c->ptotal[n] - c->ptotal[old];
1118 if (all != 0)
1119 {
1120 snprintf (c->pinfo, STAT_BUFFER_SIZE, PROC_FORMAT,
1121 PROC_DIFF (0) * 100 / all,
1122 PROC_DIFF (0) * 1000 / all % 10,
1123 PROC_DIFF (1) * 100 / all,
1124 PROC_DIFF (1) * 1000 / all % 10,
1125 PROC_DIFF (2) * 100 / all,
1126 PROC_DIFF (2) * 1000 / all % 10,
1127 PROC_DIFF (3) * 100 / all,
1128 PROC_DIFF (3) * 1000 / all % 10);
1129 }
1130
1131 if (ret != -1)
1132 {
1133 /* calculate cpu specific info */
1134 c->total[n] = c->cpu[n][0] + c->cpu[n][1] + c->cpu[n][2] + c->cpu[n][3];
1135 all = c->total[n] - c->total[old];
1136 if (all != 0)
1137 {
1138 snprintf (c->info, STAT_BUFFER_SIZE, c->cpuinfoline,
1139 CPU_DIFF (0) * 100 / all,
1140 CPU_DIFF (0) * 1000 / all % 10,
1141 CPU_DIFF (1) * 100 / all,
1142 CPU_DIFF (1) * 1000 / all % 10,
1143 CPU_DIFF (2) * 100 / all,
1144 CPU_DIFF (2) * 1000 / all % 10,
1145 CPU_DIFF (3) * 100 / all,
1146 CPU_DIFF (3) * 1000 / all % 10);
1147 }
1148 }
1149
1150 c->index = n;
1151 sock->idle_counter = CTRL_LOAD_UPDATE;
1152 return 0;
1153 }
1154