ndb_redis_mod.c 13 KB

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