/* Extracts the downloaded archive and removes it upon success. * Assumed to be in destination directory before calling this. * Returns -1 on fatal errors, > 0 on extraction errors, 0 on success. */ int extract_file(const char *filename) { /* Extract the archive */ struct archive *archive; struct archive_entry *entry; int ret; int errors = 0; int extract_flags = ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME; archive = archive_read_new(); if (!archive) { return error(PW_ERR_ARCHIVE_CREATE); } archive_read_support_compression_all(archive); archive_read_support_format_all(archive); ret = archive_read_open_filename(archive, filename, 16384); if (ret != ARCHIVE_OK) { return error(PW_ERR_ARCHIVE_OPEN); } while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) { ret = archive_read_extract(archive, entry, extract_flags); if (ret == ARCHIVE_WARN && archive_errno(archive) != ENOSPC) { pw_fprintf(PW_LOG_WARNING, stderr, "warning given when extracting %s: %s\n", archive_entry_pathname(entry), archive_error_string(archive)); } else if (ret != ARCHIVE_OK) { pw_fprintf(PW_LOG_ERROR, stderr, "Could not extract %s\n", archive_entry_pathname(entry)); ++errors; } if (config->verbose) { printf("X %s\n", archive_entry_pathname(entry)); } } archive_read_finish(archive); /* Everything successful. Remove the file */ unlink(filename); return errors; }
/* From git */ int wait_or_whine(pid_t pid, char *argv0) { int status, ret = 0; pid_t waiting; while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR) ; if (waiting < 0) { return error(PW_ERR_WAITPID_FAILED); } else if (waiting != pid) { return error(PW_ERR_WAITPID_CONFUSED); } else if (WIFSIGNALED(status)) { ret = WTERMSIG(status); ret -= 127; RET_ERR(PW_ERR_WAITPID_SIGNAL, ret); } else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret == 127) { ret = -1; pw_fprintf(PW_LOG_ERROR, stderr, "Failed to execute %s\n", argv0); } } else { return error(PW_ERR_WAITPID_CONFUSED); } return ret; }
int powaur_list_aur(void) { struct pw_hashdb *hashdb = build_hashdb(); if (!hashdb) { pw_fprintf(PW_LOG_ERROR, stderr, "Failed to build hashdb!\n"); return -1; } hash_walk(hashdb->aur, pkgpair_print_color); hashdb_free(hashdb); return 0; }
int powaur_crawl(alpm_list_t *targets) { int ret = 0; char cwd[PATH_MAX]; if (!getcwd(cwd, PATH_MAX)) { return error(PW_ERR_GETCWD); } if (chdir(powaur_dir)) { return error(PW_ERR_CHDIR, powaur_dir); } struct pw_hashdb *hashdb = build_hashdb(); if (!hashdb) { pw_fprintf(PW_LOG_ERROR, stderr, "Unable to build hash database!\n"); ret = -1; } alpm_list_t *i, *target_pkgs; struct graph *graph; struct stack *topost = stack_new(sizeof(int)); int have_cycles; for (i = targets; i; i = i->next) { stack_reset(topost); graph = NULL; target_pkgs = alpm_list_add(NULL, i->data); build_dep_graph(&graph, hashdb, target_pkgs, RESOLVE_THOROUGH); if (have_cycles) { printf("Cyclic dependencies for package \"%s\"\n", i->data); } graph_toposort(graph, topost); if (stack_empty(topost)) { printf("Package \"%s\" has no dependencies.\n", i->data); } else { printf("\n"); pw_printf(PW_LOG_INFO, "\"%s\" topological order: ", i->data); print_topo_order(graph, topost); } graph_free(graph); alpm_list_free(target_pkgs); } stack_free(topost); hashdb_free(hashdb); if (chdir(cwd)) { return error(PW_ERR_RESTORECWD); } return ret; }
/* -Qi */ static int query_info(alpm_db_t *localdb, alpm_list_t *targets) { int ret, hits, found, pkgcount; alpm_list_t *i, *k, *dbcache; alpm_pkg_t *pkg; ret = pkgcount = hits = found = 0; dbcache = alpm_db_get_pkgcache(localdb); for (i = targets; i; i = i->next, ++pkgcount) { found = 0; for (k = dbcache; k; k = k->next) { pkg = k->data; if (!strcmp(i->data, alpm_pkg_get_name(pkg))) { if (hits++) { printf("\n"); } found = 1; pacman_pkgdump(pkg, PKG_FROM_LOCAL); break; } } if (!found) { if (pkgcount) { printf("\n"); } ret = -1; pw_fprintf(PW_LOG_ERROR, stderr, "package %s not found\n", i->data); } } return ret; }
void build_dep_graph(struct graph **graph, struct pw_hashdb *hashdb, alpm_list_t *targets, int resolve_lvl) { if (!graph) { return; } if (!*graph) { *graph = graph_new((pw_hash_fn) sdbm, (pw_hashcmp_fn) strcmp); } struct stack *st = stack_new(sizeof(struct pkgpair)); struct hash_table *resolved = hash_new(HASH_TABLE, (pw_hash_fn) sdbm, (pw_hashcmp_fn) strcmp); struct hash_table *immediate = hash_new(HASH_TABLE, (pw_hash_fn) sdbm, (pw_hashcmp_fn) strcmp); int ret; struct pkgpair pkgpair, deppkg; alpm_list_t *i; alpm_list_t *deps; CURL *curl; curl = curl_easy_new(); if (!curl) { error(PW_ERR_CURL_INIT); return; } /* Push all packages down stack */ for (i = targets; i; i = i->next) { pkgpair.pkgname = i->data; pkgpair.pkg = NULL; stack_push(st, &pkgpair); } while (!stack_empty(st)) { stack_pop(st, &pkgpair); deps = NULL; if (hash_search(resolved, (void *) pkgpair.pkgname)) { goto cleanup_deps; } ret = crawl_resolve(curl, hashdb, &pkgpair, &deps, resolve_lvl); if (ret) { pw_fprintf(PW_LOG_ERROR, stderr, "Error in resolving packages.\n"); goto cleanup; } for (i = deps; i; i = i->next) { deppkg.pkgname = i->data; deppkg.pkg = NULL; /* immediate vs thorough resolve */ should_we_continue_resolving(curl, hashdb, st, &deppkg, resolve_lvl); /* dep --> current */ graph_add_edge(*graph, i->data, (void *) pkgpair.pkgname); } hash_insert(resolved, (void *) pkgpair.pkgname); /* Add immediate dependencies, for pretty printing purposes */ add_immediate_deps(hashdb, pkgpair.pkgname, deps, immediate); cleanup_deps: alpm_list_free(deps); } cleanup: hash_free(resolved); hash_free(immediate); stack_free(st); curl_easy_cleanup(curl); }
int powaur_query(alpm_list_t *targets) { alpm_db_t *localdb = alpm_option_get_localdb(config->handle); if (!localdb) { return error(PW_ERR_INIT_LOCALDB); } alpm_list_t *dblist = NULL; alpm_list_t *i, *j, *dbcache; alpm_pkg_t *pkg, *spkg; int ret = 0, found; /* -i and -s conflicting options */ if (config->op_q_info && config->op_q_search) { pw_fprintf(PW_LOG_ERROR, stderr, "-i (info) and -s (search) are " "mutually exclusive.\n"); return -1; } /* No targets */ if (targets == NULL) { dblist = alpm_list_add(dblist, localdb); if (config->op_q_info) { /* -Qi, detailed info */ ret = pacman_db_dump(PKG_FROM_LOCAL, DUMP_Q_INFO); } else if (config->op_q_search) { /* -Qs * repo/pkg ver (grp) * desc */ ret = pacman_db_dump(PKG_FROM_LOCAL, DUMP_Q_SEARCH); } else { /* -Q * repo/pkg ver (grp) */ ret = pacman_db_dump(PKG_FROM_LOCAL, DUMP_Q); } alpm_list_free(dblist); return ret; } if (config->op_q_info) { ret = query_info(localdb, targets); } else if (config->op_q_search) { ret = query_search(localdb, targets->data); } else { /* Plain -Q */ alpm_list_t *sdbs = alpm_option_get_syncdbs(config->handle); dbcache = alpm_db_get_pkgcache(localdb); for (i = targets; i; i = i->next) { found = 0; for (j = dbcache; j; j = j->next) { pkg = j->data; if (!strcmp(i->data, alpm_pkg_get_name(pkg))) { print_pkg_pretty(sdbs, pkg, DUMP_Q); found = 1; break; } } if (!found) { printf("package \"%s\" not found\n", i->data); ret = -1; } } } return ret; }
/* fp is guaranteed to be non-NULL. * returns 0 on success, -1 on failure, 1 if not all options specified */ int parse_powaur_config(FILE *fp) { int ret, parsed; char buf[PATH_MAX]; char *line, *key, *val; size_t len; ret = parsed = 0; while (line = fgets(buf, PATH_MAX, fp)) { line = strtrim(line); len = strlen(line); /* Ignore empty lines and comments */ if (!len || line[0] == '#') { continue; } val = strchr(line, '='); if (!val) { continue; } *val = 0; ++val; key = strtrim(line); val = strtrim(val); if (!strcmp(key, "Editor")) { if (powaur_editor) { pw_fprintf(PW_LOG_ERROR, stderr, "%s%sRepeated Editor option!\n", comstrs.tab, comstrs.tab); ret = -1; goto cleanup; } powaur_editor = strdup(val); if (!powaur_editor) { ret = -1; goto cleanup; } ++parsed; pw_printf(PW_LOG_DEBUG, "%s%sParsed Editor = %s\n", comstrs.tab, comstrs.tab, powaur_editor); } else if (!strcmp(key, "TmpDir")) { if (powaur_dir) { pw_fprintf(PW_LOG_ERROR, stderr, "%s%sRepeated TmpDir option!\n", comstrs.tab, comstrs.tab); ret = -1; goto cleanup; } powaur_dir = strdup(val); if (!powaur_dir) { ret = -1; goto cleanup; } ++parsed; pw_printf(PW_LOG_DEBUG, "%s%sParsed TmpDir = %s\n", comstrs.tab, comstrs.tab, powaur_dir); } } cleanup: if (ret) { if (!parsed) { free(powaur_editor); free(powaur_dir); powaur_editor = powaur_dir = NULL; return -1; } else { return 1; } } return 0; }
/* returns 0 upon success. * returns -1 upon failure to change dir / download PKGBUILD / install package */ int powaur_sync(alpm_list_t *targets) { alpm_list_t *i; int ret, status; char orgdir[PATH_MAX]; CURL *curl; ret = status = 0; curl = curl_easy_new(); if (!curl) { return error(PW_ERR_CURL_INIT); } /* Makes no sense to run info and search tgt */ if (config->op_s_search && config->op_s_info) { pw_fprintf(PW_LOG_ERROR, stderr, "-s (search) and -i (info) are mutually exclusive\n"); ret = -1; goto final_cleanup; } else if (config->op_s_check && !config->op_s_upgrade) { pw_fprintf(PW_LOG_ERROR, stderr, "--check must be used with -u!\n"); ret = -1; goto final_cleanup; } /* -Su, checks packages against AUR */ if (config->op_s_upgrade) { ret = sync_upgrade(curl, targets); goto final_cleanup; } if (targets == NULL) { if (config->op_s_search) { ret = pacman_db_dump(PKG_FROM_SYNC, DUMP_S_SEARCH); } else if (config->op_s_info) { ret = pacman_db_dump(PKG_FROM_SYNC, DUMP_S_INFO); } else { pw_fprintf(PW_LOG_ERROR, stderr, "No targets specified for sync\n"); ret = -1; } goto final_cleanup; } if (config->op_s_search) { /* Search for packages on AUR */ ret = sync_search(curl, targets); goto final_cleanup; } else if (config->op_s_info) { ret = sync_info(curl, targets); goto final_cleanup; } /* Save our current directory */ if (!getcwd(orgdir, PATH_MAX)) { ret = error(PW_ERR_GETCWD); goto final_cleanup; } if (ret = chdir(powaur_dir)) { error(PW_ERR_CHDIR, powaur_dir); goto cleanup; } /* -S */ ret = sync_targets(curl, targets); cleanup: if (chdir(orgdir)) { ret = error(PW_ERR_RESTORECWD); } final_cleanup: curl_easy_cleanup(curl); return ret ? -1 : 0; }
/* Normal -S, install packages from AUR * returns 0 on success, -1 on failure */ static int sync_targets(CURL *curl, alpm_list_t *targets) { struct pw_hashdb *hashdb = build_hashdb(); struct pkgpair pkgpair; struct pkgpair *pkgpair_ptr; struct aurpkg_t *aurpkg; alpm_pkg_t *lpkg; alpm_list_t *i; alpm_list_t *reinstall, *new_packages, *upgrade, *downgrade, *not_aur; alpm_list_t *aurpkg_list, *final_targets; int vercmp; int joined = 0, ret = 0; reinstall = new_packages = upgrade = downgrade = aurpkg_list = not_aur = NULL; final_targets = NULL; if (!hashdb) { pw_fprintf(PW_LOG_ERROR, stderr, "Failed to create hashdb\n"); goto cleanup; } for (i = targets; i; i = i->next) { aurpkg_list = query_aur(curl, i->data, AUR_QUERY_INFO); if (!aurpkg_list) { not_aur = alpm_list_add(not_aur, i->data); goto free_aurpkg; } /* Check version string */ pkgpair.pkgname = i->data; pkgpair_ptr = hash_search(hashdb->aur, &pkgpair); /* Locally installed AUR */ if (pkgpair_ptr) { aurpkg = aurpkg_list->data; lpkg = pkgpair_ptr->pkg; vercmp = alpm_pkg_vercmp(aurpkg->version, alpm_pkg_get_version(lpkg)); if (vercmp > 0) { upgrade = alpm_list_add(upgrade, i->data); } else if (vercmp == 0) { reinstall = alpm_list_add(reinstall, i->data); } else { downgrade = alpm_list_add(downgrade, i->data); } } else { new_packages = alpm_list_add(new_packages, i->data); } free_aurpkg: alpm_list_free_inner(aurpkg_list, (alpm_list_fn_free) aurpkg_free); alpm_list_free(aurpkg_list); } if (not_aur) { printf("\n%sThese packages are not from the AUR:%s\n", color.bred, color.nocolor); print_list(not_aur); } if (downgrade) { printf("\n%sLocally installed but newer than AUR, ignoring:%s\n", color.bcyan, color.nocolor); print_list(downgrade); } if (reinstall) { printf("\n%sReinstalling:%s\n", color.byellow, color.nocolor); print_list(reinstall); } if (upgrade) { printf("\n%sUpgrading:%s\n", color.bblue, color.nocolor); print_list(upgrade); } if (new_packages) { printf("\n%sSyncing:%s\n", color.bmag, color.nocolor); print_list(new_packages); } printf("\n"); if (config->noconfirm || yesno("Do you wish to proceed?")) { final_targets = alpm_list_join(reinstall, upgrade); final_targets = alpm_list_join(final_targets, new_packages); joined = 1; ret = upgrade_pkgs(final_targets, hashdb); } cleanup: hashdb_free(hashdb); alpm_list_free(downgrade); alpm_list_free(not_aur); if (joined) { alpm_list_free(final_targets); } else { alpm_list_free(reinstall); alpm_list_free(new_packages); alpm_list_free(upgrade); } return ret; }
/* -Su, checks AUR packages */ static int sync_upgrade(CURL *curl, alpm_list_t *targets) { int ret = 0; int cnt = 0; int upgrade_all; struct pkgpair pkgpair; struct pw_hashdb *hashdb = build_hashdb(); if (!hashdb) { pw_fprintf(PW_LOG_ERROR, stderr, "Failed to build hash database."); return -1; } /* Make sure that packages are from AUR */ alpm_list_t *i, *new_targs = NULL; for (i = targets; i; i = i->next) { pkgpair.pkgname = i->data; if (!hash_search(hashdb->aur, &pkgpair)) { if (cnt++) { printf(", "); } pw_printf(PW_LOG_NORM, "%s", i->data); } else { new_targs = alpm_list_add(new_targs, i->data); } } if (cnt > 1) { printf(" are not AUR packages and will not be checked.\n"); } else if (cnt == 1) { printf(" is not an AUR package and will not be checked.\n"); } alpm_list_t *outdated_pkgs = NULL; if (!targets) { /* Check all AUR packages */ outdated_pkgs = get_outdated_pkgs(curl, hashdb, NULL); } else { if (!new_targs) { goto cleanup; } outdated_pkgs = get_outdated_pkgs(curl, hashdb, new_targs); } if (!outdated_pkgs) { pw_printf(PW_LOG_INFO, "All AUR packages are up to date.\n"); goto cleanup; } printf("\n"); pw_printf(PW_LOG_INFO, "Targets:\n"); print_aurpkg_list(outdated_pkgs); printf("\n"); /* --check, don't upgrade */ if (config->op_s_check) { goto cleanup; } upgrade_all = config->noconfirm || yesno("Do you wish to upgrade the above packages?"); if (upgrade_all) { /* Experimental */ alpm_list_t *final_targets = NULL; struct aurpkg_t *aurpkg; for (i = outdated_pkgs; i; i = i->next) { aurpkg = i->data; final_targets = alpm_list_add(final_targets, aurpkg->name); } ret = upgrade_pkgs(final_targets, hashdb); alpm_list_free(final_targets); } cleanup: alpm_list_free_inner(outdated_pkgs, (alpm_list_fn_free) aurpkg_free); alpm_list_free(outdated_pkgs); alpm_list_free(new_targs); hashdb_free(hashdb); return ret; }
/* Returns a list of outdated AUR packages among targets or all AUR packages. * The list and the packages are to be freed by the caller. * * @param curl curl easy handle * @param targets list of strings (package names) that are _definitely_ AUR packages */ static alpm_list_t *get_outdated_pkgs(CURL *curl, struct pw_hashdb *hashdb, alpm_list_t *targets) { alpm_list_t *i; alpm_list_t *outdated_pkgs = NULL; alpm_list_t *pkglist, *targs; struct pkgpair pkgpair; struct pkgpair *pkgpair_ptr; struct aurpkg_t *aurpkg; const char *pkgname, *pkgver; if (targets) { targs = targets; } else { targs = NULL; alpm_list_t *tmp_targs = hash_to_list(hashdb->aur); for (i = tmp_targs; i; i = i->next) { pkgpair_ptr = i->data; targs = alpm_list_add(targs, (void *) pkgpair_ptr->pkgname); } alpm_list_free(tmp_targs); } for (i = targs; i; i = i->next) { pkglist = query_aur(curl, i->data, AUR_QUERY_INFO); if (!pkglist) { continue; } pkgpair.pkgname = i->data; pkgpair_ptr = hash_search(hashdb->aur, &pkgpair); if (!pkgpair_ptr) { /* Shouldn't happen */ pw_fprintf(PW_LOG_ERROR, stderr, "Unable to find AUR package \"%s\"" "in hashdb!\n", i->data); } aurpkg = pkglist->data; pkgver = alpm_pkg_get_version(pkgpair_ptr->pkg); pkgname = i->data; if (alpm_pkg_vercmp(aurpkg->version, pkgver) > 0) { /* Just show outdated package for now */ pw_printf(PW_LOG_INFO, "%s %s is outdated, %s%s%s%s is available\n", pkgname, pkgver, color.bred, aurpkg->version, color.nocolor, color.bold); /* Add to upgrade list */ outdated_pkgs = alpm_list_add(outdated_pkgs, aurpkg); pkglist->data = NULL; } else if (config->verbose) { pw_printf(PW_LOG_INFO, "%s %s is up to date.\n", pkgname, pkgver); } alpm_list_free_inner(pkglist, (alpm_list_fn_free) aurpkg_free); alpm_list_free(pkglist); } if (!targets) { alpm_list_free(targs); } return outdated_pkgs; }
int powaur_backup(alpm_list_t *targets) { int ret = 0; char localdb[PATH_MAX]; struct archive *a; struct archive_entry *entry; struct stat st; char cwd[PATH_MAX]; char backup_dest[PATH_MAX]; char backup[MINI_BUFSZ]; time_t time_now; struct tm tm_st; if (targets != NULL && alpm_list_count(targets) != 1) { pw_fprintf(PW_LOG_ERROR, stderr, "-B only takes 1 argument.\n"); return -1; } a = archive_write_new(); if (!a) { return error(PW_ERR_ARCHIVE_CREATE); } archive_write_set_compression_bzip2(a); archive_write_set_format_pax_restricted(a); /* Filename = pacman-YYYY-MM-DD_HHhMM.tar.bz2 */ time(&time_now); localtime_r(&time_now, &tm_st); strftime(backup, MINI_BUFSZ, "pacman-%Y-%m-%d_%Hh%M.tar.bz2", &tm_st); if (!getcwd(cwd, PATH_MAX)) { error(PW_ERR_GETCWD); ret = -1; goto cleanup; } /* Get full path */ if (targets) { snprintf(backup_dest, PATH_MAX, "%s/%s", targets->data, backup); } else { snprintf(backup_dest, PATH_MAX, "%s/%s", cwd, backup); } if (archive_write_open_filename(a, backup_dest) != ARCHIVE_OK) { PW_SETERRNO(PW_ERR_ARCHIVE_OPEN); ret = -1; goto cleanup; } if (ret = chdir(pacman_dbpath)) { error(PW_ERR_CHDIR, pacman_dbpath); goto restore_cwd; } /* Create entry for the current directory. */ entry = archive_entry_new(); if (!entry) { error(PW_ERR_ARCHIVE_ENTRY); goto restore_cwd; } snprintf(localdb, PATH_MAX, "%s", "local"); if (ret = stat(localdb, &st)) { error(PW_ERR_STAT, localdb); goto free_entry; } archive_entry_set_pathname(entry, localdb); archive_entry_copy_stat(entry, &st); archive_write_header(a, entry); pw_printf(PW_LOG_INFO, "Saving pacman database in %s\n", backup_dest); ret = write_dir_archive(localdb, a); if (!ret) { pw_printf(PW_LOG_INFO, "Pacman database successfully saved in %s\n", backup_dest); } else { pw_fprintf(PW_LOG_ERROR, stderr, "Pacman database not saved.\n"); } free_entry: archive_entry_free(entry); restore_cwd: if (chdir(cwd)) { PW_SETERRNO(PW_ERR_RESTORECWD); ret = -1; } cleanup: archive_write_finish(a); return ret; }
/* Writes a directory to an archive */ static int write_dir_archive(char *dirname, struct archive *a) { int ret = 0; struct archive_entry *entry; struct dirent *dir_entry; DIR *dirp; struct stat st; char filename[PATH_MAX]; char buf[PATH_MAX]; int fd; ssize_t bytesread; dirp = opendir(dirname); if (!dirp) { return error(PW_ERR_OPENDIR); } while (dir_entry = readdir(dirp)) { if (!strcmp(dir_entry->d_name, ".") || !strcmp(dir_entry->d_name, "..")) { continue; } snprintf(filename, PATH_MAX, "%s/%s", dirname, dir_entry->d_name); if (stat(filename, &st)) { pw_fprintf(PW_LOG_ERROR, stderr, "%s: Failed to stat file %s\n", __func__, filename); ret = -1; goto free_entry; } entry = archive_entry_new(); if (!entry) { pw_fprintf(PW_LOG_ERROR, stderr, "%s: Failed to create new entry\n", __func__); ret = -1; goto free_entry; } archive_entry_set_pathname(entry, filename); archive_entry_copy_stat(entry, &st); archive_write_header(a, entry); if (st.st_mode & S_IFDIR) { /* Directory entry. NOTE: Recursion * I don't really like recursion but there doesn't seem to be * a more elegant way out. */ snprintf(filename, PATH_MAX, "%s/%s", dirname, dir_entry->d_name); if (ret = write_dir_archive(filename, a)) { goto free_entry; } } else { fd = open(filename, O_RDONLY); if (fd < 0) { pw_fprintf(PW_LOG_ERROR, stderr, "Cannot open %s\n", filename); goto free_entry; } while (bytesread = read(fd, buf, PATH_MAX)) { archive_write_data(a, buf, bytesread); } close(fd); } free_entry: archive_entry_free(entry); entry = NULL; } closedir(dirp); return ret; }