Compare commits

..

18 Commits

Author SHA1 Message Date
morganamilo
202476b747 pacman: refactor transaction code to one path
This refactors the -S, -U and -R code to share the same implementation.

The goal here is to firstly simplify all transactoions into one code path,
then eventually allow pacman to perform multiple kinds of transactions in
one command.
2025-08-09 02:39:11 +01:00
morganamilo
5ccae096bb libalpm: edit docs for check_pgp_signature
The old wording implied that the function would return -1 if the
signature was invalid (alpm_sigresult_t->validity). When really it means
if a correctly formatted signature exists at all.
2025-08-05 15:57:06 +10:00
Allan McRae
69cc822e4b pacman: print error for -Sg with an unknown group
Fixes #174

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:53:45 +10:00
Allan McRae
56a325a2be libmakepkg: add linting of BUILDENV and OPTIONS from makepkg.conf
Currently the options array from a PKGBUILD is linted, but the global
BUILDENV and OPTIONS are not.

Fixes #233.

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:50:28 +10:00
Allan McRae
e1b05eaeec doc/repo-add: metion '--prevent-downgrade' option
Fixes #249.

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:47:13 +10:00
Allan McRae
be9fd0cc32 repo-add: add '--include-sigs' to the help message
Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:47:13 +10:00
Allan McRae
e1507ad768 Remove initialisation of unions
As of gcc-15, unions are no longer zero initialised when "{0}" is used.
To revert to the old behaviour, either assumes building for C23 or
adding a compiler option that may or may not work with other compilers.

Remove current "initialization" to make it clear that full initialization
is not occuring. All relevant fields are currently initialized before
use.

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:45:20 +10:00
Allan McRae
5f70cbd578 Adjust PATH_MAX length tests using snprintf
The return value for snprintf does not include the null delimiter.
So tests for path length should use use '>= PATH_MAX'.

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:43:52 +10:00
Allan McRae
c4445d097b Set default pacman SigLevel as "Required"
We should set the secure option as the default and require a user or
distribution to explicitly reduce the level of checking required in
their configuration file.

Implements #260

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-05 15:42:49 +10:00
Allan McRae
fe6e678e59 libmakepkg: remove pkglist linting
The ability to build only selected packages from a split package was
removed from makepkg, so this lint is no longer needed.

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-08-02 14:26:44 +10:00
morganamilo
48a784dde8 libalpm: fix -U when pkg exists but not sig 2025-06-21 23:39:57 +10:00
morganamilo
67e3a7be36 libalpm: fix regression in downloader
Fixes failure to finalize download path if the package file already
exists but the .sig file does not.

This patch also moves .sig.part files which should be done for
completeness although it's probably rare/inconsequential for them to
exist.

Hopefully this is now the right approach now. The logic is as follows:

  Check if dest_name or temp_name exists and try to move whichever
  does.

  If neither exist assume we're just downloading sig files and don't
  error.

  Figure out the .sig base filename.

  Try to move the .sig file if one was needed and if that fails try
  move the .sig.part file.

The patch leaves the logging as is. Maybe we should check if moves fail
for reasons other than non existence and log it properly. Though this is
probably rare and pacman will error out later anyway.

Fixes #256
2025-05-30 14:48:14 +01:00
morganamilo
45c4eef61d libalpm: move tempdir cleanup into _alpm_download
this ensures the dir still gets cleaned up even if killed by a signal
2025-05-26 16:13:06 +10:00
morganamilo
5c451cd976 libalpm: propagate signal from child to parent
If we ^C while downloading the parent should propagate so pacman can
exit as it did before the sandboxing.
2025-05-26 16:13:06 +10:00
morganamilo
d87dd153fc libalpm: finalize tempfile if destfile doesn't exist
On setup we move the termfile into the temp download dir to resume
downloads. We don't move these back losing any tempfiles we already had.
2025-05-26 16:13:06 +10:00
morganamilo
6fcecbd08d libalpm: set errno if child exited via signal 2025-05-26 16:13:06 +10:00
Diego Viola
528709131f Fix whitespace in README
Also ensure consistent style in API section
2025-05-26 16:11:13 +10:00
Dominik
9fca328caf makepkg: provide a field for tracking generic metadata
The xdata field is an array of key=value entries that allow a packager to
include arbitrary metadata in the generated .PKGINFO.

Fixes #241.

Signed-off-by: Dominik Peteler <archlinux+gitlab@with-h.at>
2025-05-26 16:09:02 +10:00
31 changed files with 909 additions and 786 deletions

218
README
View File

