client_test.c revision 297970
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/cdefs.h>
26__FBSDID("$FreeBSD: stable/10/sbin/devd/tests/client_test.c 297970 2016-04-14 15:24:45Z asomers $");
27
28#include <stdbool.h>
29#include <stdio.h>
30
31#include <sys/param.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35
36#include <atf-c.h>
37
38const char create_pat[] = "!system=DEVFS subsystem=CDEV type=CREATE cdev=md";
39const char destroy_pat[] = "!system=DEVFS subsystem=CDEV type=DESTROY cdev=md";
40
41/* Helper functions*/
42
43/*
44 * Create two devd events.  The easiest way I know of, that requires no special
45 * hardware, is to create md(4) devices.
46 */
47static void
48create_two_events(void)
49{
50	FILE *create_stdout;
51	FILE *destroy_stdout;
52	char mdname[80];
53	char destroy_cmd[80];
54	char *error;
55
56	create_stdout = popen("mdconfig -a -s 64 -t swap", "r");
57	ATF_REQUIRE(create_stdout != NULL);
58	error = fgets(mdname, sizeof(mdname), create_stdout);
59	ATF_REQUIRE(error != NULL);
60	/* We only expect one line of output */
61	ATF_REQUIRE_EQ(0, pclose(create_stdout));
62
63	snprintf(destroy_cmd, nitems(destroy_cmd), "mdconfig -d -u %s", mdname);
64	destroy_stdout = popen(destroy_cmd, "r");
65	ATF_REQUIRE(destroy_stdout != NULL);
66	/* We expect no output */
67	ATF_REQUIRE_EQ(0, pclose(destroy_stdout));
68}
69
70/* Setup and return an open client socket */
71static int
72common_setup(int socktype, const char* sockpath) {
73	struct sockaddr_un devd_addr;
74	int s, error;
75
76	memset(&devd_addr, 0, sizeof(devd_addr));
77	devd_addr.sun_family = PF_LOCAL;
78	strlcpy(devd_addr.sun_path, sockpath, sizeof(devd_addr.sun_path));
79	s = socket(PF_LOCAL, socktype, 0);
80	ATF_REQUIRE(s >= 0);
81	error = connect(s, (struct sockaddr*)&devd_addr, SUN_LEN(&devd_addr));
82	ATF_REQUIRE_EQ(0, error);
83
84	create_two_events();
85	return (s);
86}
87
88/*
89 * Test Cases
90 */
91
92/*
93 * Open a client connection to devd, create some events, and test that they can
94 * be read _whole_ and _one_at_a_time_ from the socket
95 */
96ATF_TC_WITHOUT_HEAD(seqpacket);
97ATF_TC_BODY(seqpacket, tc)
98{
99	int s;
100	bool got_create_event = false;
101	bool got_destroy_event = false;
102
103	s = common_setup(SOCK_SEQPACKET, "/var/run/devd.seqpacket.pipe");
104	/*
105	 * Loop until both events are detected on _different_ reads
106	 * There may be extra events due to unrelated system activity
107	 * If we never get both events, then the test will timeout.
108	 */
109	while (!(got_create_event && got_destroy_event)) {
110		int cmp;
111		ssize_t len;
112		char event[1024];
113
114		/* Read 1 less than sizeof(event) to allow space for NULL */
115		len = recv(s, event, sizeof(event) - 1, MSG_WAITALL);
116		ATF_REQUIRE(len != -1);
117		/* NULL terminate the result */
118		event[len] = '\0';
119		printf("%s", event);
120		cmp = strncmp(event, create_pat, sizeof(create_pat) - 1);
121		if (cmp == 0)
122			got_create_event = true;
123
124		cmp = strncmp(event, destroy_pat, sizeof(destroy_pat) - 1);
125		if (cmp == 0)
126			got_destroy_event = true;
127	}
128
129	close(s);
130}
131
132/*
133 * Open a client connection to devd using the stream socket, create some
134 * events, and test that they can be read in any number of reads.
135 */
136ATF_TC_WITHOUT_HEAD(stream);
137ATF_TC_BODY(stream, tc)
138{
139	int s;
140	bool got_create_event = false;
141	bool got_destroy_event = false;
142	ssize_t len = 0;
143
144	s = common_setup(SOCK_STREAM, "/var/run/devd.pipe");
145	/*
146	 * Loop until both events are detected on the same or different reads.
147	 * There may be extra events due to unrelated system activity.
148	 * If we never get both events, then the test will timeout.
149	 */
150	while (!(got_create_event && got_destroy_event)) {
151		char event[1024];
152		ssize_t newlen;
153		char *create_pos, *destroy_pos;
154
155		/* Read 1 less than sizeof(event) to allow space for NULL */
156		newlen = read(s, &event[len], sizeof(event) - len - 1);
157		ATF_REQUIRE(newlen != -1);
158		len += newlen;
159		/* NULL terminate the result */
160		event[len] = '\0';
161		printf("%s", event);
162
163		create_pos = strstr(event, create_pat);
164		if (create_pos != NULL)
165			got_create_event = true;
166
167		destroy_pos = strstr(event, destroy_pat);
168		if (destroy_pos != NULL)
169			got_destroy_event = true;
170	}
171
172	close(s);
173}
174
175/*
176 * Main.
177 */
178
179ATF_TP_ADD_TCS(tp)
180{
181	ATF_TP_ADD_TC(tp, seqpacket);
182	ATF_TP_ADD_TC(tp, stream);
183
184	return (atf_no_error());
185}
186
187