libinnhist(3)

πŸ¦‹ libinnhist(3) β€” InterNetNews Documentation πŸ¦‹

okay bestie, so this is the libinnhist(3) page from InterNetNews, and it's literally all about the history library. Part 1 covers the name and the whole synopsis, so buckle up πŸ’…


✨ NAME ✨

his β€” the routines for managing INN history. that's it, that's the vibe. it's giving "the memory of INN" honestly 🧠✨


🌸 SYNOPSIS 🌸

first things first, you gotta pull in the header, no cap:

#include <inn/history.h>

then we've got two opaque structs being all mysterious and mysterious-adjacent:

struct history;
struct token;

next up, the stats struct β€” it's literally just four ints keeping score of how the lookups went:

struct histstats {
    int hitpos;
    int hitneg;
    int misses;
    int dne;
};

then the flag macros, all defined elsewhere (hence the ..., don't come for me):

#define HIS_RDONLY ...
#define HIS_RDWR ...
#define HIS_CREAT ...
#define HIS_ONDISK ...
#define HIS_INCORE ...
#define HIS_MMAP ...

and the control-request enum, which is a whole mood:

enum {
    HISCTLG_PATH,
    HISCTLS_PATH,
    HISCTLS_SYNCCOUNT,
    HISCTLS_NPAIRS,
    HISCTLS_IGNOREOLD,
    HISCTLS_STATINTERVAL
};

now the functions, in all their glory πŸ‘‘

open a history file β€” path, method, and flags, you know the drill:

struct history *HISopen(const char *path, const char *method,
                        int flags);

close it back up again 🫢:

bool HISclose(struct history *history);

sync it (flush those changes, bestie):

bool HISsync(struct history *history);

set the cache size, very set-the-cache-size of it:

void HISsetcache(struct history *history, size_t size);

look something up by key and get back allll the timestamps plus a token:

bool HISlookup(struct history *history, const char *key,
               time_t *arrived, time_t *posted, time_t *expires,
               TOKEN *token);

just check if it's there β€” no timestamps, no token, minimal effort, love that:

bool HIScheck(struct history *history, const char *key);

write a whole new entry with every field filled in:

bool HISwrite(struct history *history, const char *key,
              time_t arrived, time_t posted, time_t expires,
              const TOKEN *token);

remember one (no expires, no token, keep it simple):

bool HISremember(struct history *history, const char *key,
                 time_t arrived, time_t posted);

replace what's already there, full house:

bool HISreplace(struct history *history, const char *key,
                time_t arrived, time_t posted, time_t expires,
                const TOKEN *token);

expire entries β€” this one is a whole callback situation with a path, a reason, a writing bool, a cookie, a threshold, and an exists function pointer. very main character energy πŸ”₯:

bool HISexpire(struct history *history, const char *path,
               const char *reason, bool writing, void *cookie,
               time_t threshold,
               bool (*exists)(void *cookie, time_t arrived,
                              time_t posted, time_t expires,
                              const TOKEN *token));

walk the whole history with a callback β€” also a callback situation, also iconic:

bool HISwalk(struct history *history, const char *reason,
             void *cookie,
             bool (*callback)(void *cookie, const HASH *hash,
                              time_t arrived, time_t posted,
                              time_t expires,
                              const TOKEN *token));

grab those stats we defined earlier:

struct histstats HISstats(struct history *history);

get the last error as a string, because something always goes sideways eventually πŸ’€:

const char *HISerror(struct history *history);

and finally, the catch-all control function for all those enum requests:

bool HISctl(struct history *history, int request, void *val);

okay that's the synopsis fully handled β€” every macro, every struct, every single function signature, all of it, verbatim, no details dropped πŸŽ€ and there's more coming in part 2, so stay tuned bestie ✨

πŸ’– DESCRIPTION πŸ’–

okay so bestie, these functions are literally your all-access pass to the INN history database ✨ They keep key/value pairs in an opaque database whilst also handling the expiry of outdated info β€” like a little bouncer that also cleans up after the party πŸŽ€

The history structure itself? Opaque handle. You get one back from HISopen, and that's all you need to know about it. Main character energy, zero internals πŸ’…


🌸 Opening things up: HISopen

HISopen opens the history file designated by path, using the mode flags, using the specified method. Your flags can be:

  • HIS_RDONLY β€” you want read-only access to the history database, no touching πŸ™…
  • HIS_RDWR β€” read/write access, you're feeling brave πŸ”₯

History methods are defined at build time, and the method currently available is "hisv6". On success you get a newly initialised history handle back, or NULL on failure 😭

You can also logically OR these into flags as a hint to the underlying history manager about how it should handle its data files:

  • HIS_ONDISK β€” the caller would like as much of the data as possible kept on disk (and out of memory)
  • HIS_INCORE β€” the data files should be kept in main memory where possible
  • HIS_MMAP β€” the files should be mmap()ed into the process's address space

HIS_INCORE is typically used where a mass rebuild of the history database is being performed β€” the underlying history manager may assume that the caller will call HISsync() to sync the data files to disk. So like, don't forget, bestie 🫢

And the HIS_CREAT flag? It indicates that the history database should be initialised as new. If any options which affect creation of the database need to be set, you should create an anonymous history handle by calling HISopen with path set to NULL, set any options using HISctl, then open the database by calling HISctl with HISCTLS_PATH. Told you it was a whole ritual ✨


πŸ’… The little housekeeping besties

  • HISclose β€” closes the handle history and deallocates any resources associated with it. Returns false on failure or true on success. Bye bestie πŸ’–
  • HISsync β€” synchronises any outstanding transactions associated with history to disk. Tie up those loose ends, queen πŸ‘‘
  • HISsetcache β€” associates a cache used for speeding up HIScheck with history. The cache will occupy approximately size bytes.

πŸ¦‹ Looking stuff up: HISlookup

HISlookup retrieves a token from history based on the passed key (normally the Message-ID). If no entry with an associated token can be found, HISlookup returns false πŸ˜”

If a token is found, arrived, expires, and posted get filled in with the message arrival, expiry, and posting times respectively (or zero, if the time component is not available), token gets set to the retrieved token, and the function returns true ✨

Any of arrived, expires, posted, or token may be NULL, in which case that component is just not returned to the caller β€” and that does not affect the return value. Flexible icon behaviour πŸ’…


πŸŽ€ HIScheck, HISwrite, HISremember, HISreplace

HIScheck checks the database history for key (normally the Message-ID). If key has previously been set via HISwrite, HIScheck returns true, else false. Simple as πŸ“

HISwrite writes a new entry to the database history associated with key. arrived, posted, and expired specify the arrival, posting, and expiry time respectively; posted and expired may be specified as <= 0, in which case that component shall be treated as absent in the database. token is associated with the specified key. HISwrite returns true on success, or false on failure.

⚠️ The behaviour when key is not unique with respect to the existing entries in history is unspecified. So maybe don't, bestie 😭

HISremember writes a new entry to the database history associated with key, merely remembering that this key has been seen, together with its arrival time arrived and also its posting time posted, if known. (Otherwise, its posting time may be specified as <= 0 in case it is absent.) HISremember returns true on success, or false on failure.

⚠️ Same warning, same vibe: the behaviour when key is not unique with respect to the existing entries in history is unspecified.

HISreplace replaces an existing entry in the database history, associated with key. arrived, posted, expired specify the arrival, posting and expiry time respectively; posted and expired may be specified as <= 0, in which case that component shall be treated as absent in the database. token is associated with the specified key; if NULL then the history database merely remembers that this key has been seen, together with its arrival time. HISreplace returns true on success, or false on failure. Glow-up complete πŸ’–


πŸ”₯ Expiring it all: HISexpire

HISexpire expires the history database associated with history, creating a new, replacement, database in the same location if path is NULL, or in path if not NULL. If path is not NULL then the replacement of the old history database with the new one is assumed to be performed out of band by the caller β€” that's on you, babe ✨

The writing flag is normally passed as true. If you wish to inhibit writing of the new database (and so merely see the callbacks), writing may be set false πŸ‘€

If the underlying history mechanism needs to pause the server, the reason string is used as the argument to the ctlinnd pause command β€” and as such the server should be reserved by the caller prior to calling HISexpire. If the caller wishes to inhibit pausing of the server, passing NULL will achieve this. If reason is not NULL, then on successful return from HISexpire the server will be left paused and the caller should unpause it. Don't leave it hanging, bestie 🫢

The history database is scanned, and entries with an associated storage token are passed to the discrimination function exists.

  • If exists() returns false, it indicates that the stored entity associated with token is no longer available (or no longer required), and therefore the associated history entry may be expired once it meets the threshold constraint.
  • If exists() returns true, the entry is kept as-is in the newly expired history database.

The exists function is passed the arrival, posting and expiry times, in addition to the token associated with the entry. Note that posting and/or expiry may be zero, but the token will never be NULL (such entries are handled solely via the threshold mechanism). The storage token passed to the discrimination function may be updated if required (for example, as might be needed by a hierarchical storage management implementation). Slay, token, evolve πŸ’…

Entries in the database with a posting time less than threshold with no token associated with them are deleted from the database. In case the posting time is unknown, the arrival time is used instead.

The parameter cookie is passed to the discrimination function, and may be used for any purpose required by the caller ✨

⚠️ If the discrimination function attempts to access the underlying database (for read or write) during the callback, the behaviour is unspecified. Keep your hands to yourself, callback 😭

🌈 Walking the whole thing: HISwalk

HISwalk provides an iteration function for the specified history database. For every entry in the history database, callback is invoked, passing the cookie, the message-ID hash, arrival, posting, and expiry times, in addition to the token associated with the entry. If the entry has no storage token (a remembered message-ID), token is NULL.

If callback() returns false, the iteration is aborted and HISwalk returns false to the caller. Malformed history lines are silently skipped β€” no drama, no notes πŸ¦‹

To process the entire database in the presence of a running server, reason may be passed; if this argument is not NULL, it is used as an argument to the ctlinnd (reserve|pause|go) commands. If reason is NULL and the server is running, the behaviour of HISwalk is undefined πŸ‘€

⚠️ If the callback function attempts to access the underlying database during the callback, the behaviour is unspecified.

⭐ Stats time: HISstats

HISstats returns statistics on the history cache mechanism; given a handle history, the return value is a struct histstats detailing:

  • "hitpos" β€” the number of times an item was found directly in the cache and known to exist in the underlying history manager πŸ’–
  • "hitneg" β€” the number of times an item was found directly in the cache and known not to exist in the underlying history manager
  • "misses" β€” the number of times an item was not found directly in the cache, but on retrieval from the underlying history manager was found to exist
  • "dne" β€” the number of times an item was not found directly in the cache, but on retrieval from the underlying history manager was found not to exist 😭

Note that the history cache is only checked by HIScheck and only affected by HIScheck, HISwrite, HISremember and HISreplace. Following a call to HISstats the history statistics associated with history are cleared. Fresh start, queen πŸ‘‘


🧚 Errors + control: HISerror and HISctl

HISerror returns a string describing the most recent error associated with history; the format and content of these strings is history manager dependent. Note that on setting an error, the history API will call the warn function from libinn(3) β€” so like, it will make a whole scene about it πŸ“£

HISctl provides a control interface to the underlying history manager. The request argument determines the type of the request and the meaning of the val argument. The values for request are:

  • "HISCTLG_PATH" (const char **) β€” get the base file path which the history handle represents. val should be a pointer to a location of type const char *. The result must not later be passed to free(3).
  • "HISCTLS_PATH" (const char *) β€” set the base file path which this history handle should use; typically this is used after an anonymous handle has been created using HISopen(NULL, ...). val should be a value of type const char * and will be copied before being stored internally.
  • "HISCTLS_SYNCCOUNT" (size_t *) β€” set an upper bound on how many history operations may be pending in core before being synced to permanent storage; 0 indicates unlimited. val should be a pointer to a value of type size_t and will not be modified by the call.
  • "HISCTLS_NPAIRS" (size_t *) β€” set a hint to the underlying history manager as to how many entries there are expected to be in the history database; 0 indicates that an automatic or default sizing should be made. val should be a pointer to a value of type size_t and will not be modified by the call.
  • "HISCTLS_IGNOREOLD" (bool *) β€” instruct the underlying history manager to ignore existing database when creating new ones; typically this option may be set to true if the administrator believes that the existing history database is corrupt and that ignoring it may help. val should be a pointer to a value of type bool and will not be modified by the call.
  • "HISCTLS_STATINTERVAL" (time_t *) β€” for the history v6 and tagged hash managers, set the interval, in seconds, between stat(2)s of the history files checking for replaced files (as happens during expire); this option is typically used by nnrpd(8) like applications. val should be a pointer to a value of type time_t and will not be modified by the call.

πŸŽ€ HISTORY πŸŽ€

Written by Alex Kiernan <alexk@demon.net> for InterNetNews 2.4.0. Giving credit where credit is due, periodt πŸ’…


and that's the whole page bestie β€” this one's from INN 2.7.4, dated 2026-06-12, living in section libinnhist(3). Go forth and expire responsibly πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from
  • arch rolling: inn 2.7.4-4 Β· ISC
  • epel 10: inn-devel 2.7.4-5.el10_3 Β· ISC AND BSD-3-Clause AND BSD-4-Clause AND FSFAP AND (GPL-1.0-or-later OR Artistic-1.0-Perl) AND GPL-3.0-or-later WITH Bison-exception-2.2 AND LicenseRef-Fedora-Public-Domain AND MIT AND NTP
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms