1df7e0e0dSStefan Hajnoczi // SPDX-License-Identifier: GPL-2.0-only 2df7e0e0dSStefan Hajnoczi /* 3df7e0e0dSStefan Hajnoczi * vsock test utilities 4df7e0e0dSStefan Hajnoczi * 5df7e0e0dSStefan Hajnoczi * Copyright (C) 2017 Red Hat, Inc. 6df7e0e0dSStefan Hajnoczi * 7df7e0e0dSStefan Hajnoczi * Author: Stefan Hajnoczi <stefanha@redhat.com> 8df7e0e0dSStefan Hajnoczi */ 9df7e0e0dSStefan Hajnoczi 10df7e0e0dSStefan Hajnoczi #include <errno.h> 11df7e0e0dSStefan Hajnoczi #include <stdio.h> 12092f32aeSStefan Hajnoczi #include <stdint.h> 13df7e0e0dSStefan Hajnoczi #include <stdlib.h> 14df7e0e0dSStefan Hajnoczi #include <signal.h> 159bb8a29dSStefan Hajnoczi #include <unistd.h> 16770ce007SStefano Garzarella #include <assert.h> 17770ce007SStefano Garzarella #include <sys/epoll.h> 18df7e0e0dSStefan Hajnoczi 19df7e0e0dSStefan Hajnoczi #include "timeout.h" 209bb8a29dSStefan Hajnoczi #include "control.h" 21df7e0e0dSStefan Hajnoczi #include "util.h" 22df7e0e0dSStefan Hajnoczi 23df7e0e0dSStefan Hajnoczi /* Install signal handlers */ 24df7e0e0dSStefan Hajnoczi void init_signals(void) 25df7e0e0dSStefan Hajnoczi { 26df7e0e0dSStefan Hajnoczi struct sigaction act = { 27df7e0e0dSStefan Hajnoczi .sa_handler = sigalrm, 28df7e0e0dSStefan Hajnoczi }; 29df7e0e0dSStefan Hajnoczi 30df7e0e0dSStefan Hajnoczi sigaction(SIGALRM, &act, NULL); 31df7e0e0dSStefan Hajnoczi signal(SIGPIPE, SIG_IGN); 32df7e0e0dSStefan Hajnoczi } 33df7e0e0dSStefan Hajnoczi 34df7e0e0dSStefan Hajnoczi /* Parse a CID in string representation */ 35df7e0e0dSStefan Hajnoczi unsigned int parse_cid(const char *str) 36df7e0e0dSStefan Hajnoczi { 37df7e0e0dSStefan Hajnoczi char *endptr = NULL; 38df7e0e0dSStefan Hajnoczi unsigned long n; 39df7e0e0dSStefan Hajnoczi 40df7e0e0dSStefan Hajnoczi errno = 0; 41df7e0e0dSStefan Hajnoczi n = strtoul(str, &endptr, 10); 42df7e0e0dSStefan Hajnoczi if (errno || *endptr != '\0') { 43df7e0e0dSStefan Hajnoczi fprintf(stderr, "malformed CID \"%s\"\n", str); 44df7e0e0dSStefan Hajnoczi exit(EXIT_FAILURE); 45df7e0e0dSStefan Hajnoczi } 46df7e0e0dSStefan Hajnoczi return n; 47df7e0e0dSStefan Hajnoczi } 48df7e0e0dSStefan Hajnoczi 49770ce007SStefano Garzarella /* Wait for the remote to close the connection */ 50770ce007SStefano Garzarella void vsock_wait_remote_close(int fd) 51770ce007SStefano Garzarella { 52770ce007SStefano Garzarella struct epoll_event ev; 53770ce007SStefano Garzarella int epollfd, nfds; 54770ce007SStefano Garzarella 55770ce007SStefano Garzarella epollfd = epoll_create1(0); 56770ce007SStefano Garzarella if (epollfd == -1) { 57770ce007SStefano Garzarella perror("epoll_create1"); 58770ce007SStefano Garzarella exit(EXIT_FAILURE); 59770ce007SStefano Garzarella } 60770ce007SStefano Garzarella 61770ce007SStefano Garzarella ev.events = EPOLLRDHUP | EPOLLHUP; 62770ce007SStefano Garzarella ev.data.fd = fd; 63770ce007SStefano Garzarella if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { 64770ce007SStefano Garzarella perror("epoll_ctl"); 65770ce007SStefano Garzarella exit(EXIT_FAILURE); 66770ce007SStefano Garzarella } 67770ce007SStefano Garzarella 68770ce007SStefano Garzarella nfds = epoll_wait(epollfd, &ev, 1, TIMEOUT * 1000); 69770ce007SStefano Garzarella if (nfds == -1) { 70770ce007SStefano Garzarella perror("epoll_wait"); 71770ce007SStefano Garzarella exit(EXIT_FAILURE); 72770ce007SStefano Garzarella } 73770ce007SStefano Garzarella 74770ce007SStefano Garzarella if (nfds == 0) { 75770ce007SStefano Garzarella fprintf(stderr, "epoll_wait timed out\n"); 76770ce007SStefano Garzarella exit(EXIT_FAILURE); 77770ce007SStefano Garzarella } 78770ce007SStefano Garzarella 79770ce007SStefano Garzarella assert(nfds == 1); 80770ce007SStefano Garzarella assert(ev.events & (EPOLLRDHUP | EPOLLHUP)); 81770ce007SStefano Garzarella assert(ev.data.fd == fd); 82770ce007SStefano Garzarella 83770ce007SStefano Garzarella close(epollfd); 84770ce007SStefano Garzarella } 85770ce007SStefano Garzarella 869bb8a29dSStefan Hajnoczi /* Connect to <cid, port> and return the file descriptor. */ 87*41b792d7SArseny Krasnov static int vsock_connect(unsigned int cid, unsigned int port, int type) 889bb8a29dSStefan Hajnoczi { 899bb8a29dSStefan Hajnoczi union { 909bb8a29dSStefan Hajnoczi struct sockaddr sa; 919bb8a29dSStefan Hajnoczi struct sockaddr_vm svm; 929bb8a29dSStefan Hajnoczi } addr = { 939bb8a29dSStefan Hajnoczi .svm = { 949bb8a29dSStefan Hajnoczi .svm_family = AF_VSOCK, 959bb8a29dSStefan Hajnoczi .svm_port = port, 969bb8a29dSStefan Hajnoczi .svm_cid = cid, 979bb8a29dSStefan Hajnoczi }, 989bb8a29dSStefan Hajnoczi }; 999bb8a29dSStefan Hajnoczi int ret; 1009bb8a29dSStefan Hajnoczi int fd; 1019bb8a29dSStefan Hajnoczi 1029bb8a29dSStefan Hajnoczi control_expectln("LISTENING"); 1039bb8a29dSStefan Hajnoczi 104*41b792d7SArseny Krasnov fd = socket(AF_VSOCK, type, 0); 1059bb8a29dSStefan Hajnoczi 1069bb8a29dSStefan Hajnoczi timeout_begin(TIMEOUT); 1079bb8a29dSStefan Hajnoczi do { 1089bb8a29dSStefan Hajnoczi ret = connect(fd, &addr.sa, sizeof(addr.svm)); 1099bb8a29dSStefan Hajnoczi timeout_check("connect"); 1109bb8a29dSStefan Hajnoczi } while (ret < 0 && errno == EINTR); 1119bb8a29dSStefan Hajnoczi timeout_end(); 1129bb8a29dSStefan Hajnoczi 1139bb8a29dSStefan Hajnoczi if (ret < 0) { 1149bb8a29dSStefan Hajnoczi int old_errno = errno; 1159bb8a29dSStefan Hajnoczi 1169bb8a29dSStefan Hajnoczi close(fd); 1179bb8a29dSStefan Hajnoczi fd = -1; 1189bb8a29dSStefan Hajnoczi errno = old_errno; 1199bb8a29dSStefan Hajnoczi } 1209bb8a29dSStefan Hajnoczi return fd; 1219bb8a29dSStefan Hajnoczi } 1229bb8a29dSStefan Hajnoczi 123*41b792d7SArseny Krasnov int vsock_stream_connect(unsigned int cid, unsigned int port) 124*41b792d7SArseny Krasnov { 125*41b792d7SArseny Krasnov return vsock_connect(cid, port, SOCK_STREAM); 126*41b792d7SArseny Krasnov } 127*41b792d7SArseny Krasnov 128*41b792d7SArseny Krasnov int vsock_seqpacket_connect(unsigned int cid, unsigned int port) 129*41b792d7SArseny Krasnov { 130*41b792d7SArseny Krasnov return vsock_connect(cid, port, SOCK_SEQPACKET); 131*41b792d7SArseny Krasnov } 132*41b792d7SArseny Krasnov 1339bb8a29dSStefan Hajnoczi /* Listen on <cid, port> and return the first incoming connection. The remote 1349bb8a29dSStefan Hajnoczi * address is stored to clientaddrp. clientaddrp may be NULL. 1359bb8a29dSStefan Hajnoczi */ 136*41b792d7SArseny Krasnov static int vsock_accept(unsigned int cid, unsigned int port, 137*41b792d7SArseny Krasnov struct sockaddr_vm *clientaddrp, int type) 1389bb8a29dSStefan Hajnoczi { 1399bb8a29dSStefan Hajnoczi union { 1409bb8a29dSStefan Hajnoczi struct sockaddr sa; 1419bb8a29dSStefan Hajnoczi struct sockaddr_vm svm; 1429bb8a29dSStefan Hajnoczi } addr = { 1439bb8a29dSStefan Hajnoczi .svm = { 1449bb8a29dSStefan Hajnoczi .svm_family = AF_VSOCK, 1459bb8a29dSStefan Hajnoczi .svm_port = port, 1469bb8a29dSStefan Hajnoczi .svm_cid = cid, 1479bb8a29dSStefan Hajnoczi }, 1489bb8a29dSStefan Hajnoczi }; 1499bb8a29dSStefan Hajnoczi union { 1509bb8a29dSStefan Hajnoczi struct sockaddr sa; 1519bb8a29dSStefan Hajnoczi struct sockaddr_vm svm; 1529bb8a29dSStefan Hajnoczi } clientaddr; 1539bb8a29dSStefan Hajnoczi socklen_t clientaddr_len = sizeof(clientaddr.svm); 1549bb8a29dSStefan Hajnoczi int fd; 1559bb8a29dSStefan Hajnoczi int client_fd; 1569bb8a29dSStefan Hajnoczi int old_errno; 1579bb8a29dSStefan Hajnoczi 158*41b792d7SArseny Krasnov fd = socket(AF_VSOCK, type, 0); 1599bb8a29dSStefan Hajnoczi 1609bb8a29dSStefan Hajnoczi if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) { 1619bb8a29dSStefan Hajnoczi perror("bind"); 1629bb8a29dSStefan Hajnoczi exit(EXIT_FAILURE); 1639bb8a29dSStefan Hajnoczi } 1649bb8a29dSStefan Hajnoczi 1659bb8a29dSStefan Hajnoczi if (listen(fd, 1) < 0) { 1669bb8a29dSStefan Hajnoczi perror("listen"); 1679bb8a29dSStefan Hajnoczi exit(EXIT_FAILURE); 1689bb8a29dSStefan Hajnoczi } 1699bb8a29dSStefan Hajnoczi 1709bb8a29dSStefan Hajnoczi control_writeln("LISTENING"); 1719bb8a29dSStefan Hajnoczi 1729bb8a29dSStefan Hajnoczi timeout_begin(TIMEOUT); 1739bb8a29dSStefan Hajnoczi do { 1749bb8a29dSStefan Hajnoczi client_fd = accept(fd, &clientaddr.sa, &clientaddr_len); 1759bb8a29dSStefan Hajnoczi timeout_check("accept"); 1769bb8a29dSStefan Hajnoczi } while (client_fd < 0 && errno == EINTR); 1779bb8a29dSStefan Hajnoczi timeout_end(); 1789bb8a29dSStefan Hajnoczi 1799bb8a29dSStefan Hajnoczi old_errno = errno; 1809bb8a29dSStefan Hajnoczi close(fd); 1819bb8a29dSStefan Hajnoczi errno = old_errno; 1829bb8a29dSStefan Hajnoczi 1839bb8a29dSStefan Hajnoczi if (client_fd < 0) 1849bb8a29dSStefan Hajnoczi return client_fd; 1859bb8a29dSStefan Hajnoczi 1869bb8a29dSStefan Hajnoczi if (clientaddr_len != sizeof(clientaddr.svm)) { 1879bb8a29dSStefan Hajnoczi fprintf(stderr, "unexpected addrlen from accept(2), %zu\n", 1889bb8a29dSStefan Hajnoczi (size_t)clientaddr_len); 1899bb8a29dSStefan Hajnoczi exit(EXIT_FAILURE); 1909bb8a29dSStefan Hajnoczi } 1919bb8a29dSStefan Hajnoczi if (clientaddr.sa.sa_family != AF_VSOCK) { 1929bb8a29dSStefan Hajnoczi fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n", 1939bb8a29dSStefan Hajnoczi clientaddr.sa.sa_family); 1949bb8a29dSStefan Hajnoczi exit(EXIT_FAILURE); 1959bb8a29dSStefan Hajnoczi } 1969bb8a29dSStefan Hajnoczi 1979bb8a29dSStefan Hajnoczi if (clientaddrp) 1989bb8a29dSStefan Hajnoczi *clientaddrp = clientaddr.svm; 1999bb8a29dSStefan Hajnoczi return client_fd; 2009bb8a29dSStefan Hajnoczi } 2019bb8a29dSStefan Hajnoczi 202*41b792d7SArseny Krasnov int vsock_stream_accept(unsigned int cid, unsigned int port, 203*41b792d7SArseny Krasnov struct sockaddr_vm *clientaddrp) 204*41b792d7SArseny Krasnov { 205*41b792d7SArseny Krasnov return vsock_accept(cid, port, clientaddrp, SOCK_STREAM); 206*41b792d7SArseny Krasnov } 207*41b792d7SArseny Krasnov 208*41b792d7SArseny Krasnov int vsock_seqpacket_accept(unsigned int cid, unsigned int port, 209*41b792d7SArseny Krasnov struct sockaddr_vm *clientaddrp) 210*41b792d7SArseny Krasnov { 211*41b792d7SArseny Krasnov return vsock_accept(cid, port, clientaddrp, SOCK_SEQPACKET); 212*41b792d7SArseny Krasnov } 213*41b792d7SArseny Krasnov 214092f32aeSStefan Hajnoczi /* Transmit one byte and check the return value. 215092f32aeSStefan Hajnoczi * 216092f32aeSStefan Hajnoczi * expected_ret: 217092f32aeSStefan Hajnoczi * <0 Negative errno (for testing errors) 218092f32aeSStefan Hajnoczi * 0 End-of-file 219092f32aeSStefan Hajnoczi * 1 Success 220092f32aeSStefan Hajnoczi */ 221092f32aeSStefan Hajnoczi void send_byte(int fd, int expected_ret, int flags) 222092f32aeSStefan Hajnoczi { 223092f32aeSStefan Hajnoczi const uint8_t byte = 'A'; 224092f32aeSStefan Hajnoczi ssize_t nwritten; 225092f32aeSStefan Hajnoczi 226092f32aeSStefan Hajnoczi timeout_begin(TIMEOUT); 227092f32aeSStefan Hajnoczi do { 228092f32aeSStefan Hajnoczi nwritten = send(fd, &byte, sizeof(byte), flags); 229092f32aeSStefan Hajnoczi timeout_check("write"); 230092f32aeSStefan Hajnoczi } while (nwritten < 0 && errno == EINTR); 231092f32aeSStefan Hajnoczi timeout_end(); 232092f32aeSStefan Hajnoczi 233092f32aeSStefan Hajnoczi if (expected_ret < 0) { 234092f32aeSStefan Hajnoczi if (nwritten != -1) { 235092f32aeSStefan Hajnoczi fprintf(stderr, "bogus send(2) return value %zd\n", 236092f32aeSStefan Hajnoczi nwritten); 237092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 238092f32aeSStefan Hajnoczi } 239092f32aeSStefan Hajnoczi if (errno != -expected_ret) { 240092f32aeSStefan Hajnoczi perror("write"); 241092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 242092f32aeSStefan Hajnoczi } 243092f32aeSStefan Hajnoczi return; 244092f32aeSStefan Hajnoczi } 245092f32aeSStefan Hajnoczi 246092f32aeSStefan Hajnoczi if (nwritten < 0) { 247092f32aeSStefan Hajnoczi perror("write"); 248092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 249092f32aeSStefan Hajnoczi } 250092f32aeSStefan Hajnoczi if (nwritten == 0) { 251092f32aeSStefan Hajnoczi if (expected_ret == 0) 252092f32aeSStefan Hajnoczi return; 253092f32aeSStefan Hajnoczi 254092f32aeSStefan Hajnoczi fprintf(stderr, "unexpected EOF while sending byte\n"); 255092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 256092f32aeSStefan Hajnoczi } 257092f32aeSStefan Hajnoczi if (nwritten != sizeof(byte)) { 258092f32aeSStefan Hajnoczi fprintf(stderr, "bogus send(2) return value %zd\n", nwritten); 259092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 260092f32aeSStefan Hajnoczi } 261092f32aeSStefan Hajnoczi } 262092f32aeSStefan Hajnoczi 263092f32aeSStefan Hajnoczi /* Receive one byte and check the return value. 264092f32aeSStefan Hajnoczi * 265092f32aeSStefan Hajnoczi * expected_ret: 266092f32aeSStefan Hajnoczi * <0 Negative errno (for testing errors) 267092f32aeSStefan Hajnoczi * 0 End-of-file 268092f32aeSStefan Hajnoczi * 1 Success 269092f32aeSStefan Hajnoczi */ 270092f32aeSStefan Hajnoczi void recv_byte(int fd, int expected_ret, int flags) 271092f32aeSStefan Hajnoczi { 272092f32aeSStefan Hajnoczi uint8_t byte; 273092f32aeSStefan Hajnoczi ssize_t nread; 274092f32aeSStefan Hajnoczi 275092f32aeSStefan Hajnoczi timeout_begin(TIMEOUT); 276092f32aeSStefan Hajnoczi do { 277092f32aeSStefan Hajnoczi nread = recv(fd, &byte, sizeof(byte), flags); 278092f32aeSStefan Hajnoczi timeout_check("read"); 279092f32aeSStefan Hajnoczi } while (nread < 0 && errno == EINTR); 280092f32aeSStefan Hajnoczi timeout_end(); 281092f32aeSStefan Hajnoczi 282092f32aeSStefan Hajnoczi if (expected_ret < 0) { 283092f32aeSStefan Hajnoczi if (nread != -1) { 284092f32aeSStefan Hajnoczi fprintf(stderr, "bogus recv(2) return value %zd\n", 285092f32aeSStefan Hajnoczi nread); 286092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 287092f32aeSStefan Hajnoczi } 288092f32aeSStefan Hajnoczi if (errno != -expected_ret) { 289092f32aeSStefan Hajnoczi perror("read"); 290092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 291092f32aeSStefan Hajnoczi } 292092f32aeSStefan Hajnoczi return; 293092f32aeSStefan Hajnoczi } 294092f32aeSStefan Hajnoczi 295092f32aeSStefan Hajnoczi if (nread < 0) { 296092f32aeSStefan Hajnoczi perror("read"); 297092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 298092f32aeSStefan Hajnoczi } 299092f32aeSStefan Hajnoczi if (nread == 0) { 300092f32aeSStefan Hajnoczi if (expected_ret == 0) 301092f32aeSStefan Hajnoczi return; 302092f32aeSStefan Hajnoczi 303092f32aeSStefan Hajnoczi fprintf(stderr, "unexpected EOF while receiving byte\n"); 304092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 305092f32aeSStefan Hajnoczi } 306092f32aeSStefan Hajnoczi if (nread != sizeof(byte)) { 307092f32aeSStefan Hajnoczi fprintf(stderr, "bogus recv(2) return value %zd\n", nread); 308092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 309092f32aeSStefan Hajnoczi } 310092f32aeSStefan Hajnoczi if (byte != 'A') { 311092f32aeSStefan Hajnoczi fprintf(stderr, "unexpected byte read %c\n", byte); 312092f32aeSStefan Hajnoczi exit(EXIT_FAILURE); 313092f32aeSStefan Hajnoczi } 314092f32aeSStefan Hajnoczi } 315092f32aeSStefan Hajnoczi 316df7e0e0dSStefan Hajnoczi /* Run test cases. The program terminates if a failure occurs. */ 317df7e0e0dSStefan Hajnoczi void run_tests(const struct test_case *test_cases, 318df7e0e0dSStefan Hajnoczi const struct test_opts *opts) 319df7e0e0dSStefan Hajnoczi { 320df7e0e0dSStefan Hajnoczi int i; 321df7e0e0dSStefan Hajnoczi 322df7e0e0dSStefan Hajnoczi for (i = 0; test_cases[i].name; i++) { 323df7e0e0dSStefan Hajnoczi void (*run)(const struct test_opts *opts); 3245a2b2425SStefano Garzarella char *line; 325df7e0e0dSStefan Hajnoczi 3265a2b2425SStefano Garzarella printf("%d - %s...", i, test_cases[i].name); 327df7e0e0dSStefan Hajnoczi fflush(stdout); 328df7e0e0dSStefan Hajnoczi 3292f65b44eSStefan Hajnoczi /* Full barrier before executing the next test. This 3302f65b44eSStefan Hajnoczi * ensures that client and server are executing the 3312f65b44eSStefan Hajnoczi * same test case. In particular, it means whoever is 3322f65b44eSStefan Hajnoczi * faster will not see the peer still executing the 3332f65b44eSStefan Hajnoczi * last test. This is important because port numbers 3342f65b44eSStefan Hajnoczi * can be used by multiple test cases. 3352f65b44eSStefan Hajnoczi */ 3365a2b2425SStefano Garzarella if (test_cases[i].skip) 3375a2b2425SStefano Garzarella control_writeln("SKIP"); 3385a2b2425SStefano Garzarella else 3392f65b44eSStefan Hajnoczi control_writeln("NEXT"); 3402f65b44eSStefan Hajnoczi 3415a2b2425SStefano Garzarella line = control_readln(); 3425a2b2425SStefano Garzarella if (control_cmpln(line, "SKIP", false) || test_cases[i].skip) { 3432f65b44eSStefan Hajnoczi 3445a2b2425SStefano Garzarella printf("skipped\n"); 3455a2b2425SStefano Garzarella 3465a2b2425SStefano Garzarella free(line); 3475a2b2425SStefano Garzarella continue; 3482f65b44eSStefan Hajnoczi } 349df7e0e0dSStefan Hajnoczi 3505a2b2425SStefano Garzarella control_cmpln(line, "NEXT", true); 3515a2b2425SStefano Garzarella free(line); 3525a2b2425SStefano Garzarella 3535a2b2425SStefano Garzarella if (opts->mode == TEST_MODE_CLIENT) 3545a2b2425SStefano Garzarella run = test_cases[i].run_client; 3555a2b2425SStefano Garzarella else 3565a2b2425SStefano Garzarella run = test_cases[i].run_server; 3575a2b2425SStefano Garzarella 358df7e0e0dSStefan Hajnoczi if (run) 359df7e0e0dSStefan Hajnoczi run(opts); 360df7e0e0dSStefan Hajnoczi 361df7e0e0dSStefan Hajnoczi printf("ok\n"); 362df7e0e0dSStefan Hajnoczi } 363df7e0e0dSStefan Hajnoczi } 3645a2b2425SStefano Garzarella 3655a2b2425SStefano Garzarella void list_tests(const struct test_case *test_cases) 3665a2b2425SStefano Garzarella { 3675a2b2425SStefano Garzarella int i; 3685a2b2425SStefano Garzarella 3695a2b2425SStefano Garzarella printf("ID\tTest name\n"); 3705a2b2425SStefano Garzarella 3715a2b2425SStefano Garzarella for (i = 0; test_cases[i].name; i++) 3725a2b2425SStefano Garzarella printf("%d\t%s\n", i, test_cases[i].name); 3735a2b2425SStefano Garzarella 3745a2b2425SStefano Garzarella exit(EXIT_FAILURE); 3755a2b2425SStefano Garzarella } 3765a2b2425SStefano Garzarella 3775a2b2425SStefano Garzarella void skip_test(struct test_case *test_cases, size_t test_cases_len, 3785a2b2425SStefano Garzarella const char *test_id_str) 3795a2b2425SStefano Garzarella { 3805a2b2425SStefano Garzarella unsigned long test_id; 3815a2b2425SStefano Garzarella char *endptr = NULL; 3825a2b2425SStefano Garzarella 3835a2b2425SStefano Garzarella errno = 0; 3845a2b2425SStefano Garzarella test_id = strtoul(test_id_str, &endptr, 10); 3855a2b2425SStefano Garzarella if (errno || *endptr != '\0') { 3865a2b2425SStefano Garzarella fprintf(stderr, "malformed test ID \"%s\"\n", test_id_str); 3875a2b2425SStefano Garzarella exit(EXIT_FAILURE); 3885a2b2425SStefano Garzarella } 3895a2b2425SStefano Garzarella 3905a2b2425SStefano Garzarella if (test_id >= test_cases_len) { 3915a2b2425SStefano Garzarella fprintf(stderr, "test ID (%lu) larger than the max allowed (%lu)\n", 3925a2b2425SStefano Garzarella test_id, test_cases_len - 1); 3935a2b2425SStefano Garzarella exit(EXIT_FAILURE); 3945a2b2425SStefano Garzarella } 3955a2b2425SStefano Garzarella 3965a2b2425SStefano Garzarella test_cases[test_id].skip = true; 3975a2b2425SStefano Garzarella } 398