[pacman-dev] [PATCH] Add a fetch callback to allow front-end download support

Dario Freddi drf54321 at gmail.com
Fri Feb 20 09:34:25 EST 2009


Hello,

I'm all in for this, thanks Sebastian. This would allow to remove a _HUGE_ 
quantity of unneeded callbacks in alpm, and from my point of view, it is 
really great (especially for Shaman/Aqpm). For what it's worth, I'd like to 
add my vote here and I really hope this patch will be in trunk soon, so that I 
can switch Shaman's frontend to this new system.

Dario

In data venerdì 20 febbraio 2009 08:31:07, Sebastian Nowicki ha scritto:
: > 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 if it is defined, otherwise the internal method
> (libfetch) is used, if available.
>
> The external download method was moved to pacman and is set as the fetch
> callback, if the command is defined in the configuration file. As a result,
> alpm_option_get_xfercommand() and alpm_option_set_xfercommand() have been
> removed.
>
> Signed-off-by: Sebastian Nowicki <sebnow at gmail.com>
> ---
>  lib/libalpm/alpm.h   |   17 +++++++-
>  lib/libalpm/dload.c  |  101
> +------------------------------------------------ lib/libalpm/handle.c |  
> 33 +++++++++-------
>  lib/libalpm/handle.h |    2 +-
>  src/pacman/conf.h    |    6 +++
>  src/pacman/pacman.c  |   96
> +++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 137
> insertions(+), 118 deletions(-)
>
> diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
> index 7b7ca4e..f444e93 100644
> --- a/lib/libalpm/alpm.h
> +++ b/lib/libalpm/alpm.h
> @@ -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);
> diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
> index 5b0a691..bc36706 100644
> --- a/lib/libalpm/dload.c
> +++ b/lib/libalpm/dload.c
> @@ -262,111 +262,16 @@ 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);
> +		handle->fetchcb = download_internal;
>  #else
>  		RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1);
>  #endif
> -	} else {
> -		ret = download_external(url, localpath, mtimeold, mtimenew);
>  	}
> -	return(ret);
> +	return handle->fetchcb(url, localpath, mtimeold, mtimeold);
>  }
>
>  /*
> diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
> index 813f439..3566889 100644
> --- a/lib/libalpm/handle.c
> +++ b/lib/libalpm/handle.c
> @@ -105,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) {
> @@ -204,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) {
> @@ -258,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) {
> @@ -519,12 +528,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;
> diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
> index ad7666d..89fc457 100644
> --- a/lib/libalpm/handle.h
> +++ b/lib/libalpm/handle.h
> @@ -40,6 +40,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 '/' */
> @@ -57,7 +58,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;
>
> diff --git a/src/pacman/conf.h b/src/pacman/conf.h
> index 466d983..650b0f9 100644
> --- a/src/pacman/conf.h
> +++ b/src/pacman/conf.h
> @@ -20,6 +20,11 @@
>  #define _PM_CONF_H
>
>  #include <alpm.h>
> +#if defined(HAVE_SYS_SYSLIMITS_H)
> +#include <sys/syslimits.h> /* PATH_MAX */
> +#else
> +#define PATH_MAX 1024
> +#endif
>
>  typedef struct __config_t {
>  	unsigned short op;
> @@ -71,6 +76,7 @@ typedef struct __config_t {
>  	unsigned short cleanmethod; /* select -Sc behavior */
>  	alpm_list_t *holdpkg;
>  	alpm_list_t *syncfirst;
> +	char xfercommand[PATH_MAX]; /* external download command */
>  } config_t;
>
>  /* Operations */
> diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
> index 59916d6..0d6c897 100644
> --- a/src/pacman/pacman.c
> +++ b/src/pacman/pacman.c
> @@ -573,6 +573,99 @@ static void setrepeatingoption(const char *ptr, const
> char *option, pm_printf(PM_LOG_DEBUG, "config: %s: %s\n", option, p);
>  }
>
> +/** External fetch callback */
> +static 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;
> +	int len;
> +
> +	if(!config->xfercommand) {
> +		return -1;
> +	}
> +
> +	filename = strrchr(url, '/');
> +	if(!filename) {
> +		return -1;
> +	} else {
> +		filename++; /* omit leading slash */
> +	}
> +
> +	len = strlen(localpath) + strlen(filename) + 1;
> +	destfile = calloc(len, sizeof(*destfile));
> +	snprintf(destfile, len, "%s%s", localpath, filename);
> +
> +	len += 5; /* ".part" */
> +	tempfile = calloc(len, sizeof(*tempfile));
> +	snprintf(tempfile, len, "%s.part", destfile);
> +
> +	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_DEBUG, "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_DEBUG, "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, @@ -736,7 +829,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);
> +						strncpy(config->xfercommand, ptr, sizeof(config->xfercommand));
> +						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) {

-- 
-------------------

Dario Freddi
KDE Developer
GPG Key Signature: 511A9A3B

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: This is a digitally signed message part.
URL: <http://www.archlinux.org/pipermail/pacman-dev/attachments/20090220/275d61ab/attachment-0001.pgp>


More information about the pacman-dev mailing list