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