diff --git a/ci/Dockerfile.fedora b/ci/Dockerfile.fedora index 8fe6b129..cf00105c 100644 --- a/ci/Dockerfile.fedora +++ b/ci/Dockerfile.fedora @@ -1,4 +1,4 @@ -FROM fedora:34 +FROM fedora:39 RUN dnf -y upgrade RUN dnf -y install gcc make cmake libcurl-devel rpm-devel rpm-build \ diff --git a/client/api.c b/client/api.c index 75c12103..ca8b7be2 100644 --- a/client/api.c +++ b/client/api.c @@ -606,12 +606,8 @@ TDNFOpenHandle( uint32_t dwError = 0; PTDNF pTdnf = NULL; PSolvSack pSack = NULL; - char *pszCacheDir = NULL; - char *pszRepoDir = NULL; char *pszConfFile = NULL; char *pszConfFileInstallRoot = NULL; - int nHasOptReposDir = 0; - int nHasOptCacheDir = 0; PTDNF_CMD_OPT pOpt = NULL; if(!pArgs || !ppTdnf) @@ -680,69 +676,6 @@ TDNFOpenHandle( GlobalSetDnfCheckUpdateCompat(pTdnf->pConf->nCheckUpdateCompat); - dwError = TDNFHasOpt(pTdnf->pArgs, TDNF_CONF_KEY_REPOSDIR, &nHasOptReposDir); - BAIL_ON_TDNF_ERROR(dwError); - - dwError = TDNFHasOpt(pTdnf->pArgs, TDNF_CONF_KEY_CACHEDIR, &nHasOptCacheDir); - BAIL_ON_TDNF_ERROR(dwError); - - if (!IsNullOrEmptyString(pTdnf->pArgs->pszInstallRoot) && - strcmp(pTdnf->pArgs->pszInstallRoot, "/")) - { - int nIsDir = 0; - - if(!nHasOptCacheDir) - { - dwError = TDNFJoinPath(&pszCacheDir, - pTdnf->pArgs->pszInstallRoot, - pTdnf->pConf->pszCacheDir, - NULL); - BAIL_ON_TDNF_ERROR(dwError); - - TDNF_SAFE_FREE_MEMORY(pTdnf->pConf->pszCacheDir); - pTdnf->pConf->pszCacheDir = pszCacheDir; - pszCacheDir = NULL; - } - - if (!nHasOptReposDir) - { - dwError = TDNFJoinPath(&pszRepoDir, - pTdnf->pArgs->pszInstallRoot, - pTdnf->pConf->pszRepoDir, - NULL); - BAIL_ON_TDNF_ERROR(dwError); - - dwError = TDNFIsDir(pszRepoDir, &nIsDir); - if (dwError == ERROR_TDNF_FILE_NOT_FOUND) - { - nIsDir = 0; - dwError = 0; - } - BAIL_ON_TDNF_ERROR(dwError); - if (nIsDir) - { - TDNF_SAFE_FREE_MEMORY(pTdnf->pConf->pszRepoDir); - pTdnf->pConf->pszRepoDir = pszRepoDir; - pszRepoDir = NULL; - } - } - } - - /* cachedir and reoposdir when set with setopt are always relative to host */ - if (nHasOptReposDir) - { - TDNF_SAFE_FREE_MEMORY(pTdnf->pConf->pszRepoDir); - dwError = TDNFGetCmdOptValue(pTdnf->pArgs, TDNF_CONF_KEY_REPOSDIR, &pTdnf->pConf->pszRepoDir); - BAIL_ON_TDNF_ERROR(dwError); - } - - if (nHasOptCacheDir) - { - TDNF_SAFE_FREE_MEMORY(pTdnf->pConf->pszCacheDir); - dwError = TDNFGetCmdOptValue(pTdnf->pArgs, TDNF_CONF_KEY_CACHEDIR, &pTdnf->pConf->pszCacheDir); - BAIL_ON_TDNF_ERROR(dwError); - } - for (pOpt = pTdnf->pArgs->pSetOpt; pOpt; pOpt = pOpt->pNext) { /* set macros from command line */ @@ -769,7 +702,7 @@ TDNFOpenHandle( if(!pArgs->nAllDeps) { - dwError = SolvReadInstalledRpms(pSack->pPool->installed, pszCacheDir); + dwError = SolvReadInstalledRpms(pSack->pPool->installed, pTdnf->pConf->pszCacheDir); BAIL_ON_TDNF_LIBSOLV_ERROR(dwError); } @@ -791,8 +724,6 @@ TDNFOpenHandle( *ppTdnf = pTdnf; cleanup: - TDNF_SAFE_FREE_MEMORY(pszCacheDir); - TDNF_SAFE_FREE_MEMORY(pszRepoDir); TDNF_SAFE_FREE_MEMORY(pszConfFile); TDNF_SAFE_FREE_MEMORY(pszConfFileInstallRoot); return dwError; diff --git a/client/config.c b/client/config.c index 81eefeb4..bd8f82b6 100644 --- a/client/config.c +++ b/client/config.c @@ -13,9 +13,11 @@ #include "../llconf/entry.h" #include "../llconf/ini.h" + #define USERAGENT_HEADER_MAX_LENGTH 256 #define OS_CONF_FILE "/etc/os-release" + int TDNFConfGetRpmVerbosity( PTDNF pTdnf @@ -82,6 +84,42 @@ static uint32_t TDNFParseOSInfo(PTDNF_CONF pConf) goto cleanup; } +struct { + char *name; + rpmtransFlags flag; +} rpmtransflags_map[] = { + {"noscripts", RPMTRANS_FLAG_NOSCRIPTS }, + {"justdb", RPMTRANS_FLAG_JUSTDB }, + {"notriggers", RPMTRANS_FLAG_NOTRIGGERS }, + {"nodocs", RPMTRANS_FLAG_NODOCS }, + {"allfiles", RPMTRANS_FLAG_ALLFILES }, + {"noplugins", RPMTRANS_FLAG_NOPLUGINS }, + {"nocontexts", RPMTRANS_FLAG_NOCONTEXTS }, + {"nocaps", RPMTRANS_FLAG_NOCAPS}, + {"nodb", RPMTRANS_FLAG_NODB}, + +// {"nopreuntrans", RPMTRANS_FLAG_NOPREUNTRANS }, +// {"nopostuntrans", RPMTRANS_FLAG_NOPOSTUNTRANS }, + {"notriggerprein", RPMTRANS_FLAG_NOTRIGGERPREIN }, + {"nopre", RPMTRANS_FLAG_NOPRE }, + {"nopost", RPMTRANS_FLAG_NOPOST }, + {"notriggerin", RPMTRANS_FLAG_NOTRIGGERIN }, + {"notriggerun", RPMTRANS_FLAG_NOTRIGGERUN }, + {"nopreun", RPMTRANS_FLAG_NOPREUN }, + {"nopostun", RPMTRANS_FLAG_NOPOSTUN }, + {"notriggerpostun", RPMTRANS_FLAG_NOTRIGGERPOSTUN }, + {"nopretrans", RPMTRANS_FLAG_NOPRETRANS }, + {"noposttrans", RPMTRANS_FLAG_NOPOSTTRANS}, +// {"nosysusers", RPMTRANS_FLAG_NOSYSUSERS}, + {"nomd5", RPMTRANS_FLAG_NOMD5}, + {"nofiledigest", RPMTRANS_FLAG_NOFILEDIGEST}, + + {"noartifacts", RPMTRANS_FLAG_NOARTIFACTS}, + {"noconfigs", RPMTRANS_FLAG_NOCONFIGS}, + {"deploops", RPMTRANS_FLAG_DEPLOOPS}, + {NULL, RPMTRANS_FLAG_NONE} +}; + static uint32_t TDNFConfigFromCnfTree(PTDNF_CONF pConf, struct cnfnode *cn_top) @@ -137,14 +175,12 @@ TDNFConfigFromCnfTree(PTDNF_CONF pConf, struct cnfnode *cn_top) } else if (strcmp(cn->name, TDNF_CONF_KEY_EXCLUDE) == 0) { - dwError = TDNFSplitStringToArray(cn->value, - " ", &pConf->ppszExcludes); + dwError = TDNFAddStringArray(&pConf->ppszExcludes, cn->value); BAIL_ON_TDNF_ERROR(dwError); } else if (strcmp(cn->name, TDNF_CONF_KEY_MINVERSIONS) == 0) { - dwError = TDNFSplitStringToArray(cn->value, - " ", &pConf->ppszMinVersions); + dwError = TDNFAddStringArray(&pConf->ppszMinVersions, cn->value); BAIL_ON_TDNF_ERROR(dwError); } else if (strcmp(cn->name, TDNF_CONF_KEY_OPENMAX) == 0) @@ -173,8 +209,7 @@ TDNFConfigFromCnfTree(PTDNF_CONF pConf, struct cnfnode *cn_top) } else if (strcmp(cn->name, TDNF_CONF_KEY_INSTALLONLYPKGS) == 0) { - dwError = TDNFSplitStringToArray(cn->value, - " ", &pConf->ppszInstallOnlyPkgs); + dwError = TDNFAddStringArray(&pConf->ppszInstallOnlyPkgs, cn->value); BAIL_ON_TDNF_ERROR(dwError); } else if (strcmp(cn->name, TDNF_CONF_KEY_VARS_DIRS) == 0) @@ -195,10 +230,45 @@ TDNFConfigFromCnfTree(PTDNF_CONF pConf, struct cnfnode *cn_top) { SET_STRING(pConf->pszPluginPath, cn->value); } + else if (strcmp(cn->name, TDNF_CONF_KEY_TSFLAGS) == 0) + { + if (cn->value == NULL || strcmp(cn->value, "") == 0) { + pConf->rpmTransFlags = RPMTRANS_FLAG_NONE; + } else { + char *value = strdup(cn->value); + char *saveptr = NULL, *str, *token; + int i; + + for (str = value; ; str = NULL){ + token = strtok_r(str, " ", &saveptr); + if (token == NULL) + break; + + for (i = 0; rpmtransflags_map[i].name != NULL; i++) { + if (strcmp(token, rpmtransflags_map[i].name) == 0){ + pConf->rpmTransFlags |= rpmtransflags_map[i].flag; + break; + } + } + if (rpmtransflags_map[i].name == NULL) { + pr_err("unknown tsflag '%s'\n", token); + free(value); + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + /* in rpmts.h, deprecated flags will be set to 0. Warn user about it. */ + if (rpmtransflags_map[i].flag == 0) { + pr_info("flag tsflag '%s' is not suported and has no effect\n", token); + } + } + free(value); + } + } } if (pszProxyUser && pszProxyPass) { + TDNF_SAFE_FREE_MEMORY(pConf->pszProxyUserPass); dwError = TDNFAllocateStringPrintf( &pConf->pszProxyUserPass, "%s:%s", @@ -227,9 +297,9 @@ TDNFReadConfig( char *pszMinVersionsDir = NULL; char *pszPkgLocksDir = NULL; char *pszProtectedDir = NULL; - + char *pszCacheDir = NULL; + char *pszRepoDir = NULL; const char *pszTdnfVersion = NULL; - struct cnfnode *cn_conf = NULL; struct cnfmodule *mod_ini; @@ -286,6 +356,51 @@ TDNFReadConfig( dwError = TDNFConfigFromCnfTree(pConf, cn_conf->first_child); 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 (!IsNullOrEmptyString(pTdnf->pArgs->pszInstallRoot) && + strcmp(pTdnf->pArgs->pszInstallRoot, "/")) + { + int nIsDir = 0; + dwError = TDNFJoinPath(&pszCacheDir, + pTdnf->pArgs->pszInstallRoot, + pConf->pszCacheDir, + NULL); + BAIL_ON_TDNF_ERROR(dwError); + + TDNF_SAFE_FREE_MEMORY(pConf->pszCacheDir); + pConf->pszCacheDir = pszCacheDir; + pszCacheDir = NULL; + + dwError = TDNFJoinPath(&pszRepoDir, + pTdnf->pArgs->pszInstallRoot, + pConf->pszRepoDir, + NULL); + BAIL_ON_TDNF_ERROR(dwError); + + dwError = TDNFIsDir(pszRepoDir, &nIsDir); + if (dwError == ERROR_TDNF_FILE_NOT_FOUND) + { + nIsDir = 0; + dwError = 0; + } + BAIL_ON_TDNF_ERROR(dwError); + if (nIsDir) + { + TDNF_SAFE_FREE_MEMORY(pConf->pszRepoDir); + pConf->pszRepoDir = pszRepoDir; + pszRepoDir = NULL; + } + } + + if (pTdnf->pArgs->cn_setopts) { + dwError = TDNFConfigFromCnfTree(pConf, pTdnf->pArgs->cn_setopts); + BAIL_ON_TDNF_ERROR(dwError); + } + pszTdnfVersion = TDNFGetVersion(); if (pConf->pszOSName == NULL) @@ -297,15 +412,12 @@ TDNFReadConfig( 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); @@ -345,6 +457,8 @@ TDNFReadConfig( cleanup: destroy_cnftree(cn_conf); + TDNF_SAFE_FREE_MEMORY(pszCacheDir); + TDNF_SAFE_FREE_MEMORY(pszRepoDir); TDNF_SAFE_FREE_MEMORY(pszConfDir); TDNF_SAFE_FREE_MEMORY(pszMinVersionsDir); TDNF_SAFE_FREE_MEMORY(pszPkgLocksDir); diff --git a/client/repolist.c b/client/repolist.c index bbf9d7d0..0d3cacbd 100644 --- a/client/repolist.c +++ b/client/repolist.c @@ -47,8 +47,124 @@ TDNFCreateRepo( const char *pszId ); +static +uint32_t +TDNFRepoConfigFromCnfTree(PTDNF_REPO_DATA pRepo, struct cnfnode *cn_top) +{ + uint32_t dwError = 0; + struct cnfnode *cn; + + for(cn = cn_top->first_child; cn; cn = cn->next) + { + if ((cn->name[0] == '.') || (cn->value == NULL)) + continue; + + if (strcmp(cn->name, TDNF_REPO_KEY_ENABLED) == 0) + { + pRepo->nEnabled = isTrue(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_NAME) == 0) + { + SET_STRING(pRepo->pszName, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_BASEURL) == 0) + { + dwError = TDNFAddStringArray(&pRepo->ppszBaseUrls, cn->value); + BAIL_ON_TDNF_ERROR(dwError); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_METALINK) == 0) + { + SET_STRING(pRepo->pszMetaLink, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_MIRRORLIST) == 0) + { + SET_STRING(pRepo->pszMirrorList, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP) == 0) + { + pRepo->nSkipIfUnavailable = isTrue(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_GPGCHECK) == 0) + { + pRepo->nGPGCheck = isTrue(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_GPGKEY) == 0) + { + dwError = TDNFAddStringArray(&pRepo->ppszUrlGPGKeys, cn->value); + BAIL_ON_TDNF_ERROR(dwError); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_USERNAME) == 0) + { + SET_STRING(pRepo->pszUser, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_PASSWORD) == 0) + { + SET_STRING(pRepo->pszPass, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_PRIORITY) == 0) + { + pRepo->nPriority = strtoi(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_TIMEOUT) == 0) + { + pRepo->nTimeout = strtoi(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_RETRIES) == 0) + { + pRepo->nRetries = strtoi(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_MINRATE) == 0) + { + pRepo->nMinrate = strtoi(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_THROTTLE) == 0) + { + pRepo->nThrottle = strtoi(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_VERIFY) == 0) + { + pRepo->nSSLVerify = isTrue(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_CA_CERT) == 0) + { + SET_STRING(pRepo->pszSSLCaCert, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_CLI_CERT) == 0) + { + SET_STRING(pRepo->pszSSLClientCert, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_CLI_KEY) == 0) + { + SET_STRING(pRepo->pszSSLClientKey, cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_METADATA_EXPIRE) == 0) + { + dwError = TDNFParseMetadataExpire( + cn->value, + &pRepo->lMetadataExpire); + BAIL_ON_TDNF_ERROR(dwError); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP_MD_FILELISTS) == 0) + { + pRepo->nSkipMDFileLists = isTrue(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP_MD_UPDATEINFO) == 0) + { + pRepo->nSkipMDUpdateInfo = isTrue(cn->value); + } + else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP_MD_OTHER) == 0) + { + pRepo->nSkipMDOther = isTrue(cn->value); + } + } +cleanup: + return dwError; +error: + goto cleanup; +} + uint32_t TDNFLoadRepoData( PTDNF pTdnf, @@ -66,6 +182,8 @@ TDNFLoadRepoData( char **ppszUrlIdTuple = NULL; PTDNF_REPO_DATA pRepoParsePre = NULL; PTDNF_REPO_DATA pRepoParseNext = NULL; + PTDNF_REPO_DATA pRepo = NULL; + struct cnfnode *cn_repo; if(!pTdnf || !pTdnf->pConf || !pTdnf->pArgs || !ppReposAll) { @@ -159,7 +277,6 @@ TDNFLoadRepoData( } for (pRepoParsePre = pReposAll; pRepoParsePre; pRepoParsePre = pRepoParsePre->pNext) { - for (pRepoParseNext = pRepoParsePre->pNext; pRepoParseNext; pRepoParseNext = pRepoParseNext->pNext) { if (!strcmp(pRepoParsePre->pszId, pRepoParseNext->pszId)) { pr_err("ERROR: duplicate repo id: %s\n", pRepoParsePre->pszId); @@ -169,6 +286,16 @@ TDNFLoadRepoData( } } + /* look for setopt settings */ + if (pTdnf->pArgs->cn_repoopts != NULL) { + for (pRepo = pReposAll; pRepo; pRepo = pRepo->pNext) { + if ((cn_repo = find_child(pTdnf->pArgs->cn_repoopts, pRepo->pszId)) != NULL) { + dwError = TDNFRepoConfigFromCnfTree(pRepo, cn_repo); + BAIL_ON_TDNF_ERROR(dwError); + } + } + } + *ppReposAll = pReposAll; cleanup: if(pDir) @@ -490,7 +617,7 @@ TDNFLoadReposFromFile( PTDNF_REPO_DATA pRepos = NULL; PTDNF_REPO_DATA pRepo = NULL; - struct cnfnode *cn_conf = NULL, *cn_section, *cn; + struct cnfnode *cn_conf = NULL, *cn_section; struct cnfmodule *mod_ini; mod_ini = find_cnfmodule("ini"); @@ -528,111 +655,9 @@ TDNFLoadReposFromFile( dwError = TDNFEventRepoReadConfigStart(pTdnf, cn_section); BAIL_ON_TDNF_ERROR(dwError); - for(cn = cn_section->first_child; cn; cn = cn->next) - { - if ((cn->name[0] == '.') || (cn->value == NULL)) - continue; + dwError = TDNFRepoConfigFromCnfTree(pRepo, cn_section); + BAIL_ON_TDNF_ERROR(dwError); - if (strcmp(cn->name, TDNF_REPO_KEY_ENABLED) == 0) - { - pRepo->nEnabled = isTrue(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_NAME) == 0) - { - SET_STRING(pRepo->pszName, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_BASEURL) == 0) - { - dwError = TDNFSplitStringToArray(cn->value, - " ", &pRepo->ppszBaseUrls); - BAIL_ON_TDNF_ERROR(dwError); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_METALINK) == 0) - { - SET_STRING(pRepo->pszMetaLink, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_MIRRORLIST) == 0) - { - pRepo->pszMirrorList = strdup(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP) == 0) - { - pRepo->nSkipIfUnavailable = isTrue(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_GPGCHECK) == 0) - { - pRepo->nGPGCheck = isTrue(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_GPGKEY) == 0) - { - dwError = TDNFSplitStringToArray(cn->value, - " ", &pRepo->ppszUrlGPGKeys); - BAIL_ON_TDNF_ERROR(dwError); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_USERNAME) == 0) - { - SET_STRING(pRepo->pszUser, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_PASSWORD) == 0) - { - SET_STRING(pRepo->pszPass, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_PRIORITY) == 0) - { - pRepo->nPriority = strtoi(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_TIMEOUT) == 0) - { - pRepo->nTimeout = strtoi(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_RETRIES) == 0) - { - pRepo->nRetries = strtoi(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_MINRATE) == 0) - { - pRepo->nMinrate = strtoi(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_THROTTLE) == 0) - { - pRepo->nThrottle = strtoi(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_VERIFY) == 0) - { - pRepo->nSSLVerify = isTrue(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_CA_CERT) == 0) - { - SET_STRING(pRepo->pszSSLCaCert, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_CLI_CERT) == 0) - { - SET_STRING(pRepo->pszSSLClientCert, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SSL_CLI_KEY) == 0) - { - SET_STRING(pRepo->pszSSLClientKey, cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_METADATA_EXPIRE) == 0) - { - dwError = TDNFParseMetadataExpire( - cn->value, - &pRepo->lMetadataExpire); - BAIL_ON_TDNF_ERROR(dwError); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP_MD_FILELISTS) == 0) - { - pRepo->nSkipMDFileLists = isTrue(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP_MD_UPDATEINFO) == 0) - { - pRepo->nSkipMDUpdateInfo = isTrue(cn->value); - } - else if (strcmp(cn->name, TDNF_REPO_KEY_SKIP_MD_OTHER) == 0) - { - pRepo->nSkipMDOther = isTrue(cn->value); - } - } /* plugin event repo readconfig end */ dwError = TDNFEventRepoReadConfigEnd(pTdnf, cn_section); BAIL_ON_TDNF_ERROR(dwError); diff --git a/client/rpmtrans.c b/client/rpmtrans.c index 60e41834..de91c320 100644 --- a/client/rpmtrans.c +++ b/client/rpmtrans.c @@ -11,6 +11,8 @@ #include "rpm/rpmcli.h" +#include "../llconf/nodes.h" + #define INSTALL_INSTALL 0 #define INSTALL_UPGRADE 1 #define INSTALL_REINSTALL 2 @@ -60,7 +62,6 @@ TDNFRpmCreateTS( { uint32_t dwError = 0; PTDNFRPMTS pTS = NULL; - PTDNF_CMD_OPT pSetOpt = NULL; if(!pTdnf || !pTdnf->pArgs || !pTdnf->pConf || !pSolvedInfo) { @@ -95,26 +96,7 @@ TDNFRpmCreateTS( BAIL_ON_TDNF_ERROR(dwError); } - /* parse transaction flags - so far only tsflags=noscripts and - tsflags=nodocs are supported */ - pTS->nTransFlags = RPMTRANS_FLAG_NONE; - for (pSetOpt = pTdnf->pArgs->pSetOpt; pSetOpt; pSetOpt = pSetOpt->pNext) - { - if (strcasecmp(pSetOpt->pszOptName, "tsflags")) - { - continue; - } - - if (!strcasecmp(pSetOpt->pszOptValue, "noscripts")) - { - pTS->nTransFlags |= RPMTRANS_FLAG_NOSCRIPTS; - } - - if (!strcasecmp(pSetOpt->pszOptValue, "nodocs")) - { - pTS->nTransFlags |= RPMTRANS_FLAG_NODOCS; - } - } + pTS->nTransFlags = pTdnf->pConf->rpmTransFlags; if(rpmtsSetRootDir (pTS->pTS, pTdnf->pArgs->pszInstallRoot)) { diff --git a/common/config.h b/common/config.h index f8a82640..9f8bbb58 100644 --- a/common/config.h +++ b/common/config.h @@ -41,6 +41,7 @@ #define TDNF_CONF_KEY_PLUGIN_CONF_PATH "pluginconfpath" #define TDNF_CONF_KEY_SSL_VERIFY "sslverify" #define TDNF_PLUGIN_CONF_KEY_ENABLED "enabled" +#define TDNF_CONF_KEY_TSFLAGS "tsflags" #define TDNF_CONF_KEY_EXCLUDE "excludepkgs" #define TDNF_CONF_KEY_MINVERSIONS "minversions" #define TDNF_CONF_KEY_OPENMAX "openmax" @@ -48,6 +49,8 @@ #define TDNF_CONF_KEY_CHECK_UPDATE_COMPAT "dnf_check_update_compat" #define TDNF_CONF_KEY_DISTROSYNC_REINSTALL_CHANGED "distrosync_reinstall_changed" +#define TDNF_PLUGIN_CONF_KEY_ENABLED "enabled" + //Repo file key names #define TDNF_REPO_KEY_BASEURL "baseurl" #define TDNF_REPO_KEY_ENABLED "enabled" diff --git a/common/includes.h b/common/includes.h index ba48bf56..ad340f72 100644 --- a/common/includes.h +++ b/common/includes.h @@ -23,6 +23,8 @@ #include #include +#include "../llconf/nodes.h" + #include #include #include diff --git a/common/prototypes.h b/common/prototypes.h index 4db4a400..2c101e07 100644 --- a/common/prototypes.h +++ b/common/prototypes.h @@ -61,6 +61,17 @@ TDNFSplitStringToArray( char ***pppszTokens ); +uint32_t +TDNFMergeStringArrays( + char ***pppszArray0, + char **ppszArray1 +); + +uint32_t +TDNFAddStringArray( + char ***pppszArray, + char *pszValue); + uint32_t TDNFJoinArrayToString( char **ppszArray, @@ -251,28 +262,6 @@ TDNFHasOpt( int *pnHasOpt ); -uint32_t -TDNFSetOpt( - PTDNF_CMD_ARGS pArgs, - const char *pszOptName, - const char *pszOptValue - ); - -uint32_t -TDNFGetCmdOptValue( - PTDNF_CMD_ARGS pArgs, - const char *pszOptName, - char **ppszOptValue - ); - -uint32_t -TDNFGetOptWithDefault( - PTDNF_CMD_ARGS pArgs, - const char *pszOptName, - const char *pszDefault, - char **ppszOptValue - ); - //log.c void GlobalSetQuiet( diff --git a/common/setopt.c b/common/setopt.c index b806130c..b768e3ee 100644 --- a/common/setopt.c +++ b/common/setopt.c @@ -7,6 +7,7 @@ */ #include "includes.h" +#include "../llconf/nodes.h" void TDNFFreeCmdOpt( @@ -41,7 +42,6 @@ AddSetOpt( return dwError; error: - TDNF_SAFE_FREE_MEMORY(pCmdArgs->pszConfFile); goto cleanup; } @@ -230,111 +230,3 @@ TDNFHasOpt( } goto cleanup; } - -uint32_t -TDNFGetCmdOptValue( - PTDNF_CMD_ARGS pArgs, - const char *pszOptName, - char **ppszOptValue - ) -{ - uint32_t dwError = 0; - PTDNF_CMD_OPT pOpt = NULL; - char *pszOptValue = NULL; - - if(!pArgs || !pArgs->pSetOpt || - IsNullOrEmptyString(pszOptName) || !ppszOptValue) - { - dwError = ERROR_TDNF_INVALID_PARAMETER; - BAIL_ON_TDNF_ERROR(dwError); - } - - dwError = _TDNFGetCmdOpt(pArgs, pszOptName, &pOpt); - BAIL_ON_TDNF_ERROR(dwError); - - if (pOpt->pszOptValue) - { - dwError = TDNFAllocateString(pOpt->pszOptValue, &pszOptValue); - BAIL_ON_TDNF_ERROR(dwError); - } - - *ppszOptValue = pszOptValue; - -cleanup: - return dwError; - -error: - TDNF_SAFE_FREE_MEMORY(pszOptValue); - goto cleanup; -} - -/* - * if get fails, use the default value (if supplied) -*/ -uint32_t -TDNFGetOptWithDefault( - PTDNF_CMD_ARGS pArgs, - const char *pszOptName, - const char *pszDefault, - char **ppszOptValue - ) -{ - uint32_t dwError = 0; - char *pszOptValue = NULL; - - dwError = TDNFGetCmdOptValue(pArgs, pszOptName, &pszOptValue); - if (!IsNullOrEmptyString(pszDefault) && - dwError == ERROR_TDNF_OPT_NOT_FOUND) - { - dwError = TDNFAllocateString(pszDefault, &pszOptValue); - } - BAIL_ON_TDNF_ERROR(dwError); - - *ppszOptValue = pszOptValue; -error: - return dwError; -} - -uint32_t -TDNFSetOpt( - PTDNF_CMD_ARGS pArgs, - const char *pszOptName, - const char *pszOptValue - ) -{ - uint32_t dwError = 0; - PTDNF_CMD_OPT pOpt = NULL; - - if (!pArgs || IsNullOrEmptyString(pszOptName) || - IsNullOrEmptyString(pszOptValue)) - { - dwError = ERROR_TDNF_INVALID_PARAMETER; - BAIL_ON_TDNF_ERROR(dwError); - } - - dwError = _TDNFGetCmdOpt(pArgs, pszOptName, &pOpt); - if (dwError == ERROR_TDNF_OPT_NOT_FOUND) - { - dwError = 0; - } - BAIL_ON_TDNF_ERROR(dwError); - - if (pOpt) - { - TDNF_SAFE_FREE_MEMORY(pOpt->pszOptValue); - - dwError = TDNFAllocateString(pszOptValue, &pOpt->pszOptValue); - BAIL_ON_TDNF_ERROR(dwError); - } - else - { - dwError = AddSetOptWithValues(pArgs, pszOptName, pszOptValue); - BAIL_ON_TDNF_ERROR(dwError); - } - -cleanup: - return dwError; - -error: - goto cleanup; -} diff --git a/common/strings.c b/common/strings.c index 718ca047..df0e320e 100644 --- a/common/strings.c +++ b/common/strings.c @@ -169,6 +169,82 @@ TDNFSplitStringToArray( goto cleanup; } +uint32_t +TDNFMergeStringArrays( + char ***pppszArray0, + char **ppszArray1 +) +{ + uint32_t dwError = 0; + int i, n, n0, n1; + + if (!pppszArray0 || !ppszArray1) { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFStringArrayCount(*pppszArray0, &n0); + BAIL_ON_TDNF_ERROR(dwError); + + dwError = TDNFStringArrayCount(ppszArray1, &n1); + BAIL_ON_TDNF_ERROR(dwError); + + n = n0 + n1; + + dwError = TDNFReAllocateMemory((n + 1) * sizeof(char *), (void **)pppszArray0); + BAIL_ON_TDNF_ERROR(dwError); + + for (i = 0; i < n1; i++) { + (*pppszArray0)[n0 + i] = ppszArray1[i]; + } + (*pppszArray0)[n] = NULL; + + TDNF_SAFE_FREE_MEMORY(ppszArray1); + +cleanup: + return dwError; +error: + goto cleanup; +} + +/* adds a space separated list of strings to an existying string array */ +uint32_t +TDNFAddStringArray( + char ***pppszArray, + char *pszValue) +{ + uint32_t dwError = 0; + char **ppszArrayToAdd = NULL; + + if(!pppszArray) { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + if (IsNullOrEmptyString(pszValue)) { + /* setting to an empty value resets it (eg. "setopt=foo=") */ + TDNF_SAFE_FREE_STRINGARRAY(*pppszArray); + } else { + dwError = TDNFSplitStringToArray(pszValue, + " ", &ppszArrayToAdd); + BAIL_ON_TDNF_ERROR(dwError); + + if (*pppszArray != NULL) { + dwError = TDNFMergeStringArrays(pppszArray, ppszArrayToAdd); + BAIL_ON_TDNF_ERROR(dwError); + } else { + /* if first list is empty, just set to what we add */ + *pppszArray = ppszArrayToAdd; + } + } + +cleanup: + return dwError; +error: + TDNF_SAFE_FREE_STRINGARRAY(ppszArrayToAdd); + goto cleanup; +} + uint32_t TDNFJoinArrayToString( char **ppszArray, @@ -653,5 +729,4 @@ TDNFStringArraySort( return dwError; error: goto cleanup; -} - +} \ No newline at end of file diff --git a/common/utils.c b/common/utils.c index 7d6b618f..542fec41 100644 --- a/common/utils.c +++ b/common/utils.c @@ -421,6 +421,8 @@ TDNFFreeCmdArgs( { TDNFFreeCmdOpt(pCmdArgs->pSetOpt); } + destroy_cnftree(pCmdArgs->cn_setopts); + destroy_cnftree(pCmdArgs->cn_repoopts); TDNF_SAFE_FREE_MEMORY(pCmdArgs); } diff --git a/include/tdnftypes.h b/include/tdnftypes.h index f07da8e6..0028ffea 100644 --- a/include/tdnftypes.h +++ b/include/tdnftypes.h @@ -9,6 +9,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -242,6 +243,8 @@ typedef struct _TDNF_CMD_ARGS char** ppszCmds; int nCmdCount; PTDNF_CMD_OPT pSetOpt; + struct cnfnode *cn_setopts; + struct cnfnode *cn_repoopts; int nArgc; char **ppszArgv; @@ -257,6 +260,7 @@ typedef struct _TDNF_CONF int nOpenMax; //set max number of open files int nCheckUpdateCompat; int nDistroSyncReinstallChanged; + rpmtransFlags rpmTransFlags; int nPluginsEnabled; char* pszRepoDir; char* pszCacheDir; diff --git a/llconf/nodes.c b/llconf/nodes.c index 0eb768b9..fc0312bf 100644 --- a/llconf/nodes.c +++ b/llconf/nodes.c @@ -43,12 +43,27 @@ struct cnfnode *create_cnfnode(const char *name) cn = (struct cnfnode *)malloc(sizeof(struct cnfnode)); if(cn){ memset(cn, 0, sizeof(struct cnfnode)); - - cn->name = strdup(name); + if (name != NULL) + cn->name = strdup(name); } return cn; } +struct cnfnode * +create_cnfnode_keyval(const char *keyval) +{ + struct cnfnode *cn = NULL; + char *psep; + + psep = strstr(keyval, "="); + if (psep) { + cn = create_cnfnode(NULL); + cnfnode_setname_n(cn, keyval, psep - keyval); + cnfnode_setval(cn, psep + 1); + } + return cn; +} + /** copies a node. * a new node will be allocated, and the name and value of cn copied. * The node will not be linked into the tree. @@ -157,6 +172,23 @@ void cnfnode_setname(struct cnfnode *cn, const char *name) } } +/** sets the name of a node. + * if the node already has a name, that name will be free'ed. + * @param cn pointer to a node + * @param name pointer to the new name + * @param n length of the name to be used + */ +void cnfnode_setname_n(struct cnfnode *cn, const char *name, size_t n) +{ + if(cn){ + if(cn->name) free(cn->name); + if(name) + cn->name = strndup(name, n); + else + cn->name = NULL; + } +} + /** frees a node structure. * the memory of the node structure will be freed. Any memory used by the name or value will be freed as well. * @param cn pointer to a node diff --git a/llconf/nodes.h b/llconf/nodes.h index 1b7dc883..aa03f3e1 100644 --- a/llconf/nodes.h +++ b/llconf/nodes.h @@ -36,6 +36,7 @@ extern "C" { #endif struct cnfnode *create_cnfnode(const char *name); +struct cnfnode *create_cnfnode_keyval(const char *keyval); struct cnfnode *clone_cnfnode(const struct cnfnode *cn); struct cnfnode *clone_cnftree(const struct cnfnode *cn_root); @@ -46,6 +47,7 @@ const char *cnfnode_getname(const struct cnfnode *cn); void cnfnode_setval(struct cnfnode *cn, const char *value); void cnfnode_setname(struct cnfnode *cn, const char *name); +void cnfnode_setname_n(struct cnfnode *cn, const char *name, size_t n); /* free memory of node, leaving children intact */ void destroy_cnfnode(struct cnfnode *cn); diff --git a/pytests/repo/setup-repo.sh b/pytests/repo/setup-repo.sh index f48f33bf..8741c923 100755 --- a/pytests/repo/setup-repo.sh +++ b/pytests/repo/setup-repo.sh @@ -65,8 +65,7 @@ mkdir -p -m 755 ${BUILD_PATH}/BUILD \ cat << EOF > ${TEST_REPO_DIR}/gpgkeydata %echo Generating a key for repogpgcheck signatures %no-protection - Key-Type: default - Subkey-Type: default + Key-Type: RSA Name-Real: tdnf test Name-Comment: tdnf test key Name-Email: tdnftest@tdnf.test diff --git a/pytests/repo/tdnf-test-doc.spec b/pytests/repo/tdnf-test-doc.spec new file mode 100644 index 00000000..8b594769 --- /dev/null +++ b/pytests/repo/tdnf-test-doc.spec @@ -0,0 +1,39 @@ +# +# tdnf-test-doc spec file +# +Summary: basic install test file. +Name: tdnf-test-doc +Version: 1.0.1 +Release: 2 +Vendor: VMware, Inc. +Distribution: Photon +License: VMware +Url: http://www.vmware.com +Group: Applications/tdnftest + +%description +Part of tdnf test spec. Basic install/remove/upgrade test + +%prep + +%build + +%install +mkdir -p %_topdir/%buildroot/usr/share/doc/tdnf-test-doc +echo "hello" > %_topdir/%buildroot/usr/share/doc/tdnf-test-doc/README +mkdir -p %_topdir/%buildroot/lib/systemd/system/ +cat << EOF >> %_topdir/%buildroot/lib/systemd/system/tdnf-test-doc.service +[Unit] +Description=tdnf-test-doc.service for whatprovides test. + +EOF + +%files +%doc /usr/share/doc/tdnf-test-doc/README +/lib/systemd/system/tdnf-test-doc.service + +%changelog +* Tue Nov 15 2016 Xiaolin Li 1.0.1-2 +- Add a service file for whatprovides test. +* Tue Dec 15 2015 Priyesh Padmavilasom 1.0 +- Initial build. First version diff --git a/pytests/tests/test_baseurls.py b/pytests/tests/test_baseurls.py index c5de8f5d..df4bb6e5 100644 --- a/pytests/tests/test_baseurls.py +++ b/pytests/tests/test_baseurls.py @@ -23,12 +23,12 @@ def setup_test(utils): def teardown_test(utils): pkgname = utils.config["mulversion_pkgname"] - utils.erase_package(pkgname) if os.path.isdir(WORKDIR): shutil.rmtree(WORKDIR) filename = os.path.join(utils.config['repo_path'], "yum.repos.d", REPOFILENAME) if os.path.isfile(filename): os.remove(filename) + utils.erase_package(pkgname) def test_multiple_baseurls(utils): @@ -54,4 +54,34 @@ def test_multiple_baseurls(utils): '--disablerepo=*', '--enablerepo={}'.format(reponame), 'install', pkgname], cwd=workdir) + assert ret['retval'] == 0 + assert utils.check_package(pkgname) + + +def test_multiple_baseurls_setopt(utils): + reponame = REPONAME + workdir = WORKDIR + utils.makedirs(workdir) + + filename = os.path.join(utils.config['repo_path'], "yum.repos.d", REPOFILENAME) + # we should get a 404 for the first url and skip to the next one + baseurls = "http://localhost:8080/doesntexist" + utils.create_repoconf(filename, baseurls, reponame) + + ret = utils.run(['tdnf', + '--disablerepo=*', '--enablerepo={}'.format(reponame), + f'--setopt={reponame}.baseurl=http://localhost:8080/photon-test', + 'makecache'], + cwd=workdir) + assert ret['retval'] == 0 + + pkgname = utils.config["mulversion_pkgname"] + utils.erase_package(pkgname) + ret = utils.run(['tdnf', + '-y', '--nogpgcheck', + '--disablerepo=*', '--enablerepo={}'.format(reponame), + f'--setopt={reponame}.baseurl=http://localhost:8080/photon-test', + 'install', pkgname], + cwd=workdir) + assert ret['retval'] == 0 assert utils.check_package(pkgname) diff --git a/pytests/tests/test_noscripts.py b/pytests/tests/test_noscripts.py deleted file mode 100644 index 32e47d63..00000000 --- a/pytests/tests/test_noscripts.py +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright (C) 2019 - 2022 VMware, Inc. All Rights Reserved. -# -# Licensed under the GNU General Public License v2 (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. -# - -import pytest - - -@pytest.fixture(scope='module', autouse=True) -def setup_test(utils): - yield - teardown_test(utils) - - -def teardown_test(utils): - pkgname = 'tdnf-bad-pre' - utils.run(['tdnf', 'erase', '-y', pkgname]) - - -# Verify that the package is really bad: -def test_install_normal(utils): - pkgname = 'tdnf-bad-pre' - ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', pkgname]) - assert ret['retval'] == 1525 - - -def test_install_noscripts(utils): - pkgname = 'tdnf-bad-pre' - ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', - '--setopt=tsflags=noscripts', pkgname]) - assert ret['retval'] == 0 diff --git a/pytests/tests/test_tsflags.py b/pytests/tests/test_tsflags.py new file mode 100644 index 00000000..1726dc38 --- /dev/null +++ b/pytests/tests/test_tsflags.py @@ -0,0 +1,156 @@ +# +# Copyright (C) 2019 - 2022 VMware, Inc. All Rights Reserved. +# +# Licensed under the GNU General Public License v2 (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. +# + +import os +import subprocess +import pytest +import shutil + + +WORKDIR = '/root/test_tsflags' +TEST_CONF_FILE = 'tdnf.conf' +TEST_CONF_PATH = os.path.join(WORKDIR, TEST_CONF_FILE) + + +@pytest.fixture(scope='function', autouse=True) +def setup_test(utils): + yield + teardown_test(utils) + + +def teardown_test(utils): + packages = ["tdnf-bad-pre", utils.config["mulversion_pkgname"], "tdnf-test-doc"] + utils.run(['tdnf', 'erase', '-y'] + packages) + + try: + os.remove(TEST_CONF_PATH) + except OSError: + pass + + +# Verify that the package is really bad: +def test_install_normal(utils): + pkgname = 'tdnf-bad-pre' + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', pkgname]) + assert ret['retval'] == 1525 + + +def test_install_noscripts(utils): + pkgname = 'tdnf-bad-pre' + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '--setopt=tsflags=noscripts', pkgname]) + assert ret['retval'] == 0 + + +def test_install_noscripts_nodocs(utils): + pkgname = 'tdnf-bad-pre' + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '--setopt=tsflags=noscripts', '--setopt=tsflags=nodocs', pkgname]) + assert ret['retval'] == 0 + + +def test_install_justdb(utils): + pkgname = utils.config["mulversion_pkgname"] + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '--setopt=tsflags=justdb', pkgname]) + assert ret['retval'] == 0 + + # package should look like it's installed, but no files should be installed + assert utils.check_package(pkgname) + process = subprocess.run(['rpm', '-ql', pkgname], stdout=subprocess.PIPE, text=True) + for f in process.stdout.split(): + assert not os.path.exists(f) + + +# opposite of --justdb +def test_install_nodb(utils): + pkgname = utils.config["mulversion_pkgname"] + utils.erase_package(pkgname) + + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '--setopt=tsflags=nodb', pkgname]) + assert ret['retval'] == 0 + + # package should appear to not be installed, but files should be there: + assert not utils.check_package(pkgname) + process = subprocess.run(['tdnf', 'repoquery', '--list', pkgname], stdout=subprocess.PIPE, text=True) + for f in process.stdout.split(): + if f.startswith("/"): + assert os.path.exists(f) + + +# opposite of --justdb +def test_install_nodocs(utils): + pkgname = "tdnf-test-doc" + utils.erase_package(pkgname) + + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '--setopt=tsflags=nodocs', pkgname]) + assert ret['retval'] == 0 + + # make sure packe is installed + assert utils.check_package(pkgname) + # but no doc file + assert not os.path.exists("/usr/share/doc/tdnf-test-doc/README") + + +def test_install_multiple(utils): + pkgname = utils.config["mulversion_pkgname"] + utils.erase_package(pkgname) + + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '--setopt=tsflags=nodb nodocs', pkgname]) + assert ret['retval'] == 0 + + # package should appear to not be installed, but files should be there: + assert not utils.check_package(pkgname) + process = subprocess.run(['tdnf', 'repoquery', '--list', pkgname], stdout=subprocess.PIPE, text=True) + for f in process.stdout.split(): + if f.startswith("/"): + assert os.path.exists(f) + + # no doc file + assert not os.path.exists("/usr/share/doc/tdnf-test-doc/README") + + +def test_install_multiple_from_config(utils): + pkgname = utils.config["mulversion_pkgname"] + utils.erase_package(pkgname) + + tdnf_conf = os.path.join(utils.config['repo_path'], 'tdnf.conf') + + utils.makedirs(WORKDIR) + shutil.copy(tdnf_conf, TEST_CONF_PATH) + utils.edit_config({'tsflags': "nodb nodocs"}, section='main', filename=TEST_CONF_PATH) + + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '-c', TEST_CONF_PATH, pkgname]) + assert ret['retval'] == 0 + + # package should appear to not be installed, but files should be there: + assert not utils.check_package(pkgname) + process = subprocess.run(['tdnf', 'repoquery', '--list', pkgname], stdout=subprocess.PIPE, text=True) + for f in process.stdout.split(): + if f.startswith("/"): + assert os.path.exists(f) + + # no doc file + assert not os.path.exists("/usr/share/doc/tdnf-test-doc/README") + + # do this again, but override config settings to "normal": + ret = utils.run(['tdnf', 'install', '-y', '--nogpgcheck', + '-c', TEST_CONF_PATH, '--setopt=tsflags=', pkgname]) + assert ret['retval'] == 0 + + assert utils.check_package(pkgname) + + # check all files exist - that includes doc files: + process = subprocess.run(['tdnf', 'repoquery', '--list', pkgname], stdout=subprocess.PIPE, text=True) + for f in process.stdout.split(): + if f.startswith("/"): + assert os.path.exists(f) diff --git a/python/tdnfpycommands.c b/python/tdnfpycommands.c index 25e07d92..917688e8 100644 --- a/python/tdnfpycommands.c +++ b/python/tdnfpycommands.c @@ -15,18 +15,13 @@ TDNFPyRepoList(PyObject *self, PyObject *args, PyObject *kwds) uint32_t dwError = 0; char *kwlist[] = { "filter", "config", NULL }; PyObject *ppyRepoList = Py_None; - TDNF_CMD_ARGS cmdArgs = {0}; - char *szCmds[] = {""}; + PTDNF_CMD_ARGS pCmdArgs = NULL; PTDNF pTDNF = NULL; PTDNF_REPO_DATA pRepos = NULL; PTDNF_REPO_DATA pReposTemp = NULL; TDNF_REPOLISTFILTER nFilter = REPOLISTFILTER_ENABLED; PyObject *CfgFile = NULL; - cmdArgs.pszInstallRoot = "/"; - cmdArgs.ppszCmds = szCmds; - cmdArgs.nCmdCount = 1; - if (!PyArg_ParseTupleAndKeywords( args, kwds, "|iO", kwlist, &nFilter, &CfgFile)) @@ -35,10 +30,16 @@ TDNFPyRepoList(PyObject *self, PyObject *args, PyObject *kwds) BAIL_ON_TDNF_ERROR(dwError); } + dwError = TDNFAllocateMemory( + 1, + sizeof(TDNF_CMD_ARGS), + (void**)&pCmdArgs); + BAIL_ON_TDNF_ERROR(dwError); + if (CfgFile) { const char *fname = PyUnicode_AsUTF8(CfgFile); - dwError = TDNFAllocateString(fname, &cmdArgs.pszConfFile); + dwError = TDNFAllocateString(fname, &pCmdArgs->pszConfFile); BAIL_ON_TDNF_ERROR(dwError); } @@ -49,7 +50,14 @@ TDNFPyRepoList(PyObject *self, PyObject *args, PyObject *kwds) BAIL_ON_TDNF_ERROR(dwError); } - dwError = TDNFOpenHandle(&cmdArgs, &pTDNF); + pCmdArgs->pszInstallRoot = strdup("/"); + pCmdArgs->nCmdCount = 1; + + dwError = TDNFAllocateMemory(2, sizeof(char *), (void **)&pCmdArgs->ppszCmds); + BAIL_ON_TDNF_ERROR(dwError); + pCmdArgs->ppszCmds[0] = strdup("repolist"); + + dwError = TDNFOpenHandle(pCmdArgs, &pTDNF); BAIL_ON_TDNF_ERROR(dwError); if (nFilter < REPOLISTFILTER_ALL || nFilter > REPOLISTFILTER_DISABLED) @@ -74,7 +82,7 @@ TDNFPyRepoList(PyObject *self, PyObject *args, PyObject *kwds) } cleanup: - TDNF_SAFE_FREE_MEMORY(cmdArgs.pszConfFile); + TDNF_SAFE_FREE_MEMORY(pCmdArgs->pszConfFile); if (pRepos) { TDNFFreeRepos(pRepos); @@ -163,7 +171,7 @@ PyObject * _TDNFPyAlter(TDNF_ALTERTYPE alterType, PyObject *self, PyObject *args, PyObject *kwds) { uint32_t dwError = 0; - TDNF_CMD_ARGS cmdArgs = {0}; + PTDNF_CMD_ARGS pCmdArgs = NULL; PTDNF pTDNF = NULL; PTDNF_SOLVED_PKG_INFO pSolvedInfo = NULL; @@ -173,12 +181,18 @@ _TDNFPyAlter(TDNF_ALTERTYPE alterType, PyObject *self, PyObject *args, PyObject BAIL_ON_TDNF_ERROR(dwError); } - cmdArgs.pszInstallRoot = "/"; + dwError = TDNFAllocateMemory( + 1, + sizeof(TDNF_CMD_ARGS), + (void**)&pCmdArgs); + BAIL_ON_TDNF_ERROR(dwError); + + pCmdArgs->pszInstallRoot = strdup("/"); - dwError = _TDNFPyGetAlterArgs(alterType, args, kwds, &cmdArgs); + dwError = _TDNFPyGetAlterArgs(alterType, args, kwds, pCmdArgs); BAIL_ON_TDNF_ERROR(dwError); - dwError = TDNFOpenHandle(&cmdArgs, &pTDNF); + dwError = TDNFOpenHandle(pCmdArgs, &pTDNF); BAIL_ON_TDNF_ERROR(dwError); dwError = TDNFResolve(pTDNF, alterType, &pSolvedInfo); @@ -195,8 +209,8 @@ _TDNFPyAlter(TDNF_ALTERTYPE alterType, PyObject *self, PyObject *args, PyObject } cleanup: - TDNF_SAFE_FREE_MEMORY(cmdArgs.pszConfFile); - TDNFFreeStringArrayWithCount(cmdArgs.ppszCmds, cmdArgs.nCmdCount); + TDNF_SAFE_FREE_MEMORY(pCmdArgs->pszConfFile); + TDNFFreeStringArrayWithCount(pCmdArgs->ppszCmds, pCmdArgs->nCmdCount); TDNFFreeSolvedPackageInfo(pSolvedInfo); if (pTDNF) { diff --git a/tools/cli/lib/parseargs.c b/tools/cli/lib/parseargs.c index deedb1bd..578e4c28 100644 --- a/tools/cli/lib/parseargs.c +++ b/tools/cli/lib/parseargs.c @@ -7,6 +7,7 @@ */ #include "includes.h" +#include "../../../llconf/nodes.h" static TDNF_CMD_ARGS _opt = {0}; @@ -144,6 +145,9 @@ TDNFCliParseArgs( (void**)&pCmdArgs); BAIL_ON_CLI_ERROR(dwError); + pCmdArgs->cn_setopts = create_cnfnode("(setopts)"); + pCmdArgs->cn_repoopts = create_cnfnode("(repoopts)"); + /* * when invoked as 'tdnfj', act as if invoked with with '-j' and '-y' * for json output and non-interactive @@ -398,16 +402,39 @@ ParseOption( } else if (!strcasecmp(pszName, "setopt")) { + struct cnfnode *cn; + char *psep_eq, *pseq_dot; + if (!optarg) { dwError = ERROR_TDNF_CLI_OPTION_ARG_REQUIRED; BAIL_ON_CLI_ERROR(dwError); } - dwError = AddSetOpt(pCmdArgs, optarg); - if (dwError == ERROR_TDNF_SETOPT_NO_EQUALS) - { + psep_eq = strstr(optarg, "="); + if (psep_eq) { + *psep_eq = 0; + + pseq_dot = strstr(optarg, "."); + if (pseq_dot == NULL) { + cn = create_cnfnode(optarg); + cnfnode_setval(cn, psep_eq + 1); + append_node(pCmdArgs->cn_setopts, cn); + } else { + struct cnfnode *cn_repo; + *pseq_dot = 0; + cn_repo = find_child(pCmdArgs->cn_repoopts, optarg); + if (!cn_repo) { + cn_repo = create_cnfnode(optarg); + append_node(pCmdArgs->cn_repoopts, cn_repo); + } + cn = create_cnfnode(pseq_dot+1); + cnfnode_setval(cn, psep_eq + 1); + append_node(cn_repo, cn); + } + } else { dwError = ERROR_TDNF_CLI_SETOPT_NO_EQUALS; + BAIL_ON_CLI_ERROR(dwError); } } else if (!strcasecmp(pszName, "exclude"))