1 /*- 2 * Copyright (c) 2014 Spectra Logic Corporation. All rights reserved. 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 1. Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 12 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 16 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 * SUCH DAMAGE. 23 */ 24 25 #include <sys/param.h> 26 #include <sys/socket.h> 27 #include <sys/un.h> 28 29 #include <stdbool.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 33 #include <atf-c.h> 34 35 const char create_pat[] = "!system=DEVFS subsystem=CDEV type=CREATE cdev=md"; 36 const char destroy_pat[] = "!system=DEVFS subsystem=CDEV type=DESTROY cdev=md"; 37 38 /* Helper functions*/ 39 40 /* 41 * Create two devd events. The easiest way I know of, that requires no special 42 * hardware, is to create md(4) devices. 43 */ 44 static void 45 create_two_events(void) 46 { 47 FILE *create_stdout; 48 FILE *destroy_stdout; 49 char mdname[80]; 50 char destroy_cmd[95]; 51 char *error; 52 53 create_stdout = popen("mdconfig -a -s 64 -t null", "r"); 54 ATF_REQUIRE(create_stdout != NULL); 55 error = fgets(mdname, sizeof(mdname), create_stdout); 56 ATF_REQUIRE(error != NULL); 57 /* We only expect one line of output */ 58 ATF_REQUIRE_EQ(0, pclose(create_stdout)); 59 60 snprintf(destroy_cmd, nitems(destroy_cmd), "mdconfig -d -u %s", mdname); 61 destroy_stdout = popen(destroy_cmd, "r"); 62 ATF_REQUIRE(destroy_stdout != NULL); 63 /* We expect no output */ 64 ATF_REQUIRE_EQ(0, pclose(destroy_stdout)); 65 } 66 67 /* Setup and return an open client socket */ 68 static int 69 common_setup(int socktype, const char* sockpath) { 70 struct sockaddr_un devd_addr; 71 int s, error; 72 73 memset(&devd_addr, 0, sizeof(devd_addr)); 74 devd_addr.sun_family = PF_LOCAL; 75 strlcpy(devd_addr.sun_path, sockpath, sizeof(devd_addr.sun_path)); 76 s = socket(PF_LOCAL, socktype, 0); 77 ATF_REQUIRE(s >= 0); 78 error = connect(s, (struct sockaddr*)&devd_addr, SUN_LEN(&devd_addr)); 79 ATF_REQUIRE_EQ(0, error); 80 81 create_two_events(); 82 return (s); 83 } 84 85 /* 86 * Test Cases 87 */ 88 89 /* 90 * Open a client connection to devd, create some events, and test that they can 91 * be read _whole_ and _one_at_a_time_ from the socket 92 */ 93 ATF_TC_WITHOUT_HEAD(seqpacket); 94 ATF_TC_BODY(seqpacket, tc) 95 { 96 int s; 97 bool got_create_event = false; 98 bool got_destroy_event = false; 99 100 s = common_setup(SOCK_SEQPACKET, "/var/run/devd.seqpacket.pipe"); 101 /* 102 * Loop until both events are detected on _different_ reads 103 * There may be extra events due to unrelated system activity 104 * If we never get both events, then the test will timeout. 105 */ 106 while (!(got_create_event && got_destroy_event)) { 107 int cmp; 108 ssize_t len; 109 char event[1024]; 110 111 /* Read 1 less than sizeof(event) to allow space for NULL */ 112 len = recv(s, event, sizeof(event) - 1, MSG_WAITALL); 113 ATF_REQUIRE(len != -1); 114 /* NULL terminate the result */ 115 event[len] = '\0'; 116 printf("%s", event); 117 cmp = strncmp(event, create_pat, sizeof(create_pat) - 1); 118 if (cmp == 0) 119 got_create_event = true; 120 121 cmp = strncmp(event, destroy_pat, sizeof(destroy_pat) - 1); 122 if (cmp == 0) 123 got_destroy_event = true; 124 } 125 126 close(s); 127 } 128 129 /* 130 * Open a client connection to devd using the stream socket, create some 131 * events, and test that they can be read in any number of reads. 132 */ 133 ATF_TC_WITHOUT_HEAD(stream); 134 ATF_TC_BODY(stream, tc) 135 { 136 char *event; 137 int s; 138 bool got_create_event = false; 139 bool got_destroy_event = false; 140 size_t len = 0, sz; 141 142 s = common_setup(SOCK_STREAM, "/var/run/devd.pipe"); 143 144 /* 145 * Use a large buffer: we're reading from a stream socket so can't rely 146 * on record boundaries. Instead, we just keep appending to the buffer. 147 */ 148 sz = 1024 * 1024; 149 event = malloc(sz); 150 ATF_REQUIRE(event != NULL); 151 152 /* 153 * Loop until both events are detected on the same or different reads. 154 * There may be extra events due to unrelated system activity. 155 * If we never get both events, then the test will timeout. 156 */ 157 while (!(got_create_event && got_destroy_event) && len < sz - 1) { 158 ssize_t newlen; 159 char *create_pos, *destroy_pos; 160 161 /* Read 1 less than sizeof(event) to allow space for NULL */ 162 newlen = read(s, &event[len], sz - len - 1); 163 ATF_REQUIRE(newlen > 0); 164 len += newlen; 165 /* NULL terminate the result */ 166 event[len] = '\0'; 167 168 create_pos = strstr(event, create_pat); 169 if (create_pos != NULL) 170 got_create_event = true; 171 172 destroy_pos = strstr(event, destroy_pat); 173 if (destroy_pos != NULL) 174 got_destroy_event = true; 175 } 176 printf("%s", event); 177 if (len >= sz - 1) 178 atf_tc_fail("Event buffer overflowed"); 179 180 free(event); 181 close(s); 182 } 183 184 /* 185 * Main. 186 */ 187 188 ATF_TP_ADD_TCS(tp) 189 { 190 ATF_TP_ADD_TC(tp, seqpacket); 191 ATF_TP_ADD_TC(tp, stream); 192 193 return (atf_no_error()); 194 } 195 196