virt-host-validate-common.c 13.8 KB
Newer Older
1 2 3
/*
 * virt-host-validate-common.c: Sanity check helper APIs
 *
4
 * Copyright (C) 2012, 2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23 24 25 26
 *
 */

#include <config.h>

#include <stdarg.h>
#include <unistd.h>
#include <sys/utsname.h>
27 28 29
#ifdef HAVE_MNTENT_H
# include <mntent.h>
#endif /* HAVE_MNTENT_H */
30
#include <sys/stat.h>
31

32
#include "viralloc.h"
33 34
#include "virfile.h"
#include "virt-host-validate-common.h"
35
#include "virstring.h"
36
#include "virarch.h"
37

38 39
#define VIR_FROM_THIS VIR_FROM_NONE

40 41
VIR_ENUM_IMPL(virHostValidateCPUFlag, VIR_HOST_VALIDATE_CPU_FLAG_LAST,
              "vmx",
42 43
              "svm",
              "sie");
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
static bool quiet;

void virHostMsgSetQuiet(bool quietFlag)
{
    quiet = quietFlag;
}

void virHostMsgCheck(const char *prefix,
                     const char *format,
                     ...)
{
    va_list args;
    char *msg;

    if (quiet)
        return;

    va_start(args, format);
    if (virVasprintf(&msg, format, args) < 0) {
        perror("malloc");
        abort();
    }
    va_end(args);

    fprintf(stdout, _("%6s: Checking %-60s: "), prefix, msg);
    VIR_FREE(msg);
}

static bool virHostMsgWantEscape(void)
{
    static bool detectTty = true;
76
    static bool wantEscape;
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
    if (detectTty) {
        if (isatty(STDOUT_FILENO))
            wantEscape = true;
        detectTty = false;
    }
    return wantEscape;
}

void virHostMsgPass(void)
{
    if (quiet)
        return;

    if (virHostMsgWantEscape())
        fprintf(stdout, "\033[32m%s\033[0m\n", _("PASS"));
    else
        fprintf(stdout, "%s\n", _("PASS"));
}


static const char * failMessages[] = {
    N_("FAIL"),
    N_("WARN"),
    N_("NOTE"),
};

verify(ARRAY_CARDINALITY(failMessages) == VIR_HOST_VALIDATE_LAST);

static const char *failEscapeCodes[] = {
    "\033[31m",
    "\033[33m",
    "\033[34m",
};

verify(ARRAY_CARDINALITY(failEscapeCodes) == VIR_HOST_VALIDATE_LAST);

void virHostMsgFail(virHostValidateLevel level,
114 115
                    const char *format,
                    ...)
116
{
117 118 119 120 121 122 123 124 125 126 127 128 129
    va_list args;
    char *msg;

    if (quiet)
        return;

    va_start(args, format);
    if (virVasprintf(&msg, format, args) < 0) {
        perror("malloc");
        abort();
    }
    va_end(args);

130 131
    if (virHostMsgWantEscape())
        fprintf(stdout, "%s%s\033[0m (%s)\n",
132
                failEscapeCodes[level], _(failMessages[level]), msg);
133 134
    else
        fprintf(stdout, "%s (%s)\n",
135 136
                _(failMessages[level]), msg);
    VIR_FREE(msg);
137 138 139
}


140 141 142 143
int virHostValidateDeviceExists(const char *hvname,
                                const char *dev_name,
                                virHostValidateLevel level,
                                const char *hint)
144
{
145 146 147
    virHostMsgCheck(hvname, "if device %s exists", dev_name);

    if (access(dev_name, F_OK) < 0) {
148
        virHostMsgFail(level, "%s", hint);
149 150 151 152 153 154 155 156 157 158 159 160 161 162
        return -1;
    }

    virHostMsgPass();
    return 0;
}