@@ -6,7 +6,7 @@ Package Management) library. This document, while not exhaustive, also
indicates some limitations (on purpose, or sometimes due to its poor design) of
the library at the present time.
There is one special file,"alpm.h", which is the public interface that
There is one special file, "alpm.h", which is the public interface that
should be distributed and installed on systems with the library. Only
structures, data and functions declared within this file are made available to
the frontend. Lots of structures are of an opaque type and their fields are
@@ -239,7 +239,7 @@ API CHANGES BETWEEN 3.1 AND 3.2
- alpm_checkdbconflicts()
- alpm_sync_newversion()
- alpm_deptest()
- error codes :
- error codes:
PM_ERR_DLT_INVALID, PM_ERR_LIBARCHIVE, PM_ERR_LIBDOWNLOAD and
PM_ERR_EXTERNAL_DOWNLOAD
- flags:
@@ -362,11 +362,11 @@ API CHANGES BETWEEN 3.4 AND 3.5
- alpm_find_grp_pkgs()
- alpm_trans_get_flags()
- error codes:
PM_ERR_DISK_SPACE, PM_ERR_WRITE
- flags
PM_TRANS_FLAG_NODEPVERSION, PM_TRANS_EVT_DISKSPACE_START,
PM_TRANS_EVT_DISKSPACE_DONE, PM_TRANS_CONV_SELECT_PROVIDER,
PM_TRANS_PROGRESS_DISKSPACE_START, PM_TRANS_PROGRESS_INTEGRITY_START
PM_ERR_DISK_SPACE, PM_ERR_WRITE
- flags:
PM_TRANS_FLAG_NODEPVERSION, PM_TRANS_EVT_DISKSPACE_START,
PM_TRANS_EVT_DISKSPACE_DONE, PM_TRANS_CONV_SELECT_PROVIDER,
PM_TRANS_PROGRESS_DISKSPACE_START, PM_TRANS_PROGRESS_INTEGRITY_START
API CHANGES BETWEEN 3.5 AND 4.0
@@ -419,7 +419,7 @@ API CHANGES BETWEEN 3.5 AND 4.0
- alpm_release
- alpm_remove_pkg
- alpm_sync_sysupgrade
- several structs are no longer opaque
- several structs are no longer opaque:
- alpm_conflict_t
- alpm_delta_t
- alpm_depend_t
@@ -431,19 +431,33 @@ API CHANGES BETWEEN 3.5 AND 4.0
[ADDED]
- option functions:
alpm_{get,set}_eventcb(), alpm_option_{get,set}_convcb(),
alpm_option_{get,set}_progresscb()
- alpm_{get,set}_eventcb()
- alpm_option_{get,set}_convcb()
- alpm_option_{get,set}_progresscb()
- package signing functions:
alpm_option_get_default_siglevel(), alpm_option_set_default_siglevel(),
alpm_option_get_gpgdir(), alpm_option_set_gpgdir(), alpm_db_get_siglevel(),
alpm_siglist_cleanup(), alpm_db_check_pgp_signature(), alpm_pkg_check_pgp_signature(),
alpm_pkg_get_origin(), alpm_pkg_get_sha256sum(), alpm_pkg_get_base64_sig()
- alpm_option_get_default_siglevel()
- alpm_option_set_default_siglevel()
- alpm_option_get_gpgdir()
- alpm_option_set_gpgdir()
- alpm_db_get_siglevel()
- alpm_siglist_cleanup()
- alpm_db_check_pgp_signature()
- alpm_pkg_check_pgp_signature()
- alpm_pkg_get_origin()
- alpm_pkg_get_sha256sum()
- alpm_pkg_get_base64_sig()
- list functions:
alpm_list_to_array(), alpm_list_previous()
- alpm_list_to_array()
- alpm_list_previous()
- structs:
alpm_backup_t, alpm_file_t, alpm_filelist_t
- alpm_backup_t
- alpm_file_t
- alpm_filelist_t
- enums:
alpm_siglevel_t, alpm_sigstatus_t, alpm_sigvalidity_t, alpm_pkgfrom_t
- alpm_siglevel_t
- alpm_sigstatus_t
- alpm_sigvalidity_t
- alpm_pkgfrom_t
- error codes:
ALPM_ERR_DB_INVALID, ALPM_ERR_DB_INVALID_SIG, ALPM_ERR_GPGME,
ALPM_ERR_PKG_INVALID_CHECKSUM, ALPM_ERR_PKG_INVALID_SIG, ALPM_ERR_SIG_INVALID,
@@ -470,10 +484,10 @@ API CHANGES BETWEEN 4.0 AND 4.1
- alpm_db_unregister_all -> alpm_unregister_all_syncdbs
- alpm_db_readgroup -> alpm_db_get_group
- alpm_db_set_pkgreason -> alpm_pkg_set_reason (handle parameter removed)
- alpm_time_t typedef used for all times
- alpm_time_t typedef used for all times:
- members of alpm_pgpkey_t
- return types of alpm_pkg_get_builddate and alpm_pkg_get_installdate
- delta options now use required ratio rather than on/off
- delta options now use required ratio rather than on/off:
- alpm_option_get_usedelta -> alpm_option_get_deltaratio
- alpm_option_set_usedelta -> alpm_option_set_deltaratio
@@ -490,21 +504,21 @@ API CHANGES BETWEEN 4.0 AND 4.1
- alpm_db_usage_t
- alpm_db_set_usage()
- alpm_db_get_usage()
- wrapper functions for reading mtree files
- wrapper functions for reading mtree files:
- alpm_pkg_mtree_open()
- alpm_pkg_mtree_next()
- alpm_pkg_mtree_close()
- utility functions
- utility functions:
- alpm_pkg_find()
- alpm_pkg_compute_optionalfor()
- alpm_filelist_contains()
- types
- types:
- alpm_time_t
- alpm_errno_t
- flags
ALPM_EVENT_OPTDEP_REQUIRED, ALPM_EVENT_DATABASE_MISSING,
ALPM_EVENT_KEYRING_START, ALPM_EVENT_KEYRING_DONE, ALPM_EVENT_KEY_DOWNLOAD_START,
ALPM_EVENT_KEY_DOWNLOAD_DONE, ALPM_PROGRESS_KEYRING_START
- flags:
ALPM_EVENT_OPTDEP_REQUIRED, ALPM_EVENT_DATABASE_MISSING,
ALPM_EVENT_KEYRING_START, ALPM_EVENT_KEYRING_DONE, ALPM_EVENT_KEY_DOWNLOAD_START,
ALPM_EVENT_KEY_DOWNLOAD_DONE, ALPM_PROGRESS_KEYRING_START
API CHANGES BETWEEN 4.1 AND 4.2
@@ -513,49 +527,49 @@ API CHANGES BETWEEN 4.1 AND 4.2
[CHANGED]
- alpm_filelist_t - removed member resolved_path
- alpm_filelist_contains - now returns alpm_file_t
- event callback
- alpm_event_t renamed to alpm_event_type_t
- alpm_event_t union added
- alpm_event_cb now takes only an alpm_event_t parameter
- alpm_event_any_t, alpm_package_operation_t, alpm_event_package_operation_t,
alpm_event_optdep_removal_t, alpm_event_delta_patch_t, alpm_event_scriptlet_info_t,
alpm_event_database_missing_t, alpm_event_pkgdownload_t, alpm_event_pacnew_created_t,
alpm_event_pacsave_created_t, alpm_event_pacorig_created_t added
- ALPM_EVENT_*_START -> ALPM_EVENT_PACKAGE_OPERATION_START
- ALPM_EVENT_*_DONE -> ALPM_EVENT_PACKAGE_OPERATION_DONE
- question callback
- alpm_question_t renamed to alpm_question_type_t
- alpm_question_t union added
- alpm_cb_question now takes only an alpm_question_t parameter
- alpm_question_any_t, alpm_question_install_ignorepkg_t, alpm_question_replace_t
alpm_question_conflict_t, alpm_question_corrupted_t, alpm_question_remove_pkgs_t,
alpm_question_select_provider_t, alpm_question_import_key_t added
- event callback:
- alpm_event_t renamed to alpm_event_type_t
- alpm_event_t union added
- alpm_event_cb now takes only an alpm_event_t parameter
- alpm_event_any_t, alpm_package_operation_t, alpm_event_package_operation_t,
alpm_event_optdep_removal_t, alpm_event_delta_patch_t, alpm_event_scriptlet_info_t,
alpm_event_database_missing_t, alpm_event_pkgdownload_t, alpm_event_pacnew_created_t,
alpm_event_pacsave_created_t, alpm_event_pacorig_created_t added
- ALPM_EVENT_*_START -> ALPM_EVENT_PACKAGE_OPERATION_START
- ALPM_EVENT_*_DONE -> ALPM_EVENT_PACKAGE_OPERATION_DONE
- question callback:
- alpm_question_t renamed to alpm_question_type_t
- alpm_question_t union added
- alpm_cb_question now takes only an alpm_question_t parameter
- alpm_question_any_t, alpm_question_install_ignorepkg_t, alpm_question_replace_t
alpm_question_conflict_t, alpm_question_corrupted_t, alpm_question_remove_pkgs_t,
alpm_question_select_provider_t, alpm_question_import_key_t added
[ADDED]
- memory management
- memory management:
- alpm_fileconflict_free()
- alpm_depmissing_free()
- alpm_conflict_free()
- alpm_dep_free()
- database usage
- database usage:
- alpm_db_usage_t
- alpm_db_set_usage()
- alpm_db_get_usage()
- assume installed
- assume installed:
- alpm_option_get_assumeinstalled()
- alpm_option_add_assumeinstalled()
- alpm_option_set_assumeinstalled()
- alpm_option_remove_assumeinstalled()
- using noupgrade/noextract
- using noupgrade/noextract:
- alpm_option_match_noupgrade()
- alpm_option_match_noextract()
- utility functions
- utility functions:
- alpm_dep_from_string()
- alpm_pkg_should_ignore()
- alpm_decode_signature()
- alpm_extract_keyid()
- flags
- ALPM_EVENT_RETRIEVE_DONE, ALPM_EVENT_RETRIEVE_FAILED, ALPM_EVENT_PKGDOWNLOAD_START,
- flags:
ALPM_EVENT_RETRIEVE_DONE, ALPM_EVENT_RETRIEVE_FAILED, ALPM_EVENT_PKGDOWNLOAD_START,
ALPM_EVENT_PKGDOWNLOAD_DONE, ALPM_EVENT_PKGDOWNLOAD_FAILED, ALPM_EVENT_OPTDEP_REMOVAL,
ALPM_EVENT_PACNEW_CREATED, ALPM_EVENT_PACSVAE_CREATED, ALPM_EVENT_PACORIG_CREATED
@@ -565,13 +579,13 @@ API CHANGES BETWEEN 4.2 AND 5.0
[REMOVED]
- alpm_siglevel_t - removed members ALPM_SIG_PACKAGE_SET, ALPM_SIG_PACKAGE_TRUST_SET
- removed .pacorig generation
- removed .pacorig generation:
- ALPM_EVENT_PACORIG_CREATED
- alpm_event_pacorig_created_t
- alpm_event_t.pacorig_created
[ADDED]
- hook support
- hook support:
- alpm_option_get_hookdirs()
- alpm_option_set_hookdirs()
- alpm_option_add_hookdir()
@@ -581,14 +595,14 @@ API CHANGES BETWEEN 4.2 AND 5.0
- ALPM_EVENT_HOOK_START, ALPM_EVENT_HOOK_DONE
- ALPM_EVENT_HOOK_RUN_START, ALPM_EVENT_HOOK_RUN_DONE
- ALPM_ERR_TRANS_HOOK_FAILED
- different database extension support
- different database extension support:
- alpm_option_get_dbext()
- alpm_option_set_dbext()
- pkgbase accessor
- pkgbase accessor:
- alpm_pkg_get_base()
- transaction events
- transaction events:
- ALPM_EVENT_TRANSACTION_START, ALPM_EVENT_TRANSACTION_DONE
- database unlocking
- database unlocking:
- alpm_unlock()
@@ -598,7 +612,7 @@ API CHANGES BETWEEN 5.0 AND 5.1
[CHANGED]
- alpm_errno_t - added member ALPM_ERR_OK
- alpm_siglevel_t - value of ALPM_SIG_USE_DEFAULT changed
- functions using bitfields return/pass an int instead of an enum
- functions using bitfields return/pass an int instead of an enum:
- alpm_option_get_default_siglevel()
- alpm_option_set_default_siglevel()
- alpm_option_get_remote_file_siglevel()
@@ -615,19 +629,19 @@ API CHANGES BETWEEN 5.0 AND 5.1
- alpm_option_set_local_file_siglevel()
[ADDED]
- overwrite support
- overwrite support:
- alpm_option_get_overwrite_files()
- alpm_option_set_overwrite_files()
- alpm_option_add_overwrite_file()
- alpm_option_remove_overwrite_file()
- download timeout control
- download timeout control:
- alpm_option_set_disable_dl_timeout()
- access make/checkdepends info
- access make/checkdepends info:
- alpm_pkg_get_checkdepends()
- alpm_pkg_get_makedepends()
- check pacman capabilities
- check pacman capabilities:
- alpm_capabilities()
- duplicate and add to list
- duplicate and add to list:
- alpm_list_append_strdup()
@@ -635,7 +649,7 @@ API CHANGES BETWEEN 5.1 AND 5.2
===============================
[REMOVED]
- package delta support
- package delta support:
- alpm_delta_t
- alpm_event_delta_patch_t
- alpm_event_t union - removed alpm_event_delta_patch_t
@@ -662,10 +676,10 @@ API CHANGES BETWEEN 5.2 AND 6.0
[REMOVED]
- ALPM_EVENT_PKGDOWNLOAD_START, ALPM_EVENT_PKGDOWNLOAD_DONE, ALPM_EVENT_PKGDOWNLOAD_FAILED
- ALPM_ERR_PKG_REPO_NOT_FOUND
- old TotalDownload implementation
- alpm_cb_totaldl
- alpm_option_get_totaldlcb()
- alpm_option_set_totaldlcb()
- old TotalDownload implementation:
- alpm_cb_totaldl
- alpm_option_get_totaldlcb()
- alpm_option_set_totaldlcb()
[CHANGED]
- alpm_db_update() now accepts a list of databases rather than a single database.
@@ -673,29 +687,29 @@ API CHANGES BETWEEN 5.2 AND 6.0
- alpm_db_search() now has an additional parameter and returns success status
- ALPM_EVENT_RETRIEVE_* -> ALPM_EVENT_DB_RETRIEVE_* and ALPM_EVENT_PKG_RETRIEVE_*
- alpm_cb_download pass event and data
- multi architecture support
- alpm_option_get_arch() -> alpm_option_get_architectures()
- alpm_option_set_arch() -> alpm_option_set_architectures()
- multi architecture support:
- alpm_option_get_arch() -> alpm_option_get_architectures()
- alpm_option_set_arch() -> alpm_option_set_architectures()
- alpm_db_get_servers() copies parameter data
[ADDED]
- parallel download support
- alpm_option_set_parallel_downloads()
- alpm_option_get_parallel_downloads()
- file download events
- alpm_download_event_type_t
- alpm_download_event_init_t
- alpm_download_event_progress_t
- alpm_download_event_completed_t
- download misc
- ALPM_DOWNLOAD_RETRY
- alpm_download_event_retry_t
- alpm_event_pkg_retrieve_t
- multiarchitecture support
- alpm_option_add_architecture()
- alpm_option_remove_architecture()
- misc
- alpm_pkg_get_sig()
- parallel download support:
- alpm_option_set_parallel_downloads()
- alpm_option_get_parallel_downloads()
- file download events:
- alpm_download_event_type_t
- alpm_download_event_init_t
- alpm_download_event_progress_t
- alpm_download_event_completed_t
- download misc:
- ALPM_DOWNLOAD_RETRY
- alpm_download_event_retry_t
- alpm_event_pkg_retrieve_t
- multiarchitecture support:
- alpm_option_add_architecture()
- alpm_option_remove_architecture()
- misc:
- alpm_pkg_get_sig()
- callbacks add front-end provided context
@@ -713,27 +727,27 @@ API CHANGES BETWEEN 6.0 AND 6.1
- alpm_transflag_t - added ALPM_TRANS_FLAG_NOHOOKS
[ADDED]
- extensible package data type
- alpm_pkg_xdata_t
- alpm_pkg_get_xdata()
- accessor functions
- alpm_db_get_handle()
- alpm_pkg_get_handle()
- cache server support
- alpm_db_get_cache_servers()
- alpm_db_set_cache_servers()
- alpm_db_add_cache_server()
- extensible package data type:
- alpm_pkg_xdata_t
- alpm_pkg_get_xdata()
- accessor functions:
- alpm_db_get_handle()
- alpm_pkg_get_handle()
- cache server support:
- alpm_db_get_cache_servers()
- alpm_db_set_cache_servers()
- alpm_db_add_cache_server()
API CHANGES BETWEEN 6.1 AND 7.0
===============================
[ADDED]
- sandbox functions
- alpm_option_get_sandboxuser()
- alpm_option_set_sandboxuser()
- alpm_option_set_disable_sandbox()
- alpm_sandbox_setup_child()
- sandbox functions:
- alpm_option_get_sandboxuser()
- alpm_option_set_sandboxuser()
- alpm_option_set_disable_sandbox()
- alpm_sandbox_setup_child()
API CHANGES BETWEEN 7.0 AND 7.1

