ruby-lxc 1.0.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/lxc/extconf.rb +7 -0
- data/ext/lxc/lxc.c +1773 -0
- data/lib/lxc.rb +2 -0
- data/lib/lxc/version.rb +3 -0
- metadata +66 -0
data/ext/lxc/extconf.rb
ADDED
data/ext/lxc/lxc.c
ADDED
|
@@ -0,0 +1,1773 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <linux/personality.h> /* for PER_* constants */
|
|
3
|
+
#include <linux/sched.h> /* for CLONE_* constants */
|
|
4
|
+
#include <lxc/lxccontainer.h>
|
|
5
|
+
#include <lxc/attach_options.h>
|
|
6
|
+
#include <stdint.h>
|
|
7
|
+
#include <string.h>
|
|
8
|
+
|
|
9
|
+
#define SYMBOL(s) ID2SYM(rb_intern(s))
|
|
10
|
+
|
|
11
|
+
extern int lxc_wait_for_pid_status(pid_t pid);
|
|
12
|
+
extern long lxc_config_parse_arch(const char *arch);
|
|
13
|
+
|
|
14
|
+
static VALUE Container;
|
|
15
|
+
static VALUE Error;
|
|
16
|
+
|
|
17
|
+
struct container_data {
|
|
18
|
+
struct lxc_container *container;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static char **
|
|
22
|
+
ruby_to_c_string_array(VALUE rb_arr)
|
|
23
|
+
{
|
|
24
|
+
size_t i, len;
|
|
25
|
+
char **arr;
|
|
26
|
+
|
|
27
|
+
len = RARRAY_LEN(rb_arr);
|
|
28
|
+
arr = calloc(len + 1, sizeof(char *));
|
|
29
|
+
if (arr == NULL)
|
|
30
|
+
rb_raise(rb_eNoMemError, "unable to allocate array");
|
|
31
|
+
for (i = 0; i < len; i++) {
|
|
32
|
+
VALUE s = rb_ary_entry(rb_arr, i);
|
|
33
|
+
arr[i] = strdup(StringValuePtr(s));
|
|
34
|
+
}
|
|
35
|
+
arr[len] = NULL;
|
|
36
|
+
|
|
37
|
+
return arr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static void
|
|
41
|
+
free_c_string_array(char **arr)
|
|
42
|
+
{
|
|
43
|
+
size_t i;
|
|
44
|
+
for (i = 0; arr[i] != NULL; i++)
|
|
45
|
+
free(arr[i]);
|
|
46
|
+
free(arr);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
* Document-module: LXC
|
|
51
|
+
*
|
|
52
|
+
* This module provides a Ruby API allowing programmatic managing of
|
|
53
|
+
* "Linux Containers"[http://linuxcontainers.org/].
|
|
54
|
+
*
|
|
55
|
+
* The +LXC+ module contains generic methods (which are not related to
|
|
56
|
+
* a specific container instance) and methods related to +liblxc+. The
|
|
57
|
+
* container-specific methods are contained in the +LXC::Container+ class.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* call-seq:
|
|
62
|
+
* LXC.arch_to_personality(arch)
|
|
63
|
+
*
|
|
64
|
+
* Converts an architecture string (x86, i686, x86_64 or amd64) to a
|
|
65
|
+
* "personality", either +:linux32+ or +:linux+, for the 32-bit and 64-bit
|
|
66
|
+
* architectures, respectively.
|
|
67
|
+
*/
|
|
68
|
+
static VALUE
|
|
69
|
+
lxc_arch_to_personality(VALUE self, VALUE rb_arch)
|
|
70
|
+
{
|
|
71
|
+
int ret;
|
|
72
|
+
char *arch;
|
|
73
|
+
|
|
74
|
+
arch = StringValuePtr(rb_arch);
|
|
75
|
+
ret = lxc_config_parse_arch(arch);
|
|
76
|
+
|
|
77
|
+
switch (ret) {
|
|
78
|
+
case PER_LINUX32:
|
|
79
|
+
return SYMBOL("linux32");
|
|
80
|
+
case PER_LINUX:
|
|
81
|
+
return SYMBOL("linux");
|
|
82
|
+
default:
|
|
83
|
+
rb_raise(Error, "unknown personality");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* call-seq:
|
|
89
|
+
* LXC.run_command(command)
|
|
90
|
+
*
|
|
91
|
+
* Runs the given command (given as a string or as an argv array) in
|
|
92
|
+
* an attached container. Useful in conjunction with +LXC::Container#attach+.
|
|
93
|
+
*/
|
|
94
|
+
static VALUE
|
|
95
|
+
lxc_run_command(VALUE self, VALUE rb_command)
|
|
96
|
+
{
|
|
97
|
+
int ret;
|
|
98
|
+
lxc_attach_command_t cmd;
|
|
99
|
+
VALUE rb_program;
|
|
100
|
+
|
|
101
|
+
if (TYPE(rb_command) == T_STRING)
|
|
102
|
+
rb_command = rb_str_split(rb_command, " ");
|
|
103
|
+
|
|
104
|
+
rb_program = rb_ary_entry(rb_command, 0);
|
|
105
|
+
cmd.program = StringValuePtr(rb_program);
|
|
106
|
+
cmd.argv = ruby_to_c_string_array(rb_command);
|
|
107
|
+
|
|
108
|
+
ret = lxc_attach_run_command(&cmd);
|
|
109
|
+
if (ret == -1)
|
|
110
|
+
rb_raise(Error, "unable to run command on attached container");
|
|
111
|
+
/* NOTREACHED */
|
|
112
|
+
return Qnil;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/*
|
|
116
|
+
* call-seq:
|
|
117
|
+
* LXC.run_shell
|
|
118
|
+
*
|
|
119
|
+
* Runs a shell in an attached container. Useful in conjunction with
|
|
120
|
+
* +LXC::Container#attach+.
|
|
121
|
+
*/
|
|
122
|
+
static VALUE
|
|
123
|
+
lxc_run_shell(VALUE self)
|
|
124
|
+
{
|
|
125
|
+
int ret;
|
|
126
|
+
|
|
127
|
+
ret = lxc_attach_run_shell(NULL);
|
|
128
|
+
if (ret == -1)
|
|
129
|
+
rb_raise(Error, "unable to run shell on attached container");
|
|
130
|
+
/* NOTREACHED */
|
|
131
|
+
return Qnil;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* call-seq:
|
|
136
|
+
* LXC.global_config_item(key)
|
|
137
|
+
*
|
|
138
|
+
* Returns value for the given global config key.
|
|
139
|
+
*/
|
|
140
|
+
static VALUE
|
|
141
|
+
lxc_global_config_item(VALUE self, VALUE rb_key)
|
|
142
|
+
{
|
|
143
|
+
char *key;
|
|
144
|
+
const char *value;
|
|
145
|
+
key = StringValuePtr(rb_key);
|
|
146
|
+
value = lxc_get_global_config_item(key);
|
|
147
|
+
if (value == NULL)
|
|
148
|
+
rb_raise(Error, "invalid configuration key %s", key);
|
|
149
|
+
return rb_str_new2(value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* call-seq:
|
|
154
|
+
* LXC.version
|
|
155
|
+
*
|
|
156
|
+
* Returns the +liblxc+ version.
|
|
157
|
+
*/
|
|
158
|
+
static VALUE
|
|
159
|
+
lxc_version(VALUE self)
|
|
160
|
+
{
|
|
161
|
+
return rb_str_new2(lxc_get_version());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/*
|
|
165
|
+
* call-seq:
|
|
166
|
+
* LXC.list_containers([opts])
|
|
167
|
+
*
|
|
168
|
+
* Returns an array of containers. Which containers are returned depends on
|
|
169
|
+
* the options hash: by default, all containers are returned. One may list
|
|
170
|
+
* only active or defined containers by setting either the +:active+ or
|
|
171
|
+
* +:defined+ keys to +true+. The +:config_path+ key allows an alternate
|
|
172
|
+
* configuration path to be scanned when building the list.
|
|
173
|
+
*/
|
|
174
|
+
static VALUE
|
|
175
|
+
lxc_list_containers(int argc, VALUE *argv, VALUE self)
|
|
176
|
+
{
|
|
177
|
+
int i, num_containers;
|
|
178
|
+
int active, defined;
|
|
179
|
+
char *config;
|
|
180
|
+
char **names;
|
|
181
|
+
VALUE rb_active, rb_defined, rb_config;
|
|
182
|
+
VALUE rb_opts;
|
|
183
|
+
VALUE rb_containers;
|
|
184
|
+
|
|
185
|
+
rb_scan_args(argc, argv, "01", &rb_opts);
|
|
186
|
+
|
|
187
|
+
if (NIL_P(rb_opts)) {
|
|
188
|
+
active = 1;
|
|
189
|
+
defined = 1;
|
|
190
|
+
config = NULL;
|
|
191
|
+
} else {
|
|
192
|
+
Check_Type(rb_opts, T_HASH);
|
|
193
|
+
rb_active = rb_hash_aref(rb_opts, SYMBOL("active"));
|
|
194
|
+
active = (rb_active != Qnil) && (rb_active != Qfalse);
|
|
195
|
+
rb_defined = rb_hash_aref(rb_opts, SYMBOL("defined"));
|
|
196
|
+
defined = (rb_defined != Qnil) && (rb_defined != Qfalse);
|
|
197
|
+
rb_config = rb_hash_aref(rb_opts, SYMBOL("config_path"));
|
|
198
|
+
config = NIL_P(rb_config) ? NULL : StringValuePtr(rb_config);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
num_containers = 0;
|
|
202
|
+
if (active && defined)
|
|
203
|
+
num_containers = list_all_containers(config, &names, NULL);
|
|
204
|
+
else if (active)
|
|
205
|
+
num_containers = list_active_containers(config, &names, NULL);
|
|
206
|
+
else if (defined)
|
|
207
|
+
num_containers = list_defined_containers(config, &names, NULL);
|
|
208
|
+
if (num_containers < 0)
|
|
209
|
+
rb_raise(Error, "failure to list containers");
|
|
210
|
+
|
|
211
|
+
rb_containers = rb_ary_new2(num_containers);
|
|
212
|
+
/*
|
|
213
|
+
* The `names` array is not NULL-terminated, so free it manually,
|
|
214
|
+
* ie, don't use free_c_string_array().
|
|
215
|
+
*/
|
|
216
|
+
for (i = 0; i < num_containers; i++) {
|
|
217
|
+
rb_ary_store(rb_containers, i, rb_str_new2(names[i]));
|
|
218
|
+
free(names[i]);
|
|
219
|
+
}
|
|
220
|
+
free(names);
|
|
221
|
+
|
|
222
|
+
return rb_containers;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* Document-class: LXC::Container
|
|
228
|
+
*
|
|
229
|
+
* This class contains methods to manage Linux containers.
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
static void
|
|
233
|
+
container_free(void *data)
|
|
234
|
+
{
|
|
235
|
+
struct container_data *d = (struct container_data *)data;
|
|
236
|
+
lxc_container_put(d->container);
|
|
237
|
+
free(d);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static VALUE
|
|
241
|
+
container_alloc(VALUE klass)
|
|
242
|
+
{
|
|
243
|
+
struct container_data *data;
|
|
244
|
+
return Data_Make_Struct(klass, struct container_data, NULL,
|
|
245
|
+
container_free, data);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* call-seq:
|
|
250
|
+
* LXC::Container.new(name, config_path = LXC.global_config_item('lxc.lxcpath'))
|
|
251
|
+
*
|
|
252
|
+
* Creates a new container instance with the given name, under the given
|
|
253
|
+
* configuration path.
|
|
254
|
+
*/
|
|
255
|
+
static VALUE
|
|
256
|
+
container_initialize(int argc, VALUE *argv, VALUE self)
|
|
257
|
+
{
|
|
258
|
+
char *name, *config_path;
|
|
259
|
+
struct lxc_container *container;
|
|
260
|
+
struct container_data *data;
|
|
261
|
+
VALUE rb_name, rb_config_path;
|
|
262
|
+
|
|
263
|
+
rb_scan_args(argc, argv, "11", &rb_name, &rb_config_path);
|
|
264
|
+
|
|
265
|
+
name = StringValuePtr(rb_name);
|
|
266
|
+
config_path = NIL_P(rb_config_path) ? NULL : StringValuePtr(rb_config_path);
|
|
267
|
+
|
|
268
|
+
container = lxc_container_new(name, config_path);
|
|
269
|
+
if (container == NULL)
|
|
270
|
+
rb_raise(Error, "error creating container %s", name);
|
|
271
|
+
|
|
272
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
273
|
+
data->container = container;
|
|
274
|
+
|
|
275
|
+
return self;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
* call-seq:
|
|
280
|
+
* container.config_file_name
|
|
281
|
+
*
|
|
282
|
+
* Returns the name of the container's configuration file.
|
|
283
|
+
*/
|
|
284
|
+
static VALUE
|
|
285
|
+
container_config_file_name(VALUE self)
|
|
286
|
+
{
|
|
287
|
+
char *config_file_name;
|
|
288
|
+
struct container_data *data;
|
|
289
|
+
|
|
290
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
291
|
+
config_file_name = data->container->config_file_name(data->container);
|
|
292
|
+
|
|
293
|
+
return rb_str_new2(config_file_name);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static VALUE
|
|
297
|
+
container_controllable_p(VALUE self)
|
|
298
|
+
{
|
|
299
|
+
int controllable;
|
|
300
|
+
struct container_data *data;
|
|
301
|
+
|
|
302
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
303
|
+
controllable = data->container->may_control(data->container);
|
|
304
|
+
|
|
305
|
+
return controllable ? Qtrue : Qfalse;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static VALUE
|
|
309
|
+
container_defined_p(VALUE self)
|
|
310
|
+
{
|
|
311
|
+
int defined;
|
|
312
|
+
struct container_data *data;
|
|
313
|
+
|
|
314
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
315
|
+
defined = data->container->is_defined(data->container);
|
|
316
|
+
|
|
317
|
+
return defined ? Qtrue : Qfalse;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
* call-seq:
|
|
322
|
+
* container.init_pid
|
|
323
|
+
*
|
|
324
|
+
* Returns the PID of the container's +init+ process from the host's
|
|
325
|
+
* point of view.
|
|
326
|
+
*/
|
|
327
|
+
static VALUE
|
|
328
|
+
container_init_pid(VALUE self)
|
|
329
|
+
{
|
|
330
|
+
pid_t pid;
|
|
331
|
+
struct container_data *data;
|
|
332
|
+
|
|
333
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
334
|
+
pid = data->container->init_pid(data->container);
|
|
335
|
+
if (pid < 0)
|
|
336
|
+
return Qnil;
|
|
337
|
+
return INT2NUM(pid);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/*
|
|
341
|
+
* call-seq:
|
|
342
|
+
* container.name
|
|
343
|
+
*
|
|
344
|
+
* Returns the name of the container.
|
|
345
|
+
*/
|
|
346
|
+
static VALUE
|
|
347
|
+
container_name(VALUE self)
|
|
348
|
+
{
|
|
349
|
+
struct container_data *data;
|
|
350
|
+
|
|
351
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
352
|
+
|
|
353
|
+
return rb_str_new2(data->container->name);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
static VALUE
|
|
357
|
+
container_running_p(VALUE self)
|
|
358
|
+
{
|
|
359
|
+
int running;
|
|
360
|
+
struct container_data *data;
|
|
361
|
+
|
|
362
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
363
|
+
running = data->container->is_running(data->container);
|
|
364
|
+
|
|
365
|
+
return running ? Qtrue : Qfalse;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/*
|
|
369
|
+
* call-seq:
|
|
370
|
+
* container.state
|
|
371
|
+
*
|
|
372
|
+
* Returns a symbol representing the state of the container.
|
|
373
|
+
*/
|
|
374
|
+
static VALUE
|
|
375
|
+
container_state(VALUE self)
|
|
376
|
+
{
|
|
377
|
+
struct container_data *data;
|
|
378
|
+
VALUE rb_state;
|
|
379
|
+
|
|
380
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
381
|
+
rb_state = rb_str_new2(data->container->state(data->container));
|
|
382
|
+
|
|
383
|
+
return rb_str_intern(rb_funcall(rb_state, rb_intern("downcase"), 0));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/*
|
|
387
|
+
* call-seq:
|
|
388
|
+
* container.add_device_node(src_path, dst_path = src_path)
|
|
389
|
+
*
|
|
390
|
+
* Adds a device node to the container.
|
|
391
|
+
*/
|
|
392
|
+
static VALUE
|
|
393
|
+
container_add_device_node(int argc, VALUE *argv, VALUE self)
|
|
394
|
+
{
|
|
395
|
+
int ret;
|
|
396
|
+
char *src_path, *dst_path;
|
|
397
|
+
struct container_data *data;
|
|
398
|
+
VALUE rb_src_path, rb_dst_path;
|
|
399
|
+
|
|
400
|
+
rb_scan_args(argc, argv, "11", &rb_src_path, &rb_dst_path);
|
|
401
|
+
src_path = NIL_P(rb_src_path) ? NULL : StringValuePtr(rb_src_path);
|
|
402
|
+
dst_path = NIL_P(rb_dst_path) ? NULL : StringValuePtr(rb_dst_path);
|
|
403
|
+
|
|
404
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
405
|
+
|
|
406
|
+
ret = data->container->add_device_node(data->container, src_path, dst_path);
|
|
407
|
+
if (!ret)
|
|
408
|
+
rb_raise(Error, "unable to add device node");
|
|
409
|
+
|
|
410
|
+
return self;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
static VALUE
|
|
414
|
+
lxc_attach_exec_block_cb(VALUE block)
|
|
415
|
+
{
|
|
416
|
+
rb_funcall3(block, rb_intern("call"), 0, NULL);
|
|
417
|
+
return INT2FIX(0);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
static VALUE
|
|
421
|
+
lxc_attach_exec_rescue_cb()
|
|
422
|
+
{
|
|
423
|
+
return INT2FIX(1);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
static int
|
|
427
|
+
lxc_attach_exec(void *payload)
|
|
428
|
+
{
|
|
429
|
+
VALUE res = rb_rescue(lxc_attach_exec_block_cb, (VALUE)payload,
|
|
430
|
+
lxc_attach_exec_rescue_cb, (VALUE)NULL);
|
|
431
|
+
return FIX2INT(res);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static int
|
|
435
|
+
io_fileno(VALUE io)
|
|
436
|
+
{
|
|
437
|
+
return NUM2INT(rb_funcall(io, rb_intern("fileno"), 0));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
static int
|
|
441
|
+
is_integer(VALUE v)
|
|
442
|
+
{
|
|
443
|
+
return (TYPE(v) == T_FIXNUM || TYPE(v) == T_BIGNUM);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
static int
|
|
447
|
+
is_string(VALUE v)
|
|
448
|
+
{
|
|
449
|
+
return TYPE(v) == T_STRING;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
static int
|
|
453
|
+
is_string_array(VALUE v)
|
|
454
|
+
{
|
|
455
|
+
size_t i, len;
|
|
456
|
+
if (TYPE(v) != T_ARRAY)
|
|
457
|
+
return 0;
|
|
458
|
+
len = RARRAY_LEN(v);
|
|
459
|
+
for (i = 0; i < len; i++) {
|
|
460
|
+
if (TYPE(rb_ary_entry(v, i)) != T_STRING)
|
|
461
|
+
return 0;
|
|
462
|
+
}
|
|
463
|
+
return 1;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
static int
|
|
467
|
+
is_io(VALUE v)
|
|
468
|
+
{
|
|
469
|
+
return rb_respond_to(v, rb_intern("sysread")) &&
|
|
470
|
+
rb_respond_to(v, rb_intern("syswrite"));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
static void
|
|
474
|
+
lxc_attach_free_options(lxc_attach_options_t *opts)
|
|
475
|
+
{
|
|
476
|
+
if (!opts)
|
|
477
|
+
return;
|
|
478
|
+
if (opts->initial_cwd)
|
|
479
|
+
free(opts->initial_cwd);
|
|
480
|
+
if (opts->extra_env_vars)
|
|
481
|
+
free_c_string_array(opts->extra_env_vars);
|
|
482
|
+
if (opts->extra_keep_env)
|
|
483
|
+
free_c_string_array(opts->extra_keep_env);
|
|
484
|
+
free(opts);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
static lxc_attach_options_t *
|
|
488
|
+
lxc_attach_parse_options(VALUE rb_opts)
|
|
489
|
+
{
|
|
490
|
+
lxc_attach_options_t default_opts = LXC_ATTACH_OPTIONS_DEFAULT;
|
|
491
|
+
lxc_attach_options_t *opts;
|
|
492
|
+
VALUE rb_attach_flags, rb_namespaces, rb_personality, rb_initial_cwd;
|
|
493
|
+
VALUE rb_uid, rb_gid, rb_env_policy, rb_extra_env_vars, rb_extra_keep_env;
|
|
494
|
+
VALUE rb_stdin, rb_stdout, rb_stderr;
|
|
495
|
+
|
|
496
|
+
opts = malloc(sizeof(*opts));
|
|
497
|
+
if (opts == NULL)
|
|
498
|
+
rb_raise(rb_eNoMemError, "unable to allocate options");
|
|
499
|
+
memcpy(opts, &default_opts, sizeof(*opts));
|
|
500
|
+
|
|
501
|
+
if (NIL_P(rb_opts))
|
|
502
|
+
return opts;
|
|
503
|
+
|
|
504
|
+
rb_attach_flags = rb_hash_aref(rb_opts, SYMBOL("flags"));
|
|
505
|
+
if (!NIL_P(rb_attach_flags)) {
|
|
506
|
+
if (is_integer(rb_attach_flags))
|
|
507
|
+
opts->attach_flags = NUM2INT(rb_attach_flags);
|
|
508
|
+
else
|
|
509
|
+
goto err;
|
|
510
|
+
}
|
|
511
|
+
rb_namespaces = rb_hash_aref(rb_opts, SYMBOL("namespaces"));
|
|
512
|
+
if (!NIL_P(rb_namespaces)) {
|
|
513
|
+
if (is_integer(rb_namespaces))
|
|
514
|
+
opts->namespaces = NUM2INT(rb_namespaces);
|
|
515
|
+
else
|
|
516
|
+
goto err;
|
|
517
|
+
}
|
|
518
|
+
rb_personality = rb_hash_aref(rb_opts, SYMBOL("personality"));
|
|
519
|
+
if (!NIL_P(rb_personality)) {
|
|
520
|
+
if (is_integer(rb_personality))
|
|
521
|
+
opts->personality = NUM2INT(rb_personality);
|
|
522
|
+
else
|
|
523
|
+
goto err;
|
|
524
|
+
}
|
|
525
|
+
rb_initial_cwd = rb_hash_aref(rb_opts, SYMBOL("initial_cwd"));
|
|
526
|
+
if (!NIL_P(rb_initial_cwd)) {
|
|
527
|
+
if (is_string(rb_initial_cwd))
|
|
528
|
+
opts->initial_cwd = StringValuePtr(rb_initial_cwd);
|
|
529
|
+
else
|
|
530
|
+
goto err;
|
|
531
|
+
}
|
|
532
|
+
rb_uid = rb_hash_aref(rb_opts, SYMBOL("uid"));
|
|
533
|
+
if (!NIL_P(rb_uid)) {
|
|
534
|
+
if (is_integer(rb_uid))
|
|
535
|
+
opts->uid = NUM2INT(rb_uid);
|
|
536
|
+
else
|
|
537
|
+
goto err;
|
|
538
|
+
}
|
|
539
|
+
rb_gid = rb_hash_aref(rb_opts, SYMBOL("gid"));
|
|
540
|
+
if (!NIL_P(rb_gid)) {
|
|
541
|
+
if (is_integer(rb_gid))
|
|
542
|
+
opts->gid = NUM2INT(rb_gid);
|
|
543
|
+
else
|
|
544
|
+
goto err;
|
|
545
|
+
}
|
|
546
|
+
rb_env_policy = rb_hash_aref(rb_opts, SYMBOL("env_policy"));
|
|
547
|
+
if (!NIL_P(rb_env_policy)) {
|
|
548
|
+
if (is_integer(rb_env_policy))
|
|
549
|
+
opts->env_policy = NUM2INT(rb_env_policy);
|
|
550
|
+
else
|
|
551
|
+
goto err;
|
|
552
|
+
}
|
|
553
|
+
rb_extra_env_vars = rb_hash_aref(rb_opts, SYMBOL("extra_env_vars"));
|
|
554
|
+
if (!NIL_P(rb_extra_env_vars)) {
|
|
555
|
+
if (is_string_array(rb_extra_env_vars))
|
|
556
|
+
opts->extra_env_vars = ruby_to_c_string_array(rb_extra_env_vars);
|
|
557
|
+
else
|
|
558
|
+
goto err;
|
|
559
|
+
}
|
|
560
|
+
rb_extra_keep_env = rb_hash_aref(rb_opts, SYMBOL("extra_keep_env"));
|
|
561
|
+
if (!NIL_P(rb_extra_keep_env)) {
|
|
562
|
+
if (is_string_array(rb_extra_keep_env))
|
|
563
|
+
opts->extra_keep_env = ruby_to_c_string_array(rb_extra_keep_env);
|
|
564
|
+
else
|
|
565
|
+
goto err;
|
|
566
|
+
}
|
|
567
|
+
rb_stdin = rb_hash_aref(rb_opts, SYMBOL("stdin"));
|
|
568
|
+
if (!NIL_P(rb_stdin)) {
|
|
569
|
+
if (is_io(rb_stdin))
|
|
570
|
+
opts->stdin_fd = io_fileno(rb_stdin);
|
|
571
|
+
else
|
|
572
|
+
goto err;
|
|
573
|
+
}
|
|
574
|
+
rb_stdout = rb_hash_aref(rb_opts, SYMBOL("stdout"));
|
|
575
|
+
if (!NIL_P(rb_stdout)) {
|
|
576
|
+
if (is_io(rb_stdout))
|
|
577
|
+
opts->stdout_fd = io_fileno(rb_stdout);
|
|
578
|
+
else
|
|
579
|
+
goto err;
|
|
580
|
+
}
|
|
581
|
+
rb_stderr = rb_hash_aref(rb_opts, SYMBOL("stderr"));
|
|
582
|
+
if (!NIL_P(rb_stderr)) {
|
|
583
|
+
if (is_io(rb_stderr))
|
|
584
|
+
opts->stderr_fd = io_fileno(rb_stderr);
|
|
585
|
+
else
|
|
586
|
+
goto err;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return opts;
|
|
590
|
+
|
|
591
|
+
err:
|
|
592
|
+
lxc_attach_free_options(opts);
|
|
593
|
+
return NULL;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/*
|
|
597
|
+
* call-seq:
|
|
598
|
+
* container.attach(opts = {}, &block)
|
|
599
|
+
*
|
|
600
|
+
* Calls +block+ in the context of the attached container. The options may
|
|
601
|
+
* contain the following keys.
|
|
602
|
+
*
|
|
603
|
+
* * +:flags+
|
|
604
|
+
* * +:namespaces+
|
|
605
|
+
* * +:personality+
|
|
606
|
+
* * +:initial_cwd+
|
|
607
|
+
* * +:uid+
|
|
608
|
+
* * +:gid+
|
|
609
|
+
* * +:env_policy+
|
|
610
|
+
* * +:extra_env_vars+
|
|
611
|
+
* * +:extra_keep_env+
|
|
612
|
+
* * +:stdin+
|
|
613
|
+
* * +:stdout+
|
|
614
|
+
* * +:stderr+
|
|
615
|
+
* * +:wait+
|
|
616
|
+
*/
|
|
617
|
+
static VALUE
|
|
618
|
+
container_attach(int argc, VALUE *argv, VALUE self)
|
|
619
|
+
{
|
|
620
|
+
int wait;
|
|
621
|
+
long ret;
|
|
622
|
+
pid_t pid;
|
|
623
|
+
lxc_attach_options_t *opts;
|
|
624
|
+
struct container_data *data;
|
|
625
|
+
VALUE block, rb_opts;
|
|
626
|
+
|
|
627
|
+
if (!rb_block_given_p())
|
|
628
|
+
rb_raise(Error, "no block given");
|
|
629
|
+
block = rb_block_proc();
|
|
630
|
+
|
|
631
|
+
rb_scan_args(argc, argv, "01", &rb_opts);
|
|
632
|
+
|
|
633
|
+
wait = 0;
|
|
634
|
+
if (!NIL_P(rb_opts)) {
|
|
635
|
+
VALUE rb_wait;
|
|
636
|
+
Check_Type(rb_opts, T_HASH);
|
|
637
|
+
rb_wait = rb_hash_delete(rb_opts, SYMBOL("wait"));
|
|
638
|
+
if (rb_wait != Qnil && rb_wait != Qfalse)
|
|
639
|
+
wait = 1;
|
|
640
|
+
}
|
|
641
|
+
opts = lxc_attach_parse_options(rb_opts);
|
|
642
|
+
if (opts == NULL)
|
|
643
|
+
rb_raise(Error, "unable to parse attach options");
|
|
644
|
+
|
|
645
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
646
|
+
|
|
647
|
+
ret = data->container->attach(data->container, lxc_attach_exec,
|
|
648
|
+
(void *)block, opts, &pid);
|
|
649
|
+
if (ret < 0)
|
|
650
|
+
goto out;
|
|
651
|
+
|
|
652
|
+
if (wait) {
|
|
653
|
+
ret = lxc_wait_for_pid_status(pid);
|
|
654
|
+
/* handle case where attach fails */
|
|
655
|
+
if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255)
|
|
656
|
+
ret = -1;
|
|
657
|
+
} else {
|
|
658
|
+
ret = pid;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
out:
|
|
662
|
+
lxc_attach_free_options(opts);
|
|
663
|
+
return LONG2NUM(ret);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/*
|
|
667
|
+
* call-seq:
|
|
668
|
+
* container.clear_config
|
|
669
|
+
*
|
|
670
|
+
* Clears the container configuration.
|
|
671
|
+
*/
|
|
672
|
+
static VALUE
|
|
673
|
+
container_clear_config(VALUE self)
|
|
674
|
+
{
|
|
675
|
+
struct container_data *data;
|
|
676
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
677
|
+
data->container->clear_config(data->container);
|
|
678
|
+
return self;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/*
|
|
682
|
+
* call-seq:
|
|
683
|
+
* container.clear_config_item(key)
|
|
684
|
+
*
|
|
685
|
+
* Clears the container configuration item +key+.
|
|
686
|
+
*/
|
|
687
|
+
static VALUE
|
|
688
|
+
container_clear_config_item(VALUE self, VALUE rb_key)
|
|
689
|
+
{
|
|
690
|
+
int ret;
|
|
691
|
+
char *key;
|
|
692
|
+
struct container_data *data;
|
|
693
|
+
|
|
694
|
+
key = StringValuePtr(rb_key);
|
|
695
|
+
|
|
696
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
697
|
+
|
|
698
|
+
ret = data->container->clear_config_item(data->container, key);
|
|
699
|
+
if (!ret)
|
|
700
|
+
rb_raise(Error, "unable to clear config item %s", key);
|
|
701
|
+
|
|
702
|
+
return self;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/*
|
|
706
|
+
* call-seq:
|
|
707
|
+
* container.clone(clone_name, opts = {})
|
|
708
|
+
*
|
|
709
|
+
* Clones the container, returning a new one with the given name. The
|
|
710
|
+
* options hash may contain the following keys:
|
|
711
|
+
*
|
|
712
|
+
* * +:config_path+
|
|
713
|
+
* * +:flags+
|
|
714
|
+
* * +:bdev_type+
|
|
715
|
+
* * +:bdev_data+
|
|
716
|
+
* * +:new_size+
|
|
717
|
+
* * +:hook_args+
|
|
718
|
+
*/
|
|
719
|
+
static VALUE
|
|
720
|
+
container_clone(int argc, VALUE *argv, VALUE self)
|
|
721
|
+
{
|
|
722
|
+
int flags;
|
|
723
|
+
uint64_t new_size;
|
|
724
|
+
char *name, *config_path, *bdev_type, *bdev_data;
|
|
725
|
+
char **hook_args;
|
|
726
|
+
struct lxc_container *container, *new_container;
|
|
727
|
+
struct container_data *data;
|
|
728
|
+
VALUE rb_name, rb_opts;
|
|
729
|
+
VALUE rb_flags, rb_config_path, rb_bdev_type, rb_bdev_data;
|
|
730
|
+
VALUE rb_new_size, rb_hook_args;
|
|
731
|
+
VALUE rb_args[2];
|
|
732
|
+
|
|
733
|
+
rb_scan_args(argc, argv, "11", &rb_name, &rb_opts);
|
|
734
|
+
|
|
735
|
+
name = StringValuePtr(rb_name);
|
|
736
|
+
|
|
737
|
+
config_path = NULL;
|
|
738
|
+
flags = 0;
|
|
739
|
+
bdev_type = NULL;
|
|
740
|
+
bdev_data = NULL;
|
|
741
|
+
new_size = 0;
|
|
742
|
+
hook_args = NULL;
|
|
743
|
+
|
|
744
|
+
rb_config_path = Qnil;
|
|
745
|
+
|
|
746
|
+
if (!NIL_P(rb_opts)) {
|
|
747
|
+
Check_Type(rb_opts, T_HASH);
|
|
748
|
+
rb_config_path = rb_hash_aref(rb_opts, SYMBOL("config_path"));
|
|
749
|
+
if (!NIL_P(rb_config_path))
|
|
750
|
+
config_path = StringValuePtr(rb_config_path);
|
|
751
|
+
|
|
752
|
+
rb_flags = rb_hash_aref(rb_opts, SYMBOL("flags"));
|
|
753
|
+
if (!NIL_P(rb_flags))
|
|
754
|
+
flags = NUM2INT(rb_flags);
|
|
755
|
+
|
|
756
|
+
rb_bdev_type = rb_hash_aref(rb_opts, SYMBOL("bdev_type"));
|
|
757
|
+
if (!NIL_P(rb_bdev_type))
|
|
758
|
+
bdev_type = StringValuePtr(rb_bdev_type);
|
|
759
|
+
|
|
760
|
+
rb_bdev_data = rb_hash_aref(rb_opts, SYMBOL("bdev_data"));
|
|
761
|
+
if (!NIL_P(rb_bdev_data))
|
|
762
|
+
bdev_data = StringValuePtr(rb_bdev_data);
|
|
763
|
+
|
|
764
|
+
rb_new_size = rb_hash_aref(rb_opts, SYMBOL("new_size"));
|
|
765
|
+
if (!NIL_P(rb_bdev_data))
|
|
766
|
+
new_size = NUM2ULL(rb_new_size);
|
|
767
|
+
|
|
768
|
+
rb_hook_args = rb_hash_aref(rb_opts, SYMBOL("hook_args"));
|
|
769
|
+
if (!NIL_P(rb_hook_args))
|
|
770
|
+
hook_args = ruby_to_c_string_array(rb_hook_args);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
774
|
+
container = data->container;
|
|
775
|
+
|
|
776
|
+
new_container = container->clone(container, name, config_path,
|
|
777
|
+
flags, bdev_type, bdev_data, new_size,
|
|
778
|
+
hook_args);
|
|
779
|
+
|
|
780
|
+
if (hook_args)
|
|
781
|
+
free_c_string_array(hook_args);
|
|
782
|
+
|
|
783
|
+
if (new_container == NULL)
|
|
784
|
+
rb_raise(Error, "unable to clone container");
|
|
785
|
+
|
|
786
|
+
lxc_container_put(new_container);
|
|
787
|
+
|
|
788
|
+
rb_args[0] = rb_name;
|
|
789
|
+
rb_args[1] = rb_config_path;
|
|
790
|
+
return rb_class_new_instance(2, rb_args, Container);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/*
|
|
794
|
+
* call-seq:
|
|
795
|
+
* container.console(opts = {})
|
|
796
|
+
*
|
|
797
|
+
* Accesses the container's console. The options hash may contain the
|
|
798
|
+
* following keys.
|
|
799
|
+
*
|
|
800
|
+
* * +:tty_num+
|
|
801
|
+
* * +:stdin_fd+
|
|
802
|
+
* * +:stdout_fd+
|
|
803
|
+
* * +:stderr_fd+
|
|
804
|
+
* * +:escape+
|
|
805
|
+
*/
|
|
806
|
+
static VALUE
|
|
807
|
+
container_console(int argc, VALUE *argv, VALUE self)
|
|
808
|
+
{
|
|
809
|
+
int ret;
|
|
810
|
+
int tty_num = -1, stdin_fd = 0, stdout_fd = 1, stderr_fd = 2, escape = 1;
|
|
811
|
+
struct container_data *data;
|
|
812
|
+
struct lxc_container *container;
|
|
813
|
+
VALUE rb_opts;
|
|
814
|
+
|
|
815
|
+
rb_scan_args(argc, argv, "01", &rb_opts);
|
|
816
|
+
switch (TYPE(rb_opts)) {
|
|
817
|
+
case T_HASH:
|
|
818
|
+
tty_num = NUM2INT(rb_hash_aref(rb_opts, SYMBOL("tty_num")));
|
|
819
|
+
stdin_fd = NUM2INT(rb_hash_aref(rb_opts, SYMBOL("stdin_fd")));
|
|
820
|
+
stdout_fd = NUM2INT(rb_hash_aref(rb_opts, SYMBOL("stdout_fd")));
|
|
821
|
+
stderr_fd = NUM2INT(rb_hash_aref(rb_opts, SYMBOL("stderr_fd")));
|
|
822
|
+
escape = NUM2INT(rb_hash_aref(rb_opts, SYMBOL("escape")));
|
|
823
|
+
break;
|
|
824
|
+
default:
|
|
825
|
+
rb_raise(rb_eArgError, "options must be a hash");
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
829
|
+
container = data->container;
|
|
830
|
+
|
|
831
|
+
ret = container->console(container, tty_num, stdin_fd, stdout_fd, stderr_fd,
|
|
832
|
+
escape);
|
|
833
|
+
if (ret != 0)
|
|
834
|
+
rb_raise(Error, "unable to access container console");
|
|
835
|
+
|
|
836
|
+
return self;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/*
|
|
840
|
+
* call-seq:
|
|
841
|
+
* container.console_fd(tty_num = nil)
|
|
842
|
+
*
|
|
843
|
+
* Returns an IO object referring to the container's console file descriptor.
|
|
844
|
+
*/
|
|
845
|
+
static VALUE
|
|
846
|
+
container_console_fd(int argc, VALUE *argv, VALUE self)
|
|
847
|
+
{
|
|
848
|
+
int ret, tty_num, master_fd;
|
|
849
|
+
struct container_data *data;
|
|
850
|
+
VALUE rb_tty_num;
|
|
851
|
+
VALUE rb_io_args[1];
|
|
852
|
+
|
|
853
|
+
rb_scan_args(argc, argv, "01", &rb_tty_num);
|
|
854
|
+
tty_num = NIL_P(rb_tty_num) ? -1 : NUM2INT(rb_tty_num);
|
|
855
|
+
|
|
856
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
857
|
+
|
|
858
|
+
ret = data->container->console_getfd(data->container, &tty_num, &master_fd);
|
|
859
|
+
if (ret < 0)
|
|
860
|
+
rb_raise(Error, "unable to allocate tty");
|
|
861
|
+
|
|
862
|
+
rb_io_args[0] = INT2NUM(master_fd);
|
|
863
|
+
return rb_class_new_instance(1, rb_io_args, rb_cIO);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/*
|
|
867
|
+
* call-seq:
|
|
868
|
+
* container.create(template, bdevtype = nil, flags = 0, args = [])
|
|
869
|
+
*
|
|
870
|
+
* Creates a structure for the container according to the given template.
|
|
871
|
+
* This usually consists of downloading and installing a Linux distribution
|
|
872
|
+
* inside the container's rootfs.
|
|
873
|
+
*
|
|
874
|
+
* The +flags+ argument is an OR of +LXC_CREATE_*+ flags.
|
|
875
|
+
*/
|
|
876
|
+
static VALUE
|
|
877
|
+
container_create(int argc, VALUE *argv, VALUE self)
|
|
878
|
+
{
|
|
879
|
+
int ret, flags;
|
|
880
|
+
char *template;
|
|
881
|
+
char *bdevtype;
|
|
882
|
+
char **args = { NULL };
|
|
883
|
+
struct container_data *data;
|
|
884
|
+
struct lxc_container *container;
|
|
885
|
+
VALUE rb_template, rb_bdevtype, rb_flags, rb_args;
|
|
886
|
+
|
|
887
|
+
rb_scan_args(argc, argv, "13", &rb_template, &rb_bdevtype, &rb_flags, &rb_args);
|
|
888
|
+
|
|
889
|
+
template = StringValuePtr(rb_template);
|
|
890
|
+
bdevtype = NIL_P(rb_bdevtype) ? NULL : StringValuePtr(rb_bdevtype);
|
|
891
|
+
flags = NIL_P(rb_flags) ? 0 : NUM2INT(rb_flags);
|
|
892
|
+
if (!NIL_P(rb_args))
|
|
893
|
+
args = ruby_to_c_string_array(rb_args);
|
|
894
|
+
|
|
895
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
896
|
+
container = data->container;
|
|
897
|
+
ret = container->create(container, template, bdevtype, NULL, flags, args);
|
|
898
|
+
|
|
899
|
+
if (!NIL_P(rb_args))
|
|
900
|
+
free_c_string_array(args);
|
|
901
|
+
|
|
902
|
+
if (!ret)
|
|
903
|
+
rb_raise(Error, "unable to create container");
|
|
904
|
+
|
|
905
|
+
return self;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/*
|
|
909
|
+
* call-seq:
|
|
910
|
+
* container.destroy
|
|
911
|
+
*
|
|
912
|
+
* Destroys the container.
|
|
913
|
+
*/
|
|
914
|
+
static VALUE
|
|
915
|
+
container_destroy(VALUE self)
|
|
916
|
+
{
|
|
917
|
+
int ret;
|
|
918
|
+
struct container_data *data;
|
|
919
|
+
|
|
920
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
921
|
+
|
|
922
|
+
ret = data->container->destroy(data->container);
|
|
923
|
+
if (!ret)
|
|
924
|
+
rb_raise(Error, "unable to destroy container");
|
|
925
|
+
return self;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/*
|
|
929
|
+
* call-seq:
|
|
930
|
+
* container.freeze
|
|
931
|
+
*
|
|
932
|
+
* Freezes the container.
|
|
933
|
+
*/
|
|
934
|
+
static VALUE
|
|
935
|
+
container_freeze(VALUE self)
|
|
936
|
+
{
|
|
937
|
+
int ret;
|
|
938
|
+
struct container_data *data;
|
|
939
|
+
|
|
940
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
941
|
+
|
|
942
|
+
ret = data->container->freeze(data->container);
|
|
943
|
+
if (!ret)
|
|
944
|
+
rb_raise(Error, "unable to freeze container");
|
|
945
|
+
|
|
946
|
+
return self;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/*
|
|
950
|
+
* call-seq:
|
|
951
|
+
* container.cgroup_item(key)
|
|
952
|
+
*
|
|
953
|
+
* Returns the value corresponding to the given cgroup item configuration.
|
|
954
|
+
*/
|
|
955
|
+
static VALUE
|
|
956
|
+
container_cgroup_item(VALUE self, VALUE rb_key)
|
|
957
|
+
{
|
|
958
|
+
int len1, len2;
|
|
959
|
+
char *key, *value;
|
|
960
|
+
struct container_data *data;
|
|
961
|
+
struct lxc_container *container;
|
|
962
|
+
VALUE ret;
|
|
963
|
+
|
|
964
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
965
|
+
container = data->container;
|
|
966
|
+
|
|
967
|
+
key = StringValuePtr(rb_key);
|
|
968
|
+
len1 = container->get_cgroup_item(container, key, NULL, 0);
|
|
969
|
+
if (len1 < 0)
|
|
970
|
+
rb_raise(Error, "invalid cgroup entry for %s", key);
|
|
971
|
+
|
|
972
|
+
value = malloc(sizeof(char) * len1 + 1);
|
|
973
|
+
if (value == NULL)
|
|
974
|
+
rb_raise(rb_eNoMemError, "unable to allocate cgroup value");
|
|
975
|
+
|
|
976
|
+
len2 = container->get_cgroup_item(container, key, value, len1 + 1);
|
|
977
|
+
if (len1 != len2) {
|
|
978
|
+
free(value);
|
|
979
|
+
rb_raise(Error, "unable to read cgroup value");
|
|
980
|
+
}
|
|
981
|
+
ret = rb_str_new2(value);
|
|
982
|
+
free(value);
|
|
983
|
+
|
|
984
|
+
return ret;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/*
|
|
988
|
+
* call-seq:
|
|
989
|
+
* container.config_item(key)
|
|
990
|
+
*
|
|
991
|
+
* Returns the value corresponding to the given configuration item.
|
|
992
|
+
*/
|
|
993
|
+
static VALUE
|
|
994
|
+
container_config_item(VALUE self, VALUE rb_key)
|
|
995
|
+
{
|
|
996
|
+
int len1, len2;
|
|
997
|
+
char *key, *value;
|
|
998
|
+
struct container_data *data;
|
|
999
|
+
struct lxc_container *container;
|
|
1000
|
+
VALUE rb_config;
|
|
1001
|
+
|
|
1002
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1003
|
+
container = data->container;
|
|
1004
|
+
|
|
1005
|
+
key = StringValuePtr(rb_key);
|
|
1006
|
+
len1 = container->get_config_item(container, key, NULL, 0);
|
|
1007
|
+
if (len1 < 0)
|
|
1008
|
+
rb_raise(Error, "invalid configuration key: %s", key);
|
|
1009
|
+
|
|
1010
|
+
value = malloc(sizeof(char) * len1 + 1);
|
|
1011
|
+
if (value == NULL)
|
|
1012
|
+
rb_raise(rb_eNoMemError, "unable to allocate configuration value");
|
|
1013
|
+
|
|
1014
|
+
len2 = container->get_config_item(container, key, value, len1 + 1);
|
|
1015
|
+
if (len1 != len2) {
|
|
1016
|
+
free(value);
|
|
1017
|
+
rb_raise(Error, "unable to read configuration file");
|
|
1018
|
+
}
|
|
1019
|
+
rb_config = rb_str_new2(value);
|
|
1020
|
+
free(value);
|
|
1021
|
+
|
|
1022
|
+
/* Return a list in case of multiple lines */
|
|
1023
|
+
return value[len2-1] == '\n' ? rb_str_split(rb_config, "\n") : rb_config;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/*
|
|
1027
|
+
* call-seq:
|
|
1028
|
+
* container.config_path
|
|
1029
|
+
*
|
|
1030
|
+
* Returns the configuration path for the container.
|
|
1031
|
+
*/
|
|
1032
|
+
static VALUE
|
|
1033
|
+
container_config_path(VALUE self)
|
|
1034
|
+
{
|
|
1035
|
+
struct container_data *data;
|
|
1036
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1037
|
+
return rb_str_new2(data->container->get_config_path(data->container));
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/*
|
|
1041
|
+
* call-seq:
|
|
1042
|
+
* container.keys(key)
|
|
1043
|
+
*
|
|
1044
|
+
* Returns a list of valid sub-keys for the given configuration key.
|
|
1045
|
+
*/
|
|
1046
|
+
static VALUE
|
|
1047
|
+
container_keys(VALUE self, VALUE rb_key)
|
|
1048
|
+
{
|
|
1049
|
+
int len1, len2;
|
|
1050
|
+
char *key, *value;
|
|
1051
|
+
struct container_data *data;
|
|
1052
|
+
struct lxc_container *container;
|
|
1053
|
+
VALUE rb_keys;
|
|
1054
|
+
|
|
1055
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1056
|
+
container = data->container;
|
|
1057
|
+
|
|
1058
|
+
key = StringValuePtr(rb_key);
|
|
1059
|
+
len1 = container->get_keys(container, key, NULL, 0);
|
|
1060
|
+
if (len1 < 0)
|
|
1061
|
+
rb_raise(Error, "invalid configuration key: %s", key);
|
|
1062
|
+
|
|
1063
|
+
value = malloc(sizeof(char) * len1 + 1);
|
|
1064
|
+
if (value == NULL)
|
|
1065
|
+
rb_raise(rb_eNoMemError, "unable to allocate configuration value");
|
|
1066
|
+
|
|
1067
|
+
len2 = container->get_keys(container, key, value, len1 + 1);
|
|
1068
|
+
if (len1 != len2) {
|
|
1069
|
+
free(value);
|
|
1070
|
+
rb_raise(Error, "unable to read configuration keys");
|
|
1071
|
+
}
|
|
1072
|
+
rb_keys = rb_str_new2(value);
|
|
1073
|
+
free(value);
|
|
1074
|
+
|
|
1075
|
+
return value[len2-1] == '\n' ? rb_str_split(rb_keys, "\n") : rb_keys;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/*
|
|
1079
|
+
* call-seq:
|
|
1080
|
+
* container.interfaces
|
|
1081
|
+
*
|
|
1082
|
+
* Returns the list of network interfaces of the container.
|
|
1083
|
+
*/
|
|
1084
|
+
static VALUE
|
|
1085
|
+
container_interfaces(VALUE self)
|
|
1086
|
+
{
|
|
1087
|
+
int i, num_interfaces;
|
|
1088
|
+
char **interfaces;
|
|
1089
|
+
struct container_data *data;
|
|
1090
|
+
VALUE rb_interfaces;
|
|
1091
|
+
|
|
1092
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1093
|
+
|
|
1094
|
+
interfaces = data->container->get_interfaces(data->container);
|
|
1095
|
+
if (!interfaces)
|
|
1096
|
+
return rb_ary_new();
|
|
1097
|
+
|
|
1098
|
+
for (num_interfaces = 0; interfaces[num_interfaces]; num_interfaces++)
|
|
1099
|
+
;
|
|
1100
|
+
|
|
1101
|
+
rb_interfaces = rb_ary_new2(num_interfaces);
|
|
1102
|
+
for (i = 0; i < num_interfaces; i++)
|
|
1103
|
+
rb_ary_store(rb_interfaces, i, rb_str_new2(interfaces[i]));
|
|
1104
|
+
|
|
1105
|
+
free_c_string_array(interfaces);
|
|
1106
|
+
|
|
1107
|
+
return rb_interfaces;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/*
|
|
1111
|
+
* call-seq:
|
|
1112
|
+
* container.ip_addresses
|
|
1113
|
+
*
|
|
1114
|
+
* Returns the list of IP addresses of the container.
|
|
1115
|
+
*/
|
|
1116
|
+
static VALUE
|
|
1117
|
+
container_ips(int argc, VALUE *argv, VALUE self)
|
|
1118
|
+
{
|
|
1119
|
+
int i, num_ips, scope;
|
|
1120
|
+
char *interface, *family;
|
|
1121
|
+
char **ips;
|
|
1122
|
+
struct container_data *data;
|
|
1123
|
+
VALUE rb_ips, rb_interface, rb_family, rb_scope;
|
|
1124
|
+
|
|
1125
|
+
rb_scan_args(argc, argv, "03", &rb_interface, &rb_family, &rb_scope);
|
|
1126
|
+
interface = NIL_P(rb_interface) ? NULL : StringValuePtr(rb_interface);
|
|
1127
|
+
family = NIL_P(rb_family) ? NULL : StringValuePtr(rb_family);
|
|
1128
|
+
scope = NIL_P(rb_scope) ? 0 : NUM2INT(rb_scope);
|
|
1129
|
+
|
|
1130
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1131
|
+
|
|
1132
|
+
ips = data->container->get_ips(data->container, interface, family, scope);
|
|
1133
|
+
if (ips == NULL)
|
|
1134
|
+
return rb_ary_new();
|
|
1135
|
+
|
|
1136
|
+
for (num_ips = 0; ips[num_ips]; num_ips++)
|
|
1137
|
+
;
|
|
1138
|
+
|
|
1139
|
+
rb_ips = rb_ary_new2(num_ips);
|
|
1140
|
+
for (i = 0; i < num_ips; i++)
|
|
1141
|
+
rb_ary_store(rb_ips, i, rb_str_new2(ips[i]));
|
|
1142
|
+
|
|
1143
|
+
free_c_string_array(ips);
|
|
1144
|
+
|
|
1145
|
+
return rb_ips;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/*
|
|
1149
|
+
* call-seq:
|
|
1150
|
+
* container.load_config(config_path = nil)
|
|
1151
|
+
*
|
|
1152
|
+
* Loads the container's configuration.
|
|
1153
|
+
*/
|
|
1154
|
+
static VALUE
|
|
1155
|
+
container_load_config(int argc, VALUE *argv, VALUE self)
|
|
1156
|
+
{
|
|
1157
|
+
int ret;
|
|
1158
|
+
char *path;
|
|
1159
|
+
struct container_data *data;
|
|
1160
|
+
VALUE rb_path;
|
|
1161
|
+
|
|
1162
|
+
rb_scan_args(argc, argv, "01", &rb_path);
|
|
1163
|
+
path = NIL_P(rb_path) ? NULL : StringValuePtr(rb_path);
|
|
1164
|
+
|
|
1165
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1166
|
+
|
|
1167
|
+
ret = data->container->load_config(data->container, path);
|
|
1168
|
+
if (!ret)
|
|
1169
|
+
rb_raise(Error, "unable to load configuration file");
|
|
1170
|
+
|
|
1171
|
+
return self;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/*
|
|
1175
|
+
* call-seq:
|
|
1176
|
+
* container.reboot
|
|
1177
|
+
*
|
|
1178
|
+
* Reboots the container.
|
|
1179
|
+
*/
|
|
1180
|
+
static VALUE
|
|
1181
|
+
container_reboot(VALUE self)
|
|
1182
|
+
{
|
|
1183
|
+
int ret;
|
|
1184
|
+
struct container_data *data;
|
|
1185
|
+
|
|
1186
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1187
|
+
|
|
1188
|
+
ret = data->container->reboot(data->container);
|
|
1189
|
+
if (!ret)
|
|
1190
|
+
rb_raise(Error, "unable to reboot container");
|
|
1191
|
+
|
|
1192
|
+
return self;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/*
|
|
1196
|
+
* call-seq:
|
|
1197
|
+
* container.remove_device_node(src_path, dst_path = src_path)
|
|
1198
|
+
*
|
|
1199
|
+
* Removes a device node from the container.
|
|
1200
|
+
*/
|
|
1201
|
+
static VALUE
|
|
1202
|
+
container_remove_device_node(int argc, VALUE *argv, VALUE self)
|
|
1203
|
+
{
|
|
1204
|
+
int ret;
|
|
1205
|
+
char *src_path, *dst_path;
|
|
1206
|
+
struct lxc_container *container;
|
|
1207
|
+
struct container_data *data;
|
|
1208
|
+
VALUE rb_src_path, rb_dst_path;
|
|
1209
|
+
|
|
1210
|
+
rb_scan_args(argc, argv, "11", &rb_src_path, &rb_dst_path);
|
|
1211
|
+
src_path = StringValuePtr(rb_src_path);
|
|
1212
|
+
dst_path = NIL_P(rb_dst_path) ? NULL : StringValuePtr(rb_dst_path);
|
|
1213
|
+
|
|
1214
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1215
|
+
container = data->container;
|
|
1216
|
+
|
|
1217
|
+
ret = container->remove_device_node(container, src_path, dst_path);
|
|
1218
|
+
if (!ret)
|
|
1219
|
+
rb_raise(Error, "unable to remove device node");
|
|
1220
|
+
|
|
1221
|
+
return self;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/*
|
|
1225
|
+
* call-seq:
|
|
1226
|
+
* container.rename(new_name)
|
|
1227
|
+
*
|
|
1228
|
+
* Renames the container and returns a new +LXC::Container+ instance of
|
|
1229
|
+
* the container with the new name.
|
|
1230
|
+
*/
|
|
1231
|
+
static VALUE
|
|
1232
|
+
container_rename(VALUE self, VALUE rb_name)
|
|
1233
|
+
{
|
|
1234
|
+
int ret;
|
|
1235
|
+
char *name;
|
|
1236
|
+
struct container_data *data;
|
|
1237
|
+
VALUE rb_args[2];
|
|
1238
|
+
|
|
1239
|
+
name = StringValuePtr(rb_name);
|
|
1240
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1241
|
+
|
|
1242
|
+
ret = data->container->rename(data->container, name);
|
|
1243
|
+
if (!ret)
|
|
1244
|
+
rb_raise(Error, "unable to rename container");
|
|
1245
|
+
|
|
1246
|
+
rb_args[0] = rb_name;
|
|
1247
|
+
rb_args[1] = Qnil;
|
|
1248
|
+
return rb_class_new_instance(2, rb_args, Container);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/*
|
|
1252
|
+
* call-seq:
|
|
1253
|
+
* container.running_config_item(key)
|
|
1254
|
+
*
|
|
1255
|
+
* Returns the value corresponding to the given configuration item from a
|
|
1256
|
+
* running container.
|
|
1257
|
+
*/
|
|
1258
|
+
static VALUE
|
|
1259
|
+
container_running_config_item(VALUE self, VALUE rb_key)
|
|
1260
|
+
{
|
|
1261
|
+
char *key, *value;
|
|
1262
|
+
struct container_data *data;
|
|
1263
|
+
struct lxc_container *container;
|
|
1264
|
+
VALUE rb_value;
|
|
1265
|
+
|
|
1266
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1267
|
+
container = data->container;
|
|
1268
|
+
|
|
1269
|
+
key = StringValuePtr(rb_key);
|
|
1270
|
+
value = container->get_running_config_item(container, key);
|
|
1271
|
+
if (value == NULL)
|
|
1272
|
+
rb_raise(Error, "unable to read running configuration item: %s", key);
|
|
1273
|
+
|
|
1274
|
+
rb_value = rb_str_new2(value);
|
|
1275
|
+
free(value);
|
|
1276
|
+
|
|
1277
|
+
return rb_value;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
static VALUE
|
|
1281
|
+
container_save_config(int argc, VALUE *argv, VALUE self)
|
|
1282
|
+
{
|
|
1283
|
+
int ret;
|
|
1284
|
+
char *path;
|
|
1285
|
+
struct container_data *data;
|
|
1286
|
+
VALUE rb_path;
|
|
1287
|
+
|
|
1288
|
+
rb_scan_args(argc, argv, "01", &rb_path);
|
|
1289
|
+
path = NIL_P(rb_path) ? NULL : StringValuePtr(rb_path);
|
|
1290
|
+
|
|
1291
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1292
|
+
|
|
1293
|
+
ret = data->container->save_config(data->container, path);
|
|
1294
|
+
if (!ret)
|
|
1295
|
+
rb_raise(Error, "unable to save configuration file");
|
|
1296
|
+
|
|
1297
|
+
return self;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/*
|
|
1301
|
+
* call-seq:
|
|
1302
|
+
* container.set_cgroup_item(key, value)
|
|
1303
|
+
*
|
|
1304
|
+
* Sets the value of a cgroup configuration item.
|
|
1305
|
+
*/
|
|
1306
|
+
static VALUE
|
|
1307
|
+
container_set_cgroup_item(VALUE self, VALUE rb_key, VALUE rb_value)
|
|
1308
|
+
{
|
|
1309
|
+
int ret;
|
|
1310
|
+
char *key, *value;
|
|
1311
|
+
struct container_data *data;
|
|
1312
|
+
|
|
1313
|
+
key = StringValuePtr(rb_key);
|
|
1314
|
+
value = StringValuePtr(rb_value);
|
|
1315
|
+
|
|
1316
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1317
|
+
|
|
1318
|
+
ret = data->container->set_cgroup_item(data->container, key, value);
|
|
1319
|
+
if (!ret)
|
|
1320
|
+
rb_raise(Error, "unable to set cgroup item %s to %s", key, value);
|
|
1321
|
+
|
|
1322
|
+
return self;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/*
|
|
1326
|
+
* call-seq:
|
|
1327
|
+
* container.set_config_item(key, value)
|
|
1328
|
+
*
|
|
1329
|
+
* Sets the value of a configuration item.
|
|
1330
|
+
*/
|
|
1331
|
+
static VALUE
|
|
1332
|
+
container_set_config_item(VALUE self, VALUE rb_key, VALUE rb_value)
|
|
1333
|
+
{
|
|
1334
|
+
int ret;
|
|
1335
|
+
char *key, *value;
|
|
1336
|
+
struct container_data *data;
|
|
1337
|
+
|
|
1338
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1339
|
+
|
|
1340
|
+
key = StringValuePtr(rb_key);
|
|
1341
|
+
switch (TYPE(rb_value)) {
|
|
1342
|
+
case T_STRING: {
|
|
1343
|
+
value = StringValuePtr(rb_value);
|
|
1344
|
+
ret = data->container->set_config_item(data->container, key, value);
|
|
1345
|
+
if (!ret) {
|
|
1346
|
+
rb_raise(Error, "unable to set configuration item %s to %s",
|
|
1347
|
+
key, value);
|
|
1348
|
+
}
|
|
1349
|
+
return self;
|
|
1350
|
+
}
|
|
1351
|
+
case T_ARRAY: {
|
|
1352
|
+
size_t i;
|
|
1353
|
+
size_t len = RARRAY_LEN(rb_value);
|
|
1354
|
+
for (i = 0; i < len; i++) {
|
|
1355
|
+
VALUE rb_entry = rb_ary_entry(rb_value, i);
|
|
1356
|
+
char *entry = StringValuePtr(rb_entry);
|
|
1357
|
+
ret = data->container->set_config_item(data->container, key, entry);
|
|
1358
|
+
if (!ret) {
|
|
1359
|
+
rb_raise(Error, "unable to set configuration item %s to %s",
|
|
1360
|
+
key, entry);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return self;
|
|
1364
|
+
}
|
|
1365
|
+
default:
|
|
1366
|
+
rb_raise(Error, "configuration value must be either string or array");
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/*
|
|
1371
|
+
* call-seq:
|
|
1372
|
+
* container.config_path = path
|
|
1373
|
+
*
|
|
1374
|
+
* Sets the container configuration path.
|
|
1375
|
+
*/
|
|
1376
|
+
static VALUE
|
|
1377
|
+
container_set_config_path(VALUE self, VALUE rb_path)
|
|
1378
|
+
{
|
|
1379
|
+
int ret;
|
|
1380
|
+
char *path;
|
|
1381
|
+
struct container_data *data;
|
|
1382
|
+
|
|
1383
|
+
path = StringValuePtr(rb_path);
|
|
1384
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1385
|
+
|
|
1386
|
+
ret = data->container->set_config_path(data->container, path);
|
|
1387
|
+
if (!ret)
|
|
1388
|
+
rb_raise(Error, "unable to set configuration path to %s", path);
|
|
1389
|
+
|
|
1390
|
+
return self;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/*
|
|
1394
|
+
* call-seq:
|
|
1395
|
+
* container.shutdown(timeout = -1)
|
|
1396
|
+
*
|
|
1397
|
+
* Shuts down the container, optionally waiting for +timeout+ seconds. If
|
|
1398
|
+
* +timeout+ is +-1+, wait as long as necessary for the container to
|
|
1399
|
+
* shutdown.
|
|
1400
|
+
*/
|
|
1401
|
+
static VALUE
|
|
1402
|
+
container_shutdown(int argc, VALUE *argv, VALUE self)
|
|
1403
|
+
{
|
|
1404
|
+
int ret, timeout;
|
|
1405
|
+
struct container_data *data;
|
|
1406
|
+
VALUE rb_timeout;
|
|
1407
|
+
|
|
1408
|
+
rb_scan_args(argc, argv, "01", &rb_timeout);
|
|
1409
|
+
timeout = NIL_P(rb_timeout) ? -1 : NUM2INT(rb_timeout);
|
|
1410
|
+
|
|
1411
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1412
|
+
|
|
1413
|
+
ret = data->container->shutdown(data->container, timeout);
|
|
1414
|
+
if (!ret)
|
|
1415
|
+
rb_raise(Error, "unable to shutdown container");
|
|
1416
|
+
|
|
1417
|
+
return self;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/*
|
|
1421
|
+
* call-seq:
|
|
1422
|
+
* container.snapshot(path = nil)
|
|
1423
|
+
*
|
|
1424
|
+
* Creates a snapshot of the container. Returns the snapshot name.
|
|
1425
|
+
*/
|
|
1426
|
+
static VALUE
|
|
1427
|
+
container_snapshot(int argc, VALUE *argv, VALUE self)
|
|
1428
|
+
{
|
|
1429
|
+
int ret;
|
|
1430
|
+
char *path;
|
|
1431
|
+
char new_name[20];
|
|
1432
|
+
struct container_data *data;
|
|
1433
|
+
VALUE rb_path;
|
|
1434
|
+
|
|
1435
|
+
rb_scan_args(argc, argv, "01", &rb_path);
|
|
1436
|
+
path = NIL_P(rb_path) ? NULL : StringValuePtr(rb_path);
|
|
1437
|
+
|
|
1438
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1439
|
+
|
|
1440
|
+
ret = data->container->snapshot(data->container, path);
|
|
1441
|
+
if (ret < 0)
|
|
1442
|
+
rb_raise(Error, "unable to snapshot container");
|
|
1443
|
+
|
|
1444
|
+
ret = snprintf(new_name, 20, "snap%d", ret);
|
|
1445
|
+
if (ret < 0 || ret >= 20)
|
|
1446
|
+
rb_raise(Error, "unable to snapshot container");
|
|
1447
|
+
|
|
1448
|
+
return rb_str_new2(new_name);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/*
|
|
1452
|
+
* call-seq:
|
|
1453
|
+
* container.snapshot_destroy(name)
|
|
1454
|
+
*
|
|
1455
|
+
* Destroys the given snapshot.
|
|
1456
|
+
*/
|
|
1457
|
+
static VALUE
|
|
1458
|
+
container_snapshot_destroy(VALUE self, VALUE rb_name)
|
|
1459
|
+
{
|
|
1460
|
+
int ret;
|
|
1461
|
+
char *name;
|
|
1462
|
+
struct container_data *data;
|
|
1463
|
+
|
|
1464
|
+
name = StringValuePtr(rb_name);
|
|
1465
|
+
|
|
1466
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1467
|
+
|
|
1468
|
+
ret = data->container->snapshot_destroy(data->container, name);
|
|
1469
|
+
if (!ret)
|
|
1470
|
+
rb_raise(Error, "unable to destroy snapshot");
|
|
1471
|
+
|
|
1472
|
+
return self;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
/*
|
|
1476
|
+
* call-seq:
|
|
1477
|
+
* container.snapshot_list
|
|
1478
|
+
*
|
|
1479
|
+
* Returns a list of existing snapshots for the container.
|
|
1480
|
+
*/
|
|
1481
|
+
static VALUE
|
|
1482
|
+
container_snapshot_list(VALUE self)
|
|
1483
|
+
{
|
|
1484
|
+
int i, num_snapshots;
|
|
1485
|
+
struct lxc_snapshot *snapshots;
|
|
1486
|
+
struct container_data *data;
|
|
1487
|
+
VALUE rb_snapshots;
|
|
1488
|
+
|
|
1489
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1490
|
+
|
|
1491
|
+
num_snapshots = data->container->snapshot_list(data->container, &snapshots);
|
|
1492
|
+
if (num_snapshots < 0)
|
|
1493
|
+
rb_raise(Error, "unable to list snapshots");
|
|
1494
|
+
|
|
1495
|
+
rb_snapshots = rb_ary_new2(num_snapshots);
|
|
1496
|
+
for (i = 0; i < num_snapshots; i++) {
|
|
1497
|
+
VALUE attrs = rb_ary_new2(4);
|
|
1498
|
+
rb_ary_store(attrs, 0, rb_str_new2(snapshots[i].name));
|
|
1499
|
+
rb_ary_store(attrs, 1, rb_str_new2(snapshots[i].comment_pathname));
|
|
1500
|
+
rb_ary_store(attrs, 2, rb_str_new2(snapshots[i].timestamp));
|
|
1501
|
+
rb_ary_store(attrs, 3, rb_str_new2(snapshots[i].lxcpath));
|
|
1502
|
+
snapshots[i].free(&snapshots[i]);
|
|
1503
|
+
rb_ary_store(rb_snapshots, i, attrs);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
return rb_snapshots;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/*
|
|
1510
|
+
* call-seq:
|
|
1511
|
+
* container.snapshot_restore(name, new_name = nil)
|
|
1512
|
+
*
|
|
1513
|
+
* Restores the given snapshot.
|
|
1514
|
+
*/
|
|
1515
|
+
static VALUE
|
|
1516
|
+
container_snapshot_restore(int argc, VALUE *argv, VALUE self)
|
|
1517
|
+
{
|
|
1518
|
+
int ret;
|
|
1519
|
+
char *name, *new_name;
|
|
1520
|
+
struct container_data *data;
|
|
1521
|
+
VALUE rb_name, rb_new_name;
|
|
1522
|
+
|
|
1523
|
+
rb_scan_args(argc, argv, "11", &rb_name, &rb_new_name);
|
|
1524
|
+
name = StringValuePtr(rb_name);
|
|
1525
|
+
new_name = NIL_P(rb_new_name) ? NULL : StringValuePtr(rb_new_name);
|
|
1526
|
+
|
|
1527
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1528
|
+
|
|
1529
|
+
ret = data->container->snapshot_restore(data->container, name, new_name);
|
|
1530
|
+
if (!ret)
|
|
1531
|
+
rb_raise(Error, "unable to restore snapshot");
|
|
1532
|
+
|
|
1533
|
+
return self;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
/*
|
|
1537
|
+
* call-seq:
|
|
1538
|
+
* container.start(opts = {})
|
|
1539
|
+
*
|
|
1540
|
+
* Starts the container. The options hash may contain the following keys.
|
|
1541
|
+
*
|
|
1542
|
+
* * +:use_init+
|
|
1543
|
+
* * +:daemonize+
|
|
1544
|
+
* * +:close_fds+
|
|
1545
|
+
* * +:args+
|
|
1546
|
+
*/
|
|
1547
|
+
static VALUE
|
|
1548
|
+
container_start(int argc, VALUE *argv, VALUE self)
|
|
1549
|
+
{
|
|
1550
|
+
int ret, use_init, daemonize, close_fds;
|
|
1551
|
+
char **args;
|
|
1552
|
+
struct container_data *data;
|
|
1553
|
+
VALUE rb_use_init, rb_daemonize, rb_close_fds, rb_args, rb_opts;
|
|
1554
|
+
|
|
1555
|
+
use_init = 0;
|
|
1556
|
+
daemonize = 1;
|
|
1557
|
+
close_fds = 0;
|
|
1558
|
+
args = NULL;
|
|
1559
|
+
rb_args = Qnil;
|
|
1560
|
+
|
|
1561
|
+
rb_scan_args(argc, argv, "01", &rb_opts);
|
|
1562
|
+
if (!NIL_P(rb_opts)) {
|
|
1563
|
+
Check_Type(rb_opts, T_HASH);
|
|
1564
|
+
rb_use_init = rb_hash_aref(rb_opts, SYMBOL("use_init"));
|
|
1565
|
+
use_init = (rb_use_init != Qnil) && (rb_use_init != Qfalse);
|
|
1566
|
+
|
|
1567
|
+
rb_daemonize = rb_hash_aref(rb_opts, SYMBOL("daemonize"));
|
|
1568
|
+
daemonize = (rb_daemonize != Qnil) && (rb_daemonize != Qfalse);
|
|
1569
|
+
|
|
1570
|
+
rb_close_fds = rb_hash_aref(rb_opts, SYMBOL("close_fds"));
|
|
1571
|
+
close_fds = (rb_close_fds != Qnil) && (rb_close_fds != Qfalse);
|
|
1572
|
+
|
|
1573
|
+
rb_args = rb_hash_aref(rb_opts, SYMBOL("args"));
|
|
1574
|
+
args = NIL_P(rb_args) ? NULL : ruby_to_c_string_array(rb_args);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1578
|
+
|
|
1579
|
+
data->container->want_close_all_fds(data->container, close_fds);
|
|
1580
|
+
data->container->want_daemonize(data->container, daemonize);
|
|
1581
|
+
ret = data->container->start(data->container, use_init, args);
|
|
1582
|
+
|
|
1583
|
+
if (!NIL_P(rb_args))
|
|
1584
|
+
free_c_string_array(args);
|
|
1585
|
+
|
|
1586
|
+
if (!ret)
|
|
1587
|
+
rb_raise(Error, "unable to start container");
|
|
1588
|
+
|
|
1589
|
+
return self;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/*
|
|
1593
|
+
* call-seq:
|
|
1594
|
+
* container.stop
|
|
1595
|
+
*
|
|
1596
|
+
* Stops the container.
|
|
1597
|
+
*/
|
|
1598
|
+
static VALUE
|
|
1599
|
+
container_stop(VALUE self)
|
|
1600
|
+
{
|
|
1601
|
+
int ret;
|
|
1602
|
+
struct container_data *data;
|
|
1603
|
+
|
|
1604
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1605
|
+
|
|
1606
|
+
ret = data->container->stop(data->container);
|
|
1607
|
+
if (!ret)
|
|
1608
|
+
rb_raise(Error, "unable to stop container");
|
|
1609
|
+
|
|
1610
|
+
return self;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/*
|
|
1614
|
+
* call-seq:
|
|
1615
|
+
* container.unfreeze
|
|
1616
|
+
*
|
|
1617
|
+
* Thaws a frozen container.
|
|
1618
|
+
*/
|
|
1619
|
+
static VALUE
|
|
1620
|
+
container_unfreeze(VALUE self)
|
|
1621
|
+
{
|
|
1622
|
+
int ret;
|
|
1623
|
+
struct container_data *data;
|
|
1624
|
+
|
|
1625
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1626
|
+
|
|
1627
|
+
ret = data->container->unfreeze(data->container);
|
|
1628
|
+
if (!ret)
|
|
1629
|
+
rb_raise(Error, "unable to unfreeze container");
|
|
1630
|
+
|
|
1631
|
+
return self;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/*
|
|
1635
|
+
* call-seq:
|
|
1636
|
+
* container.wait(state, timeout = -1)
|
|
1637
|
+
*
|
|
1638
|
+
* Waits for +timeout+ seconds (or as long as necessary if +timeout+ is +-1+)
|
|
1639
|
+
* until the container's state becomes +state+.
|
|
1640
|
+
*/
|
|
1641
|
+
static VALUE
|
|
1642
|
+
container_wait(int argc, VALUE *argv, VALUE self)
|
|
1643
|
+
{
|
|
1644
|
+
int ret, timeout;
|
|
1645
|
+
char *state;
|
|
1646
|
+
struct container_data *data;
|
|
1647
|
+
VALUE rb_state_str, rb_state, rb_timeout;
|
|
1648
|
+
|
|
1649
|
+
rb_scan_args(argc, argv, "11", &rb_state, &rb_timeout);
|
|
1650
|
+
|
|
1651
|
+
rb_state_str = rb_funcall(rb_state, rb_intern("to_s"), 0);
|
|
1652
|
+
rb_state_str = rb_funcall(rb_state_str, rb_intern("upcase"), 0);
|
|
1653
|
+
state = StringValuePtr(rb_state_str);
|
|
1654
|
+
|
|
1655
|
+
timeout = NIL_P(rb_timeout) ? -1 : NUM2INT(rb_timeout);
|
|
1656
|
+
|
|
1657
|
+
Data_Get_Struct(self, struct container_data, data);
|
|
1658
|
+
|
|
1659
|
+
ret = data->container->wait(data->container, state, timeout);
|
|
1660
|
+
if (!ret)
|
|
1661
|
+
rb_raise(Error, "error waiting for container");
|
|
1662
|
+
|
|
1663
|
+
return self;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
void
|
|
1667
|
+
Init_lxc(void)
|
|
1668
|
+
{
|
|
1669
|
+
VALUE LXC = rb_define_module("LXC");
|
|
1670
|
+
|
|
1671
|
+
rb_define_singleton_method(LXC, "arch_to_personality",
|
|
1672
|
+
lxc_arch_to_personality, 1);
|
|
1673
|
+
rb_define_singleton_method(LXC, "run_command", lxc_run_command, 1);
|
|
1674
|
+
rb_define_singleton_method(LXC, "run_shell", lxc_run_shell, 0);
|
|
1675
|
+
rb_define_singleton_method(LXC, "global_config_item",
|
|
1676
|
+
lxc_global_config_item, 1);
|
|
1677
|
+
rb_define_singleton_method(LXC, "version", lxc_version, 0);
|
|
1678
|
+
rb_define_singleton_method(LXC, "list_containers", lxc_list_containers, -1);
|
|
1679
|
+
|
|
1680
|
+
Container = rb_define_class_under(LXC, "Container", rb_cObject);
|
|
1681
|
+
rb_define_alloc_func(Container, container_alloc);
|
|
1682
|
+
|
|
1683
|
+
rb_define_method(Container, "initialize", container_initialize, -1);
|
|
1684
|
+
|
|
1685
|
+
rb_define_method(Container, "config_file_name",
|
|
1686
|
+
container_config_file_name, 0);
|
|
1687
|
+
rb_define_method(Container, "controllable?", container_controllable_p, 0);
|
|
1688
|
+
rb_define_method(Container, "defined?", container_defined_p, 0);
|
|
1689
|
+
rb_define_method(Container, "init_pid", container_init_pid, 0);
|
|
1690
|
+
rb_define_method(Container, "name", container_name, 0);
|
|
1691
|
+
rb_define_method(Container, "running?", container_running_p, 0);
|
|
1692
|
+
rb_define_method(Container, "state", container_state, 0);
|
|
1693
|
+
|
|
1694
|
+
rb_define_method(Container, "add_device_node",
|
|
1695
|
+
container_add_device_node, -1);
|
|
1696
|
+
rb_define_method(Container, "attach", container_attach, -1);
|
|
1697
|
+
rb_define_method(Container, "clear_config", container_clear_config, -1);
|
|
1698
|
+
rb_define_method(Container, "clear_config_item",
|
|
1699
|
+
container_clear_config_item, 1);
|
|
1700
|
+
rb_define_method(Container, "clone", container_clone, -1);
|
|
1701
|
+
rb_define_method(Container, "console", container_console, -1);
|
|
1702
|
+
rb_define_method(Container, "console_fd", container_console_fd, -1);
|
|
1703
|
+
rb_define_method(Container, "create", container_create, -1);
|
|
1704
|
+
rb_define_method(Container, "destroy", container_destroy, 0);
|
|
1705
|
+
rb_define_method(Container, "freeze", container_freeze, 0);
|
|
1706
|
+
rb_define_method(Container, "cgroup_item", container_cgroup_item, 1);
|
|
1707
|
+
rb_define_method(Container, "config_item", container_config_item, 1);
|
|
1708
|
+
rb_define_method(Container, "config_path", container_config_path, 0);
|
|
1709
|
+
rb_define_method(Container, "keys", container_keys, 1);
|
|
1710
|
+
rb_define_method(Container, "interfaces", container_interfaces, 0);
|
|
1711
|
+
rb_define_method(Container, "ip_addresses", container_ips, -1);
|
|
1712
|
+
rb_define_method(Container, "load_config", container_load_config, -1);
|
|
1713
|
+
rb_define_method(Container, "reboot", container_reboot, 0);
|
|
1714
|
+
rb_define_method(Container, "remove_device_node",
|
|
1715
|
+
container_remove_device_node, 0);
|
|
1716
|
+
rb_define_method(Container, "rename", container_rename, 1);
|
|
1717
|
+
rb_define_method(Container, "running_config_item",
|
|
1718
|
+
container_running_config_item, 1);
|
|
1719
|
+
rb_define_method(Container, "save_config", container_save_config, -1);
|
|
1720
|
+
rb_define_method(Container, "set_cgroup_item",
|
|
1721
|
+
container_set_cgroup_item, 2);
|
|
1722
|
+
rb_define_method(Container, "set_config_item",
|
|
1723
|
+
container_set_config_item, 2);
|
|
1724
|
+
rb_define_method(Container, "config_path=", container_set_config_path, 1);
|
|
1725
|
+
rb_define_method(Container, "shutdown", container_shutdown, -1);
|
|
1726
|
+
rb_define_method(Container, "snapshot", container_snapshot, -1);
|
|
1727
|
+
rb_define_method(Container, "snapshot_destroy",
|
|
1728
|
+
container_snapshot_destroy, 1);
|
|
1729
|
+
rb_define_method(Container, "snapshot_list", container_snapshot_list, 0);
|
|
1730
|
+
rb_define_method(Container, "snapshot_restore",
|
|
1731
|
+
container_snapshot_restore, -1);
|
|
1732
|
+
rb_define_method(Container, "start", container_start, -1);
|
|
1733
|
+
rb_define_method(Container, "stop", container_stop, 0);
|
|
1734
|
+
rb_define_method(Container, "unfreeze", container_unfreeze, 0);
|
|
1735
|
+
rb_define_method(Container, "wait", container_wait, -1);
|
|
1736
|
+
|
|
1737
|
+
#define LXC_CONTAINER_CONST(c) rb_define_const(LXC, #c, LONG2NUM(c))
|
|
1738
|
+
|
|
1739
|
+
/* namespace flags */
|
|
1740
|
+
LXC_CONTAINER_CONST(CLONE_NEWUTS);
|
|
1741
|
+
LXC_CONTAINER_CONST(CLONE_NEWIPC);
|
|
1742
|
+
LXC_CONTAINER_CONST(CLONE_NEWUSER);
|
|
1743
|
+
LXC_CONTAINER_CONST(CLONE_NEWPID);
|
|
1744
|
+
LXC_CONTAINER_CONST(CLONE_NEWNET);
|
|
1745
|
+
LXC_CONTAINER_CONST(CLONE_NEWNS);
|
|
1746
|
+
|
|
1747
|
+
/* attach: environment variable handling */
|
|
1748
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_CLEAR_ENV);
|
|
1749
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_KEEP_ENV);
|
|
1750
|
+
|
|
1751
|
+
/* attach: attach options */
|
|
1752
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_DEFAULT);
|
|
1753
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_DROP_CAPABILITIES);
|
|
1754
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_LSM_EXEC);
|
|
1755
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_LSM_NOW);
|
|
1756
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_MOVE_TO_CGROUP);
|
|
1757
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_REMOUNT_PROC_SYS);
|
|
1758
|
+
LXC_CONTAINER_CONST(LXC_ATTACH_SET_PERSONALITY);
|
|
1759
|
+
|
|
1760
|
+
/* clone: clone flags */
|
|
1761
|
+
LXC_CONTAINER_CONST(LXC_CLONE_KEEPBDEVTYPE);
|
|
1762
|
+
LXC_CONTAINER_CONST(LXC_CLONE_KEEPMACADDR);
|
|
1763
|
+
LXC_CONTAINER_CONST(LXC_CLONE_KEEPNAME);
|
|
1764
|
+
LXC_CONTAINER_CONST(LXC_CLONE_MAYBE_SNAPSHOT);
|
|
1765
|
+
LXC_CONTAINER_CONST(LXC_CLONE_SNAPSHOT);
|
|
1766
|
+
|
|
1767
|
+
/* create: create flags */
|
|
1768
|
+
LXC_CONTAINER_CONST(LXC_CREATE_QUIET);
|
|
1769
|
+
|
|
1770
|
+
#undef LXC_CONTAINER_CONST
|
|
1771
|
+
|
|
1772
|
+
Error = rb_define_class_under(LXC, "Error", rb_eStandardError);
|
|
1773
|
+
}
|