xref: /illumos-gate/usr/src/test/os-tests/tests/sockfs/timeouts.c (revision 1c1f30a61162b465dc08830707c1c2509ecfc872)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2024 Oxide Computer Company
14  */
15 
16 /*
17  * Test that setting timeout options on a UNIX stream socket after connection
18  * works, in that the timeout values are accepted and subsequently returned.
19  */
20 
21 #include <err.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <unistd.h>
29 
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/stdbool.h>
33 #include <sys/sysmacros.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
37 
38 static bool pass = true;
39 
40 typedef struct to_test {
41 	char *name;		/* Name of the test */
42 	int option;		/* Socket option */
43 	time_t sec;		/* Number of seconds */
44 	suseconds_t usec;	/* and microseconds */
45 } to_test_t;
46 
47 static to_test_t tests[] = {
48 	{
49 		.name = "Set 5s receive",
50 		.option = SO_RCVTIMEO,
51 		.sec = 5,
52 		.usec = 0
53 	}, {
54 		.name = "Set 5s send",
55 		.option = SO_SNDTIMEO,
56 		.sec = 5,
57 		.usec = 0
58 	}, {
59 		.name = "Set 15410s receive",
60 		.option = SO_RCVTIMEO,
61 		.sec = 15410,
62 		.usec = 0
63 	}, {
64 		.name = "Set 15410s send",
65 		.option = SO_SNDTIMEO,
66 		.sec = 15410,
67 		.usec = 0
68 	}, {
69 		.name = "Set 0s receive",
70 		.option = SO_RCVTIMEO,
71 		.sec = 0,
72 		.usec = 0
73 	}, {
74 		.name = "Set 0s send",
75 		.option = SO_SNDTIMEO,
76 		.sec = 0,
77 		.usec = 0
78 	}, {
79 		.name = "Set 5.5s receive",
80 		.option = SO_RCVTIMEO,
81 		.sec = 5,
82 		.usec = MICROSEC / 2,
83 	}, {
84 		.name = "Set 5.5s send",
85 		.option = SO_SNDTIMEO,
86 		.sec = 5,
87 		.usec = MICROSEC / 2,
88 	}
89 };
90 
91 static int
server(const char * sockpath)92 server(const char *sockpath)
93 {
94 	struct sockaddr_un addr;
95 	int sock;
96 
97 	sock = socket(PF_LOCAL, SOCK_STREAM, 0);
98 	if (sock == -1)
99 		err(EXIT_FAILURE, "failed to create socket");
100 	addr.sun_family = AF_UNIX;
101 	strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
102 	if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
103 		err(EXIT_FAILURE, "bind failed");
104 	if (listen(sock, 0) == -1)
105 		err(EXIT_FAILURE, "listen failed");
106 
107 	return (sock);
108 }
109 
110 static int
client(const char * sockpath)111 client(const char *sockpath)
112 {
113 	struct sockaddr_un addr;
114 	int sock;
115 
116 	sock = socket(PF_LOCAL, SOCK_STREAM, 0);
117 	if (sock == -1)
118 		err(EXIT_FAILURE, "failed to create socket");
119 	addr.sun_family = AF_UNIX;
120 	strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
121 	if (connect(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
122 		err(EXIT_FAILURE, "could not connect to server socket");
123 
124 	return (sock);
125 }
126 
127 static void __PRINTFLIKE(2)
fail(const to_test_t * t,const char * fmt,...)128 fail(const to_test_t *t, const char *fmt, ...)
129 {
130 	va_list ap;
131 
132 	fprintf(stderr, "[FAIL] %s: ", t->name);
133 	va_start(ap, fmt);
134 	vfprintf(stderr, fmt, ap);
135 	va_end(ap);
136 	fprintf(stderr, "\n");
137 	pass = false;
138 }
139 
140 int
main(int argc,const char ** argv)141 main(int argc, const char **argv)
142 {
143 	char sockpath[] = "/tmp/to.testsock.XXXXXX";
144 	int sfd, cfd;
145 
146 	if (mktemp(sockpath) == NULL)
147 		err(EXIT_FAILURE, "Failed to make temporary socket path");
148 
149 	sfd = server(sockpath);
150 	cfd = client(sockpath);
151 
152 	for (uint_t i = 0; i < ARRAY_SIZE(tests); i++) {
153 		const to_test_t *t = &tests[i];
154 		struct timeval tv = { 0 };
155 		socklen_t optlen;
156 
157 		tv.tv_sec = t->sec;
158 		tv.tv_usec = t->usec;
159 		optlen = sizeof (tv);
160 		if (setsockopt(cfd, SOL_SOCKET, t->option, &tv, optlen) != 0) {
161 			fail(t, "setsockopt error: %s", strerror(errno));
162 			pass = false;
163 			continue;
164 		}
165 
166 		bzero(&tv, sizeof (tv));
167 		if (getsockopt(cfd, SOL_SOCKET, t->option, &tv, &optlen) != 0) {
168 			fail(t, "getsockopt error: %s", strerror(errno));
169 			pass = false;
170 			continue;
171 		}
172 
173 		if (optlen != sizeof (tv)) {
174 			fail(t,
175 			    "getsockopt returned incorrect length: %ld"
176 			    " vs. %zd", (long)optlen, sizeof (tv));
177 			continue;
178 		}
179 
180 		if (tv.tv_sec != t->sec) {
181 			fail(t, "returned tv_sec value mismatch: %ld "
182 			    "vs. expected %ld", tv.tv_sec, t->sec);
183 			continue;
184 		}
185 		if (tv.tv_usec != t->usec) {
186 			fail(t, "returned tv_usec value mismatch: %ld "
187 			    "vs. expected %ld", tv.tv_usec, t->usec);
188 			continue;
189 		}
190 
191 		printf("[PASS] %s\n", t->name);
192 	}
193 
194 	close(cfd);
195 	close(sfd);
196 	unlink(sockpath);
197 
198 	return (pass ? 0 : EXIT_FAILURE);
199 }
200