<commit_before>/*
 * bhyve_parse_command.c: Bhyve command parser
 *
 * Copyright (C) 2006-2016 Red Hat, Inc.
 * Copyright (C) 2006 <NAME>
 * Copyright (c) 2011 NetApp, Inc.
 * Copyright (C) 2016 <NAME>
 *
 * 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
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Author: <NAME> <<EMAIL>>
 */

#include <config.h>
#include <libutil.h>
#include <getopt_int.h>

#include "bhyve_capabilities.h"
#include "bhyve_command.h"
#include "bhyve_parse_command.h"
#include "viralloc.h"
#include "virlog.h"
#include "virstring.h"
#include "virutil.h"
#include "c-ctype.h"

#define VIR_FROM_THIS VIR_FROM_BHYVE

VIR_LOG_INIT("bhyve.bhyve_parse_command");

/*
 * This function takes a string representation of the command line and removes
 * all newline characters, if they are prefixed by a backslash. The result
 * should be a string with one command per line.
 *
 * NB: command MUST be NULL-Terminated.
 */
static char *
bhyveParseCommandLineUnescape(const char *command)
{
    size_t len = strlen(command);
    char *unescaped = NULL;
    char *curr_src = NULL;
    char *curr_dst = NULL;

    /* Since we are only removing characters, allocating a buffer of the same
     * size as command shouldn't be a problem here */
    if (VIR_ALLOC_N(unescaped, len+1) < 0)
        return NULL;

    /* Iterate over characters in the command, skipping "\\\n", "\\\r" as well
     * as "\\\r\n". */
    for (curr_src = (char*) command, curr_dst = unescaped; *curr_src != '\0';
        curr_src++, curr_dst++) {
        if (*curr_src == '\\') {
            switch (*(curr_src + 1)) {
                case '\n': /* \LF */
                    curr_src++;
                    curr_dst--;
                    break;
                case '\r': /* \CR */
                    curr_src++;
                    curr_dst--;
                    if (*curr_src == '\n') /* \CRLF */
                        curr_src++;
                    break;
                default:
                    *curr_dst = '\\';
            }
        } else {
            *curr_dst = *curr_src;
        }
    }

    return unescaped;
}

/*
 * This function is adapted from vm_parse_memsize in
 * /lib/libvmmapi/vmmapi.c in the FreeBSD Source tree.
 */
static int
bhyveParseMemsize(const char *arg, size_t *ret_memsize)
{
    size_t val;
    int error;

    if (virStrToLong_ul(arg, NULL, 10, &val) == 0) {
        /*
         * For the sake of backward compatibility if the memory size
         * specified on the command line is less than a megabyte then
         * it is interpreted as being in units of MB.
         */
        if (val < 1024 * 1024UL)
            val *= 1024 * 1024UL;
        *ret_memsize = val;
        error = 0;
    } else {
        error = expand_number(arg, ret_memsize);
    }

    /* use memory in KiB here */
    *ret_memsize /= 1024UL;

    return error;
}

/*
 * Try to extract loader and bhyve argv lists from a command line string.
 */
