tdnf/client/config.c

642 lines
18 KiB
C

/*
* Copyright (C) 2015-2023 VMware, Inc. All Rights Reserved.
*
* Licensed under the GNU Lesser General Public License v2.1 (the "License");
* you may not use this file except in compliance with the License. The terms
* of the License are located in the COPYING file of this distribution.
*/
#include "includes.h"
#include "../llconf/nodes.h"
#include "../llconf/modules.h"
#include "../llconf/entry.h"
#include "../llconf/ini.h"
#define USERAGENT_HEADER_MAX_LENGTH 256
#define KEY_MAX_LEN 32
#define OS_REL_FILE "/etc/os-release"
int
TDNFConfGetRpmVerbosity(
PTDNF pTdnf
)
{
rpmlogLvl nLogLevel = RPMLOG_INFO;
if(pTdnf && pTdnf->pArgs->nRpmVerbosity >= 0)
{
nLogLevel = pTdnf->pArgs->nRpmVerbosity;
}
return nLogLevel;
}
static uint32_t TDNFParseOSInfo(PTDNF_CONF pConf, const char *os_rel_fn)
{
char *name = NULL;
char *version = NULL;
uint32_t dwError = 0;
FILE *fp = NULL;
size_t s;
char *buf;
fp = fopen(os_rel_fn, "r");
if (!fp) {
pr_info("Warning: '%s' file is not present in the system\n", os_rel_fn);
return dwError;
}
s = USERAGENT_HEADER_MAX_LENGTH;
dwError = TDNFAllocateMemory(1, s, (void **)&buf);
BAIL_ON_TDNF_ERROR(dwError);
const char keys[][KEY_MAX_LEN] = {
"ID",
"VERSION_ID"
};
while (getline(&buf, &s, fp) >= 0) {
buf[strcspn(buf, "\n")] = '\0';
for (int i = 0; i < (int)ARRAY_SIZE(keys); ++i) {
const char *key = keys[i];
int key_l = strlen(key);
if (strncmp(key, buf, key_l))
continue;
/* read the value within double quotes */
char *beg = strchr(buf + key_l + 1, '"'); // skip till 'KEY='
if (beg) {
char *end;
beg += 1;
end = strrchr(beg, '"');
*end = '\0';
snprintf(buf, s, "%s=%s", key, beg);
}
if (strncmp(buf, "ID=", sizeof("ID=") - 1)) {
dwError = TDNFAllocateString(buf, &name);
} else if (strncmp(buf, "VERSION_ID=", sizeof("VERSION_ID=") - 1)) {
dwError = TDNFAllocateString(buf, &version);
}
BAIL_ON_TDNF_ERROR(dwError);
}
}
pConf->pszOSName = name;
pConf->pszOSVersion = version;
cleanup:
TDNF_SAFE_FREE_MEMORY(buf);
fclose(fp);
return dwError;
error:
TDNF_SAFE_FREE_MEMORY(name);
TDNF_SAFE_FREE_MEMORY(version);
goto cleanup;
}
static
uint32_t
TDNFConfigFromCnfTree(PTDNF_CONF pConf, struct cnfnode *cn_top)
{
uint32_t dwError = 0;
struct cnfnode *cn;
const char *pszProxyUser = NULL;
const char *pszProxyPass = NULL;
for(cn = cn_top->first_child; cn; cn = cn->next)
{
if ((cn->name[0] == '.') || (cn->value == NULL))
continue;
if (strcmp(cn->name, TDNF_CONF_KEY_INSTALLONLY_LIMIT) == 0)
{
pConf->nInstallOnlyLimit = strtoi(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_CLEAN_REQ_ON_REMOVE) == 0)
{
pConf->nCleanRequirementsOnRemove = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_GPGCHECK) == 0)
{
pConf->nGPGCheck = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_SSL_VERIFY) == 0)
{
pConf->nSSLVerify = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_KEEP_CACHE) == 0)
{
pConf->nKeepCache = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_REPOSDIR) == 0 ||
strcmp(cn->name, TDNF_CONF_KEY_REPODIR) == 0)
{
SET_STRING(pConf->pszRepoDir, cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_CACHEDIR) == 0)
{
SET_STRING(pConf->pszCacheDir, cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PERSISTDIR) == 0)
{
SET_STRING(pConf->pszPersistDir, cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_DISTROVERPKGS) == 0)
{
dwError = TDNFSplitStringToArray(cn->value,
" ", &pConf->ppszDistroVerPkgs);
BAIL_ON_TDNF_ERROR(dwError);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_EXCLUDE) == 0)
{
dwError = TDNFSplitStringToArray(cn->value,
" ", &pConf->ppszExcludes);
BAIL_ON_TDNF_ERROR(dwError);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_MINVERSIONS) == 0)
{
dwError = TDNFSplitStringToArray(cn->value,
" ", &pConf->ppszMinVersions);
BAIL_ON_TDNF_ERROR(dwError);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_OPENMAX) == 0)
{
pConf->nOpenMax = strtoi(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_CHECK_UPDATE_COMPAT) == 0)
{
pConf->nCheckUpdateCompat = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_DISTROSYNC_REINSTALL_CHANGED) == 0)
{
pConf->nDistroSyncReinstallChanged = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PROXY) == 0)
{
SET_STRING(pConf->pszProxy, cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PROXY_USER) == 0)
{
pszProxyUser = cn->value;
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PROXY_PASS) == 0)
{
pszProxyPass = cn->value;
}
else if (strcmp(cn->name, TDNF_CONF_KEY_INSTALLONLYPKGS) == 0)
{
dwError = TDNFSplitStringToArray(cn->value,
" ", &pConf->ppszInstallOnlyPkgs);
BAIL_ON_TDNF_ERROR(dwError);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_VARS_DIRS) == 0)
{
dwError = TDNFSplitStringToArray(cn->value,
" ", &pConf->ppszVarsDirs);
BAIL_ON_TDNF_ERROR(dwError);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PLUGINS) == 0)
{
pConf->nPluginsEnabled = isTrue(cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PLUGIN_CONF_PATH) == 0)
{
SET_STRING(pConf->pszPluginConfPath, cn->value);
}
else if (strcmp(cn->name, TDNF_CONF_KEY_PLUGIN_PATH) == 0)
{
SET_STRING(pConf->pszPluginPath, cn->value);
}
}
if (pszProxyUser && pszProxyPass)
{
dwError = TDNFAllocateStringPrintf(
&pConf->pszProxyUserPass,
"%s:%s",
pszProxyUser,
pszProxyPass);
BAIL_ON_TDNF_ERROR(dwError);
}
cleanup:
return dwError;
error:
goto cleanup;
}
uint32_t
TDNFReadConfig(
PTDNF pTdnf,
const char* pszConfFile,
const char* pszGroup
)
{
uint32_t dwError = 0;
PTDNF_CONF pConf = NULL;
char *pszConfDir = NULL;
char *pszMinVersionsDir = NULL;
char *pszPkgLocksDir = NULL;
char *pszProtectedDir = NULL;
const char *pszTdnfVersion = NULL;
struct cnfnode *cn_conf = NULL;
struct cnfmodule *mod_ini;
if(!pTdnf ||
IsNullOrEmptyString(pszConfFile) ||
IsNullOrEmptyString(pszGroup))
{
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
dwError = TDNFAllocateMemory(
1,
sizeof(TDNF_CONF),
(void**)&pConf);
BAIL_ON_TDNF_ERROR(dwError);
/* defaults */
pConf->nGPGCheck = 0;
pConf->nInstallOnlyLimit = 1;
pConf->nCleanRequirementsOnRemove = 0;
pConf->nKeepCache = 0;
pConf->nOpenMax = TDNF_CONF_DEFAULT_OPENMAX;
pConf->nInstallOnlyLimit = TDNF_CONF_DEFAULT_INSTALLONLY_LIMIT;
pConf->nSSLVerify = TDNF_CONF_DEFAULT_SSLVERIFY;
register_ini(NULL);
mod_ini = find_cnfmodule("ini");
if (mod_ini == NULL) {
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
cn_conf = cnfmodule_parse_file(mod_ini, pszConfFile);
if (cn_conf == NULL)
{
if (errno != 0)
{
dwError = errno;
BAIL_ON_TDNF_SYSTEM_ERROR(dwError);
}
else
{
dwError = ERROR_TDNF_CONF_FILE_LOAD;
BAIL_ON_TDNF_ERROR(dwError);
}
}
dwError = TDNFParseOSInfo(pConf, OS_REL_FILE);
BAIL_ON_TDNF_ERROR(dwError);
/* cn_conf == NULL => we will not reach here */
/* coverity[var_deref_op] */
dwError = TDNFConfigFromCnfTree(pConf, cn_conf->first_child);
BAIL_ON_TDNF_ERROR(dwError);
/* override from cmd line */
if (pTdnf->pArgs->nNoGPGCheck) {
pConf->nGPGCheck = 0;
}
/* these two have no config setting (future?) */
if (pTdnf->pArgs->nSkipDigest) {
pConf->nSkipDigest = 1;
}
if (pTdnf->pArgs->nSkipSignature) {
pConf->nSkipSignature = 1;
}
pszTdnfVersion = TDNFGetVersion();
if (pConf->pszOSName == NULL)
TDNFAllocateString("UNKNOWN", &pConf->pszOSName);
if (pConf->pszOSVersion == NULL)
TDNFAllocateString("UNKNOWN", &pConf->pszOSVersion);
dwError = TDNFAllocateStringPrintf(&pConf->pszUserAgentHeader, "tdnf/%s %s/%s", pszTdnfVersion, pConf->pszOSName, pConf->pszOSVersion);
BAIL_ON_TDNF_ERROR(dwError);
if (pConf->pszRepoDir == NULL)
pConf->pszRepoDir = strdup(TDNF_DEFAULT_REPO_LOCATION);
if (pConf->pszCacheDir == NULL)
pConf->pszCacheDir = strdup(TDNF_DEFAULT_CACHE_LOCATION);
if (pConf->ppszDistroVerPkgs == NULL) {
dwError = TDNFSplitStringToArray(TDNF_DEFAULT_DISTROVERPKGS,
" ", &pConf->ppszDistroVerPkgs);
BAIL_ON_TDNF_ERROR(dwError);
}
if (pConf->pszPersistDir == NULL)
pConf->pszPersistDir = strdup(TDNF_DEFAULT_DB_LOCATION);
if (pConf->ppszVarsDirs == NULL) {
dwError = TDNFSplitStringToArray(TDNF_DEFAULT_VARS_DIRS,
" ", &pConf->ppszVarsDirs);
BAIL_ON_TDNF_ERROR(dwError);
}
if (pConf->pszPluginPath == NULL)
pConf->pszPluginPath = strdup(TDNF_DEFAULT_PLUGIN_PATH);
if (pConf->pszPluginConfPath == NULL)
pConf->pszPluginConfPath = strdup(TDNF_DEFAULT_PLUGIN_CONF_PATH);
dwError = TDNFDirName(pszConfFile, &pszConfDir);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFJoinPath(&pszMinVersionsDir, pszConfDir, "minversions.d", NULL);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFReadConfFilesFromDir(pszMinVersionsDir, &pConf->ppszMinVersions);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFJoinPath(&pszPkgLocksDir, pszConfDir, "locks.d", NULL);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFReadConfFilesFromDir(pszPkgLocksDir, &pConf->ppszPkgLocks);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFJoinPath(&pszProtectedDir, pszConfDir, "protected.d", NULL);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFReadConfFilesFromDir(pszProtectedDir, &pConf->ppszProtectedPkgs);
BAIL_ON_TDNF_ERROR(dwError);
pTdnf->pConf = pConf;
cleanup:
destroy_cnftree(cn_conf);
TDNF_SAFE_FREE_MEMORY(pszConfDir);
TDNF_SAFE_FREE_MEMORY(pszMinVersionsDir);
TDNF_SAFE_FREE_MEMORY(pszPkgLocksDir);
TDNF_SAFE_FREE_MEMORY(pszProtectedDir);
return dwError;
error:
if(pTdnf)
{
pTdnf->pConf = NULL;
}
if(pConf)
{
TDNFFreeConfig(pConf);
}
goto cleanup;
}
uint32_t
TDNFConfigExpandVars(
PTDNF pTdnf
)
{
uint32_t dwError = 0;
PTDNF_CONF pConf = NULL;
if(!pTdnf || !pTdnf->pConf)
{
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
pConf = pTdnf->pConf;
//Allow --releasever overrides
if(!pConf->pszVarReleaseVer &&
!IsNullOrEmptyString(pTdnf->pArgs->pszReleaseVer))
{
dwError = TDNFAllocateString(pTdnf->pArgs->pszReleaseVer,
&pConf->pszVarReleaseVer);
BAIL_ON_TDNF_ERROR(dwError);
}
if(!pConf->pszVarReleaseVer || IsNullOrEmptyString(pTdnf->pArgs->pszReleaseVer))
{
int i;
for (i = 0; pConf->ppszDistroVerPkgs[i]; i++) {
dwError = TDNFGetReleaseVersion(
pTdnf->pArgs->pszInstallRoot,
pConf->ppszDistroVerPkgs[i],
&pConf->pszVarReleaseVer);
if (dwError == 0)
break;
else if (dwError != ERROR_TDNF_NO_DISTROVERPKG)
BAIL_ON_TDNF_ERROR(dwError);
}
BAIL_ON_TDNF_ERROR(dwError);
}
if(!pConf->pszVarBaseArch)
{
dwError = TDNFGetKernelArch(&pConf->pszVarBaseArch);
BAIL_ON_TDNF_ERROR(dwError);
}
cleanup:
return dwError;
error:
goto cleanup;
}
void
TDNFFreeConfig(
PTDNF_CONF pConf
)
{
if(pConf)
{
TDNF_SAFE_FREE_MEMORY(pConf->pszProxy);
TDNF_SAFE_FREE_MEMORY(pConf->pszProxyUserPass);
TDNF_SAFE_FREE_MEMORY(pConf->pszRepoDir);
TDNF_SAFE_FREE_MEMORY(pConf->pszCacheDir);
TDNF_SAFE_FREE_MEMORY(pConf->pszPersistDir);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszDistroVerPkgs);
TDNF_SAFE_FREE_MEMORY(pConf->pszVarReleaseVer);
TDNF_SAFE_FREE_MEMORY(pConf->pszVarBaseArch);
TDNF_SAFE_FREE_MEMORY(pConf->pszBaseArch);
TDNF_SAFE_FREE_MEMORY(pConf->pszUserAgentHeader);
TDNF_SAFE_FREE_MEMORY(pConf->pszOSName);
TDNF_SAFE_FREE_MEMORY(pConf->pszOSVersion);
TDNF_SAFE_FREE_MEMORY(pConf->pszPluginPath);
TDNF_SAFE_FREE_MEMORY(pConf->pszPluginConfPath);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszExcludes);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszMinVersions);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszPkgLocks);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszProtectedPkgs);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszInstallOnlyPkgs);
TDNF_SAFE_FREE_STRINGARRAY(pConf->ppszVarsDirs);
TDNFFreeMemory(pConf);
}
}
uint32_t
TDNFConfigReplaceVars(
PTDNF pTdnf,
char** ppszString
)
{
uint32_t dwError = 0;
char* pszDst = NULL;
struct cnfnode * cn_vars = NULL, *cn;
if(!pTdnf || !ppszString || IsNullOrEmptyString(*ppszString))
{
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
cn_vars = parse_varsdirs(pTdnf->pConf->ppszVarsDirs);
if (cn_vars == NULL) {
pr_err("parsing vars failed: %s (%d)\n", strerror(errno), errno);
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
cn = create_cnfnode(TDNF_VAR_RELEASEVER);
cnfnode_setval(cn, pTdnf->pConf->pszVarReleaseVer);
append_node(cn_vars, cn);
cn = create_cnfnode(TDNF_VAR_BASEARCH);
cnfnode_setval(cn, pTdnf->pConf->pszVarBaseArch);
append_node(cn_vars, cn);
pszDst = replace_vars(cn_vars, *ppszString);
if (pszDst == NULL) {
pr_err("replacing vars in %s failed\n", *ppszString);
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
TDNFFreeMemory(*ppszString);
*ppszString = pszDst;
cleanup:
destroy_cnftree(cn_vars);
return dwError;
error:
TDNF_SAFE_FREE_MEMORY(pszDst);
goto cleanup;
}
/*
* Read all minimal versions files from pszDir, and store results into
* string array pointed to by pppszLines. pppszLines may already
* have values set from the config file, which are preserved.
*/
uint32_t
TDNFReadConfFilesFromDir(
char *pszDir,
char ***pppszLines
)
{
uint32_t dwError = 0;
DIR *pDir = NULL;
struct dirent *pEnt = NULL;
char *pszFile = NULL;
char **ppszNewLines = NULL;
char ***pppszArrayList = NULL;
int nFileCount = 0;
int i, j, k;
int nLineCount = 0;
int nTmp = 0;
if(IsNullOrEmptyString(pszDir) || !pppszLines)
{
dwError = ERROR_TDNF_INVALID_PARAMETER;
BAIL_ON_TDNF_ERROR(dwError);
}
/* first, open directory and count all files match *.conf,
* so we know how much memory we need */
pDir = opendir(pszDir);
if (pDir == NULL)
{
goto cleanup;
}
while((pEnt = readdir(pDir)) != NULL)
{
if (fnmatch("*.conf", pEnt->d_name, 0) != 0)
{
continue;
}
nFileCount++;
}
closedir(pDir);
pDir = NULL;
/* allocate memory for our string array */
dwError = TDNFAllocateMemory(nFileCount + 1, sizeof(char **), (void **)&pppszArrayList);
BAIL_ON_TDNF_ERROR(dwError);
/* read directory again and the files, store content of each file
* temporarily in pppszArrayList[i] */
i = 0;
pDir = opendir(pszDir);
while((pEnt = readdir(pDir)) != NULL && i < nFileCount)
{
if (fnmatch("*.conf", pEnt->d_name, 0) != 0)
{
continue;
}
dwError = TDNFJoinPath(&pszFile, pszDir, pEnt->d_name, NULL);
BAIL_ON_TDNF_ERROR(dwError);
dwError = TDNFReadFileToStringArray(pszFile, &pppszArrayList[i]);
BAIL_ON_TDNF_ERROR(dwError);
TDNF_SAFE_FREE_MEMORY(pszFile);
i++;
}
closedir(pDir);
pDir = NULL;
/* append values that are already set */
pppszArrayList[i] = *pppszLines;
/* each file can have multiple lines, count them */
for (i = 0; pppszArrayList[i]; i++)
{
dwError = TDNFStringArrayCount(pppszArrayList[i], &nTmp);
BAIL_ON_TDNF_ERROR(dwError);
nLineCount += nTmp;
}
/* move the lines from 2 dimensional pppszArrayList to
* flat pointer list ppszLines */
dwError = TDNFAllocateMemory(nLineCount+1, sizeof(char *), (void **)&ppszNewLines);
BAIL_ON_TDNF_ERROR(dwError);
for (i = 0, k = 0; pppszArrayList[i]; i++)
{
for (j = 0; pppszArrayList[i][j]; j++)
{
ppszNewLines[k++] = pppszArrayList[i][j];
}
TDNF_SAFE_FREE_MEMORY(pppszArrayList[i]);
}
*pppszLines = ppszNewLines;
cleanup:
if (pDir)
{
closedir(pDir);
}
TDNF_SAFE_FREE_MEMORY(pppszArrayList);
TDNF_SAFE_FREE_MEMORY(pszFile);
return dwError;
error:
goto cleanup;
}