]>
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> | |
63376d7d DL |
28 | #include <fcntl.h> |
29 | #include <pty.h> | |
30 | #include <sys/stat.h> | |
c2cc9f0a | 31 | #include <sys/types.h> |
32 | #include <sys/param.h> | |
33 | #include <sys/utsname.h> | |
cccc74b5 | 34 | #include <sys/personality.h> |
c2cc9f0a | 35 | #include <arpa/inet.h> |
36 | #include <netinet/in.h> | |
37 | #include <net/if.h> | |
38 | ||
b2718c72 | 39 | #include "parse.h" |
26c39028 | 40 | #include "utils.h" |
c2cc9f0a | 41 | |
36eb9bde | 42 | #include <lxc/log.h> |
00b3c2e2 | 43 | #include <lxc/conf.h> |
36eb9bde CLG |
44 | |
45 | lxc_log_define(lxc_confile, lxc); | |
576f946d | 46 | |
cccc74b5 | 47 | static int config_personality(const char *, char *, struct lxc_conf *); |
10db618d | 48 | static int config_pts(const char *, char *, struct lxc_conf *); |
b0a33c1e | 49 | static int config_tty(const char *, char *, struct lxc_conf *); |
576f946d | 50 | static int config_cgroup(const char *, char *, struct lxc_conf *); |
51 | static int config_mount(const char *, char *, struct lxc_conf *); | |
52 | static int config_rootfs(const char *, char *, struct lxc_conf *); | |
23b7ea69 | 53 | static int config_rootfs_mount(const char *, char *, struct lxc_conf *); |
bf601689 | 54 | static int config_pivotdir(const char *, char *, struct lxc_conf *); |
576f946d | 55 | static int config_utsname(const char *, char *, struct lxc_conf *); |
56 | static int config_network_type(const char *, char *, struct lxc_conf *); | |
57 | static int config_network_flags(const char *, char *, struct lxc_conf *); | |
58 | static int config_network_link(const char *, char *, struct lxc_conf *); | |
59 | static int config_network_name(const char *, char *, struct lxc_conf *); | |
e892973e DL |
60 | static int config_network_veth_pair(const char *, char *, struct lxc_conf *); |
61 | static int config_network_macvlan_mode(const char *, char *, struct lxc_conf *); | |
576f946d | 62 | static int config_network_hwaddr(const char *, char *, struct lxc_conf *); |
e892973e | 63 | static int config_network_vlan_id(const char *, char *, struct lxc_conf *); |
442cbbe6 | 64 | static int config_network_mtu(const char *, char *, struct lxc_conf *); |
576f946d | 65 | static int config_network_ipv4(const char *, char *, struct lxc_conf *); |
e3b4c4c4 | 66 | static int config_network_script(const char *, char *, struct lxc_conf *); |
576f946d | 67 | static int config_network_ipv6(const char *, char *, struct lxc_conf *); |
81810dd1 | 68 | static int config_cap_drop(const char *, char *, struct lxc_conf *); |
63376d7d | 69 | static int config_console(const char *, char *, struct lxc_conf *); |
c2cc9f0a | 70 | |
b2718c72 | 71 | typedef int (*config_cb)(const char *, char *, struct lxc_conf *); |
72 | ||
c2cc9f0a | 73 | struct config { |
74 | char *name; | |
c2cc9f0a | 75 | config_cb cb; |
76 | }; | |
77 | ||
576f946d | 78 | static struct config config[] = { |
79 | ||
cccc74b5 | 80 | { "lxc.arch", config_personality }, |
e892973e DL |
81 | { "lxc.pts", config_pts }, |
82 | { "lxc.tty", config_tty }, | |
83 | { "lxc.cgroup", config_cgroup }, | |
84 | { "lxc.mount", config_mount }, | |
23b7ea69 | 85 | { "lxc.rootfs.mount", config_rootfs_mount }, |
e892973e | 86 | { "lxc.rootfs", config_rootfs }, |
bf601689 | 87 | { "lxc.pivotdir", config_pivotdir }, |
e892973e DL |
88 | { "lxc.utsname", config_utsname }, |
89 | { "lxc.network.type", config_network_type }, | |
90 | { "lxc.network.flags", config_network_flags }, | |
91 | { "lxc.network.link", config_network_link }, | |
92 | { "lxc.network.name", config_network_name }, | |
93 | { "lxc.network.macvlan.mode", config_network_macvlan_mode }, | |
94 | { "lxc.network.veth.pair", config_network_veth_pair }, | |
e3b4c4c4 | 95 | { "lxc.network.script.up", config_network_script }, |
e892973e DL |
96 | { "lxc.network.hwaddr", config_network_hwaddr }, |
97 | { "lxc.network.mtu", config_network_mtu }, | |
98 | { "lxc.network.vlan.id", config_network_vlan_id }, | |
99 | { "lxc.network.ipv4", config_network_ipv4 }, | |
100 | { "lxc.network.ipv6", config_network_ipv6 }, | |
81810dd1 | 101 | { "lxc.cap.drop", config_cap_drop }, |
63376d7d | 102 | { "lxc.console", config_console }, |
c2cc9f0a | 103 | }; |
104 | ||
105 | static const size_t config_size = sizeof(config)/sizeof(struct config); | |
106 | ||
107 | static struct config *getconfig(const char *key) | |
108 | { | |
109 | int i; | |
110 | ||
111 | for (i = 0; i < config_size; i++) | |
a871ff6b | 112 | if (!strncmp(config[i].name, key, |
c2cc9f0a | 113 | strlen(config[i].name))) |
114 | return &config[i]; | |
115 | return NULL; | |
116 | } | |
117 | ||
e892973e DL |
118 | static int config_network_type(const char *key, char *value, |
119 | struct lxc_conf *lxc_conf) | |
c2cc9f0a | 120 | { |
5f4535a3 | 121 | struct lxc_list *network = &lxc_conf->network; |
c2cc9f0a | 122 | struct lxc_netdev *netdev; |
123 | struct lxc_list *list; | |
c2cc9f0a | 124 | |
125 | netdev = malloc(sizeof(*netdev)); | |
126 | if (!netdev) { | |
36eb9bde | 127 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 128 | return -1; |
129 | } | |
130 | ||
82d5ae15 | 131 | memset(netdev, 0, sizeof(*netdev)); |
c2cc9f0a | 132 | lxc_list_init(&netdev->ipv4); |
133 | lxc_list_init(&netdev->ipv6); | |
c2cc9f0a | 134 | |
135 | list = malloc(sizeof(*list)); | |
136 | if (!list) { | |
36eb9bde | 137 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 138 | return -1; |
139 | } | |
140 | ||
141 | lxc_list_init(list); | |
5f4535a3 | 142 | list->elem = netdev; |
c2cc9f0a | 143 | |
bac89583 | 144 | lxc_list_add_tail(network, list); |
a871ff6b | 145 | |
c2cc9f0a | 146 | if (!strcmp(value, "veth")) |
24654103 | 147 | netdev->type = LXC_NET_VETH; |
c2cc9f0a | 148 | else if (!strcmp(value, "macvlan")) |
24654103 | 149 | netdev->type = LXC_NET_MACVLAN; |
26c39028 | 150 | else if (!strcmp(value, "vlan")) |
24654103 | 151 | netdev->type = LXC_NET_VLAN; |
c2cc9f0a | 152 | else if (!strcmp(value, "phys")) |
24654103 | 153 | netdev->type = LXC_NET_PHYS; |
5f58350a | 154 | else if (!strcmp(value, "empty")) |
24654103 | 155 | netdev->type = LXC_NET_EMPTY; |
c2cc9f0a | 156 | else { |
36eb9bde | 157 | ERROR("invalid network type %s", value); |
c2cc9f0a | 158 | return -1; |
159 | } | |
160 | return 0; | |
161 | } | |
162 | ||
a059591e DL |
163 | static int config_ip_prefix(struct in_addr *addr) |
164 | { | |
165 | if (IN_CLASSA(addr->s_addr)) | |
166 | return 32 - IN_CLASSA_NSHIFT; | |
167 | if (IN_CLASSB(addr->s_addr)) | |
168 | return 32 - IN_CLASSB_NSHIFT; | |
169 | if (IN_CLASSC(addr->s_addr)) | |
170 | return 32 - IN_CLASSC_NSHIFT; | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
16950ecb DL |
175 | static struct lxc_netdev *network_netdev(const char *key, const char *value, |
176 | struct lxc_list *network) | |
c2cc9f0a | 177 | { |
c2cc9f0a | 178 | struct lxc_netdev *netdev; |
179 | ||
5f4535a3 | 180 | if (lxc_list_empty(network)) { |
16950ecb DL |
181 | ERROR("network is not created for '%s' = '%s' option", |
182 | key, value); | |
33c945e0 | 183 | return NULL; |
c2cc9f0a | 184 | } |
185 | ||
bac89583 | 186 | netdev = lxc_list_last_elem(network); |
5f4535a3 | 187 | if (!netdev) { |
16950ecb DL |
188 | ERROR("no network device defined for '%s' = '%s' option", |
189 | key, value); | |
33c945e0 | 190 | return NULL; |
c2cc9f0a | 191 | } |
192 | ||
33c945e0 | 193 | return netdev; |
c2cc9f0a | 194 | } |
195 | ||
33c945e0 | 196 | static int network_ifname(char **valuep, char *value) |
c2cc9f0a | 197 | { |
bf83c5b9 | 198 | if (strlen(value) >= IFNAMSIZ) { |
36eb9bde | 199 | ERROR("invalid interface name: %s", value); |
c2cc9f0a | 200 | return -1; |
201 | } | |
202 | ||
33c945e0 | 203 | *valuep = strdup(value); |
16950ecb DL |
204 | if (!*valuep) { |
205 | ERROR("failed to dup string '%s'", value); | |
206 | return -1; | |
207 | } | |
33c945e0 | 208 | |
c2cc9f0a | 209 | return 0; |
210 | } | |
211 | ||
e892973e DL |
212 | #ifndef MACVLAN_MODE_PRIVATE |
213 | # define MACVLAN_MODE_PRIVATE 1 | |
214 | #endif | |
215 | ||
216 | #ifndef MACVLAN_MODE_VEPA | |
217 | # define MACVLAN_MODE_VEPA 2 | |
218 | #endif | |
219 | ||
220 | #ifndef MACVLAN_MODE_BRIDGE | |
221 | # define MACVLAN_MODE_BRIDGE 4 | |
222 | #endif | |
223 | ||
224 | static int macvlan_mode(int *valuep, char *value) | |
225 | { | |
226 | struct mc_mode { | |
227 | char *name; | |
228 | int mode; | |
229 | } m[] = { | |
230 | { "private", MACVLAN_MODE_PRIVATE }, | |
231 | { "vepa", MACVLAN_MODE_VEPA }, | |
232 | { "bridge", MACVLAN_MODE_BRIDGE }, | |
233 | }; | |
234 | ||
235 | int i; | |
236 | ||
237 | for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) { | |
238 | if (strcmp(m[i].name, value)) | |
239 | continue; | |
240 | ||
241 | *valuep = m[i].mode; | |
242 | return 0; | |
243 | } | |
244 | ||
245 | return -1; | |
246 | } | |
247 | ||
33c945e0 MT |
248 | static int config_network_flags(const char *key, char *value, |
249 | struct lxc_conf *lxc_conf) | |
c2cc9f0a | 250 | { |
c2cc9f0a | 251 | struct lxc_netdev *netdev; |
252 | ||
16950ecb | 253 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 254 | if (!netdev) |
c2cc9f0a | 255 | return -1; |
c2cc9f0a | 256 | |
33c945e0 | 257 | netdev->flags |= IFF_UP; |
c2cc9f0a | 258 | |
33c945e0 MT |
259 | return 0; |
260 | } | |
261 | ||
262 | static int config_network_link(const char *key, char *value, | |
263 | struct lxc_conf *lxc_conf) | |
264 | { | |
265 | struct lxc_netdev *netdev; | |
266 | ||
16950ecb | 267 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 268 | if (!netdev) |
c2cc9f0a | 269 | return -1; |
c2cc9f0a | 270 | |
33c945e0 | 271 | return network_ifname(&netdev->link, value); |
c2cc9f0a | 272 | } |
273 | ||
33c945e0 MT |
274 | static int config_network_name(const char *key, char *value, |
275 | struct lxc_conf *lxc_conf) | |
c2cc9f0a | 276 | { |
c2cc9f0a | 277 | struct lxc_netdev *netdev; |
278 | ||
16950ecb | 279 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 280 | if (!netdev) |
c2cc9f0a | 281 | return -1; |
c2cc9f0a | 282 | |
33c945e0 MT |
283 | return network_ifname(&netdev->name, value); |
284 | } | |
285 | ||
e892973e DL |
286 | static int config_network_veth_pair(const char *key, char *value, |
287 | struct lxc_conf *lxc_conf) | |
288 | { | |
289 | struct lxc_netdev *netdev; | |
290 | ||
291 | netdev = network_netdev(key, value, &lxc_conf->network); | |
292 | if (!netdev) | |
293 | return -1; | |
294 | ||
295 | return network_ifname(&netdev->priv.veth_attr.pair, value); | |
296 | } | |
297 | ||
298 | static int config_network_macvlan_mode(const char *key, char *value, | |
299 | struct lxc_conf *lxc_conf) | |
8634bc19 MT |
300 | { |
301 | struct lxc_netdev *netdev; | |
302 | ||
303 | netdev = network_netdev(key, value, &lxc_conf->network); | |
304 | if (!netdev) | |
305 | return -1; | |
306 | ||
e892973e | 307 | return macvlan_mode(&netdev->priv.macvlan_attr.mode, value); |
8634bc19 MT |
308 | } |
309 | ||
33c945e0 MT |
310 | static int config_network_hwaddr(const char *key, char *value, |
311 | struct lxc_conf *lxc_conf) | |
312 | { | |
313 | struct lxc_netdev *netdev; | |
314 | ||
16950ecb | 315 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 316 | if (!netdev) |
c2cc9f0a | 317 | return -1; |
c2cc9f0a | 318 | |
c2cc9f0a | 319 | netdev->hwaddr = strdup(value); |
16950ecb DL |
320 | if (!netdev->hwaddr) { |
321 | SYSERROR("failed to dup string '%s'", value); | |
322 | return -1; | |
323 | } | |
33c945e0 | 324 | |
c2cc9f0a | 325 | return 0; |
326 | } | |
327 | ||
e892973e | 328 | static int config_network_vlan_id(const char *key, char *value, |
26c39028 JHS |
329 | struct lxc_conf *lxc_conf) |
330 | { | |
331 | struct lxc_netdev *netdev; | |
332 | ||
333 | netdev = network_netdev(key, value, &lxc_conf->network); | |
334 | if (!netdev) | |
335 | return -1; | |
336 | ||
f6cc1de1 | 337 | if (get_u16(&netdev->priv.vlan_attr.vid, value, 0)) |
26c39028 JHS |
338 | return -1; |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
33c945e0 MT |
343 | static int config_network_mtu(const char *key, char *value, |
344 | struct lxc_conf *lxc_conf) | |
442cbbe6 | 345 | { |
442cbbe6 TR |
346 | struct lxc_netdev *netdev; |
347 | ||
16950ecb | 348 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 349 | if (!netdev) |
442cbbe6 | 350 | return -1; |
442cbbe6 | 351 | |
442cbbe6 | 352 | netdev->mtu = strdup(value); |
16950ecb DL |
353 | if (!netdev->mtu) { |
354 | SYSERROR("failed to dup string '%s'", value); | |
355 | return -1; | |
356 | } | |
33c945e0 | 357 | |
442cbbe6 TR |
358 | return 0; |
359 | } | |
360 | ||
33c945e0 MT |
361 | static int config_network_ipv4(const char *key, char *value, |
362 | struct lxc_conf *lxc_conf) | |
c2cc9f0a | 363 | { |
c2cc9f0a | 364 | struct lxc_netdev *netdev; |
33c945e0 | 365 | struct lxc_inetdev *inetdev; |
c2cc9f0a | 366 | struct lxc_list *list; |
367 | char *cursor, *slash, *addr = NULL, *bcast = NULL, *prefix = NULL; | |
368 | ||
16950ecb | 369 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 370 | if (!netdev) |
c2cc9f0a | 371 | return -1; |
c2cc9f0a | 372 | |
373 | inetdev = malloc(sizeof(*inetdev)); | |
374 | if (!inetdev) { | |
36eb9bde | 375 | SYSERROR("failed to allocate ipv4 address"); |
c2cc9f0a | 376 | return -1; |
377 | } | |
378 | memset(inetdev, 0, sizeof(*inetdev)); | |
379 | ||
380 | list = malloc(sizeof(*list)); | |
381 | if (!list) { | |
36eb9bde | 382 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 383 | return -1; |
384 | } | |
385 | ||
386 | lxc_list_init(list); | |
387 | list->elem = inetdev; | |
388 | ||
389 | addr = value; | |
390 | ||
391 | cursor = strstr(addr, " "); | |
392 | if (cursor) { | |
393 | *cursor = '\0'; | |
394 | bcast = cursor + 1; | |
395 | } | |
396 | ||
397 | slash = strstr(addr, "/"); | |
398 | if (slash) { | |
399 | *slash = '\0'; | |
400 | prefix = slash + 1; | |
401 | } | |
402 | ||
403 | if (!addr) { | |
36eb9bde | 404 | ERROR("no address specified"); |
c2cc9f0a | 405 | return -1; |
406 | } | |
407 | ||
408 | if (!inet_pton(AF_INET, addr, &inetdev->addr)) { | |
36eb9bde | 409 | SYSERROR("invalid ipv4 address: %s", value); |
c2cc9f0a | 410 | return -1; |
411 | } | |
412 | ||
0093bb8c DL |
413 | if (bcast && !inet_pton(AF_INET, bcast, &inetdev->bcast)) { |
414 | SYSERROR("invalid ipv4 broadcast address: %s", value); | |
415 | return -1; | |
416 | } | |
c2cc9f0a | 417 | |
a059591e DL |
418 | /* no prefix specified, determine it from the network class */ |
419 | inetdev->prefix = prefix ? atoi(prefix) : | |
420 | config_ip_prefix(&inetdev->addr); | |
421 | ||
b3df193c | 422 | /* if no broadcast address, let compute one from the |
0093bb8c DL |
423 | * prefix and address |
424 | */ | |
425 | if (!bcast) { | |
1b7d4743 DL |
426 | inetdev->bcast.s_addr = inetdev->addr.s_addr; |
427 | inetdev->bcast.s_addr |= | |
428 | htonl(INADDR_BROADCAST >> inetdev->prefix); | |
0093bb8c | 429 | } |
c2cc9f0a | 430 | |
431 | lxc_list_add(&netdev->ipv4, list); | |
432 | ||
433 | return 0; | |
434 | } | |
435 | ||
e892973e DL |
436 | static int config_network_ipv6(const char *key, char *value, |
437 | struct lxc_conf *lxc_conf) | |
c2cc9f0a | 438 | { |
c2cc9f0a | 439 | struct lxc_netdev *netdev; |
440 | struct lxc_inet6dev *inet6dev; | |
441 | struct lxc_list *list; | |
442 | char *slash; | |
443 | char *netmask; | |
444 | ||
16950ecb | 445 | netdev = network_netdev(key, value, &lxc_conf->network); |
33c945e0 | 446 | if (!netdev) |
c2cc9f0a | 447 | return -1; |
c2cc9f0a | 448 | |
449 | inet6dev = malloc(sizeof(*inet6dev)); | |
450 | if (!inet6dev) { | |
36eb9bde | 451 | SYSERROR("failed to allocate ipv6 address"); |
c2cc9f0a | 452 | return -1; |
453 | } | |
454 | memset(inet6dev, 0, sizeof(*inet6dev)); | |
455 | ||
456 | list = malloc(sizeof(*list)); | |
457 | if (!list) { | |
36eb9bde | 458 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 459 | return -1; |
460 | } | |
461 | ||
462 | lxc_list_init(list); | |
463 | list->elem = inet6dev; | |
464 | ||
a059591e | 465 | inet6dev->prefix = 64; |
c2cc9f0a | 466 | slash = strstr(value, "/"); |
467 | if (slash) { | |
468 | *slash = '\0'; | |
469 | netmask = slash + 1; | |
470 | inet6dev->prefix = atoi(netmask); | |
471 | } | |
472 | ||
473 | if (!inet_pton(AF_INET6, value, &inet6dev->addr)) { | |
36eb9bde | 474 | SYSERROR("invalid ipv6 address: %s", value); |
c2cc9f0a | 475 | return -1; |
476 | } | |
477 | ||
c2cc9f0a | 478 | lxc_list_add(&netdev->ipv6, list); |
479 | ||
480 | return 0; | |
481 | } | |
482 | ||
e3b4c4c4 ST |
483 | static int config_network_script(const char *key, char *value, |
484 | struct lxc_conf *lxc_conf) | |
485 | { | |
486 | struct lxc_netdev *netdev; | |
487 | ||
488 | netdev = network_netdev(key, value, &lxc_conf->network); | |
489 | if (!netdev) | |
490 | return -1; | |
491 | ||
492 | char *copy = strdup(value); | |
493 | if (!copy) { | |
494 | SYSERROR("failed to dup string '%s'", value); | |
495 | return -1; | |
496 | } | |
497 | if (strcmp(key, "lxc.network.script.up") == 0) { | |
498 | netdev->upscript = copy; | |
499 | return 0; | |
500 | } | |
501 | SYSERROR("Unknown key: %s", key); | |
502 | free(copy); | |
503 | return -1; | |
504 | } | |
505 | ||
cccc74b5 DL |
506 | static int config_personality(const char *key, char *value, |
507 | struct lxc_conf *lxc_conf) | |
508 | { | |
509 | struct per_name { | |
510 | char *name; | |
511 | int per; | |
512 | } pername[4] = { | |
513 | { "x86", PER_LINUX32 }, | |
514 | { "i686", PER_LINUX32 }, | |
515 | { "x86_64", PER_LINUX }, | |
516 | { "amd64", PER_LINUX }, | |
517 | }; | |
6f1239c3 | 518 | size_t len = sizeof(pername) / sizeof(pername[0]); |
cccc74b5 DL |
519 | |
520 | int i; | |
521 | ||
6f1239c3 | 522 | for (i = 0; i < len; i++) { |
cccc74b5 DL |
523 | |
524 | if (strcmp(pername[i].name, value)) | |
525 | continue; | |
526 | ||
527 | lxc_conf->personality = pername[i].per; | |
528 | return 0; | |
529 | } | |
530 | ||
531 | ERROR("unsupported personality '%s'", value); | |
532 | return -1; | |
533 | } | |
534 | ||
10db618d | 535 | static int config_pts(const char *key, char *value, struct lxc_conf *lxc_conf) |
536 | { | |
537 | int maxpts = atoi(value); | |
538 | ||
539 | lxc_conf->pts = maxpts; | |
540 | ||
541 | return 0; | |
542 | } | |
543 | ||
b0a33c1e | 544 | static int config_tty(const char *key, char *value, struct lxc_conf *lxc_conf) |
545 | { | |
546 | int nbtty = atoi(value); | |
547 | ||
548 | lxc_conf->tty = nbtty; | |
549 | ||
550 | return 0; | |
551 | } | |
552 | ||
576f946d | 553 | static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf) |
554 | { | |
555 | char *token = "lxc.cgroup."; | |
556 | char *subkey; | |
bf83c5b9 MS |
557 | struct lxc_list *cglist = NULL; |
558 | struct lxc_cgroup *cgelem = NULL; | |
576f946d | 559 | |
560 | subkey = strstr(key, token); | |
561 | ||
562 | if (!subkey) | |
563 | return -1; | |
564 | ||
565 | if (!strlen(subkey)) | |
566 | return -1; | |
567 | ||
568 | if (strlen(subkey) == strlen(token)) | |
569 | return -1; | |
a871ff6b | 570 | |
576f946d | 571 | subkey += strlen(token); |
572 | ||
573 | cglist = malloc(sizeof(*cglist)); | |
574 | if (!cglist) | |
bf83c5b9 | 575 | goto out; |
576f946d | 576 | |
577 | cgelem = malloc(sizeof(*cgelem)); | |
bf83c5b9 MS |
578 | if (!cgelem) |
579 | goto out; | |
580 | memset(cgelem, 0, sizeof(*cgelem)); | |
576f946d | 581 | |
582 | cgelem->subsystem = strdup(subkey); | |
583 | cgelem->value = strdup(value); | |
bf83c5b9 MS |
584 | |
585 | if (!cgelem->subsystem || !cgelem->value) | |
586 | goto out; | |
587 | ||
576f946d | 588 | cglist->elem = cgelem; |
589 | ||
94d12f0a | 590 | lxc_list_add_tail(&lxc_conf->cgroup, cglist); |
576f946d | 591 | |
592 | return 0; | |
bf83c5b9 MS |
593 | |
594 | out: | |
595 | if (cglist) | |
596 | free(cglist); | |
597 | ||
598 | if (cgelem) { | |
599 | if (cgelem->subsystem) | |
600 | free(cgelem->subsystem); | |
601 | ||
602 | if (cgelem->value) | |
603 | free(cgelem->value); | |
604 | ||
605 | free(cgelem); | |
606 | } | |
607 | ||
608 | return -1; | |
576f946d | 609 | } |
610 | ||
e7938e9e | 611 | static int config_fstab(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 612 | { |
613 | if (strlen(value) >= MAXPATHLEN) { | |
36eb9bde | 614 | ERROR("%s path is too long", value); |
c2cc9f0a | 615 | return -1; |
616 | } | |
617 | ||
618 | lxc_conf->fstab = strdup(value); | |
619 | if (!lxc_conf->fstab) { | |
36eb9bde | 620 | SYSERROR("failed to duplicate string %s", value); |
c2cc9f0a | 621 | return -1; |
622 | } | |
623 | ||
624 | return 0; | |
625 | } | |
626 | ||
e7938e9e MN |
627 | static int config_mount(const char *key, char *value, struct lxc_conf *lxc_conf) |
628 | { | |
629 | char *fstab_token = "lxc.mount"; | |
630 | char *token = "lxc.mount.entry"; | |
631 | char *subkey; | |
632 | char *mntelem; | |
633 | struct lxc_list *mntlist; | |
634 | ||
635 | subkey = strstr(key, token); | |
636 | ||
637 | if (!subkey) { | |
638 | subkey = strstr(key, fstab_token); | |
639 | ||
640 | if (!subkey) | |
641 | return -1; | |
642 | ||
643 | return config_fstab(key, value, lxc_conf); | |
644 | } | |
645 | ||
646 | if (!strlen(subkey)) | |
647 | return -1; | |
648 | ||
649 | mntlist = malloc(sizeof(*mntlist)); | |
650 | if (!mntlist) | |
651 | return -1; | |
652 | ||
653 | mntelem = strdup(value); | |
bf83c5b9 MS |
654 | if (!mntelem) |
655 | return -1; | |
e7938e9e MN |
656 | mntlist->elem = mntelem; |
657 | ||
658 | lxc_list_add_tail(&lxc_conf->mount_list, mntlist); | |
659 | ||
660 | return 0; | |
661 | } | |
662 | ||
81810dd1 DL |
663 | static int config_cap_drop(const char *key, char *value, |
664 | struct lxc_conf *lxc_conf) | |
665 | { | |
bd288c26 | 666 | char *dropcaps, *sptr, *token; |
81810dd1 DL |
667 | struct lxc_list *droplist; |
668 | int ret = -1; | |
669 | ||
670 | if (!strlen(value)) | |
671 | return -1; | |
672 | ||
673 | dropcaps = strdup(value); | |
674 | if (!dropcaps) { | |
675 | SYSERROR("failed to dup '%s'", value); | |
676 | return -1; | |
677 | } | |
678 | ||
679 | /* in case several capability drop is specified in a single line | |
680 | * split these caps in a single element for the list */ | |
681 | for (;;) { | |
682 | token = strtok_r(dropcaps, " \t", &sptr); | |
683 | if (!token) { | |
684 | ret = 0; | |
685 | break; | |
686 | } | |
687 | dropcaps = NULL; | |
688 | ||
689 | droplist = malloc(sizeof(*droplist)); | |
690 | if (!droplist) { | |
691 | SYSERROR("failed to allocate drop list"); | |
692 | break; | |
693 | } | |
694 | ||
695 | droplist->elem = strdup(token); | |
696 | if (!droplist->elem) { | |
697 | SYSERROR("failed to dup '%s'", token); | |
698 | free(droplist); | |
699 | break; | |
700 | } | |
701 | ||
702 | lxc_list_add_tail(&lxc_conf->caps, droplist); | |
703 | } | |
704 | ||
705 | free(dropcaps); | |
706 | ||
707 | return ret; | |
708 | } | |
709 | ||
28a4b0e5 DL |
710 | static int config_console(const char *key, char *value, |
711 | struct lxc_conf *lxc_conf) | |
712 | { | |
713 | char *path; | |
714 | ||
715 | path = strdup(value); | |
716 | if (!path) { | |
717 | SYSERROR("failed to strdup '%s': %m", value); | |
718 | return -1; | |
719 | } | |
720 | ||
721 | lxc_conf->console.path = path; | |
722 | ||
723 | return 0; | |
724 | } | |
725 | ||
576f946d | 726 | static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 727 | { |
728 | if (strlen(value) >= MAXPATHLEN) { | |
36eb9bde | 729 | ERROR("%s path is too long", value); |
c2cc9f0a | 730 | return -1; |
731 | } | |
732 | ||
33fcb7a0 DL |
733 | lxc_conf->rootfs.path = strdup(value); |
734 | if (!lxc_conf->rootfs.path) { | |
36eb9bde | 735 | SYSERROR("failed to duplicate string %s", value); |
c2cc9f0a | 736 | return -1; |
737 | } | |
738 | ||
739 | return 0; | |
740 | } | |
741 | ||
23b7ea69 DL |
742 | static int config_rootfs_mount(const char *key, char *value, struct lxc_conf *lxc_conf) |
743 | { | |
744 | if (strlen(value) >= MAXPATHLEN) { | |
745 | ERROR("%s path is too long", value); | |
746 | return -1; | |
747 | } | |
748 | ||
749 | lxc_conf->rootfs.mount = strdup(value); | |
750 | if (!lxc_conf->rootfs.mount) { | |
751 | SYSERROR("failed to duplicate string '%s'", value); | |
752 | return -1; | |
753 | } | |
754 | ||
755 | return 0; | |
756 | } | |
757 | ||
bf601689 MH |
758 | static int config_pivotdir(const char *key, char *value, struct lxc_conf *lxc_conf) |
759 | { | |
760 | if (strlen(value) >= MAXPATHLEN) { | |
761 | ERROR("%s path is too long", value); | |
762 | return -1; | |
763 | } | |
764 | ||
33fcb7a0 DL |
765 | lxc_conf->rootfs.pivot = strdup(value); |
766 | if (!lxc_conf->rootfs.pivot) { | |
bf601689 MH |
767 | SYSERROR("failed to duplicate string %s", value); |
768 | return -1; | |
769 | } | |
770 | ||
771 | return 0; | |
772 | } | |
773 | ||
576f946d | 774 | static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_conf) |
c2cc9f0a | 775 | { |
776 | struct utsname *utsname; | |
777 | ||
778 | utsname = malloc(sizeof(*utsname)); | |
779 | if (!utsname) { | |
36eb9bde | 780 | SYSERROR("failed to allocate memory"); |
c2cc9f0a | 781 | return -1; |
782 | } | |
783 | ||
784 | if (strlen(value) >= sizeof(utsname->nodename)) { | |
36eb9bde | 785 | ERROR("node name '%s' is too long", |
c2cc9f0a | 786 | utsname->nodename); |
787 | return -1; | |
788 | } | |
789 | ||
790 | strcpy(utsname->nodename, value); | |
791 | lxc_conf->utsname = utsname; | |
792 | ||
793 | return 0; | |
794 | } | |
795 | ||
7a7ff0c6 | 796 | static int parse_line(char *buffer, void *data) |
c2cc9f0a | 797 | { |
798 | struct config *config; | |
81192358 | 799 | char *line, *linep; |
c2cc9f0a | 800 | char *dot; |
801 | char *key; | |
802 | char *value; | |
476d4cf1 | 803 | int ret = 0; |
c2cc9f0a | 804 | |
91480a0f | 805 | if (lxc_is_line_empty(buffer)) |
c2cc9f0a | 806 | return 0; |
807 | ||
81192358 DL |
808 | /* we have to dup the buffer otherwise, at the re-exec for |
809 | * reboot we modified the original string on the stack by | |
810 | * replacing '=' by '\0' below | |
91480a0f | 811 | */ |
81192358 | 812 | linep = line = strdup(buffer); |
91480a0f DL |
813 | if (!line) { |
814 | SYSERROR("failed to allocate memory for '%s'", buffer); | |
81192358 | 815 | return -1; |
91480a0f DL |
816 | } |
817 | ||
b2718c72 | 818 | line += lxc_char_left_gc(line, strlen(line)); |
476d4cf1 DL |
819 | |
820 | /* martian option - ignoring it, the commented lines beginning by '#' | |
821 | * fall in this case | |
822 | */ | |
823 | if (strncmp(line, "lxc.", 4)) | |
91480a0f | 824 | goto out; |
476d4cf1 DL |
825 | |
826 | ret = -1; | |
c2cc9f0a | 827 | |
b2718c72 | 828 | dot = strstr(line, "="); |
c2cc9f0a | 829 | if (!dot) { |
36eb9bde | 830 | ERROR("invalid configuration line: %s", line); |
91480a0f | 831 | goto out; |
c2cc9f0a | 832 | } |
a871ff6b | 833 | |
c2cc9f0a | 834 | *dot = '\0'; |
835 | value = dot + 1; | |
836 | ||
b2718c72 | 837 | key = line; |
838 | key[lxc_char_right_gc(key, strlen(key))] = '\0'; | |
c2cc9f0a | 839 | |
b2718c72 | 840 | value += lxc_char_left_gc(value, strlen(value)); |
841 | value[lxc_char_right_gc(value, strlen(value))] = '\0'; | |
c2cc9f0a | 842 | |
843 | config = getconfig(key); | |
844 | if (!config) { | |
36eb9bde | 845 | ERROR("unknow key %s", key); |
91480a0f | 846 | goto out; |
c2cc9f0a | 847 | } |
848 | ||
91480a0f DL |
849 | ret = config->cb(key, value, data); |
850 | ||
851 | out: | |
81192358 | 852 | free(linep); |
91480a0f | 853 | return ret; |
c2cc9f0a | 854 | } |
855 | ||
af5b0155 CLG |
856 | int lxc_config_readline(char *buffer, struct lxc_conf *conf) |
857 | { | |
858 | return parse_line(buffer, conf); | |
859 | } | |
860 | ||
b2718c72 | 861 | int lxc_config_read(const char *file, struct lxc_conf *conf) |
c2cc9f0a | 862 | { |
2382ecff | 863 | return lxc_file_for_each_line(file, parse_line, conf); |
c2cc9f0a | 864 | } |
62e46035 CLG |
865 | |
866 | int lxc_config_define_add(struct lxc_list *defines, char* arg) | |
867 | { | |
868 | struct lxc_list *dent; | |
869 | ||
870 | dent = malloc(sizeof(struct lxc_list)); | |
871 | if (!dent) | |
872 | return -1; | |
873 | ||
874 | dent->elem = arg; | |
875 | lxc_list_add_tail(defines, dent); | |
876 | return 0; | |
877 | } | |
878 | ||
226a18d6 | 879 | int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf) |
62e46035 CLG |
880 | { |
881 | struct lxc_list *it; | |
882 | int ret = 0; | |
883 | ||
884 | lxc_list_for_each(it, defines) { | |
885 | ret = lxc_config_readline(it->elem, conf); | |
886 | if (ret) | |
887 | break; | |
888 | } | |
889 | ||
890 | lxc_list_for_each(it, defines) { | |
891 | lxc_list_del(it); | |
892 | free(it); | |
893 | } | |
894 | ||
895 | return ret; | |
896 | } |