View File

@@ -313,6 +313,15 @@ underscore and the architecture name e.g., 'replaces_x86_64=()'.
Enable building packages using link time optimization. Adds '-flto'
to both CFLAGS and CXXFLAGS.
*xdata (array)*::
This array allows you to add additional metadata to the package.
This data is neither used by pacman nor by makepkg;
It has purely informational purpose, or may be interpreted by third-party tools.
+
All entries in that array must have the form 'key=value', where
'key' is an arbitrary non-empty string and 'value' must not contain an equal sign.
Furthermore, the key ``pkgtype'' is reserved for the makepkg program.
Packaging Functions
-------------------

View File

@@ -318,12 +318,12 @@ When to Check::
*Never*;;
All signature checking is suppressed, even if signatures are present.
*Optional* (default);;
*Optional*;;
Signatures are checked if present; absence of a signature is not an
error. An invalid signature is a fatal error, as is a signature from a
key not in the keyring.
*Required*;;
*Required* (default);;
Signatures are required; absence of a signature or an invalid signature
is a fatal error, as is a signature from a key not in the keyring.
@@ -349,7 +349,7 @@ level signatures for packages.
The built-in default is the following:
--------
SigLevel = Optional TrustedOnly
SigLevel = Required TrustedOnly
--------

View File

@@ -75,6 +75,9 @@ repo-add Options
Only add packages that are not already in the database. Warnings will be
printed upon detection of existing packages, but they will not be re-added.
*-p, \--prevent-downgrade*::
Do not add package to database if a newer version is already present
*\--include-sigs*::
Include package PGP signatures in the repository database (if available)

View File

@@ -308,8 +308,6 @@ typedef enum _alpm_errno_t {
/** Files conflict */
ALPM_ERR_FILE_CONFLICTS,
/* Misc */
/** Download setup failed */
ALPM_ERR_RETRIEVE_PREPARE,
/** Download failed */
ALPM_ERR_RETRIEVE,
/** Invalid Regex */
@@ -483,7 +481,7 @@ typedef struct _alpm_siglist_t {
* Check the PGP signature for the given package file.
* @param pkg the package to check
* @param siglist a pointer to storage for signature results
* @return 0 if valid, -1 if an error occurred or signature is invalid
* @return 0 on success, -1 if an error occurred or signature is missing
*/
int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, alpm_siglist_t *siglist);
@@ -491,7 +489,7 @@ int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, alpm_siglist_t *siglist);
* Check the PGP signature for the given database.
* @param db the database to check
* @param siglist a pointer to storage for signature results
* @return 0 if valid, -1 if an error occurred or signature is invalid
* @return 0 on success, -1 if an error occurred or signature is missing
*/
int alpm_db_check_pgp_signature(alpm_db_t *db, alpm_siglist_t *siglist);

View File

@@ -153,7 +153,7 @@ int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force)
syncpath = get_sync_dir(handle);
ASSERT(syncpath != NULL, return -1);
temporary_syncpath = _alpm_temporary_download_dir_setup(handle, syncpath);
temporary_syncpath = _alpm_temporary_download_dir_setup(syncpath, handle->sandboxuser);
ASSERT(temporary_syncpath != NULL, FREE(syncpath); return -1);
/* make sure we have a sane umask */
@@ -268,7 +268,6 @@ cleanup:
alpm_list_free_inner(payloads, (alpm_list_fn_free)_alpm_dload_payload_reset);
FREELIST(payloads);
}
_alpm_remove_temporary_download_dir(temporary_syncpath);
FREE(temporary_syncpath);
FREE(syncpath);
umask(oldmask);

View File