int virHostValidateDeviceAccessible(const char *hvname,
                                    const char *dev_name,
                                    virHostValidateLevel level,
                                    const char *hint)
{
    virHostMsgCheck(hvname, "if device %s is accessible", dev_name);
163

E
Eric Blake 已提交
164
    if (access(dev_name, R_OK|W_OK) < 0) {
165
        virHostMsgFail(level, "%s", hint);
166 167 168 169 170 171 172 173
        return -1;
    }

    virHostMsgPass();
    return 0;
}


174 175 176 177 178 179 180 181 182 183 184
int virHostValidateNamespace(const char *hvname,
                             const char *ns_name,
                             virHostValidateLevel level,
                             const char *hint)
{
    virHostMsgCheck(hvname, "for namespace %s", ns_name);
    char nspath[100];

    snprintf(nspath, sizeof(nspath), "/proc/self/ns/%s", ns_name);

    if (access(nspath, F_OK) < 0) {
185
        virHostMsgFail(level, "%s", hint);
186 187 188 189 190 191 192 193
        return -1;
    }

    virHostMsgPass();
    return 0;
}


194
virBitmapPtr virHostValidateGetCPUFlags(void)
195
{
196
    FILE *fp;
J
John Ferlan 已提交
197
    virBitmapPtr flags = NULL;
198

199 200 201 202
    if (!(fp = fopen("/proc/cpuinfo", "r")))
        return NULL;

    if (!(flags = virBitmapNewQuiet(VIR_HOST_VALIDATE_CPU_FLAG_LAST)))
J
John Ferlan 已提交
203
        goto cleanup;
204 205 206

    do {
        char line[1024];
207 208 209 210
        char *start;
        char **tokens;
        size_t ntokens;
        size_t i;
211 212 213 214

        if (!fgets(line, sizeof(line), fp))
            break;

215 216 217 218 219
        /* The line we're interested in is marked differently depending
         * on the architecture, so check possible prefixes */
        if (!STRPREFIX(line, "flags") &&
            !STRPREFIX(line, "Features") &&
            !STRPREFIX(line, "features"))
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
            continue;

        /* fgets() includes the trailing newline in the output buffer,
         * so we need to clean that up ourselves. We can safely access
         * line[strlen(line) - 1] because the checks above would cause
         * us to skip empty strings */
        line[strlen(line) - 1] = '\0';

        /* Skip to the separator */
        if (!(start = strchr(line, ':')))
            continue;

        /* Split the line using " " as a delimiter. The first token
         * will always be ":", but that's okay */
        if (!(tokens = virStringSplitCount(start, " ", 0, &ntokens)))
            continue;

        /* Go through all flags and check whether one of those we
         * might want to check for later on is present; if that's
         * the case, set the relevant bit in the bitmap */
        for (i = 0; i < ntokens; i++) {
            int value;

            if ((value = virHostValidateCPUFlagTypeFromString(tokens[i])) >= 0)
                ignore_value(virBitmapSetBit(flags, value));
245
        }
246

247
        virStringListFreeCount(tokens, ntokens);
248 249
    } while (1);

J
John Ferlan 已提交
250
 cleanup:
251 252
    VIR_FORCE_FCLOSE(fp);

253
    return flags;
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
}


int virHostValidateLinuxKernel(const char *hvname,
                               int version,
                               virHostValidateLevel level,
                               const char *hint)
{
    struct utsname uts;
    unsigned long thisversion;

    uname(&uts);

    virHostMsgCheck(hvname, _("for Linux >= %d.%d.%d"),
                    ((version >> 16) & 0xff),
                    ((version >> 8) & 0xff),
                    (version & 0xff));

    if (STRNEQ(uts.sysname, "Linux")) {
273
        virHostMsgFail(level, "%s", hint);
274 275 276 277
        return -1;
    }

    if (virParseVersionString(uts.release, &thisversion, true) < 0) {
278
        virHostMsgFail(level, "%s", hint);
279 280 281 282
        return -1;
    }

    if (thisversion < version) {
283
        virHostMsgFail(level, "%s", hint);
284 285 286 287 288 289
        return -1;
    } else {
        virHostMsgPass();
        return 0;
    }
}
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307


