1 /* pulleyback/api.c -- The API to the Pulley backend for the TLS Pool
3 * From: Rick van Rein <rick@openfortress.nl>
18 /* Allocate a handler, parse information into it, access the database.
20 void *pulleyback_open (int argc, char **argv, int varc) {
21 struct pulleyback_tlspool *self;
24 // Allocate memory and wipe it clean
25 self = malloc (sizeof (struct pulleyback_tlspool));
30 memset (self, 0, sizeof (struct pulleyback_tlspool));
32 // Parse arguments, connect to the database
33 rv = parse_arguments (argc, argv, varc, self);
40 rv = open_database (self);
49 // Return the successful result -- mapped to a (void *)
53 void pulleyback_close (void *pbh) {
54 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
59 // Disconnect from the database after implicit rollback
60 if (self->txn_state != TXN_NONE) {
61 pulleyback_rollback (pbh);
63 close_database (self);
65 // Cleanup fields allocated with strdup()
66 if (self->db_env != NULL) {
70 if (self->db_filename != NULL) {
71 free (self->db_filename);
72 self->db_filename = NULL;
75 // Free the basic data structure
80 /* Internal method to ensure having a transaction, return 0 on error
82 static int have_txn (struct pulleyback_tlspool *self) {
83 switch (self->txn_state) {
85 if (0 != self->env->txn_begin (self->env, NULL, &self->txn, 0)) {
88 self->txn_state = TXN_ACTIVE;
94 // You cannot have_txn() after _prepare()
95 assert ((self->txn_state == TXN_NONE) || (self->txn_state == TXN_ACTIVE));
100 /* Internal method to process a negative "ok" value by switching to TXN_ABORT
102 static int check_txn (struct pulleyback_tlspool *self, int ok) {
104 if (self->txn_state == TXN_ACTIVE) {
105 self->txn_state = TXN_ABORT;
110 int pulleyback_add (void *pbh, uint8_t **forkdata) {
111 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
113 ok = ok && have_txn (self);
114 ok = ok && (self->txn_state == TXN_ACTIVE);
115 ok = ok && self->update (self, forkdata, 0);
116 check_txn (self, ok);
120 int pulleyback_del (void *pbh, uint8_t **forkdata) {
121 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
123 ok = ok && have_txn (self);
124 ok = ok && (self->txn_state == TXN_ACTIVE);
125 ok = ok && self->update (self, forkdata, 1);
126 check_txn (self, ok);
131 int pulleyback_reset (void *pbh) {
132 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
135 ok = ok && have_txn (self);
136 ok = ok && (self->txn_state == TXN_ACTIVE);
137 ok = ok && (0 == self->db->truncate (self->db, self->txn, &count, 0));
138 check_txn (self, ok);
143 /* Transactions are somewhat complex, also because they are implicitly
144 * started when changes are made using _add(), _del() or _reset().
145 * These operations may be conditional in the calling program, and we
146 * want to aliviete the burden of maintaining a state as to whether
147 * a transaction has been started implicitly, so we accept calls to
148 * _prepare(), _rollback() or _commit() when no transaction exists
149 * yet. We will implement such occurrences equivalently to opening a
150 * new transaction and acting on it immediately (though the logs may
151 * not show it if we can optimise by not actually doing it -- it is
152 * much simpler to skip the _rollback() or _commit() on an empty
155 * We use txn_state to maintain state between _prepare() and its followup
156 * call; the followup may be _prepare(), which is idempotent and will
157 * return the same result; it may be _commit(), but only when the
158 * preceding _prepare() has reported success; or it may be _rollback(),
159 * regardless of the state reported by _prepare().
161 * Invalid sequences are reported through assert() -- which is not a
162 * bug but a facility! It helps the plugin user to code transaction
163 * logic correctly. The implicitness of transactions means that we
164 * cannot capture all logic failures though.
167 int pulleyback_prepare (void *pbh) {
168 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
170 switch (self->txn_state) {
172 // We want to return success, so we'd better create an
173 // empty transaction to permit future _commit() or _rollback()
174 ok = ok && have_txn (self);
175 // ...continue into case TXN_ACTIVE...
177 ok = ok && (0 == self->txn->prepare (self->txn, self->txn_gid));
178 self->txn_state = ok? TXN_SUCCESS: TXN_ABORT;
181 // The transaction has already been successfully prepared
185 // The transaction has already failed preparing for commit
192 int pulleyback_commit (void *pbh) {
193 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
195 switch (self->txn_state) {
197 // We can safely report success when there is no transaction
202 // The transaction is in full progress; attempt to commit it
203 ok = ok && (0 == self->txn->commit (self->txn, 0));
205 self->txn_state = TXN_NONE;
208 // Preparation fails, then the call should have been _rollback()
209 assert (self->txn_state != TXN_ABORT);
211 // Since there actually is a transaction, roll it back
212 ok = ok && (0 == self->txn->abort (self->txn));
214 self->txn_state = TXN_NONE;
220 void pulleyback_rollback (void *pbh) {
221 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
223 switch (self->txn_state) {
225 // In lieu of a transaction, rollback is a trivial matter
230 // Preparation of the transaction has been done,
231 // so process as we would an active transaction
233 // When there actually is a transaction, roll it back
234 ok = ok && (0 == self->txn->abort (self->txn));
236 self->txn_state = TXN_NONE;
241 int pulleyback_collaborate (void *pbh1, void *pbh2) {
242 struct pulleyback_tlspool *data1 = (struct pulleyback_tlspool *) pbh1;
243 struct pulleyback_tlspool *data2 = (struct pulleyback_tlspool *) pbh2;
245 ok = ok && (0 == strcmp (data1->db_env, data2->db_env));
246 //TODO// May need to copy self->env and reopen self->db in it
249 } else if (data1->txn == NULL) {
250 if (data2->txn == NULL) {
251 // Neither has a transaction, so must create it
252 ok = ok && have_txn (data2);
255 data1->txn = data2->txn;
257 } else if (data2->txn == NULL) {
258 data2->txn = data1->txn;
260 ok = (data1->txn == data2->txn);