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 int pulleyback_add (void *pbh, uint8_t **forkdata) {
101 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
103 ok = ok && have_txn (self);
104 ok = ok && (self->txn_state == TXN_ACTIVE);
105 ok = ok && self->update (self, forkdata, 0);
109 int pulleyback_del (void *pbh, uint8_t **forkdata) {
110 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
112 ok = ok && have_txn (self);
113 ok = ok && (self->txn_state == TXN_ACTIVE);
114 ok = ok && self->update (self, forkdata, 1);
119 int pulleyback_reset (void *pbh) {
120 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 && (0 == self->db->truncate (self->db, self->txn, &count, 0));
130 /* Transactions are somewhat complex, also because they are implicitly
131 * started when changes are made using _add(), _del() or _reset().
132 * These operations may be conditional in the calling program, and we
133 * want to aliviete the burden of maintaining a state as to whether
134 * a transaction has been started implicitly, so we accept calls to
135 * _prepare(), _rollback() or _commit() when no transaction exists
136 * yet. We will implement such occurrences equivalently to opening a
137 * new transaction and acting on it immediately (though the logs may
138 * not show it if we can optimise by not actually doing it -- it is
139 * much simpler to skip the _rollback() or _commit() on an empty
142 * We use txn_state to maintain state between _prepare() and its followup
143 * call; the followup may be _prepare(), which is idempotent and will
144 * return the same result; it may be _commit(), but only when the
145 * preceding _prepare() has reported success; or it may be _rollback(),
146 * regardless of the state reported by _prepare().
148 * Invalid sequences are reported through assert() -- which is not a
149 * bug but a facility! It helps the plugin user to code transaction
150 * logic correctly. The implicitness of transactions means that we
151 * cannot capture all logic failures though.
154 int pulleyback_prepare (void *pbh) {
155 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
157 switch (self->txn_state) {
159 // We want to return success, so we'd better create an
160 // empty transaction to permit future _commit() or _rollback()
161 ok = ok && have_txn (self);
162 // ...continue into case TXN_ACTIVE...
164 ok = ok && (0 == self->txn->prepare (self->txn, self->txn_gid));
165 self->txn_state = ok? TXN_SUCCESS: TXN_ABORT;
168 // The transaction has already been successfully prepared
172 // The transaction has already failed preparing for commit
179 int pulleyback_commit (void *pbh) {
180 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
182 switch (self->txn_state) {
184 // We can safely report success when there is no transaction
189 // The transaction is in full progress; attempt to commit it
190 ok = ok && (0 == self->txn->commit (self->txn, 0));
192 self->txn_state = TXN_NONE;
195 // Preparation fails, then the call should havae been _rollback()
196 assert (self->txn_state != TXN_ABORT);
203 void pulleyback_rollback (void *pbh) {
204 struct pulleyback_tlspool *self = (struct pulleyback_tlspool *) pbh;
206 switch (self->txn_state) {
208 // In lieu of a transaction, rollback is a trivial matter
213 // Preparation of the transaction has been done,
214 // so process as we would an active transaction
216 // When there actually is a transaction, roll it back
217 ok = ok && (0 == self->txn->abort (self->txn));
219 self->txn_state = TXN_NONE;
224 int pulleyback_collaborate (void *pbh1, void *pbh2) {
225 struct pulleyback_tlspool *data1 = (struct pulleyback_tlspool *) pbh1;
226 struct pulleyback_tlspool *data2 = (struct pulleyback_tlspool *) pbh2;
228 ok = ok && (0 == strcmp (data1->db_env, data2->db_env));
229 //TODO// May need to copy self->env and reopen self->db in it
232 } else if (data1->txn == NULL) {
233 if (data2->txn == NULL) {
234 // Neither has a transaction, so must create it
235 ok = ok && have_txn (data2);
238 data1->txn = data2->txn;
240 } else if (data2->txn == NULL) {
241 data2->txn = data1->txn;
243 ok = (data1->txn == data2->txn);