static int virHostValidateCGroupSupport(const char *hvname,
                                        const char *cg_name,
                                        virHostValidateLevel level,
                                        const char *config_name)
{
    virHostMsgCheck(hvname, "for cgroup '%s' controller support", cg_name);
    FILE *fp = fopen("/proc/self/cgroup", "r");
    size_t len = 0;
    char *line = NULL;
    ssize_t ret;
    bool matched = false;

    if (!fp)
        goto error;

    while ((ret = getline(&line, &len, fp)) >= 0 && !matched) {
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
        char **cgroups;
        char *start;
        char *end;
        size_t ncgroups;
        size_t i;

        /* Each line in this file looks like
         *
         *   4:cpu,cpuacct:/machine.slice/machine-qemu\x2dtest.scope/emulator
         *
         * Since multiple cgroups can be part of the same line and some cgroup
         * names can appear as part of other cgroup names (eg. 'cpu' is a
         * prefix for both 'cpuacct' and 'cpuset'), it's not enough to simply
         * check whether the cgroup name is present somewhere inside the file.
         *
         * Moreover, there's nothing stopping the cgroup name from appearing
         * in an unrelated mount point name as well */

        /* Look for the first colon.
         * The part we're interested in starts right after it */
        if (!(start = strchr(line, ':')))
329
            continue;
330
        start++;
331

332 333 334
        /* Look for the second colon.
         * The part we're interested in ends exactly there */
        if (!(end = strchr(start, ':')))
335
            continue;
336
        *end = '\0';
337

338 339 340 341 342 343 344 345 346
        if (!(cgroups = virStringSplitCount(start, ",", 0, &ncgroups)))
            continue;

        /* Look for the matching cgroup */
        for (i = 0; i < ncgroups; i++) {
            if (STREQ(cgroups[i], cg_name))
                matched = true;
        }

347
        virStringListFreeCount(cgroups, ncgroups);
348
    }
349

350 351 352 353 354 355 356 357 358 359 360 361 362 363
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
    if (!matched)
        goto error;

    virHostMsgPass();
    return 0;

 error:
    VIR_FREE(line);
    virHostMsgFail(level, "Enable CONFIG_%s in kernel Kconfig file", config_name);
    return -1;
}

364
#ifdef HAVE_MNTENT_H
365 366 367 368 369 370 371 372 373 374 375 376 377 378
static int virHostValidateCGroupMount(const char *hvname,
                                      const char *cg_name,
                                      virHostValidateLevel level)
{
    virHostMsgCheck(hvname, "for cgroup '%s' controller mount-point", cg_name);
    FILE *fp = setmntent("/proc/mounts", "r");
    struct mntent ent;
    char mntbuf[1024];
    bool matched = false;

    if (!fp)
        goto error;

    while (getmntent_r(fp, &ent, mntbuf, sizeof(mntbuf)) && !matched) {
379 380 381 382 383 384
        char **opts;
        size_t nopts;
        size_t i;

        /* Ignore non-cgroup mounts */
        if (STRNEQ(ent.mnt_type, "cgroup"))
385 386
            continue;

387
        if (!(opts = virStringSplitCount(ent.mnt_opts, ",", 0, &nopts)))
388 389
            continue;

390 391 392 393 394 395
        /* Look for a mount option matching the cgroup name */
        for (i = 0; i < nopts; i++) {
            if (STREQ(opts[i], cg_name))
                matched = true;
        }

396
        virStringListFreeCount(opts, nopts);
397 398 399 400 401 402 403 404 405 406 407 408 409
    }
    endmntent(fp);
    if (!matched)
        goto error;

    virHostMsgPass();
    return 0;

 error:
    virHostMsgFail(level, "Mount '%s' cgroup controller (suggested at /sys/fs/cgroup/%s)",
                   cg_name, cg_name);
    return -1;
}
410 411 412 413 414 415 416 417 418 419
#else /* ! HAVE_MNTENT_H */
static int virHostValidateCGroupMount(const char *hvname,
                                      const char *cg_name,
                                      virHostValidateLevel level)
{
    virHostMsgCheck(hvname, "for cgroup '%s' controller mount-point", cg_name);
    virHostMsgFail(level, "%s", "This platform does not support cgroups");
    return -1;
}
#endif /* ! HAVE_MNTENT_H */
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436

