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
create_two_events(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
common_setup(int socktype,const char * sockpath)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);
ATF_TC_BODY(seqpacket,tc)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);
ATF_TC_BODY(stream,tc)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
ATF_TP_ADD_TCS(tp)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