xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/cancel.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: cancel.c,v 1.1.1.4 2014/05/28 09:58:46 tron Exp $	*/
2 
3 /* cancel.c - LDAP cancel extended operation */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2014 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 
32 const struct berval slap_EXOP_CANCEL = BER_BVC(LDAP_EXOP_CANCEL);
33 
34 int 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