int pkgdb_repo_close(sqlite3 *sqlite, bool commit) { int retcode = EPKG_OK; if (sqlite == NULL) return (retcode); if (commit) { if (pkgdb_transaction_commit(sqlite, NULL) != SQLITE_OK) retcode = EPKG_FATAL; } else { if (pkgdb_transaction_rollback(sqlite, NULL) != SQLITE_OK) retcode = EPKG_FATAL; } finalize_prepared_statements(); return (retcode); }
int pkg_repo_binary_close(struct pkg_repo *repo, bool commit) { int retcode = EPKG_OK; sqlite3 *sqlite = PRIV_GET(repo); if (sqlite == NULL) return (retcode); if (commit) { if (pkgdb_transaction_commit(sqlite, NULL) != SQLITE_OK) retcode = EPKG_FATAL; } pkg_repo_binary_finalize_prstatements(); sqlite3_free(sqlite); repo->priv = NULL; return (retcode); }
static int apply_repo_change(struct pkgdb *db, const char *database, const struct repo_changes *repo_changes, const char *updown, int version, int *next_version) { const struct repo_changes *change; bool found = false, in_trans = false; int ret = EPKG_OK; char sql[8192]; char *errmsg; for (change = repo_changes; change->version != -1; change++) { if (change->version == version) { found = true; break; } } if (!found) { pkg_emit_error("Failed to %s \"%s\" repo schema " " version %d (target version %d) " "-- change not found", updown, database, version, REPO_SCHEMA_VERSION); return (EPKG_FATAL); } /* substitute the repo database name */ ret = substitute_into_sql(sql, sizeof(sql), change->sql, database); /* begin transaction */ if (ret == EPKG_OK) { in_trans = true; ret = pkgdb_transaction_begin(db->sqlite, "SCHEMA"); } /* apply change */ if (ret == EPKG_OK) { pkg_debug(4, "Pkgdb: running '%s'", sql); ret = sqlite3_exec(db->sqlite, sql, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { pkg_emit_error("sqlite: %s", errmsg); sqlite3_free(errmsg); ret = EPKG_FATAL; } } /* update repo user_version */ if (ret == EPKG_OK) { *next_version = change->next_version; ret = set_repo_user_version(db->sqlite, database, *next_version); } /* commit or rollback */ if (in_trans) { if (ret != EPKG_OK) pkgdb_transaction_rollback(db->sqlite, "SCHEMA"); if (pkgdb_transaction_commit(db->sqlite, "SCHEMA") != EPKG_OK) ret = EPKG_FATAL; } if (ret == EPKG_OK) { pkg_emit_notice("Repo \"%s\" %s schema %d to %d: %s", database, updown, version, change->next_version, change->message); } return (ret); }
int exec_set(int argc, char **argv) { struct pkgdb *db = NULL; struct pkgdb_it *it = NULL; struct pkg *pkg = NULL; int ch; int i; match_t match = MATCH_EXACT; int64_t newautomatic = -1; bool automatic = false; bool rc = false; const char *changed = NULL; char *newvalue = NULL; char *oldvalue = NULL; unsigned int loads = PKG_LOAD_BASIC; unsigned int sets = 0; unsigned int field = 0, depfield = 0; int retcode; struct option longopts[] = { { "automatic", required_argument, NULL, 'A' }, { "all", no_argument, NULL, 'a' }, { "case-sensitive", no_argument, NULL, 'C' }, { "glob", no_argument, NULL, 'g' }, { "case-insensitive", no_argument, NULL, 'i' }, { "change-origin", required_argument, NULL, 'o' }, { "change-name", required_argument, NULL, 'n' }, { "regex", no_argument, NULL, 'x' }, { "yes", no_argument, NULL, 'y' }, { NULL, 0, NULL, 0 }, }; while ((ch = getopt_long(argc, argv, "+A:aCgio:xyn:", longopts, NULL)) != -1) { switch (ch) { case 'A': sets |= AUTOMATIC; newautomatic = optarg[0] - '0'; if (newautomatic != 0 && newautomatic != 1) errx(EX_USAGE, "Wrong value for -A. " "Expecting 0 or 1, got: %s", optarg); break; case 'a': match = MATCH_ALL; break; case 'C': pkgdb_set_case_sensitivity(true); break; case 'g': match = MATCH_GLOB; break; case 'i': pkgdb_set_case_sensitivity(false); break; case 'o': sets |= ORIGIN; loads |= PKG_LOAD_DEPS; match = MATCH_ALL; changed = "origin"; if (!check_change_values(optarg, &oldvalue, &newvalue, '/')) { errx(EX_USAGE, "Wrong format for -o. " "Expecting oldorigin:neworigin, got: %s", optarg); } break; case 'n': sets |= NAME; loads |= PKG_LOAD_DEPS; match = MATCH_ALL; changed = "name"; if (!check_change_values(optarg, &oldvalue, &newvalue, '\0')) { errx(EX_USAGE, "Wrong format for -n. " "Expecting oldname:newname, got: %s", optarg); } break; case 'x': match = MATCH_REGEX; break; case 'y': yes = true; break; default: free(oldvalue); usage_set(); return (EX_USAGE); } } argc -= optind; argv += optind; if ((argc < 1 && match != MATCH_ALL) || (newautomatic == -1 && newvalue == NULL) || (sets & (NAME|ORIGIN)) == (NAME|ORIGIN)) { usage_set(); return (EX_USAGE); } if (sets & NAME) { field = PKG_SET_NAME; depfield = PKG_SET_DEPNAME; } else if (sets & ORIGIN) { field = PKG_SET_ORIGIN; depfield = PKG_SET_DEPORIGIN; } retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE, PKGDB_DB_LOCAL); if (retcode == EPKG_ENODB) { if (match == MATCH_ALL) return (EX_OK); if (!quiet) warnx("No packages installed. Nothing to do!"); return (EX_OK); } else if (retcode == EPKG_ENOACCESS) { warnx("Insufficient privileges to modify the package database"); return (EX_NOPERM); } else if (retcode != EPKG_OK) { warnx("Error accessing package database"); return (EX_SOFTWARE); } if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) { free(newvalue); return (EX_IOERR); } if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) { pkgdb_close(db); free(newvalue); warnx("Cannot get an exclusive lock on a database, it is locked by another process"); return (EX_TEMPFAIL); } if (pkgdb_transaction_begin(db, NULL) != EPKG_OK) { pkgdb_close(db); free(newvalue); warnx("Cannot start transaction for update"); return (EX_TEMPFAIL); } if (oldvalue != NULL) { match = MATCH_ALL; if ((it = pkgdb_query(db, oldvalue, MATCH_EXACT)) == NULL) { retcode = EX_IOERR; goto cleanup; } if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) { pkg = NULL; /* fprintf(stderr, "%s not installed\n", oldorigin); free(oldorigin); pkgdb_it_free(it); pkgdb_close(db); return (EX_SOFTWARE);*/ } rc = yes; if (!yes) { if (pkg != NULL) rc = query_yesno(false, "Change %S from %S to %S for %n-%v? ", changed, oldvalue, newvalue, pkg, pkg); else rc = query_yesno(false, "Change %S from %S to %S for all dependencies? ", changed, oldvalue, newvalue); } if (pkg != NULL && rc) { if (pkgdb_set(db, pkg, field, newvalue) != EPKG_OK) { retcode = EX_IOERR; goto cleanup; } } pkgdb_it_free(it); } i = 0; do { bool saved_rc = rc; if ((it = pkgdb_query(db, argv[i], match)) == NULL) { retcode = EX_IOERR; goto cleanup; } while (pkgdb_it_next(it, &pkg, loads) == EPKG_OK) { if ((sets & AUTOMATIC) == AUTOMATIC) { pkg_get(pkg, PKG_AUTOMATIC, &automatic); if (automatic == newautomatic) continue; if (!rc) { if (newautomatic) rc = query_yesno(false, "Mark %n-%v as automatically installed? ", pkg, pkg); else rc = query_yesno(false, "Mark %n-%v as not automatically installed? ", pkg, pkg); } if (rc) pkgdb_set(db, pkg, PKG_SET_AUTOMATIC, (int)newautomatic); rc = saved_rc; } if (sets & (ORIGIN|NAME)) { struct pkg_dep *d = NULL; while (pkg_deps(pkg, &d) == EPKG_OK) { /* * Do not query user when he has already * been queried. */ if (pkgdb_set(db, pkg, depfield, oldvalue, newvalue) != EPKG_OK) { retcode = EX_IOERR; goto cleanup; } } } } pkgdb_it_free(it); i++; } while (i < argc); cleanup: free(oldvalue); free(newvalue); pkg_free(pkg); if (retcode == 0) { pkgdb_transaction_commit(db, NULL); } else { pkgdb_transaction_rollback(db, NULL); } pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE); pkgdb_close(db); return (retcode); }
int pkg_create_repo(char *path, bool force, void (progress)(struct pkg *pkg, void *data), void *data) { FTS *fts = NULL; struct thd_data thd_data; int num_workers; size_t len; pthread_t *tids = NULL; struct pkg_dep *dep = NULL; struct pkg_category *category = NULL; struct pkg_license *license = NULL; struct pkg_option *option = NULL; struct pkg_shlib *shlib = NULL; sqlite3 *sqlite = NULL; int64_t package_id; char *errmsg = NULL; int retcode = EPKG_OK; int ret; char *repopath[2]; char repodb[MAXPATHLEN + 1]; char repopack[MAXPATHLEN + 1]; struct archive *a = NULL; struct archive_entry *ae = NULL; if (!is_dir(path)) { pkg_emit_error("%s is not a directory", path); return (EPKG_FATAL); } repopath[0] = path; repopath[1] = NULL; len = sizeof(num_workers); if (sysctlbyname("hw.ncpu", &num_workers, &len, NULL, 0) == -1) num_workers = 6; if ((fts = fts_open(repopath, FTS_PHYSICAL|FTS_NOCHDIR, NULL)) == NULL) { pkg_emit_errno("fts_open", path); retcode = EPKG_FATAL; goto cleanup; } snprintf(repodb, sizeof(repodb), "%s/repo.sqlite", path); snprintf(repopack, sizeof(repopack), "%s/repo.txz", path); if (access(repopack, F_OK) == 0) { a = archive_read_new(); archive_read_support_compression_all(a); archive_read_support_format_tar(a); ret = archive_read_open_filename(a, repopack, 4096); if (ret != ARCHIVE_OK) { /* if we can't unpack it it won't be useful for us */ unlink(repopack); } else { while (archive_read_next_header(a, &ae) == ARCHIVE_OK) { if (!strcmp(archive_entry_pathname(ae), "repo.sqlite")) { archive_entry_set_pathname(ae, repodb); archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS); break; } } } if (a != NULL) archive_read_finish(a); } if ((retcode = initialize_repo(repodb, force, &sqlite)) != EPKG_OK) goto cleanup; if ((retcode = initialize_prepared_statements(sqlite)) != EPKG_OK) goto cleanup; thd_data.root_path = path; thd_data.max_results = num_workers; thd_data.num_results = 0; thd_data.stop = false; thd_data.fts = fts; pthread_mutex_init(&thd_data.fts_m, NULL); STAILQ_INIT(&thd_data.results); thd_data.thd_finished = 0; pthread_mutex_init(&thd_data.results_m, NULL); pthread_cond_init(&thd_data.has_result, NULL); pthread_cond_init(&thd_data.has_room, NULL); /* Launch workers */ tids = calloc(num_workers, sizeof(pthread_t)); for (int i = 0; i < num_workers; i++) { pthread_create(&tids[i], NULL, (void *)&read_pkg_file, &thd_data); } for (;;) { struct pkg_result *r; const char *name, *version, *origin, *comment, *desc; const char *arch, *maintainer, *www, *prefix; int64_t flatsize; lic_t licenselogic; pthread_mutex_lock(&thd_data.results_m); while ((r = STAILQ_FIRST(&thd_data.results)) == NULL) { if (thd_data.thd_finished == num_workers) { break; } pthread_cond_wait(&thd_data.has_result, &thd_data.results_m); } if (r != NULL) { STAILQ_REMOVE_HEAD(&thd_data.results, next); thd_data.num_results--; pthread_cond_signal(&thd_data.has_room); } pthread_mutex_unlock(&thd_data.results_m); if (r == NULL) { break; } if (r->retcode != EPKG_OK) { continue; } /* do not add if package if already in repodb (possibly at a different pkg_path) */ if (run_prepared_statement(EXISTS, r->cksum) != SQLITE_ROW) { ERROR_SQLITE(sqlite); goto cleanup; } if (sqlite3_column_int(STMT(EXISTS), 0) > 0) { continue; } if (progress != NULL) progress(r->pkg, data); pkg_get(r->pkg, PKG_ORIGIN, &origin, PKG_NAME, &name, PKG_VERSION, &version, PKG_COMMENT, &comment, PKG_DESC, &desc, PKG_ARCH, &arch, PKG_MAINTAINER, &maintainer, PKG_WWW, &www, PKG_PREFIX, &prefix, PKG_FLATSIZE, &flatsize, PKG_LICENSE_LOGIC, &licenselogic); try_again: if ((ret = run_prepared_statement(PKG, origin, name, version, comment, desc, arch, maintainer, www, prefix, r->size, flatsize, (int64_t)licenselogic, r->cksum, r->path)) != SQLITE_DONE) { if (ret == SQLITE_CONSTRAINT) { switch(maybe_delete_conflicting(origin, version, r->path)) { case EPKG_FATAL: /* sqlite error */ ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; break; case EPKG_END: /* repo already has newer */ continue; break; default: /* conflict cleared, try again */ goto try_again; break; } } else { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } package_id = sqlite3_last_insert_rowid(sqlite); dep = NULL; while (pkg_deps(r->pkg, &dep) == EPKG_OK) { if (run_prepared_statement(DEPS, pkg_dep_origin(dep), pkg_dep_name(dep), pkg_dep_version(dep), package_id) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } category = NULL; while (pkg_categories(r->pkg, &category) == EPKG_OK) { const char *cat_name = pkg_category_name(category); ret = run_prepared_statement(CAT1, cat_name); if (ret == SQLITE_DONE) ret = run_prepared_statement(CAT2, package_id, cat_name); if (ret != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } license = NULL; while (pkg_licenses(r->pkg, &license) == EPKG_OK) { const char *lic_name = pkg_license_name(license); ret = run_prepared_statement(LIC1, lic_name); if (ret == SQLITE_DONE) ret = run_prepared_statement(LIC2, package_id, lic_name); if (ret != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } option = NULL; while (pkg_options(r->pkg, &option) == EPKG_OK) { if (run_prepared_statement(OPTS, pkg_option_opt(option), pkg_option_value(option), package_id) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } shlib = NULL; while (pkg_shlibs(r->pkg, &shlib) == EPKG_OK) { const char *shlib_name = pkg_shlib_name(shlib); ret = run_prepared_statement(SHLIB1, shlib_name); if (ret == SQLITE_DONE) ret = run_prepared_statement(SHLIB2, package_id, shlib_name); if (ret != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } pkg_free(r->pkg); free(r); } if (pkgdb_transaction_commit(sqlite, NULL) != SQLITE_OK) retcode = EPKG_FATAL; cleanup: if (tids != NULL) { // Cancel running threads if (retcode != EPKG_OK) { pthread_mutex_lock(&thd_data.fts_m); thd_data.stop = true; pthread_mutex_unlock(&thd_data.fts_m); } // Join on threads to release thread IDs for (int i = 0; i < num_workers; i++) { pthread_join(tids[i], NULL); } free(tids); } if (fts != NULL) fts_close(fts); finalize_prepared_statements(); if (sqlite != NULL) sqlite3_close(sqlite); if (errmsg != NULL) sqlite3_free(errmsg); sqlite3_shutdown(); return (retcode); }