diff --git a/tests/libipm/test_libipm.h b/tests/libipm/test_libipm.h index 5105a1c1..57b18f76 100644 --- a/tests/libipm/test_libipm.h +++ b/tests/libipm/test_libipm.h @@ -11,6 +11,7 @@ enum LIBIPM_VERSION = 2, LIBIPM_HEADER_SIZE = 12, LIBIPM_MAX_MESSAGE_SIZE = 8192, + LIBIPM_MAX_FD_PER_MSG = 8, LIBIPM_MAX_PAYLOAD_SIZE = LIBIPM_MAX_MESSAGE_SIZE - LIBIPM_HEADER_SIZE }; @@ -18,6 +19,7 @@ enum extern struct trans *g_t_out; /* Channel for outputting libipm messages */ extern struct trans *g_t_in; /* Channel for inputting libipm messages */ extern struct trans *g_t_vanilla; /* Non-SCP channel */ +extern int g_fd; /* An open file descriptor (/dev/zero) */ extern const char *g_supported_types; /* All recognised type codes */ extern const char *g_unimplemented_types; /* recognised, unimplemented codes */ @@ -42,6 +44,14 @@ check_binary_data_eq(const void *actual_data, const void *expected_data, unsigned int expected_len); +/** + * Check the file descriptor specified is working as /dev/zero + * + * If it isn't, an exception is raised using ck_* calls + */ +void +check_fd_is_dev_zero(int fd); + /** * Looks for the specified string in the specified stream * @param s Stream to search @@ -54,6 +64,11 @@ check_binary_data_eq(const void *actual_data, int does_stream_contain_string(const struct stream *s, const char *str); +/** + * Returns the number of open file descriptors in the process */ +unsigned int +get_open_fd_count(void); + Suite *make_suite_test_libipm_send_calls(void); Suite *make_suite_test_libipm_recv_calls(void); diff --git a/tests/libipm/test_libipm_main.c b/tests/libipm/test_libipm_main.c index b0933fae..6d43aa72 100644 --- a/tests/libipm/test_libipm_main.c +++ b/tests/libipm/test_libipm_main.c @@ -5,6 +5,8 @@ #include #include +#include +#include #include "log.h" #include "libipm.h" @@ -17,9 +19,10 @@ struct trans *g_t_out = NULL; struct trans *g_t_in = NULL; struct trans *g_t_vanilla = NULL; +int g_fd = -1; const char *g_supported_types = "ybnqiuxtsdhogB"; -const char *g_unimplemented_types = "dhog"; +const char *g_unimplemented_types = "dog"; /******************************************************************************/ static const char * @@ -37,6 +40,7 @@ suite_test_libipm_calls_start(void) struct trans *t1 = NULL; struct trans *t2 = NULL; struct trans *t3 = NULL; + int fd = -1; int success = 0; if ((t1 = trans_create(TRANS_MODE_UNIX, 128, 128)) == NULL) @@ -54,6 +58,11 @@ suite_test_libipm_calls_start(void) const char *errstr = g_get_strerror(); LOG(LOG_LEVEL_ERROR, "Can't create test transport 3 [%s]", errstr); } + else if ((fd = g_file_open("/dev/zero")) < 0) + { + const char *errstr = g_get_strerror(); + LOG(LOG_LEVEL_ERROR, "Can't open /dev/zero [%s]", errstr); + } else if ((istatus = g_sck_local_socketpair(sck)) < 0) { const char *errstr = g_get_strerror(); @@ -89,12 +98,17 @@ suite_test_libipm_calls_start(void) g_t_out = t1; g_t_in = t2; g_t_vanilla = t3; + g_fd = fd; } else { trans_delete(t1); trans_delete(t2); trans_delete(t3); + if (fd >= 0) + { + g_file_close(fd); + } } } @@ -138,6 +152,20 @@ check_binary_data_eq(const void *actual_data, } } +/******************************************************************************/ +void +check_fd_is_dev_zero(int fd) +{ + char buff[1] = { '\001' }; + int status; + status = g_file_read(fd, buff, sizeof(buff)); + ck_assert_int_eq(status, 1); + ck_assert_int_eq(buff[0], '\0'); + + status = g_file_write(fd, buff, sizeof(buff)); + ck_assert_int_eq(status, 1); +} + /******************************************************************************/ int @@ -162,6 +190,51 @@ does_stream_contain_string(const struct stream *s, const char *str) return 0; } +/******************************************************************************/ +unsigned int +get_open_fd_count(void) +{ + unsigned int i; + unsigned int rv; + + // What's the max number of file descriptors? + struct rlimit nofile; + if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) + { + const char *errstr = g_get_strerror(); + ck_abort_msg("Can't create socketpair [%s]", errstr); + } + + struct pollfd *fds = + (struct pollfd *)g_malloc(sizeof(struct pollfd) * nofile.rlim_cur, 0); + ck_assert_ptr_nonnull(fds); + + for (i = 0 ; i < nofile.rlim_cur; ++i) + { + fds[i].fd = i; + fds[i].events = 0; + fds[i].revents = 0; + } + + if (poll(fds, nofile.rlim_cur, 0) < 0) + { + const char *errstr = g_get_strerror(); + ck_abort_msg("Can't poll fds [%s]", errstr); + } + + rv = nofile.rlim_cur; + for (i = 0 ; i < nofile.rlim_cur; ++i) + { + if (fds[i].revents == POLLNVAL) + { + --rv; + } + } + + g_free(fds); + + return rv; +} /******************************************************************************/ int main (void) diff --git a/tests/libipm/test_libipm_recv_calls.c b/tests/libipm/test_libipm_recv_calls.c index dc7f135c..6129be03 100644 --- a/tests/libipm/test_libipm_recv_calls.c +++ b/tests/libipm/test_libipm_recv_calls.c @@ -245,13 +245,14 @@ START_TEST(test_libipm_send_recv_all_test) int64_t x; uint64_t t; char *s; + int h = -1; char B[sizeof(bin_out)]; char c; /* The message is small enough to fit in the socket buffer */ status = libipm_msg_out_simple_send( g_t_out, TEST_MESSAGE_NO, - "ybnqiuxtsB", + "ybnqiuxtshB", TEST_y_VALUE, TEST_b_VALUE, TEST_n_VALUE, @@ -261,6 +262,7 @@ START_TEST(test_libipm_send_recv_all_test) TEST_x_VALUE, TEST_t_VALUE, TEST_s_VALUE, + g_fd, &binary_desc); ck_assert_int_eq(status, E_LI_SUCCESS); @@ -273,8 +275,8 @@ START_TEST(test_libipm_send_recv_all_test) status = libipm_msg_in_parse( g_t_in, - "ybnqiuxtsB", - &y, &b, &n, &q, &i, &u, &x, &t, &s, &binary_desc); + "ybnqiuxtshB", + &y, &b, &n, &q, &i, &u, &x, &t, &s, &h, &binary_desc); ck_assert_int_eq(status, E_LI_SUCCESS); ck_assert_int_eq(y, TEST_y_VALUE); @@ -287,6 +289,13 @@ START_TEST(test_libipm_send_recv_all_test) ck_assert_int_eq(t, TEST_t_VALUE); check_binary_data_eq(TEST_s_VALUE, sizeof(TEST_s_VALUE) - 1, s, g_strlen(s)); + /* The file descriptor should not be -1, neither should it be + * the value we sent. It should also point to /dev/zero */ + ck_assert_int_ne(h, -1); + ck_assert_int_ne(h, g_fd); + check_fd_is_dev_zero(h); + g_file_close(h); + check_binary_data_eq(bin_out, sizeof(bin_out), B, sizeof(B)); /* Check we're at the end of the message */ @@ -540,8 +549,62 @@ START_TEST(test_libipm_receive_s_type) } END_TEST + /***************************************************************************//** - * Checks various receive errors for 's' + * Checks various receive errors for 'h' + */ +START_TEST(test_libipm_receive_h_type) +{ + enum libipm_status status; + int istatus; + unsigned int i; + int fd_count; + + /* Get the number of open file descriptors */ + int base_fd_count = get_open_fd_count(); + ck_assert_int_gt(base_fd_count, 0); + + /* Send the max number of copies of the /dev/zero + * file descriptor */ + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, NULL); + ck_assert_int_eq(status, E_LI_SUCCESS); + + for (i = 0 ; i < LIBIPM_MAX_FD_PER_MSG; ++i) + { + status = libipm_msg_out_append(g_t_out, "h", g_fd); + ck_assert_int_eq(status, E_LI_SUCCESS); + } + libipm_msg_out_mark_end(g_t_out); + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the number of file descriptors has gone up as expected */ + fd_count = get_open_fd_count(); + ck_assert_int_eq(fd_count, base_fd_count + LIBIPM_MAX_FD_PER_MSG); + + /* Check half the descriptors work */ + for (i = 0 ; i < LIBIPM_MAX_FD_PER_MSG / 2; ++i) + { + int h = -1; + status = libipm_msg_in_parse(g_t_in, "h", &h); + ck_assert_int_eq(status, E_LI_SUCCESS); + check_fd_is_dev_zero(h); + g_file_close(h); + } + + /* Close the message without reading the other descriptors */ + libipm_msg_in_reset(g_t_in); + + /* Check all the file descriptors we received have been closed */ + fd_count = get_open_fd_count(); + ck_assert_int_eq(fd_count, base_fd_count); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'B' */ START_TEST(test_libipm_receive_B_type) { @@ -844,6 +907,7 @@ make_suite_test_libipm_recv_calls(void) tcase_add_test(tc, test_libipm_receive_x_type); tcase_add_test(tc, test_libipm_receive_t_type); tcase_add_test(tc, test_libipm_receive_s_type); + tcase_add_test(tc, test_libipm_receive_h_type); tcase_add_test(tc, test_libipm_receive_B_type); tcase_add_test(tc, test_libipm_receive_no_type); tcase_add_test(tc, test_libipm_receive_unexpected_type); diff --git a/tests/libipm/test_libipm_send_calls.c b/tests/libipm/test_libipm_send_calls.c index f9856583..34cd2cb0 100644 --- a/tests/libipm/test_libipm_send_calls.c +++ b/tests/libipm/test_libipm_send_calls.c @@ -44,7 +44,7 @@ static const unsigned char all_test_expected_buff[] = { BITS16_LE(LIBIPM_VERSION), - BITS16_LE(73), /* Header : message length */ + BITS16_LE(74), /* Header : message length */ BITS16_LE(LIBIPM_FAC_TEST), BITS16_LE(TEST_MESSAGE_NO), BITS32_LE(0), @@ -59,6 +59,7 @@ static const unsigned char all_test_expected_buff[] = 't', BITS64_LE(ALL_TEST_t_VALUE), /* String + terminator */ 's', ALL_TEST_s_VALUE, + 'h', /* No buffer value is needed for 'h' */ /* Fixed size block */ 'B', BITS16_LE(7) /* length */, ALL_TEST_B_VALUE }; @@ -154,7 +155,7 @@ START_TEST(test_libipm_append_all_test) status = libipm_msg_out_init( g_t_out, TEST_MESSAGE_NO, - "ybnqiuxtsB", + "ybnqiuxtshB", ALL_TEST_y_VALUE, ALL_TEST_b_VALUE, ALL_TEST_n_VALUE, @@ -164,6 +165,7 @@ START_TEST(test_libipm_append_all_test) ALL_TEST_x_VALUE, ALL_TEST_t_VALUE, string, + g_fd, &binary_desc); ck_assert_int_eq(status, E_LI_SUCCESS); @@ -352,6 +354,30 @@ START_TEST(test_libipm_send_s_type) } END_TEST +/***************************************************************************//** + * Checks various send errors for 'h' + */ +START_TEST(test_libipm_send_h_type) +{ + enum libipm_status status; + unsigned int i; + + test_append_at_end_of_message('h', 1); + + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, NULL); + ck_assert_int_eq(status, E_LI_SUCCESS); + + for (i = 0 ; i < LIBIPM_MAX_FD_PER_MSG; ++i) + { + status = libipm_msg_out_append(g_t_out, "h", g_fd); + ck_assert_int_eq(status, E_LI_SUCCESS); + } + + status = libipm_msg_out_append(g_t_out, "h", 1); + ck_assert_int_eq(status, E_LI_TOO_MANY_FDS); +} +END_TEST + /***************************************************************************//** * Checks various send errors for 'B' */ @@ -496,6 +522,7 @@ make_suite_test_libipm_send_calls(void) tcase_add_test(tc, test_libipm_send_x_type); tcase_add_test(tc, test_libipm_send_t_type); tcase_add_test(tc, test_libipm_send_s_type); + tcase_add_test(tc, test_libipm_send_h_type); tcase_add_test(tc, test_libipm_send_B_type); tcase_add_test(tc, test_libipm_send_y_type); tcase_add_test(tc, test_libipm_send_bad_types);