xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/cancel.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: cancel.c,v 1.1.1.6 2018/02/06 01:53:13 christos 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-2017 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 <sys/cdefs.h>
20 __RCSID("$NetBSD: cancel.c,v 1.1.1.6 2018/02/06 01:53:13 christos Exp $");
21 
22 #include "portable.h"
23 
24 #include <stdio.h>
25 
26 #include <ac/socket.h>
27 #include <ac/string.h>
28 #include <ac/unistd.h>
29 
30 #include "slap.h"
31 
32 #include <lber_pvt.h>
33 #include <lutil.h>
34 
35 const struct berval slap_EXOP_CANCEL = BER_BVC(LDAP_EXOP_CANCEL);
36 
37 int cancel_extop( Operation *op, SlapReply *rs )
38 {
39 	Operation *o;
40 	int rc;
41 	int opid;
42 	BerElement *ber;
43 
44 	assert( ber_bvcmp( &slap_EXOP_CANCEL, &op->ore_reqoid ) == 0 );
45 
46 	if ( op->ore_reqdata == NULL ) {
47 		rs->sr_text = "no message ID supplied";
48 		return LDAP_PROTOCOL_ERROR;
49 	}
50 
51 	ber = ber_init( op->ore_reqdata );
52 	if ( ber == NULL ) {
53 		rs->sr_text = "internal error";
54 		return LDAP_OTHER;
55 	}
56 
57 	if ( ber_scanf( ber, "{i}", &opid ) == LBER_ERROR ) {
58 		rs->sr_text = "message ID parse failed";
59 		return LDAP_PROTOCOL_ERROR;
60 	}
61 
62 	(void) ber_free( ber, 1 );
63 
64 	Statslog( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n",
65 		op->o_log_prefix, opid, 0, 0, 0 );
66 
67 	if ( opid < 0 ) {
68 		rs->sr_text = "message ID invalid";
69 		return LDAP_PROTOCOL_ERROR;
70 	}
71 
72 	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
73 
74 	if ( op->o_abandon ) {
75 		/* FIXME: Should instead reject the cancel/abandon of this op, but
76 		 * it seems unsafe to reset op->o_abandon once it is set. ITS#6138.
77 		 */
78 		rc = LDAP_OPERATIONS_ERROR;
79 		rs->sr_text = "tried to abandon or cancel this operation";
80 		goto out;
81 	}
82 
83 	LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) {
84 		if ( o->o_msgid == opid ) {
85 			/* TODO: We could instead remove the cancelled operation
86 			 * from c_pending_ops like Abandon does, and send its
87 			 * response here.  Not if it is pending because of a
88 			 * congested connection though.
89 			 */
90 			rc = LDAP_CANNOT_CANCEL;
91 			rs->sr_text = "too busy for Cancel, try Abandon instead";
92 			goto out;
93 		}
94 	}
95 
96 	LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) {
97 		if ( o->o_msgid == opid ) {
98 			break;
99 		}
100 	}
101 
102 	if ( o == NULL ) {
103 	 	rc = LDAP_NO_SUCH_OPERATION;
104 		rs->sr_text = "message ID not found";
105 
106 	} else if ( o->o_tag == LDAP_REQ_BIND
107 			|| o->o_tag == LDAP_REQ_UNBIND
108 			|| o->o_tag == LDAP_REQ_ABANDON ) {
109 		rc = LDAP_CANNOT_CANCEL;
110 
111 	} else if ( o->o_cancel != SLAP_CANCEL_NONE ) {
112 		rc = LDAP_OPERATIONS_ERROR;
113 		rs->sr_text = "message ID already being cancelled";
114 
115 #if 0
116 	} else if ( o->o_abandon ) {
117 		/* TODO: Would this break something when
118 		 * o_abandon="suppress response"? (ITS#6138)
119 		 */
120 		rc = LDAP_TOO_LATE;
121 #endif
122 
123 	} else {
124 		rc = LDAP_SUCCESS;
125 		o->o_cancel = SLAP_CANCEL_REQ;
126 		o->o_abandon = 1;
127 	}
128 
129  out:
130 	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
131 
132 	if ( rc == LDAP_SUCCESS ) {
133 		LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) {
134 			if( !op->o_bd->be_cancel ) continue;
135 
136 			op->oq_cancel.rs_msgid = opid;
137 			if ( op->o_bd->be_cancel( op, rs ) == LDAP_SUCCESS ) {
138 				return LDAP_SUCCESS;
139 			}
140 		}
141 
142 		do {
143 			/* Fake a cond_wait with thread_yield, then
144 			 * verify the result properly mutex-protected.
145 			 */
146 			while ( o->o_cancel == SLAP_CANCEL_REQ )
147 				ldap_pvt_thread_yield();
148 			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
149 			rc = o->o_cancel;
150 			ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
151 		} while ( rc == SLAP_CANCEL_REQ );
152 
153 		if ( rc == SLAP_CANCEL_ACK ) {
154 			rc = LDAP_SUCCESS;
155 		}
156 
157 		o->o_cancel = SLAP_CANCEL_DONE;
158 	}
159 
160 	return rc;
161 }
162