The capture / output engine ships as a shared library (liboni_ripper.so) with a public C API so the same pipeline that powers the standalone oni_ripper daemon can be embedded in a long-running host application — a Java service via JNI, a Python pipeline via ctypes or cffi, another C / C++ daemon directly. The CLI binary is a thin wrapper around this library; the engine logic is identical.
| Use the standalone daemon when | Embed the library when |
|---|---|
| The captured stream lands in files / JSON / CSV / Kafka and a downstream consumer reads from there. | Captured changes flow into business logic in the same process — a queue, an event bus, an in-memory cache. |
| Operations team owns the lifecycle (systemd, monitoring, restart policy). | Application owns the lifecycle and you don't want a second supervisor for the capture process. |
| You want the simplest possible deployment — one RPM/DEB install, edit YAML, start the service. | You want stable-layout structs delivered via a callback instead of parsing a file/socket/Kafka payload. |
The single public header is <oni_ripper.h>, installed at /usr/include/. It exposes opaque oni_config_t and oni_engine_t handles, a programmatic config builder mirroring every YAML key, the engine lifecycle, two callback shapes (per-transaction delivery, log line delivery), and a stats accessor.
Per-function signatures and descriptions live on the dedicated C API Reference page; this page covers the surrounding model — the callback shape, a worked example, build / link instructions, and signal handling.
Pick the new embed output mode and register a callback. The engine never writes anywhere else — every committed transaction arrives in your callback as a stable-layout struct:
typedef struct {
const char *op; /* "INSERT" | "UPDATE" | "DELETE" */
const char *owner;
const char *table;
uint64_t lsn;
const char *sql; /* rendered SQL for the row */
} oni_record_t;
typedef struct {
int64_t txid;
int worker_id;
const char *user;
int userid;
time_t started; /* 0 if NO_BEGIN */
time_t committed; /* 0 if unknown */
uint64_t begin_lsn;
uint64_t commit_lsn;
int has_begin;
int has_commit;
int nrecords;
const oni_record_t *records;
} oni_tx_t;
typedef int (*oni_tx_cb_t)(void *user, const oni_tx_t *tx);
The callback runs on the worker thread that captured the transaction. Returning 0 acks: the worker's last_committed_lsn advances and the next start resumes from the next transaction. Returning non-zero halts the engine without advancing the LSN, so the next start replays from the same source position — the right pattern when your downstream queue is backpressured or rejected the message.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <oni_ripper.h>
static oni_engine_t *g_engine;
static int on_tx(void *user, const oni_tx_t *tx) {
int i;
printf("tx %lld w=%d records=%d\n",
(long long)tx->txid, tx->worker_id, tx->nrecords);
for (i = 0; i < tx->nrecords; i++) {
printf(" %s %s.%s lsn=%lx\n",
tx->records[i].op,
tx->records[i].owner,
tx->records[i].table,
(unsigned long)tx->records[i].lsn);
/* Hand off to your queue / event bus / cache here. */
}
return 0; /* ack -> LSN advances */
}
static void on_log(void *user, int sev, const char *tag, const char *msg) {
fprintf(stderr, "[%s] %s\n", tag, msg);
}
static void on_signal(int sig) { oni_engine_stop(g_engine); }
int main(int argc, char **argv) {
char err[512];
oni_config_t *cfg = oni_config_new();
oni_config_set_source(cfg, "stores_demo", "ol_informix1410", NULL, NULL);
oni_config_add_table(cfg, "customer");
oni_config_add_table(cfg, "order*");
oni_config_set_logging(cfg, /*verbose*/ 1, 0,0,0,0,0, /*status*/ 60);
oni_config_output_embed(cfg, on_tx, NULL);
oni_config_set_log_callback(cfg, on_log, NULL);
g_engine = oni_engine_new(cfg, err, sizeof(err));
oni_config_free(cfg);
if (!g_engine) { fprintf(stderr, "engine_new: %s\n", err); return 1; }
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
oni_engine_start(g_engine);
/* The library does NOT install signals - that's our job above.
* Block until our handler triggers oni_engine_stop(). */
while (1) {
oni_stats_t s;
if (oni_engine_stats(g_engine, &s) == 0 && s.active_workers == 0)
break;
sleep(1);
}
oni_engine_free(g_engine);
return 0;
}
The same example ships as examples/embed_simple.c in the source tree.
The recommended way is via pkg-config — the manifest carries the IBM CSDK link-line gotchas (the checkapi.o / libifgls / libthasf chain plus the $INFORMIXDIR/lib/esql rpath) so a one-liner builds:
cc embed_simple.c $(pkg-config --cflags --libs oni_ripper) \
-o embed_simple
Without pkg-config, the link line is:
cc embed.c \
-loni_ripper \
$INFORMIXDIR/lib/esql/checkapi.o \
-L$INFORMIXDIR/lib/esql -lifgls \
-L$INFORMIXDIR/lib -lthasf \
-Wl,-rpath,$INFORMIXDIR/lib/esql:$INFORMIXDIR/lib
The CSDK must be installed and INFORMIXDIR set on the host running the embedder, same as for the standalone binary. See the Install page for the rest of the per-distro prerequisites.
By default the engine writes its tagged log lines ([STATUS], [LSN], [TX], [CRITICAL], [RELOAD], etc.) to stderr. Register a oni_log_cb_t via oni_config_set_log_callback() and every line routes into your application's logging framework instead. The callback receives severity (0=trace, 1=info, 2=warn, 3=critical), the bracketed tag, and the body of the line (no leading [TAG], no trailing newline) so the host can format it however its logger expects.
The library does not install signal handlers. The CLI wrapper installs its own (SIGTERM / SIGINT / SIGHUP / SIGCHLD) because that's a CLI concern. Embedders register whatever fits the host's process model and call oni_engine_stop() / oni_engine_reload() from those handlers. This is non- negotiable: a Java service under a JVM signal manager and a Python pipeline under uWSGI / Gunicorn don't want a second handler racing the host's.
| Path | Contents |
|---|---|
| /usr/lib64/liboni_ripper.so.0 | Shared library (SONAME). |
| /usr/lib64/liboni_ripper.so | Compatibility symlink for ld. |
| /usr/lib64/liboni_ripper.a | Static library, same object set as the .so. |
| /usr/include/oni_ripper.h | Public C API header. The only header that's part of the contract. |
| /usr/lib64/pkgconfig/oni_ripper.pc | pkg-config manifest. |
To discuss how Oninit ® can assist please call on +1-913-732-8892 or alternatively just send an email specifying your requirements.
You get all this for free.. think about what you get if you pay us