]>
Commit | Line | Data |
---|---|---|
c2cc9f0a | 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 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <unistd.h> | |
27 | #include <errno.h> | |
28 | #include <sys/types.h> | |
29 | #include <sys/param.h> | |
30 | #include <sys/utsname.h> | |
31 | #include <arpa/inet.h> | |
32 | #include <netinet/in.h> | |
33 | #include <net/if.h> | |
34 | ||
b2718c72 | 35 | #include "parse.h" |
c2cc9f0a | 36 | |
b2718c72 | 37 | #include <lxc/lxc.h> |
36eb9bde CLG |
38 | #include <lxc/log.h> |
39 | ||
40 | lxc_log_define(lxc_confile, lxc); | |
576f946d | 41 | |
10db618d | 42 | static int config_pts(const char *, char *, struct lxc_conf *); |
b0a33c1e | 43 | static int config_tty(const char *, char *, struct lxc_conf *); |
576f946d | 44 | static int config_cgroup(const char *, char *, struct lxc_conf *); |
45 | static int config_mount(const char *, char *, struct lxc_conf *); | |
46 | static int config_rootfs(const char *, char *, struct lxc_conf *); | |
47 | static int config_utsname(const char *, char *, struct lxc_conf *); | |
48 | static int config_network_type(const char *, char *, struct lxc_conf *); | |
49 | static int config_network_flags(const char *, char *, struct lxc_conf *); | |
50 | static int config_network_link(const char *, char *, struct lxc_conf *); | |
51 | static int config_network_name(const char *, char *, struct lxc_conf *); | |
52 | static int config_network_hwaddr(const char *, char *, struct lxc_conf *); | |
442cbbe6 | 53 | static int config_network_mtu(const char *, char *, struct lxc_conf *); |
576f946d | 54 | static int config_network_ipv4(const char *, char *, struct lxc_conf *); |
55 | static int config_network_ipv6(const char *, char *, struct lxc_conf *); | |
c2cc9f0a | 56 | |
b2718c72 | 57 | typedef int (*config_cb)(const char *, char *, struct lxc_conf *); |
58 | ||
c2cc9f0a | 59 | struct config { |
60 | char *name; | |
c2cc9f0a | 61 | config_cb cb; |
62 | }; | |
63 | ||
576f946d | 64 | static struct config config[] = { |
65 | ||
10db618d | 66 | { "lxc.pts", config_pts }, |
b0a33c1e | 67 | { "lxc.tty", config_tty }, |
576f946d | 68 | { "lxc.cgroup", config_cgroup }, |
69 | { "lxc.mount", config_mount }, | |
70 | { "lxc.rootfs", config_rootfs }, | |
71 | { "lxc.utsname", config_utsname }, | |
72 | { "lxc.network.type", config_network_type }, | |
73 | { "lxc.network.flags", config_network_flags }, | |
74 | { "lxc.network.link", config_network_link }, | |
75 | { "lxc.network.name", config_network_name }, | |
76 | { "lxc.network.hwaddr", config_network_hwaddr }, | |
442cbbe6 | 77 | { "lxc.network.mtu", config_network_mtu }, |
576f946d | 78 | { "lxc.network.ipv4", config_network_ipv4 }, |
79 | { "lxc.network.ipv6", config_network_ipv6 }, | |
c2cc9f0a | 80 | }; |
81 | ||
82 | static const size_t config_size = sizeof(config)/sizeof(struct config); | |
83 | ||
84 | static struct config *getconfig(const char *key) | |
85 | { | |
86 | int i; | |
87 | ||
88 | for (i = 0; i < config_size; i++) | |
a871ff6b | 89 | if (!strncmp(config[i].name, key, |
c2cc9f0a | 90 | strlen(config[i].name))) |
91 | return &config[i]; | |
92 | return NULL; | |
93 | } | |
94 | ||
576f946d | 95 | static int config_network_type(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 96 | { |
97 | struct lxc_list *networks = &lxc_conf->networks; | |
98 | struct lxc_network *network; | |
99 | struct lxc_netdev *netdev; | |
100 | struct lxc_list *list; | |
101 | struct lxc_list *ndlist; | |
102 | ||
103 | network = malloc(sizeof(*network)); | |
104 | if (!network) { | |
36eb9bde | 105 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 106 | return -1; |
107 | } | |
a871ff6b | 108 | |
c2cc9f0a | 109 | lxc_list_init(&network->netdev); |
110 | ||
111 | netdev = malloc(sizeof(*netdev)); | |
112 | if (!netdev) { | |
36eb9bde | 113 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 114 | return -1; |
115 | } | |
116 | ||
117 | lxc_list_init(&netdev->ipv4); | |
118 | lxc_list_init(&netdev->ipv6); | |
119 | lxc_list_init(&netdev->route4); | |
120 | lxc_list_init(&netdev->route6); | |
121 | ||
122 | ndlist = malloc(sizeof(*ndlist)); | |
123 | if (!ndlist) { | |
36eb9bde | 124 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 125 | return -1; |
126 | } | |
127 | ||
128 | ndlist->elem = netdev; | |
129 | ||
130 | lxc_list_add(&network->netdev, ndlist); | |
131 | ||
132 | list = malloc(sizeof(*list)); | |
133 | if (!list) { | |
36eb9bde | 134 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 135 | return -1; |
136 | } | |
137 | ||
138 | lxc_list_init(list); | |
139 | list->elem = network; | |
140 | ||
141 | lxc_list_add(networks, list); | |
a871ff6b | 142 | |
c2cc9f0a | 143 | if (!strcmp(value, "veth")) |
144 | network->type = VETH; | |
145 | else if (!strcmp(value, "macvlan")) | |
146 | network->type = MACVLAN; | |
147 | else if (!strcmp(value, "phys")) | |
148 | network->type = PHYS; | |
5f58350a | 149 | else if (!strcmp(value, "empty")) |
150 | network->type = EMPTY; | |
c2cc9f0a | 151 | else { |
36eb9bde | 152 | ERROR("invalid network type %s", value); |
c2cc9f0a | 153 | return -1; |
154 | } | |
155 | return 0; | |
156 | } | |
157 | ||
576f946d | 158 | static int config_network_flags(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 159 | { |
160 | struct lxc_list *networks = &lxc_conf->networks; | |
161 | struct lxc_network *network; | |
162 | struct lxc_netdev *netdev; | |
163 | ||
164 | if (lxc_list_empty(networks)) { | |
36eb9bde | 165 | ERROR("network is not created for '%s' option", value); |
c2cc9f0a | 166 | return -1; |
167 | } | |
168 | ||
169 | network = lxc_list_first_elem(networks); | |
170 | if (!network) { | |
36eb9bde | 171 | ERROR("no network defined for '%s' option", value); |
c2cc9f0a | 172 | return -1; |
173 | } | |
174 | ||
175 | netdev = lxc_list_first_elem(&network->netdev); | |
176 | netdev->flags |= IFF_UP; | |
177 | return 0; | |
178 | } | |
179 | ||
576f946d | 180 | static int config_network_link(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 181 | { |
182 | struct lxc_list *networks = &lxc_conf->networks; | |
183 | struct lxc_network *network; | |
184 | struct lxc_netdev *netdev; | |
185 | ||
186 | if (lxc_list_empty(networks)) { | |
36eb9bde | 187 | ERROR("network is not created for %s", value); |
c2cc9f0a | 188 | return -1; |
189 | } | |
190 | ||
191 | network = lxc_list_first_elem(networks); | |
192 | if (!network) { | |
36eb9bde | 193 | ERROR("no network defined for %s", value); |
c2cc9f0a | 194 | return -1; |
195 | } | |
196 | ||
197 | if (strlen(value) > IFNAMSIZ) { | |
36eb9bde | 198 | ERROR("invalid interface name: %s", value); |
c2cc9f0a | 199 | return -1; |
200 | } | |
201 | ||
202 | netdev = lxc_list_first_elem(&network->netdev); | |
203 | netdev->ifname = strdup(value); | |
204 | return 0; | |
205 | } | |
206 | ||
576f946d | 207 | static int config_network_name(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 208 | { |
209 | struct lxc_list *networks = &lxc_conf->networks; | |
210 | struct lxc_network *network; | |
211 | struct lxc_netdev *netdev; | |
212 | ||
213 | if (lxc_list_empty(networks)) { | |
36eb9bde | 214 | ERROR("network is not created for %s", value); |
c2cc9f0a | 215 | return -1; |
216 | } | |
217 | ||
218 | network = lxc_list_first_elem(networks); | |
219 | if (!network) { | |
36eb9bde | 220 | ERROR("no network defined for %s", value); |
c2cc9f0a | 221 | return -1; |
222 | } | |
223 | ||
224 | if (strlen(value) > IFNAMSIZ) { | |
36eb9bde | 225 | ERROR("invalid interface name: %s", value); |
c2cc9f0a | 226 | return -1; |
227 | } | |
228 | ||
229 | netdev = lxc_list_first_elem(&network->netdev); | |
230 | netdev->newname = strdup(value); | |
231 | return 0; | |
232 | } | |
233 | ||
576f946d | 234 | static int config_network_hwaddr(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 235 | { |
236 | struct lxc_list *networks = &lxc_conf->networks; | |
237 | struct lxc_network *network; | |
238 | struct lxc_netdev *netdev; | |
239 | ||
240 | if (lxc_list_empty(networks)) { | |
36eb9bde | 241 | ERROR("network is not created for %s", value); |
c2cc9f0a | 242 | return -1; |
243 | } | |
244 | ||
245 | network = lxc_list_first_elem(networks); | |
246 | if (!network) { | |
36eb9bde | 247 | ERROR("no network defined for %s", value); |
c2cc9f0a | 248 | return -1; |
249 | } | |
250 | ||
251 | netdev = lxc_list_first_elem(&network->netdev); | |
252 | netdev->hwaddr = strdup(value); | |
253 | return 0; | |
254 | } | |
255 | ||
442cbbe6 TR |
256 | static int config_network_mtu(const char *key, char *value, struct lxc_conf *lxc_conf) |
257 | { | |
258 | struct lxc_list *networks = &lxc_conf->networks; | |
259 | struct lxc_network *network; | |
260 | struct lxc_netdev *netdev; | |
261 | ||
262 | if (lxc_list_empty(networks)) { | |
36eb9bde | 263 | ERROR("network is not created for %s", value); |
442cbbe6 TR |
264 | return -1; |
265 | } | |
266 | ||
267 | network = lxc_list_first_elem(networks); | |
268 | if (!network) { | |
36eb9bde | 269 | ERROR("no network defined for %s", value); |
442cbbe6 TR |
270 | return -1; |
271 | } | |
272 | ||
273 | netdev = lxc_list_first_elem(&network->netdev); | |
274 | netdev->mtu = strdup(value); | |
275 | return 0; | |
276 | } | |
277 | ||
576f946d | 278 | static int config_network_ipv4(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 279 | { |
280 | struct lxc_list *networks = &lxc_conf->networks; | |
281 | struct lxc_network *network; | |
282 | struct lxc_inetdev *inetdev; | |
283 | struct lxc_netdev *netdev; | |
284 | struct lxc_list *list; | |
285 | char *cursor, *slash, *addr = NULL, *bcast = NULL, *prefix = NULL; | |
286 | ||
287 | if (lxc_list_empty(networks)) { | |
36eb9bde | 288 | ERROR("network is not created for '%s'", value); |
c2cc9f0a | 289 | return -1; |
290 | } | |
291 | ||
292 | network = lxc_list_first_elem(networks); | |
293 | if (!network) { | |
36eb9bde | 294 | ERROR("no network defined for '%s'", value); |
c2cc9f0a | 295 | return -1; |
296 | } | |
297 | ||
298 | netdev = lxc_list_first_elem(&network->netdev); | |
299 | if (!netdev) { | |
36eb9bde | 300 | ERROR("no netdev defined for '%s'", value); |
c2cc9f0a | 301 | } |
302 | ||
303 | inetdev = malloc(sizeof(*inetdev)); | |
304 | if (!inetdev) { | |
36eb9bde | 305 | SYSERROR("failed to allocate ipv4 address"); |
c2cc9f0a | 306 | return -1; |
307 | } | |
308 | memset(inetdev, 0, sizeof(*inetdev)); | |
309 | ||
310 | list = malloc(sizeof(*list)); | |
311 | if (!list) { | |
36eb9bde | 312 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 313 | return -1; |
314 | } | |
315 | ||
316 | lxc_list_init(list); | |
317 | list->elem = inetdev; | |
318 | ||
319 | addr = value; | |
320 | ||
321 | cursor = strstr(addr, " "); | |
322 | if (cursor) { | |
323 | *cursor = '\0'; | |
324 | bcast = cursor + 1; | |
325 | } | |
326 | ||
327 | slash = strstr(addr, "/"); | |
328 | if (slash) { | |
329 | *slash = '\0'; | |
330 | prefix = slash + 1; | |
331 | } | |
332 | ||
333 | if (!addr) { | |
36eb9bde | 334 | ERROR("no address specified"); |
c2cc9f0a | 335 | return -1; |
336 | } | |
337 | ||
338 | if (!inet_pton(AF_INET, addr, &inetdev->addr)) { | |
36eb9bde | 339 | SYSERROR("invalid ipv4 address: %s", value); |
c2cc9f0a | 340 | return -1; |
341 | } | |
342 | ||
343 | if (bcast) | |
344 | if (!inet_pton(AF_INET, bcast, &inetdev->bcast)) { | |
36eb9bde | 345 | SYSERROR("invalid ipv4 address: %s", value); |
c2cc9f0a | 346 | return -1; |
347 | } | |
348 | ||
349 | if (prefix) | |
350 | inetdev->prefix = atoi(prefix); | |
351 | ||
352 | lxc_list_add(&netdev->ipv4, list); | |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
576f946d | 357 | static int config_network_ipv6(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 358 | { |
359 | struct lxc_list *networks = &lxc_conf->networks; | |
360 | struct lxc_network *network; | |
361 | struct lxc_netdev *netdev; | |
362 | struct lxc_inet6dev *inet6dev; | |
363 | struct lxc_list *list; | |
364 | char *slash; | |
365 | char *netmask; | |
366 | ||
367 | if (lxc_list_empty(networks)) { | |
36eb9bde | 368 | ERROR("network is not created for %s", value); |
c2cc9f0a | 369 | return -1; |
370 | } | |
371 | ||
372 | network = lxc_list_first_elem(networks); | |
373 | if (!network) { | |
36eb9bde | 374 | ERROR("no network defined for %s", value); |
c2cc9f0a | 375 | return -1; |
376 | } | |
377 | ||
378 | inet6dev = malloc(sizeof(*inet6dev)); | |
379 | if (!inet6dev) { | |
36eb9bde | 380 | SYSERROR("failed to allocate ipv6 address"); |
c2cc9f0a | 381 | return -1; |
382 | } | |
383 | memset(inet6dev, 0, sizeof(*inet6dev)); | |
384 | ||
385 | list = malloc(sizeof(*list)); | |
386 | if (!list) { | |
36eb9bde | 387 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 388 | return -1; |
389 | } | |
390 | ||
391 | lxc_list_init(list); | |
392 | list->elem = inet6dev; | |
393 | ||
394 | slash = strstr(value, "/"); | |
395 | if (slash) { | |
396 | *slash = '\0'; | |
397 | netmask = slash + 1; | |
398 | inet6dev->prefix = atoi(netmask); | |
399 | } | |
400 | ||
401 | if (!inet_pton(AF_INET6, value, &inet6dev->addr)) { | |
36eb9bde | 402 | SYSERROR("invalid ipv6 address: %s", value); |
c2cc9f0a | 403 | return -1; |
404 | } | |
405 | ||
406 | ||
407 | netdev = lxc_list_first_elem(&network->netdev); | |
408 | lxc_list_add(&netdev->ipv6, list); | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
10db618d | 413 | static int config_pts(const char *key, char *value, struct lxc_conf *lxc_conf) |
414 | { | |
415 | int maxpts = atoi(value); | |
416 | ||
417 | lxc_conf->pts = maxpts; | |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
b0a33c1e | 422 | static int config_tty(const char *key, char *value, struct lxc_conf *lxc_conf) |
423 | { | |
424 | int nbtty = atoi(value); | |
425 | ||
426 | lxc_conf->tty = nbtty; | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
576f946d | 431 | static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf) |
432 | { | |
433 | char *token = "lxc.cgroup."; | |
434 | char *subkey; | |
435 | struct lxc_list *cglist; | |
436 | struct lxc_cgroup *cgelem; | |
437 | ||
438 | subkey = strstr(key, token); | |
439 | ||
440 | if (!subkey) | |
441 | return -1; | |
442 | ||
443 | if (!strlen(subkey)) | |
444 | return -1; | |
445 | ||
446 | if (strlen(subkey) == strlen(token)) | |
447 | return -1; | |
a871ff6b | 448 | |
576f946d | 449 | subkey += strlen(token); |
450 | ||
451 | cglist = malloc(sizeof(*cglist)); | |
452 | if (!cglist) | |
453 | return -1; | |
454 | ||
455 | cgelem = malloc(sizeof(*cgelem)); | |
456 | if (!cgelem) { | |
457 | free(cglist); | |
458 | return -1; | |
459 | } | |
460 | ||
461 | cgelem->subsystem = strdup(subkey); | |
462 | cgelem->value = strdup(value); | |
463 | cglist->elem = cgelem; | |
464 | ||
94d12f0a | 465 | lxc_list_add_tail(&lxc_conf->cgroup, cglist); |
576f946d | 466 | |
467 | return 0; | |
468 | } | |
469 | ||
470 | static int config_mount(const char *key, char *value, struct lxc_conf *lxc_conf) | |
c2cc9f0a | 471 | { |
472 | if (strlen(value) >= MAXPATHLEN) { | |
36eb9bde | 473 | ERROR("%s path is too long", value); |
c2cc9f0a | 474 | return -1; |
475 | } | |
476 | ||
477 | lxc_conf->fstab = strdup(value); | |
478 | if (!lxc_conf->fstab) { | |
36eb9bde | 479 | SYSERROR("failed to duplicate string %s", value); |
c2cc9f0a | 480 | return -1; |
481 | } | |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
576f946d | 486 | static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 487 | { |
488 | if (strlen(value) >= MAXPATHLEN) { | |
36eb9bde | 489 | ERROR("%s path is too long", value); |
c2cc9f0a | 490 | return -1; |
491 | } | |
492 | ||
eae6543d | 493 | lxc_conf->rootfs = strdup(value); |
494 | if (!lxc_conf->rootfs) { | |
36eb9bde | 495 | SYSERROR("failed to duplicate string %s", value); |
c2cc9f0a | 496 | return -1; |
497 | } | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
576f946d | 502 | static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 503 | { |
504 | struct utsname *utsname; | |
505 | ||
506 | utsname = malloc(sizeof(*utsname)); | |
507 | if (!utsname) { | |
36eb9bde | 508 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 509 | return -1; |
510 | } | |
511 | ||
512 | if (strlen(value) >= sizeof(utsname->nodename)) { | |
36eb9bde | 513 | ERROR("node name '%s' is too long", |
c2cc9f0a | 514 | utsname->nodename); |
515 | return -1; | |
516 | } | |
517 | ||
518 | strcpy(utsname->nodename, value); | |
519 | lxc_conf->utsname = utsname; | |
520 | ||
521 | return 0; | |
522 | } | |
523 | ||
b2718c72 | 524 | static int parse_line(void *buffer, void *data) |
c2cc9f0a | 525 | { |
526 | struct config *config; | |
b2718c72 | 527 | char *line = buffer; |
c2cc9f0a | 528 | char *dot; |
529 | char *key; | |
530 | char *value; | |
531 | ||
b2718c72 | 532 | if (lxc_is_line_empty(line)) |
c2cc9f0a | 533 | return 0; |
534 | ||
b2718c72 | 535 | line += lxc_char_left_gc(line, strlen(line)); |
536 | if (line[0] == '#') | |
c2cc9f0a | 537 | return 0; |
538 | ||
b2718c72 | 539 | dot = strstr(line, "="); |
c2cc9f0a | 540 | if (!dot) { |
36eb9bde | 541 | ERROR("invalid configuration line: %s", line); |
c2cc9f0a | 542 | return -1; |
543 | } | |
a871ff6b | 544 | |
c2cc9f0a | 545 | *dot = '\0'; |
546 | value = dot + 1; | |
547 | ||
b2718c72 | 548 | key = line; |
549 | key[lxc_char_right_gc(key, strlen(key))] = '\0'; | |
c2cc9f0a | 550 | |
b2718c72 | 551 | value += lxc_char_left_gc(value, strlen(value)); |
552 | value[lxc_char_right_gc(value, strlen(value))] = '\0'; | |
c2cc9f0a | 553 | |
554 | config = getconfig(key); | |
555 | if (!config) { | |
36eb9bde | 556 | ERROR("unknow key %s", key); |
c2cc9f0a | 557 | return -1; |
558 | } | |
559 | ||
576f946d | 560 | return config->cb(key, value, data); |
c2cc9f0a | 561 | } |
562 | ||
b2718c72 | 563 | int lxc_config_read(const char *file, struct lxc_conf *conf) |
c2cc9f0a | 564 | { |
565 | char buffer[MAXPATHLEN]; | |
c2cc9f0a | 566 | |
cfd1dc09 MN |
567 | conf->rcfile = file; |
568 | ||
b2718c72 | 569 | return lxc_file_for_each_line(file, parse_line, buffer, |
570 | sizeof(buffer), conf); | |
c2cc9f0a | 571 | } |