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.
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ abort 'missing liblxc' unless find_library('lxc', 'lxc_container_new')
4
+ abort 'missing lxc/lxccontainer.h' unless have_header('lxc/lxccontainer.h')
5
+
6
+ $CFLAGS += " -Wall #{ENV['CFLAGS']}"
7
+ create_makefile('lxc/lxc')
@@ -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
+ }