]>
Commit | Line | Data |
---|---|---|
72d0e1cb SG |
1 | /* liblxcapi |
2 | * | |
3 | * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>. | |
4 | * Copyright © 2012 Canonical Ltd. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | */ | |
19 | ||
20 | #include "lxc.h" | |
21 | #include "state.h" | |
22 | #include "lxccontainer.h" | |
23 | #include "conf.h" | |
24 | #include "config.h" | |
25 | #include "confile.h" | |
26 | #include "cgroup.h" | |
27 | #include "commands.h" | |
28 | #include "log.h" | |
29 | #include <unistd.h> | |
30 | #include <sys/types.h> | |
31 | #include <sys/wait.h> | |
32 | #include <errno.h> | |
33 | ||
34 | lxc_log_define(lxc_container, lxc); | |
35 | ||
36 | /* LOCKING | |
37 | * c->privlock protects the struct lxc_container from multiple threads. | |
38 | * c->slock protects the on-disk container data | |
39 | * NOTHING mutexes two independent programs with their own struct | |
40 | * lxc_container for the same c->name, between API calls. For instance, | |
41 | * c->config_read(); c->start(); Between those calls, data on disk | |
42 | * could change (which shouldn't bother the caller unless for instance | |
43 | * the rootfs get moved). c->config_read(); update; c->config_write(); | |
44 | * Two such updaters could race. The callers should therefore check their | |
45 | * results. Trying to prevent that would necessarily expose us to deadlocks | |
46 | * due to hung callers. So I prefer to keep the locks only within our own | |
47 | * functions, not across functions. | |
48 | * | |
49 | * If you're going to fork while holding a lxccontainer, increment | |
50 | * c->numthreads (under privlock) before forking. When deleting, | |
51 | * decrement numthreads under privlock, then if it hits 0 you can delete. | |
52 | * Do not ever use a lxccontainer whose numthreads you did not bump. | |
53 | */ | |
54 | ||
55 | static void lxc_container_free(struct lxc_container *c) | |
56 | { | |
57 | if (!c) | |
58 | return; | |
59 | ||
60 | if (c->configfile) { | |
61 | free(c->configfile); | |
62 | c->configfile = NULL; | |
63 | } | |
64 | if (c->error_string) { | |
65 | free(c->error_string); | |
66 | c->error_string = NULL; | |
67 | } | |
68 | if (c->privlock) { | |
69 | sem_destroy(c->privlock); | |
70 | free(c->privlock); | |
71 | c->privlock = NULL; | |
72 | } | |
73 | if (c->name) { | |
74 | free(c->name); | |
75 | c->name = NULL; | |
76 | } | |
77 | /* | |
78 | * XXX TODO | |
79 | * note, c->lxc_conf is going to have to be freed, but the fn | |
80 | * to do that hasn't been written yet near as I can tell | |
81 | */ | |
82 | free(c); | |
83 | } | |
84 | ||
85 | int lxc_container_get(struct lxc_container *c) | |
86 | { | |
87 | if (!c) | |
88 | return 0; | |
89 | ||
90 | if (lxclock(c->privlock, 0)) | |
91 | return 0; | |
92 | if (c->numthreads < 1) { | |
93 | // bail without trying to unlock, bc the privlock is now probably | |
94 | // in freed memory | |
95 | return 0; | |
96 | } | |
97 | c->numthreads++; | |
98 | lxcunlock(c->privlock); | |
99 | return 1; | |
100 | } | |
101 | ||
102 | int lxc_container_put(struct lxc_container *c) | |
103 | { | |
104 | if (!c) | |
105 | return -1; | |
106 | if (lxclock(c->privlock, 0)) | |
107 | return -1; | |
108 | if (--c->numthreads < 1) { | |
109 | lxcunlock(c->privlock); | |
110 | lxc_container_free(c); | |
111 | return 1; | |
112 | } | |
113 | lxcunlock(c->privlock); | |
114 | return 0; | |
115 | } | |
116 | ||
117 | static bool file_exists(char *f) | |
118 | { | |
119 | struct stat statbuf; | |
120 | ||
121 | return stat(f, &statbuf) == 0; | |
122 | } | |
123 | ||
124 | static bool lxcapi_is_defined(struct lxc_container *c) | |
125 | { | |
126 | struct stat statbuf; | |
127 | bool ret = false; | |
128 | int statret; | |
129 | ||
130 | if (!c) | |
131 | return false; | |
132 | ||
133 | if (lxclock(c->privlock, 0)) | |
134 | return false; | |
135 | if (!c->configfile) | |
136 | goto out; | |
137 | statret = stat(c->configfile, &statbuf); | |
138 | if (statret != 0) | |
139 | goto out; | |
140 | ret = true; | |
141 | ||
142 | out: | |
143 | lxcunlock(c->privlock); | |
144 | return ret; | |
145 | } | |
146 | ||
147 | static const char *lxcapi_state(struct lxc_container *c) | |
148 | { | |
149 | const char *ret; | |
150 | lxc_state_t s; | |
151 | ||
152 | if (!c) | |
153 | return NULL; | |
154 | if (lxclock(c->slock, 0)) | |
155 | return NULL; | |
156 | s = lxc_getstate(c->name); | |
157 | ret = lxc_state2str(s); | |
158 | lxcunlock(c->slock); | |
159 | ||
160 | return ret; | |
161 | } | |
162 | ||
163 | static bool lxcapi_is_running(struct lxc_container *c) | |
164 | { | |
165 | const char *s; | |
166 | ||
167 | if (!c) | |
168 | return false; | |
169 | s = lxcapi_state(c); | |
170 | if (!s || strcmp(s, "STOPPED") == 0) | |
171 | return false; | |
172 | return true; | |
173 | } | |
174 | ||
175 | static bool lxcapi_freeze(struct lxc_container *c) | |
176 | { | |
177 | int ret; | |
178 | if (!c) | |
179 | return false; | |
180 | ||
181 | if (lxclock(c->slock, 0)) | |
182 | return false; | |
183 | ret = lxc_freeze(c->name); | |
184 | lxcunlock(c->slock); | |
185 | if (ret) | |
186 | return false; | |
187 | return true; | |
188 | } | |
189 | ||
190 | static bool lxcapi_unfreeze(struct lxc_container *c) | |
191 | { | |
192 | int ret; | |
193 | if (!c) | |
194 | return false; | |
195 | ||
196 | if (lxclock(c->slock, 0)) | |
197 | return false; | |
198 | ret = lxc_unfreeze(c->name); | |
199 | lxcunlock(c->slock); | |
200 | if (ret) | |
201 | return false; | |
202 | return true; | |
203 | } | |
204 | ||
205 | static pid_t lxcapi_init_pid(struct lxc_container *c) | |
206 | { | |
207 | pid_t ret; | |
208 | if (!c) | |
209 | return -1; | |
210 | ||
211 | if (lxclock(c->slock, 0)) | |
212 | return -1; | |
213 | ret = get_init_pid(c->name); | |
214 | lxcunlock(c->slock); | |
215 | return ret; | |
216 | } | |
217 | ||
8eb5694b SH |
218 | static bool load_config_locked(struct lxc_container *c, char *fname) |
219 | { | |
220 | if (!c->lxc_conf) | |
221 | c->lxc_conf = lxc_conf_init(); | |
222 | if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf)) | |
223 | return true; | |
224 | return false; | |
225 | } | |
226 | ||
72d0e1cb SG |
227 | static bool lxcapi_load_config(struct lxc_container *c, char *alt_file) |
228 | { | |
229 | bool ret = false; | |
230 | char *fname; | |
231 | if (!c) | |
232 | return false; | |
233 | ||
234 | fname = c->configfile; | |
235 | if (alt_file) | |
236 | fname = alt_file; | |
237 | if (!fname) | |
238 | return false; | |
239 | if (lxclock(c->slock, 0)) | |
240 | return false; | |
8eb5694b | 241 | ret = load_config_locked(c, fname); |
72d0e1cb SG |
242 | lxcunlock(c->slock); |
243 | return ret; | |
244 | } | |
245 | ||
246 | static void lxcapi_want_daemonize(struct lxc_container *c) | |
247 | { | |
248 | if (!c) | |
249 | return; | |
250 | c->daemonize = 1; | |
251 | } | |
252 | ||
7a44c8b4 SG |
253 | static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) |
254 | { | |
255 | int ret; | |
256 | ||
257 | if (!c) | |
258 | return false; | |
259 | ||
260 | ret = lxc_wait(c->name, state, timeout); | |
261 | return ret == 0; | |
262 | } | |
263 | ||
264 | ||
265 | static bool wait_on_daemonized_start(struct lxc_container *c) | |
266 | { | |
267 | /* we'll probably want to make this timeout configurable? */ | |
268 | int timeout = 5; | |
269 | ||
270 | return lxcapi_wait(c, "RUNNING", timeout); | |
271 | } | |
272 | ||
72d0e1cb SG |
273 | /* |
274 | * I can't decide if it'd be more convenient for callers if we accept '...', | |
275 | * or a null-terminated array (i.e. execl vs execv) | |
276 | */ | |
277 | static bool lxcapi_start(struct lxc_container *c, int useinit, char ** argv) | |
278 | { | |
279 | int ret; | |
280 | struct lxc_conf *conf; | |
281 | int daemonize = 0; | |
282 | char *default_args[] = { | |
283 | "/sbin/init", | |
284 | '\0', | |
285 | }; | |
286 | ||
287 | /* container exists */ | |
288 | if (!c) | |
289 | return false; | |
290 | /* container has been setup */ | |
291 | if (!c->lxc_conf) | |
292 | return false; | |
293 | ||
294 | /* is this app meant to be run through lxcinit, as in lxc-execute? */ | |
295 | if (useinit && !argv) | |
296 | return false; | |
297 | ||
298 | if (lxclock(c->privlock, 0)) | |
299 | return false; | |
300 | conf = c->lxc_conf; | |
301 | daemonize = c->daemonize; | |
302 | lxcunlock(c->privlock); | |
303 | ||
304 | if (useinit) { | |
305 | ret = lxc_execute(c->name, argv, 1, conf); | |
306 | return ret == 0 ? true : false; | |
307 | } | |
308 | ||
309 | if (!argv) | |
310 | argv = default_args; | |
311 | ||
312 | /* | |
313 | * say, I'm not sure - what locks do we want here? Any? | |
314 | * Is liblxc's locking enough here to protect the on disk | |
315 | * container? We don't want to exclude things like lxc_info | |
316 | * while container is running... | |
317 | */ | |
318 | if (daemonize) { | |
319 | if (!lxc_container_get(c)) | |
320 | return false; | |
321 | pid_t pid = fork(); | |
322 | if (pid < 0) { | |
323 | lxc_container_put(c); | |
324 | return false; | |
325 | } | |
326 | if (pid != 0) | |
7a44c8b4 | 327 | return wait_on_daemonized_start(c); |
72d0e1cb | 328 | /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ |
c278cef2 SH |
329 | if (chdir("/")) { |
330 | SYSERROR("Error chdir()ing to /."); | |
331 | return false; | |
332 | } | |
72d0e1cb SG |
333 | close(0); |
334 | close(1); | |
335 | close(2); | |
336 | open("/dev/null", O_RDONLY); | |
337 | open("/dev/null", O_RDWR); | |
338 | open("/dev/null", O_RDWR); | |
339 | setsid(); | |
340 | } | |
341 | ||
342 | if (putenv("container=lxc")) { | |
343 | fprintf(stderr, "failed to set environment variable"); | |
344 | if (daemonize) { | |
345 | lxc_container_put(c); | |
346 | exit(1); | |
347 | } else { | |
348 | return false; | |
349 | } | |
350 | } | |
351 | ||
352 | reboot: | |
353 | conf->reboot = 0; | |
354 | ret = lxc_start(c->name, argv, conf); | |
355 | ||
356 | if (conf->reboot) { | |
357 | INFO("container requested reboot"); | |
358 | conf->reboot = 0; | |
359 | if (conf->maincmd_fd) | |
360 | close(conf->maincmd_fd); | |
361 | conf->maincmd_fd = 0; | |
362 | goto reboot; | |
363 | } | |
364 | ||
365 | if (daemonize) { | |
366 | lxc_container_put(c); | |
367 | exit (ret == 0 ? true : false); | |
368 | } else { | |
369 | return (ret == 0 ? true : false); | |
370 | } | |
371 | } | |
372 | ||
373 | /* | |
374 | * note there MUST be an ending NULL | |
375 | */ | |
376 | static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) | |
377 | { | |
378 | va_list ap; | |
379 | char **inargs = NULL, **temp; | |
380 | int n_inargs = 0; | |
381 | bool bret = false; | |
382 | ||
383 | /* container exists */ | |
384 | if (!c) | |
385 | return false; | |
386 | ||
387 | /* build array of arguments if any */ | |
388 | va_start(ap, useinit); | |
389 | while (1) { | |
390 | char *arg; | |
391 | arg = va_arg(ap, char *); | |
392 | if (!arg) | |
393 | break; | |
394 | n_inargs++; | |
395 | temp = realloc(inargs, n_inargs * sizeof(*inargs)); | |
396 | if (!temp) | |
397 | goto out; | |
398 | inargs = temp; | |
399 | inargs[n_inargs - 1] = strdup(arg); // not sure if it's safe not to copy | |
400 | } | |
401 | va_end(ap); | |
402 | ||
403 | /* add trailing NULL */ | |
404 | if (n_inargs) { | |
405 | n_inargs++; | |
406 | temp = realloc(inargs, n_inargs * sizeof(*inargs)); | |
407 | if (!temp) | |
408 | goto out; | |
409 | inargs = temp; | |
410 | inargs[n_inargs - 1] = NULL; | |
411 | } | |
412 | ||
413 | bret = lxcapi_start(c, useinit, inargs); | |
414 | ||
415 | out: | |
416 | if (inargs) { | |
417 | int i; | |
418 | for (i = 0; i < n_inargs; i++) { | |
419 | if (inargs[i]) | |
420 | free(inargs[i]); | |
421 | } | |
422 | free(inargs); | |
423 | } | |
424 | ||
425 | return bret; | |
426 | } | |
427 | ||
428 | static bool lxcapi_stop(struct lxc_container *c) | |
429 | { | |
430 | int ret; | |
431 | ||
432 | if (!c) | |
433 | return false; | |
434 | ||
435 | ret = lxc_stop(c->name); | |
436 | ||
437 | return ret == 0; | |
72d0e1cb SG |
438 | } |
439 | ||
440 | static bool valid_template(char *t) | |
441 | { | |
442 | struct stat statbuf; | |
443 | int statret; | |
444 | ||
445 | statret = stat(t, &statbuf); | |
446 | if (statret == 0) | |
447 | return true; | |
448 | return false; | |
449 | } | |
450 | ||
451 | /* | |
452 | * create the standard expected container dir | |
453 | */ | |
454 | static bool create_container_dir(struct lxc_container *c) | |
455 | { | |
456 | char *s; | |
457 | int len, ret; | |
458 | ||
459 | len = strlen(LXCPATH) + strlen(c->name) + 2; | |
460 | s = malloc(len); | |
461 | if (!s) | |
462 | return false; | |
463 | ret = snprintf(s, len, "%s/%s", LXCPATH, c->name); | |
464 | if (ret < 0 || ret >= len) { | |
465 | free(s); | |
466 | return false; | |
467 | } | |
468 | ret = mkdir(s, 0755); | |
469 | if (ret) { | |
470 | if (errno == EEXIST) | |
471 | ret = 0; | |
472 | else | |
473 | SYSERROR("failed to create container path for %s\n", c->name); | |
474 | } | |
475 | free(s); | |
476 | return ret == 0; | |
477 | } | |
478 | ||
479 | /* | |
480 | * backing stores not (yet) supported | |
481 | * for ->create, argv contains the arguments to pass to the template, | |
482 | * terminated by NULL. If no arguments, you can just pass NULL. | |
483 | */ | |
484 | static bool lxcapi_create(struct lxc_container *c, char *t, char **argv) | |
485 | { | |
486 | bool bret = false; | |
487 | pid_t pid; | |
488 | int ret, status; | |
489 | char *tpath = NULL; | |
490 | int len, nargs = 0; | |
491 | char **newargv; | |
492 | ||
493 | if (!c) | |
494 | return false; | |
495 | ||
496 | len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1; | |
497 | tpath = malloc(len); | |
498 | if (!tpath) | |
499 | return false; | |
500 | ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t); | |
501 | if (ret < 0 || ret >= len) | |
502 | goto out; | |
503 | if (!valid_template(tpath)) { | |
504 | ERROR("bad template: %s\n", t); | |
505 | goto out; | |
506 | } | |
507 | ||
508 | if (!create_container_dir(c)) | |
509 | goto out; | |
510 | ||
511 | if (!c->save_config(c, NULL)) { | |
512 | ERROR("failed to save starting configuration for %s\n", c->name); | |
513 | goto out; | |
514 | } | |
515 | ||
516 | /* we're going to fork. but since we'll wait for our child, we | |
517 | don't need to lxc_container_get */ | |
518 | ||
519 | if (lxclock(c->slock, 0)) { | |
520 | ERROR("failed to grab global container lock for %s\n", c->name); | |
521 | goto out; | |
522 | } | |
523 | ||
524 | pid = fork(); | |
525 | if (pid < 0) { | |
526 | SYSERROR("failed to fork task for container creation template\n"); | |
527 | goto out_unlock; | |
528 | } | |
529 | ||
530 | if (pid == 0) { // child | |
531 | char *patharg, *namearg; | |
532 | int i; | |
533 | ||
534 | close(0); | |
535 | close(1); | |
536 | close(2); | |
537 | open("/dev/null", O_RDONLY); | |
538 | open("/dev/null", O_RDWR); | |
539 | open("/dev/null", O_RDWR); | |
540 | ||
541 | /* | |
542 | * create our new array, pre-pend the template name and | |
543 | * base args | |
544 | */ | |
545 | if (argv) | |
546 | for (; argv[nargs]; nargs++) ; | |
547 | nargs += 3; // template, path and name args | |
548 | newargv = malloc(nargs * sizeof(*newargv)); | |
549 | if (!newargv) | |
550 | exit(1); | |
551 | newargv[0] = t; | |
552 | ||
553 | len = strlen(LXCPATH) + strlen(c->name) + strlen("--path=") + 2; | |
554 | patharg = malloc(len); | |
555 | if (!patharg) | |
556 | exit(1); | |
557 | ret = snprintf(patharg, len, "--path=%s/%s", LXCPATH, c->name); | |
558 | if (ret < 0 || ret >= len) | |
559 | exit(1); | |
560 | newargv[1] = patharg; | |
561 | len = strlen("--name=") + strlen(c->name) + 1; | |
562 | namearg = malloc(len); | |
563 | if (!namearg) | |
564 | exit(1); | |
565 | ret = snprintf(namearg, len, "--name=%s", c->name); | |
566 | if (ret < 0 || ret >= len) | |
567 | exit(1); | |
568 | newargv[2] = namearg; | |
569 | ||
570 | /* add passed-in args */ | |
571 | if (argv) | |
572 | for (i = 3; i < nargs; i++) | |
573 | newargv[i] = argv[i-3]; | |
574 | ||
575 | /* add trailing NULL */ | |
576 | nargs++; | |
577 | newargv = realloc(newargv, nargs * sizeof(*newargv)); | |
578 | if (!newargv) | |
579 | exit(1); | |
580 | newargv[nargs - 1] = NULL; | |
581 | ||
582 | /* execute */ | |
583 | ret = execv(tpath, newargv); | |
584 | SYSERROR("failed to execute template %s", tpath); | |
585 | exit(1); | |
586 | } | |
587 | ||
588 | again: | |
589 | ret = waitpid(pid, &status, 0); | |
590 | if (ret == -1) { | |
591 | if (errno == -EINTR) | |
592 | goto again; | |
593 | SYSERROR("waitpid failed"); | |
594 | goto out_unlock; | |
595 | } | |
596 | if (ret != pid) | |
597 | goto again; | |
598 | if (!WIFEXITED(status)) { // did not exit normally | |
599 | // we could set an error code and string inside the | |
600 | // container_struct here if we like | |
601 | ERROR("container creation template exited abnormally\n"); | |
602 | goto out_unlock; | |
603 | } | |
604 | ||
8eb5694b | 605 | if (WEXITSTATUS(status) != 0) { |
72d0e1cb SG |
606 | ERROR("container creation template for %s exited with %d\n", |
607 | c->name, WEXITSTATUS(status)); | |
8eb5694b SH |
608 | goto out_unlock; |
609 | } | |
610 | ||
611 | // now clear out the lxc_conf we have, reload from the created | |
612 | // container | |
613 | if (c->lxc_conf) | |
614 | lxc_conf_free(c->lxc_conf); | |
615 | c->lxc_conf = NULL; | |
616 | bret = load_config_locked(c, c->configfile); | |
72d0e1cb SG |
617 | |
618 | out_unlock: | |
619 | lxcunlock(c->slock); | |
620 | out: | |
621 | if (tpath) | |
622 | free(tpath); | |
623 | return bret; | |
624 | } | |
625 | ||
626 | static bool lxcapi_shutdown(struct lxc_container *c, int timeout) | |
627 | { | |
628 | bool retv; | |
629 | pid_t pid; | |
630 | ||
631 | if (!c) | |
632 | return false; | |
633 | ||
634 | if (!timeout) | |
635 | timeout = -1; | |
636 | if (!c->is_running(c)) | |
637 | return true; | |
638 | pid = c->init_pid(c); | |
639 | if (pid <= 0) | |
640 | return true; | |
641 | kill(pid, SIGPWR); | |
642 | retv = c->wait(c, "STOPPED", timeout); | |
643 | if (timeout > 0) { | |
644 | c->stop(c); | |
645 | retv = c->wait(c, "STOPPED", 0); // 0 means don't wait | |
646 | } | |
647 | return retv; | |
648 | } | |
649 | ||
650 | static bool lxcapi_createl(struct lxc_container *c, char *t, ...) | |
651 | { | |
652 | bool bret = false; | |
653 | char **args = NULL, **temp; | |
654 | va_list ap; | |
655 | int nargs = 0; | |
656 | ||
657 | if (!c) | |
658 | return false; | |
659 | ||
660 | /* | |
661 | * since we're going to wait for create to finish, I don't think we | |
662 | * need to get a copy of the arguments. | |
663 | */ | |
664 | va_start(ap, t); | |
665 | while (1) { | |
666 | char *arg; | |
667 | arg = va_arg(ap, char *); | |
668 | if (!arg) | |
669 | break; | |
670 | nargs++; | |
671 | temp = realloc(args, nargs * sizeof(*args)); | |
672 | if (!temp) | |
673 | goto out; | |
674 | args = temp; | |
675 | args[nargs - 1] = arg; | |
676 | } | |
677 | va_end(ap); | |
678 | ||
679 | bret = c->create(c, t, args); | |
680 | ||
681 | out: | |
682 | if (args) | |
683 | free(args); | |
684 | return bret; | |
685 | } | |
686 | ||
687 | static bool lxcapi_clear_config_item(struct lxc_container *c, char *key) | |
688 | { | |
689 | int ret; | |
690 | ||
691 | if (!c || !c->lxc_conf) | |
692 | return false; | |
693 | if (lxclock(c->privlock, 0)) { | |
694 | return false; | |
695 | } | |
696 | ret = lxc_clear_config_item(c->lxc_conf, key); | |
697 | lxcunlock(c->privlock); | |
698 | return ret == 0; | |
699 | } | |
700 | ||
701 | static int lxcapi_get_config_item(struct lxc_container *c, char *key, char *retv, int inlen) | |
702 | { | |
703 | int ret; | |
704 | ||
705 | if (!c || !c->lxc_conf) | |
706 | return -1; | |
707 | if (lxclock(c->privlock, 0)) { | |
708 | return -1; | |
709 | } | |
710 | ret = lxc_get_config_item(c->lxc_conf, key, retv, inlen); | |
711 | lxcunlock(c->privlock); | |
712 | return ret; | |
713 | } | |
714 | ||
715 | static int lxcapi_get_keys(struct lxc_container *c, char *key, char *retv, int inlen) | |
716 | { | |
717 | if (!key) | |
718 | return lxc_listconfigs(retv, inlen); | |
719 | /* | |
720 | * Support 'lxc.network.<idx>', i.e. 'lxc.network.0' | |
721 | * This is an intelligent result to show which keys are valid given | |
722 | * the type of nic it is | |
723 | */ | |
724 | if (!c || !c->lxc_conf) | |
725 | return -1; | |
726 | if (lxclock(c->privlock, 0)) | |
727 | return -1; | |
728 | int ret = -1; | |
729 | if (strncmp(key, "lxc.network.", 12) == 0) | |
730 | ret = lxc_list_nicconfigs(c->lxc_conf, key, retv, inlen); | |
731 | lxcunlock(c->privlock); | |
732 | return ret; | |
733 | } | |
734 | ||
735 | ||
736 | /* default config file - should probably come through autoconf */ | |
737 | #define LXC_DEFAULT_CONFIG "/etc/lxc/lxc.conf" | |
738 | static bool lxcapi_save_config(struct lxc_container *c, char *alt_file) | |
739 | { | |
740 | if (!alt_file) | |
741 | alt_file = c->configfile; | |
742 | if (!alt_file) | |
743 | return false; // should we write to stdout if no file is specified? | |
744 | if (!c->lxc_conf) | |
745 | if (!c->load_config(c, LXC_DEFAULT_CONFIG)) { | |
746 | ERROR("Error loading default configuration file %s while saving %s\n", LXC_DEFAULT_CONFIG, c->name); | |
747 | return false; | |
748 | } | |
749 | ||
750 | FILE *fout = fopen(alt_file, "w"); | |
751 | if (!fout) | |
752 | return false; | |
753 | if (lxclock(c->privlock, 0)) { | |
754 | fclose(fout); | |
755 | return false; | |
756 | } | |
757 | write_config(fout, c->lxc_conf); | |
758 | fclose(fout); | |
759 | lxcunlock(c->privlock); | |
760 | return true; | |
761 | } | |
762 | ||
763 | static bool lxcapi_destroy(struct lxc_container *c) | |
764 | { | |
765 | pid_t pid; | |
766 | int ret, status; | |
767 | ||
768 | if (!c) | |
769 | return false; | |
770 | ||
771 | pid = fork(); | |
772 | if (pid < 0) | |
773 | return false; | |
774 | if (pid == 0) { // child | |
775 | ret = execlp("lxc-destroy", "lxc-destroy", "-n", c->name, NULL); | |
776 | perror("execl"); | |
777 | exit(1); | |
778 | } | |
779 | ||
780 | again: | |
781 | ret = waitpid(pid, &status, 0); | |
782 | if (ret == -1) { | |
783 | if (errno == -EINTR) | |
784 | goto again; | |
785 | perror("waitpid"); | |
786 | return false; | |
787 | } | |
788 | if (ret != pid) | |
789 | goto again; | |
790 | if (!WIFEXITED(status)) { // did not exit normally | |
791 | // we could set an error code and string inside the | |
792 | // container_struct here if we like | |
793 | return false; | |
794 | } | |
795 | ||
796 | return WEXITSTATUS(status) == 0; | |
797 | } | |
798 | ||
799 | static bool lxcapi_set_config_item(struct lxc_container *c, char *key, char *v) | |
800 | { | |
801 | int ret; | |
802 | bool b = false; | |
803 | struct lxc_config_t *config; | |
804 | ||
805 | if (!c) | |
806 | return false; | |
807 | ||
808 | if (lxclock(c->privlock, 0)) | |
809 | return false; | |
810 | ||
811 | if (!c->lxc_conf) | |
812 | c->lxc_conf = lxc_conf_init(); | |
813 | if (!c->lxc_conf) | |
814 | goto err; | |
815 | config = lxc_getconfig(key); | |
816 | if (!config) | |
817 | goto err; | |
818 | ret = config->cb(key, v, c->lxc_conf); | |
819 | if (!ret) | |
820 | b = true; | |
821 | ||
822 | err: | |
823 | lxcunlock(c->privlock); | |
824 | return b; | |
825 | } | |
826 | ||
827 | static char *lxcapi_config_file_name(struct lxc_container *c) | |
828 | { | |
829 | if (!c || !c->configfile) | |
830 | return NULL; | |
831 | return strdup(c->configfile); | |
832 | } | |
833 | ||
834 | struct lxc_container *lxc_container_new(char *name) | |
835 | { | |
836 | struct lxc_container *c; | |
837 | int ret, len; | |
838 | ||
839 | c = malloc(sizeof(*c)); | |
840 | if (!c) { | |
841 | fprintf(stderr, "failed to malloc lxc_container\n"); | |
842 | return NULL; | |
843 | } | |
844 | memset(c, 0, sizeof(*c)); | |
845 | ||
846 | c->name = malloc(strlen(name)+1); | |
847 | if (!c->name) { | |
848 | fprintf(stderr, "Error allocating lxc_container name\n"); | |
849 | goto err; | |
850 | } | |
851 | strcpy(c->name, name); | |
852 | ||
853 | c->numthreads = 1; | |
854 | c->slock = lxc_newlock(name); | |
855 | if (!c->slock) { | |
856 | fprintf(stderr, "failed to create lock\n"); | |
857 | goto err; | |
858 | } | |
859 | ||
860 | c->privlock = lxc_newlock(NULL); | |
861 | if (!c->privlock) { | |
862 | fprintf(stderr, "failed to alloc privlock\n"); | |
863 | goto err; | |
864 | } | |
865 | ||
866 | len = strlen(LXCDIR)+strlen(c->name)+strlen("/config")+2; | |
867 | c->configfile = malloc(len); | |
868 | if (!c->configfile) { | |
869 | fprintf(stderr, "Error allocating config file pathname\n"); | |
870 | goto err; | |
871 | } | |
872 | ret = snprintf(c->configfile, len, "%s/%s/config", LXCDIR, c->name); | |
873 | if (ret < 0 || ret >= len) { | |
874 | fprintf(stderr, "Error printing out config file name\n"); | |
875 | goto err; | |
876 | } | |
877 | ||
878 | if (file_exists(c->configfile)) | |
879 | lxcapi_load_config(c, NULL); | |
880 | ||
881 | // assign the member functions | |
882 | c->is_defined = lxcapi_is_defined; | |
883 | c->state = lxcapi_state; | |
884 | c->is_running = lxcapi_is_running; | |
885 | c->freeze = lxcapi_freeze; | |
886 | c->unfreeze = lxcapi_unfreeze; | |
887 | c->init_pid = lxcapi_init_pid; | |
888 | c->load_config = lxcapi_load_config; | |
889 | c->want_daemonize = lxcapi_want_daemonize; | |
890 | c->start = lxcapi_start; | |
891 | c->startl = lxcapi_startl; | |
892 | c->stop = lxcapi_stop; | |
893 | c->config_file_name = lxcapi_config_file_name; | |
894 | c->wait = lxcapi_wait; | |
895 | c->set_config_item = lxcapi_set_config_item; | |
896 | c->destroy = lxcapi_destroy; | |
897 | c->save_config = lxcapi_save_config; | |
898 | c->get_keys = lxcapi_get_keys; | |
899 | c->create = lxcapi_create; | |
900 | c->createl = lxcapi_createl; | |
901 | c->shutdown = lxcapi_shutdown; | |
902 | c->clear_config_item = lxcapi_clear_config_item; | |
903 | c->get_config_item = lxcapi_get_config_item; | |
904 | ||
905 | /* we'll allow the caller to update these later */ | |
906 | if (lxc_log_init("/var/log/lxccontainer.log", "trace", "lxc_container", 0)) { | |
907 | fprintf(stderr, "failed to open log\n"); | |
908 | goto err; | |
909 | } | |
910 | ||
911 | /* | |
912 | * default configuration file is $LXCDIR/$NAME/config | |
913 | */ | |
914 | ||
915 | return c; | |
916 | ||
917 | err: | |
918 | lxc_container_free(c); | |
919 | return NULL; | |
920 | } | |
921 | ||
4a7c7daa | 922 | int lxc_get_wait_states(const char **states) |
72d0e1cb SG |
923 | { |
924 | int i; | |
925 | ||
926 | if (states) | |
927 | for (i=0; i<MAX_STATE; i++) | |
928 | states[i] = lxc_state2str(i); | |
929 | return MAX_STATE; | |
930 | } |