1204861Sdes/*-
2137015Sdes * Copyright (c) 2014 Spectra Logic Corporation. All rights reserved.
3137015Sdes * Redistribution and use in source and binary forms, with or without
4147001Sdes * modification, are permitted provided that the following conditions
5137015Sdes * are met:
6137015Sdes * 1. Redistributions of source code must retain the above copyright
7137015Sdes *    notice, this list of conditions and the following disclaimer.
8218767Sdes * 2. Redistributions in binary form must reproduce the above copyright
9146998Sdes *    notice, this list of conditions and the following disclaimer in the
10146998Sdes *    documentation and/or other materials provided with the distribution.
11146998Sdes *
12146998Sdes * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13137015Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14137015Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15146998Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
16137015Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17137015Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18137015Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19137015Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20146998Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21146998Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22137015Sdes * SUCH DAMAGE.
23146998Sdes */
24146998Sdes
25146998Sdes#include <sys/cdefs.h>
26137015Sdes__FBSDID("$FreeBSD: releng/11.0/sbin/devd/tests/client_test.c 297838 2016-04-11 22:14:29Z asomers $");
27137015Sdes
28204861Sdes#include <stdbool.h>
29137015Sdes#include <stdio.h>
30137015Sdes
31137015Sdes#include <sys/param.h>
32137015Sdes#include <sys/types.h>
33137015Sdes#include <sys/socket.h>
34137015Sdes#include <sys/un.h>
35137015Sdes
36137015Sdes#include <atf-c.h>
37137015Sdes
38204861Sdesconst char create_pat[] = "!system=DEVFS subsystem=CDEV type=CREATE cdev=md";
39137015Sdesconst char destroy_pat[] = "!system=DEVFS subsystem=CDEV type=DESTROY cdev=md";
40137015Sdes
41137015Sdes/* Helper functions*/
42137015Sdes
43137015Sdes/*
44204861Sdes * Create two devd events.  The easiest way I know of, that requires no special
45137015Sdes * hardware, is to create md(4) devices.
46137015Sdes */
47137015Sdesstatic void
48137015Sdescreate_two_events(void)
49137015Sdes{
50137015Sdes	FILE *create_stdout;
51204861Sdes	FILE *destroy_stdout;
52137015Sdes	char mdname[80];
53137015Sdes	char destroy_cmd[80];
54137015Sdes	char *error;
55137015Sdes
56137015Sdes	create_stdout = popen("mdconfig -a -s 64 -t null", "r");
57204861Sdes	ATF_REQUIRE(create_stdout != NULL);
58137015Sdes	error = fgets(mdname, sizeof(mdname), create_stdout);
59137015Sdes	ATF_REQUIRE(error != NULL);
60137015Sdes	/* We only expect one line of output */
61137015Sdes	ATF_REQUIRE_EQ(0, pclose(create_stdout));
62137015Sdes
63137015Sdes	snprintf(destroy_cmd, nitems(destroy_cmd), "mdconfig -d -u %s", mdname);
64137015Sdes	destroy_stdout = popen(destroy_cmd, "r");
65137015Sdes	ATF_REQUIRE(destroy_stdout != NULL);
66204861Sdes	/* We expect no output */
67137015Sdes	ATF_REQUIRE_EQ(0, pclose(destroy_stdout));
68137015Sdes}
69137015Sdes
70137015Sdes/* Setup and return an open client socket */
71137015Sdesstatic int
72137015Sdescommon_setup(int socktype, const char* sockpath) {
73137015Sdes	struct sockaddr_un devd_addr;
74204861Sdes	int s, error;
75137015Sdes
76137015Sdes	memset(&devd_addr, 0, sizeof(devd_addr));
77137015Sdes	devd_addr.sun_family = PF_LOCAL;
78137015Sdes	strlcpy(devd_addr.sun_path, sockpath, sizeof(devd_addr.sun_path));
79137015Sdes	s = socket(PF_LOCAL, socktype, 0);
80137015Sdes	ATF_REQUIRE(s >= 0);
81137015Sdes	error = connect(s, (struct sockaddr*)&devd_addr, SUN_LEN(&devd_addr));
82146998Sdes	ATF_REQUIRE_EQ(0, error);
83204861Sdes
84146998Sdes	create_two_events();
85146998Sdes	return (s);
86204861Sdes}
87146998Sdes
88146998Sdes/*
89146998Sdes * Test Cases
90146998Sdes */
91147001Sdes
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