static int
bhyveCommandLineToArgv(const char *nativeConfig,
                      int *loader_argc,
                      char ***loader_argv,
                      int *bhyve_argc,
                      char ***bhyve_argv)
{
    const char *curr = NULL;
    char *nativeConfig_unescaped = NULL;
    const char *start;
    const char *next;
    char *line;
    char **lines = NULL;
    size_t i;
    size_t line_count = 0;
    size_t lines_alloc = 0;
    char **_bhyve_argv = NULL;
    char **_loader_argv = NULL;

    nativeConfig_unescaped = bhyveParseCommandLineUnescape(nativeConfig);
    if (nativeConfig_unescaped == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to unescape command line string"));
        goto error;
    }

    curr = nativeConfig_unescaped;

    /* Iterate over string, splitting on sequences of '\n' */
    while (curr && *curr != '\0') {
        start = curr;
        next = strchr(curr, '\n');

        if (VIR_STRNDUP(line, curr, next ? next - curr : -1) < 0)
            goto error;

        if (VIR_RESIZE_N(lines, lines_alloc, line_count, 2) < 0) {
            VIR_FREE(line);
            goto error;
        }

        if (*line)
            lines[line_count++] = line;
        lines[line_count] = NULL;

        while (next && (*next == '\n' || *next == '\r'
                        || STRPREFIX(next, "\r\n")))
            next++;

        curr = next;
    }

    for (i = 0; i < line_count; i++) {
        curr = lines[i];
        size_t j;
        char **arglist = NULL;
        size_t args_count = 0;
        size_t args_alloc = 0;

        /* iterate over each line, splitting on sequences of ' '. This code is
         * adapted from qemu/qemu_parse_command.c. */
        while (curr && *curr != '\0') {
            char *arg;
            start = curr;

            if (*start == '\'') {
                if (start == curr)
                    curr++;
                next = strchr(start + 1, '\'');
            } else if (*start == '"') {
                if (start == curr)
                    curr++;
                next = strchr(start + 1, '"');
            } else {
                next = strchr(start, ' ');
            }

            if (VIR_STRNDUP(arg, curr, next ? next - curr : -1) < 0)
                goto error;

            if (next && (*next == '\'' || *next == '"'))
                next++;

            if (VIR_RESIZE_N(arglist, args_alloc, args_count, 2) < 0) {
                VIR_FREE(arg);
                goto error;
            }

            arglist[args_count++] = arg;
            arglist[args_count] = NULL;

            while (next && c_isspace(*next))
                next++;

            curr = next;
        }

        VIR_FREE(nativeConfig_unescaped);

        /* To prevent a memory leak here, only set the argument lists when
         * the first matching command is found. This shouldn't really be a
         * problem, since usually no multiple loaders or bhyverun commands
         * are specified (this wouldn't really be valid anyways).
         * Otherwise, later argument lists may be assigned to _argv without
         * freeing the earlier ones. */
        if (!_bhyve_argv && STREQ(arglist[0], "/usr/sbin/bhyve")) {
            if ((VIR_REALLOC_N(_bhyve_argv, args_count + 1) < 0)
                || (!bhyve_argc))
                goto error;
            for (j = 0; j < args_count; j++)
                _bhyve_argv[j] = arglist[j];
            _bhyve_argv[j] = NULL;
            *bhyve_argc = args_count-1;
            VIR_FREE(arglist);
        } else if (!_loader_argv) {
            if ((VIR_REALLOC_N(_loader_argv, args_count + 1) < 0)
                || (!loader_argc))
                goto error;
            for (j = 0; j < args_count; j++)
                _loader_argv[j] = arglist[j];
            _loader_argv[j] = NULL;
            *loader_argc = args_count-1;
            VIR_FREE(arglist);
        } else {
            /* To prevent a use-after-free here, only free the argument list
             * when it is definitely not going to be used */
            virStringFreeList(arglist);
        }
    }

    *loader_argv = _loader_argv;
    if (!(*bhyve_argv = _bhyve_argv))
        goto error;

    virStringFreeList(lines);
    return 0;

 error:
    VIR_FREE(_loader_argv);
    VIR_FREE(_bhyve_argv);
    virStringFreeList(lines);
    return -1;
}

