Skip to content

Commit ad4f7b0

Browse files
committed
feat: add db_get_dependents for reverse dependency lookup
1 parent 074dcd8 commit ad4f7b0

4 files changed

Lines changed: 99 additions & 36 deletions

File tree

ROADMAP.md

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,5 @@
11
# libAPG Roadmap
22

3-
## v1.1 (current)
4-
5-
- [x] Package metadata parsing (JSON)
6-
- [x] File, script, and config installation
7-
- [x] Installed package database (LMDB)
8-
- [x] Checksums: CRC32, MD5, SHA-256
9-
- [x] Package signing: PGP (gpgme) / libsodium
10-
- [x] Hardware-accelerated SHA-256 (x86_64 SHA-NI, AArch64 crypto, RISC-V Zknh)
11-
- [x] Optimized assembly for CRC32 and MD5 on all three architectures
12-
13-
## v1.2 — Dependencies and conflicts
14-
15-
- [x] Dependency resolver (graph, topological sort)
16-
- [x] Conflict and circular dependency detection at install time
17-
- [x] `provides` / `replaces` support during resolution
18-
- [x] API to query: what installing package X will break
19-
20-
## v1.3 — Transactions and rollback
21-
22-
- [x] Rollback on post-install script failure
23-
- [x] Transaction journal in the database
24-
25-
## v1.4 — Database API
26-
27-
- [x] Public API for third-party tools to register installed packages
28-
- [x] Conflict-free parallel installs: no "package not installed" or "file busy" errors when multiple tools write to the DB simultaneously
29-
- [x] Lock-free read path for queries (list, search, get)
30-
- [x] Hooks: pre/post DB write callbacks for tools like custom package helpers
31-
32-
## v1.5 — Security
33-
34-
- [x] Trust chain verification (keyring)
35-
- [x] Install policies: reject unsigned packages, trusted key list
36-
- [x] Sandboxed execution of install scripts (seccomp / namespaces)
37-
- [x] Audit log of all package operations
38-
393
## v2.0 — Stable public API
404

415
- [ ] ABI stabilization and documentation

include/apg/db.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,20 @@ struct package **db_list(struct db_handle *db, int *count);
130130
* owns @p path. Caller must free() the returned string.
131131
*/
132132
char *db_owner(struct db_handle *db, const char *path);
133+
134+
/**
135+
* @brief Find all installed packages that depend on a given package.
136+
*
137+
* Checks direct dependencies and virtual names listed in the target package's
138+
* @c provides field, so removing a provider is flagged even when dependents
139+
* name the virtual package rather than the real one.
140+
*
141+
* @param db Database handle.
142+
* @param pkg_name Name of the package being queried.
143+
* @param count Output parameter set to the number of dependents found.
144+
* @return Heap-allocated array of heap-allocated package name strings, or NULL
145+
* on failure. Caller must free() each element and the array itself.
146+
* @p *count is set to 0 when no dependents are found.
147+
*/
148+
char **db_get_dependents(struct db_handle *db, const char *pkg_name,
149+
int *count);

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ libapg_sources = files(
2020
'src/db/owner.c',
2121
'src/db/journal.c',
2222
'src/db/audit.c',
23+
'src/db/dependents.c',
2324
'src/json.c',
2425
'src/graph/graph.c',
2526
'src/graph/resolve.c',

src/db/dependents.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
// SPDX-FileCopyrightText: 2026 AnmiTaliDev <anmitalidev@nuros.org>
3+
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
#include "../../include/apg/db.h"
8+
#include "../../include/apg/package.h"
9+
#include "../../include/apg/version.h"
10+
11+
static bool
12+
names_match(const char *needle, const char *pkg_name,
13+
const struct str_list *provides)
14+
{
15+
if (strcmp(needle, pkg_name) == 0)
16+
return true;
17+
for (int i = 0; i < provides->count; i++)
18+
if (strcmp(needle, provides->items[i]) == 0)
19+
return true;
20+
return false;
21+
}
22+
23+
char **
24+
db_get_dependents(struct db_handle *db, const char *pkg_name, int *count)
25+
{
26+
*count = 0;
27+
if (!db || !pkg_name)
28+
return NULL;
29+
30+
struct str_list provides = {0};
31+
struct package *target = db_get(db, pkg_name);
32+
if (target && target->meta)
33+
provides = target->meta->provides;
34+
35+
int all_count = 0;
36+
struct package **all = db_list(db, &all_count);
37+
if (!all)
38+
{
39+
package_free(target);
40+
return NULL;
41+
}
42+
43+
int cap = 8;
44+
char **result = malloc(cap * sizeof(char *));
45+
if (!result)
46+
goto out;
47+
48+
for (int i = 0; i < all_count; i++)
49+
{
50+
struct package *p = all[i];
51+
if (!p->meta || !p->meta->name)
52+
continue;
53+
if (strcmp(p->meta->name, pkg_name) == 0)
54+
continue;
55+
56+
bool found = false;
57+
const struct dep_constraint_list *deps = &p->meta->dependencies;
58+
for (int j = 0; j < deps->count && !found; j++)
59+
found = names_match(deps->items[j].name, pkg_name, &provides);
60+
61+
if (!found)
62+
continue;
63+
64+
if (*count == cap)
65+
{
66+
cap *= 2;
67+
char **tmp = realloc(result, cap * sizeof(char *));
68+
if (!tmp)
69+
break;
70+
result = tmp;
71+
}
72+
result[(*count)++] = strdup(p->meta->name);
73+
}
74+
75+
out:
76+
for (int i = 0; i < all_count; i++)
77+
package_free(all[i]);
78+
free(all);
79+
package_free(target);
80+
return result;
81+
}

0 commit comments

Comments
 (0)