├── .gitignore ├── .travis.yml ├── Makefile ├── README.markdown ├── mockeagain.c └── t ├── 000-mock-writes.c ├── 001-mock-reads.c ├── 002-pattern-matching.c ├── 003-disabled.c ├── 004-detect-unmockable-fds.c ├── echo_server.py ├── runner.c ├── test_case.c └── test_case.h /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | *.o 5 | *.so 6 | reindex 7 | *.html 8 | tags 9 | genmobi.sh 10 | *.mobi 11 | t/runner 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | os: linux 4 | 5 | language: c 6 | 7 | compiler: 8 | - gcc 9 | 10 | addons: 11 | apt: 12 | packages: [ python ] 13 | 14 | matrix: 15 | include: 16 | - os: osx 17 | compiler: clang 18 | - os: linux 19 | compiler: gcc 20 | 21 | script: 22 | - MOCKEAGAIN_VERBOSE=1 make test 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC?=gcc 2 | COPTS=-O -g -Wall -Werror 3 | ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 4 | ALL_TESTS=$(shell find t -name "[0-9]*.c") 5 | VALGRIND:=0 6 | 7 | .PHONY: all test clean 8 | 9 | all: mockeagain.so 10 | 11 | %.so: %.c 12 | $(CC) $(COPTS) -fPIC -shared $< -o $@ -ldl || \ 13 | $(CC) $(COPTS) -fPIC -shared $< -o $@ 14 | 15 | test: all $(ALL_TESTS) 16 | for t in $(ALL_TESTS); do \ 17 | $(CC) $(COPTS) -o ./t/runner $$t ./t/runner.c ./t/test_case.c \ 18 | || exit 1; \ 19 | python ./t/echo_server.py ./t/runner $(VALGRIND) $(ROOT_DIR)/mockeagain.so \ 20 | && echo "Test case $$t passed" || exit 1; \ 21 | done 22 | 23 | clean: 24 | rm -rf *.so *.o *.lo t/runner 25 | 26 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Description 2 | =========== 3 | 4 | [![Build Status](https://travis-ci.org/openresty/mockeagain.svg?branch=master)](https://travis-ci.org/openresty/mockeagain) 5 | 6 | This tool emulates an ideally slow network by mocking the 7 | polling and read/write syscalls exposed by glibc via the 8 | LD_PRELOAD technique. 9 | 10 | By preloading this dynamic library to your network server 11 | process, it will intercept the "poll", "close", "send", and "writev" 12 | syscalls, only allow the writing syscalls to actually write a single byte 13 | at a time (without flushing), and returns EAGAIN until another 14 | "poll" called on the current socket fd. 15 | 16 | Similarly, one can configure this library to intercept the "read", 17 | "recv", "recvfrom" calls on the C level to emulate extremely slow 18 | reading operations either with or without slow writes at the same 19 | time. 20 | 21 | The socket fd must be called first by a "poll" call to mark 22 | itself to this tool as an "active fd" and trigger subsequence 23 | the writing syscalls to behave differently. 24 | 25 | With this tool, one can emulate extreme network conditions 26 | even locally (with the loopback device). 27 | 28 | This tool is intended to supersed the good old 29 | [etcproxy](https://github.com/chaoslawful/etcproxy) tool. 30 | 31 | Build 32 | ===== 33 | 34 | Just issue the following command to build the file mockeagain.so 35 | 36 | make 37 | 38 | The Gnu make and gcc compiler are required. 39 | 40 | On *BSD, it's often required to run the command "gmake". 41 | 42 | Usage 43 | ===== 44 | 45 | For now, only "poll" syscall and slow writing mocking are supported. 46 | 47 | Here we take Nginx as an example: 48 | 49 | 1. Ensure that you have passed the --with-poll_module option while 50 | invoking nginx's configure script to build nginx. 51 | 52 | 2. Ensure that you've enabled the "poll" event model in your nginx.conf: 53 | 54 | events { 55 | use poll; 56 | worker_connections 1024; 57 | } 58 | 59 | 3. Ensure that you've disabled nginx's own write buffer: 60 | 61 | postpone_output 1; # only postpone a single byte, default 1460 bytes 62 | 63 | and also have the following line in your nginx.conf: 64 | 65 | env LD_PRELOAD; 66 | 67 | 4. Run your Nginx this way: 68 | 69 | MOCKEAGAIN=w LD_PRELOAD=/path/to/mockeagain.so /path/to/nginx ... 70 | 71 | Environments 72 | ============ 73 | 74 | The following system environment variables can be used 75 | to control the behavior of this tool. You should 76 | use Nginx's standard "env" directive to enable the 77 | environments that you use in your nginx.conf file, as in 78 | 79 | env MOCKEAGAIN_VERBOSE; 80 | env MOCKEAGAIN; 81 | env MOCKEAGAIN_WRITE_TIMEOUT_PATTERN; 82 | 83 | If you're using the Test::Nginx test scaffold, then these directives are 84 | automatically configured for you. 85 | 86 | MOCKEAGAIN 87 | ---------- 88 | 89 | This environment controls whether to mocking reads or writes or both. 90 | 91 | When the letter "r" or "R" appear in the value string, then reads will be mocked. 92 | 93 | When the letter "w" or "W" appear in the value string, then writes will be mocked. 94 | 95 | When this environment is either not set or set to something unrecognized, then no mocking will be performed. 96 | 97 | MOCKEAGAIN_VERBOSE 98 | ------------------ 99 | 100 | You can set the environment MOCKEAGAIN_VERBOSE to 1 if you want more diagnostical outputs of this tool in stderr. But you'll also have to add the following line to your nginx.conf: 101 | 102 | env MOCKEAGAIN_VERBOSE; 103 | 104 | When setting MOCKEAGAIN_VERBOSE to 1, you'll get messages like the following in your nginx's error.log file: 105 | 106 | mockeagain: mocking "writev" on fd 3 to signal EAGAIN. 107 | mockeagain: mocking "writev" on fd 3 to emit 1 of 188 bytes. 108 | mockeagain: mocking "writev" on fd 3 to emit 1 of 187 bytes. 109 | mockeagain: mocking "recv" on fd 5 to read 1 byte of data only 110 | 111 | MOCKEAGAIN_WRITE_TIMEOUT_PATTERN 112 | -------------------------------- 113 | 114 | When this environment is set to value "foo bar", then the when the string "foo bar" appears in the output stream (not necessarily in a single write call), then it will trigger an indefinite write timeout. 115 | 116 | This feature is very useful in mocking a write timeout in a particular position in the output stream. 117 | 118 | Note that this environment also requires that the MOCKEAGAIN variable value contains "w" or "W". 119 | 120 | For now, this feature only supports the "writev" call. 121 | 122 | Glibc API Mocked 123 | ---------------- 124 | 125 | Event API 126 | * poll 127 | 128 | Writing API 129 | * writev 130 | * send 131 | 132 | Reading API 133 | * read 134 | * recv 135 | * recvfrom 136 | 137 | Tests 138 | ===== 139 | `mockeagain` has a simple testing suite. 140 | 141 | To run tests, simply type `make test`. Note that a working standard Python is needed to run the tests. 142 | 143 | To run tests with Valgrind, run `make test VALGRIND=1` instead. 144 | 145 | To write a new test: 146 | 147 | Copy one of the existing test cases to get started. Test case files are 148 | always named in the format of `###-name.c`. You need to implement minimum the `run_test()` 149 | function. `run_test()` function takes a single parameter, `fd` which refers to a valid TCP socket 150 | that has been connected to an echo server and set to non-blocking mode. You are 151 | expected to return zero when all tests passed and non zero when one or more tests failed. 152 | 153 | Your test case is responsible for enabling read/write mocks. There is a helper 154 | function `set_mocking()` that accepts a combination of `MOCKING_READS` and 155 | `MOCKING_WRITES` to enable read and/or write mocking. There is another 156 | function `set_write_timeout_pattern()` that sets the `MOCKEAGAIN_WRITE_TIMEOUT_PATTERN` 157 | environment variable. 158 | 159 | *Note:* In general, `set_mocking()` and `set_write_timeout_pattern()` should 160 | be called before invocation of any mocked functions above. This is due to 161 | the fact that `mockeagain` will lazy load those settings and cache them forever 162 | afterwards. 163 | 164 | TODO 165 | ==== 166 | 167 | * add support for write, sendto, and more writing syscalls. 168 | * add support for other event interfaces like epoll, select, kqueue, and more. 169 | 170 | Success Stories 171 | =============== 172 | 173 | ngx_echo 174 | -------- 175 | 176 | The echo_after_body directive used to have a bug that the output filter 177 | failed to take into account the NGX_AGAIN constant returned from 178 | the downstream output filter chain. Enabling mockeagain's writing mode 179 | can trivially capture this issue even on localhost. 180 | 181 | To reproduce, update ngx_http_echo_filter.c:165 182 | 183 | #if 0 184 | 185 | to 186 | 187 | #if 1 188 | 189 | and run ngx_echo's test file t/echo-after-body.t with mockeagain's writing mode enabled. 190 | 191 | ngx_redis 192 | --------- 193 | 194 | ngx_redis 0.3.5 and earlier had a subtle bug in its redis reply parser 195 | as explained here: 196 | 197 | https://groups.google.com/group/openresty/browse_thread/thread/b5ddf1b24d3c9677 198 | 199 | Even though this bug was first captured by the [etcproxy](https://github.com/chaoslawful/etcproxy) tool, but mockeagain's reading mode 200 | can also capture it reliably even on localhost. 201 | 202 | To reproduce, just compile ngx_redis 0.3.5 with ngx_srcache, and 203 | then run the t/redis.t test file in ngx_srcache's test suite, and with mockeagain reading mode enabled. 204 | 205 | libdrizzle 206 | ---------- 207 | 208 | libdrizzle 0.8 and earlier had a subtle bug in its MySQL packet parser 209 | and mockeagain's reading mode can capture it reliably even on localhost. 210 | 211 | To reproduce, just compile libdrizzle 0.8 with ngx_drizzle, and run the t/sanity.t test file in ngx_drizzle's test suite 212 | with mockeagain's reading mode enabled. 213 | 214 | ngx_lua 215 | ------- 216 | 217 | The downstream cosocket API did have a bug in its receive(size) method call that was caught by mockeagain in its reading mode. 218 | 219 | To reproduce, check out ngx_lua v0.5.0rc24 and run its t/067-req-socket.t test file 220 | with "MOCKEAGAIN=R" and TEST 1 will just hang. 221 | 222 | Author 223 | ====== 224 | 225 | Yichun "agentzh" Zhang (章亦春) 226 | 227 | Copyright & License 228 | =================== 229 | 230 | This module is licenced under the BSD license. 231 | 232 | Copyright (C) 2012-2017 by Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. 233 | 234 | All rights reserved. 235 | 236 | Redistribution and use in source and binary forms, with or without 237 | modification, are permitted provided that the following conditions 238 | are met: 239 | 240 | * Redistributions of source code must retain the above copyright 241 | notice, this list of conditions and the following disclaimer. 242 | 243 | * Redistributions in binary form must reproduce the above copyright 244 | notice, this list of conditions and the following disclaimer in the 245 | documentation and/or other materials provided with the distribution. 246 | 247 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 248 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 249 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 250 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 251 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 252 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 253 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 254 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 255 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 256 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 257 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 258 | 259 | -------------------------------------------------------------------------------- /mockeagain.c: -------------------------------------------------------------------------------- 1 | #ifndef DDEBUG 2 | #define DDEBUG 0 3 | #endif 4 | 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #if __linux__ 18 | #include 19 | #include 20 | #endif 21 | 22 | #if DDEBUG 23 | # define dd(...) \ 24 | fprintf(stderr, "mockeagain: "); \ 25 | fprintf(stderr, __VA_ARGS__); \ 26 | fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) 27 | #else 28 | # define dd(...) 29 | #endif 30 | 31 | 32 | #define MAX_FD 1024 33 | 34 | 35 | static void *libc_handle = NULL; 36 | static short active_fds[MAX_FD + 1]; 37 | static char polled_fds[MAX_FD + 1]; 38 | static char written_fds[MAX_FD + 1]; 39 | static char weird_fds[MAX_FD + 1]; 40 | static char blacklist_fds[MAX_FD + 1]; 41 | static char snd_timeout_fds[MAX_FD + 1]; 42 | static char **matchbufs = NULL; 43 | static size_t matchbuf_len = 0; 44 | static const char *pattern = NULL; 45 | static int verbose = -1; 46 | static int mocking_type = -1; 47 | 48 | 49 | enum { 50 | MOCKING_READS = 0x01, 51 | MOCKING_WRITES = 0x02 52 | }; 53 | 54 | 55 | # define init_libc_handle() \ 56 | if (libc_handle == NULL) { \ 57 | libc_handle = RTLD_NEXT; \ 58 | } 59 | 60 | 61 | typedef int (*socket_handle) (int domain, int type, int protocol); 62 | 63 | typedef int (*poll_handle) (struct pollfd *ufds, unsigned int nfds, 64 | int timeout); 65 | 66 | typedef ssize_t (*writev_handle) (int fildes, const struct iovec *iov, 67 | int iovcnt); 68 | 69 | typedef int (*close_handle) (int fd); 70 | 71 | typedef ssize_t (*send_handle) (int sockfd, const void *buf, size_t len, 72 | int flags); 73 | 74 | typedef ssize_t (*read_handle) (int fd, void *buf, size_t count); 75 | 76 | typedef ssize_t (*recv_handle) (int sockfd, void *buf, size_t len, 77 | int flags); 78 | 79 | typedef ssize_t (*recvfrom_handle) (int sockfd, void *buf, size_t len, 80 | int flags, struct sockaddr *src_addr, socklen_t *addrlen); 81 | 82 | #if __linux__ 83 | typedef int (*accept4_handle) (int socket, struct sockaddr *address, 84 | socklen_t *address_len, int flags); 85 | 86 | typedef int (*signalfd_handle) (int fd, const sigset_t *mask, int flags); 87 | 88 | #if (defined(__GLIBC__) && __GLIBC__ <= 2) && \ 89 | (defined(__GLIBC_MINOR__) && __GLIBC_MINOR__ < 21) 90 | /* glibc < 2.21 used different signature */ 91 | typedef int (*eventfd_handle) (int initval, int flags); 92 | 93 | #else 94 | typedef int (*eventfd_handle) (unsigned int initval, int flags); 95 | #endif 96 | 97 | #endif 98 | 99 | 100 | static int get_verbose_level(); 101 | static void init_matchbufs(); 102 | static int now(); 103 | static int get_mocking_type(); 104 | 105 | 106 | #if __linux__ 107 | int 108 | accept4(int socket, struct sockaddr *address, 109 | socklen_t *address_len, int flags) 110 | { 111 | int fd; 112 | 113 | static accept4_handle orig_accept4 = NULL; 114 | 115 | init_libc_handle(); 116 | 117 | if (orig_accept4 == NULL) { 118 | orig_accept4 = dlsym(libc_handle, "accept4"); 119 | if (orig_accept4 == NULL) { 120 | fprintf(stderr, "mockeagain: could not find the underlying accept4: " 121 | "%s\n", dlerror()); 122 | exit(1); 123 | } 124 | } 125 | 126 | fd = orig_accept4(socket, address, address_len, flags); 127 | if (fd < 0) { 128 | return fd; 129 | } 130 | 131 | if (flags & SOCK_NONBLOCK) { 132 | if (matchbufs && matchbufs[fd]) { 133 | free(matchbufs[fd]); 134 | matchbufs[fd] = NULL; 135 | } 136 | 137 | active_fds[fd] = 0; 138 | polled_fds[fd] = 1; 139 | snd_timeout_fds[fd] = 0; 140 | } 141 | 142 | return fd; 143 | } 144 | 145 | 146 | int signalfd(int fd, const sigset_t *mask, int flags) 147 | { 148 | static signalfd_handle orig_signalfd = NULL; 149 | 150 | init_libc_handle(); 151 | 152 | if (orig_signalfd == NULL) { 153 | orig_signalfd = dlsym(libc_handle, "signalfd"); 154 | if (orig_signalfd == NULL) { 155 | fprintf(stderr, "mockeagain: could not find the underlying" 156 | " signalfd: %s\n", dlerror()); 157 | exit(1); 158 | } 159 | } 160 | 161 | fd = orig_signalfd(fd, mask, flags); 162 | if (fd < 0) { 163 | return fd; 164 | } 165 | 166 | if (get_verbose_level()) { 167 | fprintf(stderr, "mockeagain: signalfd: blacklist fd %d\n", fd); 168 | } 169 | 170 | blacklist_fds[fd] = 1; 171 | 172 | return fd; 173 | } 174 | 175 | 176 | #if (defined(__GLIBC__) && __GLIBC__ <= 2) && \ 177 | (defined(__GLIBC_MINOR__) && __GLIBC_MINOR__ < 21) 178 | /* glibc < 2.21 used different signature */ 179 | int eventfd(int initval, int flags) 180 | #else 181 | int eventfd(unsigned int initval, int flags) 182 | #endif 183 | { 184 | int fd; 185 | static eventfd_handle orig_eventfd = NULL; 186 | 187 | init_libc_handle(); 188 | 189 | if (orig_eventfd == NULL) { 190 | orig_eventfd = dlsym(libc_handle, "eventfd"); 191 | if (orig_eventfd == NULL) { 192 | fprintf(stderr, "mockeagain: could not find the underlying" 193 | " eventfd: %s\n", dlerror()); 194 | exit(1); 195 | } 196 | } 197 | 198 | fd = orig_eventfd(initval, flags); 199 | if (fd < 0) { 200 | return fd; 201 | } 202 | 203 | if (get_verbose_level()) { 204 | fprintf(stderr, "mockeagain: eventfd: blacklist fd %d\n", fd); 205 | } 206 | 207 | blacklist_fds[fd] = 1; 208 | 209 | return fd; 210 | } 211 | #endif 212 | 213 | 214 | int socket(int domain, int type, int protocol) 215 | { 216 | int fd; 217 | static socket_handle orig_socket = NULL; 218 | 219 | dd("calling my socket"); 220 | 221 | init_libc_handle(); 222 | 223 | if (orig_socket == NULL) { 224 | orig_socket = dlsym(libc_handle, "socket"); 225 | if (orig_socket == NULL) { 226 | fprintf(stderr, "mockeagain: could not find the underlying socket: " 227 | "%s\n", dlerror()); 228 | exit(1); 229 | } 230 | } 231 | 232 | init_matchbufs(); 233 | 234 | fd = (*orig_socket)(domain, type, protocol); 235 | 236 | dd("socket with type %d (SOCK_STREAM %d, SOCK_DGRAM %d)", type, 237 | SOCK_STREAM, SOCK_DGRAM); 238 | 239 | if (fd <= MAX_FD) { 240 | if (!(type & SOCK_STREAM)) { 241 | dd("socket: the current fd is weird: %d", fd); 242 | weird_fds[fd] = 1; 243 | 244 | } else { 245 | weird_fds[fd] = 0; 246 | } 247 | 248 | #if 1 249 | if (matchbufs && matchbufs[fd]) { 250 | free(matchbufs[fd]); 251 | matchbufs[fd] = NULL; 252 | } 253 | #endif 254 | 255 | active_fds[fd] = 0; 256 | polled_fds[fd] = 0; 257 | snd_timeout_fds[fd] = 0; 258 | } 259 | 260 | dd("socket returning %d", fd); 261 | 262 | return fd; 263 | } 264 | 265 | 266 | int 267 | poll(struct pollfd *ufds, nfds_t nfds, int timeout) 268 | { 269 | static void *libc_handle; 270 | int retval; 271 | static poll_handle orig_poll = NULL; 272 | struct pollfd *p; 273 | int i; 274 | int fd = -1; 275 | int begin = 0; 276 | int elapsed = 0; 277 | 278 | dd("calling my poll"); 279 | 280 | init_libc_handle(); 281 | 282 | if (orig_poll == NULL) { 283 | orig_poll = dlsym(libc_handle, "poll"); 284 | if (orig_poll == NULL) { 285 | fprintf(stderr, "mockeagain: could not find the underlying poll: " 286 | "%s\n", dlerror()); 287 | exit(1); 288 | } 289 | } 290 | 291 | init_matchbufs(); 292 | 293 | dd("calling the original poll"); 294 | 295 | if (pattern) { 296 | begin = now(); 297 | } 298 | 299 | retval = (*orig_poll)(ufds, nfds, timeout); 300 | 301 | if (pattern) { 302 | elapsed = now() - begin; 303 | } 304 | 305 | if (retval > 0) { 306 | struct timeval tm; 307 | 308 | p = ufds; 309 | for (i = 0; i < nfds; i++, p++) { 310 | fd = p->fd; 311 | if (fd > MAX_FD || weird_fds[fd]) { 312 | dd("skipping fd %d", fd); 313 | continue; 314 | } 315 | 316 | if (pattern && (p->revents & POLLOUT) && snd_timeout_fds[fd]) { 317 | 318 | if (get_verbose_level()) { 319 | fprintf(stderr, "mockeagain: poll: should suppress write " 320 | "event on fd %d.\n", fd); 321 | } 322 | 323 | p->revents &= ~POLLOUT; 324 | 325 | if (p->revents == 0) { 326 | retval--; 327 | continue; 328 | } 329 | } 330 | 331 | if (blacklist_fds[fd]) { 332 | if (get_verbose_level()) { 333 | fprintf(stderr, "mockeagain: poll: skip fd %d because it " 334 | "is in blacklist\n", fd); 335 | } 336 | 337 | continue; 338 | } 339 | 340 | active_fds[fd] = p->revents; 341 | polled_fds[fd] = 1; 342 | 343 | if (get_verbose_level()) { 344 | fprintf(stderr, "mockeagain: poll: fd %d polled with events " 345 | "%d\n", fd, p->revents); 346 | } 347 | } 348 | 349 | if (retval == 0) { 350 | if (get_verbose_level()) { 351 | fprintf(stderr, "mockeagain: poll: emulating timeout on " 352 | "fd %d.\n", fd); 353 | } 354 | 355 | if (timeout < 0) { 356 | tm.tv_sec = 3600 * 24; 357 | tm.tv_usec = 0; 358 | 359 | if (get_verbose_level()) { 360 | fprintf(stderr, "mockeagain: poll: sleeping 1 day " 361 | "on fd %d.\n", fd); 362 | } 363 | 364 | select(0, NULL, NULL, NULL, &tm); 365 | 366 | } else { 367 | 368 | if (elapsed < timeout) { 369 | int diff; 370 | 371 | diff = timeout - elapsed; 372 | 373 | tm.tv_sec = diff / 1000; 374 | tm.tv_usec = diff % 1000 * 1000; 375 | 376 | if (get_verbose_level()) { 377 | fprintf(stderr, "mockeagain: poll: sleeping %d ms " 378 | "on fd %d.\n", diff, fd); 379 | } 380 | 381 | select(0, NULL, NULL, NULL, &tm); 382 | } 383 | } 384 | } 385 | } 386 | 387 | return retval; 388 | } 389 | 390 | 391 | ssize_t 392 | writev(int fd, const struct iovec *iov, int iovcnt) 393 | { 394 | ssize_t retval; 395 | static writev_handle orig_writev = NULL; 396 | struct iovec new_iov[1] = { {NULL, 0} }; 397 | const struct iovec *p; 398 | int i; 399 | size_t len = 0; 400 | 401 | if ((get_mocking_type() & MOCKING_WRITES) 402 | && get_verbose_level() 403 | && fd <= MAX_FD) 404 | { 405 | fprintf(stderr, "mockeagain: writev(%d): polled=%d, written=%d, " 406 | "active=%d\n", fd, (int) polled_fds[fd], (int) written_fds[fd], 407 | (int) active_fds[fd]); 408 | } 409 | 410 | if ((get_mocking_type() & MOCKING_WRITES) 411 | && fd <= MAX_FD 412 | && polled_fds[fd] 413 | && written_fds[fd] 414 | && !(active_fds[fd] & POLLOUT)) 415 | { 416 | if (get_verbose_level()) { 417 | fprintf(stderr, "mockeagain: mocking \"writev\" on fd %d to " 418 | "signal EAGAIN.\n", fd); 419 | } 420 | 421 | errno = EAGAIN; 422 | return -1; 423 | } 424 | 425 | written_fds[fd] = 1; 426 | 427 | init_libc_handle(); 428 | 429 | if (orig_writev == NULL) { 430 | orig_writev = dlsym(libc_handle, "writev"); 431 | if (orig_writev == NULL) { 432 | fprintf(stderr, "mockeagain: could not find the underlying writev: " 433 | "%s\n", dlerror()); 434 | exit(1); 435 | } 436 | } 437 | 438 | if (!(get_mocking_type() & MOCKING_WRITES)) { 439 | return (*orig_writev)(fd, iov, iovcnt); 440 | } 441 | 442 | if (fd <= MAX_FD && polled_fds[fd]) { 443 | p = iov; 444 | for (i = 0; i < iovcnt; i++, p++) { 445 | if (p->iov_base == NULL || p->iov_len == 0) { 446 | continue; 447 | } 448 | 449 | new_iov[0].iov_base = p->iov_base; 450 | new_iov[0].iov_len = 1; 451 | break; 452 | } 453 | 454 | len = 0; 455 | p = iov; 456 | for (i = 0; i < iovcnt; i++, p++) { 457 | len += p->iov_len; 458 | } 459 | } 460 | 461 | if (new_iov[0].iov_base == NULL) { 462 | retval = (*orig_writev)(fd, iov, iovcnt); 463 | 464 | } else { 465 | if (get_verbose_level()) { 466 | fprintf(stderr, "mockeagain: mocking \"writev\" on fd %d to emit " 467 | "1 of %llu bytes.\n", fd, (unsigned long long) len); 468 | } 469 | 470 | if (pattern && new_iov[0].iov_len) { 471 | char *p; 472 | size_t len; 473 | char c; 474 | 475 | c = *(char *) new_iov[0].iov_base; 476 | 477 | if (matchbufs[fd] == NULL) { 478 | 479 | matchbufs[fd] = malloc(matchbuf_len); 480 | if (matchbufs[fd] == NULL) { 481 | fprintf(stderr, "mockeagain: ERROR: failed to allocate memory.\n"); 482 | } 483 | 484 | p = matchbufs[fd]; 485 | memset(p, 0, matchbuf_len); 486 | 487 | p[0] = c; 488 | 489 | len = 1; 490 | 491 | } else { 492 | p = matchbufs[fd]; 493 | 494 | len = strlen(p); 495 | 496 | if (len < matchbuf_len - 1) { 497 | p[len] = c; 498 | len++; 499 | 500 | } else { 501 | memmove(p, p + 1, matchbuf_len - 2); 502 | 503 | p[matchbuf_len - 2] = c; 504 | } 505 | } 506 | 507 | /* test if the pattern matches the matchbuf */ 508 | 509 | dd("matchbuf: %.*s (len: %d)", (int) len, p, 510 | (int) matchbuf_len - 1); 511 | 512 | if (len == matchbuf_len - 1 && strncmp(p, pattern, len) == 0) { 513 | if (get_verbose_level()) { 514 | fprintf(stderr, "mockeagain: \"writev\" has found a match for " 515 | "the timeout pattern \"%s\" on fd %d.\n", pattern, fd); 516 | } 517 | 518 | snd_timeout_fds[fd] = 1; 519 | } 520 | } 521 | 522 | dd("calling the original writev on fd %d", fd); 523 | retval = (*orig_writev)(fd, new_iov, 1); 524 | active_fds[fd] &= ~POLLOUT; 525 | } 526 | 527 | return retval; 528 | } 529 | 530 | 531 | int 532 | close(int fd) 533 | { 534 | int retval; 535 | static close_handle orig_close = NULL; 536 | 537 | init_libc_handle(); 538 | 539 | if (orig_close == NULL) { 540 | orig_close = dlsym(libc_handle, "close"); 541 | if (orig_close == NULL) { 542 | fprintf(stderr, "mockeagain: could not find the underlying close: " 543 | "%s\n", dlerror()); 544 | exit(1); 545 | } 546 | } 547 | 548 | if (fd <= MAX_FD) { 549 | #if (DDEBUG) 550 | if (polled_fds[fd]) { 551 | dd("calling the original close on fd %d", fd); 552 | } 553 | #endif 554 | 555 | if (matchbufs && matchbufs[fd]) { 556 | free(matchbufs[fd]); 557 | matchbufs[fd] = NULL; 558 | } 559 | 560 | active_fds[fd] = 0; 561 | polled_fds[fd] = 0; 562 | written_fds[fd] = 0; 563 | snd_timeout_fds[fd] = 0; 564 | weird_fds[fd] = 0; 565 | blacklist_fds[fd] = 0; 566 | } 567 | 568 | retval = (*orig_close)(fd); 569 | 570 | return retval; 571 | } 572 | 573 | 574 | ssize_t 575 | send(int fd, const void *buf, size_t len, int flags) 576 | { 577 | ssize_t retval; 578 | static send_handle orig_send = NULL; 579 | 580 | dd("calling my send"); 581 | 582 | if ((get_mocking_type() & MOCKING_WRITES) 583 | && fd <= MAX_FD 584 | && polled_fds[fd] 585 | && written_fds[fd] 586 | && !(active_fds[fd] & POLLOUT)) 587 | { 588 | if (get_verbose_level()) { 589 | fprintf(stderr, "mockeagain: mocking \"send\" on fd %d to " 590 | "signal EAGAIN\n", fd); 591 | } 592 | 593 | errno = EAGAIN; 594 | return -1; 595 | } 596 | 597 | written_fds[fd] = 1; 598 | 599 | init_libc_handle(); 600 | 601 | if (orig_send == NULL) { 602 | orig_send = dlsym(libc_handle, "send"); 603 | if (orig_send == NULL) { 604 | fprintf(stderr, "mockeagain: could not find the underlying send: " 605 | "%s\n", dlerror()); 606 | exit(1); 607 | } 608 | } 609 | 610 | if ((get_mocking_type() & MOCKING_WRITES) 611 | && fd <= MAX_FD 612 | && polled_fds[fd] 613 | && len) 614 | { 615 | if (get_verbose_level()) { 616 | fprintf(stderr, "mockeagain: mocking \"send\" on fd %d to emit " 617 | "1 byte data only\n", fd); 618 | } 619 | 620 | if (pattern && len) { 621 | char *p; 622 | size_t len; 623 | char c; 624 | 625 | c = *(char *) buf; 626 | 627 | if (matchbufs[fd] == NULL) { 628 | 629 | matchbufs[fd] = malloc(matchbuf_len); 630 | if (matchbufs[fd] == NULL) { 631 | fprintf(stderr, "mockeagain: ERROR: failed to allocate memory.\n"); 632 | } 633 | 634 | p = matchbufs[fd]; 635 | memset(p, 0, matchbuf_len); 636 | 637 | p[0] = c; 638 | 639 | len = 1; 640 | 641 | } else { 642 | p = matchbufs[fd]; 643 | 644 | len = strlen(p); 645 | 646 | if (len < matchbuf_len - 1) { 647 | p[len] = c; 648 | len++; 649 | 650 | } else { 651 | memmove(p, p + 1, matchbuf_len - 2); 652 | 653 | p[matchbuf_len - 2] = c; 654 | } 655 | } 656 | 657 | /* test if the pattern matches the matchbuf */ 658 | 659 | dd("matchbuf: %.*s (len: %d)", (int) len, p, 660 | (int) matchbuf_len - 1); 661 | 662 | if (len == matchbuf_len - 1 && strncmp(p, pattern, len) == 0) { 663 | if (get_verbose_level()) { 664 | fprintf(stderr, "mockeagain: \"writev\" has found a match for " 665 | "the timeout pattern \"%s\" on fd %d.\n", pattern, fd); 666 | } 667 | 668 | snd_timeout_fds[fd] = 1; 669 | } 670 | } 671 | 672 | retval = (*orig_send)(fd, buf, 1, flags); 673 | active_fds[fd] &= ~POLLOUT; 674 | 675 | } else { 676 | 677 | dd("calling the original send on fd %d", fd); 678 | 679 | retval = (*orig_send)(fd, buf, len, flags); 680 | } 681 | 682 | return retval; 683 | } 684 | 685 | 686 | ssize_t 687 | read(int fd, void *buf, size_t len) 688 | { 689 | ssize_t retval; 690 | static read_handle orig_read = NULL; 691 | 692 | dd("calling my read"); 693 | 694 | if ((get_mocking_type() & MOCKING_READS) 695 | && fd <= MAX_FD 696 | && polled_fds[fd] 697 | && !(active_fds[fd] & (POLLIN | POLLHUP))) 698 | { 699 | if (get_verbose_level()) { 700 | fprintf(stderr, "mockeagain: mocking \"read\" on fd %d to " 701 | "signal EAGAIN\n", fd); 702 | } 703 | 704 | errno = EAGAIN; 705 | return -1; 706 | } 707 | 708 | init_libc_handle(); 709 | 710 | if (orig_read == NULL) { 711 | orig_read = dlsym(libc_handle, "read"); 712 | if (orig_read == NULL) { 713 | fprintf(stderr, "mockeagain: could not find the underlying read: " 714 | "%s\n", dlerror()); 715 | exit(1); 716 | } 717 | } 718 | 719 | if ((get_mocking_type() & MOCKING_READS) 720 | && fd <= MAX_FD 721 | && polled_fds[fd] 722 | && len) 723 | { 724 | if (get_verbose_level()) { 725 | fprintf(stderr, "mockeagain: mocking \"read\" on fd %d to read " 726 | "1 byte only\n", fd); 727 | } 728 | 729 | dd("calling the original read on fd %d", fd); 730 | 731 | retval = (*orig_read)(fd, buf, 1); 732 | active_fds[fd] &= ~POLLIN; 733 | 734 | } else { 735 | retval = (*orig_read)(fd, buf, len); 736 | } 737 | 738 | return retval; 739 | } 740 | 741 | 742 | ssize_t 743 | recv(int fd, void *buf, size_t len, int flags) 744 | { 745 | ssize_t retval; 746 | static recv_handle orig_recv = NULL; 747 | 748 | dd("calling my recv"); 749 | 750 | if ((get_mocking_type() & MOCKING_READS) 751 | && fd <= MAX_FD 752 | && polled_fds[fd] 753 | && !(active_fds[fd] & POLLIN)) 754 | { 755 | if (get_verbose_level()) { 756 | fprintf(stderr, "mockeagain: mocking \"recv\" on fd %d to " 757 | "signal EAGAIN\n", fd); 758 | } 759 | 760 | errno = EAGAIN; 761 | return -1; 762 | } 763 | 764 | init_libc_handle(); 765 | 766 | if (orig_recv == NULL) { 767 | orig_recv = dlsym(libc_handle, "recv"); 768 | if (orig_recv == NULL) { 769 | fprintf(stderr, "mockeagain: could not find the underlying recv: " 770 | "%s\n", dlerror()); 771 | exit(1); 772 | } 773 | } 774 | 775 | if ((get_mocking_type() & MOCKING_READS) 776 | && fd <= MAX_FD 777 | && polled_fds[fd] 778 | && len) 779 | { 780 | if (get_verbose_level()) { 781 | fprintf(stderr, "mockeagain: mocking \"recv\" on fd %d to read " 782 | "1 byte only\n", fd); 783 | } 784 | 785 | dd("calling the original recv on fd %d", fd); 786 | 787 | retval = (*orig_recv)(fd, buf, 1, flags); 788 | active_fds[fd] &= ~POLLIN; 789 | 790 | } else { 791 | retval = (*orig_recv)(fd, buf, len, flags); 792 | } 793 | 794 | return retval; 795 | } 796 | 797 | 798 | ssize_t 799 | recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) 800 | { 801 | ssize_t retval; 802 | static recvfrom_handle orig_recvfrom = NULL; 803 | 804 | dd("calling my recvfrom"); 805 | 806 | if ((get_mocking_type() & MOCKING_READS) 807 | && fd <= MAX_FD 808 | && polled_fds[fd] 809 | && !(active_fds[fd] & POLLIN)) 810 | { 811 | if (get_verbose_level()) { 812 | fprintf(stderr, "mockeagain: mocking \"recvfrom\" on fd %d to " 813 | "signal EAGAIN\n", fd); 814 | } 815 | 816 | errno = EAGAIN; 817 | return -1; 818 | } 819 | 820 | init_libc_handle(); 821 | 822 | if (orig_recvfrom == NULL) { 823 | orig_recvfrom = dlsym(libc_handle, "recvfrom"); 824 | if (orig_recvfrom == NULL) { 825 | fprintf(stderr, "mockeagain: could not find the underlying recvfrom: " 826 | "%s\n", dlerror()); 827 | exit(1); 828 | } 829 | } 830 | 831 | if ((get_mocking_type() & MOCKING_READS) 832 | && fd <= MAX_FD 833 | && polled_fds[fd] 834 | && len) 835 | { 836 | if (get_verbose_level()) { 837 | fprintf(stderr, "mockeagain: mocking \"recvfrom\" on fd %d to read " 838 | "1 byte only\n", fd); 839 | } 840 | 841 | dd("calling the original recvfrom on fd %d", fd); 842 | 843 | retval = (*orig_recvfrom)(fd, buf, 1, flags, src_addr, addrlen); 844 | active_fds[fd] &= ~POLLIN; 845 | 846 | } else { 847 | retval = (*orig_recvfrom)(fd, buf, len, flags, src_addr, addrlen); 848 | } 849 | 850 | return retval; 851 | } 852 | 853 | 854 | static int 855 | get_mocking_type() { 856 | const char *p; 857 | 858 | #if 1 859 | if (mocking_type >= 0) { 860 | return mocking_type; 861 | } 862 | #endif 863 | 864 | mocking_type = 0; 865 | 866 | p = getenv("MOCKEAGAIN"); 867 | if (p == NULL || *p == '\0') { 868 | dd("MOCKEAGAIN env empty"); 869 | /* mocking_type = MOCKING_WRITES; */ 870 | return mocking_type; 871 | } 872 | 873 | while (*p) { 874 | if (*p == 'r' || *p == 'R') { 875 | mocking_type |= MOCKING_READS; 876 | 877 | } else if (*p == 'w' || *p == 'W') { 878 | mocking_type |= MOCKING_WRITES; 879 | } 880 | 881 | p++; 882 | } 883 | 884 | if (mocking_type == 0) { 885 | mocking_type = MOCKING_WRITES; 886 | } 887 | 888 | dd("mocking_type %d", mocking_type); 889 | 890 | return mocking_type; 891 | } 892 | 893 | 894 | static int 895 | get_verbose_level() 896 | { 897 | const char *p; 898 | 899 | if (verbose >= 0) { 900 | return verbose; 901 | } 902 | 903 | p = getenv("MOCKEAGAIN_VERBOSE"); 904 | if (p == NULL || *p == '\0') { 905 | dd("MOCKEAGAIN_VERBOSE env empty"); 906 | verbose = 0; 907 | return verbose; 908 | } 909 | 910 | if (*p >= '0' && *p <= '9') { 911 | dd("MOCKEAGAIN_VERBOSE env value: %s", p); 912 | verbose = *p - '0'; 913 | return verbose; 914 | } 915 | 916 | dd("bad verbose env value: %s", p); 917 | verbose = 0; 918 | return verbose; 919 | } 920 | 921 | 922 | static void 923 | init_matchbufs() 924 | { 925 | const char *p; 926 | int len; 927 | 928 | if (matchbufs != NULL) { 929 | return; 930 | } 931 | 932 | p = getenv("MOCKEAGAIN_WRITE_TIMEOUT_PATTERN"); 933 | if (p == NULL || *p == '\0') { 934 | dd("write_timeout env empty"); 935 | matchbuf_len = 0; 936 | return; 937 | } 938 | 939 | len = strlen(p); 940 | 941 | matchbufs = malloc((MAX_FD + 1) * sizeof(char *)); 942 | if (matchbufs == NULL) { 943 | fprintf(stderr, "mockeagain: ERROR: failed to allocate memory.\n"); 944 | return; 945 | } 946 | 947 | memset(matchbufs, 0, (MAX_FD + 1) * sizeof(char *)); 948 | matchbuf_len = len + 1; 949 | 950 | pattern = p; 951 | 952 | if (get_verbose_level()) { 953 | fprintf(stderr, "mockeagain: reading write timeout pattern: %s\n", 954 | pattern); 955 | } 956 | } 957 | 958 | 959 | /* returns a time in milliseconds */ 960 | static int now() { 961 | struct timeval tv; 962 | 963 | gettimeofday(&tv, NULL); 964 | 965 | return tv.tv_sec % (3600 * 24) + tv.tv_usec/1000; 966 | } 967 | 968 | -------------------------------------------------------------------------------- /t/000-mock-writes.c: -------------------------------------------------------------------------------- 1 | #include "test_case.h" 2 | #include 3 | 4 | int run_test(int fd) { 5 | int n; 6 | const char *buf = "test"; 7 | const int len = sizeof("test") - 1; 8 | struct iovec iov = {(void *) buf, len}; 9 | struct pollfd pfd; 10 | 11 | pfd.fd = fd; 12 | pfd.events = POLLOUT; 13 | 14 | assert(!set_mocking(MOCKING_WRITES)); 15 | 16 | /* haven't called poll(). mocking is not active */ 17 | n = send(fd, buf, len, 0); 18 | assert(n == len); 19 | 20 | assert(poll(&pfd, 1, -1) == 1); 21 | 22 | /* mocking is active and we haven't wrote yet */ 23 | n = send(fd, buf, len, 0); 24 | assert(n == 1); 25 | 26 | /* since we have already wrote it's always going to EAGAIN */ 27 | n = send(fd, buf, len, 0); 28 | assert(n == -1); 29 | assert(errno == EAGAIN); 30 | 31 | /* same as above */ 32 | n = send(fd, buf, len, 0); 33 | assert(n == -1); 34 | assert(errno == EAGAIN); 35 | 36 | assert(poll(&pfd, 1, -1) == 1); 37 | 38 | /* can write one byte now */ 39 | n = send(fd, buf, len, 0); 40 | assert(n == 1); 41 | 42 | /* EAGAIN now because we haven't polled */ 43 | n = writev(fd, &iov, 1); 44 | assert(n == -1); 45 | assert(errno == EAGAIN); 46 | 47 | assert(poll(&pfd, 1, -1) == 1); 48 | 49 | n = writev(fd, &iov, 1); 50 | assert(n == 1); 51 | 52 | n = writev(fd, &iov, 1); 53 | assert(n == -1); 54 | assert(errno == EAGAIN); 55 | 56 | return EXIT_SUCCESS; 57 | } 58 | -------------------------------------------------------------------------------- /t/001-mock-reads.c: -------------------------------------------------------------------------------- 1 | #include "test_case.h" 2 | 3 | int run_test(int fd) { 4 | int n, i; 5 | const char *buf = "test"; 6 | const int len = sizeof("test") - 1; 7 | char rcvbuf[len]; 8 | struct pollfd pfd; 9 | 10 | pfd.fd = fd; 11 | pfd.events = POLLIN; 12 | 13 | assert(!set_mocking(MOCKING_READS)); 14 | 15 | /* get 4 * 100 = 400 bytes inside receive buf for reading */ 16 | for (i = 0; i < 100; i++) { 17 | n = send(fd, buf, len, 0); 18 | assert(n == len); 19 | } 20 | 21 | /* wait until data are sent back from server */ 22 | assert(poll(&pfd, 1, -1) == 1); 23 | 24 | n = read(fd, rcvbuf, len); 25 | assert(n == 1); 26 | 27 | n = read(fd, rcvbuf, len); 28 | assert(n == -1); 29 | assert(errno == EAGAIN); 30 | 31 | assert(poll(&pfd, 1, -1) == 1); 32 | 33 | /* can read one byte now */ 34 | n = read(fd, rcvbuf, len); 35 | assert(n == 1); 36 | 37 | /* EAGAIN now because we haven't polled */ 38 | n = recv(fd, rcvbuf, len, 0); 39 | assert(n == -1); 40 | assert(errno == EAGAIN); 41 | 42 | n = recvfrom(fd, rcvbuf, len, 0, NULL, NULL); 43 | assert(n == -1); 44 | assert(errno == EAGAIN); 45 | 46 | assert(poll(&pfd, 1, -1) == 1); 47 | 48 | n = recv(fd, rcvbuf, len, 0); 49 | assert(n == 1); 50 | 51 | assert(poll(&pfd, 1, -1) == 1); 52 | 53 | n = recvfrom(fd, rcvbuf, len, 0, NULL, NULL); 54 | assert(n == 1); 55 | 56 | n = recvfrom(fd, rcvbuf, len, 0, NULL, NULL); 57 | assert(n == -1); 58 | assert(errno == EAGAIN); 59 | 60 | return EXIT_SUCCESS; 61 | } 62 | -------------------------------------------------------------------------------- /t/002-pattern-matching.c: -------------------------------------------------------------------------------- 1 | #include "test_case.h" 2 | 3 | int run_test(int fd) { 4 | int n; 5 | const char *buf = "test"; 6 | const int len = sizeof("test") - 1; 7 | struct pollfd pfd; 8 | 9 | pfd.fd = fd; 10 | pfd.events = POLLOUT; 11 | 12 | assert(!set_mocking(MOCKING_WRITES)); 13 | /* testtest */ 14 | assert(!set_write_timeout_pattern("tte")); 15 | 16 | assert(poll(&pfd, 1, -1) == 1); 17 | 18 | /* t */ 19 | n = send(fd, buf, len, 0); 20 | assert(n == 1); 21 | 22 | assert(poll(&pfd, 1, -1) == 1); 23 | 24 | /* e */ 25 | n = send(fd, buf + 1, len, 0); 26 | assert(n == 1); 27 | 28 | assert(poll(&pfd, 1, -1) == 1); 29 | 30 | /* s */ 31 | n = send(fd, buf + 2, len, 0); 32 | assert(n == 1); 33 | 34 | assert(poll(&pfd, 1, -1) == 1); 35 | 36 | /* t */ 37 | n = send(fd, buf + 3, len, 0); 38 | assert(n == 1); 39 | 40 | assert(poll(&pfd, 1, -1) == 1); 41 | 42 | /* t */ 43 | n = send(fd, buf + 3, len, 0); 44 | assert(n == 1); 45 | 46 | assert(poll(&pfd, 1, -1) == 1); 47 | 48 | /* e */ 49 | n = send(fd, buf + 1, len, 0); 50 | assert(n == 1); 51 | 52 | /* times out */ 53 | assert(poll(&pfd, 1, 100) == 0); 54 | assert(poll(&pfd, 1, 100) == 0); 55 | 56 | n = send(fd, buf + 1, len, 0); 57 | assert(n == -1); 58 | assert(errno == EAGAIN); 59 | 60 | return EXIT_SUCCESS; 61 | } 62 | -------------------------------------------------------------------------------- /t/003-disabled.c: -------------------------------------------------------------------------------- 1 | #include "test_case.h" 2 | 3 | int run_test(int fd) { 4 | int n; 5 | const char *buf = "test"; 6 | const int len = sizeof("test") - 1; 7 | char rcvbuf[len]; 8 | struct pollfd pfd; 9 | 10 | pfd.fd = fd; 11 | pfd.events = POLLIN; 12 | 13 | n = send(fd, buf, len, 0); 14 | assert(n == len); 15 | 16 | n = send(fd, buf, len, 0); 17 | assert(n == len); 18 | 19 | n = send(fd, buf, len, 0); 20 | assert(n == len); 21 | 22 | /* wait until data are sent back from server */ 23 | assert(poll(&pfd, 1, -1) == 1); 24 | 25 | n = read(fd, rcvbuf, len); 26 | assert(n == 4); 27 | 28 | n = read(fd, rcvbuf, len); 29 | assert(n == 4); 30 | 31 | return EXIT_SUCCESS; 32 | } 33 | -------------------------------------------------------------------------------- /t/004-detect-unmockable-fds.c: -------------------------------------------------------------------------------- 1 | #include "test_case.h" 2 | #if __linux__ 3 | #include 4 | #include 5 | #endif 6 | 7 | int run_test(int fd) { 8 | (void) fd; 9 | 10 | #if __linux__ 11 | struct pollfd pfd; 12 | ssize_t n; 13 | int sfd; 14 | struct signalfd_siginfo sfd_info; 15 | sigset_t mask; 16 | int efd; 17 | uint64_t efd_events; 18 | 19 | sigemptyset(&mask); 20 | sigaddset(&mask, SIGPIPE); 21 | assert(sigprocmask(SIG_BLOCK, &mask, NULL) != -1); 22 | 23 | sfd = signalfd(-1, &mask, SFD_NONBLOCK); 24 | assert(sfd != -1); 25 | 26 | pfd.fd = sfd; 27 | pfd.events = POLLIN; 28 | assert(!set_mocking(MOCKING_READS)); 29 | 30 | assert(kill(getpid(), SIGPIPE) != -1); 31 | assert(poll(&pfd, 1, -1) == 1); 32 | 33 | n = read(sfd, &sfd_info, sizeof(struct signalfd_siginfo)); 34 | assert(n == sizeof(struct signalfd_siginfo)); 35 | 36 | close(sfd); 37 | 38 | efd = eventfd(0, EFD_NONBLOCK); 39 | pfd.fd = efd; 40 | pfd.events = POLLIN; 41 | 42 | efd_events = 1; 43 | assert(write(efd, &efd_events, sizeof(uint64_t)) != -1); 44 | assert(poll(&pfd, 1, -1) == 1); 45 | 46 | n = read(efd, &efd_events, sizeof(uint64_t)); 47 | assert(n == sizeof(uint64_t)); 48 | 49 | close(efd); 50 | #endif 51 | 52 | return EXIT_SUCCESS; 53 | } 54 | -------------------------------------------------------------------------------- /t/echo_server.py: -------------------------------------------------------------------------------- 1 | """ 2 | A simple threaded echo server. 3 | """ 4 | 5 | import socket 6 | import threading 7 | import SocketServer 8 | import subprocess 9 | import sys 10 | 11 | class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler): 12 | 13 | def handle(self): 14 | while True: 15 | try: 16 | data = self.request.recv(1024) 17 | if not data: 18 | break 19 | self.request.sendall(data) 20 | except: 21 | break 22 | 23 | class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): 24 | pass 25 | 26 | if __name__ == "__main__": 27 | HOST, PORT = "localhost", 0 28 | 29 | server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) 30 | 31 | server_thread = threading.Thread(target=server.serve_forever) 32 | server_thread.daemon = True 33 | server_thread.start() 34 | 35 | print('Listening on IP %s and port %d' % server.server_address) 36 | env = { 37 | "LD_PRELOAD": sys.argv[3], 38 | "DYLD_INSERT_LIBRARIES": sys.argv[3], 39 | "DYLD_FORCE_FLAT_NAMESPACE": "1", 40 | "MOCKEAGAIN_VERBOSE": "1", 41 | } 42 | 43 | if len(sys.argv) == 4 and sys.argv[2] == '1': # valgrind 44 | ret = subprocess.call(['valgrind', '--leak-check=full', 45 | sys.argv[1], server.server_address[0], 46 | str(server.server_address[1])], env=env) 47 | else: 48 | ret = subprocess.call([sys.argv[1], server.server_address[0], 49 | str(server.server_address[1])], env=env) 50 | 51 | sys.exit(ret) 52 | -------------------------------------------------------------------------------- /t/runner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "test_case.h" 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | struct addrinfo hints; 14 | struct addrinfo *result; 15 | int fd, s; 16 | 17 | if (argc != 3) { 18 | fprintf(stderr, "expect two cmdline arguments\n"); 19 | exit(EXIT_FAILURE); 20 | } 21 | 22 | memset(&hints, 0, sizeof(struct addrinfo)); 23 | hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 24 | hints.ai_socktype = SOCK_STREAM; 25 | hints.ai_flags = 0; 26 | hints.ai_protocol = IPPROTO_TCP; 27 | 28 | s = getaddrinfo(argv[1], argv[2], &hints, &result); 29 | if (s) { 30 | fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(s)); 31 | exit(EXIT_FAILURE); 32 | } 33 | 34 | fd = socket(result->ai_family, result->ai_socktype, 35 | result->ai_protocol); 36 | if (fd == -1) { 37 | perror("socket failed"); 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | if (connect(fd, result->ai_addr, result->ai_addrlen) == -1) { 42 | /* note that since we always connect to localhost 43 | EINPROGRESS is not expected to be returned */ 44 | perror("connect failed"); 45 | exit(EXIT_FAILURE); 46 | } 47 | 48 | /* make it non-blocking after connect to save some work */ 49 | if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1) { 50 | perror("fcntl failed"); 51 | } 52 | 53 | freeaddrinfo(result); 54 | 55 | s = run_test(fd); 56 | 57 | /* we are terminating anyway, don't bother with errors */ 58 | shutdown(fd, SHUT_WR); 59 | close(fd); 60 | 61 | exit(s); 62 | } 63 | -------------------------------------------------------------------------------- /t/test_case.c: -------------------------------------------------------------------------------- 1 | #include "test_case.h" 2 | 3 | int set_mocking(int types) { 4 | char env[3] = {0}; 5 | size_t pos = 0; 6 | 7 | if (types & MOCKING_READS) { 8 | env[pos++] = 'r'; 9 | } 10 | 11 | if (types & MOCKING_WRITES) { 12 | env[pos++] = 'w'; 13 | } 14 | 15 | return setenv("MOCKEAGAIN", env, 1); 16 | } 17 | 18 | int set_write_timeout_pattern(const char *pattern) { 19 | if (!strlen(pattern)) { 20 | return 0; 21 | } 22 | 23 | return setenv("MOCKEAGAIN_WRITE_TIMEOUT_PATTERN", pattern, 1); 24 | } 25 | -------------------------------------------------------------------------------- /t/test_case.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_CASE_H 2 | #define TEST_CASE_H 3 | 4 | #define _GNU_SOURCE 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int run_test(int fd); 16 | 17 | enum { 18 | MOCKING_READS = 0x01, 19 | MOCKING_WRITES = 0x02 20 | }; 21 | 22 | int set_mocking(int types); 23 | 24 | int set_write_timeout_pattern(const char *pattern); 25 | 26 | #endif /* !TEST_CASE_H */ 27 | --------------------------------------------------------------------------------