mirror of
https://github.com/moparisthebest/pacman
synced 2024-12-22 15:58:50 -05:00
Add a fetch callback to allow front-end download support
This allows a frontend to define its own download algorithm so that the libfetch dependency can be omitted without using an external process. The callback will be used when if it is defined, otherwise the old behavior applies. Signed-off-by: Sebastian Nowicki <sebnow@gmail.com> [Dan: minor cleanups] Signed-off-by: Dan McGee <dan@archlinux.org>
This commit is contained in:
parent
1d19f0896c
commit
30c4d53ce5
@ -83,6 +83,17 @@ int alpm_logaction(char *fmt, ...);
|
||||
typedef void (*alpm_cb_download)(const char *filename,
|
||||
off_t xfered, off_t total);
|
||||
typedef void (*alpm_cb_totaldl)(off_t total);
|
||||
/** A callback for downloading files
|
||||
* @param url the URL of the file to be downloaded
|
||||
* @param localpath the directory to which the file should be downloaded
|
||||
* @param mtimeold the modification time of the file previously downloaded
|
||||
* @param mtimenew the modification time of the newly downloaded file.
|
||||
* This should be set by the callback.
|
||||
* @return 0 on success, 1 if the modification times are identical, -1 on
|
||||
* error.
|
||||
*/
|
||||
typedef int (*alpm_cb_fetch)(const char *url, const char *localpath,
|
||||
time_t mtimeold, time_t *mtimenew);
|
||||
|
||||
/*
|
||||
* Options
|
||||
@ -94,6 +105,9 @@ void alpm_option_set_logcb(alpm_cb_log cb);
|
||||
alpm_cb_download alpm_option_get_dlcb();
|
||||
void alpm_option_set_dlcb(alpm_cb_download cb);
|
||||
|
||||
alpm_cb_fetch alpm_option_get_fetchcb();
|
||||
void alpm_option_set_fetchcb(alpm_cb_fetch cb);
|
||||
|
||||
alpm_cb_totaldl alpm_option_get_totaldlcb();
|
||||
void alpm_option_set_totaldlcb(alpm_cb_totaldl cb);
|
||||
|
||||
@ -137,9 +151,6 @@ void alpm_option_add_ignoregrp(const char *grp);
|
||||
void alpm_option_set_ignoregrps(alpm_list_t *ignoregrps);
|
||||
int alpm_option_remove_ignoregrp(const char *grp);
|
||||
|
||||
const char *alpm_option_get_xfercommand();
|
||||
void alpm_option_set_xfercommand(const char *cmd);
|
||||
|
||||
unsigned short alpm_option_get_nopassiveftp();
|
||||
void alpm_option_set_nopassiveftp(unsigned short nopasv);
|
||||
void alpm_option_set_usedelta(unsigned short usedelta);
|
||||
|
@ -263,111 +263,21 @@ cleanup:
|
||||
}
|
||||
#endif
|
||||
|
||||
static int download_external(const char *url, const char *localpath,
|
||||
time_t mtimeold, time_t *mtimenew) {
|
||||
int ret = 0;
|
||||
int retval;
|
||||
int usepart = 0;
|
||||
char *ptr1, *ptr2;
|
||||
char origCmd[PATH_MAX];
|
||||
char parsedCmd[PATH_MAX] = "";
|
||||
char cwd[PATH_MAX];
|
||||
char *destfile, *tempfile, *filename;
|
||||
|
||||
if(!handle->xfercommand) {
|
||||
RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
}
|
||||
|
||||
filename = get_filename(url);
|
||||
if(!filename) {
|
||||
RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
}
|
||||
destfile = get_destfile(localpath, filename);
|
||||
tempfile = get_tempfile(localpath, filename);
|
||||
|
||||
/* replace all occurrences of %o with fn.part */
|
||||
strncpy(origCmd, handle->xfercommand, sizeof(origCmd));
|
||||
ptr1 = origCmd;
|
||||
while((ptr2 = strstr(ptr1, "%o"))) {
|
||||
usepart = 1;
|
||||
ptr2[0] = '\0';
|
||||
strcat(parsedCmd, ptr1);
|
||||
strcat(parsedCmd, tempfile);
|
||||
ptr1 = ptr2 + 2;
|
||||
}
|
||||
strcat(parsedCmd, ptr1);
|
||||
/* replace all occurrences of %u with the download URL */
|
||||
strncpy(origCmd, parsedCmd, sizeof(origCmd));
|
||||
parsedCmd[0] = '\0';
|
||||
ptr1 = origCmd;
|
||||
while((ptr2 = strstr(ptr1, "%u"))) {
|
||||
ptr2[0] = '\0';
|
||||
strcat(parsedCmd, ptr1);
|
||||
strcat(parsedCmd, url);
|
||||
ptr1 = ptr2 + 2;
|
||||
}
|
||||
strcat(parsedCmd, ptr1);
|
||||
/* cwd to the download directory */
|
||||
getcwd(cwd, PATH_MAX);
|
||||
if(chdir(localpath)) {
|
||||
_alpm_log(PM_LOG_WARNING, _("could not chdir to %s\n"), localpath);
|
||||
pm_errno = PM_ERR_EXTERNAL_DOWNLOAD;
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
/* execute the parsed command via /bin/sh -c */
|
||||
_alpm_log(PM_LOG_DEBUG, "running command: %s\n", parsedCmd);
|
||||
retval = system(parsedCmd);
|
||||
|
||||
if(retval == -1) {
|
||||
_alpm_log(PM_LOG_WARNING, _("running XferCommand: fork failed!\n"));
|
||||
pm_errno = PM_ERR_EXTERNAL_DOWNLOAD;
|
||||
ret = -1;
|
||||
} else if(retval != 0) {
|
||||
/* download failed */
|
||||
_alpm_log(PM_LOG_DEBUG, "XferCommand command returned non-zero status "
|
||||
"code (%d)\n", retval);
|
||||
ret = -1;
|
||||
} else {
|
||||
/* download was successful */
|
||||
if(usepart) {
|
||||
rename(tempfile, destfile);
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
chdir(cwd);
|
||||
if(ret == -1) {
|
||||
/* hack to let an user the time to cancel a download */
|
||||
sleep(2);
|
||||
}
|
||||
FREE(destfile);
|
||||
FREE(tempfile);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int download(const char *url, const char *localpath,
|
||||
time_t mtimeold, time_t *mtimenew) {
|
||||
int ret;
|
||||
|
||||
/* We have a few things to take into account here.
|
||||
* 1. If we have both internal/external available, choose based on
|
||||
* whether xfercommand is populated.
|
||||
* 2. If we only have external available, we should first check
|
||||
* if a command was provided before we drop into download_external.
|
||||
*/
|
||||
if(handle->xfercommand == NULL) {
|
||||
if(handle->fetchcb == NULL) {
|
||||
#if defined(INTERNAL_DOWNLOAD)
|
||||
ret = download_internal(url, localpath, mtimeold, mtimenew);
|
||||
return(download_internal(url, localpath, mtimeold, mtimenew));
|
||||
#else
|
||||
RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
#endif
|
||||
} else {
|
||||
ret = download_external(url, localpath, mtimeold, mtimenew);
|
||||
int ret = handle->fetchcb(url, localpath, mtimeold, mtimenew);
|
||||
if(ret == -1) {
|
||||
RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -79,7 +79,6 @@ void _alpm_handle_free(pmhandle_t *handle)
|
||||
FREELIST(handle->cachedirs);
|
||||
FREE(handle->logfile);
|
||||
FREE(handle->lockfile);
|
||||
FREE(handle->xfercommand);
|
||||
FREELIST(handle->dbs_sync);
|
||||
FREELIST(handle->noupgrade);
|
||||
FREELIST(handle->noextract);
|
||||
@ -106,6 +105,15 @@ alpm_cb_download SYMEXPORT alpm_option_get_dlcb()
|
||||
return handle->dlcb;
|
||||
}
|
||||
|
||||
alpm_cb_fetch SYMEXPORT alpm_option_get_fetchcb()
|
||||
{
|
||||
if (handle == NULL) {
|
||||
pm_errno = PM_ERR_HANDLE_NULL;
|
||||
return NULL;
|
||||
}
|
||||
return handle->fetchcb;
|
||||
}
|
||||
|
||||
alpm_cb_totaldl SYMEXPORT alpm_option_get_totaldlcb()
|
||||
{
|
||||
if (handle == NULL) {
|
||||
@ -205,15 +213,6 @@ alpm_list_t SYMEXPORT *alpm_option_get_ignoregrps()
|
||||
return handle->ignoregrp;
|
||||
}
|
||||
|
||||
const char SYMEXPORT *alpm_option_get_xfercommand()
|
||||
{
|
||||
if (handle == NULL) {
|
||||
pm_errno = PM_ERR_HANDLE_NULL;
|
||||
return NULL;
|
||||
}
|
||||
return handle->xfercommand;
|
||||
}
|
||||
|
||||
unsigned short SYMEXPORT alpm_option_get_nopassiveftp()
|
||||
{
|
||||
if (handle == NULL) {
|
||||
@ -259,6 +258,15 @@ void SYMEXPORT alpm_option_set_dlcb(alpm_cb_download cb)
|
||||
handle->dlcb = cb;
|
||||
}
|
||||
|
||||
void SYMEXPORT alpm_option_set_fetchcb(alpm_cb_fetch cb)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
pm_errno = PM_ERR_HANDLE_NULL;
|
||||
return;
|
||||
}
|
||||
handle->fetchcb = cb;
|
||||
}
|
||||
|
||||
void SYMEXPORT alpm_option_set_totaldlcb(alpm_cb_totaldl cb)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
@ -521,12 +529,6 @@ int SYMEXPORT alpm_option_remove_ignoregrp(const char *grp)
|
||||
return(0);
|
||||
}
|
||||
|
||||
void SYMEXPORT alpm_option_set_xfercommand(const char *cmd)
|
||||
{
|
||||
if(handle->xfercommand) FREE(handle->xfercommand);
|
||||
if(cmd) handle->xfercommand = strdup(cmd);
|
||||
}
|
||||
|
||||
void SYMEXPORT alpm_option_set_nopassiveftp(unsigned short nopasv)
|
||||
{
|
||||
handle->nopassiveftp = nopasv;
|
||||
|
@ -41,6 +41,7 @@ typedef struct _pmhandle_t {
|
||||
alpm_cb_log logcb; /* Log callback function */
|
||||
alpm_cb_download dlcb; /* Download callback function */
|
||||
alpm_cb_totaldl totaldlcb; /* Total download callback function */
|
||||
alpm_cb_fetch fetchcb; /* Download file callback function */
|
||||
|
||||
/* filesystem paths */
|
||||
char *root; /* Root path, default '/' */
|
||||
@ -58,7 +59,6 @@ typedef struct _pmhandle_t {
|
||||
/* options */
|
||||
unsigned short usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */
|
||||
unsigned short nopassiveftp; /* Don't use PASV ftp connections */
|
||||
char *xfercommand; /* External download command */
|
||||
unsigned short usedelta; /* Download deltas if possible */
|
||||
} pmhandle_t;
|
||||
|
||||
|
@ -61,6 +61,7 @@ int config_free(config_t *oldconfig)
|
||||
free(oldconfig->rootdir);
|
||||
free(oldconfig->dbpath);
|
||||
free(oldconfig->logfile);
|
||||
free(oldconfig->xfercommand);
|
||||
free(oldconfig);
|
||||
oldconfig = NULL;
|
||||
|
||||
|
@ -73,6 +73,7 @@ typedef struct __config_t {
|
||||
unsigned short cleanmethod; /* select -Sc behavior */
|
||||
alpm_list_t *holdpkg;
|
||||
alpm_list_t *syncfirst;
|
||||
char *xfercommand;
|
||||
} config_t;
|
||||
|
||||
/* Operations */
|
||||
|
@ -581,6 +581,118 @@ static void setrepeatingoption(const char *ptr, const char *option,
|
||||
pm_printf(PM_LOG_DEBUG, "config: %s: %s\n", option, p);
|
||||
}
|
||||
|
||||
static char *get_filename(const char *url) {
|
||||
char *filename = strrchr(url, '/');
|
||||
if(filename != NULL) {
|
||||
filename++;
|
||||
}
|
||||
return(filename);
|
||||
}
|
||||
|
||||
static char *get_destfile(const char *path, const char *filename) {
|
||||
char *destfile;
|
||||
/* len = localpath len + filename len + null */
|
||||
int len = strlen(path) + strlen(filename) + 1;
|
||||
destfile = calloc(len, sizeof(char));
|
||||
snprintf(destfile, len, "%s%s", path, filename);
|
||||
|
||||
return(destfile);
|
||||
}
|
||||
|
||||
static char *get_tempfile(const char *path, const char *filename) {
|
||||
char *tempfile;
|
||||
/* len = localpath len + filename len + '.part' len + null */
|
||||
int len = strlen(path) + strlen(filename) + 6;
|
||||
tempfile = calloc(len, sizeof(char));
|
||||
snprintf(tempfile, len, "%s%s.part", path, filename);
|
||||
|
||||
return(tempfile);
|
||||
}
|
||||
|
||||
/** External fetch callback */
|
||||
int download_with_xfercommand(const char *url, const char *localpath,
|
||||
time_t mtimeold, time_t *mtimenew) {
|
||||
int ret = 0;
|
||||
int retval;
|
||||
int usepart = 0;
|
||||
char *ptr1, *ptr2;
|
||||
char origCmd[PATH_MAX];
|
||||
char parsedCmd[PATH_MAX] = "";
|
||||
char cwd[PATH_MAX];
|
||||
char *destfile, *tempfile, *filename;
|
||||
|
||||
if(!config->xfercommand) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
filename = get_filename(url);
|
||||
if(!filename) {
|
||||
return -1;
|
||||
}
|
||||
destfile = get_destfile(localpath, filename);
|
||||
tempfile = get_tempfile(localpath, filename);
|
||||
|
||||
strncpy(origCmd, config->xfercommand, sizeof(origCmd));
|
||||
/* replace all occurrences of %o with fn.part */
|
||||
ptr1 = origCmd;
|
||||
while((ptr2 = strstr(ptr1, "%o"))) {
|
||||
usepart = 1;
|
||||
ptr2[0] = '\0';
|
||||
strcat(parsedCmd, ptr1);
|
||||
strcat(parsedCmd, tempfile);
|
||||
ptr1 = ptr2 + 2;
|
||||
}
|
||||
strcat(parsedCmd, ptr1);
|
||||
/* replace all occurrences of %u with the download URL */
|
||||
strncpy(origCmd, parsedCmd, sizeof(origCmd));
|
||||
parsedCmd[0] = '\0';
|
||||
ptr1 = origCmd;
|
||||
while((ptr2 = strstr(ptr1, "%u"))) {
|
||||
ptr2[0] = '\0';
|
||||
strcat(parsedCmd, ptr1);
|
||||
strcat(parsedCmd, url);
|
||||
ptr1 = ptr2 + 2;
|
||||
}
|
||||
strcat(parsedCmd, ptr1);
|
||||
/* cwd to the download directory */
|
||||
getcwd(cwd, PATH_MAX);
|
||||
if(chdir(localpath)) {
|
||||
pm_printf(PM_LOG_WARNING, "could not chdir to %s\n", localpath);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
/* execute the parsed command via /bin/sh -c */
|
||||
pm_printf(PM_LOG_DEBUG, "running command: %s\n", parsedCmd);
|
||||
retval = system(parsedCmd);
|
||||
|
||||
if(retval == -1) {
|
||||
pm_printf(PM_LOG_WARNING, "running XferCommand: fork failed!\n");
|
||||
ret = -1;
|
||||
} else if(retval != 0) {
|
||||
/* download failed */
|
||||
pm_printf(PM_LOG_DEBUG, "XferCommand command returned non-zero status "
|
||||
"code (%d)\n", retval);
|
||||
ret = -1;
|
||||
} else {
|
||||
/* download was successful */
|
||||
if(usepart) {
|
||||
rename(tempfile, destfile);
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
chdir(cwd);
|
||||
if(ret == -1) {
|
||||
/* hack to let an user the time to cancel a download */
|
||||
sleep(2);
|
||||
}
|
||||
free(destfile);
|
||||
free(tempfile);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/* The real parseconfig. Called with a null section argument by the publicly
|
||||
* visible parseconfig so we can recall from within ourself on an include */
|
||||
static int _parseconfig(const char *file, const char *givensection,
|
||||
@ -744,7 +856,8 @@ static int _parseconfig(const char *file, const char *givensection,
|
||||
pm_printf(PM_LOG_DEBUG, "config: logfile: %s\n", ptr);
|
||||
}
|
||||
} else if (strcmp(key, "XferCommand") == 0) {
|
||||
alpm_option_set_xfercommand(ptr);
|
||||
config->xfercommand = strdup(ptr);
|
||||
alpm_option_set_fetchcb(download_with_xfercommand);
|
||||
pm_printf(PM_LOG_DEBUG, "config: xfercommand: %s\n", ptr);
|
||||
} else if (strcmp(key, "CleanMethod") == 0) {
|
||||
if (strcmp(ptr, "KeepInstalled") == 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user