]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/txn.c
Do not require ac/string.h for lber_pvt.h
[openldap] / servers / slapd / txn.c
index 30be93c868a7ae5f7466078b9a992cb05c93bf01..7fcd8c2afd63c0210d8ac9c166e73a93a27ebd32 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2014 The OpenLDAP Foundation.
+ * Copyright 1998-2018 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -111,6 +111,46 @@ int txn_spec_ctrl(
        return LDAP_SUCCESS;
 }
 
+typedef struct txn_rctrls {
+       struct txn_rctrls *tr_next;
+       ber_int_t       tr_msgid;
+       LDAPControl ** tr_ctrls;
+} txn_rctrls;
+
+static int txn_result( Operation *op, SlapReply *rs )
+{
+       if ( rs->sr_ctrls ) {
+               txn_rctrls **t0, *tr;
+               for ( t0 = (txn_rctrls **) &op->o_callback->sc_private; *t0;
+                       t0 = &(*t0)->tr_next )
+                       ;
+               tr = op->o_tmpalloc( sizeof( txn_rctrls ), op->o_tmpmemctx );
+               tr->tr_next = NULL;
+               *t0 = tr;
+               tr->tr_msgid = op->o_msgid;
+               tr->tr_ctrls = ldap_controls_dup( rs->sr_ctrls );
+       }
+       return rs->sr_err;
+}
+
+static int txn_put_ctrls( Operation *op, BerElement *ber, txn_rctrls *tr )
+{
+       txn_rctrls *next;
+       int i;
+       ber_printf( ber, "{" );
+       for ( ; tr; tr  = next ) {
+               next = tr->tr_next;
+               ber_printf( ber, "{it{", tr->tr_msgid, LDAP_TAG_CONTROLS );
+               for ( i = 0; tr->tr_ctrls[i]; i++ )
+                       ldap_pvt_put_control( tr->tr_ctrls[i], ber );
+               ber_printf( ber, "}}" );
+               ldap_controls_free( tr->tr_ctrls );
+               op->o_tmpfree( tr, op->o_tmpmemctx );
+       }
+       ber_printf( ber, "}" );
+       return 0;
+}
+
 int txn_end_extop(
        Operation *op, SlapReply *rs )
 {
@@ -121,6 +161,8 @@ int txn_end_extop(
        ber_len_t len;
        ber_int_t commit=1;
        struct berval txnid;
+       Operation *o, *p;
+       Connection *c = op->o_conn;
 
        Statslog( LDAP_DEBUG_STATS, "%s TXN END\n",
                op->o_log_prefix, 0, 0, 0, 0 );
@@ -134,7 +176,7 @@ int txn_end_extop(
                return LDAP_PROTOCOL_ERROR;
        }
 
-       op->o_bd = op->o_conn->c_authz_backend;
+       op->o_bd = c->c_authz_backend;
        if( backend_check_restrictions( op, rs,
                (struct berval *)&slap_EXOP_TXN_END ) != LDAP_SUCCESS )
        {
@@ -170,48 +212,156 @@ int txn_end_extop(
        }
 
        /* acquire connection lock */
-       ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+       ldap_pvt_thread_mutex_lock( &c->c_mutex );
 
-       if( op->o_conn->c_txn != CONN_TXN_SPECIFY ) {
+       if( c->c_txn != CONN_TXN_SPECIFY ) {
                rs->sr_text = "invalid transaction identifier";
                rc = LDAP_X_TXN_ID_INVALID;
                goto done;
        }
-       op->o_conn->c_txn = CONN_TXN_SETTLE;
+       c->c_txn = CONN_TXN_SETTLE;
 
        if( commit ) {
+               slap_callback cb = {0};
+               OpExtra *txn = NULL;
                if ( op->o_abandon ) {
+                       goto drain;
                }
 
-               if( LDAP_STAILQ_EMPTY(&op->o_conn->c_txn_ops) ) {
+               if( LDAP_STAILQ_EMPTY(&c->c_txn_ops) ) {
                        /* no updates to commit */
                        rs->sr_text = "no updates to commit";
                        rc = LDAP_OPERATIONS_ERROR;
                        goto settled;
                }
 
-               rs->sr_text = "not yet implemented";
-               rc = LDAP_UNWILLING_TO_PERFORM;
-
+               cb.sc_response = txn_result;
+               LDAP_STAILQ_FOREACH( o, &c->c_txn_ops, o_next ) {
+                       o->o_bd = c->c_txn_backend;
+                       p = o;
+                       if ( !txn ) {
+                               rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_BEGIN, &txn );
+                               if ( rc ) {
+                                       rs->sr_text = "couldn't start DB transaction";
+                                       rc = LDAP_OTHER;
+                                       goto drain;
+                               }
+                       } else {
+                               LDAP_SLIST_INSERT_HEAD( &o->o_extra, txn, oe_next );
+                       }
+                       cb.sc_next = o->o_callback;
+                       o->o_callback = &cb;
+                       {
+                               SlapReply rs = {REP_RESULT};
+                               int opidx = slap_req2op( o->o_tag );
+                               assert( opidx != SLAP_OP_LAST );
+                               o->o_threadctx = op->o_threadctx;
+                               o->o_tid = op->o_tid;
+                               ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+                               rc = (&o->o_bd->bd_info->bi_op_bind)[opidx]( o, &rs );
+                               ldap_pvt_thread_mutex_lock( &c->c_mutex );
+                       }
+                       if ( rc ) {
+                               struct berval *bv = NULL;
+                               BerElementBuffer berbuf;
+                               BerElement *ber = (BerElement *)&berbuf;
+
+                               ber_init_w_nullc( ber, LBER_USE_DER );
+                               ber_printf( ber, "{i", o->o_msgid );
+                               if ( cb.sc_private )
+                                       txn_put_ctrls( op, ber, cb.sc_private );
+                               ber_printf( ber, "}" );
+                               ber_flatten( ber, &bv );
+                               ber_free_buf( ber );
+                               rs->sr_rspdata = bv;
+                               o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_ABORT, &txn );
+                               goto drain;
+                       }
+               }
+               if ( cb.sc_private ) {
+                       struct berval *bv = NULL;
+                       BerElementBuffer berbuf;
+                       BerElement *ber = (BerElement *)&berbuf;
+
+                       ber_init_w_nullc( ber, LBER_USE_DER );
+                       ber_printf( ber, "{" );
+                       txn_put_ctrls( op, ber, cb.sc_private );
+                       ber_printf( ber, "}" );
+                       ber_flatten( ber, &bv );
+                       ber_free_buf( ber );
+                       rs->sr_rspdata = bv;
+               }
+               o = p;
+               rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_COMMIT, &txn );
+               if ( rc ) {
+                       rs->sr_text = "transaction commit failed";
+                       rc = LDAP_OTHER;
+               }
        } else {
                rs->sr_text = "transaction aborted";
-               rc = LDAP_SUCCESS;;
+               rc = LDAP_SUCCESS;
        }
 
 drain:
        /* drain txn ops list */
+       while (( o = LDAP_STAILQ_FIRST( &c->c_txn_ops )) != NULL ) {
+               LDAP_STAILQ_REMOVE_HEAD( &c->c_txn_ops, o_next );
+               LDAP_STAILQ_NEXT( o, o_next ) = NULL;
+               slap_op_free( o, NULL );
+       }
 
 settled:
-       assert( LDAP_STAILQ_EMPTY(&op->o_conn->c_txn_ops) );
-       assert( op->o_conn->c_txn == CONN_TXN_SETTLE );
-       op->o_conn->c_txn = CONN_TXN_INACTIVE;
-       op->o_conn->c_txn_backend = NULL;
+       assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) );
+       assert( c->c_txn == CONN_TXN_SETTLE );
+       c->c_txn = CONN_TXN_INACTIVE;
+       c->c_txn_backend = NULL;
 
 done:
        /* release connection lock */