int virHostValidateCGroupController(const char *hvname,
                                    const char *cg_name,
                                    virHostValidateLevel level,
                                    const char *config_name)
{
    if (virHostValidateCGroupSupport(hvname,
                                     cg_name,
                                     level,
                                     config_name) < 0)
        return -1;
    if (virHostValidateCGroupMount(hvname,
                                   cg_name,
                                   level) < 0)
        return -1;
    return 0;
}
437 438 439 440

int virHostValidateIOMMU(const char *hvname,
                         virHostValidateLevel level)
{
441
    virBitmapPtr flags;
442 443
    struct stat sb;
    const char *bootarg = NULL;
444
    bool isAMD = false, isIntel = false, isPPC = false;
445 446 447
    flags = virHostValidateGetCPUFlags();

    if (flags && virBitmapIsBitSet(flags, VIR_HOST_VALIDATE_CPU_FLAG_VMX))
448
        isIntel = true;
449
    else if (flags && virBitmapIsBitSet(flags, VIR_HOST_VALIDATE_CPU_FLAG_SVM))
450 451
        isAMD = true;

452 453
    virBitmapFree(flags);

454
    isPPC = ARCH_IS_PPC64(virArchFromHost());
455 456

    if (isIntel) {
457
        virHostMsgCheck(hvname, "%s", _("for device assignment IOMMU support"));
458 459 460 461 462 463 464 465 466 467 468
        if (access("/sys/firmware/acpi/tables/DMAR", F_OK) == 0) {
            virHostMsgPass();
            bootarg = "intel_iommu=on";
        } else {
            virHostMsgFail(level,
                           "No ACPI DMAR table found, IOMMU either "
                           "disabled in BIOS or not supported by this "
                           "hardware platform");
            return -1;
        }
    } else if (isAMD) {
469
        virHostMsgCheck(hvname, "%s", _("for device assignment IOMMU support"));
470 471 472 473 474 475 476 477 478 479
        if (access("/sys/firmware/acpi/tables/IVRS", F_OK) == 0) {
            virHostMsgPass();
            bootarg = "iommu=pt iommu=1";
        } else {
            virHostMsgFail(level,
                           "No ACPI IVRS table found, IOMMU either "
                           "disabled in BIOS or not supported by this "
                           "hardware platform");
            return -1;
        }
480 481
    } else if (isPPC) {
        /* Empty Block */
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    } else {
        virHostMsgFail(level,
                       "Unknown if this platform has IOMMU support");
        return -1;
    }


    /* We can only check on newer kernels with iommu groups & vfio */
    if (stat("/sys/kernel/iommu_groups", &sb) < 0)
        return 0;

    if (!S_ISDIR(sb.st_mode))
        return 0;

    virHostMsgCheck(hvname, "%s", _("if IOMMU is enabled by kernel"));
    if (sb.st_nlink <= 2) {
498 499 500 501 502 503
        if (!isPPC)
            virHostMsgFail(level,
                           "IOMMU appears to be disabled in kernel. "
                           "Add %s to kernel cmdline arguments", bootarg);
        else
            virHostMsgFail(level, "IOMMU capability not compiled into kernel.");
504 505 506 507 508
        return -1;
    }
    virHostMsgPass();
    return 0;
}