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.
- data/ext/cmpi-bindings/cmpi_wrap.c +15317 -0
- data/ext/cmpi-bindings/extconf.rb +14 -0
- data/ext/cmpi-bindings/string_array.h +125 -0
- data/ext/src/cmpi_provider.c +1302 -0
- data/ext/src/target_ruby.c +462 -0
- data/lib/cmpi.rb +344 -0
- data/lib/cmpi/provider.rb +213 -0
- metadata +88 -0
@@ -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
|
+
|