Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2016 Red Hat
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 : #include <talloc.h>
22 : #include <tevent.h>
23 : #include <dbus/dbus.h>
24 :
25 : #include "sbus/sssd_dbus_errors.h"
26 : #include "providers/data_provider/dp_private.h"
27 : #include "providers/backend.h"
28 : #include "util/dlinklist.h"
29 : #include "util/util.h"
30 :
31 : struct dp_req {
32 : struct data_provider *provider;
33 : struct dp_client *client;
34 : uint32_t dp_flags;
35 :
36 : struct sss_domain_info *domain;
37 :
38 : enum dp_targets target;
39 : enum dp_methods method;
40 : struct dp_method *execute;
41 : const char *name;
42 : uint32_t num;
43 :
44 : struct tevent_req *req;
45 : struct tevent_req *handler_req;
46 : void *request_data;
47 :
48 : /* Active request list. */
49 : struct dp_req *prev;
50 : struct dp_req *next;
51 : };
52 :
53 13 : static bool check_data_type(const char *expected,
54 : const char *description,
55 : void *ptr)
56 : {
57 : void *tmp;
58 :
59 : /* If ptr is NULL we still return true since it is valid case. */
60 13 : tmp = talloc_check_name(ptr, expected);
61 13 : if (tmp != NULL || ptr == NULL) {
62 12 : return true;
63 : }
64 :
65 1 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid %s data type provided. Expected [%s], "
66 : "got [%s].\n", description, expected, talloc_get_name(ptr));
67 :
68 1 : return false;
69 : }
70 :
71 5 : static bool check_method_data(struct dp_method *method,
72 : void *request_data)
73 : {
74 5 : if (!check_data_type(method->method_dtype, "method", method->method_data)) {
75 0 : return false;
76 : }
77 :
78 5 : if (!check_data_type(method->request_dtype, "request", request_data)) {
79 0 : return false;
80 : }
81 :
82 5 : return true;
83 : }
84 :
85 6 : static int dp_req_destructor(struct dp_req *dp_req)
86 : {
87 6 : DLIST_REMOVE(dp_req->provider->requests.active, dp_req);
88 :
89 6 : if (dp_req->provider->requests.num_active == 0) {
90 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: there are no active requests!\n");
91 0 : return 0;
92 : }
93 :
94 6 : dp_req->provider->requests.num_active--;
95 :
96 6 : DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, dp_req->name, "Request removed.");
97 :
98 6 : DEBUG(SSSDBG_TRACE_FUNC, "Number of active DP request: %u\n",
99 : dp_req->provider->requests.num_active);
100 :
101 6 : return 0;
102 : }
103 :
104 6 : static errno_t dp_attach_req(struct dp_req *dp_req,
105 : struct data_provider *provider,
106 : const char *name,
107 : uint32_t dp_flags)
108 : {
109 : /* If we run out of numbers we simply overflow. */
110 6 : dp_req->num = provider->requests.index++;
111 6 : dp_req->name = talloc_asprintf(dp_req, "%s #%u", name, dp_req->num);
112 6 : if (dp_req->name == NULL) {
113 0 : return ENOMEM;
114 : }
115 :
116 : /* Attach this request to active request list. */
117 6 : DLIST_ADD(provider->requests.active, dp_req);
118 6 : provider->requests.num_active++;
119 :
120 6 : talloc_set_destructor(dp_req, dp_req_destructor);
121 :
122 6 : DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, dp_req->name,
123 : "New request. Flags [%#.4x].", dp_flags);
124 :
125 6 : DEBUG(SSSDBG_TRACE_FUNC, "Number of active DP request: %u\n",
126 : provider->requests.num_active);
127 :
128 6 : return EOK;
129 : }
130 :
131 : static errno_t
132 6 : dp_req_new(TALLOC_CTX *mem_ctx,
133 : struct data_provider *provider,
134 : struct dp_client *dp_cli,
135 : const char *domainname,
136 : const char *name,
137 : enum dp_targets target,
138 : enum dp_methods method,
139 : uint32_t dp_flags,
140 : void *request_data,
141 : struct tevent_req *req,
142 : struct dp_req **_dp_req)
143 : {
144 : struct dp_req *dp_req;
145 : struct be_ctx *be_ctx;
146 : errno_t ret;
147 :
148 : /* We set output even for error to simplify code flow in the caller. */
149 6 : *_dp_req = NULL;
150 :
151 6 : dp_req = talloc_zero(mem_ctx, struct dp_req);
152 6 : if (dp_req == NULL) {
153 0 : return ENOMEM;
154 : }
155 :
156 6 : dp_req->provider = provider;
157 6 : dp_req->client = dp_cli;
158 6 : dp_req->dp_flags = dp_flags;
159 6 : dp_req->target = target;
160 6 : dp_req->method = method;
161 6 : dp_req->request_data = request_data;
162 6 : dp_req->req = req;
163 :
164 6 : ret = dp_attach_req(dp_req, provider, name, dp_flags);
165 6 : if (ret != EOK) {
166 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create DP request "
167 : "[%s] [%d]: %s\n", name, ret, sss_strerror(ret));
168 0 : talloc_free(dp_req);
169 0 : return ret;
170 : }
171 :
172 : /* Now the request is created. We will return it even in case of error
173 : * so we can get better debug messages. */
174 :
175 6 : talloc_steal(dp_req, dp_req->request_data);
176 6 : *_dp_req = dp_req;
177 :
178 6 : be_ctx = provider->be_ctx;
179 6 : dp_req->domain = be_ctx->domain;
180 6 : if (domainname != NULL) {
181 1 : dp_req->domain = find_domain_by_name(be_ctx->domain, domainname, true);
182 1 : if (dp_req->domain == NULL) {
183 1 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain: %s\n", domainname);
184 1 : return ERR_DOMAIN_NOT_FOUND;
185 : }
186 : }
187 :
188 5 : ret = dp_find_method(provider, target, method, &dp_req->execute);
189 :
190 5 : return ret;
191 : }
192 :
193 : static errno_t
194 6 : file_dp_request(TALLOC_CTX *mem_ctx,
195 : struct data_provider *provider,
196 : struct dp_client *dp_cli,
197 : const char *domainname,
198 : const char *name,
199 : enum dp_targets target,
200 : enum dp_methods method,
201 : uint32_t dp_flags,
202 : void *request_data,
203 : struct tevent_req *req,
204 : struct dp_req **_dp_req)
205 : {
206 : struct dp_req_params *dp_params;
207 : dp_req_send_fn send_fn;
208 : struct dp_req *dp_req;
209 : struct be_ctx *be_ctx;
210 : errno_t ret;
211 :
212 6 : be_ctx = provider->be_ctx;
213 :
214 6 : ret = dp_req_new(mem_ctx, provider, dp_cli, domainname, name, target,
215 : method, dp_flags, request_data, req, &dp_req);
216 6 : if (ret != EOK) {
217 1 : *_dp_req = dp_req;
218 1 : goto done;
219 : }
220 :
221 : /* DP request is already created. We will always return it to get nice
222 : * debug messages. */
223 5 : *_dp_req = dp_req;
224 :
225 : /* Check that provided data are of correct type. */
226 :
227 5 : if (!check_method_data(dp_req->execute, dp_req->request_data)) {
228 0 : ret = ERR_INVALID_DATA_TYPE;
229 0 : goto done;
230 : }
231 :
232 : /* Process data provider flags */
233 :
234 5 : if (dp_flags & DP_FAST_REPLY && be_is_offline(be_ctx)) {
235 1 : ret = ERR_OFFLINE;
236 1 : goto done;
237 : }
238 :
239 : /* File request */
240 :
241 4 : dp_params = talloc_zero(dp_req, struct dp_req_params);
242 4 : if (dp_params == NULL) {
243 0 : ret = ENOMEM;
244 0 : goto done;
245 : }
246 :
247 4 : dp_params->ev = provider->ev;
248 4 : dp_params->be_ctx = be_ctx;
249 4 : dp_params->domain = dp_req->domain;
250 4 : dp_params->target = dp_req->target;
251 4 : dp_params->method = dp_req->method;
252 :
253 4 : send_fn = dp_req->execute->send_fn;
254 8 : dp_req->handler_req = send_fn(dp_req, dp_req->execute->method_data,
255 4 : dp_req->request_data, dp_params);
256 4 : if (dp_req->handler_req == NULL) {
257 0 : ret = ENOMEM;
258 0 : goto done;
259 : }
260 :
261 4 : *_dp_req = dp_req;
262 :
263 4 : ret = EOK;
264 :
265 : done:
266 6 : return ret;
267 : }
268 :
269 : struct dp_req_state {
270 : struct dp_req *dp_req;
271 : dp_req_recv_fn recv_fn;
272 : void *output_data;
273 : };
274 :
275 : static void dp_req_done(struct tevent_req *subreq);
276 :
277 6 : struct tevent_req *dp_req_send(TALLOC_CTX *mem_ctx,
278 : struct data_provider *provider,
279 : struct dp_client *dp_cli,
280 : const char *domain,
281 : const char *name,
282 : enum dp_targets target,
283 : enum dp_methods method,
284 : uint32_t dp_flags,
285 : void *request_data,
286 : const char **_request_name)
287 : {
288 : struct dp_req_state *state;
289 : const char *request_name;
290 : struct tevent_req *req;
291 : struct dp_req *dp_req;
292 : errno_t ret;
293 :
294 6 : req = tevent_req_create(mem_ctx, &state, struct dp_req_state);
295 6 : if (req == NULL) {
296 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
297 0 : return NULL;
298 : }
299 :
300 6 : ret = file_dp_request(state, provider, dp_cli, domain, name, target,
301 : method, dp_flags, request_data, req, &dp_req);
302 :
303 6 : if (dp_req == NULL) {
304 : /* An error ocurred before request could be created. */
305 0 : if (_request_name != NULL) {
306 0 : *_request_name = "Request Not Yet Created";
307 : }
308 :
309 0 : goto immediately;
310 : }
311 :
312 6 : state->dp_req = dp_req;
313 6 : if (_request_name != NULL) {
314 4 : request_name = talloc_strdup(mem_ctx, dp_req->name);
315 4 : if (request_name == NULL) {
316 0 : *_request_name = "Request Not Yet Created";
317 0 : ret = ENOMEM;
318 0 : goto immediately;
319 : }
320 4 : *_request_name = request_name;
321 : }
322 :
323 6 : if (ret != EOK) {
324 2 : goto immediately;
325 : }
326 :
327 4 : state->recv_fn = dp_req->execute->recv_fn;
328 4 : state->output_data = talloc_zero_size(state, dp_req->execute->output_size);
329 4 : if (state->output_data == NULL) {
330 0 : ret = ENOMEM;
331 0 : goto immediately;
332 : }
333 :
334 4 : talloc_set_name_const(state->output_data, dp_req->execute->output_dtype);
335 :
336 4 : tevent_req_set_callback(dp_req->handler_req, dp_req_done, req);
337 :
338 4 : return req;
339 :
340 : immediately:
341 2 : if (ret == EOK) {
342 0 : tevent_req_done(req);
343 : } else {
344 2 : tevent_req_error(req, ret);
345 : }
346 2 : tevent_req_post(req, provider->ev);
347 :
348 2 : return req;
349 : }
350 :
351 4 : static void dp_req_done(struct tevent_req *subreq)
352 : {
353 : struct dp_req_state *state;
354 : struct tevent_req *req;
355 : errno_t ret;
356 :
357 4 : req = tevent_req_callback_data(subreq, struct tevent_req);
358 4 : state = tevent_req_data(req, struct dp_req_state);
359 :
360 4 : ret = state->recv_fn(state->output_data, subreq, state->output_data);
361 :
362 : /* subreq is the same as dp_req->handler_req */
363 4 : talloc_zfree(subreq);
364 4 : state->dp_req->handler_req = NULL;
365 :
366 4 : DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->dp_req->name,
367 : "Request handler finished [%d]: %s", ret, sss_strerror(ret));
368 :
369 4 : if (ret != EOK) {
370 1 : tevent_req_error(req, ret);
371 5 : return;
372 : }
373 :
374 3 : tevent_req_done(req);
375 : }
376 :
377 6 : errno_t _dp_req_recv(TALLOC_CTX *mem_ctx,
378 : struct tevent_req *req,
379 : const char *output_dtype,
380 : void **_output_data)
381 : {
382 : struct dp_req_state *state;
383 :
384 6 : state = tevent_req_data(req, struct dp_req_state);
385 :
386 6 : if (state->dp_req != NULL) {
387 6 : DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->dp_req->name,
388 : "Receiving request data.");
389 : } else {
390 : /* dp_req may be NULL in case we error when filing request */
391 0 : DEBUG(SSSDBG_TRACE_FUNC,
392 : "Receiving data of prematurely interrupted request!\n");
393 : }
394 :
395 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
396 :
397 3 : if (!check_data_type(output_dtype, "output", state->output_data)) {
398 1 : return ERR_INVALID_DATA_TYPE;
399 : }
400 :
401 2 : *_output_data = talloc_steal(mem_ctx, state->output_data);
402 :
403 2 : return EOK;
404 : }
405 :
406 0 : static void dp_terminate_request(struct dp_req *dp_req)
407 : {
408 0 : if (dp_req->handler_req == NULL) {
409 : /* This may occur when the handler already finished but the caller
410 : * of dp request did not yet recieved data/free dp_req. We just
411 : * return here. */
412 0 : return;
413 : }
414 :
415 : /* We will end the handler request and mark dp request as terminated. */
416 :
417 0 : DP_REQ_DEBUG(SSSDBG_TRACE_ALL, dp_req->name, "Terminating.");
418 :
419 0 : talloc_zfree(dp_req->handler_req);
420 0 : tevent_req_error(dp_req->req, ERR_TERMINATED);
421 : }
422 :
423 0 : static void dp_terminate_request_list(struct data_provider *provider,
424 : const char *domain)
425 : {
426 : struct dp_req *next;
427 : struct dp_req *cur;
428 :
429 0 : if (provider == NULL || provider->requests.active == NULL) {
430 0 : return;
431 : }
432 :
433 0 : for (cur = provider->requests.active; cur != NULL; cur = next) {
434 0 : next = cur->next;
435 0 : if (domain == NULL || strcmp(cur->domain->name, domain) == 0) {
436 0 : dp_terminate_request(cur);
437 : }
438 : }
439 : }
440 :
441 0 : void dp_terminate_active_requests(struct data_provider *provider)
442 : {
443 0 : DEBUG(SSSDBG_TRACE_FUNC, "Terminating active data provider requests\n");
444 :
445 0 : dp_terminate_request_list(provider, NULL);
446 0 : }
447 :
448 0 : void dp_terminate_domain_requests(struct data_provider *provider,
449 : const char *domain)
450 : {
451 0 : DEBUG(SSSDBG_TRACE_FUNC, "Terminating active data provider requests "
452 : "for domain [%s]\n", domain);
453 :
454 0 : if (domain == NULL) {
455 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: domain is NULL!\n");
456 0 : return;
457 : }
458 :
459 0 : dp_terminate_request_list(provider, domain);
460 : }
|