From f1883c9ee4fccfbaa6c855612d968d2ef355a32d Mon Sep 17 00:00:00 2001 From: Oliver Kurth Date: Wed, 18 May 2022 22:11:17 +0000 Subject: [PATCH] implement history commands --- client/CMakeLists.txt | 2 + client/api.c | 352 +++++++++++++++++++++++++++++++ client/defines.h | 3 + client/goal.c | 240 ++++++++++++++------- client/history.c | 71 +++++++ client/includes.h | 2 + client/init.c | 3 + client/prototypes.h | 23 ++ client/rpmtrans.c | 36 ++++ client/utils.c | 45 ++++ include/tdnf.h | 21 ++ include/tdnfcli.h | 36 +++- include/tdnfclitypes.h | 46 ++-- include/tdnferror.h | 6 +- include/tdnftypes.h | 41 +++- solv/defines.h | 3 + solv/simplequery.c | 2 +- tools/cli/lib/CMakeLists.txt | 3 +- tools/cli/lib/api.c | 242 ++++++++++++++++++++- tools/cli/lib/installcmd.c | 65 ++++-- tools/cli/lib/output.c | 38 +++- tools/cli/lib/parseargs.c | 12 +- tools/cli/lib/parsehistoryargs.c | 130 ++++++++++++ tools/cli/main.c | 28 +++ tools/cli/prototypes.h | 37 +++- 25 files changed, 1340 insertions(+), 147 deletions(-) create mode 100644 client/history.c create mode 100644 tools/cli/lib/parsehistoryargs.c diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 98ff405e..8417ff2d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -42,11 +42,13 @@ add_library(${LIB_TDNF} SHARED utils.c metalink.c list.c + history.c ) target_link_libraries(${LIB_TDNF} ${LIB_TDNF_COMMON} ${LIB_TDNF_SOLV} + ${LIB_TDNF_HISTORY} ${RPM_LIBRARIES} ${LibSolv_LIBRARIES} ${CURL_LIBRARIES} diff --git a/client/api.c b/client/api.c index 0e4ef75f..06b04a8d 100644 --- a/client/api.c +++ b/client/api.c @@ -1895,6 +1895,358 @@ TDNFUpdateInfo( goto cleanup; } +uint32_t +TDNFHistoryResolve( + PTDNF pTdnf, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_SOLVED_PKG_INFO *ppSolvedPkgInfo) +{ + uint32_t dwError = 0; + int rc = 0; + char **ppszPkgsNotResolved = NULL; + Queue queueGoal = {0}; + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo = NULL; + struct history_ctx *ctx = NULL; + struct history_delta *hd = NULL; + struct history_nevra_map *hnm = NULL; + rpmts ts = NULL; + Queue qInstall = {0}; + Queue qErase = {0}; + + if(!pTdnf || !pHistoryArgs || !ppSolvedPkgInfo) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + /* args sanity checks */ + if (pHistoryArgs->nCommand == HISTORY_CMD_ROLLBACK) + { + if (pHistoryArgs->nTo <= 0) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + } + else if (pHistoryArgs->nCommand == HISTORY_CMD_UNDO || + pHistoryArgs->nCommand == HISTORY_CMD_REDO) + { + if (pHistoryArgs->nFrom <= 1 || /* cannot undo or redo the base set */ + pHistoryArgs->nFrom > pHistoryArgs->nTo) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + } + else if (pHistoryArgs->nCommand != HISTORY_CMD_INIT) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFRefresh(pTdnf); + BAIL_ON_TDNF_ERROR(dwError); + + ts = rpmtsCreate(); + if(!ts) + { + dwError = ERROR_TDNF_RPMTS_CREATE_FAILED; + BAIL_ON_TDNF_ERROR(dwError); + } + + if (rpmtsOpenDB(ts, O_RDONLY)) + { + dwError = ERROR_TDNF_RPMTS_OPENDB_FAILED; + BAIL_ON_TDNF_ERROR(dwError); + } + + if(rpmtsSetRootDir(ts, pTdnf->pArgs->pszInstallRoot)) + { + dwError = ERROR_TDNF_RPMTS_BAD_ROOT_DIR; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFGetHistoryCtx(pTdnf, &ctx, + pHistoryArgs->nCommand != HISTORY_CMD_INIT); + BAIL_ON_TDNF_ERROR(dwError); + + rc = history_sync(ctx, ts); + if (rc != 0) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + + switch (pHistoryArgs->nCommand) + { + case HISTORY_CMD_INIT: + goto cleanup; + case HISTORY_CMD_ROLLBACK: + hd = history_get_delta(ctx, pHistoryArgs->nTo); + break; + case HISTORY_CMD_UNDO: + hd = history_get_delta_range(ctx, pHistoryArgs->nFrom - 1, pHistoryArgs->nTo); + break; + case HISTORY_CMD_REDO: + hd = history_get_delta_range(ctx, pHistoryArgs->nTo, pHistoryArgs->nFrom - 1); + break; + default: + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + if (hd == NULL) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + + if (hd->added_count > 0) + { + dwError = TDNFAllocateMemory( + hd->added_count+1, /* only added pkgs, plus a NULL ptr */ + sizeof(char*), + (void**)&ppszPkgsNotResolved); + BAIL_ON_TDNF_ERROR(dwError); + } + + hnm = history_nevra_map(ctx); + if (hnm == NULL) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + + queue_init(&qInstall); + queue_init(&qErase); + + for (int i = 0; i < hd->added_count; i++) + { + char *pszPkgName = history_get_nevra(hnm, hd->added_ids[i]); + if (pszPkgName) + { + Queue qResult = {0}; + queue_init(&qResult); + + dwError = SolvFindSolvablesByNevraStr(pTdnf->pSack->pPool, + pszPkgName, &qResult, SOLV_NEVRA_UNINSTALLED); + BAIL_ON_TDNF_ERROR(dwError); + + if (qResult.count == 0) + { + dwError = TDNFAddNotResolved(ppszPkgsNotResolved, pszPkgName); + BAIL_ON_TDNF_ERROR(dwError); + } + else + { + Queue qInstalled = {0}; + queue_init(&qInstalled); + + /* find if pkg is already installed */ + /* TODO: make this more efficient by using the pool ids of the solvable + with SolvFindSolvablesByNevraId() */ + dwError = SolvFindSolvablesByNevraStr(pTdnf->pSack->pPool, + pszPkgName, &qInstalled, SOLV_NEVRA_INSTALLED); + BAIL_ON_TDNF_ERROR(dwError); + + if (qInstalled.count == 0) + { + /* We may have found multiples if they occur in multiple + repos. Take the first one. */ + queue_push(&qInstall, qResult.elements[0]); + } + queue_free(&qInstalled); + } + queue_free(&qResult); + } + else + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + } + + for (int i = 0; i < hd->removed_count; i++) + { + char *pszPkgName = history_get_nevra(hnm, hd->removed_ids[i]); + if (pszPkgName) + { + dwError = SolvFindSolvablesByNevraStr(pTdnf->pSack->pPool, pszPkgName, &qErase, SOLV_NEVRA_INSTALLED); + BAIL_ON_TDNF_ERROR(dwError); + } + else + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + } + + dwError = TDNFHistoryGoal( + pTdnf, + &qInstall, + &qErase, + &pSolvedPkgInfo); + BAIL_ON_TDNF_ERROR(dwError); + + pSolvedPkgInfo->nNeedAction = + pSolvedPkgInfo->pPkgsToInstall || + pSolvedPkgInfo->pPkgsToUpgrade || + pSolvedPkgInfo->pPkgsToDowngrade || + pSolvedPkgInfo->pPkgsToRemove || + pSolvedPkgInfo->pPkgsUnNeeded || + pSolvedPkgInfo->pPkgsToReinstall || + pSolvedPkgInfo->pPkgsObsoleted; + + pSolvedPkgInfo->nNeedDownload = + pSolvedPkgInfo->pPkgsToInstall || + pSolvedPkgInfo->pPkgsToUpgrade || + pSolvedPkgInfo->pPkgsToDowngrade || + pSolvedPkgInfo->pPkgsToReinstall; + + pSolvedPkgInfo->ppszPkgsNotResolved = ppszPkgsNotResolved; + + *ppSolvedPkgInfo = pSolvedPkgInfo; + +cleanup: + history_free_nevra_map(hnm); + history_free_delta(hd); + destroy_history_ctx(ctx); + queue_free(&queueGoal); + queue_free(&qInstall); + queue_free(&qErase); + if (ts) { + rpmtsCloseDB(ts); + rpmtsFree(ts); + } + return dwError; + +error: + if(pSolvedPkgInfo) + { + TDNFFreeSolvedPackageInfo(pSolvedPkgInfo); + } + if(ppszPkgsNotResolved) + { + TDNFFreeStringArray(ppszPkgsNotResolved); + } + goto cleanup; +} + +uint32_t +TDNFHistoryList( + PTDNF pTdnf, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_HISTORY_INFO *ppHistoryInfo) +{ + uint32_t dwError = 0; + struct history_transaction *tas = NULL; + struct history_nevra_map *hnm = NULL; + int count = 0; + PTDNF_HISTORY_INFO pHistoryInfo = NULL; + PTDNF_HISTORY_INFO_ITEM pHistoryInfoItems = NULL; + struct history_ctx *ctx = NULL; + int rc = 0; + + if(!pTdnf || !pHistoryArgs || !ppHistoryInfo) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + if (pHistoryArgs->nFrom < 0 || pHistoryArgs->nTo < 0) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + if (pHistoryArgs->nFrom != 0 && pHistoryArgs->nTo != 0) + { + if (pHistoryArgs->nFrom > pHistoryArgs->nTo) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + } + + dwError = TDNFGetHistoryCtx(pTdnf, &ctx, 1); + BAIL_ON_TDNF_ERROR(dwError); + + rc = history_get_transactions(ctx, &tas, &count, + pHistoryArgs->nReverse, + pHistoryArgs->nFrom, pHistoryArgs->nTo); + if (rc != 0) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFAllocateMemory( + count, + sizeof(TDNF_HISTORY_INFO_ITEM), + (void**)&pHistoryInfoItems); + BAIL_ON_TDNF_ERROR(dwError); + + if (pHistoryArgs->nInfo) + { + hnm = history_nevra_map(ctx); + } + + for(int i = 0; i < count; i++) + { + pHistoryInfoItems[i].nId = tas[i].id; + pHistoryInfoItems[i].nType = tas[i].type; + dwError = TDNFAllocateString(tas[i].cmdline ? + tas[i].cmdline : + "(none)", + &pHistoryInfoItems[i].pszCmdLine); + BAIL_ON_TDNF_ERROR(dwError); + pHistoryInfoItems[i].timeStamp = tas[i].timestamp; + pHistoryInfoItems[i].nAddedCount = tas[i].delta.added_count; + pHistoryInfoItems[i].nRemovedCount = tas[i].delta.removed_count; + + if (hnm) + { + dwError = TDNFAllocateMemory(tas[i].delta.added_count, sizeof(char *), (void **)&pHistoryInfoItems[i].ppszAddedPkgs); + for (int j = 0; j < tas[i].delta.added_count; j++) + { + dwError = TDNFAllocateString(history_get_nevra(hnm, tas[i].delta.added_ids[j]), + &pHistoryInfoItems[i].ppszAddedPkgs[j]); + BAIL_ON_TDNF_ERROR(dwError); + } + dwError = TDNFAllocateMemory(tas[i].delta.removed_count, sizeof(char *), (void **)&pHistoryInfoItems[i].ppszRemovedPkgs); + for (int j = 0; j < tas[i].delta.removed_count; j++) + { + dwError = TDNFAllocateString(history_get_nevra(hnm, tas[i].delta.removed_ids[j]), + &pHistoryInfoItems[i].ppszRemovedPkgs[j]); + BAIL_ON_TDNF_ERROR(dwError); + } + } + } + + dwError = TDNFAllocateMemory( + count, + sizeof(TDNF_HISTORY_INFO), + (void**)&pHistoryInfo); + BAIL_ON_TDNF_ERROR(dwError); + + pHistoryInfo->nItemCount = count; + pHistoryInfo->pItems = pHistoryInfoItems; + *ppHistoryInfo = pHistoryInfo; + +cleanup: + history_free_nevra_map(hnm); + history_free_transactions(tas, count); + destroy_history_ctx(ctx); + return dwError; + +error: + if (pHistoryInfoItems) + { + TDNFFreeHistoryInfoItems(pHistoryInfoItems, count); + } + goto cleanup; +} + //api calls to free memory allocated by tdnfclientlib void TDNFCloseHandle( diff --git a/client/defines.h b/client/defines.h index c4f45c43..449480be 100644 --- a/client/defines.h +++ b/client/defines.h @@ -128,6 +128,7 @@ typedef enum #define TDNF_REPO_BASEURL_FILE_NAME "baseurl" #define TDNF_AUTOINSTALLED_FILE "autoinstalled" +#define TDNF_HISTORY_DB_FILE "history.db" #define TDNF_DEFAULT_DATA_LOCATION "/var/lib/tdnf" // repo defaults @@ -257,6 +258,8 @@ typedef enum {ERROR_TDNF_ML_PARSER_MISSING_HASH_CONTENT, "ERROR_TDNF_ML_PARSER_MISSING_HASH_CONTENT", "Missing content in hash tag value"},\ {ERROR_TDNF_ML_PARSER_MISSING_URL_ATTR, "ERROR_TDNF_ML_PARSER_MISSING_URL_ATTR", "Missing attribute in url tag"},\ {ERROR_TDNF_ML_PARSER_MISSING_HASH_CONTENT, "ERROR_TDNF_ML_PARSER_MISSING_URL_CONTENT", "Missing content in url tag value"},\ + {ERROR_TDNF_HISTORY_NODB, "ERROR_TDNF_HISTORY_ERROR", "History database error"},\ + {ERROR_TDNF_HISTORY_NODB, "ERROR_TDNF_HISTORY_NODB", "History database does not exist"},\ }; diff --git a/client/goal.c b/client/goal.c index 79f7bbab..4b393536 100644 --- a/client/goal.c +++ b/client/goal.c @@ -294,76 +294,42 @@ SolvAddDebugInfo( goto cleanup; } +static uint32_t -TDNFGoal( +TDNFSolv( PTDNF pTdnf, - Queue* pQueuePkgList, - PTDNF_SOLVED_PKG_INFO* ppInfo, - TDNF_ALTERTYPE nAlterType + Queue *pQueueJobs, + char** ppszExcludes, + uint32_t dwExcludeCount, + int nAllowErasing, + int nAutoErase, + PTDNF_SOLVED_PKG_INFO* ppInfo ) { uint32_t dwError = 0; - - PTDNF_SOLVED_PKG_INFO pInfoTemp = NULL; + PTDNF_SOLVED_PKG_INFO pInfo = NULL; TDNF_SKIPPROBLEM_TYPE dwSkipProblem = SKIPPROBLEM_NONE; Solver *pSolv = NULL; Transaction *pTrans = NULL; - Queue queueJobs = {0}; - int nFlags = 0; int nProblems = 0; - char** ppszExcludes = NULL; - uint32_t dwExcludeCount = 0; - char **ppszAutoInstalled = NULL; - if(!pTdnf || !ppInfo || !pQueuePkgList) + if(!pTdnf || !ppInfo || !ppInfo) { dwError = ERROR_TDNF_INVALID_PARAMETER; BAIL_ON_TDNF_ERROR(dwError); } - dwError = TDNFPkgsToExclude(pTdnf, &dwExcludeCount, &ppszExcludes); - BAIL_ON_TDNF_ERROR(dwError); - - queue_init(&queueJobs); - if (nAlterType == ALTER_UPGRADEALL) - { - dwError = SolvAddUpgradeAllJob(&queueJobs); - BAIL_ON_TDNF_ERROR(dwError); - } - else if(nAlterType == ALTER_DISTRO_SYNC) - { - dwError = SolvAddDistUpgradeJob(&queueJobs); - BAIL_ON_TDNF_ERROR(dwError); - } - else - { - if (pQueuePkgList->count == 0) - { - dwError = ERROR_TDNF_ALREADY_INSTALLED; - BAIL_ON_TDNF_ERROR(dwError); - } - - for (int i = 0; i < pQueuePkgList->count; i++) - { - Id dwId = pQueuePkgList->elements[i]; - TDNFAddGoal(pTdnf, nAlterType, &queueJobs, dwId, - dwExcludeCount, ppszExcludes); - } - } - if(pTdnf->pArgs->nBest) { nFlags = nFlags | SOLVER_FORCEBEST; } - if ((pTdnf->pConf->nCleanRequirementsOnRemove && - !pTdnf->pArgs->nNoAutoRemove) || - nAlterType == ALTER_AUTOERASE) + if (nAutoErase) { nFlags = nFlags | SOLVER_CLEANDEPS; } - dwError = SolvAddFlagsToJobs(&queueJobs, nFlags); + dwError = SolvAddFlagsToJobs(pQueueJobs, nFlags); BAIL_ON_TDNF_ERROR(dwError); if (dwExcludeCount != 0 && ppszExcludes) @@ -377,10 +343,10 @@ TDNFGoal( BAIL_ON_TDNF_ERROR(dwError); } - dwError = TDNFSolvAddPkgLocks(pTdnf, &queueJobs, pTdnf->pSack->pPool); + dwError = TDNFSolvAddPkgLocks(pTdnf, pQueueJobs, pTdnf->pSack->pPool); BAIL_ON_TDNF_ERROR(dwError); - dwError = TDNFSolvAddMinVersions(pTdnf, &queueJobs, pTdnf->pSack->pPool); + dwError = TDNFSolvAddMinVersions(pTdnf, pQueueJobs, pTdnf->pSack->pPool); BAIL_ON_TDNF_ERROR(dwError); pSolv = solver_create(pTdnf->pSack->pPool); @@ -390,16 +356,8 @@ TDNFGoal( BAIL_ON_TDNF_ERROR(dwError); } - if(pTdnf->pArgs->nAllowErasing || - nAlterType == ALTER_ERASE || - nAlterType == ALTER_AUTOERASE) + if(nAllowErasing) { - dwError = TDNFReadAutoInstalled(pTdnf, &ppszAutoInstalled); - BAIL_ON_TDNF_ERROR(dwError); - - dwError = SolvAddUserInstalledToJobs(&queueJobs, pTdnf->pSack->pPool, ppszAutoInstalled); - BAIL_ON_TDNF_ERROR(dwError); - solver_set_flag(pSolv, SOLVER_FLAG_ALLOW_UNINSTALL, 1); } solver_set_flag(pSolv, SOLVER_FLAG_BEST_OBEY_POLICY, 1); @@ -410,18 +368,12 @@ TDNFGoal( solver_set_flag(pSolv, SOLVER_FLAG_ALLOW_DOWNGRADE, 1); solver_set_flag(pSolv, SOLVER_FLAG_INSTALL_ALSO_UPDATES, 1); - nProblems = solver_solve(pSolv, &queueJobs); + nProblems = solver_solve(pSolv, pQueueJobs); if (nProblems > 0) { dwError = TDNFGetSkipProblemOption(pTdnf, &dwSkipProblem); BAIL_ON_TDNF_ERROR(dwError); - if (nAlterType == ALTER_UPGRADE && dwExcludeCount != 0 && ppszExcludes) - { - /* if we had packages to exclude, then we'd have inactive ones too */ - dwSkipProblem |= SKIPPROBLEM_DISABLED; - } - dwError = SolvReportProblems(pTdnf->pSack, pSolv, dwSkipProblem); BAIL_ON_TDNF_ERROR(dwError); } @@ -442,22 +394,13 @@ TDNFGoal( dwError = TDNFGoalGetAllResultsIgnoreNoData( pTrans, pSolv, - &pInfoTemp, + &pInfo, pTdnf); BAIL_ON_TDNF_ERROR(dwError); - if (nAlterType == ALTER_INSTALL) - { - dwError = TDNFAddUserInstall(pTdnf, pQueuePkgList, pInfoTemp); - BAIL_ON_TDNF_ERROR(dwError); - } - - *ppInfo = pInfoTemp; + *ppInfo = pInfo; cleanup: - TDNF_SAFE_FREE_STRINGARRAY(ppszAutoInstalled); - TDNF_SAFE_FREE_STRINGARRAY(ppszExcludes); - queue_free(&queueJobs); if(pTrans) { transaction_free(pTrans); @@ -469,7 +412,7 @@ TDNFGoal( return dwError; error: - TDNF_SAFE_FREE_MEMORY(pInfoTemp); + TDNF_SAFE_FREE_MEMORY(pInfo); if(ppInfo) { *ppInfo = NULL; @@ -477,6 +420,151 @@ TDNFGoal( goto cleanup; } +uint32_t +TDNFGoal( + PTDNF pTdnf, + Queue* pQueuePkgList, + PTDNF_SOLVED_PKG_INFO* ppInfo, + TDNF_ALTERTYPE nAlterType + ) +{ + uint32_t dwError = 0; + + Queue queueJobs = {0}; + int nAllowErasing = 0; + char** ppszExcludes = NULL; + uint32_t dwExcludeCount = 0; + char **ppszAutoInstalled = NULL; + + if(!pTdnf || !ppInfo || !pQueuePkgList) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFPkgsToExclude(pTdnf, &dwExcludeCount, &ppszExcludes); + BAIL_ON_TDNF_ERROR(dwError); + + queue_init(&queueJobs); + if (nAlterType == ALTER_UPGRADEALL) + { + dwError = SolvAddUpgradeAllJob(&queueJobs); + BAIL_ON_TDNF_ERROR(dwError); + } + else if(nAlterType == ALTER_DISTRO_SYNC) + { + dwError = SolvAddDistUpgradeJob(&queueJobs); + BAIL_ON_TDNF_ERROR(dwError); + } + else + { + if (pQueuePkgList->count == 0) + { + dwError = ERROR_TDNF_ALREADY_INSTALLED; + BAIL_ON_TDNF_ERROR(dwError); + } + + for (int i = 0; i < pQueuePkgList->count; i++) + { + Id dwId = pQueuePkgList->elements[i]; + TDNFAddGoal(pTdnf, nAlterType, &queueJobs, dwId, + dwExcludeCount, ppszExcludes); + } + } + + nAllowErasing = + pTdnf->pArgs->nAllowErasing || + nAlterType == ALTER_ERASE || + nAlterType == ALTER_AUTOERASE; + if(nAllowErasing) + { + dwError = TDNFReadAutoInstalled(pTdnf, &ppszAutoInstalled); + BAIL_ON_TDNF_ERROR(dwError); + + dwError = SolvAddUserInstalledToJobs(&queueJobs, + pTdnf->pSack->pPool, + ppszAutoInstalled); + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFSolv(pTdnf, &queueJobs, ppszExcludes, dwExcludeCount, + nAllowErasing, + (pTdnf->pConf->nCleanRequirementsOnRemove && + !pTdnf->pArgs->nNoAutoRemove) || + nAlterType == ALTER_AUTOERASE, + ppInfo); + BAIL_ON_TDNF_ERROR(dwError); + + if (nAlterType == ALTER_INSTALL) + { + dwError = TDNFAddUserInstall(pTdnf, pQueuePkgList, *ppInfo); + BAIL_ON_TDNF_ERROR(dwError); + } + +cleanup: + TDNF_SAFE_FREE_STRINGARRAY(ppszAutoInstalled); + TDNF_SAFE_FREE_STRINGARRAY(ppszExcludes); + queue_free(&queueJobs); + return dwError; + +error: + goto cleanup; +} + +uint32_t +TDNFHistoryGoal( + PTDNF pTdnf, + Queue *pqInstall, + Queue *pqErase, + PTDNF_SOLVED_PKG_INFO* ppInfo + ) +{ + uint32_t dwError = 0; + Queue queueJobs = {0}; + char** ppszExcludes = NULL; + uint32_t dwExcludeCount = 0; + + if(!pTdnf || !ppInfo || !pqInstall || !pqErase) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFPkgsToExclude(pTdnf, &dwExcludeCount, &ppszExcludes); + BAIL_ON_TDNF_ERROR(dwError); + + queue_init(&queueJobs); + + for (int i = 0; i < pqInstall->count; i++) + { + Id id = pqInstall->elements[i]; + dwError = TDNFAddGoal(pTdnf, ALTER_INSTALL, &queueJobs, id, + dwExcludeCount, ppszExcludes); + BAIL_ON_TDNF_ERROR(dwError); + } + for (int i = 0; i < pqErase->count; i++) + { + Id id = pqErase->elements[i]; + dwError = TDNFAddGoal(pTdnf, ALTER_ERASE, &queueJobs, id, + dwExcludeCount, ppszExcludes); + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFSolv(pTdnf, &queueJobs, ppszExcludes, dwExcludeCount, + 1, /* nAllowErasing */ + 0, /* nAutoErase */ + ppInfo); + BAIL_ON_TDNF_ERROR(dwError); + +cleanup: + TDNF_SAFE_FREE_STRINGARRAY(ppszExcludes); + queue_free(&queueJobs); + return dwError; + +error: + goto cleanup; +} + uint32_t TDNFAddUserInstall( PTDNF pTdnf, diff --git a/client/history.c b/client/history.c new file mode 100644 index 00000000..069a58fa --- /dev/null +++ b/client/history.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015-2022 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" + +uint32_t +TDNFGetHistoryCtx( + PTDNF pTdnf, + struct history_ctx **ppCtx, + int nMustExist +) +{ + uint32_t dwError = 0; + char *pszDataDir = NULL; + char *pszHistoryDb = NULL; + struct history_ctx *ctx = NULL; + + if(!pTdnf || !ppCtx) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFJoinPath(&pszDataDir, + pTdnf->pArgs->pszInstallRoot, + TDNF_DEFAULT_DATA_LOCATION, + NULL); + BAIL_ON_TDNF_ERROR(dwError); + + dwError = TDNFJoinPath(&pszHistoryDb, + pszDataDir, + TDNF_HISTORY_DB_FILE, + NULL); + BAIL_ON_TDNF_ERROR(dwError); + + if (nMustExist) + { + int nExists = 0; + dwError = TDNFIsFileOrSymlink(pszHistoryDb, &nExists); + BAIL_ON_TDNF_ERROR(dwError); + if (!nExists) + { + dwError = ERROR_TDNF_HISTORY_NODB; + BAIL_ON_TDNF_ERROR(dwError); + } + } + + dwError = TDNFUtilsMakeDir(pszDataDir); + BAIL_ON_TDNF_ERROR(dwError); + + ctx = create_history_ctx(pszHistoryDb); + if (ctx == NULL) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + + *ppCtx = ctx; + +cleanup: + TDNF_SAFE_FREE_MEMORY(pszDataDir); + TDNF_SAFE_FREE_MEMORY(pszHistoryDb); + return dwError; +error: + goto cleanup; +} \ No newline at end of file diff --git a/client/includes.h b/client/includes.h index f0151fc5..9067bb84 100644 --- a/client/includes.h +++ b/client/includes.h @@ -46,6 +46,8 @@ #include #include +#include "../history/history.h" + #include #include #include diff --git a/client/init.c b/client/init.c index 2601fb7d..6be4bb4b 100644 --- a/client/init.c +++ b/client/init.c @@ -59,6 +59,9 @@ TDNFCloneCmdArgs( pCmdArgs->nNoAutoRemove = pCmdArgsIn->nNoAutoRemove; pCmdArgs->nJsonOutput = pCmdArgsIn->nJsonOutput; + pCmdArgs->nArgc = pCmdArgsIn->nArgc; + pCmdArgs->ppszArgv = pCmdArgsIn->ppszArgv; + dwError = TDNFAllocateString( pCmdArgsIn->pszInstallRoot, &pCmdArgs->pszInstallRoot); diff --git a/client/prototypes.h b/client/prototypes.h index 9be88e5f..b3a13eed 100644 --- a/client/prototypes.h +++ b/client/prototypes.h @@ -231,6 +231,12 @@ TDNFCurlErrorIsFatal( CURLcode curlError ); +void +TDNFFreeHistoryInfoItems( + PTDNF_HISTORY_INFO_ITEM pHistoryItems, + int nCount +); + //remoterepo.c uint32_t TDNFCheckHexDigest( @@ -450,6 +456,14 @@ TDNFGoal( TDNF_ALTERTYPE nAlterType ); +uint32_t +TDNFHistoryGoal( + PTDNF pTdnf, + Queue *pqInstall, + Queue *pqErase, + PTDNF_SOLVED_PKG_INFO* ppInfo + ); + uint32_t TDNFAddUserInstall( PTDNF pTdnf, @@ -1225,4 +1239,13 @@ TDNFDeleteList( TDNF_ML_LIST** head_ref, TDNF_ML_FREE_FUNC free_func ); + + +uint32_t +TDNFGetHistoryCtx( + PTDNF pTdnf, + struct history_ctx **ppCtx, + int nMustExist +); + #endif /* __CLIENT_PROTOTYPES_H__ */ diff --git a/client/rpmtrans.c b/client/rpmtrans.c index 08ec98fb..7cf6297b 100644 --- a/client/rpmtrans.c +++ b/client/rpmtrans.c @@ -32,6 +32,8 @@ TDNFRpmExecTransaction( int nDownloadOnly = 0; TDNFRPMTS ts = {0}; PTDNF_CMD_OPT pSetOpt = NULL; + struct history_ctx *pHistoryCtx = NULL; + char *pszCmdLine = NULL; if(!pTdnf || !pTdnf->pArgs || !pTdnf->pConf || !pSolvedInfo) { @@ -97,14 +99,48 @@ TDNFRpmExecTransaction( BAIL_ON_TDNF_ERROR(dwError); if (!nDownloadOnly) { + int rc; + + dwError = TDNFGetHistoryCtx(pTdnf, &pHistoryCtx, 0); + BAIL_ON_TDNF_ERROR(dwError); + + rc = history_sync(pHistoryCtx, ts.pTS); + if (rc != 0) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + dwError = TDNFRunTransaction(&ts, pTdnf); BAIL_ON_TDNF_ERROR(dwError); + if (pTdnf->pArgs->nArgc >= 1) + { + dwError = TDNFJoinArrayToString(&(pTdnf->pArgs->ppszArgv[1]), + " ", + pTdnf->pArgs->nArgc, + &pszCmdLine); + BAIL_ON_TDNF_ERROR(dwError); + } + + rc = history_update_state(pHistoryCtx, ts.pTS, pszCmdLine); + if (rc != 0) + { + dwError = ERROR_TDNF_HISTORY_ERROR; + BAIL_ON_TDNF_ERROR(dwError); + } + dwError = TDNFMarkAutoInstalled(pTdnf, pSolvedInfo); BAIL_ON_TDNF_ERROR(dwError); } cleanup: + + TDNF_SAFE_FREE_MEMORY(pszCmdLine); + if (pHistoryCtx) + { + destroy_history_ctx(pHistoryCtx); + } if(ts.pTS) { rpmtsCloseDB(ts.pTS); diff --git a/client/utils.c b/client/utils.c index 27d564d4..5fb550cd 100644 --- a/client/utils.c +++ b/client/utils.c @@ -764,3 +764,48 @@ TDNFReadAutoInstalled( TDNF_SAFE_FREE_STRINGARRAY(ppszAutoInstalled); goto cleanup; } + +void +TDNFFreeHistoryInfoItems( + PTDNF_HISTORY_INFO_ITEM pHistoryItems, + int nCount +) +{ + if (pHistoryItems) + { + for (int i = 0; i < nCount; i++) + { + TDNF_SAFE_FREE_MEMORY(pHistoryItems[i].pszCmdLine); + int j; + if (pHistoryItems[i].ppszAddedPkgs != NULL) + { + for (j = 0; j < pHistoryItems[i].nAddedCount; j++) + { + TDNF_SAFE_FREE_MEMORY(pHistoryItems[i].ppszAddedPkgs[j]); + } + TDNF_SAFE_FREE_MEMORY(pHistoryItems[i].ppszAddedPkgs); + } + if (pHistoryItems[i].ppszRemovedPkgs != NULL) + { + for (j = 0; j < pHistoryItems[i].nRemovedCount; j++) + { + TDNF_SAFE_FREE_MEMORY(pHistoryItems[i].ppszRemovedPkgs[j]); + } + TDNF_SAFE_FREE_MEMORY(pHistoryItems[i].ppszRemovedPkgs); + } + } + TDNFFreeMemory(pHistoryItems); + } +} + +void +TDNFFreeHistoryInfo( + PTDNF_HISTORY_INFO pHistoryInfo +) +{ + if (pHistoryInfo) + { + TDNFFreeHistoryInfoItems(pHistoryInfo->pItems, pHistoryInfo->nItemCount); + TDNFFreeMemory(pHistoryInfo); + } +} diff --git a/include/tdnf.h b/include/tdnf.h index 80a0e309..302b8116 100644 --- a/include/tdnf.h +++ b/include/tdnf.h @@ -139,6 +139,20 @@ TDNFUpdateInfoSummary( PTDNF_UPDATEINFO_SUMMARY* ppSummary ); +uint32_t +TDNFHistoryResolve( + PTDNF pTdnf, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_SOLVED_PKG_INFO *ppSolvedPkgInfo); + +uint32_t +TDNFHistoryList( + PTDNF pTdnf, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_HISTORY_INFO *ppHistoryInfo); + + + //confidence check. displays current installed count. //should be same as rpm -qa | wc -l uint32_t @@ -249,8 +263,15 @@ TDNFFreeUpdateInfoSummary( void TDNFFreeCmdOpt( PTDNF_CMD_OPT pCmdOpt + ); +void +TDNFFreeHistoryInfo( + PTDNF_HISTORY_INFO pHistoryInfo +); + + uint32_t TDNFUriIsRemote( const char* pszKeyUrl, int *nRemote diff --git a/include/tdnfcli.h b/include/tdnfcli.h index e2b412f4..666a314a 100644 --- a/include/tdnfcli.h +++ b/include/tdnfcli.h @@ -21,7 +21,7 @@ extern "C" { uint32_t TDNFCliParseArgs( int argc, - char* const* argv, + char** argv, PTDNF_CMD_ARGS* ppCmdArgs ); @@ -31,6 +31,17 @@ TDNFCliParseScope( TDNF_SCOPE* pnScope ); +uint32_t +TDNFCliParseHistoryArgs( + PTDNF_CMD_ARGS pArgs, + PTDNF_HISTORY_ARGS* ppHistoryArgs + ); + +void +TDNFCliFreeHistoryArgs( + PTDNF_HISTORY_ARGS pHistoryArgs + ); + uint32_t TDNFCliParseListArgs( PTDNF_CMD_ARGS pCmdArgs, @@ -237,6 +248,12 @@ TDNFCliHelpCommand( PTDNF_CMD_ARGS pCmdArgs ); +uint32_t +TDNFCliHistoryCommand( + PTDNF_CLI_CONTEXT pContext, + PTDNF_CMD_ARGS pCmdArgs + ); + //installcmd.c uint32_t TDNFCliDowngradeCommand( @@ -274,6 +291,14 @@ TDNFCliUpgradeCommand( PTDNF_CMD_ARGS pCmdArgs ); +uint32_t +TDNFCliAskAndAlter( + PTDNF_CLI_CONTEXT pContext, + PTDNF_CMD_ARGS pCmdArgs, + TDNF_ALTERTYPE nAlterType, + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo +); + uint32_t TDNFCliAlterCommand( PTDNF_CLI_CONTEXT pContext, @@ -322,14 +347,7 @@ ShowConsoleProps( uint32_t GetConsoleWidth( - int* pnWidth - ); - -int -CalculateColumnWidth( - int nTotalWidth, - int nRequestedPercent, - int nMinVal + int* pConsoleWidth ); uint32_t diff --git a/include/tdnfclitypes.h b/include/tdnfclitypes.h index 79cbbb62..ccf522cb 100644 --- a/include/tdnfclitypes.h +++ b/include/tdnfclitypes.h @@ -147,27 +147,41 @@ typedef uint32_t PTDNF_UPDATEINFO_ARGS, PTDNF_UPDATEINFO_SUMMARY *); +typedef uint32_t +(*PFN_TDNF_HISTORY_CMD)( + PTDNF_CLI_CONTEXT, + PTDNF_HISTORY_ARGS, + PTDNF_HISTORY_INFO *); + +typedef uint32_t +(*PFN_TDNF_HISTORY_RESOLVE_CMD)( + PTDNF_CLI_CONTEXT, + PTDNF_HISTORY_ARGS, + PTDNF_SOLVED_PKG_INFO *); + typedef struct _TDNF_CLI_CONTEXT_ { HTDNF hTdnf; void *pUserData; - PFN_TDNF_ALTER pFnAlter; - PFN_TDNF_CHECK_LOCAL pFnCheckLocal; - PFN_TDNF_CHECK_UPDATE pFnCheckUpdate; - PFN_TDNF_CHECK pFnCheck; - PFN_TDNF_CLEAN pFnClean; - PFN_TDNF_COUNT pFnCount; - PFN_TDNF_INFO pFnInfo; - PFN_TDNF_LIST pFnList; - PFN_TDNF_PROVIDES pFnProvides; - PFN_TDNF_REPOLIST pFnRepoList; - PFN_TDNF_REPOSYNC pFnRepoSync; - PFN_TDNF_REPOQUERY pFnRepoQuery; - PFN_TDNF_RESOLVE pFnResolve; - PFN_TDNF_SEARCH pFnSearch; - PFN_TDNF_UPDATEINFO pFnUpdateInfo; - PFN_TDNF_UPDATEINFO_SUMMARY pFnUpdateInfoSummary; + PFN_TDNF_ALTER pFnAlter; + PFN_TDNF_CHECK_LOCAL pFnCheckLocal; + PFN_TDNF_CHECK_UPDATE pFnCheckUpdate; + PFN_TDNF_CHECK pFnCheck; + PFN_TDNF_CLEAN pFnClean; + PFN_TDNF_COUNT pFnCount; + PFN_TDNF_INFO pFnInfo; + PFN_TDNF_LIST pFnList; + PFN_TDNF_PROVIDES pFnProvides; + PFN_TDNF_REPOLIST pFnRepoList; + PFN_TDNF_REPOSYNC pFnRepoSync; + PFN_TDNF_REPOQUERY pFnRepoQuery; + PFN_TDNF_RESOLVE pFnResolve; + PFN_TDNF_SEARCH pFnSearch; + PFN_TDNF_UPDATEINFO pFnUpdateInfo; + PFN_TDNF_UPDATEINFO_SUMMARY pFnUpdateInfoSummary; + PFN_TDNF_HISTORY_CMD pFnHistoryList; + PFN_TDNF_HISTORY_RESOLVE_CMD pFnHistoryResolve; }TDNF_CLI_CONTEXT, *PTDNF_CLI_CONTEXT; #ifdef __cplusplus diff --git a/include/tdnferror.h b/include/tdnferror.h index 01410d49..b601da80 100644 --- a/include/tdnferror.h +++ b/include/tdnferror.h @@ -144,9 +144,9 @@ extern "C" { #define ERROR_TDNF_NO_PLUGIN_ERROR 1522 #define ERROR_TDNF_NO_GPGKEY_CONF_ENTRY 1523 #define ERROR_TDNF_URL_INVALID 1524 - //RPM Transaction #define ERROR_TDNF_TRANSACTION_FAILED 1525 +#define ERROR_TDNF_RPMTS_OPENDB_FAILED 1526 /* event context */ #define ERROR_TDNF_EVENT_CTXT_ITEM_NOT_FOUND 1551 @@ -166,6 +166,10 @@ extern "C" { #define ERROR_TDNF_JSONDUMP 1700 +#define ERROR_TDNF_HISTORY_ERROR 1801 +#define ERROR_TDNF_HISTORY_NODB 1802 + + #define ERROR_TDNF_PLUGIN_BASE 2000 #define ERROR_TDNF_BASEURL_DOES_NOT_EXISTS 2500 diff --git a/include/tdnftypes.h b/include/tdnftypes.h index f794f626..a75556ad 100644 --- a/include/tdnftypes.h +++ b/include/tdnftypes.h @@ -229,6 +229,9 @@ typedef struct _TDNF_CMD_ARGS char** ppszCmds; int nCmdCount; PTDNF_CMD_OPT pSetOpt; + + int nArgc; + char **ppszArgv; }TDNF_CMD_ARGS, *PTDNF_CMD_ARGS; typedef struct _TDNF_CONF @@ -364,7 +367,7 @@ typedef enum { REPOQUERY_DEP_KEY_DEPENDS, REPOQUERY_DEP_KEY_REQUIRES_PRE, REPOQUERY_DEP_KEY_COUNT -} REPOQUERY_DEP_KEY; +}REPOQUERY_DEP_KEY; typedef struct _TDNF_REPOQUERY_ARGS { @@ -386,6 +389,42 @@ typedef struct _TDNF_REPOQUERY_ARGS int nSource; /* show source packages */ }TDNF_REPOQUERY_ARGS, *PTDNF_REPOQUERY_ARGS; +typedef enum { + HISTORY_CMD_LIST = 0, + HISTORY_CMD_INIT, + HISTORY_CMD_ROLLBACK, + HISTORY_CMD_UNDO, + HISTORY_CMD_REDO +} HISTORY_CMD; + +typedef struct _TDNF_HISTORY_ARGS +{ + HISTORY_CMD nCommand; + int nInfo; + int nFrom; + int nTo; + int nReverse; + char *pszSpec; +} TDNF_HISTORY_ARGS, *PTDNF_HISTORY_ARGS; + +typedef struct _TDNF_HISTORY_INFO_ITEM +{ + int nId; + int nType; + char *pszCmdLine; + time_t timeStamp; + int nAddedCount; + int nRemovedCount; + char **ppszAddedPkgs; + char **ppszRemovedPkgs; +} TDNF_HISTORY_INFO_ITEM, *PTDNF_HISTORY_INFO_ITEM; + +typedef struct _TDNF_HISTORY_INFO +{ + int nItemCount; + PTDNF_HISTORY_INFO_ITEM pItems; +} TDNF_HISTORY_INFO, *PTDNF_HISTORY_INFO; + #ifdef __cplusplus } #endif diff --git a/solv/defines.h b/solv/defines.h index c2a4493b..38f5ab1d 100644 --- a/solv/defines.h +++ b/solv/defines.h @@ -7,6 +7,9 @@ #define TDNF_SOLVCACHE_DIR_NAME "solvcache" #define SOLV_COOKIE_LEN 32 +#define SOLV_NEVRA_UNINSTALLED 0 +#define SOLV_NEVRA_INSTALLED 1 + #define BAIL_ON_TDNF_LIBSOLV_ERROR(dwError) \ do { \ if (dwError) \ diff --git a/solv/simplequery.c b/solv/simplequery.c index 448a385c..2ba56aa0 100644 --- a/solv/simplequery.c +++ b/solv/simplequery.c @@ -55,7 +55,7 @@ int split_nevra(char *nevra, char **name, char **evr, char **arch) } /* Find packages by nevra as specified with ids. Must be either installed - or not as set by the 'installed' flag. Returns results in a queue, can + or not as set by the 'installed' flag. Adds result to qresult, can be multiples if package is in multiple repos. */ uint32_t SolvFindSolvablesByNevraId( diff --git a/tools/cli/lib/CMakeLists.txt b/tools/cli/lib/CMakeLists.txt index e744e0a5..070556ee 100644 --- a/tools/cli/lib/CMakeLists.txt +++ b/tools/cli/lib/CMakeLists.txt @@ -21,9 +21,10 @@ add_library(${LIB_TDNF_CLI} SHARED parseargs.c parsecleanargs.c parselistargs.c + parsehistoryargs.c parserepolistargs.c - parsereposyncargs.c parserepoqueryargs.c + parsereposyncargs.c parseupdateinfo.c updateinfocmd.c ) diff --git a/tools/cli/lib/api.c b/tools/cli/lib/api.c index 777e0d5e..9cec5cb7 100644 --- a/tools/cli/lib/api.c +++ b/tools/cli/lib/api.c @@ -131,10 +131,10 @@ TDNFCliListCommand( char szNameAndArch[MAX_COL_LEN] = {0}; char szVersionAndRelease[MAX_COL_LEN] = {0}; - #define COL_COUNT 3 + #define LIST_COL_COUNT 3 //Name.Arch | Version-Release | Repo - int nColPercents[COL_COUNT] = {55, 25, 15}; - int nColWidths[COL_COUNT] = {0}; + int nColPercents[LIST_COL_COUNT] = {55, 25, 15}; + int nColWidths[LIST_COL_COUNT] = {0}; if(!pContext || !pContext->hTdnf || !pContext->pFnList) { @@ -180,7 +180,7 @@ TDNFCliListCommand( } else { - dwError = GetColumnWidths(COL_COUNT, nColPercents, nColWidths); + dwError = GetColumnWidths(LIST_COL_COUNT, nColPercents, nColWidths); BAIL_ON_CLI_ERROR(dwError); for(dwIndex = 0; dwIndex < dwCount; ++dwIndex) @@ -1023,3 +1023,237 @@ TDNFCliRefresh( return TDNFRefresh(pContext->hTdnf); } +static +uint32_t +TDNFCliHistoryAlter( + PTDNF_CLI_CONTEXT pContext, + PTDNF_CMD_ARGS pCmdArgs, + PTDNF_HISTORY_ARGS pHistoryArgs +) +{ + uint32_t dwError = 0; + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo = NULL; + + if(!pContext || !pCmdArgs || !pHistoryArgs) + { + dwError = ERROR_TDNF_CLI_INVALID_ARGUMENT; + BAIL_ON_CLI_ERROR(dwError); + } + + dwError = pContext->pFnHistoryResolve(pContext, pHistoryArgs, &pSolvedPkgInfo); + BAIL_ON_CLI_ERROR(dwError); + + if (pHistoryArgs->nCommand == HISTORY_CMD_INIT) + { + /* There is nothing to do here. */ + } + else if (!(pSolvedPkgInfo->ppszPkgsNotResolved && pSolvedPkgInfo->ppszPkgsNotResolved[0])) + { + dwError = TDNFCliAskAndAlter(pContext, pCmdArgs, 0, pSolvedPkgInfo); + BAIL_ON_CLI_ERROR(dwError); + } + else + { + char *pszName = NULL; + int i; + pr_crit("The following packages could not be resolved:\n\n"); + for (i = 0, pszName = pSolvedPkgInfo->ppszPkgsNotResolved[0]; + pszName; + i++, pszName = pSolvedPkgInfo->ppszPkgsNotResolved[i]) + { + pr_crit("%s\n", pszName); + } + pr_crit("\n" + "The package(s) may have been moved out of the enabled repositories since the\n" + "last time they were installed. You may be able to resolve this by enabling\n" + "additional repositories.\n"); + dwError = ERROR_TDNF_NO_MATCH; + BAIL_ON_CLI_ERROR(dwError); + } + +cleanup: + TDNFCliFreeSolvedPackageInfo(pSolvedPkgInfo); + return dwError; +error: + goto cleanup; +} + +static +uint32_t +TDNFCliHistoryList( + PTDNF_CLI_CONTEXT pContext, + PTDNF_CMD_ARGS pCmdArgs, + PTDNF_HISTORY_ARGS pHistoryArgs +) +{ + uint32_t dwError = 0; + PTDNF_HISTORY_INFO pHistoryInfo = NULL; + PTDNF_HISTORY_INFO_ITEM pItems = NULL; + struct json_dump *jd = NULL; + struct json_dump *jd_item = NULL; + struct json_dump *jd_list_added = NULL; + struct json_dump *jd_list_removed = NULL; + + if(!pContext || !pCmdArgs || !pHistoryArgs) + { + dwError = ERROR_TDNF_CLI_INVALID_ARGUMENT; + BAIL_ON_CLI_ERROR(dwError); + } + + dwError = pContext->pFnHistoryList(pContext, pHistoryArgs, &pHistoryInfo); + BAIL_ON_CLI_ERROR(dwError); + + pItems = pHistoryInfo->pItems; + + if (!pCmdArgs->nJsonOutput) + { + int nConsoleWidth, nCmdWidth; + + dwError = GetConsoleWidth(&nConsoleWidth); + BAIL_ON_CLI_ERROR(dwError); + + /* We want to give as much space as possible to the command line. + All other fields have fixed lengths. */ + /* 4 = ID, 21 = date/time, 9 = '+add/-rem', 7 = spaces */ + nCmdWidth = nConsoleWidth - 4 - 21 - 9 - 7; + + pr_crit("ID %-*s date/time +add / -rem\n", nCmdWidth, "cmd line"); + for (int i = 0; i < pHistoryInfo->nItemCount; i++) + { + char szTime[22] = {0}; + strftime(szTime, 22, "%a %b %d %Y %H:%M", localtime(&pItems[i].timeStamp)); + pr_crit("%4d %-*s %-21s +%-4d / -%-4d\n", + pItems[i].nId, + nCmdWidth, pItems[i].pszCmdLine, + szTime, + pItems[i].nAddedCount, + pItems[i].nRemovedCount); + if (pHistoryArgs->nInfo) + { + if (pItems[i].ppszAddedPkgs && pItems[i].ppszAddedPkgs[0]) + { + int j; + pr_crit("added: "); + for (j = 0; j < pItems[i].nAddedCount - 1; j++) + { + pr_crit("%s, ", pItems[i].ppszAddedPkgs[j]); + } + pr_crit("%s", pItems[i].ppszAddedPkgs[j]); + pr_crit("\n"); + } + if (pItems[i].ppszRemovedPkgs && pItems[i].ppszRemovedPkgs[0]) + { + int j; + pr_crit("removed: "); + for (j = 0; j < pItems[i].nRemovedCount - 1; j++) + { + pr_crit("%s, ", pItems[i].ppszRemovedPkgs[j]); + } + pr_crit("%s", pItems[i].ppszRemovedPkgs[j]); + pr_crit("\n"); + } + pr_crit("\n"); + } + } + } + else + { + jd = jd_create(0); + CHECK_JD_NULL(jd); + + CHECK_JD_RC(jd_list_start(jd)); + + for(int i = 0; i < pHistoryInfo->nItemCount; i++) + { + jd_item = jd_create(0); + CHECK_JD_NULL(jd_item); + + CHECK_JD_RC(jd_map_start(jd_item)); + CHECK_JD_RC(jd_map_add_int(jd_item, "Id", pItems[i].nId)); + CHECK_JD_RC(jd_map_add_string(jd_item, "CmdLine", pItems[i].pszCmdLine)); + CHECK_JD_RC(jd_map_add_int(jd_item, "TimeStamp", pItems[i].timeStamp)); + CHECK_JD_RC(jd_map_add_int(jd_item, "AddedCount", pItems[i].nAddedCount)); + CHECK_JD_RC(jd_map_add_int(jd_item, "RemovedCount", pItems[i].nRemovedCount)); + if (pHistoryArgs->nInfo) + { + jd_list_added = jd_create(0); + CHECK_JD_NULL(jd_list_added); + jd_list_start(jd_list_added); + + jd_list_removed = jd_create(0); + CHECK_JD_NULL(jd_list_removed); + jd_list_start(jd_list_removed); + + if (pItems[i].ppszAddedPkgs && pItems[i].ppszAddedPkgs[0]) + { + for (int j = 0; j < pItems[i].nAddedCount; j++) + { + CHECK_JD_RC(jd_list_add_string(jd_list_added, pItems[i].ppszAddedPkgs[j])); + } + } + if (pItems[i].ppszRemovedPkgs && pItems[i].ppszRemovedPkgs[0]) + { + for (int j = 0; j < pItems[i].nRemovedCount; j++) + { + CHECK_JD_RC(jd_list_add_string(jd_list_removed, pItems[i].ppszRemovedPkgs[j])); + } + } + CHECK_JD_RC(jd_map_add_child(jd_item, "Added", jd_list_added)); + JD_SAFE_DESTROY(jd_list_added); + + CHECK_JD_RC(jd_map_add_child(jd_item, "Removed", jd_list_removed)); + JD_SAFE_DESTROY(jd_list_removed); + } + CHECK_JD_RC(jd_list_add_child(jd, jd_item)); + JD_SAFE_DESTROY(jd_item); + } + pr_json(jd->buf); + JD_SAFE_DESTROY(jd); + } +cleanup: + TDNFFreeHistoryInfo(pHistoryInfo); + return dwError; + +error: + JD_SAFE_DESTROY(jd); + JD_SAFE_DESTROY(jd_item); + JD_SAFE_DESTROY(jd_list_added); + JD_SAFE_DESTROY(jd_list_removed); + goto cleanup; +} + +uint32_t +TDNFCliHistoryCommand( + PTDNF_CLI_CONTEXT pContext, + PTDNF_CMD_ARGS pCmdArgs +) +{ + uint32_t dwError = 0; + PTDNF_HISTORY_ARGS pHistoryArgs = NULL; + + if(!pContext || !pContext->hTdnf || !pCmdArgs) + { + dwError = ERROR_TDNF_CLI_INVALID_ARGUMENT; + BAIL_ON_CLI_ERROR(dwError); + } + dwError = TDNFCliParseHistoryArgs(pCmdArgs, &pHistoryArgs); + BAIL_ON_CLI_ERROR(dwError); + + if (pHistoryArgs->nCommand == HISTORY_CMD_LIST) + { + dwError = TDNFCliHistoryList(pContext, pCmdArgs, pHistoryArgs); + BAIL_ON_CLI_ERROR(dwError); + } + else + { + dwError = TDNFCliHistoryAlter(pContext, pCmdArgs, pHistoryArgs); + BAIL_ON_CLI_ERROR(dwError); + } + +cleanup: + TDNFCliFreeHistoryArgs(pHistoryArgs); + return dwError; + +error: + goto cleanup; +} diff --git a/tools/cli/lib/installcmd.c b/tools/cli/lib/installcmd.c index c3d08e99..5943041c 100644 --- a/tools/cli/lib/installcmd.c +++ b/tools/cli/lib/installcmd.c @@ -160,19 +160,18 @@ TDNFCliReinstallCommand( } uint32_t -TDNFCliAlterCommand( +TDNFCliAskAndAlter( PTDNF_CLI_CONTEXT pContext, PTDNF_CMD_ARGS pCmdArgs, - TDNF_ALTERTYPE nAlterType - ) + TDNF_ALTERTYPE nAlterType, + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo +) { uint32_t dwError = 0; char** ppszPackageArgs = NULL; - int nPackageCount = 0; - PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo = NULL; int nSilent = 0; - if(!pContext || !pContext->hTdnf || !pCmdArgs) + if(!pContext || !pCmdArgs || !pSolvedPkgInfo) { dwError = ERROR_TDNF_INVALID_PARAMETER; BAIL_ON_CLI_ERROR(dwError); @@ -180,18 +179,6 @@ TDNFCliAlterCommand( nSilent = pCmdArgs->nNoOutput; - dwError = TDNFCliParsePackageArgs( - pCmdArgs, - &ppszPackageArgs, - &nPackageCount); - BAIL_ON_CLI_ERROR(dwError); - - dwError = pContext->pFnResolve( - pContext, - nAlterType, - &pSolvedPkgInfo); - BAIL_ON_CLI_ERROR(dwError); - if(!nSilent && pSolvedPkgInfo->ppszPkgsNotResolved) { dwError = PrintNotAvailable(pSolvedPkgInfo->ppszPkgsNotResolved); @@ -269,6 +256,47 @@ TDNFCliAlterCommand( } } +cleanup: + TDNF_CLI_SAFE_FREE_STRINGARRAY(ppszPackageArgs); + return dwError; + +error: + goto cleanup; +} + +uint32_t +TDNFCliAlterCommand( + PTDNF_CLI_CONTEXT pContext, + PTDNF_CMD_ARGS pCmdArgs, + TDNF_ALTERTYPE nAlterType + ) +{ + uint32_t dwError = 0; + char** ppszPackageArgs = NULL; + int nPackageCount = 0; + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo = NULL; + + if(!pContext || !pContext->hTdnf || !pCmdArgs) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_CLI_ERROR(dwError); + } + + dwError = TDNFCliParsePackageArgs( + pCmdArgs, + &ppszPackageArgs, + &nPackageCount); + BAIL_ON_CLI_ERROR(dwError); + + dwError = pContext->pFnResolve( + pContext, + nAlterType, + &pSolvedPkgInfo); + BAIL_ON_CLI_ERROR(dwError); + + dwError = TDNFCliAskAndAlter(pContext, pCmdArgs, nAlterType, pSolvedPkgInfo); + BAIL_ON_CLI_ERROR(dwError); + cleanup: TDNF_CLI_SAFE_FREE_STRINGARRAY(ppszPackageArgs); TDNFCliFreeSolvedPackageInfo(pSolvedPkgInfo); @@ -279,7 +307,6 @@ TDNFCliAlterCommand( { dwError = ERROR_TDNF_CLI_NOTHING_TO_DO; } - goto cleanup; } diff --git a/tools/cli/lib/output.c b/tools/cli/lib/output.c index bd65c0da..328df64d 100644 --- a/tools/cli/lib/output.c +++ b/tools/cli/lib/output.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 VMware, Inc. All Rights Reserved. + * Copyright (C) 2015-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 @@ -34,18 +34,15 @@ ShowConsoleProps( } uint32_t -GetColumnWidths( - int nCount, - const int *pnColPercents, - int *pnColWidths +GetConsoleWidth( + int *pnConsoleWidth ) { uint32_t dwError = 0; struct winsize stWinSize = {0}; int nConsoleWidth = 0; - int nIndex = 0; - if(!pnColPercents || !pnColWidths) + if(!pnConsoleWidth) { dwError = ERROR_TDNF_INVALID_PARAMETER; BAIL_ON_CLI_ERROR(dwError); @@ -61,6 +58,33 @@ GetColumnWidths( { nConsoleWidth = stWinSize.ws_col; } + *pnConsoleWidth = nConsoleWidth; + +cleanup: + return dwError; +error: + goto cleanup; +} + +uint32_t +GetColumnWidths( + int nCount, + const int *pnColPercents, + int *pnColWidths + ) +{ + uint32_t dwError = 0; + int nConsoleWidth = 0; + int nIndex = 0; + + if(!pnColPercents || !pnColWidths) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_CLI_ERROR(dwError); + } + + dwError = GetConsoleWidth(&nConsoleWidth); + BAIL_ON_CLI_ERROR(dwError); for(nIndex = 0; nIndex < nCount; nIndex++) { diff --git a/tools/cli/lib/parseargs.c b/tools/cli/lib/parseargs.c index 2b7c0e53..fcc30fd1 100644 --- a/tools/cli/lib/parseargs.c +++ b/tools/cli/lib/parseargs.c @@ -64,7 +64,7 @@ static struct option pstOptions[] = {"skipdigest", no_argument, 0, 0}, //--skipdigest to skip verifying RPM digest {"skipobsoletes", no_argument, 0, 0}, //--skipobsoletes to skip obsolete problems {"skipsignature", no_argument, 0, 0}, //--skipsignature to skip verifying RPM signatures - {"verbose", no_argument, &_opt.nVerbose, 1}, //-v --verbose + {"verbose", no_argument, &_opt.nVerbose, 1}, //-v --verbose {"version", no_argument, &_opt.nShowVersion, 1}, //--version // reposync options {"arch", required_argument, 0, 0}, @@ -111,16 +111,20 @@ static struct option pstOptions[] = {"info", no_argument, 0, 0}, {"summary", no_argument, 0, 0}, // scope options for list and update-info - {"recent", no_argument, 0, 0}, + {"recent", no_argument, 0, 0}, {"updates", no_argument, 0, 0}, {"downgrades", no_argument, 0, 0}, + // history + {"to", required_argument, 0, 0}, + {"from", required_argument, 0, 0}, + {"reverse", no_argument, 0, 0}, {0, 0, 0, 0} }; uint32_t TDNFCliParseArgs( int argc, - char* const* argv, + char** argv, PTDNF_CMD_ARGS* ppCmdArgs ) { @@ -156,6 +160,8 @@ TDNFCliParseArgs( _opt.nAssumeYes = 1; } } + pCmdArgs->nArgc = argc; + pCmdArgs->ppszArgv = argv; opterr = 0;//tell getopt to not print errors while (1) diff --git a/tools/cli/lib/parsehistoryargs.c b/tools/cli/lib/parsehistoryargs.c new file mode 100644 index 00000000..4b34d07f --- /dev/null +++ b/tools/cli/lib/parsehistoryargs.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2022 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. + */ + +/* + * Module : parsehistoryargs.c + * + * Abstract : + * + * tdnf + * + * command line tools + */ + +#include "includes.h" + +uint32_t +TDNFCliParseHistoryArgs( + PTDNF_CMD_ARGS pArgs, + PTDNF_HISTORY_ARGS* ppHistoryArgs + ) +{ + uint32_t dwError = 0; + PTDNF_HISTORY_ARGS pHistoryArgs = NULL; + PTDNF_CMD_OPT pSetOpt = NULL; + char **ppszRange = NULL; + + if (!pArgs || !ppHistoryArgs) + { + dwError = ERROR_TDNF_CLI_INVALID_ARGUMENT; + BAIL_ON_CLI_ERROR(dwError); + } + + dwError = TDNFAllocateMemory( + 1, + sizeof(TDNF_HISTORY_ARGS), + (void**) &pHistoryArgs); + BAIL_ON_CLI_ERROR(dwError); + + /* history subcommands */ + if (pArgs->nCmdCount > 1) + { + if (strcmp(pArgs->ppszCmds[1], "list") == 0) + { + pHistoryArgs->nCommand = HISTORY_CMD_LIST; + } + else if (strcmp(pArgs->ppszCmds[1], "init") == 0 || + strcmp(pArgs->ppszCmds[1], "update") == 0) + { + pHistoryArgs->nCommand = HISTORY_CMD_INIT; + } + else if (strcmp(pArgs->ppszCmds[1], "rollback") == 0) + { + pHistoryArgs->nCommand = HISTORY_CMD_ROLLBACK; + } + else if (strcmp(pArgs->ppszCmds[1], "undo") == 0) + { + pHistoryArgs->nCommand = HISTORY_CMD_UNDO; + } + else if (strcmp(pArgs->ppszCmds[1], "redo") == 0) + { + pHistoryArgs->nCommand = HISTORY_CMD_REDO; + } + } + + if (pArgs->nCmdCount > 2 && isdigit(pArgs->ppszCmds[2][0])) + { + dwError = TDNFSplitStringToArray(pArgs->ppszCmds[2], "-", &ppszRange); + BAIL_ON_CLI_ERROR(dwError); + + pHistoryArgs->nFrom = atoi(ppszRange[0]); + if (ppszRange[1]) + { + pHistoryArgs->nTo = atoi(ppszRange[1]); + } + } + + for (pSetOpt = pArgs->pSetOpt; + pSetOpt; + pSetOpt = pSetOpt->pNext) + { + if (strcasecmp(pSetOpt->pszOptName, "info") == 0) + { + pHistoryArgs->nInfo = 1; + } + else if (strcasecmp(pSetOpt->pszOptName, "reverse") == 0) + { + pHistoryArgs->nReverse = 1; + } + else if (strcasecmp(pSetOpt->pszOptName, "from") == 0) + { + pHistoryArgs->nFrom = atoi(pSetOpt->pszOptValue); + } + else if (strcasecmp(pSetOpt->pszOptName, "to") == 0) + { + pHistoryArgs->nTo = atoi(pSetOpt->pszOptValue); + } + } + + if (pHistoryArgs->nTo == 0) + { + pHistoryArgs->nTo = pHistoryArgs->nFrom; + } + + *ppHistoryArgs = pHistoryArgs; +cleanup: + return dwError; +error: + if (pHistoryArgs) + { + TDNFCliFreeHistoryArgs(pHistoryArgs); + } + goto cleanup; +} + +void +TDNFCliFreeHistoryArgs( + PTDNF_HISTORY_ARGS pHistoryArgs + ) +{ + if(pHistoryArgs) + { + TDNFFreeMemory(pHistoryArgs); + } +} + diff --git a/tools/cli/main.c b/tools/cli/main.c index dc2f9caa..dfcc89c0 100644 --- a/tools/cli/main.c +++ b/tools/cli/main.c @@ -34,6 +34,7 @@ static TDNF_CLI_CMD_MAP arCmdMap[] = {"downgrade", TDNFCliDowngradeCommand, true}, {"erase", TDNFCliEraseCommand, true}, {"help", TDNFCliHelpCommand, false}, + {"history", TDNFCliHistoryCommand, true}, {"info", TDNFCliInfoCommand, false}, {"install", TDNFCliInstallCommand, true}, {"list", TDNFCliListCommand, false}, @@ -99,6 +100,8 @@ int main(int argc, char **argv) _context.pFnSearch = TDNFCliInvokeSearch; _context.pFnUpdateInfo = TDNFCliInvokeUpdateInfo; _context.pFnUpdateInfoSummary = TDNFCliInvokeUpdateInfoSummary; + _context.pFnHistoryList = TDNFCliInvokeHistoryList; + _context.pFnHistoryResolve = TDNFCliInvokeHistoryResolve; pszCmd = pCmdArgs->ppszCmds[0]; @@ -482,3 +485,28 @@ TDNFCliInvokeUpdateInfoSummary( ppSummary); } +uint32_t +TDNFCliInvokeHistoryList( + PTDNF_CLI_CONTEXT pContext, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_HISTORY_INFO *ppHistoryInfo +) +{ + return TDNFHistoryList( + pContext->hTdnf, + pHistoryArgs, + ppHistoryInfo); +} + +uint32_t +TDNFCliInvokeHistoryResolve( + PTDNF_CLI_CONTEXT pContext, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_SOLVED_PKG_INFO *ppSolvedPkgInfo +) +{ + return TDNFHistoryResolve( + pContext->hTdnf, + pHistoryArgs, + ppSolvedPkgInfo); +} diff --git a/tools/cli/prototypes.h b/tools/cli/prototypes.h index 9263213f..a09fde87 100644 --- a/tools/cli/prototypes.h +++ b/tools/cli/prototypes.h @@ -119,6 +119,20 @@ TDNFCliInvokeSearch( uint32_t *pdwCount ); +uint32_t +TDNFCliInvokeHistoryList( + PTDNF_CLI_CONTEXT pContext, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_HISTORY_INFO *ppHistoryInfo +); + +uint32_t +TDNFCliInvokeHistoryResolve( + PTDNF_CLI_CONTEXT pContext, + PTDNF_HISTORY_ARGS pHistoryArgs, + PTDNF_SOLVED_PKG_INFO *ppSolvedPkgInfo +); + uint32_t TDNFCliUpdateInfoInfo( PTDNF_UPDATEINFO pInfo @@ -229,14 +243,7 @@ ShowConsoleProps( uint32_t GetConsoleWidth( - int* pnWidth - ); - -int -CalculateColumnWidth( - int nTotalWidth, - int nRequestedPercent, - int nMinVal + int *pnConsoleWidth ); uint32_t @@ -276,7 +283,7 @@ HandleOptionsError( uint32_t TDNFCliParseArgs( int argc, - char* const* argv, + char** argv, PTDNF_CMD_ARGS* ppCmdArgs ); @@ -293,6 +300,18 @@ TDNFCliParseCleanArgs( uint32_t* pnCleanType ); +// psarsehistorargs.c +uint32_t +TDNFCliParseHistoryArgs( + PTDNF_CMD_ARGS pArgs, + PTDNF_HISTORY_ARGS* ppHistoryArgs + ); + +void +TDNFCliFreeHistoryArgs( + PTDNF_HISTORY_ARGS pHistoryArgs + ); + //parselistargs.c uint32_t ParseScope(