implement lxc_string_split_quoted

lxc_string_split_quoted() splits a string on spaces, but keeps
groups in single or double qoutes together.  In other words,
generally what we'd want for argv behavior.

Switch lxc-execute to use this for lxc.execute.cmd.

Switch lxc-oci template to put the lxc.execute.cmd inside single
quotes, because parse_line() will eat those.  If we don't do that,
then if we have lxc.execute.cmd = /bin/echo "hello, world", then the
last double quote will disappear.

Signed-off-by: Serge Hallyn <shallyn@cisco.com>
This commit is contained in:
Serge Hallyn 2017-10-04 05:14:00 +00:00
parent 0ef43a5c1d
commit 3dca1af064
4 changed files with 71 additions and 5 deletions

View File

@ -99,10 +99,7 @@ static bool set_argv(struct lxc_conf *conf, struct lxc_arguments *args)
if (!conf->execute_cmd)
return false;
/* TODO -
we should honor '"' etc; This seems worth a new helper in utils.c.
*/
components = lxc_string_split(conf->execute_cmd, ' ');
components = lxc_string_split_quoted(conf->execute_cmd);
if (!components)
return false;

View File

@ -816,6 +816,74 @@ error_out:
return NULL;
}
static bool complete_word(char ***result, char *start, char *end, size_t *cap, size_t *cnt)
{
int r;
r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16);
if (r < 0)
return false;
(*result)[*cnt] = strndup(start, end - start);
if (!(*result)[*cnt])
return false;
(*cnt)++;
return true;
}
/*
* Given a a string 'one two "three four"', split into three words,
* one, two, and "three four"
*/
char **lxc_string_split_quoted(char *string)
{
char *nextword = string, *p, state;
char **result = NULL;
size_t result_capacity = 0;
size_t result_count = 0;
if (!string || !*string)
return calloc(1, sizeof(char *));
// TODO I'm *not* handling escaped quote
state = ' ';
for (p = string; *p; p++) {
switch(state) {
case ' ':
if (isspace(*p))
continue;
else if (*p == '"' || *p == '\'') {
nextword = p;
state = *p;
continue;
}
nextword = p;
state = 'a';
continue;
case 'a':
if (isspace(*p)) {
complete_word(&result, nextword, p, &result_capacity, &result_count);
state = ' ';
continue;
}
continue;
case '"':
case '\'':
if (*p == state) {
complete_word(&result, nextword+1, p, &result_capacity, &result_count);
state = ' ';
continue;
}
continue;
}
}
if (state == 'a')
complete_word(&result, nextword, p, &result_capacity, &result_count);
return realloc(result, (result_count + 1) * sizeof(char *));
}
char **lxc_string_split_and_trim(const char *string, char _sep)
{
char *token, *str, *saveptr = NULL;

View File

@ -292,6 +292,7 @@ extern bool lxc_string_in_list(const char *needle, const char *haystack,
char sep);
extern char **lxc_string_split(const char *string, char sep);
extern char **lxc_string_split_and_trim(const char *string, char sep);
extern char **lxc_string_split_quoted(char *string);
/* Append string to NULL-terminated string array. */
extern int lxc_append_string(char ***list, char *entry);

View File

@ -197,7 +197,7 @@ entrypoint=$(getep ${DOWNLOAD_TEMP} latest)
rm -rf "${LXC_ROOTFS}.tmp"
LXC_CONF_FILE="${LXC_PATH}/config"
echo "lxc.execute.cmd = ${entrypoint}" >> "${LXC_CONF_FILE}"
echo "lxc.execute.cmd = '${entrypoint}'" >> "${LXC_CONF_FILE}"
echo "lxc.mount.auto = proc:mixed sys:mixed cgroup:mixed" >> "${LXC_CONF_FILE}"
echo "lxc.uts.name = ${LXC_NAME}" >> ${LXC_PATH}/config