Compare commits

...

9 Commits

Author SHA1 Message Date
morganamilo
c301327ad5 pacman: implement universal transactions
This allows using -S, -U and -R in one command:

    pacman -S foo -R bar

To make this work some breaking changes have made.

Targets have to come after the operation:

    pacman -S foo  //works
    pacman -Syu    //works
    pacman -yuS    //works
    pacman foo -S  //doesn't work

This could be supported with some code to copy all targets before the
first operation into the first operation's target list.

And -u as a short for --unneeded has been removed as it conflicts with
--sysupgrade. However has -u/--sysupgrade is bound to -S, accidently
doing `pacman -Ru` will not accidently cause a system upgrade.

Another quirk with the ui is that -S has many non transaction related
flags, -Sc -Sg -Sl -Si. These have been split off as "sync only" flags.
Meaning they show up with `pacman -Si foo` but will be invalid on
`pacman -R bar -Si foo`.

Also when -R'ing and -S'ing the same package in come command it's
treated as a full uninstall then reinstall. The backup files are
.pacsave'd and the install reason is set to explicit. I feel like this
behavious is good. This also allows you to wipe config files which what
--nokeep was intending to solve.

Other flags just have to have the op they belong to to be used for them
to be valid.

For example:

    pacman -Rn foo //works
    pacman -S -Rn //works
    pacman -Sn    //doesn't work
    pacman -Sn -R //works

We could posibly drop these flags belonging to each operation and just
make them generic transaction flags.

Implements FS#9694
2021-11-06 23:29:07 +00:00
Allan McRae
39c3cbdf56 _alpm_key_import: Initialise fetch_key
Prevents build warning.

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-05 09:58:18 +10:00
morganamilo
165e492485 pacman: don't run hooks when using --dbonly
--dbonly is meant to only touch the database and not the actual system.
However hooks still run which can leave files in place or run commands
you may not want.

The hooks being run also means `fakeroot pacman -S --dbpath test/ foo --dbonly`
fails because alpm tries to chroot for hooks which requires real root.

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 20:46:57 +10:00
morganamilo
be76f8bf06 libalpm: add ALPM_TRANS_FLAG_NOHOOKS
Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 20:46:47 +10:00
morganamilo
625f3d645b libalpm: don't use alpm_pgpkey_t in import question
When constructing an import question we never really used a proper gpg
key. We just zero initialize the key, set the uid and fingerprint, and
sent that to the front end.

Instead lets just give the import question a uid and fingerprint field.

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 20:43:16 +10:00
morganamilo
e187aa9b48 libalpm: use else when setting fingerprint
The docs [1] say keyid will always be there, so no need to check if it
exists.

[1] https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 19:52:23 +10:00
morganamilo
c5c6633dd1 libalpm: rename __foo tyes to _foo
__foo is reserved in c and should not be used.

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 19:52:23 +10:00
morganamilo
2109de613a libalpm: take alpm_trans_t out of the public API
this type is only used internally by alpm

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 19:52:23 +10:00
Allan McRae
fbb29b5047 repo-add: add --include-sigs option
Pacman now downloads the signature files for all packages when present in a
repository.  That makes distributing signatures within repository databases
redundant and costly.

Do not distribute the package signature files within the repo databases by
default and add an --include-sigs to revert to the old behaviour.

Signed-off-by: Allan McRae <allan@archlinux.org>
2021-09-04 19:52:23 +10:00
25 changed files with 859 additions and 769 deletions

View File

@@ -70,6 +70,8 @@ repo-add Options
Remove old package files from the disk when updating their entry in the
database.
*\--include-sigs*::
Include package PGP signatures in the repository database (if available)
Example
-------

View File

