e2f74871469f92e69cbc6f6137413ca289d3d2c4
[sip-router] / core_cmd.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2005 iptelorg GmbH
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28 #include <time.h>
29 #include <sys/types.h>
30 #include <signal.h>
31 #include "mem/mem.h"
32 #include "mem/shm_mem.h"
33 #include "sr_module.h"
34 #include "dprint.h"
35 #include "core_cmd.h"
36 #include "globals.h"
37 #include "pt.h"
38 #include "ut.h"
39 #include "tcp_info.h"
40 #include "tcp_options.h"
41 #include "core_cmd.h"
42
43 #ifdef USE_DNS_CACHE
44 void dns_cache_debug(rpc_t* rpc, void* ctx);
45 void dns_cache_debug_all(rpc_t* rpc, void* ctx);
46 void dns_cache_mem_info(rpc_t* rpc, void* ctx);
47 void dns_cache_view(rpc_t* rpc, void* ctx);
48 void dns_cache_delete_all(rpc_t* rpc, void* ctx);
49 void dns_cache_add_a(rpc_t* rpc, void* ctx);
50 void dns_cache_add_aaaa(rpc_t* rpc, void* ctx);
51 void dns_cache_add_srv(rpc_t* rpc, void* ctx);
52 void dns_cache_delete_a(rpc_t* rpc, void* ctx);
53 void dns_cache_delete_aaaa(rpc_t* rpc, void* ctx);
54 void dns_cache_delete_srv(rpc_t* rpc, void* ctx);
55
56
57 static const char* dns_cache_mem_info_doc[] = {
58         "dns cache memory info.",    /* Documentation string */
59         0                      /* Method signature(s) */
60 };
61 static const char* dns_cache_debug_doc[] = {
62         "dns debug  info.",    /* Documentation string */
63         0                      /* Method signature(s) */
64 };
65
66 static const char* dns_cache_debug_all_doc[] = {
67         "complete dns debug  dump",    /* Documentation string */
68         0                              /* Method signature(s) */
69 };
70
71 static const char* dns_cache_view_doc[] = {
72         "dns cache dump in a human-readable format",
73         0
74 };
75
76 static const char* dns_cache_delete_all_doc[] = {
77         "deletes all the entries from the DNS cache",
78         0
79 };
80
81 static const char* dns_cache_add_a_doc[] = {
82         "adds an A record to the DNS cache",
83         0
84 };
85
86 static const char* dns_cache_add_aaaa_doc[] = {
87         "adds an AAAA record to the DNS cache",
88         0
89 };
90 static const char* dns_cache_add_srv_doc[] = {
91         "adds an SRV record to the DNS cache",
92         0
93 };
94
95 static const char* dns_cache_delete_a_doc[] = {
96         "deletes an A record from the DNS cache",
97         0
98 };
99
100 static const char* dns_cache_delete_aaaa_doc[] = {
101         "deletes an AAAA record from the DNS cache",
102         0
103 };
104
105 static const char* dns_cache_delete_srv_doc[] = {
106         "deletes an SRV record from the DNS cache",
107         0
108 };
109
110 #ifdef USE_DNS_CACHE_STATS
111 void dns_cache_stats_get(rpc_t* rpc, void* ctx);
112
113 static const char* dns_cache_stats_get_doc[] = {
114         "returns the dns measurement counters.",
115         0
116 };
117 #endif /* USE_DNS_CACHE_STATS */
118 #ifdef DNS_WATCHDOG_SUPPORT
119 void dns_set_server_state_rpc(rpc_t* rpc, void* ctx);
120
121 static const char* dns_set_server_state_doc[] = {
122         "sets the state of the DNS servers " \
123         "(0: all the servers are down, 1: at least one server is up)",    /* Documentation string */
124         0                              /* Method signature(s) */
125 };
126
127 void dns_get_server_state_rpc(rpc_t* rpc, void* ctx);
128
129 static const char* dns_get_server_state_doc[] = {
130         "prints the state of the DNS servers " \
131         "(0: all the servers are down, 1: at least one server is up)",  /* Documentation string */
132         0                               /* Method signature(s) */
133 };
134
135 #endif /* DNS_WATCHDOG_SUPPORT */
136 #endif /* USE_DNS_CACHE */
137 #ifdef USE_DST_BLACKLIST
138 void dst_blst_debug(rpc_t* rpc, void* ctx);
139 void dst_blst_mem_info(rpc_t* rpc, void* ctx);
140 void dst_blst_view(rpc_t* rpc, void* ctx);
141 void dst_blst_delete_all(rpc_t* rpc, void* ctx);
142 void dst_blst_add(rpc_t* rpc, void* ctx);
143
144 static const char* dst_blst_mem_info_doc[] = {
145         "dst blacklist memory usage info.",  /* Documentation string */
146         0                                    /* Method signature(s) */
147 };
148 static const char* dst_blst_debug_doc[] = {
149         "dst blacklist  debug  info.",  /* Documentation string */
150         0                               /* Method signature(s) */
151 };
152 static const char* dst_blst_view_doc[] = {
153         "dst blacklist dump in human-readable format.",  /* Documentation string */
154         0                               /* Method signature(s) */
155 };
156 static const char* dst_blst_delete_all_doc[] = {
157         "Deletes all the entries from the dst blacklist except the permanent ones.",  /* Documentation string */
158         0                               /* Method signature(s) */
159 };
160 static const char* dst_blst_add_doc[] = {
161         "Adds a new entry to the dst blacklist.",  /* Documentation string */
162         0                               /* Method signature(s) */
163 };
164 #ifdef USE_DST_BLACKLIST_STATS
165 void dst_blst_stats_get(rpc_t* rpc, void* ctx);
166
167 static const char* dst_blst_stats_get_doc[] = {
168         "returns the dst blacklist measurement counters.",
169         0
170 };
171 #endif /* USE_DST_BLACKLIST_STATS */
172
173 #endif
174
175
176
177 #define MAX_CTIME_LEN 128
178
179 /* up time */
180 static char up_since_ctime[MAX_CTIME_LEN];
181
182
183 static const char* system_listMethods_doc[] = {
184         "Lists all RPC methods supported by the server.",  /* Documentation string */
185         0                                                  /* Method signature(s) */
186 };
187
188 static void system_listMethods(rpc_t* rpc, void* c)
189 {
190         struct sr_module* t;
191         rpc_export_t* ptr;
192
193         for(ptr = core_rpc_methods; ptr && ptr->name; ptr++) {
194                 if (rpc->add(c, "s", ptr->name) < 0) return;
195         }
196
197         for(t = modules; t; t = t->next) {
198                 for(ptr = t->exports->rpc_methods; ptr && ptr->name; ptr++) {
199                         if (rpc->add(c, "s", ptr->name) < 0) return;
200                 }
201         }
202 }
203
204 static const char* system_methodSignature_doc[] = {
205         "Returns signature of given method.",  /* Documentation string */
206         0                                      /* Method signature(s) */
207 };
208
209 static void system_methodSignature(rpc_t* rpc, void* c)
210 {
211         rpc->fault(c, 500, "Not Implemented Yet");
212 }
213
214
215 static const char* system_methodHelp_doc[] = {
216         "Print the help string for given method.",  /* Documentation string */
217         0                                           /* Method signature(s) */
218 };
219
220 static void system_methodHelp(rpc_t* rpc, void* c)
221 {
222         struct sr_module* t;
223         rpc_export_t* ptr;
224         char* name;
225
226         if (rpc->scan(c, "s", &name) < 1) {
227                 rpc->fault(c, 400, "Method Name Expected");
228                 return;
229         }
230
231         for(t = modules; t; t = t->next) {
232                 for(ptr = t->exports->rpc_methods; ptr && ptr->name; ptr++) {
233                         if (strcmp(name, ptr->name) == 0) {
234                                 if (ptr->doc_str && ptr->doc_str[0]) {
235                                         rpc->add(c, "s", ptr->doc_str[0]);
236                                 } else {
237                                         rpc->add(c, "s", "undocumented");
238                                 }
239                                 return;
240                         }
241                 }
242         }
243         /* try the core methods too */
244         for (ptr=core_rpc_methods;ptr && ptr->name; ptr++){
245                         if (strcmp(name, ptr->name) == 0) {
246                                 if (ptr->doc_str && ptr->doc_str[0]) {
247                                         rpc->add(c, "s", ptr->doc_str[0]);
248                                 } else {
249                                         rpc->add(c, "s", "undocumented");
250                                 }
251                                 return;
252                         }
253         }
254         rpc->fault(c, 400, "command not found");
255 }
256
257
258 static const char* core_prints_doc[] = {
259         "Returns the string given as parameter.",   /* Documentation string */
260         0                                           /* Method signature(s) */
261 };
262
263
264 static void core_prints(rpc_t* rpc, void* c)
265 {
266         char* string = 0;
267         if (rpc->scan(c, "s", &string)>0)
268                 rpc->add(c, "s", string);
269 }
270
271
272 static const char* core_version_doc[] = {
273         "Returns the version string of the server.", /* Documentation string */
274         0                                           /* Method signature(s) */
275 };
276
277 static void core_version(rpc_t* rpc, void* c)
278 {
279         rpc->add(c, "s", SERVER_HDR);
280 }
281
282
283
284 static const char* core_uptime_doc[] = {
285         "Returns uptime of SER server.",  /* Documentation string */
286         0                                 /* Method signature(s) */
287 };
288
289
290 static void core_uptime(rpc_t* rpc, void* c)
291 {
292         void* s;
293         time_t now;
294
295         time(&now);
296
297         if (rpc->add(c, "{", &s) < 0) return;
298         rpc->struct_add(s, "s", "now", ctime(&now));
299         rpc->struct_add(s, "s", "up_since", up_since_ctime);
300         /* no need for a float here (unless you're concerned that your uptime)
301         rpc->struct_add(s, "f", "uptime",  difftime(now, up_since));
302         */
303         /* on posix system we can substract time_t directly */
304         rpc->struct_add(s, "d", "uptime",  (int)(now-up_since));
305 }
306
307
308 static const char* core_ps_doc[] = {
309         "Returns the description of running SER processes.",  /* Documentation string */
310         0                                                     /* Method signature(s) */
311 };
312
313
314 static void core_ps(rpc_t* rpc, void* c)
315 {
316         int p;
317
318         for (p=0; p<*process_count;p++) {
319                 rpc->add(c, "d", pt[p].pid);
320                 rpc->add(c, "s", pt[p].desc);
321         }
322 }
323
324
325 static const char* core_pwd_doc[] = {
326         "Returns the working directory of SER server.",    /* Documentation string */
327         0                                                  /* Method signature(s) */
328 };
329
330
331 static void core_pwd(rpc_t* rpc, void* c)
332 {
333         char *cwd_buf;
334         int max_len;
335
336         max_len = pathmax();
337         cwd_buf = pkg_malloc(max_len);
338         if (!cwd_buf) {
339                 ERR("core_pwd: No memory left\n");
340                 rpc->fault(c, 500, "Server Ran Out of Memory");
341                 return;
342         }
343
344         if (getcwd(cwd_buf, max_len)) {
345                 rpc->add(c, "s", cwd_buf);
346         } else {
347                 rpc->fault(c, 500, "getcwd Failed");
348         }
349         pkg_free(cwd_buf);
350 }
351
352
353 static const char* core_arg_doc[] = {
354         "Returns the list of command line arguments used on SER startup.",  /* Documentation string */
355         0                                                                   /* Method signature(s) */
356 };
357
358
359 static void core_arg(rpc_t* rpc, void* c)
360 {
361         int p;
362
363         for (p = 0; p < my_argc; p++) {
364                 if (rpc->add(c, "s", my_argv[p]) < 0) return;
365         }
366 }
367
368
369 static const char* core_kill_doc[] = {
370         "Sends the given signal to SER.",  /* Documentation string */
371         0                                  /* Method signature(s) */
372 };
373
374
375 static void core_kill(rpc_t* rpc, void* c)
376 {
377         int sig_no = 15;
378         rpc->scan(c, "d", &sig_no);
379         rpc->send(c);
380         kill(0, sig_no);
381 }
382
383 static void core_shmmem(rpc_t* rpc, void* c)
384 {
385         struct mem_info mi;
386         void *handle;
387
388         shm_info(&mi);
389         rpc->add(c, "{", &handle);
390         rpc->struct_add(handle, "dddddd",
391                 "total", (unsigned int)mi.total_size,
392                 "free", (unsigned int)mi.free,
393                 "used", (unsigned int)mi.used,
394                 "real_used",(unsigned int)mi.real_used,
395                 "max_used", (unsigned int)mi.max_used,
396                 "fragments", (unsigned int)mi.total_frags
397         );
398 }
399
400 static const char* core_shmmem_doc[] = {
401         "Returns shared memory info.",  /* Documentation string */
402         0                               /* Method signature(s) */
403 };
404
405
406 #if defined(SF_MALLOC) || defined(LL_MALLOC)
407 static void core_sfmalloc(rpc_t* rpc, void* c)
408 {
409         void *handle;
410         int i,r;
411         unsigned long frags, main_s_frags, main_b_frags, pool_frags;
412         unsigned long misses;
413         unsigned long max_misses;
414         unsigned long max_frags;
415         unsigned long max_mem;
416         int max_frags_pool, max_frags_hash;
417         int max_misses_pool, max_misses_hash;
418         int max_mem_pool, max_mem_hash;
419         unsigned long mem;
420
421         if (rpc->scan(c, "d", &r) >= 1) {
422                 if (r>=(int)SF_HASH_POOL_SIZE){
423                         rpc->fault(c, 500, "invalid hash number %d (max %d)",
424                                                                 r, (unsigned int)SF_HASH_POOL_SIZE-1);
425                         return;
426                 }else if (r<0) goto all;
427                 rpc->add(c, "{", &handle);
428                 rpc->struct_add(handle, "dd",
429                                 "hash  ", r,
430                                 "size  ", r*SF_ROUNDTO);
431                 for (i=0; i<SFM_POOLS_NO; i++){
432                         rpc->struct_add(handle, "dddd",
433                                 "pool  ", i,
434                                 "frags ", (unsigned int)shm_block->pool[i].pool_hash[r].no,
435                                 "misses", (unsigned int)shm_block->pool[i].pool_hash[r].misses,
436                                 "mem   ",   (unsigned int)shm_block->pool[i].pool_hash[r].no *
437                                                         r*SF_ROUNDTO
438                         );
439                 }
440         }
441         return;
442 all:
443         max_frags=max_misses=max_mem=0;
444         max_frags_pool=max_frags_hash=0;
445         max_misses_pool=max_misses_hash=0;
446         max_mem_pool=max_mem_hash=0;
447         pool_frags=0;
448         for (i=0; i<SFM_POOLS_NO; i++){
449                 frags=0;
450                 misses=0;
451                 mem=0;
452                 for (r=0; r<SF_HASH_POOL_SIZE; r++){
453                         frags+=shm_block->pool[i].pool_hash[r].no;
454                         misses+=shm_block->pool[i].pool_hash[r].misses;
455                         mem+=shm_block->pool[i].pool_hash[r].no*r*SF_ROUNDTO;
456                         if (shm_block->pool[i].pool_hash[r].no>max_frags){
457                                 max_frags=shm_block->pool[i].pool_hash[r].no;
458                                 max_frags_pool=i;
459                                 max_frags_hash=r;
460                         }
461                         if (shm_block->pool[i].pool_hash[r].misses>max_misses){
462                                 max_misses=shm_block->pool[i].pool_hash[r].misses;
463                                 max_misses_pool=i;
464                                 max_misses_hash=r;
465                         }
466                         if (shm_block->pool[i].pool_hash[r].no*r*SF_ROUNDTO>max_mem){
467                                 max_mem=shm_block->pool[i].pool_hash[r].no*r*SF_ROUNDTO;
468                                 max_mem_pool=i;
469                                 max_mem_hash=r;
470                         }
471                 }
472                 rpc->add(c, "{", &handle);
473                 rpc->struct_add(handle, "dddddd",
474                         "pool  ", i,
475                         "frags ", (unsigned int)frags,
476                         "t. misses", (unsigned int)misses,
477                         "mem   ", (unsigned int)mem,
478                         "missed", (unsigned int)shm_block->pool[i].missed,
479                         "hits",   (unsigned int)shm_block->pool[i].hits
480                 );
481                 pool_frags+=frags;
482         }
483         main_s_frags=0;
484         for (r=0; r<SF_HASH_POOL_SIZE; r++){
485                 main_s_frags+=shm_block->free_hash[r].no;
486         }
487         main_b_frags=0;
488         for (; r<SF_HASH_SIZE; r++){
489                 main_b_frags+=shm_block->free_hash[r].no;
490         }
491         rpc->add(c, "{", &handle);
492         rpc->struct_add(handle, "ddddddddddddd",
493                 "max_frags      ", (unsigned int)max_frags,
494                 "max_frags_pool ", max_frags_pool,
495                 "max_frags_hash", max_frags_hash,
496                 "max_misses     ", (unsigned int)max_misses,
497                 "max_misses_pool", max_misses_pool,
498                 "max_misses_hash", max_misses_hash,
499                 "max_mem        ", (unsigned int)max_mem,
500                 "max_mem_pool   ", max_mem_pool,
501                 "max_mem_hash   ", max_mem_hash,
502                 "in_pools_frags ", (unsigned int)pool_frags,
503                 "main_s_frags   ", (unsigned int)main_s_frags,
504                 "main_b_frags   ", (unsigned int)main_b_frags,
505                 "main_frags     ", (unsigned int)(main_b_frags+main_s_frags)
506         );
507 }
508
509
510
511 static const char* core_sfmalloc_doc[] = {
512         "Returns sfmalloc debugging  info.",  /* Documentation string */
513         0                                     /* Method signature(s) */
514 };
515
516 #endif
517
518
519
520 static const char* core_tcpinfo_doc[] = {
521         "Returns tcp related info.",    /* Documentation string */
522         0                               /* Method signature(s) */
523 };
524
525 static void core_tcpinfo(rpc_t* rpc, void* c)
526 {
527 #ifdef USE_TCP
528         void *handle;
529         struct tcp_gen_info ti;
530
531         if (!tcp_disable){
532                 tcp_get_info(&ti);
533                 rpc->add(c, "{", &handle);
534                 rpc->struct_add(handle, "dddd",
535                         "readers", ti.tcp_readers,
536                         "max_connections", ti.tcp_max_connections,
537                         "opened_connections", ti.tcp_connections_no,
538                         "write_queued_bytes", ti.tcp_write_queued
539                 );
540         }else{
541                 rpc->fault(c, 500, "tcp support disabled");
542         }
543 #else
544         rpc->fault(c, 500, "tcp support not compiled");
545 #endif
546 }
547
548
549
550 static const char* core_tcp_options_doc[] = {
551         "Returns active tcp options.",    /* Documentation string */
552         0                                 /* Method signature(s) */
553 };
554
555 static void core_tcp_options(rpc_t* rpc, void* c)
556 {
557 #ifdef USE_TCP
558         void *handle;
559         struct tcp_cfg_options t;
560
561         if (!tcp_disable){
562                 tcp_options_get(&t);
563                 rpc->add(c, "{", &handle);
564                 rpc->struct_add(handle, "ddddddddddddddd",
565                         "fd_cache",             t.fd_cache,
566                         "tcp_buf_write",        t.tcp_buf_write,
567                         "tcp_connect_wait",     t.tcp_connect_wait,
568                         "tcpconn_wq_max",       t.tcpconn_wq_max,
569                         "tcp_wq_max",   t.tcp_wq_max,
570                         "tcp_wq_timeout",       TICKS_TO_S(t.tcp_wq_timeout),
571                         
572                         "defer_accept", t.defer_accept,
573                         "delayed_ack",  t.delayed_ack,
574                         "syncnt",               t.syncnt,
575                         "linger2",              t.linger2,
576                         "keepalive",    t.keepalive,
577                         "keepidle",             t.keepidle,
578                         "keepintvl",    t.keepintvl,
579                         "keepcnt",              t.keepcnt,
580                         "crlf_ping",    t.crlf_ping
581                 );
582         }else{
583                 rpc->fault(c, 500, "tcp support disabled");
584         }
585 #else
586         rpc->fault(c, 500, "tcp support not compiled");
587 #endif
588 }
589
590
591
592 /*
593  * RPC Methods exported by this module
594  */
595 rpc_export_t core_rpc_methods[] = {
596         {"system.listMethods",     system_listMethods,     system_listMethods_doc,     RET_ARRAY},
597         {"system.methodSignature", system_methodSignature, system_methodSignature_doc, 0        },
598         {"system.methodHelp",      system_methodHelp,      system_methodHelp_doc,      0        },
599         {"core.prints",            core_prints,            core_prints_doc,            0        },
600         {"core.version",           core_version,           core_version_doc,           0        },
601         {"core.uptime",            core_uptime,            core_uptime_doc,            0        },
602         {"core.ps",                core_ps,                core_ps_doc,                RET_ARRAY},
603         {"core.pwd",               core_pwd,               core_pwd_doc,               RET_ARRAY},
604         {"core.arg",               core_arg,               core_arg_doc,               RET_ARRAY},
605         {"core.kill",              core_kill,              core_kill_doc,              0        },
606         {"core.shmmem",            core_shmmem,            core_shmmem_doc,            0        },
607 #if defined(SF_MALLOC) || defined(LL_MALLOC)
608         {"core.sfmalloc",          core_sfmalloc,          core_sfmalloc_doc,   0},
609 #endif
610         {"core.tcp_info",          core_tcpinfo,           core_tcpinfo_doc,    0},
611         {"core.tcp_options",       core_tcp_options,       core_tcp_options_doc,0},
612 #ifdef USE_DNS_CACHE
613         {"dns.mem_info",          dns_cache_mem_info,     dns_cache_mem_info_doc,     0 },
614         {"dns.debug",          dns_cache_debug,           dns_cache_debug_doc,        0 },
615         {"dns.debug_all",      dns_cache_debug_all,       dns_cache_debug_all_doc,        0     },
616         {"dns.view",               dns_cache_view,        dns_cache_view_doc,        0  },
617         {"dns.delete_all",         dns_cache_delete_all,  dns_cache_delete_all_doc,  0  },
618         {"dns.add_a",              dns_cache_add_a,       dns_cache_add_a_doc,       0  },
619         {"dns.add_aaaa",           dns_cache_add_aaaa,    dns_cache_add_aaaa_doc,    0  },
620         {"dns.add_srv",            dns_cache_add_srv,     dns_cache_add_srv_doc,     0  },
621         {"dns.delete_a",           dns_cache_delete_a,    dns_cache_delete_a_doc,    0  },
622         {"dns.delete_aaaa",        dns_cache_delete_aaaa, dns_cache_delete_aaaa_doc, 0  },
623         {"dns.delete_srv",         dns_cache_delete_srv,  dns_cache_delete_srv_doc,  0  },
624 #ifdef USE_DNS_CACHE_STATS
625         {"dns.stats_get",    dns_cache_stats_get,   dns_cache_stats_get_doc,        0   },
626 #endif /* USE_DNS_CACHE_STATS */
627 #ifdef DNS_WATCHDOG_SUPPORT
628         {"dns.set_server_state",   dns_set_server_state_rpc, dns_set_server_state_doc, 0 },
629         {"dns.get_server_state",   dns_get_server_state_rpc, dns_get_server_state_doc, 0 },
630 #endif
631 #endif
632 #ifdef USE_DST_BLACKLIST
633         {"dst_blacklist.mem_info",  dst_blst_mem_info,     dst_blst_mem_info_doc,     0 },
634         {"dst_blacklist.debug",    dst_blst_debug,         dst_blst_debug_doc,        0 },
635         {"dst_blacklist.view",     dst_blst_view,         dst_blst_view_doc,         0  },
636         {"dst_blacklist.delete_all", dst_blst_delete_all, dst_blst_delete_all_doc,   0  },
637         {"dst_blacklist.add",      dst_blst_add,          dst_blst_add_doc,          0  },
638 #ifdef USE_DST_BLACKLIST_STATS
639         {"dst_blacklist.stats_get", dst_blst_stats_get, dst_blst_stats_get_doc, 0 },
640 #endif /* USE_DST_BLACKLIST_STATS */
641 #endif
642         {0, 0, 0, 0}
643 };
644
645 int rpc_init_time(void)
646 {
647         char *t;
648         t=ctime(&up_since);
649         if (strlen(t)+1>=MAX_CTIME_LEN) {
650                 ERR("Too long data %d\n", (int)strlen(t));
651                 return -1;
652         }
653         memcpy(up_since_ctime,t,strlen(t)+1);
654         return 0;
655 }