blob: fda8ded9b5e9fd3488975974e74741df9a95fc2f [file] [log] [blame]
/*****************************************************************************\
* oci_config.c - parse oci.conf configuration file.
*****************************************************************************
* Copyright (C) SchedMD LLC.
* All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* SLURM is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* SLURM 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with SLURM; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <sys/stat.h>
#include <unistd.h>
#include "slurm/slurm_errno.h"
#include "src/common/log.h"
#include "src/common/oci_config.h"
#include "src/common/parse_config.h"
#include "src/common/read_config.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#define OCI_CONF "oci.conf"
static s_p_options_t options[] = {
{"ContainerPath", S_P_STRING},
{"CreateEnvFile", S_P_STRING},
{"DisableHooks", S_P_STRING},
{"EnvExclude", S_P_STRING},
{"MountSpoolDir", S_P_STRING},
{"RunTimeCreate", S_P_STRING},
{"RunTimeDelete", S_P_STRING},
{"RunTimeKill", S_P_STRING},
{"RunTimeEnvExclude", S_P_STRING},
{"RunTimeQuery", S_P_STRING},
{"RunTimeRun", S_P_STRING},
{"RunTimeStart", S_P_STRING},
{"SrunPath", S_P_STRING},
{"SrunArgs", S_P_ARRAY},
{"DisableCleanup", S_P_BOOLEAN},
{"StdIODebug", S_P_STRING},
{"SyslogDebug", S_P_STRING},
{"FileDebug", S_P_STRING},
{"DebugFlags", S_P_STRING},
{"IgnoreFileConfigJson", S_P_BOOLEAN},
{NULL}
};
extern int get_oci_conf(oci_conf_t **oci_ptr)
{
s_p_hashtbl_t *tbl = NULL;
struct stat buf;
int rc = SLURM_SUCCESS;
oci_conf_t *oci = NULL;
char *conf_path = get_extra_conf_path(OCI_CONF);
char *disable_hooks = NULL;
char **srun_args = NULL;
int srun_args_count = 0;
char *debug_stdio = NULL, *debug_syslog = NULL;
char *debug_flags = NULL, *debug_file = NULL;
char *create_env_file = NULL;
char *runtime_env_exclude = NULL, *env_exclude = NULL;
if ((stat(conf_path, &buf) == -1)) {
error("No %s file", OCI_CONF);
xfree(conf_path);
return ENOENT;
}
oci = xmalloc(sizeof(*oci));
debug("Reading %s file %s", OCI_CONF, conf_path);
tbl = s_p_hashtbl_create(options);
if (s_p_parse_file(tbl, NULL, conf_path, 0, NULL) == SLURM_ERROR)
fatal("Could not parse %s file: %s", OCI_CONF, conf_path);
(void) s_p_get_string(&oci->container_path, "ContainerPath", tbl);
(void) s_p_get_string(&create_env_file, "CreateEnvFile", tbl);
(void) s_p_get_string(&disable_hooks, "DisableHooks", tbl);
(void) s_p_get_boolean(&oci->ignore_config_json, "IgnoreFileConfigJson", tbl);
(void) s_p_get_string(&env_exclude, "EnvExclude", tbl);
(void) s_p_get_string(&oci->mount_spool_dir, "MountSpoolDir", tbl);
(void) s_p_get_string(&oci->runtime_create, "RunTimeCreate", tbl);
(void) s_p_get_string(&oci->runtime_delete, "RunTimeDelete", tbl);
(void) s_p_get_string(&oci->runtime_kill, "RunTimeKill", tbl);
(void) s_p_get_string(&runtime_env_exclude, "RunTimeEnvExclude", tbl);
(void) s_p_get_string(&oci->runtime_query, "RunTimeQuery", tbl);
(void) s_p_get_string(&oci->runtime_run, "RunTimeRun", tbl);
(void) s_p_get_string(&oci->runtime_start, "RunTimeStart", tbl);
(void) s_p_get_string(&oci->srun_path, "SrunPath", tbl);
(void) s_p_get_array((void ***) &srun_args, &srun_args_count,
"SrunArgs", tbl);
(void) s_p_get_boolean(&oci->disable_cleanup, "DisableCleanup", tbl);
(void) s_p_get_string(&debug_stdio, "StdIODebug", tbl);
(void) s_p_get_string(&debug_syslog, "SyslogDebug", tbl);
(void) s_p_get_string(&debug_file, "FileDebug", tbl);
(void) s_p_get_string(&debug_flags, "DebugFlags", tbl);
if (debug_stdio) {
oci->stdio_log_level = log_string2num(debug_stdio);
xfree(debug_stdio);
}
if (debug_syslog) {
oci->syslog_log_level = log_string2num(debug_syslog);
xfree(debug_syslog);
}
if (debug_file) {
oci->file_log_level = log_string2num(debug_file);
xfree(debug_file);
}
if (debug_flags) {
int rc;
if ((rc = debug_str2flags(debug_flags, &oci->debug_flags)))
fatal("%s: unable to parse oci.conf debugflags=%s: %m",
__func__, debug_flags);
xfree(debug_flags);
}
if (srun_args_count) {
oci->srun_args = xcalloc((srun_args_count + 1),
sizeof(*oci->srun_args));
for (int i = 0; i < srun_args_count; i++)
oci->srun_args[i] = xstrdup(srun_args[i]);
}
if (disable_hooks) {
char *ptr1 = NULL, *ptr2 = NULL;
int i = 0;
/* NULL terminated array of char* */
oci->disable_hooks = xmalloc(sizeof(*oci->disable_hooks));
ptr1 = strtok_r(disable_hooks, ",", &ptr2);
while (ptr1) {
i++;
xrecalloc(oci->disable_hooks, (i + 1),
sizeof(*oci->disable_hooks));
oci->disable_hooks[i] = xstrdup(ptr1);
debug("%s: disable hook type %s",
__func__, oci->disable_hooks[i]);
ptr1 = strtok_r(NULL, ",", &ptr2);
}
xfree(disable_hooks);
}
if (oci->ignore_config_json) {
/*
* If site has enabled ignore config.json then sanity checking
* the runtime commands is not required as the site is faking an
* OCI runtime any way.
*/
debug("OCI container activated with IgnoreFileConfigJson=True");
} else if (!oci->runtime_create && !oci->runtime_delete &&
!oci->runtime_kill && !oci->runtime_query &&
!oci->runtime_run && !oci->runtime_start) {
error("oci.conf present but missing required options. Rejecting invalid configuration.");
rc = EINVAL;
} else if (oci->runtime_create && oci->runtime_delete &&
oci->runtime_kill && oci->runtime_query &&
!oci->runtime_run && oci->runtime_start) {
debug("OCI container activated with create/start");
} else if (!oci->runtime_create && oci->runtime_delete &&
oci->runtime_kill && oci->runtime_run &&
!oci->runtime_start) {
debug("OCI container activated with run");
} else {
error("RunTimeRun and RunCreate/RunTimeStart are mutually exclusive. All other RunTime* configurations items must be populated.");
rc = SLURM_ERROR;
}
if (!xstrcasecmp(create_env_file, "null") ||
!xstrcasecmp(create_env_file, "true") ||
!xstrcasecmp(create_env_file, "Y") ||
!xstrcasecmp(create_env_file, "Yes") ||
!xstrcasecmp(create_env_file, "1")) {
oci->create_env_file = NULL_TERMINATED_ENV_FILE;
} else if (!xstrcasecmp(create_env_file, "newline")) {
oci->create_env_file = NEWLINE_TERMINATED_ENV_FILE;
} else if (!create_env_file || !xstrcasecmp(create_env_file, "false") ||
!xstrcasecmp(create_env_file, "disabled") ||
!xstrcasecmp(create_env_file, "N") ||
!xstrcasecmp(create_env_file, "No") ||
!xstrcasecmp(create_env_file, "0")) {
oci->create_env_file = DISABLED_ENV_FILE;
} else {
error("Invalid value of CreateEnvFile=%s", create_env_file);
rc = SLURM_ERROR;
}
xfree(create_env_file);
s_p_hashtbl_destroy(tbl);
xfree(conf_path);
if (!rc && env_exclude) {
if ((rc = regcomp(&oci->env_exclude, env_exclude,
REG_EXTENDED)))
dump_regex_error(rc, &oci->env_exclude, "compile %s",
env_exclude);
else
oci->env_exclude_set = true;
}
xfree(env_exclude);
if (!rc && runtime_env_exclude) {
if ((rc = regcomp(&oci->runtime_env_exclude, runtime_env_exclude,
REG_EXTENDED)))
dump_regex_error(rc, &oci->runtime_env_exclude,
"compile %s", runtime_env_exclude);
else
oci->runtime_env_exclude_set = true;
}
xfree(runtime_env_exclude);
if (!rc) {
const char *envfile = "disabled";
free_oci_conf(*oci_ptr);
*oci_ptr = oci;
if (oci->create_env_file == NULL_TERMINATED_ENV_FILE)
envfile = "null";
else if (oci->create_env_file == NEWLINE_TERMINATED_ENV_FILE)
envfile = "newline";
debug("%s: oci.conf loaded: ContainerPath=%s CreateEnvFile=%s RunTimeCreate=%s RunTimeDelete=%s RunTimeKill=%s RunTimeQuery=%s RunTimeRun=%s RunTimeStart=%s IgnoreFileConfigJson=%c",
__func__, oci->container_path, envfile,
oci->runtime_create, oci->runtime_delete,
oci->runtime_kill, oci->runtime_query, oci->runtime_run,
oci->runtime_start,
(oci->ignore_config_json ? 'T' : 'F'));
} else {
free_oci_conf(oci);
}
return rc;
}
extern void free_oci_conf(oci_conf_t *oci)
{
if (!oci)
return;
xfree(oci->container_path);
regfree(&oci->runtime_env_exclude);
xfree(oci->mount_spool_dir);
xfree(oci->runtime_create);
xfree(oci->runtime_delete);
xfree(oci->runtime_kill);
regfree(&oci->runtime_env_exclude);
xfree(oci->runtime_query);
xfree(oci->runtime_run);
xfree(oci->runtime_start);
xfree(oci->srun_path);
for (int i = 0; oci->srun_args && oci->srun_args[i]; i++)
xfree(oci->srun_args[i]);
xfree(oci->srun_args);
if (oci->disable_hooks) {
for (int i = 0; oci->disable_hooks[i]; i++)
xfree(oci->disable_hooks[i]);
xfree(oci->disable_hooks);
}
xfree(oci);
}