@@ -916,7 +916,8 @@ static int curl_download_internal(alpm_handle_t *handle,
*/
static int curl_download_internal_sandboxed(alpm_handle_t *handle,
alpm_list_t *payloads /* struct dload_payload */,
const char *localpath)
const char *localpath,
int *childsig)
{
int pid, err = 0, ret = -1, callbacks_fd[2];
sigset_t oldblock;
@@ -1021,8 +1022,12 @@ static int curl_download_internal_sandboxed(alpm_handle_t *handle,
int wret;
while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR);
if(wret > 0) {
if(WIFSIGNALED(ret)) {
*childsig = WTERMSIG(ret);
}
if(!WIFEXITED(ret)) {
/* the child did not terminate normally */
handle->pm_errno = ALPM_ERR_RETRIEVE;
ret = -1;
}
else {
@@ -1101,36 +1106,46 @@ static int finalize_download_locations(alpm_list_t *payloads, const char *localp
ASSERT(payloads != NULL, return -1);
ASSERT(localpath != NULL, return -1);
alpm_list_t *p;
struct stat st;
int returnvalue = 0;
for(p = payloads; p; p = p->next) {
struct dload_payload *payload = p->data;
const char *filename;
const char *filename = NULL;
if(payload->destfile_name) {
if(payload->destfile_name && stat(payload->destfile_name, &st) == 0) {
filename = payload->destfile_name;
} else {
} else if(stat(payload->tempfile_name, &st) == 0) {
filename = payload->tempfile_name;
}
int ret = move_file(filename, localpath);
if(filename) {
int ret = move_file(filename, localpath);
if(ret == -1) {
/* ignore error if the file already existed - only signature file was downloaded */
if(payload->mtime_existing_file == 0) {
_alpm_log(payload->handle, ALPM_LOG_ERROR, _("could not move %s into %s (%s)\n"),
filename, localpath, strerror(errno));
returnvalue = -1;
if(ret == -1) {
if(payload->mtime_existing_file == 0) {
_alpm_log(payload->handle, ALPM_LOG_ERROR, _("could not move %s into %s (%s)\n"),
filename, localpath, strerror(errno));
returnvalue = -1;
}
}
}
if (payload->download_signature) {
const char sig_suffix[] = ".sig";
char *sig_filename = NULL;
size_t sig_filename_len = strlen(filename) + sizeof(sig_suffix);
MALLOC(sig_filename, sig_filename_len, continue);
snprintf(sig_filename, sig_filename_len, "%s%s", filename, sig_suffix);
move_file(sig_filename, localpath);
FREE(sig_filename);
char *sig_filename;
int ret;
filename = payload->destfile_name ? payload->destfile_name : payload->tempfile_name;
sig_filename = _alpm_get_fullpath("", filename, ".sig");
ASSERT(sig_filename, RET_ERR(payload->handle, ALPM_ERR_MEMORY, -1));
ret = move_file(sig_filename, localpath);
free(sig_filename);
if(ret == -1) {
sig_filename = _alpm_get_fullpath("", filename, ".sig.part");
ASSERT(sig_filename, RET_ERR(payload->handle, ALPM_ERR_MEMORY, -1));
move_file(sig_filename, localpath);
free(sig_filename);
}
}
}
return returnvalue;
@@ -1188,12 +1203,14 @@ int _alpm_download(alpm_handle_t *handle,
const char *temporary_localpath)
{
int ret;
int finalize_ret;
int childsig = 0;
prepare_resumable_downloads(payloads, localpath, handle->sandboxuser);
if(handle->fetchcb == NULL) {
#ifdef HAVE_LIBCURL
if(handle->sandboxuser) {
ret = curl_download_internal_sandboxed(handle, payloads, temporary_localpath);
ret = curl_download_internal_sandboxed(handle, payloads, temporary_localpath, &childsig);
} else {
ret = curl_download_internal(handle, payloads);
}
@@ -1268,13 +1285,21 @@ download_signature:
ret = updated ? 0 : 1;
}
if (finalize_download_locations(payloads, localpath) != 0 && ret == 0) {
finalize_ret = finalize_download_locations(payloads, localpath);
_alpm_remove_temporary_download_dir(temporary_localpath);
/* propagate after finalizing so .part files get copied over */
if(childsig != 0) {
kill(getpid(), childsig);
}
if(finalize_ret != 0 && ret == 0) {
RET_ERR(handle, ALPM_ERR_RETRIEVE, -1);
}
return ret;
}
static char *filecache_find_url(alpm_handle_t *handle, const char *url)
static const char *url_basename(const char *url)
{
const char *filebase = strrchr(url, '/');
@@ -1287,7 +1312,7 @@ static char *filecache_find_url(alpm_handle_t *handle, const char *url)
return NULL;
}
return _alpm_filecache_find(handle, filebase);
return filebase;
}
int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
@@ -1297,21 +1322,38 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
char *temporary_cachedir = NULL;
alpm_list_t *payloads = NULL;
const alpm_list_t *i;
alpm_event_t event = {0};
alpm_event_t event;
CHECK_HANDLE(handle, return -1);
ASSERT(*fetched == NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
/* find a valid cache dir to download to */
cachedir = _alpm_filecache_setup(handle);
temporary_cachedir = _alpm_temporary_download_dir_setup(handle, cachedir);
temporary_cachedir = _alpm_temporary_download_dir_setup(cachedir, handle->sandboxuser);
ASSERT(temporary_cachedir != NULL, return -1);
for(i = urls; i; i = i->next) {
char *url = i->data;
char *filepath = NULL;
const char *urlbase = url_basename(url);
if(urlbase) {
/* attempt to find the file in our pkgcache */
filepath = _alpm_filecache_find(handle, urlbase);
if(filepath && (handle->siglevel & ALPM_SIG_PACKAGE)) {
char *sig_filename = _alpm_get_fullpath("", urlbase, ".sig");
/* if there's no .sig file then forget about the pkg file and go for download */
if(!_alpm_filecache_exists(handle, sig_filename)) {
free(filepath);
filepath = NULL;
}
free(sig_filename);
}
}
/* attempt to find the file in our pkgcache */
char *filepath = filecache_find_url(handle, url);
if(filepath) {
/* the file is locally cached so add it to the output right away */
alpm_list_append(fetched, filepath);
@@ -1395,13 +1437,11 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
FREELIST(payloads);
}
_alpm_remove_temporary_download_dir(temporary_cachedir);
FREE(temporary_cachedir);
return 0;
err:
alpm_list_free_inner(payloads, (alpm_list_fn_free)_alpm_dload_payload_reset);
_alpm_remove_temporary_download_dir(temporary_cachedir);
FREE(temporary_cachedir);
FREELIST(payloads);
FREELIST(*fetched);

View File

@@ -138,8 +138,6 @@ const char SYMEXPORT *alpm_strerror(alpm_errno_t err)
case ALPM_ERR_FILE_CONFLICTS:
return _("conflicting files");
/* Miscellaneous */
case ALPM_ERR_RETRIEVE_PREPARE:
return _("failed to initialize download");
case ALPM_ERR_RETRIEVE:
return _("failed to retrieve some files");
case ALPM_ERR_INVALID_REGEX:

View File

@@ -776,11 +776,11 @@ static int download_files(alpm_handle_t *handle)
char * temporary_cachedir = NULL;
alpm_list_t *i, *files = NULL;
int ret = 0;
alpm_event_t event = {0};
alpm_event_t event;
alpm_list_t *payloads = NULL;
cachedir = _alpm_filecache_setup(handle);
temporary_cachedir = _alpm_temporary_download_dir_setup(handle, cachedir);
temporary_cachedir = _alpm_temporary_download_dir_setup(cachedir, handle->sandboxuser);
if(temporary_cachedir == NULL) {
ret = -1;
goto finish;
@@ -882,7 +882,6 @@ finish:
pkg->infolevel &= ~INFRQ_DSIZE;
pkg->download_size = 0;
}
_alpm_remove_temporary_download_dir(temporary_cachedir);
FREE(temporary_cachedir);
return ret;

View File

@@ -23,7 +23,6 @@
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
@@ -950,53 +949,32 @@ const char *_alpm_filecache_setup(alpm_handle_t *handle)
/** Create a temporary directory under the supplied directory.
* The new directory is writable by the download user, and will be
* removed after the download operation has completed.
* @param handle an alpm handle
* @param dir existing sync or cache directory
* @param user download user name
* @return pointer to a sub-directory writable by the download user inside the existing directory.
*/
char *_alpm_temporary_download_dir_setup(alpm_handle_t *handle, const char *dir)
char *_alpm_temporary_download_dir_setup(const char *dir, const char *user)
{
const char *user = handle->sandboxuser;
uid_t myuid = getuid();
struct passwd const *pw = NULL;
ASSERT(dir != NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL));
if(myuid == 0 && user != NULL) {
errno = 0;
pw = getpwnam(user);
if(pw == NULL) {
if(errno == 0) {
_alpm_log(handle, ALPM_LOG_ERROR,
_("download user '%s' does not exist\n"), user);
} else {
_alpm_log(handle, ALPM_LOG_ERROR,
_("failed to get download user '%s': %s\n"),
user, strerror(errno));
}
RET_ERR(handle, ALPM_ERR_RETRIEVE_PREPARE, NULL);
}
ASSERT(dir != NULL, return NULL);
if(user != NULL) {
ASSERT((pw = getpwnam(user)) != NULL, return NULL);
}
const char template[] = "download-XXXXXX";
size_t newdirlen = strlen(dir) + sizeof(template) + 1;
char *newdir = NULL;
MALLOC(newdir, newdirlen, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
MALLOC(newdir, newdirlen, return NULL);
snprintf(newdir, newdirlen - 1, "%s%s", dir, template);
if(mkdtemp(newdir) == NULL) {
_alpm_log(handle, ALPM_LOG_ERROR,
_("failed to create temporary download directory %s: %s\n"),
newdir, strerror(errno));
free(newdir);
RET_ERR(handle, ALPM_ERR_RETRIEVE_PREPARE, NULL);
return NULL;
}
if(pw != NULL) {
if(chown(newdir, pw->pw_uid, pw->pw_gid) == -1) {
_alpm_log(handle, ALPM_LOG_ERROR,
_("failed to chown temporary download directory %s: %s\n"),
newdir, strerror(errno));
free(newdir);
RET_ERR(handle, ALPM_ERR_RETRIEVE_PREPARE, NULL);
return NULL;
}
}
newdir[newdirlen-2] = '/';

View File

@@ -139,7 +139,7 @@ char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename);
/* Checks whether a file exists in cache */
int _alpm_filecache_exists(alpm_handle_t *handle, const char *filename);
const char *_alpm_filecache_setup(alpm_handle_t *handle);
char *_alpm_temporary_download_dir_setup(alpm_handle_t *handle, const char *dir);
char *_alpm_temporary_download_dir_setup(const char *dir, const char *user);
void _alpm_remove_temporary_download_dir(const char *dir);
/* Unlike many uses of alpm_pkgvalidation_t, _alpm_test_checksum expects

View File

@@ -0,0 +1,57 @@
#!/bin/bash
#
# buildenv.sh - Check that the BUILDENV and OPTIONS arrays are valid
#
# Copyright (c) 2025 Pacman Development Team <pacman-dev@lists.archlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
[[ -n $LIBMAKEPKG_LINT_CONFIG_BUILDENV_SH ]] && return
LIBMAKEPKG_LINT_CONFIG_BUILDENV_SH=1
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
source "$MAKEPKG_LIBRARY/util/message.sh"
lint_config_functions+=('lint_buildenv')
lint_buildenv() {
local ret=0 kopt
local known_buildenv=(ccache check color distcc sign)
local known_option=(autodeps debug docs emptydirs libtool lto purge staticlibs strip zipman)
for i in "${BUILDENV[@]}"; do
for kopt in "${known_buildenv[@]}"; do
if [[ $i = "$kopt" || $i = "!$kopt" ]]; then
continue 2
fi
done
error "$(gettext "%s array contains unknown option '%s'")" "BUILDENV" "$i"
ret=1
done
for i in "${OPTIONS[@]}"; do
for kopt in "${known_option[@]}"; do
if [[ $i = "$kopt" || $i = "!$kopt" ]]; then
continue 2
fi
done
error "$(gettext "%s array contains unknown option '%s'")" "OPTIONS" "$i"
ret=1
done
}

View File

@@ -1,6 +1,7 @@
libmakepkg_module = 'lint_config'
sources = [
'buildenv.sh.in',
'ext.sh.in',
'nproc.sh.in',
'packager.sh.in',

View File

@@ -17,7 +17,6 @@ sources = [
'package_function.sh.in',
'package_function_variable.sh.in',
'pkgbase.sh.in',
'pkglist.sh.in',
'pkgname.sh.in',
'pkgrel.sh.in',
'pkgver.sh.in',
@@ -25,6 +24,7 @@ sources = [
'source.sh.in',
'util.sh.in',
'variable.sh.in',
'xdata.sh.in',
]
foreach src : sources

View File

@@ -1,44 +0,0 @@
#!/bin/bash
#
# pkglist.sh - Check the packages selected to build exist.
#
# Copyright (c) 2014-2025 Pacman Development Team <pacman-dev@lists.archlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
[[ -n "$LIBMAKEPKG_LINT_PKGBUILD_PKGLIST_SH" ]] && return
LIBMAKEPKG_LINT_PKGBUILD_PKGLIST_SH=1
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
source "$MAKEPKG_LIBRARY/util/message.sh"
source "$MAKEPKG_LIBRARY/util/util.sh"
lint_pkgbuild_functions+=('lint_pkglist')
lint_pkglist() {
local i ret=0
for i in "${PKGLIST[@]}"; do
if ! in_array "$i" "${pkgname[@]}"; then
error "$(gettext "Requested package %s is not provided in %s")" "$i" "$BUILDFILE"
ret=1
fi
done
return $ret
}

View File

@@ -0,0 +1,58 @@
#!/bin/bash
#
# xdata.sh - Check the 'xdata' array conforms to requirements.
#
# Copyright (c) 2014-2025 Pacman Development Team <pacman-dev@lists.archlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
[[ -n "$LIBMAKEPKG_LINT_PKGBUILD_XDATA_SH" ]] && return
LIBMAKEPKG_LINT_PKGBUILD_XDATA_SH=1
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
source "$MAKEPKG_LIBRARY/util/message.sh"
source "$MAKEPKG_LIBRARY/util/pkgbuild.sh"
lint_pkgbuild_functions+=('lint_xdata')
lint_xdata() {
local xdata_list entry key value ret=0
get_pkgbuild_all_split_attributes xdata xdata_list
for entry in "${xdata_list[@]}"; do
key="${entry%%=*}"
value="${entry##*=}"
if [[ "${entry}" == "${key}=${value}" ]]; then
# Entries must contain exactly one equal sign.
error "$(gettext "%s array: Entries must contain exactly one equal sign, e.g. key=value.")" "xdata"
ret=1
elif [[ "${key}" == '' ]]; then
# Do not allow keys without values.
error "$(gettext "%s array: The key part of an entry must not be empty.")" "xdata"
ret=1
elif [[ "${key}" == "pkgtype" ]]; then
# The key "pkgtype" is reserved for makepkg.
error "$(gettext "%s array: The key 'pkgtype' is reserved for makepkg.")" "xdata"
ret=1
fi
done
return $ret
}

View File

@@ -30,7 +30,7 @@ known_hash_algos=({ck,md5,sha{1,224,256,384,512},b2})
pkgbuild_schema_arrays=(arch backup checkdepends conflicts depends groups
license makedepends noextract optdepends options
provides replaces source validpgpkeys
provides replaces source validpgpkeys xdata
"${known_hash_algos[@]/%/sums}")
pkgbuild_schema_strings=(changelog epoch install pkgbase pkgdesc pkgrel pkgver

View File

@@ -493,7 +493,7 @@ write_pkginfo() {
write_kv_pair "pkgname" "$pkgname"
write_kv_pair "pkgbase" "$pkgbase"
write_kv_pair "xdata" "pkgtype=$pkgtype"
write_kv_pair "xdata" "pkgtype=$pkgtype" "${xdata[@]}"
local fullver=$(get_full_version)
write_kv_pair "pkgver" "$fullver"

View File

@@ -68,6 +68,7 @@ Multiple packages to add can be specified on the command line.\n")"
printf -- "$(gettext "Options:\n")"
printf -- "$(gettext " -n, --new only add packages that are not already in the database\n")"
printf -- "$(gettext " -p, --prevent-downgrade do not add package to database if a newer version is already present\n")"
printf -- "$(gettext " --include-sigs Include package PGP signatures in the repository database (if available)")
elif [[ $cmd == "repo-remove" ]] ; then
printf -- "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename> ...\n")"
printf -- "\n"

View File

@@ -105,12 +105,11 @@ config_t *config_new(void)
return NULL;
}
/* defaults which may get overridden later */
newconfig->op = PM_OP_MAIN;
newconfig->op = PM_OP_NONE;
newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING;
newconfig->configfile = strdup(CONFFILE);
if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_DATABASE;
newconfig->localfilesiglevel = ALPM_SIG_USE_DEFAULT;
newconfig->remotefilesiglevel = ALPM_SIG_USE_DEFAULT;
}
@@ -131,6 +130,13 @@ config_t *config_new(void)
return newconfig;
}
void targets_free(targets_t *targets) {
FREELIST(targets->targets);
FREELIST(targets->sync);
FREELIST(targets->upgrade);
FREELIST(targets->remove);
}
int config_free(config_t *oldconfig)
{
if(oldconfig == NULL) {

View File

@@ -44,6 +44,14 @@ typedef struct __config_repo_t {
int siglevel_mask;
} config_repo_t;
typedef struct __targets_t {
alpm_list_t *targets;
alpm_list_t *sync;
alpm_list_t *upgrade;
alpm_list_t *remove;
} targets_t;
typedef struct __config_t {
unsigned short op;
unsigned short quiet;
@@ -145,14 +153,15 @@ typedef struct __config_t {
/* Operations */
enum {
PM_OP_MAIN = 1,
PM_OP_REMOVE,
PM_OP_UPGRADE,
PM_OP_QUERY,
PM_OP_SYNC,
PM_OP_DEPTEST,
PM_OP_DATABASE,
PM_OP_FILES
PM_OP_NONE = 0,
PM_OP_INVALID = (1 << 0),
PM_OP_REMOVE = (1 << 1),
PM_OP_UPGRADE = (1 << 2),
PM_OP_QUERY = (1 << 3),
PM_OP_SYNC = (1 << 4),
PM_OP_DEPTEST = (1 << 5),
PM_OP_DATABASE = (1 << 6),
PM_OP_FILES = (1 << 7)
};
/* Long Operations */
@@ -239,6 +248,9 @@ enum {
/* global config variable */
extern config_t *config;
void targets_free(targets_t *targets);
void enable_colors(int colors);
config_t *config_new(void);
int config_free(config_t *oldconfig);

View File

@@ -10,6 +10,7 @@ pacman_sources = files('''
remove.c
sighandler.h sighandler.c
sync.c
trans.c
callback.h callback.c
upgrade.c
util.h util.c

View File

@@ -104,7 +104,7 @@ static void usage(int op, const char * const myname)
char const *const str_opr = _("operation");
/* please limit your strings to 80 characters in width */
if(op == PM_OP_MAIN) {
if(op == PM_OP_NONE) {
printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr);
printf(_("operations:\n"));
printf(" %s {-h --help}\n", myname);
@@ -340,25 +340,25 @@ static int parsearg_op(int opt, int dryrun)
/* operations */
case 'D':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_DATABASE); break;
case 'F':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_FILES); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_FILES); break;
case 'Q':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_QUERY); break;
case 'R':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_REMOVE); break;
case 'S':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_SYNC); break;
case 'T':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_DEPTEST); break;
case 'U':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_UPGRADE); break;
case 'V':
if(dryrun) break;
config->version = 1; break;
@@ -996,7 +996,7 @@ static int parseargs(int argc, char *argv[])
parsearg_op(opt, 0);
}
if(config->op == 0) {
if(config->op == PM_OP_INVALID) {
pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n"));
return 1;
}
@@ -1124,6 +1124,7 @@ int main(int argc, char *argv[])
{
int ret = 0;
uid_t myuid = getuid();
targets_t targets = {0};
console_cursor_hide();
install_segv_handler();
@@ -1276,24 +1277,27 @@ int main(int argc, char *argv[])
case PM_OP_DATABASE:
ret = pacman_database(pm_targets);
break;
case PM_OP_REMOVE:
ret = pacman_remove(pm_targets);
break;
case PM_OP_UPGRADE:
ret = pacman_upgrade(pm_targets);
break;
case PM_OP_QUERY:
ret = pacman_query(pm_targets);
break;
case PM_OP_SYNC:
ret = pacman_sync(pm_targets);
break;
case PM_OP_DEPTEST:
ret = pacman_deptest(pm_targets);
break;
case PM_OP_FILES:
ret = pacman_files(pm_targets);
break;
case PM_OP_SYNC:
targets.sync = pm_targets;
ret = do_transaction(&targets);
break;
case PM_OP_UPGRADE:
targets.upgrade = pm_targets;
ret = do_transaction(&targets);
break;
case PM_OP_REMOVE:
targets.remove = pm_targets;
ret = do_transaction(&targets);
break;
default:
pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
ret = EXIT_FAILURE;

View File

@@ -22,6 +22,8 @@
#include <alpm_list.h>
#include "conf.h"
#define PACMAN_CALLER_PREFIX "PACMAN"
/* database.c */
@@ -32,12 +34,14 @@ int pacman_deptest(alpm_list_t *targets);
int pacman_files(alpm_list_t *files);
/* query.c */
int pacman_query(alpm_list_t *targets);
/* remove.c */
int pacman_remove(alpm_list_t *targets);
/* sync.c */
int pacman_sync(alpm_list_t *targets);
int sync_prepare_execute(void);
/* remove.c */
int load_remove(alpm_list_t *targets);
/* upgrade.c */
int pacman_upgrade(alpm_list_t *targets);
int load_upgrade(alpm_list_t *targets);
/* trans.c */
int do_transaction(targets_t *targets);
#endif /* PM_PACMAN_H */

View File

@@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fnmatch.h>
#include <stdlib.h>
#include <stdio.h>
@@ -30,11 +29,6 @@
#include "util.h"
#include "conf.h"
static int fnmatch_cmp(const void *pattern, const void *string)
{
return fnmatch(pattern, string, 0);
}
static int remove_target(const char *target)
{
alpm_pkg_t *pkg;
@@ -69,28 +63,16 @@ static int remove_target(const char *target)
return 0;
}
/**
* @brief Remove a specified list of packages.
*
* @param targets a list of packages (as strings) to remove from the system
*
* @return 0 on success, 1 on failure
*/
int pacman_remove(alpm_list_t *targets)
int load_remove(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *i, *data = NULL;
alpm_list_t *i;
if(targets == NULL) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
/* Step 0: create a new transaction */
if(trans_init(config->flags, 0) == -1) {
return 1;
}
/* Step 1: add targets to the created transaction */
for(i = targets; i; i = alpm_list_next(i)) {
char *target = i->data;
@@ -102,81 +84,6 @@ int pacman_remove(alpm_list_t *targets)
}
}
if(retval == 1) {
goto cleanup;
}
/* Step 2: prepare the transaction based on its type, targets and flags */
if(alpm_trans_prepare(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_UNSATISFIED_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_depmissing_t *miss = i->data;
char *depstring = alpm_dep_compute_string(miss->depend);
colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
miss->causingpkg, depstring, miss->target);
free(depstring);
alpm_depmissing_free(miss);
}
break;
default:
break;
}
alpm_list_free(data);
retval = 1;
goto cleanup;
}
/* Search for holdpkg in target list */
int holdpkg = 0;
for(i = alpm_trans_get_remove(config->handle); i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(alpm_list_find(config->holdpkg, alpm_pkg_get_name(pkg), fnmatch_cmp)) {
pm_printf(ALPM_LOG_WARNING, _("%s is designated as a HoldPkg.\n"),
alpm_pkg_get_name(pkg));
holdpkg = 1;
}
}
if(holdpkg && (noyes(_("HoldPkg was found in target list. Do you want to continue?")) == 0)) {
retval = 1;
goto cleanup;
}
/* Step 3: actually perform the removal */
alpm_list_t *pkglist = alpm_trans_get_remove(config->handle);
if(pkglist == NULL) {
printf(_(" there is nothing to do\n"));
goto cleanup; /* we are done */
}
if(config->print) {
print_packages(pkglist);
goto cleanup;
}
/* print targets and ask user confirmation */
display_targets();
printf("\n");
if(yesno(_("Do you want to remove these packages?")) == 0) {
retval = 1;
goto cleanup;
}
if(alpm_trans_commit(config->handle, &data) == -1) {
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
alpm_strerror(alpm_errno(config->handle)));
retval = 1;
}
FREELIST(data);
/* Step 4: release transaction resources */
cleanup:
if(trans_release() == -1) {
retval = 1;
}
return retval;
}

View File

@@ -35,7 +35,6 @@
#include "pacman.h"
#include "util.h"
#include "package.h"
#include "callback.h"
#include "conf.h"
static int unlink_verbose(const char *pathname, int ignore_missing)
@@ -84,7 +83,7 @@ static int sync_cleandb(const char *dbpath)
/* build the full path */
len = snprintf(path, PATH_MAX, "%s%s", dbpath, dname);
if(len > PATH_MAX) {
if(len >= PATH_MAX) {
pm_printf(ALPM_LOG_ERROR, _("could not remove %s%s: path exceeds PATH_MAX\n"),
dbpath, dname);
}
@@ -245,7 +244,7 @@ static int sync_cleancache(int level)
/* build the full filepath */
len=snprintf(path, PATH_MAX, "%s%s", cachedir, ent->d_name);
if(len > PATH_MAX) {
if(len >= PATH_MAX) {
pm_printf(ALPM_LOG_ERROR, _("skipping %s%s: path exceeds PATH_MAX\n"),
cachedir, ent->d_name);
continue;
@@ -364,6 +363,8 @@ static int sync_group(int level, alpm_list_t *syncs, alpm_list_t *targets)
}
}
if(!found) {
pm_printf(ALPM_LOG_ERROR,
_("package group '%s' was not found\n"), grpname);
ret = 1;
}
}
@@ -520,383 +521,6 @@ static int sync_list(alpm_list_t *syncs, alpm_list_t *targets)
return ret;
}
static alpm_db_t *get_db(const char *dbname)
{
alpm_list_t *i;
for(i = alpm_get_syncdbs(config->handle); i; i = i->next) {
alpm_db_t *db = i->data;
if(strcmp(alpm_db_get_name(db), dbname) == 0) {
return db;
}
}
return NULL;
}
static int process_pkg(alpm_pkg_t *pkg)
{
int ret = alpm_add_pkg(config->handle, pkg);
if(ret == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), alpm_strerror(err));
return 1;
}
config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
return 0;
}
static int group_exists(alpm_list_t *dbs, const char *name)
{
alpm_list_t *i;
for(i = dbs; i; i = i->next) {
alpm_db_t *db = i->data;
if(alpm_db_get_group(db, name)) {
return 1;
}
}
return 0;
}
static int process_group(alpm_list_t *dbs, const char *group, int error)
{
int ret = 0;
alpm_list_t *i;
alpm_list_t *pkgs = alpm_find_group_pkgs(dbs, group);
int count = alpm_list_count(pkgs);
if(!count) {
if(group_exists(dbs, group)) {
return 0;
}
pm_printf(ALPM_LOG_ERROR, _("target not found: %s\n"), group);
return 1;
}
if(error) {
/* we already know another target errored. there is no reason to prompt the
* user here; we already validated the group name so just move on since we
* won't actually be installing anything anyway. */
goto cleanup;
}
if(config->print == 0) {
char *array = malloc(count);
int n = 0;
const colstr_t *colstr = &config->colstr;
colon_printf(_n("There is %d member in group %s%s%s:\n",
"There are %d members in group %s%s%s:\n", count),
count, colstr->groups, group, colstr->title);
select_display(pkgs);
if(!array) {
ret = 1;
goto cleanup;
}
if(multiselect_question(array, count)) {
ret = 1;
free(array);
goto cleanup;
}
for(i = pkgs, n = 0; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(array[n++] == 0) {
continue;
}
if(process_pkg(pkg) == 1) {
ret = 1;
free(array);
goto cleanup;
}
}
free(array);
} else {
for(i = pkgs; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(process_pkg(pkg) == 1) {
ret = 1;
goto cleanup;
}
}
}
cleanup:
alpm_list_free(pkgs);
return ret;
}
static int process_targname(alpm_list_t *dblist, const char *targname,
int error)
{
alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname);
/* skip ignored packages when user says no */
if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) {
pm_printf(ALPM_LOG_WARNING, _("skipping target: %s\n"), targname);
return 0;
}
if(pkg) {
return process_pkg(pkg);
}
/* fallback on group */
return process_group(dblist, targname, error);
}
static int process_target(const char *target, int error)
{
/* process targets */
char *targstring = strdup(target);
char *targname = strchr(targstring, '/');
int ret = 0;
alpm_list_t *dblist;
if(targname && targname != targstring) {
alpm_db_t *db;
const char *dbname;
int usage;
*targname = '\0';
targname++;
dbname = targstring;
db = get_db(dbname);
if(!db) {
pm_printf(ALPM_LOG_ERROR, _("database not found: %s\n"),
dbname);
ret = 1;
goto cleanup;
}
/* explicitly mark this repo as valid for installs since
* a repo name was given with the target */
alpm_db_get_usage(db, &usage);
alpm_db_set_usage(db, usage|ALPM_DB_USAGE_INSTALL);
dblist = alpm_list_add(NULL, db);
ret = process_targname(dblist, targname, error);
alpm_list_free(dblist);
/* restore old usage so we don't possibly disturb later
* targets */
alpm_db_set_usage(db, usage);
} else {
targname = targstring;
dblist = alpm_get_syncdbs(config->handle);
ret = process_targname(dblist, targname, error);
}
cleanup:
free(targstring);
if(ret && access(target, R_OK) == 0) {
pm_printf(ALPM_LOG_WARNING,
_("'%s' is a file, did you mean %s instead of %s?\n"),
target, "-U/--upgrade", "-S/--sync");
}
return ret;
}
static int sync_trans(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *i;
/* Step 1: create a new transaction... */
if(trans_init(config->flags, 1) == -1) {
return 1;
}
/* process targets */
for(i = targets; i; i = alpm_list_next(i)) {
const char *targ = i->data;
if(process_target(targ, retval) == 1) {
retval = 1;
}
}
if(retval) {
trans_release();
return retval;
}
if(config->op_s_upgrade) {
if(!config->print) {
colon_printf(_("Starting full system upgrade...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"starting full system upgrade\n");
}
if(alpm_sync_sysupgrade(config->handle, config->op_s_upgrade >= 2) == -1) {
pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
trans_release();
return 1;
}
}
return sync_prepare_execute();
}
static void print_broken_dep(alpm_depmissing_t *miss)
{
char *depstring = alpm_dep_compute_string(miss->depend);
alpm_list_t *trans_add = alpm_trans_get_add(config->handle);
alpm_pkg_t *pkg;
if(miss->causingpkg == NULL) {
/* package being installed/upgraded has unresolved dependency */
colon_printf(_("unable to satisfy dependency '%s' required by %s\n"),
depstring, miss->target);
} else if((pkg = alpm_pkg_find(trans_add, miss->causingpkg))) {
/* upgrading a package breaks a local dependency */
colon_printf(_("installing %s (%s) breaks dependency '%s' required by %s\n"),
miss->causingpkg, alpm_pkg_get_version(pkg), depstring, miss->target);
} else {
/* removing a package breaks a local dependency */
colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
miss->causingpkg, depstring, miss->target);
}
free(depstring);
}
int sync_prepare_execute(void)
{
alpm_list_t *i, *packages, *data = NULL;
int retval = 0;
/* Step 2: "compute" the transaction based on targets and flags */
if(alpm_trans_prepare(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_PKG_INVALID_ARCH:
for(i = data; i; i = alpm_list_next(i)) {
char *pkg = i->data;
colon_printf(_("package %s does not have a valid architecture\n"), pkg);
free(pkg);
}
break;
case ALPM_ERR_UNSATISFIED_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
print_broken_dep(i->data);
alpm_depmissing_free(i->data);
}
break;
case ALPM_ERR_CONFLICTING_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_conflict_t *conflict = i->data;
/* only print reason if it contains new information */
if(conflict->reason->mod == ALPM_DEP_MOD_ANY) {
colon_printf(_("%s-%s and %s-%s are in conflict\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2));
} else {
char *reason = alpm_dep_compute_string(conflict->reason);
colon_printf(_("%s-%s and %s-%s are in conflict (%s)\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2),
reason);
free(reason);
}
alpm_conflict_free(conflict);
}
break;
default:
break;
}
retval = 1;
goto cleanup;
}
packages = alpm_trans_get_add(config->handle);
if(packages == NULL) {
/* nothing to do: just exit without complaining */
if(!config->print) {
printf(_(" there is nothing to do\n"));
}
goto cleanup;
}
/* Step 3: actually perform the operation */
if(config->print) {
print_packages(packages);
goto cleanup;
}
display_targets();
printf("\n");
int confirm;
if(config->op_s_downloadonly) {
confirm = yesno(_("Proceed with download?"));
} else {
confirm = yesno(_("Proceed with installation?"));
}
if(!confirm) {
retval = 1;
goto cleanup;
}
multibar_move_completed_up(true);
if(alpm_trans_commit(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_FILE_CONFLICTS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_fileconflict_t *conflict = i->data;
switch(conflict->type) {
case ALPM_FILECONFLICT_TARGET:
fprintf(stderr, _("%s exists in both '%s' and '%s'\n"),
conflict->file, conflict->target, conflict->ctarget);
break;
case ALPM_FILECONFLICT_FILESYSTEM:
if(conflict->ctarget[0]) {
fprintf(stderr, _("%s: %s exists in filesystem (owned by %s)\n"),
conflict->target, conflict->file, conflict->ctarget);
} else {
fprintf(stderr, _("%s: %s exists in filesystem\n"),
conflict->target, conflict->file);
}
break;
}
alpm_fileconflict_free(conflict);
}
break;
case ALPM_ERR_PKG_INVALID:
case ALPM_ERR_PKG_INVALID_CHECKSUM:
case ALPM_ERR_PKG_INVALID_SIG:
for(i = data; i; i = alpm_list_next(i)) {
char *filename = i->data;
fprintf(stderr, _("%s is invalid or corrupted\n"), filename);
free(filename);
}
break;
default:
break;
}
/* TODO: stderr? */
printf(_("Errors occurred, no packages were upgraded.\n"));
retval = 1;
goto cleanup;
}
/* Step 4: release transaction resources */
cleanup:
alpm_list_free(data);
if(trans_release() == -1) {
retval = 1;
}
return retval;
}
int pacman_sync(alpm_list_t *targets)
{
alpm_list_t *sync_dbs = NULL;
@@ -919,26 +543,8 @@ int pacman_sync(alpm_list_t *targets)
return ret;
}
if(check_syncdbs(1, 0)) {
return 1;
}
sync_dbs = alpm_get_syncdbs(config->handle);
if(config->op_s_sync) {
/* grab a fresh package list */
colon_printf(_("Synchronizing package databases...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"synchronizing package lists\n");
if(!sync_syncdbs(config->op_s_sync, sync_dbs)) {
return 1;
}
}
if(check_syncdbs(1, 1)) {
return 1;
}
/* search for a package */
if(config->op_s_search) {
return sync_search(sync_dbs, targets);
@@ -960,17 +566,9 @@ int pacman_sync(alpm_list_t *targets)
}
if(targets == NULL) {
if(config->op_s_upgrade) {
/* proceed */
} else if(config->op_s_sync) {
return 0;
} else {
/* don't proceed here unless we have an operation that doesn't require a
* target list */
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
return sync_trans(targets);
return 0;
}

499
src/pacman/trans.c Normal file
View File

@@ -0,0 +1,499 @@
/*
* trans.c
*
* Copyright (c) 2006-2021 Pacman Development Team <pacman-dev(a)archlinux.org>
* Copyright (c) 2002-2006 by Judd Vinet <jvinet(a)zeroflux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fnmatch.h>
#include <alpm.h>
#include <alpm_list.h>
/* pacman */
#include "pacman.h"
#include "callback.h"
#include "conf.h"
#include "util.h"
static int fnmatch_cmp(const void *pattern, const void *string)
{
return fnmatch(pattern, string, 0);
}
static void print_broken_dep(alpm_depmissing_t *miss)
{
char *depstring = alpm_dep_compute_string(miss->depend);
alpm_list_t *trans_add = alpm_trans_get_add(config->handle);
alpm_pkg_t *pkg;
if(miss->causingpkg == NULL) {
/* package being installed/upgraded has unresolved dependency */
colon_printf(_("unable to satisfy dependency '%s' required by %s\n"),
depstring, miss->target);
} else if((pkg = alpm_pkg_find(trans_add, miss->causingpkg))) {
/* upgrading a package breaks a local dependency */
colon_printf(_("installing %s (%s) breaks dependency '%s' required by %s\n"),
miss->causingpkg, alpm_pkg_get_version(pkg), depstring, miss->target);
} else {
/* removing a package breaks a local dependency */
colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
miss->causingpkg, depstring, miss->target);
}
free(depstring);
}
static void handle_trans_prepare_error(alpm_list_t *data)
{
alpm_errno_t err = alpm_errno(config->handle);
alpm_list_t *i;
pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_PKG_INVALID_ARCH:
for(i = data; i; i = alpm_list_next(i)) {
char *pkg = i->data;
colon_printf(_("package %s does not have a valid architecture\n"), pkg);
free(pkg);
}
break;
case ALPM_ERR_UNSATISFIED_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
print_broken_dep(i->data);
alpm_depmissing_free(i->data);
}
break;
case ALPM_ERR_CONFLICTING_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_conflict_t *conflict = i->data;
/* only print reason if it contains new information */
if(conflict->reason->mod == ALPM_DEP_MOD_ANY) {
colon_printf(_("%s-%s and %s-%s are in conflict\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2));
} else {
char *reason = alpm_dep_compute_string(conflict->reason);
colon_printf(_("%s-%s and %s-%s are in conflict (%s)\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2),
reason);
free(reason);
}
alpm_conflict_free(conflict);
}
break;
default:
break;
}
}
static void handle_trans_commit_error(alpm_list_t *data) {
alpm_errno_t err = alpm_errno(config->handle);
alpm_list_t *i;
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_FILE_CONFLICTS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_fileconflict_t *conflict = i->data;
switch(conflict->type) {
case ALPM_FILECONFLICT_TARGET:
fprintf(stderr, _("%s exists in both '%s' and '%s'\n"),
conflict->file, conflict->target, conflict->ctarget);
break;
case ALPM_FILECONFLICT_FILESYSTEM:
if(conflict->ctarget[0]) {
fprintf(stderr, _("%s: %s exists in filesystem (owned by %s)\n"),
conflict->target, conflict->file, conflict->ctarget);
} else {
fprintf(stderr, _("%s: %s exists in filesystem\n"),
conflict->target, conflict->file);
}
break;
}
alpm_fileconflict_free(conflict);
}
break;
case ALPM_ERR_PKG_INVALID:
case ALPM_ERR_PKG_INVALID_CHECKSUM:
case ALPM_ERR_PKG_INVALID_SIG:
for(i = data; i; i = alpm_list_next(i)) {
char *filename = i->data;
fprintf(stderr, _("%s is invalid or corrupted\n"), filename);
free(filename);
}
break;
default:
break;
}
/* TODO: stderr? */
printf(_("Errors occurred, no packages were upgraded.\n"));
}
static int trans_prepare_execute(void)
{
alpm_list_t *i, *packages, *remove_packages, *data = NULL;
int retval = 0;
/* Step 2: "compute" the transaction based on targets and flags */
if(alpm_trans_prepare(config->handle, &data) == -1) {
handle_trans_prepare_error(data);
retval = 1;
goto cleanup;
}
packages = alpm_trans_get_add(config->handle);
remove_packages = alpm_trans_get_remove(config->handle);
if(packages == NULL && remove_packages == NULL) {
/* nothing to do: just exit without complaining */
if(!config->print) {
printf(_(" there is nothing to do\n"));
}
goto cleanup;
}
/* Search for holdpkg in target list */
int holdpkg = 0;
for(i = remove_packages; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(alpm_list_find(config->holdpkg, alpm_pkg_get_name(pkg), fnmatch_cmp)) {
pm_printf(ALPM_LOG_WARNING, _("%s is designated as a HoldPkg.\n"),
alpm_pkg_get_name(pkg));
holdpkg = 1;
}
}
if(holdpkg && (noyes(_("HoldPkg was found in target list. Do you want to continue?")) == 0)) {
retval = 1;
goto cleanup;
}
/* Step 3: actually perform the operation */
if(config->print) {
print_packages(packages);
print_packages(remove_packages);
goto cleanup;
}
display_targets();
printf("\n");
int confirm;
if(config->op_s_downloadonly) {
confirm = yesno(_("Proceed with download?"));
} else {
confirm = yesno(_("Proceed with installation?"));
}
if(!confirm) {
retval = 1;
goto cleanup;
}
multibar_move_completed_up(true);
if(alpm_trans_commit(config->handle, &data) == -1) {
handle_trans_commit_error(data);
retval = 1;
goto cleanup;
}
/* Step 4: release transaction resources */
cleanup:
alpm_list_free(data);
if(trans_release() == -1) {
retval = 1;
}
return retval;
}
static int group_exists(alpm_list_t *dbs, const char *name)
{
alpm_list_t *i;
for(i = dbs; i; i = i->next) {
alpm_db_t *db = i->data;
if(alpm_db_get_group(db, name)) {
return 1;
}
}
return 0;
}
static alpm_db_t *get_db(const char *dbname)
{
alpm_list_t *i;
for(i = alpm_get_syncdbs(config->handle); i; i = i->next) {
alpm_db_t *db = i->data;
if(strcmp(alpm_db_get_name(db), dbname) == 0) {
return db;
}
}
return NULL;
}
static int process_pkg(alpm_pkg_t *pkg)
{
int ret = alpm_add_pkg(config->handle, pkg);
if(ret == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), alpm_strerror(err));
return 1;
}
config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
return 0;
}
static int process_group(alpm_list_t *dbs, const char *group, int error)
{
int ret = 0;
alpm_list_t *i;
alpm_list_t *pkgs = alpm_find_group_pkgs(dbs, group);
int count = alpm_list_count(pkgs);
if(!count) {
if(group_exists(dbs, group)) {
return 0;
}
pm_printf(ALPM_LOG_ERROR, _("target not found: %s\n"), group);
return 1;
}
if(error) {
/* we already know another target errored. there is no reason to prompt the
* user here; we already validated the group name so just move on since we
* won't actually be installing anything anyway. */
goto cleanup;
}
if(config->print == 0) {
char *array = malloc(count);
int n = 0;
const colstr_t *colstr = &config->colstr;
colon_printf(_n("There is %d member in group %s%s%s:\n",
"There are %d members in group %s%s%s:\n", count),
count, colstr->groups, group, colstr->title);
select_display(pkgs);
if(!array) {
ret = 1;
goto cleanup;
}
if(multiselect_question(array, count)) {
ret = 1;
free(array);
goto cleanup;
}
for(i = pkgs, n = 0; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(array[n++] == 0) {
continue;
}
if(process_pkg(pkg) == 1) {
ret = 1;
free(array);
goto cleanup;
}
}
free(array);
} else {
for(i = pkgs; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(process_pkg(pkg) == 1) {
ret = 1;
goto cleanup;
}
}
}
cleanup:
alpm_list_free(pkgs);
return ret;
}
static int process_targname(alpm_list_t *dblist, const char *targname,
int error)
{
alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname);
/* skip ignored packages when user says no */
if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) {
pm_printf(ALPM_LOG_WARNING, _("skipping target: %s\n"), targname);
return 0;
}
if(pkg) {
return process_pkg(pkg);
}
/* fallback on group */
return process_group(dblist, targname, error);
}
static int process_target(const char *target, int error)
{
/* process targets */
char *targstring = strdup(target);
char *targname = strchr(targstring, '/');
int ret = 0;
alpm_list_t *dblist;
if(targname && targname != targstring) {
alpm_db_t *db;
const char *dbname;
int usage;
*targname = '\0';
targname++;
dbname = targstring;
db = get_db(dbname);
if(!db) {
pm_printf(ALPM_LOG_ERROR, _("database not found: %s\n"),
dbname);
ret = 1;
goto cleanup;
}
/* explicitly mark this repo as valid for installs since
* a repo name was given with the target */
alpm_db_get_usage(db, &usage);
alpm_db_set_usage(db, usage|ALPM_DB_USAGE_INSTALL);
dblist = alpm_list_add(NULL, db);
ret = process_targname(dblist, targname, error);
alpm_list_free(dblist);
/* restore old usage so we don't possibly disturb later
* targets */
alpm_db_set_usage(db, usage);
} else {
targname = targstring;
dblist = alpm_get_syncdbs(config->handle);
ret = process_targname(dblist, targname, error);
}
cleanup:
free(targstring);
if(ret && access(target, R_OK) == 0) {
pm_printf(ALPM_LOG_WARNING,
_("'%s' is a file, did you mean %s instead of %s?\n"),
target, "-U/--upgrade", "-S/--sync");
}
return ret;
}
static int load_sync(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *i;
if(targets == NULL && !config->op_s_upgrade && !config->op_s_sync) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
/* process targets */
for(i = targets; i; i = alpm_list_next(i)) {
const char *targ = i->data;
if(process_target(targ, retval) == 1) {
retval = 1;
}
}
return retval;
}
int do_transaction(targets_t *targets) {
int need_repos = (config->op & PM_OP_SYNC);
alpm_list_t *sync_dbs;
if(targets->targets != NULL) {
pm_printf(ALPM_LOG_ERROR, _("targets must come after operation\n"));
return 1;
}
if(check_syncdbs(need_repos, 0)) {
return 1;
}
sync_dbs = alpm_get_syncdbs(config->handle);
if(config->op_s_sync) {
/* grab a fresh package list */
colon_printf(_("Synchronizing package databases...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"synchronizing package lists\n");
if(!sync_syncdbs(config->op_s_sync, sync_dbs)) {
return 1;
}
}
if(check_syncdbs(need_repos, 1)) {
return 1;
}
if(config->op_s_clean || config->op_s_search || config->op_s_info
|| config->op_q_list || config->group) {
return pacman_sync(targets->sync);
}
/* Step 1: create a new transaction... */
if(trans_init(config->flags, 1) == -1) {
return 1;
}
if(config->op & PM_OP_SYNC && load_sync(targets->sync)) {
goto cleanup;
}
if(config->op & PM_OP_REMOVE && load_remove(targets->remove)) {
goto cleanup;
}
if(config->op & PM_OP_UPGRADE && load_upgrade(targets->upgrade)) {
goto cleanup;
}
if(config->op_s_upgrade) {
if(!config->print) {
colon_printf(_("Starting full system upgrade...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"starting full system upgrade\n");
}
if(alpm_sync_sysupgrade(config->handle, config->op_s_upgrade >= 2) == -1) {
pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
trans_release();
return 1;
}
}
return trans_prepare_execute();
cleanup:
trans_release();
return 1;
}

View File

@@ -65,7 +65,7 @@ static int load_packages(alpm_list_t *targets, int siglevel)
*
* @return 0 on success, 1 on failure
*/
int pacman_upgrade(alpm_list_t *targets)
int load_upgrade(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *remote_targets = NULL, *fetched_files = NULL;
@@ -88,15 +88,6 @@ int pacman_upgrade(alpm_list_t *targets)
if(remote_targets) {
retval = alpm_fetch_pkgurl(config->handle, remote_targets, &fetched_files);
if(retval) {
goto fail_free;
}
}
/* Step 1: create a new transaction */
if(trans_init(config->flags, 1) == -1) {
retval = 1;
goto fail_free;
}
if(!config->print) {
@@ -105,23 +96,10 @@ int pacman_upgrade(alpm_list_t *targets)
retval |= load_packages(local_targets, alpm_option_get_local_file_siglevel(config->handle));
retval |= load_packages(fetched_files, alpm_option_get_remote_file_siglevel(config->handle));
if(retval) {
goto fail_release;
}
alpm_list_free(remote_targets);
alpm_list_free(local_targets);
FREELIST(fetched_files);
/* now that targets are resolved, we can hand it all off to the sync code */
return sync_prepare_execute();
fail_release:
trans_release();
fail_free:
alpm_list_free(remote_targets);
alpm_list_free(local_targets);
FREELIST(fetched_files);
return retval;
}

View File

@@ -10,9 +10,9 @@ lp3 = pmpkg("pkg3")
lp3.depends = [ "pkg1" ]
self.addpkg2db("local", lp3)
self.args = "-Ru pkg1 pkg2"
self.args = "-R --unneeded pkg1 pkg2"
self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=pkg1")
self.addrule("!PKG_EXIST=pkg2")
self.addrule("PKG_EXIST=pkg3")
self.addrule("PKG_EXIST=pkg3")

View File

@@ -1,6 +1,7 @@
self.description = 'download remote packages with -U with a URL filename'
self.require_capability("gpg")
self.require_capability("curl")
self.option['SigLevel'] = ['Required']
url = self.add_simple_http_server({
# simple

View File

@@ -115,6 +115,8 @@ def mkcfgfile(filename, root, option, db):
data = ["[options]"]
for key, value in option.items():
data.extend(["%s = %s" % (key, j) for j in value])
if "SigLevel" not in option:
data.append("SigLevel = Never\n")
# Repositories
# sort by repo name so tests can predict repo order, rather than be