pike_top.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2006 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. * [email protected]
  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  26. */
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <getopt.h>
  30. #include <arpa/inet.h>
  31. #include <stdlib.h>
  32. #include <search.h>
  33. #include <assert.h>
  34. #include <xmlrpc-c/base.h>
  35. #include <xmlrpc-c/client.h>
  36. #define NAME "SER mod_pike top console"
  37. #define VERSION "1.0"
  38. // XML elements in result
  39. // they MUST NOT have a number on the tail
  40. // because pike uses it for "row" numbering
  41. static const char * const MAX_HITS = "max_hits";
  42. static const char * const IP_ADDR = "ip_addr";
  43. static const char * const LEAF_HITS_PREV = "leaf_hits_prev";
  44. static const char * const LEAF_HITS_CURR = "leaf_hits_curr";
  45. static const char * const EXPIRES = "expires";
  46. static const char * const STATUS = "status";
  47. static const char * const NUMBER_OF_ROWS = "number_of_rows";
  48. #define IP_ADDR_MAX_LENGTH 40
  49. #define STATUS_MAX_LENGTH 10
  50. typedef struct TopItem {
  51. char ip_addr[IP_ADDR_MAX_LENGTH];
  52. in_addr_t ipv4_addr; /* uint32_t */
  53. struct in6_addr ipv6_addr;
  54. unsigned short leaf_hits[2];
  55. unsigned int expires;
  56. char status[STATUS_MAX_LENGTH];
  57. int num_of_ips; /* number of IP addresses in aggregated result */
  58. } TopItem;
  59. int compare_TopItem_hits(const void* left, const void *right)
  60. {
  61. TopItem *li = (TopItem *)left;
  62. TopItem *ri = (TopItem *)right;
  63. return li->leaf_hits[0] + li->leaf_hits[1] - ri->leaf_hits[0] - ri->leaf_hits[1];
  64. }
  65. /** Compare function to qsort array in reverse order (biger first) */
  66. int compare_TopItem_hits_reverse(const void* left, const void *right)
  67. {
  68. return compare_TopItem_hits(right, left);
  69. }
  70. int compare_TopItem_ipv4_addr(const void *item1, const void *item2)
  71. {
  72. return ((TopItem*)item1)->ipv4_addr - ((TopItem*)item2)->ipv4_addr;
  73. }
  74. /**
  75. * @return concatenated string in newly allocated memory
  76. */
  77. static char *concat( const char *name, int index )
  78. {
  79. char *ptr;
  80. int rv;
  81. rv = asprintf(&ptr, "%s%d", name, index);
  82. if ( rv == -1 )
  83. return 0;
  84. return ptr;
  85. }
  86. static void strfree( char *ptr )
  87. {
  88. if (ptr)
  89. free(ptr);
  90. }
  91. static void die_if_fault_occurred (xmlrpc_env *env)
  92. {
  93. if (env->fault_occurred) {
  94. fprintf(stderr, "XML-RPC Fault: %s (%d)\n",
  95. env->fault_string, env->fault_code);
  96. exit(1);
  97. }
  98. }
  99. /** @return 0 if everything is OK, 1 otherwise */
  100. static int fault_occurred (xmlrpc_env *env)
  101. {
  102. if (env->fault_occurred) {
  103. fprintf(stderr, "XML-RPC Fault: %s (%d)\n",
  104. env->fault_string, env->fault_code);
  105. return 1;
  106. }
  107. return 0;
  108. }
  109. static void die_if_fault_occurred_line (xmlrpc_env *env, int line)
  110. {
  111. if (env->fault_occurred)
  112. fprintf(stderr, "LINE: %d\n", line);
  113. die_if_fault_occurred(env);
  114. }
  115. void print_help()
  116. {
  117. printf("\n");
  118. if ( isatty(1) )
  119. printf("usage: \033[1mpike_top <ser addr>:<port> [--hot|--warm|--all] [--mask]\033[0m\n");
  120. else
  121. printf("usage: pike_top <ser addr>:<port> [--hot|--warm|--all] [--mask] [--ipleaf|--inner]\n");
  122. printf("\n");
  123. printf("\toptions:\n"
  124. "\t\t--hot ... show hot IP leaves\n"
  125. "\t\t--all ... show all IP leaves\n"
  126. "\t\t--mask ... aggregate results regarding IP mask length\n"
  127. "\t\t (default 32, i.e. not aggregate)\n"
  128. "\t\t IPv4 only at this time\n\n"
  129. "\t\t\tdefault is to show HOT nodes\n\n");
  130. if ( isatty(1) )
  131. printf("You can use \033[1mwatch\033[0m(1) utility for periodical output.\n\n");
  132. else
  133. printf("You can use watch(1) utility for periodical output.\n\n");
  134. /*
  135. printf("Note:\n"
  136. "It is a question if reporting warm nodes is useful and if yes, how to report\n"
  137. "them. I feel that should be more welcome to report parents of warm nodes,\n"
  138. "or something like this to show which subnets could be problematic...\n\n");
  139. */
  140. }
  141. /* Following options are conforming to definition of node status defined in pike/ip_tree.h */
  142. #define OPT_WARM 1
  143. #define OPT_HOT 2
  144. #define OPT_ALL 3
  145. /**
  146. * @param options ORed cmdline options
  147. * @return position of first non option parameter
  148. */
  149. int process_options(int argc, char *argv[], int *options, int *mask_length)
  150. {
  151. static struct option long_options[] = {
  152. {"hot", 0, 0, 'h'},
  153. /* {"warm", 0, 0, 'w'}, */
  154. {"all", 0, 0, 'a'},
  155. {"mask", 0, 0, 'm'},
  156. {"help", 0, 0, '?'},
  157. {0,0,0,0}
  158. };
  159. int c, index, counter;
  160. *options = 0;
  161. counter = 0;
  162. while ( (c=getopt_long(argc, argv, "hwam:", long_options, &index)) != -1 ) {
  163. switch (c) {
  164. case 'h':
  165. *options = OPT_HOT;
  166. ++counter;
  167. break;
  168. /* case 'w':
  169. *options = OPT_WARM;
  170. ++counter;
  171. break;
  172. */ case 'a':
  173. *options = OPT_ALL;
  174. ++counter;
  175. break;
  176. case 'm':
  177. *mask_length = atoi(optarg);
  178. ;
  179. break;
  180. case '?':
  181. default:
  182. print_help();
  183. exit(0);
  184. break;
  185. }
  186. }
  187. if ( counter > 1 ) {
  188. fprintf(stderr, "ERROR: Node type selectors are exlusive, only one of them can be used\n");
  189. print_help();
  190. exit(1);
  191. }
  192. if ( *options == 0 )
  193. *options = OPT_HOT;
  194. return optind;
  195. }
  196. /* Get an integer value from struct result of xmlrpc_client_call()
  197. * @param structP pointer to a result struct
  198. * @param element_name name of structure item
  199. * @param rv returned value
  200. * @return 1 if succeed and 0 otherwise
  201. */
  202. int get_int_from_struct_by_name(xmlrpc_value *structP, const char *element_name, int *rv)
  203. {
  204. xmlrpc_env env;
  205. xmlrpc_env_init(&env);
  206. xmlrpc_value *valueP;
  207. xmlrpc_struct_find_value(&env, structP, element_name, &valueP);
  208. if ( env.fault_occurred )
  209. goto error;
  210. xmlrpc_read_int(&env, valueP, rv);
  211. if ( env.fault_occurred )
  212. goto error1;
  213. xmlrpc_DECREF(valueP);
  214. return 1;
  215. error1:
  216. xmlrpc_DECREF(valueP);
  217. error:
  218. return 0;
  219. }
  220. /* Get a new string value from struct result of xmlrpc_client_call()
  221. * @param structP pointer to a result struct
  222. * @param element_name name of structure item
  223. * @param rv contains newly allocated string or NULL if fails
  224. * @return 1 if succeed and 0 otherwise
  225. */
  226. /* FIXME terminates the programm if it fails */
  227. int get_string_from_struct_by_name(xmlrpc_value *structP, const char *element_name, char **rv)
  228. {
  229. xmlrpc_env env;
  230. xmlrpc_env_init(&env);
  231. xmlrpc_value *valueP;
  232. int length;
  233. xmlrpc_struct_find_value(&env, structP, element_name, &valueP);
  234. die_if_fault_occurred_line(&env, __LINE__);
  235. xmlrpc_read_string(&env, valueP, (const char **)rv);
  236. die_if_fault_occurred_line(&env, __LINE__);
  237. xmlrpc_DECREF(valueP);
  238. return 1;
  239. }
  240. /* Get an integer value from struct result of xmlrpc_client_call()
  241. * @param structP pointer to a result struct
  242. * @param index index of requested element
  243. * @param rv returned value
  244. * @return 1 if succeed and 0 otherwise
  245. */
  246. /* FIXME terminates the program if it fails */
  247. int get_int_from_struct_by_idx(xmlrpc_value *structP, int index, int *rv)
  248. {
  249. xmlrpc_env env;
  250. xmlrpc_env_init(&env);
  251. xmlrpc_value *keyP;
  252. xmlrpc_value *valueP;
  253. xmlrpc_struct_read_member(&env, structP, index, &keyP, &valueP); /* increment refcount of returned values */
  254. die_if_fault_occurred_line(&env, __LINE__);
  255. xmlrpc_read_int(&env, valueP, rv);
  256. die_if_fault_occurred_line(&env, __LINE__);
  257. xmlrpc_DECREF(valueP);
  258. return 1;
  259. }
  260. enum _value_type {
  261. TYPE_NOT_DEF = 0,
  262. TYPE_INTEGER = 1,
  263. TYPE_STRING = 2
  264. };
  265. typedef enum _value_type value_type;
  266. struct _key_value_pair {
  267. char *key;
  268. value_type type;
  269. union value {
  270. int integer;
  271. char *string;
  272. void *value;
  273. } value;
  274. };
  275. typedef struct _key_value_pair key_value_pair;
  276. void key_value_pair_cleanup(key_value_pair *kvp)
  277. {
  278. if (kvp && kvp->key) {
  279. free(kvp->key);
  280. }
  281. if (kvp && kvp->type == TYPE_STRING && kvp->value.string) {
  282. free(kvp->value.string);
  283. }
  284. kvp->key = 0;
  285. kvp->type = TYPE_NOT_DEF;
  286. kvp->value.integer = 0;
  287. }
  288. /** Get a string value from struct result of xmlrpc_client_call()
  289. * @param structP pointer to a result struct
  290. * @param index index of requested element
  291. * @param rv pointer to key_value_pair
  292. * @return 1 if succeed and 0 otherwise
  293. */
  294. /* FIXME terminates the programm if it fails */
  295. int get_struct_item_by_idx(xmlrpc_value *structP, int index, key_value_pair *rv)
  296. {
  297. xmlrpc_env env;
  298. xmlrpc_env_init(&env);
  299. xmlrpc_value *keyP;
  300. xmlrpc_value *valueP;
  301. int length;
  302. const char *string;
  303. xmlrpc_struct_read_member(&env, structP, index, &keyP, &valueP); /* increment refcount of returned values */
  304. die_if_fault_occurred_line(&env, __LINE__);
  305. xmlrpc_read_string(&env, keyP, (const char **)&rv->key);
  306. /* handle value type */
  307. switch ( xmlrpc_value_type(valueP) ) {
  308. case XMLRPC_TYPE_INT:
  309. xmlrpc_read_int(&env, valueP, &rv->value.integer);
  310. die_if_fault_occurred_line(&env, __LINE__);
  311. rv->type = TYPE_INTEGER;
  312. break;
  313. case XMLRPC_TYPE_STRING:
  314. xmlrpc_read_string(&env, valueP, &string);
  315. printf("get_struct_item_by_idx: ptr = %p, string value = '%s'\n", string, string);
  316. die_if_fault_occurred_line(&env, __LINE__);
  317. rv->value.string = (char *)string;
  318. rv->type = TYPE_STRING;
  319. break;
  320. default:
  321. fprintf(stderr, "Wrong type of return value in key: '%s', exiting...\n", rv->key);
  322. exit(1);
  323. }
  324. xmlrpc_DECREF(keyP); /* decrement refcount */
  325. xmlrpc_DECREF(valueP);
  326. die_if_fault_occurred_line(&env, __LINE__);
  327. /* FIXME add error handling */
  328. return 1;
  329. }
  330. /**
  331. * Reads one toprow from given structure
  332. * @param structP
  333. * @param rownum
  334. * @param top_item
  335. */
  336. int read_row(xmlrpc_value *structP, int index, TopItem *top_item)
  337. {
  338. char *elem;
  339. char *string = 0;
  340. elem = concat(IP_ADDR, index);
  341. if ( ! get_string_from_struct_by_name(structP, elem, &string) )
  342. goto error;
  343. strncpy(top_item->ip_addr, string, sizeof(top_item->ip_addr));
  344. free(string);
  345. string = 0;
  346. free(elem);
  347. elem = concat(LEAF_HITS_PREV, index);
  348. if ( ! get_int_from_struct_by_name(structP, elem, (unsigned int *)&top_item->leaf_hits[0]) )
  349. goto error;
  350. free(elem);
  351. elem = concat(LEAF_HITS_CURR, index);
  352. if ( ! get_int_from_struct_by_name(structP, elem, (unsigned int *)&top_item->leaf_hits[1]) )
  353. goto error;
  354. free(elem);
  355. elem = concat(EXPIRES, index);
  356. if ( ! get_int_from_struct_by_name(structP, elem, (unsigned int *)&top_item->expires) )
  357. goto error;
  358. free(elem);
  359. elem = concat(STATUS, index);
  360. if ( ! get_string_from_struct_by_name(structP, elem, &string) )
  361. goto error;
  362. strncpy(top_item->status, string, sizeof(top_item->status));
  363. free(string);
  364. free(elem);
  365. return 1;
  366. error:
  367. if ( string )
  368. free(string);
  369. free(elem);
  370. return 0;
  371. }
  372. void print_row(TopItem *ti)
  373. {
  374. char *fmt;
  375. if ( ! ti ) {
  376. printf("%-15s %10s %10s %10s %-10s\n", "IP address", "HITS PREV", "HITS CURR", "EXPIRES", "STATUS");
  377. return;
  378. }
  379. if ( strlen(ti->ip_addr) > 15 ) // IPv6 addr
  380. fmt = "%s\n %10d %10d %10d %-10s\n";
  381. else
  382. fmt = "%-15s %10d %10d %10d %-10s\n";
  383. printf(fmt, ti->ip_addr, ti->leaf_hits[0], ti->leaf_hits[1], ti->expires, ti->status);
  384. }
  385. void print_row_agg(TopItem *ti) /* IPv4 only */
  386. {
  387. char *fmt;
  388. if ( ! ti ) {
  389. printf("%-15s %10s %10s %5s\n", "IP address", "HITS PREV", "HITS CURR", "COUNT");
  390. return;
  391. }
  392. fmt = "%-15s %10d %10d %5d\n";
  393. printf(fmt, ti->ip_addr, ti->leaf_hits[0], ti->leaf_hits[1], ti->num_of_ips);
  394. }
  395. uint32_t mask( int msklen )
  396. {
  397. if ( msklen )
  398. return 0xffffffff ^ ((1 << (32-msklen)) - 1);
  399. else
  400. return 0;
  401. }
  402. void print_rows(TopItem *root, int nmemb, int mask_length)
  403. {
  404. int i;
  405. void (*print_function)(TopItem *);
  406. if (mask_length == 32)
  407. print_function = print_row;
  408. else
  409. print_function = print_row_agg;
  410. print_function(0);
  411. for ( i = 0; i < nmemb; ++i, ++root ) {
  412. print_function(root);
  413. }
  414. }
  415. int main( int argc, char *argv[] )
  416. {
  417. xmlrpc_env env;
  418. xmlrpc_value * resultP;
  419. xmlrpc_value * keyP;
  420. xmlrpc_value * valueP;
  421. int struct_size;
  422. int i, j;
  423. size_t length;
  424. const char * str_key_value;
  425. xmlrpc_int int_key_value;
  426. unsigned int max_hits = 0;
  427. unsigned int rows = 0;
  428. int rv;
  429. char *uri;
  430. int options; /* what kind of nodes should be processed */
  431. int uri_pos; /* position of first non option argument */
  432. char stropts[16];
  433. int pos = 0;
  434. int mask_length = 32; /* 32 means NO aggregate */
  435. if (argc-1 < 1) {
  436. print_help();
  437. exit(0);
  438. }
  439. uri_pos = process_options(argc, argv, &options, &mask_length);
  440. switch (options) {
  441. case OPT_HOT:
  442. sprintf(stropts, "HOT");
  443. break;
  444. case OPT_ALL:
  445. sprintf(stropts, "ALL");
  446. break;
  447. case OPT_WARM:
  448. sprintf(stropts, "WARM");
  449. break;
  450. }
  451. printf("Nodes = %s\n", stropts);
  452. printf("Mask = /%d\n", mask_length);
  453. /* Start up our XML-RPC client library. */
  454. xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION);
  455. /* Initialize our error-handling environment. */
  456. xmlrpc_env_init(&env);
  457. /* prototype:
  458. xmlrpc_value * xmlrpc_client_call(xmlrpc_env * const envP,
  459. const char * const server_url, const char * const method_name,
  460. const char * const format, ...);
  461. */
  462. asprintf(&uri, "http://%s/RPC2", argv[uri_pos]);
  463. resultP = xmlrpc_client_call(&env, uri,
  464. "pike.top",
  465. "(s)", stropts);
  466. free(uri);
  467. die_if_fault_occurred_line(&env, __LINE__);
  468. /* parse returned structure */
  469. if ( xmlrpc_value_type(resultP) != XMLRPC_TYPE_STRUCT ) {
  470. printf("unexpected result - should be structure\n");
  471. xmlrpc_env_clean(&env);
  472. xmlrpc_client_cleanup();
  473. exit(1);
  474. }
  475. struct_size = xmlrpc_struct_size(&env, resultP);
  476. die_if_fault_occurred_line(&env, __LINE__);
  477. // printf("Struct size: %d\n", struct_size);
  478. if ( ! get_int_from_struct_by_name(resultP, MAX_HITS, &max_hits) ) {
  479. fprintf(stderr, "ERROR: %s not foung in result\n", MAX_HITS);
  480. exit (1);
  481. }
  482. printf("max_hits = %d\n", max_hits);
  483. if ( ! get_int_from_struct_by_name(resultP, NUMBER_OF_ROWS, &rows) ) {
  484. fprintf(stderr, "ERROR: %s not foung in result\n", NUMBER_OF_ROWS);
  485. exit (1);
  486. }
  487. printf("rows = %d\n", rows);
  488. TopItem top_items[rows];
  489. TopItem *item; /* tmp item ptr */
  490. TopItem *result_items = top_items; /* if no aggregation use this */
  491. memset(top_items, 0, sizeof(top_items));
  492. /* aggregated values */
  493. if ( rows == 0 )
  494. return 0;
  495. for ( i = 0, item = top_items; i < rows; ++i, ++item ) {
  496. if ( ! read_row(resultP, i, item) ) {
  497. fprintf(stderr, "ERROR: while reading row number %d\n", i);
  498. }
  499. /* fill in ipv4 addr */
  500. // printf("item[%d].ip_addr = %s, len = %d\n", i, item->ip_addr, strlen(item->ip_addr));
  501. rv = inet_pton(AF_INET, item->ip_addr, &item->ipv4_addr);
  502. if ( rv > 0 ) {
  503. // printf("IPv4 addr: %x\n", item->ipv4_addr);
  504. } else {
  505. fprintf(stderr, "IP conversion failed - not an IPv4 address: '%s'\n", item->ip_addr); /* conversion failed from any reason */
  506. printf("item[%d].ipv4_addr = %x\n", i, item->ipv4_addr);
  507. }
  508. }
  509. assert( rows > 0 );
  510. /* if IP mask length is shorter than 32 then aggregate list according to the mask */
  511. if ( mask_length < 32 ) {
  512. uint32_t ip_mask = htonl(mask(mask_length));
  513. qsort(top_items, rows, sizeof(TopItem), compare_TopItem_ipv4_addr); /* sort by IPv4 */
  514. /* skip items without ipv4 address */
  515. i = 0; /* index of non aggregated items */
  516. while (!top_items[i].ipv4_addr && i < rows ) {
  517. printf("Skip item[%d] - do not has IPv4 address: %s\n", i, top_items[i].ip_addr);
  518. memset(&top_items[i], 0, sizeof(TopItem));
  519. ++i;
  520. }
  521. j = 0; /* index of aggregated items */
  522. if ( i == 0 )
  523. ++i;
  524. top_items[0].ipv4_addr &= ip_mask;
  525. top_items[0].num_of_ips = 1;
  526. inet_ntop(AF_INET, &top_items[0].ipv4_addr, top_items[0].ip_addr, sizeof(top_items[0].ip_addr));
  527. while ( i < rows ) {
  528. top_items[i].ipv4_addr &= ip_mask;
  529. if ( top_items[j].ipv4_addr == top_items[i].ipv4_addr ) {
  530. top_items[j].leaf_hits[0] += top_items[i].leaf_hits[0];
  531. top_items[j].leaf_hits[1] += top_items[i].leaf_hits[1];
  532. ++(top_items[j].num_of_ips);
  533. ++i;
  534. }
  535. else {
  536. ++j;
  537. top_items[j] = top_items[i];
  538. top_items[j].num_of_ips = 1;
  539. inet_ntop(AF_INET, &top_items[j].ipv4_addr, top_items[j].ip_addr, sizeof(top_items[j].ip_addr));
  540. ++i;
  541. }
  542. }
  543. rows = j + 1;
  544. }
  545. qsort(top_items, rows, sizeof(TopItem), compare_TopItem_hits_reverse);
  546. print_rows( top_items, rows, mask_length );
  547. /* Dispose of our result value. */
  548. xmlrpc_DECREF(resultP);
  549. /* Clean up our error-handling environment. */
  550. xmlrpc_env_clean(&env);
  551. /* Shutdown our XML-RPC client library. */
  552. xmlrpc_client_cleanup();
  553. return 0;
  554. }