From 383712eefa950c5dc619f8cd5fb60be8e8041502 Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Fri, 19 Oct 2018 08:15:46 +0000 Subject: [PATCH] add support for notes coalesce devminor and devmajor into device --- .gitignore | 1 + Makefile | 7 +- db.sql | 34 ++++++---- lib/dbquery.c | 41 +++++++++++ lib/integ.c | 2 +- lib/notes.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/seterror.c | 31 +++++++++ zpm-note | 147 ---------------------------------------- zpm-syncfs.c | 71 ++++++++++++++----- zpm.h | 26 +++++++ 10 files changed, 362 insertions(+), 178 deletions(-) create mode 100644 lib/notes.c create mode 100644 lib/seterror.c delete mode 100755 zpm-note diff --git a/.gitignore b/.gitignore index 70122a6..0290ec3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ uncompress zpm-addfile zpm-foreach-path +zpm-note zpm-vercmp zpm-verify zpm-extract diff --git a/Makefile b/Makefile index fb88665..1fb6e6c 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ lib/jsw/jsw_rbtree.c JSWOBJ=$(JSWSRC:%.c=%.o) LIBZPMSRC=sha256.c db.c compress.c uncompress.c zpm.c zpm_hash.c \ foreach_path.c vercmp.c findpkg.c quote.c dbquery.c script_hash.c \ - parse.c integ.c + parse.c integ.c seterror.c notes.c LIBZPMOBJ=$(addprefix lib/, $(LIBZPMSRC:%.c=%.o)) @@ -29,7 +29,7 @@ curdir=$(shell pwd) ZPKGBIN=zpm-addfile zpm-extract zpm-init zpm-vercmp zpm-stat zpm-hash \ zpm-findpkg zpm-shell zpm-soneed zpm-foreach-path zpm-parse \ zpm-runscript zpm-soname zpm-syncfs zpm-packagehash zpm-verify \ - zpm-elftype zpm-quote + zpm-elftype zpm-quote zpm-note SCRIPTS=zpm zpm-install zpm-merge zpm-list zpm-preserve zpm-test zpm-log \ zpm-contents zpm-uninstall zpm-pathmod zpm-rmpackage zpm-newpackage \ @@ -142,6 +142,9 @@ zpm-foreach-path: zpm-foreach-path.o libzpm.a sqlite/sqlite3.h zpm-findpkg: zpm-findpkg.o libzpm.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf +zpm-note: zpm-note.o libzpm.a + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf + zpm-syncfs: zpm-syncfs.o libzpm.a libelf.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf -lm diff --git a/db.sql b/db.sql index be71bde..020f228 100644 --- a/db.sql +++ b/db.sql @@ -123,6 +123,7 @@ create table packagefiles ( uid integer, -- numeric uid, generally ignored gid integer, -- numeric gid, generally ignored configuration integer not null default 0, -- boolean if config file + confhash text, -- last hash on disk filetype varchar not null default 'r', -- r regular file -- d directory @@ -132,10 +133,9 @@ create table packagefiles ( -- b block special -- not supported -- c and b device special files add dev number column -- p fifos (i.e. pipe) -- not supported + -- s unix domain socket -- not supported target text, -- link target for links - -- device file dev numbers - devmajor integer, - devminor integer, + device integer, -- device file dev_t hash text, -- null if not a regular file mtime integer, -- seconds since epoch, finer resolution not needed primary key (package,version,release,path), @@ -147,8 +147,8 @@ create table packagefiles ( check (target is null or length(target) between 1 and 4095), check (hash is null or length(hash) between 1 and 1024), check (not (filetype = 'r' and hash is null)), - check (not (filetype = 'c' and (devmajor is null or devminor is null))), - check (not (filetype = 'b' and (devmajor is null or devminor is null))), + check (not (filetype = 'c' and device is null)), + check (not (filetype = 'b' and device is null)), check (filetype in ('r','d','l','h','c','b','p')), check(length(username) between 1 and 256), check(length(groupname) between 1 and 256), @@ -183,8 +183,7 @@ begin configuration = NEW.configuration, filetype = NEW.filetype, target = NEW.target, - devmajor = NEW.devmajor, - devminor = NEW.devminor, + device = NEW.device, hash = NEW.hash, mtime = NEW.mtime where package = OLD.package @@ -368,6 +367,16 @@ create table zpmlog ( info text -- human readable ); +create table notes ( + id integer primary key, -- rowid alias + ts text default (strftime('%Y-%m-%d %H:%M:%f', 'now')), + note text not null, + pkgid text, -- package + path text, -- file path involved + file text, -- hash of file + ack integer default 0 +); + create table history ( ts integer, -- again, probably needs timestamp sub second cmd text, @@ -473,7 +482,7 @@ syncstatus as ( newfiles as ( select distinct path,username,uid,groupname,gid,mode,filetype,mtime,hash, - target,devminor,devmajor + configuration,target,device, null as ohash from syncstatus SS where path not in (select path from syncstatus where rstatus in ('installed', 'updating', 'removing') @@ -486,7 +495,8 @@ modified as ( SS.path, SS.username, SS.uid, SS.groupname, SS.gid, SS.mode, - SS.filetype, SS.mtime, SS.hash, SS.target, SS.devminor, SS.devmajor + SS.filetype, SS.mtime, SS.hash,SS.configuration, SS.target, SS.device, + null as ohash from syncstatus SS join syncstatus OS on SS.path = OS.path and SS.pkgid is not OS.pkgid @@ -507,7 +517,7 @@ needed as ( preserve as ( select distinct path,username,uid,groupname,gid,mode,filetype,mtime,hash, - target,devminor,devmajor + configuration,target,device, null as ohash from syncstatus SS where path in (select library from needed) and SS.rstatus in ('removing', 'removed') @@ -516,7 +526,7 @@ preserve as ( remove as ( select distinct path,username,uid,groupname,gid,mode,filetype,mtime,hash, - target,devminor,devmajor + configuration,target,device, null as ohash from syncstatus SS where path not in ( select path from syncstatus where @@ -529,7 +539,7 @@ remove as ( expired as ( select distinct path,username,uid,groupname,gid,mode,filetype,mtime,hash, - target,devminor,devmajor + configuration,target,device, null as ohash from syncstatus BASE where hash in (select file from elflibraries where file is not null) and path not in (select path from preserve) diff --git a/lib/dbquery.c b/lib/dbquery.c index ab03984..cba9537 100644 --- a/lib/dbquery.c +++ b/lib/dbquery.c @@ -62,6 +62,26 @@ sqlite3_stmt *zpm_dbquery(struct zpm *zpm, char *query, ...) { return st; } +void zpm_db_run(struct zpm *zpm, char *query, ...) { + sqlite3_stmt *st; + va_list args; + int rv; + + va_start(args, query); + st = zpm_dbqueryv(zpm, query, args); + va_end(args); + + rv = sqlite3_step(st); + + if (rv != SQLITE_DONE) { + zpm->error = 1; + zpm_seterror(zpm, "db error: %s", sqlite3_errstr(rv)); + } + + sqlite3_finalize(st); + return ; +} + char *zpm_db_string(struct zpm *zpm, char *query, ...) { sqlite3_stmt *st; va_list args; @@ -84,3 +104,24 @@ char *zpm_db_string(struct zpm *zpm, char *query, ...) { sqlite3_finalize(st); return result; } + +int zpm_db_int(struct zpm *zpm, char *query, ...) { + sqlite3_stmt *st; + va_list args; + int rv; + int result = 0; + + va_start(args, query); + st = zpm_dbqueryv(zpm, query, args); + va_end(args); + + rv = sqlite3_step(st); + + if (rv == SQLITE_ROW) { + result = sqlite3_column_int(st, 0); + } + /* TODO set error if it's not SQLITE_ROW */ + + sqlite3_finalize(st); + return result; +} diff --git a/lib/integ.c b/lib/integ.c index 6abfcf3..81667ea 100644 --- a/lib/integ.c +++ b/lib/integ.c @@ -155,7 +155,7 @@ int zpm_package_hash(struct zpm *zpm, char *pkgid, char *hash) { /* hash package files */ sql = sqlite3_mprintf("select path, mode, username, groupname, configuration, " - "filetype, target, devmajor, devminor, mtime, hash " + "filetype, target, device, mtime, hash " "from packagefiles_pkgid where pkgid = %Q order by path", pkgid); hash_query(zpm, sql, &d); diff --git a/lib/notes.c b/lib/notes.c new file mode 100644 index 0000000..9622bae --- /dev/null +++ b/lib/notes.c @@ -0,0 +1,180 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include + +#include "sqlite3.h" +#include "zpm.h" + +void zpm_note_ack(struct zpm *zpm, int64_t note) { + char *in = "update notes set ack = 1 where id = %" PRId64; + zpm_db_run(zpm, in, note); +} + +void zpm_note_unack(struct zpm *zpm, int64_t note) { + char *in = "update notes set ack = 0 where id = %" PRId64; + zpm_db_run(zpm, in, note); +} + +void zpm_note_del(struct zpm *zpm, int64_t note) { + char *in = "delete from notes where id = %" PRId64; + zpm_db_run(zpm, in, note); +} + +static char *colstring(sqlite3_stmt *s, int col) { + const char *val; + char *dup = 0; + + val = (const char *)sqlite3_column_text(s, col); + if (val) { + dup = strdup(val); + } + return dup; +} + +/* normally unacked only */ +/* 0x1 = next note, + * 0x2 = include acked + * 0x4 = suppress unack, implies 0x2 + */ +/* TODO filter on pkgid/path/hash if not null */ +int64_t zpm_note(struct zpm *zpm, struct zpm_note *n, unsigned int flags) { + char *op = "=", *ack = " and ack = 0"; + + sqlite3_stmt *st; + int64_t id = 0; + + if (flags & 0x1) { + op = ">"; + } + + if (flags & 0x4) { + ack = " and ack = 1"; + } else if (flags & 0x2) { + ack = ""; + } + + st = zpm_dbquery(zpm, "select id,ts,note,pkgid,path,file,ack " + "from notes where id %s %" PRId64 "%s %s", + op, n->id, ack); + + switch (sqlite3_step(st)) { + case SQLITE_DONE: /* not found */ + break; + case SQLITE_ROW: + n->note = colstring(st, 2); + n->pkgid = colstring(st, 3); + n->path = colstring(st, 4); + n->file = colstring(st, 5); + n->ack = sqlite3_column_int(st, 6); + n->ts = sqlite3_column_int(st, 1); + n->id = sqlite3_column_int64(st, 0); + id = n->id; + break; + default: zpm->error = 1; + zpm->errmsg = strdup(sqlite3_errmsg(zpm->db)); + break; + } + + return id; +} + +/* free any memory */ +void zpm_note_free(struct zpm_note *n) { + free(n->note); + free(n->pkgid); + free(n->path); + free(n->file); +} + +int zpm_notes_available(struct zpm *zpm, int flags) { + int total; + char *sql; + + if (!flags) { + sql = "select count(*) from notes where ack = 0"; + } else { + sql = "select count(*) from notes"; + } + + total = zpm_db_int(zpm, sql); + return total; +} + +/* get up to n notes following. return total number of notes found */ +/* set the first note id to the id before the first one you want + * or to 0 to start at the beginning. This can then + * be iterated up to n notes at a time by setting the id of the + * first one to the id found in the last one. + */ +int zpm_notes(struct zpm *zpm, int n, struct zpm_note *note) { + int64_t id; + int i; + + for (i = 0; i < n; i++) { + id = zpm_note(zpm, note, 1); + if (!id) { + break; + } + note++; + if (i < 0) { + note->id = id; + } + } + + return i; +} + +int64_t zpm_note_next(struct zpm *zpm, struct zpm_note *n) { + return zpm_note(zpm, n, 1); +} + +int64_t zpm_note_add(struct zpm *zpm, char *pkgid, char *path, char *filehash, + char *notefmt, ...) { + sqlite3_stmt *st; + char *note; + va_list ap; + int64_t id = 0; + char *in = "insert into notes (note,pkgid,path,file) values (?,?,?,?);"; + + if (!notefmt) { + return 0; + } + + st = zpm_dbquery(zpm, in); + + if (!st) { + zpm->error = 1; + zpm->errmsg = strdup(sqlite3_errmsg(zpm->db)); + return 0; + } + + va_start(ap, notefmt); + note = sqlite3_vmprintf(notefmt, ap); + va_end(ap); + + if (!note) { + zpm->error = 1; + zpm_seterror(zpm, "can't alloc"); + } + + sqlite3_bind_text(st, 1, note, -1, SQLITE_STATIC); + sqlite3_bind_text(st, 2, pkgid, -1, SQLITE_STATIC); + sqlite3_bind_text(st, 3, path, -1, SQLITE_STATIC); + sqlite3_bind_text(st, 4, filehash, -1, SQLITE_STATIC); + + switch (sqlite3_step(st)) { + case SQLITE_DONE: + id = sqlite3_last_insert_rowid(zpm->db); + break; + default: zpm->error = 1; + zpm->errmsg = strdup(sqlite3_errmsg(zpm->db)); + break; + } + + sqlite3_finalize(st); + sqlite3_free(note); + return id; +} diff --git a/lib/seterror.c b/lib/seterror.c new file mode 100644 index 0000000..337c722 --- /dev/null +++ b/lib/seterror.c @@ -0,0 +1,31 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include + +#include "zpm.h" + +void zpm_seterror(struct zpm *zpm, char *msgfmt, ...) { + char msg[1024]; + va_list ap; + + if (zpm->errmsg) { + free(zpm->errmsg); + zpm->errmsg = 0; + } + + if (msgfmt != NULL) { + va_start(ap, msgfmt); + vsnprintf(msg, sizeof msg, msgfmt, ap); + va_end(ap); + + msg[1023] = 0; + if (zpm->errmsg) { + free(zpm->errmsg); + } + + zpm->errmsg = strdup(msg); + } +} diff --git a/zpm-note b/zpm-note deleted file mode 100755 index f78bdd8..0000000 --- a/zpm-note +++ /dev/null @@ -1,147 +0,0 @@ -#!/bin/sh - -# Admin notes: -# -# Each note is just a file. Stored under /var/spool/admin/notes/[open|ack]// -# -# note: list notes -# note ack - moves note to "acknowledged" spool -# note list [package] show notes for a given package, or all notes -# note delete - delete a note from the system -# note edit [file] - edit or create a note - -# TODO -# discard unchanged new note file -# quiet options to suppress new note name echo -# semi-quiet to just output the number -# renumber? to renumber all notes/reset sequence -# lock the temp files -# clean up unlocked temp files - -: ${ZPMSPOOL:=/var/lib/admin/notes} - -OPEN=$ZPMSPOOL/open - -set -e - -umask 007 - -#cd $SPOOL || { echo "can't chdir to $SPOOL" ; exit 1; } - -[ -z "$1" ] && set list - -quiet= - -cmd=$1 -shift - -findnote() { - file=$(find $ZPMSPOOL -type f -name "$1") - if [ -z "$file" ] ; then - [ -z "$quiet" ] || printf "no such note $1\n" 1>&2 - exit 1 - fi - echo $file -} - -findopen() { - file=$(find $ZPMSPOOL/open -type f -name "$1") - if [ -z "$file" ] ; then - [ -z "$quiet" ] || printf "no such open note $1\n" 1>&2 - exit 1 - fi - echo $file -} - -case $cmd in - category) - notefile=$(findnote $1) - printf "%s\n" $(basename $(dirname $notefile)) - ;; - list) - case $1 in - -a) find $ZPMSPOOL/ack -type f -printf '%P\n' | sort -n - ;; - ?*) if [ -d $ZPMSPOOL/open/$1 ] ; then - find $ZPMSPOOL/open/$1 -type f -printf '%P\n' | sort -n - else - exit 1 - fi - ;; - *) find $ZPMSPOOL/open -type f -printf '%P\n' | sort -n - ;; - esac - ;; - detail) - len=$(find $ZPMSPOOL/open -type f -printf "%P\n" | awk ' { if ( length > x ) { x = length } }END{ print x }') - find $ZPMSPOOL/open -type f -printf "%P\n" | sort -n | while read note ; do - subject=$(head -1 $ZPMSPOOL/open/$note) - date=$(stat -c '%y' $ZPMSPOOL/open/$note | cut -f1 -d' ') - owner=$(stat -c '%U' $ZPMSPOOL/open/$note) - printf '%*s %8s %s %s\n' $len $note $owner "$date" "$subject" - done - ;; - ack) - file=$(findopen $1) - subfile=$(echo $file | sed -e "s|$ZPMSPOOL/open/||") - filedir=$(dirname $subfile) - mkdir -p $ZPMSPOOL/ack/$filedir - mv $file $ZPMSPOOL/ack/$filedir - ;; - new) - # new -- edit a new file for package general (or ZPMNOTEPACKAGE) - # new package -- edit a new note for package - # new package file -- use file for new package note - if [ $# -eq 0 ]; then set ${ZPMNOTEPACKAGE:-general}; fi - - # edit a new file for the note - if [ $# -eq 1 ]; then - tmp=$(mktemp -p $ZPMSPOOL/open) - pkg=$1 - [ -f $ZPMSPOOL/.template ] && cp $ZPMSPOOL/.template $tmp - #flock -n -E3 $tmp vim $tmp - #if [ $? -eq 3 ]; then echo already editing $1; exit 1; fi - ${EDITOR:-vi} $tmp - else - # take note from file or stdin - pkg=$1 ; shift - mkdir -p $ZPMSPOOL/open/$pkg - case $1 in - '-') - tmp=$(mktemp -p $ZPMSPOOL/open) - cat - > $tmp - ;; - *) - tmp=$(mktemp -p $ZPMSPOOL/open) - cp $1 $tmp - ;; - esac - fi - file=$(zpm sequence notes) - mkdir -p $ZPMSPOOL/open/$pkg - mv $tmp $ZPMSPOOL/open/$pkg/$file || { rm -f $tmp; exit 1; } - # TODO run newhooks in $ZPMSPOOL/.hook/newnote - echo "$pkg/$file" - exit 0 - ;; - edit) - file=$(findnote $1) - flock -n -E3 $file vim $file - if [ $? -eq 3 ]; then echo already editing $1; exit 1; fi - ;; - resolve) - file=$(findnote $1) - echo $file - ;; - remove) - file=$(findnote $1) - rm $file - ;; - show) - file=$(findnote $1) - ${PAGER:-less} $file - ;; - *) - echo '$0: unknown command ' $cmd 1>&2 - exit 1 -esac diff --git a/zpm-syncfs.c b/zpm-syncfs.c index 935e715..d2847cf 100644 --- a/zpm-syncfs.c +++ b/zpm-syncfs.c @@ -40,11 +40,12 @@ struct nitem { gid_t gid; char *dest; char *path; - char *hash; + char *hash, *ohash; char *target; time_t mtime; mode_t mode; int ftype; + int configuration; struct timespec times[2]; }; @@ -285,6 +286,7 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { struct config *conf = f; char *dest; struct stat st; + int flags = 0; dest = COL("dest"); if (!dest) return seterror(conf,"no file dest"); @@ -306,21 +308,22 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { } if (S_ISDIR(st.st_mode)) { - if (conf->verbose) { - fprintf(stderr, "rmdir(%s)\n", dest); - } - rmdir(dest); - } else if (S_ISREG(st.st_mode)) { - /* TODO conf to import before removal */ - if (conf->verbose) { - fprintf(stderr, "unlink(%s)\n", dest); - } - if (unlink(dest) == -1) { - return seterror(conf, "can't unlink"); - } - } else { - if (unlink(dest) == -1) { - return seterror(conf, "can't unlink"); + flags = AT_REMOVEDIR; + } + /* TODO check that expected filetype matches actual filetype */ + + if (conf->verbose) { + fprintf(stderr, "%s(%s)\n", flags ? "rmdir" : "unlink", dest); + } + + errno = 0; + + if (unlinkat(AT_FDCWD, dest, flags) == -1) { + switch (errno) { + case ENOENT: + break; + default: + return seterror(conf, "can't unlink"); } } @@ -339,6 +342,7 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { #define D_GID 0x80 #define D_MODE 0x100 #define D_MTIME 0x200 +#define D_OHASH 0x400 #define D_ERROR 0x1000 #define D_STATERROR 0x2000 #define D_RLERROR 0x4000 @@ -376,6 +380,9 @@ static unsigned int file_compare(struct nitem *n, struct stat *st) { if (strcmp(n->hash, ehash) != 0) { diff |= D_HASH; } + if (n->ohash && strcmp(n->ohash, ehash) != 0) { + diff |= D_OHASH; + } } if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) { lsize = readlink(n->dest, link, sizeof link); @@ -460,6 +467,13 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols, n->mode = strtoul(val, NULL, 8); + val = COL("configuration"); + if (!val) { + seterror(conf, "can't determine config status"); + return 0; + } + n->configuration = strtoul(val, NULL, 10); + val = COL("filetype"); if (!val || strlen(val) == 0) { seterror(conf, "can't determine file type"); @@ -467,6 +481,8 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols, } n->ftype = *val; + n->ohash = COL("hash"); + if (n->ftype == 'r') { n->hash = COL("hash"); if (!n->hash) { @@ -601,6 +617,12 @@ static int set_md(struct config *conf, struct nitem *item) { /* flags: 1 = set md, 2 = create leading dirs, 4 = unlink existing file, * 8 = rmdir existing dir, 16 = return true/false */ +#define INS_MD 0x1 +#define INS_CLD 0x2 +#define INS_UNLINK 0x4 +#define INS_RMDIR 0x8 +#define INS_RTF 0x10 +#define INS_ZPMNEW 0x20 static int install(struct config *conf, struct nitem *item, unsigned int flags) { int rv = 1; struct zpm *source; @@ -686,6 +708,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { struct nitem nitem; struct stat existing; int update = 0; + char dest[4096]; /* TODO put the result row in a hash table. May not actually * be faster @@ -747,6 +770,22 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { return install(conf, &nitem, 3); } + if (nitem.configuration) { + /* ohash == nhash, not an update */ + /* fhash == ohash, just update */ + /* fhash != ohash, install as dest.zpmnew, warn */ + /* TODO handle replacing config file + * with config directory */ + if (diffs & D_OHASH) { + if (strlen(nitem.dest) > sizeof dest - 8) { + return seterror(conf,"config file path too long for install as %s.zpmnew", nitem.dest); + } + fprintf(stderr, "installing as .zpmnew\n"); + sprintf(dest, "%s.zpmnew", nitem.dest); + nitem.dest = dest; + } + } + /* file exists in filesystem */ if (sametype) { if (mdsame && hashsame) { diff --git a/zpm.h b/zpm.h index 64e5ac6..c714464 100644 --- a/zpm.h +++ b/zpm.h @@ -176,6 +176,7 @@ int zpm_foreach_path(struct zpm *zpm, char *pkgid, char *where, int (*callback)(void *f, int ncols, char **vals, char **cols), void *data, char **errmsg); + int zpm_script_hash(struct zpm *zpm, char *pkgstr, char *phase, char *hash); int zpm_package_hash(struct zpm *zpm, char *pkgid, char *hash); int zpm_package_sethash(struct zpm *zpm, char *pkgid, char *hash); @@ -183,7 +184,32 @@ int zpm_package_sethash(struct zpm *zpm, char *pkgid, char *hash); sqlite3_stmt *zpm_dbqueryv(struct zpm *zpm, char *query, va_list args); sqlite3_stmt *zpm_dbquery(struct zpm *zpm, char *query, ...); char *zpm_db_string(struct zpm *zpm, char *query, ...); +int zpm_db_int(struct zpm *zpm, char *query, ...); +void zpm_db_run(struct zpm *zpm, char *query, ...); +void zpm_seterror(struct zpm *zpm, char *msgfmt, ...); struct zpm *zpm_clearmem(struct zpm *zpm); +struct zpm_note { + int64_t id; + time_t ts; /* or timespec */ + char *note; + char *pkgid; + char *path; + char *file; + char *hash; + int ack; +}; + +void zpm_note_ack(struct zpm *zpm, int64_t note); +void zpm_note_unack(struct zpm *zpm, int64_t note); +void zpm_note_del(struct zpm *zpm, int64_t note); +int64_t zpm_note(struct zpm *zpm, struct zpm_note *n, unsigned int flags); +void zpm_note_free(struct zpm_note *n); +int zpm_notes(struct zpm *zpm, int n, struct zpm_note *note); +int64_t zpm_note_next(struct zpm *zpm, struct zpm_note *n); +int64_t zpm_note_add(struct zpm *zpm, char *pkgid, char *path, char *filehash, + char *notefmt, ...); +int zpm_notes_available(struct zpm *zpm, int flags); + #endif -- 2.40.0