ndb_redis_mod.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /**
  2. * $Id$
  3. *
  4. * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
  5. *
  6. * Copyright (C) 2012 Vicente Hernando Ara (System One: www.systemonenoc.com)
  7. * - for: redis array reply support
  8. *
  9. * This file is part of Kamailio, a free SIP server.
  10. *
  11. * Kamailio is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version
  15. *
  16. * Kamailio is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. */
  26. #include <stdio.h>
  27. #include <unistd.h>
  28. #include <stdlib.h>
  29. #include <ctype.h>
  30. #include "../../sr_module.h"
  31. #include "../../mem/mem.h"
  32. #include "../../dprint.h"
  33. #include "../../mod_fix.h"
  34. #include "../../trim.h"
  35. #include "redis_client.h"
  36. MODULE_VERSION
  37. /** parameters */
  38. int redis_srv_param(modparam_t type, void *val);
  39. static int w_redis_cmd3(struct sip_msg* msg, char* ssrv, char* scmd,
  40. char* sres);
  41. static int w_redis_cmd4(struct sip_msg* msg, char* ssrv, char* scmd,
  42. char *sargv1, char* sres);
  43. static int w_redis_cmd5(struct sip_msg* msg, char* ssrv, char* scmd,
  44. char *sargv1, char *sargv2, char* sres);
  45. static int w_redis_cmd6(struct sip_msg* msg, char* ssrv, char* scmd,
  46. char *sargv1, char *sargv2, char *sargv3, char* sres);
  47. static int fixup_redis_cmd6(void** param, int param_no);
  48. static int w_redis_free_reply(struct sip_msg* msg, char* res);
  49. static void mod_destroy(void);
  50. static int child_init(int rank);
  51. static int pv_get_redisc(struct sip_msg *msg, pv_param_t *param,
  52. pv_value_t *res);
  53. static int pv_parse_redisc_name(pv_spec_p sp, str *in);
  54. static pv_export_t mod_pvs[] = {
  55. { {"redis", sizeof("redis")-1}, PVT_OTHER, pv_get_redisc, 0,
  56. pv_parse_redisc_name, 0, 0, 0 },
  57. { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
  58. };
  59. static cmd_export_t cmds[]={
  60. {"redis_cmd", (cmd_function)w_redis_cmd3, 3, fixup_redis_cmd6,
  61. 0, ANY_ROUTE},
  62. {"redis_cmd", (cmd_function)w_redis_cmd4, 4, fixup_redis_cmd6,
  63. 0, ANY_ROUTE},
  64. {"redis_cmd", (cmd_function)w_redis_cmd5, 5, fixup_redis_cmd6,
  65. 0, ANY_ROUTE},
  66. {"redis_cmd", (cmd_function)w_redis_cmd6, 6, fixup_redis_cmd6,
  67. 0, ANY_ROUTE},
  68. {"redis_free", (cmd_function)w_redis_free_reply, 1, fixup_spve_null,
  69. 0, ANY_ROUTE},
  70. {0, 0, 0, 0, 0, 0}
  71. };
  72. static param_export_t params[]={
  73. {"server", PARAM_STRING|USE_FUNC_PARAM, (void*)redis_srv_param},
  74. {0, 0, 0}
  75. };
  76. struct module_exports exports = {
  77. "ndb_redis",
  78. DEFAULT_DLFLAGS, /* dlopen flags */
  79. cmds,
  80. params,
  81. 0,
  82. 0, /* exported MI functions */
  83. mod_pvs, /* exported pseudo-variables */
  84. 0, /* extra processes */
  85. 0, /* module initialization function */
  86. 0, /* response function */
  87. mod_destroy, /* destroy function */
  88. child_init /* per child init function */
  89. };
  90. /* each child get a new connection to the database */
  91. static int child_init(int rank)
  92. {
  93. /* skip child init for non-worker process ranks */
  94. if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
  95. return 0;
  96. if(redisc_init()<0)
  97. {
  98. LM_ERR("failed to initialize redis connections\n");
  99. return -1;
  100. }
  101. return 0;
  102. }
  103. /**
  104. *
  105. */
  106. static void mod_destroy(void)
  107. {
  108. LM_DBG("cleaning up\n");
  109. redisc_destroy();
  110. }
  111. /**
  112. *
  113. */
  114. static int w_redis_cmd3(struct sip_msg* msg, char* ssrv, char* scmd, char* sres)
  115. {
  116. str s[3];
  117. if(fixup_get_svalue(msg, (gparam_t*)ssrv, &s[0])!=0)
  118. {
  119. LM_ERR("no redis server name\n");
  120. return -1;
  121. }
  122. if(fixup_get_svalue(msg, (gparam_t*)scmd, &s[1])!=0)
  123. {
  124. LM_ERR("no redis command\n");
  125. return -1;
  126. }
  127. if(fixup_get_svalue(msg, (gparam_t*)sres, &s[2])!=0)
  128. {
  129. LM_ERR("no redis reply name\n");
  130. return -1;
  131. }
  132. if(redisc_exec(&s[0], &s[2], &s[1])<0)
  133. return -1;
  134. return 1;
  135. }
  136. /**
  137. *
  138. */
  139. static int w_redis_cmd4(struct sip_msg* msg, char* ssrv, char* scmd,
  140. char *sargv1, char* sres)
  141. {
  142. str s[3];
  143. str arg1;
  144. char c1;
  145. if(fixup_get_svalue(msg, (gparam_t*)ssrv, &s[0])!=0)
  146. {
  147. LM_ERR("no redis server name\n");
  148. return -1;
  149. }
  150. if(fixup_get_svalue(msg, (gparam_t*)scmd, &s[1])!=0)
  151. {
  152. LM_ERR("no redis command\n");
  153. return -1;
  154. }
  155. if(fixup_get_svalue(msg, (gparam_t*)sargv1, &arg1)!=0)
  156. {
  157. LM_ERR("no argument 1\n");
  158. return -1;
  159. }
  160. if(fixup_get_svalue(msg, (gparam_t*)sres, &s[2])!=0)
  161. {
  162. LM_ERR("no redis reply name\n");
  163. return -1;
  164. }
  165. c1 = arg1.s[arg1.len];
  166. arg1.s[arg1.len] = '\0';
  167. if(redisc_exec(&s[0], &s[2], &s[1], arg1.s)<0) {
  168. arg1.s[arg1.len] = c1;
  169. return -1;
  170. }
  171. arg1.s[arg1.len] = c1;
  172. return 1;
  173. }
  174. /**
  175. *
  176. */
  177. static int w_redis_cmd5(struct sip_msg* msg, char* ssrv, char* scmd,
  178. char *sargv1, char *sargv2, char* sres)
  179. {
  180. str s[3];
  181. str arg1, arg2;
  182. char c1, c2;
  183. if(fixup_get_svalue(msg, (gparam_t*)ssrv, &s[0])!=0)
  184. {
  185. LM_ERR("no redis server name\n");
  186. return -1;
  187. }
  188. if(fixup_get_svalue(msg, (gparam_t*)scmd, &s[1])!=0)
  189. {
  190. LM_ERR("no redis command\n");
  191. return -1;
  192. }
  193. if(fixup_get_svalue(msg, (gparam_t*)sargv1, &arg1)!=0)
  194. {
  195. LM_ERR("no argument 1\n");
  196. return -1;
  197. }
  198. if(fixup_get_svalue(msg, (gparam_t*)sargv2, &arg2)!=0)
  199. {
  200. LM_ERR("no argument 2\n");
  201. return -1;
  202. }
  203. if(fixup_get_svalue(msg, (gparam_t*)sres, &s[2])!=0)
  204. {
  205. LM_ERR("no redis reply name\n");
  206. return -1;
  207. }
  208. c1 = arg1.s[arg1.len];
  209. c2 = arg2.s[arg2.len];
  210. arg1.s[arg1.len] = '\0';
  211. arg2.s[arg2.len] = '\0';
  212. if(redisc_exec(&s[0], &s[2], &s[1], arg1.s, arg2.s)<0) {
  213. arg1.s[arg1.len] = c1;
  214. arg2.s[arg2.len] = c2;
  215. return -1;
  216. }
  217. arg1.s[arg1.len] = c1;
  218. arg2.s[arg2.len] = c2;
  219. return 1;
  220. }
  221. /**
  222. *
  223. */
  224. static int w_redis_cmd6(struct sip_msg* msg, char* ssrv, char* scmd,
  225. char *sargv1, char *sargv2, char *sargv3, char* sres)
  226. {
  227. str s[3];
  228. str arg1, arg2, arg3;
  229. char c1, c2, c3;
  230. if(fixup_get_svalue(msg, (gparam_t*)ssrv, &s[0])!=0)
  231. {
  232. LM_ERR("no redis server name\n");
  233. return -1;
  234. }
  235. if(fixup_get_svalue(msg, (gparam_t*)scmd, &s[1])!=0)
  236. {
  237. LM_ERR("no redis command\n");
  238. return -1;
  239. }
  240. if(fixup_get_svalue(msg, (gparam_t*)sargv1, &arg1)!=0)
  241. {
  242. LM_ERR("no argument 1\n");
  243. return -1;
  244. }
  245. if(fixup_get_svalue(msg, (gparam_t*)sargv2, &arg2)!=0)
  246. {
  247. LM_ERR("no argument 2\n");
  248. return -1;
  249. }
  250. if(fixup_get_svalue(msg, (gparam_t*)sargv3, &arg3)!=0)
  251. {
  252. LM_ERR("no argument 3\n");
  253. return -1;
  254. }
  255. if(fixup_get_svalue(msg, (gparam_t*)sres, &s[2])!=0)
  256. {
  257. LM_ERR("no redis reply name\n");
  258. return -1;
  259. }
  260. c1 = arg1.s[arg1.len];
  261. c2 = arg2.s[arg2.len];
  262. c3 = arg3.s[arg3.len];
  263. arg1.s[arg1.len] = '\0';
  264. arg2.s[arg2.len] = '\0';
  265. arg3.s[arg3.len] = '\0';
  266. if(redisc_exec(&s[0], &s[2], &s[1], arg1.s, arg2.s, arg3.s)<0) {
  267. arg1.s[arg1.len] = c1;
  268. arg2.s[arg2.len] = c2;
  269. arg3.s[arg3.len] = c3;
  270. return -1;
  271. }
  272. arg1.s[arg1.len] = c1;
  273. arg2.s[arg2.len] = c2;
  274. arg3.s[arg3.len] = c3;
  275. return 1;
  276. }
  277. /**
  278. *
  279. */
  280. static int fixup_redis_cmd6(void** param, int param_no)
  281. {
  282. return fixup_spve_null(param, 1);
  283. }
  284. /**
  285. *
  286. */
  287. static int w_redis_free_reply(struct sip_msg* msg, char* res)
  288. {
  289. str name;
  290. if(fixup_get_svalue(msg, (gparam_t*)res, &name)!=0)
  291. {
  292. LM_ERR("no redis reply name\n");
  293. return -1;
  294. }
  295. if(redisc_free_reply(&name)<0)
  296. return -1;
  297. return 1;
  298. }
  299. /**
  300. *
  301. */
  302. int redis_srv_param(modparam_t type, void *val)
  303. {
  304. return redisc_add_server((char*)val);
  305. }
  306. /**
  307. *
  308. */
  309. int redis_parse_index(str *in, gparam_t *gp)
  310. {
  311. if(in->s[0]==PV_MARKER)
  312. {
  313. gp->type = GPARAM_TYPE_PVS;
  314. gp->v.pvs = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t));
  315. if (gp->v.pvs == NULL)
  316. {
  317. LM_ERR("no pkg memory left for pv_spec_t\n");
  318. pkg_free(gp);
  319. return -1;
  320. }
  321. if(pv_parse_spec(in, gp->v.pvs)==NULL)
  322. {
  323. LM_ERR("invalid PV identifier\n");
  324. pkg_free(gp->v.pvs);
  325. pkg_free(gp);
  326. return -1;
  327. }
  328. } else {
  329. gp->type = GPARAM_TYPE_INT;
  330. if(str2sint(in, &gp->v.i) != 0)
  331. {
  332. LM_ERR("bad number <%.*s>\n", in->len, in->s);
  333. return -1;
  334. }
  335. }
  336. return 0;
  337. }
  338. /**
  339. *
  340. */
  341. int redis_parse_token(str *in, gparam_t *gp, int i)
  342. {
  343. str tok;
  344. while(i<in->len && isspace(in->s[i]))
  345. i++;
  346. if(i>=in->len-2)
  347. return -1;
  348. if(in->s[i++]!='[')
  349. return -1;
  350. while(i<in->len-1 && isspace(in->s[i]))
  351. i++;
  352. if(i==in->len-1 || in->s[i]==']')
  353. return -1;
  354. tok.s = &(in->s[i]);
  355. while(i<in->len && !isspace(in->s[i]) && in->s[i]!=']')
  356. i++;
  357. if(i==in->len)
  358. return -1;
  359. tok.len = &(in->s[i]) - tok.s;
  360. if(redis_parse_index(&tok, gp)!=0)
  361. return -1;
  362. while(i<in->len && isspace(in->s[i]))
  363. i++;
  364. if(i==in->len || in->s[i]!=']')
  365. return -1;
  366. return 0;
  367. }
  368. /**
  369. *
  370. */
  371. static int pv_parse_redisc_name(pv_spec_p sp, str *in)
  372. {
  373. redisc_pv_t *rpv=NULL;
  374. str pvs;
  375. int i;
  376. if(in->s==NULL || in->len<=0)
  377. return -1;
  378. rpv = (redisc_pv_t*)pkg_malloc(sizeof(redisc_pv_t));
  379. if(rpv==NULL)
  380. return -1;
  381. memset(rpv, 0, sizeof(redisc_pv_t));
  382. pvs = *in;
  383. trim(&pvs);
  384. rpv->rname.s = pvs.s;
  385. for(i=0; i<pvs.len-2; i++)
  386. {
  387. if(isspace(pvs.s[i]) || pvs.s[i]=='=') {
  388. rpv->rname.len = i;
  389. break;
  390. }
  391. }
  392. rpv->rname.len = i;
  393. if(rpv->rname.len==0)
  394. goto error_var;
  395. while(i<pvs.len-2 && isspace(pvs.s[i]))
  396. i++;
  397. if(pvs.s[i]!='=')
  398. goto error_var;
  399. if(pvs.s[i+1]!='>')
  400. goto error_var;
  401. i += 2;
  402. while(i<pvs.len && isspace(pvs.s[i]))
  403. i++;
  404. if(i>=pvs.len)
  405. goto error_key;
  406. rpv->rkey.s = pvs.s + i;
  407. rpv->rkey.len = pvs.len - i;
  408. /* Default pos param initialization. */
  409. rpv->pos.type = GPARAM_TYPE_INT;
  410. rpv->pos.v.i = -1;
  411. if(rpv->rkey.len>=5 && strncmp(rpv->rkey.s, "value", 5)==0) {
  412. rpv->rkeyid = 1;
  413. if(rpv->rkey.len>5)
  414. {
  415. i+=5;
  416. if(redis_parse_token(&pvs, &(rpv->pos), i)!=0)
  417. goto error_key;
  418. }
  419. } else if(rpv->rkey.len>=4 && strncmp(rpv->rkey.s, "type", 4)==0) {
  420. rpv->rkeyid = 0;
  421. if(rpv->rkey.len>4)
  422. {
  423. i+=4;
  424. if(redis_parse_token(&pvs, &(rpv->pos), i)!=0)
  425. goto error_key;
  426. }
  427. } else if(rpv->rkey.len==4 && strncmp(rpv->rkey.s, "info", 4)==0) {
  428. rpv->rkeyid = 2;
  429. } else if(rpv->rkey.len==4 && strncmp(rpv->rkey.s, "size", 4)==0) {
  430. rpv->rkeyid = 3;
  431. } else {
  432. goto error_key;
  433. }
  434. sp->pvp.pvn.u.dname = (void*)rpv;
  435. sp->pvp.pvn.type = PV_NAME_OTHER;
  436. return 0;
  437. error_var:
  438. LM_ERR("invalid var spec [%.*s]\n", in->len, in->s);
  439. pkg_free(rpv);
  440. return -1;
  441. error_key:
  442. LM_ERR("invalid key spec in [%.*s]\n", in->len, in->s);
  443. pkg_free(rpv);
  444. return -1;
  445. }
  446. /**
  447. *
  448. */
  449. static int pv_get_redisc(struct sip_msg *msg, pv_param_t *param,
  450. pv_value_t *res)
  451. {
  452. redisc_pv_t *rpv;
  453. str s;
  454. int pos;
  455. rpv = (redisc_pv_t*)param->pvn.u.dname;
  456. if(rpv->reply==NULL)
  457. {
  458. rpv->reply = redisc_get_reply(&rpv->rname);
  459. if(rpv->reply==NULL)
  460. return pv_get_null(msg, param, res);
  461. }
  462. if(rpv->reply->rplRedis==NULL)
  463. return pv_get_null(msg, param, res);
  464. if(fixup_get_ivalue(msg, &rpv->pos, &pos)!=0)
  465. return pv_get_null(msg, param, res);
  466. switch(rpv->rkeyid) {
  467. case 1:
  468. /* value */
  469. switch(rpv->reply->rplRedis->type) {
  470. case REDIS_REPLY_STRING:
  471. if(pos!=-1)
  472. return pv_get_null(msg, param, res);
  473. s.len = rpv->reply->rplRedis->len;
  474. s.s = rpv->reply->rplRedis->str;
  475. return pv_get_strval(msg, param, res, &s);
  476. case REDIS_REPLY_INTEGER:
  477. if(pos!=-1)
  478. return pv_get_null(msg, param, res);
  479. return pv_get_sintval(msg, param, res,
  480. (int)rpv->reply->rplRedis->integer);
  481. case REDIS_REPLY_ARRAY:
  482. if(pos<0 || pos>=(int)rpv->reply->rplRedis->elements)
  483. return pv_get_null(msg, param, res);
  484. if(rpv->reply->rplRedis->element[pos]==NULL)
  485. return pv_get_null(msg, param, res);
  486. switch(rpv->reply->rplRedis->element[pos]->type) {
  487. case REDIS_REPLY_STRING:
  488. s.len = rpv->reply->rplRedis->element[pos]->len;
  489. s.s = rpv->reply->rplRedis->element[pos]->str;
  490. return pv_get_strval(msg, param, res, &s);
  491. case REDIS_REPLY_INTEGER:
  492. return pv_get_sintval(msg, param, res,
  493. (int)rpv->reply->rplRedis->element[pos]->integer);
  494. default:
  495. return pv_get_null(msg, param, res);
  496. }
  497. default:
  498. return pv_get_null(msg, param, res);
  499. }
  500. case 2:
  501. /* info */
  502. if(rpv->reply->rplRedis->str==NULL)
  503. return pv_get_null(msg, param, res);
  504. s.len = rpv->reply->rplRedis->len;
  505. s.s = rpv->reply->rplRedis->str;
  506. return pv_get_strval(msg, param, res, &s);
  507. case 3:
  508. /* size */
  509. if(rpv->reply->rplRedis->type == REDIS_REPLY_ARRAY) {
  510. return pv_get_uintval(msg, param, res, (unsigned int)rpv->reply->rplRedis->elements);
  511. } else {
  512. return pv_get_null(msg, param, res);
  513. }
  514. case 0:
  515. /* type */
  516. if(pos==-1)
  517. return pv_get_sintval(msg, param, res,
  518. rpv->reply->rplRedis->type);
  519. if(rpv->reply->rplRedis->type != REDIS_REPLY_ARRAY)
  520. return pv_get_null(msg, param, res);
  521. if(pos<0 || pos>=(int)rpv->reply->rplRedis->elements)
  522. return pv_get_null(msg, param, res);
  523. if(rpv->reply->rplRedis->element[pos]==NULL)
  524. return pv_get_null(msg, param, res);
  525. return pv_get_sintval(msg, param, res, rpv->reply->rplRedis->element[pos]->type);
  526. default:
  527. /* We do nothing. */
  528. return pv_get_null(msg, param, res);
  529. }
  530. }