cmpi-bindings 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,462 @@
1
+ /*
2
+ * target_ruby.c
3
+ *
4
+ * Target language specific functions for cmpi_bindings
5
+ *
6
+ * Here: Ruby
7
+ *
8
+ * Written by Klaus Kaempf <kkaempf@suse.de>
9
+ *
10
+ */
11
+
12
+ /*****************************************************************************
13
+ * Copyright (C) 2008 Novell Inc. All rights reserved.
14
+ * Copyright (C) 2008 SUSE Linux Products GmbH. All rights reserved.
15
+ *
16
+ * Redistribution and use in source and binary forms, with or without
17
+ * modification, are permitted provided that the following conditions are met:
18
+ *
19
+ * - Redistributions of source code must retain the above copyright notice,
20
+ * this list of conditions and the following disclaimer.
21
+ *
22
+ * - Redistributions in binary form must reproduce the above copyright notice,
23
+ * this list of conditions and the following disclaimer in the documentation
24
+ * and/or other materials provided with the distribution.
25
+ *
26
+ * - Neither the name of Novell Inc. nor of SUSE Linux Products GmbH nor the
27
+ * names of its contributors may be used to endorse or promote products
28
+ * derived from this software without specific prior written permission.
29
+ *
30
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
31
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
+ * ARE DISCLAIMED. IN NO EVENT SHALL Novell Inc. OR SUSE Linux Products GmbH OR
34
+ * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
38
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
39
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
40
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41
+ *****************************************************************************/
42
+
43
+ /*
44
+ * How it works
45
+ *
46
+ * This target language adapter provides three functions for use by cmpi-provider.c
47
+ *
48
+ * static int TargetInitialize(ProviderMIHandle* hdl, CMPIStatus* st)
49
+ * static int TargetCall(ProviderMIHandle* hdl, CMPIStatus* st, const char* opname, int nargs, ...)
50
+ * static void TargetCleanup(ProviderMIHandle * hdl)
51
+ *
52
+ * TargetInitialize
53
+ * - loads the provider pointed to by ProviderMIHandle#miName
54
+ * miName - managed interface name - is FooBar (camel case) and loads foo_bar.rb
55
+ * expecting class Cmpi::FooBar
56
+ *
57
+ * TargetCall
58
+ * - calls opname with args of ProviderMIHandle#implementation
59
+ *
60
+ * TargetCleanup
61
+ * - tear down provider
62
+ */
63
+
64
+ #include <ctype.h>
65
+ #include <sys/types.h>
66
+ #include <sys/stat.h>
67
+ #include <unistd.h>
68
+
69
+ /* the module name for all Ruby code */
70
+ #define RB_MODULE_NAME "Cmpi"
71
+
72
+ /* an optional environment variable pointing to a directory with Ruby providers */
73
+ #define RUBY_PROVIDERS_DIR_ENV "RUBY_PROVIDERS_DIR"
74
+
75
+ /* mutex to flag Ruby call in progress - the one aquiring the lock inits the stack */
76
+ static pthread_mutex_t _stack_init_mutex = PTHREAD_MUTEX_INITIALIZER;
77
+
78
+ static void
79
+ decamelize(const char *from, char *to)
80
+ {
81
+ const char *start = from;
82
+ /* copy/decamelize classname */
83
+ while (*from) {
84
+ if (isupper(*from)) {
85
+ if (from > start /* not first char */
86
+ && (*(to-1) != '_')
87
+ && (islower(*(from-1)) || islower(*(from+1))) ) { /* last was lower or next is lower */
88
+ *to++ = '_';
89
+ }
90
+ *to++ = tolower(*from++);
91
+ }
92
+ else {
93
+ *to++ = *from++;
94
+ }
95
+ }
96
+ *to = 0;
97
+ }
98
+
99
+ /*
100
+ * load_module - load provider
101
+ *
102
+ * separate function for rb_require so it can be wrapped into rb_protect()
103
+ *
104
+ * Returns Cmpi::<Class>
105
+ *
106
+ */
107
+
108
+ static VALUE
109
+ load_provider(VALUE arg)
110
+ {
111
+ const char *classname = (const char *)arg;
112
+ VALUE req; /* result of rb_require */
113
+ if (classname == NULL || *classname == 0) {
114
+ _SBLIM_TRACE(1,("Ruby: load_provider(%s) no class given", classname));
115
+ return Qfalse;
116
+ }
117
+ char *filename = alloca(strlen(classname) * 2 + 1);
118
+ decamelize(classname, filename);
119
+ ruby_script(filename);
120
+ _SBLIM_TRACE(1,("Ruby: loading (%s)", filename));
121
+ req = rb_require(filename);
122
+ /* Qtrue == just loaded, Qfalse = already loaded, else: fail */
123
+ if ((req != Qtrue) && (req != Qfalse)) {
124
+ _SBLIM_TRACE(1,("<%d> require '%s' failed", getpid(), filename));
125
+ return Qnil;
126
+ }
127
+ /* Get Cmpi::Provider */
128
+ VALUE val = rb_const_get(rb_cObject, rb_intern(RB_MODULE_NAME));
129
+ if (val == Qnil) {
130
+ _SBLIM_TRACE(1,("<%d> No such module '%s'", getpid(), RB_MODULE_NAME));
131
+ return val;
132
+ }
133
+ val = rb_const_get(val, rb_intern(classname));
134
+ if (val == Qnil) {
135
+ _SBLIM_TRACE(1,("<%d> No such class '%s::%s'", getpid(), RB_MODULE_NAME, classname));
136
+ }
137
+ return val;
138
+ }
139
+
140
+
141
+ /*
142
+ * call_mi (called from rb_protect)
143
+ * call function of instance
144
+ *
145
+ * I args: pointer to array of at least 3 values
146
+ * args[0] -> (VALUE) instance
147
+ * args[1] -> (VALUE) id of function
148
+ * args[2] -> (int) number of arguments
149
+ * args[3...n] -> (VALUE) arguments
150
+ */
151
+
152
+ static VALUE
153
+ call_mi(VALUE args)
154
+ {
155
+ VALUE *values = (VALUE *)args;
156
+ return rb_funcall3(values[0], values[1], (int)values[2], values+3);
157
+ }
158
+
159
+
160
+
161
+ /*
162
+ * get Ruby exception trace -> CMPIString
163
+ *
164
+ */
165
+
166
+ #define TB_ERROR(str) {tbstr = str; goto cleanup;}
167
+ static CMPIString *
168
+ get_exc_trace(const CMPIBroker* broker)
169
+ {
170
+ VALUE exception = rb_gv_get("$!"); /* get last exception */
171
+ VALUE reason = rb_funcall(exception, rb_intern("to_s"), 0 );
172
+ VALUE trace = rb_gv_get("$@"); /* get last exception trace */
173
+ VALUE backtrace = rb_funcall(trace, rb_intern("join"), 1, rb_str_new("\n\t", 2));
174
+
175
+ char* tmp = fmtstr("%s\n\t%s", StringValuePtr(reason), StringValuePtr(backtrace));
176
+ return broker->eft->newString(broker, tmp, NULL);
177
+ }
178
+
179
+
180
+ /*
181
+ * Global Ruby initializer
182
+ *
183
+ * ** called with mutex locked **
184
+ *
185
+ * loads the Ruby interpreter
186
+ * init threads
187
+ */
188
+
189
+ static int
190
+ RbGlobalInitialize(const CMPIBroker* broker, CMPIStatus* st)
191
+ {
192
+ int error = 0;
193
+ char *loadpath;
194
+ VALUE searchpath;
195
+
196
+ if (_TARGET_INIT) {
197
+ return error;
198
+ }
199
+ _TARGET_INIT=1; /* safe, since mutex is locked */
200
+
201
+ _SBLIM_TRACE(1,("<%d> Ruby: Loading", getpid()));
202
+
203
+ ruby_init();
204
+ ruby_init_loadpath();
205
+ extern void SWIG_init();
206
+ SWIG_init();
207
+
208
+ searchpath = rb_gv_get("$:");
209
+ /* Append /usr/share/cmpi to $: */
210
+ rb_ary_push(searchpath, rb_str_new2("/usr/share/cmpi"));
211
+
212
+ /* Check RUBY_PROVIDERS_DIR_ENV if its a dir, append to $: */
213
+ loadpath = getenv(RUBY_PROVIDERS_DIR_ENV);
214
+ if (loadpath) {
215
+ struct stat buf;
216
+ if (stat(loadpath, &buf)) {
217
+ _SBLIM_TRACE(1,("<%d> Can't stat $RUBY_PROVIDERS_DIR '%s'", getpid(), loadpath));
218
+ return -1;
219
+ }
220
+ if ((buf.st_mode & S_IFDIR) == 0) {
221
+ _SBLIM_TRACE(1,("<%d> Not a directory: $RUBY_PROVIDERS_DIR '%s'", getpid(), loadpath));
222
+ return -1;
223
+ }
224
+ rb_ary_push(searchpath, rb_str_new2(loadpath));
225
+ }
226
+ return error;
227
+ }
228
+
229
+
230
+ /*---------------------------------------------------------------*/
231
+
232
+ /*
233
+ * local (per MI) Ruby initializer
234
+ * keeps track of reference count
235
+ *
236
+ */
237
+
238
+ static int
239
+ TargetInitialize(ProviderMIHandle* hdl, CMPIStatus* st)
240
+ {
241
+ VALUE args[6];
242
+ int error;
243
+ int have_lock = 0;
244
+
245
+ /* Set _CMPI_INIT, protected by _CMPI_INIT_MUTEX
246
+ * so we call ruby_finalize() only once.
247
+ */
248
+ if (pthread_mutex_lock(&_CMPI_INIT_MUTEX)) {
249
+ perror("Can't lock _CMPI_INIT_MUTEX");
250
+ abort();
251
+ }
252
+ error = RbGlobalInitialize(hdl->broker, st);
253
+ pthread_mutex_unlock(&_CMPI_INIT_MUTEX);
254
+ if (error != 0) {
255
+ if (st != NULL) {
256
+ st->rc = CMPI_RC_ERR_INVALID_CLASS;
257
+ st->msg = CMNewString(hdl->broker, "Failed to init Ruby", NULL);
258
+ }
259
+ goto fail;
260
+ }
261
+
262
+ _SBLIM_TRACE(1,("<%d> TargetInitialize(Ruby) called, miName '%s'", getpid(), hdl->miName));
263
+
264
+ if (pthread_mutex_trylock(&_stack_init_mutex) == 0) {
265
+ have_lock = 1;
266
+ RUBY_INIT_STACK
267
+ }
268
+
269
+ /* call static VALUE load_provider(const char *classname)
270
+ returns Cmpi::<Class>
271
+ */
272
+ args[0] = rb_protect(load_provider, (VALUE)hdl->miName, &error);
273
+ if (error) {
274
+ if (st != NULL) {
275
+ st->rc = CMPI_RC_ERR_INVALID_CLASS;
276
+ st->msg = CMNewString(hdl->broker, "Failed to load provider", NULL);
277
+ }
278
+ goto fail;
279
+ }
280
+
281
+ args[1] = rb_intern("new");
282
+ args[2] = 3;
283
+ args[3] = rb_str_new2(hdl->miName);
284
+ args[4] = SWIG_NewPointerObj((void*) hdl->broker, SWIGTYPE_p__CMPIBroker, 0);
285
+ args[5] = SWIG_NewPointerObj((void*) hdl->context, SWIGTYPE_p__CMPIContext, 0);
286
+ hdl->implementation = rb_protect(call_mi, (VALUE)args, &error);
287
+
288
+ fail:
289
+ if (error) {
290
+ CMPIString *trace = get_exc_trace(hdl->broker);
291
+ _SBLIM_TRACE(1,("Ruby: FAILED creating %s: %s", hdl->miName, CMGetCharPtr(trace)));
292
+ if (st != NULL) {
293
+ st->rc = CMPI_RC_ERR_INVALID_CLASS;
294
+ st->msg = trace;
295
+ }
296
+ }
297
+ else {
298
+ /* prevent Ruby GC from deallocating the provider
299
+ * found at http://www.lysator.liu.se/~norling/ruby_callbacks.html
300
+ */
301
+ rb_gc_register_address(&(hdl->implementation));
302
+ }
303
+ if (have_lock)
304
+ pthread_mutex_unlock(&_stack_init_mutex);
305
+ _SBLIM_TRACE(1,("Initialize() %s", (error == 0)?"succeeded":"failed"));
306
+ return error;
307
+ }
308
+
309
+
310
+ /*
311
+ * TargetCall
312
+ *
313
+ * Call function 'opname' with nargs arguments within managed interface hdl->implementation
314
+ */
315
+
316
+ static Target_Type
317
+ TargetCall(ProviderMIHandle* hdl, CMPIStatus* st,
318
+ const char* opname, int nargs, ...)
319
+ {
320
+ int have_lock = 0;
321
+ int invoke = (nargs < 0) ? 1 : 0; /* invokeMethod style call */
322
+ int i;
323
+ VALUE *args, result, op = rb_intern(opname);
324
+ va_list vargs;
325
+
326
+ if (pthread_mutex_trylock(&_stack_init_mutex) == 0) {
327
+ have_lock = 1;
328
+ RUBY_INIT_STACK
329
+ }
330
+ if (invoke) {
331
+ va_start(vargs, nargs);
332
+ args = va_arg(vargs, VALUE *);
333
+ va_end(vargs);
334
+ nargs = -nargs;
335
+ }
336
+ /* add hdl->instance, op and nargs to the args array, so rb_protect can be called */
337
+ nargs += 3;
338
+ if (!invoke) {
339
+ args = (VALUE *)alloca(nargs * sizeof(VALUE));
340
+ if (args == NULL) {
341
+ _SBLIM_TRACE(1,("Out of memory"));
342
+ abort();
343
+ }
344
+ }
345
+ args[0] = (VALUE)(hdl->implementation);
346
+ args[1] = op;
347
+ args[2] = (VALUE)(nargs-3);
348
+ if (!invoke && (nargs > 3))
349
+ {
350
+ va_start(vargs, nargs);
351
+ for (i = 3; i < nargs; ++i)
352
+ {
353
+ VALUE value;
354
+ value = va_arg(vargs, VALUE);
355
+ args[i] = (value == (VALUE)NULL) ? Qnil : value;
356
+ }
357
+ va_end(vargs);
358
+ }
359
+
360
+ /* call the Ruby function
361
+ * possible results:
362
+ * i nonzero: Exception raised
363
+ * result == nil: not (or badly) implemented
364
+ * result == true: success
365
+ * result == Array: pair of CMPIStatus rc(int) and msg(string)
366
+ */
367
+ result = rb_protect(call_mi, (VALUE)args, &i);
368
+
369
+ if (i) /* exception ? */
370
+ {
371
+ CMPIString *trace = get_exc_trace(hdl->broker);
372
+ char* str = fmtstr("Ruby: calling '%s' failed: %s", opname, CMGetCharPtr(trace));
373
+ _SBLIM_TRACE(1,("%s", str));
374
+ st->rc = CMPI_RC_ERR_FAILED;
375
+ st->msg = hdl->broker->eft->newString(hdl->broker, str, NULL);
376
+ goto done;
377
+ }
378
+ if (NIL_P(result)) /* not or wrongly implemented */
379
+ {
380
+ st->rc = CMPI_RC_ERR_NOT_SUPPORTED;
381
+ goto done;
382
+ }
383
+
384
+ if (invoke) {
385
+ st->rc = CMPI_RC_OK;
386
+ goto done;
387
+ }
388
+
389
+ if (result != Qtrue)
390
+ {
391
+ VALUE resulta = rb_check_array_type(result);
392
+ VALUE rc, msg;
393
+ if (NIL_P(resulta))
394
+ {
395
+ char* str = fmtstr("Ruby: calling '%s' returned unknown result", opname);
396
+ st->rc = CMPI_RC_ERR_FAILED;
397
+ st->msg = hdl->broker->eft->newString(hdl->broker, str, NULL);
398
+ goto done;
399
+ }
400
+
401
+ rc = rb_ary_entry(resulta, 0);
402
+ msg = rb_ary_entry(resulta, 1);
403
+ if (!FIXNUM_P(rc))
404
+ {
405
+ char* str = fmtstr("Ruby: calling '%s' returned non-numeric rc code", opname);
406
+ st->rc = CMPI_RC_ERR_FAILED;
407
+ st->msg = hdl->broker->eft->newString(hdl->broker, str, NULL);
408
+ goto done;
409
+ }
410
+ st->rc = FIX2LONG(rc);
411
+ st->msg = hdl->broker->eft->newString(hdl->broker, StringValuePtr(msg), NULL);
412
+ goto done;
413
+ }
414
+
415
+ /* all is fine */
416
+ st->rc = CMPI_RC_OK;
417
+ done:
418
+ if (have_lock)
419
+ pthread_mutex_unlock(&_stack_init_mutex);
420
+ return result;
421
+ }
422
+
423
+
424
+ /*
425
+ * TargetCleanup
426
+ */
427
+
428
+ static void
429
+ TargetCleanup(ProviderMIHandle * hdl)
430
+ {
431
+ _SBLIM_TRACE(1,("TargetCleanup(hdl %p)", hdl));
432
+ /* free() provider instance */
433
+ if (hdl && hdl->implementation) {
434
+ _SBLIM_TRACE(1,("unregister(%p)", hdl->implementation));
435
+ rb_gc_unregister_address(&(hdl->implementation));
436
+ }
437
+
438
+ /* Decrement _MI_COUNT, protected by _CMPI_INIT_MUTEX
439
+ * call ruby_finalize when _MI_COUNT drops to zero
440
+ */
441
+ if (pthread_mutex_lock(&_CMPI_INIT_MUTEX))
442
+ {
443
+ perror("Can't lock _CMPI_INIT_MUTEX");
444
+ abort();
445
+ }
446
+ if (--_MI_COUNT > 0)
447
+ {
448
+ pthread_mutex_unlock(&_CMPI_INIT_MUTEX);
449
+ _SBLIM_TRACE(1,("_MI_COUNT > 0: %d", _MI_COUNT));
450
+ return;
451
+ }
452
+
453
+ if (_TARGET_INIT) // if Ruby is initialized and _MI_COUNT == 0, call ruby_finalize
454
+ {
455
+ _SBLIM_TRACE(1,("Calling ruby_finalize()"));
456
+ ruby_finalize();
457
+ _TARGET_INIT=0; // false
458
+ }
459
+ pthread_mutex_unlock(&_CMPI_INIT_MUTEX);
460
+ return;
461
+ }
462
+