mi_rpc_mod.c

00001 /*
00002  * $Id$
00003  *
00004  * mi_rpc module
00005  *
00006  * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com).
00007  *
00008  * Permission to use, copy, modify, and distribute this software for any
00009  * purpose with or without fee is hereby granted, provided that the above
00010  * copyright notice and this permission notice appear in all copies.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00013  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00014  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00015  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00016  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00017  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00018  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00019  */
00020 
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <unistd.h>
00024 
00025 #include "../../sr_module.h"
00026 #include "../../dprint.h"
00027 
00028 #include "../../lib/kmi/mi.h"
00029 #include "../../rpc.h"
00030 #include "../../lib/binrpc/binrpc_api.h"
00031 
00032 MODULE_VERSION
00033 
00034 static int child_init(int rank);
00035 static int mod_init(void);
00036 
00037 static str mi_rpc_indent = { "\t", 1 };
00038 static char *rpc_url = "";
00039 
00040 static const char* rpc_mi_exec_doc[2] = {
00041         "Execute MI command",
00042         0
00043 };
00044 
00045 static param_export_t parameters[] = {
00046     {"rpc_url",            STR_PARAM, &rpc_url},
00047     {0, 0, 0}
00048 };
00049 
00050 enum mi_rpc_print_mode {
00051         MI_PRETTY_PRINT,
00052         MI_FIFO_PRINT,
00053         MI_DATAGRAM_PRINT,
00054         MI_XMLRPC_PRINT
00055 };
00056 
00057 
00058 
00059 static void rpc_mi_pretty_exec(rpc_t* rpc, void* c);
00060 static void rpc_mi_fifo_exec(rpc_t* rpc, void* c);
00061 static void rpc_mi_dg_exec(rpc_t* rpc, void* c);
00062 static void rpc_mi_xmlrpc_exec(rpc_t* rpc, void* c);
00063 
00064 rpc_export_t mr_rpc[] = {
00065         {"mi",                  rpc_mi_pretty_exec,     rpc_mi_exec_doc,  RET_ARRAY},
00066         {"mi_fifo",             rpc_mi_fifo_exec,       rpc_mi_exec_doc,  RET_ARRAY},
00067         {"mi_dg",               rpc_mi_dg_exec,         rpc_mi_exec_doc,  RET_ARRAY},
00068         {"mi_xmlrpc",   rpc_mi_xmlrpc_exec,     rpc_mi_exec_doc,  RET_ARRAY},
00069         {0, 0, 0, 0}
00070 };
00071 
00072 struct module_exports exports = {
00073         "mi_rpc",
00074         0,           /* Exported functions */
00075         mr_rpc,      /* RPC methods */
00076         parameters,  /* Export parameters */
00077         mod_init,    /* Module initialization function */
00078         0,           /* Response function */
00079         0,           /* Destroy function */
00080         0,           /* OnCancel function */
00081         child_init   /* Child initialization function */
00082 };
00083 
00084 static struct mi_root* mi_run_rpc(struct mi_root* cmd_tree, void* param);
00085 
00086 static mi_export_t mi_cmds[] = 
00087 {
00088   { "rpc", mi_run_rpc, 0, 0, 0,},
00089   { 0, 0, 0, 0, 0 }
00090 };
00091 
00092 
00093 static int mod_init(void)
00094 {
00095   if(register_mi_mod(exports.name, mi_cmds)!=0)
00096   {
00097     LM_ERR("Failed to register MI commands\n");
00098     return(-1);
00099   }
00100   return 0;
00101 }
00102 
00103 static int child_init(int rank)
00104 {
00105         if(is_rpc_worker(rank)) {
00106                 LM_DBG("initializing child[%d] for rpc handling\n", rank);
00107                 if(init_mi_child(rank, 0)!=0) {
00108                         LM_CRIT("Failed to init the mi commands\n");
00109                         return -1;
00110                 }
00111         }
00112 
00113         return 0;
00114 }
00115 
00116 struct mi_root *mi_rpc_read_params(rpc_t *rpc, void *ctx)
00117 {
00118         struct mi_root *root;
00119         struct mi_node *node;
00120         str name;
00121         str value;
00122 
00123         root = init_mi_tree(0,0,0);
00124         if (!root) {
00125                 LM_ERR("the MI tree cannot be initialized!\n");
00126                 goto error;
00127         }
00128         node = &root->node;
00129 
00130         while (rpc->scan(ctx, "*.S", &value) == 1)
00131         {
00132                 name.s   = 0;
00133                 name.len = 0;
00134 
00135                 if(value.len>=2 && value.s[0]=='-' && value.s[1]=='-')
00136                 {
00137                         /* name */
00138                         if(value.len>2)
00139                         {
00140                                 name.s   = value.s + 2;
00141                                 name.len = value.len - 2;
00142                         }
00143 
00144                         /* value */
00145                         if(rpc->scan(ctx, "*.S", &value) != 1)
00146                         {
00147                                 LM_ERR("value expected\n");
00148                                 goto error;
00149                         }
00150                 }
00151 
00152                 if(!add_mi_node_child(node, 0, name.s, name.len,
00153                                         value.s, value.len))
00154                 {
00155                         LM_ERR("cannot add the child node to the MI tree\n");
00156                         goto error;
00157                 }
00158         }
00159 
00160         return root;
00161 
00162 error:
00163         if (root)
00164                 free_mi_tree(root);
00165         return 0;
00166 }
00167 
00168 
00169 
00173 static int mi_rpc_print_node(rpc_t* rpc, void* ctx, struct mi_node* node,
00174                                                                 enum mi_rpc_print_mode mode, char* prefix)
00175 {
00176         static char buf[512];
00177         char* p;
00178         int n;
00179         int size;
00180         struct mi_attr *attr;
00181         
00182         p=buf;
00183         *p=0;
00184         size=sizeof(buf);
00185                 n=snprintf(p, size, "%s%.*s:: %.*s",
00186                                 prefix,
00187                                 node->name.len, (node->name.s)?node->name.s:"",
00188                                 node->value.len, (node->value.s)?node->value.s:"");
00189                 if (n==-1 || n >= size)
00190                         goto error_buf;
00191                 p+=n;
00192                 size-=n;
00193                 for( attr=node->attributes ; attr!=NULL ; attr=attr->next ) {
00194                         n=snprintf(p, size, " %.*s=%.*s",
00195                                         attr->name.len, (attr->name.s)?attr->name.s:"",
00196                                         attr->value.len, (attr->value.s)?attr->value.s:"");
00197                         if (n==-1 || n >= size)
00198                                 goto error_buf;
00199                         p+=n;
00200                         size-=n;
00201                 }
00202                 if (mode!=MI_PRETTY_PRINT){
00203                         n=snprintf(p, size, "\n");
00204                         if (n==-1 || n>= size ) 
00205                                 goto error_buf;
00206                 }
00207                 rpc->add(ctx, "s", buf);
00208         return 0;
00209 error_buf:
00210                 ERR("line too long (extra %d chars)\n", (n>=size)?n-size+1:0);
00211                 rpc->fault(ctx, 500, "Line too long");
00212                 return -1;
00213 }
00214 
00215 
00216 
00217 static int mi_rpc_rprint_all(rpc_t* rpc, void* ctx, struct mi_node *node,
00218                 enum mi_rpc_print_mode mode, int level)
00219 {
00220         char indent[32];
00221         int i;
00222         char *p;
00223 
00224         p = indent;
00225         switch(mode){
00226                 case MI_FIFO_PRINT:
00227                 case MI_DATAGRAM_PRINT:
00228                 case MI_PRETTY_PRINT:
00229                         if(level*mi_rpc_indent.len>=32)
00230                         {
00231                                 LM_ERR("too many recursive levels for indentation\n");
00232                                 return -1;
00233                         }
00234                         for(i=0; i<level; i++)
00235                         {
00236                                 memcpy(p, mi_rpc_indent.s, mi_rpc_indent.len);
00237                                 p += mi_rpc_indent.len;
00238                         }
00239                         break;
00240                 case MI_XMLRPC_PRINT:
00241                         /* no identation  in this mode */
00242                         break;
00243         }
00244         *p = 0;
00245         for( ; node ; node=node->next )
00246         {
00247                 if (mi_rpc_print_node(rpc, ctx, node, mode, p)<0)
00248                         return -1;
00249                 if (node->kids) {
00250                         if (mi_rpc_rprint_all(rpc, ctx, node->kids, mode, level+1)<0)
00251                                 return -1;
00252                 }
00253         }
00254         return 0;
00255 }
00256 
00257 
00258 
00264 static int mi_rpc_print_tree(rpc_t* rpc, void* ctx, struct mi_root *tree,
00265                                                                 enum mi_rpc_print_mode mode)
00266 {
00267         switch(mode){
00268                 case MI_FIFO_PRINT:
00269                 case MI_DATAGRAM_PRINT:
00270                         /* always success, code & reason are the part of the reply */
00271                         rpc->printf(ctx, "%d %.*s\n", tree->code,
00272                                                 tree->reason.len, tree->reason.s);
00273                         break;
00274                 case MI_PRETTY_PRINT:
00275                 case MI_XMLRPC_PRINT:
00276                         /* don't print code & reason, use fault instead */
00277                         if (tree->code<200 || tree->code> 299) {
00278                                 rpc->fault(ctx, tree->code, tree->reason.s);
00279                                 return -1;
00280                         }
00281                         break;
00282         }
00283 
00284         if (tree->node.kids)
00285         {
00286                 if (mi_rpc_rprint_all(rpc, ctx, tree->node.kids, mode, 0)<0)
00287                         return -1;
00288         }
00289         if (mode==MI_FIFO_PRINT){
00290                 /* mi fifo adds an extra "\n" at the end */
00291                 rpc->printf(ctx, "\n");
00292         }
00293         
00294         return 0;
00295 }
00296 
00297 
00298 
00299 /* structure used to pack the rpc dyn. ctx and the print mode */
00300 struct mi_rpc_handler_param{
00301         rpc_delayed_ctx_t* dctx;
00302         enum mi_rpc_print_mode mode;
00303 };
00304 
00305 /* send reply and close async context */
00306 static void mi_rpc_async_close(struct mi_root* mi_rpl,
00307                                                                         struct mi_handler* mi_h,
00308                                                                         int done)
00309 {
00310         rpc_delayed_ctx_t* dctx;
00311         rpc_t* rpc;
00312         void* c;
00313         enum mi_rpc_print_mode mode;
00314         
00315         dctx=0;
00316         if (done){
00317                 if (mi_h->param==0){
00318                         BUG("null param\n");
00319                         shm_free(mi_h);
00320                         goto error;
00321                 }
00322                 dctx=((struct mi_rpc_handler_param*)mi_h->param)->dctx;
00323                 if (dctx==0){
00324                         BUG("null dctx\n");
00325                         shm_free(mi_h->param);
00326                         shm_free(mi_h);
00327                         mi_h->param=0;
00328                         goto error;
00329                 }
00330                 mode=((struct mi_rpc_handler_param*)mi_h->param)->mode;
00331                 rpc=&dctx->rpc;
00332                 c=dctx->reply_ctx;
00333                 
00334                 mi_rpc_print_tree(rpc, c, mi_rpl, mode);
00335                 
00336                 rpc->delayed_ctx_close(dctx);
00337                 shm_free(mi_h->param);
00338                 mi_h->param=0;
00339                 shm_free(mi_h);
00340         } /* else: no provisional support => do nothing */
00341 error:
00342         if (mi_rpl)
00343                 free_mi_tree(mi_rpl);
00344         return;
00345 }
00346 
00347 
00348 
00349 static void rpc_mi_exec(rpc_t *rpc, void *ctx, enum mi_rpc_print_mode mode)
00350 {
00351         str cmd;
00352         struct mi_cmd *mic;
00353         struct mi_root *mi_req;
00354         struct mi_root *mi_rpl;
00355         struct mi_handler* mi_async_h;
00356         struct mi_rpc_handler_param* mi_handler_param;
00357 
00358         if (rpc->scan(ctx, "S", &cmd) < 1)
00359         {
00360                 LM_ERR("command parameter not found\n");
00361                 rpc->fault(ctx, 500, "command parameter missing");
00362                 return;
00363         }
00364         
00365         mi_async_h=0;
00366         mi_req = 0;
00367         mi_rpl=0;
00368         
00369         mic = lookup_mi_cmd(cmd.s, cmd.len);
00370         if(mic==0)
00371         {
00372                 LM_ERR("mi command %.*s is not available\n", cmd.len, cmd.s);
00373                 rpc->fault(ctx, 500, "command not available");
00374                 return;
00375         }
00376 
00377         if (mic->flags&MI_ASYNC_RPL_FLAG)
00378         {
00379                 if (rpc->capabilities==0 ||
00380                                 !(rpc->capabilities(ctx) & RPC_DELAYED_REPLY))
00381                 {
00382                         rpc->fault(ctx, 500,
00383                                                         "this rpc transport does not support async mode");
00384                         return;
00385                 }
00386         }
00387 
00388         if(!(mic->flags&MI_NO_INPUT_FLAG))
00389         {
00390                 mi_req = mi_rpc_read_params(rpc, ctx);
00391                 if(mi_req==NULL)
00392                 {
00393                         LM_ERR("cannot parse parameters\n");
00394                         rpc->fault(ctx, 500, "cannot parse parameters");
00395                         goto error;
00396                 }
00397                 if (mic->flags&MI_ASYNC_RPL_FLAG)
00398                 {
00399                         /* build mi async handler */
00400                         mi_handler_param=shm_malloc(sizeof(*mi_handler_param));
00401                         if (mi_handler_param==0){
00402                                 rpc->fault(ctx, 500, "out of memory");
00403                                 return;
00404                         }
00405                         mi_async_h=shm_malloc(sizeof(*mi_async_h));
00406                         if (mi_async_h==0){
00407                                 shm_free(mi_handler_param);
00408                                 mi_handler_param=0;
00409                                 rpc->fault(ctx, 500, "out of memory");
00410                                 return;
00411                         }
00412                         memset(mi_async_h, 0, sizeof(*mi_async_h));
00413                         mi_async_h->handler_f=mi_rpc_async_close;
00414                         mi_handler_param->mode=mode;
00415                         mi_handler_param->dctx=rpc->delayed_ctx_new(ctx);
00416                         if (mi_handler_param->dctx==0){
00417                                 rpc->fault(ctx, 500, "internal error: async ctx"
00418                                                                                 " creation failed");
00419                                 goto error;
00420                         }
00421                         /* switch context, since replies are not allowed anymore on the
00422                            original one */
00423                         rpc=&mi_handler_param->dctx->rpc;
00424                         ctx=mi_handler_param->dctx->reply_ctx;
00425                         mi_async_h->param=mi_handler_param;
00426                 }
00427                 mi_req->async_hdl=mi_async_h;
00428         }
00429         mi_rpl=run_mi_cmd(mic, mi_req);
00430 
00431         if(mi_rpl == 0)
00432         {
00433                 rpc->fault(ctx, 500, "execution failed");
00434                 goto error;
00435         }
00436 
00437         if (mi_rpl!=MI_ROOT_ASYNC_RPL)
00438         {
00439                 mi_rpc_print_tree(rpc, ctx, mi_rpl, mode);
00440                 goto end;
00441         }else if (mi_async_h==0){
00442                 /* async reply, but command not listed as async */
00443                 rpc->fault(ctx, 500, "bad mi command: unexpected async reply");
00444                 goto error;
00445         }
00446         mi_async_h=0; /* don't delete it */
00447 end:
00448 error:
00449         if (mi_req)
00450                 free_mi_tree(mi_req);
00451         if (mi_rpl && mi_rpl!=MI_ROOT_ASYNC_RPL)
00452                 free_mi_tree(mi_rpl);
00453         if (mi_async_h){
00454                 if (mi_async_h->param){
00455                         if (((struct mi_rpc_handler_param*)mi_async_h->param)->dctx)
00456                                 rpc->delayed_ctx_close(((struct mi_rpc_handler_param*)
00457                                                                                         mi_async_h->param)->dctx);
00458                         shm_free(mi_async_h->param);
00459                 }
00460                 shm_free(mi_async_h);
00461         }
00462         return;
00463 }
00464 
00465 
00466 
00467 static void rpc_mi_pretty_exec(rpc_t* rpc, void* c)
00468 {
00469         rpc_mi_exec(rpc, c, MI_PRETTY_PRINT);
00470 }
00471 
00472 
00473 
00474 static void rpc_mi_fifo_exec(rpc_t* rpc, void* c)
00475 {
00476         rpc_mi_exec(rpc, c, MI_FIFO_PRINT);
00477 }
00478 
00479 
00480 
00481 static void rpc_mi_dg_exec(rpc_t* rpc, void* c)
00482 {
00483         rpc_mi_exec(rpc, c, MI_DATAGRAM_PRINT);
00484 }
00485 
00486 
00487 
00488 static void rpc_mi_xmlrpc_exec(rpc_t* rpc, void* c)
00489 {
00490         rpc_mi_exec(rpc, c, MI_XMLRPC_PRINT);
00491 }
00492 
00493 
00494 static struct mi_root* mi_run_rpc(struct mi_root* cmd_tree, void* param)
00495 {
00496         const char* FAILED = "Failed";
00497         const char* CONNECT_FAILED = "Connection to RPC failed";
00498         struct binrpc_handle rpc_handle;
00499         struct binrpc_response_handle resp_handle;
00500         int i;
00501         
00502         str *fn;
00503         struct mi_node *node;
00504         char *command = NULL;
00505         int param_count = 0;
00506         char **parameters = NULL;
00507         struct mi_root* result;
00508         
00509         int resp_type;
00510         int resp_code;
00511         char *resp;
00512 
00513         /* response will be malloced by binrpc_response_to_text. 
00514            We do not free it. It must remain after this call.
00515            It will be reused by subsequent calls */
00516         static unsigned char *response = NULL;
00517         static int resp_len = 0;
00518         
00519         if (binrpc_open_connection_url(&rpc_handle, rpc_url) != 0) 
00520         {
00521                 LM_ERR( "Open connect to %s failed\n", rpc_url);
00522                 result = init_mi_tree( 500, (char *)CONNECT_FAILED, strlen(CONNECT_FAILED) );
00523                 goto end;
00524         }
00525 
00526         node = cmd_tree->node.kids;
00527 
00528         if (node==NULL || node->value.s == NULL)
00529                 return( init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN ));
00530 
00531         fn = &node->value;
00532         
00533         /* find_rpc_exports needs 0 terminated strings */
00534         command = pkg_malloc(fn->len+1);
00535     memcpy(command, fn->s, fn->len);
00536         command[fn->len] = '\0';
00537         
00538         /* Count the parameters. */
00539         node = node->next;
00540         while (node) {
00541                 if (node->value.s) {
00542                         param_count++;
00543                 }
00544                 node = node->next;
00545         }
00546         if (param_count > 0)
00547         {
00548                 /* Copy them into an array of NULL terminated strings. */
00549                 parameters = pkg_malloc(param_count * sizeof(char *));
00550 
00551                 node = cmd_tree->node.kids;
00552                 node = node->next;
00553                 param_count = 0;
00554                 while (node) {
00555                         if (node->value.s) {
00556                                 parameters[param_count] = pkg_malloc(node->value.len + 1);
00557                                 memcpy(parameters[param_count], node->value.s, node->value.len);
00558                                 parameters[param_count][node->value.len] = '\0';
00559                                 param_count++;
00560                         }
00561                         node = node->next;
00562                 }
00563         }
00564         if (binrpc_send_command(&rpc_handle, command, parameters, param_count, &resp_handle))
00565         {
00566                 result = init_mi_tree( 500, (char *)FAILED, strlen(FAILED) );
00567                 goto end;
00568         }
00569         
00570         resp_type = binrpc_get_response_type(&resp_handle);
00571         
00572         /* If we already have a buffer make it NULL terminated to discard any previous content */
00573         if (resp_len > 0)
00574                 response[0]='\0';
00575 
00576         switch (resp_type)
00577         {
00578                 case 0:
00579                         /* Success */
00580                         binrpc_response_to_text(&resp_handle, &response, &resp_len, '\n');
00581                         if (strlen((char*)response) > 0)
00582                                 result = init_mi_tree( 200, (char*)response, strlen((char*)response) );
00583                         else
00584                                 /* Some functions don't give a text answer; use a default */
00585                                 result = init_mi_tree( 200, MI_OK_S, MI_OK_LEN );
00586                         break;
00587                         
00588                 case 1:
00589                         /* Valid failure */
00590                         binrpc_parse_error_response(&resp_handle, &resp_code, &resp);
00591                         if (resp_len < strlen(resp) + 1)
00592                         { 
00593                                 if (resp_len==0)
00594                                         response = malloc(strlen(resp) + 1);
00595                                 else
00596                                         response = realloc(response, strlen(resp) + 1);
00597                         }
00598                         memcpy(response, resp, strlen(resp));
00599                         response[strlen(resp)]='\0';
00600                         if (strlen((char*)response) > 0)
00601                                 result = init_mi_tree( resp_code, (char*)response, strlen((char*)response) );
00602                         else
00603                                 /* Some functions don't give a text answer; use a default */
00604                                 result = init_mi_tree( resp_code, (char *)FAILED, strlen(FAILED) );
00605                         break;
00606                         
00607                 default:
00608                         result = init_mi_tree( 500, (char *)FAILED, strlen(FAILED) );
00609                         goto end;
00610         }
00611 
00612 end:
00613         if (param_count > 0)
00614         {
00615                 for (i=0; i<param_count; i++) 
00616                 {
00617                         pkg_free(parameters[i]);
00618                 }
00619                 pkg_free(parameters);
00620         }
00621         if (command != NULL)
00622         {
00623                 pkg_free(command);
00624                 command = NULL;
00625         }
00626         binrpc_close_connection(&rpc_handle);
00627         binrpc_release_response(&resp_handle);
00628         return( result );
00629 }