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