@@ -78,7 +78,7 @@ extern "C" {
* This struct represents an instance of libalpm.
* @ingroup libalpm_handle
*/
typedef struct __alpm_handle_t alpm_handle_t;
typedef struct _alpm_handle_t alpm_handle_t;
/** A database.
*
@@ -98,7 +98,7 @@ typedef struct __alpm_handle_t alpm_handle_t;
* Databases are automatically unregistered when the \link alpm_handle_t \endlink is released.
* @ingroup libalpm_databases
*/
typedef struct __alpm_db_t alpm_db_t;
typedef struct _alpm_db_t alpm_db_t;
/** A package.
@@ -111,13 +111,7 @@ typedef struct __alpm_db_t alpm_db_t;
* to be added or removed from the system.
* @ingroup libalpm_packages
*/
typedef struct __alpm_pkg_t alpm_pkg_t;
/** Transaction structure used internally by libalpm
* @ingroup libalpm_trans
* */
typedef struct __alpm_trans_t alpm_trans_t;
typedef struct _alpm_pkg_t alpm_pkg_t;
/** The time type used by libalpm. Represents a unix time stamp
* @ingroup libalpm_misc */
@@ -1080,8 +1074,10 @@ typedef struct _alpm_question_import_key_t {
alpm_question_type_t type;
/** Answer: whether or not to import key */
int import;
/** The key to import */
alpm_pgpkey_t *key;
/** UID of the key to import */
const char *uid;
/** Fingerprint the key to import */
const char *fingerprint;
} alpm_question_import_key_t;
/**
@@ -2719,7 +2715,8 @@ typedef enum _alpm_transflag_t {
ALPM_TRANS_FLAG_RECURSE = (1 << 5),
/** Modify database but do not commit changes to the filesystem. */
ALPM_TRANS_FLAG_DBONLY = (1 << 6),
/* (1 << 7) flag can go here */
/** Do not run hooks during a transaction */
ALPM_TRANS_FLAG_NOHOOKS = (1 << 7),
/** Use ALPM_PKG_REASON_DEPEND when installing packages. */
ALPM_TRANS_FLAG_ALLDEPS = (1 << 8),
/** Only download packages and do not actually install. */

View File

@@ -48,13 +48,13 @@ extern "C" {
*/
/** A doubly linked list */
typedef struct __alpm_list_t {
typedef struct _alpm_list_t {
/** data held by the list node */
void *data;
/** pointer to the previous node */
struct __alpm_list_t *prev;
struct _alpm_list_t *prev;
/** pointer to the next node */
struct __alpm_list_t *next;
struct _alpm_list_t *next;
} alpm_list_t;
/** Frees a list and its contents */

View File

@@ -62,7 +62,7 @@ struct db_operations {
};
/* Database */
struct __alpm_db_t {
struct _alpm_db_t {
alpm_handle_t *handle;
char *treename;
/* do not access directly, use _alpm_db_path(db) for lazy access */

View File

@@ -43,7 +43,7 @@ enum mount_fsinfo {
MOUNT_FSINFO_FAIL,
};
typedef struct __alpm_mountpoint_t {
typedef struct _alpm_mountpoint_t {
/* mount point information */
char *mount_dir;
size_t mount_dir_len;

View File

@@ -23,19 +23,19 @@
#include "alpm_list.h"
enum __alpm_graph_vertex_state {
enum _alpm_graph_vertex_state {
ALPM_GRAPH_STATE_UNPROCESSED,
ALPM_GRAPH_STATE_PROCESSING,
ALPM_GRAPH_STATE_PROCESSED
};
typedef struct __alpm_graph_t {
typedef struct _alpm_graph_t {
void *data;
struct __alpm_graph_t *parent; /* where did we come from? */
struct _alpm_graph_t *parent; /* where did we come from? */
alpm_list_t *children;
alpm_list_t *iterator; /* used for DFS without recursion */
off_t weight; /* weight of the node */
enum __alpm_graph_vertex_state state;
enum _alpm_graph_vertex_state state;
} alpm_graph_t;
alpm_graph_t *_alpm_graph_new(void);

View File

@@ -26,6 +26,7 @@
#include "alpm_list.h"
#include "alpm.h"
#include "trans.h"
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
@@ -50,7 +51,7 @@ do { \
} \
} while(0)
struct __alpm_handle_t {
struct _alpm_handle_t {
/* internal usage */
alpm_db_t *db_local; /* local db pointer */
alpm_list_t *dbs_sync; /* List of (alpm_db_t *) */

View File

@@ -85,7 +85,7 @@ struct pkg_operations {
*/
extern const struct pkg_operations default_pkg_ops;
struct __alpm_pkg_t {
struct _alpm_pkg_t {
unsigned long name_hash;
char *filename;
char *base;

View File

@@ -32,7 +32,7 @@
* A combination of a hash table and a list, allowing for fast look-up
* by package name but also iteration over the packages.
*/
struct __alpm_pkghash_t {
struct _alpm_pkghash_t {
/** data held by the hash table */
alpm_list_t **hash_table;
/** head node of the hash table data in normal list format */
@@ -45,7 +45,7 @@ struct __alpm_pkghash_t {
unsigned int limit;
};
typedef struct __alpm_pkghash_t alpm_pkghash_t;
typedef struct _alpm_pkghash_t alpm_pkghash_t;
alpm_pkghash_t *_alpm_pkghash_create(unsigned int size);

View File

@@ -350,7 +350,7 @@ static int key_search_keyserver(alpm_handle_t *handle, const char *fpr,
pgpkey->data = key;
if(key->subkeys->fpr) {
pgpkey->fingerprint = key->subkeys->fpr;
} else if(key->subkeys->keyid) {
} else {
pgpkey->fingerprint = key->subkeys->keyid;
}
pgpkey->uid = key->uids->uid;
@@ -504,19 +504,15 @@ int _alpm_key_import(alpm_handle_t *handle, const char *uid, const char *fpr)
return -1;
}
STRDUP(fetch_key.uid, uid, return -1);
STRDUP(fetch_key.fingerprint, fpr, free(fetch_key.uid); return -1);
alpm_question_import_key_t question = {
.type = ALPM_QUESTION_IMPORT_KEY,
.import = 0,
.key = &fetch_key
.uid = uid,
.fingerprint = fpr
};
QUESTION(handle, &question);
free(fetch_key.uid);
free(fetch_key.fingerprint);
if(question.import) {
/* Try to import the key from a WKD first */
if(email_from_uid(uid, &email) == 0) {

View File

@@ -198,7 +198,8 @@ int SYMEXPORT alpm_trans_commit(alpm_handle_t *handle, alpm_list_t **data)
}
}
if(_alpm_hook_run(handle, ALPM_HOOK_PRE_TRANSACTION) != 0) {
if(!(trans->flags & ALPM_TRANS_FLAG_NOHOOKS) &&
_alpm_hook_run(handle, ALPM_HOOK_PRE_TRANSACTION) != 0) {
RET_ERR(handle, ALPM_ERR_TRANS_HOOK_FAILED, -1);
}
@@ -232,7 +233,10 @@ int SYMEXPORT alpm_trans_commit(alpm_handle_t *handle, alpm_list_t **data)
event.type = ALPM_EVENT_TRANSACTION_DONE;
EVENT(handle, (void *)&event);
alpm_logaction(handle, ALPM_CALLER_PREFIX, "transaction completed\n");
_alpm_hook_run(handle, ALPM_HOOK_POST_TRANSACTION);
if(!(trans->flags & ALPM_TRANS_FLAG_NOHOOKS)) {
_alpm_hook_run(handle, ALPM_HOOK_POST_TRANSACTION);
}
}
trans->state = STATE_COMMITED;

View File

@@ -36,7 +36,7 @@ typedef enum _alpm_transstate_t {
} alpm_transstate_t;
/* Transaction */
struct __alpm_trans_t {
typedef struct _alpm_trans_t {
/* bitfield of alpm_transflag_t flags */
int flags;
alpm_transstate_t state;
@@ -44,7 +44,7 @@ struct __alpm_trans_t {
alpm_list_t *add; /* list of (alpm_pkg_t *) */
alpm_list_t *remove; /* list of (alpm_pkg_t *) */
alpm_list_t *skip_remove; /* list of (char *) */
};
} alpm_trans_t;
void _alpm_trans_free(alpm_trans_t *trans);
/* flags is a bitfield of alpm_transflag_t flags */

View File

@@ -42,6 +42,7 @@ LOCKFILE=
CLEAN_LOCK=0
USE_COLOR='y'
PREVENT_DOWNGRADE=0
INCLUDE_SIGS=0
# Import libmakepkg
source "$LIBRARY"/util/compress.sh
@@ -260,7 +261,7 @@ db_write_entry() {
fi
# compute base64'd PGP signature
if [[ -f "$pkgfile.sig" ]]; then
if (( INCLUDE_SIGS )) && [[ -f "$pkgfile.sig" ]]; then
if grep -q 'BEGIN PGP SIGNATURE' "$pkgfile.sig"; then
error "$(gettext "Cannot use armored signatures for packages: %s")" "$pkgfile.sig"
return 1
@@ -622,6 +623,9 @@ while (( $# )); do
-p|--prevent-downgrade)
PREVENT_DOWNGRADE=1
;;
--include-sigs)
INCLUDE_SIGS=1
;;
*)
args+=("$1")
;;

View File

@@ -541,12 +541,12 @@ void cb_question(void *ctx, alpm_question_t *question)
{
alpm_question_import_key_t *q = &question->import_key;
/* the uid is unknown with db signatures */
if (q->key->uid == NULL) {
if (q->uid == NULL) {
q->import = yesno(_("Import PGP key %s?"),
q->key->fingerprint);
q->fingerprint);
} else {
q->import = yesno(_("Import PGP key %s, \"%s\"?"),
q->key->fingerprint, q->key->uid);
q->fingerprint, q->uid);
}
}
break;

View File

@@ -105,7 +105,8 @@ config_t *config_new(void)
return NULL;
}
/* defaults which may get overridden later */
newconfig->op = PM_OP_MAIN;
newconfig->op = 0;
newconfig->curr_op = 0;
newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING;
newconfig->configfile = strdup(CONFFILE);
if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
@@ -131,6 +132,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

@@ -43,8 +43,17 @@ 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 curr_op;
unsigned short quiet;
unsigned short verbose;
unsigned short version;
@@ -142,14 +151,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_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,
PM_OP_TRANS = PM_OP_SYNC | PM_OP_UPGRADE | PM_OP_REMOVE
};
/* Long Operations */
@@ -235,6 +245,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

@@ -47,7 +47,7 @@
#include "sighandler.h"
/* list of targets specified on command line */
static alpm_list_t *pm_targets;
static targets_t pm_targets;
/* Used to sort the options in --help */
static int options_cmp(const void *p1, const void *p2)
@@ -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) {
printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr);
printf(_("operations:\n"));
printf(" %s {-h --help}\n", myname);
@@ -125,11 +125,10 @@ static void usage(int op, const char * const myname)
addlist(_(" -c, --cascade remove packages and all packages that depend on them\n"));
addlist(_(" -n, --nosave remove configuration files\n"));
addlist(_(" -s, --recursive remove unnecessary dependencies\n"
" (-ss includes explicitly installed dependencies)\n"));
addlist(_(" -u, --unneeded remove unneeded packages\n"));
" (-ss includes explicitly installed dependencies)\n"));
addlist(_(" --unneeded remove unneeded packages\n"));
} else if(op == PM_OP_UPGRADE) {
printf("%s: %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file);
addlist(_(" --needed do not reinstall up to date packages\n"));
printf("%s:\n", str_opt);
} else if(op == PM_OP_QUERY) {
printf("%s: %s {-Q --query} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
@@ -163,7 +162,6 @@ static void usage(int op, const char * const myname)
addlist(_(" -u, --sysupgrade upgrade installed packages (-uu enables downgrades)\n"));
addlist(_(" -y, --refresh download fresh package databases from the server\n"
" (-yy to force a refresh even if up to date)\n"));
addlist(_(" --needed do not reinstall up to date packages\n"));
} else if(op == PM_OP_DATABASE) {
printf("%s: %s {-D --database} <%s> <%s>\n", str_usg, myname, str_opt, str_pkg);
printf("%s:\n", str_opt);
@@ -185,29 +183,27 @@ static void usage(int op, const char * const myname)
addlist(_(" --machinereadable\n"
" produce machine-readable output\n"));
}
switch(op) {
case PM_OP_SYNC:
case PM_OP_UPGRADE:
addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n"));
addlist(_(" --overwrite <glob>\n"
" overwrite conflicting files (can be used more than once)\n"));
addlist(_(" --asdeps install packages as non-explicitly installed\n"));
addlist(_(" --asexplicit install packages as explicitly installed\n"));
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
addlist(_(" --ignoregroup <grp>\n"
" ignore a group upgrade (can be used more than once)\n"));
__attribute__((fallthrough));
case PM_OP_REMOVE:
addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n"));
addlist(_(" --assume-installed <package=version>\n"
" add a virtual package to satisfy dependencies\n"));
addlist(_(" --dbonly only modify database entries, not package files\n"));
addlist(_(" --noprogressbar do not show a progress bar when downloading files\n"));
addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
addlist(_(" -p, --print print the targets instead of performing the operation\n"));
addlist(_(" --print-format <string>\n"
" specify how the targets should be printed\n"));
break;
if(op & (PM_OP_UPGRADE | PM_OP_SYNC)) {
addlist(_(" --needed do not reinstall up to date packages\n"));
addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n"));
addlist(_(" --overwrite <glob>\n"
" overwrite conflicting files (can be used more than once)\n"));
addlist(_(" --asdeps install packages as non-explicitly installed\n"));
addlist(_(" --asexplicit install packages as explicitly installed\n"));
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
addlist(_(" --ignoregroup <grp>\n"
" ignore a group upgrade (can be used more than once)\n"));
}
if(op & PM_OP_TRANS) {
addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n"));
addlist(_(" --assume-installed <package=version>\n"
" add a virtual package to satisfy dependencies\n"));
addlist(_(" --dbonly only modify database entries, not package files\n"));
addlist(_(" --noprogressbar do not show a progress bar when downloading files\n"));
addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
addlist(_(" -p, --print print the targets instead of performing the operation\n"));
addlist(_(" --print-format <string>\n"
" specify how the targets should be printed\n"));
}
addlist(_(" -b, --dbpath <path> set an alternate database location\n"));
@@ -301,7 +297,7 @@ static void cleanup(int ret)
}
/* free memory */
FREELIST(pm_targets);
targets_free(&pm_targets);
console_cursor_show();
exit(ret);
}
@@ -327,6 +323,24 @@ static int parsearg_util_addlist(alpm_list_t **list)
return 0;
}
static void set_op(int opt)
{
int trans = (config->op & PM_OP_TRANS) != 0;
int setting_trans = (opt & PM_OP_TRANS) != 0;
/* we allow -SS so allow -QQ for sake of consistency */
if(config->op == opt) {
return;
}
if(!config->op || (trans && setting_trans)) {
config->op |= opt;
config->curr_op = opt;
} else {
config->op |= PM_OP_INVALID;
}
}
/** Helper function for parsing operation from command-line arguments.
* @param opt Keycode returned by getopt_long
* @param dryrun If nonzero, application state is NOT changed
@@ -338,25 +352,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;
set_op(PM_OP_DATABASE); break;
case 'F':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_FILES); break;
set_op(PM_OP_FILES); break;
case 'Q':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
set_op(PM_OP_QUERY); break;
case 'R':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
set_op(PM_OP_REMOVE); break;
case 'S':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
set_op(PM_OP_SYNC); break;
case 'T':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
set_op(PM_OP_DEPTEST); break;
case 'U':
if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
set_op(PM_OP_UPGRADE); break;
case 'V':
if(dryrun) break;
config->version = 1; break;
@@ -369,6 +383,7 @@ static int parsearg_op(int opt, int dryrun)
return 0;
}
/** Helper functions for parsing command-line arguments.
* @param opt Keycode returned by getopt_long
* @return 0 on success, 1 on failure
@@ -619,61 +634,8 @@ static void checkargs_query(void)
"--native", "--foreign");
}
/* options common to -S -R -U */
static int parsearg_trans(int opt)
{
switch(opt) {
case OP_NODEPS:
case 'd':
if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) {
config->flags |= ALPM_TRANS_FLAG_NODEPS;
} else {
config->flags |= ALPM_TRANS_FLAG_NODEPVERSION;
}
break;
case OP_DBONLY:
config->flags |= ALPM_TRANS_FLAG_DBONLY;
config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
break;
case OP_NOPROGRESSBAR:
config->noprogressbar = 1;
break;
case OP_NOSCRIPTLET:
config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
break;
case OP_PRINT:
case 'p':
config->print = 1;
break;
case OP_PRINTFORMAT:
config->print = 1;
free(config->print_format);
config->print_format = strdup(optarg);
break;
case OP_ASSUMEINSTALLED:
parsearg_util_addlist(&(config->assumeinstalled));
break;
default:
return 1;
}
return 0;
}
static void checkargs_trans(void)
{
if(config->print) {
invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY,
"--print", "--dbonly");
invalid_opt(config->flags & ALPM_TRANS_FLAG_NOSCRIPTLET,
"--print", "--noscriptlet");
}
}
static int parsearg_remove(int opt)
{
if(parsearg_trans(opt) == 0) {
return 0;
}
switch(opt) {
case OP_CASCADE:
case 'c':
@@ -692,7 +654,6 @@ static int parsearg_remove(int opt)
}
break;
case OP_UNNEEDED:
case 'u':
config->flags |= ALPM_TRANS_FLAG_UNNEEDED;
break;
default:
@@ -703,7 +664,6 @@ static int parsearg_remove(int opt)
static void checkargs_remove(void)
{
checkargs_trans();
if(config->flags & ALPM_TRANS_FLAG_NOSAVE) {
invalid_opt(config->print, "--nosave", "--print");
invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY,
@@ -714,9 +674,6 @@ static void checkargs_remove(void)
/* options common to -S -U */
static int parsearg_upgrade(int opt)
{
if(parsearg_trans(opt) == 0) {
return 0;
}
switch(opt) {
case OP_OVERWRITE_FILES:
parsearg_util_addlist(&(config->overwrite_files));
@@ -741,6 +698,7 @@ static int parsearg_upgrade(int opt)
config->op_s_downloadonly = 1;
config->flags |= ALPM_TRANS_FLAG_DOWNLOADONLY;
config->flags |= ALPM_TRANS_FLAG_NOCONFLICTS;
config->flags |= ALPM_TRANS_FLAG_NOLOCK;
break;
default: return 1;
}
@@ -749,7 +707,6 @@ static int parsearg_upgrade(int opt)
static void checkargs_upgrade(void)
{
checkargs_trans();
invalid_opt(config->flags & ALPM_TRANS_FLAG_ALLDEPS
&& config->flags & ALPM_TRANS_FLAG_ALLEXPLICIT,
"--asdeps", "--asexplicit");
@@ -757,9 +714,6 @@ static void checkargs_upgrade(void)
static int parsearg_files(int opt)
{
if(parsearg_trans(opt) == 0) {
return 0;
}
switch(opt) {
case OP_LIST:
case 'l':
@@ -798,6 +752,26 @@ static int parsearg_sync(int opt)
if(parsearg_upgrade(opt) == 0) {
return 0;
}
switch(opt) {
case OP_SYSUPGRADE:
case 'u':
(config->op_s_upgrade)++;
break;
case OP_REFRESH:
case 'y':
(config->op_s_sync)++;
break;
default:
return 1;
}
return 0;
}
static int parsearg_sync_only(int opt)
{
if(parsearg_sync(opt) == 0) {
return 0;
}
switch(opt) {
case OP_CLEAN:
case 'c':
@@ -839,7 +813,12 @@ static int parsearg_sync(int opt)
static void checkargs_sync(void)
{
checkargs_upgrade();
/* no conflicts currently */
}
static void checkargs_sync_only(void)
{
checkargs_sync();
if(config->op_s_clean) {
invalid_opt(config->group, "--clean", "--groups");
invalid_opt(config->op_s_info, "--clean", "--info");
@@ -869,6 +848,89 @@ static void checkargs_sync(void)
}
}
/* options common to -S -R -U */
static int parsearg_trans(int opt)
{
if(config->op & (PM_OP_SYNC | PM_OP_UPGRADE) && parsearg_upgrade(opt) == 0) {
return 0;
}
if(config->op & PM_OP_SYNC && parsearg_sync(opt) == 0) {
return 0;
}
if(config->op & PM_OP_REMOVE && parsearg_remove(opt) == 0) {
return 0;
}
switch(opt) {
case OP_NODEPS:
case 'd':
if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) {
config->flags |= ALPM_TRANS_FLAG_NODEPS;
} else {
config->flags |= ALPM_TRANS_FLAG_NODEPVERSION;
}
break;
case OP_DBONLY:
config->flags |= ALPM_TRANS_FLAG_DBONLY;
config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
config->flags |= ALPM_TRANS_FLAG_NOHOOKS;
break;
case OP_NOPROGRESSBAR:
config->noprogressbar = 1;
break;
case OP_NOSCRIPTLET:
config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET;
break;
case OP_PRINT:
case 'p':
config->print = 1;
break;
case OP_PRINTFORMAT:
config->print = 1;
free(config->print_format);
config->print_format = strdup(optarg);
break;
case OP_ASSUMEINSTALLED:
parsearg_util_addlist(&(config->assumeinstalled));
break;
default:
return 1;
}
return 0;
}
static void checkargs_trans(void)
{
if(config->op & (PM_OP_SYNC | PM_OP_UPGRADE)) {
checkargs_upgrade();
}
if(config->op & PM_OP_SYNC) {
checkargs_sync();
}
if(config->op & PM_OP_REMOVE) {
checkargs_remove();
}
if(config->print) {
invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY,
"--print", "--dbonly");
invalid_opt(config->flags & ALPM_TRANS_FLAG_NOSCRIPTLET,
"--print", "--noscriptlet");
}
}
static void add_target(char *targ)
{
if(config->curr_op & PM_OP_SYNC) {
pm_targets.sync = alpm_list_add(pm_targets.sync, targ);
} else if(config->curr_op & PM_OP_REMOVE) {
pm_targets.remove = alpm_list_add(pm_targets.remove, targ);
} else if(config->curr_op & PM_OP_UPGRADE) {
pm_targets.upgrade = alpm_list_add(pm_targets.upgrade, targ);
} else {
pm_targets.targets = alpm_list_add(pm_targets.targets, targ);
}
}
/** Parse command-line arguments for each operation.
* @param argc argc
* @param argv argv
@@ -879,7 +941,7 @@ static int parseargs(int argc, char *argv[])
int opt;
int option_index = 0;
int result;
const char *optstring = "DFQRSTUVb:cdefghiklmnopqr:stuvwxy";
const char *optstring = "-DFQRSTUVb:cdefghiklmnopqr:stuvwxy";
static const struct option opts[] =
{
{"database", no_argument, 0, 'D'},
@@ -954,6 +1016,11 @@ static int parseargs(int argc, char *argv[])
while((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) {
if(opt == 0) {
continue;
} else if(opt == 1) {
char *targ = strdup(optarg);
/* add the target to our target array */
add_target(targ);
continue;
} else if(opt == '?') {
/* unknown option, getopt printed an error */
return 1;
@@ -961,8 +1028,8 @@ static int parseargs(int argc, char *argv[])
parsearg_op(opt, 0);
}
if(config->op == 0) {
pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n"));
if(config->op & PM_OP_INVALID) {
pm_printf(ALPM_LOG_ERROR, _("these operations can not be used together\n"));
return 1;
}
if(config->help) {
@@ -977,7 +1044,7 @@ static int parseargs(int argc, char *argv[])
/* parse all other options */
optind = 1;
while((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) {
if(opt == 0) {
if(opt == 0 || opt == 1) {
continue;
} else if(opt == '?') {
/* this should have failed during first pass already */
@@ -994,14 +1061,9 @@ static int parseargs(int argc, char *argv[])
case PM_OP_QUERY:
result = parsearg_query(opt);
break;
case PM_OP_REMOVE:
result = parsearg_remove(opt);
break;
case PM_OP_SYNC:
result = parsearg_sync(opt);
break;
case PM_OP_UPGRADE:
result = parsearg_upgrade(opt);
/* handle -Si -Sg etc if -S is the only trans op */
result = parsearg_sync_only(opt);
break;
case PM_OP_FILES:
result = parsearg_files(opt);
@@ -1015,6 +1077,13 @@ static int parseargs(int argc, char *argv[])
continue;
}
if(config->op & PM_OP_TRANS) {
result = parsearg_trans(opt);
}
if(result == 0) {
continue;
}
/* fall back to global options */
result = parsearg_global(opt);
if(result != 0) {
@@ -1031,7 +1100,7 @@ static int parseargs(int argc, char *argv[])
while(optind < argc) {
/* add the target to our target array */
pm_targets = alpm_list_add(pm_targets, strdup(argv[optind]));
add_target(strdup(argv[optind]));
optind++;
}
@@ -1043,7 +1112,7 @@ static int parseargs(int argc, char *argv[])
/* no conflicting options */
break;
case PM_OP_SYNC:
checkargs_sync();
checkargs_sync_only();
break;
case PM_OP_QUERY:
checkargs_query();
@@ -1060,10 +1129,83 @@ static int parseargs(int argc, char *argv[])
default:
break;
}
if(config->op & PM_OP_TRANS) {
checkargs_trans();
}
return 0;
}
static void parse_stdin(alpm_list_t **targets)
{
if(!isatty(fileno(stdin))) {
int target_found = 0;
char *vdata, *line = NULL;
size_t line_size = 0;
ssize_t nread;
/* remove the '-' from the list */
*targets = alpm_list_remove_str(*targets, "-", &vdata);
free(vdata);
while((nread = getline(&line, &line_size, stdin)) != -1) {
if(line[nread - 1] == '\n') {
/* remove trailing newline */
line[nread - 1] = '\0';
}
if(line[0] == '\0') {
/* skip empty lines */
continue;
}
if(!alpm_list_append_strdup(targets, line)) {
break;
}
target_found = 1;
}
free(line);
if(ferror(stdin)) {
pm_printf(ALPM_LOG_ERROR,
_("failed to read arguments from stdin: (%s)\n"), strerror(errno));
cleanup(EXIT_FAILURE);
}
if(!freopen(ctermid(NULL), "r", stdin)) {
pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
strerror(errno));
}
if(!target_found) {
pm_printf(ALPM_LOG_ERROR, _("argument '-' specified with empty stdin\n"));
cleanup(1);
}
} else {
/* do not read stdin from terminal */
pm_printf(ALPM_LOG_ERROR, _("argument '-' specified without input on stdin\n"));
cleanup(1);
}
}
static void parse_stdins(void)
{
alpm_list_t **lists[4] = {&pm_targets.targets, &pm_targets.sync, &pm_targets.upgrade, &pm_targets.remove};
int replaced = 0;
for(int i = 0; i < 4; i++) {
alpm_list_t **list = lists[i];
if(alpm_list_find_str(*list, "-")) {
if(replaced) {
pm_printf(ALPM_LOG_ERROR, _("argument '-' can not be specified multiple times\n"));
cleanup(1);
}
parse_stdin(list);
replaced++;
}
}
}
/** Print command line to logfile.
* @param argc
* @param argv
@@ -1137,54 +1279,7 @@ int main(int argc, char *argv[])
}
/* we support reading targets from stdin if a cmdline parameter is '-' */
if(alpm_list_find_str(pm_targets, "-")) {
if(!isatty(fileno(stdin))) {
int target_found = 0;
char *vdata, *line = NULL;
size_t line_size = 0;
ssize_t nread;
/* remove the '-' from the list */
pm_targets = alpm_list_remove_str(pm_targets, "-", &vdata);
free(vdata);
while((nread = getline(&line, &line_size, stdin)) != -1) {
if(line[nread - 1] == '\n') {
/* remove trailing newline */
line[nread - 1] = '\0';
}
if(line[0] == '\0') {
/* skip empty lines */
continue;
}
if(!alpm_list_append_strdup(&pm_targets, line)) {
break;
}
target_found = 1;
}
free(line);
if(ferror(stdin)) {
pm_printf(ALPM_LOG_ERROR,
_("failed to read arguments from stdin: (%s)\n"), strerror(errno));
cleanup(EXIT_FAILURE);
}
if(!freopen(ctermid(NULL), "r", stdin)) {
pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
strerror(errno));
}
if(!target_found) {
pm_printf(ALPM_LOG_ERROR, _("argument '-' specified with empty stdin\n"));
cleanup(1);
}
} else {
/* do not read stdin from terminal */
pm_printf(ALPM_LOG_ERROR, _("argument '-' specified without input on stdin\n"));
cleanup(1);
}
}
parse_stdins();
if(config->sysroot && (chroot(config->sysroot) != 0 || chdir("/") != 0)) {
pm_printf(ALPM_LOG_ERROR,
@@ -1232,7 +1327,10 @@ int main(int argc, char *argv[])
printf("Lock File : %s\n", alpm_option_get_lockfile(config->handle));
printf("Log File : %s\n", alpm_option_get_logfile(config->handle));
printf("GPG Dir : %s\n", alpm_option_get_gpgdir(config->handle));
list_display("Targets :", pm_targets, 0);
list_display("Targets :", pm_targets.targets, 0);
list_display("Sync :", pm_targets.sync, 0);
list_display("Upgrade :", pm_targets.upgrade, 0);
list_display("Remove :", pm_targets.remove, 0);
}
/* Log command line */
@@ -1241,31 +1339,26 @@ int main(int argc, char *argv[])
}
/* start the requested operation */
switch(config->op) {
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;
default:
pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
ret = EXIT_FAILURE;
if(config->op & PM_OP_TRANS) {
ret = do_transaction(&pm_targets);
} else {
switch(config->op) {
case PM_OP_DATABASE:
ret = pacman_database(pm_targets.targets);
break;
case PM_OP_QUERY:
ret = pacman_query(pm_targets.targets);
break;
case PM_OP_DEPTEST:
ret = pacman_deptest(pm_targets.targets);
break;
case PM_OP_FILES:
ret = pacman_files(pm_targets.targets);
break;
default:
pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
ret = EXIT_FAILURE;
}
}
cleanup(ret);

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)
@@ -510,376 +509,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 and %s are in conflict\n"),
conflict->package1, conflict->package2);
} else {
char *reason = alpm_dep_compute_string(conflict->reason);
colon_printf(_("%s and %s are in conflict (%s)\n"),
conflict->package1, 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:
printf(_("%s exists in both '%s' and '%s'\n"),
conflict->file, conflict->target, conflict->ctarget);
break;
case ALPM_FILECONFLICT_FILESYSTEM:
if(conflict->ctarget[0]) {
printf(_("%s: %s exists in filesystem (owned by %s)\n"),
conflict->target, conflict->file, conflict->ctarget);
} else {
printf(_("%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;
printf(_("%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;
@@ -902,26 +531,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);
@@ -943,17 +554,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;
}

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

@@ -0,0 +1,478 @@
/*
* upgrade.c
*
* Copyright (c) 2006-2021 Pacman Development Team <pacman-dev@archlinux.org>
* Copyright (c) 2002-2006 by Judd Vinet <jvinet@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 int sync_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) {
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 and %s are in conflict\n"),
conflict->package1, conflict->package2);
} else {
char *reason = alpm_dep_compute_string(conflict->reason);
colon_printf(_("%s and %s are in conflict (%s)\n"),
conflict->package1, conflict->package2, reason);
free(reason);
}
alpm_conflict_free(conflict);
}
break;
default:
break;
}
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) {
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:
printf(_("%s exists in both '%s' and '%s'\n"),
conflict->file, conflict->target, conflict->ctarget);
break;
case ALPM_FILECONFLICT_FILESYSTEM:
if(conflict->ctarget[0]) {
printf(_("%s: %s exists in filesystem (owned by %s)\n"),
conflict->target, conflict->file, conflict->ctarget);
} else {
printf(_("%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;
printf(_("%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;
}
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 sync_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,38 +88,16 @@ 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;
}
printf(_("loading packages...\n"));
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

@@ -106,16 +106,17 @@ int needs_root(void)
if(config->sysroot) {
return 1;
}
if(config->op & PM_OP_SYNC) {
return (config->op_s_clean || config->op_s_sync ||
(!config->group && !config->op_s_info && !config->op_q_list &&
!config->op_s_search && !config->print));
}
if(config->op & PM_OP_TRANS) {
return !config->print;
}
switch(config->op) {
case PM_OP_DATABASE:
return !config->op_q_check;
case PM_OP_UPGRADE:
case PM_OP_REMOVE:
return !config->print;
case PM_OP_SYNC:
return (config->op_s_clean || config->op_s_sync ||
(!config->group && !config->op_s_info && !config->op_q_list &&
!config->op_s_search && !config->print));
case PM_OP_FILES:
return config->op_s_sync;
default:

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")