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