| 19 | | PyObject *osync_module; |
| 20 | | }; |
| 21 | | |
| 22 | | /** Insert plugin search path in sys.path |
| 23 | | * |
| 24 | | * Isn't there an easier way of setting it? |
| 25 | | * |
| 26 | | *FIXME: It would be better if we just load modules from |
| 27 | | * their filename, not setting the module search path |
| 28 | | */ |
| 29 | | static int change_sys_path() |
| 30 | | { |
| 31 | | int rv = -1; |
| 32 | | PyObject *sys = NULL; |
| 33 | | PyObject *path = NULL; |
| 34 | | PyObject *dir = NULL; |
| 35 | | |
| 36 | | sys = PyImport_ImportModule("sys"); |
| 37 | | if (!sys) goto error; |
| 38 | | |
| 39 | | path = PyObject_GetAttrString(sys, "path"); |
| 40 | | if (!path) goto error; |
| 41 | | |
| 42 | | dir = PyString_FromString(OPENSYNC_PYTHONPLG_DIR); |
| 43 | | if (!dir) goto error; |
| 44 | | |
| 45 | | if (PyList_Insert(path, 0, dir) < 0) |
| 46 | | goto error; |
| 47 | | |
| 48 | | /* Success */ |
| 49 | | rv = 0; |
| 50 | | |
| 51 | | error: |
| 52 | | Py_XDECREF(dir); |
| 53 | | Py_XDECREF(path); |
| 54 | | Py_XDECREF(sys); |
| 55 | | return rv; |
| | 19 | } MemberData; |
| | 20 | |
| | 21 | static PyObject *pm_load_opensync(OSyncError **error) |
| | 22 | { |
| | 23 | PyObject *osync_module = PyImport_ImportModule("opensync"); |
| | 24 | if (!osync_module) { |
| | 25 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load OpenSync module"); |
| | 26 | PyErr_Print(); |
| | 27 | return NULL; |
| | 28 | } |
| | 29 | return osync_module; |
| | 30 | } |
| | 31 | |
| | 32 | static PyObject *pm_load_script(const char *filename, OSyncError **error) |
| | 33 | { |
| | 34 | FILE *fp = fopen(filename, "r"); |
| | 35 | if (!fp) { |
| | 36 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to open file %s", filename); |
| | 37 | return NULL; |
| | 38 | } |
| | 39 | |
| | 40 | if (PyRun_SimpleFile(fp, filename) == -1) { |
| | 41 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't run module from file %s", filename); |
| | 42 | PyErr_Print(); |
| | 43 | return NULL; |
| | 44 | } |
| | 45 | |
| | 46 | PyObject *module = PyImport_AddModule("__main__"); |
| | 47 | if (!module) { |
| | 48 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module from file %s", filename); |
| | 49 | PyErr_Print(); |
| | 50 | return NULL; |
| | 51 | } |
| | 52 | return module; |
| | 53 | } |
| | 54 | |
| | 55 | static PyObject *pm_make_change(PyObject *module, OSyncChange *change, OSyncError **error) |
| | 56 | { |
| | 57 | PyObject *pychg_cobject = PyCObject_FromVoidPtr(change, NULL); |
| | 58 | if (!pychg_cobject) { |
| | 59 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pychg cobject"); |
| | 60 | PyErr_Print(); |
| | 61 | return NULL; |
| | 62 | } |
| | 63 | |
| | 64 | PyObject *pychg = PyObject_CallMethod(module, "OSyncChange", "O", pychg_cobject); |
| | 65 | if (!pychg) { |
| | 66 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncChange"); |
| | 67 | PyErr_Print(); |
| | 68 | Py_XDECREF(pychg_cobject); |
| | 69 | return NULL; |
| | 70 | } |
| | 71 | return pychg; |
| | 72 | } |
| | 73 | |
| | 74 | static PyObject *pm_make_context(PyObject *module, OSyncContext *ctx, OSyncError **error) |
| | 75 | { |
| | 76 | PyObject *pyctx_cobject = PyCObject_FromVoidPtr(ctx, NULL); |
| | 77 | if (!pyctx_cobject) { |
| | 78 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyctx cobject"); |
| | 79 | PyErr_Print(); |
| | 80 | return NULL; |
| | 81 | } |
| | 82 | |
| | 83 | PyObject *pyctx = PyObject_CallMethod(module, "OSyncContext", "O", pyctx_cobject); |
| | 84 | if (!pyctx) { |
| | 85 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncContext"); |
| | 86 | PyErr_Print(); |
| | 87 | Py_XDECREF(pyctx_cobject); |
| | 88 | return NULL; |
| | 89 | } |
| | 90 | return pyctx; |
| | 91 | } |
| | 92 | |
| | 93 | static PyObject *pm_make_member(PyObject *module, OSyncMember *member, OSyncError **error) |
| | 94 | { |
| | 95 | PyObject *pymember_cobject = PyCObject_FromVoidPtr(member, NULL); |
| | 96 | if (!pymember_cobject) { |
| | 97 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pymember cobject"); |
| | 98 | PyErr_Print(); |
| | 99 | return NULL; |
| | 100 | } |
| | 101 | |
| | 102 | PyObject *pymember = PyObject_CallMethod(module, "OSyncMember", "O", pymember_cobject); |
| | 103 | if (!pymember) { |
| | 104 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncMember"); |
| | 105 | PyErr_Print(); |
| | 106 | Py_XDECREF(pymember_cobject); |
| | 107 | return NULL; |
| | 108 | } |
| | 109 | return pymember; |
| 145 | | |
| 146 | | Py_EndInterpreter(mydata->interp_thread); |
| 147 | | } |
| 148 | | |
| 149 | | /** Create a new opensync.Change object for a given change */ |
| 150 | | static PyObject *new_pychange(struct MemberData *mydata, OSyncChange *chg) |
| 151 | | { |
| 152 | | PyObject *cobject = NULL; |
| 153 | | PyObject *ret = NULL; |
| 154 | | |
| 155 | | cobject = PyCObject_FromVoidPtr(chg, NULL); |
| 156 | | if (!cobject) |
| 157 | | goto error; |
| 158 | | |
| 159 | | /* ret = opensync.Context(member=None, chg=cobject) */ |
| 160 | | ret = PyObject_CallMethod(mydata->osync_module, "Change", "OO", Py_None, cobject); |
| 161 | | |
| 162 | | /* Done, drop reference even on success (the opensync.Change object |
| 163 | | * will hold a reference to the cobject) |
| 164 | | */ |
| 165 | | Py_XDECREF(cobject); |
| 166 | | |
| 167 | | return ret; |
| 168 | | |
| 169 | | error: |
| 170 | | Py_XDECREF(ret); |
| 171 | | Py_XDECREF(cobject); |
| 172 | | return NULL; |
| 173 | | } |
| 174 | | |
| 175 | | /** Create a new opensync.Context object for a given context */ |
| 176 | | static PyObject *new_pycontext(struct MemberData *mydata, OSyncContext *ctx) |
| 177 | | { |
| 178 | | PyObject *cobject = NULL; |
| 179 | | PyObject *ret = NULL; |
| 180 | | |
| 181 | | cobject = PyCObject_FromVoidPtr(ctx, NULL); |
| 182 | | if (!cobject) |
| 183 | | goto error; |
| 184 | | |
| 185 | | /* ret = opensync.Context(cobject) */ |
| 186 | | ret = PyObject_CallMethod(mydata->osync_module, "Context", "O", cobject); |
| 187 | | |
| 188 | | /* Done, drop reference even on success (the opensync.Context object |
| 189 | | * will hold a reference to the cobject) |
| 190 | | */ |
| 191 | | Py_XDECREF(cobject); |
| 192 | | |
| 193 | | return ret; |
| 194 | | |
| 195 | | error: |
| 196 | | Py_XDECREF(ret); |
| 197 | | Py_XDECREF(cobject); |
| 198 | | return NULL; |
| | 181 | osync_trace(TRACE_EXIT, "%s", __func__); |
| 227 | | } |
| 228 | | |
| 229 | | context = new_pycontext(data, ctx); |
| 230 | | if (!context) { |
| 231 | | osync_debug("python", 1, "Can't create a context object"); |
| 232 | | PyErr_Print(); |
| 233 | | osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't create a opensync.Context object"); |
| 234 | | PyErr_Clear(); |
| 235 | | goto out; |
| 236 | | } |
| 237 | | |
| 238 | | if (change) |
| 239 | | ret = PyObject_CallMethod(data->object, name, "OO", context, change); |
| 240 | | else |
| 241 | | ret = PyObject_CallMethod(data->object, name, "O", context); |
| 242 | | |
| | 217 | |
| | 218 | ret = PyObject_CallMethod(data->object, name, "OO", pycontext, pychange); |
| | 219 | |
| | 220 | Py_XDECREF(pychange); |
| | 221 | } else { |
| | 222 | ret = PyObject_CallMethod(data->object, name, "O", pycontext); |
| | 223 | } |
| | 224 | |
| 256 | | } |
| 257 | | |
| 258 | | static void py_connect(OSyncContext *ctx) |
| 259 | | { |
| 260 | | call_module_method(ctx, NULL, "connect"); |
| 261 | | } |
| 262 | | |
| 263 | | |
| 264 | | static void py_get_changeinfo(OSyncContext *ctx) |
| 265 | | { |
| 266 | | call_module_method(ctx, NULL, "get_changeinfo"); |
| 267 | | } |
| 268 | | |
| 269 | | |
| 270 | | static void py_get_data(OSyncContext *ctx, OSyncChange *change) |
| 271 | | { |
| 272 | | call_module_method(ctx, change, "get_data"); |
| 273 | | } |
| 274 | | |
| 275 | | static osync_bool py_access(OSyncContext *ctx, OSyncChange *change) |
| 276 | | { |
| 277 | | call_module_method(ctx, change, "access"); |
| | 236 | |
| | 237 | osync_trace(TRACE_EXIT, "%s", __func__); |
| 281 | | static osync_bool py_commit_change(OSyncContext *ctx, OSyncChange *change) |
| 282 | | { |
| 283 | | call_module_method(ctx, change, "commit_change"); |
| 284 | | return 0; |
| 285 | | } |
| 286 | | |
| 287 | | static void py_sync_done(OSyncContext *ctx) |
| 288 | | { |
| 289 | | call_module_method(ctx, NULL, "sync_done"); |
| 290 | | } |
| 291 | | |
| 292 | | static void py_disconnect(OSyncContext *ctx) |
| 293 | | { |
| 294 | | call_module_method(ctx, NULL, "disconnect"); |
| | 241 | static void pm_connect(OSyncContext *ctx) |
| | 242 | { |
| | 243 | osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); |
| | 244 | OSyncError *error = NULL; |
| | 245 | pm_call_module_method(ctx, NULL, "connect", &error); |
| | 246 | osync_trace(TRACE_EXIT, "%s", __func__); |
| | 247 | } |
| | 248 | |
| | 249 | |
| | 250 | static void pm_get_changeinfo(OSyncContext *ctx) |
| | 251 | { |
| | 252 | osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); |
| | 253 | OSyncError *error = NULL; |
| | 254 | pm_call_module_method(ctx, NULL, "get_changeinfo", &error); |
| | 255 | osync_trace(TRACE_EXIT, "%s", __func__); |
| | 256 | } |
| | 257 | |
| | 258 | static osync_bool pm_access(OSyncContext *ctx, OSyncChange *change) |
| | 259 | { |
| | 260 | osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change); |
| | 261 | OSyncError *error = NULL; |
| | 262 | pm_call_module_method(ctx, change, "access", &error); |
| | 263 | osync_trace(TRACE_EXIT, "%s", __func__); |
| | 264 | return TRUE; |
| | 265 | } |
| | 266 | |
| | 267 | static osync_bool pm_commit_change(OSyncContext *ctx, OSyncChange *change) |
| | 268 | { |
| | 269 | osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change); |
| | 270 | OSyncError *error = NULL; |
| | 271 | pm_call_module_method(ctx, change, "commit_change", &error); |
| | 272 | osync_trace(TRACE_EXIT, "%s", __func__); |
| | 273 | return TRUE; |
| | 274 | } |
| | 275 | |
| | 276 | static void pm_sync_done(OSyncContext *ctx) |
| | 277 | { |
| | 278 | osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); |
| | 279 | OSyncError *error = NULL; |
| | 280 | pm_call_module_method(ctx, NULL, "sync_done", &error); |
| | 281 | osync_trace(TRACE_EXIT, "%s", __func__); |
| | 282 | } |
| | 283 | |
| | 284 | static void pm_disconnect(OSyncContext *ctx) |
| | 285 | { |
| | 286 | osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); |
| | 287 | OSyncError *error = NULL; |
| | 288 | pm_call_module_method(ctx, NULL, "disconnect", &error); |
| | 289 | osync_trace(TRACE_EXIT, "%s", __func__); |
| 304 | | static int register_plugin(OSyncEnv *env, PyObject *osync_module, char *name) |
| 305 | | { |
| 306 | | PyObject *module = NULL; |
| 307 | | PyObject *get_info_result = NULL; |
| 308 | | PythonPluginInfo pyinfo; |
| 309 | | PyObject *pyinfo_cobject = NULL; |
| 310 | | PyObject *get_info_parm = NULL; |
| 311 | | OSyncPluginInfo *info; |
| 312 | | int ret = -1; |
| 313 | | |
| 314 | | osync_trace(TRACE_ENTRY, "register_plugin"); |
| 315 | | |
| 316 | | module = PyImport_ImportModule(name); |
| | 299 | static osync_bool register_plugin(OSyncEnv *env, const char *filename, OSyncError **error) |
| | 300 | { |
| | 301 | osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, filename, error); |
| | 302 | |
| | 303 | PyObject *module = pm_load_script(filename, error); |
| 318 | | osync_debug("python", 1, "Couldn't load testmodule"); |
| | 305 | osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); |
| | 306 | return FALSE; |
| | 307 | } |
| | 308 | |
| | 309 | OSyncPluginInfo *info = osync_plugin_new_info(env); |
| | 310 | info->functions.initialize = pm_initialize; |
| | 311 | info->functions.connect = pm_connect; |
| | 312 | info->functions.get_changeinfo = pm_get_changeinfo; |
| | 313 | info->functions.sync_done = pm_sync_done; |
| | 314 | info->functions.disconnect = pm_disconnect; |
| | 315 | info->functions.finalize = pm_finalize; |
| | 316 | |
| | 317 | info->plugin_data = g_strdup(filename); |
| | 318 | |
| | 319 | PyObject *pyinfo_cobject = PyCObject_FromVoidPtr(info, NULL); |
| | 320 | if (!pyinfo_cobject) { |
| | 321 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyinfo cobject"); |
| 321 | | goto out; |
| 322 | | } |
| 323 | | |
| 324 | | info = osync_plugin_new_info(env); |
| 325 | | if (!info) { |
| 326 | | osync_debug("python", 1, "Couldn't create a new plugin object"); |
| 327 | | goto error; |
| 328 | | } |
| 329 | | |
| 330 | | info->functions.initialize = py_initialize; |
| 331 | | info->functions.connect = py_connect; |
| 332 | | info->functions.sync_done = py_sync_done; |
| 333 | | info->functions.disconnect = py_disconnect; |
| 334 | | info->functions.finalize = py_finalize; |
| 335 | | info->functions.get_changeinfo = py_get_changeinfo; |
| 336 | | info->functions.get_data = py_get_data; |
| 337 | | |
| 338 | | /*TODO: let the module get_info function fill the fields below: */ |
| 339 | | info->is_threadsafe = 1; |
| 340 | | |
| 341 | | /* The plugin data is just the plugin name, |
| 342 | | * to be used on py_initialize() |
| 343 | | */ |
| 344 | | info->plugin_data = strdup(name); |
| 345 | | |
| 346 | | /** Build a PluginInfo object for use by get_info */ |
| 347 | | pyinfo.commit_fn = py_commit_change; |
| 348 | | pyinfo.access_fn = py_access; |
| 349 | | pyinfo.osync_info = info; |
| 350 | | |
| 351 | | pyinfo_cobject = PyCObject_FromVoidPtr(&pyinfo, NULL); |
| 352 | | if (!pyinfo_cobject) { |
| 353 | | osync_debug("python", 1, "Can't create pyinfo_cobject"); |
| | 324 | osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); |
| | 325 | return FALSE; |
| | 326 | } |
| | 327 | |
| | 328 | PyObject *pyinfo = PyObject_CallMethod(module, "OSyncPluginInfo", "O", pyinfo_cobject); |
| | 329 | osync_trace(TRACE_INTERNAL, "pyinfo: %p\n", pyinfo); |
| | 330 | if (!pyinfo) { |
| | 331 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncPluginInfo"); |
| 356 | | goto error_cancel_register; |
| 357 | | } |
| 358 | | |
| 359 | | /* get_info_parm = opensync.PluginInfo(pyinfo_cobject) */ |
| 360 | | get_info_parm = PyObject_CallMethod(osync_module, "PluginInfo", "O", pyinfo_cobject); |
| 361 | | if (!get_info_parm) { |
| 362 | | osync_debug("python", 1, "Can't create get_info_parm"); |
| | 334 | osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); |
| | 335 | return FALSE; |
| | 336 | } |
| | 337 | |
| | 338 | if (!PyObject_CallMethod(module, "get_info", "O", pyinfo)) { |
| | 339 | osync_error_set(error, OSYNC_ERROR_GENERIC, "Error calling get_info"); |