LCOV - code coverage report
Current view: top level - tests/cmocka - test_child_common.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 214 226 94.7 %
Date: 2015-10-19 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /*
       2             :     Authors:
       3             :         Jakub Hrozek <jhrozek@redhat.com>
       4             : 
       5             :     Copyright (C) 2014 Red Hat
       6             : 
       7             :     SSSD tests: Child handlers
       8             : 
       9             :     This program is free software; you can redistribute it and/or modify
      10             :     it under the terms of the GNU General Public License as published by
      11             :     the Free Software Foundation; either version 3 of the License, or
      12             :     (at your option) any later version.
      13             : 
      14             :     This program is distributed in the hope that it will be useful,
      15             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :     GNU General Public License for more details.
      18             : 
      19             :     You should have received a copy of the GNU General Public License
      20             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include <talloc.h>
      24             : #include <tevent.h>
      25             : #include <errno.h>
      26             : #include <popt.h>
      27             : 
      28             : #include "util/child_common.h"
      29             : #include "tests/cmocka/common_mock.h"
      30             : 
      31             : #define TEST_BIN    "dummy-child"
      32             : #define ECHO_STR    "Hello child"
      33             : 
      34             : static int destructor_called;
      35             : 
      36             : struct child_test_ctx {
      37             :     int pipefd_to_child[2];
      38             :     int pipefd_from_child[2];
      39             : 
      40             :     struct sss_test_ctx *test_ctx;
      41             : };
      42             : 
      43           6 : static int child_test_setup(void **state)
      44             : {
      45             :     struct child_test_ctx *child_tctx;
      46             :     errno_t ret;
      47             : 
      48           6 :     check_leaks_push(global_talloc_context);
      49           6 :     child_tctx = talloc(global_talloc_context, struct child_test_ctx);
      50           6 :     assert_non_null(child_tctx);
      51             : 
      52           6 :     child_tctx->test_ctx = create_ev_test_ctx(child_tctx);
      53           6 :     assert_non_null(child_tctx->test_ctx);
      54             : 
      55           6 :     ret = pipe(child_tctx->pipefd_from_child);
      56           6 :     assert_int_not_equal(ret, -1);
      57           6 :     DEBUG(SSSDBG_TRACE_LIBS, "from_child: %d:%d\n",
      58             :                              child_tctx->pipefd_from_child[0],
      59             :                              child_tctx->pipefd_from_child[1]);
      60             : 
      61           6 :     ret = pipe(child_tctx->pipefd_to_child);
      62           6 :     assert_int_not_equal(ret, -1);
      63           6 :     DEBUG(SSSDBG_TRACE_LIBS, "to_child: %d:%d\n",
      64             :                              child_tctx->pipefd_to_child[0],
      65             :                              child_tctx->pipefd_to_child[1]);
      66             : 
      67           6 :     *state = child_tctx;
      68           6 :     return 0;
      69             : }
      70             : 
      71           6 : static int child_test_teardown(void **state)
      72             : {
      73           6 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
      74             :                                                         struct child_test_ctx);
      75             : 
      76           6 :     talloc_free(child_tctx);
      77           6 :     check_leaks_pop(global_talloc_context);
      78           6 :     return 0;
      79             : }
      80             : 
      81             : /* Just make sure the exec works. The child does nothing but exits */
      82           1 : void test_exec_child(void **state)
      83             : {
      84             :     errno_t ret;
      85             :     pid_t child_pid;
      86             :     int status;
      87           1 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
      88             :                                                         struct child_test_ctx);
      89             : 
      90           1 :     child_pid = fork();
      91           2 :     assert_int_not_equal(child_pid, -1);
      92           2 :     if (child_pid == 0) {
      93           1 :         ret = exec_child(child_tctx,
      94           1 :                          child_tctx->pipefd_to_child,
      95           1 :                          child_tctx->pipefd_from_child,
      96             :                          CHILD_DIR"/"TEST_BIN, 2);
      97           0 :         assert_int_equal(ret, EOK);
      98             :     } else {
      99             :             do {
     100           1 :                 errno = 0;
     101           1 :                 ret = waitpid(child_pid, &status, 0);
     102           1 :             } while (ret == -1 && errno == EINTR);
     103             : 
     104           1 :             if (ret > 0) {
     105           1 :                 ret = EIO;
     106           1 :                 if (WIFEXITED(status)) {
     107           1 :                     ret = WEXITSTATUS(status);
     108           1 :                     assert_int_equal(ret, 0);
     109             :                 }
     110             :             } else {
     111           0 :                 DEBUG(SSSDBG_FUNC_DATA,
     112             :                     "Failed to wait for children %d\n", child_pid);
     113           0 :                 ret = EIO;
     114             :             }
     115             :     }
     116           1 : }
     117             : 
     118             : /* Make sure extra arguments are passed correctly */
     119           1 : void test_exec_child_extra_args(void **state)
     120             : {
     121             :     errno_t ret;
     122             :     pid_t child_pid;
     123             :     int status;
     124           1 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
     125             :                                                         struct child_test_ctx);
     126           1 :     const char *extra_args[] = { "--guitar=george",
     127             :                                  "--drums=ringo",
     128             :                                  NULL };
     129             : 
     130           1 :     setenv("TEST_CHILD_ACTION", "check_extra_args", 1);
     131             : 
     132           1 :     child_pid = fork();
     133           2 :     assert_int_not_equal(child_pid, -1);
     134           2 :     if (child_pid == 0) {
     135           1 :         ret = exec_child_ex(child_tctx,
     136           1 :                             child_tctx->pipefd_to_child,
     137           1 :                             child_tctx->pipefd_from_child,
     138             :                             CHILD_DIR"/"TEST_BIN, 2, extra_args,
     139             :                             STDIN_FILENO, STDOUT_FILENO);
     140           0 :         assert_int_equal(ret, EOK);
     141             :     } else {
     142             :             do {
     143           1 :                 errno = 0;
     144           1 :                 ret = waitpid(child_pid, &status, 0);
     145           1 :             } while (ret == -1 && errno == EINTR);
     146             : 
     147           1 :             if (ret > 0) {
     148           1 :                 ret = EIO;
     149           1 :                 if (WIFEXITED(status)) {
     150           1 :                     ret = WEXITSTATUS(status);
     151           1 :                     assert_int_equal(ret, 0);
     152             :                 }
     153             :             } else {
     154           0 :                 DEBUG(SSSDBG_FUNC_DATA,
     155             :                     "Failed to wait for children %d\n", child_pid);
     156           0 :                 ret = EIO;
     157             :             }
     158             :     }
     159           1 : }
     160             : 
     161             : struct tevent_req *echo_child_write_send(TALLOC_CTX *mem_ctx,
     162             :                                          struct child_test_ctx *child_tctx,
     163             :                                          struct child_io_fds *io_fds,
     164             :                                          const char *input);
     165             : static void echo_child_write_done(struct tevent_req *subreq);
     166             : static void echo_child_read_done(struct tevent_req *subreq);
     167             : 
     168             : int __real_child_io_destructor(void *ptr);
     169             : 
     170           2 : int __wrap_child_io_destructor(void *ptr)
     171             : {
     172           2 :     destructor_called = 1;
     173           2 :     return __real_child_io_destructor(ptr);
     174             : }
     175             : 
     176             : /* Test that writing to the pipes works as expected */
     177           1 : void test_exec_child_io_destruct(void **state)
     178             : {
     179           1 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
     180             :                                                         struct child_test_ctx);
     181             :     struct child_io_fds *io_fds;
     182             : 
     183           1 :     io_fds = talloc(child_tctx, struct child_io_fds);
     184           1 :     io_fds->read_from_child_fd = -1;
     185           1 :     io_fds->write_to_child_fd = -1;
     186           1 :     assert_non_null(io_fds);
     187           1 :     talloc_set_destructor((void *) io_fds, child_io_destructor);
     188             : 
     189           1 :     io_fds->read_from_child_fd = child_tctx->pipefd_from_child[0];
     190           1 :     io_fds->write_to_child_fd = child_tctx->pipefd_to_child[1];
     191             : 
     192           1 :     destructor_called = 0;
     193           1 :     talloc_free(io_fds);
     194           1 :     assert_int_equal(destructor_called, 1);
     195             : 
     196           1 :     errno = 0;
     197           1 :     close(child_tctx->pipefd_from_child[0]);
     198           1 :     assert_int_equal(errno, EBADF);
     199             : 
     200           1 :     errno = 0;
     201           1 :     close(child_tctx->pipefd_from_child[1]);
     202           1 :     assert_int_equal(errno, 0);
     203             : 
     204           1 :     errno = 0;
     205           1 :     close(child_tctx->pipefd_to_child[0]);
     206           1 :     assert_int_equal(errno, 0);
     207             : 
     208           1 :     errno = 0;
     209           1 :     close(child_tctx->pipefd_to_child[1]);
     210           1 :     assert_int_equal(errno, EBADF);
     211           1 : }
     212             : 
     213             : void test_child_cb(int child_status,
     214             :                    struct tevent_signal *sige,
     215             :                    void *pvt);
     216             : 
     217             : /* Test that writing to the pipes works as expected */
     218           1 : void test_exec_child_handler(void **state)
     219             : {
     220             :     errno_t ret;
     221             :     pid_t child_pid;
     222           1 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
     223             :                                                         struct child_test_ctx);
     224             :     struct sss_child_ctx_old *child_old_ctx;
     225             : 
     226           1 :     ret = unsetenv("TEST_CHILD_ACTION");
     227           1 :     assert_int_equal(ret, 0);
     228             : 
     229           1 :     child_pid = fork();
     230           2 :     assert_int_not_equal(child_pid, -1);
     231           2 :     if (child_pid == 0) {
     232           1 :         ret = exec_child(child_tctx,
     233           1 :                          child_tctx->pipefd_to_child,
     234           1 :                          child_tctx->pipefd_from_child,
     235             :                          CHILD_DIR"/"TEST_BIN, 2);
     236           0 :         assert_int_equal(ret, EOK);
     237             :     }
     238             : 
     239           1 :     ret = child_handler_setup(child_tctx->test_ctx->ev, child_pid,
     240             :                               test_child_cb, child_tctx, &child_old_ctx);
     241           1 :     assert_int_equal(ret, EOK);
     242             : 
     243           1 :     ret = test_ev_loop(child_tctx->test_ctx);
     244           1 :     assert_int_equal(ret, EOK);
     245           1 :     assert_int_equal(child_tctx->test_ctx->error, 0);
     246           1 : }
     247             : 
     248           1 : void test_child_cb(int child_status,
     249             :                    struct tevent_signal *sige,
     250             :                    void *pvt)
     251             : {
     252           1 :     struct child_test_ctx *child_ctx = talloc_get_type(pvt, struct child_test_ctx);
     253             : 
     254           1 :     child_ctx->test_ctx->error = EIO;
     255           1 :     if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0) {
     256           1 :         child_ctx->test_ctx->error = 0;
     257             :     }
     258             : 
     259           1 :     child_ctx->test_ctx->done = true;
     260           1 : }
     261             : 
     262             : /* Test that writing to the pipes works as expected */
     263           1 : void test_exec_child_echo(void **state)
     264             : {
     265             :     errno_t ret;
     266             :     pid_t child_pid;
     267           1 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
     268             :                                                         struct child_test_ctx);
     269             :     struct tevent_req *req;
     270             :     struct child_io_fds *io_fds;
     271             : 
     272           1 :     setenv("TEST_CHILD_ACTION", "echo", 1);
     273             : 
     274           1 :     io_fds = talloc(child_tctx, struct child_io_fds);
     275           1 :     assert_non_null(io_fds);
     276           1 :     io_fds->read_from_child_fd = -1;
     277           1 :     io_fds->write_to_child_fd = -1;
     278           1 :     talloc_set_destructor((void *) io_fds, child_io_destructor);
     279             : 
     280           1 :     child_pid = fork();
     281           2 :     assert_int_not_equal(child_pid, -1);
     282           2 :     if (child_pid == 0) {
     283           1 :         ret = exec_child_ex(child_tctx,
     284           1 :                             child_tctx->pipefd_to_child,
     285           1 :                             child_tctx->pipefd_from_child,
     286             :                             CHILD_DIR"/"TEST_BIN, 2, NULL,
     287             :                             STDIN_FILENO, 3);
     288           0 :         assert_int_equal(ret, EOK);
     289             :     }
     290             : 
     291           1 :     DEBUG(SSSDBG_FUNC_DATA, "Forked into %d\n", child_pid);
     292             : 
     293           1 :     io_fds->read_from_child_fd = child_tctx->pipefd_from_child[0];
     294           1 :     close(child_tctx->pipefd_from_child[1]);
     295           1 :     io_fds->write_to_child_fd = child_tctx->pipefd_to_child[1];
     296           1 :     close(child_tctx->pipefd_to_child[0]);
     297             : 
     298           1 :     sss_fd_nonblocking(io_fds->write_to_child_fd);
     299           1 :     sss_fd_nonblocking(io_fds->read_from_child_fd);
     300             : 
     301           1 :     ret = child_handler_setup(child_tctx->test_ctx->ev, child_pid,
     302             :                               NULL, NULL, NULL);
     303           1 :     assert_int_equal(ret, EOK);
     304             : 
     305           1 :     req = echo_child_write_send(child_tctx, child_tctx, io_fds, ECHO_STR);
     306           1 :     assert_non_null(req);
     307             : 
     308           1 :     ret = test_ev_loop(child_tctx->test_ctx);
     309           1 :     talloc_free(io_fds);
     310           1 :     assert_int_equal(ret, EOK);
     311           1 : }
     312             : 
     313             : struct test_exec_echo_state {
     314             :     struct child_io_fds *io_fds;
     315             :     struct io_buffer buf;
     316             :     struct child_test_ctx *child_test_ctx;
     317             : };
     318             : 
     319           1 : struct tevent_req *echo_child_write_send(TALLOC_CTX *mem_ctx,
     320             :                                          struct child_test_ctx *child_tctx,
     321             :                                          struct child_io_fds *io_fds,
     322             :                                          const char *input)
     323             : {
     324             :     struct tevent_req *req;
     325             :     struct tevent_req *subreq;
     326             :     struct test_exec_echo_state *echo_state;
     327             : 
     328           1 :     req = tevent_req_create(mem_ctx, &echo_state, struct test_exec_echo_state);
     329           1 :     assert_non_null(req);
     330             : 
     331           1 :     echo_state->child_test_ctx = child_tctx;
     332             : 
     333           1 :     echo_state->buf.data = (unsigned char *) talloc_strdup(echo_state, input);
     334           1 :     assert_non_null(echo_state->buf.data);
     335           1 :     echo_state->buf.size = strlen(input) + 1;
     336           1 :     echo_state->io_fds = io_fds;
     337             : 
     338           1 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Writing..\n");
     339           3 :     subreq = write_pipe_send(child_tctx, child_tctx->test_ctx->ev,
     340           2 :                              echo_state->buf.data, echo_state->buf.size,
     341           1 :                              echo_state->io_fds->write_to_child_fd);
     342           1 :     assert_non_null(subreq);
     343           1 :     tevent_req_set_callback(subreq, echo_child_write_done, req);
     344             : 
     345           1 :     return req;
     346             : }
     347             : 
     348           1 : static void echo_child_write_done(struct tevent_req *subreq)
     349             : {
     350             :     struct tevent_req *req;
     351             :     struct test_exec_echo_state *echo_state;
     352             :     errno_t ret;
     353             : 
     354           1 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     355           1 :     echo_state = tevent_req_data(req, struct test_exec_echo_state);
     356             : 
     357           1 :     ret = write_pipe_recv(subreq);
     358           1 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Writing OK\n");
     359           1 :     talloc_zfree(subreq);
     360           1 :     assert_int_equal(ret, EOK);
     361             : 
     362           1 :     close(echo_state->io_fds->write_to_child_fd);
     363           1 :     echo_state->io_fds->write_to_child_fd = -1;
     364             : 
     365           1 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Reading..\n");
     366           2 :     subreq = read_pipe_send(echo_state,
     367           1 :                             echo_state->child_test_ctx->test_ctx->ev,
     368           1 :                             echo_state->io_fds->read_from_child_fd);
     369           1 :     assert_non_null(subreq);
     370           1 :     tevent_req_set_callback(subreq, echo_child_read_done, req);
     371           1 : }
     372             : 
     373           1 : static void echo_child_read_done(struct tevent_req *subreq)
     374             : {
     375             :     struct tevent_req *req;
     376             :     struct test_exec_echo_state *echo_state;
     377             :     errno_t ret;
     378             :     ssize_t len;
     379             :     uint8_t *buf;
     380             : 
     381           1 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     382           1 :     echo_state = tevent_req_data(req, struct test_exec_echo_state);
     383             : 
     384           1 :     ret = read_pipe_recv(subreq, echo_state, &buf, &len);
     385           1 :     talloc_zfree(subreq);
     386           1 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Reading OK\n");
     387           1 :     assert_int_equal(ret, EOK);
     388             : 
     389           1 :     close(echo_state->io_fds->read_from_child_fd);
     390           1 :     echo_state->io_fds->read_from_child_fd = -1;
     391             : 
     392           1 :     assert_string_equal(buf, echo_state->buf.data);
     393           1 :     echo_state->child_test_ctx->test_ctx->done = true;
     394           1 : }
     395             : 
     396             : void sss_child_cb(int pid, int wait_status, void *pvt);
     397             : 
     398             : /* Just make sure the exec works. The child does nothing but exits */
     399           1 : void test_sss_child(void **state)
     400             : {
     401             :     errno_t ret;
     402             :     pid_t child_pid;
     403           1 :     struct child_test_ctx *child_tctx = talloc_get_type(*state,
     404             :                                                         struct child_test_ctx);
     405             :     struct sss_sigchild_ctx *sc_ctx;
     406             :     struct sss_child_ctx *sss_child;
     407             : 
     408           1 :     ret = unsetenv("TEST_CHILD_ACTION");
     409           1 :     assert_int_equal(ret, 0);
     410             : 
     411           1 :     ret = sss_sigchld_init(child_tctx, child_tctx->test_ctx->ev, &sc_ctx);
     412           1 :     assert_int_equal(ret, EOK);
     413             : 
     414           1 :     child_pid = fork();
     415           2 :     assert_int_not_equal(child_pid, -1);
     416           2 :     if (child_pid == 0) {
     417           1 :         ret = exec_child(child_tctx,
     418           1 :                          child_tctx->pipefd_to_child,
     419           1 :                          child_tctx->pipefd_from_child,
     420             :                          CHILD_DIR"/"TEST_BIN, 2);
     421           0 :         assert_int_equal(ret, EOK);
     422             :     }
     423             : 
     424           1 :     ret = sss_child_register(child_tctx, sc_ctx,
     425             :                              child_pid,
     426             :                              sss_child_cb,
     427             :                              child_tctx, &sss_child);
     428           1 :     assert_int_equal(ret, EOK);
     429             : 
     430           1 :     ret = test_ev_loop(child_tctx->test_ctx);
     431           1 :     assert_int_equal(ret, EOK);
     432           1 :     assert_int_equal(child_tctx->test_ctx->error, 0);
     433           1 : }
     434             : 
     435           1 : void sss_child_cb(int pid, int wait_status, void *pvt)
     436             : {
     437           1 :     struct child_test_ctx *child_ctx = talloc_get_type(pvt, struct child_test_ctx);
     438             : 
     439           1 :     child_ctx->test_ctx->error = EIO;
     440           1 :     if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) == 0) {
     441           1 :         child_ctx->test_ctx->error = 0;
     442             :     }
     443             : 
     444           1 :     child_ctx->test_ctx->done = true;
     445           1 : }
     446             : 
     447           1 : int main(int argc, const char *argv[])
     448             : {
     449             :     int rv;
     450             :     poptContext pc;
     451             :     int opt;
     452           6 :     struct poptOption long_options[] = {
     453             :         POPT_AUTOHELP
     454           5 :         SSSD_DEBUG_OPTS
     455             :         POPT_TABLEEND
     456             :     };
     457             : 
     458           1 :     const struct CMUnitTest tests[] = {
     459             :         cmocka_unit_test_setup_teardown(test_exec_child,
     460             :                                         child_test_setup,
     461             :                                         child_test_teardown),
     462             :         cmocka_unit_test_setup_teardown(test_exec_child_extra_args,
     463             :                                         child_test_setup,
     464             :                                         child_test_teardown),
     465             :         cmocka_unit_test_setup_teardown(test_exec_child_io_destruct,
     466             :                                         child_test_setup,
     467             :                                         child_test_teardown),
     468             :         cmocka_unit_test_setup_teardown(test_exec_child_handler,
     469             :                                         child_test_setup,
     470             :                                         child_test_teardown),
     471             :         cmocka_unit_test_setup_teardown(test_exec_child_echo,
     472             :                                         child_test_setup,
     473             :                                         child_test_teardown),
     474             :         cmocka_unit_test_setup_teardown(test_sss_child,
     475             :                                         child_test_setup,
     476             :                                         child_test_teardown),
     477             :     };
     478             : 
     479             :     /* Set debug level to invalid value so we can deside if -d 0 was used. */
     480           1 :     debug_level = SSSDBG_INVALID;
     481             : 
     482           1 :     pc = poptGetContext(argv[0], argc, argv, long_options, 0);
     483           1 :     while((opt = poptGetNextOpt(pc)) != -1) {
     484             :         switch(opt) {
     485             :         default:
     486           0 :             fprintf(stderr, "\nInvalid option %s: %s\n\n",
     487             :                     poptBadOption(pc, 0), poptStrerror(opt));
     488           0 :             poptPrintUsage(pc, stderr, 0);
     489           0 :             return 1;
     490             :         }
     491             :     }
     492           1 :     poptFreeContext(pc);
     493             : 
     494           1 :     DEBUG_CLI_INIT(debug_level);
     495             : 
     496           1 :     rv = cmocka_run_group_tests(tests, NULL, NULL);
     497           1 :     return rv;
     498             : }

Generated by: LCOV version 1.10