00001 /* 00002 !concept {:name => "Lua based config files"} 00003 */ 00004 00005 #include "cf_rcfile.h" 00006 00007 #if __FEATURE_RCFILE__ 00008 00009 #include "er_errors.h" 00010 #include "lua_cl2.h" 00011 #include "utils_cl2.h" 00012 00013 struct _cf_RcFile { 00014 // Contains the named configuration values as global variables. 00015 lua_State *L; 00016 }; 00017 00018 #ifdef __EPOC32__ 00019 #define RCFILE_BASENAME "config.txt" 00020 #else 00021 #define RCFILE_BASENAME ".cl2rc" 00022 #endif 00023 00024 #define RCFILE_DIR CONFIG_DIR 00025 #define RCFILE_FILE (RCFILE_DIR DIR_SEP RCFILE_BASENAME) 00026 00027 #define return_with_error(s...) { if (error) *error = gx_error_new(domain_cl2app, code_unspecified_error, s); return FALSE; } 00028 #define return_with_oom { if (error) *error = gx_error_no_memory; return FALSE; } 00029 00030 // See also boost/preprocessor/stringize.hpp for similar definitions. 00031 #define STRINGIZE_DETAIL(x) #x 00032 #define STRINGIZE(x) STRINGIZE_DETAIL(x) 00033 00034 #define EVALUATE(_s) { /*logt(_s);*/ if (luaL_dostring(L, _s)) throw LuaException(); } 00035 00036 /***koog 00037 (require codegen/cpp-include) 00038 (display-cpp-string-lit-decl/from-file 00039 "CF_VALIDATE_IN_LUA" 00040 "cf_validate.lua") 00041 ***/ 00042 #define CF_VALIDATE_IN_LUA \ 00043 "function is_non_empty_string (s)\n" \ 00044 "return (s ~= '')\n" \ 00045 "end\n" \ 00046 "function validate (n, rt, chk)\n" \ 00047 "local v = _G[n]\n" \ 00048 "if v then\n" \ 00049 "local t = type(v)\n" \ 00050 "if t ~= 'function' and t ~= rt then\n" \ 00051 "error(string.format('value %q not of required type %q', n, rt))\n" \ 00052 "end\n" \ 00053 "if chk and t ~= 'function' then\n" \ 00054 "if not chk(v) then\n" \ 00055 "error(string.format('value %q is not valid', n))\n" \ 00056 "end\n" \ 00057 "end\n" \ 00058 "end\n" \ 00059 "end\n" \ 00060 "validate('compress_logs', 'boolean', nil)\n" \ 00061 "validate('database_dir', 'string', is_non_empty_string)\n" \ 00062 "validate('database_disk_threshold', 'number', nil)\n" \ 00063 "validate('iap', 'number', nil)\n" \ 00064 "validate('jid', 'string', is_non_empty_string)\n" \ 00065 "validate('mcc', 'number', nil)\n" \ 00066 "validate('operator_name', 'string', is_non_empty_string)\n" \ 00067 "validate('remokon_host', 'string', is_non_empty_string)\n" \ 00068 "validate('remokon_password', 'string', is_non_empty_string)\n" \ 00069 "validate('remokon_port', 'number', nil)\n" \ 00070 "validate('upload_url', 'string', is_non_empty_string)\n" \ 00071 "validate('username', 'string', cl2.is_ascii_ident)\n" \ 00072 "if iap == nil then\n" \ 00073 "iap = IAP_DEFAULT\n" \ 00074 "end\n" \ 00075 "if upload_url == nil then\n" \ 00076 "upload_url = UPLOAD_URL_DEFAULT\n" \ 00077 "end\n" \ 00078 "if remokon_host == nil then\n" \ 00079 "remokon_host = REMOKON_HOST_DEFAULT\n" \ 00080 "end\n" \ 00081 "if jid == nil then\n" \ 00082 "jid = JID_DEFAULT\n" \ 00083 "end\n" 00084 /***end***/ 00085 00086 // This function validates the configuration, checking the types of 00087 // the configured values and such. Any defaults can also be set here 00088 // for values that are not present. 00089 static void ValidateAdjustConfig(lua_State *L) 00090 { 00091 // We first make any defaults available to Lua by defining them as 00092 // globals. (Defaults are typically given as Lua expressions to 00093 // begin with, but use STRINGIZE if you need to.) Then we invoke the 00094 // generated Lua code that does the validation etc. 00095 const char* s = 00096 "IAP_DEFAULT = " __IAP_ID_EXPR__ ";\n" 00097 "UPLOAD_URL_DEFAULT = " __UPLOAD_URL__ ";\n" 00098 "REMOKON_HOST_DEFAULT = " __REMOKON_HOST__ ";\n" 00099 "JID_DEFAULT = " __REMOKON_JID__ ";\n" 00100 CF_VALIDATE_IN_LUA; 00101 EVALUATE(s); 00102 } 00103 00104 static gboolean ReadRcFile(cf_RcFile* self, lua_State *L, GError** error) 00105 { 00106 (void)self; 00107 00108 int errCode; 00109 00110 if ((errCode = luaL_loadfile(L, RCFILE_FILE)) != 0) { 00111 if (errCode == LUA_ERRFILE) { 00112 // Could not open or read the file. This is okay since a 00113 // configuration file is not compulsory. 00114 logg("no (readable) configuration file '%s'", RCFILE_FILE); 00115 ValidateAdjustConfig(L); 00116 return TRUE; 00117 } 00118 00119 #if __DO_LOGGING__ 00120 // An error occurred, and hence an error message should have been pushed by Lua. 00121 if (!lua_isnone(L, -1)) { 00122 const char* s = lua_tostring(L, -1); 00123 if (s) logt(s); 00124 } 00125 #endif /* __DO_LOGGING__ */ 00126 00127 if (error) 00128 *error = gx_error_new(domain_cl2app, code_unspecified_error, "error parsing configuration file '%s'", RCFILE_FILE); 00129 return FALSE; 00130 } 00131 logg("config file '%s' parsed OK", RCFILE_FILE); 00132 00133 if (lua_pcall(L, 0, 1, 0)) { 00134 if (error) 00135 *error = gx_error_new(domain_cl2app, code_unspecified_error, "error evaluating configuration file '%s'", RCFILE_FILE); 00136 return FALSE; 00137 } 00138 logt("config file evaluated OK"); 00139 00140 ValidateAdjustConfig(L); 00141 00142 #if __DO_LOGGING__ 00143 EVALUATE("do\n" 00144 " local function f (n, fm) local v = _G[n]; if v ~= nil then if type(v) == 'function' then cl2.log(string.format('%s configured to <function>', n)) else cl2.log(string.format('%s configured to ' .. fm .. ' :: %s', n, v, type(v))); end; end; end;" 00145 " f('username', '%q');" 00146 " f('upload_url', '%q');" 00147 " f('remokon_host', '%q');" 00148 " f('iap', '%q');" 00149 " f('database_disk_threshold', '%q');" 00150 "end"); 00151 #endif /* __DO_LOGGING__ */ 00152 00153 return TRUE; 00154 } 00155 00156 extern "C" cf_RcFile* cf_RcFile_new(GError** error) 00157 { 00158 cf_RcFile* self = g_try_new0(cf_RcFile, 1); 00159 if (G_UNLIKELY(!self)) { 00160 if (error) *error = gx_error_no_memory; 00161 return NULL; 00162 } 00163 00164 // Reentering the same VM instance to fetch some config info seems 00165 // to work. Not sure if it is quite safe, though, so you may want to 00166 // consider that when adding cl2 module using functions into your 00167 // config file. 00168 // 00169 // Also note that *any* unprotected error occurring in this VM will 00170 // bring down the entire process. This is by design, as we consider 00171 // OOM errors and errors in config files very severe. 00172 self->L = cl_lua_new_libs(); 00173 if (G_UNLIKELY(!self->L)) { 00174 if (error) *error = gx_error_no_memory; 00175 goto fail; 00176 } 00177 00178 try { 00179 if (G_UNLIKELY(!ReadRcFile(self, self->L, error))) { 00180 goto fail; 00181 } 00182 } catch(const LuaException&) { 00183 lua_set_gerror(self->L, error); 00184 goto fail; 00185 } 00186 00187 return self; 00188 00189 fail: 00190 cf_RcFile_destroy(self); 00191 return NULL; 00192 } 00193 00194 extern "C" void cf_RcFile_destroy(cf_RcFile* self) 00195 { 00196 if (self) { 00197 if (self->L) 00198 lua_close(self->L); 00199 g_free(self); 00200 } 00201 } 00202 00203 class Fail {}; 00204 00205 static void get_value(lua_State *L, const char* name) 00206 { 00207 // It is important to clear the stack occasionally. Note that we do 00208 // not pop any return value to prevent it from being collected 00209 // immediately. 00210 lua_settop(L, 0); 00211 00212 // Not documented, but returns nil on non-existent key. Or produces 00213 // an error if there is no stack space, presumably. 00214 lua_getglobal(L, name); 00215 if (lua_isnil(L, -1)) { 00216 throw Fail(); 00217 } 00218 if (lua_isfunction(L, -1)) { 00219 // Should always return the requested number of values, even if 00220 // must pad with nil values. 00221 lua_call(L, 0, 1); // void 00222 if (lua_isnil(L, -1)) { 00223 throw Fail(); 00224 } 00225 } 00226 } 00227 00228 extern "C" int cf_RcFile_get_int_or(cf_RcFile* self, const char* name, int dval) 00229 { 00230 try { 00231 get_value(self->L, name); 00232 } catch(const Fail& e) { 00233 return dval; 00234 } 00235 if (!lua_isnumber(self->L, -1)) { 00236 return dval; 00237 } 00238 return lua_tointeger(self->L, -1); 00239 } 00240 00241 extern "C" gboolean cf_RcFile_get_bool_or(cf_RcFile* self, const char* name, gboolean dval) 00242 { 00243 try { 00244 get_value(self->L, name); 00245 } catch(const Fail& e) { 00246 return dval; 00247 } 00248 if (!lua_isboolean(self->L, -1)) { 00249 return dval; 00250 } 00251 return lua_toboolean(self->L, -1); 00252 } 00253 00254 // Note that unless you want to strdup the returned value, and if you want to keep it around for a while, then you better make sure (in your config file) that you are returning a pointer to a global Lua string. 00255 // xxx We might want to internally query and strdup settings that cannot change during runtime. 00256 extern "C" const char* cf_RcFile_get_str_maybe(cf_RcFile* self, const char* name) 00257 { 00258 try { 00259 get_value(self->L, name); 00260 } catch(const Fail& e) { 00261 return NULL; 00262 } 00263 return lua_tostring(self->L, -1); // NULL if of wrong type 00264 } 00265 00266 extern "C" const char* cf_RcFile_get_str_or(cf_RcFile* self, const char* name, const char* dval) 00267 { 00268 const char* val = cf_RcFile_get_str_maybe(self, name); 00269 if (!val) 00270 return dval; 00271 return val; 00272 } 00273 00274 extern "C" int cf_RcFile_vm_id(cf_RcFile* self) 00275 { 00276 return (int)self->L; 00277 } 00278 00279 #endif /* __FEATURE_RCFILE__ */ 00280 00281 /** 00282 00283 cf_rcfile.cpp 00284 00285 Copyright 2009 Helsinki Institute for Information Technology (HIIT) 00286 and the authors. All rights reserved. 00287 00288 Authors: Tero Hasu <tero.hasu@hut.fi> 00289 00290 Permission is hereby granted, free of charge, to any person 00291 obtaining a copy of this software and associated documentation files 00292 (the "Software"), to deal in the Software without restriction, 00293 including without limitation the rights to use, copy, modify, merge, 00294 publish, distribute, sublicense, and/or sell copies of the Software, 00295 and to permit persons to whom the Software is furnished to do so, 00296 subject to the following conditions: 00297 00298 The above copyright notice and this permission notice shall be 00299 included in all copies or substantial portions of the Software. 00300 00301 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00302 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00303 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00304 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 00305 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 00306 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00307 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00308 SOFTWARE. 00309 00310 **/
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1