static int
bhyveParseBhyveLPCArg(virDomainDefPtr def,
                      unsigned caps ATTRIBUTE_UNUSED,
                      const char *arg)
{
    /* -l emulation[,config] */
    const char *separator = NULL;
    const char *param = NULL;
    size_t last = 0;
    virDomainChrDefPtr chr = NULL;
    char *type = NULL;

    separator = strchr(arg, ',');
    param = separator + 1;

    if (!separator)
        goto error;

    if (VIR_STRNDUP(type, arg, separator - arg) < 0)
        goto error;

    /* Only support com%d */
    if (STRPREFIX(type, "com") && type[4] == 0) {
        if (!(chr = virDomainChrDefNew()))
            goto error;

        chr->source.type = VIR_DOMAIN_CHR_TYPE_NMDM;
        chr->source.data.nmdm.master = NULL;
        chr->source.data.nmdm.slave = NULL;
        chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;

        if (!STRPREFIX(param, "/dev/nmdm")) {
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("Failed to set com port %s: does not start with "
                             "'/dev/nmdm'."), type);
                goto error;
        }

        if (VIR_STRDUP(chr->source.data.nmdm.master, param) < 0) {
            virDomainChrDefFree(chr);
            goto error;
        }

        if (VIR_STRDUP(chr->source.data.nmdm.slave, chr->source.data.file.path)<commit_msg>bhyve: Need to add parameter to virDomainChrDefNew<commit_after>/*
 * bhyve_parse_command.c: Bhyve command parser
 *
 * Copyright (C) 2006-2016 Red Hat, Inc.
 * Copyright (C) 2006 <NAME>
 * Copyright (c) 2011 NetApp, Inc.
 * Copyright (C) 2016 <NAME>
 *
 * 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
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Author: <NAME> <<EMAIL>>
 */

#include <config.h>
#include <libutil.h>
#include <getopt_int.h>

#include "bhyve_capabilities.h"
#include "bhyve_command.h"
#include "bhyve_parse_command.h"
#include "viralloc.h"
#include "virlog.h"
#include "virstring.h"
#include "virutil.h"
#include "c-ctype.h"

#define VIR_FROM_THIS VIR_FROM_BHYVE

VIR_LOG_INIT("bhyve.bhyve_parse_command");

/*
 * This function takes a string representation of the command line and removes
 * all newline characters, if they are prefixed by a backslash. The result
 * should be a string with one command per line.
 *
 * NB: command MUST be NULL-Terminated.
 */
static char *
bhyveParseCommandLineUnescape(const char *command)
{
    size_t len = strlen(command);
    char *unescaped = NULL;
    char *curr_src = NULL;
    char *curr_dst = NULL;

    /* Since we are only removing characters, allocating a buffer of the same
     * size as command shouldn't be a problem here */
    if (VIR_ALLOC_N(unescaped, len+1) < 0)
        return NULL;

    /* Iterate over characters in the command, skipping "\\\n", "\\\r" as well
     * as "\\\r\n". */
    for (curr_src = (char*) command, curr_dst = unescaped; *curr_src != '\0';
        curr_src++, curr_dst++) {
        if (*curr_src == '\\') {
            switch (*(curr_src + 1)) {
                case '\n': /* \LF */
                    curr_src++;
                    curr_dst--;
                    break;
                case '\r': /* \CR */
                    curr_src++;
                    curr_dst--;
                    if (*curr_src == '\n') /* \CRLF */
                        curr_src++;
                    break;
                default:
                    *curr_dst = '\\';
            }
        } else {
            *curr_dst = *curr_src;
        }
    }

    return unescaped;
}

/*
 * This function is adapted from vm_parse_memsize in
 * /lib/libvmmapi/vmmapi.c in the FreeBSD Source tree.
 */
static int
bhyveParseMemsize(const char *arg, size_t *ret_memsize)
{
    size_t val;
    int error;

    if (virStrToLong_ul(arg, NULL, 10, &val) == 0) {
        /*
         * For the sake of backward compatibility if the memory size
         * specified on the command line is less than a megabyte then
         * it is interpreted as being in units of MB.
         */
        if (val < 1024 * 1024UL)
            val *= 1024 * 1024UL;
        *ret_memsize = val;
        error = 0;
    } else {
        error = expand_number(arg, ret_memsize);
    }

    /* use memory in KiB here */
    *ret_memsize /= 1024UL;

    return error;
}

/*
 * Try to extract loader and bhyve argv lists from a command line string.
 */
static int
bhyveCommandLineToArgv(const char *nativeConfig,
                      int *loader_argc,
                      char ***loader_argv,
                      int *bhyve_argc,
                      char ***bhyve_argv)
{
    const char *curr = NULL;
    char *nativeConfig_unescaped = NULL;
    const char *start;
    const char *next;
    char *line;
    char **lines = NULL;
    size_t i;
    size_t line_count = 0;
    size_t lines_alloc = 0;
    char **_bhyve_argv = NULL;
    char **_loader_argv = NULL;

    nativeConfig_unescaped = bhyveParseCommandLineUnescape(nativeConfig);
    if (nativeConfig_unescaped == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to unescape command line string"));
        goto error;
    }

    curr = nativeConfig_unescaped;

    /* Iterate over string, splitting on sequences of '\n' */
    while (curr && *curr != '\0') {
        start = curr;
        next = strchr(curr, '\n');

        if (VIR_STRNDUP(line, curr, next ? next - curr : -1) < 0)
            goto error;

        if (VIR_RESIZE_N(lines, lines_alloc, line_count, 2) < 0) {
            VIR_FREE(line);
            goto error;
        }

        if (*line)
            lines[line_count++] = line;
        lines[line_count] = NULL;

        while (next && (*next == '\n' || *next == '\r'
                        || STRPREFIX(next, "\r\n")))
            next++;

        curr = next;
    }

    for (i = 0; i < line_count; i++) {
        curr = lines[i];
        size_t j;
        char **arglist = NULL;
        size_t args_count = 0;
        size_t args_alloc = 0;

        /* iterate over each line, splitting on sequences of ' '. This code is
         * adapted from qemu/qemu_parse_command.c. */
        while (curr && *curr != '\0') {
            char *arg;
            start = curr;

            if (*start == '\'') {
                if (start == curr)
                    curr++;
                next = strchr(start + 1, '\'');
            } else if (*start == '"') {
                if (start == curr)
                    curr++;
                next = strchr(start + 1, '"');
            } else {
                next = strchr(start, ' ');
            }

            if (VIR_STRNDUP(arg, curr, next ? next - curr : -1) < 0)
                goto error;

            if (next && (*next == '\'' || *next == '"'))
                next++;

            if (VIR_RESIZE_N(arglist, args_alloc, args_count, 2) < 0) {
                VIR_FREE(arg);
                goto error;
            }

            arglist[args_count++] = arg;
            arglist[args_count] = NULL;

            while (next && c_isspace(*next))
                next++;

            curr = next;
        }

        VIR_FREE(nativeConfig_unescaped);

        /* To prevent a memory leak here, only set the argument lists when
         * the first matching command is found. This shouldn't really be a
         * problem, since usually no multiple loaders or bhyverun commands
         * are specified (this wouldn't really be valid anyways).
         * Otherwise, later argument lists may be assigned to _argv without
         * freeing the earlier ones. */
        if (!_bhyve_argv && STREQ(arglist[0], "/usr/sbin/bhyve")) {
            if ((VIR_REALLOC_N(_bhyve_argv, args_count + 1) < 0)
                || (!bhyve_argc))
                goto error;
            for (j = 0; j < args_count; j++)
                _bhyve_argv[j] = arglist[j];
            _bhyve_argv[j] = NULL;
            *bhyve_argc = args_count-1;
            VIR_FREE(arglist);
        } else if (!_loader_argv) {
            if ((VIR_REALLOC_N(_loader_argv, args_count + 1) < 0)
                || (!loader_argc))
                goto error;
            for (j = 0; j < args_count; j++)
                _loader_argv[j] = arglist[j];
            _loader_argv[j] = NULL;
            *loader_argc = args_count-1;
            VIR_FREE(arglist);
        } else {
            /* To prevent a use-after-free here, only free the argument list
             * when it is definitely not going to be used */
            virStringFreeList(arglist);
        }
    }

    *loader_argv = _loader_argv;
    if (!(*bhyve_argv = _bhyve_argv))
        goto error;

    virStringFreeList(lines);
    return 0;

 error:
    VIR_FREE(_loader_argv);
    VIR_FREE(_bhyve_argv);
    virStringFreeList(lines);
    return -1;
}

static int
bhyveParseBhyveLPCArg(virDomainDefPtr def,
                      unsigned caps ATTRIBUTE_UNUSED,
                      const char *arg)
{
    /* -l emulation[,config] */
    const char *separator = NULL;
    const char *param = NULL;
    size_t last = 0;
    virDomainChrDefPtr chr = NULL;
    char *type = NULL;

    separator = strchr(arg, ',');
    param = separator + 1;

    if (!separator)
        goto error;

    if (VIR_STRNDUP(type, arg, separator - arg) < 0)
        goto error;

    /* Only support com%d */
    if (STRPREFIX(type, "com") && type[4] == 0) {
        if (!(chr = virDomainChrDefNew(NULL)))
            goto error;

        chr->source.type = VIR_DOMAIN_CHR_TYPE_NMDM;
        chr->source.data.nmdm.master = NULL;
        chr->source.data.nmdm.slave = NULL;
        chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;

        if (!STRPREFIX(param, "/dev/nmdm")) {
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("Failed to set com port %s: does not start with "
                             "'/dev/nmdm'."), type);
                goto error;
        }

        if (VIR_STRDUP(chr->source.data.nmdm.master, param) < 0) {
            virDomainChrDefFree(chr);
            goto error;
        }

        if (VIR_STRDUP(chr->source.data.nmdm.slave, chr->source.data.file.path)
{'fraction_non_alphanumeric': 0.11530398322851153, 'fraction_numerical': 0.009030801483631673, 'mean_word_length': 1.81069647983079, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '904599', 'n_tokens_mistral': 5999, 'n_tokens_neox': 5705, 'n_words': 1671}