LCOV - code coverage report
Current view: top level - providers/data_provider - dp_request.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 118 168 70.2 %
Date: 2016-06-29 Functions: 9 13 69.2 %

          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             : }

Generated by: LCOV version 1.10