pkgdb_query.c
1 /*- 2 * Copyright (c) 2011-2025 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org> 4 * Copyright (c) 2011 Will Andrews <will@FreeBSD.org> 5 * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org> 6 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com> 7 * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org> 8 * Copyright (c) 2012 Bryan Drewery <bryan@shatow.net> 9 * Copyright (c) 2013 Gerald Pfeifer <gerald@pfeifer.com> 10 * Copyright (c) 2013-2014 Vsevolod Stakhov <vsevolod@FreeBSD.org> 11 * Copyright (c) 2023 Serenity Cyber Security, LLC 12 * Author: Gleb Popov <arrowd@FreeBSD.org> 13 * 14 * SPDX-License-Identifier: BSD-2-Clause 15 */ 16 17 #include "pkg/vec.h" 18 #ifdef HAVE_CONFIG_H 19 #include "pkg_config.h" 20 #endif 21 22 #include <assert.h> 23 #include <errno.h> 24 #include <regex.h> 25 #include <grp.h> 26 #if __has_include(<libutil.h>) 27 #include <libutil.h> 28 #endif 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <stdbool.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <signal.h> 35 36 #include <sqlite3.h> 37 38 #include "pkg.h" 39 #include "private/event.h" 40 #include "private/pkg.h" 41 #include "private/pkgdb.h" 42 #include "private/utils.h" 43 44 const char * 45 pkgdb_get_pattern_query(const char *pattern, match_t match) 46 { 47 char *checkorigin = NULL; 48 char *checkflavor = NULL; 49 const char *comp = NULL; 50 51 if (pattern != NULL) { 52 checkorigin = strchr(pattern, '/'); 53 if (checkorigin != NULL) 54 checkflavor = strchr(checkorigin, '@'); 55 } 56 57 switch (match) { 58 case MATCH_ALL: 59 comp = ""; 60 break; 61 case MATCH_INTERNAL: 62 comp = " WHERE p.name = ?1"; 63 break; 64 case MATCH_EXACT: 65 if (pkgdb_case_sensitive()) { 66 if (checkorigin == NULL) 67 comp = " WHERE (p.name = ?1 OR p.name || '-' || version = ?1)"; 68 else if (checkflavor == NULL) 69 comp = " WHERE (origin = ?1 OR categories.name || substr(origin, instr(origin, '/')) = ?1)"; 70 else 71 comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor = ?1)"; 72 } else { 73 if (checkorigin == NULL) 74 comp = " WHERE (p.name = ?1 COLLATE NOCASE OR " 75 "p.name || '-' || version = ?1 COLLATE NOCASE)"; 76 else if (checkflavor == NULL) 77 comp = " WHERE (origin = ?1 COLLATE NOCASE OR categories.name || substr(origin, instr(origin, '/')) = ?1 COLLATE NOCASE)"; 78 else 79 comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor = ?1 COLLATE NOCASE)"; 80 } 81 break; 82 case MATCH_GLOB: 83 if (pkgdb_case_sensitive()) { 84 if (checkorigin == NULL) 85 comp = " WHERE (p.name GLOB ?1 " 86 "OR p.name || '-' || version GLOB ?1)"; 87 else if (checkflavor == NULL) 88 comp = " WHERE (origin GLOB ?1 OR categories.name || substr(origin, instr(origin, '/')) GLOB ?1)"; 89 else 90 comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor GLOB ?1)"; 91 } else { 92 if (checkorigin == NULL) 93 comp = " WHERE (lower(p.name) GLOB lower(?1) " 94 "OR lower(p.name || '-' || version) GLOB lower(?1) )"; 95 else if (checkflavor == NULL) 96 comp = " WHERE (lower(origin) GLOB lower(?1) OR lower(categories.name || substr(origin, instr(origin, '/'))) GLOB lower(?1))"; 97 else 98 comp = "WHERE (lower(categories.name || substr(origin, instr(origin, '/')) || '@' || flavor) GLOB lower(?1))"; 99 } 100 break; 101 case MATCH_REGEX: 102 if (checkorigin == NULL) 103 comp = " WHERE (p.name REGEXP ?1 " 104 "OR p.name || '-' || version REGEXP ?1)"; 105 else if (checkflavor == NULL) 106 comp = " WHERE (origin REGEXP ?1 OR categories.name || substr(origin, instr(origin, '/')) REGEXP ?1)"; 107 else 108 comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor REGEXP ?1)"; 109 break; 110 } 111 112 return (comp); 113 } 114 115 struct pkgdb_it * 116 pkgdb_query_cond(struct pkgdb *db, const char *cond, const char *pattern, match_t match) 117 { 118 char sql[BUFSIZ]; 119 sqlite3_stmt *stmt; 120 const char *comp = NULL; 121 122 assert(db != NULL); 123 124 if (match != MATCH_ALL && (pattern == NULL || pattern[0] == '\0')) 125 return (NULL); 126 127 comp = pkgdb_get_pattern_query(pattern, match); 128 129 if (cond) { 130 sqlite3_snprintf(sizeof(sql), sql, 131 "WITH flavors AS " 132 " (SELECT package_id, value.annotation AS flavor FROM pkg_annotation " 133 " LEFT JOIN annotation tag ON pkg_annotation.tag_id = tag.annotation_id " 134 " LEFT JOIN annotation value ON pkg_annotation.value_id = value.annotation_id " 135 " WHERE tag.annotation = 'flavor') " 136 "SELECT DISTINCT(p.id), origin, p.name, p.name as uniqueid, " 137 " version, comment, desc, " 138 " message, arch, maintainer, www, " 139 " prefix, flatsize, licenselogic, automatic, " 140 " locked, time, manifestdigest, vital " 141 " FROM packages AS p " 142 " LEFT JOIN pkg_categories ON p.id = pkg_categories.package_id " 143 " LEFT JOIN categories ON categories.id = pkg_categories.category_id " 144 " LEFT JOIN flavors ON flavors.package_id = p.id " 145 " %s %s (%s) ORDER BY p.name;", 146 comp, pattern == NULL ? "WHERE" : "AND", cond + 7); 147 } else if (match == MATCH_INTERNAL) { 148 sqlite3_snprintf(sizeof(sql), sql, 149 "SELECT DISTINCT(p.id), origin, p.name, p.name as uniqueid, " 150 "version, comment, desc, " 151 "message, arch, maintainer, www, " 152 "prefix, flatsize, licenselogic, automatic, " 153 "locked, time, manifestdigest, vital " 154 "FROM packages AS p " 155 "%s" 156 " ORDER BY p.name", comp); 157 } else { 158 sqlite3_snprintf(sizeof(sql), sql, 159 "WITH flavors AS " 160 " (SELECT package_id, value.annotation AS flavor FROM pkg_annotation " 161 " LEFT JOIN annotation tag ON pkg_annotation.tag_id = tag.annotation_id " 162 " LEFT JOIN annotation value ON pkg_annotation.value_id = value.annotation_id " 163 " WHERE tag.annotation = 'flavor') " 164 "SELECT DISTINCT(p.id), origin, p.name, p.name as uniqueid, " 165 "version, comment, desc, " 166 "message, arch, maintainer, www, " 167 "prefix, flatsize, licenselogic, automatic, " 168 "locked, time, manifestdigest, vital " 169 "FROM packages AS p " 170 "LEFT JOIN pkg_categories ON p.id = pkg_categories.package_id " 171 "LEFT JOIN categories ON categories.id = pkg_categories.category_id " 172 "LEFT JOIN flavors ON flavors.package_id = p.id " 173 "%s" 174 " ORDER BY p.name", comp); 175 } 176 177 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 178 return (NULL); 179 180 if (match != MATCH_ALL) 181 sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT); 182 pkgdb_debug(4, stmt); 183 184 return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE)); 185 } 186 187 struct pkgdb_it * 188 pkgdb_query(struct pkgdb *db, const char *pattern, match_t match) 189 { 190 return pkgdb_query_cond(db, NULL, pattern, match); 191 } 192 193 bool 194 pkgdb_file_exists(struct pkgdb *db, const char *path) 195 { 196 sqlite3_stmt *stmt; 197 char sql[BUFSIZ]; 198 bool ret = false; 199 200 assert(db != NULL); 201 202 if (path == NULL) 203 return (false); 204 205 sqlite3_snprintf(sizeof(sql), sql, 206 "select path from files where path = ?1;"); 207 208 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 209 return (ret); 210 211 sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT); 212 pkgdb_debug(4, stmt); 213 214 if (sqlite3_step(stmt) != SQLITE_DONE) { 215 ret = true; 216 } 217 218 sqlite3_finalize(stmt); 219 return (ret); 220 } 221 222 struct pkgdb_it * 223 pkgdb_query_which(struct pkgdb *db, const char *path, bool glob) 224 { 225 sqlite3_stmt *stmt; 226 char sql[BUFSIZ]; 227 228 assert(db != NULL); 229 230 if (path == NULL) 231 return (NULL); 232 233 sqlite3_snprintf(sizeof(sql), sql, 234 "SELECT p.id, p.origin, p.name, p.name as uniqueid, " 235 "p.version, p.comment, p.desc, " 236 "p.message, p.arch, p.maintainer, p.www, " 237 "p.prefix, p.flatsize, p.time " 238 "FROM packages AS p " 239 "LEFT JOIN files AS f ON p.id = f.package_id " 240 "WHERE f.path %s ?1 GROUP BY p.id;", glob ? "GLOB" : "="); 241 242 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 243 return (NULL); 244 245 sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT); 246 pkgdb_debug(4, stmt); 247 248 return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE)); 249 } 250 251 struct pkgdb_it * 252 pkgdb_query_shlib_require(struct pkgdb *db, const char *shlib) 253 { 254 sqlite3_stmt *stmt; 255 const char sql[] = "" 256 "SELECT p.id, p.origin, p.name, p.name as uniqueid, " 257 "p.version, p.comment, p.desc, " 258 "p.message, p.arch, p.maintainer, p.www, " 259 "p.prefix, p.flatsize, p.time " 260 "FROM packages AS p, pkg_shlibs_required AS ps, shlibs AS s " 261 "WHERE p.id = ps.package_id " 262 "AND ps.shlib_id = s.id " 263 "AND s.name = ?1;"; 264 265 assert(db != NULL); 266 267 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 268 return (NULL); 269 270 sqlite3_bind_text(stmt, 1, shlib, -1, SQLITE_TRANSIENT); 271 pkgdb_debug(4, stmt); 272 273 return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE)); 274 } 275 276 struct pkgdb_it * 277 pkgdb_query_shlib_provide(struct pkgdb *db, const char *shlib) 278 { 279 sqlite3_stmt *stmt; 280 const char sql[] = "" 281 "SELECT p.id, p.origin, p.name, p.name as uniqueid, " 282 "p.version, p.comment, p.desc, " 283 "p.message, p.arch, p.maintainer, p.www, " 284 "p.prefix, p.flatsize, p.manifestdigest, p.time " 285 "FROM packages AS p, pkg_shlibs_provided AS ps, shlibs AS s " 286 "WHERE p.id = ps.package_id " 287 "AND ps.shlib_id = s.id " 288 "AND s.name = ?1;"; 289 290 assert(db != NULL); 291 292 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 293 return (NULL); 294 295 sqlite3_bind_text(stmt, 1, shlib, -1, SQLITE_TRANSIENT); 296 pkgdb_debug(4, stmt); 297 298 return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE)); 299 } 300 301 struct pkgdb_it * 302 pkgdb_query_require(struct pkgdb *db, const char *req) 303 { 304 sqlite3_stmt *stmt; 305 const char sql[] = "" 306 "SELECT p.id, p.origin, p.name, p.name as uniqueid, " 307 "p.version, p.comment, p.desc, " 308 "p.message, p.arch, p.maintainer, p.www, " 309 "p.prefix, p.flatsize, p.time " 310 "FROM packages AS p, pkg_requires AS ps, requires AS s " 311 "WHERE p.id = ps.package_id " 312 "AND ps.require_id = s.id " 313 "AND s.require = ?1;"; 314 315 assert(db != NULL); 316 317 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 318 return (NULL); 319 320 sqlite3_bind_text(stmt, 1, req, -1, SQLITE_TRANSIENT); 321 pkgdb_debug(4, stmt); 322 323 return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE)); 324 } 325 326 struct pkgdb_it * 327 pkgdb_query_provide(struct pkgdb *db, const char *req) 328 { 329 sqlite3_stmt *stmt; 330 const char sql[] = "" 331 "SELECT p.id, p.origin, p.name, p.name as uniqueid, " 332 "p.version, p.comment, p.desc, " 333 "p.message, p.arch, p.maintainer, p.www, " 334 "p.prefix, p.flatsize, p.time " 335 "FROM packages AS p, pkg_provides AS ps, provides AS s " 336 "WHERE p.id = ps.package_id " 337 "AND ps.provide_id = s.id " 338 "AND s.provide = ?1;"; 339 340 assert(db != NULL); 341 342 if ((stmt = prepare_sql(db->sqlite, sql)) == NULL) 343 return (NULL); 344 345 sqlite3_bind_text(stmt, 1, req, -1, SQLITE_TRANSIENT); 346 pkgdb_debug(4, stmt); 347 348 return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE)); 349 } 350 351 static bool 352 consider_this_repo(c_charv_t *repos, const char *name) 353 { 354 /* All repositories */ 355 if (repos == NULL) 356 return (true); 357 358 if (repos->len == 0) 359 return (true); 360 361 return (c_charv_contains(repos, name, true)); 362 } 363 364 struct pkgdb_it * 365 pkgdb_repo_query_cond(struct pkgdb *db, const char *cond, const char *pattern, match_t match, 366 const char *repo) 367 { 368 c_charv_t r = vec_init(); 369 struct pkgdb_it *ret; 370 371 if (repo != NULL) 372 vec_push(&r, repo); 373 374 ret = pkgdb_repo_query_cond2(db, cond, pattern, match, &r); 375 vec_free(&r); 376 377 return (ret); 378 } 379 380 struct pkgdb_it * 381 pkgdb_repo_query_cond2(struct pkgdb *db, const char *cond, const char *pattern, match_t match, 382 c_charv_t *repos) 383 { 384 struct pkgdb_it *it; 385 struct pkg_repo_it *rit; 386 387 it = pkgdb_it_new_repo(db); 388 389 vec_foreach(db->repos, i) { 390 if (consider_this_repo(repos, db->repos.d[i]->name)) { 391 if (pattern != NULL && *pattern == '@') 392 rit = db->repos.d[i]->ops->groupquery(db->repos.d[i], pattern + 1, match); 393 else 394 rit = db->repos.d[i]->ops->query(db->repos.d[i], cond, pattern, match); 395 if (rit != NULL) 396 pkgdb_it_repo_attach(it, rit); 397 } 398 } 399 400 return (it); 401 } 402 403 struct pkgdb_it *pkgdb_repo_query(struct pkgdb *db, const char *pattern, 404 match_t match, const char *repo) 405 { 406 return pkgdb_repo_query_cond(db, NULL, pattern, match, repo); 407 } 408 409 struct pkgdb_it *pkgdb_repo_query2(struct pkgdb *db, const char *pattern, 410 match_t match, c_charv_t *repos) 411 { 412 return pkgdb_repo_query_cond2(db, NULL, pattern, match, repos); 413 } 414 415 struct pkgdb_it * 416 pkgdb_repo_shlib_require(struct pkgdb *db, const char *require, c_charv_t *repos) 417 { 418 struct pkgdb_it *it; 419 struct pkg_repo_it *rit; 420 421 it = pkgdb_it_new_repo(db); 422 423 vec_foreach(db->repos, i) { 424 if (consider_this_repo(repos, db->repos.d[i]->name)) { 425 if (db->repos.d[i]->ops->shlib_required != NULL) { 426 rit = db->repos.d[i]->ops->shlib_required(db->repos.d[i], require); 427 if (rit != NULL) 428 pkgdb_it_repo_attach(it, rit); 429 } 430 } 431 } 432 433 return (it); 434 } 435 436 struct pkgdb_it * 437 pkgdb_repo_shlib_provide(struct pkgdb *db, const char *require, c_charv_t *repos) 438 { 439 struct pkgdb_it *it; 440 struct pkg_repo_it *rit; 441 442 it = pkgdb_it_new_repo(db); 443 444 vec_foreach(db->repos, i) { 445 if (consider_this_repo(repos, db->repos.d[i]->name)) { 446 if (db->repos.d[i]->ops->shlib_required != NULL) { 447 rit = db->repos.d[i]->ops->shlib_provided(db->repos.d[i], require); 448 if (rit != NULL) 449 pkgdb_it_repo_attach(it, rit); 450 } 451 } 452 } 453 454 return (it); 455 } 456 457 struct pkgdb_it * 458 pkgdb_repo_require(struct pkgdb *db, const char *require, c_charv_t *repo) 459 { 460 struct pkgdb_it *it; 461 struct pkg_repo_it *rit; 462 463 it = pkgdb_it_new_repo(db); 464 465 vec_foreach(db->repos, i) { 466 if (consider_this_repo(repo, db->repos.d[i]->name)) { 467 if (db->repos.d[i]->ops->required != NULL) { 468 rit = db->repos.d[i]->ops->required(db->repos.d[i], require); 469 if (rit != NULL) 470 pkgdb_it_repo_attach(it, rit); 471 } 472 } 473 } 474 475 return (it); 476 } 477 478 struct pkgdb_it * 479 pkgdb_repo_provide(struct pkgdb *db, const char *require, c_charv_t *repo) 480 { 481 struct pkgdb_it *it; 482 struct pkg_repo_it *rit; 483 484 it = pkgdb_it_new_repo(db); 485 486 vec_foreach(db->repos, i) { 487 if (consider_this_repo(repo, db->repos.d[i]->name)) { 488 if (db->repos.d[i]->ops->required != NULL) { 489 rit = db->repos.d[i]->ops->provided(db->repos.d[i], require); 490 if (rit != NULL) 491 pkgdb_it_repo_attach(it, rit); 492 } 493 } 494 } 495 496 return (it); 497 } 498 499 struct pkgdb_it * 500 pkgdb_repo_search(struct pkgdb *db, const char *pattern, match_t match, 501 pkgdb_field field, pkgdb_field sort, const char *repo) 502 { 503 c_charv_t r = vec_init(); 504 struct pkgdb_it *ret; 505 506 if (repo != NULL) 507 vec_push(&r, repo); 508 509 ret = pkgdb_repo_search2(db, pattern, match, field, sort, &r); 510 vec_free(&r); 511 512 return (ret); 513 } 514 515 struct pkgdb_it * 516 pkgdb_repo_search2(struct pkgdb *db, const char *pattern, match_t match, 517 pkgdb_field field, pkgdb_field sort, c_charv_t *repos) 518 { 519 struct pkgdb_it *it; 520 struct pkg_repo_it *rit; 521 522 it = pkgdb_it_new_repo(db); 523 524 vec_foreach(db->repos, i) { 525 if (consider_this_repo(repos, db->repos.d[i]->name)) { 526 if (db->repos.d[i]->ops->search != NULL) { 527 rit = db->repos.d[i]->ops->search(db->repos.d[i], pattern, match, 528 field, sort); 529 if (rit != NULL) 530 pkgdb_it_repo_attach(it, rit); 531 } 532 if (db->repos.d[i]->ops->groupsearch != NULL) { 533 rit = db->repos.d[i]->ops->groupsearch(db->repos.d[i], pattern, match, field); 534 if (rit != NULL) 535 pkgdb_it_repo_attach(it, rit); 536 } 537 } 538 } 539 540 return (it); 541 } 542 543 struct pkgdb_it * 544 pkgdb_all_search(struct pkgdb *db, const char *pattern, match_t match, 545 pkgdb_field field, pkgdb_field sort, const char *repo) 546 { 547 c_charv_t r = vec_init(); 548 struct pkgdb_it *ret; 549 550 if (repo != NULL) 551 vec_push(&r, repo); 552 553 ret = pkgdb_all_search2(db, pattern, match, field, sort, &r); 554 555 vec_free(&r); 556 557 return (ret); 558 } 559 560 struct pkgdb_it * 561 pkgdb_all_search2(struct pkgdb *db, const char *pattern, match_t match, 562 pkgdb_field field, pkgdb_field sort, c_charv_t *repos) 563 { 564 struct pkgdb_it *it; 565 struct pkg_repo_it *rit; 566 567 568 it = pkgdb_query(db, pattern, match); 569 570 vec_foreach(db->repos, i) { 571 if (consider_this_repo(repos, db->repos.d[i]->name)) { 572 if (db->repos.d[i]->ops->search != NULL) { 573 rit = db->repos.d[i]->ops->search(db->repos.d[i], pattern, match, 574 field, sort); 575 if (rit != NULL) 576 pkgdb_it_repo_attach(it, rit); 577 } 578 } 579 } 580 581 return (it); 582 }