Skip to content

Commit 3a92ce3

Browse files
committed
feat: create vsock seqpacket server program
- the functional tests use an echo server which is just a socat command. socat doesn't support seqpacket. - the program listens on a port and handles a connection in a separate thread Signed-off-by: aerosouund <aerosound161@gmail.com>
1 parent ba68e07 commit 3a92ce3

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include <stdio.h>
5+
#include <stdarg.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
#include <unistd.h>
9+
#include <errno.h>
10+
#include <sys/socket.h>
11+
#include <sys/un.h>
12+
#include <linux/vm_sockets.h>
13+
#include <sys/types.h>
14+
#include <sys/select.h>
15+
#include <sys/wait.h>
16+
#include <pthread.h>
17+
#include <signal.h>
18+
19+
#define BUF_SIZE 16384
20+
21+
volatile sig_atomic_t running = 1;
22+
volatile int listener_sockfd;
23+
24+
void log_info(const char *fmt, ...) {
25+
va_list args;
26+
va_start(args, fmt);
27+
fprintf(stdout, "[INFO] ");
28+
vfprintf(stdout, fmt, args);
29+
fprintf(stdout, "\n");
30+
va_end(args);
31+
fflush(stdout);
32+
}
33+
34+
void log_error(const char *fmt, ...) {
35+
va_list args;
36+
va_start(args, fmt);
37+
fprintf(stderr, "[ERROR] ");
38+
vfprintf(stderr, fmt, args);
39+
fprintf(stderr, "\n");
40+
va_end(args);
41+
}
42+
43+
int print_usage() {
44+
fprintf(stderr, "Usage: ./vsock_seq_server serve <port> [af_vsock|af_unix] [path]\n");
45+
fprintf(stderr, "\n");
46+
fprintf(stderr, " serve start a SEQPACKET echo server on :port.\n");
47+
fprintf(stderr, " Data received from the client is echoed back.\n");
48+
fprintf(stderr, " af_vsock|af_unix socket family to use (default: af_vsock)\n");
49+
fprintf(stderr, " path socket path for af_unix (optional; omit for no address)\n");
50+
fprintf(stderr, "\n");
51+
return -1;
52+
}
53+
54+
void *handle_conn(void *connfd_ptr) {
55+
int connfd = *(int *)(connfd_ptr);
56+
free(connfd_ptr);
57+
char buf[BUF_SIZE];
58+
ssize_t n;
59+
60+
// echo back whatever you received into the connection again
61+
while ((n = recv(connfd, buf, sizeof(buf), 0)) > 0) {
62+
log_info("received %zd bytes", n);
63+
64+
if (send(connfd, buf, n, 0) < 0) {
65+
log_error("send: %s", strerror(errno));
66+
break;
67+
}
68+
}
69+
70+
if (n == 0) {
71+
log_info("connection closed by peer");
72+
}
73+
else if (n < 0) {
74+
log_error("recv: %s (errno=%d)", strerror(errno), errno);
75+
}
76+
77+
close(connfd);
78+
return NULL;
79+
}
80+
81+
int run_seq_server(int port, int family, const char *path)
82+
{
83+
int sockfd, connfd;
84+
85+
sockfd = socket(family, SOCK_SEQPACKET, 0);
86+
if (sockfd < 0) {
87+
log_error("socket: %s", strerror(errno));
88+
exit(1);
89+
}
90+
91+
listener_sockfd = sockfd;
92+
93+
if (family == AF_VSOCK) {
94+
struct sockaddr_vm addr;
95+
memset(&addr, 0, sizeof(addr));
96+
addr.svm_family = AF_VSOCK;
97+
addr.svm_port = port;
98+
addr.svm_cid = VMADDR_CID_ANY;
99+
100+
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
101+
log_error("bind: %s", strerror(errno));
102+
close(sockfd);
103+
exit(1);
104+
}
105+
} else if (family == AF_UNIX && path != NULL) {
106+
struct sockaddr_un addr;
107+
memset(&addr, 0, sizeof(addr));
108+
addr.sun_family = AF_UNIX;
109+
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
110+
111+
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
112+
log_error("bind: %s", strerror(errno));
113+
close(sockfd);
114+
exit(1);
115+
}
116+
}
117+
118+
if (listen(sockfd, 5) < 0) {
119+
log_error("listen: %s", strerror(errno));
120+
close(sockfd);
121+
exit(1);
122+
}
123+
124+
log_info("SEQPACKET server listening on port %d (family=%s%s%s)",
125+
port,
126+
family == AF_VSOCK ? "af_vsock" : "af_unix",
127+
path ? " path=" : "",
128+
path ? path : "");
129+
130+
while (running) {
131+
connfd = accept(sockfd, NULL, NULL);
132+
if (connfd < 0) {
133+
if (errno == EINTR) break; // accept interrupted by signal
134+
log_error("accept: %s", strerror(errno));
135+
exit(1);
136+
}
137+
138+
log_info("connection accepted (fd=%d)", connfd);
139+
140+
int *connfd_ptr = malloc(sizeof(int));
141+
*connfd_ptr = connfd;
142+
143+
pthread_t tid;
144+
pthread_create(&tid, NULL, handle_conn, connfd_ptr);
145+
pthread_detach(tid);
146+
};
147+
148+
close(sockfd);
149+
return 0;
150+
}
151+
152+
void stop_server_loop(int sig) {
153+
running = 0;
154+
close(listener_sockfd);
155+
}
156+
157+
int main(int argc, char **argv) {
158+
signal(SIGTERM, stop_server_loop);
159+
signal(SIGINT, stop_server_loop);
160+
161+
if (argc < 2) {
162+
return print_usage();
163+
}
164+
165+
if (strcmp(argv[1], "serve") == 0) {
166+
if (argc < 3) {
167+
return print_usage();
168+
}
169+
170+
int port = atoi(argv[2]);
171+
if (!port) {
172+
return print_usage();
173+
}
174+
175+
int family = AF_VSOCK;
176+
const char *path = NULL;
177+
178+
if (argc >= 4) {
179+
if (strcmp(argv[3], "af_unix") == 0) {
180+
family = AF_UNIX;
181+
} else if (strcmp(argv[3], "af_vsock") == 0) {
182+
family = AF_VSOCK;
183+
} else {
184+
return print_usage();
185+
}
186+
}
187+
188+
if (argc >= 5) {
189+
path = argv[4];
190+
}
191+
192+
return run_seq_server(port, family, path);
193+
}
194+
195+
return print_usage();
196+
}

0 commit comments

Comments
 (0)