]>
Commit | Line | Data |
---|---|---|
0ad19a3f | 1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <dlezcano at fr.ibm.com> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
23 | #define _GNU_SOURCE | |
24 | #include <stdio.h> | |
25 | #undef _GNU_SOURCE | |
26 | #include <stdlib.h> | |
27 | #include <errno.h> | |
28 | #include <string.h> | |
29 | #include <dirent.h> | |
30 | #include <mntent.h> | |
31 | #include <unistd.h> | |
b0a33c1e | 32 | #include <pty.h> |
0ad19a3f | 33 | |
34 | #include <sys/types.h> | |
35 | #include <sys/utsname.h> | |
36 | #include <sys/param.h> | |
37 | #include <sys/stat.h> | |
38 | #include <sys/socket.h> | |
39 | #include <sys/mount.h> | |
40 | #include <sys/mman.h> | |
41 | ||
42 | #include <arpa/inet.h> | |
43 | #include <fcntl.h> | |
44 | #include <netinet/in.h> | |
45 | #include <net/if.h> | |
6f4a3756 | 46 | #include <libgen.h> |
0ad19a3f | 47 | |
e5bda9ee | 48 | #include "network.h" |
49 | #include "error.h" | |
50 | ||
b113348e | 51 | #include <lxc/lxc.h> |
e5bda9ee | 52 | |
0ad19a3f | 53 | |
54 | #define MAXHWLEN 18 | |
55 | #define MAXINDEXLEN 20 | |
56 | #define MAXLINELEN 128 | |
57 | ||
6f4a3756 | 58 | typedef int (*instanciate_cb)(const char *directory, |
0ad19a3f | 59 | const char *file, pid_t pid); |
60 | ||
6f4a3756 | 61 | typedef int (*dir_cb)(const char *name, const char *directory, |
0ad19a3f | 62 | const char *file, void *data); |
63 | ||
64 | typedef int (*file_cb)(void* buffer, void *data); | |
65 | ||
66 | struct netdev_conf { | |
67 | const char *type; | |
68 | instanciate_cb cb; | |
69 | int count; | |
70 | }; | |
71 | ||
72 | static int instanciate_veth(const char *, const char *, pid_t); | |
73 | static int instanciate_macvlan(const char *, const char *, pid_t); | |
74 | static int instanciate_phys(const char *, const char *, pid_t); | |
75 | static int instanciate_empty(const char *, const char *, pid_t); | |
6f4a3756 | 76 | static int unconfigure_cgroup(const char *name); |
0ad19a3f | 77 | |
78 | static struct netdev_conf netdev_conf[MAXCONFTYPE + 1] = { | |
79 | [VETH] = { "veth", instanciate_veth, 0 }, | |
80 | [MACVLAN] = { "macvlan", instanciate_macvlan, 0, }, | |
81 | [PHYS] = { "phys", instanciate_phys, 0, }, | |
82 | [EMPTY] = { "empty", instanciate_empty, 0, }, | |
83 | }; | |
84 | ||
85 | static int dir_filter(const struct dirent *dirent) | |
86 | { | |
87 | if (!strcmp(dirent->d_name, ".") || | |
88 | !strcmp(dirent->d_name, "..")) | |
89 | return 0; | |
90 | return 1; | |
91 | } | |
92 | ||
6f4a3756 | 93 | static int dir_for_each(const char *name, const char *directory, |
0ad19a3f | 94 | dir_cb callback, void *data) |
95 | { | |
96 | struct dirent **namelist; | |
97 | int n; | |
98 | ||
6f4a3756 | 99 | n = scandir(directory, &namelist, dir_filter, alphasort); |
0ad19a3f | 100 | if (n < 0) { |
6f4a3756 | 101 | lxc_log_syserror("failed to scan %s directory", directory); |
0ad19a3f | 102 | return -1; |
103 | } | |
104 | ||
105 | while (n--) { | |
6f4a3756 | 106 | if (callback(name, directory, namelist[n]->d_name, data)) { |
0ad19a3f | 107 | lxc_log_error("callback failed"); |
108 | free(namelist[n]); | |
109 | return -1; | |
110 | } | |
111 | free(namelist[n]); | |
112 | } | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static int file_for_each_line(const char *file, file_cb callback, | |
118 | void *buffer, size_t len, void* data) | |
119 | { | |
120 | FILE *f; | |
121 | int err = -1; | |
122 | ||
123 | f = fopen(file, "r"); | |
124 | if (!f) { | |
125 | lxc_log_syserror("failed to open %s", file); | |
126 | return -1; | |
127 | } | |
128 | ||
129 | while (fgets(buffer, len, f)) | |
130 | if (callback(buffer, data)) | |
131 | goto out; | |
132 | err = 0; | |
133 | out: | |
134 | fclose(f); | |
135 | return err; | |
136 | } | |
137 | ||
138 | static int write_info(const char *path, const char *file, const char *info) | |
139 | { | |
140 | int fd, err = -1; | |
22ebac19 | 141 | char f[MAXPATHLEN]; |
0ad19a3f | 142 | |
22ebac19 | 143 | snprintf(f, MAXPATHLEN, "%s/%s", path, file); |
0ad19a3f | 144 | fd = creat(f, 0755); |
145 | if (fd < 0) | |
146 | goto out; | |
147 | ||
148 | if (write(fd, info, strlen(info)) < 0 || | |
149 | write(fd, "\n", strlen("\n") + 1) < 0) | |
150 | goto out_write; | |
151 | err = 0; | |
152 | out: | |
153 | close(fd); | |
0ad19a3f | 154 | return err; |
155 | ||
156 | out_write: | |
157 | unlink(f); | |
158 | goto out; | |
159 | } | |
160 | ||
161 | static int read_info(const char *path, const char *file, char *info, size_t len) | |
162 | { | |
163 | int fd, ret = -1; | |
22ebac19 | 164 | char f[MAXPATHLEN], *token; |
0ad19a3f | 165 | |
22ebac19 | 166 | snprintf(f, MAXPATHLEN, "%s/%s", path, file); |
0ad19a3f | 167 | fd = open(f, O_RDONLY); |
168 | if (fd < 0) { | |
169 | if (errno == ENOENT) | |
170 | ret = 1; | |
171 | goto out; | |
172 | } | |
173 | ||
174 | ret = read(fd, info, len); | |
175 | if (ret < 0) | |
176 | goto out; | |
177 | ||
178 | token = strstr(info, "\n"); | |
179 | if (token) | |
180 | *token = '\0'; | |
181 | ret = 0; | |
182 | out: | |
183 | close(fd); | |
0ad19a3f | 184 | return ret; |
185 | } | |
186 | ||
187 | static int delete_info(const char *path, const char *file) | |
188 | { | |
22ebac19 | 189 | char info[MAXPATHLEN]; |
0ad19a3f | 190 | int ret; |
191 | ||
22ebac19 | 192 | snprintf(info, MAXPATHLEN, "%s/%s", path, file); |
0ad19a3f | 193 | ret = unlink(info); |
0ad19a3f | 194 | |
195 | return ret; | |
196 | } | |
197 | ||
198 | static int configure_ip4addr(int fd, struct lxc_inetdev *in) | |
199 | { | |
200 | char addr[INET6_ADDRSTRLEN]; | |
201 | char bcast[INET_ADDRSTRLEN]; | |
22ebac19 | 202 | char line[MAXLINELEN]; |
0ad19a3f | 203 | int err = -1; |
204 | ||
205 | if (!inet_ntop(AF_INET, &in->addr, addr, sizeof(addr))) { | |
206 | lxc_log_syserror("failed to convert ipv4 address"); | |
207 | goto err; | |
208 | } | |
209 | ||
210 | if (!inet_ntop(AF_INET, &in->bcast, bcast, sizeof(bcast))) { | |
211 | lxc_log_syserror("failed to convert ipv4 broadcast"); | |
212 | goto err; | |
213 | } | |
214 | ||
215 | if (in->prefix) | |
22ebac19 | 216 | snprintf(line, MAXLINELEN, "%s/%d %s\n", addr, in->prefix, bcast); |
0ad19a3f | 217 | else |
22ebac19 | 218 | snprintf(line, MAXLINELEN, "%s %s\n", addr, bcast); |
0ad19a3f | 219 | |
220 | if (write(fd, line, strlen(line)) < 0) { | |
221 | lxc_log_syserror("failed to write address info"); | |
222 | goto err; | |
223 | } | |
224 | ||
225 | err = 0; | |
226 | err: | |
0ad19a3f | 227 | return err; |
228 | } | |
229 | ||
230 | static int configure_ip6addr(int fd, struct lxc_inet6dev *in6) | |
231 | { | |
232 | char addr[INET6_ADDRSTRLEN]; | |
22ebac19 | 233 | char line[MAXLINELEN]; |
0ad19a3f | 234 | int err = -1; |
235 | ||
236 | if (!inet_ntop(AF_INET6, &in6->addr, addr, sizeof(addr))) { | |
237 | lxc_log_syserror("failed to convert ipv4 address"); | |
238 | goto err; | |
239 | } | |
240 | ||
22ebac19 | 241 | snprintf(line, MAXLINELEN, "%s/%d\n", addr, in6->prefix?in6->prefix:64); |
0ad19a3f | 242 | |
243 | if (write(fd, line, strlen(line)) < 0) { | |
244 | lxc_log_syserror("failed to write address info"); | |
245 | goto err; | |
246 | } | |
247 | ||
248 | err = 0; | |
249 | err: | |
0ad19a3f | 250 | return err; |
251 | } | |
252 | ||
253 | static int configure_ip_address(const char *path, struct lxc_list *ip, int family) | |
254 | { | |
255 | char file[MAXPATHLEN]; | |
256 | struct lxc_list *iterator; | |
257 | int fd, err = -1; | |
258 | ||
259 | if (mkdir(path, 0755)) { | |
260 | lxc_log_syserror("failed to create directory %s", path); | |
261 | return -1; | |
262 | } | |
263 | ||
264 | snprintf(file, MAXPATHLEN, "%s/addresses", path); | |
265 | fd = creat(file, 0755); | |
266 | if (fd < 0) { | |
267 | lxc_log_syserror("failed to create %s file", file); | |
268 | goto err; | |
269 | } | |
270 | ||
271 | lxc_list_for_each(iterator, ip) { | |
272 | err = family == AF_INET? | |
273 | configure_ip4addr(fd, iterator->elem): | |
274 | configure_ip6addr(fd, iterator->elem); | |
275 | if (err) | |
276 | goto err; | |
277 | } | |
278 | out: | |
279 | close(fd); | |
280 | return err; | |
281 | err: | |
282 | unlink(file); | |
283 | rmdir(path); | |
284 | goto out; | |
285 | } | |
286 | ||
287 | static int configure_netdev(const char *path, struct lxc_netdev *netdev) | |
288 | { | |
289 | int err = -1; | |
290 | char dir[MAXPATHLEN]; | |
291 | ||
292 | if (mkdir(path, 0755)) { | |
293 | lxc_log_syserror("failed to create %s directory", path); | |
294 | return -1; | |
295 | } | |
296 | ||
297 | if (netdev->ifname) { | |
298 | if (write_info(path, "link", netdev->ifname)) | |
299 | goto out_link; | |
300 | } | |
301 | ||
302 | if (netdev->newname) { | |
303 | if (write_info(path, "name", netdev->newname)) | |
304 | goto out_newname; | |
305 | } | |
306 | ||
307 | if (netdev->hwaddr) { | |
308 | if (write_info(path, "hwaddr", netdev->hwaddr)) | |
309 | goto out_up; | |
310 | } | |
311 | ||
312 | if (netdev->flags & IFF_UP) { | |
313 | if (write_info(path, "up", "")) | |
314 | goto out_hwaddr; | |
315 | } | |
316 | ||
317 | if (!lxc_list_empty(&netdev->ipv4)) { | |
318 | snprintf(dir, MAXPATHLEN, "%s/ipv4", path); | |
319 | if (configure_ip_address(dir, &netdev->ipv4, AF_INET)) | |
320 | goto out_ipv4; | |
321 | } | |
322 | ||
323 | if (!lxc_list_empty(&netdev->ipv6)) { | |
324 | snprintf(dir, MAXPATHLEN, "%s/ipv6", path); | |
325 | if (configure_ip_address(dir, &netdev->ipv6, AF_INET6)) | |
326 | goto out_ipv6; | |
327 | } | |
328 | err = 0; | |
329 | out: | |
330 | return err; | |
331 | out_ipv6: | |
332 | delete_info(path, "ipv4"); | |
333 | out_ipv4: | |
334 | delete_info(path, "up"); | |
335 | out_hwaddr: | |
336 | delete_info(path, "hwaddr"); | |
337 | out_up: | |
338 | delete_info(path, "name"); | |
339 | out_newname: | |
340 | delete_info(path, "link"); | |
341 | out_link: | |
342 | rmdir(path); | |
343 | goto out; | |
344 | } | |
345 | ||
346 | static int configure_utsname(const char *name, struct utsname *utsname) | |
347 | { | |
348 | char path[MAXPATHLEN]; | |
349 | ||
350 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
351 | ||
352 | if (write_info(path, "utsname", utsname->nodename)) { | |
353 | lxc_log_error("failed to write the utsname info"); | |
354 | return -1; | |
355 | } | |
356 | ||
357 | return 0; | |
358 | } | |
359 | ||
360 | static int configure_network(const char *name, struct lxc_list *network) | |
361 | { | |
362 | struct lxc_list *iterator; | |
363 | struct lxc_network *n; | |
364 | char networkpath[MAXPATHLEN]; | |
365 | char path[MAXPATHLEN]; | |
366 | int err = -1; | |
367 | ||
368 | if (lxc_list_empty(network)) | |
369 | return 0; | |
370 | ||
371 | snprintf(networkpath, MAXPATHLEN, LXCPATH "/%s/network", name); | |
372 | if (mkdir(networkpath, 0755)) { | |
373 | lxc_log_syserror("failed to create %s directory", networkpath); | |
374 | goto out; | |
375 | } | |
376 | ||
377 | lxc_list_for_each(iterator, network) { | |
378 | ||
379 | n = iterator->elem; | |
380 | ||
381 | if (n->type < 0 || n->type > MAXCONFTYPE) { | |
382 | lxc_log_error("invalid network configuration type '%d'", | |
383 | n->type); | |
384 | goto out; | |
385 | } | |
386 | ||
387 | snprintf(path, MAXPATHLEN, "%s/%s%d", networkpath, | |
388 | netdev_conf[n->type].type, | |
389 | netdev_conf[n->type].count++); | |
390 | ||
391 | if (configure_netdev(path, lxc_list_first_elem(&n->netdev))) { | |
392 | lxc_log_error("failed to configure network type %s", | |
393 | netdev_conf[n->type].type); | |
394 | goto out; | |
395 | } | |
396 | } | |
397 | ||
398 | err = 0; | |
399 | out: | |
400 | return err; | |
401 | } | |
402 | ||
576f946d | 403 | static int configure_cgroup(const char *name, struct lxc_list *cgroup) |
0ad19a3f | 404 | { |
576f946d | 405 | char path[MAXPATHLEN]; |
406 | struct lxc_list *iterator; | |
407 | struct lxc_cgroup *cg; | |
6f4a3756 | 408 | FILE *file; |
576f946d | 409 | |
410 | if (lxc_list_empty(cgroup)) | |
411 | return 0; | |
412 | ||
413 | snprintf(path, MAXPATHLEN, LXCPATH "/%s/cgroup", name); | |
414 | ||
6f4a3756 | 415 | file = fopen(path, "w+"); |
416 | if (!file) { | |
417 | lxc_log_syserror("failed to open '%s'", path); | |
576f946d | 418 | return -1; |
419 | } | |
420 | ||
421 | lxc_list_for_each(iterator, cgroup) { | |
422 | cg = iterator->elem; | |
6f4a3756 | 423 | fprintf(file, "%s=%s\n", cg->subsystem, cg->value); |
576f946d | 424 | } |
425 | ||
6f4a3756 | 426 | fclose(file); |
427 | ||
0ad19a3f | 428 | return 0; |
429 | } | |
430 | ||
b0a33c1e | 431 | static int configure_tty(const char *name, int tty) |
432 | { | |
433 | char path[MAXPATHLEN]; | |
434 | char *nbtty; | |
435 | int ret; | |
436 | ||
437 | if (asprintf(&nbtty, "%d", tty) < 0) { | |
438 | lxc_log_error("failed to convert tty number"); | |
439 | return -1; | |
440 | } | |
441 | ||
442 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
443 | ||
444 | ret = write_info(path, "tty", nbtty); | |
445 | if (ret) | |
446 | lxc_log_error("failed to write the tty info"); | |
447 | ||
448 | free(nbtty); | |
449 | ||
450 | return ret; | |
451 | } | |
452 | ||
eae6543d | 453 | static int configure_rootfs(const char *name, const char *rootfs) |
0ad19a3f | 454 | { |
455 | char path[MAXPATHLEN]; | |
b09ef133 | 456 | char absrootfs[MAXPATHLEN]; |
457 | char *pwd; | |
0ad19a3f | 458 | |
eae6543d | 459 | snprintf(path, MAXPATHLEN, LXCPATH "/%s/rootfs", name); |
0ad19a3f | 460 | |
b09ef133 | 461 | pwd = get_current_dir_name(); |
462 | ||
463 | snprintf(absrootfs, MAXPATHLEN, "%s/%s", pwd, rootfs); | |
464 | ||
465 | free(pwd); | |
466 | ||
467 | if (access(absrootfs, F_OK)) { | |
468 | lxc_log_syserror("'%s' is not accessible", absrootfs); | |
469 | return -1; | |
470 | } | |
471 | ||
472 | return symlink(absrootfs, path); | |
0ad19a3f | 473 | } |
474 | ||
10db618d | 475 | static int configure_pts(const char *name, int pts) |
476 | { | |
477 | char path[MAXPATHLEN]; | |
478 | char *maxpts; | |
479 | int ret; | |
480 | ||
481 | if (asprintf(&maxpts, "%d", pts) < 0) { | |
482 | lxc_log_error("failed to convert max pts number"); | |
483 | return -1; | |
484 | } | |
485 | ||
486 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
487 | ||
488 | ret = write_info(path, "pts", maxpts); | |
489 | if (ret) | |
490 | lxc_log_error("failed to write the pts info"); | |
491 | ||
492 | free(maxpts); | |
493 | ||
494 | return ret; | |
495 | } | |
496 | ||
0ad19a3f | 497 | static int configure_mount(const char *name, const char *fstab) |
498 | { | |
22ebac19 | 499 | char path[MAXPATHLEN]; |
0ad19a3f | 500 | struct stat stat; |
501 | int infd, outfd; | |
502 | void *src, *dst; | |
503 | char c = '\0'; | |
504 | int ret = -1; | |
505 | ||
22ebac19 | 506 | snprintf(path, MAXPATHLEN, LXCPATH "/%s/fstab", name); |
0ad19a3f | 507 | |
508 | outfd = open(path, O_RDWR|O_CREAT|O_EXCL, 0640); | |
509 | if (outfd < 0) { | |
510 | lxc_log_syserror("failed to creat '%s'", path); | |
511 | goto out; | |
512 | } | |
513 | ||
514 | infd = open(fstab, O_RDONLY); | |
515 | if (infd < 0) { | |
516 | lxc_log_syserror("failed to open '%s'", fstab); | |
22ebac19 | 517 | goto out_open; |
0ad19a3f | 518 | } |
519 | ||
520 | if (fstat(infd, &stat)) { | |
521 | lxc_log_syserror("failed to stat '%s'", fstab); | |
22ebac19 | 522 | goto out_open2; |
0ad19a3f | 523 | } |
524 | ||
525 | if (lseek(outfd, stat.st_size - 1, SEEK_SET) < 0) { | |
526 | lxc_log_syserror("failed to seek dest file '%s'", path); | |
22ebac19 | 527 | goto out_open2; |
0ad19a3f | 528 | } |
529 | ||
530 | /* fixup length */ | |
531 | if (write(outfd, &c, 1) < 0) { | |
532 | lxc_log_syserror("failed to write to '%s'", path); | |
22ebac19 | 533 | goto out_open2; |
0ad19a3f | 534 | } |
535 | ||
536 | src = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, infd, 0L); | |
537 | if (src == MAP_FAILED) { | |
538 | lxc_log_syserror("failed to mmap '%s'", fstab); | |
22ebac19 | 539 | goto out_open2; |
0ad19a3f | 540 | } |
541 | ||
542 | dst = mmap(NULL, stat.st_size, PROT_WRITE, MAP_SHARED, outfd, 0L); | |
543 | if (dst == MAP_FAILED) { | |
544 | lxc_log_syserror("failed to mmap '%s'", path); | |
22ebac19 | 545 | goto out_mmap; |
0ad19a3f | 546 | } |
547 | ||
548 | memcpy(dst, src, stat.st_size); | |
549 | ||
550 | munmap(src, stat.st_size); | |
551 | munmap(dst, stat.st_size); | |
552 | ||
553 | ret = 0; | |
554 | out: | |
0ad19a3f | 555 | return ret; |
22ebac19 | 556 | |
557 | out_mmap: | |
558 | munmap(src, stat.st_size); | |
559 | out_open2: | |
560 | close(infd); | |
561 | out_open: | |
562 | unlink(path); | |
563 | close(outfd); | |
564 | goto out; | |
0ad19a3f | 565 | } |
566 | ||
6f4a3756 | 567 | static int unconfigure_ip_addresses(const char *directory) |
0ad19a3f | 568 | { |
569 | char path[MAXPATHLEN]; | |
570 | ||
6f4a3756 | 571 | snprintf(path, MAXPATHLEN, "%s/ipv4", directory); |
0ad19a3f | 572 | delete_info(path, "addresses"); |
573 | rmdir(path); | |
574 | ||
6f4a3756 | 575 | snprintf(path, MAXPATHLEN, "%s/ipv6", directory); |
0ad19a3f | 576 | delete_info(path, "addresses"); |
577 | rmdir(path); | |
578 | ||
579 | return 0; | |
580 | } | |
581 | ||
6f4a3756 | 582 | static int unconfigure_network_cb(const char *name, const char *directory, |
0ad19a3f | 583 | const char *file, void *data) |
584 | { | |
585 | char path[MAXPATHLEN]; | |
586 | ||
6f4a3756 | 587 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
b09ef133 | 588 | delete_info(path, "ifindex"); |
0ad19a3f | 589 | delete_info(path, "name"); |
590 | delete_info(path, "addr"); | |
591 | delete_info(path, "link"); | |
592 | delete_info(path, "hwaddr"); | |
593 | delete_info(path, "up"); | |
594 | unconfigure_ip_addresses(path); | |
595 | rmdir(path); | |
596 | ||
597 | return 0; | |
598 | } | |
599 | ||
600 | static int unconfigure_network(const char *name) | |
601 | { | |
6f4a3756 | 602 | char directory[MAXPATHLEN]; |
0ad19a3f | 603 | |
6f4a3756 | 604 | snprintf(directory, MAXPATHLEN, LXCPATH "/%s/network", name); |
605 | dir_for_each(name, directory, unconfigure_network_cb, NULL); | |
606 | rmdir(directory); | |
0ad19a3f | 607 | |
608 | return 0; | |
609 | } | |
610 | ||
6f4a3756 | 611 | static int unconfigure_cgroup_cb(const char *name, const char *directory, |
576f946d | 612 | const char *file, void *data) |
613 | { | |
6f4a3756 | 614 | return delete_info(directory, file); |
576f946d | 615 | } |
616 | ||
0ad19a3f | 617 | static int unconfigure_cgroup(const char *name) |
618 | { | |
6f4a3756 | 619 | char filename[MAXPATHLEN]; |
620 | struct stat s; | |
b09ef133 | 621 | |
6f4a3756 | 622 | snprintf(filename, MAXPATHLEN, LXCPATH "/%s/cgroup", name); |
623 | ||
624 | if (stat(filename, &s)) { | |
625 | lxc_log_syserror("failed to stat '%s'", filename); | |
626 | return -1; | |
627 | } | |
628 | ||
629 | if (S_ISDIR(s.st_mode)) { | |
630 | /* old cgroup configuration */ | |
631 | dir_for_each(name, filename, unconfigure_cgroup_cb, NULL); | |
632 | rmdir(filename); | |
633 | } else { | |
634 | unlink(filename); | |
635 | } | |
b09ef133 | 636 | |
0ad19a3f | 637 | return 0; |
638 | } | |
639 | ||
eae6543d | 640 | static int unconfigure_rootfs(const char *name) |
0ad19a3f | 641 | { |
642 | char path[MAXPATHLEN]; | |
643 | ||
644 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
eae6543d | 645 | delete_info(path, "rootfs"); |
0ad19a3f | 646 | |
647 | return 0; | |
648 | } | |
649 | ||
10db618d | 650 | static int unconfigure_pts(const char *name) |
651 | { | |
652 | char path[MAXPATHLEN]; | |
653 | ||
654 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
655 | delete_info(path, "pts"); | |
656 | ||
657 | return 0; | |
658 | } | |
659 | ||
79cf945c | 660 | static int unconfigure_tty(const char *name) |
661 | { | |
662 | char path[MAXPATHLEN]; | |
663 | ||
664 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
665 | delete_info(path, "tty"); | |
666 | ||
667 | return 0; | |
668 | } | |
669 | ||
0ad19a3f | 670 | static int unconfigure_mount(const char *name) |
671 | { | |
672 | char path[MAXPATHLEN]; | |
673 | ||
674 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
675 | delete_info(path, "fstab"); | |
676 | ||
677 | return 0; | |
678 | } | |
679 | ||
680 | static int unconfigure_utsname(const char *name) | |
681 | { | |
682 | char path[MAXPATHLEN]; | |
683 | ||
684 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
685 | delete_info(path, "utsname"); | |
686 | ||
687 | return 0; | |
688 | } | |
689 | ||
690 | static int setup_utsname(const char *name) | |
691 | { | |
692 | int ret; | |
693 | char path[MAXPATHLEN]; | |
694 | struct utsname utsname; | |
695 | ||
696 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); | |
697 | ||
698 | ret = read_info(path, "utsname", utsname.nodename, | |
699 | sizeof(utsname.nodename)); | |
700 | if (ret < 0) { | |
701 | lxc_log_syserror("failed to read utsname info"); | |
702 | return -1; | |
703 | } | |
704 | ||
705 | if (!ret && sethostname(utsname.nodename, strlen(utsname.nodename))) { | |
706 | lxc_log_syserror("failed to set the hostname to '%s'", | |
707 | utsname.nodename); | |
708 | return -1; | |
709 | } | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
b0a33c1e | 714 | static int setup_tty(const char *name, const struct lxc_tty_info *tty_info) |
715 | { | |
716 | char path[MAXPATHLEN]; | |
717 | int i; | |
718 | ||
719 | for (i = 0; i < tty_info->nbtty; i++) { | |
720 | ||
721 | struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; | |
722 | ||
79cf945c | 723 | if (conf_has_rootfs(name)) |
724 | snprintf(path, MAXPATHLEN, | |
725 | LXCPATH "/%s/rootfs/dev/tty%d", name, i + 1); | |
726 | else | |
727 | snprintf(path, MAXPATHLEN, "/dev/tty%d", i + 1); | |
b0a33c1e | 728 | |
729 | /* At this point I can not use the "access" function | |
730 | * to check the file is present or not because it fails | |
731 | * with EACCES errno and I don't know why :( */ | |
732 | ||
733 | if (mount(pty_info->name, path, "none", MS_BIND, 0)) { | |
734 | lxc_log_warning("failed to mount '%s'->'%s'", | |
735 | pty_info->name, path); | |
736 | continue; | |
737 | } | |
738 | } | |
739 | ||
740 | return 0; | |
741 | } | |
742 | ||
eae6543d | 743 | static int setup_rootfs(const char *name) |
0ad19a3f | 744 | { |
c3f0a28c | 745 | char path[MAXPATHLEN]; |
0ad19a3f | 746 | |
eae6543d | 747 | snprintf(path, MAXPATHLEN, LXCPATH "/%s/rootfs", name); |
0ad19a3f | 748 | |
c3f0a28c | 749 | if (chroot(path)) { |
750 | lxc_log_syserror("failed to set chroot %s", path); | |
751 | return -1; | |
752 | } | |
0ad19a3f | 753 | |
c3f0a28c | 754 | if (chdir(getenv("HOME")) && chdir("/")) { |
755 | lxc_log_syserror("failed to change to home directory"); | |
756 | return -1; | |
0ad19a3f | 757 | } |
758 | ||
759 | return 0; | |
760 | } | |
761 | ||
3c26f34e | 762 | static int setup_pts(const char *name) |
763 | { | |
764 | char mountname[MAXPATHLEN]; | |
765 | ||
766 | if (!access("/dev/pts/ptmx", F_OK) && umount("/dev/pts")) { | |
767 | lxc_log_syserror("failed to umount 'dev/pts'"); | |
768 | return -1; | |
769 | } | |
770 | ||
771 | snprintf(mountname, MAXPATHLEN, "%spts", name); | |
772 | ||
773 | if (mount(mountname, "/dev/pts", "devpts", MS_MGC_VAL, "newinstance")) { | |
774 | lxc_log_syserror("failed to mount a new instance of '/dev/pts'"); | |
775 | return -1; | |
776 | } | |
777 | ||
778 | if (chmod("/dev/pts/ptmx", 0666)) { | |
779 | lxc_log_syserror("failed to set permission for '/dev/pts/ptmx'"); | |
780 | return -1; | |
781 | } | |
782 | ||
783 | if (access("/dev/ptmx", F_OK)) { | |
784 | if (!symlink("/dev/pts/ptmx", "/dev/ptmx")) | |
785 | goto out; | |
786 | lxc_log_syserror("failed to symlink '/dev/pts/ptmx'->'/dev/ptmx'"); | |
787 | return -1; | |
788 | } | |
789 | ||
790 | /* fallback here, /dev/pts/ptmx exists just mount bind */ | |
791 | if (mount("/dev/pts/ptmx", "/dev/ptmx", "none", MS_BIND, 0)) { | |
792 | lxc_log_syserror("mount failed '/dev/pts/ptmx'->'/dev/ptmx'"); | |
793 | return -1; | |
794 | } | |
795 | out: | |
796 | return 0; | |
797 | } | |
798 | ||
6e590161 | 799 | static int setup_console(const char *name, const char *tty) |
800 | { | |
ed502555 | 801 | char console[MAXPATHLEN]; |
802 | ||
803 | snprintf(console, MAXPATHLEN, LXCPATH "/%s/rootfs/dev/console", name); | |
804 | ||
805 | if (access(console, R_OK|W_OK)) | |
6e590161 | 806 | return 0; |
807 | ||
ed502555 | 808 | if (mount(tty, console, "none", MS_BIND, 0)) { |
6e590161 | 809 | lxc_log_error("failed to mount the console"); |
810 | return -1; | |
811 | } | |
812 | ||
813 | return 0; | |
814 | } | |
815 | ||
6f4a3756 | 816 | static int setup_cgroup_cb(void* buffer, void *data) |
576f946d | 817 | { |
6f4a3756 | 818 | char *key = buffer, *value; |
e7aa295e | 819 | char *name = data; |
820 | int ret; | |
6f4a3756 | 821 | |
822 | value = strchr(key, '='); | |
823 | if (!value) | |
824 | return -1; | |
825 | ||
826 | *value = '\0'; | |
827 | value += 1; | |
828 | ||
e7aa295e | 829 | ret = lxc_cgroup_set(name, key, value); |
830 | if (ret) | |
831 | lxc_log_syserror("failed to set cgroup '%s' = '%s' for '%s'", | |
832 | key, value, name); | |
833 | return ret; | |
576f946d | 834 | } |
835 | ||
6f4a3756 | 836 | static int setup_convert_cgroup_cb(const char *name, const char *directory, |
837 | const char *file, void *data) | |
838 | { | |
839 | FILE *f = data; | |
840 | char line[MAXPATHLEN]; | |
841 | ||
842 | if (read_info(directory, file, line, MAXPATHLEN)) { | |
843 | lxc_log_error("failed to read %s", file); | |
844 | return -1; | |
845 | } | |
846 | ||
847 | fprintf(f, "%s=%s\n", file, line); | |
848 | ||
849 | return 0; | |
850 | } | |
851 | ||
852 | static int setup_convert_cgroup(const char *name, char *directory) | |
853 | { | |
854 | char filename[MAXPATHLEN]; | |
855 | FILE *file; | |
856 | int ret; | |
857 | ||
858 | snprintf(filename, MAXPATHLEN, LXCPATH "/%s/cgroup.new", name); | |
859 | ||
860 | file = fopen(filename, "w+"); | |
861 | if (!file) | |
862 | return -1; | |
863 | ||
864 | ret = dir_for_each(name, directory, setup_convert_cgroup_cb, file); | |
865 | if (ret) | |
866 | goto out_error; | |
867 | ||
868 | ret = unconfigure_cgroup(name); | |
869 | if (ret) | |
870 | goto out_error; | |
871 | ||
872 | ret = rename(filename, directory); | |
873 | if (ret) | |
874 | goto out_error; | |
875 | out: | |
876 | fclose(file); | |
877 | return ret; | |
878 | ||
879 | out_error: | |
880 | unlink(filename); | |
881 | goto out; | |
882 | } | |
883 | ||
576f946d | 884 | static int setup_cgroup(const char *name) |
885 | { | |
6f4a3756 | 886 | char filename[MAXPATHLEN]; |
887 | char line[MAXPATHLEN]; | |
888 | struct stat s; | |
889 | ||
890 | snprintf(filename, MAXPATHLEN, LXCPATH "/%s/cgroup", name); | |
891 | ||
892 | if (stat(filename, &s)) { | |
893 | lxc_log_syserror("failed to stat '%s'", filename); | |
894 | return -1; | |
895 | } | |
896 | ||
897 | if (S_ISDIR(s.st_mode)) { | |
898 | if (setup_convert_cgroup(name, filename)) { | |
899 | lxc_log_error("failed to convert old cgroup configuration"); | |
900 | return -1; | |
901 | } | |
902 | } | |
903 | ||
904 | return file_for_each_line(filename, setup_cgroup_cb, | |
e7aa295e | 905 | line, MAXPATHLEN, (void *)name); |
576f946d | 906 | } |
907 | ||
0ad19a3f | 908 | static int setup_mount(const char *name) |
909 | { | |
910 | char path[MAXPATHLEN]; | |
911 | struct mntent *mntent; | |
912 | FILE *file; | |
913 | int ret = -1; | |
914 | unsigned long mntflags = 0; | |
915 | ||
916 | snprintf(path, MAXPATHLEN, LXCPATH "/%s/fstab", name); | |
917 | ||
918 | file = setmntent(path, "r"); | |
919 | if (!file) { | |
920 | if (errno == ENOENT) | |
921 | return 0; | |
922 | lxc_log_syserror("failed to open '%s'", path); | |
923 | goto out; | |
924 | } | |
925 | ||
926 | while((mntent = getmntent(file))) { | |
927 | ||
928 | if (hasmntopt(mntent, "bind")) | |
929 | mntflags |= MS_BIND; | |
4bbb9c57 | 930 | if (hasmntopt(mntent, "ro")) |
931 | mntflags |= MS_RDONLY; | |
932 | if (hasmntopt(mntent, "noexec")) | |
933 | mntflags |= MS_NOEXEC; | |
0ad19a3f | 934 | |
935 | if (mount(mntent->mnt_fsname, mntent->mnt_dir, | |
936 | mntent->mnt_type, mntflags, NULL)) { | |
937 | lxc_log_syserror("failed to mount '%s' on '%s'", | |
938 | mntent->mnt_fsname, mntent->mnt_dir); | |
939 | goto out; | |
940 | } | |
941 | } | |
942 | ret = 0; | |
943 | out: | |
944 | endmntent(file); | |
945 | return ret; | |
946 | } | |
947 | ||
948 | static int setup_ipv4_addr_cb(void *buffer, void *data) | |
949 | { | |
950 | char *ifname = data; | |
951 | char *cursor, *slash, *addr, *bcast = NULL, *prefix = NULL; | |
952 | int p = 24; | |
953 | ||
954 | addr = buffer; | |
955 | cursor = strstr(addr, " "); | |
956 | if (cursor) { | |
957 | *cursor = '\0'; | |
958 | bcast = cursor + 1; | |
959 | cursor = strstr(bcast, "\n"); | |
960 | if (cursor) | |
961 | *cursor = '\0'; | |
962 | } | |
963 | ||
964 | slash = strstr(addr, "/"); | |
965 | if (slash) { | |
966 | *slash = '\0'; | |
967 | prefix = slash + 1; | |
968 | } | |
969 | ||
970 | if (prefix) | |
971 | p = atoi(prefix); | |
972 | ||
973 | if (ip_addr_add(ifname, addr, p, bcast)) { | |
974 | lxc_log_error("failed to set %s to addr %s/%d %s", ifname, | |
975 | addr, p, bcast?bcast:""); | |
976 | return -1; | |
977 | } | |
978 | ||
979 | return 0; | |
980 | } | |
981 | ||
982 | static int setup_ipv6_addr_cb(void *buffer, void *data) | |
983 | { | |
984 | char *ifname = data; | |
985 | char *cursor, *slash, *addr, *bcast = NULL, *prefix = NULL; | |
986 | int p = 24; | |
987 | ||
988 | addr = buffer; | |
989 | cursor = strstr(addr, " "); | |
990 | if (cursor) { | |
991 | *cursor = '\0'; | |
992 | bcast = cursor + 1; | |
993 | cursor = strstr(bcast, "\n"); | |
994 | if (cursor) | |
995 | *cursor = '\0'; | |
996 | } | |
997 | ||
998 | slash = strstr(addr, "/"); | |
999 | if (slash) { | |
1000 | *slash = '\0'; | |
1001 | prefix = slash + 1; | |
1002 | } | |
1003 | ||
1004 | if (prefix) | |
1005 | p = atoi(prefix); | |
1006 | ||
1007 | if (ip6_addr_add(ifname, addr, p, bcast)) { | |
1008 | lxc_log_error("failed to set %s to addr %s/%d %s", ifname, | |
1009 | addr, p, bcast?bcast:""); | |
1010 | return -1; | |
1011 | } | |
1012 | ||
1013 | return 0; | |
1014 | } | |
1015 | ||
1016 | static int setup_hw_addr(char *hwaddr, const char *ifname) | |
1017 | { | |
1018 | struct sockaddr sockaddr; | |
1019 | struct ifreq ifr; | |
1020 | int ret, fd; | |
1021 | ||
1022 | if (lxc_convert_mac(hwaddr, &sockaddr)) { | |
1023 | fprintf(stderr, "conversion has failed\n"); | |
1024 | return -1; | |
1025 | } | |
1026 | ||
1027 | memcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
1028 | memcpy((char *) &ifr.ifr_hwaddr, (char *) &sockaddr, sizeof(sockaddr)); | |
1029 | ||
1030 | fd = socket(AF_INET, SOCK_DGRAM, 0); | |
1031 | if (fd < 0) { | |
1032 | perror("socket"); | |
1033 | return -1; | |
1034 | } | |
1035 | ||
1036 | ret = ioctl(fd, SIOCSIFHWADDR, &ifr); | |
1037 | close(fd); | |
1038 | if (ret) | |
1039 | perror("ioctl"); | |
1040 | ||
1041 | return ret; | |
1042 | } | |
1043 | ||
6f4a3756 | 1044 | static int setup_ip_addr(const char *directory, const char *ifname) |
0ad19a3f | 1045 | { |
1046 | char path[MAXPATHLEN], line[MAXLINELEN]; | |
1047 | struct stat s; | |
1048 | int ret = 0; | |
1049 | ||
6f4a3756 | 1050 | snprintf(path, MAXPATHLEN, "%s/ipv4/addresses", directory); |
0ad19a3f | 1051 | if (!stat(path, &s)) |
1052 | ret = file_for_each_line(path, setup_ipv4_addr_cb, | |
1053 | line, MAXPATHLEN, (void*)ifname); | |
1054 | return ret; | |
1055 | } | |
1056 | ||
6f4a3756 | 1057 | static int setup_ip6_addr(const char *directory, const char *ifname) |
0ad19a3f | 1058 | { |
1059 | char path[MAXPATHLEN], line[MAXLINELEN]; | |
1060 | struct stat s; | |
1061 | int ret = 0; | |
1062 | ||
6f4a3756 | 1063 | snprintf(path, MAXLINELEN, "%s/ipv6/addresses", directory); |
0ad19a3f | 1064 | if (!stat(path, &s)) |
1065 | ret = file_for_each_line(path, setup_ipv6_addr_cb, | |
1066 | line, MAXPATHLEN, (void*)ifname); | |
1067 | return ret; | |
1068 | } | |
1069 | ||
6f4a3756 | 1070 | static int setup_network_cb(const char *name, const char *directory, |
0ad19a3f | 1071 | const char *file, void *data) |
1072 | { | |
1073 | char path[MAXPATHLEN]; | |
1074 | char strindex[MAXINDEXLEN]; | |
1075 | char ifname[IFNAMSIZ]; | |
1076 | char newname[IFNAMSIZ]; | |
1077 | char hwaddr[MAXHWLEN]; | |
1078 | char *current_ifname = ifname; | |
1079 | int ifindex; | |
1080 | ||
6f4a3756 | 1081 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
0ad19a3f | 1082 | |
1083 | if (read_info(path, "ifindex", strindex, sizeof(strindex))) { | |
1084 | lxc_log_error("failed to read ifindex info"); | |
1085 | return -1; | |
1086 | } | |
1087 | ||
1088 | ifindex = atoi(strindex); | |
1089 | if (!ifindex) { | |
1090 | if (!read_info(path, "up", strindex, sizeof(strindex))) | |
1091 | if (device_up("lo")) { | |
1092 | lxc_log_error("failed to set the loopback up"); | |
1093 | return -1; | |
1094 | } | |
1095 | return 0; | |
1096 | } | |
1097 | ||
1098 | if (!if_indextoname(ifindex, current_ifname)) { | |
1099 | lxc_log_error("no interface corresponding to index '%d'", | |
1100 | ifindex); | |
1101 | return -1; | |
1102 | } | |
1103 | ||
1104 | if (!read_info(path, "name", newname, sizeof(newname))) { | |
1105 | if (device_rename(ifname, newname)) { | |
1106 | lxc_log_error("failed to rename %s->%s", | |
1107 | ifname, newname); | |
1108 | return -1; | |
1109 | } | |
1110 | current_ifname = newname; | |
1111 | } | |
1112 | ||
1113 | if (!read_info(path, "hwaddr", hwaddr, sizeof(hwaddr))) { | |
1114 | if (setup_hw_addr(hwaddr, current_ifname)) { | |
1115 | lxc_log_error("failed to setup hw address for '%s'", | |
1116 | current_ifname); | |
1117 | return -1; | |
1118 | } | |
1119 | } | |
1120 | ||
1121 | if (setup_ip_addr(path, current_ifname)) { | |
1122 | lxc_log_error("failed to setup ip addresses for '%s'", | |
1123 | ifname); | |
1124 | return -1; | |
1125 | } | |
1126 | ||
1127 | if (setup_ip6_addr(path, current_ifname)) { | |
1128 | lxc_log_error("failed to setup ipv6 addresses for '%s'", | |
1129 | ifname); | |
1130 | return -1; | |
1131 | } | |
1132 | ||
1133 | if (!read_info(path, "up", strindex, sizeof(strindex))) { | |
1134 | if (device_up(current_ifname)) { | |
1135 | lxc_log_error("failed to set '%s' up", current_ifname); | |
1136 | return -1; | |
1137 | } | |
1138 | ||
1139 | /* the network is up, make the loopback up too */ | |
1140 | if (device_up("lo")) { | |
1141 | lxc_log_error("failed to set the loopback up"); | |
1142 | return -1; | |
1143 | } | |
1144 | } | |
1145 | ||
1146 | return 0; | |
1147 | } | |
1148 | ||
1149 | static int setup_network(const char *name) | |
1150 | { | |
6f4a3756 | 1151 | char directory[MAXPATHLEN]; |
0ad19a3f | 1152 | |
6f4a3756 | 1153 | snprintf(directory, MAXPATHLEN, LXCPATH "/%s/network", name); |
1154 | return dir_for_each(name, directory, setup_network_cb, NULL); | |
0ad19a3f | 1155 | } |
1156 | ||
1157 | int conf_has(const char *name, const char *info) | |
1158 | { | |
b09ef133 | 1159 | int ret = 0; |
0ad19a3f | 1160 | char path[MAXPATHLEN]; |
1161 | struct stat st; | |
1162 | ||
1163 | snprintf(path, MAXPATHLEN, LXCPATH "/%s/%s", name, info); | |
1164 | ||
b09ef133 | 1165 | if (!stat(path, &st) || !lstat(path, &st)) { |
0ad19a3f | 1166 | ret = 1; |
1167 | goto out; | |
1168 | } | |
1169 | ||
1170 | if (errno == ENOENT) { | |
1171 | ret = 0; | |
1172 | goto out; | |
1173 | } | |
1174 | ||
1175 | lxc_log_syserror("failed to stat %s info", info); | |
1176 | out: | |
1177 | return ret; | |
1178 | } | |
1179 | ||
1180 | int lxc_configure(const char *name, struct lxc_conf *conf) | |
1181 | { | |
1182 | if (!conf) | |
1183 | return 0; | |
1184 | ||
1185 | if (conf->utsname && configure_utsname(name, conf->utsname)) { | |
1186 | lxc_log_error("failed to configure the utsname"); | |
e5bda9ee | 1187 | return -LXC_ERROR_CONF_UTSNAME; |
0ad19a3f | 1188 | } |
1189 | ||
576f946d | 1190 | if (configure_cgroup(name, &conf->cgroup)) { |
1191 | lxc_log_error("failed to configure the control group"); | |
e5bda9ee | 1192 | return -LXC_ERROR_CONF_CGROUP; |
0ad19a3f | 1193 | } |
1194 | ||
576f946d | 1195 | if (configure_network(name, &conf->networks)) { |
1196 | lxc_log_error("failed to configure the network"); | |
e5bda9ee | 1197 | return -LXC_ERROR_CONF_NETWORK; |
0ad19a3f | 1198 | } |
1199 | ||
b0a33c1e | 1200 | if (conf->tty && configure_tty(name, conf->tty)) { |
1201 | lxc_log_error("failed to configure the tty"); | |
1202 | return -LXC_ERROR_CONF_TTY; | |
1203 | } | |
1204 | ||
eae6543d | 1205 | if (conf->rootfs && configure_rootfs(name, conf->rootfs)) { |
1206 | lxc_log_error("failed to configure the rootfs"); | |
e5bda9ee | 1207 | return -LXC_ERROR_CONF_ROOTFS; |
0ad19a3f | 1208 | } |
1209 | ||
1210 | if (conf->fstab && configure_mount(name, conf->fstab)) { | |
1211 | lxc_log_error("failed to configure the mount points"); | |
e5bda9ee | 1212 | return -LXC_ERROR_CONF_MOUNT; |
0ad19a3f | 1213 | } |
1214 | ||
10db618d | 1215 | if (conf->pts && configure_pts(name, conf->pts)) { |
1216 | lxc_log_error("failed to configure a new pts instance"); | |
1217 | return -LXC_ERROR_CONF_PTS; | |
1218 | } | |
1219 | ||
0ad19a3f | 1220 | return 0; |
1221 | } | |
1222 | ||
1223 | int lxc_unconfigure(const char *name) | |
1224 | { | |
1225 | if (conf_has_utsname(name) && unconfigure_utsname(name)) | |
1226 | lxc_log_error("failed to cleanup utsname"); | |
1227 | ||
1228 | if (conf_has_network(name) && unconfigure_network(name)) | |
1229 | lxc_log_error("failed to cleanup the network"); | |
1230 | ||
576f946d | 1231 | if (conf_has_cgroup(name) && unconfigure_cgroup(name)) |
0ad19a3f | 1232 | lxc_log_error("failed to cleanup cgroup"); |
1233 | ||
79cf945c | 1234 | if (conf_has_tty(name) && unconfigure_tty(name)) |
10db618d | 1235 | lxc_log_error("failed to cleanup tty"); |
79cf945c | 1236 | |
eae6543d | 1237 | if (conf_has_rootfs(name) && unconfigure_rootfs(name)) |
1238 | lxc_log_error("failed to cleanup rootfs"); | |
0ad19a3f | 1239 | |
1240 | if (conf_has_fstab(name) && unconfigure_mount(name)) | |
1241 | lxc_log_error("failed to cleanup mount"); | |
1242 | ||
10db618d | 1243 | if (conf_has_pts(name) && unconfigure_pts(name)) |
1244 | lxc_log_error("failed to cleanup pts"); | |
1245 | ||
0ad19a3f | 1246 | return 0; |
1247 | } | |
1248 | ||
6f4a3756 | 1249 | static int instanciate_veth(const char *directory, const char *file, pid_t pid) |
0ad19a3f | 1250 | { |
1251 | char *path = NULL, *strindex = NULL, *veth1 = NULL, *veth2 = NULL; | |
1252 | char bridge[IFNAMSIZ]; | |
1253 | int ifindex, ret = -1; | |
1254 | ||
22ebac19 | 1255 | if (!asprintf(&veth1, "%s_%d", file, pid) || |
1256 | !asprintf(&veth2, "%s~%d", file, pid) || | |
6f4a3756 | 1257 | !asprintf(&path, "%s/%s", directory, file)) { |
22ebac19 | 1258 | lxc_log_syserror("failed to allocate memory"); |
1259 | goto out; | |
1260 | } | |
0ad19a3f | 1261 | |
1262 | if (read_info(path, "link", bridge, IFNAMSIZ)) { | |
1263 | lxc_log_error("failed to read bridge info"); | |
1264 | goto out; | |
1265 | } | |
1266 | ||
1267 | if (lxc_configure_veth(veth1, veth2, bridge)) { | |
1268 | lxc_log_error("failed to create %s-%s/%s", veth1, veth2, bridge); | |
1269 | goto out; | |
1270 | } | |
1271 | ||
1272 | ifindex = if_nametoindex(veth2); | |
1273 | if (!ifindex) { | |
1274 | lxc_log_error("failed to retrieve the index for %s", veth2); | |
1275 | goto out; | |
1276 | } | |
1277 | ||
22ebac19 | 1278 | if (!asprintf(&strindex, "%d", ifindex)) { |
1279 | lxc_log_syserror("failed to allocate memory"); | |
1280 | goto out; | |
1281 | } | |
1282 | ||
0ad19a3f | 1283 | if (write_info(path, "ifindex", strindex)) { |
1284 | lxc_log_error("failed to write interface index to %s", path); | |
1285 | goto out; | |
1286 | } | |
1287 | ||
1288 | if (!read_info(path, "up", strindex, sizeof(strindex))) { | |
1289 | if (device_up(veth1)) { | |
1290 | lxc_log_error("failed to set %s up", veth1); | |
1291 | goto out; | |
1292 | } | |
1293 | } | |
1294 | ||
1295 | ret = 0; | |
1296 | out: | |
1297 | free(path); | |
1298 | free(strindex); | |
1299 | free(veth1); | |
1300 | free(veth2); | |
1301 | return ret; | |
1302 | } | |
6f4a3756 | 1303 | static int instanciate_macvlan(const char *directory, const char *file, pid_t pid) |
0ad19a3f | 1304 | { |
1305 | char path[MAXPATHLEN], *strindex = NULL, *peer = NULL; | |
1306 | char link[IFNAMSIZ]; | |
1307 | int ifindex, ret = -1; | |
1308 | ||
22ebac19 | 1309 | if (!asprintf(&peer, "%s~%d", file, pid)) { |
1310 | lxc_log_syserror("failed to allocate memory"); | |
1311 | return -1; | |
1312 | } | |
1313 | ||
6f4a3756 | 1314 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
0ad19a3f | 1315 | if (read_info(path, "link", link, IFNAMSIZ)) { |
1316 | lxc_log_error("failed to read bridge info"); | |
1317 | goto out; | |
1318 | } | |
1319 | ||
1320 | if (lxc_configure_macvlan(link, peer)) { | |
1321 | lxc_log_error("failed to create macvlan interface %s", peer); | |
1322 | goto out; | |
1323 | } | |
1324 | ||
1325 | ifindex = if_nametoindex(peer); | |
1326 | if (!ifindex) { | |
1327 | lxc_log_error("failed to retrieve the index for %s", peer); | |
1328 | goto out; | |
1329 | } | |
1330 | ||
22ebac19 | 1331 | if (!asprintf(&strindex, "%d", ifindex)) { |
1332 | lxc_log_syserror("failed to allocate memory"); | |
1333 | return -1; | |
1334 | } | |
1335 | ||
0ad19a3f | 1336 | if (write_info(path, "ifindex", strindex)) { |
1337 | lxc_log_error("failed to write interface index to %s", path); | |
1338 | goto out; | |
1339 | } | |
1340 | ||
1341 | ret = 0; | |
1342 | out: | |
1343 | free(strindex); | |
1344 | free(peer); | |
1345 | return ret; | |
1346 | } | |
1347 | ||
6f4a3756 | 1348 | static int instanciate_phys(const char *directory, const char *file, pid_t pid) |
0ad19a3f | 1349 | { |
1350 | char path[MAXPATHLEN], *strindex = NULL; | |
1351 | char link[IFNAMSIZ]; | |
1352 | int ifindex, ret = -1; | |
1353 | ||
6f4a3756 | 1354 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
0ad19a3f | 1355 | if (read_info(path, "link", link, IFNAMSIZ)) { |
1356 | lxc_log_error("failed to read link info"); | |
1357 | goto out; | |
1358 | } | |
1359 | ||
1360 | ifindex = if_nametoindex(link); | |
1361 | if (!ifindex) { | |
1362 | lxc_log_error("failed to retrieve the index for %s", link); | |
1363 | goto out; | |
1364 | } | |
1365 | ||
22ebac19 | 1366 | if (!asprintf(&strindex, "%d", ifindex)) { |
1367 | lxc_log_syserror("failed to allocate memory"); | |
1368 | return -1; | |
1369 | } | |
1370 | ||
0ad19a3f | 1371 | if (write_info(path, "ifindex", strindex)) { |
1372 | lxc_log_error("failed to write interface index to %s", path); | |
1373 | goto out; | |
1374 | } | |
1375 | ||
1376 | ret = 0; | |
1377 | out: | |
1378 | free(strindex); | |
1379 | return ret; | |
1380 | } | |
1381 | ||
6f4a3756 | 1382 | static int instanciate_empty(const char *directory, const char *file, pid_t pid) |
0ad19a3f | 1383 | { |
1384 | char path[MAXPATHLEN], *strindex = NULL; | |
1385 | int ret = -1; | |
1386 | ||
6f4a3756 | 1387 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
0ad19a3f | 1388 | if (!asprintf(&strindex, "%d", 0)) { |
1389 | lxc_log_error("not enough memory"); | |
1390 | return -1; | |
1391 | } | |
1392 | ||
1393 | if (write_info(path, "ifindex", strindex)) { | |
1394 | lxc_log_error("failed to write interface index to %s", path); | |
1395 | goto out; | |
1396 | } | |
1397 | ||
1398 | ret = 0; | |
1399 | out: | |
1400 | free(strindex); | |
1401 | return ret; | |
1402 | } | |
1403 | ||
6f4a3756 | 1404 | static int instanciate_netdev_cb(const char *name, const char *directory, |
0ad19a3f | 1405 | const char *file, void *data) |
1406 | { | |
1407 | pid_t *pid = data; | |
1408 | ||
1409 | if (!strncmp("veth", file, strlen("veth"))) | |
6f4a3756 | 1410 | return instanciate_veth(directory, file, *pid); |
0ad19a3f | 1411 | else if (!strncmp("macvlan", file, strlen("macvlan"))) |
6f4a3756 | 1412 | return instanciate_macvlan(directory, file, *pid); |
0ad19a3f | 1413 | else if (!strncmp("phys", file, strlen("phys"))) |
6f4a3756 | 1414 | return instanciate_phys(directory, file, *pid); |
0ad19a3f | 1415 | else if (!strncmp("empty", file, strlen("empty"))) |
6f4a3756 | 1416 | return instanciate_empty(directory, file, *pid); |
0ad19a3f | 1417 | |
1418 | return -1; | |
1419 | } | |
1420 | ||
1421 | static int instanciate_netdev(const char *name, pid_t pid) | |
1422 | { | |
6f4a3756 | 1423 | char directory[MAXPATHLEN]; |
0ad19a3f | 1424 | |
6f4a3756 | 1425 | snprintf(directory, MAXPATHLEN, LXCPATH "/%s/network", name); |
1426 | return dir_for_each(name, directory, instanciate_netdev_cb, &pid); | |
0ad19a3f | 1427 | } |
1428 | ||
6f4a3756 | 1429 | static int move_netdev_cb(const char *name, const char *directory, |
0ad19a3f | 1430 | const char *file, void *data) |
1431 | { | |
1432 | char path[MAXPATHLEN], ifname[IFNAMSIZ], strindex[MAXINDEXLEN]; | |
1433 | pid_t *pid = data; | |
1434 | int ifindex; | |
1435 | ||
6f4a3756 | 1436 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
0ad19a3f | 1437 | if (read_info(path, "ifindex", strindex, MAXINDEXLEN) < 0) { |
1438 | lxc_log_error("failed to read index to from %s", path); | |
1439 | return -1; | |
1440 | } | |
1441 | ||
1442 | ifindex = atoi(strindex); | |
1443 | if (!ifindex) | |
1444 | return 0; | |
1445 | ||
1446 | if (!if_indextoname(ifindex, ifname)) { | |
1447 | lxc_log_error("interface with index %d does not exist", | |
1448 | ifindex); | |
1449 | return -1; | |
1450 | } | |
1451 | ||
1452 | if (device_move(ifname, *pid)) { | |
1453 | lxc_log_error("failed to move %s to %d", ifname, *pid); | |
1454 | return -1; | |
1455 | } | |
1456 | ||
1457 | return 0; | |
1458 | } | |
1459 | ||
1460 | static int move_netdev(const char *name, pid_t pid) | |
1461 | { | |
6f4a3756 | 1462 | char directory[MAXPATHLEN]; |
1463 | snprintf(directory, MAXPATHLEN, LXCPATH "/%s/network", name); | |
1464 | return dir_for_each(name, directory, move_netdev_cb, &pid); | |
0ad19a3f | 1465 | } |
1466 | ||
1467 | int conf_create_network(const char *name, pid_t pid) | |
1468 | { | |
1469 | if (instanciate_netdev(name, pid)) { | |
1470 | lxc_log_error("failed to instantiate the network devices"); | |
1471 | return -1; | |
1472 | } | |
1473 | ||
1474 | if (move_netdev(name, pid)) { | |
1475 | lxc_log_error("failed to move the netdev to the container"); | |
1476 | return -1; | |
1477 | } | |
1478 | ||
1479 | return 0; | |
1480 | } | |
1481 | ||
6f4a3756 | 1482 | static int delete_netdev_cb(const char *name, const char *directory, |
0ad19a3f | 1483 | const char *file, void *data) |
1484 | { | |
1485 | char strindex[MAXINDEXLEN]; | |
1486 | char path[MAXPATHLEN]; | |
1487 | char ifname[IFNAMSIZ]; | |
1488 | int i, ifindex; | |
1489 | ||
6f4a3756 | 1490 | snprintf(path, MAXPATHLEN, "%s/%s", directory, file); |
0ad19a3f | 1491 | |
1492 | if (read_info(path, "ifindex", strindex, MAXINDEXLEN)) { | |
1493 | lxc_log_error("failed to read ifindex info"); | |
1494 | return -1; | |
1495 | } | |
1496 | ||
1497 | ifindex = atoi(strindex); | |
1498 | if (!ifindex) | |
1499 | return 0; | |
1500 | ||
1501 | /* TODO : temporary code - needs wait on namespace */ | |
1502 | for (i = 0; i < 120; i++) { | |
1503 | if (if_indextoname(ifindex, ifname)) | |
1504 | break; | |
1505 | if (!i) | |
1506 | printf("waiting for interface #%d to come back\n", ifindex); | |
1507 | else | |
1508 | printf("."); fflush(stdout); | |
1509 | sleep(1); | |
1510 | } | |
1511 | ||
1512 | /* do not delete a physical network device */ | |
1513 | if (strncmp("phys", file, strlen("phys"))) | |
1514 | if (device_delete(ifname)) { | |
1515 | lxc_log_error("failed to remove the netdev %s", ifname); | |
1516 | } | |
1517 | ||
1518 | delete_info(path, "ifindex"); | |
1519 | ||
1520 | return 0; | |
1521 | } | |
1522 | ||
1523 | static int delete_netdev(const char *name) | |
1524 | { | |
6f4a3756 | 1525 | char directory[MAXPATHLEN]; |
0ad19a3f | 1526 | |
6f4a3756 | 1527 | snprintf(directory, MAXPATHLEN, LXCPATH "/%s/network", name); |
1528 | return dir_for_each(name, directory, delete_netdev_cb, NULL); | |
0ad19a3f | 1529 | } |
1530 | ||
1531 | int conf_destroy_network(const char *name) | |
1532 | { | |
6e590161 | 1533 | #ifdef NETWORK_DESTROY |
0ad19a3f | 1534 | if (delete_netdev(name)) { |
1535 | lxc_log_error("failed to remove the network devices"); | |
1536 | return -1; | |
1537 | } | |
6e590161 | 1538 | #endif |
0ad19a3f | 1539 | return 0; |
1540 | } | |
1541 | ||
b0a33c1e | 1542 | int lxc_create_tty(const char *name, struct lxc_tty_info *tty_info) |
1543 | { | |
1544 | char path[MAXPATHLEN]; | |
1545 | char tty[4]; | |
1546 | int i, ret = -1; | |
1547 | ||
1548 | tty_info->nbtty = 0; | |
1549 | ||
1550 | if (!conf_has_tty(name)) | |
1551 | return 0; | |
1552 | ||
79cf945c | 1553 | /* |
b0a33c1e | 1554 | if (!conf_has_rootfs(name)) { |
1555 | lxc_log_warning("no rootfs is configured, ignoring ttys"); | |
1556 | return 0; | |
1557 | } | |
79cf945c | 1558 | */ |
b0a33c1e | 1559 | snprintf(path, MAXPATHLEN, LXCPATH "/%s", name); |
1560 | ||
1561 | if (read_info(path, "tty", tty, sizeof(tty)) < 0) { | |
1562 | lxc_log_syserror("failed to read tty info"); | |
1563 | goto out; | |
1564 | } | |
1565 | ||
1566 | tty_info->nbtty = atoi(tty); | |
1567 | tty_info->pty_info = | |
1568 | malloc(sizeof(*tty_info->pty_info)*tty_info->nbtty); | |
1569 | ||
1570 | if (!tty_info->pty_info) { | |
1571 | lxc_log_syserror("failed to allocate pty_info"); | |
1572 | goto out; | |
1573 | } | |
1574 | ||
1575 | for (i = 0; i < tty_info->nbtty; i++) { | |
1576 | ||
1577 | struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; | |
1578 | ||
1579 | if (openpty(&pty_info->master, &pty_info->slave, | |
1580 | pty_info->name, NULL, NULL)) { | |
1581 | lxc_log_syserror("failed to create pty #%d", i); | |
1582 | goto out_free; | |
1583 | } | |
1584 | ||
1585 | pty_info->busy = 0; | |
1586 | } | |
1587 | ||
1588 | ret = 0; | |
1589 | out: | |
1590 | return ret; | |
1591 | ||
1592 | out_free: | |
1593 | free(tty_info->pty_info); | |
1594 | goto out; | |
1595 | } | |
1596 | ||
1597 | void lxc_delete_tty(struct lxc_tty_info *tty_info) | |
1598 | { | |
1599 | int i; | |
1600 | ||
1601 | for (i = 0; i < tty_info->nbtty; i++) { | |
1602 | struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; | |
1603 | ||
1604 | close(pty_info->master); | |
1605 | close(pty_info->slave); | |
1606 | } | |
1607 | ||
1608 | free(tty_info->pty_info); | |
1609 | tty_info->nbtty = 0; | |
1610 | } | |
1611 | ||
3c26f34e | 1612 | enum { utsname, network, cgroup, fstab, console, tty, rootfs, pts }; |
db4aa207 | 1613 | |
1614 | static int conf_is_set(long flags, int subsystem) | |
1615 | { | |
1616 | return flags & (1 << subsystem); | |
1617 | } | |
1618 | ||
1619 | static void conf_set_flag(long *flags, int subsystem) | |
1620 | { | |
1621 | *flags |= 1 << subsystem; | |
1622 | } | |
1623 | ||
1624 | static long make_conf_flagset(const char *name, const char *cons, | |
1625 | const struct lxc_tty_info *tty_info) | |
1626 | { | |
1627 | long flags = 0; | |
1628 | ||
1629 | if (conf_has_utsname(name)) | |
1630 | conf_set_flag(&flags, utsname); | |
1631 | ||
1632 | if (conf_has_network(name)) | |
1633 | conf_set_flag(&flags, network); | |
1634 | ||
1635 | if (conf_has_cgroup(name)) | |
1636 | conf_set_flag(&flags, cgroup); | |
1637 | ||
1638 | if (conf_has_fstab(name)) | |
1639 | conf_set_flag(&flags, fstab); | |
1640 | ||
1641 | if (conf_has_rootfs(name)) | |
1642 | conf_set_flag(&flags, rootfs); | |
1643 | ||
3c26f34e | 1644 | if (conf_has_pts(name)) |
1645 | conf_set_flag(&flags, pts); | |
1646 | ||
db4aa207 | 1647 | if (tty_info->nbtty) |
1648 | conf_set_flag(&flags, tty); | |
1649 | ||
1650 | if (cons[0]) | |
1651 | conf_set_flag(&flags, console); | |
1652 | ||
1653 | return flags; | |
1654 | } | |
1655 | ||
1656 | int lxc_setup(const char *name, const char *cons, | |
b0a33c1e | 1657 | const struct lxc_tty_info *tty_info) |
1658 | ||
0ad19a3f | 1659 | { |
db4aa207 | 1660 | /* store the conf flags set otherwise conf_has will not |
1661 | * work after chrooting */ | |
1662 | long flags = make_conf_flagset(name, cons, tty_info); | |
1663 | ||
1664 | if (conf_is_set(flags, utsname) && setup_utsname(name)) { | |
0ad19a3f | 1665 | lxc_log_error("failed to setup the utsname for '%s'", name); |
e5bda9ee | 1666 | return -LXC_ERROR_SETUP_UTSNAME; |
0ad19a3f | 1667 | } |
1668 | ||
db4aa207 | 1669 | if (conf_is_set(flags, network) && setup_network(name)) { |
0ad19a3f | 1670 | lxc_log_error("failed to setup the network for '%s'", name); |
e5bda9ee | 1671 | return -LXC_ERROR_SETUP_NETWORK; |
0ad19a3f | 1672 | } |
1673 | ||
db4aa207 | 1674 | if (conf_is_set(flags, cgroup) && setup_cgroup(name)) { |
8c6c9475 | 1675 | lxc_log_error("failed to setup the cgroups for '%s'", name); |
e5bda9ee | 1676 | return -LXC_ERROR_SETUP_CGROUP; |
0ad19a3f | 1677 | } |
1678 | ||
db4aa207 | 1679 | if (conf_is_set(flags, fstab) && setup_mount(name)) { |
6e590161 | 1680 | lxc_log_error("failed to setup the mounts for '%s'", name); |
e5bda9ee | 1681 | return -LXC_ERROR_SETUP_MOUNT; |
576f946d | 1682 | } |
1683 | ||
db4aa207 | 1684 | if (conf_is_set(flags, console) && setup_console(name, cons)) { |
6e590161 | 1685 | lxc_log_error("failed to setup the console for '%s'", name); |
1686 | return -LXC_ERROR_SETUP_CONSOLE; | |
1687 | } | |
1688 | ||
db4aa207 | 1689 | if (conf_is_set(flags, tty) && setup_tty(name, tty_info)) { |
b0a33c1e | 1690 | lxc_log_error("failed to setup the ttys for '%s'", name); |
1691 | return -LXC_ERROR_SETUP_TTY; | |
1692 | } | |
1693 | ||
db4aa207 | 1694 | if (conf_is_set(flags, rootfs) && setup_rootfs(name)) { |
ed502555 | 1695 | lxc_log_error("failed to set rootfs for '%s'", name); |
1696 | return -LXC_ERROR_SETUP_ROOTFS; | |
1697 | } | |
1698 | ||
3c26f34e | 1699 | if (conf_is_set(flags, pts) && setup_pts(name)) { |
1700 | lxc_log_error("failed to setup the new pts instance"); | |
1701 | return -LXC_ERROR_SETUP_PTS; | |
1702 | } | |
1703 | ||
0ad19a3f | 1704 | return 0; |
1705 | } |