ruby-lxc 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }