/* * Author : GĂ©rald FENOY * * Copyright (c) 2014 GeoLabs SARL * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "service_internal_ruby.h" #if RUBY_API_VERSION_MAJOR >= 2 || RUBY_API_VERSION_MINOR == 9 int argc=0; char **argv=NULL; #endif /** * Load a Ruby file then run the function corresponding to the service by * passing the conf, inputs and outputs parameters by refernce as Ruby Hash. * * @param main_conf the conf maps containing the main.cfg settings * @param request the map containing the HTTP request * @param s the service structure * @param real_inputs the maps containing the inputs * @param real_outputs the maps containing the outputs * @return SERVICE_SUCCEEDED or SERVICE_FAILED if the service run, -1 * if the service failed to load or throw error at runtime. */ int zoo_ruby_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){ #if RUBY_API_VERSION_MAJOR >= 2 || RUBY_API_VERSION_MINOR == 9 ruby_sysinit(&argc,&argv); RUBY_INIT_STACK; #endif ruby_init(); maps* m=*main_conf; maps* inputs=*real_inputs; maps* outputs=*real_outputs; map* tmp0=getMapFromMaps(*main_conf,"lenv","cwd"); char *ntmp=tmp0->value; map* tmp=NULL; ruby_init_loadpath(); ruby_script("ZOO_EMBEDDED_ENV"); VALUE klass=rb_define_module("Zoo"); rb_define_const(klass,"SERVICE_SUCCEEDED",INT2FIX(3)); rb_define_const(klass,"SERVICE_FAILED",INT2FIX(4)); typedef VALUE (*HOOK)(...); rb_define_module_function(klass,"Translate",reinterpret_cast(RubyTranslate),-1); rb_define_module_function(klass,"UpdateStatus",reinterpret_cast(RubyUpdateStatus),-1); int error = 0; ID rFunc=Qnil; tmp=getMap(s->content,"serviceProvider"); if(tmp!=NULL){ #if RUBY_VERSION_MINOR == 8 const char* script = ruby_sourcefile = rb_source_filename(tmp->value); rb_protect(LoadWrap, reinterpret_cast(script), &error); #else rb_load_protect(rb_str_new2(tmp->value), 0, &error); #endif if(error) { ruby_trace_error(m); return -1; } #if RUBY_VERSION_MINOR == 8 ruby_exec(); #else ruby_exec_node(NULL); #endif } else{ map* err=createMap("text","Unable to parse serviceProvider please check your zcfg file."); addToMap(err,"code","NoApplicableCode"); printExceptionReportResponse(m,err); return -1; } int res=SERVICE_FAILED; rFunc=rb_intern(s->name); if(rFunc!=Qnil){ VALUE arg1=RubyHash_FromMaps(m); VALUE arg2=RubyHash_FromMaps(inputs); VALUE arg3=RubyHash_FromMaps(outputs); VALUE rArgs[3]={arg1,arg2,arg3}; if (!rArgs) return -1; struct my_callback data; data.obj=Qnil; data.method_id=rFunc; data.nargs=3; data.args[0]=rArgs[0]; data.args[1]=rArgs[1]; data.args[2]=rArgs[2]; typedef VALUE (*HOOK)(VALUE); VALUE tres=rb_protect(reinterpret_cast(FunCallWrap),(VALUE)(&data),&error); if (TYPE(tres) == T_FIXNUM) { res=FIX2INT(tres); freeMaps(real_outputs); free(*real_outputs); freeMaps(main_conf); free(*main_conf); *main_conf=mapsFromRubyHash(arg1); *real_outputs=mapsFromRubyHash(arg3); #ifdef DEBUG dumpMaps(*main_conf); dumpMaps(*real_outputs); #endif }else{ ruby_trace_error(m); res=-1; } } else{ char tmpS[1024]; sprintf(tmpS, "Cannot find the %s function in the %s file.\n", s->name, tmp->value); map* tmps=createMap("text",tmpS); printExceptionReportResponse(m,tmps); res=-1; } ruby_finalize(); return res; } /** * Load a ruby file * * @arg the file to load * @return Qnil */ VALUE LoadWrap(VALUE arg) { const char *filename = reinterpret_cast(arg); rb_load_file(filename); return Qnil; } /** * Call a ruby function with parameters * * @arg the callback structure * @return the value returned the called ruby function */ VALUE FunCallWrap(VALUE rdata) { struct my_callback* data = (struct my_callback*) rdata; return rb_funcall2(data->obj,data->method_id,data->nargs,data->args); } /** * Print the Ruby Stack Trace in an ows:ExceptionReport XML Document * * @param m the conf maps containing the main.cfg settings * @see printExceptionReportResponse */ void ruby_trace_error(maps* m){ #if RUBY_VERSION_MINOR == 8 VALUE lasterr = rb_gv_get("$!"); #else VALUE lasterr = rb_errinfo(); VALUE ruby_errinfo = lasterr; #endif VALUE message = rb_obj_as_string(lasterr); VALUE lklass = rb_class_path(CLASS_OF(lasterr)); #if RUBY_VERSION_MINOR == 8 char *trace=(char*)malloc((strlen(RSTRING(lklass)->ptr)+strlen(RSTRING(message)->ptr)+3)*sizeof(char)); sprintf(trace,"%s: %s",RSTRING_PTR(lklass),RSTRING_PTR(message)); #else char *trace=(char*)malloc((strlen(RSTRING_PTR(lklass))+strlen(RSTRING_PTR(message))+3)*sizeof(char)); sprintf(trace,"%s: %s",RSTRING_PTR(lklass),RSTRING_PTR(message)); #endif if(!NIL_P(ruby_errinfo)) { VALUE ary = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0); int c; for (c=0; cptr[c])->ptr)+2)*sizeof(char)); sprintf(trace,"%s\n%s",tmp0,RSTRING(RARRAY(ary)->ptr[c])->ptr); #else trace=(char *) realloc(trace,(len+strlen(RSTRING_PTR(RARRAY_PTR(ary)[c]))+2)*sizeof(char)); sprintf(trace,"%s\n%s",tmp0,RSTRING_PTR(RARRAY_PTR(ary)[c])); #endif free(tmp0); } } map* err=createMap("text",trace); addToMap(err,"code","NoApplicableCode"); printExceptionReportResponse(m,err); } /** * Convert a maps to a Ruby Hash * * @param t the maps to convert * @return a new Ruby Hash */ VALUE RubyHash_FromMaps(maps* t){ VALUE res=rb_hash_new(); maps* tmp=t; while(tmp!=NULL){ VALUE value=RubyHash_FromMap(tmp->content); VALUE name=rb_str_new2(tmp->name); rb_hash_aset(res,name,value); tmp=tmp->next; } return res; } /** * Push the key on the array * * @param key the key to push * @param value not used * @param ary the Array * @return ST_CONTINUE */ int keys_i(VALUE key,VALUE value,VALUE ary) { if (key == Qundef) return ST_CONTINUE; rb_ary_push(ary, key); return ST_CONTINUE; } /** * Return the size of a Ruby Hash * * @param hash the input Hash */ VALUE rb_hash_size(VALUE hash) { return INT2FIX(RHASH_TBL(hash)->num_entries); } /** * Convert a map to a Ruby Hash * * @param t the map to convert * @return a new Ruby Hash */ VALUE RubyHash_FromMap(map* t){ VALUE res=rb_hash_new( ); map* tmp=t; int hasSize=0; map* isArray=getMap(tmp,"isArray"); map* useFile=getMap(tmp,"use_file"); map* size=getMap(tmp,"size"); map* tmap=getMapType(tmp); while(tmp!=NULL){ VALUE name= rb_str_new2(tmp->name); if(strcasecmp(tmp->name,"value")==0) { if(isArray!=NULL){ map* len=getMap(tmp,"length"); int cnt=atoi(len->value); VALUE value=rb_ary_new2(cnt); VALUE mvalue=rb_ary_new2(cnt); VALUE svalue=rb_ary_new2(cnt); VALUE cvalue=rb_ary_new2(cnt); for(int i=0;ivalue); if(sMap==NULL) lsvalue=Qnil; else lsvalue=rb_str_new2(sMap->value); } else{ lvalue=rb_str_new(vMap->value,atoi(sMap->value)); lsvalue=rb_str_new2(sMap->value); hasSize=1; } if(uMap!=NULL) lcvalue=rb_str_new2(cMap->value); else lcvalue=Qnil; rb_ary_push(value,lvalue); rb_ary_push(svalue,lsvalue); rb_ary_push(cvalue,lcvalue); } map* mMap=getMapArray(tmp,tmap->name,i); VALUE lmvalue; if(mMap!=NULL){ lmvalue=rb_str_new2(mMap->value); }else lmvalue=Qnil; rb_ary_push(mvalue,lmvalue); } rb_hash_aset(res, name, mvalue); rb_hash_aset(res, rb_str_new2(tmap->name), mvalue); VALUE lname0=rb_str_new2("cache_size"); rb_hash_aset(res, lname0, value); if(hasSize>0){ VALUE lname=rb_str_new2("size"); rb_hash_aset(res, lname, value); } } else if(size!=NULL){ VALUE value=rb_str_new(tmp->value,atoi(size->value)); rb_hash_aset(res, name, value); } else{ VALUE value=rb_str_new2(tmp->value); rb_hash_aset(res, name, value); } } else{ VALUE value=rb_str_new2(tmp->value); rb_hash_aset(res, name, value); } tmp=tmp->next; } return res; } /** * Convert a Ruby Hash to a maps * * @param t the Ruby Hash to convert * @return a new maps */ maps* mapsFromRubyHash(VALUE t){ maps* res=NULL; maps* cursor=res; VALUE list; list = rb_ary_new(); typedef int (*HOOK)(...); rb_hash_foreach(t, reinterpret_cast(keys_i), list); int nb=rb_hash_size(t); int i; for(i=0;i> DEBUG VALUES : %s => %s\n", StringValueCStr(key),StringValueCStr(value)); #endif cursor=createMaps(StringValueCStr(key)); cursor->content=mapFromRubyHash(value); if(res==NULL) res=dupMaps(&cursor); else addMapsToMaps(&res,cursor); freeMaps(&cursor); free(cursor); } return res; } /** * Convert a Ruby Hash to a map * * @param t the Ruby Hash to convert * @return a new map */ map* mapFromRubyHash(VALUE t){ map* res=NULL; VALUE list; list = rb_ary_new(); typedef int (*HOOK)(...); rb_hash_foreach(t,reinterpret_cast(keys_i), list); int nb=RHASH_TBL(t)->num_entries; int i; for(i=0;i> DEBUG VALUES : %s => %s\n", StringValueCStr(key),StringValueCStr(value)); #endif if(strcmp(StringValueCStr(key),"value")==0){ char *buffer=NULL; int size=RSTRING_LEN(value); buffer=StringValueCStr(value); if(res!=NULL){ addToMap(res,StringValueCStr(key),""); }else{ res=createMap(StringValueCStr(key),""); } map* tmpR=getMap(res,"value"); free(tmpR->value); tmpR->value=(char*)malloc((size+1)*sizeof(char)); memmove(tmpR->value,buffer,size*sizeof(char)); tmpR->value[size]=0; char sin[1024]; sprintf(sin,"%d",size); addToMap(res,"size",sin); }else{ if(res!=NULL){ addToMap(res,StringValueCStr(key),StringValueCStr(value)); } else{ res= createMap(StringValueCStr(key),StringValueCStr(value)); } } } return res; } /** * Use the ZOO-Services messages translation function from the Ruby * environment (ZOO-API) * * @param argc the number of parameters * @param argv the parameter values given from the Ruby environment * @param obj the Ruby object on which we run the method * @return a new Ruby string containing the translated value * @see _ss */ VALUE RubyTranslate(int argc, VALUE *argv, VALUE obj) { return rb_str_new2(_ss(StringValueCStr(argv[0]))); } /** * Update the ongoing status of a running service from the Ruby environment * (ZOO-API) * * @param argc the number of parameters * @param argv the parameter values given from the Ruby environment * @param obj the Ruby object on which we run the method * @return a new Ruby string containing the translated value * @see _updateStatus */ VALUE RubyUpdateStatus(int argc, VALUE *argv, VALUE obj) { maps* conf; VALUE confdict=argv[0]; int istatus=argv[1]; char* status; if (istatus < 0 || istatus > 100){ fprintf(stderr,"Status must be a percentage."); return Qnil; }else{ char tmpStatus[4]; snprintf(tmpStatus, 4, "%i", istatus); status = zStrdup(tmpStatus); } /* now update the map */ { VALUE lenv = rb_hash_aref(confdict,rb_str_new2("lenv")); if(TYPE(lenv)!=T_NIL){ VALUE valobj=rb_str_new2(status); rb_hash_aset(lenv,rb_str_new2("status"),valobj); } } conf = mapsFromRubyHash(confdict); if (getMapFromMaps(conf,"lenv","status") != NULL){ fprintf(stderr,"STATUS RETURNED : %s\n",status); if(status!=NULL){ setMapInMaps(conf,"lenv","status",status); free(status); } else setMapInMaps(conf,"lenv","status","15"); _updateStatus(conf); } freeMaps(&conf); free(conf); return Qnil; }