Skip to content

Commit

Permalink
implement history commands
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverkurth committed Jun 28, 2022
1 parent 260e010 commit f1883c9
Show file tree
Hide file tree
Showing 25 changed files with 1,340 additions and 147 deletions.
2 changes: 2 additions & 0 deletions client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
352 changes: 352 additions & 0 deletions client/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions client/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"},\
};


Expand Down
Loading

0 comments on commit f1883c9

Please sign in to comment.