diff --git a/client/defines.h b/client/defines.h index 4359ddff..ef6c150e 100644 --- a/client/defines.h +++ b/client/defines.h @@ -128,6 +128,10 @@ typedef enum #define TDNF_REPO_KEY_GPGKEY "gpgkey" #define TDNF_REPO_KEY_USERNAME "username" #define TDNF_REPO_KEY_PASSWORD "password" +#define TDNF_REPO_KEY_METADATA_EXPIRE "metadata_expire" + +//file names +#define TDNF_REPO_METADATA_MARKER "lastrefresh" //Repo defaults #define TDNF_DEFAULT_REPO_LOCATION "/etc/yum.repos.d" @@ -136,6 +140,8 @@ typedef enum #define TDNF_DEFAULT_DISTROARCHPKG "x86_64" #define TDNF_RPM_CACHE_DIR_NAME "rpms" #define TDNF_REPODATA_DIR_NAME "repodata" +#define TDNF_REPO_DEFAULT_METADATA_EXPIRE "8294400"//48 hours in seconds +#define TDNF_REPO_METADATA_EXPIRE_NEVER "never" //var names #define TDNF_VAR_RELEASEVER "$releasever" #define TDNF_VAR_BASEARCH "$basearch" @@ -192,4 +198,5 @@ typedef enum {ERROR_TDNF_RPM_GPG_NO_MATCH, "ERROR_TDNF_RPM_GPG_NO_MATCH", "RPM is signed but failed to match with known keys. Use --nogpgcheck to ignore."}, \ {ERROR_TDNF_AUTOERASE_UNSUPPORTED,"ERROR_TDNF_AUTOERASE_UNSUPPORTED","autoerase / autoremove is not supported."}, \ {ERROR_TDNF_RPM_CHECK, "ERROR_TDNF_RPM_CHECK", "rpm check reported errors"}, \ + {ERROR_TDNF_METADATA_EXPIRE_PARSE, "ERROR_TDNF_METADATA_EXPIRE_PARSE", "metadata_expire value could not be parsed. Check your repo files."},\ }; diff --git a/client/includes.h b/client/includes.h index 0cddad47..eef11c5b 100644 --- a/client/includes.h +++ b/client/includes.h @@ -28,6 +28,7 @@ #include #include #include +#include #include // #include diff --git a/client/init.c b/client/init.c index 73de4a83..12bad25d 100644 --- a/client/init.c +++ b/client/init.c @@ -250,6 +250,7 @@ TDNFRefreshSack( uint32_t dwError = 0; HyRepo hRepo = NULL; int nYumFlags = HY_LOAD_FILELISTS | HY_LOAD_UPDATEINFO; + char* pszRepoCacheDir = NULL; if(!pTdnf) { @@ -272,9 +273,30 @@ TDNFRefreshSack( PTDNF_REPO_DATA pTempRepo = pTdnf->pRepos; while(pTempRepo) { + int nMetadataExpired = 0; if(pTempRepo->nEnabled) { - if(nCleanMetadata) + //Check if expired since last sync per metadata_expire + if(!nCleanMetadata && pTempRepo->lMetadataExpire >= 0) + { + dwError = TDNFAllocateStringPrintf( + &pszRepoCacheDir, + "%s/%s", + pTdnf->pConf->pszCacheDir, + pTempRepo->pszId); + BAIL_ON_TDNF_ERROR(dwError); + + dwError = TDNFShouldSyncMetadata( + pszRepoCacheDir, + pTempRepo->lMetadataExpire, + &nMetadataExpired); + BAIL_ON_TDNF_ERROR(dwError); + + TDNF_SAFE_FREE_MEMORY(pszRepoCacheDir); + pszRepoCacheDir = NULL; + } + + if(nCleanMetadata || nMetadataExpired) { fprintf(stdout, "Refreshing metadata for: '%s'\n", @@ -318,6 +340,7 @@ TDNFRefreshSack( } cleanup: + TDNF_SAFE_FREE_MEMORY(pszRepoCacheDir); return dwError; error: diff --git a/client/prototypes.h b/client/prototypes.h index 774e768b..413b94ab 100644 --- a/client/prototypes.h +++ b/client/prototypes.h @@ -770,6 +770,11 @@ TDNFUtilsMakeDirs( const char* pszPath ); +uint32_t +TDNFTouchFile( + const char* pszFile + ); + uint32_t TDNFRawGetPackageVersion( const char* pszRootDir, @@ -782,6 +787,24 @@ TDNFGetKernelArch( char** ppszArch ); +uint32_t +TDNFUpdateMetadataMarkerFile( + const char* pszRepoDataFolder + ); + +uint32_t +TDNFParseMetadataExpire( + const char* pszMetadataExpire, + long* plMetadataExpire + ); + +uint32_t +TDNFShouldSyncMetadata( + const char* pszRepoDataFolder, + long lMetadataExpire, + int* pnShouldSync + ); + //validate.c uint32_t TDNFValidateCmdArgs( diff --git a/client/repo.c b/client/repo.c index 39e3af41..ee858b9b 100644 --- a/client/repo.c +++ b/client/repo.c @@ -37,6 +37,7 @@ TDNFInitRepo( char* pszRepoCacheDir = NULL; char* pszRepoDataDir = NULL; char* pszUserPass = NULL; + char* pszLastRefreshMarker = NULL; char* ppszRepoUrls[] = {NULL, NULL}; char* ppszLocalUrls[] = {NULL, NULL}; @@ -173,8 +174,19 @@ TDNFInitRepo( dwError = TDNFInitRepoFromMetaData(hRepo, pRepo); BAIL_ON_TDNF_ERROR(dwError); + dwError = TDNFAllocateStringPrintf( + &pszLastRefreshMarker, + "%s/%s", + pszRepoCacheDir, + TDNF_REPO_METADATA_MARKER); + BAIL_ON_TDNF_ERROR(dwError); + + dwError = TDNFTouchFile(pszLastRefreshMarker); + BAIL_ON_TDNF_ERROR(dwError); + *phRepo = hRepo; cleanup: + TDNF_SAFE_FREE_MEMORY(pszLastRefreshMarker); if(pszRepoDataDir) { g_free(pszRepoDataDir); diff --git a/client/repolist.c b/client/repolist.c index 55087e16..1a39cf9d 100644 --- a/client/repolist.c +++ b/client/repolist.c @@ -130,6 +130,7 @@ TDNFLoadReposFromFile( char** ppszRepos = NULL; char* pszRepo = NULL; char* pszValue = NULL; + char* pszMetadataExpire = NULL; PTDNF_REPO_DATA pRepos = NULL; PTDNF_REPO_DATA pRepo = NULL; @@ -238,6 +239,32 @@ TDNFLoadReposFromFile( &pRepo->pszPass); BAIL_ON_TDNF_ERROR(dwError); + dwError = TDNFRepoGetKeyValue( + pKeyFile, + pszRepo, + TDNF_REPO_KEY_METADATA_EXPIRE, + NULL, + &pszMetadataExpire); + BAIL_ON_TDNF_ERROR(dwError); + + //Set + if(IsNullOrEmptyString(pszMetadataExpire)) + { + TDNF_SAFE_FREE_MEMORY(pszMetadataExpire); + dwError = TDNFAllocateString( + TDNF_REPO_DEFAULT_METADATA_EXPIRE, + &pszMetadataExpire); + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFParseMetadataExpire( + pszMetadataExpire, + &pRepo->lMetadataExpire); + BAIL_ON_TDNF_ERROR(dwError); + + TDNF_SAFE_FREE_MEMORY(pszMetadataExpire); + pszMetadataExpire = NULL; + pRepo->pNext = pRepos; pRepos = pRepo; pRepo = NULL; @@ -252,6 +279,7 @@ TDNFLoadReposFromFile( } g_free(pszValue); g_strfreev(ppszRepos); + TDNF_SAFE_FREE_MEMORY(pszMetadataExpire); return dwError; error: diff --git a/client/utils.c b/client/utils.c index 17b2aaa7..895fc6b1 100644 --- a/client/utils.c +++ b/client/utils.c @@ -306,6 +306,64 @@ TDNFIsDir( goto cleanup; } +//update time if file exists +//create if not +uint32_t +TDNFTouchFile( + const char* pszFile + ) +{ + uint32_t dwError = 0; + struct stat st = {0}; + int fd = -1; + struct utimbuf times = {0}; + + if(IsNullOrEmptyString(pszFile)) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + if(stat(pszFile, &st) == -1) + { + if(errno == ENOENT) + { + fd = creat(pszFile, + S_IRUSR | S_IRGRP | S_IROTH); + if(fd == -1) + { + dwError = errno; + BAIL_ON_TDNF_SYSTEM_ERROR(dwError); + } + else + { + close(fd); + } + } + else + { + dwError = errno; + BAIL_ON_TDNF_SYSTEM_ERROR(dwError); + } + } + else + { + times.actime = st.st_atime; + times.modtime = time(NULL); + if(utime(pszFile, ×)) + { + dwError = errno; + BAIL_ON_TDNF_SYSTEM_ERROR(dwError); + } + } + +cleanup: + return dwError; + +error: + goto cleanup; +} + //get package version using rpmlib uint32_t TDNFRawGetPackageVersion( @@ -435,3 +493,131 @@ TDNFGetKernelArch( TDNF_SAFE_FREE_MEMORY(pszArch); goto cleanup; } + +uint32_t +TDNFParseMetadataExpire( + const char* pszMetadataExpire, + long* plMetadataExpire + ) +{ + uint32_t dwError = 0; + long lMetadataExpire = -1; + char* pszError = NULL; + + if(!lMetadataExpire || IsNullOrEmptyString(pszMetadataExpire)) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + if(!strcasecmp(TDNF_REPO_METADATA_EXPIRE_NEVER, pszMetadataExpire)) + { + lMetadataExpire = -1; + } + else + { + lMetadataExpire = strtol(pszMetadataExpire, &pszError, 10); + if(lMetadataExpire < 0) + { + lMetadataExpire = -1; + } + else if(lMetadataExpire > 0) + { + char chMultiplier = 's'; + int nMultiplier = 1; + if(pszError && *pszError) + { + chMultiplier = *pszError; + } + switch(chMultiplier) + { + case 's': nMultiplier = 1; break; + case 'm': nMultiplier = 60; break; + case 'h': nMultiplier = 60*60; break; + case 'd': nMultiplier = 60*60*24; break; + default: + dwError = ERROR_TDNF_METADATA_EXPIRE_PARSE; + BAIL_ON_TDNF_ERROR(dwError); + } + lMetadataExpire *= nMultiplier; + } + else if(pszError && *pszError) + { + dwError = ERROR_TDNF_METADATA_EXPIRE_PARSE; + BAIL_ON_TDNF_ERROR(dwError); + } + } + + *plMetadataExpire = lMetadataExpire; + +cleanup: + return dwError; + +error: + if(plMetadataExpire) + { + *plMetadataExpire = 0; + } + goto cleanup; +} + +uint32_t +TDNFShouldSyncMetadata( + const char* pszRepoDataFolder, + long lMetadataExpire, + int* pnShouldSync + ) +{ + uint32_t dwError = 0; + int nShouldSync = 0; + struct stat st = {0}; + time_t tCurrent = time(NULL); + char* pszMarkerFile = NULL; + + if(!pnShouldSync || IsNullOrEmptyString(pszRepoDataFolder)) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; + BAIL_ON_TDNF_ERROR(dwError); + } + + dwError = TDNFAllocateStringPrintf( + &pszMarkerFile, + "%s/%s", + pszRepoDataFolder, + TDNF_REPO_METADATA_MARKER); + BAIL_ON_TDNF_ERROR(dwError); + + //Look for the metadata marker file + if(stat(pszMarkerFile, &st) == -1) + { + if(errno == ENOENT) + { + nShouldSync = 1; + } + else + { + dwError = errno; + BAIL_ON_TDNF_SYSTEM_ERROR(dwError); + } + } + else + { + if(difftime(tCurrent, st.st_ctime) > lMetadataExpire) + { + nShouldSync = 1; + } + } + + *pnShouldSync = nShouldSync; + +cleanup: + TDNF_SAFE_FREE_MEMORY(pszMarkerFile); + return dwError; + +error: + if(pnShouldSync) + { + *pnShouldSync = 0; + } + goto cleanup; +} diff --git a/include/tdnferror.h b/include/tdnferror.h index 41aa2975..1388827e 100644 --- a/include/tdnferror.h +++ b/include/tdnferror.h @@ -65,6 +65,8 @@ extern "C" { #define ERROR_TDNF_ALREADY_INSTALLED 1026 #define ERROR_TDNF_NO_UPGRADE_PATH 1027 #define ERROR_TDNF_NO_DOWNGRADE_PATH 1028 +// +#define ERROR_TDNF_METADATA_EXPIRE_PARSE 1029 //Hawkey errors 1300 to 1399 #define ERROR_TDNF_HAWKEY_BASE 1300 diff --git a/include/tdnftypes.h b/include/tdnftypes.h index 94c7b5a4..4f89a1da 100644 --- a/include/tdnftypes.h +++ b/include/tdnftypes.h @@ -245,6 +245,7 @@ typedef struct _TDNF_REPO_DATA int nEnabled; int nSkipIfUnavailable; int nGPGCheck; + long lMetadataExpire; char* pszId; char* pszName; char* pszBaseUrl;