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/sss_utf8.h"
30 : #include "util/util.h"
31 :
32 0 : void dp_req_reply_default(const char *req_name,
33 : struct sbus_request *sbus_req,
34 : void *data)
35 : {
36 0 : DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, req_name, "Replying with empty message");
37 :
38 0 : sbus_request_return_and_finish(sbus_req, DBUS_TYPE_INVALID);
39 0 : }
40 :
41 0 : static DBusError *dp_req_reply_gen_error(TALLOC_CTX *mem_ctx,
42 : const char *req_name,
43 : errno_t ret)
44 : {
45 : DBusError *error;
46 :
47 0 : switch (ret) {
48 : case EOK:
49 0 : DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
50 : "Bug: Success case must be handled by custom handler.");
51 0 : error = sbus_error_new(mem_ctx, SBUS_ERROR_INTERNAL,
52 : "Operation succeeded but result was not handled");
53 0 : break;
54 : case ERR_OFFLINE:
55 0 : DP_REQ_DEBUG(SSSDBG_MINOR_FAILURE, req_name,
56 : "Finished. Backend is currently offline.");
57 :
58 0 : error = sbus_error_new(mem_ctx, SBUS_ERROR_DP_OFFLINE,
59 : "Backend is currently offline");
60 0 : break;
61 : case ERR_MISSING_DP_TARGET:
62 0 : DP_REQ_DEBUG(SSSDBG_MINOR_FAILURE, req_name,
63 : "Finished. Target is not supported "
64 : "with this configuration.");
65 :
66 0 : error = sbus_error_new(mem_ctx, SBUS_ERROR_DP_NOTSUP,
67 : "Target is not supported.");
68 0 : break;
69 : default:
70 0 : DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
71 : "Finished. Error [%d]: %s", ret, sss_strerror(ret));
72 :
73 0 : error = sbus_error_new(mem_ctx, SBUS_ERROR_DP_FATAL,
74 : "An error occurred [%d]: %s", ret, sss_strerror(ret));
75 0 : break;
76 : }
77 :
78 0 : return error;
79 : }
80 :
81 0 : void dp_req_reply_error(struct sbus_request *sbus_req,
82 : const char *req_name,
83 : errno_t ret)
84 : {
85 : DBusError *error;
86 :
87 0 : error = dp_req_reply_gen_error(sbus_req, req_name, ret);
88 0 : if (error == NULL) {
89 0 : DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
90 : "Out of memory, killing request...");
91 0 : talloc_free(sbus_req);
92 0 : return;
93 : }
94 :
95 0 : sbus_request_fail_and_finish(sbus_req, error);
96 : }
97 :
98 0 : static void dp_req_reply_list_error(struct dp_sbus_req_item *list,
99 : const char *req_name,
100 : errno_t ret)
101 : {
102 : struct dp_sbus_req_item *next_item;
103 : struct dp_sbus_req_item *item;
104 : DBusError *error;
105 :
106 0 : error = dp_req_reply_gen_error(NULL, req_name, ret);
107 0 : if (error == NULL) {
108 0 : DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
109 : "Out of memory, killing request...");
110 :
111 0 : for (item = list; item != NULL; item = next_item) {
112 0 : next_item = item->next;
113 0 : talloc_free(item->sbus_req);
114 : }
115 :
116 0 : return;
117 : }
118 :
119 0 : for (item = list; item != NULL; item = next_item) {
120 0 : next_item = item->next;
121 0 : sbus_request_fail_and_finish(item->sbus_req, error);
122 : }
123 :
124 0 : talloc_free(error);
125 0 : return;
126 : }
127 :
128 0 : static void dp_req_reply_list_success(struct dp_sbus_req_item *list,
129 : dp_req_reply_fn reply_fn,
130 : const char *request_name,
131 : void *output_data)
132 : {
133 : struct dp_sbus_req_item *next_item;
134 : struct dp_sbus_req_item *item;
135 :
136 0 : DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, request_name, "Finished. Success.");
137 :
138 0 : for (item = list; item != NULL; item = next_item) {
139 0 : next_item = item->next;
140 0 : reply_fn(request_name, item->sbus_req, output_data);
141 : }
142 0 : }
143 :
144 : struct dp_req_with_reply_state {
145 : struct data_provider *provider;
146 :
147 : void *postprocess_data;
148 : dp_req_post_fn postprocess_fn;
149 :
150 : const char *output_dtype;
151 : dp_req_reply_fn reply_fn;
152 : const char *key;
153 : const char *name;
154 : };
155 :
156 : static errno_t dp_req_with_reply_step(struct data_provider *provider,
157 : struct dp_client *dp_cli,
158 : const char *domain,
159 : const char *request_name,
160 : const char *custom_key,
161 : struct sbus_request *sbus_req,
162 : enum dp_targets target,
163 : enum dp_methods method,
164 : uint32_t dp_flags,
165 : void *request_data,
166 : dp_req_post_fn postprocess_fn,
167 : void *postprocess_data,
168 : dp_req_reply_fn reply_fn,
169 : const char *output_dtype);
170 :
171 : static void dp_req_with_reply_done(struct tevent_req *req);
172 :
173 0 : void _dp_req_with_reply(struct dp_client *dp_cli,
174 : const char *domain,
175 : const char *request_name,
176 : const char *custom_key,
177 : struct sbus_request *sbus_req,
178 : enum dp_targets target,
179 : enum dp_methods method,
180 : uint32_t dp_flags,
181 : void *request_data,
182 : dp_req_post_fn postprocess_fn,
183 : void *postprocess_data,
184 : dp_req_reply_fn reply_fn,
185 : const char *output_dtype)
186 : {
187 : TALLOC_CTX *tmp_ctx;
188 : struct data_provider *provider;
189 : const char *key;
190 : bool has_key;
191 : errno_t ret;
192 :
193 0 : tmp_ctx = talloc_new(NULL);
194 0 : if (tmp_ctx == NULL) {
195 0 : ret = ENOMEM;
196 0 : goto done;
197 : }
198 :
199 0 : provider = dp_client_provider(dp_cli);
200 :
201 0 : if (custom_key == NULL) {
202 : /* It may not be always possible or desirable to have a meaningful key
203 : * to chain sbus request. In such cases, we generate a unique key from
204 : * sbus_req address that allows us to use the same code but the
205 : * chaining is logically disabled. */
206 0 : custom_key = talloc_asprintf(tmp_ctx, "%p", sbus_req);
207 0 : if (custom_key == NULL) {
208 0 : ret = ENOMEM;
209 0 : goto done;
210 : }
211 : }
212 :
213 0 : key = dp_req_table_key(tmp_ctx, target, method, dp_flags, custom_key);
214 0 : if (key == NULL) {
215 0 : ret = ENOMEM;
216 0 : goto done;
217 : }
218 :
219 0 : has_key = dp_req_table_has_key(provider->requests.reply_table, key);
220 0 : if (has_key) {
221 0 : ret = dp_req_table_add(provider->requests.reply_table,
222 : key, NULL, sbus_req);
223 0 : if (ret != EOK) {
224 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to attach sbus request to "
225 : "existing data provider request [%d]: %s\n",
226 : ret, sss_strerror(ret));
227 0 : goto done;
228 : }
229 :
230 0 : DEBUG(SSSDBG_TRACE_FUNC, "Attaching to DP request: %s\n", key);
231 :
232 0 : ret = EOK;
233 0 : goto done;
234 : }
235 :
236 0 : ret = dp_req_with_reply_step(provider, dp_cli, domain, request_name, key,
237 : sbus_req, target, method, dp_flags,
238 : request_data, postprocess_fn, postprocess_data,
239 : reply_fn, output_dtype);
240 :
241 : done:
242 0 : if (ret == ENOMEM) {
243 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate memory for "
244 : "new DP request, killing D-Bus request...\n");
245 0 : talloc_zfree(sbus_req);
246 0 : } else if (ret != EOK) {
247 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize "
248 : "DP request [%d: %s], killing D-Bus request...\n",
249 : ret, sss_strerror(ret));
250 0 : talloc_zfree(sbus_req);
251 : }
252 :
253 0 : talloc_free(tmp_ctx);
254 0 : }
255 :
256 0 : static errno_t dp_req_with_reply_step(struct data_provider *provider,
257 : struct dp_client *dp_cli,
258 : const char *domain,
259 : const char *request_name,
260 : const char *custom_key,
261 : struct sbus_request *sbus_req,
262 : enum dp_targets target,
263 : enum dp_methods method,
264 : uint32_t dp_flags,
265 : void *request_data,
266 : dp_req_post_fn postprocess_fn,
267 : void *postprocess_data,
268 : dp_req_reply_fn reply_fn,
269 : const char *output_dtype)
270 : {
271 : TALLOC_CTX *tmp_ctx;
272 : struct dp_req_with_reply_state *state;
273 : struct tevent_req *req;
274 : errno_t ret;
275 :
276 0 : tmp_ctx = talloc_new(NULL);
277 0 : if (tmp_ctx == NULL) {
278 0 : return ENOMEM;
279 : }
280 :
281 0 : state = talloc_zero(tmp_ctx, struct dp_req_with_reply_state);
282 0 : if (state == NULL) {
283 0 : ret = ENOMEM;
284 0 : goto done;
285 : }
286 :
287 0 : state->provider = provider;
288 0 : state->reply_fn = reply_fn;
289 0 : state->key = talloc_strdup(state, custom_key);
290 0 : if (state->key == NULL) {
291 0 : ret = ENOMEM;
292 0 : goto done;
293 : }
294 :
295 0 : if (postprocess_fn != NULL) {
296 0 : state->postprocess_data = postprocess_data;
297 0 : state->postprocess_fn = postprocess_fn;
298 : }
299 :
300 0 : state->output_dtype = talloc_strdup(state, output_dtype);
301 0 : if (state->output_dtype == NULL) {
302 0 : ret = ENOMEM;
303 0 : goto done;
304 : }
305 :
306 0 : req = dp_req_send(tmp_ctx, provider, dp_cli, domain, request_name, target,
307 : method, dp_flags, request_data, &state->name);
308 0 : if (req == NULL) {
309 0 : ret = ENOMEM;
310 0 : goto done;
311 : }
312 :
313 0 : ret = dp_req_table_add(provider->requests.reply_table,
314 : custom_key, req, sbus_req);
315 0 : if (ret != EOK) {
316 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add request to table "
317 : "[%d]: %s\n", ret, sss_strerror(ret));
318 0 : goto done;
319 : }
320 :
321 0 : tevent_req_set_callback(req, dp_req_with_reply_done, state);
322 :
323 0 : talloc_steal(provider, req);
324 0 : talloc_steal(req, state);
325 0 : talloc_steal(state, state->name);
326 :
327 0 : ret = EOK;
328 :
329 : done:
330 0 : talloc_free(tmp_ctx);
331 0 : return ret;
332 : }
333 :
334 0 : static void dp_req_with_reply_done(struct tevent_req *req)
335 : {
336 : struct dp_req_with_reply_state *state;
337 : struct dp_table_value *value;
338 : void *output_data;
339 : errno_t ret;
340 :
341 0 : state = tevent_req_callback_data(req, struct dp_req_with_reply_state);
342 :
343 0 : value = dp_req_table_lookup(state->provider->requests.reply_table,
344 : state->key);
345 0 : if (value == NULL) {
346 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup table!\n");
347 0 : return;
348 : }
349 :
350 0 : ret = _dp_req_recv(state, req, state->output_dtype, &output_data);
351 0 : if (ret != EOK) {
352 0 : dp_req_reply_list_error(value->list, state->name, ret);
353 0 : goto done;
354 : }
355 :
356 : /* Run postprocess function if any. */
357 0 : if (state->postprocess_fn != NULL) {
358 0 : state->postprocess_fn(state->name,
359 : state->provider,
360 : state->postprocess_data,
361 : output_data);
362 : }
363 :
364 : /* Reply with data. */
365 0 : dp_req_reply_list_success(value->list, state->reply_fn,
366 : state->name, output_data);
367 :
368 : done:
369 : /* Freeing value will remove it from the table as well. */
370 0 : talloc_free(value);
371 0 : talloc_free(req);
372 : }
|