1 /*
2 * Copyright (C) 2010-2014 Red Hat, Inc.
3 * Copyright IBM Corp. 2008
4 *
5 * lxc_domain.h: LXC domain helpers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23
24 #include "lxc_domain.h"
25
26 #include "virlog.h"
27 #include "virerror.h"
28 #include "virstring.h"
29 #include "virfile.h"
30 #include "virtime.h"
31 #include "virsystemd.h"
32 #include "virinitctl.h"
33 #include "domain_driver.h"
34
35 #define VIR_FROM_THIS VIR_FROM_LXC
36
37 VIR_ENUM_IMPL(virLXCDomainJob,
38 LXC_JOB_LAST,
39 "none",
40 "query",
41 "destroy",
42 "modify",
43 );
44
45 VIR_LOG_INIT("lxc.lxc_domain");
46
47 static int
virLXCDomainObjInitJob(virLXCDomainObjPrivate * priv)48 virLXCDomainObjInitJob(virLXCDomainObjPrivate *priv)
49 {
50 memset(&priv->job, 0, sizeof(priv->job));
51
52 if (virCondInit(&priv->job.cond) < 0)
53 return -1;
54
55 return 0;
56 }
57
58 static void
virLXCDomainObjResetJob(virLXCDomainObjPrivate * priv)59 virLXCDomainObjResetJob(virLXCDomainObjPrivate *priv)
60 {
61 struct virLXCDomainJobObj *job = &priv->job;
62
63 job->active = LXC_JOB_NONE;
64 job->owner = 0;
65 }
66
67 static void
virLXCDomainObjFreeJob(virLXCDomainObjPrivate * priv)68 virLXCDomainObjFreeJob(virLXCDomainObjPrivate *priv)
69 {
70 ignore_value(virCondDestroy(&priv->job.cond));
71 }
72
73 /* Give up waiting for mutex after 30 seconds */
74 #define LXC_JOB_WAIT_TIME (1000ull * 30)
75
76 /*
77 * obj must be locked before calling, virLXCDriver *must NOT be locked
78 *
79 * This must be called by anything that will change the VM state
80 * in any way
81 *
82 * Upon successful return, the object will have its ref count increased.
83 * Successful calls must be followed by EndJob eventually.
84 */
85 int
virLXCDomainObjBeginJob(virLXCDriver * driver G_GNUC_UNUSED,virDomainObj * obj,enum virLXCDomainJob job)86 virLXCDomainObjBeginJob(virLXCDriver *driver G_GNUC_UNUSED,
87 virDomainObj *obj,
88 enum virLXCDomainJob job)
89 {
90 virLXCDomainObjPrivate *priv = obj->privateData;
91 unsigned long long now;
92 unsigned long long then;
93
94 if (virTimeMillisNow(&now) < 0)
95 return -1;
96 then = now + LXC_JOB_WAIT_TIME;
97
98 while (priv->job.active) {
99 VIR_DEBUG("Wait normal job condition for starting job: %s",
100 virLXCDomainJobTypeToString(job));
101 if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0)
102 goto error;
103 }
104
105 virLXCDomainObjResetJob(priv);
106
107 VIR_DEBUG("Starting job: %s", virLXCDomainJobTypeToString(job));
108 priv->job.active = job;
109 priv->job.owner = virThreadSelfID();
110
111 return 0;
112
113 error:
114 VIR_WARN("Cannot start job (%s) for domain %s;"
115 " current job is (%s) owned by (%d)",
116 virLXCDomainJobTypeToString(job),
117 obj->def->name,
118 virLXCDomainJobTypeToString(priv->job.active),
119 priv->job.owner);
120
121 if (errno == ETIMEDOUT)
122 virReportError(VIR_ERR_OPERATION_TIMEOUT,
123 "%s", _("cannot acquire state change lock"));
124 else
125 virReportSystemError(errno,
126 "%s", _("cannot acquire job mutex"));
127 return -1;
128 }
129
130
131 /*
132 * obj must be locked and have a reference before calling
133 *
134 * To be called after completing the work associated with the
135 * earlier virLXCDomainBeginJob() call
136 */
137 void
virLXCDomainObjEndJob(virLXCDriver * driver G_GNUC_UNUSED,virDomainObj * obj)138 virLXCDomainObjEndJob(virLXCDriver *driver G_GNUC_UNUSED,
139 virDomainObj *obj)
140 {
141 virLXCDomainObjPrivate *priv = obj->privateData;
142 enum virLXCDomainJob job = priv->job.active;
143
144 VIR_DEBUG("Stopping job: %s",
145 virLXCDomainJobTypeToString(job));
146
147 virLXCDomainObjResetJob(priv);
148 virCondSignal(&priv->job.cond);
149 }
150
151
152 static void *
virLXCDomainObjPrivateAlloc(void * opaque G_GNUC_UNUSED)153 virLXCDomainObjPrivateAlloc(void *opaque G_GNUC_UNUSED)
154 {
155 virLXCDomainObjPrivate *priv = g_new0(virLXCDomainObjPrivate, 1);
156
157 if (virLXCDomainObjInitJob(priv) < 0) {
158 g_free(priv);
159 return NULL;
160 }
161
162 return priv;
163 }
164
165
166 static void
virLXCDomainObjPrivateFree(void * data)167 virLXCDomainObjPrivateFree(void *data)
168 {
169 virLXCDomainObjPrivate *priv = data;
170
171 virCgroupFree(priv->cgroup);
172 virLXCDomainObjFreeJob(priv);
173 g_free(priv);
174 }
175
176
177
178 VIR_ENUM_IMPL(virLXCDomainNamespace,
179 VIR_LXC_DOMAIN_NAMESPACE_LAST,
180 "sharenet",
181 "shareipc",
182 "shareuts",
183 );
184
185 VIR_ENUM_IMPL(virLXCDomainNamespaceSource,
186 VIR_LXC_DOMAIN_NAMESPACE_SOURCE_LAST,
187 "none",
188 "name",
189 "pid",
190 "netns",
191 );
192
193 static void
lxcDomainDefNamespaceFree(void * nsdata)194 lxcDomainDefNamespaceFree(void *nsdata)
195 {
196 size_t i;
197 lxcDomainDef *lxcDef = nsdata;
198
199 if (!lxcDef)
200 return;
201
202 for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
203 g_free(lxcDef->ns_val[i]);
204 g_free(nsdata);
205 }
206
207 static int
lxcDomainDefNamespaceParse(xmlXPathContextPtr ctxt,void ** data)208 lxcDomainDefNamespaceParse(xmlXPathContextPtr ctxt,
209 void **data)
210 {
211 lxcDomainDef *lxcDef = NULL;
212 g_autofree xmlNodePtr *nodes = NULL;
213 VIR_XPATH_NODE_AUTORESTORE(ctxt)
214 int n;
215 size_t i;
216 int ret = -1;
217
218 if ((n = virXPathNodeSet("./lxc:namespace/*", ctxt, &nodes)) < 0)
219 return -1;
220
221 if (n == 0)
222 return 0;
223
224 lxcDef = g_new0(lxcDomainDef, 1);
225
226 for (i = 0; i < n; i++) {
227 g_autofree char *tmp = NULL;
228 int feature;
229
230 if ((feature = virLXCDomainNamespaceTypeFromString(
231 (const char *)nodes[i]->name)) < 0) {
232 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
233 _("unsupported Namespace feature: %s"),
234 nodes[i]->name);
235 goto cleanup;
236 }
237
238 ctxt->node = nodes[i];
239
240 if (!(tmp = virXMLPropString(nodes[i], "type"))) {
241 virReportError(VIR_ERR_INTERNAL_ERROR,
242 "%s", _("No lxc environment type specified"));
243 goto cleanup;
244 }
245 if ((lxcDef->ns_source[feature] =
246 virLXCDomainNamespaceSourceTypeFromString(tmp)) < 0) {
247 virReportError(VIR_ERR_INTERNAL_ERROR,
248 _("Unknown LXC namespace source '%s'"),
249 tmp);
250 goto cleanup;
251 }
252
253 if (!(lxcDef->ns_val[feature] =
254 virXMLPropString(nodes[i], "value"))) {
255 virReportError(VIR_ERR_INTERNAL_ERROR,
256 "%s", _("No lxc environment type specified"));
257 goto cleanup;
258 }
259 }
260
261 *data = g_steal_pointer(&lxcDef);
262 ret = 0;
263
264 cleanup:
265 lxcDomainDefNamespaceFree(lxcDef);
266 return ret;
267 }
268
269
270 static int
lxcDomainDefNamespaceFormatXML(virBuffer * buf,void * nsdata)271 lxcDomainDefNamespaceFormatXML(virBuffer *buf,
272 void *nsdata)
273 {
274 lxcDomainDef *lxcDef = nsdata;
275 size_t i;
276
277 if (!lxcDef)
278 return 0;
279
280 virBufferAddLit(buf, "<lxc:namespace>\n");
281 virBufferAdjustIndent(buf, 2);
282
283 for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
284 if (lxcDef->ns_source[i] == VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE)
285 continue;
286
287 virBufferAsprintf(buf, "<lxc:%s type='%s' value='%s'/>\n",
288 virLXCDomainNamespaceTypeToString(i),
289 virLXCDomainNamespaceSourceTypeToString(
290 lxcDef->ns_source[i]),
291 lxcDef->ns_val[i]);
292 }
293
294 virBufferAdjustIndent(buf, -2);
295 virBufferAddLit(buf, "</lxc:namespace>\n");
296 return 0;
297 }
298
299
300 virXMLNamespace virLXCDriverDomainXMLNamespace = {
301 .parse = lxcDomainDefNamespaceParse,
302 .free = lxcDomainDefNamespaceFree,
303 .format = lxcDomainDefNamespaceFormatXML,
304 .prefix = "lxc",
305 .uri = "http://libvirt.org/schemas/domain/lxc/1.0",
306 };
307
308
309 static int
virLXCDomainObjPrivateXMLFormat(virBuffer * buf,virDomainObj * vm)310 virLXCDomainObjPrivateXMLFormat(virBuffer *buf,
311 virDomainObj *vm)
312 {
313 virLXCDomainObjPrivate *priv = vm->privateData;
314
315 virBufferAsprintf(buf, "<init pid='%lld'/>\n",
316 (long long)priv->initpid);
317
318 return 0;
319 }
320
321 static int
virLXCDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,virDomainObj * vm,virDomainDefParserConfig * config G_GNUC_UNUSED)322 virLXCDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,
323 virDomainObj *vm,
324 virDomainDefParserConfig *config G_GNUC_UNUSED)
325 {
326 virLXCDomainObjPrivate *priv = vm->privateData;
327 long long thepid;
328
329 if (virXPathLongLong("string(./init[1]/@pid)", ctxt, &thepid) < 0) {
330 VIR_WARN("Failed to load init pid from state %s",
331 virGetLastErrorMessage());
332 priv->initpid = 0;
333 } else {
334 priv->initpid = thepid;
335 }
336
337 return 0;
338 }
339
340 virDomainXMLPrivateDataCallbacks virLXCDriverPrivateDataCallbacks = {
341 .alloc = virLXCDomainObjPrivateAlloc,
342 .free = virLXCDomainObjPrivateFree,
343 .format = virLXCDomainObjPrivateXMLFormat,
344 .parse = virLXCDomainObjPrivateXMLParse,
345 };
346
347 static int
virLXCDomainDefPostParse(virDomainDef * def,unsigned int parseFlags G_GNUC_UNUSED,void * opaque,void * parseOpaque G_GNUC_UNUSED)348 virLXCDomainDefPostParse(virDomainDef *def,
349 unsigned int parseFlags G_GNUC_UNUSED,
350 void *opaque,
351 void *parseOpaque G_GNUC_UNUSED)
352 {
353 virLXCDriver *driver = opaque;
354 g_autoptr(virCaps) caps = virLXCDriverGetCapabilities(driver, false);
355 if (!caps)
356 return -1;
357 if (!virCapabilitiesDomainSupported(caps, def->os.type,
358 def->os.arch,
359 def->virtType))
360 return -1;
361
362 /* check for emulator and create a default one if needed */
363 if (!def->emulator &&
364 !(def->emulator = virDomainDefGetDefaultEmulator(def, caps)))
365 return -1;
366
367 return 0;
368 }
369
370
371 static int
virLXCDomainDeviceDefPostParse(virDomainDeviceDef * dev,const virDomainDef * def G_GNUC_UNUSED,unsigned int parseFlags G_GNUC_UNUSED,void * opaque G_GNUC_UNUSED,void * parseOpaque G_GNUC_UNUSED)372 virLXCDomainDeviceDefPostParse(virDomainDeviceDef *dev,
373 const virDomainDef *def G_GNUC_UNUSED,
374 unsigned int parseFlags G_GNUC_UNUSED,
375 void *opaque G_GNUC_UNUSED,
376 void *parseOpaque G_GNUC_UNUSED)
377 {
378 if (dev->type == VIR_DOMAIN_DEVICE_CHR &&
379 dev->data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
380 dev->data.chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)
381 dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LXC;
382
383 return 0;
384 }
385
386
387 virDomainDefParserConfig virLXCDriverDomainDefParserConfig = {
388 .domainPostParseCallback = virLXCDomainDefPostParse,
389 .devicesPostParseCallback = virLXCDomainDeviceDefPostParse,
390 };
391
392
393 char *
virLXCDomainGetMachineName(virDomainDef * def,pid_t pid)394 virLXCDomainGetMachineName(virDomainDef *def, pid_t pid)
395 {
396 char *ret = NULL;
397
398 if (pid) {
399 ret = virSystemdGetMachineNameByPID(pid);
400 if (!ret)
401 virResetLastError();
402 }
403
404 if (!ret)
405 ret = virDomainDriverGenerateMachineName("lxc", NULL, def->id, def->name, true);
406
407 return ret;
408 }
409
410
411 typedef struct _lxcDomainInitctlCallbackData lxcDomainInitctlCallbackData;
412 struct _lxcDomainInitctlCallbackData {
413 int runlevel;
414 bool *st_valid;
415 struct stat *st;
416 };
417
418
419 static int
lxcDomainInitctlCallback(pid_t pid G_GNUC_UNUSED,void * opaque)420 lxcDomainInitctlCallback(pid_t pid G_GNUC_UNUSED,
421 void *opaque)
422 {
423 lxcDomainInitctlCallbackData *data = opaque;
424 size_t i;
425
426 for (i = 0; virInitctlFifos[i]; i++) {
427 const char *fifo = virInitctlFifos[i];
428 struct stat cont_sb;
429
430 if (stat(fifo, &cont_sb) < 0) {
431 if (errno == ENOENT)
432 continue;
433
434 virReportSystemError(errno, _("Unable to stat %s"), fifo);
435 return -1;
436 }
437
438 /* Check if the init fifo is not the very one that's on
439 * the host. We don't want to change the host's runlevel.
440 */
441 if (data->st_valid[i] &&
442 data->st[i].st_dev == cont_sb.st_dev &&
443 data->st[i].st_ino == cont_sb.st_ino)
444 continue;
445
446 return virInitctlSetRunLevel(fifo, data->runlevel);
447 }
448
449 /* If no usable fifo was found then declare success. Caller
450 * will try killing the domain with signal. */
451 return 0;
452 }
453
454
455 int
virLXCDomainSetRunlevel(virDomainObj * vm,int runlevel)456 virLXCDomainSetRunlevel(virDomainObj *vm,
457 int runlevel)
458 {
459 virLXCDomainObjPrivate *priv = vm->privateData;
460 lxcDomainInitctlCallbackData data;
461 size_t nfifos = 0;
462 size_t i;
463 int ret = -1;
464
465 memset(&data, 0, sizeof(data));
466
467 data.runlevel = runlevel;
468
469 for (nfifos = 0; virInitctlFifos[nfifos]; nfifos++)
470 ;
471
472 data.st = g_new0(struct stat, nfifos);
473 data.st_valid = g_new0(bool, nfifos);
474
475 for (i = 0; virInitctlFifos[i]; i++) {
476 const char *fifo = virInitctlFifos[i];
477
478 if (stat(fifo, &(data.st[i])) < 0) {
479 if (errno == ENOENT)
480 continue;
481
482 virReportSystemError(errno, _("Unable to stat %s"), fifo);
483 goto cleanup;
484 }
485
486 data.st_valid[i] = true;
487 }
488
489 ret = virProcessRunInMountNamespace(priv->initpid,
490 lxcDomainInitctlCallback,
491 &data);
492 cleanup:
493 g_free(data.st);
494 data.st = NULL;
495 g_free(data.st_valid);
496 data.st_valid = NULL;
497 return ret;
498 }
499