1/* 2 Unix SMB/CIFS implementation. 3 4 Copyright (C) Volker Lendecke 2005 5 Copyright (C) Andrew Tridgell 2005 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20/* 21 composite API helper functions 22*/ 23 24#include "includes.h" 25#include "lib/events/events.h" 26#include "libcli/raw/libcliraw.h" 27#include "libcli/smb2/smb2.h" 28#include "libcli/composite/composite.h" 29#include "lib/messaging/irpc.h" 30#include "librpc/rpc/dcerpc.h" 31#include "../libcli/nbt/libnbt.h" 32 33/* 34 create a new composite_context structure 35 and initialize it 36*/ 37_PUBLIC_ struct composite_context *composite_create(TALLOC_CTX *mem_ctx, 38 struct tevent_context *ev) 39{ 40 struct composite_context *c; 41 42 c = talloc_zero(mem_ctx, struct composite_context); 43 if (!c) return NULL; 44 c->state = COMPOSITE_STATE_IN_PROGRESS; 45 c->event_ctx = ev; 46 47 return c; 48} 49 50/* 51 block until a composite function has completed, then return the status 52*/ 53_PUBLIC_ NTSTATUS composite_wait(struct composite_context *c) 54{ 55 if (c == NULL) return NT_STATUS_NO_MEMORY; 56 57 c->used_wait = true; 58 59 while (c->state < COMPOSITE_STATE_DONE) { 60 if (event_loop_once(c->event_ctx) != 0) { 61 return NT_STATUS_UNSUCCESSFUL; 62 } 63 } 64 65 return c->status; 66} 67 68/* 69 block until a composite function has completed, then return the status. 70 Free the composite context before returning 71*/ 72_PUBLIC_ NTSTATUS composite_wait_free(struct composite_context *c) 73{ 74 NTSTATUS status = composite_wait(c); 75 talloc_free(c); 76 return status; 77} 78 79/* 80 callback from composite_done() and composite_error() 81 82 this is used to allow for a composite function to complete without 83 going through any state transitions. When that happens the caller 84 has had no opportunity to fill in the async callback fields 85 (ctx->async.fn and ctx->async.private_data) which means the usual way of 86 dealing with composite functions doesn't work. To cope with this, 87 we trigger a timer event that will happen then the event loop is 88 re-entered. This gives the caller a chance to setup the callback, 89 and allows the caller to ignore the fact that the composite 90 function completed early 91*/ 92static void composite_trigger(struct tevent_context *ev, struct tevent_timer *te, 93 struct timeval t, void *ptr) 94{ 95 struct composite_context *c = talloc_get_type(ptr, struct composite_context); 96 if (c->async.fn) { 97 c->async.fn(c); 98 } 99} 100 101 102_PUBLIC_ void composite_error(struct composite_context *ctx, NTSTATUS status) 103{ 104 /* you are allowed to pass NT_STATUS_OK to composite_error(), in which 105 case it is equivalent to composite_done() */ 106 if (NT_STATUS_IS_OK(status)) { 107 composite_done(ctx); 108 return; 109 } 110 if (!ctx->used_wait && !ctx->async.fn) { 111 event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx); 112 } 113 ctx->status = status; 114 ctx->state = COMPOSITE_STATE_ERROR; 115 if (ctx->async.fn != NULL) { 116 ctx->async.fn(ctx); 117 } 118} 119 120_PUBLIC_ bool composite_nomem(const void *p, struct composite_context *ctx) 121{ 122 if (p != NULL) { 123 return false; 124 } 125 composite_error(ctx, NT_STATUS_NO_MEMORY); 126 return true; 127} 128 129_PUBLIC_ bool composite_is_ok(struct composite_context *ctx) 130{ 131 if (NT_STATUS_IS_OK(ctx->status)) { 132 return true; 133 } 134 composite_error(ctx, ctx->status); 135 return false; 136} 137 138_PUBLIC_ void composite_done(struct composite_context *ctx) 139{ 140 if (!ctx->used_wait && !ctx->async.fn) { 141 event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx); 142 } 143 ctx->state = COMPOSITE_STATE_DONE; 144 if (ctx->async.fn != NULL) { 145 ctx->async.fn(ctx); 146 } 147} 148 149_PUBLIC_ void composite_continue(struct composite_context *ctx, 150 struct composite_context *new_ctx, 151 void (*continuation)(struct composite_context *), 152 void *private_data) 153{ 154 if (composite_nomem(new_ctx, ctx)) return; 155 new_ctx->async.fn = continuation; 156 new_ctx->async.private_data = private_data; 157 158 /* if we are setting up a continuation, and the context has 159 already finished, then we should run the callback with an 160 immediate event, otherwise we can be stuck forever */ 161 if (new_ctx->state >= COMPOSITE_STATE_DONE && continuation) { 162 event_add_timed(new_ctx->event_ctx, new_ctx, timeval_zero(), composite_trigger, new_ctx); 163 } 164} 165 166_PUBLIC_ void composite_continue_rpc(struct composite_context *ctx, 167 struct rpc_request *new_req, 168 void (*continuation)(struct rpc_request *), 169 void *private_data) 170{ 171 if (composite_nomem(new_req, ctx)) return; 172 new_req->async.callback = continuation; 173 new_req->async.private_data = private_data; 174} 175 176_PUBLIC_ void composite_continue_irpc(struct composite_context *ctx, 177 struct irpc_request *new_req, 178 void (*continuation)(struct irpc_request *), 179 void *private_data) 180{ 181 if (composite_nomem(new_req, ctx)) return; 182 new_req->async.fn = continuation; 183 new_req->async.private_data = private_data; 184} 185 186_PUBLIC_ void composite_continue_smb(struct composite_context *ctx, 187 struct smbcli_request *new_req, 188 void (*continuation)(struct smbcli_request *), 189 void *private_data) 190{ 191 if (composite_nomem(new_req, ctx)) return; 192 new_req->async.fn = continuation; 193 new_req->async.private_data = private_data; 194} 195 196_PUBLIC_ void composite_continue_smb2(struct composite_context *ctx, 197 struct smb2_request *new_req, 198 void (*continuation)(struct smb2_request *), 199 void *private_data) 200{ 201 if (composite_nomem(new_req, ctx)) return; 202 new_req->async.fn = continuation; 203 new_req->async.private_data = private_data; 204} 205 206_PUBLIC_ void composite_continue_nbt(struct composite_context *ctx, 207 struct nbt_name_request *new_req, 208 void (*continuation)(struct nbt_name_request *), 209 void *private_data) 210{ 211 if (composite_nomem(new_req, ctx)) return; 212 new_req->async.fn = continuation; 213 new_req->async.private_data = private_data; 214} 215