-       ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
 
        return rc;
 }
 
+int txn_preop( Operation *op, SlapReply *rs )
+{
+       int settle = 0;
+
+       /* acquire connection lock */
+       ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+       if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
+               rs->sr_text = "invalid transaction identifier";
+               rs->sr_err = LDAP_X_TXN_ID_INVALID;
+               goto txnReturn;
+       } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
+               settle=1;
+               goto txnReturn;
+       }
+
+       if( op->o_conn->c_txn_backend == NULL ) {
+               op->o_conn->c_txn_backend = op->o_bd;
+
+       } else if( op->o_conn->c_txn_backend != op->o_bd ) {
+               rs->sr_text = "transaction cannot span multiple database contexts";
+               rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
+               goto txnReturn;
+       }
+
+       /* insert operation into transaction */
+       LDAP_STAILQ_REMOVE( &op->o_conn->c_ops, op, Operation, o_next );
+       LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_txn_ops, op, o_next );
+
+txnReturn:
+       /* release connection lock */
+       ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+       if( !settle ) {
+               send_ldap_result( op, rs );
+               if ( !rs->sr_err )
+                       rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
+               return rs->sr_err;
+       }
+       return LDAP_SUCCESS;    /* proceed with operation */
+}
+
 #endif /* LDAP_X_TXN */