1/* $NetBSD: cancel.c,v 1.1.1.3 2010/12/12 15:22:24 adam Exp $ */ 2 3/* cancel.c - LDAP cancel extended operation */ 4/* OpenLDAP: pkg/ldap/servers/slapd/cancel.c,v 1.23.2.10 2010/04/13 20:23:12 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19#include "portable.h" 20 21#include <stdio.h> 22 23#include <ac/socket.h> 24#include <ac/string.h> 25#include <ac/unistd.h> 26 27#include "slap.h" 28 29#include <lber_pvt.h> 30#include <lutil.h> 31 32const struct berval slap_EXOP_CANCEL = BER_BVC(LDAP_EXOP_CANCEL); 33 34int cancel_extop( Operation *op, SlapReply *rs ) 35{ 36 Operation *o; 37 int rc; 38 int opid; 39 BerElement *ber; 40 41 assert( ber_bvcmp( &slap_EXOP_CANCEL, &op->ore_reqoid ) == 0 ); 42 43 if ( op->ore_reqdata == NULL ) { 44 rs->sr_text = "no message ID supplied"; 45 return LDAP_PROTOCOL_ERROR; 46 } 47 48 ber = ber_init( op->ore_reqdata ); 49 if ( ber == NULL ) { 50 rs->sr_text = "internal error"; 51 return LDAP_OTHER; 52 } 53 54 if ( ber_scanf( ber, "{i}", &opid ) == LBER_ERROR ) { 55 rs->sr_text = "message ID parse failed"; 56 return LDAP_PROTOCOL_ERROR; 57 } 58 59 (void) ber_free( ber, 1 ); 60 61 Statslog( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n", 62 op->o_log_prefix, opid, 0, 0, 0 ); 63 64 if ( opid < 0 ) { 65 rs->sr_text = "message ID invalid"; 66 return LDAP_PROTOCOL_ERROR; 67 } 68 69 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 70 71 if ( op->o_abandon ) { 72 /* FIXME: Should instead reject the cancel/abandon of this op, but 73 * it seems unsafe to reset op->o_abandon once it is set. ITS#6138. 74 */ 75 rc = LDAP_OPERATIONS_ERROR; 76 rs->sr_text = "tried to abandon or cancel this operation"; 77 goto out; 78 } 79 80 LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) { 81 if ( o->o_msgid == opid ) { 82 /* TODO: We could instead remove the cancelled operation 83 * from c_pending_ops like Abandon does, and send its 84 * response here. Not if it is pending because of a 85 * congested connection though. 86 */ 87 rc = LDAP_CANNOT_CANCEL; 88 rs->sr_text = "too busy for Cancel, try Abandon instead"; 89 goto out; 90 } 91 } 92 93 LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) { 94 if ( o->o_msgid == opid ) { 95 break; 96 } 97 } 98 99 if ( o == NULL ) { 100 rc = LDAP_NO_SUCH_OPERATION; 101 rs->sr_text = "message ID not found"; 102 103 } else if ( o->o_tag == LDAP_REQ_BIND 104 || o->o_tag == LDAP_REQ_UNBIND 105 || o->o_tag == LDAP_REQ_ABANDON ) { 106 rc = LDAP_CANNOT_CANCEL; 107 108 } else if ( o->o_cancel != SLAP_CANCEL_NONE ) { 109 rc = LDAP_OPERATIONS_ERROR; 110 rs->sr_text = "message ID already being cancelled"; 111 112#if 0 113 } else if ( o->o_abandon ) { 114 /* TODO: Would this break something when 115 * o_abandon="suppress response"? (ITS#6138) 116 */ 117 rc = LDAP_TOO_LATE; 118#endif 119 120 } else { 121 rc = LDAP_SUCCESS; 122 o->o_cancel = SLAP_CANCEL_REQ; 123 o->o_abandon = 1; 124 } 125 126 out: 127 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 128 129 if ( rc == LDAP_SUCCESS ) { 130 LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) { 131 if( !op->o_bd->be_cancel ) continue; 132 133 op->oq_cancel.rs_msgid = opid; 134 if ( op->o_bd->be_cancel( op, rs ) == LDAP_SUCCESS ) { 135 return LDAP_SUCCESS; 136 } 137 } 138 139 do { 140 /* Fake a cond_wait with thread_yield, then 141 * verify the result properly mutex-protected. 142 */ 143 while ( o->o_cancel == SLAP_CANCEL_REQ ) 144 ldap_pvt_thread_yield(); 145 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 146 rc = o->o_cancel; 147 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 148 } while ( rc == SLAP_CANCEL_REQ ); 149 150 if ( rc == SLAP_CANCEL_ACK ) { 151 rc = LDAP_SUCCESS; 152 } 153 154 o->o_cancel = SLAP_CANCEL_DONE; 155 } 156 157 return rc; 158} 159