pax_global_header00006660000000000000000000000064126336171660014525gustar00rootroot0000000000000052 comment=5467299450ecf61635657557b6e01aaaf6c3fdf4 libuv-v1.8.0/000077500000000000000000000000001263361716600130425ustar00rootroot00000000000000libuv-v1.8.0/.gitignore000066400000000000000000000015071263361716600150350ustar00rootroot00000000000000*.swp *.[oa] *.l[oa] *.opensdf *.orig *.pyc *.sdf *.suo core vgcore.* .buildstamp .dirstamp .deps/ /.libs/ /aclocal.m4 /ar-lib /autom4te.cache/ /compile /config.guess /config.log /config.status /config.sub /configure /depcomp /install-sh /libtool /libuv.a /libuv.dylib /libuv.pc /libuv.so /ltmain.sh /missing /test-driver Makefile Makefile.in # Generated by gyp for android *.target.mk /out/ /build/gyp /test/.libs/ /test/run-tests /test/run-tests.exe /test/run-tests.dSYM /test/run-benchmarks /test/run-benchmarks.exe /test/run-benchmarks.dSYM *.sln *.sln.cache *.ncb *.vcproj *.vcproj*.user *.vcxproj *.vcxproj.filters *.vcxproj.user _UpgradeReport_Files/ UpgradeLog*.XML Debug Release ipch # sphinx generated files /docs/build/ # Clion / IntelliJ project files /.idea/ *.xcodeproj *.xcworkspace # make dist output libuv-*.tar.* libuv-v1.8.0/.mailmap000066400000000000000000000037251263361716600144720ustar00rootroot00000000000000Aaron Bieber Alan Gutierrez Andrius Bentkus Bert Belder Bert Belder Brandon Philips Brian White Brian White Caleb James DeLisle Christoph Iserlohn Devchandra Meetei Leishangthem Fedor Indutny Frank Denis Isaac Z. Schlueter Jason Williams Justin Venus Keno Fischer Keno Fischer Leith Bade Leonard Hecker Maciej Małecki Marc Schlaich Michael Michael Neumann Nicholas Vavilov Rasmus Christian Pedersen Rasmus Christian Pedersen Robert Mustacchi Ryan Dahl Ryan Emery Sam Roberts San-Tai Hsu Santiago Gimeno Saúl Ibarra Corretgé Shigeki Ohtsu Timothy J. Fontaine Yasuhiro Matsumoto Yazhong Liu Yuki Okumura libuv-v1.8.0/AUTHORS000066400000000000000000000215041263361716600141140ustar00rootroot00000000000000# Authors ordered by first contribution. Ryan Dahl Bert Belder Josh Roesslein Alan Gutierrez Joshua Peek Igor Zinkovsky San-Tai Hsu Ben Noordhuis Henry Rawas Robert Mustacchi Matt Stevens Paul Querna Shigeki Ohtsu Tom Hughes Peter Bright Jeroen Janssen Andrea Lattuada Augusto Henrique Hentz Clifford Heath Jorge Chamorro Bieling Luis Lavena Matthew Sporleder Erick Tryzelaar Isaac Z. Schlueter Pieter Noordhuis Marek Jelen Fedor Indutny Saúl Ibarra Corretgé Felix Geisendörfer Yuki Okumura Roman Shtylman Frank Denis Carter Allen Tj Holowaychuk Shimon Doodkin Ryan Emery Bruce Mitchener Maciej Małecki Yasuhiro Matsumoto Daisuke Murase Paddy Byers Dan VerWeire Brandon Benvie Brandon Philips Nathan Rajlich Charlie McConnell Vladimir Dronnikov Aaron Bieber Bulat Shakirzyanov Brian White Erik Dubbelboer Keno Fischer Ira Cooper Andrius Bentkus Iñaki Baz Castillo Mark Cavage George Yohng Xidorn Quan Roman Neuhauser Shuhei Tanuma Bryan Cantrill Trond Norbye Tim Holy Prancesco Pertugio Leonard Hecker Andrew Paprocki Luigi Grilli Shannen Saez Artur Adib Hiroaki Nakamura Ting-Yu Lin Stephen Gallagher Shane Holloway Andrew Shaffer Vlad Tudose Ben Leslie Tim Bradshaw Timothy J. Fontaine Marc Schlaich Brian Mazza Elliot Saba Ben Kelly Nils Maier Nicholas Vavilov Miroslav Bajtoš Sean Silva Wynn Wilkes Andrei Sedoi Alex Crichton Brent Cook Brian Kaisner Luca Bruno Reini Urban Maks Naumov Sean Farrell Chris Bank Geert Jansen Christoph Iserlohn Steven Kabbes Alex Gaynor huxingyi Tenor Biel Andrej Manduch Joshua Neuheisel Alexis Campailla Yazhong Liu Sam Roberts River Tarnell Nathan Sweet Trevor Norris Oguz Bastemur Dylan Cali Austin Foxley Benjamin Saunders Geoffry Song Rasmus Christian Pedersen William Light Oleg Efimov Lars Gierth Rasmus Christian Pedersen Justin Venus Kristian Evensen Linus Mårtensson Navaneeth Kedaram Nambiathan Yorkie StarWing thierry-FreeBSD Isaiah Norton Raul Martins David Capello Paul Tan Javier Hernández Tonis Tiigi Norio Kobota 李港平 Chernyshev Viacheslav Stephen von Takach JD Ballard Luka Perkov Ryan Cole HungMingWu Jay Satiro Leith Bade Peter Atashian Tim Cooper Caleb James DeLisle Jameson Nash Graham Lee Andrew Low Pavel Platto Tony Kelman John Firebaugh lilohuang Paul Goldsmith Julien Gilli Michael Hudson-Doyle Recep ASLANTAS Rob Adams Zachary Newman Robin Hahling Jeff Widman cjihrig Tomasz Kołodziejski Unknown W. Brackets Emmanuel Odeke Mikhail Mukovnikov Thorsten Lorenz Yuri D'Elia Manos Nikolaidis Elijah Andrews Michael Ira Krufky Helge Deller Joey Geralnik Tim Caswell Logan Rosen Kenneth Perry John Marino Alexey Melnichuk Johan Bergström Alex Mo Luis Martinez de Bartolome Michael Penick Michael Massimiliano Torromeo TomCrypto Brett Vickers Ole André Vadla Ravnås Kazuho Oku Ryan Phillips Brian Green Devchandra Meetei Leishangthem Corey Farrell Per Nilsson Alan Rogers Daryl Haresign Rui Abreu Ferreira João Reis farblue68 Jason Williams Igor Soarez Miodrag Milanovic Cheng Zhao Michael Neumann Stefano Cristiano heshamsafi A. Hauptmann John McNamee Yosuke Furukawa Santiago Gimeno guworks RossBencina Roger A. Light chenttuuvv Richard Lau ronkorving Corbin Simpson Zachary Hamm Karl Skomski Jeremy Whitlock Willem Thiart Ben Trask Jianghua Yang Colin Snover Sakthipriyan Vairamani Eli Skeggs nmushell Gireesh Punathil Ryan Johnston Adam Stylinski Nathan Corvino Wink Saville Angel Leon Louis DeJardin Imran Iqbal Petka Antonov Ian Kronquist kkdaemon Yuval Brik Joran Dirk Greef Andrey Mazo sztomi libuv-v1.8.0/CONTRIBUTING.md000066400000000000000000000125561263361716600153040ustar00rootroot00000000000000# CONTRIBUTING The libuv project welcomes new contributors. This document will guide you through the process. ### FORK Fork the project [on GitHub](https://github.com/libuv/libuv) and check out your copy. ``` $ git clone https://github.com/username/libuv.git $ cd libuv $ git remote add upstream https://github.com/libuv/libuv.git ``` Now decide if you want your feature or bug fix to go into the master branch or the stable branch. As a rule of thumb, bug fixes go into the stable branch while new features go into the master branch. The stable branch is effectively frozen; patches that change the libuv API/ABI or affect the run-time behavior of applications get rejected. In case of doubt, open an issue in the [issue tracker][], post your question to the [libuv mailing list], or contact one of project maintainers (@bnoordhuis, @piscisaureus, @indutny or @saghul) on [IRC][]. Especially do so if you plan to work on something big. Nothing is more frustrating than seeing your hard work go to waste because your vision does not align with that of a project maintainers. ### BRANCH Okay, so you have decided on the proper branch. Create a feature branch and start hacking: ``` $ git checkout -b my-feature-branch -t origin/v1.x ``` (Where v1.x is the latest stable branch as of this writing.) ### CODE Please adhere to libuv's code style. In general it follows the conventions from the [Google C/C++ style guide]. Some of the key points, as well as some additional guidelines, are enumerated below. * Code that is specific to unix-y platforms should be placed in `src/unix`, and declarations go into `include/uv-unix.h`. * Source code that is Windows-specific goes into `src/win`, and related publicly exported types, functions and macro declarations should generally be declared in `include/uv-win.h`. * Names should be descriptive and concise. * All the symbols and types that libuv makes available publicly should be prefixed with `uv_` (or `UV_` in case of macros). * Internal, non-static functions should be prefixed with `uv__`. * Use two spaces and no tabs. * Lines should be wrapped at 80 characters. * Ensure that lines have no trailing whitespace, and use unix-style (LF) line endings. * Use C89-compliant syntax. In other words, variables can only be declared at the top of a scope (function, if/for/while-block). * When writing comments, use properly constructed sentences, including punctuation. * When documenting APIs and/or source code, don't make assumptions or make implications about race, gender, religion, political orientation or anything else that isn't relevant to the project. * Remember that source code usually gets written once and read often: ensure the reader doesn't have to make guesses. Make sure that the purpose and inner logic are either obvious to a reasonably skilled professional, or add a comment that explains it. ### COMMIT Make sure git knows your name and email address: ``` $ git config --global user.name "J. Random User" $ git config --global user.email "j.random.user@example.com" ``` Writing good commit logs is important. A commit log should describe what changed and why. Follow these guidelines when writing one: 1. The first line should be 50 characters or less and contain a short description of the change prefixed with the name of the changed subsystem (e.g. "net: add localAddress and localPort to Socket"). 2. Keep the second line blank. 3. Wrap all other lines at 72 columns. A good commit log looks like this: ``` subsystem: explaining the commit in one line Body of commit message is a few lines of text, explaining things in more detail, possibly giving some background about the issue being fixed, etc etc. The body of the commit message can be several paragraphs, and please do proper word-wrap and keep columns shorter than about 72 characters or so. That way `git log` will show things nicely even when it is indented. ``` The header line should be meaningful; it is what other people see when they run `git shortlog` or `git log --oneline`. Check the output of `git log --oneline files_that_you_changed` to find out what subsystem (or subsystems) your changes touch. ### REBASE Use `git rebase` (not `git merge`) to sync your work from time to time. ``` $ git fetch upstream $ git rebase upstream/v1.x # or upstream/master ``` ### TEST Bug fixes and features should come with tests. Add your tests in the `test/` directory. Tests also need to be registered in `test/test-list.h`. Look at other tests to see how they should be structured (license boilerplate, the way entry points are declared, etc.). Check README.md file to find out how to run the test suite and make sure that there are no test regressions. ### PUSH ``` $ git push origin my-feature-branch ``` Go to https://github.com/username/libuv and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days. If there are comments to address, apply your changes in a separate commit and push that to your feature branch. Post a comment in the pull request afterwards; GitHub does not send out notifications when you add commits. [issue tracker]: https://github.com/libuv/libuv/issues [libuv mailing list]: http://groups.google.com/group/libuv [IRC]: http://webchat.freelibuv.net/?channels=libuv [Google C/C++ style guide]: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml libuv-v1.8.0/ChangeLog000066400000000000000000002151031263361716600146160ustar00rootroot000000000000002015.12.15, Version 1.8.0 (Stable) Changes since version 1.7.5: * unix: fix memory leak in uv_interface_addresses (Jianghua Yang) * unix: make uv_guess_handle work properly for AIX (Gireesh Punathil) * fs: undo uv__req_init when uv__malloc failed (Jianghua Yang) * build: remove unused 'component' GYP option (Saúl Ibarra Corretgé) * include: remove duplicate extern declaration (Jianghua Yang) * win: use the MSVC provided snprintf where possible (Jason Williams) * win, test: fix compilation warning (Saúl Ibarra Corretgé) * win: fix compilation with VS < 2012 (Ryan Johnston) * stream: support empty uv_try_write on unix (Fedor Indutny) * unix: fix request handle leak in uv__udp_send (Jianghua Yang) * src: replace QUEUE_SPLIT with QUEUE_MOVE (Ben Noordhuis) * unix: use QUEUE_MOVE when iterating over lists (Ben Noordhuis) * unix: squelch harmless valgrind warning (Ben Noordhuis) * test: don't abort on setrlimit() failure (Ben Noordhuis) * unix: only undo fs req registration in async mode (Ben Noordhuis) * unix: fix uv__getiovmax return value (HungMingWu) * unix: make work with Solaris Studio. (Adam Stylinski) * test: fix fs_event_watch_file_currentdir flakiness (Santiago Gimeno) * unix: skip prohibited syscalls on tvOS and watchOS (Nathan Corvino) * test: use FQDN in getaddrinfo_fail test (Wink Saville) * docs: clarify documentation of uv_tcp_init_ex (Andrius Bentkus) * win: fix comment (Miodrag Milanovic) * doc: fix typo in README (Angel Leon) * darwin: abort() if (un)locking fs mutex fails (Ben Noordhuis) * pipe: enable inprocess uv_write2 on Windows (Louis DeJardin) * win: properly return UV_EBADF when _close() fails (Nicholas Vavilov) * test: skip process_title for AIX (Imran Iqbal) * misc: expose handle print APIs (Petka Antonov) * include: add stdio.h to uv.h (Saúl Ibarra Corretgé) * misc: remove unnecessary null pointer checks (Ian Kronquist) * test,freebsd: skip udp_dual_stack if not supported (Santiago Gimeno) * linux: don't retry dup2/dup3 on EINTR (Ben Noordhuis) * unix: don't retry dup2/dup3 on EINTR (Ben Noordhuis) * test: fix -Wtautological-pointer-compare warnings (Saúl Ibarra Corretgé) * win: map ERROR_BAD_PATHNAME to UV_ENOENT (Tony Kelman) * test: fix test/test-tty.c for AIX (Imran Iqbal) * android: support api level less than 21 (kkdaemon) * fsevents: fix race on simultaneous init+close (Fedor Indutny) * linux,fs: fix p{read,write}v with a 64bit offset (Saúl Ibarra Corretgé) * fs: add uv_fs_realpath() (Yuval Brik) * win: fix path for removed and renamed fs events (Joran Dirk Greef) * win: do not read more from stream than available (Jeremy Whitlock) * test: test that uv_close() doesn't corrupt QUEUE (Andrey Mazo) * unix: fix uv_fs_event_stop() from fs_event_cb (Andrey Mazo) * test: fix self-deadlocks in thread_rwlock_trylock (Ben Noordhuis) * src: remove non ascii character (sztomi) * test: fix test udp_multicast_join6 for AIX (Imran Iqbal) 2015.09.23, Version 1.7.5 (Stable), a8c1136de2cabf25b143021488cbaab05834daa8 Changes since version 1.7.4: * unix: Support atomic compare & swap xlC on AIX (nmushell) * unix: Fix including uv-aix.h on AIX (nmushell) * unix: consolidate rwlock tryrdlock trywrlock errors (Saúl Ibarra Corretgé) * unix, win: consolidate mutex trylock errors (Saúl Ibarra Corretgé) * darwin: fix memory leak in uv_cpu_info (Jianghua Yang) * test: add tests for the uv_rwlock implementation (Bert Belder) * win: redo/fix the uv_rwlock APIs (Bert Belder) * win: don't fetch function pointers to SRWLock APIs (Bert Belder) 2015.09.12, Version 1.7.4 (Stable), a7ad4f52189d89cfcba35f78bfc5ff3b1f4105c4 Changes since version 1.7.3: * doc: uv_read_start and uv_read_cb clarifications (Ben Trask) * freebsd: obtain true uptime through clock_gettime() (Jianghua Yang) * win, tty: do not convert \r to \r\n (Colin Snover) * build,gyp: add DragonFly to the list of OSes (Michael Neumann) * fs: fix bug in sendfile for DragonFly (Michael Neumann) * doc: add uv_dlsym() return type (Brian White) * tests: fix fs tests run w/o full getdents support (Jeremy Whitlock) * doc: fix typo (Devchandra Meetei Leishangthem) * doc: fix uv-unix.h location (Sakthipriyan Vairamani) * unix: fix error check when closing process pipe fd (Ben Noordhuis) * test,freebsd: fix ipc_listen_xx_write tests (Santiago Gimeno) * win: fix unsavory rwlock fallback implementation (Bert Belder) * doc: clarify repeat timer behavior (Eli Skeggs) 2015.08.28, Version 1.7.3 (Stable), 93877b11c8b86e0a6befcda83a54555c1e36e4f0 Changes since version 1.7.2: * threadpool: fix thread starvation bug (Ben Noordhuis) 2015.08.25, Version 1.7.2 (Stable), 4d13a013fcfa72311f0102751fdc7951873f466c Changes since version 1.7.1: * unix, win: make uv_loop_init return on error (Willem Thiart) * win: reset pipe handle for pipe servers (Saúl Ibarra Corretgé) * win: fix replacing pipe handle for pipe servers (Saúl Ibarra Corretgé) * win: fix setting pipe pending instances after bind (Saúl Ibarra Corretgé) 2015.08.20, Version 1.7.1 (Stable), 44f4b6bd82d8ae4583ccc4768a83af778ef69f85 Changes since version 1.7.0: * doc: document the procedure for verifying releases (Saúl Ibarra Corretgé) * doc: add note about Windows binaries to the README (Saúl Ibarra Corretgé) * doc: use long GPG IDs in MAINTAINERS.md (Saúl Ibarra Corretgé) * Revert "stream: squelch ECONNRESET error if already closed" (Saúl Ibarra Corretgé) * doc: clarify uv_read_stop() is idempotent (Corbin Simpson) * unix: OpenBSD's setsockopt needs an unsigned char for multicast (Zachary Hamm) * test: Fix two memory leaks (Karl Skomski) * unix,win: return EINVAL on nullptr args in uv_fs_{read,write} (Karl Skomski) * win: set accepted TCP sockets as non-inheritable (Saúl Ibarra Corretgé) * unix: remove superfluous parentheses in fs macros (Ben Noordhuis) * unix: don't copy arguments for sync fs requests (Ben Noordhuis) * test: plug small memory leak in unix test runner (Ben Noordhuis) * unix,windows: allow NULL loop for sync fs requests (Ben Noordhuis) * unix,windows: don't assert on unknown error code (Ben Noordhuis) * stream: retry write on EPROTOTYPE on OSX (Brian White) * common: fix use of snprintf on Windows (Saúl Ibarra Corretgé) * tests: refactored fs watch_dir tests for stability (Jeremy Whitlock) 2015.08.06, Version 1.7.0 (Stable), 415a865d6365ba58d02b92b89d46ba5d7744ec8b Changes since version 1.6.1: * win,stream: add slot to remember CRT fd (Bert Belder) * win,pipe: properly close when created from CRT fd (Bert Belder) * win,pipe: don't close fd 0-2 (Bert Belder) * win,tty: convert fd -> handle safely (Bert Belder) * win,tty: properly close when created from CRT fd (Bert Belder) * win,tty: don't close fd 0-2 (Bert Belder) * win,fs: don't close fd 0-2 (Bert Belder) * win: include "malloc.h" (Cheng Zhao) * windows: MSVC 2015 has C99 inline (Jason Williams) * dragonflybsd: fixes for nonblocking and cloexec (Michael Neumann) * dragonflybsd: use sendfile(2) for uv_fs_sendfile (Michael Neumann) * dragonflybsd: fix uv_exepath (Michael Neumann) * win,fs: Fixes align(8) directive on mingw (Stefano Cristiano) * unix, win: prevent replacing fd in uv_{udp,tcp,pipe}_t (Saúl Ibarra Corretgé) * win: move logic to set socket non-inheritable to uv_tcp_set_socket (Saúl Ibarra Corretgé) * unix, win: add ability to create tcp/udp sockets early (Saúl Ibarra Corretgé) * test: retry select() on EINTR, honor milliseconds (Ben Noordhuis) * unix: consolidate tcp and udp bind error (Saúl Ibarra Corretgé) * test: conditionally skip udp_ipv6_multicast_join6 (heshamsafi) * core: add UV_VERSION_HEX macro (Saúl Ibarra Corretgé) * doc: add section with version-checking macros and functions (Saúl Ibarra Corretgé) * tty: cleanup handle if uv_tty_init fails (Saúl Ibarra Corretgé) * darwin: save a fd when FSEvents is used (Saúl Ibarra Corretgé) * win: fix returning thread id in uv_thread_self (Saúl Ibarra Corretgé) * common: use offsetof for QUEUE_DATA (Saúl Ibarra Corretgé) * win: remove UV_HANDLE_CONNECTED (A. Hauptmann) * docs: add Windows specific note for uv_fs_open (Saúl Ibarra Corretgé) * doc: add note about uv_fs_scandir (Saúl Ibarra Corretgé) * test,unix: reduce stack size of watchdog threads (Ben Noordhuis) * win: add support for recursive file watching (Saúl Ibarra Corretgé) * win,tty: support consoles with non-default colors (John McNamee) * doc: add missing variable name (Yosuke Furukawa) * stream: squelch ECONNRESET error if already closed (Santiago Gimeno) * build: remove ancient condition from common.gypi (Saúl Ibarra Corretgé) * tests: skip some tests when network is unreachable (Luca Bruno) * build: proper support for android cross compilation (guworks) * android: add missing include to pthread-fixes.c (RossBencina) * test: fix compilation warning (Saúl Ibarra Corretgé) * doc: add a note about uv_dirent_t.type (Saúl Ibarra Corretgé) * win,test: fix shared library build (Saúl Ibarra Corretgé) * test: fix compilation warning (Santiago Gimeno) * build: add experimental Windows installer (Roger A. Light) * threadpool: send signal only when queue is empty (chenttuuvv) * aix: fix uv_exepath with relative paths (Richard Lau) * build: fix version syntax in AppVeyor file (Saúl Ibarra Corretgé) * unix: allow nbufs > IOV_MAX in uv_fs_{read,write} (ronkorving) 2015.06.06, Version 1.6.1 (Stable), 30c8be07bb78a66fdee5141626bf53a49a17094a Changes since version 1.6.0: * unix: handle invalid _SC_GETPW_R_SIZE_MAX values (cjihrig) 2015.06.04, Version 1.6.0 (Stable), adfccad76456061dfcf79b8df8e7dbfee51791d7 Changes since version 1.5.0: * aix: fix setsockopt for multicast options (Michael) * unix: don't block for io if any io handle is primed (Saúl Ibarra Corretgé) * windows: MSVC 2015 has snprintf() (Rui Abreu Ferreira) * windows: Add VS2015 support to vcbuild.bat (Jason Williams) * doc: fix typo in tcp.rst (Igor Soarez) * linux: work around epoll bug in kernels < 2.6.37 (Ben Noordhuis) * unix,win: add uv_os_homedir() (cjihrig) * stream: fix `select()` race condition (Fedor Indutny) * unix: prevent infinite loop in uv__run_pending (Saúl Ibarra Corretgé) * unix: make sure UDP send callbacks are asynchronous (Saúl Ibarra Corretgé) * test: fix `platform_output` netmask printing. (Andrew Paprocki) * aix: add ahafs autoconf detection and README notes (Andrew Paprocki) * core: add ability to customize memory allocator (Saúl Ibarra Corretgé) 2015.05.07, Version 1.5.0 (Stable), 4e77f74c7b95b639b3397095db1bc5bcc016c203 Changes since version 1.4.2: * doc: clarify that the thread pool primites are not thread safe (Andrius Bentkus) * aix: always deregister closing fds from epoll (Michael) * unix: fix glibc-2.20+ macro incompatibility (Massimiliano Torromeo) * doc: add Sphinx plugin for generating links to man pages (Saúl Ibarra Corretgé) * doc: link system and library calls to man pages (Saúl Ibarra Corretgé) * doc: document uv_getnameinfo_t.{host|service} (Saúl Ibarra Corretgé) * build: update the location of gyp (Stephen von Takach) * win: name all anonymous structs and unions (TomCrypto) * linux: work around epoll bug in kernels 3.10-3.19 (Ben Noordhuis) * darwin: fix size calculation in select() fallback (Ole André Vadla Ravnås) * solaris: fix setsockopt for multicast options (Julien Gilli) * test: fix race condition in multithreaded test (Ben Noordhuis) * doc: fix long lines in tty.rst (Ben Noordhuis) * test: use UV_TTY_MODE_* values in tty test (Ben Noordhuis) * unix: don't clobber errno in uv_tty_reset_mode() (Ben Noordhuis) * unix: reject non-tty fds in uv_tty_init() (Ben Noordhuis) * win: fix pipe blocking writes (Alexis Campailla) * build: fix cross-compiling for iOS (Steven Kabbes) * win: remove unnecessary malloc.h * include: use `extern "c++"` for defining C++ code (Kazuho Oku) * unix: reap child on execvp() failure (Ryan Phillips) * windows: fix handle leak on EMFILE (Brian Green) * test: fix tty_file, close handle if initialized (Saúl Ibarra Corretgé) * doc: clarify what uv_*_open accepts (Saúl Ibarra Corretgé) * doc: clarify that we don't maintain external doc resources (Saúl Ibarra Corretgé) * build: add documentation for ninja support (Devchandra Meetei Leishangthem) * doc: document uv_buf_t members (Corey Farrell) * linux: fix epoll_pwait() fallback on arm64 (Ben Noordhuis) * android: fix compilation warning (Saúl Ibarra Corretgé) * unix: don't close the fds we just setup (Sam Roberts) * test: spawn child replacing std{out,err} to stderr (Saúl Ibarra Corretgé) * unix: fix swapping fds order in uv_spawn (Saúl Ibarra Corretgé) * unix: fix potential bug if dup2 fails in uv_spawn (Saúl Ibarra Corretgé) * test: remove LOG and LOGF variadic macros (Saúl Ibarra Corretgé) * win: fix uv_fs_access on directories (Saúl Ibarra Corretgé) * win: fix of double free in uv_uptime (Per Nilsson) * unix: open "/dev/null" instead of "/" for emfile_fd (Alan Rogers) * docs: add some missing words (Daryl Haresign) * unix: clean up uv_fs_open() O_CLOEXEC logic (Ben Noordhuis) * build: set SONAME for shared library in uv.gyp (Rui Abreu Ferreira) * windows: define snprintf replacement as inline instead of static (Rui Abreu Ferreira) * win: fix unlink of readonly files (João Reis) * doc: fix uv_run(UV_RUN_DEFAULT) description (Ben Noordhuis) * linux: intercept syscall when running under memory sanitizer (Keno Fischer) * aix: fix uv_interface_addresses return value (farblue68) * windows: defer reporting TCP write failure until next tick (Saúl Ibarra Corretgé) * test: add test for deferred TCP write failure (Saúl Ibarra Corretgé) 2015.02.27, Version 1.4.2 (Stable), 1a7391348a11d5450c0f69c828d5302e2cb842eb Changes since version 1.4.1: * stream: ignore EINVAL for SO_OOBINLINE on OS X (Fedor Indutny) 2015.02.25, Version 1.4.1 (Stable), e8e3fc5789cc0f02937879d141cca0411274093c Changes since version 1.4.0: * win: don't use inline keyword in thread.c (Ben Noordhuis) * windows: fix setting dirent types on uv_fs_scandir_next (Saúl Ibarra Corretgé) * unix,windows: make uv_thread_create() return errno (Ben Noordhuis) * tty: fix build for SmartOS (Julien Gilli) * unix: fix for uv_async data race (Michael Penick) * unix, windows: map EHOSTDOWN errno (Ben Noordhuis) * stream: use SO_OOBINLINE on OS X (Fedor Indutny) 2015.02.10, Version 1.4.0 (Stable), 19fb8a90648f3763240db004b77ab984264409be Changes since version 1.3.0: * unix: check Android support for pthread_cond_timedwait_monotonic_np (Leith Bade) * test: use modified path in test (cjihrig) * unix: implement uv_stream_set_blocking() (Ben Noordhuis) 2015.01.29, Version 1.3.0 (Stable), 165685b2a9a42cf96501d79cd6d48a18aaa16e3b Changes since version 1.2.1: * unix, windows: set non-block mode in uv_poll_init (Saúl Ibarra Corretgé) * doc: clarify which flags are supported in uv_fs_event_start (Saúl Ibarra Corretgé) * win,unix: move loop functions which have identical implementations (Andrius Bentkus) * doc: explain how the threadpool is allocated (Alex Mo) * doc: clarify uv_default_loop (Saúl Ibarra Corretgé) * unix: fix implicit declaration compiler warning (Ben Noordhuis) * unix: fix long line introduced in commit 94e628fa (Ben Noordhuis) * unix, win: add synchronous uv_get{addr,name}info (Saúl Ibarra Corretgé) * linux: fix epoll_pwait() regression with < 2.6.19 (Ben Noordhuis) * build: compile -D_GNU_SOURCE on linux (Ben Noordhuis) * build: use -fvisibility=hidden in autotools build (Ben Noordhuis) * fs, pipe: no trailing terminator in exact sized buffers (Andrius Bentkus) * style: rename buf to buffer and len to size for consistency (Andrius Bentkus) * test: fix test-spawn on MinGW32 (Luis Martinez de Bartolome) * win, pipe: fix assertion when destroying timer (Andrius Bentkus) * win, unix: add pipe_peername implementation (Andrius Bentkus) 2015.01.29, Version 0.10.33 (Stable), 7a2253d33ad8215a26c1b34f1952aee7242dd687 Changes since version 0.10.32: * linux: fix epoll_pwait() regression with < 2.6.19 (Ben Noordhuis) * test: back-port uv_loop_configure() test (Ben Noordhuis) 2015.01.15, Version 1.2.1 (Stable), 4ca78e989062a1099dc4b9ad182a98e8374134b1 Changes since version 1.2.0: * unix: remove unused dtrace file (Saúl Ibarra Corretgé) * test: skip TTY select test if /dev/tty can't be opened (Saúl Ibarra Corretgé) * doc: clarify the behavior of uv_tty_init (Saúl Ibarra Corretgé) * doc: clarify how uv_async_send behaves (Saúl Ibarra Corretgé) * build: make dist now generates a full tarball (Johan Bergström) * freebsd: make uv_exepath more resilient (Saúl Ibarra Corretgé) * unix: make setting the tty mode to the same value a no-op (Saúl Ibarra Corretgé) * win,tcp: support uv_try_write (Bert Belder) * test: enable test-tcp-try-write on windows (Bert Belder) * win,tty: support uv_try_write (Bert Belder) * unix: set non-block mode in uv_{pipe,tcp,udp}_open (Ben Noordhuis) 2015.01.06, Version 1.2.0 (Stable), 09f25b13cd149c7981108fc1a75611daf1277f83 Changes since version 1.1.0: * linux: fix epoll_pwait() sigmask size calculation (Ben Noordhuis) * tty: implement binary I/O terminal mode (Yuri D'Elia) * test: fix spawn test with autotools build (Ben Noordhuis) * test: skip ipv6 tests when ipv6 is not supported (Ben Noordhuis) * common: move STATIC_ASSERT to uv-common.h (Alexey Melnichuk) * win/thread: store thread handle in a TLS slot (Alexey Melnichuk) * unix: fix ttl, multicast ttl and loop options on IPv6 (Saúl Ibarra Corretgé) * linux: fix support for preadv/pwritev-less kernels (Ben Noordhuis) * unix: make uv_exepath(size=0) return UV_EINVAL (Ben Noordhuis) * darwin: fix uv_exepath(smallbuf) UV_EPERM error (Ben Noordhuis) * openbsd: fix uv_exepath(smallbuf) UV_EINVAL error (Ben Noordhuis) * linux: fix uv_exepath(size=1) UV_EINVAL error (Ben Noordhuis) * sunos: preemptively fix uv_exepath(size=1) (Ben Noordhuis) * win: fix and clarify comments in winapi.h (Bert Belder) * win: make available NtQueryDirectoryFile (Bert Belder) * win: add definitions for directory information types (Bert Belder) * win: use NtQueryDirectoryFile to implement uv_fs_scandir (Bert Belder) * unix: don't unlink unix socket on bind error (Ben Noordhuis) * build: fix bad comment in autogen.sh (Ben Noordhuis) * build: add AC_PROG_LIBTOOL to configure.ac (Ben Noordhuis) * test: skip udp_options6 if there no IPv6 support (Saúl Ibarra Corretgé) * win: add definitions for MUI errors mingw lacks (Bert Belder) * build: enable warnings in autotools build (Ben Noordhuis) * build: remove -Wno-dollar-in-identifier-extension (Ben Noordhuis) * build: move flags from Makefile.am to configure.ac (Ben Noordhuis) 2015.01.06, Version 0.10.32 (Stable), 378de30c59aef5fdb6d130fa5cfcb0a68fce571c Changes since version 0.10.31: * linux: fix epoll_pwait() sigmask size calculation (Ben Noordhuis) 2014.12.25, Version 1.1.0 (Stable), 9572f3e74a167f59a8017e57ca3ebe91ffd88e18 Changes since version 1.0.2: * test: test that closing a poll handle doesn't corrupt the stack (Bert Belder) * win: fix compilation of tests (Marc Schlaich) * Revert "win: keep a reference to AFD_POLL_INFO in cancel poll" (Bert Belder) * win: avoid stack corruption when closing a poll handle (Bert Belder) * test: fix test-fs-file-loop on Windows (Bert Belder) * test: fix test-cwd-and-chdir (Bert Belder) * doc: indicate what version uv_loop_configure was added on (Saúl Ibarra Corretgé) * doc: fix sphinx warning (Saúl Ibarra Corretgé) * test: skip spawn_setuid_setgid if we get EACCES (Saúl Ibarra Corretgé) * test: silence some Clang warnings (Saúl Ibarra Corretgé) * test: relax osx_select_many_fds (Saúl Ibarra Corretgé) * test: fix compilation warnings when building with Clang (Saúl Ibarra Corretgé) * win: fix autotools build of tests (Luis Lavena) * gitignore: ignore Visual Studio files (Marc Schlaich) * win: set fallback message if FormatMessage fails (Marc Schlaich) * win: fall back to default language in uv_dlerror (Marc Schlaich) * test: improve compatibility for dlerror test (Marc Schlaich) * test: check dlerror is "no error" in no error case (Marc Schlaich) * unix: change uv_cwd not to return a trailing slash (Saúl Ibarra Corretgé) * test: fix cwd_and_chdir test on Unix (Saúl Ibarra Corretgé) * test: add uv_cwd output to platform_output test (Saúl Ibarra Corretgé) * build: fix dragonflybsd autotools build (John Marino) * win: scandir use 'ls' for formatting long strings (Kenneth Perry) * build: remove clang and gcc_version gyp defines (Ben Noordhuis) * unix, windows: don't treat uv_run_mode as a bitmask (Saúl Ibarra Corretgé) * unix, windows: fix UV_RUN_ONCE mode if progress was made (Saúl Ibarra Corretgé) 2014.12.25, Version 0.10.31 (Stable), 4dbd27e2219069a6daa769fb37f98673b77b4261 Changes since version 0.10.30: * test: test that closing a poll handle doesn't corrupt the stack (Bert Belder) * win: fix compilation of tests (Marc Schlaich) * Revert "win: keep a reference to AFD_POLL_INFO in cancel poll" (Bert Belder) * win: avoid stack corruption when closing a poll handle (Bert Belder) * gitignore: ignore Visual Studio files (Marc Schlaich) * win: set fallback message if FormatMessage fails (Marc Schlaich) * win: fall back to default language in uv_dlerror (Marc Schlaich) * test: improve compatibility for dlerror test (Marc Schlaich) * test: check dlerror is "no error" in no error case (Marc Schlaich) * build: link against -pthread (Logan Rosen) * win: scandir use 'ls' for formatting long strings (Kenneth Perry) 2014.12.10, Version 1.0.2 (Stable), eec671f0059953505f9a3c9aeb7f9f31466dd7cd Changes since version 1.0.1: * linux: fix sigmask size arg in epoll_pwait() call (Ben Noordhuis) * linux: handle O_NONBLOCK != SOCK_NONBLOCK case (Helge Deller) * doc: fix spelling (Joey Geralnik) * unix, windows: fix typos in comments (Joey Geralnik) * test: canonicalize test runner path (Ben Noordhuis) * test: fix compilation warnings (Saúl Ibarra Corretgé) * test: skip tty test if detected width and height are 0 (Saúl Ibarra Corretgé) * doc: update README with IRC channel (Saúl Ibarra Corretgé) * Revert "unix: use cfmakeraw() for setting raw TTY mode" (Ben Noordhuis) * doc: document how to get result of uv_fs_mkdtemp (Tim Caswell) * unix: add flag for blocking SIGPROF during poll (Ben Noordhuis) * unix, windows: add uv_loop_configure() function (Ben Noordhuis) * win: keep a reference to AFD_POLL_INFO in cancel poll (Marc Schlaich) * test: raise fd limit for OSX select test (Saúl Ibarra Corretgé) * unix: remove overzealous assert in uv_read_stop (Saúl Ibarra Corretgé) * unix: reset the reading flag when a stream gets EOF (Saúl Ibarra Corretgé) * unix: stop reading if an error is produced (Saúl Ibarra Corretgé) * cleanup: remove all dead assignments (Maciej Małecki) * linux: return early if we have no interfaces (Maciej Małecki) * cleanup: remove a dead increment (Maciej Małecki) 2014.12.10, Version 0.10.30 (Stable), 5a63f5e9546dca482eeebc3054139b21f509f21f Changes since version 0.10.29: * linux: fix sigmask size arg in epoll_pwait() call (Ben Noordhuis) * linux: handle O_NONBLOCK != SOCK_NONBLOCK case (Helge Deller) * doc: update project links (Ben Noordhuis) * windows: fix compilation of tests (Marc Schlaich) * unix: add flag for blocking SIGPROF during poll (Ben Noordhuis) * unix, windows: add uv_loop_configure() function (Ben Noordhuis) * win: keep a reference to AFD_POLL_INFO in cancel poll (Marc Schlaich) 2014.11.27, Version 1.0.1 (Stable), 0a8e81374e861d425b56c45c8599595d848911d2 Changes since version 1.0.0: * readme: remove Rust from users (Elijah Andrews) * doc,build,include: update project links (Ben Noordhuis) * doc: fix typo: Strcutures -> Structures (Michael Ira Krufky) * unix: fix processing process handles queue (Saúl Ibarra Corretgé) * win: replace non-ansi characters in source file (Bert Belder) 2014.11.21, Version 1.0.0 (Stable), feb2a9e6947d892f449b2770c4090f7d8c88381b Changes since version 1.0.0-rc2: * doc: fix git/svn url for gyp repo in README (Emmanuel Odeke) * windows: fix fs_read with nbufs > 1 and offset (Unknown W. Brackets) * win: add missing IP_ADAPTER_UNICAST_ADDRESS_LH definition for MinGW (huxingyi) * doc: mention homebrew in README (Mikhail Mukovnikov) * doc: add learnuv workshop to README (Thorsten Lorenz) * doc: fix parameter name in uv_fs_access (Saúl Ibarra Corretgé) * unix: use cfmakeraw() for setting raw TTY mode (Yuri D'Elia) * win: fix uv_thread_self() (Alexis Campailla) * build: add x32 support to gyp build (Ben Noordhuis) * build: remove dtrace probes (Ben Noordhuis) * doc: fix link in misc.rst (Manos Nikolaidis) * mailmap: remove duplicated entries (Saúl Ibarra Corretgé) * gyp: fix comment regarding version info location (Saúl Ibarra Corretgé) 2014.10.21, Version 1.0.0-rc2 (Pre-release) Changes since version 1.0.0-rc1: * build: add missing fixtures to distribution tarball (Rob Adams) * doc: update references to current stable branch (Zachary Newman) * fs: fix readdir on empty directory (Fedor Indutny) * fs: rename uv_fs_readdir to uv_fs_scandir (Saúl Ibarra Corretgé) * doc: document uv_alloc_cb (Saúl Ibarra Corretgé) * doc: add migration guide from version 0.10 (Saúl Ibarra Corretgé) * build: add DragonFly BSD support in autotools (Robin Hahling) * doc: document missing stream related structures (Saúl Ibarra Corretgé) * doc: clarify uv_loop_t.data field lifetime (Saúl Ibarra Corretgé) * doc: add documentation for missing functions and structures (Saúl Ibarra Corretgé) * doc: fix punctuation and grammar in README (Jeff Widman) * windows: return libuv error codes in uv_poll_init() (cjihrig) * unix, windows: add uv_fs_access() (cjihrig) * windows: fix netmask detection (Alexis Campailla) * unix, windows: don't include null byte in uv_cwd size (Saúl Ibarra Corretgé) * unix, windows: add uv_thread_equal (Tomasz Kołodziejski) * windows: fix fs_write with nbufs > 1 and offset (Unknown W. Brackets) 2014.10.21, Version 0.10.29 (Stable), 2d728542d3790183417f8f122a110693cd85db14 Changes since version 0.10.28: * darwin: allocate enough space for select() hack (Fedor Indutny) * linux: try epoll_pwait if epoll_wait is missing (Michael Hudson-Doyle) * windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) 2014.09.18, Version 1.0.0-rc1 (Unstable), 0c28bbf7b42882853d1799ab96ff68b07f7f8d49 Changes since version 0.11.29: * windows: improve timer precision (Alexis Campailla) * build, gyp: set xcode flags (Recep ASLANTAS) * ignore: include m4 files which are created manually (Recep ASLANTAS) * build: add m4 for feature/flag-testing (Recep ASLANTAS) * ignore: ignore Xcode project and workspace files (Recep ASLANTAS) * unix: fix warnings about dollar symbol usage in identifiers (Recep ASLANTAS) * unix: fix warnings when loading functions with dlsym (Recep ASLANTAS) * linux: try epoll_pwait if epoll_wait is missing (Michael Hudson-Doyle) * test: add test for closing and recreating default loop (Saúl Ibarra Corretgé) * windows: properly close the default loop (Saúl Ibarra Corretgé) * version: add ability to specify a version suffix (Saúl Ibarra Corretgé) * doc: add API documentation (Saúl Ibarra Corretgé) * test: don't close connection on write error (Trevor Norris) * windows: further simplify the code for timers (Saúl Ibarra Corretgé) * gyp: remove UNLIMITED_SELECT from dependent define (Fedor Indutny) * darwin: allocate enough space for select() hack (Fedor Indutny) * unix, windows: don't allow a NULL callback on timers (Saúl Ibarra Corretgé) * windows: simplify code in uv_timer_again (Saúl Ibarra Corretgé) * test: use less requests on tcp-write-queue-order (Saúl Ibarra Corretgé) * unix: stop child process watcher after last one exits (Saúl Ibarra Corretgé) * unix: simplify how process handle queue is managed (Saúl Ibarra Corretgé) * windows: remove duplicated field (mattn) * core: add a reserved field to uv_handle_t and uv_req_t (Saúl Ibarra Corretgé) * windows: fix buffer leak after failed udp send (Bert Belder) * windows: make sure sockets and handles are reset on close (Saúl Ibarra Corretgé) * unix, windows: add uv_fileno (Saúl Ibarra Corretgé) * build: use same CFLAGS in autotools build as in gyp (Saúl Ibarra Corretgé) * build: remove unneeded define in uv.gyp (Saúl Ibarra Corretgé) * test: fix watcher_cross_stop on Windows (Saúl Ibarra Corretgé) * unix, windows: move includes for EAI constants (Saúl Ibarra Corretgé) * unix: fix exposing EAI_* glibc-isms (Saúl Ibarra Corretgé) * unix: fix tcp write after bad connect freezing (Andrius Bentkus) 2014.08.20, Version 0.11.29 (Unstable), 35451fed830807095bbae8ef981af004a4b9259e Changes since version 0.11.28: * windows: make uv_read_stop immediately stop reading (Jameson Nash) * windows: fix uv__getaddrinfo_translate_error (Alexis Campailla) * netbsd: fix build (Saúl Ibarra Corretgé) * unix, windows: add uv_recv_buffer_size and uv_send_buffer_size (Andrius Bentkus) * windows: add support for UNC paths on uv_spawn (Paul Goldsmith) * windows: replace use of inet_addr with uv_inet_pton (Saúl Ibarra Corretgé) * unix: replace some asserts with returning errors (Andrius Bentkus) * windows: use OpenBSD implementation for uv_fs_mkdtemp (Pavel Platto) * windows: fix GetNameInfoW error handling (Alexis Campailla) * fs: introduce uv_readdir_next() and report types (Fedor Indutny) * fs: extend reported types in uv_fs_readdir_next (Saúl Ibarra Corretgé) * unix: read on stream even when UV__POLLHUP set. (Julien Gilli) 2014.08.08, Version 0.11.28 (Unstable), fc9e2a0bc487b299c0cd3b2c9a23aeb554b5d8d1 Changes since version 0.11.27: * unix, windows: const-ify handle in uv_udp_getsockname (Rasmus Pedersen) * windows: use UV_ECANCELED for aborted TCP writes (Saúl Ibarra Corretgé) * windows: add more required environment variables (Jameson Nash) * windows: sort environment variables before calling CreateProcess (Jameson Nash) * unix, windows: move uv_loop_close out of assert (John Firebaugh) * windows: fix buffer overflow on uv__getnameinfo_work() (lilohuang) * windows: add uv_backend_timeout (Jameson Nash) * test: disable tcp_close_accept on Windows (Saúl Ibarra Corretgé) * windows: read the PATH env var of the child (Alex Crichton) * include: avoid using C++ 'template' reserved word (Iñaki Baz Castillo) * include: fix version number (Saúl Ibarra Corretgé) 2014.07.32, Version 0.11.27 (Unstable), ffe24f955032d060968ea0289af365006afed55e Changes since version 0.11.26: * unix, windows: use the same threadpool implementation (Saúl Ibarra Corretgé) * unix: use struct sockaddr_storage for target UDP addr (Saúl Ibarra Corretgé) * doc: add documentation to uv_udp_start_recv (Andrius Bentkus) * common: use common uv__count_bufs code (Andrius Bentkus) * unix, win: add send_queue_size and send_queue_count to uv_udp_t (Andrius Bentkus) * unix, win: add uv_udp_try_send (Andrius Bentkus) * unix: return UV_EAGAIN if uv_try_write cannot write any data (Saúl Ibarra Corretgé) * windows: fix compatibility with cygwin pipes (Jameson Nash) * windows: count queued bytes even if request completed immediately (Saúl Ibarra Corretgé) * windows: disable CRT debug handler on MinGW32 (Saúl Ibarra Corretgé) * windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) * unix: try to write immediately in uv_udp_send (Saúl Ibarra Corretgé) * unix: remove incorrect assert (Saúl Ibarra Corretgé) * openbsd: avoid requiring privileges for uv_resident_set_memory (Aaron Bieber) * unix: guarantee write queue cb execution order in streams (Andrius Bentkus) * img: add logo files (Saúl Ibarra Corretgé) * aix: improve AIX compatibility (Andrew Low) * windows: return bind error immediately when implicitly binding (Saúl Ibarra Corretgé) * windows: don't use atexit for cleaning up the threadpool (Saúl Ibarra Corretgé) * windows: destroy work queue elements when colsing a loop (Saúl Ibarra Corretgé) * unix, windows: add uv_fs_mkdtemp (Pavel Platto) * build: handle platforms without multiprocessing.synchronize (Saúl Ibarra Corretgé) * windows: change GENERIC_ALL to GENERIC_WRITE in fs__create_junction (Tony Kelman) * windows: relay TCP bind errors via ipc (Alexis Campailla) 2014.07.32, Version 0.10.28 (Stable), 9c14b616f5fb84bfd7d45707bab4bbb85894443e Changes since version 0.10.27: * windows: fix handling closed socket while poll handle is closing (Saúl Ibarra Corretgé) * unix: return system error on EAI_SYSTEM (Saúl Ibarra Corretgé) * unix: fix bogus structure field name (Saúl Ibarra Corretgé) * darwin: invoke `mach_timebase_info` only once (Fedor Indutny) 2014.06.28, Version 0.11.26 (Unstable), 115281a1058c4034d5c5ccedacb667fe3f6327ea Changes since version 0.11.25: * windows: add VT100 codes ?25l and ?25h (JD Ballard) * windows: add invert ANSI (7 / 27) emulation (JD Ballard) * unix: fix handling error on UDP socket creation (Saúl Ibarra Corretgé) * unix, windows: getnameinfo implementation (Rasmus Pedersen) * heap: fix `heap_remove()` (Fedor Indutny) * unix, windows: fix parsing scoped IPv6 addresses (Saúl Ibarra Corretgé) * windows: fix handling closed socket while poll handle is closing (Saúl Ibarra Corretgé) * thread: barrier functions (Ben Noordhuis) * windows: fix PYTHON environment variable usage (Jay Satiro) * unix, windows: return system error on EAI_SYSTEM (Saúl Ibarra Corretgé) * windows: fix handling closed socket while poll handle is closing (Saúl Ibarra Corretgé) * unix: don't run i/o callbacks after prepare callbacks (Saúl Ibarra Corretgé) * windows: add tty unicode support for input (Peter Atashian) * header: introduce `uv_loop_size()` (Andrius Bentkus) * darwin: invoke `mach_timebase_info` only once (Fedor Indutny) 2014.05.02, Version 0.11.25 (Unstable), 2acd544cff7142e06aa3b09ec64b4a33dd9ab996 Changes since version 0.11.24: * osx: pass const handle pointer to uv___stream_fd (Chernyshev Viacheslav) * unix, windows: pass const handle ptr to uv_tcp_get*name (Chernyshev Viacheslav) * common: pass const sockaddr ptr to uv_ip*_name (Chernyshev Viacheslav) * unix, windows: validate flags on uv_udp|tcp_bind (Saúl Ibarra Corretgé) * unix: handle case when addr is not initialized after recvmsg (Saúl Ibarra Corretgé) * unix, windows: uv_now constness (Rasmus Pedersen) 2014.04.15, Version 0.11.24 (Unstable), ed948c29f6e8c290f79325a6f0bc9ef35bcde644 Changes since version 0.11.23: * linux: reduce file descriptor count of async pipe (Ben Noordhuis) * sunos: support IPv6 qualified link-local addresses (Saúl Ibarra Corretgé) * windows: fix opening of read-only stdin pipes (Alexis Campailla) * windows: Fix an infinite loop in uv_spawn (Alex Crichton) * windows: fix console signal handler refcount (李港平) * inet: allow scopeid in uv_inet_pton (Fedor Indutny) 2014.04.07, Version 0.11.23 (Unstable), e54de537efcacd593f36fcaaf8b4cb9e64313275 Changes since version 0.11.22: * fs: avoid using readv/writev where possible (Fedor Indutny) * mingw: fix build with autotools (Saúl Ibarra Corretgé) * bsd: support IPv6 qualified link-local addresses (Saúl Ibarra Corretgé) * unix: add UV_HANDLE_IPV6 flag to tcp and udp handles (Saúl Ibarra Corretgé) * unix, windows: do not set SO_REUSEADDR by default on udp (Saúl Ibarra Corretgé) * windows: fix check in uv_tty_endgame() (Maks Naumov) * unix, windows: add IPv6 support for uv_udp_multicast_interface (Saúl Ibarra Corretgé) * unix: fallback to blocking writes if reopening a tty fails (Saúl Ibarra Corretgé) * unix: fix handling uv__open_cloexec failure (Saúl Ibarra Corretgé) * unix, windows: add IPv6 support to uv_udp_set_membership (Saúl Ibarra Corretgé) * unix, windows: removed unused status parameter (Saúl Ibarra Corretgé) * android: add support of ifaddrs in android (Javier Hernández) * build: fix SunOS and AIX build with autotools (Saúl Ibarra Corretgé) * build: freebsd link with libelf if dtrace enabled (Saúl Ibarra Corretgé) * stream: do not leak `alloc_cb` buffers on error (Fedor Indutny) * unix: fix setting written size on uv_wd (Saúl Ibarra Corretgé) 2014.03.11, Version 0.11.22 (Unstable), cd0c19b1d3c56acf0ade7687006e12e75fbda36d Changes since version 0.11.21: * unix, windows: map ERANGE errno (Saúl Ibarra Corretgé) * unix, windows: make uv_cwd be consistent with uv_exepath (Saúl Ibarra Corretgé) * process: remove debug perror() prints (Fedor Indutny) * windows: fall back for volume info query (Isaiah Norton) * pipe: allow queueing pending handles (Fedor Indutny) * windows: fix winsock status codes for address errors (Raul Martins) * windows: Remove unused variable from uv__pipe_insert_pending_socket (David Capello) * unix: workaround broken pthread_sigmask on Android (Paul Tan) * error: add ENXIO for O_NONBLOCK FIFO open() (Fedor Indutny) * freebsd: use accept4, introduced in version 10 (Saúl Ibarra Corretgé) * windows: fix warnings of MinGW -Wall -O3 (StarWing) * openbsd, osx: fix compilation warning on scandir (Saúl Ibarra Corretgé) * linux: always deregister closing fds from epoll (Geoffry Song) * unix: reopen tty as /dev/tty (Saúl Ibarra Corretgé) * kqueue: invalidate fd in uv_fs_event_t (Fedor Indutny) 2014.02.28, Version 0.11.21 (Unstable), 3ef958158ae1019e027ebaa93114160099db5206 Changes since version 0.11.20: * unix: fix uv_fs_write when using an empty buffer (Saúl Ibarra Corretgé) * unix, windows: add assertion in uv_loop_delete (Saúl Ibarra Corretgé) 2014.02.27, Version 0.11.20 (Unstable), 88355e081b51c69ee1e2b6b0015a4e3d38bd0579 Changes since version 0.11.19: * stream: start thread after assignments (Oguz Bastemur) * fs: `uv__cloexec()` opened fd (Fedor Indutny) * gyp: qualify `library` variable (Fedor Indutny) * unix, win: add uv_udp_set_multicast_interface() (Austin Foxley) * unix: fix uv_tcp_nodelay return value in case of error (Saúl Ibarra Corretgé) * unix: call setgoups before calling setuid/setgid (Saúl Ibarra Corretgé) * include: mark close_cb field as private (Saúl Ibarra Corretgé) * unix, windows: map EFBIG errno (Saúl Ibarra Corretgé) * unix: correct error when calling uv_shutdown twice (Keno Fischer) * windows: fix building on MinGW (Alex Crichton) * windows: always initialize uv_process_t (Alex Crichton) * include: expose libuv version in header files (Saúl Ibarra Corretgé) * fs: vectored IO API for filesystem read/write (Benjamin Saunders) * windows: freeze in uv_tcp_endgame (Alexis Campailla) * sunos: handle rearm errors (Fedor Indutny) * unix: use a heap for timers (Ben Noordhuis) * linux: always deregister closing fds from epoll (Geoffry Song) * linux: include grp.h for setgroups() (William Light) * unix, windows: add uv_loop_init and uv_loop_close (Saúl Ibarra Corretgé) * unix, windows: add uv_getrusage() function (Oleg Efimov) * win: minor error handle fix to uv_pipe_write_impl (Rasmus Pedersen) * heap: fix node removal (Keno Fischer) * win: fix C99/C++ comment (Rasmus Pedersen) * fs: vectored IO API for filesystem read/write (Benjamin Saunders) * unix, windows: add uv_pipe_getsockname (Saúl Ibarra Corretgé) * unix, windows: map ENOPROTOOPT errno (Saúl Ibarra Corretgé) * errno: add ETXTBSY (Fedor Indutny) * fsevent: rename filename field to path (Saúl Ibarra Corretgé) * unix, windows: add uv_fs_event_getpath (Saúl Ibarra Corretgé) * unix, windows: add uv_fs_poll_getpath (Saúl Ibarra Corretgé) * unix, windows: map ERANGE errno (Saúl Ibarra Corretgé) * unix, windows: set required size on UV_ENOBUFS (Saúl Ibarra Corretgé) * unix, windows: clarify what uv_stream_set_blocking does (Saúl Ibarra Corretgé) * fs: use preadv on Linux if available (Brian White) 2014.01.30, Version 0.11.19 (Unstable), 336a1825309744f920230ec3e427e78571772347 Changes since version 0.11.18: * linux: move sscanf() out of the assert() (Trevor Norris) * linux: fix C99/C++ comment (Fedor Indutny) 2014.05.02, Version 0.10.27 (Stable), 6e24ce23b1e7576059f85a608eca13b766458a01 Changes since version 0.10.26: * windows: fix console signal handler refcount (Saúl Ibarra Corretgé) * win: always leave crit section in get_proc_title (Fedor Indutny) 2014.04.07, Version 0.10.26 (Stable), d864907611c25ec986c5e77d4d6d6dee88f26926 Changes since version 0.10.25: * process: don't close stdio fds during spawn (Tonis Tiigi) * build, windows: do not fail on Windows SDK Prompt (Marc Schlaich) * build, windows: fix x64 configuration issue (Marc Schlaich) * win: fix buffer leak on error in pipe.c (Fedor Indutny) * kqueue: invalidate fd in uv_fs_event_t (Fedor Indutny) * linux: always deregister closing fds from epoll (Geoffry Song) * error: add ENXIO for O_NONBLOCK FIFO open() (Fedor Indutny) 2014.02.19, Version 0.10.25 (Stable), d778dc588507588b12b9f9d2905078db542ed751 Changes since version 0.10.24: * stream: start thread after assignments (Oguz Bastemur) * unix: correct error when calling uv_shutdown twice (Saúl Ibarra Corretgé) 2014.01.30, Version 0.10.24 (Stable), aecd296b6bce9b40f06a61c5c94e43d45ac7308a Changes since version 0.10.23: * linux: move sscanf() out of the assert() (Trevor Norris) * linux: fix C99/C++ comment (Fedor Indutny) 2014.01.23, Version 0.11.18 (Unstable), d47962e9d93d4a55a9984623feaf546406c9cdbb Changes since version 0.11.17: * osx: Fix a possible segfault in uv__io_poll (Alex Crichton) * windows: improved handling of invalid FDs (Alexis Campailla) * doc: adding ARCHS flag to OS X build command (Nathan Sweet) * tcp: reveal bind-time errors before listen (Alexis Campailla) * tcp: uv_tcp_dualstack() (Fedor Indutny) * linux: relax assumption on /proc/stat parsing (Luca Bruno) * openbsd: fix obvious bug in uv_cpu_info (Fedor Indutny) * process: close stdio after dup2'ing it (Fedor Indutny) * linux: move sscanf() out of the assert() (Trevor Norris) 2014.01.23, Version 0.10.23 (Stable), dbd218e699fec8be311d85e4788be9e28ae884f8 Changes since version 0.10.22: * linux: relax assumption on /proc/stat parsing (Luca Bruno) * openbsd: fix obvious bug in uv_cpu_info (Fedor Indutny) * process: close stdio after dup2'ing it (Fedor Indutny) 2014.01.08, Version 0.10.22 (Stable), f526c90eeff271d9323a9107b9a64a4671fd3103 Changes since version 0.10.21: * windows: avoid assertion failure when pipe server is closed (Bert Belder) 2013.12.32, Version 0.11.17 (Unstable), 589c224d4c2e79fec65db01d361948f1e4976858 Changes since version 0.11.16: * stream: allow multiple buffers for uv_try_write (Fedor Indutny) * unix: fix a possible memory leak in uv_fs_readdir (Alex Crichton) * unix, windows: add uv_loop_alive() function (Sam Roberts) * windows: avoid assertion failure when pipe server is closed (Bert Belder) * osx: Fix a possible segfault in uv__io_poll (Alex Crichton) * stream: fix uv__stream_osx_select (Fedor Indutny) 2013.12.14, Version 0.11.16 (Unstable), ae0ed8c49d0d313c935c22077511148b6e8408a4 Changes since version 0.11.15: * fsevents: remove kFSEventStreamCreateFlagNoDefer polyfill (ci-innoq) * libuv: add more getaddrinfo errors (Steven Kabbes) * unix: fix accept() EMFILE error handling (Ben Noordhuis) * linux: fix up SO_REUSEPORT back-port (Ben Noordhuis) * fsevents: fix subfolder check (Fedor Indutny) * fsevents: fix invalid memory access (huxingyi) * windows/timer: fix uv_hrtime discontinuity (Bert Belder) * unix: fix various memory leaks and undef behavior (Fedor Indutny) * unix, windows: always update loop time (Saúl Ibarra Corretgé) * windows: translate system errors in uv_spawn (Alexis Campailla) * windows: uv_spawn code refactor (Alexis Campailla) * unix, windows: detect errors in uv_ip4/6_addr (Yorkie) * stream: introduce uv_try_write(...) (Fedor Indutny) 2013.12.13, Version 0.10.20 (Stable), 04141464dd0fba90ace9aa6f7003ce139b888a40 Changes since version 0.10.19: * linux: fix up SO_REUSEPORT back-port (Ben Noordhuis) * fs-event: fix invalid memory access (huxingyi) 2013.11.21, Version 0.11.15 (Unstable), bfe645ed7e99ca5670d9279ad472b604c129d2e5 Changes since version 0.11.14: * fsevents: report errors to user (Fedor Indutny) * include: UV_FS_EVENT_RECURSIVE is a flag (Fedor Indutny) * linux: use CLOCK_MONOTONIC_COARSE if available (Ben Noordhuis) * build: make systemtap probes work with gyp build (Ben Noordhuis) * unix: update events from pevents between polls (Fedor Indutny) * fsevents: support japaneese characters in path (Chris Bank) * linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) * queue: strengthen type checks (Ben Noordhuis) * include: remove uv_strlcat() and uv_strlcpy() (Ben Noordhuis) * build: fix windows smp build with gyp (Geert Jansen) * unix: return exec errors from uv_spawn, not async (Alex Crichton) * fsevents: use native character encoding file paths (Ben Noordhuis) * linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis) * windows: use _snwprintf(), not swprintf() (Ben Noordhuis) * fsevents: use FlagNoDefer for FSEventStreamCreate (Fedor Indutny) * unix: fix reopened fd bug (Fedor Indutny) * core: fix fake watcher list and count preservation (Fedor Indutny) * unix: set close-on-exec flag on received fds (Ben Noordhuis) * netbsd, openbsd: enable futimes() wrapper (Ben Noordhuis) * unix: nicer error message when kqueue() fails (Ben Noordhuis) * samples: add socks5 proxy sample application (Ben Noordhuis) 2013.11.13, Version 0.10.19 (Stable), 33959f7524090b8d2c6c41e2400ca77e31755059 Changes since version 0.10.18: * darwin: avoid calling GetCurrentProcess (Fedor Indutny) * unix: update events from pevents between polls (Fedor Indutny) * fsevents: support japaneese characters in path (Chris Bank) * linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) * build: fix windows smp build with gyp (Geert Jansen) * linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis) * unix: fix reopened fd bug (Fedor Indutny) * core: fix fake watcher list and count preservation (Fedor Indutny) 2013.10.30, Version 0.11.14 (Unstable), d7a6482f45c1b4eb4a853dbe1a9ce8090a35633a Changes since version 0.11.13: * darwin: create fsevents thread on demand (Ben Noordhuis) * fsevents: FSEvents is most likely not thread-safe (Fedor Indutny) * fsevents: use shared FSEventStream (Fedor Indutny) * windows: make uv_fs_chmod() report errors correctly (Bert Belder) * windows: make uv_shutdown() for write-only pipes work (Bert Belder) * windows/fs: wrap multi-statement macros in do..while block (Bert Belder) * windows/fs: make uv_fs_open() report EINVAL correctly (Bert Belder) * windows/fs: handle _open_osfhandle() failure correctly (Bert Belder) * windows/fs: wrap multi-statement macros in do..while block (Bert Belder) * windows/fs: make uv_fs_open() report EINVAL correctly (Bert Belder) * windows/fs: handle _open_osfhandle() failure correctly (Bert Belder) * build: clarify instructions for Windows (Brian Kaisner) * build: remove GCC_WARN_ABOUT_MISSING_NEWLINE (Ben Noordhuis) * darwin: fix 10.6 build error in fsevents.c (Ben Noordhuis) * windows: run close callbacks after polling for i/o (Saúl Ibarra Corretgé) * include: clarify uv_tcp_bind() behavior (Ben Noordhuis) * include: clean up includes in uv.h (Ben Noordhuis) * include: remove UV_IO_PRIVATE_FIELDS macro (Ben Noordhuis) * include: fix typo in comment in uv.h (Ben Noordhuis) * include: update uv_is_active() documentation (Ben Noordhuis) * include: make uv_process_options_t.cwd const (Ben Noordhuis) * unix: wrap long lines at 80 columns (Ben Noordhuis) * unix, windows: make uv_is_*() always return 0 or 1 (Ben Noordhuis) * bench: measure total/init/dispatch/cleanup times (Ben Noordhuis) * build: use -pthread on sunos (Timothy J. Fontaine) * windows: remove duplicate check in stream.c (Ben Noordhuis) * unix: sanity-check fds before closing (Ben Noordhuis) * unix: remove uv__pipe_accept() (Ben Noordhuis) * unix: fix uv_spawn() NULL pointer deref on ENOMEM (Ben Noordhuis) * unix: don't close inherited fds on uv_spawn() fail (Ben Noordhuis) * unix: revert recent FSEvent changes (Ben Noordhuis) * fsevents: fix clever rescheduling (Fedor Indutny) * linux: ignore fractional time in uv_uptime() (Ben Noordhuis) * unix: fix SIGCHLD waitpid() race in process.c (Ben Noordhuis) * unix, windows: add uv_fs_event_start/stop functions (Saúl Ibarra Corretgé) * unix: fix non-synchronized access in signal.c (Ben Noordhuis) * unix: add atomic-ops.h (Ben Noordhuis) * unix: add spinlock.h (Ben Noordhuis) * unix: clean up uv_tty_set_mode() a little (Ben Noordhuis) * unix: make uv_tty_reset_mode() async signal-safe (Ben Noordhuis) * include: add E2BIG status code mapping (Ben Noordhuis) * windows: fix duplicate case build error (Ben Noordhuis) * windows: remove unneeded check (Saúl Ibarra Corretgé) * include: document pipe path truncation behavior (Ben Noordhuis) * fsevents: increase stack size for OSX 10.9 (Fedor Indutny) * windows: _snprintf expected wrong parameter type in string (Maks Naumov) * windows: "else" keyword is missing (Maks Naumov) * windows: incorrect check for SOCKET_ERROR (Maks Naumov) * windows: add stdlib.h to satisfy reference to abort (Sean Farrell) * build: fix check target for mingw (Sean Farrell) * unix: move uv_shutdown() assertion (Keno Fischer) * darwin: avoid calling GetCurrentProcess (Fedor Indutny) 2013.10.19, Version 0.10.18 (Stable), 9ec52963b585e822e87bdc5de28d6143aff0d2e5 Changes since version 0.10.17: * unix: fix uv_spawn() NULL pointer deref on ENOMEM (Ben Noordhuis) * unix: don't close inherited fds on uv_spawn() fail (Ben Noordhuis) * unix: revert recent FSEvent changes (Ben Noordhuis) * unix: fix non-synchronized access in signal.c (Ben Noordhuis) 2013.09.25, Version 0.10.17 (Stable), 9670e0a93540c2f0d86c84a375f2303383c11e7e Changes since version 0.10.16: * build: remove GCC_WARN_ABOUT_MISSING_NEWLINE (Ben Noordhuis) * darwin: fix 10.6 build error in fsevents.c (Ben Noordhuis) 2013.09.06, Version 0.10.16 (Stable), 2bce230d81f4853a23662cbeb26fe98010b1084b Changes since version 0.10.15: * windows: make uv_shutdown() for write-only pipes work (Bert Belder) * windows: make uv_fs_open() report EINVAL when invalid arguments are passed (Bert Belder) * windows: make uv_fs_open() report _open_osfhandle() failure correctly (Bert Belder) * windows: make uv_fs_chmod() report errors correctly (Bert Belder) * windows: wrap multi-statement macros in do..while block (Bert Belder) 2013.09.05, Version 0.11.13 (Unstable), f5b6db6c1d7f93d28281207fd47c3841c9a9792e Changes since version 0.11.12: * unix: define _GNU_SOURCE, exposes glibc-isms (Ben Noordhuis) * windows: check for nonconforming swprintf arguments (Brent Cook) * build: include internal headers in source list (Brent Cook) * include: merge uv_tcp_bind and uv_tcp_bind6 (Ben Noordhuis) * include: merge uv_tcp_connect and uv_tcp_connect6 (Ben Noordhuis) * include: merge uv_udp_bind and uv_udp_bind6 (Ben Noordhuis) * include: merge uv_udp_send and uv_udp_send6 (Ben Noordhuis) 2013.09.03, Version 0.11.12 (Unstable), 82d01d5f6780d178f5176a01425ec297583c0811 Changes since version 0.11.11: * test: fix epoll_wait() usage in test-embed.c (Ben Noordhuis) * include: uv_alloc_cb now takes uv_buf_t* (Ben Noordhuis) * include: uv_read{2}_cb now takes const uv_buf_t* (Ben Noordhuis) * include: uv_ip[46]_addr now takes sockaddr_in* (Ben Noordhuis) * include: uv_tcp_bind{6} now takes sockaddr_in* (Ben Noordhuis) * include: uv_tcp_connect{6} now takes sockaddr_in* (Ben Noordhuis) * include: uv_udp_recv_cb now takes const uv_buf_t* (Ben Noordhuis) * include: uv_udp_bind{6} now takes sockaddr_in* (Ben Noordhuis) * include: uv_udp_send{6} now takes sockaddr_in* (Ben Noordhuis) * include: uv_spawn takes const uv_process_options_t* (Ben Noordhuis) * include: make uv_write{2} const correct (Ben Noordhuis) * windows: fix flags assignment in uv_fs_readdir() (Ben Noordhuis) * windows: fix stray comments (Ben Noordhuis) * windows: remove unused is_path_dir() function (Ben Noordhuis) 2013.08.30, Version 0.11.11 (Unstable), ba876d53539ed0427c52039012419cd9374c6f0d Changes since version 0.11.10: * unix, windows: add thread-local storage API (Ben Noordhuis) * linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) * darwin: fix 10.6 build error in fsevents.c (Ben Noordhuis) * windows: make uv_shutdown() for write-only pipes work (Bert Belder) * include: update uv_udp_open() / uv_udp_bind() docs (Ben Noordhuis) * unix: req queue must be empty when destroying loop (Ben Noordhuis) * unix: move loop functions from core.c to loop.c (Ben Noordhuis) * darwin: remove CoreFoundation dependency (Ben Noordhuis) * windows: make autotools build system work with mingw (Keno Fischer) * windows: fix mingw build (Alex Crichton) * windows: tweak Makefile.mingw for easier usage (Alex Crichton) * build: remove _GNU_SOURCE macro definition (Ben Noordhuis) 2013.08.25, Version 0.11.10 (Unstable), 742dadcb7154cc7bb89c0c228a223b767a36cf0d * windows: Re-implement uv_fs_stat. The st_ctime field now contains the change time, not the creation time, like on unix systems. st_dev, st_ino, st_blocks and st_blksize are now also filled out. (Bert Belder) * linux: fix setsockopt(SO_REUSEPORT) error handling (Ben Noordhuis) * windows: report uv_process_t exit code correctly (Bert Belder) * windows: make uv_fs_chmod() report errors correctly (Bert Belder) * windows: make some more NT apis available for libuv's internal use (Bert Belder) * windows: squelch some compiler warnings (Bert Belder) 2013.08.24, Version 0.11.9 (Unstable), a2d29b5b068cbac93dc16138fb30a74e2669daad Changes since version 0.11.8: * fsevents: share FSEventStream between multiple FS watchers, which removes a limit on the maximum number of file watchers that can be created on OS X. (Fedor Indutny) * process: the `exit_status` parameter for a uv_process_t's exit callback now is an int64_t, and no longer an int. (Bert Belder) * process: make uv_spawn() return some types of errors immediately on windows, instead of passing the error code the the exit callback. This brings it on par with libuv's behavior on unix. (Bert Belder) 2013.08.24, Version 0.10.15 (Stable), 221078a8fdd9b853c6b557b3d9a5dd744b4fdd6b Changes since version 0.10.14: * fsevents: create FSEvents thread on demand (Ben Noordhuis) * fsevents: use a single thread for interacting with FSEvents, because it's not thread-safe. (Fedor Indutny) * fsevents: share FSEventStream between multiple FS watchers, which removes a limit on the maximum number of file watchers that can be created on OS X. (Fedor Indutny) 2013.08.22, Version 0.11.8 (Unstable), a5260462db80ab0deab6b9e6a8991dd8f5a9a2f8 Changes since version 0.11.7: * unix: fix missing return value warning in stream.c (Ben Noordhuis) * build: serial-tests was added in automake v1.12 (Ben Noordhuis) * windows: fix uninitialized local variable warning (Ben Noordhuis) * windows: fix missing return value warning (Ben Noordhuis) * build: fix string comparisons in autogen.sh (Ben Noordhuis) * windows: move INLINE macro, remove UNUSED (Ben Noordhuis) * unix: clean up __attribute__((quux)) usage (Ben Noordhuis) * sunos: remove futimes() macro (Ben Noordhuis) * unix: fix uv__signal_unlock() prototype (Ben Noordhuis) * unix, windows: allow NULL async callback (Ben Noordhuis) * build: apply dtrace -G to all object files (Timothy J. Fontaine) * darwin: fix indentation in uv__hrtime() (Ben Noordhuis) * darwin: create fsevents thread on demand (Ben Noordhuis) * darwin: reduce fsevents thread stack size (Ben Noordhuis) * darwin: call pthread_setname_np() if available (Ben Noordhuis) * build: fix automake serial-tests check again (Ben Noordhuis) * unix: retry waitpid() on EINTR (Ben Noordhuis) * darwin: fix ios build error (Ben Noordhuis) * darwin: fix ios compiler warning (Ben Noordhuis) * test: simplify test-ip6-addr.c (Ben Noordhuis) * unix, windows: fix ipv6 link-local address parsing (Ben Noordhuis) * fsevents: FSEvents is most likely not thread-safe (Fedor Indutny) * windows: omit stdint.h, fix msvc 2008 build error (Ben Noordhuis) 2013.08.22, Version 0.10.14 (Stable), 15d64132151c18b26346afa892444b95e2addad0 Changes since version 0.10.13: * unix: retry waitpid() on EINTR (Ben Noordhuis) 2013.08.07, Version 0.11.7 (Unstable), 3cad361f8776f70941b39d65bd9426bcb1aa817b Changes since version 0.11.6: * unix, windows: fix uv_fs_chown() function prototype (Ben Noordhuis) * unix, windows: remove unused variables (Brian White) * test: fix signed/unsigned comparison warnings (Ben Noordhuis) * build: dtrace shouldn't break out of tree builds (Timothy J. Fontaine) * unix, windows: don't read/recv if buf.len==0 (Ben Noordhuis) * build: add mingw makefile (Ben Noordhuis) * unix, windows: add MAC to uv_interface_addresses() (Brian White) * build: enable AM_INIT_AUTOMAKE([subdir-objects]) (Ben Noordhuis) * unix, windows: make buf arg to uv_fs_write const (Ben Noordhuis) * sunos: fix build breakage introduced in e3a657c (Ben Noordhuis) * aix: fix build breakage introduced in 3ee4d3f (Ben Noordhuis) * windows: fix mingw32 build, define JOB_OBJECT_XXX (Yasuhiro Matsumoto) * windows: fix mingw32 build, include limits.h (Yasuhiro Matsumoto) * test: replace sprintf() with snprintf() (Ben Noordhuis) * test: replace strcpy() with strncpy() (Ben Noordhuis) * openbsd: fix uv_ip6_addr() unused variable warnings (Ben Noordhuis) * openbsd: fix dlerror() const correctness warning (Ben Noordhuis) * openbsd: fix uv_fs_sendfile() unused variable warnings (Ben Noordhuis) * build: disable parallel automake tests (Ben Noordhuis) * test: add windows-only snprintf() function (Ben Noordhuis) * build: add automake serial-tests version check (Ben Noordhuis) 2013.07.26, Version 0.10.13 (Stable), 381312e1fe6fecbabc943ccd56f0e7d114b3d064 Changes since version 0.10.12: * unix, windows: fix uv_fs_chown() function prototype (Ben Noordhuis) 2013.07.21, Version 0.11.6 (Unstable), 6645b93273e0553d23823c576573b82b129bf28c Changes since version 0.11.5: * test: open stdout fd in write-only mode (Ben Noordhuis) * windows: uv_spawn shouldn't reject reparse points (Bert Belder) * windows: use WSAGetLastError(), not errno (Ben Noordhuis) * build: darwin: disable -fstrict-aliasing warnings (Ben Noordhuis) * test: fix signed/unsigned compiler warning (Ben Noordhuis) * test: add 'start timer from check handle' test (Ben Noordhuis) * build: `all` now builds static and dynamic lib (Ben Noordhuis) * unix, windows: add extra fields to uv_stat_t (Saúl Ibarra Corretgé) * build: add install target to the makefile (Navaneeth Kedaram Nambiathan) * build: switch to autotools (Ben Noordhuis) * build: use AM_PROG_AR conditionally (Ben Noordhuis) * test: fix fs_fstat test on sunos (Ben Noordhuis) * test: fix fs_chown when running as root (Ben Noordhuis) * test: fix spawn_setgid_fails and spawn_setuid_fails (Ben Noordhuis) * build: use AM_SILENT_RULES conditionally (Ben Noordhuis) * build: add DTrace detection for autotools (Timothy J. Fontaine) * linux,darwin,win: link-local IPv6 addresses (Miroslav Bajtoš) * unix: fix build when !defined(PTHREAD_MUTEX_ERRORCHECK) (Ben Noordhuis) * unix, windows: return error codes directly (Ben Noordhuis) 2013.07.10, Version 0.10.12 (Stable), 58a46221bba726746887a661a9f36fe9ff204209 Changes since version 0.10.11: * linux: add support for MIPS (Andrei Sedoi) * windows: uv_spawn shouldn't reject reparse points (Bert Belder) * windows: use WSAGetLastError(), not errno (Ben Noordhuis) * build: darwin: disable -fstrict-aliasing warnings (Ben Noordhuis) * build: `all` now builds static and dynamic lib (Ben Noordhuis) * unix: fix build when !defined(PTHREAD_MUTEX_ERRORCHECK) (Ben Noordhuis) 2013.06.27, Version 0.11.5 (Unstable), e3c63ff1627a14e96f54c1c62b0d68b446d8425b Changes since version 0.11.4: * build: remove CSTDFLAG, use only CFLAGS (Ben Noordhuis) * unix: support for android builds (Linus Mårtensson) * unix: avoid extra read, short-circuit on POLLHUP (Ben Noordhuis) * uv: support android libuv standalone build (Linus Mårtensson) * src: make queue.h c++ compatible (Ben Noordhuis) * unix: s/ngx-queue.h/queue.h/ in checksparse.sh (Ben Noordhuis) * unix: unconditionally stop handle on close (Ben Noordhuis) * freebsd: don't enable dtrace if it's not available (Brian White) * build: make HAVE_DTRACE=0 should disable dtrace (Timothy J. Fontaine) * unix: remove overzealous assert (Ben Noordhuis) * unix: remove unused function uv_fatal_error() (Ben Noordhuis) * unix, windows: clean up uv_thread_create() (Ben Noordhuis) * queue: fix pointer truncation on LLP64 platforms (Bert Belder) * build: set OS=="android" for android builds (Linus Mårtensson) * windows: don't use uppercase in include filename (Ben Noordhuis) * stream: add an API to make streams do blocking writes (Henry Rawas) * windows: use WSAGetLastError(), not errno (Ben Noordhuis) 2013.06.13, Version 0.10.11 (Stable), c3b75406a66a10222a589cb173e8f469e9665c7e Changes since version 0.10.10: * unix: unconditionally stop handle on close (Ben Noordhuis) * freebsd: don't enable dtrace if it's not available (Brian White) * build: make HAVE_DTRACE=0 should disable dtrace (Timothy J. Fontaine) * unix: remove overzealous assert (Ben Noordhuis) * unix: clear UV_STREAM_SHUTTING after shutdown() (Ben Noordhuis) * unix: fix busy loop, write if POLLERR or POLLHUP (Ben Noordhuis) 2013.06.05, Version 0.10.10 (Stable), 0d95a88bd35fce93863c57a460be613aea34d2c5 Changes since version 0.10.9: * include: document uv_update_time() and uv_now() (Ben Noordhuis) * linux: fix cpu model parsing on newer arm kernels (Ben Noordhuis) * linux: fix a memory leak in uv_cpu_info() error path (Ben Noordhuis) * linux: don't ignore out-of-memory errors in uv_cpu_info() (Ben Noordhuis) * unix, windows: move uv_now() to uv-common.c (Ben Noordhuis) * test: fix a compilation problem in test-osx-select.c that was caused by the use of c-style comments (Bert Belder) * darwin: use uv_fs_sendfile() use the sendfile api correctly (Wynn Wilkes) 2013.05.30, Version 0.11.4 (Unstable), e43e5b3d954a0989db5588aa110e1fe4fe6e0219 Changes since version 0.11.3: * windows: make uv_spawn not fail when the libuv embedding application is run under external job control (Bert Belder) * darwin: assume CFRunLoopStop() isn't thread-safe, fixing a race condition when stopping the 'stdin select hack' thread (Fedor Indutny) * win: fix UV_EALREADY not being reported correctly to the libuv user in some cases (Bert Belder) * darwin: make the uv__cf_loop_runner and uv__cf_loop_cb functions static (Ben Noordhuis) * darwin: task_info() cannot fail (Ben Noordhuis) * unix: add error mapping for ENETDOWN (Ben Noordhuis) * unix: implicitly signal write errors to the libuv user (Ben Noordhuis) * unix: fix assertion error on signal pipe overflow (Bert Belder) * unix: turn off POLLOUT after stream connect (Ben Noordhuis) * unix: fix stream refcounting buglet (Ben Noordhuis) * unix: remove assert statements that are no longer correct (Ben Noordhuis) * unix: appease warning about non-standard `inline` (Sean Silva) * unix: add uv__is_closing() macro (Ben Noordhuis) * unix: stop stream POLLOUT watcher on write error (Ben Noordhuis) * include: document uv_update_time() and uv_now() (Ben Noordhuis) * linux: fix cpu model parsing on newer arm kernels (Ben Noordhuis) * linux: fix a memory leak in uv_cpu_info() error path (Ben Noordhuis) * linux: don't ignore out-of-memory errors in uv_cpu_info() (Ben Noordhuis) * unix, windows: move uv_now() to uv-common.c (Ben Noordhuis) * test: fix a compilation problem in test-osx-select.c that was caused by the use of c-style comments (Bert Belder) * darwin: use uv_fs_sendfile() use the sendfile api correctly (Wynn Wilkes) * windows: call idle handles on every loop iteration, something the unix implementation already did (Bert Belder) * test: update the idle-starvation test to verify that idle handles are called in every loop iteration (Bert Belder) * unix, windows: ensure that uv_run() in RUN_ONCE mode calls timers that expire after blocking (Ben Noordhuis) 2013.05.29, Version 0.10.9 (Stable), a195f9ace23d92345baf57582678bfc3017e6632 Changes since version 0.10.8: * unix: fix stream refcounting buglet (Ben Noordhuis) * unix: remove erroneous asserts (Ben Noordhuis) * unix: add uv__is_closing() macro (Ben Noordhuis) * unix: stop stream POLLOUT watcher on write error (Ben Noordhuis) 2013.05.25, Version 0.10.8 (Stable), 0f39be12926fe2d8766a9f025797a473003e6504 Changes since version 0.10.7: * windows: make uv_spawn not fail under job control (Bert Belder) * darwin: assume CFRunLoopStop() isn't thread-safe (Fedor Indutny) * win: fix UV_EALREADY incorrectly set (Bert Belder) * darwin: make two uv__cf_*() functions static (Ben Noordhuis) * darwin: task_info() cannot fail (Ben Noordhuis) * unix: add mapping for ENETDOWN (Ben Noordhuis) * unix: implicitly signal write errors to libuv user (Ben Noordhuis) * unix: fix assert on signal pipe overflow (Bert Belder) * unix: turn off POLLOUT after stream connect (Ben Noordhuis) 2013.05.16, Version 0.11.3 (Unstable), 0a48c05b5988aea84c605751900926fa25443b34 Changes since version 0.11.2: * unix: clean up uv_accept() (Ben Noordhuis) * unix: remove errno preserving code (Ben Noordhuis) * darwin: fix ios build, don't require ApplicationServices (Ben Noordhuis) * windows: kill child processes when the parent dies (Bert Belder) * build: set soname in shared library (Ben Noordhuis) * build: make `make test` link against .a again (Ben Noordhuis) * build: only set soname on shared object builds (Timothy J. Fontaine) * build: convert predefined $PLATFORM to lower case (Elliot Saba) * test: fix process_title failing on linux (Miroslav Bajtoš) * test, sunos: disable process_title test (Miroslav Bajtoš) * test: add error logging to tty unit test (Miroslav Bajtoš) 2013.05.15, Version 0.10.7 (Stable), 028baaf0846b686a81e992cb2f2f5a9b8e841fcf Changes since version 0.10.6: * windows: kill child processes when the parent dies (Bert Belder) 2013.05.15, Version 0.10.6 (Stable), 11e6613e6260d95c8cf11bf89a2759c24649319a Changes since version 0.10.5: * stream: fix osx select hack (Fedor Indutny) * stream: fix small nit in select hack, add test (Fedor Indutny) * build: link with libkvm on openbsd (Ben Noordhuis) * stream: use harder sync restrictions for osx-hack (Fedor Indutny) * unix: fix EMFILE error handling (Ben Noordhuis) * darwin: fix unnecessary include headers (Daisuke Murase) * darwin: rename darwin-getproctitle.m (Ben Noordhuis) * build: convert predefined $PLATFORM to lower case (Elliot Saba) * build: set soname in shared library (Ben Noordhuis) * build: make `make test` link against .a again (Ben Noordhuis) * darwin: fix ios build, don't require ApplicationServices (Ben Noordhuis) * build: only set soname on shared object builds (Timothy J. Fontaine) 2013.05.11, Version 0.11.2 (Unstable), 3fba0bf65f091b91a9760530c05c6339c658d88b Changes since version 0.11.1: * darwin: look up file path with F_GETPATH (Ben Noordhuis) * unix, windows: add uv_has_ref() function (Saúl Ibarra Corretgé) * build: avoid double / in paths for dtrace (Timothy J. Fontaine) * unix: remove src/unix/cygwin.c (Ben Noordhuis) * windows: deal with the fact that GetTickCount might lag (Bert Belder) * unix: silence STATIC_ASSERT compiler warnings (Ben Noordhuis) * linux: don't use fopen() in uv_resident_set_memory() (Ben Noordhuis) 2013.04.24, Version 0.10.5 (Stable), 6595a7732c52eb4f8e57c88655f72997a8567a67 Changes since version 0.10.4: * unix: silence STATIC_ASSERT compiler warnings (Ben Noordhuis) * windows: make timers handle large timeouts (Miroslav Bajtoš) * windows: remove superfluous assert statement (Bert Belder) * unix: silence STATIC_ASSERT compiler warnings (Ben Noordhuis) * linux: don't use fopen() in uv_resident_set_memory() (Ben Noordhuis) 2013.04.12, Version 0.10.4 (Stable), 85827e26403ac6dfa331af8ec9916ea7e27bd833 Changes since version 0.10.3: * include: update uv_backend_fd() documentation (Ben Noordhuis) * unix: include uv.h in src/version.c (Ben Noordhuis) * unix: don't write more than IOV_MAX iovecs (Fedor Indutny) * mingw-w64: don't call _set_invalid_parameter_handler (Nils Maier) * build: gyp disable thin archives (Timothy J. Fontaine) * sunos: re-export entire library when static (Timothy J. Fontaine) * unix: dtrace probes for tick-start and tick-stop (Timothy J. Fontaine) * windows: fix memory leak in fs__sendfile (Shannen Saez) * windows: remove double initialization in uv_tty_init (Shannen Saez) * build: fix dtrace-enabled out of tree build (Ben Noordhuis) * build: squelch -Wdollar-in-identifier-extension warnings (Ben Noordhuis) * inet: snprintf returns int, not size_t (Brian White) * win: refactor uv_cpu_info (Bert Belder) * build: add support for Visual Studio 2012 (Nicholas Vavilov) * build: -Wno-dollar-in-identifier-extension is clang only (Ben Noordhuis) 2013.04.11, Version 0.11.1 (Unstable), 5c10e82ae0bc99eff86d4b9baff1f1aa0bf84c0a This is the first versioned release from the current unstable libuv branch. Changes since Node.js v0.11.0: * all platforms: nanosecond resolution support for uv_fs_[fl]stat (Timothy J. Fontaine) * all platforms: add netmask to uv_interface_address (Ben Kelly) * unix: make sure the `status` parameter passed to the `uv_getaddrinfo` is 0 or -1 (Ben Noordhuis) * unix: limit the number of iovecs written in a single `writev` syscall to IOV_MAX (Fedor Indutny) * unix: add dtrace probes for tick-start and tick-stop (Timothy J. Fontaine) * mingw-w64: don't call _set_invalid_parameter_handler (Nils Maier) * windows: fix memory leak in fs__sendfile (Shannen Saez) * windows: fix edge case bugs in uv_cpu_info (Bert Belder) * include: no longer ship with / include ngx-queue.h (Ben Noordhuis) * include: remove UV_VERSION_* macros from uv.h (Ben Noordhuis) * documentation updates (Kristian Evensen, Ben Kelly, Ben Noordhuis) * build: fix dtrace-enabled builds (Ben Noordhuis, Timothy J. Fontaine) * build: gyp disable thin archives (Timothy J. Fontaine) * build: add support for Visual Studio 2012 (Nicholas Vavilov) 2013.03.28, Version 0.10.3 (Stable), 31ebe23973dd98fd8a24c042b606f37a794e99d0 Changes since version 0.10.2: * include: remove extraneous const from uv_version() (Ben Noordhuis) * doc: update README, replace `OS` by `PLATFORM` (Ben Noordhuis) * build: simplify .buildstamp rule (Ben Noordhuis) * build: disable -Wstrict-aliasing on darwin (Ben Noordhuis) * darwin: don't select(&exceptfds) in fallback path (Ben Noordhuis) * unix: don't clear flags after closing UDP handle (Saúl Ibarra Corretgé) 2013.03.25, Version 0.10.2 (Stable), 0f36a00568f3e7608f97f6c6cdb081f4800a50c9 This is the first officially versioned release of libuv. Starting now libuv will make releases independently of Node.js. Changes since Node.js v0.10.0: * test: add tap output for windows (Timothy J. Fontaine) * unix: fix uv_tcp_simultaneous_accepts() logic (Ben Noordhuis) * include: bump UV_VERSION_MINOR (Ben Noordhuis) * unix: improve uv_guess_handle() implementation (Ben Noordhuis) * stream: run try_select only for pipes and ttys (Fedor Indutny) Changes since Node.js v0.10.1: * build: rename OS to PLATFORM (Ben Noordhuis) * unix: make uv_timer_init() initialize repeat (Brian Mazza) * unix: make timers handle large timeouts (Ben Noordhuis) * build: add OBJC makefile var (Ben Noordhuis) * Add `uv_version()` and `uv_version_string()` APIs (Bert Belder) libuv-v1.8.0/LICENSE000066400000000000000000000037621263361716600140570ustar00rootroot00000000000000libuv is part of the Node project: http://nodejs.org/ libuv may be distributed alone under Node's license: ==== Copyright Joyent, Inc. and other Node contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== This license applies to all parts of libuv that are not externally maintained libraries. The externally maintained libraries used by libuv are: - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. - inet_pton and inet_ntop implementations, contained in src/inet.c, are copyright the Internet Systems Consortium, Inc., and licensed under the ISC license. - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three clause BSD license. - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. Three clause BSD license. - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement n° 289016). Three clause BSD license. libuv-v1.8.0/MAINTAINERS.md000066400000000000000000000023421263361716600151370ustar00rootroot00000000000000 # Project Maintainers libuv is currently managed by the following individuals: * **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis)) - GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis) * **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus)) * **Fedor Indutny** ([@indutny](https://github.com/indutny)) - GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny) * **Saúl Ibarra Corretgé** ([@saghul](https://github.com/saghul)) - GPG key: FDF5 1936 4458 319F A823 3DC9 410E 5553 AE9B C059 (pubkey-saghul) ## Storing a maintainer key in Git It's quite handy to store a maintainer's signature as a git blob, and have that object tagged and signed with such key. Export your public key: $ gpg --armor --export saghul@gmail.com > saghul.asc Store it as a blob on the repo: $ git hash-object -w saghul.asc The previous command returns a hash, copy it. For the sake of this explanation, we'll assume it's 'abcd1234'. Storing the blob in git is not enough, it could be garbage collected since nothing references it, so we'll create a tag for it: $ git tag -s pubkey-saghul abcd1234 Commit the changes and push: $ git push origin pubkey-saghul libuv-v1.8.0/Makefile.am000066400000000000000000000322271263361716600151040ustar00rootroot00000000000000# Copyright (c) 2013, Ben Noordhuis # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include \ -I$(top_srcdir)/src include_HEADERS=include/uv.h include/uv-errno.h include/uv-threadpool.h include/uv-version.h CLEANFILES = lib_LTLIBRARIES = libuv.la libuv_la_CFLAGS = @CFLAGS@ libuv_la_LDFLAGS = -no-undefined -version-info 1:0:0 libuv_la_SOURCES = src/fs-poll.c \ src/heap-inl.h \ src/inet.c \ src/queue.h \ src/threadpool.c \ src/uv-common.c \ src/uv-common.h \ src/version.c if SUNOS # Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers # on other platforms complain that the argument is unused during compilation. libuv_la_CFLAGS += -pthread endif if WINNT include_HEADERS += include/uv-win.h include/tree.h AM_CPPFLAGS += -I$(top_srcdir)/src/win \ -DWIN32_LEAN_AND_MEAN \ -D_WIN32_WINNT=0x0600 LIBS += -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv libuv_la_SOURCES += src/win/async.c \ src/win/atomicops-inl.h \ src/win/core.c \ src/win/dl.c \ src/win/error.c \ src/win/fs-event.c \ src/win/fs.c \ src/win/getaddrinfo.c \ src/win/getnameinfo.c \ src/win/handle.c \ src/win/handle-inl.h \ src/win/internal.h \ src/win/loop-watcher.c \ src/win/pipe.c \ src/win/poll.c \ src/win/process-stdio.c \ src/win/process.c \ src/win/req.c \ src/win/req-inl.h \ src/win/signal.c \ src/win/stream.c \ src/win/stream-inl.h \ src/win/tcp.c \ src/win/thread.c \ src/win/timer.c \ src/win/tty.c \ src/win/udp.c \ src/win/util.c \ src/win/winapi.c \ src/win/winapi.h \ src/win/winsock.c \ src/win/winsock.h else # WINNT include_HEADERS += include/uv-unix.h AM_CPPFLAGS += -I$(top_srcdir)/src/unix libuv_la_SOURCES += src/unix/async.c \ src/unix/atomic-ops.h \ src/unix/core.c \ src/unix/dl.c \ src/unix/fs.c \ src/unix/getaddrinfo.c \ src/unix/getnameinfo.c \ src/unix/internal.h \ src/unix/loop-watcher.c \ src/unix/loop.c \ src/unix/pipe.c \ src/unix/poll.c \ src/unix/process.c \ src/unix/signal.c \ src/unix/spinlock.h \ src/unix/stream.c \ src/unix/tcp.c \ src/unix/thread.c \ src/unix/timer.c \ src/unix/tty.c \ src/unix/udp.c endif # WINNT EXTRA_DIST = test/fixtures/empty_file \ test/fixtures/load_error.node \ include \ test \ docs \ img \ samples \ android-configure \ CONTRIBUTING.md \ LICENSE \ README.md \ checksparse.sh \ vcbuild.bat \ Makefile.mingw \ common.gypi \ gyp_uv.py \ uv.gyp TESTS = test/run-tests check_PROGRAMS = test/run-tests test_run_tests_CFLAGS = test_run_tests_SOURCES = test/blackhole-server.c \ test/dns-server.c \ test/echo-server.c \ test/run-tests.c \ test/runner.c \ test/runner.h \ test/task.h \ test/test-active.c \ test/test-async.c \ test/test-async-null-cb.c \ test/test-barrier.c \ test/test-callback-order.c \ test/test-callback-stack.c \ test/test-close-fd.c \ test/test-close-order.c \ test/test-condvar.c \ test/test-connection-fail.c \ test/test-cwd-and-chdir.c \ test/test-default-loop-close.c \ test/test-delayed-accept.c \ test/test-dlerror.c \ test/test-embed.c \ test/test-emfile.c \ test/test-error.c \ test/test-fail-always.c \ test/test-fs-event.c \ test/test-fs-poll.c \ test/test-fs.c \ test/test-get-currentexe.c \ test/test-get-loadavg.c \ test/test-get-memory.c \ test/test-getaddrinfo.c \ test/test-getnameinfo.c \ test/test-getsockname.c \ test/test-handle-fileno.c \ test/test-homedir.c \ test/test-hrtime.c \ test/test-idle.c \ test/test-ip4-addr.c \ test/test-ip6-addr.c \ test/test-ipc-send-recv.c \ test/test-ipc.c \ test/test-list.h \ test/test-loop-handles.c \ test/test-loop-alive.c \ test/test-loop-close.c \ test/test-loop-stop.c \ test/test-loop-time.c \ test/test-loop-configure.c \ test/test-multiple-listen.c \ test/test-mutexes.c \ test/test-osx-select.c \ test/test-pass-always.c \ test/test-ping-pong.c \ test/test-pipe-bind-error.c \ test/test-pipe-connect-error.c \ test/test-pipe-connect-multiple.c \ test/test-pipe-connect-prepare.c \ test/test-pipe-getsockname.c \ test/test-pipe-pending-instances.c \ test/test-pipe-sendmsg.c \ test/test-pipe-server-close.c \ test/test-pipe-close-stdout-read-stdin.c \ test/test-pipe-set-non-blocking.c \ test/test-platform-output.c \ test/test-poll-close.c \ test/test-poll-close-doesnt-corrupt-stack.c \ test/test-poll-closesocket.c \ test/test-poll.c \ test/test-process-title.c \ test/test-queue-foreach-delete.c \ test/test-ref.c \ test/test-run-nowait.c \ test/test-run-once.c \ test/test-semaphore.c \ test/test-shutdown-close.c \ test/test-shutdown-eof.c \ test/test-shutdown-twice.c \ test/test-signal-multiple-loops.c \ test/test-signal.c \ test/test-socket-buffer-size.c \ test/test-spawn.c \ test/test-stdio-over-pipes.c \ test/test-tcp-bind-error.c \ test/test-tcp-bind6-error.c \ test/test-tcp-close-accept.c \ test/test-tcp-close-while-connecting.c \ test/test-tcp-close.c \ test/test-tcp-create-socket-early.c \ test/test-tcp-connect-error-after-write.c \ test/test-tcp-connect-error.c \ test/test-tcp-connect-timeout.c \ test/test-tcp-connect6-error.c \ test/test-tcp-flags.c \ test/test-tcp-open.c \ test/test-tcp-read-stop.c \ test/test-tcp-shutdown-after-write.c \ test/test-tcp-unexpected-read.c \ test/test-tcp-oob.c \ test/test-tcp-write-to-half-open-connection.c \ test/test-tcp-write-after-connect.c \ test/test-tcp-writealot.c \ test/test-tcp-write-fail.c \ test/test-tcp-try-write.c \ test/test-tcp-write-queue-order.c \ test/test-thread-equal.c \ test/test-thread.c \ test/test-threadpool-cancel.c \ test/test-threadpool.c \ test/test-timer-again.c \ test/test-timer-from-check.c \ test/test-timer.c \ test/test-tty.c \ test/test-udp-bind.c \ test/test-udp-create-socket-early.c \ test/test-udp-dgram-too-big.c \ test/test-udp-ipv6.c \ test/test-udp-multicast-interface.c \ test/test-udp-multicast-interface6.c \ test/test-udp-multicast-join.c \ test/test-udp-multicast-join6.c \ test/test-udp-multicast-ttl.c \ test/test-udp-open.c \ test/test-udp-options.c \ test/test-udp-send-and-recv.c \ test/test-udp-send-immediate.c \ test/test-udp-send-unreachable.c \ test/test-udp-try-send.c \ test/test-walk-handles.c \ test/test-watcher-cross-stop.c test_run_tests_LDADD = libuv.la if WINNT test_run_tests_SOURCES += test/runner-win.c \ test/runner-win.h else test_run_tests_SOURCES += test/runner-unix.c \ test/runner-unix.h endif if AIX test_run_tests_CFLAGS += -D_ALL_SOURCE -D_XOPEN_SOURCE=500 -D_LINUX_SOURCE_COMPAT endif if SUNOS test_run_tests_CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=500 endif if AIX libuv_la_CFLAGS += -D_ALL_SOURCE -D_XOPEN_SOURCE=500 -D_LINUX_SOURCE_COMPAT include_HEADERS += include/uv-aix.h libuv_la_SOURCES += src/unix/aix.c endif if ANDROID include_HEADERS += include/android-ifaddrs.h \ include/pthread-fixes.h libuv_la_SOURCES += src/unix/android-ifaddrs.c \ src/unix/pthread-fixes.c endif if DARWIN include_HEADERS += include/uv-darwin.h libuv_la_CFLAGS += -D_DARWIN_USE_64_BIT_INODE=1 libuv_la_CFLAGS += -D_DARWIN_UNLIMITED_SELECT=1 libuv_la_SOURCES += src/unix/darwin.c \ src/unix/darwin-proctitle.c \ src/unix/fsevents.c \ src/unix/kqueue.c \ src/unix/proctitle.c endif if DRAGONFLY include_HEADERS += include/uv-bsd.h endif if FREEBSD include_HEADERS += include/uv-bsd.h libuv_la_SOURCES += src/unix/freebsd.c src/unix/kqueue.c endif if LINUX include_HEADERS += include/uv-linux.h libuv_la_CFLAGS += -D_GNU_SOURCE libuv_la_SOURCES += src/unix/linux-core.c \ src/unix/linux-inotify.c \ src/unix/linux-syscalls.c \ src/unix/linux-syscalls.h \ src/unix/proctitle.c endif if NETBSD include_HEADERS += include/uv-bsd.h libuv_la_SOURCES += src/unix/kqueue.c src/unix/netbsd.c endif if OPENBSD include_HEADERS += include/uv-bsd.h libuv_la_SOURCES += src/unix/kqueue.c src/unix/openbsd.c endif if SUNOS include_HEADERS += include/uv-sunos.h libuv_la_CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=500 libuv_la_SOURCES += src/unix/sunos.c endif if HAVE_PKG_CONFIG pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = @PACKAGE_NAME@.pc endif libuv-v1.8.0/Makefile.mingw000066400000000000000000000045751263361716600156350ustar00rootroot00000000000000# Copyright (c) 2013, Ben Noordhuis # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. CC ?= gcc CFLAGS += -Wall \ -Wextra \ -Wno-unused-parameter \ -Iinclude \ -Isrc \ -Isrc/win \ -DWIN32_LEAN_AND_MEAN \ -D_WIN32_WINNT=0x0600 INCLUDES = include/stdint-msvc2008.h \ include/tree.h \ include/uv-errno.h \ include/uv-threadpool.h \ include/uv-version.h \ include/uv-win.h \ include/uv.h \ src/heap-inl.h \ src/queue.h \ src/uv-common.h \ src/win/atomicops-inl.h \ src/win/handle-inl.h \ src/win/internal.h \ src/win/req-inl.h \ src/win/stream-inl.h \ src/win/winapi.h \ src/win/winsock.h OBJS = src/fs-poll.o \ src/inet.o \ src/threadpool.o \ src/uv-common.o \ src/version.o \ src/win/async.o \ src/win/core.o \ src/win/dl.o \ src/win/error.o \ src/win/fs-event.o \ src/win/fs.o \ src/win/getaddrinfo.o \ src/win/getnameinfo.o \ src/win/handle.o \ src/win/loop-watcher.o \ src/win/pipe.o \ src/win/poll.o \ src/win/process-stdio.o \ src/win/process.o \ src/win/req.o \ src/win/signal.o \ src/win/stream.o \ src/win/tcp.o \ src/win/thread.o \ src/win/timer.o \ src/win/tty.o \ src/win/udp.o \ src/win/util.o \ src/win/winapi.o \ src/win/winsock.o all: libuv.a clean: -$(RM) $(OBJS) libuv.a libuv.a: $(OBJS) $(AR) crs $@ $^ $(OBJS): %.o : %.c $(INCLUDES) $(CC) $(CFLAGS) -c -o $@ $< libuv-v1.8.0/README.md000066400000000000000000000155041263361716600143260ustar00rootroot00000000000000![libuv][libuv_banner] ## Overview libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by [Node.js](http://nodejs.org), but it's also used by [Luvit](http://luvit.io/), [Julia](http://julialang.org/), [pyuv](https://github.com/saghul/pyuv), and [others](https://github.com/libuv/libuv/wiki/Projects-that-use-libuv). ## Feature highlights * Full-featured event loop backed by epoll, kqueue, IOCP, event ports. * Asynchronous TCP and UDP sockets * Asynchronous DNS resolution * Asynchronous file and file system operations * File system events * ANSI escape code controlled TTY * IPC with socket sharing, using Unix domain sockets or named pipes (Windows) * Child processes * Thread pool * Signal handling * High resolution clock * Threading and synchronization primitives ## Versioning Starting with version 1.0.0 libuv follows the [semantic versioning](http://semver.org/) scheme. The API change and backwards compatibility rules are those indicated by SemVer. libuv will keep a stable ABI across major releases. ## Community * [Mailing list](http://groups.google.com/group/libuv) * [IRC chatroom (#libuv@irc.freenode.org)](http://webchat.freenode.net?channels=libuv&uio=d4) ## Documentation ### Official API documentation Located in the docs/ subdirectory. It uses the [Sphinx](http://sphinx-doc.org/) framework, which makes it possible to build the documentation in multiple formats. Show different supported building options: $ make help Build documentation as HTML: $ make html Build documentation as man pages: $ make man Build documentation as ePub: $ make epub NOTE: Windows users need to use make.bat instead of plain 'make'. Documentation can be browsed online [here](http://docs.libuv.org). The [tests and benchmarks](https://github.com/libuv/libuv/tree/master/test) also serve as API specification and usage examples. ### Other resources * [An Introduction to libuv](http://nikhilm.github.com/uvbook/) — An overview of libuv with tutorials. * [LXJS 2012 talk](http://www.youtube.com/watch?v=nGn60vDSxQ4) — High-level introductory talk about libuv. * [libuv-dox](https://github.com/thlorenz/libuv-dox) — Documenting types and methods of libuv, mostly by reading uv.h. * [learnuv](https://github.com/thlorenz/learnuv) — Learn uv for fun and profit, a self guided workshop to libuv. These resources are not handled by libuv maintainers and might be out of date. Please verify it before opening new issues. ## Downloading libuv can be downloaded either from the [GitHub repository](https://github.com/libuv/libuv) or from the [downloads site](http://dist.libuv.org/dist/). Starting with libuv 1.7.0, binaries for Windows are also provided. This is to be considered EXPERIMENTAL. Before verifying the git tags or signature files, importing the relevant keys is necessary. Key IDs are listed in the [MAINTAINERS](https://github.com/libuv/libuv/blob/master/MAINTAINERS.md) file, but are also available as git blob objects for easier use. Importing a key the usual way: $ gpg --keyserver pool.sks-keyservers.net \ --recv-keys AE9BC059 Importing a key from a git blob object: $ git show pubkey-saghul | gpg --import ### Verifying releases Git tags are signed with the developer's key, they can be verified as follows: $ git verify-tag v1.6.1 Starting with libuv 1.7.0, the tarballs stored in the [downloads site](http://dist.libuv.org/dist/) are signed and an accompanying signature file sit alongside each. Once both the release tarball and the signature file are downloaded, the file can be verified as follows: $ gpg --verify libuv-1.7.0.tar.gz.sign ## Build Instructions For GCC there are two build methods: via autotools or via [GYP][]. GYP is a meta-build system which can generate MSVS, Makefile, and XCode backends. It is best used for integration into other projects. To build with autotools: $ sh autogen.sh $ ./configure $ make $ make check $ make install ### Windows First, [Python][] 2.6 or 2.7 must be installed as it is required by [GYP][]. If python is not in your path, set the environment variable `PYTHON` to its location. For example: `set PYTHON=C:\Python27\python.exe` To build with Visual Studio, launch a git shell (e.g. Cmd or PowerShell) and run vcbuild.bat which will checkout the GYP code into build/gyp and generate uv.sln as well as related project files. To have GYP generate build script for another system, checkout GYP into the project tree manually: $ git clone https://chromium.googlesource.com/external/gyp.git build/gyp ### Unix Run: $ ./gyp_uv.py -f make $ make -C out Run `./gyp_uv.py -f make -Dtarget_arch=x32` to build [x32][] binaries. ### OS X Run: $ ./gyp_uv.py -f xcode $ xcodebuild -ARCHS="x86_64" -project uv.xcodeproj \ -configuration Release -target All Using Homebrew: $ brew install --HEAD libuv Note to OS X users: Make sure that you specify the architecture you wish to build for in the "ARCHS" flag. You can specify more than one by delimiting with a space (e.g. "x86_64 i386"). ### Android Run: $ source ./android-configure NDK_PATH gyp $ make -C out Note for UNIX users: compile your project with `-D_LARGEFILE_SOURCE` and `-D_FILE_OFFSET_BITS=64`. GYP builds take care of that automatically. ### Using Ninja To use ninja for build on ninja supported platforms, run: $ ./gyp_uv.py -f ninja $ ninja -C out/Debug #for debug build OR $ ninja -C out/Release ### Running tests Run: $ ./gyp_uv.py -f make $ make -C out $ ./out/Debug/run-tests ## Supported Platforms Microsoft Windows operating systems since Windows XP SP2. It can be built with either Visual Studio or MinGW. Consider using [Visual Studio Express 2010][] or later if you do not have a full Visual Studio license. Linux using the GCC toolchain. OS X using the GCC or XCode toolchain. Solaris 121 and later using GCC toolchain. AIX 6 and later using GCC toolchain (see notes). ### AIX Notes AIX support for filesystem events requires the non-default IBM `bos.ahafs` package to be installed. This package provides the AIX Event Infrastructure that is detected by `autoconf`. [IBM documentation](http://www.ibm.com/developerworks/aix/library/au-aix_event_infrastructure/) describes the package in more detail. AIX support for filesystem events is not compiled when building with `gyp`. ## Patches See the [guidelines for contributing][]. [node.js]: http://nodejs.org/ [GYP]: http://code.google.com/p/gyp/ [Python]: https://www.python.org/downloads/ [Visual Studio Express 2010]: http://www.microsoft.com/visualstudio/eng/products/visual-studio-2010-express [guidelines for contributing]: https://github.com/libuv/libuv/blob/master/CONTRIBUTING.md [libuv_banner]: https://raw.githubusercontent.com/libuv/libuv/master/img/banner.png libuv-v1.8.0/android-configure000077500000000000000000000010331263361716600163640ustar00rootroot00000000000000#!/bin/bash export TOOLCHAIN=$PWD/android-toolchain mkdir -p $TOOLCHAIN $1/build/tools/make-standalone-toolchain.sh \ --toolchain=arm-linux-androideabi-4.8 \ --arch=arm \ --install-dir=$TOOLCHAIN \ --platform=android-21 export PATH=$TOOLCHAIN/bin:$PATH export AR=arm-linux-androideabi-ar export CC=arm-linux-androideabi-gcc export CXX=arm-linux-androideabi-g++ export LINK=arm-linux-androideabi-g++ export PLATFORM=android if [ $2 -a $2 == 'gyp' ] then ./gyp_uv.py -Dtarget_arch=arm -DOS=android -f make-android fi libuv-v1.8.0/appveyor.yml000066400000000000000000000013461263361716600154360ustar00rootroot00000000000000version: v1.8.0.build{build} install: - cinst -y nsis matrix: fast_finish: true allow_failures: - platform: x86 configuration: Release - platform: x64 configuration: Release platform: - x86 - x64 configuration: - Release build_script: # Fixed tag version number if using a tag. - cmd: if "%APPVEYOR_REPO_TAG%" == "true" set APPVEYOR_BUILD_VERSION=%APPVEYOR_REPO_TAG_NAME% # vcbuild overwrites the platform variable. - cmd: set ARCH=%platform% - cmd: vcbuild.bat release %ARCH% shared after_build: - '"%PROGRAMFILES(x86)%\NSIS\makensis" /DVERSION=%APPVEYOR_BUILD_VERSION% /DARCH=%ARCH% libuv.nsi' artifacts: - name: Installer path: 'libuv-*.exe' cache: - C:\projects\libuv\build\gyp libuv-v1.8.0/autogen.sh000077500000000000000000000032531263361716600150460ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2013, Ben Noordhuis # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. cd `dirname "$0"` if [ "$LIBTOOLIZE" = "" ] && [ "`uname`" = "Darwin" ]; then LIBTOOLIZE=glibtoolize fi ACLOCAL=${ACLOCAL:-aclocal} AUTOCONF=${AUTOCONF:-autoconf} AUTOMAKE=${AUTOMAKE:-automake} LIBTOOLIZE=${LIBTOOLIZE:-libtoolize} automake_version=`"$AUTOMAKE" --version | head -n 1 | sed 's/[^.0-9]//g'` automake_version_major=`echo "$automake_version" | cut -d. -f1` automake_version_minor=`echo "$automake_version" | cut -d. -f2` UV_EXTRA_AUTOMAKE_FLAGS= if test "$automake_version_major" -gt 1 || \ test "$automake_version_major" -eq 1 && \ test "$automake_version_minor" -gt 11; then # serial-tests is available in v1.12 and newer. UV_EXTRA_AUTOMAKE_FLAGS="$UV_EXTRA_AUTOMAKE_FLAGS serial-tests" fi echo "m4_define([UV_EXTRA_AUTOMAKE_FLAGS], [$UV_EXTRA_AUTOMAKE_FLAGS])" \ > m4/libuv-extra-automake-flags.m4 set -ex "$LIBTOOLIZE" "$ACLOCAL" -I m4 "$AUTOCONF" "$AUTOMAKE" --add-missing --copy libuv-v1.8.0/checksparse.sh000077500000000000000000000127711263361716600157040ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2013, Ben Noordhuis # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. SPARSE=${SPARSE:-sparse} SPARSE_FLAGS=${SPARSE_FLAGS:-" -D__POSIX__ -Wsparse-all -Wno-do-while -Wno-transparent-union -Iinclude -Isrc "} SOURCES=" include/tree.h include/uv-unix.h include/uv.h src/fs-poll.c src/inet.c src/queue.h src/unix/async.c src/unix/core.c src/unix/dl.c src/unix/fs.c src/unix/getaddrinfo.c src/unix/internal.h src/unix/loop-watcher.c src/unix/loop.c src/unix/pipe.c src/unix/poll.c src/unix/process.c src/unix/signal.c src/unix/stream.c src/unix/tcp.c src/unix/thread.c src/unix/threadpool.c src/unix/timer.c src/unix/tty.c src/unix/udp.c src/uv-common.c src/uv-common.h " TESTS=" test/benchmark-async-pummel.c test/benchmark-async.c test/benchmark-fs-stat.c test/benchmark-getaddrinfo.c test/benchmark-loop-count.c test/benchmark-million-async.c test/benchmark-million-timers.c test/benchmark-multi-accept.c test/benchmark-ping-pongs.c test/benchmark-pound.c test/benchmark-pump.c test/benchmark-sizes.c test/benchmark-spawn.c test/benchmark-tcp-write-batch.c test/benchmark-thread.c test/benchmark-udp-pummel.c test/blackhole-server.c test/dns-server.c test/echo-server.c test/run-benchmarks.c test/run-tests.c test/runner-unix.c test/runner-unix.h test/runner.c test/runner.h test/task.h test/test-active.c test/test-async.c test/test-barrier.c test/test-callback-order.c test/test-callback-stack.c test/test-condvar.c test/test-connection-fail.c test/test-cwd-and-chdir.c test/test-delayed-accept.c test/test-dlerror.c test/test-embed.c test/test-error.c test/test-fail-always.c test/test-fs-event.c test/test-fs-poll.c test/test-fs.c test/test-get-currentexe.c test/test-get-loadavg.c test/test-get-memory.c test/test-getaddrinfo.c test/test-getsockname.c test/test-homedir.c test/test-hrtime.c test/test-idle.c test/test-ip6-addr.c test/test-ipc-send-recv.c test/test-ipc.c test/test-loop-handles.c test/test-multiple-listen.c test/test-mutexes.c test/test-pass-always.c test/test-ping-pong.c test/test-pipe-bind-error.c test/test-pipe-connect-error.c test/test-pipe-sendmsg.c test/test-pipe-server-close.c test/test-platform-output.c test/test-poll-close.c test/test-poll.c test/test-process-title.c test/test-ref.c test/test-run-nowait.c test/test-run-once.c test/test-semaphore.c test/test-shutdown-close.c test/test-shutdown-eof.c test/test-signal-multiple-loops.c test/test-signal.c test/test-spawn.c test/test-stdio-over-pipes.c test/test-tcp-bind-error.c test/test-tcp-bind6-error.c test/test-tcp-close-while-connecting.c test/test-tcp-close-accept.c test/test-tcp-close.c test/test-tcp-connect-error-after-write.c test/test-tcp-connect-error.c test/test-tcp-connect-timeout.c test/test-tcp-connect6-error.c test/test-tcp-flags.c test/test-tcp-open.c test/test-tcp-read-stop.c test/test-tcp-shutdown-after-write.c test/test-tcp-unexpected-read.c test/test-tcp-oob.c test/test-tcp-write-error.c test/test-tcp-write-to-half-open-connection.c test/test-tcp-writealot.c test/test-thread.c test/test-threadpool-cancel.c test/test-threadpool.c test/test-timer-again.c test/test-timer.c test/test-tty.c test/test-udp-dgram-too-big.c test/test-udp-ipv6.c test/test-udp-multicast-join.c test/test-udp-multicast-ttl.c test/test-udp-open.c test/test-udp-options.c test/test-udp-send-and-recv.c test/test-walk-handles.c test/test-watcher-cross-stop.c " case `uname -s` in AIX) SPARSE_FLAGS="$SPARSE_FLAGS -D_AIX=1" SOURCES="$SOURCES src/unix/aix.c" ;; Darwin) SPARSE_FLAGS="$SPARSE_FLAGS -D__APPLE__=1" SOURCES="$SOURCES include/uv-bsd.h src/unix/darwin.c src/unix/kqueue.c src/unix/fsevents.c" ;; DragonFly) SPARSE_FLAGS="$SPARSE_FLAGS -D__DragonFly__=1" SOURCES="$SOURCES include/uv-bsd.h src/unix/kqueue.c src/unix/freebsd.c" ;; FreeBSD) SPARSE_FLAGS="$SPARSE_FLAGS -D__FreeBSD__=1" SOURCES="$SOURCES include/uv-bsd.h src/unix/kqueue.c src/unix/freebsd.c" ;; Linux) SPARSE_FLAGS="$SPARSE_FLAGS -D__linux__=1" SOURCES="$SOURCES include/uv-linux.h src/unix/linux-inotify.c src/unix/linux-core.c src/unix/linux-syscalls.c src/unix/linux-syscalls.h" ;; NetBSD) SPARSE_FLAGS="$SPARSE_FLAGS -D__NetBSD__=1" SOURCES="$SOURCES include/uv-bsd.h src/unix/kqueue.c src/unix/netbsd.c" ;; OpenBSD) SPARSE_FLAGS="$SPARSE_FLAGS -D__OpenBSD__=1" SOURCES="$SOURCES include/uv-bsd.h src/unix/kqueue.c src/unix/openbsd.c" ;; SunOS) SPARSE_FLAGS="$SPARSE_FLAGS -D__sun=1" SOURCES="$SOURCES include/uv-sunos.h src/unix/sunos.c" ;; esac for ARCH in __i386__ __x86_64__ __arm__ __mips__; do $SPARSE $SPARSE_FLAGS -D$ARCH=1 $SOURCES done # Tests are architecture independent. $SPARSE $SPARSE_FLAGS -Itest $TESTS libuv-v1.8.0/common.gypi000066400000000000000000000160721263361716600152320ustar00rootroot00000000000000{ 'variables': { 'visibility%': 'hidden', # V8's visibility setting 'target_arch%': 'ia32', # set v8's target architecture 'host_arch%': 'ia32', # set v8's host architecture 'uv_library%': 'static_library', # allow override to 'shared_library' for DLL/.so builds 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way }, 'target_defaults': { 'default_configuration': 'Debug', 'configurations': { 'Debug': { 'defines': [ 'DEBUG', '_DEBUG' ], 'cflags': [ '-g', '-O0', '-fwrapv' ], 'msvs_settings': { 'VCCLCompilerTool': { 'target_conditions': [ ['uv_library=="static_library"', { 'RuntimeLibrary': 1, # static debug }, { 'RuntimeLibrary': 3, # DLL debug }], ], 'Optimization': 0, # /Od, no optimization 'MinimalRebuild': 'false', 'OmitFramePointers': 'false', 'BasicRuntimeChecks': 3, # /RTC1 }, 'VCLinkerTool': { 'LinkIncremental': 2, # enable incremental linking }, }, 'xcode_settings': { 'GCC_OPTIMIZATION_LEVEL': '0', 'OTHER_CFLAGS': [ '-Wno-strict-aliasing' ], }, 'conditions': [ ['OS == "android"', { 'cflags': [ '-fPIE' ], 'ldflags': [ '-fPIE', '-pie' ] }] ] }, 'Release': { 'defines': [ 'NDEBUG' ], 'cflags': [ '-O3', '-fstrict-aliasing', '-fomit-frame-pointer', '-fdata-sections', '-ffunction-sections', ], 'msvs_settings': { 'VCCLCompilerTool': { 'target_conditions': [ ['uv_library=="static_library"', { 'RuntimeLibrary': 0, # static release }, { 'RuntimeLibrary': 2, # debug release }], ], 'Optimization': 3, # /Ox, full optimization 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible 'WholeProgramOptimization': 'true', # /GL, whole program optimization, needed for LTCG 'OmitFramePointers': 'true', 'EnableFunctionLevelLinking': 'true', 'EnableIntrinsicFunctions': 'true', }, 'VCLibrarianTool': { 'AdditionalOptions': [ '/LTCG', # link time code generation ], }, 'VCLinkerTool': { 'LinkTimeCodeGeneration': 1, # link-time code generation 'OptimizeReferences': 2, # /OPT:REF 'EnableCOMDATFolding': 2, # /OPT:ICF 'LinkIncremental': 1, # disable incremental linking }, }, } }, 'msvs_settings': { 'VCCLCompilerTool': { 'StringPooling': 'true', # pool string literals 'DebugInformationFormat': 3, # Generate a PDB 'WarningLevel': 3, 'BufferSecurityCheck': 'true', 'ExceptionHandling': 1, # /EHsc 'SuppressStartupBanner': 'true', 'WarnAsError': 'false', 'AdditionalOptions': [ '/MP', # compile across multiple CPUs ], }, 'VCLibrarianTool': { }, 'VCLinkerTool': { 'GenerateDebugInformation': 'true', 'RandomizedBaseAddress': 2, # enable ASLR 'DataExecutionPrevention': 2, # enable DEP 'AllowIsolation': 'true', 'SuppressStartupBanner': 'true', 'target_conditions': [ ['_type=="executable"', { 'SubSystem': 1, # console executable }], ], }, }, 'conditions': [ ['OS == "win"', { 'msvs_cygwin_shell': 0, # prevent actions from trying to use cygwin 'defines': [ 'WIN32', # we don't really want VC++ warning us about # how dangerous C functions are... '_CRT_SECURE_NO_DEPRECATE', # ... or that C implementations shouldn't use # POSIX names '_CRT_NONSTDC_NO_DEPRECATE', ], 'target_conditions': [ ['target_arch=="x64"', { 'msvs_configuration_platform': 'x64' }] ] }], ['OS in "freebsd dragonflybsd linux openbsd solaris android"', { 'cflags': [ '-Wall' ], 'cflags_cc': [ '-fno-rtti', '-fno-exceptions' ], 'target_conditions': [ ['_type=="static_library"', { 'standalone_static_library': 1, # disable thin archive which needs binutils >= 2.19 }], ], 'conditions': [ [ 'host_arch != target_arch and target_arch=="ia32"', { 'cflags': [ '-m32' ], 'ldflags': [ '-m32' ], }], [ 'target_arch=="x32"', { 'cflags': [ '-mx32' ], 'ldflags': [ '-mx32' ], }], [ 'OS=="linux"', { 'cflags': [ '-ansi' ], }], [ 'OS=="solaris"', { 'cflags': [ '-pthreads' ], 'ldflags': [ '-pthreads' ], }], [ 'OS not in "solaris android"', { 'cflags': [ '-pthread' ], 'ldflags': [ '-pthread' ], }], [ 'visibility=="hidden"', { 'cflags': [ '-fvisibility=hidden' ], }], ], }], ['OS=="mac"', { 'xcode_settings': { 'ALWAYS_SEARCH_USER_PATHS': 'NO', 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic # (Equivalent to -fPIC) 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics 'PREBINDING': 'NO', # No -Wl,-prebind 'USE_HEADERMAP': 'NO', 'OTHER_CFLAGS': [ '-fstrict-aliasing', ], 'WARNING_CFLAGS': [ '-Wall', '-Wendif-labels', '-W', '-Wno-unused-parameter', ], }, 'conditions': [ ['target_arch=="ia32"', { 'xcode_settings': {'ARCHS': ['i386']}, }], ['target_arch=="x64"', { 'xcode_settings': {'ARCHS': ['x86_64']}, }], ], 'target_conditions': [ ['_type!="static_library"', { 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']}, }], ], }], ['OS=="solaris"', { 'cflags': [ '-fno-omit-frame-pointer' ], # pull in V8's postmortem metadata 'ldflags': [ '-Wl,-z,allextract' ] }], ], }, } libuv-v1.8.0/configure.ac000066400000000000000000000061301263361716600153300ustar00rootroot00000000000000# Copyright (c) 2013, Ben Noordhuis # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) AC_INIT([libuv], [1.8.0], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) m4_include([m4/libuv-check-flags.m4]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects] UV_EXTRA_AUTOMAKE_FLAGS) AC_CANONICAL_HOST AC_ENABLE_SHARED AC_ENABLE_STATIC AC_PROG_CC AM_PROG_CC_C_O CC_CHECK_CFLAGS_APPEND([-fvisibility=hidden]) CC_CHECK_CFLAGS_APPEND([-g]) CC_CHECK_CFLAGS_APPEND([-std=gnu89]) CC_CHECK_CFLAGS_APPEND([-pedantic]) CC_CHECK_CFLAGS_APPEND([-Wall]) CC_CHECK_CFLAGS_APPEND([-Wextra]) CC_CHECK_CFLAGS_APPEND([-Wno-unused-parameter]) # AM_PROG_AR is not available in automake v0.11 but it's essential in v0.12. m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) # autoconf complains if AC_PROG_LIBTOOL precedes AM_PROG_AR. AC_PROG_LIBTOOL m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) LT_INIT # TODO(bnoordhuis) Check for -pthread vs. -pthreads AC_CHECK_LIB([dl], [dlopen]) AC_CHECK_LIB([kstat], [kstat_lookup]) AC_CHECK_LIB([kvm], [kvm_open]) AC_CHECK_LIB([nsl], [gethostbyname]) AC_CHECK_LIB([perfstat], [perfstat_cpu]) AC_CHECK_LIB([pthread], [pthread_mutex_init]) AC_CHECK_LIB([rt], [clock_gettime]) AC_CHECK_LIB([sendfile], [sendfile]) AC_CHECK_LIB([socket], [socket]) AC_SYS_LARGEFILE AM_CONDITIONAL([AIX], [AS_CASE([$host_os],[aix*], [true], [false])]) AM_CONDITIONAL([ANDROID], [AS_CASE([$host_os],[linux-android*],[true], [false])]) AM_CONDITIONAL([DARWIN], [AS_CASE([$host_os],[darwin*], [true], [false])]) AM_CONDITIONAL([DRAGONFLY],[AS_CASE([$host_os],[dragonfly*], [true], [false])]) AM_CONDITIONAL([FREEBSD], [AS_CASE([$host_os],[freebsd*], [true], [false])]) AM_CONDITIONAL([LINUX], [AS_CASE([$host_os],[linux*], [true], [false])]) AM_CONDITIONAL([NETBSD], [AS_CASE([$host_os],[netbsd*], [true], [false])]) AM_CONDITIONAL([OPENBSD], [AS_CASE([$host_os],[openbsd*], [true], [false])]) AM_CONDITIONAL([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])]) AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])]) AC_CHECK_HEADERS([sys/ahafs_evProds.h]) AC_CHECK_PROG(PKG_CONFIG, pkg-config, yes) AM_CONDITIONAL([HAVE_PKG_CONFIG], [test "x$PKG_CONFIG" != "x"]) AS_IF([test "x$PKG_CONFIG" != "x"], [ AC_CONFIG_FILES([libuv.pc]) ]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT libuv-v1.8.0/docs/000077500000000000000000000000001263361716600137725ustar00rootroot00000000000000libuv-v1.8.0/docs/Makefile000066400000000000000000000152111263361716600154320ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build SRCDIR = src # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR) # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR) .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/libuv.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/libuv.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/libuv" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/libuv" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." libuv-v1.8.0/docs/make.bat000066400000000000000000000151101263361716600153750ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set SRCDIR=src set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SRCDIR% set I18NSPHINXOPTS=%SPHINXOPTS% %SRCDIR% if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\libuv.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\libuv.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end libuv-v1.8.0/docs/src/000077500000000000000000000000001263361716600145615ustar00rootroot00000000000000libuv-v1.8.0/docs/src/async.rst000066400000000000000000000027131263361716600164330ustar00rootroot00000000000000 .. _async: :c:type:`uv_async_t` --- Async handle ===================================== Async handles allow the user to "wakeup" the event loop and get a callback called from another thread. Data types ---------- .. c:type:: uv_async_t Async handle type. .. c:type:: void (*uv_async_cb)(uv_async_t* handle) Type definition for callback passed to :c:func:`uv_async_init`. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_async_init(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb) Initialize the handle. A NULL callback is allowed. .. note:: Unlike other handle initialization functions, it immediately starts the handle. .. c:function:: int uv_async_send(uv_async_t* async) Wakeup the event loop and call the async handle's callback. .. note:: It's safe to call this function from any thread. The callback will be called on the loop thread. .. warning:: libuv will coalesce calls to :c:func:`uv_async_send`, that is, not every call to it will yield an execution of the callback. For example: if :c:func:`uv_async_send` is called 5 times in a row before the callback is called, the callback will only be called once. If :c:func:`uv_async_send` is called again after the callback was called, it will be called again. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/check.rst000066400000000000000000000015661263361716600164000ustar00rootroot00000000000000 .. _check: :c:type:`uv_check_t` --- Check handle ===================================== Check handles will run the given callback once per loop iteration, right after polling for i/o. Data types ---------- .. c:type:: uv_check_t Check handle type. .. c:type:: void (*uv_check_cb)(uv_check_t* handle) Type definition for callback passed to :c:func:`uv_check_start`. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_check_init(uv_loop_t* loop, uv_check_t* check) Initialize the handle. .. c:function:: int uv_check_start(uv_check_t* check, uv_check_cb cb) Start the handle with the given callback. .. c:function:: int uv_check_stop(uv_check_t* check) Stop the handle, the callback will no longer be called. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/conf.py000066400000000000000000000257151263361716600160720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # libuv API documentation documentation build configuration file, created by # sphinx-quickstart on Sun Jul 27 11:47:51 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import re import sys def get_libuv_version(): with open('../../include/uv-version.h') as f: data = f.read() try: m = re.search(r"""^#define UV_VERSION_MAJOR (\d)$""", data, re.MULTILINE) major = int(m.group(1)) m = re.search(r"""^#define UV_VERSION_MINOR (\d)$""", data, re.MULTILINE) minor = int(m.group(1)) m = re.search(r"""^#define UV_VERSION_PATCH (\d)$""", data, re.MULTILINE) patch = int(m.group(1)) m = re.search(r"""^#define UV_VERSION_IS_RELEASE (\d)$""", data, re.MULTILINE) is_release = int(m.group(1)) m = re.search(r"""^#define UV_VERSION_SUFFIX \"(\w*)\"$""", data, re.MULTILINE) suffix = m.group(1) return '%d.%d.%d%s' % (major, minor, patch, '-%s' % suffix if not is_release else '') except Exception: return 'unknown' # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('sphinx-plugins')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['manpage'] # Add any paths that contain templates here, relative to this directory. templates_path = ['templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'libuv API documentation' copyright = u'libuv contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = get_libuv_version() # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = 'libuv API documentation' # A shorter title for the navigation bar. Default is the same as html_title. html_short_title = 'libuv %s API documentation' % version # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'static/logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = 'static/favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'libuv' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'libuv.tex', u'libuv API documentation', u'libuv contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'libuv', u'libuv API documentation', [u'libuv contributors'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'libuv', u'libuv API documentation', u'libuv contributors', 'libuv', 'Cross-platform asynchronous I/O', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'libuv API documentation' epub_author = u'libuv contributors' epub_publisher = u'libuv contributors' epub_copyright = u'2014, libuv contributors' # The basename for the epub file. It defaults to the project name. epub_basename = u'libuv' # The HTML theme for the epub output. Since the default themes are not optimized # for small screen space, using the same theme for HTML and epub output is # usually not wise. This defaults to 'epub', a theme designed to save visual # space. #epub_theme = 'epub' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. #epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # Choose between 'default' and 'includehidden'. #epub_tocscope = 'default' # Fix unsupported image types using the PIL. #epub_fix_images = False # Scale large images. #epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. #epub_show_urls = 'inline' # If false, no index is generated. #epub_use_index = True libuv-v1.8.0/docs/src/design.rst000066400000000000000000000146001263361716600165650ustar00rootroot00000000000000 .. _design: Design overview =============== libuv is cross-platform support library which was originally written for NodeJS. It's designed around the event-driven asynchronous I/O model. The library provides much more than simply abstraction over different I/O polling mechanisms: 'handles' and 'streams' provide a high level abstraction for sockets and other entities; cross-platform file I/O and threading functionality is also provided, amongst other things. Here is a diagram illustrating the different parts that compose libuv and what subsystem they relate to: .. image:: static/architecture.png :scale: 75% :align: center Handles and requests ^^^^^^^^^^^^^^^^^^^^ libuv provides users with 2 abstractions to work with, in combination with the event loop: handles and requests. Handles represent long-lived objects capable of performing certain operations while active. Some examples: a prepare handle gets its callback called once every loop iteration when active, and a TCP server handle get its connection callback called every time there is a new connection. Requests represent (typically) short-lived operations. These operations can be performed over a handle: write requests are used to write data on a handle; or standalone: getaddrinfo requests don't need a handle they run directly on the loop. The I/O loop ^^^^^^^^^^^^ The I/O (or event) loop is the central part of libuv. It establishes the content for all I/O operations, and it's meant to be tied to a single thread. One can run multiple event loops as long as each runs in a different thread. The libuv event loop (or any other API involving the loop or handles, for that matter) **is not thread-safe** except where stated otherwise. The event loop follows the rather usual single threaded asynchronous I/O approach: all (network) I/O is performed on non-blocking sockets which are polled using the best mechanism available on the given platform: epoll on Linux, kqueue on OSX and other BSDs, event ports on SunOS and IOCP on Windows. As part of a loop iteration the loop will block waiting for I/O activity on sockets which have been added to the poller and callbacks will be fired indicating socket conditions (readable, writable hangup) so handles can read, write or perform the desired I/O operation. In order to better understand how the event loop operates, the following diagram illustrates all stages of a loop iteration: .. image:: static/loop_iteration.png :scale: 75% :align: center #. The loop concept of 'now' is updated. The event loop caches the current time at the start of the event loop tick in order to reduce the number of time-related system calls. #. If the loop is *alive* an iteration is started, otherwise the loop will exit immediately. So, when is a loop considered to be *alive*? If a loop has active and ref'd handles, active requests or closing handles it's considered to be *alive*. #. Due timers are run. All active timers scheduled for a time before the loop's concept of *now* get their callbacks called. #. Pending callbacks are called. All I/O callbacks are called right after polling for I/O, for the most part. There are cases, however, in which calling such a callback is deferred for the next loop iteration. If the previous iteration deferred any I/O callback it will be run at this point. #. Idle handle callbacks are called. Despite the unfortunate name, idle handles are run on every loop iteration, if they are active. #. Prepare handle callbacks are called. Prepare handles get their callbacks called right before the loop will block for I/O. #. Poll timeout is calculated. Before blocking for I/O the loop calculates for how long it should block. These are the rules when calculating the timeout: * If the loop was run with the ``UV_RUN_NOWAIT`` flag, the timeout is 0. * If the loop is going to be stopped (:c:func:`uv_stop` was called), the timeout is 0. * If there are no active handles or requests, the timeout is 0. * If there are any idle handles active, the timeout is 0. * If there are any handles pending to be closed, the timeout is 0. * If none of the above cases was matched, the timeout of the closest timer is taken, or if there are no active timers, infinity. #. The loop blocks for I/O. At this point the loop will block for I/O for the timeout calculated on the previous step. All I/O related handles that were monitoring a given file descriptor for a read or write operation get their callbacks called at this point. #. Check handle callbacks are called. Check handles get their callbacks called right after the loop has blocked for I/O. Check handles are essentially the counterpart of prepare handles. #. Close callbacks are called. If a handle was closed by calling :c:func:`uv_close` it will get the close callback called. #. Special case in case the loop was run with ``UV_RUN_ONCE``, as it implies forward progress. It's possible that no I/O callbacks were fired after blocking for I/O, but some time has passed so there might be timers which are due, those timers get their callbacks called. #. Iteration ends. If the loop was run with ``UV_RUN_NOWAIT`` or ``UV_RUN_ONCE`` modes the iteration is ended and :c:func:`uv_run` will return. If the loop was run with ``UV_RUN_DEFAULT`` it will continue from the start if it's still *alive*, otherwise it will also end. .. important:: libuv uses a thread pool to make asynchronous file I/O operations possible, but network I/O is **always** performed in a single thread, each loop's thread. .. note:: While the polling mechanism is different, libuv makes the execution model consistent across Unix systems and Windows. File I/O ^^^^^^^^ Unlike network I/O, there are no platform-specific file I/O primitives libuv could rely on, so the current approach is to run blocking file I/O operations in a thread pool. For a thorough explanation of the cross-platform file I/O landscape, checkout `this post `_. libuv currently uses a global thread pool on which all loops can queue work on. 3 types of operations are currently run on this pool: * Filesystem operations * DNS functions (getaddrinfo and getnameinfo) * User specified code via :c:func:`uv_queue_work` .. warning:: See the :c:ref:`threadpool` section for more details, but keep in mind the thread pool size is quite limited. libuv-v1.8.0/docs/src/dll.rst000066400000000000000000000017031263361716600160670ustar00rootroot00000000000000 .. _dll: Shared library handling ======================= libuv provides cross platform utilities for loading shared libraries and retrieving symbols from them, using the following API. Data types ---------- .. c:type:: uv_lib_t Shared library data type. Public members ^^^^^^^^^^^^^^ N/A API --- .. c:function:: int uv_dlopen(const char* filename, uv_lib_t* lib) Opens a shared library. The filename is in utf-8. Returns 0 on success and -1 on error. Call :c:func:`uv_dlerror` to get the error message. .. c:function:: void uv_dlclose(uv_lib_t* lib) Close the shared library. .. c:function:: int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) Retrieves a data pointer from a dynamic library. It is legal for a symbol to map to NULL. Returns 0 on success and -1 if the symbol was not found. .. c:function:: const char* uv_dlerror(const uv_lib_t* lib) Returns the last uv_dlopen() or uv_dlsym() error message. libuv-v1.8.0/docs/src/dns.rst000066400000000000000000000067661263361716600161160ustar00rootroot00000000000000 .. _dns: DNS utility functions ===================== libuv provides asynchronous variants of `getaddrinfo` and `getnameinfo`. Data types ---------- .. c:type:: uv_getaddrinfo_t `getaddrinfo` request type. .. c:type:: void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res) Callback which will be called with the getaddrinfo request result once complete. In case it was cancelled, `status` will have a value of ``UV_ECANCELED``. .. c:type:: uv_getnameinfo_t `getnameinfo` request type. .. c:type:: void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, int status, const char* hostname, const char* service) Callback which will be called with the getnameinfo request result once complete. In case it was cancelled, `status` will have a value of ``UV_ECANCELED``. Public members ^^^^^^^^^^^^^^ .. c:member:: uv_loop_t* uv_getaddrinfo_t.loop Loop that started this getaddrinfo request and where completion will be reported. Readonly. .. c:member:: struct addrinfo* uv_getaddrinfo_t.addrinfo Pointer to a `struct addrinfo` containing the result. Must be freed by the user with :c:func:`uv_freeaddrinfo`. .. versionchanged:: 1.3.0 the field is declared as public. .. c:member:: uv_loop_t* uv_getnameinfo_t.loop Loop that started this getnameinfo request and where completion will be reported. Readonly. .. c:member:: char[NI_MAXHOST] uv_getnameinfo_t.host Char array containing the resulting host. It's null terminated. .. versionchanged:: 1.3.0 the field is declared as public. .. c:member:: char[NI_MAXSERV] uv_getnameinfo_t.service Char array containing the resulting service. It's null terminated. .. versionchanged:: 1.3.0 the field is declared as public. .. seealso:: The :c:type:`uv_req_t` members also apply. API --- .. c:function:: int uv_getaddrinfo(uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb, const char* node, const char* service, const struct addrinfo* hints) Asynchronous :man:`getaddrinfo(3)`. Either node or service may be NULL but not both. `hints` is a pointer to a struct addrinfo with additional address type constraints, or NULL. Consult `man -s 3 getaddrinfo` for more details. Returns 0 on success or an error code < 0 on failure. If successful, the callback will get called sometime in the future with the lookup result, which is either: * status == 0, the res argument points to a valid `struct addrinfo`, or * status < 0, the res argument is NULL. See the UV_EAI_* constants. Call :c:func:`uv_freeaddrinfo` to free the addrinfo structure. .. versionchanged:: 1.3.0 the callback parameter is now allowed to be NULL, in which case the request will run **synchronously**. .. c:function:: void uv_freeaddrinfo(struct addrinfo* ai) Free the struct addrinfo. Passing NULL is allowed and is a no-op. .. c:function:: int uv_getnameinfo(uv_loop_t* loop, uv_getnameinfo_t* req, uv_getnameinfo_cb getnameinfo_cb, const struct sockaddr* addr, int flags) Asynchronous :man:`getnameinfo(3)`. Returns 0 on success or an error code < 0 on failure. If successful, the callback will get called sometime in the future with the lookup result. Consult `man -s 3 getnameinfo` for more details. .. versionchanged:: 1.3.0 the callback parameter is now allowed to be NULL, in which case the request will run **synchronously**. .. seealso:: The :c:type:`uv_req_t` API functions also apply. libuv-v1.8.0/docs/src/errors.rst000066400000000000000000000113701263361716600166310ustar00rootroot00000000000000 .. _errors: Error handling ============== In libuv errors are negative numbered constants. As a rule of thumb, whenever there is a status parameter, or an API functions returns an integer, a negative number will imply an error. .. note:: Implementation detail: on Unix error codes are the negated `errno` (or `-errno`), while on Windows they are defined by libuv to arbitrary negative numbers. Error constants --------------- .. c:macro:: UV_E2BIG argument list too long .. c:macro:: UV_EACCES permission denied .. c:macro:: UV_EADDRINUSE address already in use .. c:macro:: UV_EADDRNOTAVAIL address not available .. c:macro:: UV_EAFNOSUPPORT address family not supported .. c:macro:: UV_EAGAIN resource temporarily unavailable .. c:macro:: UV_EAI_ADDRFAMILY address family not supported .. c:macro:: UV_EAI_AGAIN temporary failure .. c:macro:: UV_EAI_BADFLAGS bad ai_flags value .. c:macro:: UV_EAI_BADHINTS invalid value for hints .. c:macro:: UV_EAI_CANCELED request canceled .. c:macro:: UV_EAI_FAIL permanent failure .. c:macro:: UV_EAI_FAMILY ai_family not supported .. c:macro:: UV_EAI_MEMORY out of memory .. c:macro:: UV_EAI_NODATA no address .. c:macro:: UV_EAI_NONAME unknown node or service .. c:macro:: UV_EAI_OVERFLOW argument buffer overflow .. c:macro:: UV_EAI_PROTOCOL resolved protocol is unknown .. c:macro:: UV_EAI_SERVICE service not available for socket type .. c:macro:: UV_EAI_SOCKTYPE socket type not supported .. c:macro:: UV_EALREADY connection already in progress .. c:macro:: UV_EBADF bad file descriptor .. c:macro:: UV_EBUSY resource busy or locked .. c:macro:: UV_ECANCELED operation canceled .. c:macro:: UV_ECHARSET invalid Unicode character .. c:macro:: UV_ECONNABORTED software caused connection abort .. c:macro:: UV_ECONNREFUSED connection refused .. c:macro:: UV_ECONNRESET connection reset by peer .. c:macro:: UV_EDESTADDRREQ destination address required .. c:macro:: UV_EEXIST file already exists .. c:macro:: UV_EFAULT bad address in system call argument .. c:macro:: UV_EFBIG file too large .. c:macro:: UV_EHOSTUNREACH host is unreachable .. c:macro:: UV_EINTR interrupted system call .. c:macro:: UV_EINVAL invalid argument .. c:macro:: UV_EIO i/o error .. c:macro:: UV_EISCONN socket is already connected .. c:macro:: UV_EISDIR illegal operation on a directory .. c:macro:: UV_ELOOP too many symbolic links encountered .. c:macro:: UV_EMFILE too many open files .. c:macro:: UV_EMSGSIZE message too long .. c:macro:: UV_ENAMETOOLONG name too long .. c:macro:: UV_ENETDOWN network is down .. c:macro:: UV_ENETUNREACH network is unreachable .. c:macro:: UV_ENFILE file table overflow .. c:macro:: UV_ENOBUFS no buffer space available .. c:macro:: UV_ENODEV no such device .. c:macro:: UV_ENOENT no such file or directory .. c:macro:: UV_ENOMEM not enough memory .. c:macro:: UV_ENONET machine is not on the network .. c:macro:: UV_ENOPROTOOPT protocol not available .. c:macro:: UV_ENOSPC no space left on device .. c:macro:: UV_ENOSYS function not implemented .. c:macro:: UV_ENOTCONN socket is not connected .. c:macro:: UV_ENOTDIR not a directory .. c:macro:: UV_ENOTEMPTY directory not empty .. c:macro:: UV_ENOTSOCK socket operation on non-socket .. c:macro:: UV_ENOTSUP operation not supported on socket .. c:macro:: UV_EPERM operation not permitted .. c:macro:: UV_EPIPE broken pipe .. c:macro:: UV_EPROTO protocol error .. c:macro:: UV_EPROTONOSUPPORT protocol not supported .. c:macro:: UV_EPROTOTYPE protocol wrong type for socket .. c:macro:: UV_ERANGE result too large .. c:macro:: UV_EROFS read-only file system .. c:macro:: UV_ESHUTDOWN cannot send after transport endpoint shutdown .. c:macro:: UV_ESPIPE invalid seek .. c:macro:: UV_ESRCH no such process .. c:macro:: UV_ETIMEDOUT connection timed out .. c:macro:: UV_ETXTBSY text file is busy .. c:macro:: UV_EXDEV cross-device link not permitted .. c:macro:: UV_UNKNOWN unknown error .. c:macro:: UV_EOF end of file .. c:macro:: UV_ENXIO no such device or address .. c:macro:: UV_EMLINK too many links API --- .. c:function:: const char* uv_strerror(int err) Returns the error message for the given error code. Leaks a few bytes of memory when you call it with an unknown error code. .. c:function:: const char* uv_err_name(int err) Returns the error name for the given error code. Leaks a few bytes of memory when you call it with an unknown error code. libuv-v1.8.0/docs/src/fs.rst000066400000000000000000000225451263361716600157330ustar00rootroot00000000000000 .. _fs: Filesystem operations ===================== libuv provides a wide variety of cross-platform sync and async filesystem operations. All functions defined in this document take a callback, which is allowed to be NULL. If the callback is NULL the request is completed synchronously, otherwise it will be performed asynchronously. All file operations are run on the threadpool, see :ref:`threadpool` for information on the threadpool size. Data types ---------- .. c:type:: uv_fs_t Filesystem request type. .. c:type:: uv_timespec_t Portable equivalent of ``struct timespec``. :: typedef struct { long tv_sec; long tv_nsec; } uv_timespec_t; .. c:type:: uv_stat_t Portable equivalent of ``struct stat``. :: typedef struct { uint64_t st_dev; uint64_t st_mode; uint64_t st_nlink; uint64_t st_uid; uint64_t st_gid; uint64_t st_rdev; uint64_t st_ino; uint64_t st_size; uint64_t st_blksize; uint64_t st_blocks; uint64_t st_flags; uint64_t st_gen; uv_timespec_t st_atim; uv_timespec_t st_mtim; uv_timespec_t st_ctim; uv_timespec_t st_birthtim; } uv_stat_t; .. c:type:: uv_fs_type Filesystem request type. :: typedef enum { UV_FS_UNKNOWN = -1, UV_FS_CUSTOM, UV_FS_OPEN, UV_FS_CLOSE, UV_FS_READ, UV_FS_WRITE, UV_FS_SENDFILE, UV_FS_STAT, UV_FS_LSTAT, UV_FS_FSTAT, UV_FS_FTRUNCATE, UV_FS_UTIME, UV_FS_FUTIME, UV_FS_ACCESS, UV_FS_CHMOD, UV_FS_FCHMOD, UV_FS_FSYNC, UV_FS_FDATASYNC, UV_FS_UNLINK, UV_FS_RMDIR, UV_FS_MKDIR, UV_FS_MKDTEMP, UV_FS_RENAME, UV_FS_SCANDIR, UV_FS_LINK, UV_FS_SYMLINK, UV_FS_READLINK, UV_FS_CHOWN, UV_FS_FCHOWN } uv_fs_type; .. c:type:: uv_dirent_t Cross platform (reduced) equivalent of ``struct dirent``. Used in :c:func:`uv_fs_scandir_next`. :: typedef enum { UV_DIRENT_UNKNOWN, UV_DIRENT_FILE, UV_DIRENT_DIR, UV_DIRENT_LINK, UV_DIRENT_FIFO, UV_DIRENT_SOCKET, UV_DIRENT_CHAR, UV_DIRENT_BLOCK } uv_dirent_type_t; typedef struct uv_dirent_s { const char* name; uv_dirent_type_t type; } uv_dirent_t; Public members ^^^^^^^^^^^^^^ .. c:member:: uv_loop_t* uv_fs_t.loop Loop that started this request and where completion will be reported. Readonly. .. c:member:: uv_fs_type uv_fs_t.fs_type FS request type. .. c:member:: const char* uv_fs_t.path Path affecting the request. .. c:member:: ssize_t uv_fs_t.result Result of the request. < 0 means error, success otherwise. On requests such as :c:func:`uv_fs_read` or :c:func:`uv_fs_write` it indicates the amount of data that was read or written, respectively. .. c:member:: uv_stat_t uv_fs_t.statbuf Stores the result of :c:func:`uv_fs_stat` and other stat requests. .. c:member:: void* uv_fs_t.ptr Stores the result of :c:func:`uv_fs_readlink` and serves as an alias to `statbuf`. .. seealso:: The :c:type:`uv_req_t` members also apply. API --- .. c:function:: void uv_fs_req_cleanup(uv_fs_t* req) Cleanup request. Must be called after a request is finished to deallocate any memory libuv might have allocated. .. c:function:: int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) Equivalent to :man:`close(2)`. .. c:function:: int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) Equivalent to :man:`open(2)`. .. note:: On Windows libuv uses `CreateFileW` and thus the file is always opened in binary mode. Because of this the O_BINARY and O_TEXT flags are not supported. .. c:function:: int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) Equivalent to :man:`preadv(2)`. .. c:function:: int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) Equivalent to :man:`unlink(2)`. .. c:function:: int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) Equivalent to :man:`pwritev(2)`. .. c:function:: int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) Equivalent to :man:`mkdir(2)`. .. note:: `mode` is currently not implemented on Windows. .. c:function:: int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb) Equivalent to :man:`mkdtemp(3)`. .. note:: The result can be found as a null terminated string at `req->path`. .. c:function:: int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) Equivalent to :man:`rmdir(2)`. .. c:function:: int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) .. c:function:: int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) Equivalent to :man:`scandir(3)`, with a slightly different API. Once the callback for the request is called, the user can use :c:func:`uv_fs_scandir_next` to get `ent` populated with the next directory entry data. When there are no more entries ``UV_EOF`` will be returned. .. note:: Unlike `scandir(3)`, this function does not return the "." and ".." entries. .. note:: On Linux, getting the type of an entry is only supported by some filesystems (btrfs, ext2, ext3 and ext4 at the time of this writing), check the :man:`getdents(2)` man page. .. c:function:: int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) .. c:function:: int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) .. c:function:: int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) Equivalent to :man:`stat(2)`, :man:`fstat(2)` and :man:`fstat(2)` respectively. .. c:function:: int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) Equivalent to :man:`rename(2)`. .. c:function:: int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) Equivalent to :man:`fsync(2)`. .. c:function:: int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) Equivalent to :man:`fdatasync(2)`. .. c:function:: int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb) Equivalent to :man:`ftruncate(2)`. .. c:function:: int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb) Limited equivalent to :man:`sendfile(2)`. .. c:function:: int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) Equivalent to :man:`access(2)` on Unix. Windows uses ``GetFileAttributesW()``. .. c:function:: int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) .. c:function:: int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb) Equivalent to :man:`chmod(2)` and :man:`fchmod(2)` respectively. .. c:function:: int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) .. c:function:: int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb) Equivalent to :man:`utime(2)` and :man:`futime(2)` respectively. .. c:function:: int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) Equivalent to :man:`link(2)`. .. c:function:: int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) Equivalent to :man:`symlink(2)`. .. note:: On Windows the `flags` parameter can be specified to control how the symlink will be created: * ``UV_FS_SYMLINK_DIR``: indicates that `path` points to a directory. * ``UV_FS_SYMLINK_JUNCTION``: request that the symlink is created using junction points. .. c:function:: int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) Equivalent to :man:`readlink(2)`. .. c:function:: int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) Equivalent to :man:`realpath(3)` on Unix. Windows uses ``GetFinalPathNameByHandle()``. .. note:: This function is not implemented on Windows XP and Windows Server 2003. On these systems, UV_ENOSYS is returned. .. versionadded:: 1.8.0 .. c:function:: int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) .. c:function:: int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) Equivalent to :man:`chown(2)` and :man:`fchown(2)` respectively. .. note:: These functions are not implemented on Windows. .. seealso:: The :c:type:`uv_req_t` API functions also apply. libuv-v1.8.0/docs/src/fs_event.rst000066400000000000000000000073001263361716600171240ustar00rootroot00000000000000 .. _fs_event: :c:type:`uv_fs_event_t` --- FS Event handle =========================================== FS Event handles allow the user to monitor a given path for changes, for example, if the file was renamed or there was a generic change in it. This handle uses the best backend for the job on each platform. Data types ---------- .. c:type:: uv_fs_event_t FS Event handle type. .. c:type:: void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, int events, int status) Callback passed to :c:func:`uv_fs_event_start` which will be called repeatedly after the handle is started. If the handle was started with a directory the `filename` parameter will be a relative path to a file contained in the directory. The `events` parameter is an ORed mask of :c:type:`uv_fs_event` elements. .. c:type:: uv_fs_event Event types that :c:type:`uv_fs_event_t` handles monitor. :: enum uv_fs_event { UV_RENAME = 1, UV_CHANGE = 2 }; .. c:type:: uv_fs_event_flags Flags that can be passed to :c:func:`uv_fs_event_start` to control its behavior. :: enum uv_fs_event_flags { /* * By default, if the fs event watcher is given a directory name, we will * watch for all events in that directory. This flags overrides this behavior * and makes fs_event report only changes to the directory entry itself. This * flag does not affect individual files watched. * This flag is currently not implemented yet on any backend. */ UV_FS_EVENT_WATCH_ENTRY = 1, /* * By default uv_fs_event will try to use a kernel interface such as inotify * or kqueue to detect events. This may not work on remote filesystems such * as NFS mounts. This flag makes fs_event fall back to calling stat() on a * regular interval. * This flag is currently not implemented yet on any backend. */ UV_FS_EVENT_STAT = 2, /* * By default, event watcher, when watching directory, is not registering * (is ignoring) changes in it's subdirectories. * This flag will override this behaviour on platforms that support it. */ UV_FS_EVENT_RECURSIVE = 4 }; Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) Initialize the handle. .. c:function:: int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) Start the handle with the given callback, which will watch the specified `path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`. .. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and only on OSX and Windows. .. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle) Stop the handle, the callback will no longer be called. .. c:function:: int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) Get the path being monitored by the handle. The buffer must be preallocated by the user. Returns 0 on success or an error code < 0 in case of failure. On success, `buffer` will contain the path and `size` its length. If the buffer is not big enough UV_ENOBUFS will be returned and len will be set to the required size. .. versionchanged:: 1.3.0 the returned length no longer includes the terminating null byte, and the buffer is not null terminated. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/fs_poll.rst000066400000000000000000000046071263361716600167600ustar00rootroot00000000000000 .. _fs_poll: :c:type:`uv_fs_poll_t` --- FS Poll handle ========================================= FS Poll handles allow the user to monitor a given path for changes. Unlike :c:type:`uv_fs_event_t`, fs poll handles use `stat` to detect when a file has changed so they can work on file systems where fs event handles can't. Data types ---------- .. c:type:: uv_fs_poll_t FS Poll handle type. .. c:type:: void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr) Callback passed to :c:func:`uv_fs_poll_start` which will be called repeatedly after the handle is started, when any change happens to the monitored path. The callback is invoked with `status < 0` if `path` does not exist or is inaccessible. The watcher is *not* stopped but your callback is not called again until something changes (e.g. when the file is created or the error reason changes). When `status == 0`, the callback receives pointers to the old and new :c:type:`uv_stat_t` structs. They are valid for the duration of the callback only. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) Initialize the handle. .. c:function:: int uv_fs_poll_start(uv_fs_poll_t* handle, uv_fs_poll_cb poll_cb, const char* path, unsigned int interval) Check the file at `path` for changes every `interval` milliseconds. .. note:: For maximum portability, use multi-second intervals. Sub-second intervals will not detect all changes on many file systems. .. c:function:: int uv_fs_poll_stop(uv_fs_poll_t* handle) Stop the handle, the callback will no longer be called. .. c:function:: int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) Get the path being monitored by the handle. The buffer must be preallocated by the user. Returns 0 on success or an error code < 0 in case of failure. On success, `buffer` will contain the path and `size` its length. If the buffer is not big enough UV_ENOBUFS will be returned and len will be set to the required size. .. versionchanged:: 1.3.0 the returned length no longer includes the terminating null byte, and the buffer is not null terminated. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/handle.rst000066400000000000000000000143301263361716600165470ustar00rootroot00000000000000 .. _handle: :c:type:`uv_handle_t` --- Base handle ===================================== `uv_handle_t` is the base type for all libuv handle types. Structures are aligned so that any libuv handle can be cast to `uv_handle_t`. All API functions defined here work with any handle type. Data types ---------- .. c:type:: uv_handle_t The base libuv handle type. .. c:type:: uv_any_handle Union of all handle types. .. c:type:: void (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) Type definition for callback passed to :c:func:`uv_read_start` and :c:func:`uv_udp_recv_start`. The user must fill the supplied :c:type:`uv_buf_t` structure with whatever size, as long as it's > 0. A suggested size (65536 at the moment) is provided, but it doesn't need to be honored. Setting the buffer's length to 0 will trigger a ``UV_ENOBUFS`` error in the :c:type:`uv_udp_recv_cb` or :c:type:`uv_read_cb` callback. .. c:type:: void (*uv_close_cb)(uv_handle_t* handle) Type definition for callback passed to :c:func:`uv_close`. Public members ^^^^^^^^^^^^^^ .. c:member:: uv_loop_t* uv_handle_t.loop Pointer to the :c:type:`uv_loop_t` where the handle is running on. Readonly. .. c:member:: void* uv_handle_t.data Space for user-defined arbitrary data. libuv does not use this field. API --- .. c:function:: int uv_is_active(const uv_handle_t* handle) Returns non-zero if the handle is active, zero if it's inactive. What "active" means depends on the type of handle: - A uv_async_t handle is always active and cannot be deactivated, except by closing it with uv_close(). - A uv_pipe_t, uv_tcp_t, uv_udp_t, etc. handle - basically any handle that deals with i/o - is active when it is doing something that involves i/o, like reading, writing, connecting, accepting new connections, etc. - A uv_check_t, uv_idle_t, uv_timer_t, etc. handle is active when it has been started with a call to uv_check_start(), uv_idle_start(), etc. Rule of thumb: if a handle of type `uv_foo_t` has a `uv_foo_start()` function, then it's active from the moment that function is called. Likewise, `uv_foo_stop()` deactivates the handle again. .. c:function:: int uv_is_closing(const uv_handle_t* handle) Returns non-zero if the handle is closing or closed, zero otherwise. .. note:: This function should only be used between the initialization of the handle and the arrival of the close callback. .. c:function:: void uv_close(uv_handle_t* handle, uv_close_cb close_cb) Request handle to be closed. `close_cb` will be called asynchronously after this call. This MUST be called on each handle before memory is released. Handles that wrap file descriptors are closed immediately but `close_cb` will still be deferred to the next iteration of the event loop. It gives you a chance to free up any resources associated with the handle. In-progress requests, like uv_connect_t or uv_write_t, are cancelled and have their callbacks called asynchronously with status=UV_ECANCELED. .. c:function:: void uv_ref(uv_handle_t* handle) Reference the given handle. References are idempotent, that is, if a handle is already referenced calling this function again will have no effect. See :ref:`refcount`. .. c:function:: void uv_unref(uv_handle_t* handle) Un-reference the given handle. References are idempotent, that is, if a handle is not referenced calling this function again will have no effect. See :ref:`refcount`. .. c:function:: int uv_has_ref(const uv_handle_t* handle) Returns non-zero if the handle referenced, zero otherwise. See :ref:`refcount`. .. c:function:: size_t uv_handle_size(uv_handle_type type) Returns the size of the given handle type. Useful for FFI binding writers who don't want to know the structure layout. Miscellaneous API functions --------------------------- The following API functions take a :c:type:`uv_handle_t` argument but they work just for some handle types. .. c:function:: int uv_send_buffer_size(uv_handle_t* handle, int* value) Gets or sets the size of the send buffer that the operating system uses for the socket. If `*value` == 0, it will return the current send buffer size, otherwise it will use `*value` to set the new send buffer size. This function works for TCP, pipe and UDP handles on Unix and for TCP and UDP handles on Windows. .. note:: Linux will set double the size and return double the size of the original set value. .. c:function:: int uv_recv_buffer_size(uv_handle_t* handle, int* value) Gets or sets the size of the receive buffer that the operating system uses for the socket. If `*value` == 0, it will return the current receive buffer size, otherwise it will use `*value` to set the new receive buffer size. This function works for TCP, pipe and UDP handles on Unix and for TCP and UDP handles on Windows. .. note:: Linux will set double the size and return double the size of the original set value. .. c:function:: int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) Gets the platform dependent file descriptor equivalent. The following handles are supported: TCP, pipes, TTY, UDP and poll. Passing any other handle type will fail with `UV_EINVAL`. If a handle doesn't have an attached file descriptor yet or the handle itself has been closed, this function will return `UV_EBADF`. .. warning:: Be very careful when using this function. libuv assumes it's in control of the file descriptor so any change to it may lead to malfunction. .. _refcount: Reference counting ------------------ The libuv event loop (if run in the default mode) will run until there are no active `and` referenced handles left. The user can force the loop to exit early by unreferencing handles which are active, for example by calling :c:func:`uv_unref` after calling :c:func:`uv_timer_start`. A handle can be referenced or unreferenced, the refcounting scheme doesn't use a counter, so both operations are idempotent. All handles are referenced when active by default, see :c:func:`uv_is_active` for a more detailed explanation on what being `active` involves. libuv-v1.8.0/docs/src/idle.rst000066400000000000000000000022731263361716600162340ustar00rootroot00000000000000 .. _idle: :c:type:`uv_idle_t` --- Idle handle =================================== Idle handles will run the given callback once per loop iteration, right before the :c:type:`uv_prepare_t` handles. .. note:: The notable difference with prepare handles is that when there are active idle handles, the loop will perform a zero timeout poll instead of blocking for i/o. .. warning:: Despite the name, idle handles will get their callbacks called on every loop iteration, not when the loop is actually "idle". Data types ---------- .. c:type:: uv_idle_t Idle handle type. .. c:type:: void (*uv_idle_cb)(uv_idle_t* handle) Type definition for callback passed to :c:func:`uv_idle_start`. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle) Initialize the handle. .. c:function:: int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) Start the handle with the given callback. .. c:function:: int uv_idle_stop(uv_idle_t* idle) Stop the handle, the callback will no longer be called. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/index.rst000066400000000000000000000033571263361716600164320ustar00rootroot00000000000000 Welcome to the libuv API documentation ====================================== Overview -------- libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by `Node.js`_, but it's also used by `Luvit`_, `Julia`_, `pyuv`_, and `others`_. .. note:: In case you find errors in this documentation you can help by sending `pull requests `_! .. _Node.js: http://nodejs.org .. _Luvit: http://luvit.io .. _Julia: http://julialang.org .. _pyuv: https://github.com/saghul/pyuv .. _others: https://github.com/libuv/libuv/wiki/Projects-that-use-libuv Features -------- * Full-featured event loop backed by epoll, kqueue, IOCP, event ports. * Asynchronous TCP and UDP sockets * Asynchronous DNS resolution * Asynchronous file and file system operations * File system events * ANSI escape code controlled TTY * IPC with socket sharing, using Unix domain sockets or named pipes (Windows) * Child processes * Thread pool * Signal handling * High resolution clock * Threading and synchronization primitives Downloads --------- libuv can be downloaded from `here `_. Installation ------------ Installation instructions can be found on `the README `_. Upgrading --------- Migration guides for different libuv versions, starting with 1.0. .. toctree:: :maxdepth: 1 migration_010_100 Documentation ------------- .. toctree:: :maxdepth: 1 design errors version loop handle request timer prepare check idle async poll signal process stream tcp pipe tty udp fs_event fs_poll fs threadpool dns dll threading misc libuv-v1.8.0/docs/src/loop.rst000066400000000000000000000135471263361716600162760ustar00rootroot00000000000000 .. _loop: :c:type:`uv_loop_t` --- Event loop ================================== The event loop is the central part of libuv's functionality. It takes care of polling for i/o and scheduling callbacks to be run based on different sources of events. Data types ---------- .. c:type:: uv_loop_t Loop data type. .. c:type:: uv_run_mode Mode used to run the loop with :c:func:`uv_run`. :: typedef enum { UV_RUN_DEFAULT = 0, UV_RUN_ONCE, UV_RUN_NOWAIT } uv_run_mode; .. c:type:: void (*uv_walk_cb)(uv_handle_t* handle, void* arg) Type definition for callback passed to :c:func:`uv_walk`. Public members ^^^^^^^^^^^^^^ .. c:member:: void* uv_loop_t.data Space for user-defined arbitrary data. libuv does not use this field. libuv does, however, initialize it to NULL in :c:func:`uv_loop_init`, and it poisons the value (on debug builds) on :c:func:`uv_loop_close`. API --- .. c:function:: int uv_loop_init(uv_loop_t* loop) Initializes the given `uv_loop_t` structure. .. c:function:: int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) .. versionadded:: 1.0.2 Set additional loop options. You should normally call this before the first call to :c:func:`uv_run` unless mentioned otherwise. Returns 0 on success or a UV_E* error code on failure. Be prepared to handle UV_ENOSYS; it means the loop option is not supported by the platform. Supported options: - UV_LOOP_BLOCK_SIGNAL: Block a signal when polling for new events. The second argument to :c:func:`uv_loop_configure` is the signal number. This operation is currently only implemented for SIGPROF signals, to suppress unnecessary wakeups when using a sampling profiler. Requesting other signals will fail with UV_EINVAL. .. c:function:: int uv_loop_close(uv_loop_t* loop) Closes all internal loop resources. This function must only be called once the loop has finished its execution or it will return UV_EBUSY. After this function returns the user shall free the memory allocated for the loop. .. c:function:: uv_loop_t* uv_default_loop(void) Returns the initialized default loop. It may return NULL in case of allocation failure. This function is just a convenient way for having a global loop throughout an application, the default loop is in no way different than the ones initialized with :c:func:`uv_loop_init`. As such, the default loop can (and should) be closed with :c:func:`uv_loop_close` so the resources associated with it are freed. .. c:function:: int uv_run(uv_loop_t* loop, uv_run_mode mode) This function runs the event loop. It will act differently depending on the specified mode: - UV_RUN_DEFAULT: Runs the event loop until there are no more active and referenced handles or requests. Returns non-zero if :c:func:`uv_stop` was called and there are still active handles or requests. Returns zero in all other cases. - UV_RUN_ONCE: Poll for i/o once. Note that this function blocks if there are no pending callbacks. Returns zero when done (no active handles or requests left), or non-zero if more callbacks are expected (meaning you should run the event loop again sometime in the future). - UV_RUN_NOWAIT: Poll for i/o once but don't block if there are no pending callbacks. Returns zero if done (no active handles or requests left), or non-zero if more callbacks are expected (meaning you should run the event loop again sometime in the future). .. c:function:: int uv_loop_alive(const uv_loop_t* loop) Returns non-zero if there are active handles or request in the loop. .. c:function:: void uv_stop(uv_loop_t* loop) Stop the event loop, causing :c:func:`uv_run` to end as soon as possible. This will happen not sooner than the next loop iteration. If this function was called before blocking for i/o, the loop won't block for i/o on this iteration. .. c:function:: size_t uv_loop_size(void) Returns the size of the `uv_loop_t` structure. Useful for FFI binding writers who don't want to know the structure layout. .. c:function:: int uv_backend_fd(const uv_loop_t* loop) Get backend file descriptor. Only kqueue, epoll and event ports are supported. This can be used in conjunction with `uv_run(loop, UV_RUN_NOWAIT)` to poll in one thread and run the event loop's callbacks in another see test/test-embed.c for an example. .. note:: Embedding a kqueue fd in another kqueue pollset doesn't work on all platforms. It's not an error to add the fd but it never generates events. .. c:function:: int uv_backend_timeout(const uv_loop_t* loop) Get the poll timeout. The return value is in milliseconds, or -1 for no timeout. .. c:function:: uint64_t uv_now(const uv_loop_t* loop) Return the current timestamp in milliseconds. The timestamp is cached at the start of the event loop tick, see :c:func:`uv_update_time` for details and rationale. The timestamp increases monotonically from some arbitrary point in time. Don't make assumptions about the starting point, you will only get disappointed. .. note:: Use :c:func:`uv_hrtime` if you need sub-millisecond granularity. .. c:function:: void uv_update_time(uv_loop_t* loop) Update the event loop's concept of "now". Libuv caches the current time at the start of the event loop tick in order to reduce the number of time-related system calls. You won't normally need to call this function unless you have callbacks that block the event loop for longer periods of time, where "longer" is somewhat subjective but probably on the order of a millisecond or more. .. c:function:: void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) Walk the list of handles: `walk_cb` will be executed with the given `arg`. libuv-v1.8.0/docs/src/migration_010_100.rst000066400000000000000000000147501263361716600202530ustar00rootroot00000000000000 .. _migration_010_100: libuv 0.10 -> 1.0.0 migration guide =================================== Some APIs changed quite a bit throughout the 1.0.0 development process. Here is a migration guide for the most significant changes that happened after 0.10 was released. Loop initialization and closing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In libuv 0.10 (and previous versions), loops were created with `uv_loop_new`, which allocated memory for a new loop and initialized it; and destroyed with `uv_loop_delete`, which destroyed the loop and freed the memory. Starting with 1.0, those are deprecated and the user is responsible for allocating the memory and then initializing the loop. libuv 0.10 :: uv_loop_t* loop = uv_loop_new(); ... uv_loop_delete(loop); libuv 1.0 :: uv_loop_t* loop = malloc(sizeof *loop); uv_loop_init(loop); ... uv_loop_close(loop); free(loop); .. note:: Error handling was omitted for brevity. Check the documentation for :c:func:`uv_loop_init` and :c:func:`uv_loop_close`. Error handling ~~~~~~~~~~~~~~ Error handling had a major overhaul in libuv 1.0. In general, functions and status parameters would get 0 for success and -1 for failure on libuv 0.10, and the user had to use `uv_last_error` to fetch the error code, which was a positive number. In 1.0, functions and status parameters contain the actual error code, which is 0 for success, or a negative number in case of error. libuv 0.10 :: ... assume 'server' is a TCP server which is already listening r = uv_listen((uv_stream_t*) server, 511, NULL); if (r == -1) { uv_err_t err = uv_last_error(uv_default_loop()); /* err.code contains UV_EADDRINUSE */ } libuv 1.0 :: ... assume 'server' is a TCP server which is already listening r = uv_listen((uv_stream_t*) server, 511, NULL); if (r < 0) { /* r contains UV_EADDRINUSE */ } Threadpool changes ~~~~~~~~~~~~~~~~~~ In libuv 0.10 Unix used a threadpool which defaulted to 4 threads, while Windows used the `QueueUserWorkItem` API, which uses a Windows internal threadpool, which defaults to 512 threads per process. In 1.0, we unified both implementations, so Windows now uses the same implementation Unix does. The threadpool size can be set by exporting the ``UV_THREADPOOL_SIZE`` environment variable. See :c:ref:`threadpool`. Allocation callback API change ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In libuv 0.10 the callback had to return a filled :c:type:`uv_buf_t` by value: :: uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { return uv_buf_init(malloc(size), size); } In libuv 1.0 a pointer to a buffer is passed to the callback, which the user needs to fill: :: void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = malloc(size); buf->len = size; } Unification of IPv4 / IPv6 APIs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ libuv 1.0 unified the IPv4 and IPv6 APIS. There is no longer a `uv_tcp_bind` and `uv_tcp_bind6` duality, there is only :c:func:`uv_tcp_bind` now. IPv4 functions took ``struct sockaddr_in`` structures by value, and IPv6 functions took ``struct sockaddr_in6``. Now functions take a ``struct sockaddr*`` (note it's a pointer). It can be stack allocated. libuv 0.10 :: struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", 1234); ... uv_tcp_bind(&server, addr) libuv 1.0 :: struct sockaddr_in addr; uv_ip4_addr("0.0.0.0", 1234, &addr) ... uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); The IPv4 and IPv6 struct creating functions (:c:func:`uv_ip4_addr` and :c:func:`uv_ip6_addr`) have also changed, make sure you check the documentation. ..note:: This change applies to all functions that made a distinction between IPv4 and IPv6 addresses. Streams / UDP data receive callback API change ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The streams and UDP data receive callbacks now get a pointer to a :c:type:`uv_buf_t` buffer, not a structure by value. libuv 0.10 :: void on_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { ... } void recv_cb(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, struct sockaddr* addr, unsigned flags) { ... } libuv 1.0 :: void on_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { ... } void recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { ... } Receiving handles over pipes API change ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In libuv 0.10 (and earlier versions) the `uv_read2_start` function was used to start reading data on a pipe, which could also result in the reception of handles over it. The callback for such function looked like this: :: void on_read(uv_pipe_t* pipe, ssize_t nread, uv_buf_t buf, uv_handle_type pending) { ... } In libuv 1.0, `uv_read2_start` was removed, and the user needs to check if there are pending handles using :c:func:`uv_pipe_pending_count` and :c:func:`uv_pipe_pending_type` while in the read callback: :: void on_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { ... while (uv_pipe_pending_count((uv_pipe_t*) handle) != 0) { pending = uv_pipe_pending_type((uv_pipe_t*) handle); ... } ... } Extracting the file descriptor out of a handle ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ While it wasn't supported by the API, users often accessed the libuv internals in order to get access to the file descriptor of a TCP handle, for example. :: fd = handle->io_watcher.fd; This is now properly exposed through the :c:func:`uv_fileno` function. uv_fs_readdir rename and API change ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `uv_fs_readdir` returned a list of strings in the `req->ptr` field upon completion in libuv 0.10. In 1.0, this function got renamed to :c:func:`uv_fs_scandir`, since it's actually implemented using ``scandir(3)``. In addition, instead of allocating a full list strings, the user is able to get one result at a time by using the :c:func:`uv_fs_scandir_next` function. This function does not need to make a roundtrip to the threadpool, because libuv will keep the list of *dents* returned by ``scandir(3)`` around. libuv-v1.8.0/docs/src/misc.rst000066400000000000000000000250061263361716600162510ustar00rootroot00000000000000 .. _misc: Miscellaneous utilities ======================= This section contains miscellaneous functions that don't really belong in any other section. Data types ---------- .. c:type:: uv_buf_t Buffer data type. .. c:member:: char* uv_buf_t.base Pointer to the base of the buffer. Readonly. .. c:member:: size_t uv_buf_t.len Total bytes in the buffer. Readonly. .. note:: On Windows this field is ULONG. .. c:type:: void* (*uv_malloc_func)(size_t size) Replacement function for :man:`malloc(3)`. See :c:func:`uv_replace_allocator`. .. c:type:: void* (*uv_realloc_func)(void* ptr, size_t size) Replacement function for :man:`realloc(3)`. See :c:func:`uv_replace_allocator`. .. c:type:: void* (*uv_calloc_func)(size_t count, size_t size) Replacement function for :man:`calloc(3)`. See :c:func:`uv_replace_allocator`. .. c:type:: void (*uv_free_func)(void* ptr) Replacement function for :man:`free(3)`. See :c:func:`uv_replace_allocator`. .. c:type:: uv_file Cross platform representation of a file handle. .. c:type:: uv_os_sock_t Cross platform representation of a socket handle. .. c:type:: uv_os_fd_t Abstract representation of a file descriptor. On Unix systems this is a `typedef` of `int` and on Windows a `HANDLE`. .. c:type:: uv_rusage_t Data type for resource usage results. :: typedef struct { uv_timeval_t ru_utime; /* user CPU time used */ uv_timeval_t ru_stime; /* system CPU time used */ uint64_t ru_maxrss; /* maximum resident set size */ uint64_t ru_ixrss; /* integral shared memory size */ uint64_t ru_idrss; /* integral unshared data size */ uint64_t ru_isrss; /* integral unshared stack size */ uint64_t ru_minflt; /* page reclaims (soft page faults) */ uint64_t ru_majflt; /* page faults (hard page faults) */ uint64_t ru_nswap; /* swaps */ uint64_t ru_inblock; /* block input operations */ uint64_t ru_oublock; /* block output operations */ uint64_t ru_msgsnd; /* IPC messages sent */ uint64_t ru_msgrcv; /* IPC messages received */ uint64_t ru_nsignals; /* signals received */ uint64_t ru_nvcsw; /* voluntary context switches */ uint64_t ru_nivcsw; /* involuntary context switches */ } uv_rusage_t; .. c:type:: uv_cpu_info_t Data type for CPU information. :: typedef struct uv_cpu_info_s { char* model; int speed; struct uv_cpu_times_s { uint64_t user; uint64_t nice; uint64_t sys; uint64_t idle; uint64_t irq; } cpu_times; } uv_cpu_info_t; .. c:type:: uv_interface_address_t Data type for interface addresses. :: typedef struct uv_interface_address_s { char* name; char phys_addr[6]; int is_internal; union { struct sockaddr_in address4; struct sockaddr_in6 address6; } address; union { struct sockaddr_in netmask4; struct sockaddr_in6 netmask6; } netmask; } uv_interface_address_t; API --- .. c:function:: uv_handle_type uv_guess_handle(uv_file file) Used to detect what type of stream should be used with a given file descriptor. Usually this will be used during initialization to guess the type of the stdio streams. For :man:`isatty(3)` equivalent functionality use this function and test for ``UV_TTY``. .. c:function:: int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, uv_free_func free_func) .. versionadded:: 1.6.0 Override the use of the standard library's :man:`malloc(3)`, :man:`calloc(3)`, :man:`realloc(3)`, :man:`free(3)`, memory allocation functions. This function must be called before any other libuv function is called or after all resources have been freed and thus libuv doesn't reference any allocated memory chunk. On success, it returns 0, if any of the function pointers is NULL it returns UV_EINVAL. .. warning:: There is no protection against changing the allocator multiple times. If the user changes it they are responsible for making sure the allocator is changed while no memory was allocated with the previous allocator, or that they are compatible. .. c:function:: uv_buf_t uv_buf_init(char* base, unsigned int len) Constructor for :c:type:`uv_buf_t`. Due to platform differences the user cannot rely on the ordering of the `base` and `len` members of the uv_buf_t struct. The user is responsible for freeing `base` after the uv_buf_t is done. Return struct passed by value. .. c:function:: char** uv_setup_args(int argc, char** argv) Store the program arguments. Required for getting / setting the process title. .. c:function:: int uv_get_process_title(char* buffer, size_t size) Gets the title of the current process. .. c:function:: int uv_set_process_title(const char* title) Sets the current process title. .. c:function:: int uv_resident_set_memory(size_t* rss) Gets the resident set size (RSS) for the current process. .. c:function:: int uv_uptime(double* uptime) Gets the current system uptime. .. c:function:: int uv_getrusage(uv_rusage_t* rusage) Gets the resource usage measures for the current process. .. note:: On Windows not all fields are set, the unsupported fields are filled with zeroes. .. c:function:: int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) Gets information about the CPUs on the system. The `cpu_infos` array will have `count` elements and needs to be freed with :c:func:`uv_free_cpu_info`. .. c:function:: void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) Frees the `cpu_infos` array previously allocated with :c:func:`uv_cpu_info`. .. c:function:: int uv_interface_addresses(uv_interface_address_t** addresses, int* count) Gets address information about the network interfaces on the system. An array of `count` elements is allocated and returned in `addresses`. It must be freed by the user, calling :c:func:`uv_free_interface_addresses`. .. c:function:: void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) Free an array of :c:type:`uv_interface_address_t` which was returned by :c:func:`uv_interface_addresses`. .. c:function:: void uv_loadavg(double avg[3]) Gets the load average. See: ``_ .. note:: Returns [0,0,0] on Windows (i.e., it's not implemented). .. c:function:: int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) Convert a string containing an IPv4 addresses to a binary structure. .. c:function:: int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) Convert a string containing an IPv6 addresses to a binary structure. .. c:function:: int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) Convert a binary structure containing an IPv4 address to a string. .. c:function:: int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) Convert a binary structure containing an IPv6 address to a string. .. c:function:: int uv_inet_ntop(int af, const void* src, char* dst, size_t size) .. c:function:: int uv_inet_pton(int af, const char* src, void* dst) Cross-platform IPv6-capable implementation of :man:`inet_ntop(3)` and :man:`inet_pton(3)`. On success they return 0. In case of error the target `dst` pointer is unmodified. .. c:function:: int uv_exepath(char* buffer, size_t* size) Gets the executable path. .. c:function:: int uv_cwd(char* buffer, size_t* size) Gets the current working directory. .. versionchanged:: 1.1.0 On Unix the path no longer ends in a slash. .. c:function:: int uv_chdir(const char* dir) Changes the current working directory. .. c:function:: int uv_os_homedir(char* buffer, size_t* size) Gets the current user's home directory. On Windows, `uv_os_homedir()` first checks the `USERPROFILE` environment variable using `GetEnvironmentVariableW()`. If `USERPROFILE` is not set, `GetUserProfileDirectoryW()` is called. On all other operating systems, `uv_os_homedir()` first checks the `HOME` environment variable using :man:`getenv(3)`. If `HOME` is not set, :man:`getpwuid_r(3)` is called. The user's home directory is stored in `buffer`. When `uv_os_homedir()` is called, `size` indicates the maximum size of `buffer`. On success or `UV_ENOBUFS` failure, `size` is set to the string length of `buffer`. .. warning:: `uv_os_homedir()` is not thread safe. .. versionadded:: 1.6.0 .. uint64_t uv_get_free_memory(void) .. c:function:: uint64_t uv_get_total_memory(void) Gets memory information (in bytes). .. c:function:: uint64_t uv_hrtime(void) Returns the current high-resolution real time. This is expressed in nanoseconds. It is relative to an arbitrary time in the past. It is not related to the time of day and therefore not subject to clock drift. The primary use is for measuring performance between intervals. .. note:: Not every platform can support nanosecond resolution; however, this value will always be in nanoseconds. .. c:function:: void uv_print_all_handles(uv_loop_t* loop, FILE* stream) Prints all handles associated with the given `loop` to the given `stream`. Example: :: uv_print_all_handles(uv_default_loop(), stderr); /* [--I] signal 0x1a25ea8 [-AI] async 0x1a25cf0 [R--] idle 0x1a7a8c8 */ The format is `[flags] handle-type handle-address`. For `flags`: - `R` is printed for a handle that is referenced - `A` is printed for a handle that is active - `I` is printed for a handle that is internal .. warning:: This function is meant for ad hoc debugging, there is no API/ABI stability guarantees. .. versionadded:: 1.8.0 .. c:function:: void uv_print_active_handles(uv_loop_t* loop, FILE* stream) This is the same as :c:func:`uv_print_all_handles` except only active handles are printed. .. warning:: This function is meant for ad hoc debugging, there is no API/ABI stability guarantees. .. versionadded:: 1.8.0 libuv-v1.8.0/docs/src/pipe.rst000066400000000000000000000064451263361716600162610ustar00rootroot00000000000000 .. _pipe: :c:type:`uv_pipe_t` --- Pipe handle =================================== Pipe handles provide an abstraction over local domain sockets on Unix and named pipes on Windows. :c:type:`uv_pipe_t` is a 'subclass' of :c:type:`uv_stream_t`. Data types ---------- .. c:type:: uv_pipe_t Pipe handle type. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_stream_t` members also apply. API --- .. c:function:: int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) Initialize a pipe handle. The `ipc` argument is a boolean to indicate if this pipe will be used for handle passing between processes. .. c:function:: int uv_pipe_open(uv_pipe_t* handle, uv_file file) Open an existing file descriptor or HANDLE as a pipe. .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. .. note:: The passed file descriptor or HANDLE is not checked for its type, but it's required that it represents a valid pipe. .. c:function:: int uv_pipe_bind(uv_pipe_t* handle, const char* name) Bind the pipe to a file path (Unix) or a name (Windows). .. note:: Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between 92 and 108 bytes. .. c:function:: void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) Connect to the Unix domain socket or the named pipe. .. note:: Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between 92 and 108 bytes. .. c:function:: int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) Get the name of the Unix domain socket or the named pipe. A preallocated buffer must be provided. The size parameter holds the length of the buffer and it's set to the number of bytes written to the buffer on output. If the buffer is not big enough ``UV_ENOBUFS`` will be returned and len will contain the required size. .. versionchanged:: 1.3.0 the returned length no longer includes the terminating null byte, and the buffer is not null terminated. .. c:function:: int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) Get the name of the Unix domain socket or the named pipe to which the handle is connected. A preallocated buffer must be provided. The size parameter holds the length of the buffer and it's set to the number of bytes written to the buffer on output. If the buffer is not big enough ``UV_ENOBUFS`` will be returned and len will contain the required size. .. versionadded:: 1.3.0 .. c:function:: void uv_pipe_pending_instances(uv_pipe_t* handle, int count) Set the number of pending pipe instance handles when the pipe server is waiting for connections. .. note:: This setting applies to Windows only. .. c:function:: int uv_pipe_pending_count(uv_pipe_t* handle) .. c:function:: uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) Used to receive handles over IPC pipes. First - call :c:func:`uv_pipe_pending_count`, if it's > 0 then initialize a handle of the given `type`, returned by :c:func:`uv_pipe_pending_type` and call ``uv_accept(pipe, handle)``. .. seealso:: The :c:type:`uv_stream_t` API functions also apply. libuv-v1.8.0/docs/src/poll.rst000066400000000000000000000066501263361716600162700ustar00rootroot00000000000000 .. _poll: :c:type:`uv_poll_t` --- Poll handle =================================== Poll handles are used to watch file descriptors for readability and writability, similar to the purpose of :man:`poll(2)`. The purpose of poll handles is to enable integrating external libraries that rely on the event loop to signal it about the socket status changes, like c-ares or libssh2. Using uv_poll_t for any other purpose is not recommended; :c:type:`uv_tcp_t`, :c:type:`uv_udp_t`, etc. provide an implementation that is faster and more scalable than what can be achieved with :c:type:`uv_poll_t`, especially on Windows. It is possible that poll handles occasionally signal that a file descriptor is readable or writable even when it isn't. The user should therefore always be prepared to handle EAGAIN or equivalent when it attempts to read from or write to the fd. It is not okay to have multiple active poll handles for the same socket, this can cause libuv to busyloop or otherwise malfunction. The user should not close a file descriptor while it is being polled by an active poll handle. This can cause the handle to report an error, but it might also start polling another socket. However the fd can be safely closed immediately after a call to :c:func:`uv_poll_stop` or :c:func:`uv_close`. .. note:: On windows only sockets can be polled with poll handles. On Unix any file descriptor that would be accepted by :man:`poll(2)` can be used. Data types ---------- .. c:type:: uv_poll_t Poll handle type. .. c:type:: void (*uv_poll_cb)(uv_poll_t* handle, int status, int events) Type definition for callback passed to :c:func:`uv_poll_start`. .. c:type:: uv_poll_event Poll event types :: enum uv_poll_event { UV_READABLE = 1, UV_WRITABLE = 2 }; Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) Initialize the handle using a file descriptor. .. versionchanged:: 1.2.2 the file descriptor is set to non-blocking mode. .. c:function:: int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket) Initialize the handle using a socket descriptor. On Unix this is identical to :c:func:`uv_poll_init`. On windows it takes a SOCKET handle. .. versionchanged:: 1.2.2 the socket is set to non-blocking mode. .. c:function:: int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) Starts polling the file descriptor. `events` is a bitmask consisting made up of UV_READABLE and UV_WRITABLE. As soon as an event is detected the callback will be called with `status` set to 0, and the detected events set on the `events` field. If an error happens while polling, `status` will be < 0 and corresponds with one of the UV_E* error codes (see :ref:`errors`). The user should not close the socket while the handle is active. If the user does that anyway, the callback *may* be called reporting an error status, but this is **not** guaranteed. .. note:: Calling :c:func:`uv_poll_start` on a handle that is already active is fine. Doing so will update the events mask that is being watched for. .. c:function:: int uv_poll_stop(uv_poll_t* poll) Stop polling the file descriptor, the callback will no longer be called. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/prepare.rst000066400000000000000000000016411263361716600167530ustar00rootroot00000000000000 .. _prepare: :c:type:`uv_prepare_t` --- Prepare handle ========================================= Prepare handles will run the given callback once per loop iteration, right before polling for i/o. Data types ---------- .. c:type:: uv_prepare_t Prepare handle type. .. c:type:: void (*uv_prepare_cb)(uv_prepare_t* handle) Type definition for callback passed to :c:func:`uv_prepare_start`. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) Initialize the handle. .. c:function:: int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) Start the handle with the given callback. .. c:function:: int uv_prepare_stop(uv_prepare_t* prepare) Stop the handle, the callback will no longer be called. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/process.rst000066400000000000000000000164401263361716600167760ustar00rootroot00000000000000 .. _process: :c:type:`uv_process_t` --- Process handle ========================================= Process handles will spawn a new process and allow the user to control it and establish communication channels with it using streams. Data types ---------- .. c:type:: uv_process_t Process handle type. .. c:type:: uv_process_options_t Options for spawning the process (passed to :c:func:`uv_spawn`. :: typedef struct uv_process_options_s { uv_exit_cb exit_cb; const char* file; char** args; char** env; const char* cwd; unsigned int flags; int stdio_count; uv_stdio_container_t* stdio; uv_uid_t uid; uv_gid_t gid; } uv_process_options_t; .. c:type:: void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal) Type definition for callback passed in :c:type:`uv_process_options_t` which will indicate the exit status and the signal that caused the process to terminate, if any. .. c:type:: uv_process_flags Flags to be set on the flags field of :c:type:`uv_process_options_t`. :: enum uv_process_flags { /* * Set the child process' user id. */ UV_PROCESS_SETUID = (1 << 0), /* * Set the child process' group id. */ UV_PROCESS_SETGID = (1 << 1), /* * Do not wrap any arguments in quotes, or perform any other escaping, when * converting the argument list into a command line string. This option is * only meaningful on Windows systems. On Unix it is silently ignored. */ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2), /* * Spawn the child process in a detached state - this will make it a process * group leader, and will effectively enable the child to keep running after * the parent exits. Note that the child process will still keep the * parent's event loop alive unless the parent process calls uv_unref() on * the child's process handle. */ UV_PROCESS_DETACHED = (1 << 3), /* * Hide the subprocess console window that would normally be created. This * option is only meaningful on Windows systems. On Unix it is silently * ignored. */ UV_PROCESS_WINDOWS_HIDE = (1 << 4) }; .. c:type:: uv_stdio_container_t Container for each stdio handle or fd passed to a child process. :: typedef struct uv_stdio_container_s { uv_stdio_flags flags; union { uv_stream_t* stream; int fd; } data; } uv_stdio_container_t; .. c:type:: uv_stdio_flags Flags specifying how a stdio should be transmitted to the child process. :: typedef enum { UV_IGNORE = 0x00, UV_CREATE_PIPE = 0x01, UV_INHERIT_FD = 0x02, UV_INHERIT_STREAM = 0x04, /* * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE * determine the direction of flow, from the child process' perspective. Both * flags may be specified to create a duplex data stream. */ UV_READABLE_PIPE = 0x10, UV_WRITABLE_PIPE = 0x20 } uv_stdio_flags; Public members ^^^^^^^^^^^^^^ .. c:member:: uv_process_t.pid The PID of the spawned process. It's set after calling :c:func:`uv_spawn`. .. note:: The :c:type:`uv_handle_t` members also apply. .. c:member:: uv_process_options_t.exit_cb Callback called after the process exits. .. c:member:: uv_process_options_t.file Path pointing to the program to be executed. .. c:member:: uv_process_options_t.args Command line arguments. args[0] should be the path to the program. On Windows this uses `CreateProcess` which concatenates the arguments into a string this can cause some strange errors. See the ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:type:`uv_process_flags`. .. c:member:: uv_process_options_t.env Environment for the new process. If NULL the parents environment is used. .. c:member:: uv_process_options_t.cwd Current working directory for the subprocess. .. c:member:: uv_process_options_t.flags Various flags that control how :c:func:`uv_spawn` behaves. See :c:type:`uv_process_flags`. .. c:member:: uv_process_options_t.stdio_count .. c:member:: uv_process_options_t.stdio The `stdio` field points to an array of :c:type:`uv_stdio_container_t` structs that describe the file descriptors that will be made available to the child process. The convention is that stdio[0] points to stdin, fd 1 is used for stdout, and fd 2 is stderr. .. note:: On Windows file descriptors greater than 2 are available to the child process only if the child processes uses the MSVCRT runtime. .. c:member:: uv_process_options_t.uid .. c:member:: uv_process_options_t.gid Libuv can change the child process' user/group id. This happens only when the appropriate bits are set in the flags fields. .. note:: This is not supported on Windows, :c:func:`uv_spawn` will fail and set the error to ``UV_ENOTSUP``. .. c:member:: uv_stdio_container_t.flags Flags specifying how the stdio container should be passed to the child. See :c:type:`uv_stdio_flags`. .. c:member:: uv_stdio_container_t.data Union containing either the stream or fd to be passed on to the child process. API --- .. c:function:: void uv_disable_stdio_inheritance(void) Disables inheritance for file descriptors / handles that this process inherited from its parent. The effect is that child processes spawned by this process don't accidentally inherit these handles. It is recommended to call this function as early in your program as possible, before the inherited file descriptors can be closed or duplicated. .. note:: This function works on a best-effort basis: there is no guarantee that libuv can discover all file descriptors that were inherited. In general it does a better job on Windows than it does on Unix. .. c:function:: int uv_spawn(uv_loop_t* loop, uv_process_t* handle, const uv_process_options_t* options) Initializes the process handle and starts the process. If the process is successfully spawned, this function will return 0. Otherwise, the negative error code corresponding to the reason it couldn't spawn is returned. Possible reasons for failing to spawn would include (but not be limited to) the file to execute not existing, not having permissions to use the setuid or setgid specified, or not having enough memory to allocate for the new process. .. c:function:: int uv_process_kill(uv_process_t* handle, int signum) Sends the specified signal to the given process handle. Check the documentation on :c:ref:`signal` for signal support, specially on Windows. .. c:function:: int uv_kill(int pid, int signum) Sends the specified signal to the given PID. Check the documentation on :c:ref:`signal` for signal support, specially on Windows. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/request.rst000066400000000000000000000040051263361716600170020ustar00rootroot00000000000000 .. _request: :c:type:`uv_req_t` --- Base request =================================== `uv_req_t` is the base type for all libuv request types. Structures are aligned so that any libuv request can be cast to `uv_req_t`. All API functions defined here work with any request type. Data types ---------- .. c:type:: uv_req_t The base libuv request structure. .. c:type:: uv_any_req Union of all request types. Public members ^^^^^^^^^^^^^^ .. c:member:: void* uv_req_t.data Space for user-defined arbitrary data. libuv does not use this field. .. c:member:: uv_req_type uv_req_t.type Indicated the type of request. Readonly. :: typedef enum { UV_UNKNOWN_REQ = 0, UV_REQ, UV_CONNECT, UV_WRITE, UV_SHUTDOWN, UV_UDP_SEND, UV_FS, UV_WORK, UV_GETADDRINFO, UV_GETNAMEINFO, UV_REQ_TYPE_PRIVATE, UV_REQ_TYPE_MAX, } uv_req_type; API --- .. c:function:: int uv_cancel(uv_req_t* req) Cancel a pending request. Fails if the request is executing or has finished executing. Returns 0 on success, or an error code < 0 on failure. Only cancellation of :c:type:`uv_fs_t`, :c:type:`uv_getaddrinfo_t`, :c:type:`uv_getnameinfo_t` and :c:type:`uv_work_t` requests is currently supported. Cancelled requests have their callbacks invoked some time in the future. It's **not** safe to free the memory associated with the request until the callback is called. Here is how cancellation is reported to the callback: * A :c:type:`uv_fs_t` request has its req->result field set to `UV_ECANCELED`. * A :c:type:`uv_work_t`, :c:type:`uv_getaddrinfo_t` or c:type:`uv_getnameinfo_t` request has its callback invoked with status == `UV_ECANCELED`. .. c:function:: size_t uv_req_size(uv_req_type type) Returns the size of the given request type. Useful for FFI binding writers who don't want to know the structure layout. libuv-v1.8.0/docs/src/signal.rst000066400000000000000000000047331263361716600165770ustar00rootroot00000000000000 .. _signal: :c:type:`uv_signal_t` --- Signal handle ======================================= Signal handles implement Unix style signal handling on a per-event loop bases. Reception of some signals is emulated on Windows: * SIGINT is normally delivered when the user presses CTRL+C. However, like on Unix, it is not generated when terminal raw mode is enabled. * SIGBREAK is delivered when the user pressed CTRL + BREAK. * SIGHUP is generated when the user closes the console window. On SIGHUP the program is given approximately 10 seconds to perform cleanup. After that Windows will unconditionally terminate it. * SIGWINCH is raised whenever libuv detects that the console has been resized. SIGWINCH is emulated by libuv when the program uses a :c:type:`uv_tty_t` handle to write to the console. SIGWINCH may not always be delivered in a timely manner; libuv will only detect size changes when the cursor is being moved. When a readable :c:type:`uv_tty_t` handle is used in raw mode, resizing the console buffer will also trigger a SIGWINCH signal. Watchers for other signals can be successfully created, but these signals are never received. These signals are: `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM` and `SIGKILL.` Calls to raise() or abort() to programmatically raise a signal are not detected by libuv; these will not trigger a signal watcher. .. note:: On Linux SIGRT0 and SIGRT1 (signals 32 and 33) are used by the NPTL pthreads library to manage threads. Installing watchers for those signals will lead to unpredictable behavior and is strongly discouraged. Future versions of libuv may simply reject them. Data types ---------- .. c:type:: uv_signal_t Signal handle type. .. c:type:: void (*uv_signal_cb)(uv_signal_t* handle, int signum) Type definition for callback passed to :c:func:`uv_signal_start`. Public members ^^^^^^^^^^^^^^ .. c:member:: int uv_signal_t.signum Signal being monitored by this handle. Readonly. .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_signal_init(uv_loop_t* loop, uv_signal_t* signal) Initialize the handle. .. c:function:: int uv_signal_start(uv_signal_t* signal, uv_signal_cb cb, int signum) Start the handle with the given callback, watching for the given signal. .. c:function:: int uv_signal_stop(uv_signal_t* signal) Stop the handle, the callback will no longer be called. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/sphinx-plugins/000077500000000000000000000000001263361716600175515ustar00rootroot00000000000000libuv-v1.8.0/docs/src/sphinx-plugins/manpage.py000066400000000000000000000022631263361716600215360ustar00rootroot00000000000000# encoding: utf-8 # # Copyright (c) 2013 Dariusz Dwornikowski. All rights reserved. # # Adapted from https://github.com/tdi/sphinxcontrib-manpage # License: Apache 2 # import re from docutils import nodes, utils from docutils.parsers.rst.roles import set_classes from string import Template def make_link_node(rawtext, app, name, manpage_num, options): ref = app.config.man_url_regex if not ref: ref = "http://linux.die.net/man/%s/%s" % (manpage_num, name) else: s = Template(ref) ref = s.substitute(num=manpage_num, topic=name) set_classes(options) node = nodes.reference(rawtext, "%s(%s)" % (name, manpage_num), refuri=ref, **options) return node def man_role(name, rawtext, text, lineno, inliner, options={}, content=[]): app = inliner.document.settings.env.app p = re.compile("([a-zA-Z0-9_\.-_]+)\((\d)\)") m = p.match(text) manpage_num = m.group(2) name = m.group(1) node = make_link_node(rawtext, app, name, manpage_num, options) return [node], [] def setup(app): app.info('Initializing manpage plugin') app.add_role('man', man_role) app.add_config_value('man_url_regex', None, 'env') return libuv-v1.8.0/docs/src/static/000077500000000000000000000000001263361716600160505ustar00rootroot00000000000000libuv-v1.8.0/docs/src/static/architecture.png000066400000000000000000006236571263361716600212630ustar00rootroot00000000000000PNG  IHDRu AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs  @IDATx%GukrڬrHB I(L<0Ƙ`61mt{uӾ.u_W]G/os2lwxx$׻۳᯷tW}YmL}Lv͛7{ޞ<+\'䄯' ~}aP.V;m۲ek80~zNMOUuVr׬rS|}pEG][Wc*>`sǮ0VbܵkO;lql+\:Ss|}џD`wo-Hq mZWYZh!x`|c劾enC]MfZ3q}q 5POLžc_XceVw튱tغ~vCdhGY}񈾉튲(}uc7,XŘ<v8Uhwf}uQ7l |P~|`}>q?p_Y藬% q/<1Nh gys u }1|f61ݏW;v{_{q\a֮ vc@_H};~η|7ڨ78v>3 #>e89T8|?BoUx}^Wm{6`υ[M([1 ?`d8o\p|=o~mc(~*}7eT[* WpL.<+$1+7c&׆s  Vp|2se?כ}2^꼥_O€o?O/3si } &1|+2\۵墿9.=o6o{|%$Λs~m|Pp>0}k8g,W6m;Qwu@~ʏ^?!9,?^8 ly\r~M &@>K{‡'e`cl߻q͏ǭdf펓p̣1H3^8f.Bͥm-٬rQ|6zނsXp,&d8+p2ʅaee}|pA\}ḩ:,yK3c?uqcy~\YT6Cw?ą mp9bmsX t;inw{mfJD~~?W!4NO9,K/7XN>}W;s-WK/H,Ò ]J\UO^EXچO+W==_ >c\U/O?/emwY[X,*!Xe{ 0A`rL. evı1g+NwC?5eG XJa5r5aI*9~hPigp]0V)hPq泿sa @f,ha kf@ǒ@,+X`[;xD]?&X2㵑E9hKn%e6\a^>Tsopvflc0m.]x o3mO,ggm~V^neah7e~- (/Y^TVs }q*s,c,EG_,q~|-);b_+*+Gt箝L @1߇*1>݁O3W>chS-Ku1V!:b\Eep\6a>-Kw\y 2?o x:o W< ?h+0E{>TrbcK *ԡ,Q"` yTyOWƁJ9%9\sLc~2Gއvcb } C"_ K1Jfx os88c8<"ضDY9# >.xC@<`qFF#3}'ql߈K.НsmQNݕW}7tVN|ܱǸ>|Іp Y> pė>t4N ?ڀFA%NnoPш8G6mS:c҇fx7$~ s7öRlP.Pog0 p$89l`v|_̌eqr Y.П8` zapcYmkD`Y~> HŶЇ:? )?Dzb7@rQ?=*g8_hu`^y.ԗY?Oz\gVONlZ} \{'X8~ذ~}8q!o? os` &eʶ/F1AB_+;؎ -lp\eQ;m\m <>4s?8ٰ1?j9u8in8g{qb_?c ? ʭu[yp :?| blEqRcAc g11 ?(\Y߬: a;s<]9vQ7􁸝!C o{n =lۊb~+IEx޷'^@_d Q/YO8ЇRƲ/2q<8Gؗ2-WuEgpEp?޾+-طЎ>؇p߁W}} >>Z=8 |y+Pw' ]S~&#G0 >o,vޒk``KFkaҏ!.(b- l3m*\+ M (PA"8ñ%soM*9uMpPSbPo8CV;Նp zmP':֭q~~ MxXw҉'䊮''_UPcxtܣݚ %D@D@D@D@D@D@D@J~zFw~.;IOxU%&7rҗZ " " " " " " "3~{D偅-\6X,M "#6yy{_wEYMj&7|kxy3 Ǥ8g>Ŗ7\S o*l4)Ҋ,38rAE~x͵%"~|f`EWXz;̢=[H1_yGq;iED@D@D@D@D@D@D@'PS~GM& [ 駝Rh ֯wk֬.*sソ*qڵ;䐃"" " " " " " " CϟNV<@X?ǰo>7::d,&#`Ϟ5FJΎLMMդ(&Թ={"" " " " " " " Cv_`M;daI@_>/&O>D_ַvy]x{v2#۳'}Auww'@f7z{zjL d$5̯+ݿ_{]Үؘ_. )u蚟mw$_MBN1oo/bb¯ " " " " " " "lÄ*zHA" " " " " " " KGx:߭V$LM>?;Y`$`׹ֶuնE@D@D@D@D@D@D@$Q}f3D-"sx?7?ݳ K)" " " " " " :6Wi?>>&ÿjՀڰ>ЬSN>OAD@D@D@D@D@D@Dq=|^.͋zsҋ,!G'Ѱy34Tl}{}mtw&U5ܶm;;\ww[n;#<םv+Vtoo;̇&r];ﶪ"z*w)'q[tYN4[̃SNtk׬+CD@D@D@D@D@DuW}D#~{`O6;`"fݫ^d^#>qcY>[W Cq/{ 9g5.V{?\XG~w}2* ;cUE3Nwo7i\G?>+*ڟyߗޯ" " " " " " KJw4*KmаM.0NM*+ŁV ^lml͸m&~#q%m1VBD@D@D@D@D@D` twuuY>^CsUWO/;h_%'8?Ǿ]Muw+76xs1%NX2Ŷׯ_WH}_~&ey#<xqnؘsO~aFwktJ6{W„뭾=y.^0 gu\1WG_rQ֟>ߘQ. !Uٹ|dQėcQsq;܈Í}W9< w 2scr-ƈ?.%D))" " " " " ˆ["\9Cnb:o_wXoz;أko)Ovor߹5y6sZ!{guFq7ޔo߾2C N5l}UD@D@D@D@D@D@-ʦa~ݾ./7OfMY <_N;{Q}Ͻλf%dٲ~LsㅀrQ#WE@D@D@D@D@D@fص{O*q$L{dn.|;KI๥66o.ߙ<\yyV&kdj QBD@D@D@D@D@DعX# WW~b|&\Ȇz_^- tC/Qoo;s%_67tKwG~X2OJ&gOleÄ?~x֡3<㴆wgmsw̳!ҒݐX~FWz`ڄ,3[K^L&SӵknJac·zh*қ_}k֬Nn[o/]]q gBK.0Y" " " " " " O`zz䖭Ga&1"Sj2[4jjmT0ƧsqwnuoI3q _2`ZRD@D@D@D@D@D`y/`|p|=YY}}tTVߐH]JbBå\-/ó7r[FXeEu?#Bn""߸򪆪{0ߑ=58ͷܚf=Pw'&y[oy sfɧJ)" " " " " "r;xq_*~ קl[^k~>Xz_7ܱWʯzwY>Vqw;U;Sͷdx?MAD@D@D@D@D@D`yvS/3b[-E3iҾ+gt͵^>}wІ<ٿ~s_t7#nٲ;Pf.<3}qqWe1QZD@D@D@D@D@D`XY^Lѹ~⮕0ݛwƊy+^7Yg=F0>Rz{_>wUuo+ܞ={mq YڝsY2&''g9(wܱK!" " " " " "LMO_cweo vOr[wkoksGu$M|;¹8;s?Qﺻܞ͛qpUXMbƫw 2` =72=O+K\([OQd`bDWw., _XL" " " " " " ˄}\ۗy|4N:LogN蠺v2X܆܍b¿{Ϟ/[/};\uL7;Sx/uG}i77*KD@D@D@D@D@D`K ń>ß{|#. >-o~{/׫ڵk^G_zE>|{Ӟ|S~a89cC⼅|nubp  #m~x=>~Si ʀ;7Saݺ~ ,ſXWT8 Meoً4[~Øt>}Wm߱]]~_ y4~an6snsn?Zpᇹ<̢xνho~uIm;o{W|`U{i&X6O#q}+;RL?O ay)'}܂" " " " " " " M#d(7/I],hHK)" " " " " " "v~~b|"[boe!" " " " " " "tQl|hx|PL <þX?" " " " " " " -B`tt&cc5i& (unrj+"" " " " " " ":UEńphxf9@an{{13544Ĩ+{0RDD@D@D@D@D@D@D`feabBK @ 1Z]]څ Ta{_a17%o%i5mTD@D@D@D@D@D@.,x)ی}mRq!n횢&0xo߾Y!nݺ2~I. {{Ph==շe+&KȖ E'`_֞|R?af_Ww^Sm@D@D@D@D@D@D@Da#cm#a_SDD@D@D@D@D@D@ZDQ{En᷌!ndUL? NҾi7>^o+&X?6V}KD@D@D@D@D@D@D@Z@4eOV2L>?114RD@D@D@D@D@D@D`i T&DoR, {2+?CNL, ѱM>g_g+TLׯ_W5/P("" " " " " " -ENjń3 ED@D@D@D@D@D@D%=ńțԒ"" " " " " " "B]l͊ ]?99uPhk~ PQ"@,*$ ;S|ݙW%*"" " " " " " "$ǫUoϪK1wfJ!" " " " " " "FGGD1}}6@)jVrW;-鯲PLD@D@D@D@D@D@Z3U_WZVIED@D@D@D@D@D@4w'&oz*CD@D@D@D@D@D@D`,Y>3᯾Ͼ(E%'`'SqŊ x7::Vh]];eb?:V}Dnj"" " " " " " "U@ؽ{O#ńhh2΅R%'`_m ?ٟ*z{{"" " " " " " " Cޤ߾}˭үL%}'@A#Vf"L;::X_WBD@D@D@D@D@D@D`i ٷۚ}OwՁIk@;;o쏫&oϭؿcSŵ(ym)"" " " " " " "&'Jx3s=젴}cń1I,=J˯N Ձ@%!٧''{]Jk" " " " " " " 7+Yb?9Q}Ν" " " " " " " -BWL'&od@ ꪩMwwwMd@ +jzzJ&V^U8+"" " " " " " ":x;cE@D@D@D@D@D@D@@[[%}Fa?Mc;E@D@D@D@D@D@D@Z]?00Q&oeKQ~!P;sR#0eV0\WWׯ:(CD@D@D@D@D@D@D`m<\V] Iih k׬.*g"G d_a,YLG̩"@طo_Q~oE~i1k@/j{5+&6wpp&hGQ]w'k&" " " " " " " KB`brfifob_}vMEM)M(pxmh@k:>&;VJ6v#ǍJdsQq!\ʐ&oJ?MbMhB{S&;ہ]xbDŽwu~.Y eSZ?ؗA6. 6Ny??l}$c6>1OGgI阧ړ/B϶m+QG\/^O 됭6q{/sGGGM=j2f6i18mR:5ck>~OHdö淋O|ڷ;@bF ?GTc9A zᡋ]..r7[>c??:~t~rz?74~4gyesUA f.h|}G~)&n16wuu%8/N$اV8VPNq^+}>ٜ7c;=ӏ_n-D2SիBeа18Nuww<{P1N=$0N;/ӷ+įr}O4~hH zDGGghǘ\[m|r^$n)&nLQ7@8\l)N1ırh o~׮Y Ä ~&N ;>>:&8N1 6ӐqicG`ŏ}q?#x84~h~#x84~49%Nq,b"Il':߭؀ ϶a{`?hqglLCzơGyc@{n.cttt4!2ĖGllvLCg~b_`{we90g=4:ql`amCx:W戟=fl\O?5~1GbuƏXO|v숏L2q.خYi=L0΁zXI?惘bN>D@ߝo+\{E | O[p!BQ!BgzHkGX/ [񫜈T7?qF_{\CͰ8S;ñ2[hEOեhL\1'FrRv9=lٯX@k{U/{~Ï mذ"Q'Evx;` !n,:J!qu_l@ܦO^G m:Jl@ܦO^G m:J;eh4N2y|B|b߬jV?޳ %kV1оL[ƈtlSە1w* "+*S(2F%QA{U"P(z\``ʠO+cԱGG}J4/X~Cʸ47 aLՋDy1z`|-m1ծh>tȇCyj4G9(Lj _I<ϩ}Mhk3v޽QL #7)uksW*e`pXQ Ӕ]N/ 9N9C?q"2zh'*N*M(Jw6ޡmpG+1}mRn#h/(:Hχm{`Bwp0#Ba"ťw֯[gLQ6.&8lZϜ_r$W%z8WK )Vqů@SN_ӷ"?bRewu%NNu[ccb 8&gO ;oqrv9R=xqK\FGc$9}]Ny|-# e+;s76Y0_4zJ@Z=6_%`0N)~eTUsGi}[[)_Kra2ԖPϖזo֖qJ\1%Գe>۸eR,)sLm lOi}m6nm-˅qS[B=[SZ_[[[)'b'0Oww&Δ1cg6M鬏lS:c)۔x6>6M鬏lS:c)۔x6>6M鬏lS:c)۔x6>6M鬏lS:c)۔x6>6M鬏lS:c)۔x6>6M鬏lS:c)۔x6>q㩺t𣏍lS:c)۔x6>6M00vJ?x6>6M鬏lS:c)۔>mt𣏍lS:c)۔x6>6M304T,-[jٲ;x!k@2 M1nml\˛>ʴuqGG3eMlظڣyϐ2LLL&>X蠽?`#hD64L0^L`=׾_u2ӆi/|5q7n#}d<ϼgROaWc +G?Ѣ˕q|Нvi}{_e?z#t'x[n;sܟo}V?-qHf-sH3nD!7Jg\?tA_~Aik80Ŕcof @1%6tçR}xP6bE87|?w)?pЇ&e`iq]&fMB},c1+2m?!|􅔿?? #6>CƏRUYq2mP{,]]b}gqY OuQcl`;*ĭ-4FQZ[) tXq;U3%u֖qJkX$KSJylhȷqCկvgu{[~_C܆xȳ;{-!g>6ldG?Un۳nm۶lȩ?m~[- 7񍐅;Gۿ[XAo oxò.ȏCvsݾONǟS9XI~?k[xS{7o%=W,`hx|b9ՠP mR8_ x{&s㋕s񷶸h7z}{ֳǒqo}!~tPɑƛC?J0ö` ݽ;8\:(aGDŽe!}뭷vO>=pn}Z=~nȧ}x0Z<g:(Xh痿xҖ_b<$Ν7c>8 ^4B*F>},].^_sv9+,'~/_rK⬏͇@'fBYrZݟ~?_L }n9@a?3ܟ:֎:HG۷ow0|;/9,!.*`ROq%džqk׮u^z۳gG,:lW򕰴wa x%/yI''S>c_P'\p.j`{xx?co^z`G ?g\M&sX'>њP<.)f-x{?txCvD~G7(qBGfUʋkv}٭Xڣ]g1ؗrw[ (⩀|u t D/r [to&ҴäoDŽ|{.Np.&'Kps w1iǤۅꪫ 0!8<|., &xIV*:k_ 7g䳎PZ~xt~XPiv8VN < O"BC?wy!-(_! :Q.NS/W!c )yi~ >6f-CQ1Xl^̾n4kRs\dwwu[Mqx'&>gA~Yo~,7䝡iKq#_]_W$[Gt. O~V8&]tQX;K9L;;+Yp%0g>$H 6Oq,aw 77cgYۻwo__Gbo 0ే>AL /̘`Jo6^fS2l_!Pƨ,OHY>~x7S)eyCMYJ~,Te<ڗٔBea(@Yeys93f0̘Kk`l?׎]pSw,>')O eIJ7=vmvaA>Oe zx&+x;s\w-Gzj!3ڵ+. 5 ۠Mʟ Vcb ?G)+EXnn.w_Q'(Oz%+V2GǏ? ?s&js|Lg撩r„feR?=^Sd"ܑC:Qo|;餓K.7|ËR74: =`q}WՐ4&A*W^0~`ev8_8mM<-mk_~y%c?ۋ% op1C^އ x ?Rߺuk>2+'h1؆i1%=$J ?:~'b<SybY{a٭P{>ݗzq]_<-YbN+}k\,;OO&$K'?ϧ̂KpG/1ąsx܀/F֭[&޴eP^xS=V>=pGGlz补]>ޘ ' ^PL# 3qw/)D>O^ 9g_!~[?+:CAOe I?:~R:? ?r\_~/}@O1],Ƴ\wt{=D-+X5\#s>ccc4f>Lӆi|bxEcxU#&68_FLY,c7)#9A >ޡ[ǸMNo?˧ ,˦??_,ȇ2[g>`DN4o8Dq6?˫3Eve1`}/}TbU{T.hCYO[\ewnkfd+rG`JPҍiHiK!q8^M<p)}s!uӔa?/{a0sL,;=äG?QXp'=b` \g0\0'w}km9(iJԙzxь駟^t__Y DE*O'sda3MID*o(gt/hr'KjxObS{,/%^Ÿ : Mwqm 6sxlK[zc].m}l6sxlK[zc].m}l6sxlK[zc].m}l6sxlK[zc].m}l .g?s{|0-q~?L[8nb^:o~G>^6mr/g}ne\[FOʏeC'CY<??b΋V{ͯ-PC>B_{{&}{{(z!&V; xYkWoտ=q{ [?90!Do߳gDw{[ܕF'rN opǙKaC~C W^]SxG=-4g?jժpz3MSKcoox/\r-8|61w-1 ܭ;a(*n@ ͨv/nƦů!q?| CG_4:~|kt{:e>j~Oc6Z:?/1GWnH@2.҈#0y ̿Kn,,{^F3t.sw <ϖysmX֏ ?d梋. X^aOH<Ǐ Urx/;p!+pKO6l>v[A:nywq_ `=aJ-~#c?K^=0Ûn)[D[|@N;y p+4w>ۆa>W;_|qx=1)ŤyL 81!?묳ax .菋gyfh||wq7~wS><ˏgձt?tx/ x\{a&_q3z?.&?.3w!?^pG=xgl1-_=pY6~nկ~u1P2n$裏wG02~'ތvY9I]N;q'^PηrYqp4=0!n}ƮF aO;?g:~ouot_Ʒ g?Q. "cBxV}>U@7ȿu.r\~nZRfM:=~^Hݨ1, G~jzcDYgRQQYY_x/̈́FO|Bh>naҏs=0&^D?t>VcIBڃvn{ .-w.C,|{CS~Jg{h+i[xv9=m_qW_lSp>@?zZ=3oe?:ҿ#4ۄRІrZN @?MrszGumӫ=CGG1EXT(ac҅eY=ud>%hkFXlܸ1)G[6̧dm:ڴK3⸻ler[G5[.pܯXFHa:s߽HH1dz ]]6,cH?NJ:JɿaYomɠka -Jr $uC~ŏ}RB@Ǐ1ce1%i@_(g"[&'}/.&Nc ҦQXf?tEE4~hNj8mƏ?xX2qZ|jsl],LvBqodXf s<(mY=p6H[;_lP<7uBǏ CjaE+.f<(t?^,fLc[߲}$-Y*\&\5aAg0y:W_e O@}%fEHW쒏i 3O%W;T':~ ?V}}1ZE\y1}'aXol۶=ZL;z{{mXrlˊq֍qFjcm<ǹXvS6Re@od3n[vSҦLZ[)gmMS12NIs~f>SQLoeiTLЏR _c#+q_M?c߈KOo2vr`?Rbc?wm{8-Ts<[^ڣXb߶caeꏍ[h\t$62tS_Gl_``ӱ>6N;W%+-3E.>cWQQ v̰qubI[GZ>N\JMg~͒ValNgjrjTvuu՘W/ٌxu?<cq?~øq?Ximl\gr]m#W{5^n6P{,0㣻c1Jel|W}|kVN ۳ztJ)cV8|iJꭴ؎i2[8(c;)lmJ혦,yӏz+m;%#7>q"WQQ,rnjշú-7&j{1=5]ʙHgO?_T]Nom%zSN/~rrZo^j 89N9qkrrFvJ+?ޟsrx{9~t[No/gӯ4xri~s|­M9D /LƸ }D߇XUЇH?FʁFu::F_]^4Vfihf)󢬓#uy٭Rf2ҌG߅%J>x0Iiclc7G7lbwpKX P)_Uc_2_z?M3d+M4SM"PƦ7ODw }{ތGJżO1puSһOj8;^l<ҿ#m :%FueE~0/MF5O3?ӹ}D~?M<'z7fҌǚ56Ǹq뵇q x?ǿ[3geҥKK4bC+E5=3M4a34G974g3B#!&֞qݾkxsHyUk˗h_6^"|Μ܀8|)(SGWoG~J?{b?I!n1:݀ |Z47z3f1{Μ-ZH{z_~ӯTl䍉 މڏ4q!2@qu#?QNog.4qaOE"M\77wb~D<ʉ7M̅&z#c".,HB }1VSl΁Epmw'_o ⏲^{mZVyVYo҆Wwy4ɓӴiҞ{bEB&MJ .LOxLW]uU;O^?яNozsESV]` #XNmӧOϱ|c6ds]w3f>}dPׯաoCg.ɓ%նb_1_EA .j$M~nU/+16ҚiELHěEgb>7k!LģLěa<];G|8}m׵'l3*E?<ߟ|yAw=]wu3Hw\ڸ+h;(/?sLzueJozӛQG`(;Izj:iX\|3qb1:uj>'~&> ??͜93_ n?駟X :bv[Q'ק5<߂ѢEmW 6b70xàE:<:r~&ZD~wfiMҘ'̟̟ q^x8?S7s,mǸu& 77!_\b#Hp ڱ}eƧl_#]!??k#::ձ]a>ҵc@8'5w,]&N)@ xW7Z><#"įwG5?is!ˆ7G3flOCĥ#Mt!ac|A}o^+_Bx/x Ғ%Kȶ-EѿKF<+T#D}^%Mgavi _馛nJ_Wӫ_>c^W&lP#x]MQ'GxFx׏{~ ?^sRQtА ŁBz #e #O+i~ؔcd܄|4yF̅|4eN&OX'y~+ƤMzs1FMZ'!xE^I'lPP wAοǒǝ}K)['ߟ}dr%--ʏ& %?:^2.c7 ԕÿR8b4P"^zģNKXxԉx):/eb=Q'LGN4uO?ʍh?N oҏ~}-8O $wⱊbE`{˗/OZ>zO:ǣcҷM:|.o~+_JCsNSȓ"\yK_@|o)qyA_oh?/O_ҭޚŰW.,^mB@ ˖-"C²cK&.l&~16䏹@D 4qa?M M4O̅fhE D\80VоўpsgqܹYU=a>-ܒCo~.=?c'0ąeQ[mGߤHDP}l?V[E#__K1uF{ul_]4׊F.>q7UH֛iEx#^2>V-XY_]>O~C?^??)/~1wү4ʌĮKhqNHz뭳 ~\Sz,9_יBܡ=Sҳc%Ou YSrYs׿U x^J~lz]D+nAd#|/ypYa)K#O\|Y7!.4Oʺ&!.4Oʺ&!.4Oʺ&!.4Oʺ&F?q4 c8 K#>8/K?Bh" >߁Hς=x7!tS6ai?|YN?o޼b1^PO|.G?z(F~'|NlL#^_qOϋ;/}?ӧH/mFu?Qw(mG?3{WG)w>~ ŋre]/\m=+GsD?8wnŋǸ˯|:δv?yܟYW7֥2eJ;0`7'N|"xnu?7`D^%?S*y:y#K~YWv|8.VI;?Oo66ٖ%Xtr;ȱD8~~teD1umXgN SG /1<"OFX>O޽s|fblw-_ﰮ> KZY\ ̚5Ktؾ"߯g;,fG"-F]e~&Z>?sM3ν戰?? fډfl#] g"Y>x@kC(?QL2zcLʶGپuu7_uUi}oNģ>c[lK3ȋ6S[ц!@c=+w_mݲ,tcQk,l^k<] ^+`W_B釦r2k>XX6ez?GcF5Wc4焈7C3?4C3?9!c5Zx7G|}[y_{w׿U}9wE}偾zX/Ew#ݙgJ&.?ig̎>F[UxN#_&?]e6~рwۛ=rlu`h?o/=qmwq_,X.׼&~_rtM 5X Z]тc[׮r7u#_ҭޚ/t8?G ?c׿n?}鵯}m;|̒ 7IrG?x?ᶎ}?"_Gn(P/9+{GaY Aqd |~&z/_z-cO?R˜O3q Q;_W׿2KpsO~+5WY̗u|Ϟ8uQi=2c#e~0-?ӷ.:?>8l/s">| :R|Oڃ|~+b7 (M秜rJ?d>eqN;)w+?{k<9x-Sx7cG>֕u*T/JC1~+&(x@F:4qa o_4>?̟4Ǐ?:Opt~#EÁcqXlp7~́+3b! tt}bu{]:_1l 1sB0Qz ԏrx4'馛]fdY<Sb[,yg_x3&RuI' ۣvxK}'G O;v'OïD9|0.~@+/un,y!SB]PB%/o]( PPuw<+ҹ_Oiz;)G6|@>@;LEPF_?w?'’??PB ||)|8/~ -8R5aKn~4a$V[p';? 6l gjEx:@Sxߊ1flvc"O?3qqΉx+zs&~M4A3"x6^?-_kclJPdݞ ,Mczm4?RP'<N שQF~&z}x Nq4!&9N9"^BVwCߵnK3wyx"`P9RߘlF*-XVh}pTȗa7MiCZ??i~tf=n5o4O K]|F]g;i1ZF~3127hB[0. R-Sc%͓Nrjލ*cm̢L&~E i8Wa^?#Gukn1;ѕVז5&މތG߅hKw71`=Q?R/ Yqa7Ecȏ N#qƘE<{;ʊ n'QV\qƘF<¨o{#`\1f^f1.G~aY#W?"1xI񋾌n?MBuxE҅7)]phՇzBuxE҅k~u08?gYp*W9i0ڴx&!2,ieYi¡s%K]a!H?3qc.=`I+Q?6܈mio(ɟwI1)ie=OQܺP:،񯋗49~ /~1SK0'> %U?mJP}4ѻoסz:HƶKZYX oa톍z 71(=(.P!'lӧM4-=p4}ƥJdpC,_<,[,ؐ䑣`+o/^ƍ4ڈT?>6#A6"F}:}G,ӏ&lad ;OLm1[=|Хs%@ʶm3#KҏW)LOSn;'z`^顕M 5 70M~rM&qi#]Y2~.ǎg}f3҃wN3[g|~ sz3GiÍ6Hn<>mQƎ*vi$bgg[, HyvϜtӫǬ*7涎+yc%7֟0.m&iGmvoqF^9~렲' u¹3M|ݴbN3Gx} Ƨq믗6v.].N뼩.DZ&neG~hO뮻u]7<H[[~\1s4yt$qe 7y>؃,,萠SN2G~ρ IWMIן79͝Zl[gY:tqž){raM{,έ# {N^H@69a>!A .zL4d",iv_3F*Jiy$KMmA黥{ٶ)x)DAؖ.^=t%Sŭ&qetKwsI;'>o4qˍm.ı2^ꔑA^-]ntwP: H3Gc$˖, o.íVvړX1^&VNģNK}:Ƿ['piOBo\&7ZǿNǏg-JV`yi6O{uah>1o ::oe+)?SoiIzQ:zybvK/'79tߡq8ecHO~oo_e߱k&O_.'^ӧOk6]LO^>qK/zы-O}x#7s=~w4iRvuvK'NL57>xǺԿqXh/sEeŁ_|qЇ>[no>C- ޸qX/[F|M3N%zn,l{{7kP`LP`n[gI)4 f^@W[\ |6v.@;vЕ.v- q'(I]е YpyIMj|_dbYB[/a#w`췷sfFoɃ-OIw\}zʋ=dל:'9B%D?%2W8 }wH~s7cq=sp%N1G`~m3I3EݶW6|Ϙ:']\̩ \7sɳNv. q bM&tJ(I^BФs?{aXu 3< `7`3oM^>5}qMǧo n۞raKxg$>łRQ ҮQGzޗΝ3}w9vmK.I\L8S76D6n{P~ ^֥jW^ɷ:xJ[N2u%,KXYWS]hnɷ^RϺre]P~ Kue]*_B夗uC}q׾ÿ;fYF[:'kw^wȻzd_p 8ƅ.@ eS ,…Y _)RF?qp?_qLͩ_;_ꖴta=ny0wt/K|gу-P!7RӜ-}fg>MFg?Y^^鷿m~|ܽ|_%S'מp-8tǦ=3}/<1O" `Ϸݟ._zJkN=.^zANZ n;J98%ztE^Ac:9p[K#CGv8 ]}tStC=HHGΘ\9 zo?:=W]kV6-[8toϱ/lX6ՉP"c?tk؟Rť5MmۮWUN0^O9nVeJ4 AWuWZuaɍύpe㺩yL`9q >yᆋ.Or9~KN?(:4aˢ|$8^qwSZ4oi7sK?r[;cc/]dGVg]S@Y 7ܐg䘡7u/~1ߡnllKLqd+%@ }z|{z"52,]6;$|C/.9!.uDž2|Eb{.)ؔzö2Бhŀ.?mن=FFS>-rWGW.3[ɃE+bVƭ[ypmſ1=U`?m#7_~O~gɍWXV4.{o9q̙(`n忿71eF)p~;`k4_[&ȃwLOzCc9~;1 nl:~zgڠӾMۋ8mZnw;Ӟxy?atjvȏ3Ig}v:#?74Jʜ~d|͹yk]|Ho~ 3݄ٳ:+]z |~-^ޛC𲗽,?ymG^7p7yS ~x+`ot{zkq*=д[d~9+~D[gNOx2:oL_~yo:ۯ Ϧ|H6&VglJ] MYA1vS#fKS=_ݵ"::&AQ`F=&K}bEWnRl[.|C#뢟G(BF@lA>А"k ƍԣ]W4imU.#?i%I?9=]>C<<)kyڢ_vHg:4Jw•JWzTnN]=4oւN!=xϬ3'Uo.>}juqjuոiFڹؕ[B#>>yFnUƚ6G\Z]e?ܣǖyX G\9aɳ.ƒXoqUܜ^,q?zGv؇:ߢ\)k[Qڅ^w۸,Pc./|beG\}aɳ.T_цH]ۮɓ|K_׳nH>=w6y*j4pa)McKVmbƫ /O.\L6-s\>w6ϓW1w8&l?g2~ĕf[ύ?ƶE'r"WI>BGB }uhs6K?O+r"68*|Fv}-Ÿqa>>rni~ƒ{O}i%=ʀx{Q5H*@ 4\qlQ|報@}ES2.8cp8Em!K0 mb4nqQ">@'أ] )S_b3~-KFAl#3؀N̑68E;؅Ft9(N,đ'-Eݙ[/?X#I:9[.ircM#C;wr_'Cl̏Hc`ޏa_ǚs`3v:憴ȗ\]ש=}KSJEYi=e4[[M~|#|du+Ӊ>r] }K{E뭷`-;̣ܥf(]p^uUm[81׿'ݡ6Fh{O2<.\,? |{TW _}Gyd2s_N?$9ٰ;@=lrq&>k8^~cGF`Յ'˃Ӧ!2$6[Vl-2bώ nABNf?7߹rQ=B9h "ypŽ]exD6k_[:<tؐg@hR{G źA:6}2f+FB?w4}ʜC`3G\x,s}9 qDF}F8|?ͯk1?㾹c8cIq.M~K+݂6_m9>mqn\څϹ.cl`щzTmKm.h V_( _>wPÇ(=i O|"[_^K;tu:_G[7stq#|$qC]PiH|htB:9pUǁRm9ف$8 LB m6?a?Q6 #MFᣇ|#䠧=K;آ, 12=mm?ыox >c~LeFU<owW-zrGr078~SnQztn!O<:>x|_cK uczs1yoZ2@Khc݂#4q˷..]a>JƝdir|d1cBZ@CݶGg~ցN>lPRjC}GC9$=yQԉxl{ ď#iG.it}U$l~tP;Bmc gO]lj1*m3+)EAkƈmM̓Sgw+NhA><-rc=X r1~><'o:t6ǜ?y#=3vW㏞).O9~5q a< ,)Ý?J{#F*$GwS>b.ع{ۮb[@4qz+ų5q}Ow){eW~]rRREĉŃNtW|w˂.O0.e" |ns1yV۞YuqANzܠIÆ:<$ȋV24e<Ȱ`@OGG `ti=_$ Mtب6< ]N^-S/P<ѥ<1h96h 9e7y-ߢP[,P۶Myquاrcws. :;qV7sC?G.z6<]|wC9~Ib,97-FqL:sdNsă?ĸ{nm`9"fn0Ύ}4cQS?yqIQϻ矟뜻ն3}u}ލB;ۮ?>z'l~Hэ_GFY? }{tDV6X>hm \|V>b <3đ \çv. PR[o[u^F{|J|XSf_/_Gl,B@IWvL:0.}'mcg4@Ĺ`Jԑ by,) $v(IAusvQА?l@xjvƅSO=5?l;º;tw䏐!7h2?">.s _~GUzb} ?g)/ixS|[Oxҋ_tE/j[X/ieK ?e[D~<"2쓿S3"kAi w?RVkQ8.yk/W>{'xyܠ:[BȓFvuպOs ܁c'@T|uJȨGB4pvfB:ŶXTpۅNQ: dSr@C?)GbаG Y/{2Ұ J|QYJ'E:4yVC+[cؖP~̑:Zg&7Z2G00O0v#N9b 0--w.D!?Z(f1fƉh?in0v 2e:ѠWG&1]#1uɁhLpj'{SaA~ӟ soƋGP ׾^lKu4y%44J?Ƕ;7=tM}IqfӦe~agl6 Ȣm#p嗯b}'末q?,*SciY lmy埖<3V @+_yf׽.˴4S{s\?4yl&c9&m1<_jK|KᤓNj?z׻r;z96E}c?!> hӳn4ŋ[8(r;#] / nkn[02& PPxu]i]} a r`?@[ +oԱ>tl.m# >؞uRmC:mP}ȠLJ]ڰmd2K{uYB}?rKq\#CAɃ1'X‹xsڐ_SsY:B:s=&yر<hf]= K^9>K?+WXu]g\'?I^Gv%Ϻ7Zݩ/~}.ߡN<:Ip>wi Fb 'Ͻ9Xg|'1nRhv~c SS4Y2:(?R ~GC-iO=dJd^UAN( @ x<AUbm 착Gdz_2bgE]6<> tpx1<8]ؠѮvA;ǎ eSdFQJ6E` <}D8ՙq3I_zkA8@sGl]3lrcύᎿy: MOshSc1}fM87p8㏞кU3m 9kӛf:Ƌ;~';M> •GYaw\X>?wy\|_UDn= Msb颜͝b_gm\^}^.on5T8?C~{bÇي2/zыr@#>#/@"b?Г]S‹%ݺ>iS |hCEO|nuQŘT b:/SuɷO>t ɍy\]{$o~ɇ4ͼcusDZsX;XlkyձecQ?}?B|uJ>[1: ɹNNHlp`.8Nt#MPBV'%w ?nq1@c |l| }W[~SqbCbf_G>(lKS3| P,xiS=2QPʂ}>PK'.d.[rSv\8$E* K{+OԹ}GC}82@lYhԁ'NWm#,ߺG[i6Ƀ5/xS4Fɍgpq\GcͧhS|qVX.ͼf? c 3㭬ueo,L,Chkg4 }Y>K1ϡF?tA\ˎzeN?/zBw҇[$E'-:LP.Y)0΢2'U:7Ճ'=(˕ȡG{lD6O]QYh%6ĊIyP8Ш;.^<&7ZϚF6cF\Q8n@Onh88hs84<|k9ۄw_-\S^[P(9QdaL!iac:|,:[\X];mVXķN&=l1jLkZ}{lMrѡ_ ϟ(3遴hޒW㣶Nov,+,(M:+M:;-[bPiQZ4qe$ Wlvk>;@c)7\rgzܡ{fY=t̍Dk;efξ.X s:4~q?^SZ_-ㆯc5G80m|H&Rvu?6bbcɢ?7ͨcOl;[o.uy q1f!Wqz-DNu"l7JFx֟LQ_ 'Nҩ?d~ t-$?$:'oh$qJQ:t1p 'Cp2ˁԱ`ZǛTvAX\/sŲI ٦mӿ{x~׭Ҭ;o{flMO-=Kw45+g3=|{?a?+-[W]@WA჆~E:guZhx"o9"FcAzBؐGr\ NE[vω,qVpo{Fu}m;si81v--^4ͺ^ѷC8 zU7kU um%r]_gF^g|h%7C/rӯL,XcBeG< E_ՑM.]pp;pJlȅ={><@TsVz[sJ$j3dL...Hf/HG|'_ی<1gG#?F<ןQ'c #mCTNģY?sx]\b #mɏ:|8~8 #l$+ʄDpDi[g\NHq0EMگtId8n{L:.NɅ~Iyхp  ΙMg|An83c=G-2/K_NVG/PuW53~x^kzM[l;1ǧ^G.}qfgf銻ҿ1g\sŹy V^FJG۞Iarz|#6xe,Q c#<Ɓ:|6;l=q06ԍA)'_q>Vei&nQ3}/\ %1t?FG>jxu ,zАcA 4p0= <=bS] 3zF^t~Sِ M/9cӉE6ev֧}e.V-vzyG>-c6 ,Y,}/!]}-I;MWd8&qD cRA4 iK=_H/uS1.هnAne Oy@'.l.t[]"C7Vy?F@iॎrGfu9P7GoՅ|to17Aw@S@g,*O^*ۨdxzH~u_ӿؠ- 6V2;8~1xiWsWǼOYqRdi?:?sVoBq/ק|7ycnyu k.ѵne.uE?}4.ڴWʠ%:t8RwIy8;~t ZC2k~Ƨuc<fx+d}IgOw |S?>]qMiҵf:rH7#?l{, ]4X2N󜛫Cyܟ^MnЏydnN 궃:s,BB-ǃ23P/nL["?u|i݄u>"?#? xnGc ݟˁp^%xzG ,_d8}E2~!gg84guqlH +ghñF|'VGn@.q@0sD =ϸ*6wtR ~݇ri{N?F6_rϑ1 bJxڈ:D37 vsUqu~K/KnܚwwLOzc 0nl>! ~4zMՅ'?gߴ[ @S sݷ޿Jw.$ݱ<=xrZ|3~=i˳V>QN>01qdl uƎ/t u#o@?u:W}@&}<~(u6xlMns9o9 i;ҟLd]h@`:RhOYhEՁ?Ǜ>ˡ<_~3}i~vKwv.̑/I3oLc|o1#Ka3G,K;:>pS||:P\}czb.2vl5>Ɨm| k9zi{#_h^xЗe7,=h\'kڪ<0j[؞3!4,s@37)anH7~cMS'7~ۚ@۾O]isHWvĺ9iqDޒgK֏'rym[9y_ S^3zk7}BzW"}Ϗ+4 +C7zΦ-mLO矚ytjo|}?ޏ[cD+Vʖ*s,YuG̃A父<@^:/p^_sWfBZMlPzGڼ_>aBN5րXo#BІMZK ($WY\Ju6Z,@nֹ{@$t-.Y\8op>mxs1ꅙ WWNI²7zN>);D?6x,{ ~9I[E#8tb4+tex䡩{Ha BQ>+/)K~$>{͜WfXvt6o|Aoj5q٪<0A"7enx9q/t肟ouߟ _GFwDOdar\qEow27nC|yW׏遗ݓ5$>:N{yo#ݛyp|蘎9fx̓w7Jwr>r#dO_HߛGߕ^ӧ?yR vn8n 4I/A+Ҿ-˃|4G`#'7hyp̃iA 5sv[;2e凍m&_۠㋭ΧgcGc\ټ! nd^ g8i 9 GZUv y%]~A1YbMeqܸ_x(?}sˏ+ȇ'~M> Uy@!8m|a2mߪЮr(sü1:Gu6 r#ӡc"?D_xRC؊pIi/;miЭ#ߌ=?=U'ðF@yh*c~n!}>~@G[ni+W<~KSgwʼ=J7:O?K .KmUn8nl.M 6*Сq4ڍmabsr#f|E@sz]C!o<@Veq[4dv&~>co{a` j48C8=1 w|Anœ@ sfʄ°B@8 1HmmʷxsydԱȅ ]΀t2xoiGP\yQ /7۹cGb:^!:}x=h }[k>x~>FScxKؖcK~B^ N) gϺ@i ؖ/o@?yy9:6}?cM@z0cf/۬W(Vwx,ޖ3mXdÄf} k<覴Ƶ@dmv-׮]oӗk! jT "i} / 2 m.R.fcT*ot ŀRYCǦH?nF~-.ݼ,Qviw|Ƞ1QGPCOFqEFaph<\q]M٬t >z"Nh0w35NئĵW`c2ŷ*7~[27<x~]=G66 c&Ŀ<:8_ e 'YW^>󉧻!2oo[?107bLɏeb:[nc5wm;8yj/n17+9.,`O΃戸~Ar/V?VFO zF {'1c[:fƺ(c㩿 zc]Z tAf2fZ:ݗG/'AY\+C|/X4Y| 6ui;2\\Qs@:@l`!܋=K.kGpW ;i>؎<wy?Fƀi yiMQ_q2&e;ud׿^6 t*?ȁuYb}Íy4>r1n^E9̥݌gñ25n a`OߏZNhFZģOc;zԵ:Gf~o^\4in /7 #Q WJq4洉6ەޜfp؏/+ӡqen::bnLLwnvnOP~~)=/6x!l yG.rymVԑeٱu:@"a?\y(;\?V#qSboV#h_ot|FF6)w+GZSq7`_o͊Kqy\Ϗ|_nZ _fXy抬|q ^OW+QnᄐYՓa,D^P2^pЏXgB>d\D_ڇ^E<_e/m[y.Epty7ԑ>n.vO-^tПŵ^6hh7tiC^tr?蓢_GG>]2;.efrׯX?\>3|ܔWx1<0[덿276vct`9G(s70!Lhbn`j;vcim>u~;9nݛ䝩15F7bcOiL~'q̡f _H3H#1~?ujg&9<iß+r5CXŵ:pw#эҜu%6 jo(k<:\O>); ȹ س wY/έtᰍGSb#nwV<]NgCp a=x`ifçmC8<.ڴS?di |~$K_O4zO}KxeC})F!6ц6G::}Ե߾>x:#-AAGtcύM[_Ə@l5Kooaє̇Vb1[p;/YoP1륌j3J8lUn# 3WcqG<˴;G6GF57>vm_s& "f;8X7fnF)sou;֙sc,9/>7s? zW?xqC'?{ww_e~`Laߜg+A9susg;Ə|czĜ6D# QEG~uD˻0nF?Xkvƃ5elY|cY7$aĥ5K]sq=,p&2h.^y(/ml#4dq} G ~mCj޼UMhS}>3wg?B{]mb;Yc~6 d ~bcFtDZc<Ջm H..Ҁq>#mW_m_qPkt,H`쳱oI'zGVu.iͿ_J|Gcg's3Lߜ|Uo~$_R{~/ѷ.ꍭK|,QQ?nэ^ _87#qۅ #]#ƄqlvݘO_Vwܠ94}x[ ?Ho|@mW9;01q =x~O|w 4O-1QnK[@ۄzmnenwG$y:98"Ch y,enp#2ʹmeaQh{GvmB@|jX7ڱp4Vmp?kcCߒ>>׏鞇O7~UO$L>{?7>Ox';WMO5?;zl~z9?f9Lk+mа=-6"vjGEO'M}Q؎w:b<}e\_-oxoɼnt^8#/MoG?{"2G#]tP>-e!mvH&,ԣ.ۇ c-/ۄzm&,6yb[)_3V'r\ m.MXҩmĶx"zq[V6aL&6mqۅ K:MVzģJ%jl}9J '+, NpL hwbB }N= }<8|oom? QۑVǛ37ӿ}̯oiG2_KO?i6^}]lƉ c_(͎h;t6dЭ_QG~ 82\ӸaTK>JzVGOӻ;iި[|SqQF:mܭ̷7ay3 )Ƶ+\~ X@:mDѷw_܈o.?#>6ȮS}mǚHӏ>VF_3H$(Cgmt XssSS4hC"}umm̃K4?(n(ѯ5m{1wlmGfq#7#ݻ͟7?nV{6:.޸1؞|4+սPzͫ_ޱWgy&]~}]sc)c᚝MϟOO[7:* m XSXXu" w\ѧnwtĤA|d]B:߻Htbm@hrB`:yd} *o;ZmOrsҟ¹FyƹyU<{4]͛s_м$k<0NOtȑ܉y\C ]Bnh/qJ͍W8KGGeO7Hnw-tgi6sy_{ɔl\?Mɽ㥆~ :^H/:vg=ީyСCM^sZosN:\ FwWw#EvJgΜIˮ=WM[ks=.^{vGؠ-9N>}j' Ygb[}F|o<[1c)?Hjȏ,MUvFO:}6a"vme : '7u m^dYiʏu96@tG=ӍI2@7Aߔxi?l:O~ 'u< Nplt h(M^dK/hK_I~N{2.o9>}۽dxe}59~tۅ ?{Tr#Fgb%my1#rcogcn;o3- QX?bntW`_c`A88׶~|]ߨE~~?fAxzo[bb(#ߋ`!t'i!rcl?shn>f1gHiЀȱ Dž=?^0h8!Sf>7sDG`_~3Dvq8.hbA_FV_C.-ȩ~Jy2lyU|3y0ǜc^@=8?K]|#܂vAn5E]u~uy0\7:91Os/B{!tq %4B@vo߄ꏭ&QYoE?x.N&Z /L'5C])XoR^6AspQ/r9 ss>贻ٕo tu9v ^M2wՏnW7 ʸ0 nL)V^v>ƣqtt!6Oonc'Ev?{i 9xGEݐhcc{!]e"۷|u|_\xn=qXg pax##ohp fD^p:qtC17tB'vxh':hB>/hfQ֍!u輫@>hM2 H8g |A[vd7>"D9hn5OXiCGRs3WQ4JsD0&uiئyc~v!}2\+s'#i˶HWٴ^G0G(~9b/myl:d?mٻ0!e73UD8g.p-D^mnoGym^ nҗm].6%\-8ɮ7#ʌu6%S.8DŽkWeԡcgxI[GgY/8 &~<1N Wlb]c-f] A_ti>o_5ߜ2T\?aA?~qy"6m|p57FܤŸ6cK9)=o`,/1O#:\1ZA?b#o$1Onj_2͈e;.xIlv}`M(:N3 :Q`\YV:fŃ@tn ui@v! y7~>'HAS>@+KI<~:&<ϻܘZoi+->sD[ZoO\d}yެFw.37'Y_)Jtsc#XX4+_͆)iW~uZ߉qKԩNg9V\Ҭ_ WWҬcn^ _|/{w3͏e_T.7osnlNo6Bs»s憝>Ї}h;)ѿLF{Ɲb}(XS<Ȁvi?C>yt4kgdqݛmGw@B?vM -QG>ګ|]):iT7[?/ԧ4QVAy@ ]>9~Q4sF͍ыȅkf\.Vv$/#i/7 oo&͈K鿲N1npmMyXߕ+/MI1 ogމ;ptQGwڵ.}6>=]ƀ`D ɦ6xC;tczRhtKġ!/ Q޽{ҵF5~f 61~ Z̓-ʃލ1JqyAޏ}c[sMܠݜ͹ѹZ:3@o vƷĩ:8GcK=+Cg#4hjnwÂ<ߍc8$6='D|Do6?Ds'O|4`wҥ.}f&کnuHpNΘ\p{ sу @IDATw.`Rw dpz1 Mi! ]nCq#NAj 2_lIF/|,|b:1R^n ~CF^:O< C];(Б-~8:=ݴV/mtmŦ}Yi``Fg&'|mέi57F=7ow^,: 2X?kn4yܘ: 47;mg}눭p frayUڻύc6ύ/S:'ѽ\\Z avzO Ov7,QE Eܶ64 W7qAvp:rRF٠:n@cl <2ئ,zhFqQ!I}ц t1uw'p <)%.Pu]OK?6w7Ou^hc?SïiC6lopV:r`ߴ- cSg?&- 5r<pd ٩6bj7 E1oC''iB#8WG17߸^{w"nqzG8AnLNHOW1ʹ1y"?w|G.\m<` >b]?Fx8pto:tbw!1 j0ި;Y>iqF;ٟw_iOč0^ɞgG? m>&nk?X;4ttV.PG/=# 88nI"| M,|l\9 Tmj6a6M4䠣BЬܸG{ ?㆗"tbG94))y8a б5_9ĄvG^tF}_wT[rT51iĹɶhޓ.࣒nϝo)eH#rKܸﵓݹ#UXm%vpC:bdA#, Rҕ~K쳴K[Jz?{K. k$x|X+~-nHZ_ dB3BimƂvPx4\~#X.ncſz٘+_:qPG?胋gth3@/hʀSৠ}B+Ç1 6Ph [nNCc@Wl^hwɗL58Qox\}4L_茱t ?qKk?2䡓NW?s59b4qv:-9Ṱ:GsjqbԖVvs_~QqEPBc'_L>^<b΅P cꡍM+b8P'9ܼ6}t{t~d uiA>oO;tĵ1F=7phv7b_u3;1E3zN:Bg&.a\؁ԝzp|%B/ z8'']ClVㇵ{_~Bc#M^tim ;ɽCgӍ.O/83~pomØ#Ҟ;'c'tHŖXKkIW8\?k_HW>s#6OQ獭 n% S y+>nLOOoO(?-7әyEy{^?Nw&\1=Ң.iÆҶث>u {0hc98+cܵc#Wt6y4x(Ӡ r 6a6{,'Ϗ]N7Ln~#G, xXKٸG\,M<4t".P:<d(9hS6oiM{GOh/ !Ot˃J>~ TktR~2``\K?GI//ܸ]:، [)5z1[y0qNz[7l<..>H#o:X#~9sմ٩n>WsSߗ<|>s7 ØS~F~<8sJ F]?Vw '_7}ٱf 6fb܅Ł+9q5Տ߆yp'yiJDpE<9(1 6yll/cnvLi5ϟm0^}V1x8<Ѐ6K2m5Х",l$`k._r;s9]-Q~\D @yqBE6ڏ m؀ iF/t6?6yEQ7vSÏʁ[G?toaa>M?|;=coݹX|xKRͷx;=i8@/nj~ c ѕӆy @/;eo;9GrJpar;G<~;=Zt΍۝MA͍^o컙tKOÇFyɹ͍s%;F᭹qws[橠0~1m:]?X\L?׾'Nj<9=N67k!zwi_w'oFl˾C7룻 G[뉃s ; ?ˀDb {}quJ{K=6{\1/zi?]_@ Td68Ѯb >lA/}ѷ669(*[KG^3^ui~piۤSo]u{#'8;o<`o~wcWm>ǸƘ6!M37hJy6ߙjnltN'n 2goxuJ]zO֩ 6yy2oO]M2Q3ܸ;1yro΍A8K_OFrG].OL9:Xyϱ:[poGVs̾tBbt儑\>p(w,g9>Vm3mҩ \>pmҕڼO4Yq-µ9k_[iql|`_YK 1 zp#6_M?#ο⎁b4M"Yݍr,6d|BC/4쎋&rh]iCN[˃>h 4 ./]G:}P#)?i²ͺ+'>krbL.}v!=ѝ8љ[\C{/S #a(>"_-a Gٹ&7n9X #7̹tf(e,c mօ1'kgW=u?qyn#K_1}Gmgڭ #M~3hRf]/FzķcK[mF_2vJu+XxF E? kj6]]'yFoa1?}rr knڬsÅ!u >Gu6Ҁ,b+{8 tYAM%  (zA7}n{f 4~(tl~ t緖|#&O6JÇH^BڥFclelyLbu^7*B~!?֍/_ph7zkAߞ|3*z|*] l/Ԉqѧ+8 xh0?rgrTIן_H7^۵ F~G~ Z爥Vrn^Lߜ| 7}'GVW֥ ixO~9ȇxX?fv>o_?]˹Qsc뇹נNnLKO<5>Xz xh}cL=אsiiR:Ƀ9bsDǁ|d2 ZO#grt!'av !>+8 xh͢}%lVYom_66Z)g& xmv)N׎f;U^?l,h}3/Q0m̫_i6727;Hw0BK\)uܑ͂4|fqaܾ7zv,qۅjNk!.k:;| PÝ;}|ÜRGoO~E?mvhه-vl@sa<悙e?KB-vi@Vpr0yobp< g8gؗP#^-tQ~#ou79-?b/~cZгMw>;C=yɃ=yEjd;n={cFYMbvb7H Ɨ73>ƚN&taG|y{/;p;bzsDGg{nΝk]޹9B;sDG`y cc'h!4`^Dy7^=[:viµO6'Ģ뇹F%#^֏SlyyY;9o#-"Ol4]?3O4/N/4y>-戲z&._1G;kH X}~xQ߈|[In.*.֔R6n׿@A?9}w?_lbxhg9s ^N7PyYiHE(#.¬/~V6NGh, iğ 6GQC}Jx 'c)_h0ʂt儶 Ky탎 |:?G_n7H_>FJW=..ܨ<u}w9b{đSXJ}mFY𒮜vJ嬫_(](~v<`"J?7aIBۅ Kro6?KrBۅ;MϰQe%]9&_gu(ԏ²vN/3HwY{?&lV;Dz m1(Ǻ| R\#o𡼤.,z?9%Y,z?9%BCzϱ.JFa{AngfC+%Y''/%Y''/A:0#~Q}Ct`_m4-격KKONz_KON:x#%0b<"r6,y7'.mu㥏%V[?͹HwKv1smqǺcBȿk8lvfަO xܽxܺ{Jq~!eyT ?wcb߿rk]7-GzD^{# :iKMF? +~_hkmDuGQ5B̓ۉu%xDQ:6Z͟O_ϣmt^mmxluf2_zۭaŜkX UB%҄&|~ ,. - N*OR?uc]?Ѷ&uLXכ7шG1'Zϑ+w߷#21h`q!BdҪ|ߠWO[^ JSg\iS-/?p3 >UH?6Z'}.l}csa\k@݂RT|xyWϿ:\1/"O=~Ο;c䂙zwKzM=kQZq܍ fZ?Ă#31>\ ?0q<==K[Ǐu_i !L: r]r>^"/O6HSϟΛJ='QsQ<慴:Ο;c  ~ 0 z:;o'wN<}ѥѾ} &j+nߟ#qc/k=?@}=N:YX:q #M ."qz1_/=~?5J̋^jSϟ:xte駭?҉߳sSXzw1^ ƹ5w-8}uZFsx8p{?/.؍~\{J~99H{0ŋ 2pt%u(UU_͟2'|8]~-]x :m҄юV֫|9!@铲^ϟz^ OsBX'e?;kP6|KYQw;^]K'}`n46Ă Lbb?Zƃ,oݬ1n0رKG__X&܉|L<WH?CYCI.\Ni;umym4mTF׶Hw}G%ʶH\0G̋+E|5jsB+l4{ɓ'K7}^oӧ qcdN[eoPgoR= ڌ'a8?˹S97j<$nE⻵@.oy…8e G_tg|Ёƣ i@jXBo @9s}D;nƋqboPWdbӶy)s!n<9Wx 7Cba<1|r/$vs|r2ƃwԧ?;|ץo7|.; k-C!,' XHFO~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~x/~2^zG'LG~xg2כo+>t'ܰzy^_;)Ąį71M47!bsKגlؤqI7/^,oy=׻spD#.w#61 };|=6ܴpx=O[|cbo#6ܨ`α `쌗xxsq{x~ fXݝbo@=(crfR l+ "VB@@@@@@@<3lוйsM y7^:26ćظ6BKh#>po_w13V7Āsx}}zz.sS"ƃ=7`߱)Mڈ-S, BN~ˍXF h sw÷6AިϪ|@@@@@@n@ĸ:<.zW36l66?;~g;?oیV+o6&/ }{;2FxvxQƳXWA{k>la d AL+aح"~:6J>&ܩ׻s]7/h { p6]lwsA'xh9?x |۵ t0 c[7[aAb'P#˺ vp=ӯw8XZ|bJ0nPKߗQ}[JS/i'ٹu?PO ?̳7|enV?Uo@@@@@@@@Vx\VPy8{-*yXǻK9ɉ.CEFyfj= ~lKWDzzzzzzzzzzzzz`y`x#Ht6% b-6~uR֫v+ _[}[e_ޗ&u3|^=P=P=P=P=P=P=P=P=P=P=P=P=P=0NxJ nB.^Uyos֊TTTTTTTTTTTTTTu\r͛7ӍszDߺ{Q˗#OūF7o.vxb?6t7Q򕫑#={,$/6t71h#hϋ ?_G'NP)z`zjJ4?x ɉpEwيnk#~5wG-"՞ꁭf]9fv}0+ZZ=V6d{ k{x<5WnZsw#U<lSoo_-48o\=P=P=P=P=P=0dx!㚄\z..O>{9=}y&޳/_ݻͫƐgxF[涅weٗu*Z:NNN/A{ҭ,BӇP"R,66f|cF'ZݗLNٳ4f,&۟xo:2{9C4c}"ds16.3nO8F8Ŷq1̋-31̶ЎʣB= }/}{ҡ҃'8ӛ>ٴG3<|3_G t'ӕ}&cf\b6>0 u|EAW,Cm&nv*?{ƍN!K;9M&@tpxi·#i#}1ڰ:cF/k}ǡ}Án!֡! ] ݌4ǡϑ}"?EU^ F1k#mч HA#n3VFu<Cv;]ytQ7Oyt#tۇ9cp@PW:-7y0;v27%ydã,2) {sսh:"D:E@຅ }C~zm闢 c.?pLN?[}耗>u}zgAoDt&99C)tSc,zƈ ؿ!+[u itϧggt!ߞ:݌+oav{KI>pGP|4p>}4=7{>.ȋr^rC1q` eԑ_ O?$2Йݓ唵?dR{с,.:zG:~tBH5 K4qEݱofƉ.ᣔctB /:f۰%څu 雾W<=AC9'N= =@.k#wArƝvnxێ >M>Յ/Fe ="kP@=_x|>h4l^l ܩ_tSy ri[n cE^ ]Y u r@}0puyc./9|Wtƥ>~ӿ~LyVK|Yl0nAs}n~1X' %Fhƚyќu?{w_vtYzɾ@$B$A àRX35:Ni) c8SC:LQL8 "YLXA6[oIkzy;&Ͽs{kϊb=nMѻh=M^O֧L5gt1j/!y[ty&At7!/):ƛ# ,:O= f~Yv'g2û6D_5>}b]- }>̫ݸċN k?͂黱p=n|G{^u"ӷ@O[{7v\Z ^3 -rh\y$wg'>x|A^[>wj|%|(FGv{>j+_Svdğj ?ӹE&@>:~qIw>C6GAj6{rVd//۶[=?{l}zb/x/]~μ5[IU} R$Jx}#_wx%_ /Ф(i;lY36_ϛl3Ӭq17/,ch?O>$)o>W5~~A7zNϞlfNY1r͑dV󉏃\ Aov퍷̫OX<\<I<|9'l4:`_w}b4w~yT'VOWU5k|&Z,@IDAT^y˧?K}MuGN)65W){A6#\42u}f.>>'\~a4vNV)“<\~#:|:k/baNjG[ G!tq s/um>hӛ=_xS;x+?< ^ox8gr] bgWrqCWHkw.'> =)(~fCc1߮p>C Vu7]}׿gTx<*J`@/A- <liAFylS@W_O./|ʑvyZ>8tWָZ_cC.4]'t5'?#:oq3}SbU)>rdz3z'~G^}?J(OO_{ˡ|:8>;;t[?ed5x!#:|K7y4>.P??FÆNrhŃ|ȧx@@Ə>>{޿]_ڞ۾~8'`yox3_eϾOK_uW=]i55v׿,l|:ȕ\}A5yx>HN}mrc(GW+MG{Yo4ŧ_vcOί6'g=]㣓\>\<ɗ|Bh|k7?m~5^cm ȫvsL:o5mo%~_]O߅n'77T>/=$ph>8M&=p^yqpwu>nLip\m|VY֏ w Oc^D1;@~#W|C#n9k+򄎦W[}fsdo Ȑ-/LQW~o~EuhmO<.4/ocۋ{lؕ;獑|_خW3i5o͕q}gtˉ|`M^Y<@KW9 :;M_n?ӵCWt@:[=<{O.bQl]M7Jyt,J'9eo hƇ|>]h;B9m~n W[{eu,øѥnkӗN}/d^rگ)r-j9n<ɝ.Zr!_}sW9Cb;o|۶o¯[c6.d-zzo͋c\.Szb=/!7kțwôhl M0pjˏU/h|]7o%gd͓x_kkz>d^-Wt:Vl__[ ЕiK5_WKЖ}2\2Szcm:ؠ>9 7|m~ugˏ3֍ѷqmە3,~c~z CG~x=sF>ּ8X,˞kF^uyoct>{j!tEK] r.Цf?r ĝ]|_tQhdd3||hě)Vy(7d^6x1o-=7nÏ>?tQ}><6|Џnf?_ht<ҏ@+wr/Yz'n2 î=&sw}\{zGAvuat|rX&܊uj6f[o@m5e|S ST5>5û`dM6Ćc.#J|5}_CN{pk ׽7>Lߐ.)G׿[~௎Oh5c],N^ɠ)+σ+]߮;xdrz#{d/ i^n?<%_<B_2&@KB_2M~~XlBqKbS?+n8}v>jCǗ_o<A%?mh_ J?9ӟlb/_{dP[U /#^zxol Tӳ'{ó.7n}ы˯z?7g_/34'q[~>z׍gK J|A@#$ Hb6g~ Use搶9֜>m~5Un~=n~| Ym_17kGkH77Na=Kb֙k޺ Yr4<@KB_2dՍb)lvU|?z>*%_[ML[:X7,ͮ¿I:M?׸}^U#+^_: cbpuxWכ>ِKB\M\Վglqg<~Ź4fvW[F+]9ܾO1UxW+^>~G L@;'Fh ` _;JG _?j2bVFW)S~gr/}]4J @aqEk<cǃVj|~x8%Y?OzVצ+|&} eftE&_;"|6]|d]OͽmO>[2'S?Ϳ3gC<~5B qS{e{YI!yQ? l |$ZDTg Prk/_P5}Jmt&zenl7Ewv܄Ǔ[Kx>F|E?L6nt7Nex`?/:)8;>.<t@g7_+>b"\9']8rө~J>,a 'q$nu96叞xV9ӗ|^z=:_r&7j9Wϱց}W7s zٺm|$c+u{ ̚m[~hNz oŷG?iətt(Y8V}0gtSj{\z\sn՞3Eq~z8\ѻ~_G<6~|Qm\1g(X:G oF"gK'zsdj^NѴɭ|,ۣ_^כ7{:nNa_?n& wёG4@B6~}O,ɯ;}|p@{ `fȿvZ7]lk-53wɱ#_G|gn@<쩋@I_\e\q;=^6~vӣFAr PQ_v: \70Ndk A^: |__+?[ }lF[J><98#]pu~t'l+/=緛vr3RIX.܍p9pà{hw.$& jѶpdɃG$nã] Ggt dbC:+|S'{ىNr>6?s`7\p492Am@ylΝ!X[+?x~ٵ_=?/k] =/y5\>[Cow\|q]N#mh|ce!#ЇM}?G]}:yKuA[ As_krx8?GGtfz@G2L~CG~Qd@6ɝs8q/qw7>8>|鳱Û+r_ s  :52E}m'Y:ɩ膏]Z O&+366;g}?]-pوw ϟW]ueO6K.w~&[nzr$K.>'}bsH -a%Fh7.6i;m~=}Fckͷ}i;ZkPm~ko,ت6E4tbG4>W'^w Irֈz[A>[.P_{84[c͞cak!C:U;Kx &컆߁|i|?ѷ5{mW{hnj/i9:|Skl'oV#$9(vǽ/hFWxgzs[o ^Ȼ2ǒqOrɰM q10W/eO3b~tϽc~:sw-;g?׮ zGዦ/t<eGt%7y4]@[>]WkG#'7{`3?}Ļ G7dKN zsWg9u~c.x%ƛMy~MO4ɏfOg ??Ů-ǵVG>hp1p a??|oz=rг7GCZ щXݏ@x*Dof~<rGwzI q᥃AǾׁ_?oYc_+G,0an P{ |`L@ʑ3k&O}p.}4I Ȳ<^_&}렽~oc`wxYk~M+|dC2s3!wtS{-~>0lE0+Q:`81Ѧ-%3mu<ӯǿcn1/vYgo<ƞ17vHy7<;|n8$f>o1Y??t{oy;7~[=[|FshUm&G@.Gy!+좳^bІ3}xv兼x m (V#|+kᧇV#Ѧ i)Ǘ>aמ@F/ZïWS:Gk<+6f_6ү^l=OŁ?Y9);?ޕ?Y\=O-\G,p\[V-wbPZDڠ$7OoO=[?}6+׺ s|R1sis6wtŚ9pگb~vbX.\~řÚ,pٞ'ó]5v.vf׽/k-{}~>8}xO2c3HðOk g|x(Nˣx#>/e&Hp{ 79xwq^}}VkK=xOtN;[>Mɬ<?;n/{y:K6 riµOɽ7pXSl%+jW+b't>x }]>Y9qtdOMdm,:^Yq൳MtNgt/U.tP(x)Opx rӯ-ᣛCǣ]pn44Gn>&~$./j85j .?ɡÓ+_p?zt8<o>s~[sەlvuk |*s\uU˾>5/ܠ,:']tJ 8F6?ޢ2W&{9߂2wZd`Ϳi[P-$x2+ /)Gz{#}3~m 6|7͛6?=ۿ/ٵ)ɶ?a@~Tu:WsqE~-X׷=G;^+&:p}ֱkY32gX>vOG\ҡgnj7 eWn}>⛶zFzLtп>>:` 4iǷ1tVvYνكwxȯl.}7h L[LKx&cC|+7/yM_|/|yJ_bcq}gs_6J_'6>rhƖ/81 qSLt䛃4hCS-g} _>j 6h‘C:O4p?rNM7b+{>Nq!~|l+tc }ү"rNt5J__~:5yNJ O3W<~ׁp睇=ڜKνI  )J-Y+j& `koU筏W!oPߠ |o@Siid#K(xQ\@lK54| t+}ł>M[p@9C1ёoM~oj~6__>on5fVW`FuDQ Ỷߺ:B7o{/ZOp9MVAK_+s4W>+kK j;Z\O`k~7Måpݺ^o nڅ98BvX__k {~vc w >O׾1ġ^xF:KóC/#/~|=km_񎬾}kyb)+?|Fô帘Eqo]~hݔ < Zi/N7N<x<rZ 8x9?B],;sbbK#\É#ɩǯxTN7{=sD<Ug^x%N G zm%.{tћ=<htiӑmx2év6F>v>{_sk۽.]k |ڊ\l\qVx˟t]8rl6M<ou9bГN^55x Ntm2EOc'ױ酋?N]<Œ>W8K~z#kCx\wu=}Kv΅ts\@pnIZILN-Xj`Љ\jC yN_Ӽ0O%jL[orb)RZrh{Gߵwu'&=hxI_ ~$ϧ*4|{y3g\ 4{P_P#ӼQj7FQ|q珵Г| .<sbMFB}`=i8~'zk E;|M~+42bC,k|O~dsL6OCކ'{?ಭ /+f485\' ٪&٠ }pM6ߗfv>bAc#ad.?ĢgtmcA>iᶏny;m~]-Rga/7fi:\9i5N۟} J>c f~ux x#jukz<<>| oNGx]8: '7FWͮٷ~~pp5YOXFڷyO/N2^n:O==7Y /<;u9~ku֯8}Y.h5;6čvw[\Эm5v[85iwy?^b)Ky#_?_ܞrœ?~օk\rO?]ۇol1>}r5۟؏wuw_ Ӯx_s1j.qû֟o[|y./qxCopqS;_rʕyFVŻحsگ}5rVϾCU73k!POq[ftyB_᝽U;Yb:cyvO!hjX/t.ozfrFgUq en x)?{b֡b;Jד'ʥ>~-?K?zW{B7W>}=1u+}`@bh%g"wƕo@{clYОƘ,\o~?k>MdrM{ 6+@9\~kJ4|)ptu(yn<?}ů6>OI/;hEOgЁ>tl~Qd/h<ph6~v{}g^Ƿzsg.@c3(>u߼k~d{ޓ/?|ݿnW{ۙ 't`:W5;c31&^5/xs[S`';.6O|11@F@E(f J|?<]4]1n_铧|I{BʻvӣMVMj󱾶8F;mp5Aqphɡ/Z$OJN>.[swb勜x}843 >|x]k)tщg'tjg3}r;{צKV؃C"F5 ՙ?śxO}x8mpxӖ3|j |XC?dAWIm~ĖǜR$0`f: @*@w |4}no~yцַ/n?7]ֵn]2Ͽ /3N+hcu?_<tw_Wm|߫{?;-6?:ϝs_aSc߸>q"'>Hi:]d^ctvX[xܳ.''۳~VsYK6=D窤 k=et9pO{]BЯٛ͟>vbϞ'kݽ~ \ÿ6wfkށfdI{M8/ߝ{o-ȅۏ7|gw]o# NGԕu#epntߕ]:ЈońW]ߘsw}|a#;{_z~Oryum]>_NMaSOtW%< У'{i+Sğ-mO飇 @?|/ b(O~{0mOd48tKV@AAhxmtj0zfYO&Jriw^GWBџo|)wtkǫm_dWbzq#~\OS>ӯ#/{̏M O1^Ç}ηmW^vW_ۭw޺>~zq̗e ë#y9P7.x}گNuZ!Zv֓[GIOjzh]!~d>o'w6i>WݵYƄ=JOg=L:Ғ``cM{v?zדn/7~_?އ_&D񎜽"%v?yY`=w7ϨG֛G{d:/Gx6k~ ٓuSeQ>o5*7[pubAG2]'kj[|d?_c<<(]j'S]6kdžk|:bSxs,@x'dЕlkxGCˍh?9-GtE엣};5YEmUKTa k^kzIo|񐍇νxëSŒ [ɤ)3JE|nk|hO|ץ{] 6?6HA]6 >7^co˯^mjm~oK9?h [ǯ~{>Gߏg'g=K͏zr%x X^e7i:W^պi}Ez_`j5knB'˳=Y }7Գ,}d^|lky6>z:v^_7 F:lO{35pe'W}.Ϯxm᧛+wÇ_{J>;B}~xp^oC_?!Fc`c_sj6N@/5=y_7񧗞V5cFƯ/\n>ɠ9 (x6ӗc~Q~nlGjw/H6%srKx!\WCW%tyJ×W5'-zG+4}5z|J'zzrCK6>[6~>!T݀rBӡ]Ί|r{hxӃillҟ+٫F!l7?S]p_iÉ ୽ύ7xaWs(+j>aI%%HBv8NJ4+It(ty|t ЕhJ@]F(FuDc' +6/rdȢW|/DKOvd/Xђ/֯&-wr (Opvglҡ 8d_{O'z{-cyQ±XSq \6&gSwoo}_򻶟y˖?d_y۞:?WO.Ys?=`mf;>nu{ ^W.߲EGy7~j{#i';|'簮E:bK(<|\:Gc+rwگNan>h5sD=ұc9dگ{`'v˧^eo88ks8@CLtՖ/߱O}~9@0uqv+g0'n Ntucrd#k_=/ųfty}~`@ |7G^ xKqھ_{?G)|uàXz^c(_?[ xow'N.>w˩8q#}+do7}ld,v4vgSl C 6\>?;z]~ԁ6%_ѳXѣ5{@&>󀿍I2h {SLd;42x?t[;|~k' ȏl[>7;bV >ÓCc'ɠtm8| G>f/9 h@OV9Hf >?7Ϗ볠yفwǜɻ~{vmM&A$hɬA>m~\wc޴͓6.>|贉S/G>tG_4ﯾ/>I7%G=67Zavé@Yiki:W0SSMۚg !#uхJ9񙜯.fl[ݯs i;P3ۍ?19F_ m<񳉏?t 4|)~ďl+bhۼÃN~O/|^.y| >>9~.l~o()E@IDATѓ)6YQc@/a#.9{Y%^}|/@ O.c! —lfg)?OG]b^,^nm9=񑫍?}.~|k^^=?^gn@r{z9OkˏË85__#n/|dwًr Z1|?"喎Yl>-^gr|6 GWw}e5Mg0uϚ3lgtc53h 4#mK6Ʃ1u6zǓn>Yzmm: cSK@y\ya_⥗hvs5lQWrL8Or{gswt$k;>2B ~Y^m?>78r|m}`O1੯CG/ƽ~m|驏Woǯ_yy2Uaݯ~}ï훞%]~|*^>j =]&^9 O|Ǐθ.ߦΏw{㟹tK g?Y޽h7o:QWX%ܠ/xm~>>؅o.-!+/ [xp̶Z *.}t(]=W156yQ˩1;>7]8j^g؍kxt8h="{FvW>Fuubhͦټ[;i9.t7ޜz.o]P}mXa++7_7N놈\L{6X.4|ܝ8~kk<3_KO.=dVF=O{q죑>+pxt k obneS-2lU+&k*'j>+ȴ\{6|'/l!1}t85|}2&*uЕW ҅~8?<('굿JWG/ņ8Bƌ\<=_d.~e;s>{n/Ȃ~ V`$iңOVi;m~惹4K1᛫ͯ[~^rw%޾њ_o~l}җnſs{{~q/w'}S+t*͏p|i_^ԯ^{V~׽c{۵mR?tL>>s \*ϣ1V/Ҝj0j.r'W"[3_-u:ΜZOmOcre] ˁY1-ʼ3k^ۿdS;XsGfwY3wd7J~::jo ׾ԱҸ# ] yZ⟘W7ns d<΍y|ˑxnlMߠx9V.gE~s|#;lkT>6~/}xKJXruXzttN>|Іe[A>ʼn/qOϊ6>v4V+H}Qɯu:VuЋA??>u8t?~٤\(J;ǟ,^6s8|Ww@5m~қb)%\uU{no;>i$q]3Cd'M(%`\{7<ĄrP$7{:˶x;ͯ5B6ؾٿg_n'mz_/_/}m~tMͦ>s]]2ϙ V~fЋ7=ڭ/<=mi:W s<&[x['[?th+ bm@ >wOdv{'W?.Xb (m1P}JtHt޿/~ag7ޘ,8{=#\/M( qd.5tn}vw3둿0^'Yy#,>A{oP@$}@./p<`76g10ƥu$|ӳyp`q#7q(˯5 ӧ& <ϿdkEC^;Y|FA\ģ_\}%~٪]Gt} |O7rx|>Ntczӓ<^9dGv'2ɳE<+G銇v9Gmt>}r=NO6-~5<9W_}kLYwܯ*^' MV% SR'.!$Ѓ'>Ȝ6\1gO^/G{ >'o_3Nwo?+?g_(h/"9FG 8}2b8W>䨜ʛɕ+_㥏o<6: LcQNkbm%!~E~SuНY̠.qj])ZhluzgsgEq>3o;`xJ^+XjxeAv<=}Ne0~P{ɔ~U:5|kY+i[O90vy2<m­`'@zBΗ7ϏrA{~xYS}_'keO^=Enk`9FlCfNG|g i~6&5wGۏ5f^n|tUiol]Mw~S>:&ř|!<6%/@?dπ ŎM͇C5Ў|Wkj/_+r| V%NbM^&~|]E'ty3+#\{>qvᆏn9N3'iK.:}Ps^H< _KDEO/|_;j Oauqgb?/_nϙW/ޮ͏9{rEߏί_q飷ysu$u[˅q{+jÇ|x޾_۟mxخ_߰׾l͹⠯1½c~MO?.7U{9WOb [/smZ§m~o{"bt!jFq;i;vHdKɩz!q6u)3 55f@mpkFF'5޾N:^ӫ+n>x~8>[/u3dhbjo_3}1N_6k_>mi?prbxns:>+G\oL{+ ucxƞgqݮOÕg}՘ٷ́l~=N>5o&h OH:rJ'5n{A/{m:Sk/sw<(5㵓eW'< d%[ _]˭F9de䌅^8x/|\ Ww|D]`} qU.9I՗-=W6g(yah[Dwܲ}p Ŧ,/}+Άt?}}o? _kI}e~PG|?k';_v\w/y/or-h}g~3? ~;{6S 7YIL ~Y9=f- =Q.{.q~P-)k|&>vђ>>unv ͚+'S{4W~~zbִwHX V>|s{j̀E?ŭЗuɩO DndB'mPߊ X^O6\q+7"hӧKy\s_>ƏhӁV=nK~\5w#yzA —xjrrKN?y>~6O>:/=/h@zO7ҹot.hN~[#U_-wtQ^1vё~x|ŧg.^7ɶZIn|t>g _w &ف x N?Ǟ6?wNamt|~oO_N{mGoG^}E-qmo?{:pjC~ z^_ye6F_a8ꢋlntی?6}XxtvKaX9A9RzC''{5%7Y h?:.qn(=Qxð:H-:ͩ?u7źٟ>EO̦W/7+?_{qva7Qd=w^7GӦg^7;?3L&3L H*ڏ xAV륧=-=*Z=Gj9G)*[&!d9Ο30cygzw{v_h(::lPBjekΒȓ>K.ɥ:1WSİ/\r Y/m>I 2X,ڬQ\V\|$ohdOO 6{}->:]YOb\fzEK#q5FsѫF8>&:_4> .}|t1qiwL_;<=86 .zt?FĞĶ~ljAd㈯WrN6ptg$OGEn3=lөMNkf|6v!HަE\|^|%N_iƮ~O8Ác2lD~x#WV2XJ(8w㍷MrЌя"Lp sI8}[&D7f|+~-ִ\Fyw={n˥]~8`O:Qn^ғ5+k?2֐97ǻUʟk:bZQbs#r&'ࣗYxz|~>|e-|XSzɚ"5 ?1rݡ(~mn'?c^~w#66ɑϏK_#H@lw{AʋcAb%G>doP6ƙQyʓw/Νoga)=)8Q|>v&}^ҳFln+zA6q^7KhǚL y/2w.87tl!7h=ldpO~r&t?%SN+#;=7e˗6ì|qDOZa1Usw,#q≽?m-zp]6ٲC?1Kco pC7Ymx|'tQ˖Ӂ?vW/~&V8VdW{@6  Vƴށ1 ԉb&d% p$ß@#+xXTYE&Oɗ y7yf5ceMlK ҿu6ʓ>Wu,8=y7|J;OdW<"o<C>'u>_s!OZ=$s!i;PF9Ӹ{.In>Z}uWŦ>yf6>z|OɹN){{^bAx鵑>W {_x\ؘ^hHʮز!Mme311L}Q좷lQ~-v-z~3y><9-$c8_3'38qF?zp^aZe+эG&i =ψ :3!ߩQ+'NotgOroNGgt?Lt_QO xL/+NCK)䶭ǎGIg>27 ?X Ӆ|,Tpd̃6 J= 6o>Y _k'sY9Ǝ|||1d Wzz.NpxIDqӰ;]~ӛ*vrGg䳮o37Ɠ<:Z6vk@'H+ +=%$#氹o/^|Kzܽ1B#t? m]O7x]]+6l/T dsҹ,.(.ds:1$p{t\rb-k_.M6e:? :|_(Y9tn`)>Tl/Z9u$ߡ1EMfA/:#ѵ47S^s mjFԄkwߑ ‡']xx|D>Y+tcOMx@t2 ~F>'|D(>#NӓO zq8܂7稜c0'E=jU1Y7Sʟ"@t?#4!664+}:#OOɈX2l޲5Ctf.*wa~R8XG9sFf9W<@睾\S4o۫1{ӧ zԺswݜD,6f)|Ē9~,֩XΞ6H?܍?ʸF/˼ɗcw[ 6˼yM>7㥣|"C/_~r>&_l9~{ y2Fy_H+= k" 9PL9;]d:y/}dz*_;G%1^:3O)5vo_M.:ɰc|l:\ z =󉎴p >OW;"\Ė =69Es?[d9=x8qxO8rxlG>~:Lt&vcć#~-Ht1?c~;ˮЍѵ-}t/~>~9< ?~ =/lR~X +={°wZ֭[Ǚǟ>'?ܛ_%ęl # gR7px|& 'lYZBw2pYo8ᝰ$p9|v6ᬝbM'ޗr̆0w3Z۾_769Ȇݙo>=g5??`?GѴ)kE< 秶c}ߟ/G{pWz ̕~)^Xʲgj{s]MFʅyڢ7"C6םsņ{ w[\`pΥ}9:&tggfG.?va\-7w,-/Iy>7|G~wu oX_4Pb]c`L=k9ug߼}GYMs1 ;Z8ڵϺuľx {@.;f4_hF?6'=ᝮ"W,|Ht$\ĿĒ&Eq]\Y;<]S`bC|px+69+rx-Z` R#`3 ws;7q~lp ~}IH8Cȷe,|0Ir&G|>55'Y'МLsrM >끼51Wsf-XY_Oڱcx9۶m;gYin#w?/]|5W\;G=5x/~-p>pgyO^JڝӾ-M;e99cB/YZc]:=EwWhWL{\ȲQ&&>œPO NYㆥ-u#6l+y;? (^?-(Is+wrn~<%) 0x{T(݇ 9+g>s@ Nj~Eީ^p@^gm[ q-md>6m>Kc>էS< 82o ߢ,\3 0^<`}Z"GG֫EcƱ3Ř\=䃃Ox/r0Zft<>g" 9wz&nENE7?gnCx6Ʊ/n=1EgqГ!?Gg !vds*Pf;X3+p~ӡ@<}N3T%p䓀ȅɖ|2re!%_ <z'ʼnUnr2G}|93L礘927ߜf L?CՄ|v>g}Y9wd}Y?#ȹd-kA"9GM_'6@9_nx5{}<;tjp&α^FX\62ns_zzcLƚr$_8wٸLlMCwQg|J~Q9) ]KG9<ގ߫bj_@LL GeMK~^\.J=zqo?@GAG^£z#Yg΃b}Bgi|%_^}8rZccӟ?H/m:Xcd-e<@. T/ġ]}чNY:ɢ[rd1\#K#keCrK؀;5?6Yt~gIF9脣7?Ȁd+}Ɏ#>W /"7 L-șW|r>~4r5HO f}?\mۓl+;ߕ9$O8Nu׾qUK|ЁEEo'N~ bNhT𹠅 Tgqϑ/|5W'q||M?sV9$6i 9 n2Y^m<վ\}Y. })_<=Pn$wCzh9?UUm\7e/}wAٴAKr@ 3\6OyK/Q=SklaaN[z0~VfGL}q_;l8/<Q:զy_ɓ. ßc a19Z>ɹ9<ɯx= >4wVm)ߙd][MNc<^$VąN>?ҩ ~c>,{x؏\tc:Nx?Zx+\>0]Yb c8xm-f3 qLb=9iן3얷+\8XLɁЍ$> _I_}ɞO~/`mڱƬYZIYd'Cw[;̇ϴ0 sf~N_| M&O_Y 9<<KdBӧ>txk$Ѓ_/}Gog~8lj'89U3:T`u)Q;~5T,ުcK藢kl 4ξCMƚu…KGou믎k^r1|vĭ wK?=j)w\cָ+(k.K>kűjȗ޾P6Bk _[頓|N˖N%GuO Ը7_U.&8>Tm=\bztj<hA懏݃h͇mllAe=@&~vk x#^8<;Ya _>lxѵS\t>Yvf_6/9HLlO_h|+';:x/?Oܱ5ş؍/dAtx< 2G<94ѭ@;%o{֊>~9wEᏯDG>cя''9:i \)>'f}፾M~Rm|dlO783i_Pj ! ˣ2Yܞ{9qkYo]L,,.a>'?K-|'!3$N 3]> F .}tk@,O~I_ϑ|0nN \faXY7>[cTOxp ?^zG< Md[6}oڏx]Jnݙ7sw֋ϟ-朮ޔ?4Tmmf}]ʰz~ `0巻zd+Q[{S ftğ|ÍDZ-eo+uoՊ4ٵ2t|*?s-"+-&Ș2S SoA2C1Q:DIiN h>\g+^9Ih9n/qM-ƙ$fx;7/V\}C|C?^|pډ>|~2a+o߬}nv'ˇĢ;~4pѡ-[*1Ѓ9hQG>l,_:{'o Z$Ɔ]w xy%o ̟/>Ws~x~F}7+h|?; (9vq gSٶ)Tf|!V5wφp%0j_EgES*GJ'^x yKқcZlOe>x譯&ěGxBe]h𤂘sDn:Zі~7 eA1_6bk g Wuȏ?mdΓ1Qqǥ@ϭ\-{*V{]a~ISԋ|HA=}:M~O^<1_#:/G₋}b3~}5|3:>=@mxc^ O'hf^Aג%KtAρ?%&7YSx;ՁOn+&+I/@h5&zN-Co<~k6lEO|qx^-]on|#눮Vr9 -7˜Wܑ$ׇBUN`|f30g`9s w %#_LqնvQ,ËM~A wޛ=Kq-m e!ebO[m:(6ãAE G>x7كȘ#*rמ]ws۾-|I|(<]qE zm]q[xJם'\\DFt{\_zZ)xWk=korqag{Sz^ݳX^^F,Υy)սL>T\_5fXptkm|ϓSLWwѵٴ"NU j8xdz hC?SrY-b~6lzԼ2KTl![>BcwIȉ}(9вXW\)*O?op ~O/Co)A.w5rs< CAz?-_:atLբ2 Fk];́6>](<񧟍Xddٔu /l|{[@3SSn@O7M:$. > phbCx&e2k Щ%ĝy!~Ȳyt}v*'/ک]:;9H^/[cl~~|>6#1s>艏dO^s!w|3Fs-LtƗ_AwJ?Չ?(zZ㫘-<شE/=?o /xB{0=dv\<-vo 섺{~r`?`3㜁9s 35Ն<QUjm޼n~@!,@hل+ G )Ŗx] )Es *l!P(Z롛GCň?,G).慝AwŅuoZv|Y@2@v%6+6ˏ0bd;+~_cs76w}chQ6]5YnBŖ8m?Pp|T4yR9o˛Ь`V,d៮]2^A͚C#G@'+f+n-htAw7zXv惮EX;鈮N?1qx6טO>ht?[p{.]Z8@&bW 9s 30g`HjG᪌?WAHB- ;|_Pªu8NZ Jtէ߆>Xq9/ .Eaux\<]~-BE=dAşOI+}UT` NtwRU|1?VEm 1*XʏRl=\aC~*xcM5(}7plO_M<{R< k^Is]D?13ӧȅ(#}@gnXb#2lCFJ?\CK^^wz:^cӏM-:5vA#s#Wٴ|#~O/MˎL|o~?Yc|'O* h'> 8}O>>%/_r9cǁoG?=dl&:@/]Ӝw2lÏ/{tGVĞ<ѣ}Џ_A'%{'Yds 9o;?s 30g`̀Ɔ}w!T`rQ<&OaԛEQl6 ob6}`YxёL!(TGC1WmJw.پ(@tj_ȊGl6MPzW*ʎl.>$/仍/LI>G^p{kS<ݭ.U /5O6>q;]ъ7ޅm#Tk}|fd]+*WI8] ݱ'C rewl3c~K.>4 ďK³a /}I耋N6]thei}At5NW}[Әd=S^)McNyX O4ӗ_V~_BWgg30g`9s  Tvᅬ&dCN ¬ˆIG«ws)KŗJ!Բ#"Meck _] b_WEUs^ ?`e?/Xo?^toO>9՘ht .Ȧ> QްX%+#vkLfZN_<ؼ?}$ l]-?YRrkZ +J =7$׀n9Q ִt>ыjk7>}>mt)1|h;4yF|Zb#}Gd_?<.%2h<;7NcmY>Zvq᥋hlEkG|  6,rN7 ? s 30g`Ñuwis ?ެ- G"ȆOQUUoeʫq]DU7DWwP+ꂂtfl<R౭^6K̓6W¡za 9t*Z 7mpJ.cS,|tX%>%9؊'> 8s'z>:>vl䆺oD30g`9s (xtUdfMTőGS}$PTfnj;Tt<c6Z)m&|ho9<^:;&xD0 I O@pY艑-:㋜ Nˏ,<Ǝ|< vg3)lЙ?Yyx%7)~4O>] JZ9;㻖nrQfj}'='wVthu˖qm[vz&k9s 30g+̀O=YhΰbUAB7fjYEFQ<8Q;Rԕ|UeM'oݿ/r6{_c}5PL6ݟ/D`)Лۛ-~b\K7% f/f9M~r]R4sȩԝuy?5ĢLlxPk~WTyˮb=BoZdnej(tC)3_+$WW6 tG67ȑMn#trp;A.#d.AΩyA즅?|>6́ƾP_v%e3W~3?lD/v%ɉ>[6~b#o|'l;>p y<A#C^ɄΘ^ltԢ"C.Kj_ҧChlz#FVS >WX<>^F'>>ӭuL|'v2N @ klo9p'?{CAY< 30g`9_i(|5B-|oWei3[To'KA 㲀 Mgݛƺk۴e˦lq{xx:&:C8<ʯLK)h6u[2|XĶg_#/GTA[O<oi>+YEgSsT\e@-*)~>!/' )?z% b9nZ~Ә[6&P?ZKeodcb=>1x9Klܣ3]ƉU#/y;~4ph'~MLba_.hsΚ+CV'|tӇ-^v|dy>GF&:v| ._FUE]}lc-{}py-cdŸlLgt<%؆ѥMjy.:‹``EᤓN\m3&0 30g`9_I6zQ,/PX.iF]誮Xߥ/y[9u&*zCLEU\V,m|ʆ_rU. Х޸@+KR/|zQmU7>v/<,ib‡tIޔ;NZo_7o1{*z2S tZőϳI.|d E|H.ȝq?.th#s U[sB7vWc]~``77=8z9Rzez.Y_<򿚏b9g^"&汁O?9xrmuVzЧ>el^64uG6-9x@>Gp|&rA>hǃ?~TgKn2f/9c2ÓÁ#/xq'49ytYT/GS #dAO6A?>''yƕ /؊O^-?Kv#>GG$dd \}tc jǬxv޿Wȹ3g`9s 8ݕѬR-7,"=IQML=q)CWL)lV6Uiu° OP=VxǕy^26dm[g0=ެ{W'oܸ3WcWԾoRH<Yo]ŝ rcG/}cIaE:159v|[;_A.E..ɉh] ˭8 V..-\HB&(hnn͕a,GZ$<ٜ)W j.nvelL.:ۉ}v|}s {~V?od0SZZ?㺋>|O?\'|O<8O&:'>>Փ <=t?is "/Dž1gڻw^wuyKwl'~ǘn~n}%Y'?Iyƾ݋JNbr0>>`pq5O|ƓK G7f<8\"o?]|p|0_LvO ũ(9s 30g`@KU.yUD#/5Ob66 ؼY>޹+MZ)~=&aMv]K^t}y,]-q l9%V-k fh .Y+h= x9<ǟ cӉ'k{mæѧgI<}?<׎ҋ3KW Ol=7ێ'<;t9G# ^86'.:}]#6y>?~.\l oZ{Zͷl_qzՆl{* s 30g`CFU$WGoo@o H;*p.ePJQªpnձnYE" .JՆ=-`kAşB/v /ۏ\}߻hܶ_~6o]>w<6/uAJqMePG__xƣ?<_)>7^u8O;n}n+6_:1}}-hVs_>{NCV i˙'^K\`1&ɍ|X 5^BtwE7ZrKK?$46=׼Ƶo= g~Y#[R D'<~ o#F>k|K:7EGWre "=S{+60zS|+dZ 1^c`׍~{ Gl2?59c_.=/bFЧȑ>9Ë'/YkON$whv ~ %=>̧1 ?]dvc/'OAox/o]y3BL: {8᛻s 30g`8VTQgsm#b(&&*{[ oy=ZܹWkgݝ;MR) ǾnZ0i(F)ZGѶm>joyȸ7ʷu[FZλvcrDx.7n7_ssIa <'ڏ/Z< R޺sx]4z㻟pw\<;;ūbW/ Syr*f\0}'ʁK5Wyf޸yʶ^OoX+\||M£:ɍύՆ/xر~%un\vg'>j/u\+&>hœf s?@ެs O'y}1 V^&_+~@'gM { n8nt?]]vT2a9s 3p3P[UQlY]޸Uey #wv&~?,Wд~k&Zrhnp)R3v= w(Syor*~|RU8 ǿ6SEx!e }A8{e׌^}U`v+|_CJ|޼ysyW,y+oCbv(r7 |+mt.b+>E_u.`E[9=}ğ|@¼xˆ<sު/NgɝC9*YsY-i9t862m`~4lё6 y ?rzI\Oxi!=!:pkgwl:zs_x6qΫ'O/Ld;طnwf^-g=Nx#ƶW'ǟ<ƦOݿvG;r睥fyK;68N^X*˟#{q֘M/c9y=z;O3{l:C 聣/hpS:qC}|k]"6>Ne@\x+iO$s9s 30g`Ȁ pYUw2ZlxkAS(`&~\"F!H²媵VFoB< ~?Jn~nGˆK3NfM\ g=}wҫ/SXTm oQ˿r?8n~RPz}I늖eϛ*3+o}-Wx644p\޵ ԏel?lb(քx:vu+Л~RAQjžyw6>#:[x6e9#/uĒv'NS/^vuy7>^ogo߼>elܳ\ܽqqF`Z֟K~cn]6l=f<8iOwGlj㣯?Î##;a\q#. sg=^3>VO! /_jN;uǖ`]'e턇_8ZSpqv_1?_T1,Yp.2hsy&ӃşupA?$q%e K_ni+`<_>.69z츠6Wş[.[_oܟ1~͗oqC92eoۯr5.2=-v!G_3?+L_nܯ6an+330g`9sGmU0H۬)RdWeuU\T@UfhYVSe#M!flso[lh0ʧ!=%om,{RcxMnPxT[w4~l}ƝUk/^ĤH K rm/itN]lMC*47Vn;z{Ȗ.~KΚyOx vwq5ֆW`lٴw=kl\ty2'zbOao3D g 3p%8-D>L\ZGtgLdcNqo 1y_pc8.?xOwΟmuG߆L o~#l9~yZ5=twm|+~<x/ۖ}zM?}g:̝o=ο ?@|ǟ;v\-W}}~z}!9!Gɇ-tO5\X_~7#~/j:]xH|ݫ~^F)q}N?Ǟ;ow mg=p%O.G}:~찕<_ﰦQ?kTȳ?]<&OóKF?xc>8<1= WȢ6*GU|!\7.9W/Iwnπ5WileQULUK&FSFOdl#췳E1;ަf^LecNnC,#h-Omom*\\!Vʵ%|37u{Vɾuӷܱdu ?ŝnyp [ǏՋm{_gc?ҹ^Wu\ 6b8z/W奈lz/gv<>mE?9Y8̔>?pA5tE?`},G[nƬ9ǟO2?:ȦŦlPl@<}St&.4(r kNLcIp6j/3Φh{9>]QxmspuqSڀtJ52M,ͧ>g_;ouֽ{Gc7v] Ń(k3oqs}=TzC^&_\UO?R847p8Q/^oq~.\4oF=4N*]'օzɿ?z#xTO r{=%5H, bFf,r{nJ.>' yǿvFGx3_s| IM:^@6AL:9{--5#)86{&o?>(IUyU>q O|ػެoq-&!I'I%YK>K/B`"g3PHbN'dȲ1Ӂh"O41umc #;-zpdJ/[tK9t_87֏e1vMcO=3/Ud,Np/G >4 'jZ_W]|I vwE>C|xsqޮA"w^Z1o]_g96 l~ |qΉO5_aZ6H ؅?u1#~y''sf{ܢ-Ϋ˹ S|Ejb<];~(:z1޽k3qQ䣏q|'Sܺ_+ݷ|gĴTOd66}3N8;G OX3V}&&U{#^~F[}.6Opɾ#y#ƚ[;lەyvVf`ӦEsgދnOlnmos_G>|Y8aQo=,;w?c?0^w=suVK$œ[W<'.Z^Z'Ё]1[\e'wq k7йZ_9Ӈ >yDKΦ2B11Hۘoɓ!7k7zbth8؋8u> xkBΛnߔonl׭\GD~;.I.o|^d?wKLbW5O<}Uu~i|||t]\5>̓s;sdO~kcW~GEw[guz[w]ϵO|j=qָO󄾘'_sdںt#.w|Ϫ3bΜinCGbM觓-Zpi顓ZO^'?|vlg -CO|/mksЂ_䟼S勰4iY _ !w:~?|kdXZv/Dl&ap&[}p&K^dK95i9ҋG tt@ d1Wv錝ї#|tLʇ|"iK$p_Hbopqع޾ooxxFkG]RW];oOB#X.0wrvwDFۮ~cӆ܇?{kggCd{"X,Cqz.q{R.,C SkSU|>O8zByA݋ͯ Ώ]ʷN>_}y >`q_|| WW,z|YĞ"E\>6: 6~0^/."ȵ ,r_:]x^Sr&~.>kv%pW~+6~/O9q=A<-mRWy!_խyw^}gLz}Qrp6|exlm4>ۥOgV\t}|w߳_@rhc|ō뎺>ehgFc\skl^w'ؐ KJ"8po/uLrQc-@Ts}Q U@z([۹*Ao o!碑7daJvQ'eɏ9lҒ|S <ZlvZ?uy@hђs42#D}Ab,d'y:Z_-)lsaFvqE-zk1}\nays˙g;8 nنuKN7M% F{og߿/"?=ڈ'vz䒯p\Ё5:,}?U7sAYO9.0mlL}kZn;>}e `G:i(L0#GBh B$9I8\s& CZ}k~>pu>y|p6?჋х's?Kj/?/mo |k.\^ck:єud'Z;sO=x]o|m[?]^o6wOsO{g?\;uP_ #7n5ێu'3G oTmDkk]%n8fCͮMq?|ӉE;\ct!1}.n},I?@LUi=Н,fsn=&o<{o)+՞<ؙ9ύ/~Eq1=x|s3|ar6cYUXuo__ 唓N)=G=e޹y|  &ahb+W{7zz+ApsBT$Cmǻj$\^'ʠxȀok x|fO/Jǜu> `# w]}Ok }悷sP}@BH93~}b sG1g4c q8&Ӣk\Ȋ?_7j06}=Zx[`rk؏NтHyyǷrk?u2Wz< GMZқc>:7zl y4bԙ=ʕF}~E~I6?Uy]\ˁ]\D+hCl:ۙ{xF?2×$#/ܹg˷\bl~G|QKVl|໸%-ٙ殏v뛂`y_L[s1w0'vCu}ڸΆEﳁn;ֽ"lun.{p1"z4Yf̖́x!;m,16Ma O\tb69 L^ 'E<-^y[N*\N<'':􉑭9ǿv!}c`˷6rрٙ@IDAT=ɭ'3 W/yr /?uq6(sO+/6|+&_IsRo΋13T~Eh; cq4H4zo o=烶ctnT*_޹˯|誶 80˗eKY^'-_-ڶo5|_o7"N9Mq߽'(a%'nb8sz.nGhy㢒5?z&>d ؓm;,}Wƅ-} 'r[},[5{̪[Gv%gb_7|vm'ox:vbTG]vmPvi+)sT&7~Fε C]| r= O&drcW\$Y'V:鉟xvҜs&'d}=ş1"H_S{Zn:W<$XN8QcםNNd]qd[n'xbw)_J+kU5m^ vt@|g:Kd':^qA^N?z'^^_Oނǧofl 9l?~G/[A;N Sq쫟x^w4N+T}pX:8-pDO&A'YG<,^m|ѿƳ3 mL|_챥~ iG/*S 6[ ~9Hv/ȨBF?YNWW_7}OM# l^7ӥ9@Kݝ{}XorǾ|\͗=]s\m+h.%|ur Yr11',2_ֳ@g]sφ76tg'ol̺Iv6d%s<[lW/xٟ EȰ:|;g T' 'YYcnR8'E|\x5|9>mܼ'|ΛɳK^nscE.ɣY,k|66rP_O kx܉&Hb ^n䘍rB7n\խcѽ=OMݛ-ކh{vzܒ/dAGw4='-x/65|3Fq| ox"\_ew7yH.Q%O;*3g;/#>FFv紜4O!B=8zv^q?K|Ȩ _{n;~-G_se?U_/%v<+rZ}y|~r{3&kptz}u@/9??Ǐ#9|:7ˑ3Ŕ\ \OGM+/"d=x+iw`YN1Y]'9 uO^N8/F6ױzlǂ/i .㑚 p8/ЫϦZ!dϏz]l^|;d8M󅎇轒r"'Oȇ|qXp>/{V蓼HT)0}7:))x Df@b dGN/z?N飁ĠMdOܱW>]VG'cdƒlwXOk޷ݯjzc "W?E$|M$FtWm1f£x)?rt:/d@r;sw1w?C{Qg>?ݷߐbU[œM9fZֹ틇%;xdO{۔̀P[s1}p3 ?I1Bډ66ؚj>xr'ԛBs" =|ݳf ^x/t|wy|n훷;qa-u`o_s)&1zmuzƉc8ck^dߴGMf݉6l|.R6^4o7 x]52x6hi]ھ#wr~X@ >3T<{h Kwڝˏ|v /^c/6bDunq{OoncNș8{ mt:5F˔~7:&ʎ c\S.6rsy=Y2OŮoy'E7^=8| < Om~e7{:٦sX LwtMڎ~1hxok/_Tq. c+3Ƨlf,!ɝ\758ء?|#z39xLjN W|f][.zy7lt~J}粿0}ÝSe[s&Ӌ捇]~| H,b6~vWWKn~ 3wzŇrM.z%|Vŗ~b["'b!O5@S #Zw q\~;HO&W Ob\zF։Łx96 fn_T:n渵grL͚t|޹097\>Ug"_ ~y1lJ~.~wZM]~Hɶ5`|9s\psed9y A㼽`Lz.F&ž#{b.9Wo{ٱ f͓}(/[s sw~"Oawslߌ\{>`3isg΢&gb >q>g._<ӗx+3{>6ޯ{'w}]st~rF}}˾Qo#bVZ:[WoS+)o j!玟j+zBqʍƶyV^:/yj>KOt#O.m=cZ'x1WȊ]>s_٦>I8{/=O}>{lj[Շ'mCt _4_*>ɹs_˳l'}KsEeovh}i9/zɖ-xN{OL=>m9o_Qpn|f`|OoYMO)K. <ߴgqF}x52l^_[\K/g<)Gr},ZNXXromR;iAgP:ƹ8ą"2 Ё7h+dѣ?=N_^rE ^x#^?{h6YH]ҟ$tć#>ʟ]|G_{]óqJ~qg=ͷݲP 'O|MĈ3wl~ ˋU˹gc<8`.t?[f c*`y =?۸14}6MwݕMu[t cI{lD8rqmtg8{5[61̦ydfco`w.hsC% zn4_?=kMro΅%+w/c7˵].jrw,O/{P6OcPc&'oXЫV C멚rLOM?&ȜG??]K'v 'ׂj3ѡ1>mDϵv^o=n8 y{uB/tu^wϑAFm~GW=xKS|7J "M+| 36/y/,'+}?^Wq'u|̈́n] \_˱5G@SAl8k<,~_xy//7]xӟ\N; :|p}|~M_moGcJc˙O}7n^߾f29ݱm∮ă|^|Q>3$gRkw-&;kvbƃ?rf|Ξpd>q?QP,}xsޗ,g|.d9_w\L";0$!tIτ@rpjedj< Mo#68pkp& |h ` v|g+:b?rj2bMiG6t5>@r/}bFp)-yAHnT85Kp^-o_-ݙ;swysfusyO|Gwť_ѸyB>7l6Lp`szr\&zxmfxCɷfeЅGw| k6O.[Y WZY ﬣG1~$nx.6/$=&^)WQﱫ rm]L笣ΗIYGn~H e;cɯ5wm-oz+k@7 ~ހ;>zn[nWm_/a~ry/;?a-<ğA{٥zr㟸<_p˞K/Y>K^zo]7A˾rAOGS|M5?[v_ѾYxUwnOFn?y̹pw}3v^^|MdKxA&g.' E}[46 βBG|^ zW+F}4jvۿ?kl xstw,{W87&&4޴K[-Y8mAg! |tx$M d# zd`'=|KɅB98| _/^7yա%5: ď/VrxXZ8iǞxc׶Nݞͥw~`;:?˃zgz!/|1pIǜ_<~F.ܕgc`mn-IMQ^wIF׃:̊6cfؔZlgcq. y|hk=k[,E7i-Vmmx̫؞M({bv;qnnts\Fw.#WanJKltq{|06 ~y MLʴ隋WƧtmux3ƞ1f'.^՜zO[ou⪫t2\Xő伓^zaEg1\Hmu^M!ͅd-_sƫ»W&{GO+DG˾q\]x;cM[N(>9 +}T Y~!՗}'>/;W%p|ڳgr5,'tne^MñiH;GbOAƓB-z3&Ov ?\lOx/ȳAx⛚Չp@G/ -yO>ut^ƿ?Y~:F6ՀڂOt*|_xe&W~lsć=~>k O!6=G,:hN«7Ztƒ\Ǧ&_݀d}tu> ?ѱYp^s9_@pŕW-?[ucœrur^]+_?'pRpw|FL'I %FAIRtIV3PvbX_> Ov?^v n9`^3boW&/y}A7>p?^~lF >lrx_x9|\ˢE\c?\i_۶[N??~g;6'XE3ww{:KOpmd> MWݳ;Ü8/[\X뎇 aⲱ- FEWN,xuon8[A/ʣN瘶ZS/kHi0Ǭj66^ǯȍl|]LhA\s!>y+&숑0yrL41ݬ̸ıHlxse>^B׌ē;;.DDsw}C}ld- ߑ5|'9&X3G$(^zqb{ɴ\ں QeH؈&zf r|xuzʞ 5tLg۾.Ã싏{M^7l}xlrmjվar7ۥ)>XMvo6n"e[*vtXεGO7앏{j|%伞/.c&_SPoE}ѷq?{u|x G7[bksx"c^rw4gי!V\s-ڇЕk-aFg?vl:s|Nl%k_6Z|Z+yY#>Վn<􈇯5>'u|1o| >6*]&'loJ8%k~rG2v莬, =^ #dЋ֝x}򓜸b?tGv*R=ÙpgΡ\}$@Kߎm?6&Z,j[1N`Ծ861͙븧Kџ/óF V?kelfMs%g"7'</)ShoKg3\X?kʹ'98m+{YG'|<; $3c'6&g޶35L>歈WFۧʗ"f ? ڱ3!p.^oGTf&6ߜ/7._]:3/^k:T7Nl8m2=ͧ-vIyP[-C6=oJ_|YX<΃x1>'qA No1efw7ڷ;?_G_ 'AM~&r]GnԿ+zJolɕmkc?/z_ %}˗C㥧.O! x>P1zEO8]GB{}#q.; 2w*Y3g>r V ڝ \5HO;8>G']l Ggb 퐹[Hћљ~luhCDFm486 =wNBwzQ'N䫏ոOlDN3>lc|8dxmJO@?q&e?>k[pEx:\G|S_E'Zы'sW>: @c)k^?:z>Oxs?p?N=tk_އ&S-}4OzVMLAIRs2!$?$>Y5HRԡdz}\LtD7mቾGȶ>[j@y2ڙHt񱥠?my_rf=k;EY r[k1"qȯ~|♛ |2\s>) w>*߉cT79knM 7`s77|+?|GtF\u ?O9ʑM]g@69 '' rtZ}:_b @m To=J@w?.?M\ψ'-s ;W@eZ1P@63B㣳y75B۷A-9%v Γzs4=[vYjceN-㸷bW߬>{ ;^7]F/v(k[uǍxfkyl&V^=B'򛼄74vH^r \rE<M\|%/<kx;GzصA%"-?H -zȬ.KbSH>ء+Q'fmO>'c/qyN>?8| qcWـOPy2S 2&xK^41?j86 `Y>6O/ ut/:Ͽ86ܘ%G4E|.2 Cdj_љuE^ iBMnE!8D_j4H /)WK|xȫ%Fi$}:ȯ}ƛBBO \2LM&g:LH8,l h = v+eGc/:ȯq t~%@t$t(x)OpxdoSˈ/z#G?n_?9߰e6xm,ήq־Mh9!&0EWf?J1qr6R5s{;:7NۜsէoU]Tx[ksȑC ?n'Flk[_cφ|6FN1Sno-ܚ͸?Yf_i'E謉ߒ?ūRtW\EX_;x߶WO#OſK:cG<ٿlv/g@x>p@Gfm .ʁ1Q$?6~|Ht7/pO,Xn;qjA:4@|A]MdvȲM>y.ulF8ȣ/~ ĔBēB?]ҭcgKCxpn@^2>Zԇۇ#cK~2̑ g?S,r_ɱ~|= =BUk>7y}xx[޵N0`\ASMj$%jԙpgkmLoa < }]pk~  v,Wr|hY|DԷ'mDv]|]vne2s2W?ysפw,l= 5rx6v9>lXf] QM @9WXK&~c8d#bu\;w6svEG7/>Mrsn^J7 g }\IӦpy UË?;iX7K!kt&Boޒ㮪ss~rdD}Bѳ/O7{Sb#T:r^z\+og.|+3ʏXnS ~&KG$?_=1 hxK7}d7Q)Szd7ܼIdl}uZt|W[| ט렞zЦW /Z%釗‡< M["9} ?pI^kG6Bpt,^yC'YE]8t[ ˸Ymѝ_&V5};}rp|jPNj/~M_2_x@|EdTI5k]6NbN,xdn<^:Z>b'^<>vE}x,m7Qv8lmsC[/|ҹłs2\I! x-\:EWfO`ҧxX2c^xB,G zmA=}zcx>]tp]m6е# =XnV;ua9xlq y1 Z㍛=tˑtdz9̺[BE7Kr2f/dOV_⦀CJsU<6|qPp r|1c] \Cd6d#Gwbw#oG b῱!>˗~Ο@ӏMsynj9NG5} :h|~@/C -=k4v6/q)5~mLJ$uLW{`i(|Kͯ䍮Mn W :!".kMȬm';ph |x~6Hn#囶B?3ōM:ۉme_<@؈.?Z'g1> ``K Ic8X<@볫dr%t'Eo&jd >x%}v.y2&v:|D?k=lCOIKp?>K_=hG.F~+cj2o| ŧv3כCMZ /%82>|:"'Fo;sw?sH[sԼ6h)>;Sج9^EMZcΜ7_+T9ֺfVU:G ,ta47fS/pkyy5}Γnk '2ⲁeRѴ;Ϻɦ5q<+XuW{K|6_Ɵa1wzs{S\:mWa'lԙWj28d^#]||ϸ2vxb,C\ ^Zñ}L{k=_C1U@#%< ?ɡ/9PG/:5WܥKMZl37vNZ=ExM\h㱱|7m_29kIn.Ʀ 0z{]E"y=x|G_KGHœcܴ} MFdZbOAN\=>{`s|fۗI:[Yq:bBo  FY1c}㏱bO޶~({ `c͇s{c8H~ RWSnq橍(_#/=qVK^osjc@SQ@&UWSh8߈/'SKr.4_K>9j ٦x}q^dۇs͒ yl p.U| ,luW.IAO`̧bwP ƖRj#40|4:|xECN^(}x-X-1_~m *n\r1u='NJ":[u5uVpDEW]dk4isG_r.y|<Eo|O.}5?3wɳּy؏d];5v>IB?Z]cñnݘ䡏|Sh-4]gCV+Kx'xsrj|%O ZBdşkKob >|ϯ$wE_r1a {(pj}Lh [c뜫H v|KdKDjdWLd DcC?J"6. zldУ#vM8ѣO;H¯-lS؍~)NxӓMg>tAɁtpáM#+7?G .ˉ8էn?]?w~o s;nN٠_{b/CWȏGJm~M|ٞsp7c^S lw9a~5}&MOm~Kl9G!dwOۤkh{3]xAЯ/Hl-X 6JoV }h[5P8-yQ;9aO䌾%'ՙOO1˥xmԟUW'WtsN2)&/rBwl^bآ+qk'7>1;b~ /g_xf?}h5ۉW?j}]Onyw_l@XW^4r /|xFۑW+%6YÛ\gPtPL&=&Kxd`h@_ɄƧ$L4;d}x&&]i1}mqiBEzm?/|pC_K쉃\:rhb>~ ``iLB;qk q"n8^O6ֱ<:mwǷ-YDz9P7\XƎMlLJgXO]l͛Y֤np1zO'V]n09Mr)}g'kcu6?Q|'Wg1y }K6'>rplecrA Gޣ YGRեs} {X _]4πmϼ 2֝҇g ²淛}s Sm>ڪ&s\AF#&V8CdV#[3$;KF;#慜BmG\s#y>)Wdrk^T$/7,Hأw0-HsɩbK)рqxk?“لG?~2yя/z݆co |&Y;4rIbkGĵ~ .t%m~i+ >_Еуhr3)SXM/>r^vGB}ف7>2W+Ƃ|[t~9e_m>rS'ɓ>|>dǯ_𑣏a#zқ[E'<Y8KdGWă.؂K?z|+[7A?@ &o{-8Isq8IN_^Lt%6|:bGĩ-B+ LgȐWM7hp2?~D>9 NdJ:H3򱙃 ->x/8tv'-Ču-X:|_#FOdw9a$r3wƛWVʕ|l6kum6q6}} Aߜ|ףc ''fcj1|3fsį{uj'2ş-VŇښ@?YsbwcA||r- q䫪 }# 9/{ nDk4LNg!.LI~X;ck҅IϜۘvշ- ycz;Cշaدk{zLu]c\e3y|Hś5fB}F]o|ĚNMwZGmt+99%>jxc2N͉IF|" >n6Dd |9~h|r B9'5B4-- 4Yg;|W>xkc%.&'Ft | >8'G.2K_<4~Uoel Ƕǿ>CGu| ?BOd?#d>cJV:_2}:= y<>?؂W: ~I :P[ ؠG'PWzv57:d>7c+yO,?h C燲u.I>g[у1t5Jkh &myz9/@`J`&_eDod@ǯD>rhiG>r)tg>I]}<ZZ8z (Od\7A<r7Sg3rdS;jiX&Կ_O̟ɺFs&{;swa8dݙ5Y_<=DYm4>H?昛El&g ]sPqc u۹ňq o_O߅u&tudѺYg3_xS1]ccxk[bnk9[Hc<_8}l )ɗ$OrXs2'g~'|=>@C&F8ifRz57 0~pa/\ohJ]9cE4٦탵2\}nl@qæ0>rh Z>@f3x4.Lا+:4^:yХO$I>)yąG-vŭ V| NX[tQ3g^>_MM舟"PA7y ]}vO+­/]mO Ga7:Gv| .5; \|olAlWeCW<íu#D]gl~[jLvטjg? ] e @`$XI$%ld@=>@7, M\Cc'OGb_Ҧ/~#7/?EO~{ =xcL/Nn6ܺ3y6Nd81}L>'m$|;sw%}|{O?96 ۹8f79'UO=a66/k<] X$e[7ǫMb_ o<ڈ]+A;vm68|uggW4!φj'7R>}F/S˷1G ZMƮugtL> 84pQҊ`6*c8tw1ޤullёuM{b{≍?u6\WmR峟 orox?_t}4z=gjr䁚 y+C&Z]6>wK hx,-<'٤mX=t|| /a_ `+E2jK7`.CrK@Fl<<xYgm74:O>i'݇\I+N=IA+/Q6 Or3}k@&/Zdֺ$/ω>:y>@&=Y˫/-?[~\rئ+?~Bѓu@Lb豅zfg)ci#@ǟ?,O8{|}ocy(>{]~lm=e67ꨪqؚ,k|Z7k(ΦЅs֋Iߘ gʭC¦/s\3|}F5]]26ܼ9g.bgS?ͳn<뻓>8ȭ~?kablM.GQ(KSÜƗW@.韛1C͹#k2_`|%;>t?yhCG_bn6')Xp7 26 |S+E;tؐ❜bcXWZ#4k6W%YnLm7I'ѽ鯵}cM/Yڹ}4~֟>tCqg>E_>G>W,žrUm`r.:j<ͮ3wފWכhCult|yy7ܠy;Ȇ@Dc1o8c# ӵ'růiG8uP8'5w+N:AkG _S'v?rm6\:Wb'hli:ZwG~Mڱ%fr{^bo\GɣK~A'pr =ރ?@66xɰG>:n|3ll$VbN>m N}5#{Ex^c\Н8e}1:W% =،~~)yO DȀq> 0]p4F >=kx&>CM}Μ/яb ]MM695Mrh?O&YL|;\iW9XP'W>9x=pI6[!nbɜMy9#{G@6tΆ pF~Zt}y?k- k¿c6s4>Iű=MyIvë8) Sm<\4$:y2YKgX[48[`GԳo{__-7ypkxt}].&kcj4tJYklz9>\,&6,|~_>7d6y= tMg-MO?urcwK$jv\ K[<6nJtE^绛/]}7Sxo_dXtߠ16r]BgvrG/=1Oky| :dE  zCW$_xG;rNQ@Wjᗚ>2O}5]@s<dG|X+tn/ZbG[tEGkx‹ZA"CY Ǒ=zz- @ 0xACV\K&CK]1pѣNqȇ/vLĉ&vpGdSxNow[ndѓ ɣ;鷑]li>%x6+>7}6lu-[6 /&_Ŭѓ!Ibқ'vʇ ?yIBF>wgxw7kizZ2W@ֹчfu U{SbMiL~$ek%&9Oe킒vi?2ޔVm]ںf[ 6yrB\`NzzþوґM|^$:Ƿ gU7Q6:gUgqb㐣:?;'o3Wx1`,k:Z^tϻjpp#D-:/\+Ԋt,2ث4K돭%}dl|сF=>Q\@ KԎrh§?b^72&tj_ӟ ~ l wmJNإpڂʱ% $% DR|i0ѠG/I=ml ^]" :Cf "m6)xĦhs6?!țOP{>+ y| q)n6w~ym! }ۆ|i5s'-ڣyb/wY(.ƹzjpUP_"ʒM1tb,Lإke{3ouWxlp},,>:d}/3X6[NڵrG*)ѓ&,Ku6+%bH'eVTd^B/N}=>ja6;cW q鱮vwq"{}xg9m-=%p 2ؖ>rF|h\ށg"?hd˹lԿs_lħć!ۍ\D]t&thGRwW.%΅fxmrp>OObjGxɃ@NM/]b[!OnNĀ xb~FɅ#3xrRGCN|6гiyJB{7.vFm<[ӿdЙ3z QzNJYuxKeh4`iG"Nx6]K>P#o:)Ѯ]8C Ty#: lq猋=XNn7WS?}CtiӷNǎ[0s߱L$)ھ],kw~?|%˸I n6ИLѩƀ @>쮧O]l"+/ob]~&Jj1/8 .1Sʽ=w}\{k3;k|o}z[=YS=ՉXj1G8eVʍkǸƎ{#O;f|wLLrjum䮜X ~f`>͖[V;>W]w,:7܉ʚ \ZG>>Eg_u;^m]sL=tX噫>[s/Eb '乘md..H_c4us/8oza㔳Ok{,j`vuS /lU|Wg\w;qy|4^4]u5'[U ΩXrSD oSb^s4" c>~z+<џ;p%ѿAvAC MbgN?1֎Hg'#zhdG=bxo,b'p4ibi|1:ifG.p _ Ay8hTAI—GߒG}اfAOCESoq}^ ֏ijNO_ -=n9^q{WfZswA}\Mk䳯WqnZA=n-d2vrywqn3ׁ>>u{̶߾=ǟ{rvͶg[殿+_}8ciw?lCmqu}<66}aZwyxs廸pߏ}q8q''n }2u9tlZ?8i ~sLQsyGO}>z>+xLq;qnm,ϯG_|Ыٿfϛ\wZqvü:^k;ɽhU^pg]iﱾy>vAy83 Y^wW/wxR|׻ޝmmٳ?|dϓͿ&-qzC#'ʎ̍s{k~ޯlx2nv}o_:k㩕k'uR˘ UOGz˼/Xmu;?|<cǭv/?~ۢ.@.7~5c}kSt>VX$s?cn߲'"Ӟ{"8=se8.oUr:gFc֪Y-D4'u}*l[~J^/k<|3U?0e.<;u_]@Q[W' T;|Gj#鯏nùo g^Sxi'M:/(=OW 'kj{ʠ#Pm6_Hyڱ& r|t@F]N$GG.xybǬxy-A & K6vg-:C:wPyxm1$Ρ&lN% m-Ӆxٖ2…!YbYK_lЎ]ti.%+fMK] [dD6[/kG:}Jv6ut|h嬶֧u}log_@L%2_|O6~> /M3Yn^@z^dK3v;ۮ\\'nS @Y\`psqѡxIҡ3 |ˠYǗ!{ۭIs>vsնÛ?t=;,}QJz;?j#>9Nqwo{q͝=imCܫ^/;?=}-6 >oؿY4~6pelhҖpг\rDL4rLYrc;\ჁtfV kdӺZn&q{T}lR ߿{O_}Iߩ6v{z<[OHL5b.wixei+=Q}O1z|Ǜq?W ^8n_5A#VVN^/_-xb|֓ gk^a[>r_2/S..rr eom>g&X}N-8d,W]oN|Ƨy}~s|>GWX׾{߹s`۪Wx^ߙ7?غ]r[=ιQ"7'^s֢m18ccʉGAw~Z-Z6o{ڏ}u!h:"\,g߅/;_ WY';_WQ㺻=g֜Xx3krއ/w:np~ ̺p-6z79>{{Azo:Q7=j {;b% 0y <>ʭn?C.<[us K{1(+@eAzAmlгv<' g.mXx)Es|E!k1 jⶵj+oS]ok_~91wi+.zmZzcJbܿW?ev<1TwQ/omw;s>Ye/h[K'qğ]3~}q𑅧( ~-08~dF7>z9J9zg>#W_Њ z7V4J}/m?B:}lXA/|&>菜GPEv% 6>wGDco^'?<Զǣ/ܫƋ>q=n1nw[}}BgT6'uxM]y^Xs5X^XO+ǝyz,}ʉjs\Txz1>ƉgXx<z2ӎMHxH-ᖣ|r٢^qzgqWدȟן9^+ߨIg2>8΍ujՏ-n:\ؚ}C9x8#)]gwwZ3ut,\=n>xI<*_?qkʸӵnW̻y} ;g~|8ڼ.XwSoW rJ |a/[?Պ"Ǐ(Wj\W;yXyck`j  *+ڙ;bORTIWbe^LV:1xc mCKȱ+?,1`>e N zIԕ6jgDYM:zv~%ї6Zm@ Fzt>؄沂c*͵ڂoz48PF'`dh \Q>  p_A ү&n#ɲyg7}p{U822ʗ}'Ur|/! ~oGv@skܾ'O/:l䂗}7w۟Ə'3Ѳ(wt_GԎȡ/(CI,A>"'p9q󘻉q{t>|hG!{e{u̜x ܶܚ er:dڏh.NʅkA޴ $j}cO^&h]ozk?c#6׎"藮vΝ*++yC O^-973ǺK1OWX|~MPdo_o_C2[t}wޣHzjw5^ٿǗ;ΧQρ."mgŞy{=ݔ .$|^k=M/{mM%WE󯩼ׯ?wu.0UrQ>mMら'v7IJ ا6T={EЎoyj+H'QsЬvM5~LyN.~~NMɗ3_㸊 myVMA#Kɝ`#lk橾 :- 3r\G|윫k5EcN,hµ,bL.)V]vt2bvӲ)z'o8jun xB^@OtVKYtr7+)㏺w 1+bNX%Jr:Vʦe'm6<=^%K=6 6@х]秶8,'GG~d#[;s.dķ+z| cx o.S=I+x6;(:#[ olckM[h 6: >Wȍ8Kɏ m $xFWhf+b x4ˍ6S^ue?'/IsEm4>y`p$ziFqKC9oɩ`r6,`C&td$ڂ7!l <_W'qvG::VrڻO|wv3p ;_w#Wnӻsz6ҩ~ > ̏S8%єo.Ed А Im& m2#1~xI?|vVYCKr`mv{/1_G4?dq.kwVEK殏y7(H2<1ֻdϫ2.ӎyUV~^s={^I{X-|7:5W]v[,Ŝ|,ei,?ɺX;>qgkA?=%H!'~{ruwݛfG77NcжS#9CJ缠ல1_4<.٥=?V#E ?N [T~n m|3ɑN6/w-L=-rgwB97f}8q!J;2qڷ9O?d?o}kadq;rʓKk^ǜl48^iy|=vkݾba1۷S5`Ȣ]c.w>\DaDz-"hw']bX;Nrcs__OIJ[۪4ka/4MDԱ,9.0%P߲U(}Us7 זWNK{u|> 6~jr‰3|%/YR-<[mԎ u@^6v|ox#egƄo-?^:D-J~2bɦ" =Z6zvDzǯ ͑ o|聧9p6td/v ed^e#;Oy|3zWPz= ^BG+̃K˨TU&y'\sOnyhȏMrukΥ֒bcW]W+?&v1TbN?" ; 0^IC3^;cow6/ytggkO짇 ~hR߿+v؜Xz_nc(5 'g757I'1C>=i~vwKDVϵ,K>:l-xsB5ja{Gԉ_劮?U[/99:~r7YEkgmN,Ɂl;GGoQw.,ȼV}O5d^4~{_in3-|=fWk{q[=EK{GH_q;?eQ7't՚OauiY~Iw}~meo$k͘19+nN7i;Z4WU\owﺀCsىyi Ӯʄc\1V+/uO]'8.XR?t92y90@wsL o6 QKuOm.ڻYT;>cFx""aEb"yYF?^42qd)r9n$ge ~~ G9Ot&p€ Ub'^D6/[pB}*E.˪E1)^k .҈;\ :%ǦCv㾢8#9p?-w/Kod)mChtZtǛն ]S_;cvhAW>zCG:%/6Gv$__QVk;f%Wĉld% S׷l@7o|uŪ}t;_c6Ɋ>-Kx$\6&v ^ݑMOh-mVN4ISalMo-W25gȃ|!<;#$dzg&tŅ}t"(9+S-]@X9sySy><}A]~Ÿ?:m|.»i7y-x>oqo1n-ꃈ{]XrA}z;b?>}e춏;8댋3+sZ*9/Fu_PǭE'GjEgQ3.s~'MFc%_ntţBOp٢ns;h%c2|j_|([0} w%uѷcvǧ :bW<.xtmU Oghw m}ůo%uؕ-ʦ 6h󆶧7AV9_kOƋM +[_J^Tݹili6ɋȃSWOlB <|@iY%oФ_sDZ.%s7د%7G)ɉ S}dac.wvK&E M\ѫ$3cZ4@? <_=%hbKܗmC /Sgl%CEO] -rX<8|?;R'e vxo/ysK ф~x@3q\ x-'ڂ߻ke)>A+9cK҇S P"K]2A.HK.h R8N >X?[xr1X1(ӗ;'x';ql@y[\4wn#zv̏K,7F}r}=}uaắn.%جW(6 m]1͸їܽʶWc#=}` ]6Hd!-w?eykp=3[-6ёh܍rwsncZQrƼ^q063&bsiF8/G?Q,83j)0~m\r.;M{]x՗^r`<9A3ǬMXɠ3OعK9 {rl '&0糋V roq7;p-q>r wS`v'._28d+01I1Ck^uptn#oیqtOVcھi ?b88`Rk|ܘEߦc I{G-V;NE/ ][t NbZ'u2 \s'PμvycK-\qf=t~;޾+m/E˨|U\d /ގ}! yFA/ T 2bZ*_EktէT|uJCV3dS#gcA/ l~F+d,pmbEF]]|#_e+Ah׶_ۦO?ІK|lE'K~|Kti':Uߑ;OJ .Gn3b2Ɏ^mJZdK.)s)':b)ā؅>/4_ζȠtK&>Ez1Z>W?vE.<: (mxZe| /,4юїhfl'R6$X mfeFi|& mF ~gc ^;}oiy( t$Nbf0ٝņ7Xx;1[y5&?5K.Yؑ?:ir¸iĢضl~<Ⱥ8vH;y;>I<$NhO] Yd' L&6md.~@Ƃ~t~^rǝ~B/Qdu'E5NSg$%|RO]qV3>jn-'c]sW:?2F{_]}ێϭC6;l`7)O%7/lx 3ڝGl&(˼S<G#m, hW|M|xddl->yݻ{skoo"/q:?ChzZdu1e!UOu~#ͧ?x\oܴgQ:3s^Mx3N_¸o<@}oۍusW r/6y>OmP}|˵~u<&v]zU|~Rn{j-2?~;wΧ/f< ݉ȢS>;:ǽck0۟/7ĵ\/'s:t{V|~zjcE96| GX. wдe#:tp}\V2--&u_q'݁\{эGѶOe'GǠd:X:fUo \-9.\H` l~hɳmX1fd'숿YZVz.b2_F/]"?ߦ.ë[F>'#خ$$&/:#M|ܔoh:Jv|mK<'bKx;.vITO>V #vEvӷԩO~~$MpBǟNe})>㏟B?z:e?9d[:_-^^G G~%KlgX3㠮x%mS\Ʀ;~?߁-~ʷwE1P_H,XNy"CGhn6ʠ# >: ~uK M{7: I!7+A45ܻtGN }#ޯpHQ7y`N|+s'YC1>a/1CMp&T:/^ؑ^O<ؘqVGkm:^uuFC%bbzO⓾rםy[??G}fyg?=߯qc͇:MtߣQB>X;' ~o-wl4Ć1a[~Ư[lWE9?n :v٦͞ymhm=5Pvc:Oz̋1ׯ lWQEN ͩx s䅟Y= 춘:[#^w-.S|2ʼk玮́[oubӼ]i'sp+wO^a ,ѐmsK<0s󅳸ovǯ=ևcQ¥cG5Ojw{1^·oϧf7t#-뾣!vp@ "b[ MGmJ |Wm碟6AX(n6 .{n9@=1,\d2ѭ1WĵUGR6ݒݹ[%y`9F9dg_c.%wW`}6u݉hv|t*BKO Ol[C^4d8OЍo x(cɈ>"S_}dfsCN,%ȱd<茍؄ pR||"'r.gckz0_EDvzhmkZxҕ#i/mi?ß 7 ʷ>tQvl ~Xƃ/!$ݒ-9Jrrg[ G<ѩҿݪhzoӝ/զgD~U:9bO">sԱy-us<:9gX4v$ؑ⌞81g&:}K}[h#>%~}ms ͸gkl`c`CMJV坻N֔OGr'}we|aAi-rx[/{lۚ7׏ǎ<9u_X殟m{دеWw]( 9oM6"~]xE]P ggY];}?q깧%kxoR?x4y!oo/ ;.X N݅D`w?0QWm8.Tm=xRO5}/l8 _ϭ۾lI>rg\\ϯm7*o߯ƿc}u-#W}qw>'"M;~E^_qy Ȟs9Yofp{>2;sf6WqW qc\;8oXpI|66X8|'6ʂoblξB׋:XȰ@621QE?Mw׹`œo1lنb^:-D 9乛thWe|a7(}oEo!%Ân x{}7:EO^KW#x>kFIb|4,y؋ٌ/nM}R_<Au\ $6v&~|ieN8:vEbعR>G0.hz[hG<,*q(=ce$>yV8q/;K<:F.*ɲ?p+mL^]BGW@`#z ԣhe[_:.1O.ȁѩKY=J2@b׎|vGk Mbxgeb>t 6'f})32f=6tWrli'3x D>[䨧/H6|Ju:g#\m[E'𧮌?l3Y~h9gQ~m7n$^cS,HxH %|h'K8AG7iӕV ?}OϫZ>}xzAGavd\C` U081%?;7,sdGAFW[v2f->h?^tg>ߘI_8d<m2@}/̫+WcB>q}Qz6Ќ'xJ?xG^xn7葟Ł/_rdHCPa(?9}E#\u7;)d!YtTزgXɿ甮 8 Ϋ/}4~a.@6ıqw.l:wǃ_碅_K>7'2zU?Oͣjm]>WvZ~82c:c}v=e<O?z_󤘋%ת'`OMb7||_!c5F|7^9=O~[{ Ա$Q3>{O gyzzdcb)_3X[x0X8{V +_`xN {< zڦJ^a%]LZl`k@PtrݝΫ.(f]*he?{bO/VLt/>6[6cjgYP9^dmattVoQ[D/f;_cc}>ca[FG7&K6[/-w3^}sВ 1g/[˒\˹S:[t_X9CM;;х,%zC>m2%bPq_x7\t6 rEo^2§Dg)@w##ejOlՏ6{-icH?xAh/ cK݆^[l^l={ ccЈ6ZDGV|VG>hc^A.vi|n8TWl ,y Ҡpc6<SүġӗWK64mlIv]z:qֻ7~KSL~)v)Q*Ϋ;B qp%,mwsGiׯwYSpݶ ?ѿ@^=?zl6&+~u%8.}D68F~|>xweQ o)[VOT^̊x<ӷ|q:~׻OEn۾_7ƫ^'BNʆI8+]}|_xM)ǧ_4O}ի*7CbZSTM~>rh/QN݅6;'3ɹ_}dqw?_p?薋J 'ţǏS/F<=>#?֯iG=~8Teȷ붯6j;ցfpskYO}|~nc~ xgNݴI߯M?uw*wl2>_I@-˛W|swtz*͹z nzzH-ko+?jkW2|?GԾWmk_^}`'c/u 33E`m1v3?]uO>vMr\?ju=VvƧB_ >c y[n9Cy^ٟ%yͼB>r%,,*E:Xvl}eZg#Ek"Ugk hNO?y16MB&KxFNGpׇix/d7F^@&t6>#nc?F2ХؚD27|ē<b :Q"Ck{vmPoPgmz\o0%olT^:KN#i >'vS{1ե\2e[jl=_9SWG'9C>4sxk-6]O-dhW]|'/1DJN ƅ<8L[xХMdE>;ٱ;2[FCwtuW0a%sN9Sq|Q8'}&Xq"[)a2weZ|^.qԸ/sg уc$ >qhYqY{L^]܀#3ߵ}Zk.`jZ7ٷmDfo~a'[Fi5OMǡ۴< W2ɢircN::^-Z'Γ(w71wP^Qb6tQO W6ǘ.(]Cة/cN:]6R+uo?1P\XDӃF8_O_$?vM_K3|X49r%mu>A= zY`$(ʀqD$(J@'CkK(<K 09AY&?I&Q`}tD7[ ^2tjqPGdgͯ|7+-hoӦ{H}|~{do Käs,)_1Ï8wB 댍E=y\9n|E c`l"kv9hO}]h-V=nwrQ>'.'w35{>qh*}WKySumwsuA6ڽ8-y@h;Ȭ-~6_,ŦG_t)]İt߅ߜ-e0hGP-8y=_#K?R_eX2tb':@gνpO}dk%/c]с6~%M,ˆ_^2Іs646؂d743G}9CȞ@oӓ]x>vjM_ ~ϩKj/emCVlpxm'::ltgؒn,F.:j'ݑ?͍Sm~9 R.)>胏Nu~<2i/˵Ղ%O3?c92Lޠx^e· :zlOK >}V~I^EJ̍g?y+rg?5 'INh0B{g0%߅ s-f~ι89́o% -x:3i۔Il8:FL?Eϼx2c¶7O˻-VOu zqdu)?C<[>b0<^KgǭZK0Ǵ X2b\ڣhaploxcK!/hz6 w?x\AV/Ȃ^~z---Ȑ%ʕtY7+ek(`7n>D_w_5\yG89.|bq2 Rq]PFi)PYeEW8Cc1I!;BشgcCǼ?ߒk*ïԎdsF:ȅς[?"'IӇ<>sx/6E{&phG&9K{+}ዌȄ<|<:lgyf O$O^l  KseUwtGU?~؋.mƇnv$؝q~8[ʁ#?rE2dh# ^'ЇV#% .㯌]0Nу7;wEĵZE89i?+wK.Lf<:|,ra"/tZ s-_QsvA-3n>rR.4?^?_|g:[f\rQb>3YX;sb,;Aam.3wl1o2 ﮼)2N4CO%=&.%uZȂV|ytP~׼lv_538sޣydp/51Cp׶(Ddo\ckǰda6^w-~dkAGd{ #₶?d:n'KDE:;AP@zt|K6?]8 * [Q[ X͞Ac>s~q/z |Ǵd',or3ϧ.twrܩ/ж'珎`obܱ-z HsN_k1]#>x-9чl) G/Z4r ;Gݢ|1Yol_ѭύ% l:mxc~8pYeLӎ],mur@Ɓ,x%9Fbb3. ?:xqbWN6Ã.x_آplliácM?{쎬؀N\hJ|?p}rx7~?GnyAu.<?;J^q2f9dXв sA;>cbt.2:aJ+JJ5͹f:8vk+M~x"O txbD7y@?|ПcǮC_t,cg&1 [Sj#]]hk#J}"G m:ɲ䫣iG^s'ѩ\oYyd;8K c9~+2!Ju`hY?oQO0֯ߐ\[ӃS tDo&.线h8h NY:qm|Jmt <]- ROt|WWsg}ߠ./86QdYv u'@[ &&\\%#8V}97X'2£.)bJ!qŷYNUYCjWJ}uf\A__[~oS= =_:^?:砪N9<>B|n>濇쒘޵r<AοTobWA@֭}g++v\:n^Jx?C q|Y\|nvVM*^#kg]qul4}^vTYƲmUvs=O{[ËÀ5_x>WKy0#Ƿo=}Ȓ%Xpy>ZBuZ_emOn`^}W'E]7[o%$iStA]_#Qj[i}o_KGG8x ]bWu]]|PxƊޠTM}b7`cnIyӲC,[|(?#5ċ7yob ̖Jɕ5KNj˞4zbKoOe[d(%tӦN.ɧi'h}hɞ8|vQu8:=l5>u _ʴ\ѐ'Fv5=MȌ~جTOW0 K-Whe%@Lמk ̷ȏϡ'W|J%rȤu[>"\˱k767ؠ3vtJ/:1'mIgaҒԩSKw 8P@Ih%4M@5Hlc OC/r<~Y"_;PJdvGG#l9):/h:}S @IDAT̿|zïne/LײV1|r`eShvCDV]؄^h?D2ѯ/M6x}uǯæC^?H3kKfrMxnJ8A˜^Y!kܢkm3:}dX(Kdב=ϬU)k)u%CO?:(KFtAit]\u\:Yl,{S}<;كI_;y#wjG stަi#^k(T8{ 5x;'|];՗Mj |ov8l?WtYkޕQp;lP"b]>W"Q&9Rvw0!TjzJfr]o7^̓vD_ۛMQ]|@Nvɦ[_muMCOb~d1pCLYKkcϢ)ڼΖÑƹ4_,͗lwZQ4 v]Ɂ>xyYBOR޵>IhQw>rk73/!Os]ǎ?2.zZnht%y>9|ڃ@`}2G&ȑ6h_e:, ;f أ_] =cWEOh{/L/Lѻ>6RK;`ߵ|AN)t\+kgm|S}/u}KGhÏ?ׇf.?zTZ3x\Rs+>}࿝iG٣7 ^ v  `Kd$x8.WmO{ :ٮO?HK鋎W J)OXQC-!"M[贱[J֏s>v}"C`)>226D  ]Ofq!WቍXn66]͈͉M [xA7O0RnNCOysxx-fHA|dΒw Q^roAu˦.+6X{fYPpJQ f0~wk _&o??PH6Jls6ieqmʐwKC(wN6UwVvmè]~1\hPO=f;9eWks+^['=hfW4_]wEOUwe972`iKt(ե%6i۳dy/RhV%ۃ n]>mՇ]7Oaaھla~%ٟUƟ%uC]B4Ǎ,21ӆNv=mb]+c~%~vHFzߣ+36 G?[[hdՃrlʾЯ/[ᩍ<(s7}Jrn~H/z_Gć^hbvsM/9CL'=;׎NoTL% '앴d>G,:tvח~f}`=Ӽΰ=+72cq&N:f@`$zI vt z2= Dcr( AAK?tIڃ%:Ilu=и.ISǯ?FF3:ӮNKn^ꮵ-O sOFǯ;ΫllؽD6k>hr=41qsՓ0;mMMh~cWk.ڀZ3(NY$rdݡC;|AiƜloD.owm|룟l;6nw>/u'36ku$ޱ"mhպOZ&+>я)5}~ٵugMl)˨yi-kyI^6#b}l%K}MɦCqhxd.tO9}mCrwaD(?es;æIO䔒v`߇E6{jov FN~[Á(XhɸcV/'5+_sP;Lh,rG)f^§=rT6{Q'[rCg0F:t| &:3hjǯL_?{l؃N)G6hɉrlL6}ɣ(#ӵ4Qמ]f~7-]=q}y;bAC-fenJK]e~[_Ё5o$Լo&nEO ]t Bnשk%m o^GZ{=`5Ś{@zҝ&^?[~ksj}4m:dun]vDb/{Tᠨ^[Y!a}>wcݎlĽۻ}wu5cOTc\xY뭷p2q5l/J@;li9T1s^7_'ہ]OS|J}oӍFI ~Ѵk%͟*@84U_ݯ{`).|sOd;]h h’+ؗ1[{qn*^8tu2=N3EOv%ܥFE>e5Tݼj6 =Щ,a~fsu~]u]بݵ.L~e:~-z4'"3^:ЫGvòڃkd^~7rÏW~vńD6>)mx);tCLve]=}॓/>G'Yd?a]ɗ ȘER%~7ѧ-8V}lWjWJC''}@s!DF}m9͓M 'pV (IP$Ӧ~@!2| D@O~'Ն$/ڥm&WуOhc'xbsl&iǫ=Fd/{xȐ";s{&>a[]O7&/'m%&fCF⟶ȤG?LR܌-U]]I0)}bw㷌{{'6C;1Uktsd}~~H9#{MͻD 7'J";Fk.>,n7O,ڲq)7dq$Q&c;mv _} .&8mu Fhx ãg`li Y} ;Zg:^]lV{lzbcE~RwŸ~B܃ 6٧>0]f}5ϚXmrS9*'müa,V66㋔PJ>&\P&E?\?rԛ:\  ƻ7zv`PawJ> zmIJ@G;f{x؀.0nmWc3(>%|)ӡ]kZ-Ծai_t=Xj13c#M,g]6x% =eȡ'CA okb;^{[R.utdK mRЅb7~)c؈_$]cgD:4h^#i# E>8NL:$cv~kC+MR_6 vtӦ`Z\ 02]kG|< D`ć6m|#72$IG~0P''ޙtͯSGMDХ ٮ l%e7푡a'[ц'?N\r\glRF:yk>f$CT6tjuq_fЭoߔbޝ{[L]A/s܆Íٚ{i%~5%o>[ӖO~:ڪtUOy3@4˧'csi7z*n_,ә} __?Ԣ֯jOa0{lz2-9< Xu{>vaf]63y|£/k6l{aƣF1_/d͐M$Kgek=Wa;}w܆uu]<xDl"_hhbg562V=@ۚ _69H;A]x/zo6i6?v/nlkmk#" ,4^/5P !@*= (#>o.g~ٿKF{3dxPDh@ux[ODv$Gftt_oiF}q>  ={vs u9lMVjã>gt?˵}Kb-%|xUA-,\G,?+^ <>+GtsZ}艝O]#{슏Ƀ}yᵖ [dd<кFئ-xWFoOI;~IWblNz2.3vi m̹D#{^XkGkG:k9]n:UmjQcw-XkIr-:>2zqtťCH D@T7riw$Y[d\!VpIGoJmiNËNJ~rb 2[b>20+<=Kg<جn$حo.}0fz%H t-yxwo5}臚ϡo:aqd};;ͅo]o>Wc!onwݝZ}f.Y榵g}l__5W3k}N{?ΛIV?8n ¡d֊>zq߼9jdn n0Z}ב~ko<'kolף.Ӄ fCu0q-NF߿d_[R-suk1Ønؒc5}d; aZq|]:G)ƻ.~%<o^9K;TAaQrlom%gSwnԵk(dY"X(ۡs!;á__?s+Kt=Rped^7J7v2F>U?{'ucnI}A-1bɽ҇k>yR)w/QۃO8/GmKL'< tlK,>Ï'cLw-EyJu}C^)w:瘐፬« vA=vGO~-]OM;كa||?У9htNZmڌ>cD߹J?ڴic`/59>p"=#u:} c;ԮM_G0>c_ I Gsd/4xhBG6>e~zG{8>,/=bw1":cQ71wOt`xی$v3ϟotZk|߿o[KM/qZis |T7{{ݦ7'zچߜJ}Je~KYynqg-ޏ| 1נ>:驷+=mXAKF[wd`~9G>/YﭯY3n?OJ|zw0A]Gw??suZ?~=?xTsd`0ajf֧uoI'e`g'E,󲥢6|kE`ld|nU_*} &c1׍ S}tK %k諿CP9x+'6<8x;. =MgDGtz}M~/H|3{`\]MD2{ _ksfAl@W8+(C.yh'dzdݽt~3MOfki CKF02bWIVk2кVON}yzڙٳO)rn:~rE)'|tÇcO={t#j׆O;>^m?}dEn[ɏ +Z$ӥ-rv<?ECCC{xigOl?}%Er.3ڂ;ɂN~->u~y͝|Ēky~i!Â"hHr=}]mЧ2;>^jZov+' %#}=TêYU=~gISUօLG'0]`)nKtT\kkvU( 3q*qiv>'t~g=P"67-ϝ 7G>5]|֞}fb>RIk؅Os¯O~%?6oD{E&^پ\tx`^JxgoE|gz=rdVf>v.!Gv9Y;):ɑO~Ȟi ڥ>%>%$mItE<ɵ9$ >zŭv>-qx:cvm$!)٠ 7k :-7fD;KW^^z|'_O{S}nDֶ7@,p8q2NIh}d mhȈ|&G k/>'i??D6#uJN; Gѫ/ؐ_#bѥMƫLIڒ##onC>n ¯tСv0G~S+qs//6u}5bwnbOl]yE;-ɘ'#_JZc>&_fkcbRm3G ^5H[E_Caw}MkOu%j톦ߧ}vΧ!r{C_O9}^K>8UO,Aznw`Gq.wv,2 ݷqo6@ԧxXïe;Ya~L>b :N_=po,kY9hѻ::%6Նi2́!G_[t9`_Jph|˭co`cq//.ot.tys,'PwȖc?t=%F:7{߃nʌ'm0n6ul#5_ 㦧L}tmS/x4{ {LWU=].~-+d )amd;m/e/=аL{\m#'cXtjS:IO]cO)%tTJl[в'4x F~} 0pnz76G>~~+!v>=X[Gۤo//{?O,zX׿_W^}Oל=_~T3c ,@8ZK. M>FFՏ^я]J}RM5_du]7/zFO;m GҞ6%SgSh*23:rC~h+9rgbĮ/,JFt}^tiF&CĶ뵠>}ڋ1]Ɔmأvn'qS},8{ ;nVҍ:̞zxT+!AO}P?qk; 3vb|4k.6YPGNsO#9!vmN\mb)`uuxfes m=(36ŮkUǽ.d ~&UĶ޿+^Yx{D~]-}?Dk\9>giu' k"#rz\v޸<~ҹw ghcCY f .R>=Ưi69Рx|bMpk#<ȧ0]sӱ-VW>׋N\/[ԵCxx=A{Pt  ^\:aW?-z >R%ŇX.[C|Jn ls=?WRN负 ]޵v,z{=YRg9ɏiG`- >K"K_ڹ/фW]Oh:Fu>vI𗏱E\7aIGڔM?rP؀VNѩK>.[淾5K|X_?OZ{vzcs tJN'֡IIiK[TG>`$ۤ<<UG.z؂ 3x2xsktّۜ"}n ]S$\m @oLZ r+#/:z3'Ӊ'nfܣ+ӷ4bcɕǦv:YOhפ̍[d][vLGzXCp6>絙Edn_7^5Ngݰ!Wڈ2ogW?%|u&"gkݚ: k >Zunup,_}_mMZG>\Gk1uL[Ŗn3}}lN7Ò.m|_ŵw==>φ#*Œ18pizʗ1QX9@k[nA}]nTݡQ687v/%KX{h0nK%76+zrA|R&E{Q%z!,~?9IqOI.yo|r9:+у&=6/:9:~ѥۡ \-hKtU.ҥ?2hkWtD#?΋7|g><Y/_LN>Ӊtw`?OM߿28 PJ&Jڔqzm'SBCOW'c42%w 8thMYRJt9#kO,2"+] b;EtN,)2.u%Mf42to*ck"/v??rdwlNEH[MM߾J}o '؏Q>|N>Jng$غwz>ƕ__jb>=w&\&-}(׋Tq`C*u'6K6_mMnrUu?X^0OmIhZa+~ж=;ou;`FWVQ5}}YQxknoBM,Ğ,tk%i$Ș+>uHkdOG. ]ԃ%"R'3oF^p7ףO]99e|E)tɱ7~'C'>B+{exyhƎ\=c;'gG>~G &?:ƇKO>3:x챛֮7Aw;/~w| ;l/w /LOo'y-xjL+tҵ\%E> 8_ P _%&922$h) []F>5|ѯ%tC6ЛkC:} ƚ,1>G&:r؂u6R4LIv|lQZuiOZ=lo%/^c܎FI7Nl;:=v/Mg[Kmb0&/ϟG'N>7sIWO]rzYTutdx"kz} ( !M>쩞>o0m.Wi֑!3l2_qHb_SWpyKk?{?yMjgk0vkMOFJ0Zkywr;JˣvoYu]V7ox=mpK6:yMX/'ӏŮPU~mvbmWxfS7/ asjء) ۸,=L WhuKܰX8C~9r:?^Pce"n64se ~.舦MST8}mT}zφ[+XDP$v7Sl )4,͟&l2JJ{& _]~@)96x7f{'_8|iñcǖ_CkF?HInzp ױszuEn0QKҟ>'+sB;mJI':ldq i%S&K╢//mdJ1|hȝgID.?bv2)G& ~ )MQ=9S;]B9ybБ#-QM)92}sbR,K:Hd46}K;G6)liOQh%x"_]?ݩC?/tQ><ߝNzd)X#3]h"n2׎ND&ؽe3nOXԾ}dl?sf1O;L?vjpZ*GOrԯd=;tl]HKqbW6GtY=}psӉ뇧7/޺q5Ư|!&Yj,am0$8`\-t>r^X:}f?5˺?X zdXspօ*{goIn4.,nwୖ&ԱV;s/m:DH[UR>g?57{X}XwPߌJvni+*|@~1x|ြUhK.ztrhl }]Cwjfz;Ȗ~t|?/^!֜a{'fp<8̊>p\S| ҧ}_()kj\9ꮛawU|-,b *ҭg^|t=rm_>az`56"_fvV괥A|oV}`Nze#,)COFx^<ѪG:lxY_gb9޲mƦyLMrO,ig= Ev|: G'OcZ ]֖kQjwȜ)Bа2FڥiȌ,63I|v<+ZRG<&ڔrhٓvF=}`Azd~"c]ņ=e7%?v.ܯLίO4_x>}[n䫫+?s k?r\ nqguYǦ_;;}wWOom?7]_SZߤY\ X)| خmlFzљ>|>ס:i# =||<Mt&[~>}ts[6^r$|>:գ7l]ж}Ӊ-GwC/Lou=~AEe|zoM{K7^No?;][_?qIl%X;gg?:>yޱUrxzv{>KЉ] 14NtY:+bס\L/~eٯM/^yeŭ3[fF_߼f 9M>gz|}t}s8I<>NKln|sm]EvpՏkN797nh4w}cQ:4|#3t跾no{ͿI{>  6kɆ 3Dܶ~X0=qdMOŮ5A-}&v`OOʵo֡w 3Y:h.#|,zsx{|غ`YAfR_Ѵ9Ho&O<2]-zkCa[ۛe 隌@eUgkoͿ*gd*na\u3vlS&0"h}@nϟ#+{ ~4[K@vqa+={:r@qVqn 7rJW07txLӅs #s5~!_d6`Z)''C?J oEnh>nIr/է-6fisȌ$eO56d%iGeq+"O]Jmģ/<ك.'\Y? OhȔС&K|,ΫIDATSj)cgx҆fWrD;;d }K{Ah e|l.|7kLGc=~ɢ ۙzW?i_,{Pi9_ݻvߖD<]n29fnN;HnrGLF\(bCÊ`o8=sW߸2X_|q 3'kտk_ :}c= ptueߡAҮLЧt{;F.l]7\骍d;W_K }}[7dQ|}s͆ClP><ŃI}eL{|/5l|#9i]a+55Ib}U!<+=٘xkLD߶*íۯM6=9͇fJ̈q3^}f=}t30]צm|g3M)^Xxn͖d ӧ$#|)dIO6NRuݒb;1׏O]CKhd}dF !|d^$}0G>zMR-.Ym.Ty{# #MDvekqg=o\) m쏽h؂'8K<'mn/k]''(c_Fm%=4jSدX{N׏>6m۟y~:@[veo&`Ҟw93p|p)0T$@'LQ\d\k726*umbF";?0#d Q슍rH =lTb c6ZwnCG 1am͚6_w7Lm\gNO?|"vGxp(~(Lkw_ܜ.Ul$]_!n}YH}g^e[Aيs-/\d[罴8xOGml9NŝԷNOtu}wx;O}6HJ낿bm]>LVأڵR?g\Rpݥ!tDO 5,zy 26wǬ;oZN69+ŋТX)-Z:<އ5:C@!5>lkW{j*KS^5!VZ -vdtw77gmiQn}&=/Tlv<p,xZؗlۗ4(\bCJ}]ɇy+%CG@,{G@62׏ΪkNHN̦6@` @֩y.67_e79}←}+ͶqlݍPmF[Fj)VG8x7"0b8j 'BtsӋ}W^}0 @` 0 Y"?02@` 0 @` 0 ?ZQw۶n[ݟР8 @` 0 _Ҿ}{syKY)[gn! @` 0 @` 0@` 0 @` 0xt\|yX^O8~g@` 0 @` 0 .{Mۧ'xB曧ni @` 0 @` 0<:I?lDҥn@` 0 @` 0 :' wԿž__/LxG9 @` 0 Aa⯚=wOϿ;*^m6oi忘; @` 0 @` 0:uz[nVVW'OزQ\͆nMFQFkի*Ne;v>Kߋc^] &r{*hggϞߋM0x<2䫕u[n[*o.]* ZۭY/\ցk۷okkuAUe36w\msF>6nL`{?n/bJ뎹#q|gϞ{[ռw]a6Jx15?P9)q{&Bu<,\ʾŚu*Vw:taoY][mV~0|>Gy/`^>z }+{%.Ok ۱sǴ^aw/ }/ufu]+.uv/{Q{0>ƞoW{_7?˯X~z:t/iw-o;lmݲ9evnx5&ڨuX@6ܴIDcfxt,7Xu%alp$,k=`E-atdmigW8vpټwJ_IsD]`}bcܱv3`#]q/([ XqVm,.^8.3(i6;w&;z\3! nVJ nee1]]E-ʕjo9uPdFI^m.JE[x`~*u[+kFc6s|Gf3;U<^Μ9B#=hlxme~bcpmZvu;tw}_p^qmr;͎k-Aē-EblȑC{=gm%<\c޽Li>+T)'-d7LF>.5Mٵ&tϘ(qgfmޕm{WKpT9^Ӣ]Fn_j^e1)y(m-.yVX9mb-g>[W 5d%:ZC+5N*Zm5Nt7qowCkYGٚ oxf.[s8n'₳7JdQ\VE≼=϶S5QZo窯X5Gv5]M߹:q'1µ[Mٳ/llcO'mĒ8Q>龑{!̑+W;/[j́y.|hҥZd|t`,Ǧ}ˉ]_:0XFVg ̾RagviXT XoEb-;uZ8B2 xt [:=a*jAo=p}re>tp:TϽqpD-nyrpɬ5E.`O#^s1N>P"2/ڤ}*v}xzk5eu8`27ڷاb] /}['ckM|žx~p}K[//^U6N3gζMn^/vujmq76֔^ְOկR;wioyxn uAbf؛^`CH}7=6dx5 SAn x3ӟx@[}H~{^ J|x$뷩;_(U>{=7߬\<>]+:׆qx!UҼf`S,^q#j>Kc'W+0UqL[ɲQʫt{eͫ%^ik6^3hQfU1rhawT؉SO>Ѱb9^qtIpijkj$\Wn x}W_T{=^sАKWUWNcGNǏr6fǥ\~yEL6X7ic{pZOõ6j}l;g#Ll*E9y&dxbp]L굛=R%ëګST}yI[k:p6Zhk3dymh(V_0vb 0/6R zUKv^zm p#C\_m#߫⁚^k#+]uW}MĘLkcoS|@c-ɀ1?y⯽BX`^.{k@µWsn^l|6Ib+{@R߫\kK?W5\ [sOjS >E^}e㉆yb.k1Xz\6XzT[sI5Zgq_I_Oa,0^RNqqF>zm53HmT1+ZqXk]4l5fӲ5F5LcBuFp'NCokVn|l@ŪqZÇ:*VµT%z Yy\ ~WVj+/7:kOc2|Ƹx5QV}|50iV ܣ| nX_]<‚]볉$W*w?`"C>zE{•Nbݣ_ݧ$7ڭW[J;ڙOlR SW̫sa#Zj׊ۆkp[]˧ -e1./-kŃ1 ?xGCʛ-Bi ;yٷ7xcTY010yR,-/yбcG:pDWߨ!ـx:L}N#tHכ-% }|kX06b-=0&ka/?@j.}Jwop-0s\g-$机u'hƦK[Ĭdƫ-oT*\UCu?O*usizMvZaS{{{8'H ' tk *1n 0-k{ݿ.ӿK_ĨX۬7wuǂ1xŀX׽Y T) oy=y>{uϲCr3e48׸#^}-{*kVV8Y]kX{g{lsd[an0ˮzd}|/CվV=r̼my,#n{5V}/pun޲6ƃ>'tELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmLExifMM*i C    C " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((((((((((((((((((((((((((((((((((((((((((((q~ ?S5|j>Ɩ>T77:*Ue $"ng;O~<|/V{B|s.g?,nln?q!tf[޻IW_ϳ>U>|D7{%ZT G -e腳8)X.{5K AIs.Kqqb돥-Ï1\_.B.Q$X.{%Rd?TJ=9S· 0z ~``tWC"IA|JR+ 8<⡏4ۿ/zϿ\+S  26'=dor9si kh`ey.eoI  褰$ws῏D?Ļ`ƙ!Ͻ="|62ߴ/$/Dqa Ex+_(w$,Xg-bi|6iBK?^X~frO?kqĸ 3D'sQ´4ON?zF:ˇCEQ^/?oh_NII#ГdI 2aͦh?0h\J+ck <3)RwgKp}qc_Ld7s14 E^3?o2,-#tؒi2h?r;֋dr῏ˤB=8"O;qlhx<3`tW·VU&fY~ 2?S?h_z \XsEQ^0 "Ih_<`"i:*}rlfHhoz! ǩ$`W⍜\iWj-cF峑A"[C)Sd} t>"'o\heƯj[*MHP6>`s߆h<)Aex®[?)=Ah7>~?\',5隵:>Q(-40Em[d>3qb ~DW0 `Њ2侗I+Zϝ%S%J1Mx^⧊>3|oտb#kjvY-[|8dSE+{ /(g ~о55-X^wR,7J|sKSχyTk6JHPnA+ &>qW;+}N(XR`>v1'ӎorςHs*7xJ] _ si{:[[Ϊ=K i?^'>.5׽m4bF$Y& E3n'pm% RD}1Epo=\4m}PyEpo=\4m}PyEpo=\4m}PyEpo=\4m}PyEpo=\4m}PyEpo=\4m}PyEpo=\4m}PyEpo=\4m}PyEq~$D%, 񊻭2Z *sNx?tWՏ ^;+P= %p^ZӁ9${Epo=[&-.f^ߤjr\^L Rx\w9dxYQdm2i#3ӋF \yGt_?(囨eRwIvo͞'j={sɊt r*mtb1.<xg9F c4#_̿ 1C- 7Ѵ8mӭf9?hGTEU' Cf[I#p|wrQBp}1_ŏO̮ 2u5~)%z+oFϥl=:+ 5M" x?LtٶVx,[u[̀y^|Y-_٬7nNG$-ݞ+? 3DP|=<{^%i֓m%e6񢺰唂0A gf|O|+EIsaxs^%CH2g`7 '+4_g}L̺u7NW?6Q3se_X5Ĵ0%FҟM#`(Ν5fN|SE  t=zu[ ; ~+k:tEx__k]K\ VZ[-giXcf2n[ [x ;ecyMkᵟmgږޙ?AOZoqMcI^ Yӥ^N{GS 5ֺ|ug%_+hz> xN}]hu,s!6R /fuQWȵ>D(=m}jmF{Hij+u:[wfGS)F+|Gɰk}5vԹUkwzSK$Zv?5o*wǬkzgk-iY$V <3157 Gm\Ct0"3?UUēe>WXgkyQ!a A&ou⅗&v2H:]QO#+1WK|A_xs X}.ϻo*u(ҋ//# uHeP*O7$d2+ Q1 3[o.%&Y Y\ p?bk~;ftG;$HDHaXF>\I'[ xlf!$+x{X_6T„id+G`h (8F?h_5:6iϷԦ9HGDEYU@U*Ē/y?#StfEҌEK0 Ld($5.C 䣩eR (##?l?ᾍo2M[C:oܷDvGA#NKry6GYe Щ'Ҿn>/3zJP¤(]}#_*X/V9M_m?࢟w//ß >0xǾנH%}2m:h-4 "e*A {_|KãWQ'4:Uk,l#(g܆/kp95p?˻u9Q*K N1^S7MY]It1  RNJ]E~Kl߉7~i? xsޓ\[|6#.u\G~݅s ͓}iJ$r Cw8ݯ-~%V^b% 27N3H7WNZ1IG/hK  wW}?J-sޑȕ3 dxsź⿈^93_iW:^AQ$$s8[mr ߊn/}(0qbv߳2e2TeYlۉ~3Vq?O|Cnf'hn(o:O93<-yo?FϿ)iq ;H[{FEtiۭ0QdA?¯گCwjkKNN n$a0/Z?nDtV~V%?|]N9_~5|K/|wY6P[MxnY414?$Ph ՙfZُH];IoUUpUʲ~x|Hj^=a<Ѭvwri"9,:[0ڭ !:3]<Ԑb?G$Nm,&5AU7YjV~֦C'%I}[EՓ+d`t\ϩj):ʔ{ܯg]8V&d֜Qmy];ds[./;Hg×]~ҿG?w4>:9L1rFU!UY_ iG'r4I7s]KgۂG|\Q㞃kω}2Tn-qsr)ʷQ8<2+k/-紻+YP$n7+ vL+z!R?V3rFU jUq]'5GzO֬~BjO/Uv-+1ZQD`՝3ArWq3?MjL?T&ZuB;JcxƟ$~:'?GwD?_ '#g Z1[Yrij=Y&_*O1ވIFTJe$j\ؾ:-|b'*%dfX7 k_Cx`(B3FrIc&H;d.~;bFT&I$#_L?N5E.ܤU?r.XfHww~ywׯ|| ׮|Q#oۛɮ3ʆV!3JJ>?টMOjv:U/uq*xSRX h=YZ* gQ?--SX#.+H~Pyqŵt]TiM~]0IQ\RK~ |UkgO(]$C#`FA#I''9w+6 ɴky1iH8ȫpH 9P 2^#JkvQ*M5 }5V+n?߲>P.texJ>[Ne)"6##~7; -A#Q*r~1?C?]IT ?*챫<Ÿ$Q"u{+5E,`,p5& ip QP7v>?Dx)s^&M].MHΉu FPٻ?h?|wxS-"kؤ% .8"GR$F8 b5\]{Y%Xn+-~ۙr{}-)ߩ!f+{tF hI~o+7~ƭ܋X/f㎑m8a"56[Ag3OU?em=6MnH{etcPAdd8?Z_M7;C^* Bm$%$~G͖D?83OU?2K_UNkhu).1EFǶaW SA 9Z ߋ !?^~bf"?' ટfڎwigAG"k+Qο^όo۶>ƪ+'OƝP Qe? UODckZNL:\Iqeib*!XWҿt|?klSI9^7ֿ9i''2],{P a Jc9toim$k"WӭiJJINGf9n# YtNq2N-zf?࣫yk~P]?Uѱv'ù@ϣW̑dk|ÿ_XgU187l WM@:_'zpxts}W9Q~WJ -+_]W_uZeEjhOq}nO?/x6[ Cvrn-Y.F%Ϟ%+4dT$y1 js,{}ßU{ ~&t{ÿ 15헆4์tt ڥz+Vݖ2CI[Fqno~ G}xse9T9 R*zg]hD3E֭ / k{̗b < +Ś+x\i͠=7 !/8sYV/BHJa:jW p]U;Ԧ{韡pk$s*Ynz3dw XE ۗ;rgw$} ^⯆ZxRtoOP.8`|HV&9fdFOXtbQ.WӹUMŠI+weFb2;WWA~~ _{a}#TviLk.RW6q +iă?FƙkUxJ-Jh"X"[kyrHs.&FWvi{^[}G1n,[zm $KFؗ?A_qfV1\Ιֿ;Y_(H']#3 $Ʋ ˞ ?QXAm iz_#>SRSK#N[?i<#/jE+L4_VO+5k_MPUn͔Ҫ8W>"¿vh?-ݿ<}E~ɿ|-L&]j : , Iy<#:B$ ]eډ]mvp|Gy}yf*`qZsAXڙkS)FPM!w5|i+x~mBmRZ$f7&s.ᖮE~:wg5o,3kvO) Z$e}18CᗇoτvgJohe%πA$ X;+VE4J?i4\/{d "pF]*lpuTenkoͯϲ܇.:RP9_J*^'~~e*HHq__A=N?fzd dǨ*9 Q$ֿ1T$>*;Q5gF~;|:+į-wAi|=l5 lg|cuq~!ftd՞>ǥ>Caf%+Jv<_ ǩjwOm[4s,GUqz+G|\1|:懥f/mʈlF3Hʠ d#|]qDzz-67\L? |!W{\~!FreH3"ȲlC.RTFA+lU"+]kߑvE|O"ISxŻ{~c2H4 JS[[(kO3\#<ZΦ3 18Rx5H|E%ilYL3 FI݁:i 㞘 |=f{fmtI'M?'͕Ă6SD{Dx>2ē^[pumE;i<&\?$ױ7et[zOQLH.w-:3[̍(JI'X?A_R?d\ZX 8rmXQ`M6~ kZ5.s6 xڿ#7|-+⿇'M^!Ln--;y O 2 }yK؊|I*8QMhM+]/S|k8|mEGc|5\Py֕~ս[kyR&y58]ɾa!\“?i+\{g5+Ms:c~V,.oMYVkUj ; 5YZY|8͵ٚ }J[hE% GdbTRUj{ju-nַ{g삂Y'~TkkoVkZx1|[OgO ˵.%Icnyo9#0l?a{ oO[Q|ID[6ɹ <s @|;÷oMJ];E%[?Lq.&T, ޓ i ˓_Ǿ0\; $1?1*9磁æJUdK+_NlqO [T1 eʿ^o_Lz^ka}uymjq__*ID7}u\G~$*kzޡsk {wu;fwś$ԓ_W> Ah GÞ u cew34bpl"(Q@K|p<{4eOyhPb(h l k)<6tN4ԩ-6n4n>d2Fi΋SVnm=~]Rz ]3 ;WMߌ|!pi_F[uAXʋ>Rg; ك/.tT70D>;XyEW_ٳ ~3k5_LR<w*SJqk_^_/lGy幖K1\k r <';>`JWg^'F X?+_*xh4+}ƪh9};:W(F[URVNޗ>B.?ݮ@㓋 uH?W|P/¿7.u gK-c?5TSXq?W?^߅~׾ Xx^}bWҭ9DH lAe#_Z8joo-}ng~.]gh^g:\`94ޘ'5_x[?+~F?<KP5fܾ6[AgY/fjžs8#@ (#Ɗ07ac>*:mG UT>6})OMyM?)jjf`?g`FWLh֩O_}\"'s^+_Wxw]|4 ^d2|崄Wb$֞@|q43HҾ2|O=>]* v8۾P*.<\mُy Q0NgeGJZ>97%CR^]4;j:ơyjS=̭,3;gwbK3I$I<_/~3{Wك73OoDb 9>9Z? yn%3$³>J Jm+?j~˿|_ _#v lp#UVeT}͌3Uգf84$8wjƤ\[mo}%3,>+O 54tWKUucBGφ"? \eu}rOnhKYXFՕW k>#*?G$ϕo9jK5'>=&@Sf~O\>x7¤|#8,Nz/^E 1 .pLi@K?%e3ƒ^EƏ>>B =Nt>٦?@=[?0z(,# sA*kZ_ '_\fcJ(XǶ ѻ&~?d_  3O܌`i.|)0p x!ͨAo>T״EǓm7¯pw~k@94/?10V&%~Qp<\xiY94x>1?3\ \s~=+`6%~:(/h 7~|*PHstX4~ğ¤fI\ S%hc>=}L~|/w6N1F"<, >"4cNt3׳EsLoJq+ˏ]\EnHXb]=MvoM~ ~xWt6MWiR0uPX(C ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?libuv-v1.8.0/docs/src/static/diagrams.key/Data/st1-475.jpg000066400000000000000000000305571263361716600230370ustar00rootroot00000000000000JFIF XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmLExifMM*i C    C " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((+:z72!tOn{y{D9똥?Ҁ;X݃? ~_K(sȒGs${J"f@[_S-`4ŵLJ.h;oWq'yW1n,)Zm|QeUqp};ɫjg+O;ƒ7!$F沧e,BjzG}zRi]-ۦk3z о\m5cЌ/~ $xfxBaDO3p^i:-.;FMQZp @Pb;\2gCEE[xK:EGEjZw&"I%TWp8P :szjAp((((((((umVG{˦r8.Ǡ5IݙڧzƏT9OC]|Io,{(}8V+ئ}O,A2quW= &깻IP˼eK}Gl0Q 3 8W<g/'qT7^S >˸~zO|Eҡ-Hy- vK2 Xv^i|H$s4r+eXT[ 3 8g/'q]_t3ϪG*ڛg*+UxA7S k_JgȍK<0`P:ڬ$`$WqDRlr&9RyǸoy>EEnOBg|,7/94!y Ŧp$0C.J)l>*畮pfY#Xʬqw9@B,Hq_P_W~̞5V_$z%8#3*YsгMUDu>0xc^4fEiBU:Je90,>9o{/ama,CU:H֣R:;nTǷEKI4ufRv>qE7&G׍y5P_WO@g n|`UUUQK/℩jmf[YfMJU9v ѵ?4it}^7^wqvt>8Dcmo< -t+k}J4KY4krCM W+wd|ٗjg7i2V7TF.x mB]3>,>$ =XVRH Dn[pW 089bcmz徳TK#7V򵯗m15 q W%Bfv_O6in bmB I#E@̥XQ\7*O| ~5<symenaӦK]KrZx&biLvB2j6-gNMMtdVY eZGvwO'&!+{x5Q@TJrS__\O?6N/|TԾ%sionuo$TiDͰXF13:p[}Q\baa]kVc`+1?sa+1 ۟\%QE (((((((+~? }}skxgZJb󘜖hZ2ms_@@)k>[]%֯y`rіM6Ammmlm$H#P0ҧ (Wź#6E({6>$% Go09\2׭W_깯\Үߦ^>t)&~4QH{ ] smoZh(QEQEQEQEQEQEQEQEQEw??}~ڴqg WIek=ԙ؊XqxI_.&h_:tmRu/Zܼd@x]rPA4 O|K?l&{}'QKIamgI&,eڶȤiٜ ߵ _H7MӇ_:^orw? gOgtG,5.mng"YxXH-I}u,k'AAiW>?|]yIE?=ղ2mo1nL ӿ/$^ٯP3wM߅#3Sk?ۗ$k:ͅVڅDp [=CeDσ\ޟ0:n]OOQ>ѾΥ:Gݕws,J7I"oH7@;REn?hKkT/c3oOֿQ} ;I>Sk/? Ӈ_Ki*fSY쨢1 ۟\=FJ宆-(aEPEPEPEPEPEPEPEPEP%˯x+wǓKcc+~"xWO7mcjtJlyRXT8o470adذ[]ӴcF4a Uռ+J8'| WIXxk=GZT"i>6%ԡR,L yQ}kM[q9QxixYy.>8 tZJտ?nVmSsRkN6[_{%'G~9jz4ܟ /S ͥ4߾XJFV3+IY9/ڏ$QMڍ/kD[WyAxݜ!EwV) qiE+V꜕Ӷ|zJ{tg8xZ+ Z[#,f((`77 001#%7@?]8 uGOM~_kخXEG28` x玝ZRww&*(3n#rC\?sa+ (Q@Q@Q@Q@Q@Q@Q@Q@Q@^&|i%Ϧ'Wh߇YtH潂b y<$Nzghm~F_wZ}ŽWhW'?Z')yѷ8a52M~zO1tw /lYiq=ZtV2K5Gln'2jX sh:>0 Mޥr<69IB˺lpoo;/? m5)\G,B6FX.Jlɷax|Qxo ͨ,\~D#UeUc`Cu洺v[Wli_jٵ+ ɢQ/d-!&v/! , 0uC0m_eŲ( ǧ4'F D̽b;b@2²d7']2(&&\"+r b[Ia|:RI*}q#2"1mrJTPxV]E+.DAFg-nShylj)KyVP-wmh/d2hm$$71"_n` (>>$}Yl|먢8־7O;{,#Nр Ou=^E-nϖ+0I =Jܪa/c(=FJ宆 67W-t4 lQE ( ( ( ( ( ( ( ( (3uӧiwDd א^r}~2{Hf].风5,&rI5y gNOcd ^oMź>X@on!&ҡ,F,ܹ+3i?¸hWF kNgt>MihcRhmQjQ?M_Z W4L2Gkħ`O|Mbjva,DWs8dHG5Q@?sa+1 ۟\%QE ((((((((({tV2cwz.ޫ._gNs.}6W#|S8`.pq4'zvz/G'zwƭM0>;?e;ӳ?;ӽ?W߈kÚW'ŪA|ɵHf"Ʉyr,JFJ]x4E㋫M_OoemchPGmeYKœKt"=ܯN_NEMx<3_o/$A}&גBnlMmOxP5/ũ~˦ڜpMӍ۬Ng2w-I+3ܷ;ӳ漢\O L[ ?ğKx1,R=$hYŸvo Č f G~ RϝZ׮TZ+w?I(C9 67W-t5xcA?ҹkK`(QEQEQEQEQEQEQEQEQECTӯuX!f⸊A62A"_hԟUzn_M'W~3>/AWQ˛8 n$L q~N@nһO)o?%hԟU+E[4ED9_5^X|"Wqj{ЕRT–;(ObBpCh ba.-3_V$-z,߶|+Zl5TtX-ZSy$ѳOM?GЕRT–k?(]o_ -pzd6{pm"[{9j0ѬbG8˼*O[SO鳽ͩGnY: u?ݷFU6&Zj~–BV}IVΑZOK}JVH$1@r2=~ZG u_>jzl&TUh.%ʓ"7E0^?|7"lװE׮T̖gQE9 67W-t5xcA?ҹkK`(QEQEQEQEQEQEQEQEQEEĞMhBg8ȯᐼME.k(?d/Jk?ᐼME.k(?d/Jk˰F[D -L_L 3b+yeQ9؟ +-r!ZoN+FXjlyc9H?ᐼKE*(B7?=ƟM?bOt?zsd/Jk쯯>$s +.%Әn>g33O&1G' GQ`Q\_' GQ >>4oQX.icA?ҹkg2cq^,\[lkYIIe WMHK`(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQElibuv-v1.8.0/docs/src/static/diagrams.key/Index.zip000066400000000000000000002127701263361716600222320ustar00rootroot00000000000000PK&EI.--Index/Document.iwa)2iD* = :en"*2:@JApplication/White/StandardaB*[| " D@D*@HQ@Y@x < @ *2  W!LR?%?-?5 0C]]?%^?-`0 '?%*?-*)4W>%/l>-i>H!*%-jx0ݐ>%"'?-7Wz`0 8<%>-{@0#r=%H>-լ0)2%>-X 0W^>%??-q0\3%l?- /,43=%%>-O=0B<%1E>-0'Yv?% T?- H0B]?%2>?-,, 0C?%?-xH0#?%T>-0`t?%x?-N0@_?%~>-0*>?%>-xSA4?%>-<0Dl?%P>-f0q5I?%>-]04?%1=-$;H0>%Uh<-Eq00Ϋ3?%">-Ylb=0R>%.z|>- 0&>%4pH>-H0|Hn>%=- 0 ZX" AJLAA? $JXa$ *˖@2\WFHB\JT\,+a|?%`a?-MB\0>?%B?-W&)ؒ\0wp?%1?-S82JL\0{?%X>-SZ\JH\0|,?%>-ss2I BJD^\" A&0E4%o?-W}Z?2@$034%5 Q?- AnJ$A,4%0a?-,>5H2H$0 ?-#=5($6l$Ƙ)>-a:Jl0l?%FY>-d<.-EA:vB@  ?08v/4/ @ / ^A /^/@ 4v]p*? -! (08R y~6/6e6(>9jli - 6J 1>%S~;3A .;>% ""*(2:B4s  ]]  g  ! <$666B202E-F9BB-4CA4-B8B2-C09B05ADD8EC*28. P*#0 |9* " *!It:BJRZ` \\X*2S (08@R DCHCZ$1D1A26AB-D0D4-4674-AD7E-80CC02228B56hpxS!?.n 2Qbn0AB7B6E42-CF7F!IH9-A169-A57C900F8760nQn HC' *6A(a;!ca'*2!   2  X:ѢLB~W@W]H>A@AA@("   d:)AyH ^,zJE5*8!! 7 P:_@DcIJn. 2Mf884EB4F-BBF8-4151-852C-4DB4895C512DyM..n 2PbnBA1A3BE9-F28F-4BC3-A83C-7E3A3DA4A9A4hpxP6n2n 2Vbn0E2C0EAB-0289-4AB6-BEDD-244305D126C7hnV.nD?U3>*2 A(aVAa A-!!!  (X:聆~BJe:0#6@Ao1,BJ`.( 2WbC5344F4E-BD6B-4AF9-87B0-1EF74F4F713](W.6( 2Ubn@2C919C43-DFD8-462h8E5-0E9475036A46yU.n:T,#:T@3%Ts!T!s!Ts1T`:x,#:x@Ax9xA^:x!x`. 2Ob^TEAC8E065-FC41-4C9E-BE1!c 6985E8A3A=^O.^9 .n 2Nbn3B3216D6-99D0-42EE-A322-A904A5EE3BA8hpbN.nH> Р!I %a/%!ca aH! !    X:   (# P:Z= LLnjHwSoT .9 2Tb914812A21-B3EC-4E99-A290-837483ADE293hU9T.9 2x ,#:@uA*Y!<YaaY`6 2Lb7BC3A132-5906-46D7-8446-8DC5E222FA6DhL. .T 2Rbn@8A27A73C-2552-454 484-ABB63AFCD4ER.n:,#:T@5T:YA6YJȵY$` (L*  " 0Z$6AA3C7E4-6FL478-8A4B-6B4F1AE7443M2.a0Y*2bH@0099E750-43F4-4A1!H8C1-2076B752852Fѡ 6p 2p$ 26 C23C498C-ABA5-43CC-A04F-5B6C4CFCA2BEhM& .p DXT> PK&ER΢WIndex/MasterSlide.iwa L#* "*(0B$ Transitionnone?)*2RBlank^DҀ*   >  C 6D %l%@A-?0(8$h*b.=T . B_6BB >D< @<** (2 :Pr :$:$ B$ZD*C$2$=f$ "5$ *C6$*CZ$.$H*)$, Title Text500:0 6T)%0N!0D0=f0 "90 D60DZ0.0RPhoto - Horizontal MediaZLorem Ipsum DolorDonec quis nunc, %8$ ,D @ (C **   >  C6D !>%l%@A-?0(8$h*b.=T . B_6BB >D$ 4<** ! 2 :Pr -O(s*2 1"HB$@DrfD" " 5*8J!jtDDZ b j   5) Cf=8f "5 C6CN 6l 6lE@IlNDBrl2l =f " :lBN$.lH*Il, Title TextUxx:0 N0!=0B06%00:0BZ0$uo-1  BMC ZDC%Al%@A-?0(8$h*b.=T   _6C 0>D<dNBody Level One Two2hre2 Four.! Five* yT   / ?2(  /?: EPr7 :FC\C!F ֥=f "9 C2R$5<*%*1255$b$:$F$E B!$Ij*$=f$ "9$ *:$*CZ$.$H!$%$ v Text500  i'AC B:R:C 6D!Re%~2=fRȁ9R B6vBZR.R<*)R5FFPK&EFFIndex/MasterSlide-3.iwaB L#* "*(0B$ Transitionnone?)4*2: : 4RTitle & Subt h^Lorem Ipsum DolorDonec quis nunc(%*   >  C 6D %l%@A-?0(8$h*b.=T . B_6BB >D< @<** (2 :Pr   Q8  $ C C:Z 6Z C NDCZ2Z =jZ"9ZC6ZCZZ.ZH*)Z I Text5ff:0 N0=0B06N!mB0:BZ00I$a]-1  CB  D%Aal%@A-?0(8$h*b.=T H _:D 0D >D< @<** (2 :Pr   Q B8:8B %8ZD*C8=f8 "98 *C68*CZ8.8H!8%8, Title Text5DDB0R0Ih%0h=0h$Uha; )1  B~C $NDC%! l%@A-?0(8$h*b.=T H _:C 0C >D< 4H* ) Text* 42 :Pr :0:0C 6D 020 =j0%=0B60BZ0.0<*)05$$  ) :4 NdC%4EBd64 =j4"=4:4BZ4$Uda7)5@NBody Level One Two2hre2 Four.!Five*ML   / ?2(Q /?:Z B9DN DD6DZ:  *qPK&E4BIndex/Tables/DataList.iwaL .PK&EJ  Index/Slide1.iwa,lC>*2 "*(0B$ Transitionnone?)  @IaC $|DFC%%@A-?0(8"yIA D} }@<** c02 :Pr 6 $Af @(D06CpA D}26 PDDC@D CD|B. D}O*)kqueue5) es2A]Y 4D1 BD}S], File
I/O抌^6 qf \DA 2BND}y. …߶* ($64290E89-73B7-433D-A0C9-28492B2D87AB=5(* tB% t h,*ba=T . B_6BB >D.T!]$ Network .D^DA}*PM*b*}QD6*$DNS
Ops.UK*  ɐ !`}: 0(C:^:}L!ݱ UDP:} !X.fb}T.( User
cod 0* .c >*e@5^J3bTIA(* bC 68ibbb$5 L*%&5+N+J9 Z6ax-,  !N]&¹b}]&, event portsa (gbg}M.g Pipy2 &$A@?avC (I M D}Q. uv__io_t!g.& D M J D}6IOC,,  U4zCJD} , Thread Pool!a 6  B ZD*.=f" """ *C6" *CZ" 1< "RD^(i,%2Ps*2 >  ;B kD*D%%@A-?0(8"5*8JTtDDZ b j   C! .Z h*b.8T V Z:D 0D >DBH2 D0%HCH5 *-H@uD#!H bj %HRH A5>HCHC2HJHI0*  YB)JNDDJA. =j"]:R< @<** (2 A:Pr  m'-E D ([C  A 48D  :u 6uC 6DE;U 6u =ju%=uB2@J@$:u 6uu:$FA EZD*}_b+6=f$ "9$ *6dV$.$HA)$, Title TextU0:0Z0MCAT0060V0$uɁ EU@NBody Level One Two2hre2 Four.!Five* L   / ?2(* /?JZJB2 ѶDL!C45 *Ͷa b j ŶR @@@A'2ͶPK&E_;I I Index/Slide2.iwaE 8oE @*4!"( ##& #" !!#!&!#& &" "*(0B$ Transitionnone?)  @*C ³C CPB% %@A-?0(8h*b=T O  BJB 0B >D0!! ΈY*Run idle handles* 802 :Pr es - "B=":=.===""==U.=0 Poll for I/O9!9(=9Av( Mv 4CBYBv!*IfEVBM5\ﵪBPjA & [Bn  ( (! / K.( 5D - (- P! \ s (Y ((=L. End) (* Ya  ZD*C=f "+ *C6*C=2ZO$ ґ<*5/ 2#]ǡ= #9"'CW =2==f"I9" =##]TK! =No5$/./$*# {mvD!. A:>P 4e. = m5@M0 k CQ#.<}N%2&] & OD72DB CzV2=&&= a  y C0QAfB=B^B .%6B " Me{ v ". ]PCall close callback  a 6C 6%Cњ2=b$  L*  u2  * J  . az ,  F` >` az!.` \.` preparc 2c >@!BÑ@@@!.@_.@epending.>T #T !ܵCx y^T4=TT#TBT> .^E 2 =b  W.aduerNN> &* `XD_K SC?%:|=|& B >&2 C0 B K=>&H&+Ha* >  6 $HB!.V.&  
alive?=# V .*2H DHHH#.L.HY*xI.U& &Ք$DV)AjBV=V& b& &:*  6 Mo6&"; $ ұ<*uNZ .f*&{C$Ǿ!! !,Dn@Cr2=e&:>5>  55v5".5Z.check&   * . >*6Ns ^PK&ExIndex/MasterSlide-4.iwaN% * "*(0B$ Transitionnone?)H*2:::BRTitle, Bullets & Photo MediaZLorem Ipsum DolorMaecenas aliquam ma   CMC 9DC%%@A-?0(8"5*8J DDZ b j  A, C6Z h*b.8T A Z6C 0>D 0*  B BlZD*CJ!g=f " *6J< 4H* e$ Text* 42 !p:Pr :0 60@!$Iz%z60=f0"=0!Qj$.0  B\C $NDC%l%@A-?0(8$h*b.=T H _:C 0C >D$ @<** (2 :Pr R=$:$C 6D $6$ =j$%=$B6$BZ$=$ 6$$R=$FHAHMC!$ZDH=f$"=$6HVH$5$a %%@NBody Level One Two2hre2 Four.!Five*ML   / ?2(Q / ?YZ  iuB C:Z B5***C6*^QHI Text*L PK&EJ[Index/ViewState.iwad*( (   t*$%* D2Z(`m?u4/0 CB8DPDHCHnCB(  0 (08PK&Efdw5Index/MasterSlide-8.iwaN% * "*(0B$ Transitionnone?)H*2:::  VCHB @sD*"D%%@A-?0(8"5*8JTtDDZ b j    .Z h*b.8T ! Z:"D 0"D >D  %G4 0*  B5^ C^!{BCC6CN< pH* Title Text* 42 !:Pr :0N0C50B0060J050A-1  BC $NDB%(,@A-?0(8$h*b.=T H _:B 0B >D0 Q*–Johnny Appleseed* 2 : PPr >5 B5=5X5J5X5X:5X^5.5W!5-5,“Type a qA$ here.” 5;;Io(* .=BM%=$ZDCr2==fr"]rC2rVr$Ut IuMD2V.( Title Text50/:0:/C 6DET" /2/=jTE]T:lBZ0.0<*)05$TPK&EߺIndex/MasterSlide-7.iwaN% * "*(0B$ Transitionnone?)*2:RPhoto MediaD^(i  4 d*   >  B $NDD%l%@A-?0(8$h*b.=T H _:D 0D >D< @<** (2 :Pr  R(s*2 5"( D?""5*8J!j!j`DZ b j   9!<@f98b "1 @:@RBl 6l El ZD*Cl=b " *C6l*CZl]lH*El, Title TextUxxB0R0MCa=!00060V$uo )1%/l>-i>ZbaJ^J^ZA- * $.kp* j,ZXV%k4?%?- ?@ !~@2'!)%N:Pen)1* ! b  AbPK&Eo퐸Index/ThemeStylesheet.iwaF4jL*            A       - #<    F    7   F       # - #     #  P  (    A #(   _  #     7        (    7   (    (  (# 4 table-2-Style chart-0-series_34464  4(2-paragraphS(_4 h(Name-4-shapt@0>@$9 aP text-3-lists0-Lettered22!1.! "DB2" f3RDD4R"  DD5-valueaxis_19413.=5>p56<5-head2<3:`B40 note.x% $ . ~Column15]9,1:B !iacter--nullAl64FS#,stickyCommenA[2$ slide-10- g B 6j!BBB2 imag!}1S" e6Y-Labe!=7-26?46' Z ZBody Sma%c  lin^%7% %ZY0M}]4]) "|9>(Caption Bolff3+ $D17>ET~ %E -9B_!=B 7E#At2A]B^}ADo.oa9w4-body .l:N=f )fBulle'6}0I"=W]f!B8=B=;B!1!"B"TB"63M6XBH"Bh"B]nBCBBB1 movip% 1-2646% E&2>Subtit6E6E}ua]u-A2u_= B!:1םD%1Š]BeA A]B1y02:~36~!>pC :[2FDB!B=GBB="=ExA9A. category !:466686C.0hyperlink}Bc:32j 0F^B=4F@=BŒ%d-116}u!g{BV1!"B"& ]]9E-TZ Al}MFlA5t.Ki""FDF"&6:n2-=3FB4n !box!} % ]B=& 1" ] Bik>s2=)Ř W:*I2F("Q 1=z"R R16* #!KA6'-S]( Number" }g1F8"i 8҅v-16% 36ȽwB  !B x56]* U] =n)!:5FR]B1A:s ^ 6.==)AttribuP * 4:]B=IB!%A2&* w2:wVBw:J1x *&]0:`* 3u. 'I])4.Z:2 6:0F=f4ybI9 i5-legen]q 86݁" !]" =zBeV16; 36rA]B"͕$aW]B;56=F|B"=B?>=Lu=Bu]4B0[I4.a?ͥ;͒E9Yo4YoB.[" eH ZZio{=LV *5u=B!BD! ~0 -Non=B7`=S39S*7 %_1]\"D 5:X4~)n6eY%S4.SM# "1}) Quot!|}DB#!16 36puA#=o M56MBjB&9 =)iw y2YhhG( !%bB.sH :45& E b* =B{A}Q<2  Wood :  B  J  R< Z 2@b j z  . L      !0D  -j_C4 !u?0800`200 @@ r0BE8 - r0 `r0`@ 000r0Fr0FҺ0ں00i4_>%0:>-b>q@. RG!ZQR R0,>%^>-ʣ VR6 RH9H RH9H R R RV?%n-?Ef(fffxpAIVd@2 RVn R R)HVRHAY  n8@ -> 07L?%>-2Ivl^   2+*8  _1 |P4ZA*Helvetica-Light:B Ja(@PX`hu#} j88B +@ -K^f B x ,Tbe.%B(=y@HP] $jL?px &v W le4(   ',K%K A0 D@ Fill Center_? : "R->2G 1 8@J  A[A$ Direcal Key @?fR4(GdH  %RAg LBat!1 Lef =f-:( Medium  Top ebR?QA3:A3@A3 -  %I Highbht?P4Z,A*GillSans$?%?-Oo1$  & B{6F6)VX>ex?mA m Bm Cm/RuCr •&n9)T4ZQz  "&"!3%!RP Gray Paper :B  BB  JB  RB  Z0Hjv>%tv>-gvXbbjbzbbf Zbbbbb\"0=>%`>-%):{fJu.q VRH9HffVBbe$>%>-  ғV| $%Hv>- ΤV0->%ˠ>-B .Bz+12 :`2Bil)* "-0%N  V ( Green" :F  BF  JF  RF  Z 8mѐ=%v+?->5R J J J J J  =*  4j!=*=*=*R=* 0hX>%8>-hE6+ J VR6-  H9H f  f V Z Jm JB f  VR 0=%^?-g(>( N *5("."(*n($5(((1W. *B2 F^ *: * * *865& "5 555556565J &'6 65Z/*5@&5@&5@*5@&5@55555555555:50>%Ԧ>-R R ZR 0M?%W#?-sf -RRRR H9HR fR BHR fR VJ&5@5555%neD 5F5ED VRD D & %K?-D :D vN .5 ^ $*^D*IPZ4 Q0?%?-c%N4L4@@@4@L& "*6"MC" "6666V6 Br5"2  B2"2!2&!DL[?%>->X>5333JJJ!JJ     6ZA(    r) ? 6E & !f!JVRH9H f  f V B ɚJm00L?%6L?--LW!J! VR  p?-|{gN Rw+"mw+w+w+w+w+Nw+Nw+Nw+Nw+ 0>%sU>-y  J J J J J RJw+w+w+w+~w+& f J VR H9H f  f V Z Jm   *&*w+J  ,x>%>>-d́ڤ V  0?%G!?-%"[ X:X"z7 2 $ UW|7BmW\X>v7({7 ff> {7u (  V4* V)W Z(0^?%k^?-R_IH7( nP<Q>QQ>Q ?/ , 0XXX>kY("'J&k_RRq)RRRR'RRk_RR'RRk_RR_JKڐ RRBRRBRRBRRBRRBRRBR}o&K. AF^F42626gN26j A A Al N26B[lt .cBZ QJ52626OOO f+J)u&+0{>%>-P f+& , [ !*2:mt>k0&?%^>-^~kp  s,,M - master p mJp vZ|,J"| U.BCo1k,.:b6? :::.*TB:9PPPPs."t..FuVſ;;;&U * Nsr.L)0 Z ZX" f+a|?%-J4d? $$>?%>?-$a(? *˖@r.6!D1?"nh` @ ;Yb.4*P Z#" dB1Rsv$* 2 sDarkfs)X*$UJ>*J>$Uh*h,%8B&*h*h&*h-hKW " 4~- *vK~)/X>ej?, Am i mXWmh, 4&: z.qWBqWqWqWqW&r+ qWqWqWqWqWN+N+N+N++01>%8>-=? +J+J+J+J+J+++++V:6++f+JڕVR6IH9H:FBV6H ^5J Jm++++JV+R?+F+,>%>-́ڤtV9)Lu&Lu0>%^E?-R" +:+ G҇2*҇ Zvj*6n Tf @ " &  @ BɎ *"vW Hwc "v ~ &x:AC&')]&0D>%pH?-B< f&E rMFA^J9-.ye!wzwr A.FP{ .uu+uuu.Q. jB8in j:PPPfj3jj0Nh>%]>-?fj&P2&P&%ggggjg(Rich Metal&g"gEgEB$ Z" dG?%Z?-T$d?5>2@b$j$z$%%6 *>F[] [][][][]n& z 2l6F F&FZfZ:BN6 nZ0 &?%q-?-֣0ڠZH9HZfZZfZ9rZr94VJΐ.>4VLΤ4&40j+V?%H_?- cz4:4&- &- "\"- 6\6\6\6\Z\*\*\*\.\.\\\\\\\\\\\\B\7 &7  f 0 >%؈>-$hB VRH9H f  f Vk r  & HVn f  VR 0qb>%?-浪 B * *& "1GR*"GR"GRfJ*0x$?%*[>-,A?2*2b$*b$*b$.b%.b%:R&RE fE JE VR=H9HE fE ,$?%D>-ȭںE fE VE BE &Jqɟ.J- f- Y- V- - 0W?%?-b 2t6t&F6F6 7M6: PZ R4h=%?-=&6'n5@''?085 Zn5V<&B3 .$^<&J;<&##–#^$BV"vfl&*** r"i*8B*>nG:"#J&888A86&"%%%%%R%Yk$Marble k  k  k  k   Z.g<0W^>%??-q9Ag<,\3%l?- /s $Rg<b\j\j\j\z\j\]j]]n])m  )m  BF&&&&F0>%:>-:uW6}1fVΦ V%VR6)H9HffVrj''''VXfVR0?%,s?-O:&  O2O` J?h0w?%{?-tV1Bi<Jw < 8m7 z b``?~[&# o:  =0 "E +*=e%XPNV E$F7e 0&j8BMh2)U&g8M7RI  5bd*`jI ZQ IB I PZ^&;;. Z;0s?%w?-jvuken1p?1-y 9"&) *8 *AR+s+s+s+s+s+sN1GN1G6*   i0?.?% |0?-4im1GJ1GJ1GJ|J1GJ6 .+s+s+s+s~+s9&9fJƢVR) fVBb???F&Ff*V|?4,7.?%t0?-́?V??0_?%b?-GNgg 9>9*  5 C Alty "Z r:r&D"SGڮDuFuF&.DB]1DD&[&[&[I F.^GBDQ ."!^VJjM"!"!YYY.D > **~щZ fD..GV*_7 R.ZJ  HHXQr).)))))8! ZiEP.Yw0$9?%*9?-!9fl (8MAerbb ~[$0&8Rns&nnnnn:F,2F2F2F"҄:"*nJJJJJnnnnnr/&r/fJVRΑH9H(!f(!fV;r(!JmJf\3%P#l?- /,>5fff?@ -@2'%% N 0O \3%BR@R$? -d- 5'B@ >08,W4%;?-lʔ>*  shape-5-Dstyle*P Z ):4?%?- ?Φ!"j,C-,T?08*P Z#" 2 !N@% 0R  \ chart-5-paragraphS_8 4Z0A*GillSans:A0!EP9(@PX`hu} f<8Bo1$@ -K?A! B ,Tbe.%B(=y@HP] 8jL?px e'W le@=O=O3>O01O1OHA*Helvetica-LightV))z)Ax'e` Qxseries_1%!4 6 = A4@@L*line8"iA :aG?S*Q>Q 2 BrA( :2  B2  J2  R2 ZLW^>%??-q>5333?bbjbzbbfD      4     jS. ? S00f `200 @@ r04? -pIw4>%:>-:>я6~ RR RJul VR6 RH9H RH9H R R RVXJ ??-q Jm J6fffxpAI00L?%6L?--L ҃ RJ' R R VR 0?%,s?-Og 6 . * 0> 5s P4Z &  + . * 4   V P Gray Paper :B  BB  JB  RB  Z"0?%K?-VJ J J J J J     * 0 v e  0hX>%8>-h Z J VR H9H f  f V Z Jm $>%>- ΍  J   ,v?%A?-́ڤ VH&0@?%D?-Kg N  . 1> H J   DM - master slide  -8-7 Z k $?%?-e *Rp*  !stickyComment-0-Zu Y Z Z Zr: >jenr*P Z "pD?28@RU$* %>ps=W4Z @A* "" Neue Z6h_r_bZbB9I>4&b&, 6jJ u 4-legend%zZ *?7.CIPtableCell-5-footerRowI$Z @JR"2&3BR$  Wood, 2@b j z  .  Zf ffffn+"r0000r0Fr0FҺ0ں00&f0^>%>-" VR~fVr0_>%0:>-buΥVnpVRH&0q>%h ?--F  9j J   43-categoryaxis&0$9?%*9?-!95(8Me rb>b' ~[&2+ (%o: &?)& $(8!cd)   5  NameZ hapZR PZQV!3BI PZZ%*he + imag2^  )!% "+j J*"!t-t.*2&e_4)[n*J9***覂3! **J1r JJJJVJ( Green"K:F*F*F*Fb$*n0O:k#OJOJV *OOJ6O  O $*0OOOOJO(&(TfTJ!Ɛ-TVRL $*$*$*aTڋTfTVZ rTJmbbb&bJ bbbVRbb:$*mJ j6 / .OB$* OOOOO.V. BV .O.1> ,,,,,.:: C7!* .:&w!J*PZ^:A7`Rk8"-k8k8k8k8k8>k8>k8>k8>k8-4ݐ>%"'?-7Wz?.k8JGJGJGJGJk8k8k8k8k8JGVRGH9H-f-GfGVGZGJmGGG7&7GJGGGVRGG vC>%+?8+?F.) cNDBv1"H*^)~))~) q=*4> 9*"9^*J)m@P@ -GWDWDzWDAr jWDWDWDWDWD> > > > 90??%6>-,pl<3J J J J J $ԘJ>%J>- 6 3f3J VR6WDH9H f  f V Z Jm J B  VR 0Fr?%>-ӕg F  "3V &30-body`%^%Z47J f3f%(# ! NoneW acter--nullk.32&32S$"3333R3C$Rich Metal)C3B*3B3B3" 80eg?%C&C?-3 N ?2@b$j$z$%%$"C *C$ $$$Ҋ$j)J3s46s4B@ *iDs4s4s4s46s4F s4s4s4JB VR 0g?% C?-۩ f V B r5PPbP3&3VV%Y V 1%RW?-?9a%B%13 [ \value3 ?Y3&38[mz~3^B?~[3\8;3N[3=[" [[[[R[]6[U5A6  Q 6p Default Fill Center Bh4$4?ar?iA"R->e_GF8lB 7 AUA5L Directional Key @?fR4(GdH  %RP\ LB5x Lef =f-:( Medium  Top _bR?23:A3@A3 A %I Highbht??%?-=&"$ *˖@b\j\j\j\z\j\]j]]n])  'D  B'DҝKn0>%Ԧ>-uW6"&fv)f)Vέ'DVR6)H9H)f)fVr'Dj6*6*6*&pVXpfppVRpppd?-S/pBp 9 R7m"37m7m7m7m7m)*Lm ) ) )"3024F?%J?-WQS3J3J3J3J3J6^)7m7m7m7m7mm2&m2 f J! VRiMH9H f  f V B MJm   DM&DM J) ',-F?%ɦJ?-́ڤ V  Iq?%wx g" x> xw>N9%09%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9%9% +Agpg moviW*AZQrg6}j$.LB1.k$^LJ`XLS{{{k$N2v4'RthtttttRtRtRtRtZ43=%%>-O=m'J'J'J'J'J'tttttV,6&'f'J'VR60H9H'f''f'V6r0'Jm''''Jo&'f''VR''0=%Ɓ?-m^..fBs1M.RRRRR.*.K>Bs6<.)=)I[[[V3VVK? -ff h h h^VB@b .f.BV!VJR4-:&o4f&o0RP?%Q?-lRJ$K$VR$$cc %Tp\?-ǩgKFK *[6*3Z*0Nh>%]>-  . F" ؊؊B - Rpb)NpbpbpbpbpbĖ=bɀ2bŀ22.\:aarpb0 8<%>-{@iM@Zpb\:\2pb\:\2pb\:\6pb]:]6pb]:] pbpbpbpbpb0&0 f V- XVR H9H f  f Vr1ZjZ VP4γ XVX1  VRH;&;:/ X6Xr headF% *=2&n"xJxxxxxb2xb&b&bb&م0b#@?%?-Bdx@.xb$*xb$*xb$.xb%.xb%-<5" Bk&Dpn  <) Labeltext-6%-)*^=6*hX,%8*XX&X*.2.Y(B_[7B,,,,,AcoѴ&Ѵ2`E[))ZY7~0Dl?%P>-f7~@.|G$:Chalk2T۴ۡqN.nFF , ,`2BFil=B=B=B.4* - Pres4( Notesnot__V0 Avenir-Romanmj ? ˡ-A list}X>e`m-mAm mX Cr  .F4Jhh.h~j 3 "  (b-2-*b Z(,$C?%B?-= 0>%T>-ά  QQQQ ?/&%  O >%T>->5?@@ -@2'6=bQ>QQ>Q , 0Helvetica-Light\0x>%~>-t?R>RR>RRBRRBRRBRRBRRBRRBRRBRRBRRBR R,>%>-& RBR R|z u @> RBR  M - master slide style -4-(*PZ _ $?%?-_   ]\ chart-4-paragraphSb_14 Q44ZA*>: !)!"- j(@PX`hu} - :;d4BP@ ->08" B Dbe.%B(=m@HP] 8jL?px %W le@=W )W0>W2BVZVJV<8Bo1$@ -K?VVVUp* 6 Caption Red"text-1=ge -$1q EBV4q5I?%>-]<ښhA,%8]YD 'iu I2-legen)!Z i?*aF6e3~ V3 }}2>8Q6,A*GillSansN ..O1>OLfO..O3>O.}5>O0qqZJ~# . d[ !tableCell-1-footerRow )*Z4e 0}k?%k?-{ke ,@J @%.. B1F) ^ ?`?- ~n ) ) j   j0-headf0 8<%>-{@)>fQ-^ $Y-kColumnR00c>% ?-N~n^Bb  5:0zJ@f!O  P (4-valueaxis- ! ?"R 0R ?%6 ?- )$8 m z `0$9?%*9?-!9` ?~[&@%o:: B4A8! . & Linkacter- hyperlinkZX./ / 4) Free Form" Hdefault F4Z @A* " ZEhr"2 AM I$0-series_5%44 6 *C 4@@L*line8mMJM D2 Marble :2  B2  J2  R2( ZZX"90"?%ҷ>-l2u? $0>%P|>-$?!:,? *˖@b\j\j\j\z\j\]j]]n]D      =4rC ? /00n`200 @@ r0E` lD0_>%0:>-b"@.RVEVR6H9HH9HVJ jfxfffxpA= Vΐ% RVX R R)HVRH  nS@ ?' 03/?%z>-e" 2t.  7 Bol9& .$] P4vJq~ !H 0*  : -5-shap.Z )JY)« "js& Ms4?08*PZ2RRb NF "J~  + 222!W.1*1e_><&< 6.[ $5-category6?K,^9^(8Maer~&! ~[Q$! rw3Qwwwww>w>w>w>w2w4p?%(?-0=$! tw0=_?%>-$w\:\2w\:\2w\:\6w]:]6w]:]wwwww0=>%`>-%wwZwV-΋wVRwH9HwfwwfwVwVs6ZwwwwVKXwfwwVRww % ?-" w:w"9 2&2" RP Rich Metal :B  BB  JB  RB$ Z" &dt?%A,?-I.?5>2@b$j$z$%%    w ww wwwwjAA ? "r0@ 0r0 00r0F`r0Fr0F`Һ0ں0r0F  0>%:>-: Z JB" VR= H9H f ,?%9,?-㭊 f V B rVV- f- Y- V- - 09?%*^?-|b \6 &_9N .R_9W9-11`9&NBb4`9`9.!W   5 z.Name-1s.Z; PZQr&6O$ PZ^Q.:BF0::::f:"1Vj - F :*32e I ` 8^:g-<` ` ` ^34&!eM_aj0Z.Iʧ}6$ imag\E ZKJ!BE j*Jk""? ?.2&3BR$  Wood, 2@b j z  .  *S &S0  ----j)JS6SFSSSS6SF SSS0o?%>-"6 H$SVRcSSSSfSVrVnkfkkVRkk0ѢR?%b>-ik6kg ZA(T -0-*T  Z(20c?%e?-h/jH&kR0(=%2>- )-)QQ*QQ ?/*_&*_*_*_*_*_077_N_&_V56v5D0([>%?-F"6R'RRDD__RR___RR_RR O s/RR_RRA A.VB,$7*"^,$J:~A"VV*'z$a0'1.%*O$>.#a=nBd? -ffEډ^.`"`" movib%RIZc aeJB_"B Head@Es5"#*;5bE=":jE Z &$-0l?%FY>-d<z?3V6jEXDa0*P Z#" fE.{.B&AYJ|a0``..v[1B'F*v[v[b6 5Pz.W.B29^VJ/.^S(-0J[(v[(:[( !~NIJNINININIʥ[^Zn )notp list-i4X>e?maRmAmj mX Cr Icx>!( .*h: _1F^e(M:(M+:q.K0K0w?%{?-t A46v ^ N0 ^KbbBKb[K6K!cQ.P BOFt^F $h$h$h$h$h.rVF^V"E">oo+dJ 5 ^BC. A*Rq::8:8:8!V.S3SSSSSS![Q cLt1r3mr3r3r3r3Rr3T Green Paper :F  BF  JF  RF  Z\*L ?%??-?>5fff?bbjbzbbf%J>-S6V.bafaJuƨ,T2VR6H9HJY BV6 f` Jmb2b2b2$>%>- q1qaJFb2 qa`VRHqa&qa43?%,s?-Q?gWb26b2*~D n>w2-3BZXՉPP" Oh jV?a.C.MV?V?.$B{&A*:0E4%o?-W}*wO&~D 8 SNumber#D1]H%-.%I*wZ(Jwfw~~~~.z2z6  n?, A -333z7N0*Zzb*88Rdu``t2du?%XaX›fz![z.RB1FhRRhhh*z%7%a1.BiKKKKK.ee : T}$ Title 1$f*&f2&Bg^'gggVg.L)K3 )ESmall)I&q#-H%hA&y"pZDDN2Ikm!m!m!m!?^j@zZʇn*V6!"n%V.1B1111Z*F65CF n݁A*5# V~Br*~ &~ &~ &~ ~R8L?%L?-L?51JJJJJk   6O$    OOOvO? -T]Vx6ifJ.OVR6RH9HffVZJm00L?%6L?--L"sJVRH&0?%?-egF^^0.ZNx&NxV``Bb[* *q25J  em8>m8 r$al Shadow/7j NZ . .*N00b555R Body *y""```"BJ*)aiM.*1I..q24q28&8^q2b*6 Bq2b[j>~B 'fei:B 034%5 Q?- %==`= &oB .4.LB4LLLLL..B=B=777!W  b&5Bb&Zg (?.=.B# # b$q q q .W.BE82P4////j/.u" 38Bx & o^O6:O6E&'9<oT&&O ZhB>" .? )'  9 *Q$r9 1l"3 {l{l{l3 "3 (z3  N_1V__0!b?%`?-ݲZrV_0(=%0>-ǧ_QQ_QQ._VVHQQF*_*_*_*_*_*_*_*_*_*_*_*_*_7&7_RR%RR%RR%RRtARR%RRtARR_VR69_VR]&_#_RRp^_RR _RRi?F 3-s:[2 Vj[N# .*1&*A*⫯"__~_.$.i"BOlYlYlYlYSYW< * Lettered"cW->jWXeDZ?lWHNWCmHzlWlW >5>R!Rm3:_1m3^#0͎3%b?-,T"7f#!J2.D.)DD.0*  AzboxJC 2m01ڮ Z)v0a,8@R5o*Z18T8T.. F#Z?;dd!W 7R >>>>20{?%ؒ>-y@&^a"0/I?%p>-l"\:\2\:\2\:\6]:]6]:]8 S& S8f8V-6)VR8H9H,:]U2.- VrQT.VsZ888VJ;ΐ78VKXT88VRH8&8X>-Ɩ&/ T6T.*>XJ 2*7&#).*"K",)^ JtPPk*k*BT*d*.Kd*:+"K6nK. ++J8m"7Rx~x R`4-`````:F` R`R`R`E4B<%1E>-=sYEJEJEJEJEJ`````ί&ί f JB VR1 fH9H f  f V B IJmG G G 6`&6`G JEG G VRG G 0 =%>-KgG . R 4G BM.G  P4Z A*"-º"9n & 66aZ4zP0?%K?-V5nsPiDB.F 5"IS*r]bDDDh71W.5[B .TT%VƀƀƀAN83v80]?%e?-sfiV802%>-. 8QQ7QQ.8VV7QQF*8*8*8*8*8*8*8*8*8*8*8&*88J8RR%RRXRRXN%XN%2O - 5? @2'>C:RRBRRBRRBRRBRRBRRBRRBRRBRR  chart-3-paragraphStyle_5*P4ZA*Helvetica-Light:V0(@PX`hua(} -bk`8B +@ -K?08a B ,Tbe.%B(=y@HP] 8jL?px eW leT .)Vx[ !tableCell-4-headerRowStyle)WZ4 %4>%>-?n(@J @y@ %  k( l-5-d Z( g0m?%n?-n g< n@@Q>QQ>Q ?/ , 0>u4nʴR1 >RRBRRBRRNRHRRRRRRR'  1-qt? Default Fill Center y&?#??"R->2G18 , ApAA[P Directional Key @?fR4(GdH  %R LB1x Lef =f-:( Medium Top LrR?23:A3@A3B=  %I Highbht?hr<# ? AHB    p "movie-1-* "ZQr!* *N$ TFN$13hP*O$O$O$O$O$ZO$.W*%2>%01V%V9V $x%x%jx%&61)"&)9)&o1$&&&2 2* i2-series(4 6 *E' &4@@L*line8"i>i*Q>QT2 Rich Metal :B  BB  JB  RB$ Z" ,d >%.>-5>5?2*@b$j$z$%%D      *4 jK* C` K*00f* `200 @@ r0 ` r0 `r0k @ 0r0@ 0r0F`r0Fr0F`Һ0ں0r0F P0_>%0:>-ba32X+\~V0RJB+VR6H9HH,>%p>-V c. -c. fC fxZVL. RVV R RY)HVHK  n @ -> 0>%J ?-NAa2pzt1-  * 04-valueaxis_1~ a? 0R ?%6 ?- ΃82m!= z)0$9?%*9?-!9`3?~[&$7)%o:##>#P38![ .^B1"" ^^J4444. 2& 3 4Q R 8  Wood :  B  J  R< Z 2@b j z  .  *1 *1 0  1 1 1 1 1 1 F 1 1 1 1 61 F 1 $ԘJ>%J>-9BE O1 1 0>%>-1 VRA 1 H9H1 f1 1 f1 Vr1 f r1 b I I I VnI fI I VRI I ,| ?%0?->I 6I D?C6D?5ZD? n,?   $1-category > &h>? &? (8Ma@e r~F jD@ ~[¡ $jAD" @* 6 Attribu+ "text-14&sA-$m P4ZA*H6kBObliquerBhEB,%8*EBEB&EB,..C09=.H.OBV9Q"O^GaH$H~GeADD^BqOOOOO.##0/ Subtitle =c BB*>7j.]GV F]G2-body)(ZXG0f?%bd?-ٌWn,%&I0* Numbered%4-list) uX>eDZ?mNmHBm/mCz Cr  x>bl-V<*   )=4ZPZ 0( Italic +Z.%$  UnderS"(X"#*{{P B*GillSansb7O* C99Z1 "jD?08PZ8@. BolMMZ.# /  Red 'Ze4q5I?%>-]<(.RyZ m..QRZ&ub *)5V".~6 .0r..qpJenv?qAmXaa"0=CmXCPK&EW!Index/AnnotationAuthorStorage.iwa< PK&E0޸(%(%Index/Metadata.iwa$%g Uf(($6045C023-2B5A-4BC5-8F76-B18A1E552682 MasterSlide MasterSlide-7"*22222 !      E: : PZFѥZעԥqف呺5Z툑Â"ZঠjZϮĦZٝZŊԤO塯7Z㾘PZضơӣ^ZԁߝɝVZȾ*ϛ`j5FR--   1Q 1 1-%!ׯЧ翥Z鿰ʧd瞏IZ7\ȣÁ9ZРῳ|ZЌףáрZ!Xڰ{ֆZX燿ɄHZXÏ#š8ZXܦQٮÈ}ZAH⾲ѳAb\݅(ZnXďbބgZh^B`j8Fa-1^ )M1  q$: : E!Hً#ո!PՇƃՅՑ4ZεʧZY׉ҌZ!cHDāQ!HXٹG(ZḼǻ8!Pa܎'Z!}d0ZTL՛˓TdͶq`n1FV1q11qq-i!\ݱʚhZXհ̢AZ7HۥvПlߑϠGש{ZRX񋔞꛶/ZR\¹fZHߝΥ%nSXȀӧAZ½nޖݒ'`ZDocumentStylesheet"*2eCM   2 (P`n4Fl1b h1 112!FHϡ`aHǼ:‡H݈ҍ$͗a\ݠ⼤>ZaRXђpZXΨ2վQZLݖo\˺JZH󈛪ڶ8HϚԡ凍Ttɻȴʄf`  L 22 2)mV-q 8 1 0 `1q% pzЭ&敂㶥3Z#ҴɰΝ\!Ң!` ΰ٭9#@ ܎_ÿZ&擁ʈ监۫aR!ɥZ  Z"Ӄ'싙jZ&DР銠·!H*伅\(މ!\ݔ֥ćtZH͔ly֑^ϓZ"ڎΥoo\ͥZ!Lҹ댥ٳ9\ ԘVԽ!9L9rȃϲĠǹZ#Ņΰ[)Z!H܉ݝQSS\ŏȢZ!H젢!DƑ ɖˇAL߳qqDŎŽ`!ƣ듭VALģ¦Vp"·Ӿϒ=Zr\ǨɣBZ!P݌̸_鉵?Z;d٥ۥ|Z!miݦr7Z&ؘǤ ҥZ(Z ޡ톘㛪cZ!HܓrιaXֆָ͜5ZAKLօ󧦈ǯ֎!1!\Ѱ%ZXՖӰޤ7ΒGZHڐĥPЭ׽!pLœp9hƥ(璩ެ`:s*-102t\ : :+ HຌݥݼEL硢Hՠ!OXء…3Ϟխ0Z\쁒퐰ץ/Z LԍѠpHO\ݳ˼ǥZqHٯF! LɖˣϪU9\ǻ3ZHמնǮܜLȠɊȶr!SLӳ˩!7 h֠d韩=` 6 E<*  %   2UP HX"iQA  X a 81hÃȼ!^Do߳!I`99逦ۦZδɥ+:ZߐցZ΢W〱𺻉Zǁ፦8\أ=ZС9AcHI핀Ӱ!D㜤qHڛ ڨȋAa Xڥ͸{힢,ZoDqe_\àҊZ\ޗϯ昂ZLĒ!!H욲˨ Hç3ע9aCLӘpޱ܄vZ!HԔ/˘̾UL㝦Րː!7aDϷﰢ=ܢ5!Dϸt؇!\󸑶԰nZ!Dĺq\ѣrZHďˮϨ⣁x!QLܾΠ!5LآƁ!5:0pZ!QH▜3뻶!ܗ"ె_Z oZ݇ϨϤ峊nZޅ 堶Zn̅sZHތơTƣ䎺XъșA!\𒦚ˢcZс'Xܧ*¡Z標4庢` "Tables/DataList P`:2602.:rK >6PZD랓"h A.šСaHެԦjAg9쇡 L!adX͢ ֛4ZqHڡR蝤7!D㿆w񁉲7T󐤀[`!:6 6d 2p >}-Y> 1TҘSZTǴ羃ߕa9Hꔥ  * !1Hܮ:ǿ @ǠLΠ¡;"H붩ދIо!7XڴяOZ LۥسT :23ZQQbF-^<D3ǞALܜ!ADeѦ!macXˀ?ZSLɛ禤ǍS8X˫أaZ8HԔͪӺ8X٪ıVÔZ!LŅՍۋ! n6kJ^!Xɞަ¢ˆ&ZA\ӳұܢْdZAHŞ旦Kҳ!KSH镬m  SHŎ!/Hƒ࣯ﯚݣ pDá/ͥe HӘ׿!ȋ¤t`*AnnotationAuthorStoragey A n902A9:9Iw&WHџζ]!3\ۍ6Z!H؝y͟߁8腰0̡YZ!D헹ك a&L딃!8P˟(ǴZ!X`˒טņ.Z\Ꝃ٤gZoLܫ͗ɇAKLϗסԹaz 6Z>^s1ryv`׷Z%Z!tHʚ!<ӻxЭuZ–ƣ١ܤ%!=\ڢ:ZH칆ۥ!a2Dл؟Τ?!W`쐁崏᧜A7ǝ:`$CalculelEngin=xeeֽؕoN55rL2390_2880x1921-small87-11cXOSJwPQ{S) u!c2=c -79ohT=dG9~,cdcǦ1P}jgreen)#!a8PhotoPortfolio/>*l"KJ^ԧ PLk8@H A,med9iWh: l"RTqS>>)[MJ"~xay".qREfQ53v(016_981x654=65-14%t/êi&N_st) st0-3111hPEa>L|q?o= red)!v pred"3Rբ(r{u2sA75]7 -82`3QU2v[f~]>3545 -8 hy tVMͺҙa,M.? iv.d"R (Jj,.Ю6W2D blue azD)d"V)cQNg] ҪleI?2Myellow V$v +d"3SjXf^V/.h!\6=\ -83%d/-JEW07jqAI) st1-47d RO(՗ rw`?9z02{}vN7-1IN`OMQc 2񼜒[W>)Kb#d"3TQ,6*ĄakE!C7=C -8AVhg"V!ʄMk{Z a@n02purplM&z +d"} ɦAas]xV Hz6# 68*+,2:@PK>&EI.--Index/Document.iwaPK>&ER΢W]Index/MasterSlide.iwaPK>&EeLg  Index/MasterSlide-9.iwaPK>&ExAAIndex/MasterSlide-2.iwaPK>&EFFDIndex/MasterSlide-3.iwaPK>&E1pp Index/MasterSlide-11.iwaPK>&E))e%Index/MasterSlide-6.iwaPK>&E4B(Index/Tables/DataList.iwaPK>&EJ  )Index/Slide1.iwaPK>&E{d M1Index/MasterSlide-10.iwaPK>&E_;I I k6Index/Slide2.iwaPK>&Ex?Index/MasterSlide-4.iwaPK>&EY++EIndex/MasterSlide-1.iwaPK>&EJ[vHIndex/ViewState.iwaPK>&Efdw5TIIndex/MasterSlide-8.iwaPK>&ERMIndex/MasterSlide-5.iwaPK>&EߺQIndex/MasterSlide-7.iwaPK>&E_25UIndex/CalculationEngine.iwaPK>&EqۅVIndex/DocumentStylesheet.iwaPK>&Eo퐸]\Index/ThemeStylesheet.iwaPK>&EW!LIndex/AnnotationAuthorStorage.iwaPK>&E0޸(%(%Index/Metadata.iwaPKlibuv-v1.8.0/docs/src/static/diagrams.key/Metadata/000077500000000000000000000000001263361716600221465ustar00rootroot00000000000000libuv-v1.8.0/docs/src/static/diagrams.key/Metadata/BuildVersionHistory.plist000066400000000000000000000004231263361716600272110ustar00rootroot00000000000000 Template: White (2014-02-28 09:41) M6.2.2-1878-1 libuv-v1.8.0/docs/src/static/diagrams.key/Metadata/DocumentIdentifier000066400000000000000000000000441263361716600256500ustar00rootroot00000000000000F69E9CD9-EEF1-4223-9DA4-A1EA7FE112BAlibuv-v1.8.0/docs/src/static/diagrams.key/Metadata/Properties.plist000066400000000000000000000005241263361716600253600ustar00rootroot00000000000000bplist00 XrevisionVlocale\documentUUID[versionUUID[isMultiPage_fileFormatVersionYshareUUID_'0::6045C023-2B5A-4BC5-8F76-B18A1E552682Uen_NL_$F69E9CD9-EEF1-4223-9DA4-A1EA7FE112BA_$6045C023-2B5A-4BC5-8F76-B18A1E552682 U1.2.0_$F69E9CD9-EEF1-4223-9DA4-A1EA7FE112BA '4@L`jlibuv-v1.8.0/docs/src/static/diagrams.key/preview-micro.jpg000066400000000000000000000026211263361716600237210ustar00rootroot00000000000000JFIF@ExifMM*i-"C      C  "-" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?}2xIo@Bft;i#2,_a`qx>Χ_f(O?k}I€.QŢ_g ֵ Y\~gƿ{P)E| PԱ<4Cr|NT ":}:NmE%!!))zR|~>%DoCwP5C=׋<2*.m ?:!W:gW/h{VW>r|CI;[7óP~#BHOa6u!NV$\N1 N_]#㏊i"> ݬW$TV0/cڸ={Neᕞkqqx-|\Ip?7Q^_7 3oo`Ӯ{I$EhU6c@=b%()y^KZ="t鼾Rw^[]۟fh6$7kJ/w@ft{U F3]g]cW[ų] fh_z;xF>weSӦ@_I؛oiO]v{D7 8[q c+0Y+V4"/k:Ἧ.N2YZ)+7c(B((libuv-v1.8.0/docs/src/static/diagrams.key/preview-web.jpg000066400000000000000000000176521263361716600233770ustar00rootroot00000000000000JFIF@ExifMM*iC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((((((((((((((((Z?i+_,Ӯ5csiK+7VYǦKkD#XK,<6xm_GkLw%ov=9IPdh%A#'i(O2>p?k~ [Oᕽ{ߘ (I$mʠ3ײZ|*CF@]&y/&s>rɹԋp?+w~hgnc^GMnȴtW27Gn=n2T>A??65̷ fh"rkwEGy52T`gV<h` Й2[99Cy7:jd͠. Ϋt5~hI=OtW2~fB̚c]G*yo/SokH&$g?5>Q=Hcv:z+E߄x05"W)ntW2t59X0rOnZ- FSծ?@5^|#ѡV#=Z4g0̘5kZ7^FS91*GNp2I>#u5|?\l+&xtNW:M?"L>1{OT{ &!4_t`^Q}/OI&|?oɨ~}|R}(j*G ~o%|_y Ҁ%8[oj)rva>߷C@L%RQ ڙ(ooV'e#4QReFm'H#w\@y㋝x0D_.Eg"n6#3pO75qJm՛þX6hƦ_*0jLv,Kw`ֵ.W9?}6fTӢGEe>;_`J7_T|/o+3Hf%I|l(!7!v'KVKyRHe]ru $G߭WDӬhu=>PKVS ȦO4 Z?"?kLwsG4N2VGmfhM:)YOE \H[tgJ𳴟 i[jmIcx0e`Xrkp+Mf5?γ"?ޮ[յ]:UxZ:[Zχ[w ~ ЛFzr;j0n۴3AۆB> XM+՝ PbU̘}p򽿏~9jz8WҬ14{yDnn| .jIHqI>53wꯟpFHZ5_j? LlFbҼ['?Fp |9kVqpG,-@o08u\%qњn卛W?5j*~l*sj_|I,t3S)odH! [{ŷ09O|(] |4=4QVu"{{n5EZ6]XPpAhwY\H^_;Y#\ eCᏇ| YZ;Fkh F쁁@KRYXǮ9U66靜 )TMݢ+|KrݹN:73~:ԭڳ럴jg)UX=+u_ pGH@@%skc'-+4_ XteϘ@q W&%=̍gxKy1; 2qks."sSwZzɆR:Z]B_|7$Ԧ5"YʊR\hޟI_C#+iنr~οg7&ݢ+޾| ?go'^ -f繚 Is#[6Ss67 itV n6j\ww~^x/GӼ{~&idE7!<GG uObT>) q:AVUm`s XQxCPOռYeHB ̙ t皪??.ϯ٢+|Km56nSNƺ|LH/n2klsvS0 ~kV2j O=#e,{zZ 6߳k xj RVy$ l (0MJ{Zu,Ĥ^LoqLz9'RWJm5cNI=C \`a:v5C-{;=BiKnń(Rڹ73z^J?_۴_E}O ~V _-Khܩ/#dnTUAޣ=7wOk  {G$w^(E{/}qk>|$>nү.e)6|o'ΑP;n6񐥊̀21o k1#,ؕ$t8 ג-۰ mtI,I+O\y\c}TPpGC_+~m!c1!FP.6ABI/٢W |ZZ\!"1ZSa=gxZJ\G$gk+BA,Yao:jV"Ys\ Z *t…)A$iVJE)'6"vVLT ԓҾR]-U^iS{ai#AKľ#c 30Gڄ3W0zn}wWU[IWx<3au/IHh}G˂~L5Pcu'_|hX~ƴc,K?8(_^b vVSZk]s2|>&(|Nٻ|M;a6|~Ȟ3t |do[+"9,0 K􍈤nR\g>%?g,zö3H!mCXd|gjn8{3Kmd%$ qo,*<қjtѻĘyb1Ns?;6[?  &k#E,ۮAF]LlygO?]֧<sld.Mֻ31~ʈU1K5tbf~ OFK_4tyVG#,#Vf$Id6]W,^֭գ3Җm_%KEnTW˧x[^=/L|%qxSL6Aq"Ʋ#4̧i`H#ZſU5!F[D^Ef1D́e s$ƛ) OM?ңBGTԕ:1=R qhvk\ۯݹn~q"}6gLC.?&k<^FB H6l0;c9 BAǨiIPnav[zO,GioOL'?` so-@pDl`H9O•@ҭG\$;~o7h[$O]#Z^Ue:һRk_D~)VUsJtt"W?8ඌ-A>d8e_ |94o{`yp!2cjc[ x7sBΧ#|e z b$#ƩcVQiҊrcG|y/~,̦H-n~_<hV68ݠ*1L8s/EXzuPM.VX淞ܰBK/g|֧??<;\ҾKn]N-6Ȯ/g&~fNm(.]uӴt= S2np+67o%q0D=0'$rF?`ik+ 5>7x#Qv+I0|d~Szג%/`y0ȫVݻCx3b{73kKCZ᷁/C˩a{ɢysR#vvj_χ |Ass?,Lm146rǶ;? eh-/#8?Ï'υ2ӼG߇1PѮ^ U Lbd\Jjmo+<\S$ӝkm-|ϕ?C-I_ XkZLY;ᑣaT#5S k>5x?77:ZYMo:&2.@8=Ldג?G")Ÿr¿r^.`04գ.h;$]qaWBpM]K+n8.n&XP$K$(' w_kC>G_-}J~7 +Ǘ:8yU?,>*y݈|keTө9ɦ{&|o7e2:xRW5qW'_%|-5m.obSM}[#_|H򌲆 Y&{{?bnX.gh…:0}*.SR)FWr `ך{?G/U"?~qPG/S_#zu?G>"mc1Z_E%Iln88U__ֿb_=s/qp:O#G"?~8qKTƿ/2E*1ς7W'…C9ƕj:X|>~?e7x7$[{IoټyfB W{t9iuS-6T~U]S,úUZw/ ل18XEAGI;F5(O ( ( ( ( ( ( ( ( ( ( (?libuv-v1.8.0/docs/src/static/diagrams.key/preview.jpg000066400000000000000000003217001263361716600226140ustar00rootroot00000000000000JFIF@ExifMM*iC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((#>7J?Νd;] x ]7`ڽ /w eSWP2)D >7eO,mw(Ω{84R}.=A=ꕒ/Wc :j |{m.|ٛ@1?Op:vͽÒ=7¢vk{Vn7:cEoAdx.Mk?5=r-? 9:x<31egǯLmr񼯰(*>C_؊2t_/}gy'r$T?}E>v 'Vk]{CˆxZ~<'V99-$B_eK*>L"[ў5kh?#TS {a=_ -܌p@M}E|?&ċ |j!@T~q?c T# /_^QO*x Cy $SOvU?'/˲o$B\%`R״RcGֿKF2jZHNJVgR0L-عQmZO܌P~[&o/#X~>xHy_^G;T|5~ ihZD> ^/;#ŲF7d&_\QK*>< 餸~?xKG{75?>=[Y.7WhÕ#,-ExPtA}4/#.dECxs+*^B+J)s_7(xP5y@_@j(~:_F\Sk)r&L~;xF'-\RM:?lr^ Ur{ }OȓK}t6I&"4O'&??Grq+z(aʏ/`:|9Bxs#˃TCS]̃k裝*>-ӿ_m|[y/ W%P'G{QqEMEt@WQÕ/ K|?Knwu_ji$?4.vtQÕA*)9 l;Yax,9u` n>Oqt}E>v ;JE/&eH/YK?K}^!{Ig?}69r_'_QGjML|E%yi,~rO KS㖳qFylՍ I}ǟX7Z*|9QW;3^$^8fS]./5&mnq 9k(aʏt&?lW/!&ngf -A]zl}-W7_`O*>EsI+iD:,It^?r>?k)sG0|qb|x+璍kp ?ʠ>?!Z8!BТ;T|qu_|WGO+^[dZO6#pRcGr1#w6Z9ynvg/|q/_$ m>_f+)r=c 㫙6E:F0 7wGVvv0ʩt6G¾;T|eqjF㏈Mg8?OߊXWgWt"\}[퀟}cE9QOۓv4~jIwYNs$=y>(Õ0|fј>9kd8nAQ3V+Zߌ&$?*>A?˧О0A1\M ~W[#ōqCvtq\9QԿL\y|}KLs!kkrj^_X?#x>?}E>v.퍮DxK21eAYZi/6ɒ+[\RV6\9QuղFx΃[Fbۺ0I3?__G;T|JLOW!Vg t>57~"_f`w ~WSaʏtO'=" PN }w^0?W◈O'n\$%Qea߭,|K{}چ*|T@1[c?lĢ;*>, { x~7i& r2=s/? lh*>>='?P\#~:oS7l+)sGMo:\_#qx=.o%Ǐxݬ?}_־բ;T|ioeXy}V`/ NCxr.=~ʏW{|A} [ArT~R%ߎ^W#П_hQO[4>dk{^ 9#~:x?| +J+(aʏ/2N/>=xheN?t+cBG!G/F+)sG~YjLJ;{CM1&k9?mQÕ/|}kz>8kc2(nwζev|J-ZXe-0r۽7?:9r=/ ]|)6N%ᶸm5?~(?h8ncC|~5sG~gQYnw$Bk?_nWfT@S r- K1~о0R0R;Z?hh?Ooz-K $Sw^0?}[E.v>T|O'/)?xԜw qP8oc.|vK\d-ߡP"Ңv.T|wg09ϊf./T-?&q럵uk:)GWL@|}񌉏^ [ G4F@x>Ar;T|k| ?fx&5ƙ|-:$,`m=eL`gixR@#OW??׼*x[[_Ɯ,4BuɓZkQEAGLdhboy;[dlVA8??ZH~71)f:g{nMt_=ɟ5xQČ~Grfi5Km67 @k!AxqgiڕVjWM;[E!`@'\"W_Կ]\l?Q%*Uq': uxGSF?<;[YZ[DZ6bG PKO_L~ [ k:Ɲ<]LRQG%DxZ_>*?<m,9s1boC^;ir06=+5'14}vCf~O>F>:xǁs⟌no {dfIkՕ$z_|}[=W'H4S$1 p =} Ǿ/Gt$KhoZ>| ՊcnCjr_?_Olm/Pfә<٢O8Qe_7]ٷ0KY_hZPeI"yc,%GB2;5jnyJj#Q_ǿڛ 񵏁|IEcvֺl0nА~s!FLeTi0_(TU$D4GOaf:FTk+k!֟ %q?i{Y >Yf19Y,( +Á4NPPo6\jIq_c  Z5<3D6i<5wr[̊pNqz\?oMk-C\z=ljE}hq3&u$0/^<ʚtZ1Ғ}aE~}$n~ e+ kLM3\`3Lv<x_U)v0{t (,(((((((((((((((((((((((((((((((((((((((((((((((((((((+ۋB]S _ulsʺLW5_~Zף_J;pLŠ(SFedQcu܋ LWe?ʙzqx?c+R$*7># K1?G_d|5*ȕ/mW?%?-edr_ Rvs SG_&JG>KG_^:o__IE7?m~fs#/7H/$ ?6?HQ? >xįxgFtZʣ*Ƥ⿠MO>[xFխ#Xno5-22p_y$@L~~ A? ~0xSKڏMW<"dn{gf?~dV1uXj^mr- l!T QVrqQ֮O,d 𱙴K9<,m#_P:2\uo}~XgvKUGqJ$g4FS{f[Z*."9P5''i[mΨt9;L?1[3_=yU~^9Ҿڟ uE:m^V  7d{W H};_񟏵KM?MXwgwl(}9sl*(E](lpez3wzΕW}r%@mW=FS\E~%)one5 'pGuZ]B+4R)GR= V2-mmzVM6_4:쬖>;4¤>DL} ?l`~Zol&g="8Nws?ꦾk\ ᧊RhkdF ]tg6KY&''!>XPvyEF^_C_?:-_ k\2ۑ.{|]}T5/xvI kvZƒ QDP=]kUj9-z-iB^}^ (5 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( 3h߷k!YdVom?LH]B3n)l}ET ( ws->XGך4ouk~WOy(>y} k~>8wu խ] #;i'_d|>$yo:s+/BY7locD>#~_|yeDxwkQ?ȑ:ͼ tS7J3[ go G p]lX[ohG\ r_Q/ _SF}:+??\H̉VD<15%-ބe͔ƷUo?=5i^59kaF\o).IK6T>+'i:t+p.|?s-IGwX:%E 20O'_S?a!_?KdoYmCj|㍉a ~Om:IgLڪ) :.0;U*ju֝?g.t? j!_5/Q:|4}՝_l"~X vV2(S*EO_Y|Z.[Vo%[dW.\TSZPI=}B56އO?_>,iwi5h4| gg۵~QE)B4K6!qS_KZDh/_bכW[o,+&X>\#WC:3Nֺmix9$mfȕ|3Ӛ)Q*q Ur-o(̰((((((((((((((((((((((((((((((((((((((((((((((((((((((o[mB<W=^Cʾį?oi?nρ+ Y|?ʮ[iE<# ann?bb5 ZO(N}37_P6yv\w"~5oJH*?hz((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((nֆ/W~iWkY׵c.n|]N9F=VoE\7>¢*x/*sF?ZF#x49 G/KՅ()[[4>`5~}L~F~dzQPPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW{kȅRO}q_WROK<5v4ovF{`QE=?ʷaxe|jΔYH?_'ETQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWs"~<5 &_ruBկm 5> y:άqKc(gJ&e_JGs?SH؟_)bČvNuN{i?_'L-30u,i=ϥe#.g!=AFnF(j{UQb4~ڞmOʳڞgyyؠ GS:;j{UQb4~ڞmOʳڞgyyؠ GS:;j{UQb4~ڞmOʳڞgyyؠ GS:;j{UQb4~ڞmOʳڞgyyؠ GS:;j{UQb4~ڞmOʳڞgyyؠ GS:;j{UQb4F8'KX.ᆫ:]VsQ?~(5@3+j=Jt7_SYwܜ}PB8Q;4yj{w<r{T}=;uh=*Xc?~3ź<ɧޥ*"iZUWR%!P'0_M|/iqx~msĶ0Zmf;yP::C#>r['g}Ki=ں35φ?o_H|M;/!du϶Epܬ@fUlAJ>*G{|S_!_qM~ 2ߝwp߳3ړ 6iڵs+Zlb""F i-]_kCz+OOgyz;J`nIFi/V_{hvm77JGyzo;m-~<{~6A;-"S+!FAA^kiMK}+Y,o$_Mdo`2czTJc To݋i2NRifmzϝw=Z-~~'~ZNbthԵ{J66U7̊#PJY_"ڭeTm#imw3Ǚ޴-'c8hFki+wK{y5 K GPFմW×K'oj70;[k*v::/ Sv5}P55t[5Sϩ(sGjG@hMTߝw}[5SϠ ~w4yަyyo;O?ޏ>-z;-z;-z;-Oy}VCTT>W~zAȢQ@/zlN 35MR|OI2SQz:;O?ޏ>-yަyyoΣ5SϠO|m55,oȮP~RWr: IbYM!Os)>\We'WYL?f}}[3{zUO>>-*G@yޕSϣϠ fozwK^jpd|65Kϩ۵ddI@=s/M`."tY8Dmj;ޏ:ߝGU yoΪz<{UC=}[꦳{H"b<~,f4ꇉeݥcCR;|` }C KV[ ̄,L"Wז+ۚ =,7\~*@jḥQP3ÿZio,;XOMl_l<綖|/)YtuR37OWsAuBdv[nn)꧝V>(1oYuAGGLاO׭lo5X[\+WGFee$z\ $G}%|OGOT,c#9AV̎$$U^ k.h؃ojo*/a 5 ޾M5x +X[̒'̊Oy 2N~Ƌki%$q䥷^E&9EUggV'~Nxoë?埊wn#z{RBO+eIfn|OoR<xsE|A /.n0+vyxw/O}/xGFY<IJVchLlvW?)$)`A~Ɵψ~_ ?ࠚl~ ~jg,ujQ^D$&E-#= 6P4TGemvxҳrwZR-}u@5|^?jvR|U ?(\kD&m1V-~6O?h?65k=ny ;{9Q e"w@ϋ~V$ФMOO6f 1$gv1y}|ޕkM%]I[T⚍H齽6Wq}Su_ Mdo Yk[A]GPN[G?rpưC2Fc*B@:¿ >O_ ?i [㭿-I(_}>"j?Bs u'G룘k mfر &q* / .??P"Za]Q$1yvoWRM_.: V"^=:z4|e8g/9x?ÕWǟ^"֥as.= ƋrKǖ;F|kxQR{]iV|#𷆿\9->'\4w7yMΧJp'd)E[֝<֩(ڄb}^uuUkΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@<{P::ykΣΪwuZ󪯝G@~[M;ދwݬ=z(tP)B?OOqgy?K/:-yyWΠ ^uuUםGU|z<QU_;ޏ:< Ӓt+&wH%z0($VJ ˋ G5VJ \Z=.fэ@ul_;ޏ6-yU|z<PfUhםA5W͠ ^u^v۵=`̟)+_V??ZP[\Ӎ5kKLJϥZQU_;ޏ;QU_;ޏ;QU_;ޏ;QU_;ޏ;U=zMx;ުnQMы@o>:&?n3 Ey}o>N?&B߰MNjḞ֔QE@$/M5V}5qE$g:V+?#🆗c"\VkMnVݷ#8$ __[ÓŴoIڥ1f쯚f&0IbqlyyԖ/>|4cVH-'ÖP}d8!HWΣΡhV{ kxB|;j^!躥݄S^tmgu/++FE?;6NxJJ+pF) ,n+2Auu]{W|1h94kFխ.mo!u*K#) $Mot^VhzEZuZYB 0F0yyWo x]_x{C5? \y}aL[iKBIRA W?΍)x⦷kZ5^/nyDH㴈ƫQ69 15uu m{Om_p=RO7\Ykv^Y"aղH$x9W ,&?ghݿo"F뇗n+?::/ubM6EӠѭഴbc (ª@jGCw fj[G.쐑mpՊ[kQE< f/x;k36:l8qlO[}JNw%Z8ċ߰{?W'? \k!ҏ<\xߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-yަy ߝGj@-oòou? VOW'&CQ@( +я7iwIE=r}[U< 3e|`%B ]i MYFޭiql,mʁ*0s&I]by_([]~n]|g & =&85H:rpB!5R*kgs֪yyZOCԛW#[}+,5C5ox|2/hCΪh?i# tEx Ww9S}5$od[؈Snw%woLQ{[i/şsyy_`_υ%[pk-^[ \g}i F:8c e׿zWO|~?XX"-YjPj%Zy . z<@"(VL>6xn}[7 熴yNd܂V0ʶ\IStv뺍o$(/iR/}~NW[{WJw-gY5x/G,$2LWN)+co>!"m'5k҅%G ˻i# ZZUko[};wu[w;ԏ6°|F-v5q8Gы@GUW闝_|AxŞl4O 7%l"N"cll߻=~ZέNRK~3I?|6d߂4MIZmޡ+K;txșlIsUMK෋_ho:-<9wi>4F2K-Jc.\`/1ѾUg3ne&Zucy(AERaU:3?i+7l#>jះqquc T$0++qxk7湯ThNϝEK$gk?Gszk>wj{ynmxFM/?t>n$^eơ~=.Ы$VJLjڀ?Y? x_Կo1F~8wguB{-IXވdW5Nd ~-V7ſ3(<030bk/R8ss_|?[ *"dOe|6:ShǥzğWt ?D~&)7}wÿ ?í*<{wiٯ &t u )H{yivS!Q'6Yvm^?#~{t_QI%=u~7)mg$?0 /e'u&c%tT,t;<`a{ēxIumjַi{eŮ]Kjo2 nh~ߟΑv~z5tma5?kׇh2k?F_7{Ÿ'JK}z<[Yj{=7I6RvV,r6ѹr9OmF<[[Eߙ6nis[|NݛnZZMwx~ Kυ?7F,׋/^[r1# #;7=_gĿvVsoiqIlYe~S#+[@> |+zcR_xTԮML6=Ů%xRYCQOث_b[? |sYx;Omv=$lI$`;A bfsvpQVVwmhzߝ]&kYuz7úP?+Yq<}C:<x-Isj  W $^V7_>ո|_}ؾ[Z捬ր#"eN=_?~&~^.ӼW*oOE%b1a>fOBBAeg~UOםxݶwd^kW~J4Qw^omZwfx/ ⯀~4ws|nu ^Q͝k#`,lwe(ā9dQ7^_L>#Geo p[Id)G%א@}WSk!'Þ]ƙڑ֡-و$xGT7n?I-=~ƾ=,>0~ztnR d'a dDi:[G|.WZ5RQHNMڞ]2W=[<+LKோ_ho:-<9wi>4F2K-Jcg'h_w 5h~V&u ҠԎfMnYB$1o  N~_M`x|V ? jWFX 꿳/ծ|>?xMƛWvCk$=VeȪd6$HQ?)YkʝiIvmVRO~7YjfIh1T|Nvl3-B6+Dkx|S`-O_yPA_ߺ| ~XZ3+d:dE'ٴQE@!>DpNiOMK 5䶖~jjMF$06lW V*5k[ՙU'~yy޵/xvZ.cRhVT` +Gίify%xSRX[k@E铎I5I7rX.39I+7_&m{ t}>~IUP0N΢鎘*c t}>~)b#^3O*鑏w?`t}: %x,`w?EΨc@<oy?~ܟ_ΏG$`OcI0"GO_?z8V0.X7d1ȯA7Ux6Oj1CM+Kg7B;dab+:5M~7d&^?n~KV6HDR u1Qoy?~ܟ_ΏG*;U)uS KpPƌܓqGO_?r}:>w_ο-a4$+b0>L(SK/'ېupps_KPIٵ+lo QߦYN;[޲#Mjߍ3ɤ;Kuwy_OhG›oaM±fW d)2V='ίJtfY KT[j\?/ڗ$oPuu:j6 ,.Pi )#'oni I$;,ͻq?թ*_dקJ~?n_6pyozS'>b' m]o4t}:@@<*r1p>v0x Qoy?>ܟ_ΏG!֨.D$D98N/lؿ~?n_3P"c)!$N/lؿ~}?*+p\6rzq=kW_]]4{DW2BGԩ~tmO&vΣ?Qߌ^$uPQ 2ȠY@Px|fy\WJ~hО|ܷVÙ7xkƶSr: (ߴmM(vm^yW~ +jO:-yW:+&P*H?jyr8W:*oW5OT;/:m^U)nv:G־aaxIz߉g[m;F{c}u+,QEh؟@O_ |77O~>%o|{OOڴ}Kix{6tA`âQ~TZ|ީ(BjTcZZ'vʒ=3dfu_43ҮydA/h7]т3'nY.yfߖ$# ߌ|QYZYo`3Ir;#cEs9GWj?;mW]/}캣?::u :?Iƺou? WἛsC{($VJMޤ?O_DI~&w~q|>#c_ڶVԾ|!\ #T,}QXZ8Jr,WFߒM/ͤ{j ǙnMfgeѮixeYL5d~oݟM]'W|a[_ZnNn]zu2\YY1|RN6Opya>7o6)H٧~o ;$o4XM{ ,ik4{٥mz֚4nki?TAFhz'xwM'Ưx#U_ɦM,n_ %u=6Pʺ7E TbIznmZw][Ջ{Y/軽)W< :A/ xX^LRWt,# qg@}׉ϊ~u̖5[˥<|Jl|X;HzdeJM_j]}zW}yguXq'-S]׺hgiwE[ 3[m<9xsE_K{ok:)@6nnbwBWdB>;?q5f~w]%f}^oVtywj'+";h粑&e#WR2#=j&rg['N {PEq"? B}|XZ|dHDf̲W ϯ袊!Dس`Hq]Z'>6->.G\řboy@vn@cA?_ƯMWהyꟴm݉ 9Ww>YҎ.t6?fHه+b[f[/#X=g~2Rxm&&Ǚϗp2WW.G%6DDq^P\1i7# 22Ӓ8's_b{")V] N9Q8?xV|Bo&jפNB!"YVcl'(y(EVs nӒ^8ٳ̢3FBCu!9V6P{ cş/-(x_ĞB=(8,")qy5hNF|) dRT+/\Ll)L49M$XRZ?p+8#qZC\-(bߍGtmVm|ܛ_W&6cvk(ұU"Z,$9N1voSΣf?܀9*F4G$2pjK>}Q\vxf'SGBVTT w.zsA9M=QC6$g88Rm2qԬyGrxrL$_;隔Ua'c*Vǵ}a{_Fv- k/5_g1QVK>G;w-Ks6oAƟpin3%<+_s?=rߵ'$>E޽Nx7/GVPY~IA!8>簨"su0}*@awGJa  ǭ~} ,<: cpz8*iꪪ<˞?M%Zj6[Lcq.EVd60i9$l/ry uj{o}p|>MjmZNR7MYmFglGϿ8TEx;!ܻOcTu@)Y3S '>c>\ii1Ŝ,m2>GֿIɖ_Y9vθ*8ȵ+g*e5E{uMAs&`Ǐ7i_j]%R:#ԓ޼Tp[9 cWiEwx硝"*=q¿cF4_tRTdF#%Y#Tx83G7a3*} s܆njFH6@<'F]W׭v>#|SUeK2~եW̊2ᑜ涿>1>O%Ce,E(;Ji?TGP`WI{VTc^b/wcz"URk:+I|e(?ĭ` m_rpqOh:{X22v&聍0*Ĕ@}IA>w474 4r$CB0r k M Zϋ|=i>>X-%u (JEjmw.v{O1Ϋͽ.[:R?bx<ޠ`#08*A8k% :=-Akqe:g Lh>%ALO5Ova$?~䓟u3X`'ϋIѝ3OϭwSIQ/_M^i)5[x' CWr-@7=Z%73_3ƿ2sxqO=pSQ{:^O%GV=0FJ$G#?E@'?Q* XFr8$o1gFxio-b~`{ԛF2 k?cޟ E͕I=xjXgeda ApEDF^qDn~j@`W["5*_f+Kw#p[hH{oc]ʘ s1ф1}J !XI})$mَX97ϰ^7-cWH eqݱp<@@M.[:N|;c?Zb`93c?>_Fi|EXZPd@xeD/;޿-yŵ~ |Ok_!"难|BM\]Πƪu\OJϿ,g|]#bJÇ⥘ROfyrO LEwv:F6zcOYW8YG thT~}X³nBzJfc ԰n O8rHsUW'i|ҙpp>;=wқ$@>j O@zPau.FwF5^֕Ҙnto##0_LWN}ML ~ %X' ggχ9k8*e9J~fDqh?Uy-*>A ^[W~mϠ fj\ɿJZ]ϟT)7Qĝ'N״bY=U`CQN>'&rT窽$ŒQKWק,OKdzwԛdmj}J4uc_ en4˝689?%?j? IԾoj7!FiھOӴ^hzD keacmi*G 5J^VKmoȶm_-Ro󪧟GHGWM<ӿ? GA_;|% ?A5H($VJ[ouAvğ_/]*G=ί?>~?G/så:4X&>_摩9wc+&K>j77_GAK3XjZs֋ob3l#}v~ǚ@n4{s76Co B]yv$%^;𗅯5"A$WwK-5K8KHFI.V|E?owkN;UΡn`A%F;pv23~Wƚ=Re,i]_GJ41CǘU) aYL&0n׼3t9{PCEy<1CK:to|e𵭎ךF 'vb7r;1;wnlڴR7x/wNmrk[7--w[ku^YIwsŞ_"Om#[Kw&(8]~5Ov ݵbX.-Ybdu$0>/tE/  ƸX3,sy,>~~6ev>']~)EP|A6j7^mȮ\l˨#pS_%]ii/b:d%p܆!7w햡rח]$qs;7'gODK{KηZj:vvnl bXL1f, f%A{8{N[v[]s|ѧr;۫^g5> ]Go{--:a[fge$W}`|7qxX\7&POXDZ~WSm D{%vGkoxGOt ž6Zc~{$Ф291G)c?.IϑxW6x?:U M-7G+&Dk~ ӥ9F2Z+Ќ%n[n:{iG;*|4u{~-/mzՃO"Zī43IJA$ IC'o;+Cr5_ZVTGշߑ(*pEc Gg|bY#Qʾ׷PA_~Z\?'I$ȓS %&oЭ(nT>Ȣ*xg?أ䥘ۃfؚ߲g n xtm5M2?by_.E&^`O_'?jGS׋ײVgS׈yud9nz76rW xcןM׊8Λp3ȹYz{u|!*=៩]r;$pOH41pt`x$.I&OL`i amKַ6z߀OD,ٌOm=T,d2Y:|>%kK/Z_k.&I]5ub A &b?5@J5m<{יӄsJ)jr*u(N S?ik? \ZT:֡-ۮZ=fm+_k|]O9. QYs[[ "[¤7;d_H?!o9""[ Sa Ԭ32X o>4x[>_-Ֆ[#k?8Gr1w7;cg/v]|sKŚ#QO($l -ݸlێյٟ/p_5 /=׍.M~ܔhuy||k37@N}Ru""#! T0GJN*SXXYۛksz/lcNңP$>.ם_)C|) ]vN/7yLTڽ{j^R|Ayu_;fv$sI5QwxCki7{_m^J^GJdܬ6} xw <̱X)8s;ʜ2}묳>x8Fà3-?o*Ř }{oSǟ5~E~57SE֫ɳk'-e1ocŎ N8YG)ms%$DclӤW'MPs7Ք~C>BW pS?๻GYA~ۡJ9qFG #/fx1ms۽خc)cAx:F+IS'ey}g,ɠFyE?hCI$^T|w'a 6jlF5<<)~ό1T|?e?%Wƽ_w߉^jP4DV%ʄ<2퉛T_d|G=<<)f폍vt5V:fӇM(JsNHJ=o/2kOWOw,.mUoiQ$&vFRmV_*>'i<^/'- Hm])،f6d_~_<ךENMX}S'!sCe??I~4isO"y3p 8VM՚¥zW4K\VNmRIǙ[ekۿ; x 욬8qJ@v(5~G7מ¿<~r>s72Ysu)ft~vj Fo y_,yG Y><2YXW' *x`:Mw`dF~uK8\4ц2x7z¿#(>e@HJrFqQ;f~N@֦Nሎ+åG$o.J(vhvi 8te)3-+[lp8O^=,_K/o&9?-;Zyq\qzmvbI}`.ij1)V}'ɠvFڇgcI$|@񾻤>~cw7VےPr9\8|_(oxD&xW}/5˫I %܆VFt#+Јo}Q8G57+ḀOӎTN ܐ迚 0JqVW ۳6'IGyu":ͷ71i2Éd?)P~Pש[|r3E<+Ѽ-G+k8AƓhb0QGW&itxUR)0G0<4,^@pWx|]vW}ǂq^.;1x6;w6B9RmS9`W9?ҘHp7 :½ FǛrw`ϤÚ)Ͽ^Ƒ~ u3,\9ofGM})|jMJ_;޿!>8]fogye̿]w?Y:Ms5r|cc#:|?:=OO2KdCO _9 BNH)F6r3IW?Jn3n?' _QyҼA?~ 8tjծI1o|f/?ō$o.J(v 6'|gύA.ᮑw-bgC!g^NeNoX(o\ׇ,|S𦥩Y'坋_>X $$pX7A>#_@x~ /:p"/"F[.O.xcmk'ԗŚLku^w"ۣ;U$I 0xPk~êxH4Fvڋ, =fWZ2yE'E+nkmqR5Jr'e'_ ŸwWm:YKB{ xĹuOYl~ӠQÏ^qoj6OspDX`lW1c 4-sWu_5WUkKkh@TK*} 88__>-^m[׭6t<60&' "I#W Uyբ-JU}}:ӄN2u6Z{?mS;'74? O 4CM{-OK Ǚhk͵|cf28>(??:?7}a6|1y_b;U3detdO\c;Hs++UBrV7MIggمѵ/[iGd QLhBŎy~`c g$S>zA[1~ӷRf9h&W?}/j h7׊?ߟ>g5;E;ֿ?? ??Wf2x¥YT ]kaK<?ɟ<(?;=1Ȥ1G*Ob'<BR'͏WG+"srIR)!Q?S̭{du|S)7[9s la Կxཷ~'SxLVd˕/mdt 8e*%]|)'k=wQf9P ҒAI<%٧4 , >(B r1@ a&Hx K5U~# 0q~xJoykx\i?wuUٮl!Ҿ$ϤAEm%@opyعEU!F2| >嚺ɜ#QZJWh]}5?'ּQ{̩lX?,JH)[|TmtKV YxrN#% $޼ILqH'GJ?ZaMAS\pI4t?o/-FԓյY|(ecTǺݮ/nEPXp3ûa(9k8A8=XsnJ"RV*VrWIw6Hԍ['Se9#-T`X2V17&X8>ױ_cQ?w(z$kk`YWs~~0/iۣ] S}Hg?_.j槍ItOo>o6u]j?;=;-$QC'~Dǿѵ9X?qI"6x>W_FSqwo `ߥy[ Xmn.(ʿ#R![88? i9g9x<ѵC`0/Qc מlG"aavN+S=~&O|@ֵjoENKʨ)B͵@YJ,G}ivW?ZN) ks__u߆zÝkW G}Ig:\0Pyh^F$3K+ T?˿Jlp'C+ߨ%mQx'ME] Z1E}$vw  vHUFpzV2)1lFqSp ,)ӊ|O0GG $]A$* ׭LFT3M\GՌ($Pc0XrNsLª''zW~LGa<W#0oˍ{{%muͮ`sFRj #%oOJ<ꪲ&:#><]fo\3vzV.;ԏa*K)5^xf|(Kt[=p+Jj8Y/#2E m}pwG1SYRkt)x,soZ#]jc׶id,nRTeNAk<ڀtӰ ,)ӊѧnW-[-oGh {hoXmX Q5´J>zp;O)Tc9\GՌ($UB).X$IEYl8.7g9_’W Hc0XrNsMF`߼;p'M)%yb0x_oWw5aikh)!HZ_ +.&>ItI~O[s_]dvO|>s^v{V \]x{ImgNb1M:f9wޚ3IG}`>=td@ px=3yD s8>X6۫(?Tϗn9G3xcO3qJIw+6بPh3`ҵO_kW֣N[.wƼ =+%6`69b3*a ^&Յ⿉FWYFM?OK.lrwƹW ; ⯈$查[!-MP,  Dh0j +Ɍ[[ ?jD |u 4$lϑ >GB*ɻ$XzH~W}=:0Hd`_?;7`u? M̌|\qSf庌z*tLw 8zW_~-GRgjI CP ~0r&xY̓p^&?nl?J|{wīҾ,AEt3zQUO:;7uTM[3zQUO:;7uTM[3zV7xMW9VÉwxLP+G;tb>Z9l_[O?o 9Ub~Ё\7>ʢ*x_TOaKSfvퟲ?vcOKOLuO?}o[߰<KY?fdKsO)Ww} `6߇K!_7.NC _~ٿ̾$7؇ۙ}L=ϯ}Se2ֺp*fiV s~C? ~°пo| _VG[}/x#WFL}qcx/[bJZ=LW_+ a>lQ-53m cٻ! 2Yqs }GO_0|8-4VlYa[HlWpQUl֭HX~nP P0+[~u/_2ye:7h %^ @CGqu\ҕYКM٭PJ/T~N{gÍA=eF% W4֡b0N+SޟF)~ g:<Ÿ)7Tz*%S?%O55ibxE~?JCnfAG]L~x__4/@U_3T~J7SH?tI0^(o$E~°пo| _VG[}/dF\Mˊk'ZSLy?¿ZXh_/+ o>fʏU6n}'SQ/ul 'S g ?+@R°пo| ?}?g ׉tIZ|3wSR*J'6'oΣT_:l:(R%N?AS #766p*ha>4/_{*?%_ӕ٤jjg(YLg-dx֟VK B}([ϥٟ#ROkLAQfkiwNJja>BCG[}/ ?e_QaNfBGgg-;~\vV ^_frðN촕ƟoXWDWgXѩWE88ژ%\jKWq|h~ȶeGZes5~u@浣ZxyݤeBr%(Q+O*&v[kI f@ KZdj`͈=R𦛫}~_}=>.F)NoV9Dg?[ 4}TtAl1__4/@QKa?G!q66p*GFvis B})Xh_}o3T~J}\JYON-dOkLA__4/@QKa?G^i5&Ť| egZ!e>`<ٮQS}d 8WO`VZiI ʷg}ȞoVJI_N MXW.痕Vcܓq]T_:^sI9Iݳʔ:,(xFKIb :_:m;l~dm?g'Y hQ$;JCr8ɥ_oK9:~U B})Xh_}o3D~KGiXҵ,ЋW)? X.d [H3WO+ a>l&| Hs<*4K=2l'WG+ a>l'fŤdzI3ʴ|=g~.Au+dAR W+>ߏ\ӼM8ca(i"aا _[ndڠry!xB@M\")u|+SQժ&yujʴ-rnn?1^qW~/f}X: (㿵<k| Π ~v{U?:Π ~v{U?:Π ~v{U?:Π ~v{U?:ΠBdF֯GA_'ogJto|sMuOM}'Wd[@uoZRysΠU6-WA]M-'W%'6c2B}|k5^ޞ ,[[QEZ|0VZx2958ܤzKJc0}^}९})|3/C@%>t˙SMԯP:m\)A~yޏ:?xM+@im)%e*zW|5?j>?p;\ O_PXY&hۨTAj>? +Kol d`@% )*zҙhٞV}gk'wjNU=f3GipC[34oYY_r4l/hoo&4z ՘u/GhOYY_r4WRp_s /;޻@lAv`vʹ x^C.U cE<}Y+ tL|i#?@kc}k7o ׳6#ǣ5 |Uzlo09}[0C[jEPA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| ?V&>PA4Ed ϔM'|k@i?(I(?ZPG OA| U6" +ZMqi =PEPM%q*(2eӖcܨV&>PA4Ed ϔM'|k@+m)NEpUM6 k =QEQEZIfDA| ֢2?Q&>P A4 ϔz(#M'|Km 6[ZB;E5#X#juPEP: =>š}TV&>PA4Ed ϔM'|k@i?(A(?ZPk-O#f(?orC'?J8"A<-`0g=zg? #Ag?FG.џ@8ܩ,fO'HpKts%9cJ݂B"Ai? #Ag?G? 2|'os6ŧ\YB^?ZeʙG?׬?X'. GUIPpt Uy:k rH4Gz?#%"~4Q? ?/D!_$lg:Fœڧ NX#=Xg?CG?׬?X' ’h?3םHptyLsY_ێsҙ,T(?~ ^aȟ?g_ e, $gS) FMǍQc<K'*G5f*K|%N=&ӣB0|M&c(GRshi7;E?~ ^aȟG 2Fr¨@[;z>+sbGY1N|Q1rXG?׬?X'pdJ'*#?p#mBʿ9#3yB8n~}8rĂN;4Gz?#%"~!+}ЪSKB&OF?ο8vp^N?jurB}j? #Ag?GxFi*V |~n* *x V];UQ=(X1GKDp; sg<9? OU_GF|ݒIX(X1GKDO89q{T/#1'(?x󄧚?zC篧ӊ`B\;q8?~ ^aȟ?b|&WF~DpWg@ OU49Q!Y˴Z? #Ag?H$$qtJO~nP?wu>Ǹ 䠐'Hn}(X1GKD$8X n􊜿plRG%UI`T~Gw2Hb}Gҏ?YOS#8T?(!*G262?MgNs֏?YO+8I] ?>Ś#ɆPC ?_qQdtb1'Tax럩=hX1GKD;_DcO_uM.=8ϖC?)D"#m:ȇ 28`:Ѓ_j7ߧϭ}?Ngkq0м[rVj呜\gB3 ^YUhĻŻ^5t>x,T(f Ni^4މd}<ۊ) Uף Oن 9k|IػEyrl,08eU?ట{$֞7+-D̅&+ uM%|@֜Ř69'іK>?~\ͻj/>+t=3%]h 3^|) !0 q =7e!["7)Ϋ`{)/h */>W_-_џCq1֕2g?عCgAU?9W'ByF smIyD7B}Yt=4ǵ|ݼB%@@iZg\yMعCgAU?9_h?Ny:hj:[+?zh󶯛BTa sJᶠPszuh?? ϳ~p0Z'6}0=v7XUO۽~k߆x^YO1ȠaÐk26'ؙ8<ׯ?ߴ%@Kt_Ü+!ltܲz#l-l4`8inX eT0tһզNâ h ӫ#$(p+(O/Iu/-FɮvHA.yl߷ǹf{ž*} <;D;#ҿ]_PYVyr+,9$I'־cz4]-oyzxs3cUAGI[NX~cM:9VC1d 1X?|-h:g?-_/mO朊*Xr3_,""&HдmC #A?Ve ݒqP#OHg^a>#_ZfOJpG~>#)dF0GQY P,C0L~?WB {`}fޥKȣp牒mH_Oni?'bVrG?:폲Յ~Ѯv'q0ewiYqQ_0VrGMψH̏bh7ĩ 3ipiHRp~MpЎ?x/ 9?Rp#~Kf`p:-,XB5>$; 0ھ^Ҍ2'?HAs9_G3KO@T\?xbiQ|F 7~eX>D 0Zbퟲ!L,\XKR@(OY M/BH|`:YjDrIZg5|ҿ6pFG5+fKO??g'*_.}&s䏉qtÏڣ_,;<3hk| ػ!lucWK녓T4_0VrG_??|<մOZm )vIjz9?o Ln`jZ=ā')IÀ:`_D" sUI|\| ܌˼哸,%Xy~#XQka9=O@ߏ~s#w jH1뜜fm4Aޘ;`З?3q`~]ޞ‹,Mjȥ3GO2ID% ~c3֒KH܁G@|iPa7rWq߿zlsXNGbP$F9NM/ 0"=0l=(p1Kc~A,J ؄@uۥ!mg=qPDP8m=;R+0H>2@;nhwdmLڀ#r_-`|{/Қb1X#!LyhVfgK;(NshcpnW,Sxg1o#'xr  IJ̣?>ǐvEr#njFfy?RF';cƀap?+pP$3N;{{*yݥ09=U̎sׯzu\_GHe6F9~5_OԴ M5J$%\E< qTN;UR[:W_Ujz [.n@cs};~|t>cx#{m,y?^oO5o'S 剼MWG #|K FHc?⯄>'|C#=b+]W֭oWVZH^7nw)1b0M|`hԣ z8>+T~#[xY9{7 $nQ%k0Tݜt'oWk/6}a6uN) 봫ыR(צƙ_mXԳe5̊*m1Ԑ>jwKŶi;JFeF(Z?_ |pmm'BQ#n7LGi wJZx?Lrķ̲S,v$À1Z u00-VkDg2aeZMӨo57y5ǐx?M.]CQ`7ڤKMy8j~>%m?Wm m:]L"05ـ(*?y$K>\4[#z_N\[rJ#xOJ #}E }i }cS[쿳t%k}p#]~3Q0C)plǵ=nm /~N?U nlt\ψSp<9"D^;X,Nwvcrd pW;XyDZWax)P#'kgd?Z._#a v8N2X9Dr0>ދ嶓>q@Qc< _J oxJG% rGӏCo<ASո)˰c!_vs*aGhYUcc\><c\rgޕ9'F]q; I # 88d%|JIJ{=?JzݶdI>U#?ր##;-%?u~8o O8}3W/ʞcgW#[῍U/GGkUm\Ut> !@#)De#&V?ry<2r@> k! n?X#?m/Fe`|`)0~@\ HDJz:5ĎSn8)C t!S^>7#j2(#*4;"aH,}2{T[HJa ($+ 0K`$$%Xn ,Hқqyht@)BSQ,X^ PF#) VS M۸@ !<Қoyn؊Vy fb3}yS;@9{qր4 -،c&RWx3S$ : rTr[o(VM0ĚUAQ@{{IsT,04%3׽v߳ qJxGdvrrqpsMY -WPř9ҶӏրCOb1Zc 98Vu^Qm^@Bb_`?w<&f!|I-qL(3Jlʕ%bc<z,/<ٙpFŵK3wzǒqIE}jpcUTgH([(sӸ 3ٲ?zza+ƘX }>䍙Ѹ\`R@IcT-F (I"[y8$. 9l7_jiL,<1œ+q?)#GTڼ|< vOϯ2XNCJC(` 9ಜ֦? Hys1MFYU$/>tv%=)^,1ҋEd`n7sN >F$u>]ز1;! Fq!=OdH{$'1(hbMaG‰U#ǜ[mC֦M cc:wڿU'€?|!_7~؋in{##MW ?k IcedBOi_s_E bMcYaBd^*/nۦŅ<#93ЏczqSW%f.l<gb̿A%O_yߏjx Jpc|5BQ?OJlf2yXoGv/|T%Gɑ1Ó+}HC^Gڷ'$ !YK/[S<dGv/|T%G9H=lG4ed>IϿ~k)03R=0?$?T\ 8w2_i@? 劥wNyۑO&ݑ.PplWC|P_qlGS|^~ib'C;>e *#ˡR߼7'su?Az d@8WSMAjDR/YI%nrsA_?3OK%v?zyszRs=OO\JO؇c>Ԏxx#EOn#EϷߣ;>e *#Mpק⧓ rn9^߱'Yq+~Av%}~ؿ/`f SSF2mV>$RҕVEvǷz ̑_؋9'D|gb̿A%O_yq@-Hp!$3޽Ab_Oj\>JbIoj[2l9ϙ/4JӁrW#=iŔÝ'/Q_؏$jH%NOأ27R dK:?_ ?/<X}?:TO^ -cyq[}ePgB76i>*.J/QpGGDا⓰ȿ\7Jږ0æBjIXL{s^36ۥ;FNp@ϣ`,~ h|0 >sOʸibϊǃo?̈'ؿⷒ ~ԃap/HϪ-R;2<!A8?ywl1*+h~_С>["O_P|g<'JܴeH]otvc_U0R5ut'.x'ͩ&j.x`kc/=SBes,hiIf* $,WibŜV3~?&9$X}(;Q̈պx˞)MFNކV^'V[)rKϯd_ '2|w}w.u(4Ÿ/{8+f@CoFS/Vh0P^O-<a~x~(H>ԗwH@?j'C((EוȩZjpM4is' |6[p=Ԇ{ "OGv/|T%G[ ˩S?K9\wZQT_LH!|P߱'YP 9Ltgb̿A%O_yt(1r7R$-6>d99Q^'G ,zXMO؋ QW,8??_ ?/e *#˗lGhn<jط_j/ZhI>ɩ+ BB!-靠}Mlx; [ŚVˋ%sT,J%y;lf[|I_QNWG]?seY&"xάbۥⱔi8SM+7ml^#"3U@*mO༨_PoBy=?qq_@~z?8סw~RV08${_G~co9^ju,a?i Y83 rAUXY4kJ QSde'.t+5£S eZ;n+TQ)F su+g\~7.}+E08~$%%X ۔+hAp'g cp7_4%gnW̥˿ )QrqSQ\.<.[-Oez# rZOH'Ï1Xm ׬xc&L0[{U*[?XU7a$`lɲE'p D$b0AgL~_'\I & %=s](o;AM;#ڣloOR4C:Unh?cUt?O?,?eߴ~-t*3}yhCe)%C\ RGR5G+'0%P)+ge vp My#~A%2v_~>A4(1`:bybFMfBʈ1,ރ~ xZX p{QP۸JjzIWz"y {PYvu  d`McsK/H&M{ S} -)r269_OG$cOykǜPKbT1G#݇3Z6ykK[m 14zUrI 'l. s \׶7-fb6;d;9 %_]hh ռ\+TVw5h;G^u!7z^f6(T -'+UBnH6Y[FZtd˕zzbFF.HG 9 Q:>P Ͽ^joڿ>֧cr.%ӤDO.i֑FZ5*x~G\>W>AhrZ]c.y%Pbi IS8裗ԭϚ)Iv,7䢤bdܒ/CPn9I 9_zzvq\W<~U߶w7  .4-fa( nGWxǂZ%NiM-dQԨp :S+4ѣhciSFiiJ>ifPLJN$9Sx{k]2Se`用v$z%Ծ#z{F).-Ȩr2gFueE|N֥xRRr oCʃRC`rAn; ۛv4~~0?.u#@+i$<w"); 8^]=zEхz kѫ|ɉHH ?Bj$?BO+8_pn4YF:ԛ a~; Z89+/\Oz$Xgӝƀ!5 :NAR! p}A ts:" F8M)P[m5PݷoCƍ+(A9g'J=!_7~ڶ~?ZI%"? ?koe{­ݮn3(gACl_؁#O'dDO%\?*IbbA fE?tTkBA`8>rOM_' ?࡟dO_\jZt4Y&HXG@qWnX~_K?-@Nq!ڄr\M~xu[N;oWcK~?9yuRW^n1d<|2Nno?8bi {`?3Ufrt':gC`` I ~P?GgmO$WK}nj2?hu-nK?(Է,0 9l~G_yA,?g"w_A*?8&IT=Eʁ~o,J3?YOѡJ@h@8,fOqɅI|\@?Ml8H m L4Gz?#%"~A2_2^ ?Ct8%L&_ό~rNבQgC2"zX1GKD_8A3F·\JǰGqWсMl'r~`?yA,?_8AqO#o`t8>B- F@>'_(H^wa?*d!bQc<Dp-'?3<TBʿ7c]{Sg(ۆ-p:xc< O0|`Ƴ]0m$_m?ZW  -5M.15մXCXpkǁFF:_i?lKGƫo%1amdX2tH=}jԫ8o{kF# &cf줕ok+4R#P<ּٟ;:Z}C]<5[OF|)vqWn]eZ%AmFBGp.Gp xY. %}v޶OэK+JW<7NEpC3>>(>jdgրHo?־U?~~#7Y,?}>r2?c81?Ld2y~plۇðrO?õ-/ [QcGKD*9p 6d^:tGz?#%"~|"x:6|-B93F/y6H|iB՞FUVbOua+ȁFF:_hkG?$1aod JtH=}jԫ8o{kN# &ai))$M{igK_f~Zڧ H[@'$O2-#NA5Sќ5><Uۧ_gyּKl#STtO{;Y_'?N7Ac'*"ݼa+ pO ? #Ag?F)? @>ƣƏǷ&%s55 /:3c<98z$3~cC>1u3;^D!G+'M ȋ #gA`?yA,?a/J:p*3_"C+c;F6HCK??YO =<`Q? 1~p!y݄/u'aG?׬?X'²2\t? P{s *u7vMPnIG?׬?X'O/?Oišτb]p5+xډ j{~6>"bZ6iZb[kYDLA@w@##I?ߵㅿ.E?:EO_I n<0zcE$9M_D0CI<_??x#ov?mM2pޛ,96A?ww$㜎⤎\p؟ 67i&،u_Tm+#ǡ31aVng sw~R3d%ZX\?I<9f ~M]{={W/9?kqijQl #@B2G'zm+32|K1&om(ZXZ1Ψ:S@#s|> JoٟUfr%ah6$!mi|-4gWľ)!\8#2 6r # Tv+.nn:#uXqF=Sxw>ON?60ZtUw$2W#xggCGx|I  Cc+<g (*M4I8&?nҹ?#KQ ?cUV>,j@J#ŵpÓ_]V1h:cG~d1mrnnz@^(g4+Ek_ p$=1ZBnpGupTJuqUgZҬo.en[Of(OW$I|׵n?/h2x_~CelCFPETveܠ ]_EGP>8~ξ4Yxt5--e&6$*猒Kq_[]G,W~%^ψmnt{Jr0.*` 6)~̞)M?[^M?m[jekï4Ú ♭&j>mi#mۘo2bV[f/~ϊZ.~+ ]Gx,嶑K7>B3.9*UeoGƿjc1s@*&Љ^CSgmG->*о&iPz4}Nt]sQbK6# vLʢksu\IF&%}xZ#*͹*{X:M՛RQZ4״k~6S_-Ra \k$yyQ c&P_T~_'][Oӯ֞;XDQKr*d=k/?jsZo GxryAyK㒫0oG gᗃ?g kӾ 9aq";"8g 31J)). =m&',N]|Zҳnn_onmG`?_ { suW\^էJ+^02r2[5៰WZ|)jN-q% mev$N8Ϝ|B>3ַى`T2Ē3±ѣ2sz{}''u':Tx4VvoDooO |s lt *~yFvful&v'j@|AΥ4/x~+Hp8* ?[h,|g߉|E[]jomeܹU,Yya+>-'#:]W"[k!% !Hʩʀ*5gW䒔o濻Od_VZ8VCJ^FWO㗈O ~QxsXWMrhP8D)ډ0"?|{/9bO i[b8⶗Xp(SL$ ^wڣ4Om-3Vt[h>$C+c)8!B I_}_o| 췢kkPxX+,ghKdgr SUeRuS9c^g)UorpXΣ[+J/54zuK୷G?oT-A%Q־s7*B6WܟtW<oYizS[E~rwnc<<^S8CrA^VR&jSTVafB@~f$ exVq:Gi'r fzE0- g#<6Gc}*Q9@'p.E#2ǍF&M;pO<$_.:Eʣ'\ ~U$ +ǥ+MxbsJ]6 A@*-z"&f`T$ӊJ_ ?o?_PC.٫OOtWn|>DmJ>Ƣ*xwZ^7cQ'{I~>62DL?P? ?boom~ߊ]sŋ?"JO>m\97b'#f-_˂U~wx_%O(+1F }k~%}~?K1ѮAPrpF?ZzFɔ∛vO 4%C rB)s+>Y"kdUfSן .Gc,r!(`sH6$R8׿z j,/ i7:ڴ΋$('SI!.o*"BITfEor'?J̒4/+h z`p c [W5N]q P`UxZrq얤!N8'1/ Xf k5K?aZjG]Y.DKs *K Lg}_o|![JO_<$|@P@OZ评4Wu:+‹4RߤKgl rG?QU@s*BB:s鎒1.r$W1A%Au4?*'B6fM#G 8~ V*x8WCI<^#71=p2T*1eӽw?[i[῍UGGIfcժ_^) ;\W囸xrbA_])['Z@?cf(װ?~wĿx#y#Z51NOHY2Qn~adHE?.|[ 22G(mr86Yc DZ~) j7?1o?;~ݻ8c{ִʬg8wuͣ)4''wJ[1J$1dg𤷛˔NG?ኒ6fl)t8v$&{ĵc QYٳRv(qy#[p܂ S-a|:)BǨ銈Blvbl*D٢`cⓎ `~5ǂdV;s%%<8Df<rzZ? }7e˨Cu@y$}&Wv0d=(|̧yA,N8(ANf\~l"1g\lr9 e z Y1jiZ);Ƿq HWi8I)ӳ*P-̬9Ӗ\ 3p;$ X ۙGI4$G=p)I+r_<c f;`d~O:sSی,QeBF[;q7;` 5_ٗQ4A,]&ݛуV [_?[O/5Fec{w\H<+C >k$둁~'(+1F }k%}~?ѮAPrpF?ZzFɔ∛vO 5gM^C-tR]&ܐkjo?h޼Sv$Mlʬz cHmK/[n_Hgcg ̼?/zB[1J$1dg𤷛˔NG?|x)^*<5 Zη$b@ 1V$N1@H\8<ȭ8nA#-M|65V)Ƶuq3z]LJf~"e–-ar9I(?*JrM_6%8vbײMZ|x{cAr!v{t{Dޥ{i+BoaIڠr9 9;#'dpb2v>fS`OƝyeqct_M HE(t{{rA M$pAcߥ?:[rɹgv6*,~ɺh֠&$%n'j?g-'q؃>_N:0 ' 8_7x#_r?]daX/#KngN6#{ aUToaHW؟"oc}hW \8?QQE#3)1ᛃw>$+UJ#1=[q=?*. v3ɨ6F̦=4ܳP'>S!\3=(-Ύc)w}yǰ#`K)nd98ejtYGW}<"@'R'Қ +`OjX)@ 6UTgޙDVdsQ s,lN+cʁG܀aNɤ-֒iz)֜#9W;`5PB~95~F|?l7#[G\`:H+vih*xN'TyscG^m\uSA=sʭB|Om{ZDu)(>k c7?+aۢ|Y͟~Ț}'U< T'dG9'I vmc_.~ 1  fld?Pjf.I<3pN?*/s@q=GHK'zmqm?6;lj/M}hKP*&!MYrO,<Ɲ+abt$z%1WY8 PH8bj< '&r=1@ (㜓u/$1rF0%¶1+rGU914ϵbSgoʂ1E &%H-Zsqe8@ aNΚ-H1Ձ,9i^뻽+8X*WrH?jbʡS>4P` #8-)V ೐#Уj[4j7[=;4U1iʓ'?I32GyrdNv)AbKu5#6O  H>47CY`ĂAtR0ڜQ-DrHNzgg>vߌ}({:~\T'7@~$L3ẊTsH#8_gBX>slR8Q]du3@ bcsJs+#v.%I da0GpzdPo"#g'9?J6WbP$gSRUs9vRHܸ-dSbnX6 #>pUrr bQn8GG//*ymow xu4K=dFC8SUo'?c8Ŝ]ʹqƦm=@#Os貣ZEe$-18e&싩Ou'˗DT(0_?]<4p?S'8W屟!x+ǥ~sĿ#w"^&q8$HF0xd+䁎1Wϟ VM;0ݏz]nܜ/cbXUfI~j6F$(W#>hNV dv1߲7ď =Sդ5{=[Ze%ϖvDYwo֒86DsDm 2~^?GһA|S;c=i2H/o53D c|Z~q>k|73/z(h/[1Vꦅ ko*~Tx#Y>l8'd3,f嫢*Yӎ.Ԣ>g8X'83W呟!x+ǥ~w#wqȬi!סcɤd!nG>I!Q+1%u9 cu•dħp7c޾g yæW-/|K:w<? ʯ$"=2HeVldwֽrOArj^3>#k-͟6cŞ+ʱTUbU;j{Y**q䚳6Zk+Ծ&~?0keL|flI 2Jy2_f~~ޏOF]kB,)Jā Fpk/e㏆u/Y>_4oˎK][OwdY } KfFW |F|L|A"[wnx.VbCݳB<XoJX?h)?uזN=ݒU*sw2rJ-$%~&muO,Ok; + m1٢6wP((P@|m۔~'=C~^׽?"0m%~'cu-K*k(\?v>{˛~JM*ɮg)M֟2qJWӭG[Fi( Y;sܚ?/_kI~iĖX.Y,2A &?Qi^GM/ƞNDH%V dPNsrp+ k ? sP|5ؼ=}q<14l2 d+%Q5:~|zm~½Lej9wy44m,{>oڧ6y־RIm$ p,Ȥcs|?;?7xLqE6::uŴ<3[ C9s_Mo,GֵkyMw- H|W7xzgk>y-mG.V4*py9eR乯9;>[''ͥ]uĸb0ϝ576եʛkr/"ϴL8~1#k>$ہ6dz~%|Y~?L>h^<"",bo,2yBA !@s⾣K⮃wa .>"'H~&xGci it˲CɉIIaKdW;jT)PTjm-]/+KSaӌ*EӼR{I'ǭﭏLO|E@=wޚ&ӮUᅣ%p[**BW_dqxĚkO|htэ|%@џq- :,Wɦ*Ƒ X ,8 )d?m!ҵ- t8$[i.&)gF?s=MfuEVN[SGMTMTz4 itrJtt~~/𿄬 ivڞr.9ixv࿊|=R>j#ŋmB+?e(yCH$i_zV @Ҭ".t1< po< x:|ͣXYZ[om,Bip\!s}(VS\Vd\T}:FW8FQdGs7ʣ87[|RMs?p>-/ÿ\?>c(5] $# ^x]4x+hd[C'جhFK_q5_Ii xڝޗ_=6H;#ʬ ^Y|!_i^1 &{ Hjsr0 ʕ02nzM]zu<쳈'[bm7*-ɮU*RQ1PD{|3JrK6G__OOeᯀpxr^pYDZE;;#Ӄup9kP W*izޙ2] A%mhٔ8,y `k*U9==on4N+y?Nhc}eNn1W]]_w2|Y/㟊v\ommj{m^Hm*_gڃY ^Xxs_F {(>9qV]O5||kc_>^ OxکYnK0Hn.I_^?eϏG_:G_t<9ʁ˔\1d@Rr{*bQVP|ۛ܂浵\oj='K qSO2RJ{7־W3EAh^<}XCc+ i7(/vbȿ⩯ /o$( 0TFe<;WQ?^>ϭxGԬ^ z#4Zv̡f8\b;8z:Řu WҮ{DzU3ۤRv y #$fygZpRQrWi8OiS={RUou=bV^֬;,.qþ k$ѼQ^2}CG{׃\8# N>5~F?2ĚwlsS/:4ж {o?[Un?.3햽-ҿcq~bGC>J`򄤑:G+gg&_e9\q)!AD夓 A|[4;W 5C Gcs/6g=)JB@}Hd'Lc@:U#G\pP!YPNƙ3D bb1 yKչoJrX:: !El܎\s&⬫ @V\Rp3OW@pX҇t]F|act8^sO)&7dd7_D%iG`s8Dc+{09JLR\In'4ي"@㩧#',pqzʞXC6AI9M2 Pd'g,qޣ[e}O^)#_jm;IlNcf@<ڝ.h lh$!91B2]H@.1)|œΐnp8$C.2G#FNNOtݘdPZ= ls>v.6M"7S#6V\~*BYA_̠wO zpYJ< õKy+8%zfrc9!GXߝ"B^5++l<7$Np{>bq I4ys}OC߯2UL~uXp0Ã2}YF' zM$nECA(g-8>UVBn@ҧ'_p_+5g ChMVk ~'_p_+~Ke3f_s[\QE<3 K6xš f&F9:R7K Ӡ?cG`"ZE2xb+u%xvĘw+j?h{ bnyʖHـs(dW嗘`7'(迴葝F?6Lsw#qP32+ľ|['Ɩjt[eFA4^5o?'/O eE+j}ڮ)DBQ ^zp~ څrGo:>˜;69?6b~db18h g${~?JsXr{>a1 )Tx*?#{PJ_/Wɩ gRKȧ!H[H"ߍ~4_$9 1DaQ\eAs5Ybl|qTr 8r8Hǽ*ҳM}p0)Uہ˓+`pg~XP9.#' "ܮ .'Afh#gBܹ s1~[+AuM%=yT쓻]׍cFyLA "Q}~M s > 6K($n99 +cck: O h\ ReSWNj U{xK=CNc)ʫ' pzpӍx׷վhWĬ1%٦~n3s!(/=8?AڐDmB#{RszeUAY W؜ u1?21|Z@4y=9Yp@=Xjda~oOҀDlu=%Fiz/+dSUYI3i#btnc84 [wb "! G|x m1O8!d'i @ 'ef &#A'<\cj,a*}/{tQ8E.H]by+nXoxp)2JiVfT{jI%6mo#p0&HxӏƜ 2h*0+?Nx#l]͏:A2'G)ZViorN6P;p?\trwlU̢{'+ ;Ed@$V[b^_7F~%dhhz3ٳb \Unt5ߖ᧊]S~InkaF%y7䓻]׍cGyVD Qz(>U?,nz$1_\Hو{vo('g-sGښ!-nQlᗈ4+ k6"Kk))p8^gPx{ѕG#SܔRoODZNHDc-4 Y. /=B.2H?D &P ?:D2t31GGw~?$bт` >X =ݿƟ-$YB82q |2grwsۚI8L(Q1*؏dcR]'Atr 9-ɒEʆ gu;s==>R1N4Ĉ%9$9x0+jPI 2 j ' IWqO5TCԂW+H#&i@yNҗ`v8ZqӯfdѕsxOjfsKDpyϥ,1bpT79;0cλٞ7<O$1s?ʸ"#l$яg¾S͚??K׮m2}IJ%ȃ/y*<Zڲ_6z;F%w̟NUV;HF?@+>!? rڒGo%~3G.} {_Qohٻ\-p..!`W3x/wz_OUFö=;: |КB֭M:K3淓QWӿm;F'݉=( 8E:Hz8V> q+c2{W|HTTqHNp$bP*0{ҀGI>) lЖldzIOJI<褐>%UX[S]Gy F*KP-I4vw)^c'v*dp@K#K6Ys¥d @rwqԡS=<0 'PB91<~Uop?9ZA6J6 QԾN|hW&,jjU=@}RLN<g$N@jYp~tû%d/ZX0)VbѰ8?Q@ yfu;@j0mʺ:OK2_qқ_@ F ACl61^@ٍK ~{R+3m9#`<-Rx9$g>< 1pd\m&"&>v`;e$p2H[HD`$MGu۸:0Tm $g@}Ӓ 41J>f8M0nض * |Ȩ#$1Ly~*CYF[ױ5#dG߁@H\ci3d1hs8,=A1^Xq Dr֨19u4_vݏۥ{O8j_Z4Vs6ܪaoV#{Vl } T r?,B%9gh_/R?O!}Gkڡދ$Z(y5X_nK>y?D//Zd:UA߆_] o *U졆\=\]ˢᅤek (;FY@*x"~ ͮD;'3_AQ:qʵx:ubj Eg뺲FI*<;>Jp0]"[os?t NB`I շŢ'8a/7_tRϤ/K'h?~i!]h0H1e!|@ X64XuWE/R?Ba!c=ŏJ?01,lAd FFuq8h5vojq?_4OqMnOjduWx,.> } T r?,B%9gh_/R?O!}Gkڡދ$Z(y5X_nK>y?D'#>zuP"yKiCaXBں +r҂KpYv.{ZŠ(ѲF%B ?lf]Oo;'AD0:WTљ֣ONTM]??7u/!}wWH%Go)U Zmx~^> ˤxOh[Hn-?t.9ƿ p+> }!Y?G3H [G5P1A-8 _jҺ(ϤO@g[ V9,Q-Q`1nec`K'h?~j70h{S sj}gEu_4O3,$H5lwEdA)̾ [=E_tRϤO@g =^~_n'YCɯďu]E/S'׌Ֆ6 "TGȯ5ҾPT"(8WAEvP«R^pYn.{ZŠ(;Th28|b+|5@ɫhnGW> Q}EoTUhK9XZ8nx)Ez3ƶeQIw*SRS?8?,zQJWOo>uNJO?)x&8Vcjꧨ?R`G=)aȋH?&$~=IRƭn7 Ys)W6g?2a+'ycҏ7,?R2Oɧ)Pn=V1gP?^> OX=aG=([#%GpV}7Wl-c?r`G=([#%Ram ?Bz α[?2a|G=([#%c{j?}^Y-UhO'x}пrg~ 8Woȸ'߽zP)kؼͧoe6 )Ma)(wQEyǦGslps SW}R#kpVI>{}_`YVO M>kU|4&iٟƖ:dx7}㏨*~:lm?m~ʔ(ǥy/r.^"JZ)'<C]v z}{ z1kv>N-~cҏ,zQe"7WIO?g'ݸCѴ xp?veG=(W2"7WIO?e}v?Wup;}_~Xwea+$ y!\': vpnnkoycҏs._?#q?;ɏ[)#J?>>OX=xȵ1J? 2\nE_Xwea+$ {#sAm)xoGo,zQJ?̻}~20_Sy?^= '#i4}n_ϯ޵+J [|?i]ѢgAKW a{[il}SKrRRh 'k4mosCGm#÷}&ߘ >,ng#fԄx[u0gk^$~\=EKs2?Jvq] O&|Qpqu(IT9j5<6ek9e-٪Wc{^x]BR&:z,G/?q&W\&!hA?š JMfj?G6?5+Gj},8',$}x_A1xF8*7F: u|bB;7dPcMo(3EMNg|sM²[fn~~)_kyz/1y'MjH%s^"~[Ynjy7j1Dջ$;&(/~2k;,u|{Iwi-cr\2+'I<|42j:njobA gg⟆&F#^Xbľ0jVE|)G+m"jotcfmk`?Kj-v})wK:B_ĿͨMwi"Ю+~_>hHQu=#:.aFjY $'d~$=6=?44-wu]{emψZj'5I$P%_5]~8xo@/?չ?I'cH/;G]B]|f}5r /oxS^ ۛ[/Mթ$i0Ⱥ6?-v{ož ]=|=me_o.ohI ux%4^#PkHυ:DiLno~cjVp"_6mwFHok{^ĺHT-_:{ I7+\8}qIɺQ7аcӴ{u _C;5Eus–-z+ھp#"Mg}{lbڵt$K&?^Iyj8E_v.Ϡb`W纟A2FAwW$~LG Ov{g=SNR?qP F:G=^?֦8b'#b꒖~;R{s–϶Qժx$xvxï讃-D~{替#[>7O-'#rA?n{oNgoQ#>Y SB 7U>#z(״UF ?I߄FcqO&_TE(B!VH"5>_h2/ʁu皫sKw ߈ez[ܓ>[}n1|ۊK#6s[vp>㯂"G/*7E:'I7݈.$v7Tv1{yk?2$"gCf%"/9E|y{J͋.KwOom?o֋Gj}+q'öwz{蕿"V',+'8| lk7/{S!9|2B]s/햠7KggO4]~͡k]Cw?&|{*u'p>G=^ 694bW<}+=? ᝟,Whb< X^@Bͪ_qpb>yGT]Z_>j_Gυײ Vh ߭>:x#P0Xx³ q`>kF7T5BнJWIWM-5a 3Jj_?WoJ@[|EͦJ?ؽTc⇆xB;>|_/i-H69mMո 2G}SQG'-x̀u$)=MgG4K/y7I~(xd;)ző#Ǎ-Л`WM+tlwmᏳQh >5x6vK T}kE*0jz{ۑ5n?+=?d?/ &QoFԡB $a#Rz3j b1Y?GO3LZk }3lM;GϥGecD~{F)J_:xٝ[i+^O$'Qm5eRmZ=>#zV^ѕ_{TmU5}0IpE|)B vzd"*}|WƷcv}?O $w>$c!F" P[<>D:o ?o~? _9m?ŸNgo}OxQn?խ;ZR|DViub=;^ uCqV: MMosQGL[R#a@h O}Ë?h/o7į%JPE~{#[AdgB.cG֓ %K 2H3 Ew1({ccæ@pWJV%"M訇5@~{srdf㝹߶Z?U?$kĖk"d#J".ϡ.No.O 7y>nN"tMH5 ^-,ϬWh Q]Ư,eғPoT,(PvP>{|o~[ [e|g+}ҠWZߎ#ԏڝ.ϦgZ۬:Oʻ^FWυmuω?GcndoQ?5/-~_Qh =+⿅ۏ'D.y/ !%bjt:@ǠW. ]F?jQGD[%c%QʟZVq}s#ök{EzKڠKkoJމj ,`>ZeҨAsemoCyjmN+kĿͩ?|/g7Ě :ZOɫ LwzD#UO̚?c$!` pLh Oa\oh:}[PV{oH#^F~%_5_]JT[rjCXc r({$j*Iҕ5>?<> 2wG/%&{x 74=Ncj]J^SF\"$ͺ+N+67Pz-֡~bC[ ;R@,u;3Z=~!~ͮh辭{UO]ì$[&F|(I}*;G]pVqi0ܠd:7]I_a'}j+$;&o[H"@. .,ye&B+G>PȣV2-/ yL;7XSkӿ m/̗ό兮`W Q\Yj"DZ=v}I ɥk-i+/Ԇ~^0zn8JˬxF'!#bٿ ]ZrOm#==/W|% _H7}kX"CpW̺Ofx%g#sk?m4Z=v} ֭:¦ ZjN |_׾ = #HhU|~$K$]g;Fꙧh O~%#f!%u)h%?-د_$Ag hoa c'ī`9a}mh >4~ڟ xb{sĚfv|;N(DcQڼk_u_ͭ6L>cFN BInY?%woj^^k[x$Y#D =A }aae V#("F`*; . 7-QPPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQElibuv-v1.8.0/docs/src/static/favicon.ico000066400000000000000000000353561263361716600202050ustar00rootroot00000000000000 h6  00 %F(  JHHJHHJHHJHHJHHJHH1չJHHJHHJHHJHHJHHJHHJHHJHHJHHJHH1չJHHJHH1չJHHJHHJHHJHHJHHJHHJHHJHH1չ1չJHHJHH1չJHHJHHJHHJHHJHHJHH=qJHH1չ1չ1չJHHJHHJHHJHHJHHJHHJHHJHHyy1չ1չ1չJHH1չJHHJHHJHHJHHJHHJHHJHHJHHJHH1չ1չ1չ1չJHHJHHJHHJHHJHH1չ1չ1չJHH1չ1չ1չJHHJHH1չ1չ1չ1չ1չ1չ1չ1չ1չ1չ1չJHHJHH1չ1չ1չ1չ1չJHH1չ1չ1չ7JHHJHHJHH1չ1չ1չ1չ1չ=q1չJHHJHHJHHJHHJHHJHHJHH1չJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHH?( @ ljjJHHJHHljjJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHHJHH1չ1չ1չ1չ1չJHHJHHJHHJHHJHHJHHOXV?{mOXVJHHJHHJHHJHHJHHJHHOXV1չ1չJHHJHHJHHJHHJHHJHHJHHJHH1չJHHJHHJHHJHHJHHJHHJHHljj~JHH1չ1չ H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 6? IDATx}Py&!X`(@tq v[voٻ=w5x}{1=]mE_lGMWtx`g*".M-^Z.@!lI/Od2RDTI}d*?@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @<:1O"@ !(%:o{~y o>eo- &d;1ܯڹLwmZ϶c``A 9؂dVĤ.y$e8v:n#@q| @JS6-"2 L|#R0"B==`Ol}_Dm9zH $#-sG__/F#&&&p 7NHeAbzz&޽{D*={UN;ݩ" 㸳 B Vkc}޸0,<°! 1 񟞞LN>gZFՅ؁ 'ABB !h@,Fo/kl; 3/8g #c<اƱMN>4$E͟׺:FݻC|  >"BT,qx;% /''e|h{X\|iLN>{ ɞ={QWWD_By!$r@fCWW\.:;qsgh4ԩSؿ9~1==&1==I:***PWW:5HZL@ .Hho?ՊvN:7<.&'#ڨ޽{P[/"! l__/fs@'x<> ~EҧNĢx#"!NADssfsY]]<nܸ! U3f9rNlᛸx".]Ȩ __v @aQ677=77%U=z'(^08|D z}LT!Йytnnӹ:ra6qXVvY\t oq>4:;r!8.\᠓ATTT7J3[D #[8l޻;w;T[}l*! %14{ai2XK$N:{D "v!7oG[V9r9`aXsA.Vׯ_5 ш7hiy]-,..ҥKr Z[;_YraeqX?}088jB1xzbPzh4*!`+NY[Y("pyջr\K .áZZZj^׮ 3, *Q#!!h<~$8)[E 5!dȅaC]%bbqqW\Qں"!XVtw| ahebpmKrk!8 S9`ӧϤ<`"XT|E%A^[?FBH>y0w1wppgvY+%ퟅ)SK G+-d&'Ƚ /Ga#!9ı/!. oٳݸvm0mÚN'z{\.W>8zcqqϟ"C 2:kD uȣ(dj:q\tIcav;vo>"an vl $N>+WDZ0H$6:cjXu Z}{`pp75w콽}p:.7 `mz pS\Y|JE@Ig4I ׯ@Gɰ *RsF\u&d^/.^ͭEjqXG( ".^/- o-WUGWW'HcrrgvcppP-LA<~Iu ٳ)KMb$bZ&`5rZ {0„R!8X]dU Bc @%mP:"'v0==M !!WS`j?3VI0fV7kJ%e`.+x".]Xg~8N>L*$"%C N.-""* Rڂ$p:1B8Tt]zr Ο :yCjppϟ:!  'RZAloX,>,w"E:h<:cxf;bQ#Ju*h6l6LOO3r3D &R]t W\B8}qт "9h<.KG4]+XJ%mȥK䞹2""-GydS#Trƍq}/z=q%\t)tY"$y,O |2`TWVV*V! ---8~2o߉D"D@4IQtb1||DC =|H -""eZ}6<\.8CGGzz.P$199I$Bm!$EAu% xSt- zz. GHz#?i碒HooYՉND""Ƀ\8NtwF/}bW>F$B$B !T9} z{p8x0>> z9~ƈ$A6Hyt < *!lF__o\#hB<'?9(~iUU;YB:ɣ#K^dԇɓHb^-7~]k_T"LýT@7HH+6$!7yt@=?N#b%хjCoC^4 şs0rÄ@7 )A1czz/^Qf>Ol6+&1Kmqh}yz m'q4AĘXVD c_p4';bjA"rupU XoKxS{)̈́T""-ŗHDyX\\$2Ox UkxG{Sj $"Q D FeD@MSԖ>[ZO M+h\]>N}y5!AX0#@"_C4Üd  9r8؇-TK:`.9Fn߾R/uP q" 7ER*Ep1@1YQ7N,6޹sZGW\JuynnFOZ9\QtK@~j;$q}LLL8amm _C}EEE 1O@J8r0<Txsr6ypr>z&_:W~\r04%d`䡆㌲9rg4zTBZH6vbkrb.sQQQ 9M"9 ?oAl6ȃ₴XZhA{瓜DHL'Ȧ̙LݻrtfEh<jW ULOOʕ+R/u$/ qEً#GSvˇlƅ =p:Ų^ZLw088(/r9s277 @ت2%CL|TVV^o{KoD{Re53 ׮ bddT_jzNܼ2As=Bn,Rǃ.A #LLe͇<9u]t eԑd9~ Q܃湁@VD{B8 D:Tz&lGh|3ꖀ-8$KՇl܃PR\п9v{Xa#%$<M>h@ߎ_ oX1<58ȉ(BPl4 gu QBDږH?{ ["چGSE_CԯBo؄ߗl7Z-F?00dسg/or7J}H+`pR7L@U} ߅nkM&y}!h(؆ VB6Cdl x9N}wO>-9T .r)y,助*\Hc}}7]: c0L++{iȽmq.`g#G/b8{;SvM0mߐuw(AM*jfl/<.XqVX#A8Np@[W<̍505VcY5Rر8zx2Z +Wd]+fk0mPs/TBM*N,K~{weHDN'_!kl@#g B@s) 0;cJΝF]]x󵪪$3 2rrΟ?MoDc0ŤF|jCWKFLl4|Lqwx[T#a^2umiiQtn=]I#XCL$nM$&2oEEΝ+Ov^#,s]y^=۝YWvKL8 l>XjƊCw>AMwMǪ>\n:W~_bW\4pˎ?k*(z?I T}Sf?ގpӉoˋ!1&OdwY:FU2!b|1 wJph}CWB%0xw$,k*߀w5ߗ%k^Ss86w:^s`||^R.RD[i&ۇ)!z [|)7r}6gv;>d@f*@ۉeE,pI SzjDahG&"% ~r+̻F:10`aVR\ Ӱxjd*oV$"Q6Re[KƕuV(͛EɪTik WzKZի6T(FZɭ7Mfk `VN3#YXdf Ncf(,.7[O+3bԩSx̓UU/@C 펌ŋ=f6!^(BPjJh ]DW c!Av?@N)eJjhDO NHwu 0Waϖ dC?Ó(QK#Da7lr׺mG j}Mh}э'T,V샦P4)"`R_U$Hwpҥ=h-PC=&Icn;N(ϪSRcj)Ɗm]z`ׄU7DX1Whu~o.VÔLa+w\|Ѻ Imy$M3zIݷp)VUoT7Zt<,Rk0o~]kpZrTTS,D=fO6K.V+2~A:u*vu n޼T6W KG} NcϾ4Bn&,{ȃ]V~Zr9xU IDATEQUxI= }^BEJKox k/\U%N,>Wýn׬s5U'm3ɻʒ~y:->axsW+~h޾ ,ƗwjaҲEda*G۟0Vm' %SkDsՈas~_~bחFRソXqLuo>j X,GoqCL;a)klżg '8a5(աi% k?cY$9/#Y )m'USc:Ҩב$p_Fvtk\o`^ha)~H,g1Q{7>P5]T<wjYT\SS=VSW } )Wi&5| "䨏d^ h~K^06f􂇂#VIQf'35_M8 1uX^)";!)* SlX>hRe1tTQ\@R4$nkIm0xMKm/4[`*B:BD *"eꣾ/{ùy굤-2# ,T!p$,T-Fж>vb9DCWKc`|Œ sfvRFs`)J?R`#c"e/|΅ JZ}!7oeh> sdsD>KvPD| ])u91y4U( L) -N9` )V57l@ 촸 iC,ES,OD"BD *BVťI*37ii0p/ݓOUo.;ǪBzoD q DΧ6,*ڊ5VR٤.bYX ]h0~ݡ!R9 kVAxz4>9VBǽjy2|x Z^ba?UT+BBEf'@$pxZ4Lע9ʷ h|}Ƹ~M+] Z_kk n1:Cq|1-lNJ*R])9RlڲErI)=L2QnfH5oDX%O /C+a޵u|UP۰b+iU5ȺfupkUx»WP۰GSzY9:ky+C}VWh u/yņ՝Ak;j?%)% ī@š3 ҂6++Z!ҕ^ubx[jT&\ieh _}ZH(a+cC? "-6YTknTƎGL&uЈ0[a^;24 p05V_ kVZPEzh|eaC%A_~4("[}P} +@eX[_3I7\P\ !yzM6o޼7<(k;K~6 (榋BF!^7PeVZLkjy\jx4iux/ M+as`c=YTy4ǣ)=guwp>B!6”ہ }ۛJk|#VE +t(-P\xp8*ųga4 @vj͍%rTVa["^ f vach{>59J'7ZiCWMSX[|ۇ%L8 ;MV !8Lɢ5w.aWKjm}'̍UUS3*D3 ?op"o&ȣVbc=/_و[)}UXio?PK :UtfY5[ a\v/\vJ7񥑻nMy6D $}eo*cQ15N @C &<+Vj=<^ KC8LY@mr2f"B8{Œv ݙ"***K"!bMejJBDb^,'[#vnю|.ɸ //D }5==Mw>AXx>kK,UCԏ.ў`H"s~5`;^YHL;K-bͤ.A`JRa@FLqU Ѷ8|a*UJ1==-5(H6}`2E7e`i*5doĽ( q/ĝCHBxG*5K'Ɠ }Z鍥|`B+DcUj=Ki>T#fukWcSƲih!.8c 9Uf*$j]->B )\|â06QSTێQY9Vā+VaA&ПQ-uxfʨݟHEScŲvb9iǮ$8HHk̤ybذ&TruW| ]-M9yXLOψ7r@l YjTCR!vb99(W#ϹT#?%qtJ|MiG!7! P?!qO3U$@/N фe_Vjq.Cby>O NذH!*"RU ! W8H1mP1-lZ`FFxkҖ֢USb͎ \a&d _jtaaS"د".'o٥uVxN-&uBҍEx5 B<[*@i\N M+D *@\i%_S_ Gw$ɄF?M<50oDL͂mss󖪪\ M7`|<,ߗ׬.iޏ?ҜA Sж|4͇9A Vc(fup/5[(PӰ`u4FnBo{?ZNF-ȟ?ʿ~ZcsyqwAwO>M >r@)8iTQ\hXx\W00WŇJN%L&.;ޏF$O xB% XqafuÌ=e0(%Z ]- [P9FQSS0W.}x,MhlPczf(xzzZL iIM5 'iMݔ ehHe _EC 04ؽH\: ˒s)'Rm%yn:S8L@nIRjtQt-&Up‚bŏQWkFmGݡ-c(guk[S>\*e.,^-*L  :BLՁz 0m_y׺f{\V|40&Y۰*Jv$67>h 4vԍͯQJZQ*c aJK)B%@HR$Fgz "Vcl4FoqP} NhmL +QwH5Xqh%XߴR'hwŴj;,lޕg2>y|دBگWZ DK5 5qb\:ٝx4+2ʔZWb2"-i@Hz88<-q­j=vb};4qӦ@ zf; ,{\.= ˕Ӯ[Foq0Wa!e%0rS<}\oQߴ);,\Ր&Bk; ~,TfñbI뺙(y\}jm7$_?yC@jm }BmeZχYV׬.4uŇoy9J$=0`s.jzf\<6s:Jr@;0@닰wjLgh`Xvv]`A/,Kgض#G=^Gwwk8bK'^]h|UK=96^ZUC46w!oZ\ETT<xƃUx13kƈK[Um񉸎C>IGG9u==?"ί`PDwd&@Pkt2ԇ8xG_TddC0zCe 5C[0RVb*c( bbbBRI)0QɃ)vYBX,BH8I$58DJ@Emh/}]~$6ܜivJ[Y ;ݓOex#'\Dw:JRBt^Oj^JTVV*|$PGSF @ߎPkͺa_ϑ'C~!~;bŗc\SJI0oVegNTME pB 7a,~xE4---qV4nc(UOJdm<3K`4J*X2z ]Maa F}$~GIkha}Є&X+g^;b$ `>]|X OLG56Mߦ%`ɾI}0ܻzO8P{{Vb$N8 6vY̾R&~gDG؎Uۉe6C[SLta ^#48Έ 6b5t!C)Gh`!ߞعKLIfs$FTGZvh{YScz4Ѥ3c~wxK0oJb͡ۍJv;%,5%WRj^SQ#7y|8Un$"<{/fu=RO4"_88]F ȑ#a.ۋ}& IDAT!'JR~V$+FrҒcoGXs{1cbJQD VK* Ӊ\ پq\DòU jV(ﺈR#Bw.eEI-g",Uw9\.p'zW|nbn'hpYDaq9H4!D劒fT>.\)K vO  ^4%vES;ׯߐ5тj59~zܟMT B#O|1'>EXF̏ݐI?$z{"3Ӊӧ```˫ 'B1ډB"}V-7(J5rtğߗ)D}ԄQD 689.b4JTܱD3o^1Pg].;gC@Xv*%<6Uu,n 8mT$㭤.߸ RIE)1vgۣHG:g ]ߑ9U9;4Dܒ@P7pMDsłJ b9JHHNE),ΰF<1ac^R1lw[aZ|ӵC7ě@7TlB"c}N@"j_(%q%HR/Fv΢nDž =xɉT̔ ?l҄TSs/,8x<XD"VkbRq .޾F>c]]]K4bx[Xฦ~USՆްӢI7RB UU;h4 *ƨMx@WןKfhES|c.&]3a$b}Ga*|b: _ ˂fOɵ 1t,l{ۉg9W$X+azJy%?EB,p\g$R`[>aJH<|c޾Ǡļ*I"r2wXVPe7 >7CJσ>;+U]- \!ONNZH?K Rڒ;wEP,//cyYYgomWj=qayyNWX[[D)4-6+~ U۷ǡ,bj5DEJV+~UOuO͛ϛTvm9+M[fJ>b0p1ZFen.fux T}[;A^1F ~=Xc ,Y,3nxuG(K"y, 瘒Wy/\ExÊ[G{{v{{^]kW_Y 0L+r=JxkVR&ڻqi?NH)8N9s:E8"/6ck/zhH|#p@;f^{XwwGI7,َ~_>Fn%9 Zh*=- O[:-1XqXQ=z }ߓUOAH5 ާRC.+ 3[|! /T Gr~I1)|AR;9YWr@~X]]%]:Ya(6& )tF›*JpZ:.!USE |_-8] ojXݺ8ERBN ERU3.THbBV]aS*AȑwI ,K NWTk˅ c!TQy[5, YrǐTL 00K+*vI &8T5zT%wa דw`9+wawQNW _=:䮒"Te kV'%ԇ2d:0@ a5 %yTIP NW pXw`ɖ*yx!/ ?yX%fBKecI!JKDӡ@w !cw,ES 0[/xWblу/tb"ޥJc ][0qɨ$ٙp"&~gL[,D =SM H߮ݎJc|+E4 wNc?ywn{!r|o+1-v/m#;L/Jyk:Hҝ &FU~(2ԌPiTLQә%|:c/ {>*8a  W/݈1qGH= cP\∶B#sw}l%;^ARA,xWS|::R)>>\!C7.:e^ñ┟pU= $Gw^LNN]7M՚8ݥ.5m0)" ]̆*?ç3O,~bQ4~VQ5~N|?䡛1NL|6@Nu H:$!)CpoY(oQ~ÜuY4CoD}nH<߽T׬.la2uquB yi¯̇ [1Q鎻ѠdʣE)%MzqY::9ٳ!9\JfR<۸0Y:~w:*l)raQY K`caȰK͔Й7RrDfG)$-qO'2***`4R ag>?y!iYƗV_7 zUcoT$%0.%ī=d`9sIPWWGX9sg K剤͊Kb K *\Kfr}`0%"Q ,޽{@rTbGg_{{^)"7 Ggd_oZ(P@ijiq ˞ zUN'Dut*.#[뿻Ŀ٫@CURy8z`SlcPKB*UϿ3o--³b(m7C}Rp?M~'kR>3*VTT`qqXBJ0+ 3畔QQ[q׸7Lq j?!H,x 9"@]]-!?̒?3駡QڶGx$3c[Xr$xÞ={122Jw-!m?htɗ|t橠-J_GLK1q.H}(A<Ǻu})5ttDH]]4|T [􄔆˷*jh;JC Ue?WSJmە*8Ŀr76s@>߰wrcrH?sc YUf谻 .>YDo;LV)@O%J jgH8ADRi,wƝ]}D!?^H؇RyiyX7ZdzHUNܼƊ!hk[uO{⎅ԯevwbfJy߉mAqB&k9GU&EdJ:':]oĻ'҅KI̥oϜ&A 7;t cl+ˆorE9Jg>x7]łw-Q#ۊݓOh0V$() @OwC+ D={Ҋb:^ ɊB )XD+h>䡬Ja*: tM Ǖ4Lw0!^#z^mq=^lشa*tAcUW  4ЂnP=hD]]N&EFDb/| ck(6}]DWW:p9W}RIȝ]LH cc_%UTY=074;M4ƥ@@GMX "`#0ɠt^B[ļz.f!b(_ѤЊ ;!uuu;R7Q_hǪWmk(p@oHzc>"", DtBa, _<:cŚ:zZ۷k@v^h-A)wS/\E4Eb ǃ DV1/;QV[Η67cV yp=4Р9a޵F5Ffu O4mW@dWD ;1" sYp3O$΢@(3M7xr[C"wޡV):3պ__vx]Pe$w'5"g_iQ ѝMH*M(+3O1LU~6?Gy5lQ%RG\䡥珴v޴F a'dc[2zh{Ny"nUVvXV_yŘl }Wi^{S_SߴB5Fަ㑈8wX,^֑M7ƀ%5Pag7J+HDTBPUWOQV/֏#8s4l6ǿ/$ y)*<*-NLS&xK|WX0b!5Q߄ijݻ0/1bk/PV[h NYcnw](p7l-r_)ߗ\ޮ&(>&Vϣ& jg? !^U|kM-C {s=/\.ĩc\SiP{M?8x$F G+:B& >TY=(6ㅫ_?0bn<"ՆC8gqx+B|(b* 샦l'D7nJmHR{V " ~z* ,Ȉvs] L"|,Pߨ=G P#JvbԇB ]-KjŹ:>VQ+rs;H ݻ鄄`,_~6W$P |`xH/9vS r2\f. ׯ{bAwYtuul6+=͇<'</7[-@T!G,֋ҪU찬vsz텤eZq5Cw3 tJf]]~n c<(k{][A 7h4SAkQ%U߷. b>DL;,0"X,ݓOFkVK?5dLmA-~y(IJ]z شZ)_o IGTD _=ߌDvAOK[>"eV񶐩+6gπcDwR!7yz2a2HU$rB.\ mCoo*wm#K)-@ߑKuf¹.VU\%lhDk;vmBT#klF QlZˊO .XXpn HdݟWxCA* ^5.p8011;w! >yw.kqw$-5*\t )UTXHPjġMy.\?K*~Չw!fFᅫy\ú??,|J!׬.:U  p[ K۶WUO qBڛ"ۈuׇS$r5DN?r0zz.Rq&|0)0gh;L6|aMXbt\& :ְK)%П/HgភA@wWaXm'HS00O͸;TE^W !z_8ƊP0f >.4n* ~<6"덫œCY>CW4*V+9G)R!EaYqDWWBU~PQAvEf9X/-[A ]doe8S!ǎ`!`` + \dK έnv]֕Ňڈ 8r0GC+*1*w)*ßeD"y.MQaX޾b, }H$Z xE"Foq+לࣦ^٢EqP.ʞ&DJBtE!G(&cWVoo#3Za攪\,Cn5#S NKUɌu>9|0a0u(=Qg|~N>#k%˙"£"| LkX뛔M,Tyƪ% 9'V!P'` $RmT08lnȊDggxxd[O=IPuMRAFs)㰷*yzsseP~l7ɲ8yߗHfPڲ84svAC?+ +9am')^f;&pW\Qɿ &4jLfB6ufy8c_0I)55Sh&BVTҤ"HV ESZ Њ+A-eIJaVC* u2QIP!@L&4cp}k^>? A:{yybf4667GHgs"ҙՊ)-ͣQf GGjܔV}bH(פ&#7v?@ķtRyzaXDַ\7J]tGdOU;B<|Pv<9(U|Ri:4aHiY4?GifyyoQ|>:f꒗ G vaXV<}ʆǔqWBrf9vn\|Yr-/Q"k x>T*g\ٿ ?𶑑Q?ξr u|'3ۊ{켾x1=@x[1f\_+ޞJ.PDUU%9R)+m`fsxUЫ4[u T0*s %={%TJ#ݗzmp|GbpdoO+1pxxXr5T&umҐƃ -/tMJ3a.PsZ8>EOԱij"wsUM z-*`0D_Eqy䎁R+hitS`ŏW }4V+ye#l TǞ/⤮.eqXKeQŢiփ}@* sT4ng򐧜a\C4!G6e,EM>Ke.D[Xul@(sQX_+CQuݑ'feYzI?;Owq(UXJQGJ癜 IeL;P_OO&\~a4A"g6S tbT^PN.Lj05G1y?n&π?e>[s#SWY#U,DߞU + rNC{Ք ꙳9` -#:W.VԚwktS *,~4yѥ_$p>[gEP)ވ9+RW7c&f20/?~~.^Y0s "~ݝN'~q Skz08TXqrC|- ſl[+r_ĖfڠDHD''|&fadd###[E^ 0am[J8S͉,uӫ)Ž0p;AUX0[|1?oǽ[HBw$_y2s{:/piKnw o?~= CjGi-6Ħ |#*5qɜV{8P!n]Q>t˭kE((@S22c0$G'Ɣx8S5ؼgbO= KvI \-$\TW8~?F/3 n? a%~dCfb#6pD,ߍ~C{{{?/,uvƆ OʸUKe]#5S{z=Fge"vn ow#dIۿb1N#Z{ċx|ؐДA%ʍAm.y9u{hZ>}J.G6=(AD=<{桢2"=b6#{ *׋NT՝NgYX xd[\%t[gQn \)Ws8&,uȂD'?F^6 _K͓2 `FD/kWY cGWSQBob-b+Yy p*my\J\V/v/ [3.& dq9v?1 !\.~8nMnaP&] I]vk@KK ._<&5<|--- ?$]"LR5ʍe[)Ԅnؐ2ȍdGT=D65 @STӧO $Erc&NQDt=jJhxdcZE^| ÀeY\ޙT#tq,7cCä| b>+=Riv:e@Q0\T&2Wff0 -U @Aggh:teY9XvaI>_ϧM%e4G'vA>UJi*jU{||+H)^4ZX$Q{z=N>E Ͷm GBݧ[va Q]7C{\ [n:Jȃm|umn½[뒖R5i4a?DCxsIi!HJDte/g EĽ{qOћ=q_b@wMh s}D r:3{օ.xy`s=(Xrf~T Mxoh.@sI IJ~1455ee-KD3`I:7 Bo޼)m;vGK9U^;꬚_ŎˋڰD6m]¸ bA3^L%x^#IX, ra_X=Bb**ET27a6zȥV֬ b.M" buyyܠ!ŋ:yd=z=Z-8LN@QQrrm غuk}>]vEw0>>gƁC0fNJn `N0(X~yS=4 *= y%, 'OOaYf9`0w|17߿eE/֭[E_yswά8]׊Y?1wє̡jMbΙAX^(N ւGM5蚄a.rpR/ID1C{ִH~OO7a`D/-W+:\⍅Acc#Ƀ"fC}~"u'E [2cۃ0R;+%q,BL0 Ӫ VGR5I$G s16D&(&A$4H3br ߴZHm/;gRJIx4V^ȹ PUU)%HI{5 G|+,~l92c0eE-uPT- "~{j8~e$I 7l322VY*39lm jlX{L&@X> "C I}Ak,(FHc}hll  D|e]rWx89XmS$bUPhll*tH Z"-/ptB֑jG$ $$!s\hmGvkjx>&x8VBZi%3*$Upۿ$uχ6oom {i3MM(y&ZfɃ$!$]buTjw?+"Hh. $ E*ԄF[Px>l6u@Z"Ew%]zO{?.ߺV!ewĸ3;;ERRZ*CjhjjɴC!RVN:Z$lqRZ. mm!h$z|EY~xb1h3b v0}y[Z*IP((eE!bD 9ZrFt'`+-젣E!b%b2dJ4Bɐ7;h #"p"x=6 uL*DGB$/vǃYZGqp'1hDt\tttȲN*VKQ XX6PZHHW\E$bh>VWW'GcHI d"U1. d;y۰o߾x"${H$EREGG-%Vlxu+$bm$0pN8 X{٭$"1 "t H3XC H'qhu HC"1{\zzzN8f@"EOmj'w$+s4BdH<zzzpn/5$aZ-l6*-%q@l h pR[[D$A"rdI#4ǿ q@I=_!-J2ZTIp3hU <={<^Z ,c*QYYlmq$BD$E#z{{?@2& 555H_P8A!I}H$>(i\S !$_%\#_hZL;PYYiG*FE Xnzę,/NV.رiz}OW۸AA!VC&l( ejzL;XPS'H ,e rzHyzz}9v0A/_L!4$ BL:>.ðT A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs  @IDATx-EѷG%'d.*`J0T0TČAE$H˳nߙ wsN7:g߈k%@"$@"Ђ[xJD HD B>@"$@"Љ@ dD"$@"@"$@"t"B'4$@"$i(3$@"$ MF$@"$@ $@"$@'i(tB@"$@"DB&~+7DvjA5u:°>/wvmcFK*F~+'O*__Ƿ!џT\u|[2?HE_kCiKڿm=`$r-r-Wd^,qSj~MggggfoYz饛{<r顟C;hfe):wmX|~&j?C! pP; U~sK_Gܤu|Ec;> i( }ZVDV%O/*㵓߿пnKlN7 T8ÑW&? φH+3HOS4u l0N*@ ԍR .>X&ŶF C]8`;ygA/֞33U> V| {qO7oXT(;0wqH`_"/x6E7߈J>?".uq_޿Eϒ;vP:(.8b唩5;c?Vl/uV^aexF~Qvj?<'Q~y׻U(7o~} ܿ~(ς,'orFP>vV5EgXj\.o~\uUc3 | s9EupmZ׿njnk/vmO~xkuI]|_? 4Y..JI?a&4뮻"~?[R7,''풓tHCaPՆNM?Qˏ>qߖ2PGJ\(_k}a,:]w]{6GqD?v,\~ x2OE-ŭ-5묳U,tvknrb9 (W.ȯTF. UK'+ 9i( 1;6NO`i&> w 雖rW3i|AufoQoZK ɻom)ʷʗȻtcct 7؜tmj:ɤ4HCaC vJ\)2~G:'7ڕM7\r%jDt|uɟ%/YkZjH0K ׿[oY}՛W\go}Cqijl S줣NLs<}QOykXb~W,WUz6j2|Dz?Oc㕿-+,>e_i E<({,c13< #i(O{w,| _h>O5W\qE[nG>{FWMWYgUcXf5Gyd,? oh^zlp?~77A5Ї 6`k8rС^l)Jw| ';1yXQOǸ0|i(Ι `f҄#/0q,(oS'k"~6E^tEf7.{^sQG5ӟ> G͛f/ g?YW9rg_lf|[9.1vu n=(sbLķZfٿ׾1g<җԬzc8:x0/~q7Ff>v.4o}[`C}o1.2~oN9唂/_r,`H~Ol76x#}>S g=g>S1} W^yaCiQF +|-q0,'ؾl^җ ٽuRx|_n0JJ7pa%]O~a _09T w1,:tkJ| E2(!>U2o!3Ӏկ~ vK `4/YF _bPS?f`#?qɏ8w+~esǖDP_5|H_Xe;b$ozY vm v?O,*36y10h-h-b#G~0`+5|qGlue{qɀ;ͥO{JУnoo~e&r_z饥/yKwdYCiԧ6K `:D5itvDW@`ꈙ:;Q:f1GW]qu5''GɠOg3>QjfixYLjΟMrFq810m,/3){x/lq.Ճ{1RCYfV]v-Yf;\rɅWY䮿e6H/wݯw}roAJNl.``r2Cq5׌Չef10_)aHa4ɏ4q1:p/ 30᫈5 QW )J$c1t g[R}جrg8<(EWL#p t+_JI=q!oqז@,0^ e,7w2萯7jz,ixyƫ'? ,ec :yseg}fy^gVfciHMpƩe}Bs,P/~Q=7,N(FW#P%WgMKHƸ+ 6*n n^uzv\=hᙘ}ICaО!O[gGmq.jn͋z!qFq50G~/%o8p`_ԉ~d3N Xsg=g ZpLM`t9^nVWuŁS 3=)O!jLwP fDtž ka:qiO|8UVYe,>e<{`3Ar,i0~'5.xqR5&V4zi8KfvvPu^-#x10.}61XFIgvr|Za.@8JEo]G_I<~)12\ȲƋgtc%~g?}|>x$ԗTX3#!8> xAKȏ,\(g?|cFB)"]q>#uęzRt OzZ8M7RMKj|ԍQ'MgQhm0,J;r3c̔f:(;Y^((k\+H:OozusnCu|Fh&8%Y2 e&s?-"_j`Sx =19DAe tȒ BqCY~z)y.F,'2ԗ}GP]Ci"/^WefNLjqPY#Ͷc٦Nie@ ]j:=fGf'OY:yve"`-~%;7~ߗp12g2 83A^}"88>F :eљ&꘦ $e<>d#"K>T^>9? l,Y[i ʾ>eS(H٩Z=Cud! ٫a^:FL?E wC_r:8;9|;2ۯ H.7xy8e@̟1e^{c|  b{fC7]qP:iy͓ >3d34-d?q{\Bxhb]Q ,i7;{.>5ҋFJp< ַ7yDyaKC:W?a>dD9(18W9/qPӓ4"O]y~I0ܣ00r*bGͣ<$uXe8H;H>|U iTx7&`{voF b`0@288dG1/gЦ,CPN#$ >d?(ɡJgv$T9=È I-|GZA\kZg0bA޼KԏuC^bਘF'yk3:0*{R8gp1d`_OVp p(j2}⍍78;2Tte} 2`ezv3eϲG8oQ29 g@`|- ptģ2e0#֑sL~ʛ&n!&`qD)8 lr.e\)St *RWѳCWP; 0`ƅzru0_)9{@y8_5~ ~y zҺ g=DDeM6QxƩB!c<; lT+fa0/ʍNřt"]$Q'd|oo~WztSȍwnJ Ǘ%u{06tฮ<#W҉|eh҉|e(r3#3y*i}70b\iKz.%z_L1NC63A~?u[kS.41M-0萘M)1PO YD` @X_jJ8gAP!@g#% >sJ&BqᐆDG| qh(Jo"(."d3@ 3l&;;d4;8g.Dd[OC]< T#@"&I%@"$ ˬI"$@"sP9`"$@"08082k$@"4zi&$@" i( ν̚$@"$=G C &@"$@ s/&@"$@zi&$A<<匃r̂$Mf4!@EX;8'NH=Ch@m$uuMlGc5j:\b,/.ȟD 6}i(qp?p&N |Z!Kz8;+O aO?ެ:~B@#gޓ@#5/Ӧ(G 4=}e(yp1寁q4t4:\S&k9. ҡP1i0^~E6ZjՇ Hǰ2I'L "4- gZ С0px`"E'Hǰ:ҧ7:7RI].fظR [hX˓Fyh_\D7; :%X.}[;d,q.#?|VEKOmFmwsf&']d0< ^D sP;R;_(#n61 c$r9;n.#P 8Q]֢?ЧM t@"̹:;$ hT ?+NWLSJ4,KI0pqs-Gz3QF.Cw&ÉS3 t&t 4n:$\2|;!iudxOƥZ/FH,nڌc(vdq>Rne],sf?33 4j,M7 N$521> ED`. f$P.O)S76\ҷKb7D쌘gd7 ddE7/W.Eޯ].rm.1sWG='MBn#XD YrkmՈ vFyus]c,kb6K&>v򤱼5/״W\?mFN k%#Шۜ|;dT: U} '0,w] '3M74gvc[36dy1Y6c,Жrf4<eFυ3 X#)ʗʇF^ɴG^/}6%3FF*FB=[-|4k¦U^3 4Yy(jD@@Էcypc"/(æ~ln-H".O&B6S ەf~؎纾/3ٶK#03 46.%`ۑo50qm|uè_'m&Yg&gVG}7t.r]e)H:، } ZiWS[ 0;a~Cg٨믿~%ڔ mLpAfuJ ;3j(bqŲZA?񏛣:9s&U.o \p1i$ח8D<6q:"8//ʶNɶNּ0N=~gB>cP6@ ~_Z...z..z..z.f+wtk~ l^5o8OSo;PE#!.7U.㺞(SQKOfV[mf}m^ƫ}ks+2 3b(0-IbjN( GZw *"WNT9MY768Gӿ՘cs"υφH)X G?Ϙa+`X/ko4{nQik_SN) ?9V'yI'Gͥ^:V.}G>~X`o{\s}os4vҳ:9ÛG<Vd&3@O?QpҁOtĵ_ՙ,r] 8c4G'=tMz=O4I R^{G ؏~˳& <g5X9[QqktY29%/yIsc= L'/:=Qfzx C#/*yQȋG^5>e(k|EY#/yQl*]j[o/yy"\p,`$pZi~;|,Y& >l7Gydܰ|t6W $0ÞSCzg|ckاv5\r]"MrFJ?zNaӟ._V}s KhOǭ%sٌ Z4jWW%N'%).T^-K\S8Z,qm0 EǹV[8jO]uU~2tzԣ'>)'8x0ظvvdzg Zԯ=蠃>XaJ:|G7{ K>ϗ gbvk}{ד7)7>9i6xfm-FC=~]C}AJ&y^ʏAn>/̴l;c.kN<IJ² i ?a=b_ך?̛7af&]"0=Q`Ï1gEu_u␉w}{ 5;Ra)w {XCk>O~rCۯ{gϵ^[ی!/})?X 4:+fi89[p4GpK\Sफַ1&75tt֬2mDZ5풇Ϟ6.2c, 8 7뗢T?:묳@8_I$IM-l\M%flsIЇN#ӟ/>y{na030&.zj(+j|1>oQ2 c&s z7m)XR8∾HnTo{VF3_3G8d`)~3]ep[pW;6}4j}XE Z̠`D}h!.9K~WEá8`ͱr]wmJ^"P虡@qVkƆFDOftb79w%FN;rܶc[/O] a}[*3 *a#o?#C`E( Q٠\lʦs^f ؜ȹ oP]_tnlsNg#?W]lo LkmI?/5^L<}H}aS^|5_(.a z ~, y0c``i IC$đour'N=6} 4>o‘6}sMx8D,0T'?)wiO?LP>y:WJ'%.cwrIG Zΰ=<)Wh~ IcC#G>9pxfXmFε-2hD&>w'O~)#C-ލحAǗ<[sUf5:2[1;ɏM]mÄk1^-8c|ԟ c|849c=`'X:%m:599/o8v6RYD##kXqmc$h$Pv4x)q8ȋ~|6~lg`,G8)OڸF18A{ mm'cHp& #|Я[hVum뵱𲗽c9 2?lעc1 i(Dd/2HAi#1)oW2?;<>i˩gNU3?^E7}d' .;ֱ^7\#GF-tkӯӛ0/ YRWNUWyyۏY.ÑY GGWM<}{2bȋr?ΰ4@lGT辰y8ꨣ?@+|; NTr3 ?O|".hC`Z GFS; qIGX'҇b%r/\gpPLYs\]W:Xfy6t10 (o[Lt\qMZlc 0Y8юuܹgH/b30֩\?k np.A˧ |F^f.3٠/H؋f;/~btxeDzb(7h lo}q6돑K,O?_WOLE\v<'@ymȋA/蟩OM8grJ);LPAqe`Քpg}\-_vis#8)i@f-X5lcIWl7ï\Umq6t#ٓ@ |N50K KkṬO;c?X\.?X(ȟD`!0W"?v,6ȋ lb(Cw*2W򕲡N裏.ؽg>f=ALSRNulӇgYc3V:wȿh H`@al KF}+UZs㫯茟ePz9fqIͿ(1[Sxʷg͏M@ȇ8`DZgìL?bi9[JjpIhN=2+["%48ęS>G\{K^h]&B`چQ[ȋ~cmQ|[Y 9yêg1LMC9?~:D?6x^җ3Ykӌ25V귥EJ#}b sIr=5,E/t8ԟ=5XT0@ P:fSCQ9E~OV9>y"?H٫jn|̮z=5vk8 r1._=ig#_?#? ɜ>r8H #?yux<ٶ4S2`Į 6ޢ}g I+]"0/+ë]xӑ|ʶQcwӴÏcFpb\ԉ(9ÈQ]/m*i}k3EUD@'åULiX\u1j1xfRяzs@IDATESz]2?~LǏS6ǷFZبZ`|ØlWK.d9,A3(G-{ Oρrk^T& ğ 8:tK׼ZG9ig0~;YBCY@"Do+?K|V͋2Xh9kJLP<~Cma;јEӟ!g<5Xÿm!}gbJf8rv@'k0tv1srKa,C\ve叒8nϕ~|iy4VFk .ө۳gALՏ4Ʒ18ΛAO9#0yiN̷-!o P2QF'9@ . pb48e㢌qm YuSW'Rc<Ce4 9yk꛶mQƴOꚗ4 !eriOP_5(BG^4"(# }[+/"Ef2J_m՚we!_& 4vLj:Ͱ@O8,̟D`"0xȝȹt o^SշLOgݘ/#G v׼ ׫[>(i2C"i0ջ7o6k <+ $ (J W\߆}ވï~>'ypy` ߫I8aum)FBL>c\,~t+?3* O-3G&>KӞQ! Jڈ/(imT?:/vsB~ɯ^{U79(_@&HIZo7#?yQVȋ3ˠ㔦.A=XN 1u>Rm~y+⊲$Bm3e1-`e_^`u09Ru|(Ǡ  !g?F}.1,a5e:/eL#AԒ2ظ2sdj,eÏrMF]ip"G|fEgÛYgU$`pV~)<]25OGkZI6ܳχ2]|{Q7l߼mo+6fP`i@oY?q7vء =*m.h>{쬔(S&G? 30 J ڐE񌗎'kA%RR:_-cZc*e_#'mtl~F]Stf۞~>|&K}f CuГO>,70koTOz|91 YFyxsKXa3ۻ:1͘2;6/KqDEK#A 28ؽxlt5H6ȗ4W0Tz(_YPyI~e C'25 4W0TzP QO `2<1cZgZ\e8r'ua9TyRԉ=(teψe?UA^~ +04LڇrH8)z5pM~^r4 2Vf0hHCzVl?5 ŏG.=H 9kؖ KkYm]Pgsi{v\t3<貌! <1V[Lq\`IJq102D"),!3Nv7.Ymɰqc۔Gzlir` _ qXCO,Hhg>o<ˋ>tM;.r| #3lƸ!4D)!0mC@h %3|ظ"_?q? |PeOןp/wjP,5:o(g&lksSO ?[ l3}{g_30gQ8e_9iF$dG΋nf5LK/u#4>D?s~F[nd,@;c Cx"5K^M˃vکנLg2L9&D>d;NyvYf&bq1c9|yQsM>Үe|[QrLHtzf(0Ս n%odpcc1\lQ ?1ouXrG jÙ į~WSc3=f*rm[i)R}+U7Ʒ.袲'#Hw,@O(qii(0X4F'@Rm~yE@%01؀FÊ0ؔEu:u>1ON/% ^>=r.RNL] I-WN:ËF{r&A2=5h 4Xul xW6}ެ>!"P lc3L؎3a\9qW&dY\o#>sTvuײg)H..K+9P ;(IOz&϶4TG Xd_/I H "u:N4V0YАxH Vq6:.~%&}f鬘~8sV:Ã6Ⱥ?gi# qdPDiuxG#waGb^P,ۓA#bg@$ַuq>ӡ">|.(3 Zt=ms#ʓ*7L/q&cD`d3#ҁY1ʚ"ҵ^9;C O?9SotÅ6S|^H[d%ضD )~uf0^ =GABg`Lefpt$"0c|vJ2ƓHD}>e#co(>(`Xx E| F_7' x* :=7|P`)DP״1>˯i#w}v7.40 2gvAcn㵏:0yD~Gq>x$Пi$z?dzj(wXtRpt_^[gϫ(~~ 6H >%Ii< j <'L= ,Cl{u( >F5akD`fFAbYva_z:묣 ȓʗF~mqD~e}:iΩg6OR>]" < l8 G2^y-?#/ʶ>F;LI'B4zim/i$p%#0cˀƆ ވ1Ѳ`<àf`:o>n`tDV|f68^x mh;3L;4sEsHr&4F`F crc#<o6"]M NxeŰ2@qs1*:nfRo"0yx'2͛`jG)mnO1M³K w.7|D`|fP`c5KX\|E@c.v2\ˋ:ѯ\C'}pS[}Ջptĺ? 񌅋F%1 -YLoTšF'M& NAͯ 82lxtacxR~g"@9~7Fw5tg,e tu3.-n}^Pr&;4b(P~ݯ2 mU@FatAKygօffܭ{D`Q-  l^.5#'VNCZ KH 'ԧ꫋!qaJtj-ƓYl `[$/!WcelGҶ8yH*)#2$0+IMy9} KΉbD`r,VC"!9  4l1`,M_s5Դ86q!:1^tMe: Ό'` i$PtBgߙ6c= !ՎEf#iWҭJcw .:PȾ=9p1Xb8`,Iq~?_0 i:K' ͆>g"&Fo:^"Pp7VōmXfoH6^{e lc$P&$оx$(L9c(Pp:%3 tN\  m_M^{ms0o ؙ n~|ә 7PJ10(/3,CȜE t\BgX+ȁ8yqԑ02Ry/󢑐3t4sP*t v `8f`˅p73oYVbGd(&,-KM~`0p00400cR$sh,XflݶeLF6^ 9l['SG` VM'Q:+ c;;yavznŏq@yWڔrPVIk X/h>1's SW(0j?NR~ec]u[7}N\(g8FtiGӟ,:sPJt^t>4|"]#}/6 Gd;|k qI.G1(K a8Lv']"OxnymPƂbc;$.CHh?]PAݎ'RELGLi:?!`[X#DY@f2$Dҟi(ىI '}u v̱HLB 'P i /1z:0K#AD&4k"08w,eC1F>Y>D >iYD`#e,eg 3 s.g4Ng=YFX F8sMH% et",:])bgn3 3 wj&3@ 3j$ct  p:b$/y,I+i(,V3D`8{0ȿ\i$ sPLXBΟa(),jvB18~ќtʩ zaG66[o5NO"HC!D Q400 Q`o3 W qi$6lSO;ܖ*x#.B E ' !ſb0ߴc8Gl3&|C;kN2.PB&@"0ch,``0xva2„1ل P#D H YM=CICadD`b=)Գ 9H^ wϳƉ@"0Գ 90"V9 E.D 7Uل~s nYD Hf8_:l xSJ@"t!_9t!.PB&@" 907u{fL>HD gP绗eOD Hf4fL>HD grB?߽!-Q3џϨVom8HCPNn(N0vVP]q6A?> .X{%OP6ԥX:!;'A5Nk/8!4Gɰt|x+T~En٥@ sdAhxނxqʏ'3^\"0F%~1i#Báo-]|]-_>?t6b~7nF C_^ ǯk#rm2?@6߂3Zm c?i(=Fo:t8)#蠖δH|M% P8w5"x;6x_>?i( o9nu|/\#n:>K4>e-|XL|+~YIl_P{7%m6ATW S*OjH+τgp')@ tuK' ReFqpw)҅S,|bI#B߿.}]< HD ^kOnj*a8xym7j,LYxfn*gP8uӊկyr:c^[wݛ%\r䦛l_Yʟ|?=qNjOԟ$&кĸ/i(Y;3Pw`\`fM6',L7}Cw߽^?~ t 6(ff~7tS7~8`3SϏo[@ qtu+5߰K=y ^`t]{yw2XU3_##045FP|..zrJs14rH+3uz*|..z..z..z..z.9I 4~Um贸ۗ҉꣋ȬJ椓Nj(:r)K}=#x ԃ򐇔|a:3o~I4m ۜq7OEٺ\>"N^YqWIU=( u;Sycۼu+_W`4omݦT뮻yW*_ aVǾ3,K*76jZ˿{6nasG+!cÀ#8fm&,?3Llwy_\k׼5%믿 gy{Ӭ*e_je¨+=y͡Zpxc}N=fWl}{~n׾@s'M_KrO|KYΆ%o}[1O~c˟-X'i(}R `(#/ q,(_ՁWg>S7|B:Z_i)ӟ7Q!U:-4'[n D jp| F_Si&S~g=Yec -AAcX.0{ |ʀ@~ܧ}y0揣,~^<1F :GٷB?c(?R}٧@9 ۯ\`uaP:|+coئUS A|Oz/yKf-ROؼ3hb0$Co2i%X ui3[gќx}{_%FPlJ7_Kz֍]zWyӞVȓ[{S`wVXfT00ptqy+^Q/_01O8)~ʏ녾$?}@ } :;?]}pu9|# Y7e|ӡƱG>~-sLd!c??O5%R7{`Ѥc |Woe60ʘipk_Oz_ylDtb0=`b) -:f/00N0wïՏiHCY.bdžL*H8qAzGǺ_?qYSf f Y'[hġD!x8f-0d,4Ѱ1͝76Wc} /%N_ڱ/&(W1j^ 7N=XNӈ)v1^D~[lIM)}yRØ1O>IgF~vqb rlbz뭛? |ѦӋ~ӗ'mi+j(D oY|  ;(:i6)zKy–Ka)2bL1P޸g>?WL07t9 ,_g^]-J#?~R(?ළ2[e/+@cZk,MtNrA( 3ĜbfYĀ"L1L(*`D̊DDwofonyvw?g7===`P!@ 'P^<‰9ʛ!d?byw2g2/e h׼ ttd_+qi3N:[\&=VQn6<L' ֟ L[勖ځO\]KE>i#O;Q򥶝J~E|hnN';2GuTʧ/JPVj&s̏%]L^ 8iQּSW2k%Pڦρ5,h q ]񛏾a#mE}H>X՞viSu@.A5՟G#h7U38y5Q[:E}T8"/ɇZ<$tIPp I99`k'xj8&l>yjk|k/ϢA Es)ېA 4)K J@^jU'ie/?򬏺Py-Ͷ8:|BZ>՟x[ͮzT˒8"Y? !'.U&KS_[C ^Gڣ.qä{O2-Ǵ}q8xYgM]vQUKdՓj?GF[ѧ ˫|ߟvX"6ʏGꬩnǷ8h;ԁHU?ud>裧&AysIVԟ!o/_zTֺ %|Kg0cC||֚OS϶^V< BvIbHk-8hV LX^["88 ~KEGT[uYu͟.뫯 FۅUX|yyUAkn6uk_bEYIu?&:D}&\v =(,k8jmթө FP$5&#G  ۂzby@.E^'~xxE^l҅@: e'U;hR-xiñI~@: >;oIYW[-_u) tdy k Dj:lO-xia=j?HHGaa_A~ 6h6pv'( ͏*Hײmņ%-(M}饗6n DqgA ~SL{&s嗗vP@oQ bb7/n-[VnlwPi?99K73O"??˗/o.]:5^xtF KgQZqs :!6ԟD@Of/ ;o >zZ_߼ ok^җN#L}տT&@( փJl=y1GUzU|e] npfɒ%4'|rsm 6hGNQMXL7-''G=da+^񊭓:uyQ|..z..j<̗F]vu..'ˈr.~ԍՓ|IG 1Enz~nL@DN:8 &]zEMx^]wݵlˊquh,8hok3:EmK'E9PV[m՜p SY)c;'/ƕmQ+ަ'K'mQ+ަ'~bQN, >mzt"_6?BlmvE7?i䣇~5nr3ɯXy_^wxβeˊ@Y_ʶu.At]ge:E~lGf{к/vvk˛5c`٤I_OE w{p, ]ig0m>|O<(|X(ߦ[ߺ=p𮖥̟>/Kse5WU{]r ڇaJ1D?NwS;ngaLCF7jv5WՊZl/_|k_kN9唢G9<%c9l͚o}[eyhn~kfFGo~<9svjmu(G#H9r_܏}c͵}톝QSO=<?m^~_`tܧp nv&7j/6~P9ak6& 7ivR/Sw'h&u?X-o)ΜeӟԼ/lgeyP&Η<9i?2p3*LguV*Q]؍zXظ ?ZVwC[nesk\LO}S'?I/L,%fBp>rԡ:ub8s>`b-ʵ‰a֖hlqpi}ni'N򱎑m{6Gydq>!>z+^Q,׽uxdž{WL[eE~_?#; C>, a@P4!iuև?STy[Y3Щ_"c&ƕgr-px>6ӟjR}&a$m~_.g.,iO{Zqp>Oi`"Qx ^PV$񲗽VajЇ6Wc=L\ q:p[v|i@a;y T=?gQ&vy8;8K8t1oebg=&\x]vbSvJb<&WG\/,?Nmmrt5~q|%=vwhmb WT/8` m6|R$;GuTѡ{ ?%mCy]}lYឺҕ45NDzBߢT(O<3'/xss(Hqx <.]ZX[}3Ѳd@7h-X( `u#r@;$><hq$c^f%*hqF^'C[ x}]3Y`1<!V,9IquQ uP|^9e'V,î;48~GWq{o̗@ܫ_H G.S7ﰋ?dɓ>:PB' "ڹNcy1u i?p}\-C1;8ڇ2s#(L;Tug ~›.s |hg>xb92V&P mivGʤyޖW?"[Iiȇ"k_Y&P}x vz\ΧȓN;Aʹ88a8K1B#JAq7%;/řQC[ѷ8S#6,GC>,ۑq;1mm-폕y=9<@fՃFqE~W&s]Kx$9008\;eC/%j(O.<3q蒕uT'R&Fs,*ߺu>:ȧ׹uH-bO~Pk۶ip;gVl(||+WǺ>iT|(S9^v& W>k9ڀvZ%g2p(3Oyũ[+ڊ-(+'Mh=(A[onRgg`fō21nPVl:2aeVģlL1/gM`g375f `T\(C8 i|eqi[ks(m5X8||8o0_"$;+Dٙ++D9XN14]~eC Gі} 8GY1ʤ33#(ۨ׻޵Ї>I&oF֠̃Z>|yVNr則<jJ>Ae @rg|[˒&O>>y" _XsY-y/JӾ foH7 x<8mqq닳-6ԑs=d0 N|52_+a7Gi&ol~HG_g uHa|[ZxKNnn7Ne2IYpw!iˇs<]eUGQ|Y<qT2QF=u]SXRݍˏ4ƕՓ"GgGL8jl$o{_x[/y h:8E|V9#&cyh:FG.F|tq3_P2g(YozVdefyؘ8>4&xHdjqiK](A3X~ƭP4OYyi692P m,9>\%GڮDF - _ьo>yc >$k>y+zeӗ,'Ǚ~#n 3>Ta3+>8fX˷ , C-[ium"h|&|x"O]>@YTocpC<_@=02`18d$͉lVS<`R h݌{Qc]<8nWp <'<Cf~u %+q@]65u/PQ}C h}ԑ65Wׇ k3qV< 0[Fg2 ;65ɋg֗íE\+vS+ԋm-@J6<Bxtƣ|7 y͐Í8pHaP7R {p}y#okp/(z6X_Cq'p@Ypm>SBܞg\kuW4,<0؇ch|i>+v @+G =R:-YA0#g 8E~!%9-eu2m"||;03!Xo y袇,vϼZ4X%BAQh gXv,86S| yo&gW8K~[:|&Wa_9$,87ǪkeyL8fg71/'XiSlg4+R>}&"_cC+E)Zɔ7x\`ӧ ,;\ ]vp\f !ESo r'ѥorPڥv:ʏ[b>Oa|4qk~LqnxxwHGaΠ̂D : n;xf0(^҉6=yQ+lU=(ߦyQ+xN:1QwD~$g"o3|pˀ}]C:銷҉|eh늷҉|eh늷҉|eh늷aG"QjpC;ɝ#Rq!򍫳-M>r6ENZp0`.b>>-3[O+E ~5rvB+ Qzbğg[gsȁG M`jaS)~cI%0 @: a=HKNuLP3.ӱ 1ϸt>sg3<|8jd+צϗyx[kv8[TQ^~]M|]_@I*@: }#>0{Ʒ\m63߼ǢbR6>j mFjB/% B?8֊R`*uSG]EB_\H_Ǥ!BXR=B Ttg1?gQZarŠ@Ëq"%/'(q/q1m~_"ߴ`7C?惲nx&Eiyʑqo%~]gA ~p xM@V'g:''3qI,>i/ރWm|5NN1O&:i?΃ROZ 5pȥK/O#[mJ/( Ł*fIUo<شX$~#eb2( -켭 :BjO ǃLtFKf*pү(A봲VgƫN'~dYXV!夣A :RuW'(A!Hc<c ]˶IϾm+m&@"$ tU:&@"$cB 1fD H@: *eD H1!˜O@"$B@ p@"$. n_.6gD`tEF,e5(ԈdzNQwIYH" ?_|ϥOGac$CփM}饗1%x03ecOLN07?_~-A ~p(v.1 q"|"?WUfO3?r,_7Z u"hd8 ]vٴATGKIj\,c_X "Bx7u=y @Sr1?}u NG c1?N:N?CH<'WߤD dY}gn1?W=@: }#>0{Z5/:QI C'?Is痪|͛\*vmlFSխG}g6ovۭtM4'l9/Ym:ghn7o2<ڴ .hN9斷es+^6Ѯq?ַT=?isܦ\zYHpQxBѧG 1E& wÛ~|p@}ok6yCg?N;45·WxEo|u[pA|g_!'pB=Llu=?7t|?Kw3ՆGS~?͗5a]lxOC5$z0-UNwvuՓF9x[nesG˞^zi׿LGo}[10yT]r]|p2!-]t9P6 s4[oulF_Yڢyv\_]vk\ٷ]}˱~5~]'}۔?QAZj`\  =~Qֲ"oh;C{Zێ 9cG7 =a)<̃^&lkZ7w%.wivZ^v|͉'6{P~UzUUٯI˃^ve|qwGKmZg?.1=yφz?,=,x_/Luj4s|*wk\cQ>֕m5?эn񑺱:?\pE5ûoIGY#/7=iɐ% $ji5K?Mvف [V767;{_?xV&{^͗fm-x{<(T,c{_ |P_^?.2 11 ]bEC{qd/L{DԱȇG8<8 W;y#|C*+WCֵUΒ<h{ROۄw߽9ꨣ#CqGN;iU}cm?2Ism FLCΣ}sl <׾tC\rI>8}O nIӦii'y]r]Ǥ"; =8k qB:bEYQ|V_4˗/onwQim5d{pC ߚ=iaw(A&I&=&"ph^ז gÎ'=L; l.Q"k s0!V`r'nX=Z?QX֟IE| yʯFc7v(x$@~ĵ G1#pp xՁ]vJSn`%6 DԟȪOSq'FILzEyoJ._W%uQ<Ŏ0iq.}{3Lŷcǣ/v[~]Yn[+o>_QYcԼqIk>:OWtG]3a_ereV}>ǎ-]S+(kj2WGԓI<_D<2qO/q`mo[mïk ':ৣ(gga67*<[>w߸dia~1/Rt/5OZIy$(AXvccXWӑWS.%_(#o]iO>qfUDVBץA5eryD=qPCg˚@Y'%8{b[9@!ة0M>qJaZyR5͡R3Q𚧫?ڏ C^Գ N- ~LA M>Ȼҕ4&Gm'tRq8?AFJF}uKJqc;C?惲$@wQFj~Է^REzr8 Q6Nj=KbeHj-_[RdCgz:aԉqVN?qd̊3:Oỷ?ڪ':9e~N+'?ʶ/zri(x6x1K g~\:p9Sa OF v ]9ʍCT:@;2$D7]ƭOuZ_'se|: xrg#)l1 /'#6!+2K~8ȃ 7SxÃ7p'GSȅoucmE,ɟxm^ibg/yKū2bzkK(|fK7 h&xC`[_1-<p83|Q`!4goPWLeYϛq,^K sPeǭ?64V)vB@8nY4)R~Ҷ[(C^W%.] &q~&?7,#kLZx2B IJL2P&#R|+-Iq42q`؈qmF^] _|4[] B35*9D/{9keo &#^ў6:n:%oʕ^R;(ZfĪl]Af]Bj mcC?կʶ7}2kV^ qhl6$:4w&?6mOGa.P2!?=$uŧ]:_LKF4*ѥʴdOS]:_LKF4*ѥʴdOS]:_LKF4*ѥʴdOS]:_LKF4*ѥʴd>iHV99s۝8 R65 IϾ`3DZ"iփ@O>|"q ??AU@~HG_gɽn8΀yƥb:?$o1.3Ϫ+>HGl# Cx曗iC|?m}aBlGHG?iA)6~*uSA,ďތ+>UtFm<@`k 9:k561eYOtyV"1bܴHKI'J\K\Li~b7m~??(,2ƇG:`)|QZg^r$jĨ曆&~_W?C~HGk%[NFIy(tONfeY}~HG_g-0b&(k\:;b5M"Pgau~HGkk8K#`_nG0+C۔_QAZU;hJ-xiñI?I7GZˌĺe?QAZy[t1u802eS(AσU_;Q&VieSoƫN+%~=IߨKWn[}OҹG t%r[t{9 i~Dq ZN'~h[_i㩓g%KxY@: ==$SlA7oys94^xa|!AmMD` 7l6tfjnG!."ֳT%([;MdM-ܲ,\r%_vt" ,:pXoHG_aG8tŊ}li`d#D`)YaQXSqHGa`KQr/[lLz$@D1q!w":QX&pSoFIL㳡@"0t_Fb$ mE VXxSo9$@"0^6$@"$tɺ%@"$cF 1_4$@"gQW'$@"t||"$@"0HGa>_["$@"0fQH@"$|F |un@"$HGa '@"$tɺ%@"$cF ?< X?2XۚJ"!?-]B2_gp 6˗//4: BǸu~[2?HE1^!1T\u~[2E//cz%QAYaᆾKM6٤ B( BG-.O YbE\ !BX=7E]lewr8F>:$~κg VWarҥNMG'dGK.6;Tlj"|r?@%~ Q`_QXcPO^.xԭ㤣\LG~O_]!ϴ^/$#?x'NPO /瑦yftyVkjii-_k9ZNr|LKk:]˙u3-t-gZZZδӵii-_k9ZNr|LKk:]˙u3-t-gZZZδӵii-"x_tߵ2)Of)/' f#3UE ;y#5p72ebxĸx[lL/( Վ@b\pE>qC[^kIIڰI?n(?^"Bx+\0("4'''orž"ǁ"Bxx〮C_5aK,gp2qU%O/Ϫ]Pa0zmHPIS*OjH+}Bj0qek~HGmpIdZ ک0OݘN7Epeo>HGaB]< NH)W֞e3uLOGaLŬ!8Om7,'Vߙ[UϿ}#B߈̞Vlv͋N@sZQ-/I Z湃(MrHsqMePe˖5WU]j͞{κO=,~4oxͶn`\x ~|y׻|,RNIQ~mzl@: ==DS .#5ߴK7rJ?X)ze5:k$1%0ѮԼ:WN%WO%WO%WO%WO%WO%WO%'kN>䆾zRt_9i\_=i\o~_iC.AvtF-03ӥ]o}[;mh^7ǚk_͓bxJfN~7x3-t7xaAg:iU۠sx[Y.ߦ'/uŕmVNǙ :&wD~c\oal9OfV?C///_/P㱞u4"@( 98rn1X6_YɃF^'πnnf7k~ͩ}?Y|yN;5K,J~:fw.NWAl;m~:>yo7߼PG>"MxZ׺V3ce,߲RΊpNxG]~_W˃Z_B9s-MK.&\?YE]zի6lTGZ7x׸ƔNS5~Wo_Dr'_jVXQj󣞲.ƾ5^yO]2@: =8k mA<;s@IDAT VNZIs<<'> _iy\Ӵv?? 5yM04ycdJy|3&Wߜp G},NK_~5Oaӟַ-pGQ#Cnq[8#D/| =p.{l?Ɨ%I'"8ci < Wҕ'?Ksɜ<( Ƭ:)/OS=m݊]f+^^Lfb\Ǐ2!3e}s[&A&h&7MwR/LX]я~t?5{]`XN+x;o}[efg·^[ <06)'mIx#YbP_׻J_0lvSm_yz<]:)u>W&v2`q^yM7-ԛruAzFZE{SZvh7|;i5zjy IJ(y{^;N 5KݨNg`8 #/ՙI #Bʢv`P9?mZ>%3y:vrL?O4zAIq&g?e0 ^wop}߾8#GʊUzƩ+yLڼ"AM6٤fá`BҗT&~˄2)~ld}׻޵ȳa?;8V>+knc ~G`A]v٥lFX;0nw),ሿc-;8Gi^lK;pژ4 w-3PQu. mIV8Q8Xi׼fs߿\^Rb=b A'-g,aƱ>/JW2kҧ V`IɃ $+YV %t/:g۞Ic%(e7A=hG=yϴ|ÁCJ ( vXyr̺|ڷc`;cyc*|i[:]yevNK|;kaUy5>|ol?G>U=-V,Y.خhkCz'XyN 8?֟ vb, O1d<ԝz>d+\ ewMeѷ]IE w{p YQHu2Pi:2cֺrCp[jjtD>9gg~䙈jg`&ԭo}t9r(x\ՒO|wS<ئ.;MlE2% /|r.Xٲsj:\bQNyv##V\Qԏx+ 81~‡ !:[ڵ`@.H[au3>pҧ #P>N6֍(ƣq2tƃ`7i@t/5Ox8'<z' [٬ T$mRxy`=Bq8/m WJѧ 1 zv^M2ZN>YU˅g"4|smyLʼÃMνqq 3(k:کb?12#$y54 ?mbx :sȃ,ItI͋7?xHGa/zm'ǀD tB "m++E4lcs Vse'8hWʌ:Ǻ)<v Lg8 `Gs0rxHcY7Ǚ l'X(N(Cr ?u=Q΅s1e77xEL7[SZ*=ǺDQVA >R/EƸ4ʙ"0r -Rn0L1^'D^c>2KyV|3-UW'qw͏guzR!C]4̏1W敯|e>cGG}`qQ|%;tP)G}ԉ'mPSFe<Ϯ &qt>67͋`P~MC#xL|+&uCX@YN,8>@YxlAG5ҏ89fa]h?/EDȳ!zd`GY"O]yʨ6OYg$Ee UixQe<WȳjU_lxJe>T}l uuSLgG?M>L(!+uSie?9$N)^5ϴA!@Ƀ{+x>@qJagRVWGяef?QAZb`p80ȇ/ԕ7}ec㓵˵ >Dq>(#X)<ˑ7ȏT]y|wegE<<7nYm?Gz.Pz2$»]bD=&Ϯٖp]vaQ.#e+̉?AW>!YVlshC>e˗F{!M]7>%j!O{Ӧ^zX | Gn{ۖoIcfŷrh+A>WuE;1!Zߟ[HΤ?Z:Owq%,?NNzt1++VLPnmSWD-D3xvj-t @-u&eGiuk@k @&z&x.~7p.8N`\z3(6X$H1`5 Yn C ɤ!BXɆ8)zCЏmyh?ahl\3@: =8k]77"ʋg\P:Xț/NҬ¾IA ~pB%6-F}Ǜ ګ< &u016irp\?^@: 7-/RR҉Ge//Mv3@: c>( .ꆷXmr(_Vꙗ'1_O}ꐡQZᴼ ԧ͓OZ@ySr2/b@: =8kq6GY_A#o? د@: xtF`K&dM,8 /YbE L6<HfM7ݴr-m٦p u!)8'0'0f!5[oulٲ.k.tj2$0lfo^8 H2Q .ԕ; $ OduAd 6(C( 믓c%Tpcs@n6*[oqVjLD`5A`\ᗻ A4rF: #x P lu": SF(~= r0 R$HZ^( p>k-OnC>HD HF@: #6MD H(,mHD HF@: #6MD H(,mHD HF@: #6MD H(,mHD HF@: #6MD H(,mHD HF@~qDb|dD`}X{B#@1˘=-&ܒhm㵨Vl/hê׮ݔV__?+V(=Rǡ-(-YL 8 ]vYÿv 1n'Oh7O~ONN1qG=yʨqM߾ gֶ,_p Q#(PLqM `<㤢\~gtҩ>t0Z|[:%\lS I_^0MUN$~MGy(lF͒%K@: =<4LZ v Ā<~s#NP'S?30SHGan8Eyyj$~Q?έBG 1E$%i @;GqiOG RtVwZƥ?8dj0 T) 2l c<3Qe=y3陧lک1*%~陨f*;t,afZEԟx"&]aKvڰjkڙ@sŵ$40\ԕ0P:pAlw_"W,'W={'BU/񛜼,NJO/ 5)nnQ(CI,S~_>[ҥKvءqw޹+^\7k=c駟wqcfٲeb'9~衇6'|r7rHO/nZ=3?9Sˏy;xߦp݋kQאhD(BT2LLQ܀&B"$!TDQ %C!å׹}κ{{ιYT9f έR `ĸ@Edŏ:1&}Xz 7?_"k/؝ x,gZukBO׊tC@o~nʟ>r1Nէ<Go~溫=zh:ӆn?B QL'!>O_ _Bάֿ۴Y,ޭ KƁYoO:~篅p2q,i-Lw^ZtE?HQ_4]e9.Ag/ UXu]w x:^zIi7$(Ougy#vؑї._T?^} 'a2cMS5y ǂG-~|ͧޱi^%[2fgNǛCBsX+L:0|c* ",{48q3[~{>XW:miUWy5ċ}8#W^yepdo?dzWos=9w35\ ?Oi=믟X&?!<TĈS U<<=ir-y7dN\ި NzJ21/i)1^)]tKLLxt)S%gvúͮ)iP䑮/ҁ8S~ɗeS~oRۄki^2Jə~{K/yK:XQq8XpX9XB%~).}'|MnivT(G%\>wS>80[^T?Oiu\WrC/enoeL{̠{'xt"9\š`QGYyAQ-AfWx5p :2Qv2yjrnO<~}e%V؜'矟+|L`Hߟ?p '73Kn4g7>; PP^_8c/s>yT-|#R;S_Wd)(.Zgv(+NF̏:}íʂΎDi; C.>r@h6C=AVZiZ&OܙE`]m/|yN+B_HN +dˀG(nr* J;u-ޚPI/E&ѕsoDvA!IgC\$ӗlۡy(4y,2駆ǁx JGLk|Fx#>yOiVG?qF{Ĺu.sTD?v/_|I`yYe}p|zIFT-6'\sk҅9XzU:mb?+φjisQhVZ07 q|E׶񦂂QZ +^xU/bQ$K\3rC9 *GAm&?8S:%/駟9!dDC̾l{~~7n8`9>}weX W)!u#>3y`OHQ&ƫyklCH@GRKv`#H%*t%'C[&|"|ӑ"`GY[gnPETreZJNTeZJNTeZJNTeZJNTeZ|Qu ɉ*LχxȑDGF|G҃ʴxW~?AW87dtiu7pw.cL*+(LGYU(cuYVU-ivȇeGTeon^T^U^U~U~U~U~_TȴY|Y[gX J'FȗxJKO-u/򏍶v۴+mo1RST_K71brJ|P_^Sd7Bc*D xM (۸ `E]9Tcŗ$'#Zjl/ڧLUhY/%)|6諍"`GY[i4X3' 8k0g~ ,|&MԙoV\qtQG#<29gz뜎9Sx H/2d_wGY!I':7`!>P6~luEtMd`;4WHU^9h‹X~$gȄϪ:}>zD?0o(zpfCJv?1e:"1#2SV[q\q? ˍG}BrA; #*CGjxǸ(g'fGiQhY+'8qiwAĸEJ'7?qNGx#%OrK^|G941oEGʆWp$#4MOy֟H8 4~RoPf έ(ނ '-ȓxJ[2.gvtߛ6fnˆ/'/ 's)2={(=ƭPNZ Pu<"O2.rʫYf,ޭ'JFUE_c?Tay~:6fn5=;/AZALr7~?sm46fpnr  Hw6?CieF; KV|% l#EN:3[%E=X/ K7rivú5MxꩧҤIE8#D?1~;Sd`?(LfgԩiC3QhVYaQ`G!K*pJ&D#[YafáY(4wk19qSQ{[m!N]< $'iڥQm1 _&{?拇 A qh_D ̸37;?WFBӈ̞(I ϊ3<~ӟEvk,"E/zQ13Կ;C=Ygj&=iק[,J::rJ?+NV;#4o}+]s5oՈv?3;?pQZV}c-oyKg>u8ؐH쏆]wݕe]VYeN}vm8 O ړ':]m2( .u).ESSя~T";1@uYc/}KPYe/9::D^\_uru| ueZcZ%_iQɖ:D+)x']hNlV%_i::Dߴ9(4u+-qN'G]3aEM/|^jK;Xc,:O#Bg]|2t"N(W_DN'eFxx#DGA/H}VeN'Kru(_GS!PvwI-G-:/ud~+J @{uq`ӉЧ]6G>[ol+?^O7m(4u+-ikNiKăFp5Sa0}5x≎׿5]qimM -P瞛^z7SO?=կN{/|V෿mw;>oKW]uU҃>[wuomGT&{> y+iO/xd/~,Q/UJmYzK^)3G?Jzkzҋ_7!mVi9t?O+b` :u/:aJjnki~8};Iυ^8_;a*b+]xεpnH{oz^6p ρEt#ׄO뭷^G^WZG+ӯx+2`&h駟NsN[raG늓LU}D@X[&Bh~<%tG}]Bc<2!x1a}yWk;s:H2393)r!iw'muץ3 ,LL93 my|K_wtgfw]3utGɕÐ~?ϳN8! =?zsSgfmҥ^^te3>m%}{ͤ &~x>c`mMɓ'SO=5L5uQ9W/0U}y O~2/Ɏ<˗Lm>0UxtMO<1=گ~| }$sCS/~1NlC+ۑ%K~Lו2wQ]*" U\y,ËiWJ|:}Mey]ӇOog/ _jo2S&.'/&I&e[L]tQv>N:}(}Hkfց]+>zC9yg;.m喈2yՔIw=Lӡ$ÁQv?Op) ڏB9 HNrOu~cUI[o/IZ7N0S6N ;JD]!,z׾608,8? .`ޡZuG{I*?ȞX*EpT6d|p#;9);808(g}v/C=G>O'^LG#W9"fn5  hHq$ UԑJ^l=[ k6&ڢ}leO<<ر1[ƥe={/>w+;l3I>xvIjf%dG b-P3rLFqՠ~TWU?`c^]dcrqV8P| :* #8r+_ ypٙ'>ѹcBq!GzVN;-`ޱiN;-H =8 '} ʏ] w{yU&1p2a#Q8s'6TA<ٗ-ҊG*W9 fpntc2XU}Ϥ̯.0 wyyR6/)χxޭ2mV2N ޜЄ&;>t&x\ |xn|_x\O|p^]~9 VLQGC=4?c@ asV;-1~W<xDDi+@p!#o>Š 9Bymi?g897)M2SF?׉kHh]{鳟 xg?m]vh3;cis79?&V~IƲ:i:N7fpn80g0(JCW^UPŕ1Xr. (lKerd]ц|q8my&'(rby1K9%ɏ3 )aEȑȳ|x<hYl<  6a@ ĉnԏ#_qʝ&aʃ;guAW"qzXS_ɈbKX7E[NgSV_})9 ~7uɋSz(SY vypg>D(y%ˊ(OTyJCW^9t; 5V˛[iQ1 /IDAT|Q剖|edb۫6#Yd{]AeC'/ƕ.yU1|Q&ĕoԉqRB)CZy| _eQ&!I'*<bg4p8%ifQ1/]I8\܈!JHW-K=ҦN* /R9g@,ߟEd|)8;n,)Xl*/c\-<ɘv; þzQc5le|+̪>Qrx+I^>(O6O9!NVV^>[gM-+_PTǺigAcEeT*}ʥM49c#>8cT=Ayj+qJJxdɣ6p8G2]lWG[ąAcG]p\H,YRq oxpYu rx#'HAPbѮ+eē|,[ziw[cUh Ɉ*?Yէ,l@rR2!GЫkʓ>EGqQf;z˾dd9,V:[ ϙ#ͪ;|Rv WYڵ! }mli')S@ .x;}I_i(?Uj+9&W'LEŏ4ƑoU[utQ!e8f¨<Ųc\ȳÆsw9;i2+q_TGZg?ӗ]f[:k ӏ[*|C+|a98E}&s^Vٮ~ˆt|i\2U>gx  )W|QTҢ҃:,9[LV(8Aqق.;#m9>ߍ Ȇœ +W:oxO=T~ݓ,d$ˁԝw9;KT/9(e }qh; cJU@h OăUxJgi>_cf@lK3 .#KOehL|Hyj?lg<~L#YhO|1&c P>U=|&+78"/P&G|gAy4-Mx IVԅʁL2ؖO~YD}W| W9; 鋏wwdΨva#p8v"T?x}UxJSxPdLEBx yo4PF 4ɐ 3%;|$gLXmD`ǡdZڧ)ⱀ-S9xPbU"x2Q2D$ȷ5\{§,W|||U>b]>DeOP9P~<$ ;$@}sk*ku(;P8& ّɗvW:y !oA :C!>rOl+OOs\OiJ~ L$|nw|\GJ}F$̄O:O*:HRws}^ c oq U?yQcN|WgːqjAeDz>w"xnsʏT,q,+xVʬ<9Ѝ70=rq(q]Ye3iN;x9:}phFzuʟ-}I}y;G*L 슰Rg?YT/lŲ7eT/Ms=q,pWOCѧYt<8Oy̎ȱf QhbF=\]a+XM.Q'ƭ_@ĩ.^m0鱣0XIi,?zs"z!`G9[iIRl`T[A,㾬7c?wCaxU`!ǠTO?Vbgvbw(4sk0YiŠ@4/ƕ/RD %\V~GDBE|ivGB"~eëP XUr|VF#`z; =t1\#`0^"0FBB] W#`@!`G׮c0F'{bNU?#,P.kv!s/=]41.9D/~?DTF~UyD~[a"*\D#?2qU\'WǷsRwC=; cq-h5A`p PI'xS:R%c}>!tK'^ ǛAB38 m9& E%Ct*'ݘ~"u8y; cq- [ 8qwAdd??Px; ]-f5d|!h$nwwfoQhӠ]4 DokqJ-m}fmw 0G3flh2#.>(Oqa$37tS{<-iJ}{u]7CTϑկ~<Ƨ~:~_lo|ӞYixc=AiN=ԎyyT֬/~z\ A(v}ƸtGZ\G]+EK#<>m&?'?%H;C1:;?1};IO=T=\ lBWyõ$G?vcY:;uzŲ_ygW]u^k~te clCuGn]b?z0B I+>X\]|<^~wܑwoT9|%p ١8;}AAXA<#Q*~|Q.^eS:ȗlru*=t"_%E$ȇ|H| :P$#@c[W/dCQh趚0@ЫKNeY9묳pas̑>򑏤|0*wu]WUȖhi3e_<yuU/(G;ʯ⍤CO/U_9sJJG%ʓ~^Uco?zhZaЏҀ4'u~';uR=UG?'_<ٗ.R>q%2HhJ/y~>?G=Uՙrݗ\rɴvۥ|iҤI:DKv[Zh|U}O\r%sgv=t~ߤ;.z{ӫ_7*cG Gĕh'k⥜MEBxΚ5t48ţ,eD2-&6xLw3>22+j&f&^뮻׿8 Wʋ!Gu&_q\+OW\qE>S9 &t _i&}kiKO9NnڱO{qp6l<L7xc:co p:3)3s}ewd:rK?4瞉nmƂ:0ANtP$ :>8V[--Y~vPp p$?03`RQocB698 vpL?я3rcC[,9cJg>Evb>1.(yo<><歲AHґL]䆫+ol(c{'rﲎoxʼ̓]CxK}Y13ԟ}߂`w!DM8/|_8rl׿5Eᑯ:8$Ya瀃rT &ȗV;Lnz+?7Ңy^C™v=pX. h5\>:X7k~* >2 Tfvp`pK9Ci 6$Ot(و4!oA P "SzAd|#T_udeV{,Gyj y SSDj&e鋢d[8Q$¶;Tԧ ^xk:ȚkjsQ0[6L .`>/dgƓdGbU(2n9j^rT>sKăVǜш|k(;9Pp4i;q/N Auϣ&hmHy9A裏;(&Or'ʎ i8^WPŕD4TqEJCw1(-q*O䓎yXWҙ+ A4&]Vy欁xQ3AppPrgbvV(!=׬b `˚ a5gJ}K]ŸY9 k' ^8pzg$4!hU 1_+.ɣH|vo{ub>EVJ 9Y{ã#29Y3(C|鉖|edb^)1uv}+, sIA2/(pt]VDxIg WdK<㪨 L/zы:VQr ]x?x}Hs@qx8X^yy)'@Dr.@gIbD)y^.9љ3A Vԕ e^ɋ~l .)}ט'z׻:R'^3q>ؑ\ϟE]wh$/}6>gιd89юU^!@a*ǴY(4wi`! |"hS` 9=}N3s6< rڜʥls |x#c)09p_Qn(*KpPC#!}9&-2;< m3<37^*̾?pϮh 84ldWQheS}[֬.?|~rK}vH8w[6|߁WhyD ^1U( h٥O>ׂTGq0=tWԇRz21<΋İClvx['p]u~EW&/7RlGB㐷`ՠH4,qE'7|++#9L-jh9<=&*N-(ϐ7}Y Ӥ\ s\c?l-$#PV<Î:|l0 Y]JFe$3xMozS5aa }0M >,DѾlJ=,|EC^A<*!ȱѫT&ɘ3 U(h8$Q_*}񠳢q(4y 20hq`~']F.<6`0UyfeYBsg%&:$ƇtEVȱ-3e"iȁYg3CYi~ϳnNxD;ߙx'DG 78d8P8\cz&2>יAzɮhЍA|x`K'MšDrZib^|7._[K%d=5Zz\0i<(ArEyx8GA8K|^xa~E8dP*i?}e2j `\8GukΗ ϙ ˤ$k1 +lz4@ L;3$)$LWf889%x>ȎN+|vOّqcmvj\;ybڅ3SlAeḱ_0g;vЫ +Z'WǗh;6ܓU}Jmo)7yu Z.Ed̖8;]L7uSϪ>4$nlF~Q.xN:1QD~Q.xN:1QD~Q.xN:1QC+/9,7ΓÌy yȁ͘9BΌ# BU(2kӠD^KF:Nu\Bնn?;V"`GY[iM! As%&no ,H <TVd$)'aT?WOM vVkPH|҉<(̄s"`GY[g-0b&(tʴgtTq׏@?צ `G[kKh EO#C7ɴY(4w+Ł*5h J)coN#0d4;) 袋qN!5A;  9iK?|{3<C0F@0qĄ0y< vUVqQ MàV#0b4Cɂc„ yLqVvFC@+njVI 1#Pvp5l06TnhNB뻃0#F@DG\GCgš Pd-Na )#`z(ep%0F&/}7Z#`0F; ݾo0FU3F#m(t ؾ0FFB_W#`@+`F#`z; =|q\5#`0FB#`a(qՌ0Ft; ݾo0FOadydIENDB`libuv-v1.8.0/docs/src/stream.rst000066400000000000000000000171061263361716600166130ustar00rootroot00000000000000 .. _stream: :c:type:`uv_stream_t` --- Stream handle ======================================= Stream handles provide an abstraction of a duplex communication channel. :c:type:`uv_stream_t` is an abstract type, libuv provides 3 stream implementations in the for of :c:type:`uv_tcp_t`, :c:type:`uv_pipe_t` and :c:type:`uv_tty_t`. Data types ---------- .. c:type:: uv_stream_t Stream handle type. .. c:type:: uv_connect_t Connect request type. .. c:type:: uv_shutdown_t Shutdown request type. .. c:type:: uv_write_t Write request type. .. c:type:: void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) Callback called when data was read on a stream. `nread` is > 0 if there is data available or < 0 on error. When we've reached EOF, `nread` will be set to ``UV_EOF``. When `nread` < 0, the `buf` parameter might not point to a valid buffer; in that case `buf.len` and `buf.base` are both set to 0. .. note:: `nread` might be 0, which does *not* indicate an error or EOF. This is equivalent to ``EAGAIN`` or ``EWOULDBLOCK`` under ``read(2)``. The callee is responsible for stopping closing the stream when an error happens by calling :c:func:`uv_read_stop` or :c:func:`uv_close`. Trying to read from the stream again is undefined. The callee is responsible for freeing the buffer, libuv does not reuse it. The buffer may be a null buffer (where buf->base=NULL and buf->len=0) on error. .. c:type:: void (*uv_write_cb)(uv_write_t* req, int status) Callback called after data was written on a stream. `status` will be 0 in case of success, < 0 otherwise. .. c:type:: void (*uv_connect_cb)(uv_connect_t* req, int status) Callback called after a connection started by :c:func:`uv_connect` is done. `status` will be 0 in case of success, < 0 otherwise. .. c:type:: void (*uv_shutdown_cb)(uv_shutdown_t* req, int status) Callback called after s shutdown request has been completed. `status` will be 0 in case of success, < 0 otherwise. .. c:type:: void (*uv_connection_cb)(uv_stream_t* server, int status) Callback called when a stream server has received an incoming connection. The user can accept the connection by calling :c:func:`uv_accept`. `status` will be 0 in case of success, < 0 otherwise. Public members ^^^^^^^^^^^^^^ .. c:member:: size_t uv_stream_t.write_queue_size Contains the amount of queued bytes waiting to be sent. Readonly. .. c:member:: uv_stream_t* uv_connect_t.handle Pointer to the stream where this connection request is running. .. c:member:: uv_stream_t* uv_shutdown_t.handle Pointer to the stream where this shutdown request is running. .. c:member:: uv_stream_t* uv_write_t.handle Pointer to the stream where this write request is running. .. c:member:: uv_stream_t* uv_write_t.send_handle Pointer to the stream being sent using this write request.. .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) Shutdown the outgoing (write) side of a duplex stream. It waits for pending write requests to complete. The `handle` should refer to a initialized stream. `req` should be an uninitialized shutdown request struct. The `cb` is called after shutdown is complete. .. c:function:: int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) Start listening for incoming connections. `backlog` indicates the number of connections the kernel might queue, same as :man:`listen(2)`. When a new incoming connection is received the :c:type:`uv_connection_cb` callback is called. .. c:function:: int uv_accept(uv_stream_t* server, uv_stream_t* client) This call is used in conjunction with :c:func:`uv_listen` to accept incoming connections. Call this function after receiving a :c:type:`uv_connection_cb` to accept the connection. Before calling this function the client handle must be initialized. < 0 return value indicates an error. When the :c:type:`uv_connection_cb` callback is called it is guaranteed that this function will complete successfully the first time. If you attempt to use it more than once, it may fail. It is suggested to only call this function once per :c:type:`uv_connection_cb` call. .. note:: `server` and `client` must be handles running on the same loop. .. c:function:: int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) Read data from an incoming stream. The :c:type:`uv_read_cb` callback will be made several times until there is no more data to read or :c:func:`uv_read_stop` is called. .. c:function:: int uv_read_stop(uv_stream_t*) Stop reading data from the stream. The :c:type:`uv_read_cb` callback will no longer be called. This function is idempotent and may be safely called on a stopped stream. .. c:function:: int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) Write data to stream. Buffers are written in order. Example: :: uv_buf_t a[] = { { .base = "1", .len = 1 }, { .base = "2", .len = 1 } }; uv_buf_t b[] = { { .base = "3", .len = 1 }, { .base = "4", .len = 1 } }; uv_write_t req1; uv_write_t req2; /* writes "1234" */ uv_write(&req1, stream, a, 2); uv_write(&req2, stream, b, 2); .. c:function:: int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb) Extended write function for sending handles over a pipe. The pipe must be initialized with `ipc` == 1. .. note:: `send_handle` must be a TCP socket or pipe, which is a server or a connection (listening or connected state). Bound sockets or pipes will be assumed to be servers. .. c:function:: int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs) Same as :c:func:`uv_write`, but won't queue a write request if it can't be completed immediately. Will return either: * > 0: number of bytes written (can be less than the supplied buffer size). * < 0: negative error code (``UV_EAGAIN`` is returned if no data can be sent immediately). .. c:function:: int uv_is_readable(const uv_stream_t* handle) Returns 1 if the stream is readable, 0 otherwise. .. c:function:: int uv_is_writable(const uv_stream_t* handle) Returns 1 if the stream is writable, 0 otherwise. .. c:function:: int uv_stream_set_blocking(uv_stream_t* handle, int blocking) Enable or disable blocking mode for a stream. When blocking mode is enabled all writes complete synchronously. The interface remains unchanged otherwise, e.g. completion or failure of the operation will still be reported through a callback which is made asynchronously. .. warning:: Relying too much on this API is not recommended. It is likely to change significantly in the future. Currently only works on Windows for :c:type:`uv_pipe_t` handles. On UNIX platforms, all :c:type:`uv_stream_t` handles are supported. Also libuv currently makes no ordering guarantee when the blocking mode is changed after write requests have already been submitted. Therefore it is recommended to set the blocking mode immediately after opening or creating the stream. .. versionchanged:: 1.4.0 UNIX implementation added. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/tcp.rst000066400000000000000000000075251263361716600161120ustar00rootroot00000000000000 .. _tcp: :c:type:`uv_tcp_t` --- TCP handle ================================= TCP handles are used to represent both TCP streams and servers. :c:type:`uv_tcp_t` is a 'subclass' of :c:type:`uv_stream_t`. Data types ---------- .. c:type:: uv_tcp_t TCP handle type. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_stream_t` members also apply. API --- .. c:function:: int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) Initialize the handle. No socket is created as of yet. .. c:function:: int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) Initialize the handle with the specified flags. At the moment only the lower 8 bits of the `flags` parameter are used as the socket domain. A socket will be created for the given domain. If the specified domain is ``AF_UNSPEC`` no socket is created, just like :c:func:`uv_tcp_init`. .. versionadded:: 1.7.0 .. c:function:: int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) Open an existing file descriptor or SOCKET as a TCP handle. .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. .. note:: The passed file descriptor or SOCKET is not checked for its type, but it's required that it represents a valid stream socket. .. c:function:: int uv_tcp_nodelay(uv_tcp_t* handle, int enable) Enable / disable Nagle's algorithm. .. c:function:: int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) Enable / disable TCP keep-alive. `delay` is the initial delay in seconds, ignored when `enable` is zero. .. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) Enable / disable simultaneous asynchronous accept requests that are queued by the operating system when listening for new TCP connections. This setting is used to tune a TCP server for the desired performance. Having simultaneous accepts can significantly improve the rate of accepting connections (which is why it is enabled by default) but may lead to uneven load distribution in multi-process setups. .. c:function:: int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags) Bind the handle to an address and port. `addr` should point to an initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``. When the port is already taken, you can expect to see an ``UV_EADDRINUSE`` error from either :c:func:`uv_tcp_bind`, :c:func:`uv_listen` or :c:func:`uv_tcp_connect`. That is, a successful call to this function does not guarantee that the call to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well. `flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support is disabled and only IPv6 is used. .. c:function:: int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) Get the current address to which the handle is bound. `addr` must point to a valid and big enough chunk of memory, ``struct sockaddr_storage`` is recommended for IPv4 and IPv6 support. .. c:function:: int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) Get the address of the peer connected to the handle. `addr` must point to a valid and big enough chunk of memory, ``struct sockaddr_storage`` is recommended for IPv4 and IPv6 support. .. c:function:: int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, uv_connect_cb cb) Establish an IPv4 or IPv6 TCP connection. Provide an initialized TCP handle and an uninitialized :c:type:`uv_connect_t`. `addr` should point to an initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``. The callback is made when the connection has been established or when a connection error happened. .. seealso:: The :c:type:`uv_stream_t` API functions also apply. libuv-v1.8.0/docs/src/threading.rst000066400000000000000000000106771263361716600172730ustar00rootroot00000000000000 .. _threading: Threading and synchronization utilities ======================================= libuv provides cross-platform implementations for multiple threading and synchronization primitives. The API largely follows the pthreads API. Data types ---------- .. c:type:: uv_thread_t Thread data type. .. c:type:: void (*uv_thread_cb)(void* arg) Callback that is invoked to initialize thread execution. `arg` is the same value that was passed to :c:func:`uv_thread_create`. .. c:type:: uv_key_t Thread-local key data type. .. c:type:: uv_once_t Once-only initializer data type. .. c:type:: uv_mutex_t Mutex data type. .. c:type:: uv_rwlock_t Read-write lock data type. .. c:type:: uv_sem_t Semaphore data type. .. c:type:: uv_cond_t Condition data type. .. c:type:: uv_barrier_t Barrier data type. API --- Threads ^^^^^^^ .. c:function:: int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg) .. versionchanged:: 1.4.1 returns a UV_E* error code on failure .. c:function:: uv_thread_t uv_thread_self(void) .. c:function:: int uv_thread_join(uv_thread_t *tid) .. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) Thread-local storage ^^^^^^^^^^^^^^^^^^^^ .. note:: The total thread-local storage size may be limited. That is, it may not be possible to create many TLS keys. .. c:function:: int uv_key_create(uv_key_t* key) .. c:function:: void uv_key_delete(uv_key_t* key) .. c:function:: void* uv_key_get(uv_key_t* key) .. c:function:: void uv_key_set(uv_key_t* key, void* value) Once-only initialization ^^^^^^^^^^^^^^^^^^^^^^^^ Runs a function once and only once. Concurrent calls to :c:func:`uv_once` with the same guard will block all callers except one (it's unspecified which one). The guard should be initialized statically with the UV_ONCE_INIT macro. .. c:function:: void uv_once(uv_once_t* guard, void (*callback)(void)) Mutex locks ^^^^^^^^^^^ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. c:function:: int uv_mutex_init(uv_mutex_t* handle) .. c:function:: void uv_mutex_destroy(uv_mutex_t* handle) .. c:function:: void uv_mutex_lock(uv_mutex_t* handle) .. c:function:: int uv_mutex_trylock(uv_mutex_t* handle) .. c:function:: void uv_mutex_unlock(uv_mutex_t* handle) Read-write locks ^^^^^^^^^^^^^^^^ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. c:function:: int uv_rwlock_init(uv_rwlock_t* rwlock) .. c:function:: void uv_rwlock_destroy(uv_rwlock_t* rwlock) .. c:function:: void uv_rwlock_rdlock(uv_rwlock_t* rwlock) .. c:function:: int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) .. c:function:: void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) .. c:function:: void uv_rwlock_wrlock(uv_rwlock_t* rwlock) .. c:function:: int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) .. c:function:: void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) Semaphores ^^^^^^^^^^ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. c:function:: int uv_sem_init(uv_sem_t* sem, unsigned int value) .. c:function:: void uv_sem_destroy(uv_sem_t* sem) .. c:function:: void uv_sem_post(uv_sem_t* sem) .. c:function:: void uv_sem_wait(uv_sem_t* sem) .. c:function:: int uv_sem_trywait(uv_sem_t* sem) Conditions ^^^^^^^^^^ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. note:: Callers should be prepared to deal with spurious wakeups on :c:func:`uv_cond_wait` and :c:func:`uv_cond_timedwait`. .. c:function:: int uv_cond_init(uv_cond_t* cond) .. c:function:: void uv_cond_destroy(uv_cond_t* cond) .. c:function:: void uv_cond_signal(uv_cond_t* cond) .. c:function:: void uv_cond_broadcast(uv_cond_t* cond) .. c:function:: void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) .. c:function:: int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) Barriers ^^^^^^^^ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. note:: :c:func:`uv_barrier_wait` returns a value > 0 to an arbitrarily chosen "serializer" thread to facilitate cleanup, i.e. :: if (uv_barrier_wait(&barrier) > 0) uv_barrier_destroy(&barrier); .. c:function:: int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) .. c:function:: void uv_barrier_destroy(uv_barrier_t* barrier) .. c:function:: int uv_barrier_wait(uv_barrier_t* barrier) libuv-v1.8.0/docs/src/threadpool.rst000066400000000000000000000041601263361716600174550ustar00rootroot00000000000000 .. _threadpool: Thread pool work scheduling =========================== libuv provides a threadpool which can be used to run user code and get notified in the loop thread. This thread pool is internally used to run all filesystem operations, as well as getaddrinfo and getnameinfo requests. Its default size is 4, but it can be changed at startup time by setting the ``UV_THREADPOOL_SIZE`` environment variable to any value (the absolute maximum is 128). The threadpool is global and shared across all event loops. When a particular function makes use of the threadpool (i.e. when using :c:func:`uv_queue_work`) libuv preallocates and initializes the maximum number of threads allowed by ``UV_THREADPOOL_SIZE``. This causes a relatively minor memory overhead (~1MB for 128 threads) but increases the performance of threading at runtime. .. note:: Note that even though a global thread pool which is shared across all events loops is used, the functions are not thread safe. Data types ---------- .. c:type:: uv_work_t Work request type. .. c:type:: void (*uv_work_cb)(uv_work_t* req) Callback passed to :c:func:`uv_queue_work` which will be run on the thread pool. .. c:type:: void (*uv_after_work_cb)(uv_work_t* req, int status) Callback passed to :c:func:`uv_queue_work` which will be called on the loop thread after the work on the threadpool has been completed. If the work was cancelled using :c:func:`uv_cancel` `status` will be ``UV_ECANCELED``. Public members ^^^^^^^^^^^^^^ .. c:member:: uv_loop_t* uv_work_t.loop Loop that started this request and where completion will be reported. Readonly. .. seealso:: The :c:type:`uv_req_t` members also apply. API --- .. c:function:: int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb) Initializes a work request which will run the given `work_cb` in a thread from the threadpool. Once `work_cb` is completed, `after_work_cb` will be called on the loop thread. This request can be cancelled with :c:func:`uv_cancel`. .. seealso:: The :c:type:`uv_req_t` API functions also apply. libuv-v1.8.0/docs/src/timer.rst000066400000000000000000000044261263361716600164410ustar00rootroot00000000000000 .. _timer: :c:type:`uv_timer_t` --- Timer handle ===================================== Timer handles are used to schedule callbacks to be called in the future. Data types ---------- .. c:type:: uv_timer_t Timer handle type. .. c:type:: void (*uv_timer_cb)(uv_timer_t* handle) Type definition for callback passed to :c:func:`uv_timer_start`. Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) Initialize the handle. .. c:function:: int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat) Start the timer. `timeout` and `repeat` are in milliseconds. If `timeout` is zero, the callback fires on the next event loop iteration. If `repeat` is non-zero, the callback fires first after `timeout` milliseconds and then repeatedly after `repeat` milliseconds. .. c:function:: int uv_timer_stop(uv_timer_t* handle) Stop the timer, the callback will not be called anymore. .. c:function:: int uv_timer_again(uv_timer_t* handle) Stop the timer, and if it is repeating restart it using the repeat value as the timeout. If the timer has never been started before it returns UV_EINVAL. .. c:function:: void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) Set the repeat interval value in milliseconds. The timer will be scheduled to run on the given interval, regardless of the callback execution duration, and will follow normal timer semantics in the case of a time-slice overrun. For example, if a 50ms repeating timer first runs for 17ms, it will be scheduled to run again 33ms later. If other tasks consume more than the 33ms following the first timer callback, then the callback will run as soon as possible. .. note:: If the repeat value is set from a timer callback it does not immediately take effect. If the timer was non-repeating before, it will have been stopped. If it was repeating, then the old repeat value will have been used to schedule the next timeout. .. c:function:: uint64_t uv_timer_get_repeat(const uv_timer_t* handle) Get the timer repeat value. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/tty.rst000066400000000000000000000046501263361716600161400ustar00rootroot00000000000000 .. _tty: :c:type:`uv_tty_t` --- TTY handle ================================= TTY handles represent a stream for the console. :c:type:`uv_tty_t` is a 'subclass' of :c:type:`uv_stream_t`. Data types ---------- .. c:type:: uv_tty_t TTY handle type. .. c:type:: uv_tty_mode_t .. versionadded:: 1.2.0 TTY mode type: :: typedef enum { /* Initial/normal terminal mode */ UV_TTY_MODE_NORMAL, /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */ UV_TTY_MODE_RAW, /* Binary-safe I/O mode for IPC (Unix-only) */ UV_TTY_MODE_IO } uv_tty_mode_t; Public members ^^^^^^^^^^^^^^ N/A .. seealso:: The :c:type:`uv_stream_t` members also apply. API --- .. c:function:: int uv_tty_init(uv_loop_t* loop, uv_tty_t* handle, uv_file fd, int readable) Initialize a new TTY stream with the given file descriptor. Usually the file descriptor will be: * 0 = stdin * 1 = stdout * 2 = stderr `readable`, specifies if you plan on calling :c:func:`uv_read_start` with this stream. stdin is readable, stdout is not. On Unix this function will try to open ``/dev/tty`` and use it if the passed file descriptor refers to a TTY. This lets libuv put the tty in non-blocking mode without affecting other processes that share the tty. .. note:: If opening ``/dev/tty`` fails, libuv falls back to blocking writes for non-readable TTY streams. .. versionchanged:: 1.5.0: trying to initialize a TTY stream with a file descriptor that refers to a file returns `UV_EINVAL` on UNIX. .. c:function:: int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode) .. versionchanged:: 1.2.0: the mode is specified as a :c:type:`uv_tty_mode_t` value. Set the TTY using the specified terminal mode. .. c:function:: int uv_tty_reset_mode(void) To be called when the program exits. Resets TTY settings to default values for the next process to take over. This function is async signal-safe on Unix platforms but can fail with error code ``UV_EBUSY`` if you call it when execution is inside :c:func:`uv_tty_set_mode`. .. c:function:: int uv_tty_get_winsize(uv_tty_t* handle, int* width, int* height) Gets the current Window size. On success it returns 0. .. seealso:: The :c:type:`uv_stream_t` API functions also apply. libuv-v1.8.0/docs/src/udp.rst000066400000000000000000000235171263361716600161130ustar00rootroot00000000000000 .. _udp: :c:type:`uv_udp_t` --- UDP handle ================================= UDP handles encapsulate UDP communication for both clients and servers. Data types ---------- .. c:type:: uv_udp_t UDP handle type. .. c:type:: uv_udp_send_t UDP send request type. .. c:type:: uv_udp_flags Flags used in :c:func:`uv_udp_bind` and :c:type:`uv_udp_recv_cb`.. :: enum uv_udp_flags { /* Disables dual stack mode. */ UV_UDP_IPV6ONLY = 1, /* * Indicates message was truncated because read buffer was too small. The * remainder was discarded by the OS. Used in uv_udp_recv_cb. */ UV_UDP_PARTIAL = 2, /* * Indicates if SO_REUSEADDR will be set when binding the handle in * uv_udp_bind. * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that * multiple threads or processes can bind to the same address without error * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ UV_UDP_REUSEADDR = 4 }; .. c:type:: void (*uv_udp_send_cb)(uv_udp_send_t* req, int status) Type definition for callback passed to :c:func:`uv_udp_send`, which is called after the data was sent. .. c:type:: void (*uv_udp_recv_cb)(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) Type definition for callback passed to :c:func:`uv_udp_recv_start`, which is called when the endpoint receives data. * `handle`: UDP handle * `nread`: Number of bytes that have been received. 0 if there is no more data to read. You may discard or repurpose the read buffer. Note that 0 may also mean that an empty datagram was received (in this case `addr` is not NULL). < 0 if a transmission error was detected. * `buf`: :c:type:`uv_buf_t` with the received data. * `addr`: ``struct sockaddr*`` containing the address of the sender. Can be NULL. Valid for the duration of the callback only. * `flags`: One or more or'ed UV_UDP_* constants. Right now only ``UV_UDP_PARTIAL`` is used. .. note:: The receive callback will be called with `nread` == 0 and `addr` == NULL when there is nothing to read, and with `nread` == 0 and `addr` != NULL when an empty UDP packet is received. .. c:type:: uv_membership Membership type for a multicast address. :: typedef enum { UV_LEAVE_GROUP = 0, UV_JOIN_GROUP } uv_membership; Public members ^^^^^^^^^^^^^^ .. c:member:: size_t uv_udp_t.send_queue_size Number of bytes queued for sending. This field strictly shows how much information is currently queued. .. c:member:: size_t uv_udp_t.send_queue_count Number of send requests currently in the queue awaiting to be processed. .. c:member:: uv_udp_t* uv_udp_send_t.handle UDP handle where this send request is taking place. .. seealso:: The :c:type:`uv_handle_t` members also apply. API --- .. c:function:: int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) Initialize a new UDP handle. The actual socket is created lazily. Returns 0 on success. .. c:function:: int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) Initialize the handle with the specified flags. At the moment the lower 8 bits of the `flags` parameter are used as the socket domain. A socket will be created for the given domain. If the specified domain is ``AF_UNSPEC`` no socket is created, just like :c:func:`uv_udp_init`. .. versionadded:: 1.7.0 .. c:function:: int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) Opens an existing file descriptor or Windows SOCKET as a UDP handle. Unix only: The only requirement of the `sock` argument is that it follows the datagram contract (works in unconnected mode, supports sendmsg()/recvmsg(), etc). In other words, other datagram-type sockets like raw sockets or netlink sockets can also be passed to this function. .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. .. note:: The passed file descriptor or SOCKET is not checked for its type, but it's required that it represents a valid datagram socket. .. c:function:: int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) Bind the UDP handle to an IP address and port. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param addr: `struct sockaddr_in` or `struct sockaddr_in6` with the address and port to bind to. :param flags: Indicate how the socket will be bound, ``UV_UDP_IPV6ONLY`` and ``UV_UDP_REUSEADDR`` are supported. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen) Get the local IP and port of the UDP handle. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init` and bound. :param name: Pointer to the structure to be filled with the address data. In order to support IPv4 and IPv6 `struct sockaddr_storage` should be used. :param namelen: On input it indicates the data of the `name` field. On output it indicates how much of it was filled. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership) Set membership for a multicast address :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param multicast_addr: Multicast address to set membership for. :param interface_addr: Interface address. :param membership: Should be ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) Set IP multicast loop flag. Makes multicast packets loop back to local sockets. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param on: 1 for on, 0 for off. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) Set the multicast ttl. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param ttl: 1 through 255. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) Set the multicast interface to send or receive data on. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param interface_addr: interface address. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_set_broadcast(uv_udp_t* handle, int on) Set broadcast on or off. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param on: 1 for on, 0 for off. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_set_ttl(uv_udp_t* handle, int ttl) Set the time to live. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param ttl: 1 through 255. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, uv_udp_send_cb send_cb) Send data over the UDP socket. If the socket has not previously been bound with :c:func:`uv_udp_bind` it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address) and a random port number. :param req: UDP request handle. Need not be initialized. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param bufs: List of buffers to send. :param nbufs: Number of buffers in `bufs`. :param addr: `struct sockaddr_in` or `struct sockaddr_in6` with the address and port of the remote peer. :param send_cb: Callback to invoke when the data has been sent out. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr) Same as :c:func:`uv_udp_send`, but won't queue a send request if it can't be completed immediately. :returns: >= 0: number of bytes sent (it matches the given buffer size). < 0: negative error code (``UV_EAGAIN`` is returned when the message can't be sent immediately). .. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) Prepare for receiving data. If the socket has not previously been bound with :c:func:`uv_udp_bind` it is bound to 0.0.0.0 (the "all interfaces" IPv4 address) and a random port number. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :param alloc_cb: Callback to invoke when temporary storage is needed. :param recv_cb: Callback to invoke with received data. :returns: 0 on success, or an error code < 0 on failure. .. c:function:: int uv_udp_recv_stop(uv_udp_t* handle) Stop listening for incoming datagrams. :param handle: UDP handle. Should have been initialized with :c:func:`uv_udp_init`. :returns: 0 on success, or an error code < 0 on failure. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. libuv-v1.8.0/docs/src/version.rst000066400000000000000000000027271263361716600170100ustar00rootroot00000000000000 .. _version: Version-checking macros and functions ===================================== Starting with version 1.0.0 libuv follows the `semantic versioning`_ scheme. This means that new APIs can be introduced throughout the lifetime of a major release. In this section you'll find all macros and functions that will allow you to write or compile code conditionally, in order to work with multiple libuv versions. .. _semantic versioning: http://semver.org Macros ------ .. c:macro:: UV_VERSION_MAJOR libuv version's major number. .. c:macro:: UV_VERSION_MINOR libuv version's minor number. .. c:macro:: UV_VERSION_PATCH libuv version's patch number. .. c:macro:: UV_VERSION_IS_RELEASE Set to 1 to indicate a release version of libuv, 0 for a development snapshot. .. c:macro:: UV_VERSION_SUFFIX libuv version suffix. Certain development releases such as Release Candidates might have a suffix such as "rc". .. c:macro:: UV_VERSION_HEX Returns the libuv version packed into a single integer. 8 bits are used for each component, with the patch number stored in the 8 least significant bits. E.g. for libuv 1.2.3 this would be 0x010203. .. versionadded:: 1.7.0 Functions --------- .. c:function:: unsigned int uv_version(void) Returns :c:macro:`UV_VERSION_HEX`. .. c:function:: const char* uv_version_string(void) Returns the libuv version number as a string. For non-release versions the version suffix is included. libuv-v1.8.0/gyp_uv.py000077500000000000000000000053011263361716600147270ustar00rootroot00000000000000#!/usr/bin/env python import os import platform import sys try: import multiprocessing.synchronize gyp_parallel_support = True except ImportError: gyp_parallel_support = False CC = os.environ.get('CC', 'cc') script_dir = os.path.dirname(__file__) uv_root = os.path.normpath(script_dir) output_dir = os.path.join(os.path.abspath(uv_root), 'out') sys.path.insert(0, os.path.join(uv_root, 'build', 'gyp', 'pylib')) try: import gyp except ImportError: print('You need to install gyp in build/gyp first. See the README.') sys.exit(42) def host_arch(): machine = platform.machine() if machine == 'i386': return 'ia32' if machine == 'x86_64': return 'x64' if machine.startswith('arm'): return 'arm' if machine.startswith('mips'): return 'mips' return machine # Return as-is and hope for the best. def run_gyp(args): rc = gyp.main(args) if rc != 0: print 'Error running GYP' sys.exit(rc) if __name__ == '__main__': args = sys.argv[1:] # GYP bug. # On msvs it will crash if it gets an absolute path. # On Mac/make it will crash if it doesn't get an absolute path. if sys.platform == 'win32': args.append(os.path.join(uv_root, 'uv.gyp')) common_fn = os.path.join(uv_root, 'common.gypi') options_fn = os.path.join(uv_root, 'options.gypi') # we force vs 2010 over 2008 which would otherwise be the default for gyp if not os.environ.get('GYP_MSVS_VERSION'): os.environ['GYP_MSVS_VERSION'] = '2010' else: args.append(os.path.join(os.path.abspath(uv_root), 'uv.gyp')) common_fn = os.path.join(os.path.abspath(uv_root), 'common.gypi') options_fn = os.path.join(os.path.abspath(uv_root), 'options.gypi') if os.path.exists(common_fn): args.extend(['-I', common_fn]) if os.path.exists(options_fn): args.extend(['-I', options_fn]) args.append('--depth=' + uv_root) # There's a bug with windows which doesn't allow this feature. if sys.platform != 'win32': if '-f' not in args: args.extend('-f make'.split()) if 'eclipse' not in args and 'ninja' not in args: args.extend(['-Goutput_dir=' + output_dir]) args.extend(['--generator-output', output_dir]) if not any(a.startswith('-Dhost_arch=') for a in args): args.append('-Dhost_arch=%s' % host_arch()) if not any(a.startswith('-Dtarget_arch=') for a in args): args.append('-Dtarget_arch=%s' % host_arch()) if not any(a.startswith('-Duv_library=') for a in args): args.append('-Duv_library=static_library') # Some platforms (OpenBSD for example) don't have multiprocessing.synchronize # so gyp must be run with --no-parallel if not gyp_parallel_support: args.append('--no-parallel') gyp_args = list(args) print gyp_args run_gyp(gyp_args) libuv-v1.8.0/img/000077500000000000000000000000001263361716600136165ustar00rootroot00000000000000libuv-v1.8.0/img/banner.png000066400000000000000000001261061263361716600155770ustar00rootroot00000000000000PNG  IHDR3 pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FqIDATxmP[w'~͓ecLn89'!ۙͽu3v1ڹ5'teýoRe"5f(3΍v=|ؗ 8t0`;B`g5DDDDDLP$%]aog Z-LJBgg'>Ν w>rTU@UUU2d LLPpCV_MԠ:;;n;7lO*>|xOpLLP@پ^hڈ |ÁsX,_XZZ;39hhǡCqD*\7Ƚ1@& :q’ᴷ BF}}=>BA Vɀ? DDDD9»dol6[Ǚ3+U5xz>}˿: ^ ~&\)LQv. oa.~jҪ*8 4@\jqD wl,--+\xccܵ'~Z/YuܛDDDDDLRuj]%[[[_|1lkia@ N'n6 077n:[_YܓDDDDD)x]xn} Z!#]]ݰl?f3w嗏رc(`gz @DDDDLP non}[OV [,>}n|Zoy@Çرc!6 p1@DDDDL0 fnܸqxMM Nhߛa2pyۚf?]k`2}RZL2N'144G$ ]f"@M8|LQ(.ii 7n[t&\jëQog9>hii]AqѦtZ """"b ֭oPaw:hl|  QKKKx"\CREK|MpIdG௃gH!${zzPSSz /p\I:T*U,Y@"@DDDD.u_j.~H|y|GI @}[ƅ +qNC8} "M)"ej]#""""b f"D񺻻x+pj];M2S) w%\r%R~RSSl6tuu~4 ~O 2Ԁ~qZq Ń K/|.>}&-?៚{ Q}VvYA.( &;b!"""""&/;N ٞtW&׾# 9'D̝p7+պ8n.q1պ<%bu[=ۃsSb/2j enii }><ݬ&XkDDDDDLb`A9|y\x1Rp6& &)w u7l`V!C3338} \)I7`#""""Ju.!<>X,tttڵT*u܏W]]`˦uo֭oىz|e.~ %+3/3 ׮ THd-WSS `0!Ӊ>,ڴwj"""""&4YD[ZZg}>,c!|NBRIٳ= w<po1-0 =ۓeR%U`zz*j'L =ۃp7{+1 j]a:{:8M6Ī6] %H:US .r o^Xoff&k^l>,#jqD O,'V\. z]uB/fŨUX,l1ݶUYBX2B6xqo1 ߿?>}Fs#,O@V'Yp:r >|DΛ"""""b@jhh}Q֍⋁np8|SZmLy%& N>;nrj])DDDDDLH%ou--- ľ0͘za_@Nc ;9N|Gv PǽEDDDD@&_Bxϲ҅ "w8 Kt:,188iJ@ V%gŮg}%/]ru: Z- Μ9^&@)`_"""""&S]s?łӧτ4dn0/6qZ-Μ9. s”\Z?"""""b  2_X,[f34t8 ͛o57 9$@rJ2es@"""""&R :Gsn,b]] 1Nw[F Os@&Hj.~Ff-x a N>.VMqO1u/^ĕ+WpM0p?۲ {xǭ[2 @DDDD@ʂKb":@l [̷ttYxb\)DDDDDLl7p'%'t~@̽^zKf/ŋKDDDDm;ٳ=lM_|1h`zzwޘn_؈7l Y֭o#%.1 @DDDDLж>:%$t?OMMt/tJJZuBؗqoŰ/*DZe """"+B/>g#===_ du*[h8DpBSl ƴYɓ+*X@DDDD`9|gAj{0lZ6@\ϕLUt~`Ñqݿ+>]KKK t"K/5e{ok6TȆ$@u"""""b@K~@t:g_z{bjp8鿻;llL}@_}l9_ gwE"""""&}tsss;2p800)_el6'N5?Xj:T76:/L1h>n}o]BDDDD\yG _r\6k׆^]ӡc{1]p@…ۮ0|r%хehytܺUEiDDDD<q$q9 ٌӧτ==g0Wׄ\jq鄓 'WPܑ}ݿ^) BKuQwUAuidbjj SSSadժם8Z޾) O`u49w SΝAyyU$kVn#""""ɹ oӿ/ `HZ/$TU]r/@&uY. i e'Z3gN+i_|lX,0 Q{H1TRTʕ+bWuXEDDDDLHպ D:suD z{6-j8,Kă}_orr[АU) 1 vt)ݻ==t:C([hZMP-$+ҵ4 all\/SPCL\Zހkjp8 q=}*:?etLx"/.%eDDDD|yl<Lw #4H֔m6([0 gzSDӈ 4r@JfYVL0Lvm8\TE0 i0з;1;Y +5oJ:?Or<wiQ"""""I+yay)K U55I}\Ik׆pF ϟ-ѝ-B~ ԯA܂ەBGƿQcʬ}9?kϥoСhhKub qDDDD$UY[m0 (o/l \0L!Վ+I ݮ<߈l]hz}y-_2 -izSzŀ0 DON#"""")=L| 9|y.q\o~EДmhcT7$׮h㬩 B}}=/>~?/DDDD@]BВ33wp ٿ0r Ս.Я%<«)Du Ս^S>)D]]AhpvavRwF סP>oG@Y{*]P1& of=.ZwJKc ٗ1z.9Uf%nrS@R_f.q*1BbN/^JUYл1zfe V_/]V ntaڬiH*[xCT"n0ec!ܫycܮ2Db6+tt6Q`2})}W_ZwJ]y!Vśoj׮  5)Y0Q9⿒,--ahhoVU @DDDDL0/HАlKWcF p{‘o]<֍K0~Sl5sADmnc$kGsp8ۇ0^EcKp:{3BF8XJZښ5558}T/t:tww`0?`B!?Ѧ'iY V5w=lFnj7P_߀Ç_,|q*1 KJ31Z{1npn4EQo w,-Z8X0k_Tk9|{ "4:a jk!T;hAOOV55ոpbosAEVHA2ۏe+nI =j:榅 h ]˒xL]=Z"7pرUt?7DDDD6Y=/>sss+[h>H]m?5] M儮Jgh?ttNX,4N{+& qCmLM3Hchvo`j,/Ν Z,CDDDD'獷Zu~|+WehZD *t?wǵ_(79 >O@SkkOX a`ڬFOͅ;rg71=!_og>doԾ~nWhzzp^?eV]0 i`$hjەjK1~S!c$'8aV1x)%""""N6 Bt:14$h q{]$7<ؗKcZq@?Y!Q_'S܁Fƿv=' Ͽ#Mqn:N\r x""""b ~_044?Le;+ߨ1vSR&xv_vW_1 4=F+ƔY FU]EuKOߗK~qÇ/Y=zx1Z;_a-0V{؀ovw'1; d|:6 B^Y l6ڵaLMMTGi!MVGöPE7[}3;YwSCh6p)\"7V\-/p6˶m7w0;Yo{Mh6#/c-m~`ڬ;a_Ώxi\v*b'fK|hz6yqz f'I]BX Rxbܛ-5 /Ʃ!y1ʨ1pַxc _ADDDDLD.Jvu[Mbw-bLUxØFi5e~_lN'$/| Q=%*a 0S׮h Gbi+W**3j]$""""&"FKv)[kx6ʧS8a.qaZlnʸ)ۄff**8N*f'ܮ"|rm _ ",W9WcaɦDW'WPnaڷg–*HKL2mb#¨u}yK^qlCf  mOFWܣBIԟL|_hUr (:jh󓰣+I/?IntV b{K%>V5]y}}gB i2.B?9mV;ۻ@S r>>5~Kƾ6+#=0 iB&"L L- BiHWAp<+bc[BCdWX{ik2Ttxl mZw؀ . ͈ЈPSPJ²uɢݿ^\}a);$iH 'W|=I pqh^*%FCv ;$`/""""bպ؁=/ |Ô<ە+>8b2z!rs/Yc,+ DK]y"XKŴ AI <~S>*P(pIN(DԟAlp`BEaڼӞr+Ѱ0f> 7wlY6B'*ی8o_nT5B嫔-{~pP \ ֥"WKc~g}wD<4e8p$J)Ҹ\a}!o&J!(uu1KHlܮF_)x_-g[(L 6,nWFbv8-1vT{cR+~J_ؒlHDDDDYk73s'eK@5?"ѠMXQd 0ZgWQ}m Zm`J|r_99OBdoO^ۥ)%R1!LQRHh[ڂ/[)}]7yHBu }̮ݿM&2ԯA_bƑ(G`v|^݅:"H Awɕ b*B:izG}DfjjBaRWS7N=%e5lii cchh`.**Y@)6!m[!""@@ӫT7<$?wFlgP]! gJ"V;f'1vSP.yƩr]y}ķ`"]7zx4;Y!t5O8ݿl);q1:]}}g҂t5%^ݵ_Rc|,8xzRidd="" )VbyP,4{ho'8::@=`*v;7\ ݞ6+1W"m 0 ipý |7hz?4 k/ Bg;n} |1Qv%ľ4b g%ZS m3z ݌Ѧ'4m-^!`~q9oh_ 1wmq|?ӫ@_j%yDDDDD&)m,--aff&O(~BnW\mp>ޕF-Zݰ-bWZR{c7U׺qv5tB-½"M kWp!DB+it4-z(hcڬLi+VRƍxci&""""&boK5F@DЃms{qFb:&|UT0?[KPv© vmh2nWJ_rRM&vnW^Ԋ M&^q@_p߄tԯ0mVF/$LCkh.v:ڻ/n9DDDD$2PLH똆&*[ppo \< m~=.Vő׮bʮ=g8>5uOF]Qu4w$*@HTTljllMM_Z**MӁC =YZZ\e\PWgN;r`([׺}d-c7U۞`Ҷ_'x%t~pVlD@ jG w%Bmƴ0 P6$"""mh@=AS}&':cDR:]ŴY1mzi2Unu |Nwתݿ)2*#Y~ 5_fʬ {]y^ ǝI;Fh>CN """L Ctd%=s% TPS+MB+ߚMwl`G¾%cߨE)RtDT7p2^@DDDD)m/Xm? qito<"$J:?_ ۘѾ=0 iV{BrDh$2`.ꐱ.+JGXRPp@I܋# TM(Dt~`'7xø?st:/n#""""Y& 2uL u&gywGۏ۟H pgxJ>r G. qaIyDDDDDKf9δU #հ{d4T(ݿ+//72xpvy6L!M&ڻQ-&eF~-> cbxVQ20 ׮{/)݄B5 1(Ǫntan\R}9cߨ1($ɢPnR1R{tOVǣMO0;Y,ڄPLVqw}jR-;o0;vAB N33wp<""""JT|A:+<([@IxhF Pvԯk6Q_Վ ܮIH IB"?$-DVb""""JDYZZRڞܖ/$-{u.M&-ؗvU]M:F*kە+I}FW@@2GV|`h D/ L:P{&x edd=LD@u@elG)M<"19P-5MdqMM&8f%vm&uZŽE)΀C8r*U@WyP:o (9 *u\KIU67`&L##YOu/YHM H:G<_:o?kI@dk7?[9$}v{FړB!ؗ=ٙ~ mhcj)n@_&|y2տMUU ?C W }!!0422<Ynk}#|R̒fO$|LA2KzI*&7#$J"$R]ntR`ڬ+p0-mzmV"D%hl /u4!!hl%z"lC#JʉTWOe"U(qM{ rv:*k8DvFݾHv%$C*Ƕ`)@1/5[zƖ.AAdH9`Pזk2x͗SiM? isą`P{#zALۧcߨz k8=&I>D93/%&(]-k̪DFݛȞu%};i6}%jJGvaꮴ@,}y& ;d%FY[=!̥iR##ÖlK|g |r%% U)W0*{ƿQ4KGxՍPj.xĉr&y_ʒ_,D(H -G>GKeZ$NӉaLT s0eVf$`0L#H_Z0#6?XI2e~o_Lk\N˹ ִbU 4a. }}zII_dnNt/x:/5[iRxV j##+~3\ t=QڢojV> 뾀϶>u?=.4RԎ%"S-UǣOct3-@3ķ/?k?C xw3VŒ= ܮ<R! bY\V(|MwhImJ|w}g_CMc, Qug@ 5oi71ۧHYFFM٘06e}9Ecj)] B7,fX}StE%F+kx%t~pV;^ n{1mVmF~-lY%b em_ʽ,0dnPۗX'ǒj@gAl?½{wvmMTp@FpF (;k)56GGcIהm-”YF|^-BNiz!Mgf5W |=6 phlAd"UmNeo /RNCY@B!-"j_iԏB)w6޷ JZ2}6=7Q+GKpxÔ_]}$lA)SuE nI iQj4 g Lmdk./qo(g~,_Ge G}IOܮ<^p:V;6=iHR`[(/G{rة%T&Fgs]y+ۿ]JY_OʩÇ_@D95#J~'ڐ}dpNd_+Hg mnW^HvavRRc\D5z4$w4s+kBݮ}yB!ʔ<^ذw:FVNY}DcƖOFF i/MX 8NIYg2+EW7pv5)(9Lr oz?.){^*+ߨ}yBrJ@D]e(g% K>C6 oJ*-<:_ffH-ђ_h 7?  ł8v:6? ̳GaʞJMܮΉ"q0zuWQ8--‘Ծ/6=iH+jO({' QOdd6R u;dP9O&!;_JK _ۻS }-{%̤p m+kݜ A)7~SKD{f{Nqoj4p@ԁ9DDh3[ lH2rY 3 `BVՑٞ_ C} qnܮ)mdo$V_-ѝq%ǚO>6kO{ >}9ܮ<5}>O""qg0@022wW?ᕗBnRʳ wD?<+J`.$`HiWh5~S͏8z}9yⳄij$DAO/&2˾oԘ6+㪚Q(uhch63NH 3ӥ $""hlyϻ: -nz7 M/ϦW8j%C.N:0YaXp8r6 a[(Mq\M[/&-T]M{SJ'$JDx*ڐ&[Dqp*i2(Fdd\ u:^z5!MOO/޷'NNj5,?t/`Xnjq\Zpll6[~º]yM5pRh~c71-x5s7Jܾ3JH4|""7L\ -H8f4) JM;Y,ZvᏗvo;p|kjp8pڰNuj55վϟx5a!AkIp8`2}UUB^-U4Hyyq0mVbu^([٢!ls&KQlH23CLhKeC>f%6= DRE kb~N|V5<azNS/&>Rԯhc/bnEۯ]6\ސk5 j~?.i"YAD1|U~_\cC:\gK)jIx Iu@H`f$R_(Mx0AT 5pdtF 54p:#ow4]z`޽1?7l_BG;iKg0зM4‾vUBgyćX >tg~1<XBAX~S=0zu4e8$A-mz¯'D$Ut6 hlӯUh]6 G \JMcMh6x':-h}ccc h [ VKB 6bjjOxr>LC4_G5ݿ X  Ƶס}~#iMg0eV4v({;0mV42F*;gz?'IxF mx罟=@*$##VHyA‡vPj I"i^ylv==] a[( ;=NP(|QsʘT׮j9-ѝ-Bɕi7R2o5eE:M,~3@V4ASFc9xF#0T!,V')}(vx-9!Vp4&W 6Aj6A5ѐ.R4Y?.hIFaF m^ a>ڻH6BG vIs 6TA)Y/y8-UuR|?Mc)SV$d2]RVB.{s£6 f9bYzbMf$c Hp8.C)!k/h3l"4:}~ݛBGEP]yTdq@e7dv8 R_ DKu/0-.AM-2N`P^1[!ʛ6Hou ). J) +l)\&U FÍǓt[G[} <*^3@B6}\6sJl9)Q(*bIDBXBK1O4667f_x?i`ʭgl +@6,GQ@{~ Hs2XR*yD\p@J\nW2/Qt_ Z6K/:㝫p8Z0;$v :m?/}^|QZvtN|qyH ~IgGb}"ʤa`eO'rR}?Ia5OHe)0VÎzձ/K{'Nd V#Mx>}J?GM2$zd4Axƙ\0 i22ە==Zpv:~I6scݿ8"ʤsRm5d_2@rtYmK<ޝ}ȾbAi$_,KFZXm VXm;#Vb_{ׇHCl)x't0zeWZsB!BV+ntk/Ky%")K@)O)A oJ[%s9 Ss6 j:i? V~7 PPm )G^2w! Dv*l6[e -a/wV#p:o6g[(ww5:0|rEVd6[/MJ$;T`4BHs$ \zNMM_b ؛L&>}8vww'F <'D6vm8lvnõJD K~k,ݞ ĺ*~+?ȪVQX?yȈRfH֖-c@N2Z,>}^ V}mjhUi&ע*I ${H+>z4v4IkPnAB+d1?Ј(崱##&Hɑ7"4!@9|::N%%x_( ܶD ·|oKq D{LkV@gf9 jbJؗ/gx]2@D'ux"vedNJ "B@PPSSp]Sjf9$ RC,A*U_}푶wm߱oI=_•ZY/]A i2S,X1@IM@$G\ ϙ NJ \Kˠ?hѦģrHp+ $B*hu8!+<BTo Y€؜l/ԻC.THn;? hpDY$Ǎ&-9TӋd2Ht21 @bڵQxV R#h {z$N8}ŪbgotD[L!gђ& _@{{i2)~ꮐn91_#{K$&ExRF#i LJu|AUe/d)hA c #%jjcN"mb}b2`0D_^GdLΐw5Z9q+[!SX@DYkB'o_)O/~ o[[>W6%**$T*rGoooT:j@{ڰhGX$Z-qm|ccc@}EES BOOOf{~!vؗյ~~GKB7N=̩s``C+/"(d:_` #6X ak>W<&Lj & O2 9bbݿ]! v UhOMM$ WDOQ@^ӀiZttgĪܮ<^- #([9uYc>w( |-0𗇞 {povFVrߟ⭬He^ ~1 a$JAH7іhli4WX&"}1=|߽b&mZs-T8G(PWZ{w)C~} +c7Co|r%JFfe̕O"&('KtZex,2x)Jci".s2*[H< Ë́0>|?^U5u_[Hbi8:$ntASɓv>˴¼ BYDoG(Zrim<ɓV!_L077Ƿ/ec[!TeoSL8{ <E]my NOdW_[^_0?On7;Y71O /ieSf%dx$**K7I wQj O-f_6$yT\aY~JG dǨT*{I˅Xs'5; ùy_]ݏ&#/CϡfϳU^ڷ7%`ʬ +g M&0x}9f%].6$J)W]@YDV$,^ɓmKaVڑ$k|yX6 gZO=x& BK vv)e]a!SfUFʂ_d"lx##&EWӌ O$]mFcKik1`@LE>պ(ۖb.(Sws-؁[ TeP4d9=Q>6w`͑n/xi.|N*Bi2?Gc'WQƒ@DDFF-FcˠD6$ @&9mIW۳ ]JojiA3;`sarɁ`J 8 \Nܫ*_(vL-/lh@lgT!""9CM$sT/IW`A$ϴxn{UE},v&z7>}?W ua>^;P<\fϳl}!q?~jX$WZ! "p 7BimLDpTU໖dgT\-{1/R&?j;~>:_'!R_K&L<"DM Q: nWY`4AIq ?/UWu){)aV|K%pSwCQw{սe߾[/϶P T́ݿ 6[ܝ,N+-I ?DɘDOtNpamgZU~Ilˋxq7$B_pUE-տ\սީQ_5 gԯP!2d1 >N (ZQoG(xWްIlӒ1 M222<@lB>d/u#4 Gf5{J?Ӧ|_ڷ˗x G-kShsQmy,d>ڔ?& P<#n_o𣙿[mrW_#EDD%]/p,:toNHDm)ro6} jTT|ͣ@DDOHp ۸o_D63i+ J)wo\)\ͷW]JԖUܮjMfZKȶq> e>*?$m*5[3/r3@yy9T*N.A+~xԃ'}皯Е_7/Xogi+o7hܮn>aVHA &481q+,^ܥ Xo;so{2^ܥ_(,,/(.׮d }ۏiI(E\FFWƖ~HoI6T!_T>wl KO|=g忇Gl^ДmD >LO8t G>}s@/l:QҖgAPV\gSdk[DA'5D*&xsW$i&_g q_XJi#?lrX _;_| nI S=%?O;@VVP&ɶ??DѦ9n׎n:t """sIoC`6 iطŕ+PwHQc}/>M._mfl ۏxއ""""Hxd9'pUUT*N'߮Q²%Cϥ"@H8L.9xFm5%zw).G͞ .%O?RUP_A| jre.vG [ƖAHmܕ##&k>|('/>jԃ'aGՅ-/N^U!^ܥVYTK+<1-ʓ6&**1@DDD I0nCiR,'G&!_\ث*_z/uab;B T]ʀ@_QF|Dzm~¹&~L#"iJчFJD$!uD7ڦRmddxB 3$,VN}Hʄԅ xi.}RVG\r$R#{ ? |RF>Iѭ? vHp?Qu )c >˅א/* UUU|RF7#yO*[Gi**jxA$G?%V_^'v Q,OHlJƖ:o>'FF'd cNة]S%5T8=P(PMː?JL]@$9R~, 'D&r)/ѮI@3*?CQs(e/?ALByy9ߦV-7;k|I/Rk#EDHZK- *L)LXHC+6tS'|5G>~o%)cw'%=" WUSDhJ ("pI#PQoe@$6VZR1l3iԽ%/_<ȣDa[&˒סGAy׵_ឈ(gkImgsSb+ UvW)}:ƚ#QU.:\.}U{AyOQ] Ɓ18]yI/V>cl&=YGNƖ6lD#Zz{H# -Pʓ8ve)T#*c [qCNё{ct:466~_s$AF -kƁoَ3gN`0\+M6lƸ_q'a~7I}LE'MrmRHɰA zD0a!M @;¨ JkxOPZ_=tvwwTl`V<諾 ƃ _aܮ|i[Te=>kW !I/\ɾl7fڵп:zz΢ Z6S܁JlnW^ʃm[M=Q.&򙷝̠}E&7R>s1)-9-##ÃLHOVS))"7U=UhxDt555P*>```Ev}} x`5Z<1}e)ߡ[žXɎRD#c2?5$թ$w^̕r{$Ԙ嗏ARmJq]ڴDMEۭ |eˆ}O٘}p\$ta7N=A¶PjS6_ǐ#$6'<-$vB>乾r:xf0!9_i':ߦb4>K/5\'+x9} I,&)a޵yQT_S0[gɺ$x`T*(fZ+bۻ`RǴܿo !$ ` \D9}Ml63GWW7L&h޵\\0~S_٢?fFw4]V gԺ\o6 lL0V O<\,ƖOY`4S !pKgp6|fU_%7q9 lw;VFFy= ,Vb?߇Ppַ<@wH9 [`,e0p8|]SXP+(\6e }z{D;3mWveiϷ8fZ3gNTJVU{7wbp8p8 wʭ?nWLC }9_|0@5WŮ_N,llFD,-%@|Hx8Cp?5zcqxDs<:`?xd-Bi峀MUU0;fPSSp8pvWub_}e^BG1 giRP_}Χ7Y*I~D;(YHFFMFcٱ$ h(M@Eždsz-MX/ƚ#tykc؊|xVWc1 Hl6h__ۿ]ôYRYuG({Ue:ܗEt"JCKpjl0$@P>|yDs8؁GVOVqDK)| [(lHu:.\8x:]Z;^l N A߿{\yjlCe㨷ٓעw*m ԇĝˑ$Jl(glJx|[oel=b·ttt4pˢ uttZ6m_/H Ac! .~\ۣ%-ەB!gd]Q8Vs$x0pQjgwqw?e,跳222<#e }\0 h!gZZx\Zl{ b͆DT7)G˿}9? ~$  qp=Na}9n"{e<× SrY,G AWL/ҋM*_H,X,lQ*kY???[Ӑ\ڠߟuUٿ 7 &`A?[޹_#+);䄦}; ZZZx`)=g- R@!nz Q+E]߉R 'W`ooa[(̉sR_ɨ}bW\_(9<ӽo ~@adk8 ~KU>|/| n}#,CEMh.7CG"9QĮ5(BZFww7zzz|ۭ (5PCS˞r/ p9s:f X,YiX*-a-۵#gxi䣨;}>$rٿ ( ρLLFc9P+'aLSe0K'" UU{V~o%eXpjtww\fUpl8} ׮ 3~ ]|rE}97ո^|B>-4(}2?tOn 1QN@B VGF?PZy<M تT*N=UfB*Cn#`>? $XX i_|17/Z/,W_,/4 ۻNX݉]uR>N!$j &2aPze+Ro? ;_/:Ole唻fYDSj٬=N,䰴ەݘ,ɖ;!In"Js%$,!z6˥!`Hhgg'H-Z73 9 }Ojz`jj*l~c,#m/Kɟ}9WKqC-Gj_/MD)5122#ϤAHhj6 x X%IOPBE#`>1>’~K G^Sv? ؾb Tlnt$y6+vDKv::?G(?MJRpQ[ >c@BYBU@p*i*xOP|kc؊V tSۗP0La; qNт\-@ Yl;K1'x"ʽ/=Ҁ$/2ǘ,0(ma@<$b+½UvW{;ȇ'N-x֐ j.Gcccm,'lOmДm S^&NMI>^cw/MT9I]x)$TI}3/6ʀy[;UĹ/?Vf…mK=W($w.ŵ?C)U,_ 71@dc,ddA`'M8|ZyӨ@ׯ9E w^HZ6ઽeOC@|jj }ad2]5ڻ%1߾/WƩ8qԯ`hu|ݮ#?\ p?VKq5{n,؁"&]A~oo_tC=ݮ<4ހхhc40 ixw=@{׃~}9ep A|=5&z9%i@bH;;K֦:OwTyA'6_բ=1{hP] H4N>k׆nv݇7ո7=Ir Go~Ӑ}{_ ?ѩcx~bWط½,/- | /fӗ{oLǷH?GT=:r׿G}3 s 7V} j?\WpOhԔnU@]bvUቭ(1::Ҕm䣌;aI?!הmhckP(`[(/f-`WnхWДmn뱞Įԛl$i7[&|d}]F/[)O>%FcEbK_(;* 3V6ܞοuu皚 :VꄶJF/R+>]P(0mVbUqꡯRb5/DE>~ > e8Ɵz/ɈA @H`Jy!/ M$zZƙ3}6vhw_W7>eZ +'H|r]8m=mE,ƞp=//+/R$5o>$ܔCx;%/$-\22B+*}j. _W8{KKK<R`0`0w0-`l;쿦SSS" !\:DlKyqZd1FBG_8EGGbڬI)D~ kW]y-`~V?³)/| /bw=%kFcYxI|5-YMr`822c4G&q֜ o G2SY* Gt:yF$ZFG;Pjl GU*uH/Bpd2,|?É?32߶Pݮ< HB~ k׮n߶PbGEZUUN8?+&o"7JI\] gt0Ͼ ٕ<@tVS_ o.^3"}%/q_H ƵZԈV_FcccNhb ;] \p޵y*l +'M&q@ʃk^Ѿb}Byy9~3_X9?:o |! $2[*y$DoGX?1'&)ycx` J,kjj·v@ة)b 4P*$f%LCݿ8wR}bL,><{r+\>_2UR!ع7MIx-MNBI']<)ԯ%5vaڬyw6p1~^4E/}yͻX2_^=E" %X2heg{$|ڼ?RH93@BIE]ى{㝈s;::Vpth޽ڀDN  .oCLMM… Q_[~ oz(l-k8wC_N~/TU9!MPb7{ϻpo/$?{ȍ~dA,xVR #)9ƃϠƖ:xϠ{|'^shly-COc۰ӧ{;$RxQt:L&kZzS!U_~pDNÅ E ;_ӡ,j5l6b G54:3LbTǽحxԿ駜ODrS<8+~_'KRcZƉ-aڵkjl)?o_/~7"n\p/n+[8pq@kMSMDDDD${Wg~5A{5C ۿbsH\6,:o2}vNhlmTFݮ<ܾ^8nKt `cnQB!j6qq)g쳀M L[Ktvvŋ9O,?~nllVfZ b*/y Áᰣ]yRo)NT̒~`[({]Ww{gkl?z~OFev:;"x$eV?1I_~dߩS8>|w7lHLMM`0ܮ_}eM&\6*7Ǽ54\IZ򉚝,iHlc_·}9*k8ss;τ _c""""6y!IY~IΜSS UHSGl6z{® xn]2ۗ1з;tntڻD wQi_""""FONNU `6_G;%ww80LU55hllM\68|}l uvτ"*tᅯ_4;ƾQcaV/'%M4Jd[ODDDD/(G[:ѫ|sH~ Mл}~q?d1NcvR%@_%vOQ`YbI zm~UTu?^6 _b]VPp8Ƶk1הmh빧M5LC~r$HS nOՆu+B{wv*:J UUU+TU ߓd""""b CI+Wt50z4fvZf?Xv[=@ꁿەѫ0;YO)[׺qv)1?[cSUU T*ݲ """"b CI9|yY'4qW$C~ 5NI4~qV{ʏߴYwFs-gH_lK'ѝ)ot)ۄv5eR7rr/%4o?2vS6![oe}1D%;N|y55\/ 2^_Ák{Kĥ¶P2'@S䘌TGeu1 $g-fffdz-w-G ?̇BW= >R 'Wu 'W8nWf'm0;;;}+sYٶ """"&ҟpV캋/֭oe6?A+[;+%I)D+kWva~ȷ_,JN44ԇ _7 """"b 3Ix2Ս.]E~-\H3G܂vg]"ɀUO%kG-7D[& % P||g^!RI\l ]2<#+ټ """"&2&t---Vi6Qʊi 姩u[^LH @]uNW\e_AqOqh6ah3PTx뭷5szre0@DDDDLH' B hZlnl?d n |ǎ'x>A3@DDDDL0@ɀ6$N'ff0! J*>|'h劊}ܫLLPJ!!077{l(觪 UUpaTUHd_0g 3@DDDDL0@JDtbn/) '*{F`,g Ja2@M tΝ,--aiT TUUARCP*={P^^}+< """"bҝ0xBB Ľ{sBUA>J% -<Ә """"b&갍)* ╢`> """"bdyBR@LTT3,!& ֤@_RK,_gT` """"&% _/oooa ?1'"""JeO_ AWM1H$} """""& """""""b """""""& """""""& """""""b """""""& """""""b & """""""& """""""̖/IENDB`libuv-v1.8.0/img/logos.svg000066400000000000000000001772241263361716600154770ustar00rootroot00000000000000 image/svg+xml libuv-v1.8.0/include/000077500000000000000000000000001263361716600144655ustar00rootroot00000000000000libuv-v1.8.0/include/android-ifaddrs.h000066400000000000000000000034751263361716600177010ustar00rootroot00000000000000/* * Copyright (c) 1995, 1999 * Berkeley Software Design, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp */ #ifndef _IFADDRS_H_ #define _IFADDRS_H_ struct ifaddrs { struct ifaddrs *ifa_next; char *ifa_name; unsigned int ifa_flags; struct sockaddr *ifa_addr; struct sockaddr *ifa_netmask; struct sockaddr *ifa_dstaddr; void *ifa_data; }; /* * This may have been defined in . Note that if is * to be included it must be included before this header file. */ #ifndef ifa_broadaddr #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ #endif #include __BEGIN_DECLS extern int getifaddrs(struct ifaddrs **ifap); extern void freeifaddrs(struct ifaddrs *ifa); __END_DECLS #endif libuv-v1.8.0/include/pthread-fixes.h000066400000000000000000000055611263361716600174100ustar00rootroot00000000000000/* Copyright (c) 2013, Sony Mobile Communications AB * Copyright (c) 2012, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H #define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H #include /*Android doesn't provide pthread_barrier_t for now.*/ #ifndef PTHREAD_BARRIER_SERIAL_THREAD /* Anything except 0 will do here.*/ #define PTHREAD_BARRIER_SERIAL_THREAD 0x12345 typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; unsigned count; } pthread_barrier_t; int pthread_barrier_init(pthread_barrier_t* barrier, const void* barrier_attr, unsigned count); int pthread_barrier_wait(pthread_barrier_t* barrier); int pthread_barrier_destroy(pthread_barrier_t *barrier); #endif /* defined(PTHREAD_BARRIER_SERIAL_THREAD) */ int pthread_yield(void); /* Workaround pthread_sigmask() returning EINVAL on versions < 4.1 by * replacing all calls to pthread_sigmask with sigprocmask. See: * https://android.googlesource.com/platform/bionic/+/9bf330b5 * https://code.google.com/p/android/issues/detail?id=15337 */ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); #ifdef pthread_sigmask #undef pthread_sigmask #endif #define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset) #endif /* GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H */ libuv-v1.8.0/include/stdint-msvc2008.h000066400000000000000000000170601263361716600174270ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] libuv-v1.8.0/include/tree.h000066400000000000000000001472311263361716600156050ustar00rootroot00000000000000/*- * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UV_TREE_H_ #define UV_TREE_H_ #ifndef UV__UNUSED # if __GNUC__ # define UV__UNUSED __attribute__((unused)) # else # define UV__UNUSED # endif #endif /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (/*CONSTCOND*/ 0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (/*CONSTCOND*/ 0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (/*CONSTCOND*/ 0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (/*CONSTCOND*/ 0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (/*CONSTCOND*/ 0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (/*CONSTCOND*/ 0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (/*CONSTCOND*/ 0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (/*CONSTCOND*/ 0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (/*CONSTCOND*/ 0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (/*CONSTCOND*/ 0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (/*CONSTCOND*/ 0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, UV__UNUSED static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, UV__UNUSED static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) != NULL && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field); \ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field); \ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field); \ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, \ struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field); \ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)) \ != NULL) \ RB_COLOR(oleft, field) = RB_BLACK; \ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field); \ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \ RB_ROTATE_LEFT(head, parent, tmp, field); \ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)) \ != NULL) \ RB_COLOR(oright, field) = RB_BLACK; \ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field); \ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field)) != NULL) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old) \ RB_LEFT(RB_PARENT(old, field), field) = elm; \ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm; \ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field)) != NULL); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_FROM(x, name, y) \ for ((x) = (y); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_FROM(x, name, y) \ for ((x) = (y); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ (x) = (y)) #endif /* UV_TREE_H_ */ libuv-v1.8.0/include/uv-aix.h000066400000000000000000000031171263361716600160510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_AIX_H #define UV_AIX_H #define UV_PLATFORM_LOOP_FIELDS \ int fs_fd; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ uv__io_t event_watcher; \ char *dir_filename; \ #endif /* UV_AIX_H */ libuv-v1.8.0/include/uv-bsd.h000066400000000000000000000031511263361716600160360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_BSD_H #define UV_BSD_H #define UV_PLATFORM_FS_EVENT_FIELDS \ uv__io_t event_watcher; \ #define UV_IO_PRIVATE_PLATFORM_FIELDS \ int rcount; \ int wcount; \ #define UV_HAVE_KQUEUE 1 #endif /* UV_BSD_H */ libuv-v1.8.0/include/uv-darwin.h000066400000000000000000000062151263361716600165560ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_DARWIN_H #define UV_DARWIN_H #if defined(__APPLE__) && defined(__MACH__) # include # include # include # include # define UV_PLATFORM_SEM_T semaphore_t #endif #define UV_IO_PRIVATE_PLATFORM_FIELDS \ int rcount; \ int wcount; \ #define UV_PLATFORM_LOOP_FIELDS \ uv_thread_t cf_thread; \ void* _cf_reserved; \ void* cf_state; \ uv_mutex_t cf_mutex; \ uv_sem_t cf_sem; \ void* cf_signals[2]; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ uv__io_t event_watcher; \ char* realpath; \ int realpath_len; \ int cf_flags; \ uv_async_t* cf_cb; \ void* cf_events[2]; \ void* cf_member[2]; \ int cf_error; \ uv_mutex_t cf_mutex; \ #define UV_STREAM_PRIVATE_PLATFORM_FIELDS \ void* select; \ #define UV_HAVE_KQUEUE 1 #endif /* UV_DARWIN_H */ libuv-v1.8.0/include/uv-errno.h000066400000000000000000000222671263361716600164240ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_ERRNO_H_ #define UV_ERRNO_H_ #include #define UV__EOF (-4095) #define UV__UNKNOWN (-4094) #define UV__EAI_ADDRFAMILY (-3000) #define UV__EAI_AGAIN (-3001) #define UV__EAI_BADFLAGS (-3002) #define UV__EAI_CANCELED (-3003) #define UV__EAI_FAIL (-3004) #define UV__EAI_FAMILY (-3005) #define UV__EAI_MEMORY (-3006) #define UV__EAI_NODATA (-3007) #define UV__EAI_NONAME (-3008) #define UV__EAI_OVERFLOW (-3009) #define UV__EAI_SERVICE (-3010) #define UV__EAI_SOCKTYPE (-3011) #define UV__EAI_BADHINTS (-3013) #define UV__EAI_PROTOCOL (-3014) /* Only map to the system errno on non-Windows platforms. It's apparently * a fairly common practice for Windows programmers to redefine errno codes. */ #if defined(E2BIG) && !defined(_WIN32) # define UV__E2BIG (-E2BIG) #else # define UV__E2BIG (-4093) #endif #if defined(EACCES) && !defined(_WIN32) # define UV__EACCES (-EACCES) #else # define UV__EACCES (-4092) #endif #if defined(EADDRINUSE) && !defined(_WIN32) # define UV__EADDRINUSE (-EADDRINUSE) #else # define UV__EADDRINUSE (-4091) #endif #if defined(EADDRNOTAVAIL) && !defined(_WIN32) # define UV__EADDRNOTAVAIL (-EADDRNOTAVAIL) #else # define UV__EADDRNOTAVAIL (-4090) #endif #if defined(EAFNOSUPPORT) && !defined(_WIN32) # define UV__EAFNOSUPPORT (-EAFNOSUPPORT) #else # define UV__EAFNOSUPPORT (-4089) #endif #if defined(EAGAIN) && !defined(_WIN32) # define UV__EAGAIN (-EAGAIN) #else # define UV__EAGAIN (-4088) #endif #if defined(EALREADY) && !defined(_WIN32) # define UV__EALREADY (-EALREADY) #else # define UV__EALREADY (-4084) #endif #if defined(EBADF) && !defined(_WIN32) # define UV__EBADF (-EBADF) #else # define UV__EBADF (-4083) #endif #if defined(EBUSY) && !defined(_WIN32) # define UV__EBUSY (-EBUSY) #else # define UV__EBUSY (-4082) #endif #if defined(ECANCELED) && !defined(_WIN32) # define UV__ECANCELED (-ECANCELED) #else # define UV__ECANCELED (-4081) #endif #if defined(ECHARSET) && !defined(_WIN32) # define UV__ECHARSET (-ECHARSET) #else # define UV__ECHARSET (-4080) #endif #if defined(ECONNABORTED) && !defined(_WIN32) # define UV__ECONNABORTED (-ECONNABORTED) #else # define UV__ECONNABORTED (-4079) #endif #if defined(ECONNREFUSED) && !defined(_WIN32) # define UV__ECONNREFUSED (-ECONNREFUSED) #else # define UV__ECONNREFUSED (-4078) #endif #if defined(ECONNRESET) && !defined(_WIN32) # define UV__ECONNRESET (-ECONNRESET) #else # define UV__ECONNRESET (-4077) #endif #if defined(EDESTADDRREQ) && !defined(_WIN32) # define UV__EDESTADDRREQ (-EDESTADDRREQ) #else # define UV__EDESTADDRREQ (-4076) #endif #if defined(EEXIST) && !defined(_WIN32) # define UV__EEXIST (-EEXIST) #else # define UV__EEXIST (-4075) #endif #if defined(EFAULT) && !defined(_WIN32) # define UV__EFAULT (-EFAULT) #else # define UV__EFAULT (-4074) #endif #if defined(EHOSTUNREACH) && !defined(_WIN32) # define UV__EHOSTUNREACH (-EHOSTUNREACH) #else # define UV__EHOSTUNREACH (-4073) #endif #if defined(EINTR) && !defined(_WIN32) # define UV__EINTR (-EINTR) #else # define UV__EINTR (-4072) #endif #if defined(EINVAL) && !defined(_WIN32) # define UV__EINVAL (-EINVAL) #else # define UV__EINVAL (-4071) #endif #if defined(EIO) && !defined(_WIN32) # define UV__EIO (-EIO) #else # define UV__EIO (-4070) #endif #if defined(EISCONN) && !defined(_WIN32) # define UV__EISCONN (-EISCONN) #else # define UV__EISCONN (-4069) #endif #if defined(EISDIR) && !defined(_WIN32) # define UV__EISDIR (-EISDIR) #else # define UV__EISDIR (-4068) #endif #if defined(ELOOP) && !defined(_WIN32) # define UV__ELOOP (-ELOOP) #else # define UV__ELOOP (-4067) #endif #if defined(EMFILE) && !defined(_WIN32) # define UV__EMFILE (-EMFILE) #else # define UV__EMFILE (-4066) #endif #if defined(EMSGSIZE) && !defined(_WIN32) # define UV__EMSGSIZE (-EMSGSIZE) #else # define UV__EMSGSIZE (-4065) #endif #if defined(ENAMETOOLONG) && !defined(_WIN32) # define UV__ENAMETOOLONG (-ENAMETOOLONG) #else # define UV__ENAMETOOLONG (-4064) #endif #if defined(ENETDOWN) && !defined(_WIN32) # define UV__ENETDOWN (-ENETDOWN) #else # define UV__ENETDOWN (-4063) #endif #if defined(ENETUNREACH) && !defined(_WIN32) # define UV__ENETUNREACH (-ENETUNREACH) #else # define UV__ENETUNREACH (-4062) #endif #if defined(ENFILE) && !defined(_WIN32) # define UV__ENFILE (-ENFILE) #else # define UV__ENFILE (-4061) #endif #if defined(ENOBUFS) && !defined(_WIN32) # define UV__ENOBUFS (-ENOBUFS) #else # define UV__ENOBUFS (-4060) #endif #if defined(ENODEV) && !defined(_WIN32) # define UV__ENODEV (-ENODEV) #else # define UV__ENODEV (-4059) #endif #if defined(ENOENT) && !defined(_WIN32) # define UV__ENOENT (-ENOENT) #else # define UV__ENOENT (-4058) #endif #if defined(ENOMEM) && !defined(_WIN32) # define UV__ENOMEM (-ENOMEM) #else # define UV__ENOMEM (-4057) #endif #if defined(ENONET) && !defined(_WIN32) # define UV__ENONET (-ENONET) #else # define UV__ENONET (-4056) #endif #if defined(ENOSPC) && !defined(_WIN32) # define UV__ENOSPC (-ENOSPC) #else # define UV__ENOSPC (-4055) #endif #if defined(ENOSYS) && !defined(_WIN32) # define UV__ENOSYS (-ENOSYS) #else # define UV__ENOSYS (-4054) #endif #if defined(ENOTCONN) && !defined(_WIN32) # define UV__ENOTCONN (-ENOTCONN) #else # define UV__ENOTCONN (-4053) #endif #if defined(ENOTDIR) && !defined(_WIN32) # define UV__ENOTDIR (-ENOTDIR) #else # define UV__ENOTDIR (-4052) #endif #if defined(ENOTEMPTY) && !defined(_WIN32) # define UV__ENOTEMPTY (-ENOTEMPTY) #else # define UV__ENOTEMPTY (-4051) #endif #if defined(ENOTSOCK) && !defined(_WIN32) # define UV__ENOTSOCK (-ENOTSOCK) #else # define UV__ENOTSOCK (-4050) #endif #if defined(ENOTSUP) && !defined(_WIN32) # define UV__ENOTSUP (-ENOTSUP) #else # define UV__ENOTSUP (-4049) #endif #if defined(EPERM) && !defined(_WIN32) # define UV__EPERM (-EPERM) #else # define UV__EPERM (-4048) #endif #if defined(EPIPE) && !defined(_WIN32) # define UV__EPIPE (-EPIPE) #else # define UV__EPIPE (-4047) #endif #if defined(EPROTO) && !defined(_WIN32) # define UV__EPROTO (-EPROTO) #else # define UV__EPROTO (-4046) #endif #if defined(EPROTONOSUPPORT) && !defined(_WIN32) # define UV__EPROTONOSUPPORT (-EPROTONOSUPPORT) #else # define UV__EPROTONOSUPPORT (-4045) #endif #if defined(EPROTOTYPE) && !defined(_WIN32) # define UV__EPROTOTYPE (-EPROTOTYPE) #else # define UV__EPROTOTYPE (-4044) #endif #if defined(EROFS) && !defined(_WIN32) # define UV__EROFS (-EROFS) #else # define UV__EROFS (-4043) #endif #if defined(ESHUTDOWN) && !defined(_WIN32) # define UV__ESHUTDOWN (-ESHUTDOWN) #else # define UV__ESHUTDOWN (-4042) #endif #if defined(ESPIPE) && !defined(_WIN32) # define UV__ESPIPE (-ESPIPE) #else # define UV__ESPIPE (-4041) #endif #if defined(ESRCH) && !defined(_WIN32) # define UV__ESRCH (-ESRCH) #else # define UV__ESRCH (-4040) #endif #if defined(ETIMEDOUT) && !defined(_WIN32) # define UV__ETIMEDOUT (-ETIMEDOUT) #else # define UV__ETIMEDOUT (-4039) #endif #if defined(ETXTBSY) && !defined(_WIN32) # define UV__ETXTBSY (-ETXTBSY) #else # define UV__ETXTBSY (-4038) #endif #if defined(EXDEV) && !defined(_WIN32) # define UV__EXDEV (-EXDEV) #else # define UV__EXDEV (-4037) #endif #if defined(EFBIG) && !defined(_WIN32) # define UV__EFBIG (-EFBIG) #else # define UV__EFBIG (-4036) #endif #if defined(ENOPROTOOPT) && !defined(_WIN32) # define UV__ENOPROTOOPT (-ENOPROTOOPT) #else # define UV__ENOPROTOOPT (-4035) #endif #if defined(ERANGE) && !defined(_WIN32) # define UV__ERANGE (-ERANGE) #else # define UV__ERANGE (-4034) #endif #if defined(ENXIO) && !defined(_WIN32) # define UV__ENXIO (-ENXIO) #else # define UV__ENXIO (-4033) #endif #if defined(EMLINK) && !defined(_WIN32) # define UV__EMLINK (-EMLINK) #else # define UV__EMLINK (-4032) #endif /* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is * defined. Fortunately, its value is always 64 so it's possible albeit * icky to hard-code it. */ #if defined(EHOSTDOWN) && !defined(_WIN32) # define UV__EHOSTDOWN (-EHOSTDOWN) #elif defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) # define UV__EHOSTDOWN (-64) #else # define UV__EHOSTDOWN (-4031) #endif #endif /* UV_ERRNO_H_ */ libuv-v1.8.0/include/uv-linux.h000066400000000000000000000033651263361716600164340ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_LINUX_H #define UV_LINUX_H #define UV_PLATFORM_LOOP_FIELDS \ uv__io_t inotify_read_watcher; \ void* inotify_watchers; \ int inotify_fd; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ void* watchers[2]; \ int wd; \ #endif /* UV_LINUX_H */ libuv-v1.8.0/include/uv-sunos.h000066400000000000000000000037011263361716600164360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_SUNOS_H #define UV_SUNOS_H #include #include /* For the sake of convenience and reduced #ifdef-ery in src/unix/sunos.c, * add the fs_event fields even when this version of SunOS doesn't support * file watching. */ #define UV_PLATFORM_LOOP_FIELDS \ uv__io_t fs_event_watcher; \ int fs_fd; \ #if defined(PORT_SOURCE_FILE) # define UV_PLATFORM_FS_EVENT_FIELDS \ file_obj_t fo; \ int fd; \ #endif /* defined(PORT_SOURCE_FILE) */ #endif /* UV_SUNOS_H */ libuv-v1.8.0/include/uv-threadpool.h000066400000000000000000000027311263361716600174320ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * This file is private to libuv. It provides common functionality to both * Windows and Unix backends. */ #ifndef UV_THREADPOOL_H_ #define UV_THREADPOOL_H_ struct uv__work { void (*work)(struct uv__work *w); void (*done)(struct uv__work *w, int status); struct uv_loop_s* loop; void* wq[2]; }; #endif /* UV_THREADPOOL_H_ */ libuv-v1.8.0/include/uv-unix.h000066400000000000000000000374461263361716600162670ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_UNIX_H #define UV_UNIX_H #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __ANDROID__ #include "pthread-fixes.h" #endif #include #include "uv-threadpool.h" #if defined(__linux__) # include "uv-linux.h" #elif defined(_AIX) # include "uv-aix.h" #elif defined(__sun) # include "uv-sunos.h" #elif defined(__APPLE__) # include "uv-darwin.h" #elif defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "uv-bsd.h" #endif #ifndef NI_MAXHOST # define NI_MAXHOST 1025 #endif #ifndef NI_MAXSERV # define NI_MAXSERV 32 #endif #ifndef UV_IO_PRIVATE_PLATFORM_FIELDS # define UV_IO_PRIVATE_PLATFORM_FIELDS /* empty */ #endif struct uv__io_s; struct uv__async; struct uv_loop_s; typedef void (*uv__io_cb)(struct uv_loop_s* loop, struct uv__io_s* w, unsigned int events); typedef struct uv__io_s uv__io_t; struct uv__io_s { uv__io_cb cb; void* pending_queue[2]; void* watcher_queue[2]; unsigned int pevents; /* Pending event mask i.e. mask at next tick. */ unsigned int events; /* Current event mask. */ int fd; UV_IO_PRIVATE_PLATFORM_FIELDS }; typedef void (*uv__async_cb)(struct uv_loop_s* loop, struct uv__async* w, unsigned int nevents); struct uv__async { uv__async_cb cb; uv__io_t io_watcher; int wfd; }; #ifndef UV_PLATFORM_SEM_T # define UV_PLATFORM_SEM_T sem_t #endif #ifndef UV_PLATFORM_LOOP_FIELDS # define UV_PLATFORM_LOOP_FIELDS /* empty */ #endif #ifndef UV_PLATFORM_FS_EVENT_FIELDS # define UV_PLATFORM_FS_EVENT_FIELDS /* empty */ #endif #ifndef UV_STREAM_PRIVATE_PLATFORM_FIELDS # define UV_STREAM_PRIVATE_PLATFORM_FIELDS /* empty */ #endif /* Note: May be cast to struct iovec. See writev(2). */ typedef struct uv_buf_t { char* base; size_t len; } uv_buf_t; typedef int uv_file; typedef int uv_os_sock_t; typedef int uv_os_fd_t; #define UV_ONCE_INIT PTHREAD_ONCE_INIT typedef pthread_once_t uv_once_t; typedef pthread_t uv_thread_t; typedef pthread_mutex_t uv_mutex_t; typedef pthread_rwlock_t uv_rwlock_t; typedef UV_PLATFORM_SEM_T uv_sem_t; typedef pthread_cond_t uv_cond_t; typedef pthread_key_t uv_key_t; #if defined(__APPLE__) && defined(__MACH__) typedef struct { unsigned int n; unsigned int count; uv_mutex_t mutex; uv_sem_t turnstile1; uv_sem_t turnstile2; } uv_barrier_t; #else /* defined(__APPLE__) && defined(__MACH__) */ typedef pthread_barrier_t uv_barrier_t; #endif /* defined(__APPLE__) && defined(__MACH__) */ /* Platform-specific definitions for uv_spawn support. */ typedef gid_t uv_gid_t; typedef uid_t uv_uid_t; typedef struct dirent uv__dirent_t; #if defined(DT_UNKNOWN) # define HAVE_DIRENT_TYPES # if defined(DT_REG) # define UV__DT_FILE DT_REG # else # define UV__DT_FILE -1 # endif # if defined(DT_DIR) # define UV__DT_DIR DT_DIR # else # define UV__DT_DIR -2 # endif # if defined(DT_LNK) # define UV__DT_LINK DT_LNK # else # define UV__DT_LINK -3 # endif # if defined(DT_FIFO) # define UV__DT_FIFO DT_FIFO # else # define UV__DT_FIFO -4 # endif # if defined(DT_SOCK) # define UV__DT_SOCKET DT_SOCK # else # define UV__DT_SOCKET -5 # endif # if defined(DT_CHR) # define UV__DT_CHAR DT_CHR # else # define UV__DT_CHAR -6 # endif # if defined(DT_BLK) # define UV__DT_BLOCK DT_BLK # else # define UV__DT_BLOCK -7 # endif #endif /* Platform-specific definitions for uv_dlopen support. */ #define UV_DYNAMIC /* empty */ typedef struct { void* handle; char* errmsg; } uv_lib_t; #define UV_LOOP_PRIVATE_FIELDS \ unsigned long flags; \ int backend_fd; \ void* pending_queue[2]; \ void* watcher_queue[2]; \ uv__io_t** watchers; \ unsigned int nwatchers; \ unsigned int nfds; \ void* wq[2]; \ uv_mutex_t wq_mutex; \ uv_async_t wq_async; \ uv_rwlock_t cloexec_lock; \ uv_handle_t* closing_handles; \ void* process_handles[2]; \ void* prepare_handles[2]; \ void* check_handles[2]; \ void* idle_handles[2]; \ void* async_handles[2]; \ struct uv__async async_watcher; \ struct { \ void* min; \ unsigned int nelts; \ } timer_heap; \ uint64_t timer_counter; \ uint64_t time; \ int signal_pipefd[2]; \ uv__io_t signal_io_watcher; \ uv_signal_t child_watcher; \ int emfile_fd; \ UV_PLATFORM_LOOP_FIELDS \ #define UV_REQ_TYPE_PRIVATE /* empty */ #define UV_REQ_PRIVATE_FIELDS /* empty */ #define UV_PRIVATE_REQ_TYPES /* empty */ #define UV_WRITE_PRIVATE_FIELDS \ void* queue[2]; \ unsigned int write_index; \ uv_buf_t* bufs; \ unsigned int nbufs; \ int error; \ uv_buf_t bufsml[4]; \ #define UV_CONNECT_PRIVATE_FIELDS \ void* queue[2]; \ #define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */ #define UV_UDP_SEND_PRIVATE_FIELDS \ void* queue[2]; \ struct sockaddr_storage addr; \ unsigned int nbufs; \ uv_buf_t* bufs; \ ssize_t status; \ uv_udp_send_cb send_cb; \ uv_buf_t bufsml[4]; \ #define UV_HANDLE_PRIVATE_FIELDS \ uv_handle_t* next_closing; \ unsigned int flags; \ #define UV_STREAM_PRIVATE_FIELDS \ uv_connect_t *connect_req; \ uv_shutdown_t *shutdown_req; \ uv__io_t io_watcher; \ void* write_queue[2]; \ void* write_completed_queue[2]; \ uv_connection_cb connection_cb; \ int delayed_error; \ int accepted_fd; \ void* queued_fds; \ UV_STREAM_PRIVATE_PLATFORM_FIELDS \ #define UV_TCP_PRIVATE_FIELDS /* empty */ #define UV_UDP_PRIVATE_FIELDS \ uv_alloc_cb alloc_cb; \ uv_udp_recv_cb recv_cb; \ uv__io_t io_watcher; \ void* write_queue[2]; \ void* write_completed_queue[2]; \ #define UV_PIPE_PRIVATE_FIELDS \ const char* pipe_fname; /* strdup'ed */ #define UV_POLL_PRIVATE_FIELDS \ uv__io_t io_watcher; #define UV_PREPARE_PRIVATE_FIELDS \ uv_prepare_cb prepare_cb; \ void* queue[2]; \ #define UV_CHECK_PRIVATE_FIELDS \ uv_check_cb check_cb; \ void* queue[2]; \ #define UV_IDLE_PRIVATE_FIELDS \ uv_idle_cb idle_cb; \ void* queue[2]; \ #define UV_ASYNC_PRIVATE_FIELDS \ uv_async_cb async_cb; \ void* queue[2]; \ int pending; \ #define UV_TIMER_PRIVATE_FIELDS \ uv_timer_cb timer_cb; \ void* heap_node[3]; \ uint64_t timeout; \ uint64_t repeat; \ uint64_t start_id; #define UV_GETADDRINFO_PRIVATE_FIELDS \ struct uv__work work_req; \ uv_getaddrinfo_cb cb; \ struct addrinfo* hints; \ char* hostname; \ char* service; \ struct addrinfo* addrinfo; \ int retcode; #define UV_GETNAMEINFO_PRIVATE_FIELDS \ struct uv__work work_req; \ uv_getnameinfo_cb getnameinfo_cb; \ struct sockaddr_storage storage; \ int flags; \ char host[NI_MAXHOST]; \ char service[NI_MAXSERV]; \ int retcode; #define UV_PROCESS_PRIVATE_FIELDS \ void* queue[2]; \ int status; \ #define UV_FS_PRIVATE_FIELDS \ const char *new_path; \ uv_file file; \ int flags; \ mode_t mode; \ unsigned int nbufs; \ uv_buf_t* bufs; \ off_t off; \ uv_uid_t uid; \ uv_gid_t gid; \ double atime; \ double mtime; \ struct uv__work work_req; \ uv_buf_t bufsml[4]; \ #define UV_WORK_PRIVATE_FIELDS \ struct uv__work work_req; #define UV_TTY_PRIVATE_FIELDS \ struct termios orig_termios; \ int mode; #define UV_SIGNAL_PRIVATE_FIELDS \ /* RB_ENTRY(uv_signal_s) tree_entry; */ \ struct { \ struct uv_signal_s* rbe_left; \ struct uv_signal_s* rbe_right; \ struct uv_signal_s* rbe_parent; \ int rbe_color; \ } tree_entry; \ /* Use two counters here so we don have to fiddle with atomics. */ \ unsigned int caught_signals; \ unsigned int dispatched_signals; #define UV_FS_EVENT_PRIVATE_FIELDS \ uv_fs_event_cb cb; \ UV_PLATFORM_FS_EVENT_FIELDS \ #endif /* UV_UNIX_H */ libuv-v1.8.0/include/uv-version.h000066400000000000000000000034611263361716600167570ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_VERSION_H #define UV_VERSION_H /* * Versions with the same major number are ABI stable. API is allowed to * evolve between minor releases, but only in a backwards compatible way. * Make sure you update the -soname directives in configure.ac * and uv.gyp whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but * not UV_VERSION_PATCH.) */ #define UV_VERSION_MAJOR 1 #define UV_VERSION_MINOR 8 #define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" #define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \ (UV_VERSION_MINOR << 8) | \ (UV_VERSION_PATCH)) #endif /* UV_VERSION_H */ libuv-v1.8.0/include/uv-win.h000066400000000000000000000747701263361716600161020ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0502 #endif #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) typedef intptr_t ssize_t; # define _SSIZE_T_ # define _SSIZE_T_DEFINED #endif #include #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) typedef struct pollfd { SOCKET fd; short events; short revents; } WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; #endif #ifndef LOCALE_INVARIANT # define LOCALE_INVARIANT 0x007f #endif #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "stdint-msvc2008.h" #else # include #endif #include "tree.h" #include "uv-threadpool.h" #define MAX_PIPENAME_LEN 256 #ifndef S_IFLNK # define S_IFLNK 0xA000 #endif /* Additional signals supported by uv_signal and or uv_kill. The CRT defines * the following signals already: * * #define SIGINT 2 * #define SIGILL 4 * #define SIGABRT_COMPAT 6 * #define SIGFPE 8 * #define SIGSEGV 11 * #define SIGTERM 15 * #define SIGBREAK 21 * #define SIGABRT 22 * * The additional signals have values that are common on other Unix * variants (Linux and Darwin) */ #define SIGHUP 1 #define SIGKILL 9 #define SIGWINCH 28 /* The CRT defines SIGABRT_COMPAT as 6, which equals SIGABRT on many */ /* unix-like platforms. However MinGW doesn't define it, so we do. */ #ifndef SIGABRT_COMPAT # define SIGABRT_COMPAT 6 #endif /* * Guids and typedefs for winsock extension functions * Mingw32 doesn't have these :-( */ #ifndef WSAID_ACCEPTEX # define WSAID_ACCEPTEX \ {0xb5367df1, 0xcbac, 0x11cf, \ {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} # define WSAID_CONNECTEX \ {0x25a207b9, 0xddf3, 0x4660, \ {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} # define WSAID_GETACCEPTEXSOCKADDRS \ {0xb5367df2, 0xcbac, 0x11cf, \ {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} # define WSAID_DISCONNECTEX \ {0x7fda2e11, 0x8630, 0x436f, \ {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} # define WSAID_TRANSMITFILE \ {0xb5367df0, 0xcbac, 0x11cf, \ {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} typedef BOOL PASCAL (*LPFN_ACCEPTEX) (SOCKET sListenSocket, SOCKET sAcceptSocket, PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived, LPOVERLAPPED lpOverlapped); typedef BOOL PASCAL (*LPFN_CONNECTEX) (SOCKET s, const struct sockaddr* name, int namelen, PVOID lpSendBuffer, DWORD dwSendDataLength, LPDWORD lpdwBytesSent, LPOVERLAPPED lpOverlapped); typedef void PASCAL (*LPFN_GETACCEPTEXSOCKADDRS) (PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength, LPSOCKADDR* LocalSockaddr, LPINT LocalSockaddrLength, LPSOCKADDR* RemoteSockaddr, LPINT RemoteSockaddrLength); typedef BOOL PASCAL (*LPFN_DISCONNECTEX) (SOCKET hSocket, LPOVERLAPPED lpOverlapped, DWORD dwFlags, DWORD reserved); typedef BOOL PASCAL (*LPFN_TRANSMITFILE) (SOCKET hSocket, HANDLE hFile, DWORD nNumberOfBytesToWrite, DWORD nNumberOfBytesPerSend, LPOVERLAPPED lpOverlapped, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, DWORD dwFlags); typedef PVOID RTL_SRWLOCK; typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; #endif typedef int (WSAAPI* LPFN_WSARECV) (SOCKET socket, LPWSABUF buffers, DWORD buffer_count, LPDWORD bytes, LPDWORD flags, LPWSAOVERLAPPED overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); typedef int (WSAAPI* LPFN_WSARECVFROM) (SOCKET socket, LPWSABUF buffers, DWORD buffer_count, LPDWORD bytes, LPDWORD flags, struct sockaddr* addr, LPINT addr_len, LPWSAOVERLAPPED overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); #ifndef _NTDEF_ typedef LONG NTSTATUS; typedef NTSTATUS *PNTSTATUS; #endif #ifndef RTL_CONDITION_VARIABLE_INIT typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE; #endif typedef struct _AFD_POLL_HANDLE_INFO { HANDLE Handle; ULONG Events; NTSTATUS Status; } AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; typedef struct _AFD_POLL_INFO { LARGE_INTEGER Timeout; ULONG NumberOfHandles; ULONG Exclusive; AFD_POLL_HANDLE_INFO Handles[1]; } AFD_POLL_INFO, *PAFD_POLL_INFO; #define UV_MSAFD_PROVIDER_COUNT 3 /** * It should be possible to cast uv_buf_t[] to WSABUF[] * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx */ typedef struct uv_buf_t { ULONG len; char* base; } uv_buf_t; typedef int uv_file; typedef SOCKET uv_os_sock_t; typedef HANDLE uv_os_fd_t; typedef HANDLE uv_thread_t; typedef HANDLE uv_sem_t; typedef CRITICAL_SECTION uv_mutex_t; /* This condition variable implementation is based on the SetEvent solution * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html * We could not use the SignalObjectAndWait solution (section 3.4) because * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. */ typedef union { CONDITION_VARIABLE cond_var; struct { unsigned int waiters_count; CRITICAL_SECTION waiters_count_lock; HANDLE signal_event; HANDLE broadcast_event; } fallback; } uv_cond_t; typedef union { struct { unsigned int num_readers_; CRITICAL_SECTION num_readers_lock_; HANDLE write_semaphore_; } state_; /* TODO: remove me in v2.x. */ struct { SRWLOCK unused_; } unused1_; /* TODO: remove me in v2.x. */ struct { uv_mutex_t unused1_; uv_mutex_t unused2_; } unused2_; } uv_rwlock_t; typedef struct { unsigned int n; unsigned int count; uv_mutex_t mutex; uv_sem_t turnstile1; uv_sem_t turnstile2; } uv_barrier_t; typedef struct { DWORD tls_index; } uv_key_t; #define UV_ONCE_INIT { 0, NULL } typedef struct uv_once_s { unsigned char ran; HANDLE event; } uv_once_t; /* Platform-specific definitions for uv_spawn support. */ typedef unsigned char uv_uid_t; typedef unsigned char uv_gid_t; typedef struct uv__dirent_s { int d_type; char d_name[1]; } uv__dirent_t; #define HAVE_DIRENT_TYPES #define UV__DT_DIR UV_DIRENT_DIR #define UV__DT_FILE UV_DIRENT_FILE #define UV__DT_LINK UV_DIRENT_LINK #define UV__DT_FIFO UV_DIRENT_FIFO #define UV__DT_SOCKET UV_DIRENT_SOCKET #define UV__DT_CHAR UV_DIRENT_CHAR #define UV__DT_BLOCK UV_DIRENT_BLOCK /* Platform-specific definitions for uv_dlopen support. */ #define UV_DYNAMIC FAR WINAPI typedef struct { HMODULE handle; char* errmsg; } uv_lib_t; RB_HEAD(uv_timer_tree_s, uv_timer_s); #define UV_LOOP_PRIVATE_FIELDS \ /* The loop's I/O completion port */ \ HANDLE iocp; \ /* The current time according to the event loop. in msecs. */ \ uint64_t time; \ /* Tail of a single-linked circular queue of pending reqs. If the queue */ \ /* is empty, tail_ is NULL. If there is only one item, */ \ /* tail_->next_req == tail_ */ \ uv_req_t* pending_reqs_tail; \ /* Head of a single-linked list of closed handles */ \ uv_handle_t* endgame_handles; \ /* The head of the timers tree */ \ struct uv_timer_tree_s timers; \ /* Lists of active loop (prepare / check / idle) watchers */ \ uv_prepare_t* prepare_handles; \ uv_check_t* check_handles; \ uv_idle_t* idle_handles; \ /* This pointer will refer to the prepare/check/idle handle whose */ \ /* callback is scheduled to be called next. This is needed to allow */ \ /* safe removal from one of the lists above while that list being */ \ /* iterated over. */ \ uv_prepare_t* next_prepare_handle; \ uv_check_t* next_check_handle; \ uv_idle_t* next_idle_handle; \ /* This handle holds the peer sockets for the fast variant of uv_poll_t */ \ SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]; \ /* Counter to keep track of active tcp streams */ \ unsigned int active_tcp_streams; \ /* Counter to keep track of active udp streams */ \ unsigned int active_udp_streams; \ /* Counter to started timer */ \ uint64_t timer_counter; \ /* Threadpool */ \ void* wq[2]; \ uv_mutex_t wq_mutex; \ uv_async_t wq_async; #define UV_REQ_TYPE_PRIVATE \ /* TODO: remove the req suffix */ \ UV_ACCEPT, \ UV_FS_EVENT_REQ, \ UV_POLL_REQ, \ UV_PROCESS_EXIT, \ UV_READ, \ UV_UDP_RECV, \ UV_WAKEUP, \ UV_SIGNAL_REQ, #define UV_REQ_PRIVATE_FIELDS \ union { \ /* Used by I/O operations */ \ struct { \ OVERLAPPED overlapped; \ size_t queued_bytes; \ } io; \ } u; \ struct uv_req_s* next_req; #define UV_WRITE_PRIVATE_FIELDS \ int ipc_header; \ uv_buf_t write_buffer; \ HANDLE event_handle; \ HANDLE wait_handle; #define UV_CONNECT_PRIVATE_FIELDS \ /* empty */ #define UV_SHUTDOWN_PRIVATE_FIELDS \ /* empty */ #define UV_UDP_SEND_PRIVATE_FIELDS \ /* empty */ #define UV_PRIVATE_REQ_TYPES \ typedef struct uv_pipe_accept_s { \ UV_REQ_FIELDS \ HANDLE pipeHandle; \ struct uv_pipe_accept_s* next_pending; \ } uv_pipe_accept_t; \ \ typedef struct uv_tcp_accept_s { \ UV_REQ_FIELDS \ SOCKET accept_socket; \ char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; \ HANDLE event_handle; \ HANDLE wait_handle; \ struct uv_tcp_accept_s* next_pending; \ } uv_tcp_accept_t; \ \ typedef struct uv_read_s { \ UV_REQ_FIELDS \ HANDLE event_handle; \ HANDLE wait_handle; \ } uv_read_t; #define uv_stream_connection_fields \ unsigned int write_reqs_pending; \ uv_shutdown_t* shutdown_req; #define uv_stream_server_fields \ uv_connection_cb connection_cb; #define UV_STREAM_PRIVATE_FIELDS \ unsigned int reqs_pending; \ int activecnt; \ uv_read_t read_req; \ union { \ struct { uv_stream_connection_fields } conn; \ struct { uv_stream_server_fields } serv; \ } stream; #define uv_tcp_server_fields \ uv_tcp_accept_t* accept_reqs; \ unsigned int processed_accepts; \ uv_tcp_accept_t* pending_accepts; \ LPFN_ACCEPTEX func_acceptex; #define uv_tcp_connection_fields \ uv_buf_t read_buffer; \ LPFN_CONNECTEX func_connectex; #define UV_TCP_PRIVATE_FIELDS \ SOCKET socket; \ int delayed_error; \ union { \ struct { uv_tcp_server_fields } serv; \ struct { uv_tcp_connection_fields } conn; \ } tcp; #define UV_UDP_PRIVATE_FIELDS \ SOCKET socket; \ unsigned int reqs_pending; \ int activecnt; \ uv_req_t recv_req; \ uv_buf_t recv_buffer; \ struct sockaddr_storage recv_from; \ int recv_from_len; \ uv_udp_recv_cb recv_cb; \ uv_alloc_cb alloc_cb; \ LPFN_WSARECV func_wsarecv; \ LPFN_WSARECVFROM func_wsarecvfrom; #define uv_pipe_server_fields \ int pending_instances; \ uv_pipe_accept_t* accept_reqs; \ uv_pipe_accept_t* pending_accepts; #define uv_pipe_connection_fields \ uv_timer_t* eof_timer; \ uv_write_t ipc_header_write_req; \ int ipc_pid; \ uint64_t remaining_ipc_rawdata_bytes; \ struct { \ void* queue[2]; \ int queue_len; \ } pending_ipc_info; \ uv_write_t* non_overlapped_writes_tail; \ uv_mutex_t readfile_mutex; \ volatile HANDLE readfile_thread; #define UV_PIPE_PRIVATE_FIELDS \ HANDLE handle; \ WCHAR* name; \ union { \ struct { uv_pipe_server_fields } serv; \ struct { uv_pipe_connection_fields } conn; \ } pipe; /* TODO: put the parser states in an union - TTY handles are always */ /* half-duplex so read-state can safely overlap write-state. */ #define UV_TTY_PRIVATE_FIELDS \ HANDLE handle; \ union { \ struct { \ /* Used for readable TTY handles */ \ HANDLE read_line_handle; \ uv_buf_t read_line_buffer; \ HANDLE read_raw_wait; \ /* Fields used for translating win keystrokes into vt100 characters */ \ char last_key[8]; \ unsigned char last_key_offset; \ unsigned char last_key_len; \ WCHAR last_utf16_high_surrogate; \ INPUT_RECORD last_input_record; \ } rd; \ struct { \ /* Used for writable TTY handles */ \ /* utf8-to-utf16 conversion state */ \ unsigned int utf8_codepoint; \ unsigned char utf8_bytes_left; \ /* eol conversion state */ \ unsigned char previous_eol; \ /* ansi parser state */ \ unsigned char ansi_parser_state; \ unsigned char ansi_csi_argc; \ unsigned short ansi_csi_argv[4]; \ COORD saved_position; \ WORD saved_attributes; \ } wr; \ } tty; #define UV_POLL_PRIVATE_FIELDS \ SOCKET socket; \ /* Used in fast mode */ \ SOCKET peer_socket; \ AFD_POLL_INFO afd_poll_info_1; \ AFD_POLL_INFO afd_poll_info_2; \ /* Used in fast and slow mode. */ \ uv_req_t poll_req_1; \ uv_req_t poll_req_2; \ unsigned char submitted_events_1; \ unsigned char submitted_events_2; \ unsigned char mask_events_1; \ unsigned char mask_events_2; \ unsigned char events; #define UV_TIMER_PRIVATE_FIELDS \ RB_ENTRY(uv_timer_s) tree_entry; \ uint64_t due; \ uint64_t repeat; \ uint64_t start_id; \ uv_timer_cb timer_cb; #define UV_ASYNC_PRIVATE_FIELDS \ struct uv_req_s async_req; \ uv_async_cb async_cb; \ /* char to avoid alignment issues */ \ char volatile async_sent; #define UV_PREPARE_PRIVATE_FIELDS \ uv_prepare_t* prepare_prev; \ uv_prepare_t* prepare_next; \ uv_prepare_cb prepare_cb; #define UV_CHECK_PRIVATE_FIELDS \ uv_check_t* check_prev; \ uv_check_t* check_next; \ uv_check_cb check_cb; #define UV_IDLE_PRIVATE_FIELDS \ uv_idle_t* idle_prev; \ uv_idle_t* idle_next; \ uv_idle_cb idle_cb; #define UV_HANDLE_PRIVATE_FIELDS \ uv_handle_t* endgame_next; \ unsigned int flags; #define UV_GETADDRINFO_PRIVATE_FIELDS \ struct uv__work work_req; \ uv_getaddrinfo_cb getaddrinfo_cb; \ void* alloc; \ WCHAR* node; \ WCHAR* service; \ /* The addrinfoW field is used to store a pointer to the hints, and */ \ /* later on to store the result of GetAddrInfoW. The final result will */ \ /* be converted to struct addrinfo* and stored in the addrinfo field. */ \ struct addrinfoW* addrinfow; \ struct addrinfo* addrinfo; \ int retcode; #define UV_GETNAMEINFO_PRIVATE_FIELDS \ struct uv__work work_req; \ uv_getnameinfo_cb getnameinfo_cb; \ struct sockaddr_storage storage; \ int flags; \ char host[NI_MAXHOST]; \ char service[NI_MAXSERV]; \ int retcode; #define UV_PROCESS_PRIVATE_FIELDS \ struct uv_process_exit_s { \ UV_REQ_FIELDS \ } exit_req; \ BYTE* child_stdio_buffer; \ int exit_signal; \ HANDLE wait_handle; \ HANDLE process_handle; \ volatile char exit_cb_pending; #define UV_FS_PRIVATE_FIELDS \ struct uv__work work_req; \ int flags; \ DWORD sys_errno_; \ union { \ /* TODO: remove me in 0.9. */ \ WCHAR* pathw; \ int fd; \ } file; \ union { \ struct { \ int mode; \ WCHAR* new_pathw; \ int file_flags; \ int fd_out; \ unsigned int nbufs; \ uv_buf_t* bufs; \ int64_t offset; \ uv_buf_t bufsml[4]; \ } info; \ struct { \ double atime; \ double mtime; \ } time; \ } fs; #define UV_WORK_PRIVATE_FIELDS \ struct uv__work work_req; #define UV_FS_EVENT_PRIVATE_FIELDS \ struct uv_fs_event_req_s { \ UV_REQ_FIELDS \ } req; \ HANDLE dir_handle; \ int req_pending; \ uv_fs_event_cb cb; \ WCHAR* filew; \ WCHAR* short_filew; \ WCHAR* dirw; \ char* buffer; #define UV_SIGNAL_PRIVATE_FIELDS \ RB_ENTRY(uv_signal_s) tree_entry; \ struct uv_req_s signal_req; \ unsigned long pending_signum; int uv_utf16_to_utf8(const WCHAR* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size); int uv_utf8_to_utf16(const char* utf8Buffer, WCHAR* utf16Buffer, size_t utf16Size); #ifndef F_OK #define F_OK 0 #endif #ifndef R_OK #define R_OK 4 #endif #ifndef W_OK #define W_OK 2 #endif #ifndef X_OK #define X_OK 1 #endif libuv-v1.8.0/include/uv.h000066400000000000000000001507051263361716600153000ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* See https://github.com/libuv/libuv#documentation for documentation. */ #ifndef UV_H #define UV_H #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 /* Windows - set up dll import/export decorators. */ # if defined(BUILDING_UV_SHARED) /* Building shared library. */ # define UV_EXTERN __declspec(dllexport) # elif defined(USING_UV_SHARED) /* Using shared library. */ # define UV_EXTERN __declspec(dllimport) # else /* Building static library. */ # define UV_EXTERN /* nothing */ # endif #elif __GNUC__ >= 4 # define UV_EXTERN __attribute__((visibility("default"))) #else # define UV_EXTERN /* nothing */ #endif #include "uv-errno.h" #include "uv-version.h" #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "stdint-msvc2008.h" #else # include #endif #if defined(_WIN32) # include "uv-win.h" #else # include "uv-unix.h" #endif /* Expand this list if necessary. */ #define UV_ERRNO_MAP(XX) \ XX(E2BIG, "argument list too long") \ XX(EACCES, "permission denied") \ XX(EADDRINUSE, "address already in use") \ XX(EADDRNOTAVAIL, "address not available") \ XX(EAFNOSUPPORT, "address family not supported") \ XX(EAGAIN, "resource temporarily unavailable") \ XX(EAI_ADDRFAMILY, "address family not supported") \ XX(EAI_AGAIN, "temporary failure") \ XX(EAI_BADFLAGS, "bad ai_flags value") \ XX(EAI_BADHINTS, "invalid value for hints") \ XX(EAI_CANCELED, "request canceled") \ XX(EAI_FAIL, "permanent failure") \ XX(EAI_FAMILY, "ai_family not supported") \ XX(EAI_MEMORY, "out of memory") \ XX(EAI_NODATA, "no address") \ XX(EAI_NONAME, "unknown node or service") \ XX(EAI_OVERFLOW, "argument buffer overflow") \ XX(EAI_PROTOCOL, "resolved protocol is unknown") \ XX(EAI_SERVICE, "service not available for socket type") \ XX(EAI_SOCKTYPE, "socket type not supported") \ XX(EALREADY, "connection already in progress") \ XX(EBADF, "bad file descriptor") \ XX(EBUSY, "resource busy or locked") \ XX(ECANCELED, "operation canceled") \ XX(ECHARSET, "invalid Unicode character") \ XX(ECONNABORTED, "software caused connection abort") \ XX(ECONNREFUSED, "connection refused") \ XX(ECONNRESET, "connection reset by peer") \ XX(EDESTADDRREQ, "destination address required") \ XX(EEXIST, "file already exists") \ XX(EFAULT, "bad address in system call argument") \ XX(EFBIG, "file too large") \ XX(EHOSTUNREACH, "host is unreachable") \ XX(EINTR, "interrupted system call") \ XX(EINVAL, "invalid argument") \ XX(EIO, "i/o error") \ XX(EISCONN, "socket is already connected") \ XX(EISDIR, "illegal operation on a directory") \ XX(ELOOP, "too many symbolic links encountered") \ XX(EMFILE, "too many open files") \ XX(EMSGSIZE, "message too long") \ XX(ENAMETOOLONG, "name too long") \ XX(ENETDOWN, "network is down") \ XX(ENETUNREACH, "network is unreachable") \ XX(ENFILE, "file table overflow") \ XX(ENOBUFS, "no buffer space available") \ XX(ENODEV, "no such device") \ XX(ENOENT, "no such file or directory") \ XX(ENOMEM, "not enough memory") \ XX(ENONET, "machine is not on the network") \ XX(ENOPROTOOPT, "protocol not available") \ XX(ENOSPC, "no space left on device") \ XX(ENOSYS, "function not implemented") \ XX(ENOTCONN, "socket is not connected") \ XX(ENOTDIR, "not a directory") \ XX(ENOTEMPTY, "directory not empty") \ XX(ENOTSOCK, "socket operation on non-socket") \ XX(ENOTSUP, "operation not supported on socket") \ XX(EPERM, "operation not permitted") \ XX(EPIPE, "broken pipe") \ XX(EPROTO, "protocol error") \ XX(EPROTONOSUPPORT, "protocol not supported") \ XX(EPROTOTYPE, "protocol wrong type for socket") \ XX(ERANGE, "result too large") \ XX(EROFS, "read-only file system") \ XX(ESHUTDOWN, "cannot send after transport endpoint shutdown") \ XX(ESPIPE, "invalid seek") \ XX(ESRCH, "no such process") \ XX(ETIMEDOUT, "connection timed out") \ XX(ETXTBSY, "text file is busy") \ XX(EXDEV, "cross-device link not permitted") \ XX(UNKNOWN, "unknown error") \ XX(EOF, "end of file") \ XX(ENXIO, "no such device or address") \ XX(EMLINK, "too many links") \ XX(EHOSTDOWN, "host is down") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ XX(CHECK, check) \ XX(FS_EVENT, fs_event) \ XX(FS_POLL, fs_poll) \ XX(HANDLE, handle) \ XX(IDLE, idle) \ XX(NAMED_PIPE, pipe) \ XX(POLL, poll) \ XX(PREPARE, prepare) \ XX(PROCESS, process) \ XX(STREAM, stream) \ XX(TCP, tcp) \ XX(TIMER, timer) \ XX(TTY, tty) \ XX(UDP, udp) \ XX(SIGNAL, signal) \ #define UV_REQ_TYPE_MAP(XX) \ XX(REQ, req) \ XX(CONNECT, connect) \ XX(WRITE, write) \ XX(SHUTDOWN, shutdown) \ XX(UDP_SEND, udp_send) \ XX(FS, fs) \ XX(WORK, work) \ XX(GETADDRINFO, getaddrinfo) \ XX(GETNAMEINFO, getnameinfo) \ typedef enum { #define XX(code, _) UV_ ## code = UV__ ## code, UV_ERRNO_MAP(XX) #undef XX UV_ERRNO_MAX = UV__EOF - 1 } uv_errno_t; typedef enum { UV_UNKNOWN_HANDLE = 0, #define XX(uc, lc) UV_##uc, UV_HANDLE_TYPE_MAP(XX) #undef XX UV_FILE, UV_HANDLE_TYPE_MAX } uv_handle_type; typedef enum { UV_UNKNOWN_REQ = 0, #define XX(uc, lc) UV_##uc, UV_REQ_TYPE_MAP(XX) #undef XX UV_REQ_TYPE_PRIVATE UV_REQ_TYPE_MAX } uv_req_type; /* Handle types. */ typedef struct uv_loop_s uv_loop_t; typedef struct uv_handle_s uv_handle_t; typedef struct uv_stream_s uv_stream_t; typedef struct uv_tcp_s uv_tcp_t; typedef struct uv_udp_s uv_udp_t; typedef struct uv_pipe_s uv_pipe_t; typedef struct uv_tty_s uv_tty_t; typedef struct uv_poll_s uv_poll_t; typedef struct uv_timer_s uv_timer_t; typedef struct uv_prepare_s uv_prepare_t; typedef struct uv_check_s uv_check_t; typedef struct uv_idle_s uv_idle_t; typedef struct uv_async_s uv_async_t; typedef struct uv_process_s uv_process_t; typedef struct uv_fs_event_s uv_fs_event_t; typedef struct uv_fs_poll_s uv_fs_poll_t; typedef struct uv_signal_s uv_signal_t; /* Request types. */ typedef struct uv_req_s uv_req_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; typedef struct uv_getnameinfo_s uv_getnameinfo_t; typedef struct uv_shutdown_s uv_shutdown_t; typedef struct uv_write_s uv_write_t; typedef struct uv_connect_s uv_connect_t; typedef struct uv_udp_send_s uv_udp_send_t; typedef struct uv_fs_s uv_fs_t; typedef struct uv_work_s uv_work_t; /* None of the above. */ typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; typedef enum { UV_LOOP_BLOCK_SIGNAL } uv_loop_option; typedef enum { UV_RUN_DEFAULT = 0, UV_RUN_ONCE, UV_RUN_NOWAIT } uv_run_mode; UV_EXTERN unsigned int uv_version(void); UV_EXTERN const char* uv_version_string(void); typedef void* (*uv_malloc_func)(size_t size); typedef void* (*uv_realloc_func)(void* ptr, size_t size); typedef void* (*uv_calloc_func)(size_t count, size_t size); typedef void (*uv_free_func)(void* ptr); UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, uv_free_func free_func); UV_EXTERN uv_loop_t* uv_default_loop(void); UV_EXTERN int uv_loop_init(uv_loop_t* loop); UV_EXTERN int uv_loop_close(uv_loop_t* loop); /* * NOTE: * This function is DEPRECATED (to be removed after 0.12), users should * allocate the loop manually and use uv_loop_init instead. */ UV_EXTERN uv_loop_t* uv_loop_new(void); /* * NOTE: * This function is DEPRECATED (to be removed after 0.12). Users should use * uv_loop_close and free the memory manually instead. */ UV_EXTERN void uv_loop_delete(uv_loop_t*); UV_EXTERN size_t uv_loop_size(void); UV_EXTERN int uv_loop_alive(const uv_loop_t* loop); UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...); UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode); UV_EXTERN void uv_stop(uv_loop_t*); UV_EXTERN void uv_ref(uv_handle_t*); UV_EXTERN void uv_unref(uv_handle_t*); UV_EXTERN int uv_has_ref(const uv_handle_t*); UV_EXTERN void uv_update_time(uv_loop_t*); UV_EXTERN uint64_t uv_now(const uv_loop_t*); UV_EXTERN int uv_backend_fd(const uv_loop_t*); UV_EXTERN int uv_backend_timeout(const uv_loop_t*); typedef void (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); typedef void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); typedef void (*uv_write_cb)(uv_write_t* req, int status); typedef void (*uv_connect_cb)(uv_connect_t* req, int status); typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status); typedef void (*uv_connection_cb)(uv_stream_t* server, int status); typedef void (*uv_close_cb)(uv_handle_t* handle); typedef void (*uv_poll_cb)(uv_poll_t* handle, int status, int events); typedef void (*uv_timer_cb)(uv_timer_t* handle); typedef void (*uv_async_cb)(uv_async_t* handle); typedef void (*uv_prepare_cb)(uv_prepare_t* handle); typedef void (*uv_check_cb)(uv_check_t* handle); typedef void (*uv_idle_cb)(uv_idle_t* handle); typedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal); typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg); typedef void (*uv_fs_cb)(uv_fs_t* req); typedef void (*uv_work_cb)(uv_work_t* req); typedef void (*uv_after_work_cb)(uv_work_t* req, int status); typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res); typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, int status, const char* hostname, const char* service); typedef struct { long tv_sec; long tv_nsec; } uv_timespec_t; typedef struct { uint64_t st_dev; uint64_t st_mode; uint64_t st_nlink; uint64_t st_uid; uint64_t st_gid; uint64_t st_rdev; uint64_t st_ino; uint64_t st_size; uint64_t st_blksize; uint64_t st_blocks; uint64_t st_flags; uint64_t st_gen; uv_timespec_t st_atim; uv_timespec_t st_mtim; uv_timespec_t st_ctim; uv_timespec_t st_birthtim; } uv_stat_t; typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, int events, int status); typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr); typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum); typedef enum { UV_LEAVE_GROUP = 0, UV_JOIN_GROUP } uv_membership; UV_EXTERN const char* uv_strerror(int err); UV_EXTERN const char* uv_err_name(int err); #define UV_REQ_FIELDS \ /* public */ \ void* data; \ /* read-only */ \ uv_req_type type; \ /* private */ \ void* active_queue[2]; \ void* reserved[4]; \ UV_REQ_PRIVATE_FIELDS \ /* Abstract base class of all requests. */ struct uv_req_s { UV_REQ_FIELDS }; /* Platform-specific request types. */ UV_PRIVATE_REQ_TYPES UV_EXTERN int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb); struct uv_shutdown_s { UV_REQ_FIELDS uv_stream_t* handle; uv_shutdown_cb cb; UV_SHUTDOWN_PRIVATE_FIELDS }; #define UV_HANDLE_FIELDS \ /* public */ \ void* data; \ /* read-only */ \ uv_loop_t* loop; \ uv_handle_type type; \ /* private */ \ uv_close_cb close_cb; \ void* handle_queue[2]; \ union { \ int fd; \ void* reserved[4]; \ } u; \ UV_HANDLE_PRIVATE_FIELDS \ /* The abstract base class of all handles. */ struct uv_handle_s { UV_HANDLE_FIELDS }; UV_EXTERN size_t uv_handle_size(uv_handle_type type); UV_EXTERN size_t uv_req_size(uv_req_type type); UV_EXTERN int uv_is_active(const uv_handle_t* handle); UV_EXTERN void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg); /* Helpers for ad hoc debugging, no API/ABI stability guaranteed. */ UV_EXTERN void uv_print_all_handles(uv_loop_t* loop, FILE* stream); UV_EXTERN void uv_print_active_handles(uv_loop_t* loop, FILE* stream); UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb); UV_EXTERN int uv_send_buffer_size(uv_handle_t* handle, int* value); UV_EXTERN int uv_recv_buffer_size(uv_handle_t* handle, int* value); UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ size_t write_queue_size; \ uv_alloc_cb alloc_cb; \ uv_read_cb read_cb; \ /* private */ \ UV_STREAM_PRIVATE_FIELDS /* * uv_stream_t is a subclass of uv_handle_t. * * uv_stream is an abstract class. * * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t and uv_tty_t. */ struct uv_stream_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS }; UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client); UV_EXTERN int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb); UV_EXTERN int uv_read_stop(uv_stream_t*); UV_EXTERN int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); UV_EXTERN int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb); UV_EXTERN int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs); /* uv_write_t is a subclass of uv_req_t. */ struct uv_write_s { UV_REQ_FIELDS uv_write_cb cb; uv_stream_t* send_handle; uv_stream_t* handle; UV_WRITE_PRIVATE_FIELDS }; UV_EXTERN int uv_is_readable(const uv_stream_t* handle); UV_EXTERN int uv_is_writable(const uv_stream_t* handle); UV_EXTERN int uv_stream_set_blocking(uv_stream_t* handle, int blocking); UV_EXTERN int uv_is_closing(const uv_handle_t* handle); /* * uv_tcp_t is a subclass of uv_stream_t. * * Represents a TCP stream or TCP server. */ struct uv_tcp_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS UV_TCP_PRIVATE_FIELDS }; UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle); UV_EXTERN int uv_tcp_init_ex(uv_loop_t*, uv_tcp_t* handle, unsigned int flags); UV_EXTERN int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock); UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable); UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay); UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable); enum uv_tcp_flags { /* Used with uv_tcp_bind, when an IPv6 address is used. */ UV_TCP_IPV6ONLY = 1 }; UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags); UV_EXTERN int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen); UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen); UV_EXTERN int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, uv_connect_cb cb); /* uv_connect_t is a subclass of uv_req_t. */ struct uv_connect_s { UV_REQ_FIELDS uv_connect_cb cb; uv_stream_t* handle; UV_CONNECT_PRIVATE_FIELDS }; /* * UDP support. */ enum uv_udp_flags { /* Disables dual stack mode. */ UV_UDP_IPV6ONLY = 1, /* * Indicates message was truncated because read buffer was too small. The * remainder was discarded by the OS. Used in uv_udp_recv_cb. */ UV_UDP_PARTIAL = 2, /* * Indicates if SO_REUSEADDR will be set when binding the handle. * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that * multiple threads or processes can bind to the same address without error * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ UV_UDP_REUSEADDR = 4 }; typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); typedef void (*uv_udp_recv_cb)(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags); /* uv_udp_t is a subclass of uv_handle_t. */ struct uv_udp_s { UV_HANDLE_FIELDS /* read-only */ /* * Number of bytes queued for sending. This field strictly shows how much * information is currently queued. */ size_t send_queue_size; /* * Number of send requests currently in the queue awaiting to be processed. */ size_t send_queue_count; UV_UDP_PRIVATE_FIELDS }; /* uv_udp_send_t is a subclass of uv_req_t. */ struct uv_udp_send_s { UV_REQ_FIELDS uv_udp_t* handle; uv_udp_send_cb cb; UV_UDP_SEND_PRIVATE_FIELDS }; UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle); UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags); UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); UV_EXTERN int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags); UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen); UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership); UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on); UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr); UV_EXTERN int uv_udp_set_broadcast(uv_udp_t* handle, int on); UV_EXTERN int uv_udp_set_ttl(uv_udp_t* handle, int ttl); UV_EXTERN int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, uv_udp_send_cb send_cb); UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr); UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); /* * uv_tty_t is a subclass of uv_stream_t. * * Representing a stream for the console. */ struct uv_tty_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS UV_TTY_PRIVATE_FIELDS }; typedef enum { /* Initial/normal terminal mode */ UV_TTY_MODE_NORMAL, /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */ UV_TTY_MODE_RAW, /* Binary-safe I/O mode for IPC (Unix-only) */ UV_TTY_MODE_IO } uv_tty_mode_t; UV_EXTERN int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable); UV_EXTERN int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode); UV_EXTERN int uv_tty_reset_mode(void); UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); #ifdef __cplusplus extern "C++" { inline int uv_tty_set_mode(uv_tty_t* handle, int mode) { return uv_tty_set_mode(handle, static_cast(mode)); } } #endif UV_EXTERN uv_handle_type uv_guess_handle(uv_file file); /* * uv_pipe_t is a subclass of uv_stream_t. * * Representing a pipe stream or pipe server. On Windows this is a Named * Pipe. On Unix this is a Unix domain socket. */ struct uv_pipe_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS int ipc; /* non-zero if this pipe is used for passing handles */ UV_PIPE_PRIVATE_FIELDS }; UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file); UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); UV_EXTERN void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb); UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size); UV_EXTERN int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size); UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count); UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle); UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); struct uv_poll_s { UV_HANDLE_FIELDS uv_poll_cb poll_cb; UV_POLL_PRIVATE_FIELDS }; enum uv_poll_event { UV_READABLE = 1, UV_WRITABLE = 2 }; UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd); UV_EXTERN int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket); UV_EXTERN int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb); UV_EXTERN int uv_poll_stop(uv_poll_t* handle); struct uv_prepare_s { UV_HANDLE_FIELDS UV_PREPARE_PRIVATE_FIELDS }; UV_EXTERN int uv_prepare_init(uv_loop_t*, uv_prepare_t* prepare); UV_EXTERN int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb); UV_EXTERN int uv_prepare_stop(uv_prepare_t* prepare); struct uv_check_s { UV_HANDLE_FIELDS UV_CHECK_PRIVATE_FIELDS }; UV_EXTERN int uv_check_init(uv_loop_t*, uv_check_t* check); UV_EXTERN int uv_check_start(uv_check_t* check, uv_check_cb cb); UV_EXTERN int uv_check_stop(uv_check_t* check); struct uv_idle_s { UV_HANDLE_FIELDS UV_IDLE_PRIVATE_FIELDS }; UV_EXTERN int uv_idle_init(uv_loop_t*, uv_idle_t* idle); UV_EXTERN int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb); UV_EXTERN int uv_idle_stop(uv_idle_t* idle); struct uv_async_s { UV_HANDLE_FIELDS UV_ASYNC_PRIVATE_FIELDS }; UV_EXTERN int uv_async_init(uv_loop_t*, uv_async_t* async, uv_async_cb async_cb); UV_EXTERN int uv_async_send(uv_async_t* async); /* * uv_timer_t is a subclass of uv_handle_t. * * Used to get woken up at a specified time in the future. */ struct uv_timer_s { UV_HANDLE_FIELDS UV_TIMER_PRIVATE_FIELDS }; UV_EXTERN int uv_timer_init(uv_loop_t*, uv_timer_t* handle); UV_EXTERN int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat); UV_EXTERN int uv_timer_stop(uv_timer_t* handle); UV_EXTERN int uv_timer_again(uv_timer_t* handle); UV_EXTERN void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat); UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle); /* * uv_getaddrinfo_t is a subclass of uv_req_t. * * Request object for uv_getaddrinfo. */ struct uv_getaddrinfo_s { UV_REQ_FIELDS /* read-only */ uv_loop_t* loop; /* struct addrinfo* addrinfo is marked as private, but it really isn't. */ UV_GETADDRINFO_PRIVATE_FIELDS }; UV_EXTERN int uv_getaddrinfo(uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb, const char* node, const char* service, const struct addrinfo* hints); UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai); /* * uv_getnameinfo_t is a subclass of uv_req_t. * * Request object for uv_getnameinfo. */ struct uv_getnameinfo_s { UV_REQ_FIELDS /* read-only */ uv_loop_t* loop; /* host and service are marked as private, but they really aren't. */ UV_GETNAMEINFO_PRIVATE_FIELDS }; UV_EXTERN int uv_getnameinfo(uv_loop_t* loop, uv_getnameinfo_t* req, uv_getnameinfo_cb getnameinfo_cb, const struct sockaddr* addr, int flags); /* uv_spawn() options. */ typedef enum { UV_IGNORE = 0x00, UV_CREATE_PIPE = 0x01, UV_INHERIT_FD = 0x02, UV_INHERIT_STREAM = 0x04, /* * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE * determine the direction of flow, from the child process' perspective. Both * flags may be specified to create a duplex data stream. */ UV_READABLE_PIPE = 0x10, UV_WRITABLE_PIPE = 0x20 } uv_stdio_flags; typedef struct uv_stdio_container_s { uv_stdio_flags flags; union { uv_stream_t* stream; int fd; } data; } uv_stdio_container_t; typedef struct uv_process_options_s { uv_exit_cb exit_cb; /* Called after the process exits. */ const char* file; /* Path to program to execute. */ /* * Command line arguments. args[0] should be the path to the program. On * Windows this uses CreateProcess which concatenates the arguments into a * string this can cause some strange errors. See the note at * windows_verbatim_arguments. */ char** args; /* * This will be set as the environ variable in the subprocess. If this is * NULL then the parents environ will be used. */ char** env; /* * If non-null this represents a directory the subprocess should execute * in. Stands for current working directory. */ const char* cwd; /* * Various flags that control how uv_spawn() behaves. See the definition of * `enum uv_process_flags` below. */ unsigned int flags; /* * The `stdio` field points to an array of uv_stdio_container_t structs that * describe the file descriptors that will be made available to the child * process. The convention is that stdio[0] points to stdin, fd 1 is used for * stdout, and fd 2 is stderr. * * Note that on windows file descriptors greater than 2 are available to the * child process only if the child processes uses the MSVCRT runtime. */ int stdio_count; uv_stdio_container_t* stdio; /* * Libuv can change the child process' user/group id. This happens only when * the appropriate bits are set in the flags fields. This is not supported on * windows; uv_spawn() will fail and set the error to UV_ENOTSUP. */ uv_uid_t uid; uv_gid_t gid; } uv_process_options_t; /* * These are the flags that can be used for the uv_process_options.flags field. */ enum uv_process_flags { /* * Set the child process' user id. The user id is supplied in the `uid` field * of the options struct. This does not work on windows; setting this flag * will cause uv_spawn() to fail. */ UV_PROCESS_SETUID = (1 << 0), /* * Set the child process' group id. The user id is supplied in the `gid` * field of the options struct. This does not work on windows; setting this * flag will cause uv_spawn() to fail. */ UV_PROCESS_SETGID = (1 << 1), /* * Do not wrap any arguments in quotes, or perform any other escaping, when * converting the argument list into a command line string. This option is * only meaningful on Windows systems. On Unix it is silently ignored. */ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2), /* * Spawn the child process in a detached state - this will make it a process * group leader, and will effectively enable the child to keep running after * the parent exits. Note that the child process will still keep the * parent's event loop alive unless the parent process calls uv_unref() on * the child's process handle. */ UV_PROCESS_DETACHED = (1 << 3), /* * Hide the subprocess console window that would normally be created. This * option is only meaningful on Windows systems. On Unix it is silently * ignored. */ UV_PROCESS_WINDOWS_HIDE = (1 << 4) }; /* * uv_process_t is a subclass of uv_handle_t. */ struct uv_process_s { UV_HANDLE_FIELDS uv_exit_cb exit_cb; int pid; UV_PROCESS_PRIVATE_FIELDS }; UV_EXTERN int uv_spawn(uv_loop_t* loop, uv_process_t* handle, const uv_process_options_t* options); UV_EXTERN int uv_process_kill(uv_process_t*, int signum); UV_EXTERN int uv_kill(int pid, int signum); /* * uv_work_t is a subclass of uv_req_t. */ struct uv_work_s { UV_REQ_FIELDS uv_loop_t* loop; uv_work_cb work_cb; uv_after_work_cb after_work_cb; UV_WORK_PRIVATE_FIELDS }; UV_EXTERN int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb); UV_EXTERN int uv_cancel(uv_req_t* req); struct uv_cpu_info_s { char* model; int speed; struct uv_cpu_times_s { uint64_t user; uint64_t nice; uint64_t sys; uint64_t idle; uint64_t irq; } cpu_times; }; struct uv_interface_address_s { char* name; char phys_addr[6]; int is_internal; union { struct sockaddr_in address4; struct sockaddr_in6 address6; } address; union { struct sockaddr_in netmask4; struct sockaddr_in6 netmask6; } netmask; }; typedef enum { UV_DIRENT_UNKNOWN, UV_DIRENT_FILE, UV_DIRENT_DIR, UV_DIRENT_LINK, UV_DIRENT_FIFO, UV_DIRENT_SOCKET, UV_DIRENT_CHAR, UV_DIRENT_BLOCK } uv_dirent_type_t; struct uv_dirent_s { const char* name; uv_dirent_type_t type; }; UV_EXTERN char** uv_setup_args(int argc, char** argv); UV_EXTERN int uv_get_process_title(char* buffer, size_t size); UV_EXTERN int uv_set_process_title(const char* title); UV_EXTERN int uv_resident_set_memory(size_t* rss); UV_EXTERN int uv_uptime(double* uptime); typedef struct { long tv_sec; long tv_usec; } uv_timeval_t; typedef struct { uv_timeval_t ru_utime; /* user CPU time used */ uv_timeval_t ru_stime; /* system CPU time used */ uint64_t ru_maxrss; /* maximum resident set size */ uint64_t ru_ixrss; /* integral shared memory size */ uint64_t ru_idrss; /* integral unshared data size */ uint64_t ru_isrss; /* integral unshared stack size */ uint64_t ru_minflt; /* page reclaims (soft page faults) */ uint64_t ru_majflt; /* page faults (hard page faults) */ uint64_t ru_nswap; /* swaps */ uint64_t ru_inblock; /* block input operations */ uint64_t ru_oublock; /* block output operations */ uint64_t ru_msgsnd; /* IPC messages sent */ uint64_t ru_msgrcv; /* IPC messages received */ uint64_t ru_nsignals; /* signals received */ uint64_t ru_nvcsw; /* voluntary context switches */ uint64_t ru_nivcsw; /* involuntary context switches */ } uv_rusage_t; UV_EXTERN int uv_getrusage(uv_rusage_t* rusage); UV_EXTERN int uv_os_homedir(char* buffer, size_t* size); UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, int* count); UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, int count); typedef enum { UV_FS_UNKNOWN = -1, UV_FS_CUSTOM, UV_FS_OPEN, UV_FS_CLOSE, UV_FS_READ, UV_FS_WRITE, UV_FS_SENDFILE, UV_FS_STAT, UV_FS_LSTAT, UV_FS_FSTAT, UV_FS_FTRUNCATE, UV_FS_UTIME, UV_FS_FUTIME, UV_FS_ACCESS, UV_FS_CHMOD, UV_FS_FCHMOD, UV_FS_FSYNC, UV_FS_FDATASYNC, UV_FS_UNLINK, UV_FS_RMDIR, UV_FS_MKDIR, UV_FS_MKDTEMP, UV_FS_RENAME, UV_FS_SCANDIR, UV_FS_LINK, UV_FS_SYMLINK, UV_FS_READLINK, UV_FS_CHOWN, UV_FS_FCHOWN, UV_FS_REALPATH } uv_fs_type; /* uv_fs_t is a subclass of uv_req_t. */ struct uv_fs_s { UV_REQ_FIELDS uv_fs_type fs_type; uv_loop_t* loop; uv_fs_cb cb; ssize_t result; void* ptr; const char* path; uv_stat_t statbuf; /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */ UV_FS_PRIVATE_FIELDS }; UV_EXTERN void uv_fs_req_cleanup(uv_fs_t* req); UV_EXTERN int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); UV_EXTERN int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb); UV_EXTERN int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb); UV_EXTERN int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb); UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb); UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent); UV_EXTERN int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); UV_EXTERN int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); UV_EXTERN int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); UV_EXTERN int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); UV_EXTERN int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb); UV_EXTERN int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb); UV_EXTERN int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); UV_EXTERN int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); UV_EXTERN int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); UV_EXTERN int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb); UV_EXTERN int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); /* * This flag can be used with uv_fs_symlink() on Windows to specify whether * path argument points to a directory. */ #define UV_FS_SYMLINK_DIR 0x0001 /* * This flag can be used with uv_fs_symlink() on Windows to specify whether * the symlink is to be created using junction points. */ #define UV_FS_SYMLINK_JUNCTION 0x0002 UV_EXTERN int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); UV_EXTERN int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); UV_EXTERN int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb); UV_EXTERN int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); UV_EXTERN int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); enum uv_fs_event { UV_RENAME = 1, UV_CHANGE = 2 }; struct uv_fs_event_s { UV_HANDLE_FIELDS /* private */ char* path; UV_FS_EVENT_PRIVATE_FIELDS }; /* * uv_fs_stat() based polling file watcher. */ struct uv_fs_poll_s { UV_HANDLE_FIELDS /* Private, don't touch. */ void* poll_ctx; }; UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle); UV_EXTERN int uv_fs_poll_start(uv_fs_poll_t* handle, uv_fs_poll_cb poll_cb, const char* path, unsigned int interval); UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle); UV_EXTERN int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size); struct uv_signal_s { UV_HANDLE_FIELDS uv_signal_cb signal_cb; int signum; UV_SIGNAL_PRIVATE_FIELDS }; UV_EXTERN int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle); UV_EXTERN int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum); UV_EXTERN int uv_signal_stop(uv_signal_t* handle); UV_EXTERN void uv_loadavg(double avg[3]); /* * Flags to be passed to uv_fs_event_start(). */ enum uv_fs_event_flags { /* * By default, if the fs event watcher is given a directory name, we will * watch for all events in that directory. This flags overrides this behavior * and makes fs_event report only changes to the directory entry itself. This * flag does not affect individual files watched. * This flag is currently not implemented yet on any backend. */ UV_FS_EVENT_WATCH_ENTRY = 1, /* * By default uv_fs_event will try to use a kernel interface such as inotify * or kqueue to detect events. This may not work on remote filesystems such * as NFS mounts. This flag makes fs_event fall back to calling stat() on a * regular interval. * This flag is currently not implemented yet on any backend. */ UV_FS_EVENT_STAT = 2, /* * By default, event watcher, when watching directory, is not registering * (is ignoring) changes in it's subdirectories. * This flag will override this behaviour on platforms that support it. */ UV_FS_EVENT_RECURSIVE = 4 }; UV_EXTERN int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle); UV_EXTERN int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags); UV_EXTERN int uv_fs_event_stop(uv_fs_event_t* handle); UV_EXTERN int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size); UV_EXTERN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr); UV_EXTERN int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr); UV_EXTERN int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size); UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); UV_EXTERN int uv_exepath(char* buffer, size_t* size); UV_EXTERN int uv_cwd(char* buffer, size_t* size); UV_EXTERN int uv_chdir(const char* dir); UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); UV_EXTERN uint64_t uv_hrtime(void); UV_EXTERN void uv_disable_stdio_inheritance(void); UV_EXTERN int uv_dlopen(const char* filename, uv_lib_t* lib); UV_EXTERN void uv_dlclose(uv_lib_t* lib); UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle); UV_EXTERN int uv_rwlock_init(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_destroy(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock); UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock); UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock); UV_EXTERN int uv_sem_init(uv_sem_t* sem, unsigned int value); UV_EXTERN void uv_sem_destroy(uv_sem_t* sem); UV_EXTERN void uv_sem_post(uv_sem_t* sem); UV_EXTERN void uv_sem_wait(uv_sem_t* sem); UV_EXTERN int uv_sem_trywait(uv_sem_t* sem); UV_EXTERN int uv_cond_init(uv_cond_t* cond); UV_EXTERN void uv_cond_destroy(uv_cond_t* cond); UV_EXTERN void uv_cond_signal(uv_cond_t* cond); UV_EXTERN void uv_cond_broadcast(uv_cond_t* cond); UV_EXTERN int uv_barrier_init(uv_barrier_t* barrier, unsigned int count); UV_EXTERN void uv_barrier_destroy(uv_barrier_t* barrier); UV_EXTERN int uv_barrier_wait(uv_barrier_t* barrier); UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex); UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout); UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); UV_EXTERN int uv_key_create(uv_key_t* key); UV_EXTERN void uv_key_delete(uv_key_t* key); UV_EXTERN void* uv_key_get(uv_key_t* key); UV_EXTERN void uv_key_set(uv_key_t* key, void* value); typedef void (*uv_thread_cb)(void* arg); UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg); UV_EXTERN uv_thread_t uv_thread_self(void); UV_EXTERN int uv_thread_join(uv_thread_t *tid); UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2); /* The presence of these unions force similar struct layout. */ #define XX(_, name) uv_ ## name ## _t name; union uv_any_handle { UV_HANDLE_TYPE_MAP(XX) }; union uv_any_req { UV_REQ_TYPE_MAP(XX) }; #undef XX struct uv_loop_s { /* User data - use this for whatever. */ void* data; /* Loop reference counting. */ unsigned int active_handles; void* handle_queue[2]; void* active_reqs[2]; /* Internal flag to signal loop stop. */ unsigned int stop_flag; UV_LOOP_PRIVATE_FIELDS }; /* Don't export the private CPP symbols. */ #undef UV_HANDLE_TYPE_PRIVATE #undef UV_REQ_TYPE_PRIVATE #undef UV_REQ_PRIVATE_FIELDS #undef UV_STREAM_PRIVATE_FIELDS #undef UV_TCP_PRIVATE_FIELDS #undef UV_PREPARE_PRIVATE_FIELDS #undef UV_CHECK_PRIVATE_FIELDS #undef UV_IDLE_PRIVATE_FIELDS #undef UV_ASYNC_PRIVATE_FIELDS #undef UV_TIMER_PRIVATE_FIELDS #undef UV_GETADDRINFO_PRIVATE_FIELDS #undef UV_GETNAMEINFO_PRIVATE_FIELDS #undef UV_FS_REQ_PRIVATE_FIELDS #undef UV_WORK_PRIVATE_FIELDS #undef UV_FS_EVENT_PRIVATE_FIELDS #undef UV_SIGNAL_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_PLATFORM_FIELDS #ifdef __cplusplus } #endif #endif /* UV_H */ libuv-v1.8.0/libuv.nsi000066400000000000000000000054541263361716600147060ustar00rootroot00000000000000; NSIS installer script for libuv !include "MUI2.nsh" Name "libuv" OutFile "libuv-${ARCH}-${VERSION}.exe" !include "x64.nsh" # Default install location, for 32-bit files InstallDir "$PROGRAMFILES\libuv" # Override install and registry locations if this is a 64-bit install. function .onInit ${If} ${ARCH} == "x64" SetRegView 64 StrCpy $INSTDIR "$PROGRAMFILES64\libuv" ${EndIf} functionEnd ;-------------------------------- ; Installer pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH ;-------------------------------- ; Uninstaller pages !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH ;-------------------------------- ; Languages !insertmacro MUI_LANGUAGE "English" ;-------------------------------- ; Installer sections Section "Files" SecInstall SectionIn RO SetOutPath "$INSTDIR" File "Release\*.dll" File "Release\*.lib" File "LICENSE" File "README.md" SetOutPath "$INSTDIR\include" File "include\uv.h" File "include\uv-errno.h" File "include\uv-threadpool.h" File "include\uv-version.h" File "include\uv-win.h" File "include\tree.h" WriteUninstaller "$INSTDIR\Uninstall.exe" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "DisplayName" "libuv-${ARCH}-${VERSION}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "UninstallString" "$\"$INSTDIR\Uninstall.exe$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "QuietUninstallString" "$\"$INSTDIR\Uninstall.exe$\" /S" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "HelpLink" "http://libuv.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "URLInfoAbout" "http://libuv.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "DisplayVersion" "${VERSION}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" "NoRepair" "1" SectionEnd Section "Uninstall" Delete "$INSTDIR\libuv.dll" Delete "$INSTDIR\libuv.lib" Delete "$INSTDIR\LICENSE" Delete "$INSTDIR\README.md" Delete "$INSTDIR\include\uv.h" Delete "$INSTDIR\include\uv-errno.h" Delete "$INSTDIR\include\uv-threadpool.h" Delete "$INSTDIR\include\uv-version.h" Delete "$INSTDIR\include\uv-win.h" Delete "$INSTDIR\include\tree.h" Delete "$INSTDIR\Uninstall.exe" RMDir "$INSTDIR" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libuv-${ARCH}-${VERSION}" SectionEnd libuv-v1.8.0/libuv.pc.in000066400000000000000000000004031263361716600151110ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@prefix@ libdir=@libdir@ includedir=@includedir@ Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Description: multi-platform support library with a focus on asynchronous I/O. Libs: -L${libdir} -luv @LIBS@ Cflags: -I${includedir} libuv-v1.8.0/m4/000077500000000000000000000000001263361716600133625ustar00rootroot00000000000000libuv-v1.8.0/m4/.gitignore000066400000000000000000000001141263361716600153460ustar00rootroot00000000000000# Ignore libtoolize-generated files. *.m4 !as_case.m4 !libuv-check-flags.m4 libuv-v1.8.0/m4/as_case.m4000066400000000000000000000010031263361716600152140ustar00rootroot00000000000000# AS_CASE(WORD, [PATTERN1], [IF-MATCHED1]...[DEFAULT]) # ---------------------------------------------------- # Expand into # | case WORD in # | PATTERN1) IF-MATCHED1 ;; # | ... # | *) DEFAULT ;; # | esac m4_define([_AS_CASE], [m4_if([$#], 0, [m4_fatal([$0: too few arguments: $#])], [$#], 1, [ *) $1 ;;], [$#], 2, [ $1) m4_default([$2], [:]) ;;], [ $1) m4_default([$2], [:]) ;; $0(m4_shiftn(2, $@))])dnl ]) m4_defun([AS_CASE], [m4_ifval([$2$3], [case $1 in _AS_CASE(m4_shift($@)) esac])]) libuv-v1.8.0/m4/libuv-check-flags.m4000066400000000000000000000245571263361716600171270ustar00rootroot00000000000000dnl Macros to check the presence of generic (non-typed) symbols. dnl Copyright (c) 2006-2008 Diego Pettenà dnl Copyright (c) 2006-2008 xine project dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 3, or (at your option) dnl any later version. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA dnl 02110-1301, USA. dnl dnl As a special exception, the copyright owners of the dnl macro gives unlimited permission to copy, distribute and modify the dnl configure scripts that are the output of Autoconf when processing the dnl Macro. You need not follow the terms of the GNU General Public dnl License when using or distributing such scripts, even though portions dnl of the text of the Macro appear in them. The GNU General Public dnl License (GPL) does govern all other use of the material that dnl constitutes the Autoconf Macro. dnl dnl This special exception to the GPL applies to versions of the dnl Autoconf Macro released by this project. When you make and dnl distribute a modified version of the Autoconf Macro, you may extend dnl this special exception to the GPL to apply to your modified version as dnl well. dnl Check if the flag is supported by compiler dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [ AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]), [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" AC_COMPILE_IFELSE([AC_LANG_SOURCE([int a;])], [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"], [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"]) CFLAGS="$ac_save_CFLAGS" ]) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], [$2], [$3]) ]) dnl Check if the flag is supported by compiler (cacheable) dnl CC_CHECK_CFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) AC_DEFUN([CC_CHECK_CFLAGS], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_cflags_$1]), CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! ) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], [$2], [$3]) ]) dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found]) dnl Check for CFLAG and appends them to CFLAGS if supported AC_DEFUN([CC_CHECK_CFLAG_APPEND], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_cflags_$1]), CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! ) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], [CFLAGS="$CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) ]) dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) AC_DEFUN([CC_CHECK_CFLAGS_APPEND], [ for flag in $1; do CC_CHECK_CFLAG_APPEND($flag, [$2], [$3]) done ]) dnl Check if the flag is supported by linker (cacheable) dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) AC_DEFUN([CC_CHECK_LDFLAGS], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_ldflags_$1]), [ac_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $1" AC_LANG_PUSH([C]) AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])], [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) AC_LANG_POP([C]) LDFLAGS="$ac_save_LDFLAGS" ]) AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], [$2], [$3]) ]) dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for dnl the current linker to avoid undefined references in a shared object. AC_DEFUN([CC_NOUNDEFINED], [ dnl We check $host for which systems to enable this for. AC_REQUIRE([AC_CANONICAL_HOST]) case $host in dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads dnl are requested, as different implementations are present; to avoid problems dnl use -Wl,-z,defs only for those platform not behaving this way. *-freebsd* | *-openbsd*) ;; *) dnl First of all check for the --no-undefined variant of GNU ld. This allows dnl for a much more readable commandline, so that people can understand what dnl it does without going to look for what the heck -z defs does. for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"]) break done ;; esac AC_SUBST([LDFLAGS_NOUNDEFINED]) ]) dnl Check for a -Werror flag or equivalent. -Werror is the GCC dnl and ICC flag that tells the compiler to treat all the warnings dnl as fatal. We usually need this option to make sure that some dnl constructs (like attributes) are not simply ignored. dnl dnl Other compilers don't support -Werror per se, but they support dnl an equivalent flag: dnl - Sun Studio compiler supports -errwarn=%all AC_DEFUN([CC_CHECK_WERROR], [ AC_CACHE_CHECK( [for $CC way to treat warnings as errors], [cc_cv_werror], [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror], [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])]) ]) ]) AC_DEFUN([CC_CHECK_ATTRIBUTE], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))], AS_TR_SH([cc_cv_attribute_$1]), [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" AC_LANG_PUSH([C]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) AC_LANG_POP([C]) CFLAGS="$ac_save_CFLAGS" ]) AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes], [AC_DEFINE( AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1, [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))] ) $4], [$5]) ]) AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ CC_CHECK_ATTRIBUTE( [constructor],, [void __attribute__((constructor)) ctor() { int a; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ CC_CHECK_ATTRIBUTE( [format], [format(printf, n, n)], [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [ CC_CHECK_ATTRIBUTE( [format_arg], [format_arg(printf)], [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ CC_CHECK_ATTRIBUTE( [visibility_$1], [visibility("$1")], [void __attribute__((visibility("$1"))) $1_function() { }], [$2], [$3]) ]) AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ CC_CHECK_ATTRIBUTE( [nonnull], [nonnull()], [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ CC_CHECK_ATTRIBUTE( [unused], , [void some_function(void *foo, __attribute__((unused)) void *bar);], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [ CC_CHECK_ATTRIBUTE( [sentinel], , [void some_function(void *foo, ...) __attribute__((sentinel));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [ CC_CHECK_ATTRIBUTE( [deprecated], , [void some_function(void *foo, ...) __attribute__((deprecated));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_ALIAS], [ CC_CHECK_ATTRIBUTE( [alias], [weak, alias], [void other_function(void *foo) { } void some_function(void *foo) __attribute__((weak, alias("other_function")));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_MALLOC], [ CC_CHECK_ATTRIBUTE( [malloc], , [void * __attribute__((malloc)) my_alloc(int n);], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_PACKED], [ CC_CHECK_ATTRIBUTE( [packed], , [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));], [$1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_CONST], [ CC_CHECK_ATTRIBUTE( [const], , [int __attribute__((const)) twopow(int n) { return 1 << n; } ], [$1], [$2]) ]) AC_DEFUN([CC_FLAG_VISIBILITY], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([if $CC supports -fvisibility=hidden], [cc_cv_flag_visibility], [cc_flag_visibility_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden], cc_cv_flag_visibility='yes', cc_cv_flag_visibility='no') CFLAGS="$cc_flag_visibility_save_CFLAGS"]) AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, [Define this if the compiler supports the -fvisibility flag]) $1], [$2]) ]) AC_DEFUN([CC_FUNC_EXPECT], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([if compiler has __builtin_expect function], [cc_cv_func_expect], [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" AC_LANG_PUSH([C]) AC_COMPILE_IFELSE([AC_LANG_SOURCE( [int some_function() { int a = 3; return (int)__builtin_expect(a, 3); }])], [cc_cv_func_expect=yes], [cc_cv_func_expect=no]) AC_LANG_POP([C]) CFLAGS="$ac_save_CFLAGS" ]) AS_IF([test "x$cc_cv_func_expect" = "xyes"], [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, [Define this if the compiler supports __builtin_expect() function]) $1], [$2]) ]) AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ AC_REQUIRE([CC_CHECK_WERROR]) AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported], [cc_cv_attribute_aligned], [ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $cc_cv_werror" AC_LANG_PUSH([C]) for cc_attribute_align_try in 64 32 16 8 4 2; do AC_COMPILE_IFELSE([AC_LANG_SOURCE([ int main() { static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; return c; }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) done AC_LANG_POP([C]) CFLAGS="$ac_save_CFLAGS" ]) if test "x$cc_cv_attribute_aligned" != "x"; then AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned], [Define the highest alignment supported]) fi ])libuv-v1.8.0/samples/000077500000000000000000000000001263361716600145065ustar00rootroot00000000000000libuv-v1.8.0/samples/.gitignore000066400000000000000000000021441263361716600164770ustar00rootroot00000000000000# Copyright StrongLoop, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. *.mk *.Makefile libuv-v1.8.0/samples/socks5-proxy/000077500000000000000000000000001263361716600170745ustar00rootroot00000000000000libuv-v1.8.0/samples/socks5-proxy/.gitignore000066400000000000000000000021341263361716600210640ustar00rootroot00000000000000# Copyright StrongLoop, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. /build/ libuv-v1.8.0/samples/socks5-proxy/LICENSE000066400000000000000000000051231263361716600201020ustar00rootroot00000000000000Files: * ======== Copyright StrongLoop, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: getopt.c =============== Copyright (c) 1987, 1993, 1994 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libuv-v1.8.0/samples/socks5-proxy/Makefile000066400000000000000000000031311263361716600205320ustar00rootroot00000000000000# Copyright StrongLoop, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. BUILDTYPE ?= Debug BUILDDIR ?= build GYP ?= gyp V ?= SOURCES := client.c defs.h getopt.c main.c s5.c s5.h server.c util.c .PHONY: all clean all: $(BUILDDIR)/$(BUILDTYPE)/s5-proxy clean: $(RM) $(BUILDDIR) $(BUILDDIR)/$(BUILDTYPE)/s5-proxy: $(BUILDDIR)/Makefile $(SOURCES) $(MAKE) -C $(BUILDDIR) V=$(V) $(BUILDDIR)/Makefile: ../../common.gypi build.gyp $(GYP) \ -Duv_library=static_library \ -Goutput_dir=. \ -I../../common.gypi \ -f make \ --depth=. \ --generator-output=$(BUILDDIR) \ build.gyp libuv-v1.8.0/samples/socks5-proxy/build.gyp000066400000000000000000000030771263361716600207230ustar00rootroot00000000000000# Copyright StrongLoop, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. { 'targets': [ { 'dependencies': ['../../uv.gyp:libuv'], 'target_name': 's5-proxy', 'type': 'executable', 'sources': [ 'client.c', 'defs.h', 'main.c', 's5.c', 's5.h', 'server.c', 'util.c', ], 'conditions': [ ['OS=="win"', { 'defines': ['HAVE_UNISTD_H=0'], 'sources': ['getopt.c'] }, { 'defines': ['HAVE_UNISTD_H=1'] }] ] } ] } libuv-v1.8.0/samples/socks5-proxy/client.c000066400000000000000000000527351263361716600205320ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "defs.h" #include #include #include /* A connection is modeled as an abstraction on top of two simple state * machines, one for reading and one for writing. Either state machine * is, when active, in one of three states: busy, done or stop; the fourth * and final state, dead, is an end state and only relevant when shutting * down the connection. A short overview: * * busy done stop * ----------|---------------------------|--------------------|------| * readable | waiting for incoming data | have incoming data | idle | * writable | busy writing out data | completed write | idle | * * We could remove the done state from the writable state machine. For our * purposes, it's functionally equivalent to the stop state. * * When the connection with upstream has been established, the client_ctx * moves into a state where incoming data from the client is sent upstream * and vice versa, incoming data from upstream is sent to the client. In * other words, we're just piping data back and forth. See conn_cycle() * for details. * * An interesting deviation from libuv's I/O model is that reads are discrete * rather than continuous events. In layman's terms, when a read operation * completes, the connection stops reading until further notice. * * The rationale for this approach is that we have to wait until the data * has been sent out again before we can reuse the read buffer. * * It also pleasingly unifies with the request model that libuv uses for * writes and everything else; libuv may switch to a request model for * reads in the future. */ enum conn_state { c_busy, /* Busy; waiting for incoming data or for a write to complete. */ c_done, /* Done; read incoming data or write finished. */ c_stop, /* Stopped. */ c_dead }; /* Session states. */ enum sess_state { s_handshake, /* Wait for client handshake. */ s_handshake_auth, /* Wait for client authentication data. */ s_req_start, /* Start waiting for request data. */ s_req_parse, /* Wait for request data. */ s_req_lookup, /* Wait for upstream hostname DNS lookup to complete. */ s_req_connect, /* Wait for uv_tcp_connect() to complete. */ s_proxy_start, /* Connected. Start piping data. */ s_proxy, /* Connected. Pipe data back and forth. */ s_kill, /* Tear down session. */ s_almost_dead_0, /* Waiting for finalizers to complete. */ s_almost_dead_1, /* Waiting for finalizers to complete. */ s_almost_dead_2, /* Waiting for finalizers to complete. */ s_almost_dead_3, /* Waiting for finalizers to complete. */ s_almost_dead_4, /* Waiting for finalizers to complete. */ s_dead /* Dead. Safe to free now. */ }; static void do_next(client_ctx *cx); static int do_handshake(client_ctx *cx); static int do_handshake_auth(client_ctx *cx); static int do_req_start(client_ctx *cx); static int do_req_parse(client_ctx *cx); static int do_req_lookup(client_ctx *cx); static int do_req_connect_start(client_ctx *cx); static int do_req_connect(client_ctx *cx); static int do_proxy_start(client_ctx *cx); static int do_proxy(client_ctx *cx); static int do_kill(client_ctx *cx); static int do_almost_dead(client_ctx *cx); static int conn_cycle(const char *who, conn *a, conn *b); static void conn_timer_reset(conn *c); static void conn_timer_expire(uv_timer_t *handle, int status); static void conn_getaddrinfo(conn *c, const char *hostname); static void conn_getaddrinfo_done(uv_getaddrinfo_t *req, int status, struct addrinfo *ai); static int conn_connect(conn *c); static void conn_connect_done(uv_connect_t *req, int status); static void conn_read(conn *c); static void conn_read_done(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf); static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf); static void conn_write(conn *c, const void *data, unsigned int len); static void conn_write_done(uv_write_t *req, int status); static void conn_close(conn *c); static void conn_close_done(uv_handle_t *handle); /* |incoming| has been initialized by server.c when this is called. */ void client_finish_init(server_ctx *sx, client_ctx *cx) { conn *incoming; conn *outgoing; cx->sx = sx; cx->state = s_handshake; s5_init(&cx->parser); incoming = &cx->incoming; incoming->client = cx; incoming->result = 0; incoming->rdstate = c_stop; incoming->wrstate = c_stop; incoming->idle_timeout = sx->idle_timeout; CHECK(0 == uv_timer_init(sx->loop, &incoming->timer_handle)); outgoing = &cx->outgoing; outgoing->client = cx; outgoing->result = 0; outgoing->rdstate = c_stop; outgoing->wrstate = c_stop; outgoing->idle_timeout = sx->idle_timeout; CHECK(0 == uv_tcp_init(cx->sx->loop, &outgoing->handle.tcp)); CHECK(0 == uv_timer_init(cx->sx->loop, &outgoing->timer_handle)); /* Wait for the initial packet. */ conn_read(incoming); } /* This is the core state machine that drives the client <-> upstream proxy. * We move through the initial handshake and authentication steps first and * end up (if all goes well) in the proxy state where we're just proxying * data between the client and upstream. */ static void do_next(client_ctx *cx) { int new_state; ASSERT(cx->state != s_dead); switch (cx->state) { case s_handshake: new_state = do_handshake(cx); break; case s_handshake_auth: new_state = do_handshake_auth(cx); break; case s_req_start: new_state = do_req_start(cx); break; case s_req_parse: new_state = do_req_parse(cx); break; case s_req_lookup: new_state = do_req_lookup(cx); break; case s_req_connect: new_state = do_req_connect(cx); break; case s_proxy_start: new_state = do_proxy_start(cx); break; case s_proxy: new_state = do_proxy(cx); break; case s_kill: new_state = do_kill(cx); break; case s_almost_dead_0: case s_almost_dead_1: case s_almost_dead_2: case s_almost_dead_3: case s_almost_dead_4: new_state = do_almost_dead(cx); break; default: UNREACHABLE(); } cx->state = new_state; if (cx->state == s_dead) { if (DEBUG_CHECKS) { memset(cx, -1, sizeof(*cx)); } free(cx); } } static int do_handshake(client_ctx *cx) { unsigned int methods; conn *incoming; s5_ctx *parser; uint8_t *data; size_t size; int err; parser = &cx->parser; incoming = &cx->incoming; ASSERT(incoming->rdstate == c_done); ASSERT(incoming->wrstate == c_stop); incoming->rdstate = c_stop; if (incoming->result < 0) { pr_err("read error: %s", uv_strerror(incoming->result)); return do_kill(cx); } data = (uint8_t *) incoming->t.buf; size = (size_t) incoming->result; err = s5_parse(parser, &data, &size); if (err == s5_ok) { conn_read(incoming); return s_handshake; /* Need more data. */ } if (size != 0) { /* Could allow a round-trip saving shortcut here if the requested auth * method is S5_AUTH_NONE (provided unauthenticated traffic is allowed.) * Requires client support however. */ pr_err("junk in handshake"); return do_kill(cx); } if (err != s5_auth_select) { pr_err("handshake error: %s", s5_strerror(err)); return do_kill(cx); } methods = s5_auth_methods(parser); if ((methods & S5_AUTH_NONE) && can_auth_none(cx->sx, cx)) { s5_select_auth(parser, S5_AUTH_NONE); conn_write(incoming, "\5\0", 2); /* No auth required. */ return s_req_start; } if ((methods & S5_AUTH_PASSWD) && can_auth_passwd(cx->sx, cx)) { /* TODO(bnoordhuis) Implement username/password auth. */ } conn_write(incoming, "\5\377", 2); /* No acceptable auth. */ return s_kill; } /* TODO(bnoordhuis) Implement username/password auth. */ static int do_handshake_auth(client_ctx *cx) { UNREACHABLE(); return do_kill(cx); } static int do_req_start(client_ctx *cx) { conn *incoming; incoming = &cx->incoming; ASSERT(incoming->rdstate == c_stop); ASSERT(incoming->wrstate == c_done); incoming->wrstate = c_stop; if (incoming->result < 0) { pr_err("write error: %s", uv_strerror(incoming->result)); return do_kill(cx); } conn_read(incoming); return s_req_parse; } static int do_req_parse(client_ctx *cx) { conn *incoming; conn *outgoing; s5_ctx *parser; uint8_t *data; size_t size; int err; parser = &cx->parser; incoming = &cx->incoming; outgoing = &cx->outgoing; ASSERT(incoming->rdstate == c_done); ASSERT(incoming->wrstate == c_stop); ASSERT(outgoing->rdstate == c_stop); ASSERT(outgoing->wrstate == c_stop); incoming->rdstate = c_stop; if (incoming->result < 0) { pr_err("read error: %s", uv_strerror(incoming->result)); return do_kill(cx); } data = (uint8_t *) incoming->t.buf; size = (size_t) incoming->result; err = s5_parse(parser, &data, &size); if (err == s5_ok) { conn_read(incoming); return s_req_parse; /* Need more data. */ } if (size != 0) { pr_err("junk in request %u", (unsigned) size); return do_kill(cx); } if (err != s5_exec_cmd) { pr_err("request error: %s", s5_strerror(err)); return do_kill(cx); } if (parser->cmd == s5_cmd_tcp_bind) { /* Not supported but relatively straightforward to implement. */ pr_warn("BIND requests are not supported."); return do_kill(cx); } if (parser->cmd == s5_cmd_udp_assoc) { /* Not supported. Might be hard to implement because libuv has no * functionality for detecting the MTU size which the RFC mandates. */ pr_warn("UDP ASSOC requests are not supported."); return do_kill(cx); } ASSERT(parser->cmd == s5_cmd_tcp_connect); if (parser->atyp == s5_atyp_host) { conn_getaddrinfo(outgoing, (const char *) parser->daddr); return s_req_lookup; } if (parser->atyp == s5_atyp_ipv4) { memset(&outgoing->t.addr4, 0, sizeof(outgoing->t.addr4)); outgoing->t.addr4.sin_family = AF_INET; outgoing->t.addr4.sin_port = htons(parser->dport); memcpy(&outgoing->t.addr4.sin_addr, parser->daddr, sizeof(outgoing->t.addr4.sin_addr)); } else if (parser->atyp == s5_atyp_ipv6) { memset(&outgoing->t.addr6, 0, sizeof(outgoing->t.addr6)); outgoing->t.addr6.sin6_family = AF_INET6; outgoing->t.addr6.sin6_port = htons(parser->dport); memcpy(&outgoing->t.addr6.sin6_addr, parser->daddr, sizeof(outgoing->t.addr6.sin6_addr)); } else { UNREACHABLE(); } return do_req_connect_start(cx); } static int do_req_lookup(client_ctx *cx) { s5_ctx *parser; conn *incoming; conn *outgoing; parser = &cx->parser; incoming = &cx->incoming; outgoing = &cx->outgoing; ASSERT(incoming->rdstate == c_stop); ASSERT(incoming->wrstate == c_stop); ASSERT(outgoing->rdstate == c_stop); ASSERT(outgoing->wrstate == c_stop); if (outgoing->result < 0) { /* TODO(bnoordhuis) Escape control characters in parser->daddr. */ pr_err("lookup error for \"%s\": %s", parser->daddr, uv_strerror(outgoing->result)); /* Send back a 'Host unreachable' reply. */ conn_write(incoming, "\5\4\0\1\0\0\0\0\0\0", 10); return s_kill; } /* Don't make assumptions about the offset of sin_port/sin6_port. */ switch (outgoing->t.addr.sa_family) { case AF_INET: outgoing->t.addr4.sin_port = htons(parser->dport); break; case AF_INET6: outgoing->t.addr6.sin6_port = htons(parser->dport); break; default: UNREACHABLE(); } return do_req_connect_start(cx); } /* Assumes that cx->outgoing.t.sa contains a valid AF_INET/AF_INET6 address. */ static int do_req_connect_start(client_ctx *cx) { conn *incoming; conn *outgoing; int err; incoming = &cx->incoming; outgoing = &cx->outgoing; ASSERT(incoming->rdstate == c_stop); ASSERT(incoming->wrstate == c_stop); ASSERT(outgoing->rdstate == c_stop); ASSERT(outgoing->wrstate == c_stop); if (!can_access(cx->sx, cx, &outgoing->t.addr)) { pr_warn("connection not allowed by ruleset"); /* Send a 'Connection not allowed by ruleset' reply. */ conn_write(incoming, "\5\2\0\1\0\0\0\0\0\0", 10); return s_kill; } err = conn_connect(outgoing); if (err != 0) { pr_err("connect error: %s\n", uv_strerror(err)); return do_kill(cx); } return s_req_connect; } static int do_req_connect(client_ctx *cx) { const struct sockaddr_in6 *in6; const struct sockaddr_in *in; char addr_storage[sizeof(*in6)]; conn *incoming; conn *outgoing; uint8_t *buf; int addrlen; incoming = &cx->incoming; outgoing = &cx->outgoing; ASSERT(incoming->rdstate == c_stop); ASSERT(incoming->wrstate == c_stop); ASSERT(outgoing->rdstate == c_stop); ASSERT(outgoing->wrstate == c_stop); /* Build and send the reply. Not very pretty but gets the job done. */ buf = (uint8_t *) incoming->t.buf; if (outgoing->result == 0) { /* The RFC mandates that the SOCKS server must include the local port * and address in the reply. So that's what we do. */ addrlen = sizeof(addr_storage); CHECK(0 == uv_tcp_getsockname(&outgoing->handle.tcp, (struct sockaddr *) addr_storage, &addrlen)); buf[0] = 5; /* Version. */ buf[1] = 0; /* Success. */ buf[2] = 0; /* Reserved. */ if (addrlen == sizeof(*in)) { buf[3] = 1; /* IPv4. */ in = (const struct sockaddr_in *) &addr_storage; memcpy(buf + 4, &in->sin_addr, 4); memcpy(buf + 8, &in->sin_port, 2); conn_write(incoming, buf, 10); } else if (addrlen == sizeof(*in6)) { buf[3] = 4; /* IPv6. */ in6 = (const struct sockaddr_in6 *) &addr_storage; memcpy(buf + 4, &in6->sin6_addr, 16); memcpy(buf + 20, &in6->sin6_port, 2); conn_write(incoming, buf, 22); } else { UNREACHABLE(); } return s_proxy_start; } else { pr_err("upstream connection error: %s\n", uv_strerror(outgoing->result)); /* Send a 'Connection refused' reply. */ conn_write(incoming, "\5\5\0\1\0\0\0\0\0\0", 10); return s_kill; } UNREACHABLE(); return s_kill; } static int do_proxy_start(client_ctx *cx) { conn *incoming; conn *outgoing; incoming = &cx->incoming; outgoing = &cx->outgoing; ASSERT(incoming->rdstate == c_stop); ASSERT(incoming->wrstate == c_done); ASSERT(outgoing->rdstate == c_stop); ASSERT(outgoing->wrstate == c_stop); incoming->wrstate = c_stop; if (incoming->result < 0) { pr_err("write error: %s", uv_strerror(incoming->result)); return do_kill(cx); } conn_read(incoming); conn_read(outgoing); return s_proxy; } /* Proxy incoming data back and forth. */ static int do_proxy(client_ctx *cx) { if (conn_cycle("client", &cx->incoming, &cx->outgoing)) { return do_kill(cx); } if (conn_cycle("upstream", &cx->outgoing, &cx->incoming)) { return do_kill(cx); } return s_proxy; } static int do_kill(client_ctx *cx) { int new_state; if (cx->state >= s_almost_dead_0) { return cx->state; } /* Try to cancel the request. The callback still runs but if the * cancellation succeeded, it gets called with status=UV_ECANCELED. */ new_state = s_almost_dead_1; if (cx->state == s_req_lookup) { new_state = s_almost_dead_0; uv_cancel(&cx->outgoing.t.req); } conn_close(&cx->incoming); conn_close(&cx->outgoing); return new_state; } static int do_almost_dead(client_ctx *cx) { ASSERT(cx->state >= s_almost_dead_0); return cx->state + 1; /* Another finalizer completed. */ } static int conn_cycle(const char *who, conn *a, conn *b) { if (a->result < 0) { if (a->result != UV_EOF) { pr_err("%s error: %s", who, uv_strerror(a->result)); } return -1; } if (b->result < 0) { return -1; } if (a->wrstate == c_done) { a->wrstate = c_stop; } /* The logic is as follows: read when we don't write and write when we don't * read. That gives us back-pressure handling for free because if the peer * sends data faster than we consume it, TCP congestion control kicks in. */ if (a->wrstate == c_stop) { if (b->rdstate == c_stop) { conn_read(b); } else if (b->rdstate == c_done) { conn_write(a, b->t.buf, b->result); b->rdstate = c_stop; /* Triggers the call to conn_read() above. */ } } return 0; } static void conn_timer_reset(conn *c) { CHECK(0 == uv_timer_start(&c->timer_handle, conn_timer_expire, c->idle_timeout, 0)); } static void conn_timer_expire(uv_timer_t *handle, int status) { conn *c; CHECK(0 == status); c = CONTAINER_OF(handle, conn, timer_handle); c->result = UV_ETIMEDOUT; do_next(c->client); } static void conn_getaddrinfo(conn *c, const char *hostname) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; CHECK(0 == uv_getaddrinfo(c->client->sx->loop, &c->t.addrinfo_req, conn_getaddrinfo_done, hostname, NULL, &hints)); conn_timer_reset(c); } static void conn_getaddrinfo_done(uv_getaddrinfo_t *req, int status, struct addrinfo *ai) { conn *c; c = CONTAINER_OF(req, conn, t.addrinfo_req); c->result = status; if (status == 0) { /* FIXME(bnoordhuis) Should try all addresses. */ if (ai->ai_family == AF_INET) { c->t.addr4 = *(const struct sockaddr_in *) ai->ai_addr; } else if (ai->ai_family == AF_INET6) { c->t.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr; } else { UNREACHABLE(); } } uv_freeaddrinfo(ai); do_next(c->client); } /* Assumes that c->t.sa contains a valid AF_INET or AF_INET6 address. */ static int conn_connect(conn *c) { ASSERT(c->t.addr.sa_family == AF_INET || c->t.addr.sa_family == AF_INET6); conn_timer_reset(c); return uv_tcp_connect(&c->t.connect_req, &c->handle.tcp, &c->t.addr, conn_connect_done); } static void conn_connect_done(uv_connect_t *req, int status) { conn *c; if (status == UV_ECANCELED) { return; /* Handle has been closed. */ } c = CONTAINER_OF(req, conn, t.connect_req); c->result = status; do_next(c->client); } static void conn_read(conn *c) { ASSERT(c->rdstate == c_stop); CHECK(0 == uv_read_start(&c->handle.stream, conn_alloc, conn_read_done)); c->rdstate = c_busy; conn_timer_reset(c); } static void conn_read_done(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { conn *c; c = CONTAINER_OF(handle, conn, handle); ASSERT(c->t.buf == buf->base); ASSERT(c->rdstate == c_busy); c->rdstate = c_done; c->result = nread; uv_read_stop(&c->handle.stream); do_next(c->client); } static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) { conn *c; c = CONTAINER_OF(handle, conn, handle); ASSERT(c->rdstate == c_busy); buf->base = c->t.buf; buf->len = sizeof(c->t.buf); } static void conn_write(conn *c, const void *data, unsigned int len) { uv_buf_t buf; ASSERT(c->wrstate == c_stop || c->wrstate == c_done); c->wrstate = c_busy; /* It's okay to cast away constness here, uv_write() won't modify the * memory. */ buf.base = (char *) data; buf.len = len; CHECK(0 == uv_write(&c->write_req, &c->handle.stream, &buf, 1, conn_write_done)); conn_timer_reset(c); } static void conn_write_done(uv_write_t *req, int status) { conn *c; if (status == UV_ECANCELED) { return; /* Handle has been closed. */ } c = CONTAINER_OF(req, conn, write_req); ASSERT(c->wrstate == c_busy); c->wrstate = c_done; c->result = status; do_next(c->client); } static void conn_close(conn *c) { ASSERT(c->rdstate != c_dead); ASSERT(c->wrstate != c_dead); c->rdstate = c_dead; c->wrstate = c_dead; c->timer_handle.data = c; c->handle.handle.data = c; uv_close(&c->handle.handle, conn_close_done); uv_close((uv_handle_t *) &c->timer_handle, conn_close_done); } static void conn_close_done(uv_handle_t *handle) { conn *c; c = handle->data; do_next(c->client); } libuv-v1.8.0/samples/socks5-proxy/defs.h000066400000000000000000000107701263361716600201730ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef DEFS_H_ #define DEFS_H_ #include "s5.h" #include "uv.h" #include #include /* sockaddr_in, sockaddr_in6 */ #include /* size_t, ssize_t */ #include #include /* sockaddr */ struct client_ctx; typedef struct { const char *bind_host; unsigned short bind_port; unsigned int idle_timeout; } server_config; typedef struct { unsigned int idle_timeout; /* Connection idle timeout in ms. */ uv_tcp_t tcp_handle; uv_loop_t *loop; } server_ctx; typedef struct { unsigned char rdstate; unsigned char wrstate; unsigned int idle_timeout; struct client_ctx *client; /* Backlink to owning client context. */ ssize_t result; union { uv_handle_t handle; uv_stream_t stream; uv_tcp_t tcp; uv_udp_t udp; } handle; uv_timer_t timer_handle; /* For detecting timeouts. */ uv_write_t write_req; /* We only need one of these at a time so make them share memory. */ union { uv_getaddrinfo_t addrinfo_req; uv_connect_t connect_req; uv_req_t req; struct sockaddr_in6 addr6; struct sockaddr_in addr4; struct sockaddr addr; char buf[2048]; /* Scratch space. Used to read data into. */ } t; } conn; typedef struct client_ctx { unsigned int state; server_ctx *sx; /* Backlink to owning server context. */ s5_ctx parser; /* The SOCKS protocol parser. */ conn incoming; /* Connection with the SOCKS client. */ conn outgoing; /* Connection with upstream. */ } client_ctx; /* server.c */ int server_run(const server_config *cf, uv_loop_t *loop); int can_auth_none(const server_ctx *sx, const client_ctx *cx); int can_auth_passwd(const server_ctx *sx, const client_ctx *cx); int can_access(const server_ctx *sx, const client_ctx *cx, const struct sockaddr *addr); /* client.c */ void client_finish_init(server_ctx *sx, client_ctx *cx); /* util.c */ #if defined(__GNUC__) # define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__((format(printf, a, b))) #else # define ATTRIBUTE_FORMAT_PRINTF(a, b) #endif void pr_info(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); void pr_warn(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); void pr_err(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); void *xmalloc(size_t size); /* main.c */ const char *_getprogname(void); /* getopt.c */ #if !HAVE_UNISTD_H extern char *optarg; int getopt(int argc, char **argv, const char *options); #endif /* ASSERT() is for debug checks, CHECK() for run-time sanity checks. * DEBUG_CHECKS is for expensive debug checks that we only want to * enable in debug builds but still want type-checked by the compiler * in release builds. */ #if defined(NDEBUG) # define ASSERT(exp) # define CHECK(exp) do { if (!(exp)) abort(); } while (0) # define DEBUG_CHECKS (0) #else # define ASSERT(exp) assert(exp) # define CHECK(exp) assert(exp) # define DEBUG_CHECKS (1) #endif #define UNREACHABLE() CHECK(!"Unreachable code reached.") /* This macro looks complicated but it's not: it calculates the address * of the embedding struct through the address of the embedded struct. * In other words, if struct A embeds struct B, then we can obtain * the address of A by taking the address of B and subtracting the * field offset of B in A. */ #define CONTAINER_OF(ptr, type, field) \ ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) #endif /* DEFS_H_ */ libuv-v1.8.0/samples/socks5-proxy/getopt.c000066400000000000000000000077131263361716600205520ustar00rootroot00000000000000/* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */ /* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include extern const char *_getprogname(void); int opterr = 1, /* if error message should be printed */ optind = 1, /* index into parent argv vector */ optopt, /* character checked for validity */ optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define BADARG (int)':' #define EMSG "" /* * getopt -- * Parse argc/argv argument vector. */ int getopt(nargc, nargv, ostr) int nargc; char * const nargv[]; const char *ostr; { static char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (optreset || *place == 0) { /* update scanning pointer */ optreset = 0; place = nargv[optind]; if (optind >= nargc || *place++ != '-') { /* Argument is absent or is not an option */ place = EMSG; return (-1); } optopt = *place++; if (optopt == '-' && *place == 0) { /* "--" => end of options */ ++optind; place = EMSG; return (-1); } if (optopt == 0) { /* Solitary '-', treat as a '-' option if the program (eg su) is looking for it. */ place = EMSG; if (strchr(ostr, '-') == NULL) return (-1); optopt = '-'; } } else optopt = *place++; /* See if option letter is one the caller wanted... */ if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { if (*place == 0) ++optind; if (opterr && *ostr != ':') (void)fprintf(stderr, "%s: illegal option -- %c\n", _getprogname(), optopt); return (BADCH); } /* Does this option need an argument? */ if (oli[1] != ':') { /* don't need argument */ optarg = NULL; if (*place == 0) ++optind; } else { /* Option-argument is either the rest of this argument or the entire next argument. */ if (*place) optarg = place; else if (nargc > ++optind) optarg = nargv[optind]; else { /* option-argument absent */ place = EMSG; if (*ostr == ':') return (BADARG); if (opterr) (void)fprintf(stderr, "%s: option requires an argument -- %c\n", _getprogname(), optopt); return (BADCH); } place = EMSG; ++optind; } return (optopt); /* return option letter */ } libuv-v1.8.0/samples/socks5-proxy/main.c000066400000000000000000000055171263361716600201740ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "defs.h" #include #include #include #if HAVE_UNISTD_H #include /* getopt */ #endif #define DEFAULT_BIND_HOST "127.0.0.1" #define DEFAULT_BIND_PORT 1080 #define DEFAULT_IDLE_TIMEOUT (60 * 1000) static void parse_opts(server_config *cf, int argc, char **argv); static void usage(void); static const char *progname = __FILE__; /* Reset in main(). */ int main(int argc, char **argv) { server_config config; int err; progname = argv[0]; memset(&config, 0, sizeof(config)); config.bind_host = DEFAULT_BIND_HOST; config.bind_port = DEFAULT_BIND_PORT; config.idle_timeout = DEFAULT_IDLE_TIMEOUT; parse_opts(&config, argc, argv); err = server_run(&config, uv_default_loop()); if (err) { exit(1); } return 0; } const char *_getprogname(void) { return progname; } static void parse_opts(server_config *cf, int argc, char **argv) { int opt; while (-1 != (opt = getopt(argc, argv, "H:hp:"))) { switch (opt) { case 'H': cf->bind_host = optarg; break; case 'p': if (1 != sscanf(optarg, "%hu", &cf->bind_port)) { pr_err("bad port number: %s", optarg); usage(); } break; default: usage(); } } } static void usage(void) { printf("Usage:\n" "\n" " %s [-b
[-h] [-p ]\n" "\n" "Options:\n" "\n" " -b Bind to this address or hostname.\n" " Default: \"127.0.0.1\"\n" " -h Show this help message.\n" " -p Bind to this port number. Default: 1080\n" "", progname); exit(1); } libuv-v1.8.0/samples/socks5-proxy/s5.c000066400000000000000000000145431263361716600175760ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "s5.h" #include #include #include /* abort() */ #include /* memset() */ enum { s5_version, s5_nmethods, s5_methods, s5_auth_pw_version, s5_auth_pw_userlen, s5_auth_pw_username, s5_auth_pw_passlen, s5_auth_pw_password, s5_req_version, s5_req_cmd, s5_req_reserved, s5_req_atyp, s5_req_atyp_host, s5_req_daddr, s5_req_dport0, s5_req_dport1, s5_dead }; void s5_init(s5_ctx *cx) { memset(cx, 0, sizeof(*cx)); cx->state = s5_version; } s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size) { s5_err err; uint8_t *p; uint8_t c; size_t i; size_t n; p = *data; n = *size; i = 0; while (i < n) { c = p[i]; i += 1; switch (cx->state) { case s5_version: if (c != 5) { err = s5_bad_version; goto out; } cx->state = s5_nmethods; break; case s5_nmethods: cx->arg0 = 0; cx->arg1 = c; /* Number of bytes to read. */ cx->state = s5_methods; break; case s5_methods: if (cx->arg0 < cx->arg1) { switch (c) { case 0: cx->methods |= S5_AUTH_NONE; break; case 1: cx->methods |= S5_AUTH_GSSAPI; break; case 2: cx->methods |= S5_AUTH_PASSWD; break; /* Ignore everything we don't understand. */ } cx->arg0 += 1; } if (cx->arg0 == cx->arg1) { err = s5_auth_select; goto out; } break; case s5_auth_pw_version: if (c != 1) { err = s5_bad_version; goto out; } cx->state = s5_auth_pw_userlen; break; case s5_auth_pw_userlen: cx->arg0 = 0; cx->userlen = c; cx->state = s5_auth_pw_username; break; case s5_auth_pw_username: if (cx->arg0 < cx->userlen) { cx->username[cx->arg0] = c; cx->arg0 += 1; } if (cx->arg0 == cx->userlen) { cx->username[cx->userlen] = '\0'; cx->state = s5_auth_pw_passlen; } break; case s5_auth_pw_passlen: cx->arg0 = 0; cx->passlen = c; cx->state = s5_auth_pw_password; break; case s5_auth_pw_password: if (cx->arg0 < cx->passlen) { cx->password[cx->arg0] = c; cx->arg0 += 1; } if (cx->arg0 == cx->passlen) { cx->password[cx->passlen] = '\0'; cx->state = s5_req_version; err = s5_auth_verify; goto out; } break; case s5_req_version: if (c != 5) { err = s5_bad_version; goto out; } cx->state = s5_req_cmd; break; case s5_req_cmd: switch (c) { case 1: /* TCP connect */ cx->cmd = s5_cmd_tcp_connect; break; case 3: /* UDP associate */ cx->cmd = s5_cmd_udp_assoc; break; default: err = s5_bad_cmd; goto out; } cx->state = s5_req_reserved; break; case s5_req_reserved: cx->state = s5_req_atyp; break; case s5_req_atyp: cx->arg0 = 0; switch (c) { case 1: /* IPv4, four octets. */ cx->state = s5_req_daddr; cx->atyp = s5_atyp_ipv4; cx->arg1 = 4; break; case 3: /* Hostname. First byte is length. */ cx->state = s5_req_atyp_host; cx->atyp = s5_atyp_host; cx->arg1 = 0; break; case 4: /* IPv6, sixteen octets. */ cx->state = s5_req_daddr; cx->atyp = s5_atyp_ipv6; cx->arg1 = 16; break; default: err = s5_bad_atyp; goto out; } break; case s5_req_atyp_host: cx->arg1 = c; cx->state = s5_req_daddr; break; case s5_req_daddr: if (cx->arg0 < cx->arg1) { cx->daddr[cx->arg0] = c; cx->arg0 += 1; } if (cx->arg0 == cx->arg1) { cx->daddr[cx->arg1] = '\0'; cx->state = s5_req_dport0; } break; case s5_req_dport0: cx->dport = c << 8; cx->state = s5_req_dport1; break; case s5_req_dport1: cx->dport |= c; cx->state = s5_dead; err = s5_exec_cmd; goto out; case s5_dead: break; default: abort(); } } err = s5_ok; out: *data = p + i; *size = n - i; return err; } unsigned int s5_auth_methods(const s5_ctx *cx) { return cx->methods; } int s5_select_auth(s5_ctx *cx, s5_auth_method method) { int err; err = 0; switch (method) { case S5_AUTH_NONE: cx->state = s5_req_version; break; case S5_AUTH_PASSWD: cx->state = s5_auth_pw_version; break; default: err = -EINVAL; } return err; } const char *s5_strerror(s5_err err) { #define S5_ERR_GEN(_, name, errmsg) case s5_ ## name: return errmsg; switch (err) { S5_ERR_MAP(S5_ERR_GEN) default: ; /* Silence s5_max_errors -Wswitch warning. */ } #undef S5_ERR_GEN return "Unknown error."; } libuv-v1.8.0/samples/socks5-proxy/s5.h000066400000000000000000000060321263361716600175750ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef S5_H_ #define S5_H_ #include #include #define S5_ERR_MAP(V) \ V(-1, bad_version, "Bad protocol version.") \ V(-2, bad_cmd, "Bad protocol command.") \ V(-3, bad_atyp, "Bad address type.") \ V(0, ok, "No error.") \ V(1, auth_select, "Select authentication method.") \ V(2, auth_verify, "Verify authentication.") \ V(3, exec_cmd, "Execute command.") \ typedef enum { #define S5_ERR_GEN(code, name, _) s5_ ## name = code, S5_ERR_MAP(S5_ERR_GEN) #undef S5_ERR_GEN s5_max_errors } s5_err; typedef enum { S5_AUTH_NONE = 1 << 0, S5_AUTH_GSSAPI = 1 << 1, S5_AUTH_PASSWD = 1 << 2 } s5_auth_method; typedef enum { s5_auth_allow, s5_auth_deny } s5_auth_result; typedef enum { s5_atyp_ipv4, s5_atyp_ipv6, s5_atyp_host } s5_atyp; typedef enum { s5_cmd_tcp_connect, s5_cmd_tcp_bind, s5_cmd_udp_assoc } s5_cmd; typedef struct { uint32_t arg0; /* Scratch space for the state machine. */ uint32_t arg1; /* Scratch space for the state machine. */ uint8_t state; uint8_t methods; uint8_t cmd; uint8_t atyp; uint8_t userlen; uint8_t passlen; uint16_t dport; uint8_t username[257]; uint8_t password[257]; uint8_t daddr[257]; /* TODO(bnoordhuis) Merge with username/password. */ } s5_ctx; void s5_init(s5_ctx *ctx); s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size); /* Only call after s5_parse() has returned s5_want_auth_method. */ unsigned int s5_auth_methods(const s5_ctx *cx); /* Call after s5_parse() has returned s5_want_auth_method. */ int s5_select_auth(s5_ctx *cx, s5_auth_method method); const char *s5_strerror(s5_err err); #endif /* S5_H_ */ libuv-v1.8.0/samples/socks5-proxy/server.c000066400000000000000000000151351263361716600205530ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "defs.h" #include /* INET6_ADDRSTRLEN */ #include #include #ifndef INET6_ADDRSTRLEN # define INET6_ADDRSTRLEN 63 #endif typedef struct { uv_getaddrinfo_t getaddrinfo_req; server_config config; server_ctx *servers; uv_loop_t *loop; } server_state; static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *ai); static void on_connection(uv_stream_t *server, int status); int server_run(const server_config *cf, uv_loop_t *loop) { struct addrinfo hints; server_state state; int err; memset(&state, 0, sizeof(state)); state.servers = NULL; state.config = *cf; state.loop = loop; /* Resolve the address of the interface that we should bind to. * The getaddrinfo callback starts the server and everything else. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; err = uv_getaddrinfo(loop, &state.getaddrinfo_req, do_bind, cf->bind_host, NULL, &hints); if (err != 0) { pr_err("getaddrinfo: %s", uv_strerror(err)); return err; } /* Start the event loop. Control continues in do_bind(). */ if (uv_run(loop, UV_RUN_DEFAULT)) { abort(); } /* Please Valgrind. */ uv_loop_delete(loop); free(state.servers); return 0; } /* Bind a server to each address that getaddrinfo() reported. */ static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *addrs) { char addrbuf[INET6_ADDRSTRLEN + 1]; unsigned int ipv4_naddrs; unsigned int ipv6_naddrs; server_state *state; server_config *cf; struct addrinfo *ai; const void *addrv; const char *what; uv_loop_t *loop; server_ctx *sx; unsigned int n; int err; union { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; } s; state = CONTAINER_OF(req, server_state, getaddrinfo_req); loop = state->loop; cf = &state->config; if (status < 0) { pr_err("getaddrinfo(\"%s\"): %s", cf->bind_host, uv_strerror(status)); uv_freeaddrinfo(addrs); return; } ipv4_naddrs = 0; ipv6_naddrs = 0; for (ai = addrs; ai != NULL; ai = ai->ai_next) { if (ai->ai_family == AF_INET) { ipv4_naddrs += 1; } else if (ai->ai_family == AF_INET6) { ipv6_naddrs += 1; } } if (ipv4_naddrs == 0 && ipv6_naddrs == 0) { pr_err("%s has no IPv4/6 addresses", cf->bind_host); uv_freeaddrinfo(addrs); return; } state->servers = xmalloc((ipv4_naddrs + ipv6_naddrs) * sizeof(state->servers[0])); n = 0; for (ai = addrs; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { continue; } if (ai->ai_family == AF_INET) { s.addr4 = *(const struct sockaddr_in *) ai->ai_addr; s.addr4.sin_port = htons(cf->bind_port); addrv = &s.addr4.sin_addr; } else if (ai->ai_family == AF_INET6) { s.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr; s.addr6.sin6_port = htons(cf->bind_port); addrv = &s.addr6.sin6_addr; } else { UNREACHABLE(); } if (uv_inet_ntop(s.addr.sa_family, addrv, addrbuf, sizeof(addrbuf))) { UNREACHABLE(); } sx = state->servers + n; sx->loop = loop; sx->idle_timeout = state->config.idle_timeout; CHECK(0 == uv_tcp_init(loop, &sx->tcp_handle)); what = "uv_tcp_bind"; err = uv_tcp_bind(&sx->tcp_handle, &s.addr, 0); if (err == 0) { what = "uv_listen"; err = uv_listen((uv_stream_t *) &sx->tcp_handle, 128, on_connection); } if (err != 0) { pr_err("%s(\"%s:%hu\"): %s", what, addrbuf, cf->bind_port, uv_strerror(err)); while (n > 0) { n -= 1; uv_close((uv_handle_t *) (state->servers + n), NULL); } break; } pr_info("listening on %s:%hu", addrbuf, cf->bind_port); n += 1; } uv_freeaddrinfo(addrs); } static void on_connection(uv_stream_t *server, int status) { server_ctx *sx; client_ctx *cx; CHECK(status == 0); sx = CONTAINER_OF(server, server_ctx, tcp_handle); cx = xmalloc(sizeof(*cx)); CHECK(0 == uv_tcp_init(sx->loop, &cx->incoming.handle.tcp)); CHECK(0 == uv_accept(server, &cx->incoming.handle.stream)); client_finish_init(sx, cx); } int can_auth_none(const server_ctx *sx, const client_ctx *cx) { return 1; } int can_auth_passwd(const server_ctx *sx, const client_ctx *cx) { return 0; } int can_access(const server_ctx *sx, const client_ctx *cx, const struct sockaddr *addr) { const struct sockaddr_in6 *addr6; const struct sockaddr_in *addr4; const uint32_t *p; uint32_t a; uint32_t b; uint32_t c; uint32_t d; /* TODO(bnoordhuis) Implement proper access checks. For now, just reject * traffic to localhost. */ if (addr->sa_family == AF_INET) { addr4 = (const struct sockaddr_in *) addr; d = ntohl(addr4->sin_addr.s_addr); return (d >> 24) != 0x7F; } if (addr->sa_family == AF_INET6) { addr6 = (const struct sockaddr_in6 *) addr; p = (const uint32_t *) &addr6->sin6_addr.s6_addr; a = ntohl(p[0]); b = ntohl(p[1]); c = ntohl(p[2]); d = ntohl(p[3]); if (a == 0 && b == 0 && c == 0 && d == 1) { return 0; /* "::1" style address. */ } if (a == 0 && b == 0 && c == 0xFFFF && (d >> 24) == 0x7F) { return 0; /* "::ffff:127.x.x.x" style address. */ } return 1; } return 0; } libuv-v1.8.0/samples/socks5-proxy/util.c000066400000000000000000000041741263361716600202230ustar00rootroot00000000000000/* Copyright StrongLoop, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "defs.h" #include #include #include static void pr_do(FILE *stream, const char *label, const char *fmt, va_list ap); void *xmalloc(size_t size) { void *ptr; ptr = malloc(size); if (ptr == NULL) { pr_err("out of memory, need %lu bytes", (unsigned long) size); exit(1); } return ptr; } void pr_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); pr_do(stdout, "info", fmt, ap); va_end(ap); } void pr_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); pr_do(stderr, "warn", fmt, ap); va_end(ap); } void pr_err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); pr_do(stderr, "error", fmt, ap); va_end(ap); } static void pr_do(FILE *stream, const char *label, const char *fmt, va_list ap) { char fmtbuf[1024]; vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap); fprintf(stream, "%s:%s: %s\n", _getprogname(), label, fmtbuf); } libuv-v1.8.0/src/000077500000000000000000000000001263361716600136315ustar00rootroot00000000000000libuv-v1.8.0/src/fs-poll.c000066400000000000000000000150631263361716600153560ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "uv-common.h" #include #include #include struct poll_ctx { uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */ int busy_polling; unsigned int interval; uint64_t start_time; uv_loop_t* loop; uv_fs_poll_cb poll_cb; uv_timer_t timer_handle; uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */ uv_stat_t statbuf; char path[1]; /* variable length */ }; static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b); static void poll_cb(uv_fs_t* req); static void timer_cb(uv_timer_t* timer); static void timer_close_cb(uv_handle_t* handle); static uv_stat_t zero_statbuf; int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL); return 0; } int uv_fs_poll_start(uv_fs_poll_t* handle, uv_fs_poll_cb cb, const char* path, unsigned int interval) { struct poll_ctx* ctx; uv_loop_t* loop; size_t len; int err; if (uv__is_active(handle)) return 0; loop = handle->loop; len = strlen(path); ctx = uv__calloc(1, sizeof(*ctx) + len); if (ctx == NULL) return UV_ENOMEM; ctx->loop = loop; ctx->poll_cb = cb; ctx->interval = interval ? interval : 1; ctx->start_time = uv_now(loop); ctx->parent_handle = handle; memcpy(ctx->path, path, len + 1); err = uv_timer_init(loop, &ctx->timer_handle); if (err < 0) goto error; ctx->timer_handle.flags |= UV__HANDLE_INTERNAL; uv__handle_unref(&ctx->timer_handle); err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb); if (err < 0) goto error; handle->poll_ctx = ctx; uv__handle_start(handle); return 0; error: uv__free(ctx); return err; } int uv_fs_poll_stop(uv_fs_poll_t* handle) { struct poll_ctx* ctx; if (!uv__is_active(handle)) return 0; ctx = handle->poll_ctx; assert(ctx != NULL); assert(ctx->parent_handle != NULL); ctx->parent_handle = NULL; handle->poll_ctx = NULL; /* Close the timer if it's active. If it's inactive, there's a stat request * in progress and poll_cb will take care of the cleanup. */ if (uv__is_active(&ctx->timer_handle)) uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); uv__handle_stop(handle); return 0; } int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) { struct poll_ctx* ctx; size_t required_len; if (!uv__is_active(handle)) { *size = 0; return UV_EINVAL; } ctx = handle->poll_ctx; assert(ctx != NULL); required_len = strlen(ctx->path); if (required_len > *size) { *size = required_len; return UV_ENOBUFS; } memcpy(buffer, ctx->path, required_len); *size = required_len; return 0; } void uv__fs_poll_close(uv_fs_poll_t* handle) { uv_fs_poll_stop(handle); } static void timer_cb(uv_timer_t* timer) { struct poll_ctx* ctx; ctx = container_of(timer, struct poll_ctx, timer_handle); assert(ctx->parent_handle != NULL); assert(ctx->parent_handle->poll_ctx == ctx); ctx->start_time = uv_now(ctx->loop); if (uv_fs_stat(ctx->loop, &ctx->fs_req, ctx->path, poll_cb)) abort(); } static void poll_cb(uv_fs_t* req) { uv_stat_t* statbuf; struct poll_ctx* ctx; uint64_t interval; ctx = container_of(req, struct poll_ctx, fs_req); if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */ uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); uv_fs_req_cleanup(req); return; } if (req->result != 0) { if (ctx->busy_polling != req->result) { ctx->poll_cb(ctx->parent_handle, req->result, &ctx->statbuf, &zero_statbuf); ctx->busy_polling = req->result; } goto out; } statbuf = &req->statbuf; if (ctx->busy_polling != 0) if (ctx->busy_polling < 0 || !statbuf_eq(&ctx->statbuf, statbuf)) ctx->poll_cb(ctx->parent_handle, 0, &ctx->statbuf, statbuf); ctx->statbuf = *statbuf; ctx->busy_polling = 1; out: uv_fs_req_cleanup(req); if (ctx->parent_handle == NULL) { /* handle has been stopped by callback */ uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); return; } /* Reschedule timer, subtract the delay from doing the stat(). */ interval = ctx->interval; interval -= (uv_now(ctx->loop) - ctx->start_time) % interval; if (uv_timer_start(&ctx->timer_handle, timer_cb, interval, 0)) abort(); } static void timer_close_cb(uv_handle_t* handle) { uv__free(container_of(handle, struct poll_ctx, timer_handle)); } static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) { return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && a->st_birthtim.tv_nsec == b->st_birthtim.tv_nsec && a->st_ctim.tv_sec == b->st_ctim.tv_sec && a->st_mtim.tv_sec == b->st_mtim.tv_sec && a->st_birthtim.tv_sec == b->st_birthtim.tv_sec && a->st_size == b->st_size && a->st_mode == b->st_mode && a->st_uid == b->st_uid && a->st_gid == b->st_gid && a->st_ino == b->st_ino && a->st_dev == b->st_dev && a->st_flags == b->st_flags && a->st_gen == b->st_gen; } #if defined(_WIN32) #include "win/internal.h" #include "win/handle-inl.h" void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) { assert(handle->flags & UV__HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } #endif /* _WIN32 */ libuv-v1.8.0/src/heap-inl.h000066400000000000000000000156151263361716600155070ustar00rootroot00000000000000/* Copyright (c) 2013, Ben Noordhuis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef UV_SRC_HEAP_H_ #define UV_SRC_HEAP_H_ #include /* NULL */ #if defined(__GNUC__) # define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration #else # define HEAP_EXPORT(declaration) static declaration #endif struct heap_node { struct heap_node* left; struct heap_node* right; struct heap_node* parent; }; /* A binary min heap. The usual properties hold: the root is the lowest * element in the set, the height of the tree is at most log2(nodes) and * it's always a complete binary tree. * * The heap function try hard to detect corrupted tree nodes at the cost * of a minor reduction in performance. Compile with -DNDEBUG to disable. */ struct heap { struct heap_node* min; unsigned int nelts; }; /* Return non-zero if a < b. */ typedef int (*heap_compare_fn)(const struct heap_node* a, const struct heap_node* b); /* Public functions. */ HEAP_EXPORT(void heap_init(struct heap* heap)); HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)); HEAP_EXPORT(void heap_insert(struct heap* heap, struct heap_node* newnode, heap_compare_fn less_than)); HEAP_EXPORT(void heap_remove(struct heap* heap, struct heap_node* node, heap_compare_fn less_than)); HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)); /* Implementation follows. */ HEAP_EXPORT(void heap_init(struct heap* heap)) { heap->min = NULL; heap->nelts = 0; } HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) { return heap->min; } /* Swap parent with child. Child moves closer to the root, parent moves away. */ static void heap_node_swap(struct heap* heap, struct heap_node* parent, struct heap_node* child) { struct heap_node* sibling; struct heap_node t; t = *parent; *parent = *child; *child = t; parent->parent = child; if (child->left == child) { child->left = parent; sibling = child->right; } else { child->right = parent; sibling = child->left; } if (sibling != NULL) sibling->parent = child; if (parent->left != NULL) parent->left->parent = parent; if (parent->right != NULL) parent->right->parent = parent; if (child->parent == NULL) heap->min = child; else if (child->parent->left == parent) child->parent->left = child; else child->parent->right = child; } HEAP_EXPORT(void heap_insert(struct heap* heap, struct heap_node* newnode, heap_compare_fn less_than)) { struct heap_node** parent; struct heap_node** child; unsigned int path; unsigned int n; unsigned int k; newnode->left = NULL; newnode->right = NULL; newnode->parent = NULL; /* Calculate the path from the root to the insertion point. This is a min * heap so we always insert at the left-most free node of the bottom row. */ path = 0; for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2) path = (path << 1) | (n & 1); /* Now traverse the heap using the path we calculated in the previous step. */ parent = child = &heap->min; while (k > 0) { parent = child; if (path & 1) child = &(*child)->right; else child = &(*child)->left; path >>= 1; k -= 1; } /* Insert the new node. */ newnode->parent = *parent; *child = newnode; heap->nelts += 1; /* Walk up the tree and check at each node if the heap property holds. * It's a min heap so parent < child must be true. */ while (newnode->parent != NULL && less_than(newnode, newnode->parent)) heap_node_swap(heap, newnode->parent, newnode); } HEAP_EXPORT(void heap_remove(struct heap* heap, struct heap_node* node, heap_compare_fn less_than)) { struct heap_node* smallest; struct heap_node** max; struct heap_node* child; unsigned int path; unsigned int k; unsigned int n; if (heap->nelts == 0) return; /* Calculate the path from the min (the root) to the max, the left-most node * of the bottom row. */ path = 0; for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2) path = (path << 1) | (n & 1); /* Now traverse the heap using the path we calculated in the previous step. */ max = &heap->min; while (k > 0) { if (path & 1) max = &(*max)->right; else max = &(*max)->left; path >>= 1; k -= 1; } heap->nelts -= 1; /* Unlink the max node. */ child = *max; *max = NULL; if (child == node) { /* We're removing either the max or the last node in the tree. */ if (child == heap->min) { heap->min = NULL; } return; } /* Replace the to be deleted node with the max node. */ child->left = node->left; child->right = node->right; child->parent = node->parent; if (child->left != NULL) { child->left->parent = child; } if (child->right != NULL) { child->right->parent = child; } if (node->parent == NULL) { heap->min = child; } else if (node->parent->left == node) { node->parent->left = child; } else { node->parent->right = child; } /* Walk down the subtree and check at each node if the heap property holds. * It's a min heap so parent < child must be true. If the parent is bigger, * swap it with the smallest child. */ for (;;) { smallest = child; if (child->left != NULL && less_than(child->left, smallest)) smallest = child->left; if (child->right != NULL && less_than(child->right, smallest)) smallest = child->right; if (smallest == child) break; heap_node_swap(heap, child, smallest); } /* Walk up the subtree and check that each parent is less than the node * this is required, because `max` node is not guaranteed to be the * actual maximum in tree */ while (child->parent != NULL && less_than(child, child->parent)) heap_node_swap(heap, child->parent, child); } HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) { heap_remove(heap, heap->min, less_than); } #undef HEAP_EXPORT #endif /* UV_SRC_HEAP_H_ */ libuv-v1.8.0/src/inet.c000066400000000000000000000200111263361716600147260ustar00rootroot00000000000000/* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "stdint-msvc2008.h" #else # include #endif #include "uv.h" #include "uv-common.h" #define UV__INET_ADDRSTRLEN 16 #define UV__INET6_ADDRSTRLEN 46 static int inet_ntop4(const unsigned char *src, char *dst, size_t size); static int inet_ntop6(const unsigned char *src, char *dst, size_t size); static int inet_pton4(const char *src, unsigned char *dst); static int inet_pton6(const char *src, unsigned char *dst); int uv_inet_ntop(int af, const void* src, char* dst, size_t size) { switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: return UV_EAFNOSUPPORT; } /* NOTREACHED */ } static int inet_ntop4(const unsigned char *src, char *dst, size_t size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[UV__INET_ADDRSTRLEN]; int l; l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || (size_t) l >= size) { return UV_ENOSPC; } strncpy(dst, tmp, size); dst[size - 1] = '\0'; return 0; } static int inet_ntop6(const unsigned char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[UV__INET6_ADDRSTRLEN], *tp; struct { int base, len; } best, cur; unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)]; int i; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < (int) sizeof(struct in6_addr); i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; best.len = 0; cur.base = -1; cur.len = 0; for (i = 0; i < (int) ARRAY_SIZE(words); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; for (i = 0; i < (int) ARRAY_SIZE(words); i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) *tp++ = ':'; continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) *tp++ = ':'; /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 7 && words[7] != 0x0001) || (best.len == 5 && words[5] == 0xffff))) { int err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)); if (err) return err; tp += strlen(tp); break; } tp += sprintf(tp, "%x", words[i]); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) *tp++ = ':'; *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((size_t)(tp - tmp) > size) { return UV_ENOSPC; } strcpy(dst, tmp); return 0; } int uv_inet_pton(int af, const char* src, void* dst) { if (src == NULL || dst == NULL) return UV_EINVAL; switch (af) { case AF_INET: return (inet_pton4(src, dst)); case AF_INET6: { int len; char tmp[UV__INET6_ADDRSTRLEN], *s, *p; s = (char*) src; p = strchr(src, '%'); if (p != NULL) { s = tmp; len = p - src; if (len > UV__INET6_ADDRSTRLEN-1) return UV_EINVAL; memcpy(s, src, len); s[len] = '\0'; } return inet_pton6(s, dst); } default: return UV_EAFNOSUPPORT; } /* NOTREACHED */ } static int inet_pton4(const char *src, unsigned char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; unsigned char tmp[sizeof(struct in_addr)], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { unsigned int nw = *tp * 10 + (pch - digits); if (saw_digit && *tp == 0) return UV_EINVAL; if (nw > 255) return UV_EINVAL; *tp = nw; if (!saw_digit) { if (++octets > 4) return UV_EINVAL; saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return UV_EINVAL; *++tp = 0; saw_digit = 0; } else return UV_EINVAL; } if (octets < 4) return UV_EINVAL; memcpy(dst, tmp, sizeof(struct in_addr)); return 0; } static int inet_pton6(const char *src, unsigned char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, seen_xdigits; unsigned int val; memset((tp = tmp), '\0', sizeof tmp); endp = tp + sizeof tmp; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return UV_EINVAL; curtok = src; seen_xdigits = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (++seen_xdigits > 4) return UV_EINVAL; continue; } if (ch == ':') { curtok = src; if (!seen_xdigits) { if (colonp) return UV_EINVAL; colonp = tp; continue; } else if (*src == '\0') { return UV_EINVAL; } if (tp + sizeof(uint16_t) > endp) return UV_EINVAL; *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; seen_xdigits = 0; val = 0; continue; } if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) { int err = inet_pton4(curtok, tp); if (err == 0) { tp += sizeof(struct in_addr); seen_xdigits = 0; break; /*%< '\\0' was seen by inet_pton4(). */ } } return UV_EINVAL; } if (seen_xdigits) { if (tp + sizeof(uint16_t) > endp) return UV_EINVAL; *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return UV_EINVAL; for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return UV_EINVAL; memcpy(dst, tmp, sizeof tmp); return 0; } libuv-v1.8.0/src/queue.h000066400000000000000000000132671263361716600151370ustar00rootroot00000000000000/* Copyright (c) 2013, Ben Noordhuis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef QUEUE_H_ #define QUEUE_H_ #include typedef void *QUEUE[2]; /* Private macros. */ #define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) #define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) #define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) #define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) /* Public macros. */ #define QUEUE_DATA(ptr, type, field) \ ((type *) ((char *) (ptr) - offsetof(type, field))) /* Important note: mutating the list while QUEUE_FOREACH is * iterating over its elements results in undefined behavior. */ #define QUEUE_FOREACH(q, h) \ for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) #define QUEUE_EMPTY(q) \ ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) #define QUEUE_HEAD(q) \ (QUEUE_NEXT(q)) #define QUEUE_INIT(q) \ do { \ QUEUE_NEXT(q) = (q); \ QUEUE_PREV(q) = (q); \ } \ while (0) #define QUEUE_ADD(h, n) \ do { \ QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ QUEUE_PREV(h) = QUEUE_PREV(n); \ QUEUE_PREV_NEXT(h) = (h); \ } \ while (0) #define QUEUE_SPLIT(h, q, n) \ do { \ QUEUE_PREV(n) = QUEUE_PREV(h); \ QUEUE_PREV_NEXT(n) = (n); \ QUEUE_NEXT(n) = (q); \ QUEUE_PREV(h) = QUEUE_PREV(q); \ QUEUE_PREV_NEXT(h) = (h); \ QUEUE_PREV(q) = (n); \ } \ while (0) #define QUEUE_MOVE(h, n) \ do { \ if (QUEUE_EMPTY(h)) \ QUEUE_INIT(n); \ else { \ QUEUE* q = QUEUE_HEAD(h); \ QUEUE_SPLIT(h, q, n); \ } \ } \ while (0) #define QUEUE_INSERT_HEAD(h, q) \ do { \ QUEUE_NEXT(q) = QUEUE_NEXT(h); \ QUEUE_PREV(q) = (h); \ QUEUE_NEXT_PREV(q) = (q); \ QUEUE_NEXT(h) = (q); \ } \ while (0) #define QUEUE_INSERT_TAIL(h, q) \ do { \ QUEUE_NEXT(q) = (h); \ QUEUE_PREV(q) = QUEUE_PREV(h); \ QUEUE_PREV_NEXT(q) = (q); \ QUEUE_PREV(h) = (q); \ } \ while (0) #define QUEUE_REMOVE(q) \ do { \ QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ } \ while (0) #endif /* QUEUE_H_ */ libuv-v1.8.0/src/threadpool.c000066400000000000000000000160351263361716600161430ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv-common.h" #if !defined(_WIN32) # include "unix/internal.h" #else # include "win/req-inl.h" /* TODO(saghul): unify internal req functions */ static void uv__req_init(uv_loop_t* loop, uv_req_t* req, uv_req_type type) { uv_req_init(loop, req); req->type = type; uv__req_register(loop, req); } # define uv__req_init(loop, req, type) \ uv__req_init((loop), (uv_req_t*)(req), (type)) #endif #include #define MAX_THREADPOOL_SIZE 128 static uv_once_t once = UV_ONCE_INIT; static uv_cond_t cond; static uv_mutex_t mutex; static unsigned int idle_threads; static unsigned int nthreads; static uv_thread_t* threads; static uv_thread_t default_threads[4]; static QUEUE exit_message; static QUEUE wq; static volatile int initialized; static void uv__cancelled(struct uv__work* w) { abort(); } /* To avoid deadlock with uv_cancel() it's crucial that the worker * never holds the global mutex and the loop-local mutex at the same time. */ static void worker(void* arg) { struct uv__work* w; QUEUE* q; (void) arg; for (;;) { uv_mutex_lock(&mutex); while (QUEUE_EMPTY(&wq)) { idle_threads += 1; uv_cond_wait(&cond, &mutex); idle_threads -= 1; } q = QUEUE_HEAD(&wq); if (q == &exit_message) uv_cond_signal(&cond); else { QUEUE_REMOVE(q); QUEUE_INIT(q); /* Signal uv_cancel() that the work req is executing. */ } uv_mutex_unlock(&mutex); if (q == &exit_message) break; w = QUEUE_DATA(q, struct uv__work, wq); w->work(w); uv_mutex_lock(&w->loop->wq_mutex); w->work = NULL; /* Signal uv_cancel() that the work req is done executing. */ QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq); uv_async_send(&w->loop->wq_async); uv_mutex_unlock(&w->loop->wq_mutex); } } static void post(QUEUE* q) { uv_mutex_lock(&mutex); QUEUE_INSERT_TAIL(&wq, q); if (idle_threads > 0) uv_cond_signal(&cond); uv_mutex_unlock(&mutex); } #ifndef _WIN32 UV_DESTRUCTOR(static void cleanup(void)) { unsigned int i; if (initialized == 0) return; post(&exit_message); for (i = 0; i < nthreads; i++) if (uv_thread_join(threads + i)) abort(); if (threads != default_threads) uv__free(threads); uv_mutex_destroy(&mutex); uv_cond_destroy(&cond); threads = NULL; nthreads = 0; initialized = 0; } #endif static void init_once(void) { unsigned int i; const char* val; nthreads = ARRAY_SIZE(default_threads); val = getenv("UV_THREADPOOL_SIZE"); if (val != NULL) nthreads = atoi(val); if (nthreads == 0) nthreads = 1; if (nthreads > MAX_THREADPOOL_SIZE) nthreads = MAX_THREADPOOL_SIZE; threads = default_threads; if (nthreads > ARRAY_SIZE(default_threads)) { threads = uv__malloc(nthreads * sizeof(threads[0])); if (threads == NULL) { nthreads = ARRAY_SIZE(default_threads); threads = default_threads; } } if (uv_cond_init(&cond)) abort(); if (uv_mutex_init(&mutex)) abort(); QUEUE_INIT(&wq); for (i = 0; i < nthreads; i++) if (uv_thread_create(threads + i, worker, NULL)) abort(); initialized = 1; } void uv__work_submit(uv_loop_t* loop, struct uv__work* w, void (*work)(struct uv__work* w), void (*done)(struct uv__work* w, int status)) { uv_once(&once, init_once); w->loop = loop; w->work = work; w->done = done; post(&w->wq); } static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { int cancelled; uv_mutex_lock(&mutex); uv_mutex_lock(&w->loop->wq_mutex); cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL; if (cancelled) QUEUE_REMOVE(&w->wq); uv_mutex_unlock(&w->loop->wq_mutex); uv_mutex_unlock(&mutex); if (!cancelled) return UV_EBUSY; w->work = uv__cancelled; uv_mutex_lock(&loop->wq_mutex); QUEUE_INSERT_TAIL(&loop->wq, &w->wq); uv_async_send(&loop->wq_async); uv_mutex_unlock(&loop->wq_mutex); return 0; } void uv__work_done(uv_async_t* handle) { struct uv__work* w; uv_loop_t* loop; QUEUE* q; QUEUE wq; int err; loop = container_of(handle, uv_loop_t, wq_async); uv_mutex_lock(&loop->wq_mutex); QUEUE_MOVE(&loop->wq, &wq); uv_mutex_unlock(&loop->wq_mutex); while (!QUEUE_EMPTY(&wq)) { q = QUEUE_HEAD(&wq); QUEUE_REMOVE(q); w = container_of(q, struct uv__work, wq); err = (w->work == uv__cancelled) ? UV_ECANCELED : 0; w->done(w, err); } } static void uv__queue_work(struct uv__work* w) { uv_work_t* req = container_of(w, uv_work_t, work_req); req->work_cb(req); } static void uv__queue_done(struct uv__work* w, int err) { uv_work_t* req; req = container_of(w, uv_work_t, work_req); uv__req_unregister(req->loop, req); if (req->after_work_cb == NULL) return; req->after_work_cb(req, err); } int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb) { if (work_cb == NULL) return UV_EINVAL; uv__req_init(loop, req, UV_WORK); req->loop = loop; req->work_cb = work_cb; req->after_work_cb = after_work_cb; uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done); return 0; } int uv_cancel(uv_req_t* req) { struct uv__work* wreq; uv_loop_t* loop; switch (req->type) { case UV_FS: loop = ((uv_fs_t*) req)->loop; wreq = &((uv_fs_t*) req)->work_req; break; case UV_GETADDRINFO: loop = ((uv_getaddrinfo_t*) req)->loop; wreq = &((uv_getaddrinfo_t*) req)->work_req; break; case UV_GETNAMEINFO: loop = ((uv_getnameinfo_t*) req)->loop; wreq = &((uv_getnameinfo_t*) req)->work_req; break; case UV_WORK: loop = ((uv_work_t*) req)->loop; wreq = &((uv_work_t*) req)->work_req; break; default: return UV_EINVAL; } return uv__work_cancel(loop, req, wreq); } libuv-v1.8.0/src/unix/000077500000000000000000000000001263361716600146145ustar00rootroot00000000000000libuv-v1.8.0/src/unix/aix.c000066400000000000000000000704771263361716600155600ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_AHAFS_EVPRODS_H #include #endif #include #include #include #include #include #define RDWR_BUF_SIZE 4096 #define EQ(a,b) (strcmp(a,b) == 0) int uv__platform_loop_init(uv_loop_t* loop) { loop->fs_fd = -1; /* Passing maxfd of -1 should mean the limit is determined * by the user's ulimit or the global limit as per the doc */ loop->backend_fd = pollset_create(-1); if (loop->backend_fd == -1) return -1; return 0; } void uv__platform_loop_delete(uv_loop_t* loop) { if (loop->fs_fd != -1) { uv__close(loop->fs_fd); loop->fs_fd = -1; } if (loop->backend_fd != -1) { pollset_destroy(loop->backend_fd); loop->backend_fd = -1; } } void uv__io_poll(uv_loop_t* loop, int timeout) { struct pollfd events[1024]; struct pollfd pqry; struct pollfd* pe; struct poll_ctl pc; QUEUE* q; uv__io_t* w; uint64_t base; uint64_t diff; int nevents; int count; int nfds; int i; int rc; int add_failed; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); return; } while (!QUEUE_EMPTY(&loop->watcher_queue)) { q = QUEUE_HEAD(&loop->watcher_queue); QUEUE_REMOVE(q); QUEUE_INIT(q); w = QUEUE_DATA(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); pc.events = w->pevents; pc.fd = w->fd; add_failed = 0; if (w->events == 0) { pc.cmd = PS_ADD; if (pollset_ctl(loop->backend_fd, &pc, 1)) { if (errno != EINVAL) { assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); abort(); } /* Check if the fd is already in the pollset */ pqry.fd = pc.fd; rc = pollset_query(loop->backend_fd, &pqry); switch (rc) { case -1: assert(0 && "Failed to query pollset for file descriptor"); abort(); case 0: assert(0 && "Pollset does not contain file descriptor"); abort(); } /* If we got here then the pollset already contained the file descriptor even though * we didn't think it should. This probably shouldn't happen, but we can continue. */ add_failed = 1; } } if (w->events != 0 || add_failed) { /* Modify, potentially removing events -- need to delete then add. * Could maybe mod if we knew for sure no events are removed, but * content of w->events is handled above as not reliable (falls back) * so may require a pollset_query() which would have to be pretty cheap * compared to a PS_DELETE to be worth optimizing. Alternatively, could * lazily remove events, squelching them in the mean time. */ pc.cmd = PS_DELETE; if (pollset_ctl(loop->backend_fd, &pc, 1)) { assert(0 && "Failed to delete file descriptor (pc.fd) from pollset"); abort(); } pc.cmd = PS_ADD; if (pollset_ctl(loop->backend_fd, &pc, 1)) { assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); abort(); } } w->events = w->pevents; } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), timeout); /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { assert(timeout != -1); return; } if (nfds == -1) { if (errno != EINTR) { abort(); } if (timeout == -1) continue; if (timeout == 0) return; /* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; } nevents = 0; assert(loop->watchers != NULL); loop->watchers[loop->nwatchers] = (void*) events; loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; pc.cmd = PS_DELETE; pc.fd = pe->fd; /* Skip invalidated events, see uv__platform_invalidate_fd */ if (pc.fd == -1) continue; assert(pc.fd >= 0); assert((unsigned) pc.fd < loop->nwatchers); w = loop->watchers[pc.fd]; if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. * * Ignore all errors because we may be racing with another thread * when the file descriptor is closed. */ pollset_ctl(loop->backend_fd, &pc, 1); continue; } w->cb(loop, w, pe->revents); nevents++; } loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); diff = loop->time - base; if (diff >= (uint64_t) timeout) return; timeout -= diff; } } uint64_t uv__hrtime(uv_clocktype_t type) { uint64_t G = 1000000000; timebasestruct_t t; read_wall_time(&t, TIMEBASE_SZ); time_base_to_time(&t, TIMEBASE_SZ); return (uint64_t) t.tb_high * G + t.tb_low; } /* * We could use a static buffer for the path manipulations that we need outside * of the function, but this function could be called by multiple consumers and * we don't want to potentially create a race condition in the use of snprintf. * There is no direct way of getting the exe path in AIX - either through /procfs * or through some libc APIs. The below approach is to parse the argv[0]'s pattern * and use it in conjunction with PATH environment variable to craft one. */ int uv_exepath(char* buffer, size_t* size) { int res; char args[PATH_MAX]; char abspath[PATH_MAX]; size_t abspath_size; struct procsinfo pi; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; pi.pi_pid = getpid(); res = getargs(&pi, sizeof(pi), args, sizeof(args)); if (res < 0) return -EINVAL; /* * Possibilities for args: * i) an absolute path such as: /home/user/myprojects/nodejs/node * ii) a relative path such as: ./node or ../myprojects/nodejs/node * iii) a bare filename such as "node", after exporting PATH variable * to its location. */ /* Case i) and ii) absolute or relative paths */ if (strchr(args, '/') != NULL) { if (realpath(args, abspath) != abspath) return -errno; abspath_size = strlen(abspath); *size -= 1; if (*size > abspath_size) *size = abspath_size; memcpy(buffer, abspath, *size); buffer[*size] = '\0'; return 0; } else { /* Case iii). Search PATH environment variable */ char trypath[PATH_MAX]; char *clonedpath = NULL; char *token = NULL; char *path = getenv("PATH"); if (path == NULL) return -EINVAL; clonedpath = uv__strdup(path); if (clonedpath == NULL) return -ENOMEM; token = strtok(clonedpath, ":"); while (token != NULL) { snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); if (realpath(trypath, abspath) == abspath) { /* Check the match is executable */ if (access(abspath, X_OK) == 0) { abspath_size = strlen(abspath); *size -= 1; if (*size > abspath_size) *size = abspath_size; memcpy(buffer, abspath, *size); buffer[*size] = '\0'; uv__free(clonedpath); return 0; } } token = strtok(NULL, ":"); } uv__free(clonedpath); /* Out of tokens (path entries), and no match found */ return -EINVAL; } } uint64_t uv_get_free_memory(void) { perfstat_memory_total_t mem_total; int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1); if (result == -1) { return 0; } return mem_total.real_free * 4096; } uint64_t uv_get_total_memory(void) { perfstat_memory_total_t mem_total; int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1); if (result == -1) { return 0; } return mem_total.real_total * 4096; } void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); if (result == -1) { avg[0] = 0.; avg[1] = 0.; avg[2] = 0.; return; } avg[0] = ps_total.loadavg[0] / (double)(1 << SBITS); avg[1] = ps_total.loadavg[1] / (double)(1 << SBITS); avg[2] = ps_total.loadavg[2] / (double)(1 << SBITS); } #ifdef HAVE_SYS_AHAFS_EVPRODS_H static char *uv__rawname(char *cp) { static char rawbuf[FILENAME_MAX+1]; char *dp = rindex(cp, '/'); if (dp == 0) return 0; *dp = 0; strcpy(rawbuf, cp); *dp = '/'; strcat(rawbuf, "/r"); strcat(rawbuf, dp+1); return rawbuf; } /* * Determine whether given pathname is a directory * Returns 0 if the path is a directory, -1 if not * * Note: Opportunity here for more detailed error information but * that requires changing callers of this function as well */ static int uv__path_is_a_directory(char* filename) { struct stat statbuf; if (stat(filename, &statbuf) < 0) return -1; /* failed: not a directory, assume it is a file */ if (statbuf.st_type == VDIR) return 0; return -1; } /* * Check whether AHAFS is mounted. * Returns 0 if AHAFS is mounted, or an error code < 0 on failure */ static int uv__is_ahafs_mounted(void){ int rv, i = 2; struct vmount *p; int size_multiplier = 10; size_t siz = sizeof(struct vmount)*size_multiplier; struct vmount *vmt; const char *dev = "/aha"; char *obj, *stub; p = uv__malloc(siz); if (p == NULL) return -errno; /* Retrieve all mounted filesystems */ rv = mntctl(MCTL_QUERY, siz, (char*)p); if (rv < 0) return -errno; if (rv == 0) { /* buffer was not large enough, reallocate to correct size */ siz = *(int*)p; uv__free(p); p = uv__malloc(siz); if (p == NULL) return -errno; rv = mntctl(MCTL_QUERY, siz, (char*)p); if (rv < 0) return -errno; } /* Look for dev in filesystems mount info */ for(vmt = p, i = 0; i < rv; i++) { obj = vmt2dataptr(vmt, VMT_OBJECT); /* device */ stub = vmt2dataptr(vmt, VMT_STUB); /* mount point */ if (EQ(obj, dev) || EQ(uv__rawname(obj), dev) || EQ(stub, dev)) { uv__free(p); /* Found a match */ return 0; } vmt = (struct vmount *) ((char *) vmt + vmt->vmt_length); } /* /aha is required for monitoring filesystem changes */ return -1; } /* * Recursive call to mkdir() to create intermediate folders, if any * Returns code from mkdir call */ static int uv__makedir_p(const char *dir) { char tmp[256]; char *p = NULL; size_t len; int err; snprintf(tmp, sizeof(tmp),"%s",dir); len = strlen(tmp); if (tmp[len - 1] == '/') tmp[len - 1] = 0; for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = 0; err = mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if(err != 0) return err; *p = '/'; } } return mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); } /* * Creates necessary subdirectories in the AIX Event Infrastructure * file system for monitoring the object specified. * Returns code from mkdir call */ static int uv__make_subdirs_p(const char *filename) { char cmd[2048]; char *p; int rc = 0; /* Strip off the monitor file name */ p = strrchr(filename, '/'); if (p == NULL) return 0; if (uv__path_is_a_directory((char*)filename) == 0) { sprintf(cmd, "/aha/fs/modDir.monFactory"); } else { sprintf(cmd, "/aha/fs/modFile.monFactory"); } strncat(cmd, filename, (p - filename)); rc = uv__makedir_p(cmd); if (rc == -1 && errno != EEXIST){ return -errno; } return rc; } /* * Checks if /aha is mounted, then proceeds to set up the monitoring * objects for the specified file. * Returns 0 on success, or an error code < 0 on failure */ static int uv__setup_ahafs(const char* filename, int *fd) { int rc = 0; char mon_file_write_string[RDWR_BUF_SIZE]; char mon_file[PATH_MAX]; int file_is_directory = 0; /* -1 == NO, 0 == YES */ /* Create monitor file name for object */ file_is_directory = uv__path_is_a_directory((char*)filename); if (file_is_directory == 0) sprintf(mon_file, "/aha/fs/modDir.monFactory"); else sprintf(mon_file, "/aha/fs/modFile.monFactory"); if ((strlen(mon_file) + strlen(filename) + 5) > PATH_MAX) return -ENAMETOOLONG; /* Make the necessary subdirectories for the monitor file */ rc = uv__make_subdirs_p(filename); if (rc == -1 && errno != EEXIST) return rc; strcat(mon_file, filename); strcat(mon_file, ".mon"); *fd = 0; errno = 0; /* Open the monitor file, creating it if necessary */ *fd = open(mon_file, O_CREAT|O_RDWR); if (*fd < 0) return -errno; /* Write out the monitoring specifications. * In this case, we are monitoring for a state change event type * CHANGED=YES * We will be waiting in select call, rather than a read: * WAIT_TYPE=WAIT_IN_SELECT * We only want minimal information for files: * INFO_LVL=1 * For directories, we want more information to track what file * caused the change * INFO_LVL=2 */ if (file_is_directory == 0) sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=2"); else sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=1"); rc = write(*fd, mon_file_write_string, strlen(mon_file_write_string)+1); if (rc < 0) return -errno; return 0; } /* * Skips a specified number of lines in the buffer passed in. * Walks the buffer pointed to by p and attempts to skip n lines. * Returns the total number of lines skipped */ static int uv__skip_lines(char **p, int n) { int lines = 0; while(n > 0) { *p = strchr(*p, '\n'); if (!p) return lines; (*p)++; n--; lines++; } return lines; } /* * Parse the event occurrence data to figure out what event just occurred * and take proper action. * * The buf is a pointer to the buffer containing the event occurrence data * Returns 0 on success, -1 if unrecoverable error in parsing * */ static int uv__parse_data(char *buf, int *events, uv_fs_event_t* handle) { int evp_rc, i; char *p; char filename[PATH_MAX]; /* To be used when handling directories */ p = buf; *events = 0; /* Clean the filename buffer*/ for(i = 0; i < PATH_MAX; i++) { filename[i] = 0; } i = 0; /* Check for BUF_WRAP */ if (strncmp(buf, "BUF_WRAP", strlen("BUF_WRAP")) == 0) { assert(0 && "Buffer wrap detected, Some event occurrences lost!"); return 0; } /* Since we are using the default buffer size (4K), and have specified * INFO_LVL=1, we won't see any EVENT_OVERFLOW conditions. Applications * should check for this keyword if they are using an INFO_LVL of 2 or * higher, and have a buffer size of <= 4K */ /* Skip to RC_FROM_EVPROD */ if (uv__skip_lines(&p, 9) != 9) return -1; if (sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1) { if (uv__path_is_a_directory(handle->path) == 0) { /* Directory */ if (evp_rc == AHAFS_MODDIR_UNMOUNT || evp_rc == AHAFS_MODDIR_REMOVE_SELF) { /* The directory is no longer available for monitoring */ *events = UV_RENAME; handle->dir_filename = NULL; } else { /* A file was added/removed inside the directory */ *events = UV_CHANGE; /* Get the EVPROD_INFO */ if (uv__skip_lines(&p, 1) != 1) return -1; /* Scan out the name of the file that triggered the event*/ if (sscanf(p, "BEGIN_EVPROD_INFO\n%sEND_EVPROD_INFO", filename) == 1) { handle->dir_filename = uv__strdup((const char*)&filename); } else return -1; } } else { /* Regular File */ if (evp_rc == AHAFS_MODFILE_RENAME) *events = UV_RENAME; else *events = UV_CHANGE; } } else return -1; return 0; } /* This is the internal callback */ static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) { char result_data[RDWR_BUF_SIZE]; int bytes, rc = 0; uv_fs_event_t* handle; int events = 0; int i = 0; char fname[PATH_MAX]; char *p; handle = container_of(event_watch, uv_fs_event_t, event_watcher); /* Clean all the buffers*/ for(i = 0; i < PATH_MAX; i++) { fname[i] = 0; } i = 0; /* At this point, we assume that polling has been done on the * file descriptor, so we can just read the AHAFS event occurrence * data and parse its results without having to block anything */ bytes = pread(event_watch->fd, result_data, RDWR_BUF_SIZE, 0); assert((bytes <= 0) && "uv__ahafs_event - Error reading monitor file"); /* Parse the data */ if(bytes > 0) rc = uv__parse_data(result_data, &events, handle); /* For directory changes, the name of the files that triggered the change * are never absolute pathnames */ if (uv__path_is_a_directory(handle->path) == 0) { p = handle->dir_filename; while(*p != NULL){ fname[i]= *p; i++; p++; } } else { /* For file changes, figure out whether filename is absolute or not */ if (handle->path[0] == '/') { p = strrchr(handle->path, '/'); p++; while(*p != NULL) { fname[i]= *p; i++; p++; } } } /* Unrecoverable error */ if (rc == -1) return; else /* Call the actual JavaScript callback function */ handle->cb(handle, (const char*)&fname, events, 0); } #endif int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { #ifdef HAVE_SYS_AHAFS_EVPRODS_H uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); return 0; #else return -ENOSYS; #endif } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* filename, unsigned int flags) { #ifdef HAVE_SYS_AHAFS_EVPRODS_H int fd, rc, i = 0, res = 0; char cwd[PATH_MAX]; char absolute_path[PATH_MAX]; char fname[PATH_MAX]; char *p; /* Clean all the buffers*/ for(i = 0; i < PATH_MAX; i++) { cwd[i] = 0; absolute_path[i] = 0; fname[i] = 0; } i = 0; /* Figure out whether filename is absolute or not */ if (filename[0] == '/') { /* We have absolute pathname, create the relative pathname*/ sprintf(absolute_path, filename); p = strrchr(filename, '/'); p++; } else { if (filename[0] == '.' && filename[1] == '/') { /* We have a relative pathname, compose the absolute pathname */ sprintf(fname, filename); snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); res = readlink(cwd, absolute_path, sizeof(absolute_path) - 1); if (res < 0) return res; p = strrchr(absolute_path, '/'); p++; p++; } else { /* We have a relative pathname, compose the absolute pathname */ sprintf(fname, filename); snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); res = readlink(cwd, absolute_path, sizeof(absolute_path) - 1); if (res < 0) return res; p = strrchr(absolute_path, '/'); p++; } /* Copy to filename buffer */ while(filename[i] != NULL) { *p = filename[i]; i++; p++; } } if (uv__is_ahafs_mounted() < 0) /* /aha checks failed */ return UV_ENOSYS; /* Setup ahafs */ rc = uv__setup_ahafs((const char *)absolute_path, &fd); if (rc != 0) return rc; /* Setup/Initialize all the libuv routines */ uv__handle_start(handle); uv__io_init(&handle->event_watcher, uv__ahafs_event, fd); handle->path = uv__strdup((const char*)&absolute_path); handle->cb = cb; uv__io_start(handle->loop, &handle->event_watcher, UV__POLLIN); return 0; #else return -ENOSYS; #endif } int uv_fs_event_stop(uv_fs_event_t* handle) { #ifdef HAVE_SYS_AHAFS_EVPRODS_H if (!uv__is_active(handle)) return 0; uv__io_close(handle->loop, &handle->event_watcher); uv__handle_stop(handle); if (uv__path_is_a_directory(handle->path) == 0) { uv__free(handle->dir_filename); handle->dir_filename = NULL; } uv__free(handle->path); handle->path = NULL; uv__close(handle->event_watcher.fd); handle->event_watcher.fd = -1; return 0; #else return -ENOSYS; #endif } void uv__fs_event_close(uv_fs_event_t* handle) { #ifdef HAVE_SYS_AHAFS_EVPRODS_H uv_fs_event_stop(handle); #else UNREACHABLE(); #endif } char** uv_setup_args(int argc, char** argv) { return argv; } int uv_set_process_title(const char* title) { return 0; } int uv_get_process_title(char* buffer, size_t size) { if (size > 0) { buffer[0] = '\0'; } return 0; } int uv_resident_set_memory(size_t* rss) { char pp[64]; psinfo_t psinfo; int err; int fd; snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid()); fd = open(pp, O_RDONLY); if (fd == -1) return -errno; /* FIXME(bnoordhuis) Handle EINTR. */ err = -EINVAL; if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { *rss = (size_t)psinfo.pr_rssize * 1024; err = 0; } uv__close(fd); return err; } int uv_uptime(double* uptime) { struct utmp *utmp_buf; size_t entries = 0; time_t boot_time; utmpname(UTMP_FILE); setutent(); while ((utmp_buf = getutent()) != NULL) { if (utmp_buf->ut_user[0] && utmp_buf->ut_type == USER_PROCESS) ++entries; if (utmp_buf->ut_type == BOOT_TIME) boot_time = utmp_buf->ut_time; } endutent(); if (boot_time == 0) return -ENOSYS; *uptime = time(NULL) - boot_time; return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { uv_cpu_info_t* cpu_info; perfstat_cpu_total_t ps_total; perfstat_cpu_t* ps_cpus; perfstat_id_t cpu_id; int result, ncpus, idx = 0; result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); if (result == -1) { return -ENOSYS; } ncpus = result = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0); if (result == -1) { return -ENOSYS; } ps_cpus = (perfstat_cpu_t*) uv__malloc(ncpus * sizeof(perfstat_cpu_t)); if (!ps_cpus) { return -ENOMEM; } strcpy(cpu_id.name, FIRST_CPU); result = perfstat_cpu(&cpu_id, ps_cpus, sizeof(perfstat_cpu_t), ncpus); if (result == -1) { uv__free(ps_cpus); return -ENOSYS; } *cpu_infos = (uv_cpu_info_t*) uv__malloc(ncpus * sizeof(uv_cpu_info_t)); if (!*cpu_infos) { uv__free(ps_cpus); return -ENOMEM; } *count = ncpus; cpu_info = *cpu_infos; while (idx < ncpus) { cpu_info->speed = (int)(ps_total.processorHZ / 1000000); cpu_info->model = uv__strdup(ps_total.description); cpu_info->cpu_times.user = ps_cpus[idx].user; cpu_info->cpu_times.sys = ps_cpus[idx].sys; cpu_info->cpu_times.idle = ps_cpus[idx].idle; cpu_info->cpu_times.irq = ps_cpus[idx].wait; cpu_info->cpu_times.nice = 0; cpu_info++; idx++; } uv__free(ps_cpus); return 0; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; ++i) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { uv_interface_address_t* address; int sockfd, size = 1; struct ifconf ifc; struct ifreq *ifr, *p, flg; *count = 0; if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { return -errno; } if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { SAVE_ERRNO(uv__close(sockfd)); return -errno; } ifc.ifc_req = (struct ifreq*)uv__malloc(size); ifc.ifc_len = size; if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { SAVE_ERRNO(uv__close(sockfd)); return -errno; } #define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) /* Count all up and running ipv4/ipv6 addresses */ ifr = ifc.ifc_req; while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { p = ifr; ifr = (struct ifreq*) ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); if (!(p->ifr_addr.sa_family == AF_INET6 || p->ifr_addr.sa_family == AF_INET)) continue; memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { SAVE_ERRNO(uv__close(sockfd)); return -errno; } if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) continue; (*count)++; } /* Alloc the return interface structs */ *addresses = (uv_interface_address_t*) uv__malloc(*count * sizeof(uv_interface_address_t)); if (!(*addresses)) { uv__close(sockfd); return -ENOMEM; } address = *addresses; ifr = ifc.ifc_req; while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { p = ifr; ifr = (struct ifreq*) ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); if (!(p->ifr_addr.sa_family == AF_INET6 || p->ifr_addr.sa_family == AF_INET)) continue; memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { uv__close(sockfd); return -ENOSYS; } if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) continue; /* All conditions above must match count loop */ address->name = uv__strdup(p->ifr_name); if (p->ifr_addr.sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); } else { address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); } /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; address++; } #undef ADDR_SIZE uv__close(sockfd); return 0; } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; ++i) { uv__free(addresses[i].name); } uv__free(addresses); } void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct pollfd* events; uintptr_t i; uintptr_t nfds; struct poll_ctl pc; assert(loop->watchers != NULL); events = (struct pollfd*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; if (events != NULL) /* Invalidate events with same file descriptor */ for (i = 0; i < nfds; i++) if ((int) events[i].fd == fd) events[i].fd = -1; /* Remove the file descriptor from the poll set */ pc.events = 0; pc.cmd = PS_DELETE; pc.fd = fd; if(loop->backend_fd >= 0) pollset_ctl(loop->backend_fd, &pc, 1); } libuv-v1.8.0/src/unix/android-ifaddrs.c000066400000000000000000000466741263361716600200330ustar00rootroot00000000000000/* Copyright (c) 2013, Kenneth MacKay Copyright (c) 2014, Emergya (Cloud4all, FP7/2007-2013 grant agreement #289016) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "android-ifaddrs.h" #include "uv-common.h" #include #include #include #include #include #include #include #include #include typedef struct NetlinkList { struct NetlinkList *m_next; struct nlmsghdr *m_data; unsigned int m_size; } NetlinkList; static int netlink_socket(void) { struct sockaddr_nl l_addr; int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if(l_socket < 0) { return -1; } memset(&l_addr, 0, sizeof(l_addr)); l_addr.nl_family = AF_NETLINK; if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0) { close(l_socket); return -1; } return l_socket; } static int netlink_send(int p_socket, int p_request) { char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))]; struct nlmsghdr *l_hdr; struct rtgenmsg *l_msg; struct sockaddr_nl l_addr; memset(l_buffer, 0, sizeof(l_buffer)); l_hdr = (struct nlmsghdr *)l_buffer; l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr); l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg)); l_hdr->nlmsg_type = p_request; l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; l_hdr->nlmsg_pid = 0; l_hdr->nlmsg_seq = p_socket; l_msg->rtgen_family = AF_UNSPEC; memset(&l_addr, 0, sizeof(l_addr)); l_addr.nl_family = AF_NETLINK; return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr))); } static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) { struct sockaddr_nl l_addr; struct msghdr l_msg; struct iovec l_iov; l_iov.iov_base = p_buffer; l_iov.iov_len = p_len; for(;;) { int l_result; l_msg.msg_name = (void *)&l_addr; l_msg.msg_namelen = sizeof(l_addr); l_msg.msg_iov = &l_iov; l_msg.msg_iovlen = 1; l_msg.msg_control = NULL; l_msg.msg_controllen = 0; l_msg.msg_flags = 0; l_result = recvmsg(p_socket, &l_msg, 0); if(l_result < 0) { if(errno == EINTR) { continue; } return -2; } /* Buffer was too small */ if(l_msg.msg_flags & MSG_TRUNC) { return -1; } return l_result; } } static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done) { size_t l_size = 4096; void *l_buffer = NULL; for(;;) { int l_read; uv__free(l_buffer); l_buffer = uv__malloc(l_size); if (l_buffer == NULL) { return NULL; } l_read = netlink_recv(p_socket, l_buffer, l_size); *p_size = l_read; if(l_read == -2) { uv__free(l_buffer); return NULL; } if(l_read >= 0) { pid_t l_pid = getpid(); struct nlmsghdr *l_hdr; for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) { if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } if(l_hdr->nlmsg_type == NLMSG_DONE) { *p_done = 1; break; } if(l_hdr->nlmsg_type == NLMSG_ERROR) { uv__free(l_buffer); return NULL; } } return l_buffer; } l_size *= 2; } } static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) { NetlinkList *l_item = uv__malloc(sizeof(NetlinkList)); if (l_item == NULL) { return NULL; } l_item->m_next = NULL; l_item->m_data = p_data; l_item->m_size = p_size; return l_item; } static void freeResultList(NetlinkList *p_list) { NetlinkList *l_cur; while(p_list) { l_cur = p_list; p_list = p_list->m_next; uv__free(l_cur->m_data); uv__free(l_cur); } } static NetlinkList *getResultList(int p_socket, int p_request) { int l_size; int l_done; NetlinkList *l_list; NetlinkList *l_end; if(netlink_send(p_socket, p_request) < 0) { return NULL; } l_list = NULL; l_end = NULL; l_done = 0; while(!l_done) { NetlinkList *l_item; struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); /* Error */ if(!l_hdr) { freeResultList(l_list); return NULL; } l_item = newListItem(l_hdr, l_size); if (!l_item) { freeResultList(l_list); return NULL; } if(!l_list) { l_list = l_item; } else { l_end->m_next = l_item; } l_end = l_item; } return l_list; } static size_t maxSize(size_t a, size_t b) { return (a > b ? a : b); } static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) { switch(p_family) { case AF_INET: return sizeof(struct sockaddr_in); case AF_INET6: return sizeof(struct sockaddr_in6); case AF_PACKET: return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); default: return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); } } static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size) { switch(p_family) { case AF_INET: memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); break; case AF_INET6: memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); break; case AF_PACKET: memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; break; default: memcpy(p_dest->sa_data, p_data, p_size); break; } p_dest->sa_family = p_family; } static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) { if(!*p_resultList) { *p_resultList = p_entry; } else { struct ifaddrs *l_cur = *p_resultList; while(l_cur->ifa_next) { l_cur = l_cur->ifa_next; } l_cur->ifa_next = p_entry; } } static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) { struct ifaddrs *l_entry; char *l_index; char *l_name; char *l_addr; char *l_data; struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); size_t l_nameSize = 0; size_t l_addrSize = 0; size_t l_dataSize = 0; size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); struct rtattr *l_rta; for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); switch(l_rta->rta_type) { case IFLA_ADDRESS: case IFLA_BROADCAST: l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); break; case IFLA_IFNAME: l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); break; case IFLA_STATS: l_dataSize += NLMSG_ALIGN(l_rtaSize); break; default: break; } } l_entry = uv__malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); if (l_entry == NULL) { return -1; } memset(l_entry, 0, sizeof(struct ifaddrs)); l_entry->ifa_name = ""; l_index = ((char *)l_entry) + sizeof(struct ifaddrs); l_name = l_index + sizeof(int); l_addr = l_name + l_nameSize; l_data = l_addr + l_addrSize; /* Save the interface index so we can look it up when handling the * addresses. */ memcpy(l_index, &l_info->ifi_index, sizeof(int)); l_entry->ifa_flags = l_info->ifi_flags; l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { void *l_rtaData = RTA_DATA(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); switch(l_rta->rta_type) { case IFLA_ADDRESS: case IFLA_BROADCAST: { size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; if(l_rta->rta_type == IFLA_ADDRESS) { l_entry->ifa_addr = (struct sockaddr *)l_addr; } else { l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; } l_addr += NLMSG_ALIGN(l_addrLen); break; } case IFLA_IFNAME: strncpy(l_name, l_rtaData, l_rtaDataSize); l_name[l_rtaDataSize] = '\0'; l_entry->ifa_name = l_name; break; case IFLA_STATS: memcpy(l_data, l_rtaData, l_rtaDataSize); l_entry->ifa_data = l_data; break; default: break; } } addToEnd(p_resultList, l_entry); return 0; } static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks) { int l_num = 0; struct ifaddrs *l_cur = *p_links; while(l_cur && l_num < p_numLinks) { char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs); int l_index; memcpy(&l_index, l_indexPtr, sizeof(int)); if(l_index == p_index) { return l_cur; } l_cur = l_cur->ifa_next; ++l_num; } return NULL; } static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks) { struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); size_t l_nameSize = 0; size_t l_addrSize = 0; int l_addedNetmask = 0; size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); struct rtattr *l_rta; struct ifaddrs *l_entry; char *l_name; char *l_addr; for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); if(l_info->ifa_family == AF_PACKET) { continue; } switch(l_rta->rta_type) { case IFA_ADDRESS: case IFA_LOCAL: if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) { /* Make room for netmask */ l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); l_addedNetmask = 1; } case IFA_BROADCAST: l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); break; case IFA_LABEL: l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); break; default: break; } } l_entry = uv__malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); if (l_entry == NULL) { return -1; } memset(l_entry, 0, sizeof(struct ifaddrs)); l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); l_name = ((char *)l_entry) + sizeof(struct ifaddrs); l_addr = l_name + l_nameSize; l_entry->ifa_flags = l_info->ifa_flags; if(l_interface) { l_entry->ifa_flags |= l_interface->ifa_flags; } l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { void *l_rtaData = RTA_DATA(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); switch(l_rta->rta_type) { case IFA_ADDRESS: case IFA_BROADCAST: case IFA_LOCAL: { size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); if(l_info->ifa_family == AF_INET6) { if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) { ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index; } } /* Apparently in a point-to-point network IFA_ADDRESS contains * the dest address and IFA_LOCAL contains the local address */ if(l_rta->rta_type == IFA_ADDRESS) { if(l_entry->ifa_addr) { l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; } else { l_entry->ifa_addr = (struct sockaddr *)l_addr; } } else if(l_rta->rta_type == IFA_LOCAL) { if(l_entry->ifa_addr) { l_entry->ifa_dstaddr = l_entry->ifa_addr; } l_entry->ifa_addr = (struct sockaddr *)l_addr; } else { l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; } l_addr += NLMSG_ALIGN(l_addrLen); break; } case IFA_LABEL: strncpy(l_name, l_rtaData, l_rtaDataSize); l_name[l_rtaDataSize] = '\0'; l_entry->ifa_name = l_name; break; default: break; } } if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6)) { unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); char l_mask[16] = {0}; unsigned i; for(i=0; i<(l_prefix/8); ++i) { l_mask[i] = 0xff; } if(l_prefix % 8) { l_mask[i] = 0xff << (8 - (l_prefix % 8)); } makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8); l_entry->ifa_netmask = (struct sockaddr *)l_addr; } addToEnd(p_resultList, l_entry); return 0; } static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) { int l_numLinks = 0; pid_t l_pid = getpid(); for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { unsigned int l_nlsize = p_netlinkList->m_size; struct nlmsghdr *l_hdr; for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } if(l_hdr->nlmsg_type == NLMSG_DONE) { break; } if(l_hdr->nlmsg_type == RTM_NEWLINK) { if(interpretLink(l_hdr, p_resultList) == -1) { return -1; } ++l_numLinks; } } } return l_numLinks; } static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) { pid_t l_pid = getpid(); for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { unsigned int l_nlsize = p_netlinkList->m_size; struct nlmsghdr *l_hdr; for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } if(l_hdr->nlmsg_type == NLMSG_DONE) { break; } if(l_hdr->nlmsg_type == RTM_NEWADDR) { if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) { return -1; } } } } return 0; } int getifaddrs(struct ifaddrs **ifap) { int l_socket; int l_result; int l_numLinks; NetlinkList *l_linkResults; NetlinkList *l_addrResults; if(!ifap) { return -1; } *ifap = NULL; l_socket = netlink_socket(); if(l_socket < 0) { return -1; } l_linkResults = getResultList(l_socket, RTM_GETLINK); if(!l_linkResults) { close(l_socket); return -1; } l_addrResults = getResultList(l_socket, RTM_GETADDR); if(!l_addrResults) { close(l_socket); freeResultList(l_linkResults); return -1; } l_result = 0; l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) { l_result = -1; } freeResultList(l_linkResults); freeResultList(l_addrResults); close(l_socket); return l_result; } void freeifaddrs(struct ifaddrs *ifa) { struct ifaddrs *l_cur; while(ifa) { l_cur = ifa; ifa = ifa->ifa_next; uv__free(l_cur); } } libuv-v1.8.0/src/unix/async.c000066400000000000000000000141341263361716600161000ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* This file contains both the uv__async internal infrastructure and the * user-facing uv_async_t functions. */ #include "uv.h" #include "internal.h" #include "atomic-ops.h" #include #include /* snprintf() */ #include #include #include #include static void uv__async_event(uv_loop_t* loop, struct uv__async* w, unsigned int nevents); static int uv__async_eventfd(void); int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { int err; err = uv__async_start(loop, &loop->async_watcher, uv__async_event); if (err) return err; uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC); handle->async_cb = async_cb; handle->pending = 0; QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue); uv__handle_start(handle); return 0; } int uv_async_send(uv_async_t* handle) { /* Do a cheap read first. */ if (ACCESS_ONCE(int, handle->pending) != 0) return 0; if (cmpxchgi(&handle->pending, 0, 1) == 0) uv__async_send(&handle->loop->async_watcher); return 0; } void uv__async_close(uv_async_t* handle) { QUEUE_REMOVE(&handle->queue); uv__handle_stop(handle); } static void uv__async_event(uv_loop_t* loop, struct uv__async* w, unsigned int nevents) { QUEUE queue; QUEUE* q; uv_async_t* h; QUEUE_MOVE(&loop->async_handles, &queue); while (!QUEUE_EMPTY(&queue)) { q = QUEUE_HEAD(&queue); h = QUEUE_DATA(q, uv_async_t, queue); QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->async_handles, q); if (cmpxchgi(&h->pending, 1, 0) == 0) continue; if (h->async_cb == NULL) continue; h->async_cb(h); } } static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { struct uv__async* wa; char buf[1024]; unsigned n; ssize_t r; n = 0; for (;;) { r = read(w->fd, buf, sizeof(buf)); if (r > 0) n += r; if (r == sizeof(buf)) continue; if (r != -1) break; if (errno == EAGAIN || errno == EWOULDBLOCK) break; if (errno == EINTR) continue; abort(); } wa = container_of(w, struct uv__async, io_watcher); #if defined(__linux__) if (wa->wfd == -1) { uint64_t val; assert(n == sizeof(val)); memcpy(&val, buf, sizeof(val)); /* Avoid alignment issues. */ wa->cb(loop, wa, val); return; } #endif wa->cb(loop, wa, n); } void uv__async_send(struct uv__async* wa) { const void* buf; ssize_t len; int fd; int r; buf = ""; len = 1; fd = wa->wfd; #if defined(__linux__) if (fd == -1) { static const uint64_t val = 1; buf = &val; len = sizeof(val); fd = wa->io_watcher.fd; /* eventfd */ } #endif do r = write(fd, buf, len); while (r == -1 && errno == EINTR); if (r == len) return; if (r == -1) if (errno == EAGAIN || errno == EWOULDBLOCK) return; abort(); } void uv__async_init(struct uv__async* wa) { wa->io_watcher.fd = -1; wa->wfd = -1; } int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb) { int pipefd[2]; int err; if (wa->io_watcher.fd != -1) return 0; err = uv__async_eventfd(); if (err >= 0) { pipefd[0] = err; pipefd[1] = -1; } else if (err == -ENOSYS) { err = uv__make_pipe(pipefd, UV__F_NONBLOCK); #if defined(__linux__) /* Save a file descriptor by opening one of the pipe descriptors as * read/write through the procfs. That file descriptor can then * function as both ends of the pipe. */ if (err == 0) { char buf[32]; int fd; snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]); fd = uv__open_cloexec(buf, O_RDWR); if (fd >= 0) { uv__close(pipefd[0]); uv__close(pipefd[1]); pipefd[0] = fd; pipefd[1] = fd; } } #endif } if (err < 0) return err; uv__io_init(&wa->io_watcher, uv__async_io, pipefd[0]); uv__io_start(loop, &wa->io_watcher, UV__POLLIN); wa->wfd = pipefd[1]; wa->cb = cb; return 0; } void uv__async_stop(uv_loop_t* loop, struct uv__async* wa) { if (wa->io_watcher.fd == -1) return; if (wa->wfd != -1) { if (wa->wfd != wa->io_watcher.fd) uv__close(wa->wfd); wa->wfd = -1; } uv__io_stop(loop, &wa->io_watcher, UV__POLLIN); uv__close(wa->io_watcher.fd); wa->io_watcher.fd = -1; } static int uv__async_eventfd() { #if defined(__linux__) static int no_eventfd2; static int no_eventfd; int fd; if (no_eventfd2) goto skip_eventfd2; fd = uv__eventfd2(0, UV__EFD_CLOEXEC | UV__EFD_NONBLOCK); if (fd != -1) return fd; if (errno != ENOSYS) return -errno; no_eventfd2 = 1; skip_eventfd2: if (no_eventfd) goto skip_eventfd; fd = uv__eventfd(0); if (fd != -1) { uv__cloexec(fd, 1); uv__nonblock(fd, 1); return fd; } if (errno != ENOSYS) return -errno; no_eventfd = 1; skip_eventfd: #endif return -ENOSYS; } libuv-v1.8.0/src/unix/atomic-ops.h000066400000000000000000000052601263361716600170430ustar00rootroot00000000000000/* Copyright (c) 2013, Ben Noordhuis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef UV_ATOMIC_OPS_H_ #define UV_ATOMIC_OPS_H_ #include "internal.h" /* UV_UNUSED */ #if defined(__SUNPRO_C) || defined(__SUNPRO_CC) #include #define __sync_val_compare_and_swap(p, o, n) atomic_cas_ptr(p, o, n) #endif UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)); UV_UNUSED(static void cpu_relax(void)); /* Prefer hand-rolled assembly over the gcc builtins because the latter also * issue full memory barriers. */ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { #if defined(__i386__) || defined(__x86_64__) int out; __asm__ __volatile__ ("lock; cmpxchg %2, %1;" : "=a" (out), "+m" (*(volatile int*) ptr) : "r" (newval), "0" (oldval) : "memory"); return out; #elif defined(_AIX) && defined(__xlC__) const int out = (*(volatile int*) ptr); __compare_and_swap(ptr, &oldval, newval); return out; #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif } UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { #if defined(__i386__) || defined(__x86_64__) long out; __asm__ __volatile__ ("lock; cmpxchg %2, %1;" : "=a" (out), "+m" (*(volatile long*) ptr) : "r" (newval), "0" (oldval) : "memory"); return out; #elif defined(_AIX) && defined(__xlC__) const long out = (*(volatile int*) ptr); # if defined(__64BIT__) __compare_and_swaplp(ptr, &oldval, newval); # else __compare_and_swap(ptr, &oldval, newval); # endif /* if defined(__64BIT__) */ return out; #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif } UV_UNUSED(static void cpu_relax(void)) { #if defined(__i386__) || defined(__x86_64__) __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ #endif } #endif /* UV_ATOMIC_OPS_H_ */ libuv-v1.8.0/src/unix/core.c000066400000000000000000000562441263361716600157230ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include /* NULL */ #include /* printf */ #include #include /* strerror */ #include #include #include #include #include #include #include #include #include #include #include /* INT_MAX, PATH_MAX, IOV_MAX */ #include /* writev */ #include /* getrusage */ #include #ifdef __linux__ # include #endif #ifdef __sun # include # include #endif #ifdef __APPLE__ # include /* _NSGetExecutablePath */ # include # include #endif #if defined(__FreeBSD__) || defined(__DragonFly__) # include # include # include # include # define UV__O_CLOEXEC O_CLOEXEC # if defined(__FreeBSD__) && __FreeBSD__ >= 10 # define uv__accept4 accept4 # define UV__SOCK_NONBLOCK SOCK_NONBLOCK # define UV__SOCK_CLOEXEC SOCK_CLOEXEC # endif # if !defined(F_DUP2FD_CLOEXEC) && defined(_F_DUP2FD_CLOEXEC) # define F_DUP2FD_CLOEXEC _F_DUP2FD_CLOEXEC # endif #endif #ifdef _AIX #include #endif #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 # include /* for dlsym */ #endif static int uv__run_pending(uv_loop_t* loop); /* Verify that uv_buf_t is ABI-compatible with struct iovec. */ STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec)); STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->base) == sizeof(((struct iovec*) 0)->iov_base)); STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->len) == sizeof(((struct iovec*) 0)->iov_len)); STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base)); STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len)); uint64_t uv_hrtime(void) { return uv__hrtime(UV_CLOCK_PRECISE); } void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); handle->flags |= UV_CLOSING; handle->close_cb = close_cb; switch (handle->type) { case UV_NAMED_PIPE: uv__pipe_close((uv_pipe_t*)handle); break; case UV_TTY: uv__stream_close((uv_stream_t*)handle); break; case UV_TCP: uv__tcp_close((uv_tcp_t*)handle); break; case UV_UDP: uv__udp_close((uv_udp_t*)handle); break; case UV_PREPARE: uv__prepare_close((uv_prepare_t*)handle); break; case UV_CHECK: uv__check_close((uv_check_t*)handle); break; case UV_IDLE: uv__idle_close((uv_idle_t*)handle); break; case UV_ASYNC: uv__async_close((uv_async_t*)handle); break; case UV_TIMER: uv__timer_close((uv_timer_t*)handle); break; case UV_PROCESS: uv__process_close((uv_process_t*)handle); break; case UV_FS_EVENT: uv__fs_event_close((uv_fs_event_t*)handle); break; case UV_POLL: uv__poll_close((uv_poll_t*)handle); break; case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t*)handle); break; case UV_SIGNAL: uv__signal_close((uv_signal_t*) handle); /* Signal handles may not be closed immediately. The signal code will */ /* itself close uv__make_close_pending whenever appropriate. */ return; default: assert(0); } uv__make_close_pending(handle); } int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { int r; int fd; socklen_t len; if (handle == NULL || value == NULL) return -EINVAL; if (handle->type == UV_TCP || handle->type == UV_NAMED_PIPE) fd = uv__stream_fd((uv_stream_t*) handle); else if (handle->type == UV_UDP) fd = ((uv_udp_t *) handle)->io_watcher.fd; else return -ENOTSUP; len = sizeof(*value); if (*value == 0) r = getsockopt(fd, SOL_SOCKET, optname, value, &len); else r = setsockopt(fd, SOL_SOCKET, optname, (const void*) value, len); if (r < 0) return -errno; return 0; } void uv__make_close_pending(uv_handle_t* handle) { assert(handle->flags & UV_CLOSING); assert(!(handle->flags & UV_CLOSED)); handle->next_closing = handle->loop->closing_handles; handle->loop->closing_handles = handle; } int uv__getiovmax(void) { #if defined(IOV_MAX) return IOV_MAX; #elif defined(_SC_IOV_MAX) static int iovmax = -1; if (iovmax == -1) { iovmax = sysconf(_SC_IOV_MAX); /* On some embedded devices (arm-linux-uclibc based ip camera), * sysconf(_SC_IOV_MAX) can not get the correct value. The return * value is -1 and the errno is EINPROGRESS. Degrade the value to 1. */ if (iovmax == -1) iovmax = 1; } return iovmax; #else return 1024; #endif } static void uv__finish_close(uv_handle_t* handle) { /* Note: while the handle is in the UV_CLOSING state now, it's still possible * for it to be active in the sense that uv__is_active() returns true. * A good example is when the user calls uv_shutdown(), immediately followed * by uv_close(). The handle is considered active at this point because the * completion of the shutdown req is still pending. */ assert(handle->flags & UV_CLOSING); assert(!(handle->flags & UV_CLOSED)); handle->flags |= UV_CLOSED; switch (handle->type) { case UV_PREPARE: case UV_CHECK: case UV_IDLE: case UV_ASYNC: case UV_TIMER: case UV_PROCESS: case UV_FS_EVENT: case UV_FS_POLL: case UV_POLL: case UV_SIGNAL: break; case UV_NAMED_PIPE: case UV_TCP: case UV_TTY: uv__stream_destroy((uv_stream_t*)handle); break; case UV_UDP: uv__udp_finish_close((uv_udp_t*)handle); break; default: assert(0); break; } uv__handle_unref(handle); QUEUE_REMOVE(&handle->handle_queue); if (handle->close_cb) { handle->close_cb(handle); } } static void uv__run_closing_handles(uv_loop_t* loop) { uv_handle_t* p; uv_handle_t* q; p = loop->closing_handles; loop->closing_handles = NULL; while (p) { q = p->next_closing; uv__finish_close(p); p = q; } } int uv_is_closing(const uv_handle_t* handle) { return uv__is_closing(handle); } int uv_backend_fd(const uv_loop_t* loop) { return loop->backend_fd; } int uv_backend_timeout(const uv_loop_t* loop) { if (loop->stop_flag != 0) return 0; if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) return 0; if (!QUEUE_EMPTY(&loop->idle_handles)) return 0; if (!QUEUE_EMPTY(&loop->pending_queue)) return 0; if (loop->closing_handles) return 0; return uv__next_timeout(loop); } static int uv__loop_alive(const uv_loop_t* loop) { return uv__has_active_handles(loop) || uv__has_active_reqs(loop) || loop->closing_handles != NULL; } int uv_loop_alive(const uv_loop_t* loop) { return uv__loop_alive(loop); } int uv_run(uv_loop_t* loop, uv_run_mode mode) { int timeout; int r; int ran_pending; r = uv__loop_alive(loop); if (!r) uv__update_time(loop); while (r != 0 && loop->stop_flag == 0) { uv__update_time(loop); uv__run_timers(loop); ran_pending = uv__run_pending(loop); uv__run_idle(loop); uv__run_prepare(loop); timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); uv__io_poll(loop, timeout); uv__run_check(loop); uv__run_closing_handles(loop); if (mode == UV_RUN_ONCE) { /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. * * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ uv__update_time(loop); uv__run_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; } /* The if statement lets gcc compile it to a conditional store. Avoids * dirtying a cache line. */ if (loop->stop_flag != 0) loop->stop_flag = 0; return r; } void uv_update_time(uv_loop_t* loop) { uv__update_time(loop); } int uv_is_active(const uv_handle_t* handle) { return uv__is_active(handle); } /* Open a socket in non-blocking close-on-exec mode, atomically if possible. */ int uv__socket(int domain, int type, int protocol) { int sockfd; int err; #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) sockfd = socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); if (sockfd != -1) return sockfd; if (errno != EINVAL) return -errno; #endif sockfd = socket(domain, type, protocol); if (sockfd == -1) return -errno; err = uv__nonblock(sockfd, 1); if (err == 0) err = uv__cloexec(sockfd, 1); if (err) { uv__close(sockfd); return err; } #if defined(SO_NOSIGPIPE) { int on = 1; setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); } #endif return sockfd; } int uv__accept(int sockfd) { int peerfd; int err; assert(sockfd >= 0); while (1) { #if defined(__linux__) || __FreeBSD__ >= 10 static int no_accept4; if (no_accept4) goto skip; peerfd = uv__accept4(sockfd, NULL, NULL, UV__SOCK_NONBLOCK|UV__SOCK_CLOEXEC); if (peerfd != -1) return peerfd; if (errno == EINTR) continue; if (errno != ENOSYS) return -errno; no_accept4 = 1; skip: #endif peerfd = accept(sockfd, NULL, NULL); if (peerfd == -1) { if (errno == EINTR) continue; return -errno; } err = uv__cloexec(peerfd, 1); if (err == 0) err = uv__nonblock(peerfd, 1); if (err) { uv__close(peerfd); return err; } return peerfd; } } int uv__close(int fd) { int saved_errno; int rc; assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */ assert(fd > STDERR_FILENO); /* Catch stdio close bugs. */ saved_errno = errno; rc = close(fd); if (rc == -1) { rc = -errno; if (rc == -EINTR) rc = -EINPROGRESS; /* For platform/libc consistency. */ errno = saved_errno; } return rc; } #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \ defined(_AIX) || defined(__DragonFly__) int uv__nonblock(int fd, int set) { int r; do r = ioctl(fd, FIONBIO, &set); while (r == -1 && errno == EINTR); if (r) return -errno; return 0; } int uv__cloexec(int fd, int set) { int r; do r = ioctl(fd, set ? FIOCLEX : FIONCLEX); while (r == -1 && errno == EINTR); if (r) return -errno; return 0; } #else /* !(defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \ defined(_AIX) || defined(__DragonFly__)) */ int uv__nonblock(int fd, int set) { int flags; int r; do r = fcntl(fd, F_GETFL); while (r == -1 && errno == EINTR); if (r == -1) return -errno; /* Bail out now if already set/clear. */ if (!!(r & O_NONBLOCK) == !!set) return 0; if (set) flags = r | O_NONBLOCK; else flags = r & ~O_NONBLOCK; do r = fcntl(fd, F_SETFL, flags); while (r == -1 && errno == EINTR); if (r) return -errno; return 0; } int uv__cloexec(int fd, int set) { int flags; int r; do r = fcntl(fd, F_GETFD); while (r == -1 && errno == EINTR); if (r == -1) return -errno; /* Bail out now if already set/clear. */ if (!!(r & FD_CLOEXEC) == !!set) return 0; if (set) flags = r | FD_CLOEXEC; else flags = r & ~FD_CLOEXEC; do r = fcntl(fd, F_SETFD, flags); while (r == -1 && errno == EINTR); if (r) return -errno; return 0; } #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \ defined(_AIX) || defined(__DragonFly__) */ /* This function is not execve-safe, there is a race window * between the call to dup() and fcntl(FD_CLOEXEC). */ int uv__dup(int fd) { int err; fd = dup(fd); if (fd == -1) return -errno; err = uv__cloexec(fd, 1); if (err) { uv__close(fd); return err; } return fd; } ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { struct cmsghdr* cmsg; ssize_t rc; int* pfd; int* end; #if defined(__linux__) static int no_msg_cmsg_cloexec; if (no_msg_cmsg_cloexec == 0) { rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */ if (rc != -1) return rc; if (errno != EINVAL) return -errno; rc = recvmsg(fd, msg, flags); if (rc == -1) return -errno; no_msg_cmsg_cloexec = 1; } else { rc = recvmsg(fd, msg, flags); } #else rc = recvmsg(fd, msg, flags); #endif if (rc == -1) return -errno; if (msg->msg_controllen == 0) return rc; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) if (cmsg->cmsg_type == SCM_RIGHTS) for (pfd = (int*) CMSG_DATA(cmsg), end = (int*) ((char*) cmsg + cmsg->cmsg_len); pfd < end; pfd += 1) uv__cloexec(*pfd, 1); return rc; } int uv_cwd(char* buffer, size_t* size) { if (buffer == NULL || size == NULL) return -EINVAL; if (getcwd(buffer, *size) == NULL) return -errno; *size = strlen(buffer); if (*size > 1 && buffer[*size - 1] == '/') { buffer[*size-1] = '\0'; (*size)--; } return 0; } int uv_chdir(const char* dir) { if (chdir(dir)) return -errno; return 0; } void uv_disable_stdio_inheritance(void) { int fd; /* Set the CLOEXEC flag on all open descriptors. Unconditionally try the * first 16 file descriptors. After that, bail out after the first error. */ for (fd = 0; ; fd++) if (uv__cloexec(fd, 1) && fd > 15) break; } int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { int fd_out; switch (handle->type) { case UV_TCP: case UV_NAMED_PIPE: case UV_TTY: fd_out = uv__stream_fd((uv_stream_t*) handle); break; case UV_UDP: fd_out = ((uv_udp_t *) handle)->io_watcher.fd; break; case UV_POLL: fd_out = ((uv_poll_t *) handle)->io_watcher.fd; break; default: return -EINVAL; } if (uv__is_closing(handle) || fd_out == -1) return -EBADF; *fd = fd_out; return 0; } static int uv__run_pending(uv_loop_t* loop) { QUEUE* q; QUEUE pq; uv__io_t* w; if (QUEUE_EMPTY(&loop->pending_queue)) return 0; QUEUE_MOVE(&loop->pending_queue, &pq); while (!QUEUE_EMPTY(&pq)) { q = QUEUE_HEAD(&pq); QUEUE_REMOVE(q); QUEUE_INIT(q); w = QUEUE_DATA(q, uv__io_t, pending_queue); w->cb(loop, w, UV__POLLOUT); } return 1; } static unsigned int next_power_of_two(unsigned int val) { val -= 1; val |= val >> 1; val |= val >> 2; val |= val >> 4; val |= val >> 8; val |= val >> 16; val += 1; return val; } static void maybe_resize(uv_loop_t* loop, unsigned int len) { uv__io_t** watchers; void* fake_watcher_list; void* fake_watcher_count; unsigned int nwatchers; unsigned int i; if (len <= loop->nwatchers) return; /* Preserve fake watcher list and count at the end of the watchers */ if (loop->watchers != NULL) { fake_watcher_list = loop->watchers[loop->nwatchers]; fake_watcher_count = loop->watchers[loop->nwatchers + 1]; } else { fake_watcher_list = NULL; fake_watcher_count = NULL; } nwatchers = next_power_of_two(len + 2) - 2; watchers = uv__realloc(loop->watchers, (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) abort(); for (i = loop->nwatchers; i < nwatchers; i++) watchers[i] = NULL; watchers[nwatchers] = fake_watcher_list; watchers[nwatchers + 1] = fake_watcher_count; loop->watchers = watchers; loop->nwatchers = nwatchers; } void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { assert(cb != NULL); assert(fd >= -1); QUEUE_INIT(&w->pending_queue); QUEUE_INIT(&w->watcher_queue); w->cb = cb; w->fd = fd; w->events = 0; w->pevents = 0; #if defined(UV_HAVE_KQUEUE) w->rcount = 0; w->wcount = 0; #endif /* defined(UV_HAVE_KQUEUE) */ } void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { assert(0 == (events & ~(UV__POLLIN | UV__POLLOUT))); assert(0 != events); assert(w->fd >= 0); assert(w->fd < INT_MAX); w->pevents |= events; maybe_resize(loop, w->fd + 1); #if !defined(__sun) /* The event ports backend needs to rearm all file descriptors on each and * every tick of the event loop but the other backends allow us to * short-circuit here if the event mask is unchanged. */ if (w->events == w->pevents) { if (w->events == 0 && !QUEUE_EMPTY(&w->watcher_queue)) { QUEUE_REMOVE(&w->watcher_queue); QUEUE_INIT(&w->watcher_queue); } return; } #endif if (QUEUE_EMPTY(&w->watcher_queue)) QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); if (loop->watchers[w->fd] == NULL) { loop->watchers[w->fd] = w; loop->nfds++; } } void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { assert(0 == (events & ~(UV__POLLIN | UV__POLLOUT))); assert(0 != events); if (w->fd == -1) return; assert(w->fd >= 0); /* Happens when uv__io_stop() is called on a handle that was never started. */ if ((unsigned) w->fd >= loop->nwatchers) return; w->pevents &= ~events; if (w->pevents == 0) { QUEUE_REMOVE(&w->watcher_queue); QUEUE_INIT(&w->watcher_queue); if (loop->watchers[w->fd] != NULL) { assert(loop->watchers[w->fd] == w); assert(loop->nfds > 0); loop->watchers[w->fd] = NULL; loop->nfds--; w->events = 0; } } else if (QUEUE_EMPTY(&w->watcher_queue)) QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); } void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT); QUEUE_REMOVE(&w->pending_queue); /* Remove stale events for this file descriptor */ uv__platform_invalidate_fd(loop, w->fd); } void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { if (QUEUE_EMPTY(&w->pending_queue)) QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue); } int uv__io_active(const uv__io_t* w, unsigned int events) { assert(0 == (events & ~(UV__POLLIN | UV__POLLOUT))); assert(0 != events); return 0 != (w->pevents & events); } int uv_getrusage(uv_rusage_t* rusage) { struct rusage usage; if (getrusage(RUSAGE_SELF, &usage)) return -errno; rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec; rusage->ru_utime.tv_usec = usage.ru_utime.tv_usec; rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec; rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec; rusage->ru_maxrss = usage.ru_maxrss; rusage->ru_ixrss = usage.ru_ixrss; rusage->ru_idrss = usage.ru_idrss; rusage->ru_isrss = usage.ru_isrss; rusage->ru_minflt = usage.ru_minflt; rusage->ru_majflt = usage.ru_majflt; rusage->ru_nswap = usage.ru_nswap; rusage->ru_inblock = usage.ru_inblock; rusage->ru_oublock = usage.ru_oublock; rusage->ru_msgsnd = usage.ru_msgsnd; rusage->ru_msgrcv = usage.ru_msgrcv; rusage->ru_nsignals = usage.ru_nsignals; rusage->ru_nvcsw = usage.ru_nvcsw; rusage->ru_nivcsw = usage.ru_nivcsw; return 0; } int uv__open_cloexec(const char* path, int flags) { int err; int fd; #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 9) || \ defined(__DragonFly__) static int no_cloexec; if (!no_cloexec) { fd = open(path, flags | UV__O_CLOEXEC); if (fd != -1) return fd; if (errno != EINVAL) return -errno; /* O_CLOEXEC not supported. */ no_cloexec = 1; } #endif fd = open(path, flags); if (fd == -1) return -errno; err = uv__cloexec(fd, 1); if (err) { uv__close(fd); return err; } return fd; } int uv__dup2_cloexec(int oldfd, int newfd) { int r; #if defined(__FreeBSD__) && __FreeBSD__ >= 10 r = dup3(oldfd, newfd, O_CLOEXEC); if (r == -1) return -errno; return r; #elif defined(__FreeBSD__) && defined(F_DUP2FD_CLOEXEC) r = fcntl(oldfd, F_DUP2FD_CLOEXEC, newfd); if (r != -1) return r; if (errno != EINVAL) return -errno; /* Fall through. */ #elif defined(__linux__) static int no_dup3; if (!no_dup3) { do r = uv__dup3(oldfd, newfd, UV__O_CLOEXEC); while (r == -1 && errno == EBUSY); if (r != -1) return r; if (errno != ENOSYS) return -errno; /* Fall through. */ no_dup3 = 1; } #endif { int err; do r = dup2(oldfd, newfd); #if defined(__linux__) while (r == -1 && errno == EBUSY); #else while (0); /* Never retry. */ #endif if (r == -1) return -errno; err = uv__cloexec(newfd, 1); if (err) { uv__close(newfd); return err; } return r; } } int uv_os_homedir(char* buffer, size_t* size) { struct passwd pw; struct passwd* result; char* buf; uid_t uid; size_t bufsize; size_t len; long initsize; int r; #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 int (*getpwuid_r)(uid_t, struct passwd*, char*, size_t, struct passwd**); #endif if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; /* Check if the HOME environment variable is set first */ buf = getenv("HOME"); if (buf != NULL) { len = strlen(buf); if (len >= *size) { *size = len; return -ENOBUFS; } memcpy(buffer, buf, len + 1); *size = len; return 0; } #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 getpwuid_r = dlsym(RTLD_DEFAULT, "getpwuid_r"); if (getpwuid_r == NULL) return -ENOSYS; #endif /* HOME is not set, so call getpwuid() */ initsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (initsize <= 0) bufsize = 4096; else bufsize = (size_t) initsize; uid = getuid(); buf = NULL; for (;;) { uv__free(buf); buf = uv__malloc(bufsize); if (buf == NULL) return -ENOMEM; r = getpwuid_r(uid, &pw, buf, bufsize, &result); if (r != ERANGE) break; bufsize *= 2; } if (r != 0) { uv__free(buf); return -r; } if (result == NULL) { uv__free(buf); return -ENOENT; } len = strlen(pw.pw_dir); if (len >= *size) { *size = len; uv__free(buf); return -ENOBUFS; } memcpy(buffer, pw.pw_dir, len + 1); *size = len; uv__free(buf); return 0; } libuv-v1.8.0/src/unix/darwin-proctitle.c000066400000000000000000000172351263361716600202570ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #if !TARGET_OS_IPHONE # include # include #endif static int uv__pthread_setname_np(const char* name) { int (*dynamic_pthread_setname_np)(const char* name); char namebuf[64]; /* MAXTHREADNAMESIZE */ int err; /* pthread_setname_np() first appeared in OS X 10.6 and iOS 3.2. */ *(void **)(&dynamic_pthread_setname_np) = dlsym(RTLD_DEFAULT, "pthread_setname_np"); if (dynamic_pthread_setname_np == NULL) return -ENOSYS; strncpy(namebuf, name, sizeof(namebuf) - 1); namebuf[sizeof(namebuf) - 1] = '\0'; err = dynamic_pthread_setname_np(namebuf); if (err) return -err; return 0; } int uv__set_process_title(const char* title) { #if TARGET_OS_IPHONE return uv__pthread_setname_np(title); #else CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, const char*, CFStringEncoding); CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef); void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef); void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef); CFTypeRef (*pLSGetCurrentApplicationASN)(void); OSStatus (*pLSSetApplicationInformationItem)(int, CFTypeRef, CFStringRef, CFStringRef, CFDictionaryRef*); void* application_services_handle; void* core_foundation_handle; CFBundleRef launch_services_bundle; CFStringRef* display_name_key; CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef); CFBundleRef (*pCFBundleGetMainBundle)(void); CFBundleRef hi_services_bundle; OSStatus (*pSetApplicationIsDaemon)(int); CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef); void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t, void*); CFTypeRef asn; int err; err = -ENOENT; application_services_handle = dlopen("/System/Library/Frameworks/" "ApplicationServices.framework/" "Versions/A/ApplicationServices", RTLD_LAZY | RTLD_LOCAL); core_foundation_handle = dlopen("/System/Library/Frameworks/" "CoreFoundation.framework/" "Versions/A/CoreFoundation", RTLD_LAZY | RTLD_LOCAL); if (application_services_handle == NULL || core_foundation_handle == NULL) goto out; *(void **)(&pCFStringCreateWithCString) = dlsym(core_foundation_handle, "CFStringCreateWithCString"); *(void **)(&pCFBundleGetBundleWithIdentifier) = dlsym(core_foundation_handle, "CFBundleGetBundleWithIdentifier"); *(void **)(&pCFBundleGetDataPointerForName) = dlsym(core_foundation_handle, "CFBundleGetDataPointerForName"); *(void **)(&pCFBundleGetFunctionPointerForName) = dlsym(core_foundation_handle, "CFBundleGetFunctionPointerForName"); if (pCFStringCreateWithCString == NULL || pCFBundleGetBundleWithIdentifier == NULL || pCFBundleGetDataPointerForName == NULL || pCFBundleGetFunctionPointerForName == NULL) { goto out; } #define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) launch_services_bundle = pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices")); if (launch_services_bundle == NULL) goto out; *(void **)(&pLSGetCurrentApplicationASN) = pCFBundleGetFunctionPointerForName(launch_services_bundle, S("_LSGetCurrentApplicationASN")); if (pLSGetCurrentApplicationASN == NULL) goto out; *(void **)(&pLSSetApplicationInformationItem) = pCFBundleGetFunctionPointerForName(launch_services_bundle, S("_LSSetApplicationInformationItem")); if (pLSSetApplicationInformationItem == NULL) goto out; display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle, S("_kLSDisplayNameKey")); if (display_name_key == NULL || *display_name_key == NULL) goto out; *(void **)(&pCFBundleGetInfoDictionary) = dlsym(core_foundation_handle, "CFBundleGetInfoDictionary"); *(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle, "CFBundleGetMainBundle"); if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL) goto out; /* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */ hi_services_bundle = pCFBundleGetBundleWithIdentifier(S("com.apple.HIServices")); err = -ENOENT; if (hi_services_bundle == NULL) goto out; *(void **)(&pSetApplicationIsDaemon) = pCFBundleGetFunctionPointerForName( hi_services_bundle, S("SetApplicationIsDaemon")); *(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName( launch_services_bundle, S("_LSApplicationCheckIn")); *(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) = pCFBundleGetFunctionPointerForName( launch_services_bundle, S("_LSSetApplicationLaunchServicesServerConnectionStatus")); if (pSetApplicationIsDaemon == NULL || pLSApplicationCheckIn == NULL || pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) { goto out; } if (pSetApplicationIsDaemon(1) != noErr) goto out; pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL); /* Check into process manager?! */ pLSApplicationCheckIn(-2, pCFBundleGetInfoDictionary(pCFBundleGetMainBundle())); asn = pLSGetCurrentApplicationASN(); err = -EINVAL; if (pLSSetApplicationInformationItem(-2, /* Magic value. */ asn, *display_name_key, S(title), NULL) != noErr) { goto out; } uv__pthread_setname_np(title); /* Don't care if it fails. */ err = 0; out: if (core_foundation_handle != NULL) dlclose(core_foundation_handle); if (application_services_handle != NULL) dlclose(application_services_handle); return err; #endif /* !TARGET_OS_IPHONE */ } libuv-v1.8.0/src/unix/darwin.c000066400000000000000000000212141263361716600162440ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include /* _NSGetExecutablePath */ #include #include #include /* sysconf */ int uv__platform_loop_init(uv_loop_t* loop) { loop->cf_state = NULL; if (uv__kqueue_init(loop)) return -errno; return 0; } void uv__platform_loop_delete(uv_loop_t* loop) { uv__fsevents_loop_delete(loop); } uint64_t uv__hrtime(uv_clocktype_t type) { static mach_timebase_info_data_t info; if ((ACCESS_ONCE(uint32_t, info.numer) == 0 || ACCESS_ONCE(uint32_t, info.denom) == 0) && mach_timebase_info(&info) != KERN_SUCCESS) abort(); return mach_absolute_time() * info.numer / info.denom; } int uv_exepath(char* buffer, size_t* size) { /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */ char abspath[PATH_MAX * 2 + 1]; char exepath[PATH_MAX + 1]; uint32_t exepath_size; size_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; exepath_size = sizeof(exepath); if (_NSGetExecutablePath(exepath, &exepath_size)) return -EIO; if (realpath(exepath, abspath) != abspath) return -errno; abspath_size = strlen(abspath); if (abspath_size == 0) return -EIO; *size -= 1; if (*size > abspath_size) *size = abspath_size; memcpy(buffer, abspath, *size); buffer[*size] = '\0'; return 0; } uint64_t uv_get_free_memory(void) { vm_statistics_data_t info; mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count) != KERN_SUCCESS) { return -EINVAL; /* FIXME(bnoordhuis) Translate error. */ } return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); } uint64_t uv_get_total_memory(void) { uint64_t info; int which[] = {CTL_HW, HW_MEMSIZE}; size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; return (uint64_t) info; } void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } int uv_resident_set_memory(size_t* rss) { mach_msg_type_number_t count; task_basic_info_data_t info; kern_return_t err; count = TASK_BASIC_INFO_COUNT; err = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) &info, &count); (void) &err; /* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than * KERN_SUCCESS implies a libuv bug. */ assert(err == KERN_SUCCESS); *rss = info.resident_size; return 0; } int uv_uptime(double* uptime) { time_t now; struct timeval info; size_t size = sizeof(info); static int which[] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; now = time(NULL); *uptime = now - info.tv_sec; return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), multiplier = ((uint64_t)1000L / ticks); char model[512]; uint64_t cpuspeed; size_t size; unsigned int i; natural_t numcpus; mach_msg_type_number_t msg_type; processor_cpu_load_info_data_t *info; uv_cpu_info_t* cpu_info; size = sizeof(model); if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) && sysctlbyname("hw.model", &model, &size, NULL, 0)) { return -errno; } size = sizeof(cpuspeed); if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0)) return -errno; if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, (processor_info_array_t*)&info, &msg_type) != KERN_SUCCESS) { return -EINVAL; /* FIXME(bnoordhuis) Translate error. */ } *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) { vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); return -ENOMEM; } *count = numcpus; for (i = 0; i < numcpus; i++) { cpu_info = &(*cpu_infos)[i]; cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier; cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier; cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier; cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier; cpu_info->cpu_times.irq = 0; cpu_info->model = uv__strdup(model); cpu_info->speed = cpuspeed/1000000; } vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); return 0; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs *addrs, *ent; uv_interface_address_t* address; int i; struct sockaddr_dl *sa_addr; if (getifaddrs(&addrs)) return -errno; *count = 0; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family == AF_LINK)) { continue; } (*count)++; } *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return -ENOMEM; } address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) continue; if (ent->ifa_addr == NULL) continue; /* * On Mac OS X getifaddrs returns information related to Mac Addresses for * various devices, such as firewire, etc. These are not relevant here. */ if (ent->ifa_addr->sa_family == AF_LINK) continue; address->name = uv__strdup(ent->ifa_name); if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); } else { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } if (ent->ifa_netmask->sa_family == AF_INET6) { address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); } else { address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); } address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); address++; } /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != AF_LINK)) { continue; } address = *addresses; for (i = 0; i < (*count); i++) { if (strcmp(address->name, ent->ifa_name) == 0) { sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); } address++; } } freeifaddrs(addrs); return 0; } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; i++) { uv__free(addresses[i].name); } uv__free(addresses); } libuv-v1.8.0/src/unix/dl.c000066400000000000000000000043251263361716600153630ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include static int uv__dlerror(uv_lib_t* lib); int uv_dlopen(const char* filename, uv_lib_t* lib) { dlerror(); /* Reset error status. */ lib->errmsg = NULL; lib->handle = dlopen(filename, RTLD_LAZY); return lib->handle ? 0 : uv__dlerror(lib); } void uv_dlclose(uv_lib_t* lib) { uv__free(lib->errmsg); lib->errmsg = NULL; if (lib->handle) { /* Ignore errors. No good way to signal them without leaking memory. */ dlclose(lib->handle); lib->handle = NULL; } } int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) { dlerror(); /* Reset error status. */ *ptr = dlsym(lib->handle, name); return uv__dlerror(lib); } const char* uv_dlerror(const uv_lib_t* lib) { return lib->errmsg ? lib->errmsg : "no error"; } static int uv__dlerror(uv_lib_t* lib) { const char* errmsg; uv__free(lib->errmsg); errmsg = dlerror(); if (errmsg) { lib->errmsg = uv__strdup(errmsg); return -1; } else { lib->errmsg = NULL; return 0; } } libuv-v1.8.0/src/unix/freebsd.c000066400000000000000000000244241263361716600164000ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* VM_LOADAVG */ #include #include #include /* sysconf */ #include #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) #ifndef CPUSTATES # define CPUSTATES 5U #endif #ifndef CP_USER # define CP_USER 0 # define CP_NICE 1 # define CP_SYS 2 # define CP_IDLE 3 # define CP_INTR 4 #endif static char *process_title; int uv__platform_loop_init(uv_loop_t* loop) { return uv__kqueue_init(loop); } void uv__platform_loop_delete(uv_loop_t* loop) { } uint64_t uv__hrtime(uv_clocktype_t type) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); } #ifdef __DragonFly__ int uv_exepath(char* buffer, size_t* size) { char abspath[PATH_MAX * 2 + 1]; ssize_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath)); if (abspath_size < 0) return -errno; assert(abspath_size > 0); *size -= 1; if (*size > abspath_size) *size = abspath_size; memcpy(buffer, abspath, *size); buffer[*size] = '\0'; return 0; } #else int uv_exepath(char* buffer, size_t* size) { char abspath[PATH_MAX * 2 + 1]; int mib[4]; size_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; abspath_size = sizeof abspath; if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0)) return -errno; assert(abspath_size > 0); abspath_size -= 1; *size -= 1; if (*size > abspath_size) *size = abspath_size; memcpy(buffer, abspath, *size); buffer[*size] = '\0'; return 0; } #endif uint64_t uv_get_free_memory(void) { int freecount; size_t size = sizeof(freecount); if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0)) return -errno; return (uint64_t) freecount * sysconf(_SC_PAGESIZE); } uint64_t uv_get_total_memory(void) { unsigned long info; int which[] = {CTL_HW, HW_PHYSMEM}; size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; return (uint64_t) info; } void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } char** uv_setup_args(int argc, char** argv) { process_title = argc ? uv__strdup(argv[0]) : NULL; return argv; } int uv_set_process_title(const char* title) { int oid[4]; uv__free(process_title); process_title = uv__strdup(title); oid[0] = CTL_KERN; oid[1] = KERN_PROC; oid[2] = KERN_PROC_ARGS; oid[3] = getpid(); sysctl(oid, ARRAY_SIZE(oid), NULL, NULL, process_title, strlen(process_title) + 1); return 0; } int uv_get_process_title(char* buffer, size_t size) { if (process_title) { strncpy(buffer, process_title, size); } else { if (size > 0) { buffer[0] = '\0'; } } return 0; } int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc *kinfo = NULL; pid_t pid; int nprocs; size_t page_size = getpagesize(); pid = getpid(); kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); if (kd == NULL) goto error; kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs); if (kinfo == NULL) goto error; #ifdef __DragonFly__ *rss = kinfo->kp_vm_rssize * page_size; #else *rss = kinfo->ki_rssize * page_size; #endif kvm_close(kd); return 0; error: if (kd) kvm_close(kd); return -EPERM; } int uv_uptime(double* uptime) { int r; struct timespec sp; r = clock_gettime(CLOCK_MONOTONIC, &sp); if (r) return -errno; *uptime = sp.tv_sec; return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus, cur = 0; uv_cpu_info_t* cpu_info; const char* maxcpus_key; const char* cptimes_key; char model[512]; long* cp_times; int numcpus; size_t size; int i; #if defined(__DragonFly__) /* This is not quite correct but DragonFlyBSD doesn't seem to have anything * comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total, * not per CPU). At least this stops uv_cpu_info() from failing completely. */ maxcpus_key = "hw.ncpu"; cptimes_key = "kern.cp_time"; #else maxcpus_key = "kern.smp.maxcpus"; cptimes_key = "kern.cp_times"; #endif size = sizeof(model); if (sysctlbyname("hw.model", &model, &size, NULL, 0)) return -errno; size = sizeof(numcpus); if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0)) return -errno; *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) return -ENOMEM; *count = numcpus; size = sizeof(cpuspeed); if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) { SAVE_ERRNO(uv__free(*cpu_infos)); return -errno; } /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of * ncpu. */ size = sizeof(maxcpus); if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) { SAVE_ERRNO(uv__free(*cpu_infos)); return -errno; } size = maxcpus * CPUSTATES * sizeof(long); cp_times = uv__malloc(size); if (cp_times == NULL) { uv__free(*cpu_infos); return -ENOMEM; } if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) { SAVE_ERRNO(uv__free(cp_times)); SAVE_ERRNO(uv__free(*cpu_infos)); return -errno; } for (i = 0; i < numcpus; i++) { cpu_info = &(*cpu_infos)[i]; cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier; cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier; cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier; cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier; cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier; cpu_info->model = uv__strdup(model); cpu_info->speed = cpuspeed; cur+=CPUSTATES; } uv__free(cp_times); return 0; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs *addrs, *ent; uv_interface_address_t* address; int i; struct sockaddr_dl *sa_addr; if (getifaddrs(&addrs)) return -errno; *count = 0; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family == AF_LINK)) { continue; } (*count)++; } *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return -ENOMEM; } address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) continue; if (ent->ifa_addr == NULL) continue; /* * On FreeBSD getifaddrs returns information related to the raw underlying * devices. We're not interested in this information yet. */ if (ent->ifa_addr->sa_family == AF_LINK) continue; address->name = uv__strdup(ent->ifa_name); if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); } else { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } if (ent->ifa_netmask->sa_family == AF_INET6) { address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); } else { address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); } address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); address++; } /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != AF_LINK)) { continue; } address = *addresses; for (i = 0; i < (*count); i++) { if (strcmp(address->name, ent->ifa_name) == 0) { sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); } address++; } } freeifaddrs(addrs); return 0; } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; i++) { uv__free(addresses[i].name); } uv__free(addresses); } libuv-v1.8.0/src/unix/fs.c000066400000000000000000001022501263361716600153700ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* Caveat emptor: this file deviates from the libuv convention of returning * negated errno codes. Most uv_fs_*() functions map directly to the system * call of the same name. For more complex wrappers, it's easier to just * return -1 with errno set. The dispatcher in uv__fs_work() takes care of * getting the errno to the right place (req->result or as the return value.) */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # define HAVE_PREADV 1 #else # define HAVE_PREADV 0 #endif #if defined(__linux__) || defined(__sun) # include #endif #define INIT(subtype) \ do { \ req->type = UV_FS; \ if (cb != NULL) \ uv__req_init(loop, req, UV_FS); \ req->fs_type = UV_FS_ ## subtype; \ req->result = 0; \ req->ptr = NULL; \ req->loop = loop; \ req->path = NULL; \ req->new_path = NULL; \ req->cb = cb; \ } \ while (0) #define PATH \ do { \ assert(path != NULL); \ if (cb == NULL) { \ req->path = path; \ } else { \ req->path = uv__strdup(path); \ if (req->path == NULL) { \ uv__req_unregister(loop, req); \ return -ENOMEM; \ } \ } \ } \ while (0) #define PATH2 \ do { \ if (cb == NULL) { \ req->path = path; \ req->new_path = new_path; \ } else { \ size_t path_len; \ size_t new_path_len; \ path_len = strlen(path) + 1; \ new_path_len = strlen(new_path) + 1; \ req->path = uv__malloc(path_len + new_path_len); \ if (req->path == NULL) { \ uv__req_unregister(loop, req); \ return -ENOMEM; \ } \ req->new_path = req->path + path_len; \ memcpy((void*) req->path, path, path_len); \ memcpy((void*) req->new_path, new_path, new_path_len); \ } \ } \ while (0) #define POST \ do { \ if (cb != NULL) { \ uv__work_submit(loop, &req->work_req, uv__fs_work, uv__fs_done); \ return 0; \ } \ else { \ uv__fs_work(&req->work_req); \ return req->result; \ } \ } \ while (0) static ssize_t uv__fs_fdatasync(uv_fs_t* req) { #if defined(__linux__) || defined(__sun) || defined(__NetBSD__) return fdatasync(req->file); #elif defined(__APPLE__) && defined(F_FULLFSYNC) return fcntl(req->file, F_FULLFSYNC); #else return fsync(req->file); #endif } static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ static int no_utimesat; struct timespec ts[2]; struct timeval tv[2]; char path[sizeof("/proc/self/fd/") + 3 * sizeof(int)]; int r; if (no_utimesat) goto skip; ts[0].tv_sec = req->atime; ts[0].tv_nsec = (unsigned long)(req->atime * 1000000) % 1000000 * 1000; ts[1].tv_sec = req->mtime; ts[1].tv_nsec = (unsigned long)(req->mtime * 1000000) % 1000000 * 1000; r = uv__utimesat(req->file, NULL, ts, 0); if (r == 0) return r; if (errno != ENOSYS) return r; no_utimesat = 1; skip: tv[0].tv_sec = req->atime; tv[0].tv_usec = (unsigned long)(req->atime * 1000000) % 1000000; tv[1].tv_sec = req->mtime; tv[1].tv_usec = (unsigned long)(req->mtime * 1000000) % 1000000; snprintf(path, sizeof(path), "/proc/self/fd/%d", (int) req->file); r = utimes(path, tv); if (r == 0) return r; switch (errno) { case ENOENT: if (fcntl(req->file, F_GETFL) == -1 && errno == EBADF) break; /* Fall through. */ case EACCES: case ENOTDIR: errno = ENOSYS; break; } return r; #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) \ || defined(__sun) struct timeval tv[2]; tv[0].tv_sec = req->atime; tv[0].tv_usec = (unsigned long)(req->atime * 1000000) % 1000000; tv[1].tv_sec = req->mtime; tv[1].tv_usec = (unsigned long)(req->mtime * 1000000) % 1000000; # if defined(__sun) return futimesat(req->file, NULL, tv); # else return futimes(req->file, tv); # endif #else errno = ENOSYS; return -1; #endif } static ssize_t uv__fs_mkdtemp(uv_fs_t* req) { return mkdtemp((char*) req->path) ? 0 : -1; } static ssize_t uv__fs_open(uv_fs_t* req) { static int no_cloexec_support; int r; /* Try O_CLOEXEC before entering locks */ if (no_cloexec_support == 0) { #ifdef O_CLOEXEC r = open(req->path, req->flags | O_CLOEXEC, req->mode); if (r >= 0) return r; if (errno != EINVAL) return r; no_cloexec_support = 1; #endif /* O_CLOEXEC */ } if (req->cb != NULL) uv_rwlock_rdlock(&req->loop->cloexec_lock); r = open(req->path, req->flags, req->mode); /* In case of failure `uv__cloexec` will leave error in `errno`, * so it is enough to just set `r` to `-1`. */ if (r >= 0 && uv__cloexec(r, 1) != 0) { r = uv__close(r); if (r != 0 && r != -EINPROGRESS) abort(); r = -1; } if (req->cb != NULL) uv_rwlock_rdunlock(&req->loop->cloexec_lock); return r; } static ssize_t uv__fs_read(uv_fs_t* req) { #if defined(__linux__) static int no_preadv; #endif ssize_t result; #if defined(_AIX) struct stat buf; if(fstat(req->file, &buf)) return -1; if(S_ISDIR(buf.st_mode)) { errno = EISDIR; return -1; } #endif /* defined(_AIX) */ if (req->off < 0) { if (req->nbufs == 1) result = read(req->file, req->bufs[0].base, req->bufs[0].len); else result = readv(req->file, (struct iovec*) req->bufs, req->nbufs); } else { if (req->nbufs == 1) { result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off); goto done; } #if HAVE_PREADV result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); #else # if defined(__linux__) if (no_preadv) retry: # endif { off_t nread; size_t index; nread = 0; index = 0; result = 1; do { if (req->bufs[index].len > 0) { result = pread(req->file, req->bufs[index].base, req->bufs[index].len, req->off + nread); if (result > 0) nread += result; } index++; } while (index < req->nbufs && result > 0); if (nread > 0) result = nread; } # if defined(__linux__) else { result = uv__preadv(req->file, (struct iovec*)req->bufs, req->nbufs, req->off); if (result == -1 && errno == ENOSYS) { no_preadv = 1; goto retry; } } # endif #endif } done: return result; } #if defined(__OpenBSD__) || (defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8)) static int uv__fs_scandir_filter(uv__dirent_t* dent) { #else static int uv__fs_scandir_filter(const uv__dirent_t* dent) { #endif return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0; } static ssize_t uv__fs_scandir(uv_fs_t* req) { uv__dirent_t **dents; int saved_errno; int n; dents = NULL; n = scandir(req->path, &dents, uv__fs_scandir_filter, alphasort); /* NOTE: We will use nbufs as an index field */ req->nbufs = 0; if (n == 0) goto out; /* osx still needs to deallocate some memory */ else if (n == -1) return n; req->ptr = dents; return n; out: saved_errno = errno; if (dents != NULL) { int i; for (i = 0; i < n; i++) uv__free(dents[i]); uv__free(dents); } errno = saved_errno; req->ptr = NULL; return n; } static ssize_t uv__fs_pathmax_size(const char* path) { ssize_t pathmax; pathmax = pathconf(path, _PC_PATH_MAX); if (pathmax == -1) { #if defined(PATH_MAX) return PATH_MAX; #else return 4096; #endif } return pathmax; } static ssize_t uv__fs_readlink(uv_fs_t* req) { ssize_t len; char* buf; len = uv__fs_pathmax_size(req->path); buf = uv__malloc(len + 1); if (buf == NULL) { errno = ENOMEM; return -1; } len = readlink(req->path, buf, len); if (len == -1) { uv__free(buf); return -1; } buf[len] = '\0'; req->ptr = buf; return 0; } static ssize_t uv__fs_realpath(uv_fs_t* req) { ssize_t len; char* buf; len = uv__fs_pathmax_size(req->path); buf = uv__malloc(len + 1); if (buf == NULL) { errno = ENOMEM; return -1; } if (realpath(req->path, buf) == NULL) { uv__free(buf); return -1; } req->ptr = buf; return 0; } static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) { struct pollfd pfd; int use_pread; off_t offset; ssize_t nsent; ssize_t nread; ssize_t nwritten; size_t buflen; size_t len; ssize_t n; int in_fd; int out_fd; char buf[8192]; len = req->bufsml[0].len; in_fd = req->flags; out_fd = req->file; offset = req->off; use_pread = 1; /* Here are the rules regarding errors: * * 1. Read errors are reported only if nsent==0, otherwise we return nsent. * The user needs to know that some data has already been sent, to stop * them from sending it twice. * * 2. Write errors are always reported. Write errors are bad because they * mean data loss: we've read data but now we can't write it out. * * We try to use pread() and fall back to regular read() if the source fd * doesn't support positional reads, for example when it's a pipe fd. * * If we get EAGAIN when writing to the target fd, we poll() on it until * it becomes writable again. * * FIXME: If we get a write error when use_pread==1, it should be safe to * return the number of sent bytes instead of an error because pread() * is, in theory, idempotent. However, special files in /dev or /proc * may support pread() but not necessarily return the same data on * successive reads. * * FIXME: There is no way now to signal that we managed to send *some* data * before a write error. */ for (nsent = 0; (size_t) nsent < len; ) { buflen = len - nsent; if (buflen > sizeof(buf)) buflen = sizeof(buf); do if (use_pread) nread = pread(in_fd, buf, buflen, offset); else nread = read(in_fd, buf, buflen); while (nread == -1 && errno == EINTR); if (nread == 0) goto out; if (nread == -1) { if (use_pread && nsent == 0 && (errno == EIO || errno == ESPIPE)) { use_pread = 0; continue; } if (nsent == 0) nsent = -1; goto out; } for (nwritten = 0; nwritten < nread; ) { do n = write(out_fd, buf + nwritten, nread - nwritten); while (n == -1 && errno == EINTR); if (n != -1) { nwritten += n; continue; } if (errno != EAGAIN && errno != EWOULDBLOCK) { nsent = -1; goto out; } pfd.fd = out_fd; pfd.events = POLLOUT; pfd.revents = 0; do n = poll(&pfd, 1, -1); while (n == -1 && errno == EINTR); if (n == -1 || (pfd.revents & ~POLLOUT) != 0) { errno = EIO; nsent = -1; goto out; } } offset += nread; nsent += nread; } out: if (nsent != -1) req->off = offset; return nsent; } static ssize_t uv__fs_sendfile(uv_fs_t* req) { int in_fd; int out_fd; in_fd = req->flags; out_fd = req->file; #if defined(__linux__) || defined(__sun) { off_t off; ssize_t r; off = req->off; r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len); /* sendfile() on SunOS returns EINVAL if the target fd is not a socket but * it still writes out data. Fortunately, we can detect it by checking if * the offset has been updated. */ if (r != -1 || off > req->off) { r = off - req->off; req->off = off; return r; } if (errno == EINVAL || errno == EIO || errno == ENOTSOCK || errno == EXDEV) { errno = 0; return uv__fs_sendfile_emul(req); } return -1; } #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) { off_t len; ssize_t r; /* sendfile() on FreeBSD and Darwin returns EAGAIN if the target fd is in * non-blocking mode and not all data could be written. If a non-zero * number of bytes have been sent, we don't consider it an error. */ #if defined(__FreeBSD__) || defined(__DragonFly__) len = 0; r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0); #else /* The darwin sendfile takes len as an input for the length to send, * so make sure to initialize it with the caller's value. */ len = req->bufsml[0].len; r = sendfile(in_fd, out_fd, req->off, &len, NULL, 0); #endif /* * The man page for sendfile(2) on DragonFly states that `len` contains * a meaningful value ONLY in case of EAGAIN and EINTR. * Nothing is said about it's value in case of other errors, so better * not depend on the potential wrong assumption that is was not modified * by the syscall. */ if (r == 0 || ((errno == EAGAIN || errno == EINTR) && len != 0)) { req->off += len; return (ssize_t) len; } if (errno == EINVAL || errno == EIO || errno == ENOTSOCK || errno == EXDEV) { errno = 0; return uv__fs_sendfile_emul(req); } return -1; } #else /* Squelch compiler warnings. */ (void) &in_fd; (void) &out_fd; return uv__fs_sendfile_emul(req); #endif } static ssize_t uv__fs_utime(uv_fs_t* req) { struct utimbuf buf; buf.actime = req->atime; buf.modtime = req->mtime; return utime(req->path, &buf); /* TODO use utimes() where available */ } static ssize_t uv__fs_write(uv_fs_t* req) { #if defined(__linux__) static int no_pwritev; #endif ssize_t r; /* Serialize writes on OS X, concurrent write() and pwrite() calls result in * data loss. We can't use a per-file descriptor lock, the descriptor may be * a dup(). */ #if defined(__APPLE__) static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; if (pthread_mutex_lock(&lock)) abort(); #endif if (req->off < 0) { if (req->nbufs == 1) r = write(req->file, req->bufs[0].base, req->bufs[0].len); else r = writev(req->file, (struct iovec*) req->bufs, req->nbufs); } else { if (req->nbufs == 1) { r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); goto done; } #if HAVE_PREADV r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); #else # if defined(__linux__) if (no_pwritev) retry: # endif { off_t written; size_t index; written = 0; index = 0; r = 0; do { if (req->bufs[index].len > 0) { r = pwrite(req->file, req->bufs[index].base, req->bufs[index].len, req->off + written); if (r > 0) written += r; } index++; } while (index < req->nbufs && r >= 0); if (written > 0) r = written; } # if defined(__linux__) else { r = uv__pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); if (r == -1 && errno == ENOSYS) { no_pwritev = 1; goto retry; } } # endif #endif } done: #if defined(__APPLE__) if (pthread_mutex_unlock(&lock)) abort(); #endif return r; } static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_dev = src->st_dev; dst->st_mode = src->st_mode; dst->st_nlink = src->st_nlink; dst->st_uid = src->st_uid; dst->st_gid = src->st_gid; dst->st_rdev = src->st_rdev; dst->st_ino = src->st_ino; dst->st_size = src->st_size; dst->st_blksize = src->st_blksize; dst->st_blocks = src->st_blocks; #if defined(__APPLE__) dst->st_atim.tv_sec = src->st_atimespec.tv_sec; dst->st_atim.tv_nsec = src->st_atimespec.tv_nsec; dst->st_mtim.tv_sec = src->st_mtimespec.tv_sec; dst->st_mtim.tv_nsec = src->st_mtimespec.tv_nsec; dst->st_ctim.tv_sec = src->st_ctimespec.tv_sec; dst->st_ctim.tv_nsec = src->st_ctimespec.tv_nsec; dst->st_birthtim.tv_sec = src->st_birthtimespec.tv_sec; dst->st_birthtim.tv_nsec = src->st_birthtimespec.tv_nsec; dst->st_flags = src->st_flags; dst->st_gen = src->st_gen; #elif defined(__ANDROID__) dst->st_atim.tv_sec = src->st_atime; dst->st_atim.tv_nsec = src->st_atime_nsec; dst->st_mtim.tv_sec = src->st_mtime; dst->st_mtim.tv_nsec = src->st_mtime_nsec; dst->st_ctim.tv_sec = src->st_ctime; dst->st_ctim.tv_nsec = src->st_ctime_nsec; dst->st_birthtim.tv_sec = src->st_ctime; dst->st_birthtim.tv_nsec = src->st_ctime_nsec; dst->st_flags = 0; dst->st_gen = 0; #elif !defined(_AIX) && ( \ defined(_BSD_SOURCE) || \ defined(_SVID_SOURCE) || \ defined(_XOPEN_SOURCE) || \ defined(_DEFAULT_SOURCE)) dst->st_atim.tv_sec = src->st_atim.tv_sec; dst->st_atim.tv_nsec = src->st_atim.tv_nsec; dst->st_mtim.tv_sec = src->st_mtim.tv_sec; dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec; dst->st_ctim.tv_sec = src->st_ctim.tv_sec; dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec; # if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) dst->st_birthtim.tv_sec = src->st_birthtim.tv_sec; dst->st_birthtim.tv_nsec = src->st_birthtim.tv_nsec; dst->st_flags = src->st_flags; dst->st_gen = src->st_gen; # else dst->st_birthtim.tv_sec = src->st_ctim.tv_sec; dst->st_birthtim.tv_nsec = src->st_ctim.tv_nsec; dst->st_flags = 0; dst->st_gen = 0; # endif #else dst->st_atim.tv_sec = src->st_atime; dst->st_atim.tv_nsec = 0; dst->st_mtim.tv_sec = src->st_mtime; dst->st_mtim.tv_nsec = 0; dst->st_ctim.tv_sec = src->st_ctime; dst->st_ctim.tv_nsec = 0; dst->st_birthtim.tv_sec = src->st_ctime; dst->st_birthtim.tv_nsec = 0; dst->st_flags = 0; dst->st_gen = 0; #endif } static int uv__fs_stat(const char *path, uv_stat_t *buf) { struct stat pbuf; int ret; ret = stat(path, &pbuf); uv__to_stat(&pbuf, buf); return ret; } static int uv__fs_lstat(const char *path, uv_stat_t *buf) { struct stat pbuf; int ret; ret = lstat(path, &pbuf); uv__to_stat(&pbuf, buf); return ret; } static int uv__fs_fstat(int fd, uv_stat_t *buf) { struct stat pbuf; int ret; ret = fstat(fd, &pbuf); uv__to_stat(&pbuf, buf); return ret; } typedef ssize_t (*uv__fs_buf_iter_processor)(uv_fs_t* req); static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process) { unsigned int iovmax; unsigned int nbufs; uv_buf_t* bufs; ssize_t total; ssize_t result; iovmax = uv__getiovmax(); nbufs = req->nbufs; bufs = req->bufs; total = 0; while (nbufs > 0) { req->nbufs = nbufs; if (req->nbufs > iovmax) req->nbufs = iovmax; result = process(req); if (result <= 0) { if (total == 0) total = result; break; } if (req->off >= 0) req->off += result; req->bufs += req->nbufs; nbufs -= req->nbufs; total += result; } if (bufs != req->bufsml) uv__free(bufs); req->bufs = NULL; return total; } static void uv__fs_work(struct uv__work* w) { int retry_on_eintr; uv_fs_t* req; ssize_t r; req = container_of(w, uv_fs_t, work_req); retry_on_eintr = !(req->fs_type == UV_FS_CLOSE); do { errno = 0; #define X(type, action) \ case UV_FS_ ## type: \ r = action; \ break; switch (req->fs_type) { X(ACCESS, access(req->path, req->flags)); X(CHMOD, chmod(req->path, req->mode)); X(CHOWN, chown(req->path, req->uid, req->gid)); X(CLOSE, close(req->file)); X(FCHMOD, fchmod(req->file, req->mode)); X(FCHOWN, fchown(req->file, req->uid, req->gid)); X(FDATASYNC, uv__fs_fdatasync(req)); X(FSTAT, uv__fs_fstat(req->file, &req->statbuf)); X(FSYNC, fsync(req->file)); X(FTRUNCATE, ftruncate(req->file, req->off)); X(FUTIME, uv__fs_futime(req)); X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); X(LINK, link(req->path, req->new_path)); X(MKDIR, mkdir(req->path, req->mode)); X(MKDTEMP, uv__fs_mkdtemp(req)); X(OPEN, uv__fs_open(req)); X(READ, uv__fs_buf_iter(req, uv__fs_read)); X(SCANDIR, uv__fs_scandir(req)); X(READLINK, uv__fs_readlink(req)); X(REALPATH, uv__fs_realpath(req)); X(RENAME, rename(req->path, req->new_path)); X(RMDIR, rmdir(req->path)); X(SENDFILE, uv__fs_sendfile(req)); X(STAT, uv__fs_stat(req->path, &req->statbuf)); X(SYMLINK, symlink(req->path, req->new_path)); X(UNLINK, unlink(req->path)); X(UTIME, uv__fs_utime(req)); X(WRITE, uv__fs_buf_iter(req, uv__fs_write)); default: abort(); } #undef X } while (r == -1 && errno == EINTR && retry_on_eintr); if (r == -1) req->result = -errno; else req->result = r; if (r == 0 && (req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_FSTAT || req->fs_type == UV_FS_LSTAT)) { req->ptr = &req->statbuf; } } static void uv__fs_done(struct uv__work* w, int status) { uv_fs_t* req; req = container_of(w, uv_fs_t, work_req); uv__req_unregister(req->loop, req); if (status == -ECANCELED) { assert(req->result == 0); req->result = -ECANCELED; } req->cb(req); } int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { INIT(ACCESS); PATH; req->flags = flags; POST; } int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { INIT(CHMOD); PATH; req->mode = mode; POST; } int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { INIT(CHOWN); PATH; req->uid = uid; req->gid = gid; POST; } int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(CLOSE); req->file = file; POST; } int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb) { INIT(FCHMOD); req->file = file; req->mode = mode; POST; } int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { INIT(FCHOWN); req->file = file; req->uid = uid; req->gid = gid; POST; } int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FDATASYNC); req->file = file; POST; } int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FSTAT); req->file = file; POST; } int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FSYNC); req->file = file; POST; } int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t off, uv_fs_cb cb) { INIT(FTRUNCATE); req->file = file; req->off = off; POST; } int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb) { INIT(FUTIME); req->file = file; req->atime = atime; req->mtime = mtime; POST; } int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(LSTAT); PATH; POST; } int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { INIT(LINK); PATH2; POST; } int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { INIT(MKDIR); PATH; req->mode = mode; POST; } int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb) { INIT(MKDTEMP); req->path = uv__strdup(tpl); if (req->path == NULL) { if (cb != NULL) uv__req_unregister(loop, req); return -ENOMEM; } POST; } int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { INIT(OPEN); PATH; req->flags = flags; req->mode = mode; POST; } int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t off, uv_fs_cb cb) { if (bufs == NULL || nbufs == 0) return -EINVAL; INIT(READ); req->file = file; req->nbufs = nbufs; req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(*bufs)); if (req->bufs == NULL) { if (cb != NULL) uv__req_unregister(loop, req); return -ENOMEM; } memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); req->off = off; POST; } int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { INIT(SCANDIR); PATH; req->flags = flags; POST; } int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(READLINK); PATH; POST; } int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char * path, uv_fs_cb cb) { INIT(REALPATH); PATH; POST; } int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { INIT(RENAME); PATH2; POST; } int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(RMDIR); PATH; POST; } int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t off, size_t len, uv_fs_cb cb) { INIT(SENDFILE); req->flags = in_fd; /* hack */ req->file = out_fd; req->off = off; req->bufsml[0].len = len; POST; } int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(STAT); PATH; POST; } int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) { INIT(SYMLINK); PATH2; req->flags = flags; POST; } int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UNLINK); PATH; POST; } int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) { INIT(UTIME); PATH; req->atime = atime; req->mtime = mtime; POST; } int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t off, uv_fs_cb cb) { if (bufs == NULL || nbufs == 0) return -EINVAL; INIT(WRITE); req->file = file; req->nbufs = nbufs; req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(*bufs)); if (req->bufs == NULL) { if (cb != NULL) uv__req_unregister(loop, req); return -ENOMEM; } memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); req->off = off; POST; } void uv_fs_req_cleanup(uv_fs_t* req) { /* Only necessary for asychronous requests, i.e., requests with a callback. * Synchronous ones don't copy their arguments and have req->path and * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP is the * exception to the rule, it always allocates memory. */ if (req->path != NULL && (req->cb != NULL || req->fs_type == UV_FS_MKDTEMP)) uv__free((void*) req->path); /* Memory is shared with req->new_path. */ req->path = NULL; req->new_path = NULL; if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) uv__fs_scandir_cleanup(req); if (req->ptr != &req->statbuf) uv__free(req->ptr); req->ptr = NULL; } libuv-v1.8.0/src/unix/fsevents.c000066400000000000000000000667441263361716600166360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #if TARGET_OS_IPHONE /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */ int uv__fsevents_init(uv_fs_event_t* handle) { return 0; } int uv__fsevents_close(uv_fs_event_t* handle) { return 0; } void uv__fsevents_loop_delete(uv_loop_t* loop) { } #else /* TARGET_OS_IPHONE */ #include #include #include #include #include #include /* These are macros to avoid "initializer element is not constant" errors * with old versions of gcc. */ #define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod | \ kFSEventStreamEventFlagItemModified | \ kFSEventStreamEventFlagItemInodeMetaMod | \ kFSEventStreamEventFlagItemChangeOwner | \ kFSEventStreamEventFlagItemXattrMod) #define kFSEventsRenamed (kFSEventStreamEventFlagItemCreated | \ kFSEventStreamEventFlagItemRemoved | \ kFSEventStreamEventFlagItemRenamed) #define kFSEventsSystem (kFSEventStreamEventFlagUserDropped | \ kFSEventStreamEventFlagKernelDropped | \ kFSEventStreamEventFlagEventIdsWrapped | \ kFSEventStreamEventFlagHistoryDone | \ kFSEventStreamEventFlagMount | \ kFSEventStreamEventFlagUnmount | \ kFSEventStreamEventFlagRootChanged) typedef struct uv__fsevents_event_s uv__fsevents_event_t; typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; typedef struct uv__cf_loop_state_s uv__cf_loop_state_t; enum uv__cf_loop_signal_type_e { kUVCFLoopSignalRegular, kUVCFLoopSignalClosing }; typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t; struct uv__cf_loop_signal_s { QUEUE member; uv_fs_event_t* handle; uv__cf_loop_signal_type_t type; }; struct uv__fsevents_event_s { QUEUE member; int events; char path[1]; }; struct uv__cf_loop_state_s { CFRunLoopRef loop; CFRunLoopSourceRef signal_source; int fsevent_need_reschedule; FSEventStreamRef fsevent_stream; uv_sem_t fsevent_sem; uv_mutex_t fsevent_mutex; void* fsevent_handles[2]; unsigned int fsevent_handle_count; }; /* Forward declarations */ static void uv__cf_loop_cb(void* arg); static void* uv__cf_loop_runner(void* arg); static int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle, uv__cf_loop_signal_type_t type); /* Lazy-loaded by uv__fsevents_global_init(). */ static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef, const void**, CFIndex, const CFArrayCallBacks*); static void (*pCFRelease)(CFTypeRef); static void (*pCFRunLoopAddSource)(CFRunLoopRef, CFRunLoopSourceRef, CFStringRef); static CFRunLoopRef (*pCFRunLoopGetCurrent)(void); static void (*pCFRunLoopRemoveSource)(CFRunLoopRef, CFRunLoopSourceRef, CFStringRef); static void (*pCFRunLoopRun)(void); static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef, CFIndex, CFRunLoopSourceContext*); static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef); static void (*pCFRunLoopStop)(CFRunLoopRef); static void (*pCFRunLoopWakeUp)(CFRunLoopRef); static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)( CFAllocatorRef, const char*); static CFStringEncoding (*pCFStringGetSystemEncoding)(void); static CFStringRef (*pkCFRunLoopDefaultMode); static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef, FSEventStreamCallback, FSEventStreamContext*, CFArrayRef, FSEventStreamEventId, CFTimeInterval, FSEventStreamCreateFlags); static void (*pFSEventStreamFlushSync)(FSEventStreamRef); static void (*pFSEventStreamInvalidate)(FSEventStreamRef); static void (*pFSEventStreamRelease)(FSEventStreamRef); static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef, CFRunLoopRef, CFStringRef); static Boolean (*pFSEventStreamStart)(FSEventStreamRef); static void (*pFSEventStreamStop)(FSEventStreamRef); #define UV__FSEVENTS_PROCESS(handle, block) \ do { \ QUEUE events; \ QUEUE* q; \ uv__fsevents_event_t* event; \ int err; \ uv_mutex_lock(&(handle)->cf_mutex); \ /* Split-off all events and empty original queue */ \ QUEUE_MOVE(&(handle)->cf_events, &events); \ /* Get error (if any) and zero original one */ \ err = (handle)->cf_error; \ (handle)->cf_error = 0; \ uv_mutex_unlock(&(handle)->cf_mutex); \ /* Loop through events, deallocating each after processing */ \ while (!QUEUE_EMPTY(&events)) { \ q = QUEUE_HEAD(&events); \ event = QUEUE_DATA(q, uv__fsevents_event_t, member); \ QUEUE_REMOVE(q); \ /* NOTE: Checking uv__is_active() is required here, because handle \ * callback may close handle and invoking it after it will lead to \ * incorrect behaviour */ \ if (!uv__is_closing((handle)) && uv__is_active((handle))) \ block \ /* Free allocated data */ \ uv__free(event); \ } \ if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \ (handle)->cb((handle), NULL, 0, err); \ } while (0) /* Runs in UV loop's thread, when there're events to report to handle */ static void uv__fsevents_cb(uv_async_t* cb) { uv_fs_event_t* handle; handle = cb->data; UV__FSEVENTS_PROCESS(handle, { handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0); }); } /* Runs in CF thread, pushed event into handle's event list */ static void uv__fsevents_push_event(uv_fs_event_t* handle, QUEUE* events, int err) { assert(events != NULL || err != 0); uv_mutex_lock(&handle->cf_mutex); /* Concatenate two queues */ if (events != NULL) QUEUE_ADD(&handle->cf_events, events); /* Propagate error */ if (err != 0) handle->cf_error = err; uv_mutex_unlock(&handle->cf_mutex); uv_async_send(handle->cf_cb); } /* Runs in CF thread, when there're events in FSEventStream */ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, void* info, size_t numEvents, void* eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) { size_t i; int len; char** paths; char* path; char* pos; uv_fs_event_t* handle; QUEUE* q; uv_loop_t* loop; uv__cf_loop_state_t* state; uv__fsevents_event_t* event; QUEUE head; loop = info; state = loop->cf_state; assert(state != NULL); paths = eventPaths; /* For each handle */ uv_mutex_lock(&state->fsevent_mutex); QUEUE_FOREACH(q, &state->fsevent_handles) { handle = QUEUE_DATA(q, uv_fs_event_t, cf_member); QUEUE_INIT(&head); /* Process and filter out events */ for (i = 0; i < numEvents; i++) { /* Ignore system events */ if (eventFlags[i] & kFSEventsSystem) continue; path = paths[i]; len = strlen(path); /* Filter out paths that are outside handle's request */ if (strncmp(path, handle->realpath, handle->realpath_len) != 0) continue; if (handle->realpath_len > 1 || *handle->realpath != '/') { path += handle->realpath_len; len -= handle->realpath_len; /* Skip forward slash */ if (*path != '\0') { path++; len--; } } #ifdef MAC_OS_X_VERSION_10_7 /* Ignore events with path equal to directory itself */ if (len == 0) continue; #endif /* MAC_OS_X_VERSION_10_7 */ /* Do not emit events from subdirectories (without option set) */ if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != 0) { pos = strchr(path + 1, '/'); if (pos != NULL) continue; } #ifndef MAC_OS_X_VERSION_10_7 path = ""; len = 0; #endif /* MAC_OS_X_VERSION_10_7 */ event = uv__malloc(sizeof(*event) + len); if (event == NULL) break; memset(event, 0, sizeof(*event)); memcpy(event->path, path, len + 1); if ((eventFlags[i] & kFSEventsModified) != 0 && (eventFlags[i] & kFSEventsRenamed) == 0) event->events = UV_CHANGE; else event->events = UV_RENAME; QUEUE_INSERT_TAIL(&head, &event->member); } if (!QUEUE_EMPTY(&head)) uv__fsevents_push_event(handle, &head, 0); } uv_mutex_unlock(&state->fsevent_mutex); } /* Runs in CF thread */ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { uv__cf_loop_state_t* state; FSEventStreamContext ctx; FSEventStreamRef ref; CFAbsoluteTime latency; FSEventStreamCreateFlags flags; /* Initialize context */ ctx.version = 0; ctx.info = loop; ctx.retain = NULL; ctx.release = NULL; ctx.copyDescription = NULL; latency = 0.05; /* Explanation of selected flags: * 1. NoDefer - without this flag, events that are happening continuously * (i.e. each event is happening after time interval less than `latency`, * counted from previous event), will be deferred and passed to callback * once they'll either fill whole OS buffer, or when this continuous stream * will stop (i.e. there'll be delay between events, bigger than * `latency`). * Specifying this flag will invoke callback after `latency` time passed * since event. * 2. FileEvents - fire callback for file changes too (by default it is firing * it only for directory changes). */ flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents; /* * NOTE: It might sound like a good idea to remember last seen StreamEventId, * but in reality one dir might have last StreamEventId less than, the other, * that is being watched now. Which will cause FSEventStream API to report * changes to files from the past. */ ref = pFSEventStreamCreate(NULL, &uv__fsevents_event_cb, &ctx, paths, kFSEventStreamEventIdSinceNow, latency, flags); assert(ref != NULL); state = loop->cf_state; pFSEventStreamScheduleWithRunLoop(ref, state->loop, *pkCFRunLoopDefaultMode); if (!pFSEventStreamStart(ref)) { pFSEventStreamInvalidate(ref); pFSEventStreamRelease(ref); return -EMFILE; } state->fsevent_stream = ref; return 0; } /* Runs in CF thread */ static void uv__fsevents_destroy_stream(uv_loop_t* loop) { uv__cf_loop_state_t* state; state = loop->cf_state; if (state->fsevent_stream == NULL) return; /* Flush all accumulated events */ pFSEventStreamFlushSync(state->fsevent_stream); /* Stop emitting events */ pFSEventStreamStop(state->fsevent_stream); /* Release stream */ pFSEventStreamInvalidate(state->fsevent_stream); pFSEventStreamRelease(state->fsevent_stream); state->fsevent_stream = NULL; } /* Runs in CF thread, when there're new fsevent handles to add to stream */ static void uv__fsevents_reschedule(uv_fs_event_t* handle, uv__cf_loop_signal_type_t type) { uv__cf_loop_state_t* state; QUEUE* q; uv_fs_event_t* curr; CFArrayRef cf_paths; CFStringRef* paths; unsigned int i; int err; unsigned int path_count; state = handle->loop->cf_state; paths = NULL; cf_paths = NULL; err = 0; /* NOTE: `i` is used in deallocation loop below */ i = 0; /* Optimization to prevent O(n^2) time spent when starting to watch * many files simultaneously */ uv_mutex_lock(&state->fsevent_mutex); if (state->fsevent_need_reschedule == 0) { uv_mutex_unlock(&state->fsevent_mutex); goto final; } state->fsevent_need_reschedule = 0; uv_mutex_unlock(&state->fsevent_mutex); /* Destroy previous FSEventStream */ uv__fsevents_destroy_stream(handle->loop); /* Any failure below will be a memory failure */ err = -ENOMEM; /* Create list of all watched paths */ uv_mutex_lock(&state->fsevent_mutex); path_count = state->fsevent_handle_count; if (path_count != 0) { paths = uv__malloc(sizeof(*paths) * path_count); if (paths == NULL) { uv_mutex_unlock(&state->fsevent_mutex); goto final; } q = &state->fsevent_handles; for (; i < path_count; i++) { q = QUEUE_NEXT(q); assert(q != &state->fsevent_handles); curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); assert(curr->realpath != NULL); paths[i] = pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath); if (paths[i] == NULL) { uv_mutex_unlock(&state->fsevent_mutex); goto final; } } } uv_mutex_unlock(&state->fsevent_mutex); err = 0; if (path_count != 0) { /* Create new FSEventStream */ cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL); if (cf_paths == NULL) { err = -ENOMEM; goto final; } err = uv__fsevents_create_stream(handle->loop, cf_paths); } final: /* Deallocate all paths in case of failure */ if (err != 0) { if (cf_paths == NULL) { while (i != 0) pCFRelease(paths[--i]); uv__free(paths); } else { /* CFArray takes ownership of both strings and original C-array */ pCFRelease(cf_paths); } /* Broadcast error to all handles */ uv_mutex_lock(&state->fsevent_mutex); QUEUE_FOREACH(q, &state->fsevent_handles) { curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); uv__fsevents_push_event(curr, NULL, err); } uv_mutex_unlock(&state->fsevent_mutex); } /* * Main thread will block until the removal of handle from the list, * we must tell it when we're ready. * * NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close` */ if (type == kUVCFLoopSignalClosing) uv_sem_post(&state->fsevent_sem); } static int uv__fsevents_global_init(void) { static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER; static void* core_foundation_handle; static void* core_services_handle; int err; err = 0; pthread_mutex_lock(&global_init_mutex); if (core_foundation_handle != NULL) goto out; /* The libraries are never unloaded because we currently don't have a good * mechanism for keeping a reference count. It's unlikely to be an issue * but if it ever becomes one, we can turn the dynamic library handles into * per-event loop properties and have the dynamic linker keep track for us. */ err = -ENOSYS; core_foundation_handle = dlopen("/System/Library/Frameworks/" "CoreFoundation.framework/" "Versions/A/CoreFoundation", RTLD_LAZY | RTLD_LOCAL); if (core_foundation_handle == NULL) goto out; core_services_handle = dlopen("/System/Library/Frameworks/" "CoreServices.framework/" "Versions/A/CoreServices", RTLD_LAZY | RTLD_LOCAL); if (core_services_handle == NULL) goto out; err = -ENOENT; #define V(handle, symbol) \ do { \ *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ if (p ## symbol == NULL) \ goto out; \ } \ while (0) V(core_foundation_handle, CFArrayCreate); V(core_foundation_handle, CFRelease); V(core_foundation_handle, CFRunLoopAddSource); V(core_foundation_handle, CFRunLoopGetCurrent); V(core_foundation_handle, CFRunLoopRemoveSource); V(core_foundation_handle, CFRunLoopRun); V(core_foundation_handle, CFRunLoopSourceCreate); V(core_foundation_handle, CFRunLoopSourceSignal); V(core_foundation_handle, CFRunLoopStop); V(core_foundation_handle, CFRunLoopWakeUp); V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation); V(core_foundation_handle, CFStringGetSystemEncoding); V(core_foundation_handle, kCFRunLoopDefaultMode); V(core_services_handle, FSEventStreamCreate); V(core_services_handle, FSEventStreamFlushSync); V(core_services_handle, FSEventStreamInvalidate); V(core_services_handle, FSEventStreamRelease); V(core_services_handle, FSEventStreamScheduleWithRunLoop); V(core_services_handle, FSEventStreamStart); V(core_services_handle, FSEventStreamStop); #undef V err = 0; out: if (err && core_services_handle != NULL) { dlclose(core_services_handle); core_services_handle = NULL; } if (err && core_foundation_handle != NULL) { dlclose(core_foundation_handle); core_foundation_handle = NULL; } pthread_mutex_unlock(&global_init_mutex); return err; } /* Runs in UV loop */ static int uv__fsevents_loop_init(uv_loop_t* loop) { CFRunLoopSourceContext ctx; uv__cf_loop_state_t* state; pthread_attr_t attr_storage; pthread_attr_t* attr; int err; if (loop->cf_state != NULL) return 0; err = uv__fsevents_global_init(); if (err) return err; state = uv__calloc(1, sizeof(*state)); if (state == NULL) return -ENOMEM; err = uv_mutex_init(&loop->cf_mutex); if (err) goto fail_mutex_init; err = uv_sem_init(&loop->cf_sem, 0); if (err) goto fail_sem_init; QUEUE_INIT(&loop->cf_signals); err = uv_sem_init(&state->fsevent_sem, 0); if (err) goto fail_fsevent_sem_init; err = uv_mutex_init(&state->fsevent_mutex); if (err) goto fail_fsevent_mutex_init; QUEUE_INIT(&state->fsevent_handles); state->fsevent_need_reschedule = 0; state->fsevent_handle_count = 0; memset(&ctx, 0, sizeof(ctx)); ctx.info = loop; ctx.perform = uv__cf_loop_cb; state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx); if (state->signal_source == NULL) { err = -ENOMEM; goto fail_signal_source_create; } /* In the unlikely event that pthread_attr_init() fails, create the thread * with the default stack size. We'll use a little more address space but * that in itself is not a fatal error. */ attr = &attr_storage; if (pthread_attr_init(attr)) attr = NULL; if (attr != NULL) if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN)) abort(); loop->cf_state = state; /* uv_thread_t is an alias for pthread_t. */ err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop); if (attr != NULL) pthread_attr_destroy(attr); if (err) goto fail_thread_create; /* Synchronize threads */ uv_sem_wait(&loop->cf_sem); return 0; fail_thread_create: loop->cf_state = NULL; fail_signal_source_create: uv_mutex_destroy(&state->fsevent_mutex); fail_fsevent_mutex_init: uv_sem_destroy(&state->fsevent_sem); fail_fsevent_sem_init: uv_sem_destroy(&loop->cf_sem); fail_sem_init: uv_mutex_destroy(&loop->cf_mutex); fail_mutex_init: uv__free(state); return err; } /* Runs in UV loop */ void uv__fsevents_loop_delete(uv_loop_t* loop) { uv__cf_loop_signal_t* s; uv__cf_loop_state_t* state; QUEUE* q; if (loop->cf_state == NULL) return; if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0) abort(); uv_thread_join(&loop->cf_thread); uv_sem_destroy(&loop->cf_sem); uv_mutex_destroy(&loop->cf_mutex); /* Free any remaining data */ while (!QUEUE_EMPTY(&loop->cf_signals)) { q = QUEUE_HEAD(&loop->cf_signals); s = QUEUE_DATA(q, uv__cf_loop_signal_t, member); QUEUE_REMOVE(q); uv__free(s); } /* Destroy state */ state = loop->cf_state; uv_sem_destroy(&state->fsevent_sem); uv_mutex_destroy(&state->fsevent_mutex); pCFRelease(state->signal_source); uv__free(state); loop->cf_state = NULL; } /* Runs in CF thread. This is the CF loop's body */ static void* uv__cf_loop_runner(void* arg) { uv_loop_t* loop; uv__cf_loop_state_t* state; loop = arg; state = loop->cf_state; state->loop = pCFRunLoopGetCurrent(); pCFRunLoopAddSource(state->loop, state->signal_source, *pkCFRunLoopDefaultMode); uv_sem_post(&loop->cf_sem); pCFRunLoopRun(); pCFRunLoopRemoveSource(state->loop, state->signal_source, *pkCFRunLoopDefaultMode); return NULL; } /* Runs in CF thread, executed after `uv__cf_loop_signal()` */ static void uv__cf_loop_cb(void* arg) { uv_loop_t* loop; uv__cf_loop_state_t* state; QUEUE* item; QUEUE split_head; uv__cf_loop_signal_t* s; loop = arg; state = loop->cf_state; uv_mutex_lock(&loop->cf_mutex); QUEUE_MOVE(&loop->cf_signals, &split_head); uv_mutex_unlock(&loop->cf_mutex); while (!QUEUE_EMPTY(&split_head)) { item = QUEUE_HEAD(&split_head); QUEUE_REMOVE(item); s = QUEUE_DATA(item, uv__cf_loop_signal_t, member); /* This was a termination signal */ if (s->handle == NULL) pCFRunLoopStop(state->loop); else uv__fsevents_reschedule(s->handle, s->type); uv__free(s); } } /* Runs in UV loop to notify CF thread */ int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle, uv__cf_loop_signal_type_t type) { uv__cf_loop_signal_t* item; uv__cf_loop_state_t* state; item = uv__malloc(sizeof(*item)); if (item == NULL) return -ENOMEM; item->handle = handle; item->type = type; uv_mutex_lock(&loop->cf_mutex); QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member); uv_mutex_unlock(&loop->cf_mutex); state = loop->cf_state; assert(state != NULL); pCFRunLoopSourceSignal(state->signal_source); pCFRunLoopWakeUp(state->loop); return 0; } /* Runs in UV loop to initialize handle */ int uv__fsevents_init(uv_fs_event_t* handle) { int err; uv__cf_loop_state_t* state; err = uv__fsevents_loop_init(handle->loop); if (err) return err; /* Get absolute path to file */ handle->realpath = realpath(handle->path, NULL); if (handle->realpath == NULL) return -errno; handle->realpath_len = strlen(handle->realpath); /* Initialize event queue */ QUEUE_INIT(&handle->cf_events); handle->cf_error = 0; /* * Events will occur in other thread. * Initialize callback for getting them back into event loop's thread */ handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb)); if (handle->cf_cb == NULL) { err = -ENOMEM; goto fail_cf_cb_malloc; } handle->cf_cb->data = handle; uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb); handle->cf_cb->flags |= UV__HANDLE_INTERNAL; uv_unref((uv_handle_t*) handle->cf_cb); err = uv_mutex_init(&handle->cf_mutex); if (err) goto fail_cf_mutex_init; /* Insert handle into the list */ state = handle->loop->cf_state; uv_mutex_lock(&state->fsevent_mutex); QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member); state->fsevent_handle_count++; state->fsevent_need_reschedule = 1; uv_mutex_unlock(&state->fsevent_mutex); /* Reschedule FSEventStream */ assert(handle != NULL); err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular); if (err) goto fail_loop_signal; return 0; fail_loop_signal: uv_mutex_destroy(&handle->cf_mutex); fail_cf_mutex_init: uv__free(handle->cf_cb); handle->cf_cb = NULL; fail_cf_cb_malloc: uv__free(handle->realpath); handle->realpath = NULL; handle->realpath_len = 0; return err; } /* Runs in UV loop to de-initialize handle */ int uv__fsevents_close(uv_fs_event_t* handle) { int err; uv__cf_loop_state_t* state; if (handle->cf_cb == NULL) return -EINVAL; /* Remove handle from the list */ state = handle->loop->cf_state; uv_mutex_lock(&state->fsevent_mutex); QUEUE_REMOVE(&handle->cf_member); state->fsevent_handle_count--; state->fsevent_need_reschedule = 1; uv_mutex_unlock(&state->fsevent_mutex); /* Reschedule FSEventStream */ assert(handle != NULL); err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing); if (err) return -err; /* Wait for deinitialization */ uv_sem_wait(&state->fsevent_sem); uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free); handle->cf_cb = NULL; /* Free data in queue */ UV__FSEVENTS_PROCESS(handle, { /* NOP */ }); uv_mutex_destroy(&handle->cf_mutex); uv__free(handle->realpath); handle->realpath = NULL; handle->realpath_len = 0; return 0; } #endif /* TARGET_OS_IPHONE */ libuv-v1.8.0/src/unix/getaddrinfo.c000066400000000000000000000123701263361716600172510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* Expose glibc-specific EAI_* error codes. Needs to be defined before we * include any headers. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include "uv.h" #include "internal.h" #include #include /* NULL */ #include #include /* EAI_* constants. */ #include int uv__getaddrinfo_translate_error(int sys_err) { switch (sys_err) { case 0: return 0; #if defined(EAI_ADDRFAMILY) case EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY; #endif #if defined(EAI_AGAIN) case EAI_AGAIN: return UV_EAI_AGAIN; #endif #if defined(EAI_BADFLAGS) case EAI_BADFLAGS: return UV_EAI_BADFLAGS; #endif #if defined(EAI_BADHINTS) case EAI_BADHINTS: return UV_EAI_BADHINTS; #endif #if defined(EAI_CANCELED) case EAI_CANCELED: return UV_EAI_CANCELED; #endif #if defined(EAI_FAIL) case EAI_FAIL: return UV_EAI_FAIL; #endif #if defined(EAI_FAMILY) case EAI_FAMILY: return UV_EAI_FAMILY; #endif #if defined(EAI_MEMORY) case EAI_MEMORY: return UV_EAI_MEMORY; #endif #if defined(EAI_NODATA) case EAI_NODATA: return UV_EAI_NODATA; #endif #if defined(EAI_NONAME) # if !defined(EAI_NODATA) || EAI_NODATA != EAI_NONAME case EAI_NONAME: return UV_EAI_NONAME; # endif #endif #if defined(EAI_OVERFLOW) case EAI_OVERFLOW: return UV_EAI_OVERFLOW; #endif #if defined(EAI_PROTOCOL) case EAI_PROTOCOL: return UV_EAI_PROTOCOL; #endif #if defined(EAI_SERVICE) case EAI_SERVICE: return UV_EAI_SERVICE; #endif #if defined(EAI_SOCKTYPE) case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE; #endif #if defined(EAI_SYSTEM) case EAI_SYSTEM: return -errno; #endif } assert(!"unknown EAI_* error code"); abort(); return 0; /* Pacify compiler. */ } static void uv__getaddrinfo_work(struct uv__work* w) { uv_getaddrinfo_t* req; int err; req = container_of(w, uv_getaddrinfo_t, work_req); err = getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo); req->retcode = uv__getaddrinfo_translate_error(err); } static void uv__getaddrinfo_done(struct uv__work* w, int status) { uv_getaddrinfo_t* req; req = container_of(w, uv_getaddrinfo_t, work_req); uv__req_unregister(req->loop, req); /* See initialization in uv_getaddrinfo(). */ if (req->hints) uv__free(req->hints); else if (req->service) uv__free(req->service); else if (req->hostname) uv__free(req->hostname); else assert(0); req->hints = NULL; req->service = NULL; req->hostname = NULL; if (status == -ECANCELED) { assert(req->retcode == 0); req->retcode = UV_EAI_CANCELED; } if (req->cb) req->cb(req, req->retcode, req->addrinfo); } int uv_getaddrinfo(uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb cb, const char* hostname, const char* service, const struct addrinfo* hints) { size_t hostname_len; size_t service_len; size_t hints_len; size_t len; char* buf; if (req == NULL || (hostname == NULL && service == NULL)) return -EINVAL; hostname_len = hostname ? strlen(hostname) + 1 : 0; service_len = service ? strlen(service) + 1 : 0; hints_len = hints ? sizeof(*hints) : 0; buf = uv__malloc(hostname_len + service_len + hints_len); if (buf == NULL) return -ENOMEM; uv__req_init(loop, req, UV_GETADDRINFO); req->loop = loop; req->cb = cb; req->addrinfo = NULL; req->hints = NULL; req->service = NULL; req->hostname = NULL; req->retcode = 0; /* order matters, see uv_getaddrinfo_done() */ len = 0; if (hints) { req->hints = memcpy(buf + len, hints, sizeof(*hints)); len += sizeof(*hints); } if (service) { req->service = memcpy(buf + len, service, service_len); len += service_len; } if (hostname) req->hostname = memcpy(buf + len, hostname, hostname_len); if (cb) { uv__work_submit(loop, &req->work_req, uv__getaddrinfo_work, uv__getaddrinfo_done); return 0; } else { uv__getaddrinfo_work(&req->work_req); uv__getaddrinfo_done(&req->work_req, 0); return req->retcode; } } void uv_freeaddrinfo(struct addrinfo* ai) { if (ai) freeaddrinfo(ai); } libuv-v1.8.0/src/unix/getnameinfo.c000066400000000000000000000070401263361716600172550ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "uv.h" #include "internal.h" static void uv__getnameinfo_work(struct uv__work* w) { uv_getnameinfo_t* req; int err; socklen_t salen; req = container_of(w, uv_getnameinfo_t, work_req); if (req->storage.ss_family == AF_INET) salen = sizeof(struct sockaddr_in); else if (req->storage.ss_family == AF_INET6) salen = sizeof(struct sockaddr_in6); else abort(); err = getnameinfo((struct sockaddr*) &req->storage, salen, req->host, sizeof(req->host), req->service, sizeof(req->service), req->flags); req->retcode = uv__getaddrinfo_translate_error(err); } static void uv__getnameinfo_done(struct uv__work* w, int status) { uv_getnameinfo_t* req; char* host; char* service; req = container_of(w, uv_getnameinfo_t, work_req); uv__req_unregister(req->loop, req); host = service = NULL; if (status == -ECANCELED) { assert(req->retcode == 0); req->retcode = UV_EAI_CANCELED; } else if (req->retcode == 0) { host = req->host; service = req->service; } if (req->getnameinfo_cb) req->getnameinfo_cb(req, req->retcode, host, service); } /* * Entry point for getnameinfo * return 0 if a callback will be made * return error code if validation fails */ int uv_getnameinfo(uv_loop_t* loop, uv_getnameinfo_t* req, uv_getnameinfo_cb getnameinfo_cb, const struct sockaddr* addr, int flags) { if (req == NULL || addr == NULL) return UV_EINVAL; if (addr->sa_family == AF_INET) { memcpy(&req->storage, addr, sizeof(struct sockaddr_in)); } else if (addr->sa_family == AF_INET6) { memcpy(&req->storage, addr, sizeof(struct sockaddr_in6)); } else { return UV_EINVAL; } uv__req_init(loop, (uv_req_t*)req, UV_GETNAMEINFO); req->getnameinfo_cb = getnameinfo_cb; req->flags = flags; req->type = UV_GETNAMEINFO; req->loop = loop; req->retcode = 0; if (getnameinfo_cb) { uv__work_submit(loop, &req->work_req, uv__getnameinfo_work, uv__getnameinfo_done); return 0; } else { uv__getnameinfo_work(&req->work_req); uv__getnameinfo_done(&req->work_req, 0); return req->retcode; } } libuv-v1.8.0/src/unix/internal.h000066400000000000000000000255771263361716600166210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_UNIX_INTERNAL_H_ #define UV_UNIX_INTERNAL_H_ #include "uv-common.h" #include #include /* abort */ #include /* strrchr */ #include /* O_CLOEXEC, may be */ #if defined(__STRICT_ANSI__) # define inline __inline #endif #if defined(__linux__) # include "linux-syscalls.h" #endif /* __linux__ */ #if defined(__sun) # include # include #endif /* __sun */ #if defined(_AIX) #define reqevents events #define rtnevents revents #include #endif /* _AIX */ #if defined(__APPLE__) && !TARGET_OS_IPHONE # include #endif #define ACCESS_ONCE(type, var) \ (*(volatile type*) &(var)) #define ROUND_UP(a, b) \ ((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a)) #define UNREACHABLE() \ do { \ assert(0 && "unreachable code"); \ abort(); \ } \ while (0) #define SAVE_ERRNO(block) \ do { \ int _saved_errno = errno; \ do { block; } while (0); \ errno = _saved_errno; \ } \ while (0) /* The __clang__ and __INTEL_COMPILER checks are superfluous because they * define __GNUC__. They are here to convey to you, dear reader, that these * macros are enabled when compiling with clang or icc. */ #if defined(__clang__) || \ defined(__GNUC__) || \ defined(__INTEL_COMPILER) || \ defined(__SUNPRO_C) # define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration # define UV_UNUSED(declaration) __attribute__((unused)) declaration #else # define UV_DESTRUCTOR(declaration) declaration # define UV_UNUSED(declaration) declaration #endif #if defined(__linux__) # define UV__POLLIN UV__EPOLLIN # define UV__POLLOUT UV__EPOLLOUT # define UV__POLLERR UV__EPOLLERR # define UV__POLLHUP UV__EPOLLHUP #endif #if defined(__sun) || defined(_AIX) # define UV__POLLIN POLLIN # define UV__POLLOUT POLLOUT # define UV__POLLERR POLLERR # define UV__POLLHUP POLLHUP #endif #ifndef UV__POLLIN # define UV__POLLIN 1 #endif #ifndef UV__POLLOUT # define UV__POLLOUT 2 #endif #ifndef UV__POLLERR # define UV__POLLERR 4 #endif #ifndef UV__POLLHUP # define UV__POLLHUP 8 #endif #if !defined(O_CLOEXEC) && defined(__FreeBSD__) /* * It may be that we are just missing `__POSIX_VISIBLE >= 200809`. * Try using fixed value const and give up, if it doesn't work */ # define O_CLOEXEC 0x00100000 #endif typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t; /* handle flags */ enum { UV_CLOSING = 0x01, /* uv_close() called but not finished. */ UV_CLOSED = 0x02, /* close(2) finished. */ UV_STREAM_READING = 0x04, /* uv_read_start() called. */ UV_STREAM_SHUTTING = 0x08, /* uv_shutdown() called but not complete. */ UV_STREAM_SHUT = 0x10, /* Write side closed. */ UV_STREAM_READABLE = 0x20, /* The stream is readable */ UV_STREAM_WRITABLE = 0x40, /* The stream is writable */ UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */ UV_STREAM_READ_PARTIAL = 0x100, /* read(2) read less than requested. */ UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */ UV_TCP_NODELAY = 0x400, /* Disable Nagle. */ UV_TCP_KEEPALIVE = 0x800, /* Turn on keep-alive. */ UV_TCP_SINGLE_ACCEPT = 0x1000, /* Only accept() when idle. */ UV_HANDLE_IPV6 = 0x10000, /* Handle is bound to a IPv6 socket. */ UV_UDP_PROCESSING = 0x20000 /* Handle is running the send callback queue. */ }; /* loop flags */ enum { UV_LOOP_BLOCK_SIGPROF = 1 }; typedef enum { UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */ UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */ } uv_clocktype_t; struct uv__stream_queued_fds_s { unsigned int size; unsigned int offset; int fds[1]; }; /* core */ int uv__nonblock(int fd, int set); int uv__close(int fd); int uv__cloexec(int fd, int set); int uv__socket(int domain, int type, int protocol); int uv__dup(int fd); ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle); int uv__getiovmax(void); void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd); void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events); void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events); void uv__io_close(uv_loop_t* loop, uv__io_t* w); void uv__io_feed(uv_loop_t* loop, uv__io_t* w); int uv__io_active(const uv__io_t* w, unsigned int events); void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */ /* async */ void uv__async_send(struct uv__async* wa); void uv__async_init(struct uv__async* wa); int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb); void uv__async_stop(uv_loop_t* loop, struct uv__async* wa); /* loop */ void uv__run_idle(uv_loop_t* loop); void uv__run_check(uv_loop_t* loop); void uv__run_prepare(uv_loop_t* loop); /* stream */ void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type); int uv__stream_open(uv_stream_t*, int fd, int flags); void uv__stream_destroy(uv_stream_t* stream); #if defined(__APPLE__) int uv__stream_try_select(uv_stream_t* stream, int* fd); #endif /* defined(__APPLE__) */ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); int uv__accept(int sockfd); int uv__dup2_cloexec(int oldfd, int newfd); int uv__open_cloexec(const char* path, int flags); /* tcp */ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); int uv__tcp_nodelay(int fd, int on); int uv__tcp_keepalive(int fd, int on, unsigned int delay); /* pipe */ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); /* timer */ void uv__run_timers(uv_loop_t* loop); int uv__next_timeout(const uv_loop_t* loop); /* signal */ void uv__signal_close(uv_signal_t* handle); void uv__signal_global_once_init(void); void uv__signal_loop_cleanup(uv_loop_t* loop); /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop); void uv__platform_loop_delete(uv_loop_t* loop); void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); /* various */ void uv__async_close(uv_async_t* handle); void uv__check_close(uv_check_t* handle); void uv__fs_event_close(uv_fs_event_t* handle); void uv__idle_close(uv_idle_t* handle); void uv__pipe_close(uv_pipe_t* handle); void uv__poll_close(uv_poll_t* handle); void uv__prepare_close(uv_prepare_t* handle); void uv__process_close(uv_process_t* handle); void uv__stream_close(uv_stream_t* handle); void uv__tcp_close(uv_tcp_t* handle); void uv__timer_close(uv_timer_t* handle); void uv__udp_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle); uv_handle_type uv__handle_type(int fd); #if defined(__APPLE__) int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle))) #else #define uv__stream_fd(handle) ((handle)->io_watcher.fd) #endif /* defined(__APPLE__) */ #ifdef UV__O_NONBLOCK # define UV__F_NONBLOCK UV__O_NONBLOCK #else # define UV__F_NONBLOCK 1 #endif int uv__make_socketpair(int fds[2], int flags); int uv__make_pipe(int fds[2], int flags); #if defined(__APPLE__) int uv__fsevents_init(uv_fs_event_t* handle); int uv__fsevents_close(uv_fs_event_t* handle); void uv__fsevents_loop_delete(uv_loop_t* loop); /* OSX < 10.7 has no file events, polyfill them */ #ifndef MAC_OS_X_VERSION_10_7 static const int kFSEventStreamCreateFlagFileEvents = 0x00000010; static const int kFSEventStreamEventFlagItemCreated = 0x00000100; static const int kFSEventStreamEventFlagItemRemoved = 0x00000200; static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400; static const int kFSEventStreamEventFlagItemRenamed = 0x00000800; static const int kFSEventStreamEventFlagItemModified = 0x00001000; static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000; static const int kFSEventStreamEventFlagItemChangeOwner = 0x00004000; static const int kFSEventStreamEventFlagItemXattrMod = 0x00008000; static const int kFSEventStreamEventFlagItemIsFile = 0x00010000; static const int kFSEventStreamEventFlagItemIsDir = 0x00020000; static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000; #endif /* __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 */ #endif /* defined(__APPLE__) */ UV_UNUSED(static void uv__req_init(uv_loop_t* loop, uv_req_t* req, uv_req_type type)) { req->type = type; uv__req_register(loop, req); } #define uv__req_init(loop, req, type) \ uv__req_init((loop), (uv_req_t*)(req), (type)) UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) { /* Use a fast time source if available. We only need millisecond precision. */ loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000; } UV_UNUSED(static char* uv__basename_r(const char* path)) { char* s; s = strrchr(path, '/'); if (s == NULL) return (char*) path; return s + 1; } #endif /* UV_UNIX_INTERNAL_H_ */ libuv-v1.8.0/src/unix/kqueue.c000066400000000000000000000253411263361716600162640ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags); int uv__kqueue_init(uv_loop_t* loop) { loop->backend_fd = kqueue(); if (loop->backend_fd == -1) return -errno; uv__cloexec(loop->backend_fd, 1); return 0; } void uv__io_poll(uv_loop_t* loop, int timeout) { struct kevent events[1024]; struct kevent* ev; struct timespec spec; unsigned int nevents; unsigned int revents; QUEUE* q; uv__io_t* w; sigset_t* pset; sigset_t set; uint64_t base; uint64_t diff; int filter; int fflags; int count; int nfds; int fd; int op; int i; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); return; } nevents = 0; while (!QUEUE_EMPTY(&loop->watcher_queue)) { q = QUEUE_HEAD(&loop->watcher_queue); QUEUE_REMOVE(q); QUEUE_INIT(q); w = QUEUE_DATA(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); if ((w->events & UV__POLLIN) == 0 && (w->pevents & UV__POLLIN) != 0) { filter = EVFILT_READ; fflags = 0; op = EV_ADD; if (w->cb == uv__fs_event) { filter = EVFILT_VNODE; fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */ } EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0); if (++nevents == ARRAY_SIZE(events)) { if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) abort(); nevents = 0; } } if ((w->events & UV__POLLOUT) == 0 && (w->pevents & UV__POLLOUT) != 0) { EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); if (++nevents == ARRAY_SIZE(events)) { if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) abort(); nevents = 0; } } w->events = w->pevents; } pset = NULL; if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { pset = &set; sigemptyset(pset); sigaddset(pset, SIGPROF); } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;; nevents = 0) { if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; } if (pset != NULL) pthread_sigmask(SIG_BLOCK, pset, NULL); nfds = kevent(loop->backend_fd, events, nevents, events, ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); if (pset != NULL) pthread_sigmask(SIG_UNBLOCK, pset, NULL); /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { assert(timeout != -1); return; } if (nfds == -1) { if (errno != EINTR) abort(); if (timeout == 0) return; if (timeout == -1) continue; /* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; } nevents = 0; assert(loop->watchers != NULL); loop->watchers[loop->nwatchers] = (void*) events; loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { ev = events + i; fd = ev->ident; /* Skip invalidated events, see uv__platform_invalidate_fd */ if (fd == -1) continue; w = loop->watchers[fd]; if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. */ /* TODO batch up */ struct kevent events[1]; EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) if (errno != EBADF && errno != ENOENT) abort(); continue; } if (ev->filter == EVFILT_VNODE) { assert(w->events == UV__POLLIN); assert(w->pevents == UV__POLLIN); w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ nevents++; continue; } revents = 0; if (ev->filter == EVFILT_READ) { if (w->pevents & UV__POLLIN) { revents |= UV__POLLIN; w->rcount = ev->data; } else { /* TODO batch up */ struct kevent events[1]; EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) if (errno != ENOENT) abort(); } } if (ev->filter == EVFILT_WRITE) { if (w->pevents & UV__POLLOUT) { revents |= UV__POLLOUT; w->wcount = ev->data; } else { /* TODO batch up */ struct kevent events[1]; EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) if (errno != ENOENT) abort(); } } if (ev->flags & EV_ERROR) revents |= UV__POLLERR; if (revents == 0) continue; w->cb(loop, w, revents); nevents++; } loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); diff = loop->time - base; if (diff >= (uint64_t) timeout) return; timeout -= diff; } } void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct kevent* events; uintptr_t i; uintptr_t nfds; assert(loop->watchers != NULL); events = (struct kevent*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; if (events == NULL) return; /* Invalidate events with same file descriptor */ for (i = 0; i < nfds; i++) if ((int) events[i].ident == fd) events[i].ident = -1; } static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { uv_fs_event_t* handle; struct kevent ev; int events; const char* path; #if defined(F_GETPATH) /* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */ char pathbuf[MAXPATHLEN]; #endif handle = container_of(w, uv_fs_event_t, event_watcher); if (fflags & (NOTE_ATTRIB | NOTE_EXTEND)) events = UV_CHANGE; else events = UV_RENAME; path = NULL; #if defined(F_GETPATH) /* Also works when the file has been unlinked from the file system. Passing * in the path when the file has been deleted is arguably a little strange * but it's consistent with what the inotify backend does. */ if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0) path = uv__basename_r(pathbuf); #endif handle->cb(handle, path, events, 0); if (handle->event_watcher.fd == -1) return; /* Watcher operates in one-shot mode, re-arm it. */ fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0); if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) abort(); } int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); return 0; } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) { #if defined(__APPLE__) struct stat statbuf; #endif /* defined(__APPLE__) */ int fd; if (uv__is_active(handle)) return -EINVAL; /* TODO open asynchronously - but how do we report back errors? */ fd = open(path, O_RDONLY); if (fd == -1) return -errno; uv__handle_start(handle); uv__io_init(&handle->event_watcher, uv__fs_event, fd); handle->path = uv__strdup(path); handle->cb = cb; #if defined(__APPLE__) /* Nullify field to perform checks later */ handle->cf_cb = NULL; handle->realpath = NULL; handle->realpath_len = 0; handle->cf_flags = flags; if (fstat(fd, &statbuf)) goto fallback; /* FSEvents works only with directories */ if (!(statbuf.st_mode & S_IFDIR)) goto fallback; /* The fallback fd is no longer needed */ uv__close(fd); handle->event_watcher.fd = -1; return uv__fsevents_init(handle); fallback: #endif /* defined(__APPLE__) */ uv__io_start(handle->loop, &handle->event_watcher, UV__POLLIN); return 0; } int uv_fs_event_stop(uv_fs_event_t* handle) { if (!uv__is_active(handle)) return 0; uv__handle_stop(handle); #if defined(__APPLE__) if (uv__fsevents_close(handle)) #endif /* defined(__APPLE__) */ { uv__io_close(handle->loop, &handle->event_watcher); } uv__free(handle->path); handle->path = NULL; if (handle->event_watcher.fd != -1) { /* When FSEvents is used, we don't use the event_watcher's fd under certain * confitions. (see uv_fs_event_start) */ uv__close(handle->event_watcher.fd); handle->event_watcher.fd = -1; } return 0; } void uv__fs_event_close(uv_fs_event_t* handle) { uv_fs_event_stop(handle); } libuv-v1.8.0/src/unix/linux-core.c000066400000000000000000000552451263361716600170600ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define HAVE_IFADDRS_H 1 #ifdef __UCLIBC__ # if __UCLIBC_MAJOR__ < 0 || __UCLIBC_MINOR__ < 9 || __UCLIBC_SUBLEVEL__ < 32 # undef HAVE_IFADDRS_H # endif #endif #ifdef HAVE_IFADDRS_H # if defined(__ANDROID__) # include "android-ifaddrs.h" # else # include # endif # include # include # include #endif /* HAVE_IFADDRS_H */ /* Available from 2.6.32 onwards. */ #ifndef CLOCK_MONOTONIC_COARSE # define CLOCK_MONOTONIC_COARSE 6 #endif /* This is rather annoying: CLOCK_BOOTTIME lives in but we can't * include that file because it conflicts with . We'll just have to * define it ourselves. */ #ifndef CLOCK_BOOTTIME # define CLOCK_BOOTTIME 7 #endif static int read_models(unsigned int numcpus, uv_cpu_info_t* ci); static int read_times(unsigned int numcpus, uv_cpu_info_t* ci); static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); static unsigned long read_cpufreq(unsigned int cpunum); int uv__platform_loop_init(uv_loop_t* loop) { int fd; fd = uv__epoll_create1(UV__EPOLL_CLOEXEC); /* epoll_create1() can fail either because it's not implemented (old kernel) * or because it doesn't understand the EPOLL_CLOEXEC flag. */ if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { fd = uv__epoll_create(256); if (fd != -1) uv__cloexec(fd, 1); } loop->backend_fd = fd; loop->inotify_fd = -1; loop->inotify_watchers = NULL; if (fd == -1) return -errno; return 0; } void uv__platform_loop_delete(uv_loop_t* loop) { if (loop->inotify_fd == -1) return; uv__io_stop(loop, &loop->inotify_read_watcher, UV__POLLIN); uv__close(loop->inotify_fd); loop->inotify_fd = -1; } void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct uv__epoll_event* events; struct uv__epoll_event dummy; uintptr_t i; uintptr_t nfds; assert(loop->watchers != NULL); events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; if (events != NULL) /* Invalidate events with same file descriptor */ for (i = 0; i < nfds; i++) if ((int) events[i].data == fd) events[i].data = -1; /* Remove the file descriptor from the epoll. * This avoids a problem where the same file description remains open * in another process, causing repeated junk epoll events. * * We pass in a dummy epoll_event, to work around a bug in old kernels. */ if (loop->backend_fd >= 0) { /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. */ memset(&dummy, 0, sizeof(dummy)); uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &dummy); } } void uv__io_poll(uv_loop_t* loop, int timeout) { /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes * effectively infinite on 32 bits architectures. To avoid blocking * indefinitely, we cap the timeout and poll again if necessary. * * Note that "30 minutes" is a simplification because it depends on * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, * that being the largest value I have seen in the wild (and only once.) */ static const int max_safe_timeout = 1789569; static int no_epoll_pwait; static int no_epoll_wait; struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; struct uv__epoll_event e; int real_timeout; QUEUE* q; uv__io_t* w; sigset_t sigset; uint64_t sigmask; uint64_t base; int nevents; int count; int nfds; int fd; int op; int i; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); return; } while (!QUEUE_EMPTY(&loop->watcher_queue)) { q = QUEUE_HEAD(&loop->watcher_queue); QUEUE_REMOVE(q); QUEUE_INIT(q); w = QUEUE_DATA(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); e.events = w->pevents; e.data = w->fd; if (w->events == 0) op = UV__EPOLL_CTL_ADD; else op = UV__EPOLL_CTL_MOD; /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching * events, skip the syscall and squelch the events after epoll_wait(). */ if (uv__epoll_ctl(loop->backend_fd, op, w->fd, &e)) { if (errno != EEXIST) abort(); assert(op == UV__EPOLL_CTL_ADD); /* We've reactivated a file descriptor that's been watched before. */ if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_MOD, w->fd, &e)) abort(); } w->events = w->pevents; } sigmask = 0; if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { sigemptyset(&sigset); sigaddset(&sigset, SIGPROF); sigmask |= 1 << (SIGPROF - 1); } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ real_timeout = timeout; for (;;) { /* See the comment for max_safe_timeout for an explanation of why * this is necessary. Executive summary: kernel bug workaround. */ if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; if (sigmask != 0 && no_epoll_pwait != 0) if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) abort(); if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { nfds = uv__epoll_pwait(loop->backend_fd, events, ARRAY_SIZE(events), timeout, sigmask); if (nfds == -1 && errno == ENOSYS) no_epoll_pwait = 1; } else { nfds = uv__epoll_wait(loop->backend_fd, events, ARRAY_SIZE(events), timeout); if (nfds == -1 && errno == ENOSYS) no_epoll_wait = 1; } if (sigmask != 0 && no_epoll_pwait != 0) if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) abort(); /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { assert(timeout != -1); timeout = real_timeout - timeout; if (timeout > 0) continue; return; } if (nfds == -1) { if (errno == ENOSYS) { /* epoll_wait() or epoll_pwait() failed, try the other system call. */ assert(no_epoll_wait == 0 || no_epoll_pwait == 0); continue; } if (errno != EINTR) abort(); if (timeout == -1) continue; if (timeout == 0) return; /* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; } nevents = 0; assert(loop->watchers != NULL); loop->watchers[loop->nwatchers] = (void*) events; loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->data; /* Skip invalidated events, see uv__platform_invalidate_fd */ if (fd == -1) continue; assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); w = loop->watchers[fd]; if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. * * Ignore all errors because we may be racing with another thread * when the file descriptor is closed. */ uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, pe); continue; } /* Give users only events they're interested in. Prevents spurious * callbacks when previous callback invocation in this loop has stopped * the current watcher. Also, filters out events that users has not * requested us to watch. */ pe->events &= w->pevents | UV__POLLERR | UV__POLLHUP; /* Work around an epoll quirk where it sometimes reports just the * EPOLLERR or EPOLLHUP event. In order to force the event loop to * move forward, we merge in the read/write events that the watcher * is interested in; uv__read() and uv__write() will then deal with * the error or hangup in the usual fashion. * * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user * reads the available data, calls uv_read_stop(), then sometime later * calls uv_read_start() again. By then, libuv has forgotten about the * hangup and the kernel won't report EPOLLIN again because there's * nothing left to read. If anything, libuv is to blame here. The * current hack is just a quick bandaid; to properly fix it, libuv * needs to remember the error/hangup event. We should get that for * free when we switch over to edge-triggered I/O. */ if (pe->events == UV__EPOLLERR || pe->events == UV__EPOLLHUP) pe->events |= w->pevents & (UV__EPOLLIN | UV__EPOLLOUT); if (pe->events != 0) { w->cb(loop, w, pe->events); nevents++; } } loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); real_timeout -= (loop->time - base); if (real_timeout <= 0) return; timeout = real_timeout; } } uint64_t uv__hrtime(uv_clocktype_t type) { static clock_t fast_clock_id = -1; struct timespec t; clock_t clock_id; /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may * decide to make a costly system call. */ /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE * when it has microsecond granularity or better (unlikely). */ if (type == UV_CLOCK_FAST && fast_clock_id == -1) { if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 && t.tv_nsec <= 1 * 1000 * 1000) { fast_clock_id = CLOCK_MONOTONIC_COARSE; } else { fast_clock_id = CLOCK_MONOTONIC; } } clock_id = CLOCK_MONOTONIC; if (type == UV_CLOCK_FAST) clock_id = fast_clock_id; if (clock_gettime(clock_id, &t)) return 0; /* Not really possible. */ return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; } void uv_loadavg(double avg[3]) { struct sysinfo info; if (sysinfo(&info) < 0) return; avg[0] = (double) info.loads[0] / 65536.0; avg[1] = (double) info.loads[1] / 65536.0; avg[2] = (double) info.loads[2] / 65536.0; } int uv_exepath(char* buffer, size_t* size) { ssize_t n; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; n = *size - 1; if (n > 0) n = readlink("/proc/self/exe", buffer, n); if (n == -1) return -errno; buffer[n] = '\0'; *size = n; return 0; } uint64_t uv_get_free_memory(void) { return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); } uint64_t uv_get_total_memory(void) { return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); } int uv_resident_set_memory(size_t* rss) { char buf[1024]; const char* s; ssize_t n; long val; int fd; int i; do fd = open("/proc/self/stat", O_RDONLY); while (fd == -1 && errno == EINTR); if (fd == -1) return -errno; do n = read(fd, buf, sizeof(buf) - 1); while (n == -1 && errno == EINTR); uv__close(fd); if (n == -1) return -errno; buf[n] = '\0'; s = strchr(buf, ' '); if (s == NULL) goto err; s += 1; if (*s != '(') goto err; s = strchr(s, ')'); if (s == NULL) goto err; for (i = 1; i <= 22; i++) { s = strchr(s + 1, ' '); if (s == NULL) goto err; } errno = 0; val = strtol(s, NULL, 10); if (errno != 0) goto err; if (val < 0) goto err; *rss = val * getpagesize(); return 0; err: return -EINVAL; } int uv_uptime(double* uptime) { static volatile int no_clock_boottime; struct timespec now; int r; /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system * is suspended. */ if (no_clock_boottime) { retry: r = clock_gettime(CLOCK_MONOTONIC, &now); } else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) { no_clock_boottime = 1; goto retry; } if (r) return -errno; *uptime = now.tv_sec; return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int numcpus; uv_cpu_info_t* ci; int err; *cpu_infos = NULL; *count = 0; numcpus = sysconf(_SC_NPROCESSORS_ONLN); assert(numcpus != (unsigned int) -1); assert(numcpus != 0); ci = uv__calloc(numcpus, sizeof(*ci)); if (ci == NULL) return -ENOMEM; err = read_models(numcpus, ci); if (err == 0) err = read_times(numcpus, ci); if (err) { uv_free_cpu_info(ci, numcpus); return err; } /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo. * We don't check for errors here. Worst case, the field is left zero. */ if (ci[0].speed == 0) read_speeds(numcpus, ci); *cpu_infos = ci; *count = numcpus; return 0; } static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { unsigned int num; for (num = 0; num < numcpus; num++) ci[num].speed = read_cpufreq(num) / 1000; } /* Also reads the CPU frequency on x86. The other architectures only have * a BogoMIPS field, which may not be very accurate. * * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup. */ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { static const char model_marker[] = "model name\t: "; static const char speed_marker[] = "cpu MHz\t\t: "; const char* inferred_model; unsigned int model_idx; unsigned int speed_idx; char buf[1024]; char* model; FILE* fp; /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */ (void) &model_marker; (void) &speed_marker; (void) &speed_idx; (void) &model; (void) &buf; (void) &fp; model_idx = 0; speed_idx = 0; #if defined(__arm__) || \ defined(__i386__) || \ defined(__mips__) || \ defined(__x86_64__) fp = fopen("/proc/cpuinfo", "r"); if (fp == NULL) return -errno; while (fgets(buf, sizeof(buf), fp)) { if (model_idx < numcpus) { if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { model = buf + sizeof(model_marker) - 1; model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ if (model == NULL) { fclose(fp); return -ENOMEM; } ci[model_idx++].model = model; continue; } } #if defined(__arm__) || defined(__mips__) if (model_idx < numcpus) { #if defined(__arm__) /* Fallback for pre-3.8 kernels. */ static const char model_marker[] = "Processor\t: "; #else /* defined(__mips__) */ static const char model_marker[] = "cpu model\t\t: "; #endif if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { model = buf + sizeof(model_marker) - 1; model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ if (model == NULL) { fclose(fp); return -ENOMEM; } ci[model_idx++].model = model; continue; } } #else /* !__arm__ && !__mips__ */ if (speed_idx < numcpus) { if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) { ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1); continue; } } #endif /* __arm__ || __mips__ */ } fclose(fp); #endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */ /* Now we want to make sure that all the models contain *something* because * it's not safe to leave them as null. Copy the last entry unless there * isn't one, in that case we simply put "unknown" into everything. */ inferred_model = "unknown"; if (model_idx > 0) inferred_model = ci[model_idx - 1].model; while (model_idx < numcpus) { model = uv__strndup(inferred_model, strlen(inferred_model)); if (model == NULL) return -ENOMEM; ci[model_idx++].model = model; } return 0; } static int read_times(unsigned int numcpus, uv_cpu_info_t* ci) { unsigned long clock_ticks; struct uv_cpu_times_s ts; unsigned long user; unsigned long nice; unsigned long sys; unsigned long idle; unsigned long dummy; unsigned long irq; unsigned int num; unsigned int len; char buf[1024]; FILE* fp; clock_ticks = sysconf(_SC_CLK_TCK); assert(clock_ticks != (unsigned long) -1); assert(clock_ticks != 0); fp = fopen("/proc/stat", "r"); if (fp == NULL) return -errno; if (!fgets(buf, sizeof(buf), fp)) abort(); num = 0; while (fgets(buf, sizeof(buf), fp)) { if (num >= numcpus) break; if (strncmp(buf, "cpu", 3)) break; /* skip "cpu " marker */ { unsigned int n; int r = sscanf(buf, "cpu%u ", &n); assert(r == 1); (void) r; /* silence build warning */ for (len = sizeof("cpu0"); n /= 10; len++); } /* Line contains user, nice, system, idle, iowait, irq, softirq, steal, * guest, guest_nice but we're only interested in the first four + irq. * * Don't use %*s to skip fields or %ll to read straight into the uint64_t * fields, they're not allowed in C89 mode. */ if (6 != sscanf(buf + len, "%lu %lu %lu %lu %lu %lu", &user, &nice, &sys, &idle, &dummy, &irq)) abort(); ts.user = clock_ticks * user; ts.nice = clock_ticks * nice; ts.sys = clock_ticks * sys; ts.idle = clock_ticks * idle; ts.irq = clock_ticks * irq; ci[num++].cpu_times = ts; } fclose(fp); assert(num == numcpus); return 0; } static unsigned long read_cpufreq(unsigned int cpunum) { unsigned long val; char buf[1024]; FILE* fp; snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", cpunum); fp = fopen(buf, "r"); if (fp == NULL) return 0; if (fscanf(fp, "%lu", &val) != 1) val = 0; fclose(fp); return val; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { #ifndef HAVE_IFADDRS_H return -ENOSYS; #else struct ifaddrs *addrs, *ent; uv_interface_address_t* address; int i; struct sockaddr_ll *sll; if (getifaddrs(&addrs)) return -errno; *count = 0; *addresses = NULL; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family == PF_PACKET)) { continue; } (*count)++; } if (*count == 0) return 0; *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return -ENOMEM; } address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) continue; if (ent->ifa_addr == NULL) continue; /* * On Linux getifaddrs returns information related to the raw underlying * devices. We're not interested in this information yet. */ if (ent->ifa_addr->sa_family == PF_PACKET) continue; address->name = uv__strdup(ent->ifa_name); if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); } else { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } if (ent->ifa_netmask->sa_family == AF_INET6) { address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); } else { address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); } address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); address++; } /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != PF_PACKET)) { continue; } address = *addresses; for (i = 0; i < (*count); i++) { if (strcmp(address->name, ent->ifa_name) == 0) { sll = (struct sockaddr_ll*)ent->ifa_addr; memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); } address++; } } freeifaddrs(addrs); return 0; #endif } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; i++) { uv__free(addresses[i].name); } uv__free(addresses); } void uv__set_process_title(const char* title) { #if defined(PR_SET_NAME) prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ #endif } libuv-v1.8.0/src/unix/linux-inotify.c000066400000000000000000000164541263361716600176100ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "tree.h" #include "internal.h" #include #include #include #include #include #include #include #include struct watcher_list { RB_ENTRY(watcher_list) entry; QUEUE watchers; int iterating; char* path; int wd; }; struct watcher_root { struct watcher_list* rbh_root; }; #define CAST(p) ((struct watcher_root*)(p)) static int compare_watchers(const struct watcher_list* a, const struct watcher_list* b) { if (a->wd < b->wd) return -1; if (a->wd > b->wd) return 1; return 0; } RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) static void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, unsigned int revents); static int new_inotify_fd(void) { int err; int fd; fd = uv__inotify_init1(UV__IN_NONBLOCK | UV__IN_CLOEXEC); if (fd != -1) return fd; if (errno != ENOSYS) return -errno; fd = uv__inotify_init(); if (fd == -1) return -errno; err = uv__cloexec(fd, 1); if (err == 0) err = uv__nonblock(fd, 1); if (err) { uv__close(fd); return err; } return fd; } static int init_inotify(uv_loop_t* loop) { int err; if (loop->inotify_fd != -1) return 0; err = new_inotify_fd(); if (err < 0) return err; loop->inotify_fd = err; uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); uv__io_start(loop, &loop->inotify_read_watcher, UV__POLLIN); return 0; } static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { struct watcher_list w; w.wd = wd; return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w); } static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { /* if the watcher_list->watchers is being iterated over, we can't free it. */ if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) { /* No watchers left for this path. Clean up. */ RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w); uv__inotify_rm_watch(loop->inotify_fd, w->wd); uv__free(w); } } static void uv__inotify_read(uv_loop_t* loop, uv__io_t* dummy, unsigned int events) { const struct uv__inotify_event* e; struct watcher_list* w; uv_fs_event_t* h; QUEUE queue; QUEUE* q; const char* path; ssize_t size; const char *p; /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ char buf[4096]; while (1) { do size = read(loop->inotify_fd, buf, sizeof(buf)); while (size == -1 && errno == EINTR); if (size == -1) { assert(errno == EAGAIN || errno == EWOULDBLOCK); break; } assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ /* Now we have one or more inotify_event structs. */ for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { e = (const struct uv__inotify_event*)p; events = 0; if (e->mask & (UV__IN_ATTRIB|UV__IN_MODIFY)) events |= UV_CHANGE; if (e->mask & ~(UV__IN_ATTRIB|UV__IN_MODIFY)) events |= UV_RENAME; w = find_watcher(loop, e->wd); if (w == NULL) continue; /* Stale event, no watchers left. */ /* inotify does not return the filename when monitoring a single file * for modifications. Repurpose the filename for API compatibility. * I'm not convinced this is a good thing, maybe it should go. */ path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path); /* We're about to iterate over the queue and call user's callbacks. * What can go wrong? * A callback could call uv_fs_event_stop() * and the queue can change under our feet. * So, we use QUEUE_MOVE() trick to safely iterate over the queue. * And we don't free the watcher_list until we're done iterating. * * First, * tell uv_fs_event_stop() (that could be called from a user's callback) * not to free watcher_list. */ w->iterating = 1; QUEUE_MOVE(&w->watchers, &queue); while (!QUEUE_EMPTY(&queue)) { q = QUEUE_HEAD(&queue); h = QUEUE_DATA(q, uv_fs_event_t, watchers); QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&w->watchers, q); h->cb(h, path, events, 0); } /* done iterating, time to (maybe) free empty watcher_list */ w->iterating = 0; maybe_free_watcher_list(w, loop); } } } int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); return 0; } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) { struct watcher_list* w; int events; int err; int wd; if (uv__is_active(handle)) return -EINVAL; err = init_inotify(handle->loop); if (err) return err; events = UV__IN_ATTRIB | UV__IN_CREATE | UV__IN_MODIFY | UV__IN_DELETE | UV__IN_DELETE_SELF | UV__IN_MOVE_SELF | UV__IN_MOVED_FROM | UV__IN_MOVED_TO; wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events); if (wd == -1) return -errno; w = find_watcher(handle->loop, wd); if (w) goto no_insert; w = uv__malloc(sizeof(*w) + strlen(path) + 1); if (w == NULL) return -ENOMEM; w->wd = wd; w->path = strcpy((char*)(w + 1), path); QUEUE_INIT(&w->watchers); w->iterating = 0; RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w); no_insert: uv__handle_start(handle); QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers); handle->path = w->path; handle->cb = cb; handle->wd = wd; return 0; } int uv_fs_event_stop(uv_fs_event_t* handle) { struct watcher_list* w; if (!uv__is_active(handle)) return 0; w = find_watcher(handle->loop, handle->wd); assert(w != NULL); handle->wd = -1; handle->path = NULL; uv__handle_stop(handle); QUEUE_REMOVE(&handle->watchers); maybe_free_watcher_list(w, handle->loop); return 0; } void uv__fs_event_close(uv_fs_event_t* handle) { uv_fs_event_stop(handle); } libuv-v1.8.0/src/unix/linux-syscalls.c000066400000000000000000000263521263361716600177620ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "linux-syscalls.h" #include #include #include #include #include #if defined(__has_feature) # if __has_feature(memory_sanitizer) # define MSAN_ACTIVE 1 # include # endif #endif #if defined(__i386__) # ifndef __NR_socketcall # define __NR_socketcall 102 # endif #endif #if defined(__arm__) # if defined(__thumb__) || defined(__ARM_EABI__) # define UV_SYSCALL_BASE 0 # else # define UV_SYSCALL_BASE 0x900000 # endif #endif /* __arm__ */ #ifndef __NR_accept4 # if defined(__x86_64__) # define __NR_accept4 288 # elif defined(__i386__) /* Nothing. Handled through socketcall(). */ # elif defined(__arm__) # define __NR_accept4 (UV_SYSCALL_BASE + 366) # endif #endif /* __NR_accept4 */ #ifndef __NR_eventfd # if defined(__x86_64__) # define __NR_eventfd 284 # elif defined(__i386__) # define __NR_eventfd 323 # elif defined(__arm__) # define __NR_eventfd (UV_SYSCALL_BASE + 351) # endif #endif /* __NR_eventfd */ #ifndef __NR_eventfd2 # if defined(__x86_64__) # define __NR_eventfd2 290 # elif defined(__i386__) # define __NR_eventfd2 328 # elif defined(__arm__) # define __NR_eventfd2 (UV_SYSCALL_BASE + 356) # endif #endif /* __NR_eventfd2 */ #ifndef __NR_epoll_create # if defined(__x86_64__) # define __NR_epoll_create 213 # elif defined(__i386__) # define __NR_epoll_create 254 # elif defined(__arm__) # define __NR_epoll_create (UV_SYSCALL_BASE + 250) # endif #endif /* __NR_epoll_create */ #ifndef __NR_epoll_create1 # if defined(__x86_64__) # define __NR_epoll_create1 291 # elif defined(__i386__) # define __NR_epoll_create1 329 # elif defined(__arm__) # define __NR_epoll_create1 (UV_SYSCALL_BASE + 357) # endif #endif /* __NR_epoll_create1 */ #ifndef __NR_epoll_ctl # if defined(__x86_64__) # define __NR_epoll_ctl 233 /* used to be 214 */ # elif defined(__i386__) # define __NR_epoll_ctl 255 # elif defined(__arm__) # define __NR_epoll_ctl (UV_SYSCALL_BASE + 251) # endif #endif /* __NR_epoll_ctl */ #ifndef __NR_epoll_wait # if defined(__x86_64__) # define __NR_epoll_wait 232 /* used to be 215 */ # elif defined(__i386__) # define __NR_epoll_wait 256 # elif defined(__arm__) # define __NR_epoll_wait (UV_SYSCALL_BASE + 252) # endif #endif /* __NR_epoll_wait */ #ifndef __NR_epoll_pwait # if defined(__x86_64__) # define __NR_epoll_pwait 281 # elif defined(__i386__) # define __NR_epoll_pwait 319 # elif defined(__arm__) # define __NR_epoll_pwait (UV_SYSCALL_BASE + 346) # endif #endif /* __NR_epoll_pwait */ #ifndef __NR_inotify_init # if defined(__x86_64__) # define __NR_inotify_init 253 # elif defined(__i386__) # define __NR_inotify_init 291 # elif defined(__arm__) # define __NR_inotify_init (UV_SYSCALL_BASE + 316) # endif #endif /* __NR_inotify_init */ #ifndef __NR_inotify_init1 # if defined(__x86_64__) # define __NR_inotify_init1 294 # elif defined(__i386__) # define __NR_inotify_init1 332 # elif defined(__arm__) # define __NR_inotify_init1 (UV_SYSCALL_BASE + 360) # endif #endif /* __NR_inotify_init1 */ #ifndef __NR_inotify_add_watch # if defined(__x86_64__) # define __NR_inotify_add_watch 254 # elif defined(__i386__) # define __NR_inotify_add_watch 292 # elif defined(__arm__) # define __NR_inotify_add_watch (UV_SYSCALL_BASE + 317) # endif #endif /* __NR_inotify_add_watch */ #ifndef __NR_inotify_rm_watch # if defined(__x86_64__) # define __NR_inotify_rm_watch 255 # elif defined(__i386__) # define __NR_inotify_rm_watch 293 # elif defined(__arm__) # define __NR_inotify_rm_watch (UV_SYSCALL_BASE + 318) # endif #endif /* __NR_inotify_rm_watch */ #ifndef __NR_pipe2 # if defined(__x86_64__) # define __NR_pipe2 293 # elif defined(__i386__) # define __NR_pipe2 331 # elif defined(__arm__) # define __NR_pipe2 (UV_SYSCALL_BASE + 359) # endif #endif /* __NR_pipe2 */ #ifndef __NR_recvmmsg # if defined(__x86_64__) # define __NR_recvmmsg 299 # elif defined(__i386__) # define __NR_recvmmsg 337 # elif defined(__arm__) # define __NR_recvmmsg (UV_SYSCALL_BASE + 365) # endif #endif /* __NR_recvmsg */ #ifndef __NR_sendmmsg # if defined(__x86_64__) # define __NR_sendmmsg 307 # elif defined(__i386__) # define __NR_sendmmsg 345 # elif defined(__arm__) # define __NR_sendmmsg (UV_SYSCALL_BASE + 374) # endif #endif /* __NR_sendmmsg */ #ifndef __NR_utimensat # if defined(__x86_64__) # define __NR_utimensat 280 # elif defined(__i386__) # define __NR_utimensat 320 # elif defined(__arm__) # define __NR_utimensat (UV_SYSCALL_BASE + 348) # endif #endif /* __NR_utimensat */ #ifndef __NR_preadv # if defined(__x86_64__) # define __NR_preadv 295 # elif defined(__i386__) # define __NR_preadv 333 # elif defined(__arm__) # define __NR_preadv (UV_SYSCALL_BASE + 361) # endif #endif /* __NR_preadv */ #ifndef __NR_pwritev # if defined(__x86_64__) # define __NR_pwritev 296 # elif defined(__i386__) # define __NR_pwritev 334 # elif defined(__arm__) # define __NR_pwritev (UV_SYSCALL_BASE + 362) # endif #endif /* __NR_pwritev */ #ifndef __NR_dup3 # if defined(__x86_64__) # define __NR_dup3 292 # elif defined(__i386__) # define __NR_dup3 330 # elif defined(__arm__) # define __NR_dup3 (UV_SYSCALL_BASE + 358) # endif #endif /* __NR_pwritev */ int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) { #if defined(__i386__) unsigned long args[4]; int r; args[0] = (unsigned long) fd; args[1] = (unsigned long) addr; args[2] = (unsigned long) addrlen; args[3] = (unsigned long) flags; r = syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args); /* socketcall() raises EINVAL when SYS_ACCEPT4 is not supported but so does * a bad flags argument. Try to distinguish between the two cases. */ if (r == -1) if (errno == EINVAL) if ((flags & ~(UV__SOCK_CLOEXEC|UV__SOCK_NONBLOCK)) == 0) errno = ENOSYS; return r; #elif defined(__NR_accept4) return syscall(__NR_accept4, fd, addr, addrlen, flags); #else return errno = ENOSYS, -1; #endif } int uv__eventfd(unsigned int count) { #if defined(__NR_eventfd) return syscall(__NR_eventfd, count); #else return errno = ENOSYS, -1; #endif } int uv__eventfd2(unsigned int count, int flags) { #if defined(__NR_eventfd2) return syscall(__NR_eventfd2, count, flags); #else return errno = ENOSYS, -1; #endif } int uv__epoll_create(int size) { #if defined(__NR_epoll_create) return syscall(__NR_epoll_create, size); #else return errno = ENOSYS, -1; #endif } int uv__epoll_create1(int flags) { #if defined(__NR_epoll_create1) return syscall(__NR_epoll_create1, flags); #else return errno = ENOSYS, -1; #endif } int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event* events) { #if defined(__NR_epoll_ctl) return syscall(__NR_epoll_ctl, epfd, op, fd, events); #else return errno = ENOSYS, -1; #endif } int uv__epoll_wait(int epfd, struct uv__epoll_event* events, int nevents, int timeout) { #if defined(__NR_epoll_wait) int result; result = syscall(__NR_epoll_wait, epfd, events, nevents, timeout); #if MSAN_ACTIVE if (result > 0) __msan_unpoison(events, sizeof(events[0]) * result); #endif return result; #else return errno = ENOSYS, -1; #endif } int uv__epoll_pwait(int epfd, struct uv__epoll_event* events, int nevents, int timeout, uint64_t sigmask) { #if defined(__NR_epoll_pwait) int result; result = syscall(__NR_epoll_pwait, epfd, events, nevents, timeout, &sigmask, sizeof(sigmask)); #if MSAN_ACTIVE if (result > 0) __msan_unpoison(events, sizeof(events[0]) * result); #endif return result; #else return errno = ENOSYS, -1; #endif } int uv__inotify_init(void) { #if defined(__NR_inotify_init) return syscall(__NR_inotify_init); #else return errno = ENOSYS, -1; #endif } int uv__inotify_init1(int flags) { #if defined(__NR_inotify_init1) return syscall(__NR_inotify_init1, flags); #else return errno = ENOSYS, -1; #endif } int uv__inotify_add_watch(int fd, const char* path, uint32_t mask) { #if defined(__NR_inotify_add_watch) return syscall(__NR_inotify_add_watch, fd, path, mask); #else return errno = ENOSYS, -1; #endif } int uv__inotify_rm_watch(int fd, int32_t wd) { #if defined(__NR_inotify_rm_watch) return syscall(__NR_inotify_rm_watch, fd, wd); #else return errno = ENOSYS, -1; #endif } int uv__pipe2(int pipefd[2], int flags) { #if defined(__NR_pipe2) int result; result = syscall(__NR_pipe2, pipefd, flags); #if MSAN_ACTIVE if (!result) __msan_unpoison(pipefd, sizeof(int[2])); #endif return result; #else return errno = ENOSYS, -1; #endif } int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen, unsigned int flags) { #if defined(__NR_sendmmsg) return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags); #else return errno = ENOSYS, -1; #endif } int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen, unsigned int flags, struct timespec* timeout) { #if defined(__NR_recvmmsg) return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout); #else return errno = ENOSYS, -1; #endif } int uv__utimesat(int dirfd, const char* path, const struct timespec times[2], int flags) { #if defined(__NR_utimensat) return syscall(__NR_utimensat, dirfd, path, times, flags); #else return errno = ENOSYS, -1; #endif } ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { #if defined(__NR_preadv) return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); #else return errno = ENOSYS, -1; #endif } ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { #if defined(__NR_pwritev) return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); #else return errno = ENOSYS, -1; #endif } int uv__dup3(int oldfd, int newfd, int flags) { #if defined(__NR_dup3) return syscall(__NR_dup3, oldfd, newfd, flags); #else return errno = ENOSYS, -1; #endif } libuv-v1.8.0/src/unix/linux-syscalls.h000066400000000000000000000116401263361716600177610ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_LINUX_SYSCALL_H_ #define UV_LINUX_SYSCALL_H_ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #if defined(__alpha__) # define UV__O_CLOEXEC 0x200000 #elif defined(__hppa__) # define UV__O_CLOEXEC 0x200000 #elif defined(__sparc__) # define UV__O_CLOEXEC 0x400000 #else # define UV__O_CLOEXEC 0x80000 #endif #if defined(__alpha__) # define UV__O_NONBLOCK 0x4 #elif defined(__hppa__) # define UV__O_NONBLOCK O_NONBLOCK #elif defined(__mips__) # define UV__O_NONBLOCK 0x80 #elif defined(__sparc__) # define UV__O_NONBLOCK 0x4000 #else # define UV__O_NONBLOCK 0x800 #endif #define UV__EFD_CLOEXEC UV__O_CLOEXEC #define UV__EFD_NONBLOCK UV__O_NONBLOCK #define UV__IN_CLOEXEC UV__O_CLOEXEC #define UV__IN_NONBLOCK UV__O_NONBLOCK #define UV__SOCK_CLOEXEC UV__O_CLOEXEC #if defined(SOCK_NONBLOCK) # define UV__SOCK_NONBLOCK SOCK_NONBLOCK #else # define UV__SOCK_NONBLOCK UV__O_NONBLOCK #endif /* epoll flags */ #define UV__EPOLL_CLOEXEC UV__O_CLOEXEC #define UV__EPOLL_CTL_ADD 1 #define UV__EPOLL_CTL_DEL 2 #define UV__EPOLL_CTL_MOD 3 #define UV__EPOLLIN 1 #define UV__EPOLLOUT 4 #define UV__EPOLLERR 8 #define UV__EPOLLHUP 16 #define UV__EPOLLONESHOT 0x40000000 #define UV__EPOLLET 0x80000000 /* inotify flags */ #define UV__IN_ACCESS 0x001 #define UV__IN_MODIFY 0x002 #define UV__IN_ATTRIB 0x004 #define UV__IN_CLOSE_WRITE 0x008 #define UV__IN_CLOSE_NOWRITE 0x010 #define UV__IN_OPEN 0x020 #define UV__IN_MOVED_FROM 0x040 #define UV__IN_MOVED_TO 0x080 #define UV__IN_CREATE 0x100 #define UV__IN_DELETE 0x200 #define UV__IN_DELETE_SELF 0x400 #define UV__IN_MOVE_SELF 0x800 #if defined(__x86_64__) struct uv__epoll_event { uint32_t events; uint64_t data; } __attribute__((packed)); #else struct uv__epoll_event { uint32_t events; uint64_t data; }; #endif struct uv__inotify_event { int32_t wd; uint32_t mask; uint32_t cookie; uint32_t len; /* char name[0]; */ }; struct uv__mmsghdr { struct msghdr msg_hdr; unsigned int msg_len; }; int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags); int uv__eventfd(unsigned int count); int uv__epoll_create(int size); int uv__epoll_create1(int flags); int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event *ev); int uv__epoll_wait(int epfd, struct uv__epoll_event* events, int nevents, int timeout); int uv__epoll_pwait(int epfd, struct uv__epoll_event* events, int nevents, int timeout, uint64_t sigmask); int uv__eventfd2(unsigned int count, int flags); int uv__inotify_init(void); int uv__inotify_init1(int flags); int uv__inotify_add_watch(int fd, const char* path, uint32_t mask); int uv__inotify_rm_watch(int fd, int32_t wd); int uv__pipe2(int pipefd[2], int flags); int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen, unsigned int flags, struct timespec* timeout); int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen, unsigned int flags); int uv__utimesat(int dirfd, const char* path, const struct timespec times[2], int flags); ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); int uv__dup3(int oldfd, int newfd, int flags); #endif /* UV_LINUX_SYSCALL_H_ */ libuv-v1.8.0/src/unix/loop-watcher.c000066400000000000000000000105211263361716600173630ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #define UV_LOOP_WATCHER_DEFINE(name, type) \ int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \ uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \ handle->name##_cb = NULL; \ return 0; \ } \ \ int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ if (uv__is_active(handle)) return 0; \ if (cb == NULL) return -EINVAL; \ QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \ handle->name##_cb = cb; \ uv__handle_start(handle); \ return 0; \ } \ \ int uv_##name##_stop(uv_##name##_t* handle) { \ if (!uv__is_active(handle)) return 0; \ QUEUE_REMOVE(&handle->queue); \ uv__handle_stop(handle); \ return 0; \ } \ \ void uv__run_##name(uv_loop_t* loop) { \ uv_##name##_t* h; \ QUEUE queue; \ QUEUE* q; \ QUEUE_MOVE(&loop->name##_handles, &queue); \ while (!QUEUE_EMPTY(&queue)) { \ q = QUEUE_HEAD(&queue); \ h = QUEUE_DATA(q, uv_##name##_t, queue); \ QUEUE_REMOVE(q); \ QUEUE_INSERT_TAIL(&loop->name##_handles, q); \ h->name##_cb(h); \ } \ } \ \ void uv__##name##_close(uv_##name##_t* handle) { \ uv_##name##_stop(handle); \ } UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) UV_LOOP_WATCHER_DEFINE(check, CHECK) UV_LOOP_WATCHER_DEFINE(idle, IDLE) libuv-v1.8.0/src/unix/loop.c000066400000000000000000000101101263361716600157220ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "tree.h" #include "internal.h" #include "heap-inl.h" #include #include #include int uv_loop_init(uv_loop_t* loop) { int err; uv__signal_global_once_init(); memset(loop, 0, sizeof(*loop)); heap_init((struct heap*) &loop->timer_heap); QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->active_reqs); QUEUE_INIT(&loop->idle_handles); QUEUE_INIT(&loop->async_handles); QUEUE_INIT(&loop->check_handles); QUEUE_INIT(&loop->prepare_handles); QUEUE_INIT(&loop->handle_queue); loop->nfds = 0; loop->watchers = NULL; loop->nwatchers = 0; QUEUE_INIT(&loop->pending_queue); QUEUE_INIT(&loop->watcher_queue); loop->closing_handles = NULL; uv__update_time(loop); uv__async_init(&loop->async_watcher); loop->signal_pipefd[0] = -1; loop->signal_pipefd[1] = -1; loop->backend_fd = -1; loop->emfile_fd = -1; loop->timer_counter = 0; loop->stop_flag = 0; err = uv__platform_loop_init(loop); if (err) return err; err = uv_signal_init(loop, &loop->child_watcher); if (err) goto fail_signal_init; uv__handle_unref(&loop->child_watcher); loop->child_watcher.flags |= UV__HANDLE_INTERNAL; QUEUE_INIT(&loop->process_handles); err = uv_rwlock_init(&loop->cloexec_lock); if (err) goto fail_rwlock_init; err = uv_mutex_init(&loop->wq_mutex); if (err) goto fail_mutex_init; err = uv_async_init(loop, &loop->wq_async, uv__work_done); if (err) goto fail_async_init; uv__handle_unref(&loop->wq_async); loop->wq_async.flags |= UV__HANDLE_INTERNAL; return 0; fail_async_init: uv_mutex_destroy(&loop->wq_mutex); fail_mutex_init: uv_rwlock_destroy(&loop->cloexec_lock); fail_rwlock_init: uv__signal_loop_cleanup(loop); fail_signal_init: uv__platform_loop_delete(loop); return err; } void uv__loop_close(uv_loop_t* loop) { uv__signal_loop_cleanup(loop); uv__platform_loop_delete(loop); uv__async_stop(loop, &loop->async_watcher); if (loop->emfile_fd != -1) { uv__close(loop->emfile_fd); loop->emfile_fd = -1; } if (loop->backend_fd != -1) { uv__close(loop->backend_fd); loop->backend_fd = -1; } uv_mutex_lock(&loop->wq_mutex); assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); assert(!uv__has_active_reqs(loop)); uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); /* * Note that all thread pool stuff is finished at this point and * it is safe to just destroy rw lock */ uv_rwlock_destroy(&loop->cloexec_lock); #if 0 assert(QUEUE_EMPTY(&loop->pending_queue)); assert(QUEUE_EMPTY(&loop->watcher_queue)); assert(loop->nfds == 0); #endif uv__free(loop->watchers); loop->watchers = NULL; loop->nwatchers = 0; } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { if (option != UV_LOOP_BLOCK_SIGNAL) return UV_ENOSYS; if (va_arg(ap, int) != SIGPROF) return UV_EINVAL; loop->flags |= UV_LOOP_BLOCK_SIGPROF; return 0; } libuv-v1.8.0/src/unix/netbsd.c000066400000000000000000000212471263361716600162450ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) static char *process_title; int uv__platform_loop_init(uv_loop_t* loop) { return uv__kqueue_init(loop); } void uv__platform_loop_delete(uv_loop_t* loop) { } uint64_t uv__hrtime(uv_clocktype_t type) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); } void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } int uv_exepath(char* buffer, size_t* size) { int mib[4]; size_t cb; pid_t mypid; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; mypid = getpid(); mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; mib[2] = mypid; mib[3] = KERN_PROC_ARGV; cb = *size; if (sysctl(mib, 4, buffer, &cb, NULL, 0)) return -errno; *size = strlen(buffer); return 0; } uint64_t uv_get_free_memory(void) { struct uvmexp info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } uint64_t uv_get_total_memory(void) { #if defined(HW_PHYSMEM64) uint64_t info; int which[] = {CTL_HW, HW_PHYSMEM64}; #else unsigned int info; int which[] = {CTL_HW, HW_PHYSMEM}; #endif size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; return (uint64_t) info; } char** uv_setup_args(int argc, char** argv) { process_title = argc ? uv__strdup(argv[0]) : NULL; return argv; } int uv_set_process_title(const char* title) { if (process_title) uv__free(process_title); process_title = uv__strdup(title); setproctitle("%s", title); return 0; } int uv_get_process_title(char* buffer, size_t size) { if (process_title) { strncpy(buffer, process_title, size); } else { if (size > 0) { buffer[0] = '\0'; } } return 0; } int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL; pid_t pid; int nprocs; int max_size = sizeof(struct kinfo_proc2); int page_size; page_size = getpagesize(); pid = getpid(); kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); if (kd == NULL) goto error; kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs); if (kinfo == NULL) goto error; *rss = kinfo->p_vm_rssize * page_size; kvm_close(kd); return 0; error: if (kd) kvm_close(kd); return -EPERM; } int uv_uptime(double* uptime) { time_t now; struct timeval info; size_t size = sizeof(info); static int which[] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; now = time(NULL); *uptime = (double)(now - info.tv_sec); return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK); unsigned int multiplier = ((uint64_t)1000L / ticks); unsigned int cur = 0; uv_cpu_info_t* cpu_info; u_int64_t* cp_times; char model[512]; u_int64_t cpuspeed; int numcpus; size_t size; int i; size = sizeof(model); if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) && sysctlbyname("hw.model", &model, &size, NULL, 0)) { return -errno; } size = sizeof(numcpus); if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0)) return -errno; *count = numcpus; /* Only i386 and amd64 have machdep.tsc_freq */ size = sizeof(cpuspeed); if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0)) cpuspeed = 0; size = numcpus * CPUSTATES * sizeof(*cp_times); cp_times = uv__malloc(size); if (cp_times == NULL) return -ENOMEM; if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0)) return -errno; *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) { uv__free(cp_times); uv__free(*cpu_infos); return -ENOMEM; } for (i = 0; i < numcpus; i++) { cpu_info = &(*cpu_infos)[i]; cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier; cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier; cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier; cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier; cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier; cpu_info->model = uv__strdup(model); cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6); cur += CPUSTATES; } uv__free(cp_times); return 0; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs *addrs, *ent; uv_interface_address_t* address; int i; struct sockaddr_dl *sa_addr; if (getifaddrs(&addrs)) return -errno; *count = 0; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != PF_INET)) { continue; } (*count)++; } *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return -ENOMEM; } address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) continue; if (ent->ifa_addr == NULL) continue; if (ent->ifa_addr->sa_family != PF_INET) continue; address->name = uv__strdup(ent->ifa_name); if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); } else { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } if (ent->ifa_netmask->sa_family == AF_INET6) { address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); } else { address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); } address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); address++; } /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != AF_LINK)) { continue; } address = *addresses; for (i = 0; i < (*count); i++) { if (strcmp(address->name, ent->ifa_name) == 0) { sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); } address++; } } freeifaddrs(addrs); return 0; } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; i++) { uv__free(addresses[i].name); } uv__free(addresses); } libuv-v1.8.0/src/unix/openbsd.c000066400000000000000000000215631263361716600164210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) static char *process_title; int uv__platform_loop_init(uv_loop_t* loop) { return uv__kqueue_init(loop); } void uv__platform_loop_delete(uv_loop_t* loop) { } uint64_t uv__hrtime(uv_clocktype_t type) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); } void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } int uv_exepath(char* buffer, size_t* size) { int mib[4]; char **argsbuf = NULL; char **argsbuf_tmp; size_t argsbuf_size = 100U; size_t exepath_size; pid_t mypid; int err; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; mypid = getpid(); for (;;) { err = -ENOMEM; argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size); if (argsbuf_tmp == NULL) goto out; argsbuf = argsbuf_tmp; mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; mib[2] = mypid; mib[3] = KERN_PROC_ARGV; if (sysctl(mib, 4, argsbuf, &argsbuf_size, NULL, 0) == 0) { break; } if (errno != ENOMEM) { err = -errno; goto out; } argsbuf_size *= 2U; } if (argsbuf[0] == NULL) { err = -EINVAL; /* FIXME(bnoordhuis) More appropriate error. */ goto out; } *size -= 1; exepath_size = strlen(argsbuf[0]); if (*size > exepath_size) *size = exepath_size; memcpy(buffer, argsbuf[0], *size); buffer[*size] = '\0'; err = 0; out: uv__free(argsbuf); return err; } uint64_t uv_get_free_memory(void) { struct uvmexp info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } uint64_t uv_get_total_memory(void) { uint64_t info; int which[] = {CTL_HW, HW_PHYSMEM64}; size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; return (uint64_t) info; } char** uv_setup_args(int argc, char** argv) { process_title = argc ? uv__strdup(argv[0]) : NULL; return argv; } int uv_set_process_title(const char* title) { uv__free(process_title); process_title = uv__strdup(title); setproctitle(title); return 0; } int uv_get_process_title(char* buffer, size_t size) { if (process_title) { strncpy(buffer, process_title, size); } else { if (size > 0) { buffer[0] = '\0'; } } return 0; } int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); size_t size = sizeof(struct kinfo_proc); int mib[6]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); mib[4] = sizeof(struct kinfo_proc); mib[5] = 1; if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0) return -errno; *rss = kinfo.p_vm_rssize * page_size; return 0; } int uv_uptime(double* uptime) { time_t now; struct timeval info; size_t size = sizeof(info); static int which[] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(which, 2, &info, &size, NULL, 0)) return -errno; now = time(NULL); *uptime = (double)(now - info.tv_sec); return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), multiplier = ((uint64_t)1000L / ticks), cpuspeed; uint64_t info[CPUSTATES]; char model[512]; int numcpus = 1; int which[] = {CTL_HW,HW_MODEL,0}; size_t size; int i; uv_cpu_info_t* cpu_info; size = sizeof(model); if (sysctl(which, 2, &model, &size, NULL, 0)) return -errno; which[1] = HW_NCPU; size = sizeof(numcpus); if (sysctl(which, 2, &numcpus, &size, NULL, 0)) return -errno; *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) return -ENOMEM; *count = numcpus; which[1] = HW_CPUSPEED; size = sizeof(cpuspeed); if (sysctl(which, 2, &cpuspeed, &size, NULL, 0)) { SAVE_ERRNO(uv__free(*cpu_infos)); return -errno; } size = sizeof(info); which[0] = CTL_KERN; which[1] = KERN_CPTIME2; for (i = 0; i < numcpus; i++) { which[2] = i; size = sizeof(info); if (sysctl(which, 3, &info, &size, NULL, 0)) { SAVE_ERRNO(uv__free(*cpu_infos)); return -errno; } cpu_info = &(*cpu_infos)[i]; cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier; cpu_info->cpu_times.nice = (uint64_t)(info[CP_NICE]) * multiplier; cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier; cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier; cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier; cpu_info->model = uv__strdup(model); cpu_info->speed = cpuspeed; } return 0; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs *addrs, *ent; uv_interface_address_t* address; int i; struct sockaddr_dl *sa_addr; if (getifaddrs(&addrs) != 0) return -errno; *count = 0; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != PF_INET)) { continue; } (*count)++; } *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return -ENOMEM; } address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) continue; if (ent->ifa_addr == NULL) continue; if (ent->ifa_addr->sa_family != PF_INET) continue; address->name = uv__strdup(ent->ifa_name); if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); } else { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } if (ent->ifa_netmask->sa_family == AF_INET6) { address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); } else { address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); } address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); address++; } /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != AF_LINK)) { continue; } address = *addresses; for (i = 0; i < (*count); i++) { if (strcmp(address->name, ent->ifa_name) == 0) { sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); } address++; } } freeifaddrs(addrs); return 0; } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; i++) { uv__free(addresses[i].name); } uv__free(addresses); } libuv-v1.8.0/src/unix/pipe.c000066400000000000000000000162131263361716600157200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); handle->shutdown_req = NULL; handle->connect_req = NULL; handle->pipe_fname = NULL; handle->ipc = ipc; return 0; } int uv_pipe_bind(uv_pipe_t* handle, const char* name) { struct sockaddr_un saddr; const char* pipe_fname; int sockfd; int err; pipe_fname = NULL; sockfd = -1; /* Already bound? */ if (uv__stream_fd(handle) >= 0) return -EINVAL; /* Make a copy of the file name, it outlives this function's scope. */ pipe_fname = uv__strdup(name); if (pipe_fname == NULL) return -ENOMEM; /* We've got a copy, don't touch the original any more. */ name = NULL; err = uv__socket(AF_UNIX, SOCK_STREAM, 0); if (err < 0) goto err_socket; sockfd = err; memset(&saddr, 0, sizeof saddr); strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1); saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0'; saddr.sun_family = AF_UNIX; if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) { err = -errno; /* Convert ENOENT to EACCES for compatibility with Windows. */ if (err == -ENOENT) err = -EACCES; goto err_bind; } /* Success. */ handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ handle->io_watcher.fd = sockfd; return 0; err_bind: uv__close(sockfd); err_socket: uv__free((void*)pipe_fname); return err; } int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { if (uv__stream_fd(handle) == -1) return -EINVAL; if (listen(uv__stream_fd(handle), backlog)) return -errno; handle->connection_cb = cb; handle->io_watcher.cb = uv__server_io; uv__io_start(handle->loop, &handle->io_watcher, UV__POLLIN); return 0; } void uv__pipe_close(uv_pipe_t* handle) { if (handle->pipe_fname) { /* * Unlink the file system entity before closing the file descriptor. * Doing it the other way around introduces a race where our process * unlinks a socket with the same name that's just been created by * another thread or process. */ unlink(handle->pipe_fname); uv__free((void*)handle->pipe_fname); handle->pipe_fname = NULL; } uv__stream_close((uv_stream_t*)handle); } int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { int err; err = uv__nonblock(fd, 1); if (err) return err; #if defined(__APPLE__) err = uv__stream_try_select((uv_stream_t*) handle, &fd); if (err) return err; #endif /* defined(__APPLE__) */ return uv__stream_open((uv_stream_t*)handle, fd, UV_STREAM_READABLE | UV_STREAM_WRITABLE); } void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) { struct sockaddr_un saddr; int new_sock; int err; int r; new_sock = (uv__stream_fd(handle) == -1); if (new_sock) { err = uv__socket(AF_UNIX, SOCK_STREAM, 0); if (err < 0) goto out; handle->io_watcher.fd = err; } memset(&saddr, 0, sizeof saddr); strncpy(saddr.sun_path, name, sizeof(saddr.sun_path) - 1); saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0'; saddr.sun_family = AF_UNIX; do { r = connect(uv__stream_fd(handle), (struct sockaddr*)&saddr, sizeof saddr); } while (r == -1 && errno == EINTR); if (r == -1 && errno != EINPROGRESS) { err = -errno; goto out; } err = 0; if (new_sock) { err = uv__stream_open((uv_stream_t*)handle, uv__stream_fd(handle), UV_STREAM_READABLE | UV_STREAM_WRITABLE); } if (err == 0) uv__io_start(handle->loop, &handle->io_watcher, UV__POLLIN | UV__POLLOUT); out: handle->delayed_error = err; handle->connect_req = req; uv__req_init(handle->loop, req, UV_CONNECT); req->handle = (uv_stream_t*)handle; req->cb = cb; QUEUE_INIT(&req->queue); /* Force callback to run on next tick in case of error. */ if (err) uv__io_feed(handle->loop, &handle->io_watcher); /* Mimic the Windows pipe implementation, always * return 0 and let the callback handle errors. */ } typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); static int uv__pipe_getsockpeername(const uv_pipe_t* handle, uv__peersockfunc func, char* buffer, size_t* size) { struct sockaddr_un sa; socklen_t addrlen; int err; addrlen = sizeof(sa); memset(&sa, 0, addrlen); err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen); if (err < 0) { *size = 0; return -errno; } #if defined(__linux__) if (sa.sun_path[0] == 0) /* Linux abstract namespace */ addrlen -= offsetof(struct sockaddr_un, sun_path); else #endif addrlen = strlen(sa.sun_path); if (addrlen > *size) { *size = addrlen; return UV_ENOBUFS; } memcpy(buffer, sa.sun_path, addrlen); *size = addrlen; return 0; } int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) { return uv__pipe_getsockpeername(handle, getsockname, buffer, size); } int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { return uv__pipe_getsockpeername(handle, getpeername, buffer, size); } void uv_pipe_pending_instances(uv_pipe_t* handle, int count) { } int uv_pipe_pending_count(uv_pipe_t* handle) { uv__stream_queued_fds_t* queued_fds; if (!handle->ipc) return 0; if (handle->accepted_fd == -1) return 0; if (handle->queued_fds == NULL) return 1; queued_fds = handle->queued_fds; return queued_fds->offset + 1; } uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { if (!handle->ipc) return UV_UNKNOWN_HANDLE; if (handle->accepted_fd == -1) return UV_UNKNOWN_HANDLE; else return uv__handle_type(handle->accepted_fd); } libuv-v1.8.0/src/unix/poll.c000066400000000000000000000057601263361716600157360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_poll_t* handle; int pevents; handle = container_of(w, uv_poll_t, io_watcher); if (events & UV__POLLERR) { uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT); uv__handle_stop(handle); handle->poll_cb(handle, -EBADF, 0); return; } pevents = 0; if (events & UV__POLLIN) pevents |= UV_READABLE; if (events & UV__POLLOUT) pevents |= UV_WRITABLE; handle->poll_cb(handle, 0, pevents); } int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { int err; err = uv__nonblock(fd, 1); if (err) return err; uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL); uv__io_init(&handle->io_watcher, uv__poll_io, fd); handle->poll_cb = NULL; return 0; } int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket) { return uv_poll_init(loop, handle, socket); } static void uv__poll_stop(uv_poll_t* handle) { uv__io_stop(handle->loop, &handle->io_watcher, UV__POLLIN | UV__POLLOUT); uv__handle_stop(handle); } int uv_poll_stop(uv_poll_t* handle) { assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); uv__poll_stop(handle); return 0; } int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { int events; assert((pevents & ~(UV_READABLE | UV_WRITABLE)) == 0); assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); uv__poll_stop(handle); if (pevents == 0) return 0; events = 0; if (pevents & UV_READABLE) events |= UV__POLLIN; if (pevents & UV_WRITABLE) events |= UV__POLLOUT; uv__io_start(handle->loop, &handle->io_watcher, events); uv__handle_start(handle); handle->poll_cb = poll_cb; return 0; } void uv__poll_close(uv_poll_t* handle) { uv__poll_stop(handle); } libuv-v1.8.0/src/unix/process.c000066400000000000000000000326551263361716600164510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) && !TARGET_OS_IPHONE # include # define environ (*_NSGetEnviron()) #else extern char **environ; #endif #ifdef __linux__ # include #endif static void uv__chld(uv_signal_t* handle, int signum) { uv_process_t* process; uv_loop_t* loop; int exit_status; int term_signal; int status; pid_t pid; QUEUE pending; QUEUE* q; QUEUE* h; assert(signum == SIGCHLD); QUEUE_INIT(&pending); loop = handle->loop; h = &loop->process_handles; q = QUEUE_HEAD(h); while (q != h) { process = QUEUE_DATA(q, uv_process_t, queue); q = QUEUE_NEXT(q); do pid = waitpid(process->pid, &status, WNOHANG); while (pid == -1 && errno == EINTR); if (pid == 0) continue; if (pid == -1) { if (errno != ECHILD) abort(); continue; } process->status = status; QUEUE_REMOVE(&process->queue); QUEUE_INSERT_TAIL(&pending, &process->queue); } h = &pending; q = QUEUE_HEAD(h); while (q != h) { process = QUEUE_DATA(q, uv_process_t, queue); q = QUEUE_NEXT(q); QUEUE_REMOVE(&process->queue); QUEUE_INIT(&process->queue); uv__handle_stop(process); if (process->exit_cb == NULL) continue; exit_status = 0; if (WIFEXITED(process->status)) exit_status = WEXITSTATUS(process->status); term_signal = 0; if (WIFSIGNALED(process->status)) term_signal = WTERMSIG(process->status); process->exit_cb(process, exit_status, term_signal); } assert(QUEUE_EMPTY(&pending)); } int uv__make_socketpair(int fds[2], int flags) { #if defined(__linux__) static int no_cloexec; if (no_cloexec) goto skip; if (socketpair(AF_UNIX, SOCK_STREAM | UV__SOCK_CLOEXEC | flags, 0, fds) == 0) return 0; /* Retry on EINVAL, it means SOCK_CLOEXEC is not supported. * Anything else is a genuine error. */ if (errno != EINVAL) return -errno; no_cloexec = 1; skip: #endif if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) return -errno; uv__cloexec(fds[0], 1); uv__cloexec(fds[1], 1); if (flags & UV__F_NONBLOCK) { uv__nonblock(fds[0], 1); uv__nonblock(fds[1], 1); } return 0; } int uv__make_pipe(int fds[2], int flags) { #if defined(__linux__) static int no_pipe2; if (no_pipe2) goto skip; if (uv__pipe2(fds, flags | UV__O_CLOEXEC) == 0) return 0; if (errno != ENOSYS) return -errno; no_pipe2 = 1; skip: #endif if (pipe(fds)) return -errno; uv__cloexec(fds[0], 1); uv__cloexec(fds[1], 1); if (flags & UV__F_NONBLOCK) { uv__nonblock(fds[0], 1); uv__nonblock(fds[1], 1); } return 0; } /* * Used for initializing stdio streams like options.stdin_stream. Returns * zero on success. See also the cleanup section in uv_spawn(). */ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { int mask; int fd; mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM; switch (container->flags & mask) { case UV_IGNORE: return 0; case UV_CREATE_PIPE: assert(container->data.stream != NULL); if (container->data.stream->type != UV_NAMED_PIPE) return -EINVAL; else return uv__make_socketpair(fds, 0); case UV_INHERIT_FD: case UV_INHERIT_STREAM: if (container->flags & UV_INHERIT_FD) fd = container->data.fd; else fd = uv__stream_fd(container->data.stream); if (fd == -1) return -EINVAL; fds[1] = fd; return 0; default: assert(0 && "Unexpected flags"); return -EINVAL; } } static int uv__process_open_stream(uv_stdio_container_t* container, int pipefds[2], int writable) { int flags; int err; if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0) return 0; err = uv__close(pipefds[1]); if (err != 0 && err != -EINPROGRESS) abort(); pipefds[1] = -1; uv__nonblock(pipefds[0], 1); if (container->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*)container->data.stream)->ipc) flags = UV_STREAM_READABLE | UV_STREAM_WRITABLE; else if (writable) flags = UV_STREAM_WRITABLE; else flags = UV_STREAM_READABLE; return uv__stream_open(container->data.stream, pipefds[0], flags); } static void uv__process_close_stream(uv_stdio_container_t* container) { if (!(container->flags & UV_CREATE_PIPE)) return; uv__stream_close((uv_stream_t*)container->data.stream); } static void uv__write_int(int fd, int val) { ssize_t n; do n = write(fd, &val, sizeof(val)); while (n == -1 && errno == EINTR); if (n == -1 && errno == EPIPE) return; /* parent process has quit */ assert(n == sizeof(val)); } #if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) /* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be * avoided. Since this isn't called on those targets, the function * doesn't even need to be defined for them. */ static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { int close_fd; int use_fd; int fd; if (options->flags & UV_PROCESS_DETACHED) setsid(); /* First duplicate low numbered fds, since it's not safe to duplicate them, * they could get replaced. Example: swapping stdout and stderr; without * this fd 2 (stderr) would be duplicated into fd 1, thus making both * stdout and stderr go to the same fd, which was not the intention. */ for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd < 0 || use_fd >= fd) continue; pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); if (pipes[fd][1] == -1) { uv__write_int(error_fd, -errno); _exit(127); } } for (fd = 0; fd < stdio_count; fd++) { close_fd = pipes[fd][0]; use_fd = pipes[fd][1]; if (use_fd < 0) { if (fd >= 3) continue; else { /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is * set */ use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; if (use_fd == -1) { uv__write_int(error_fd, -errno); _exit(127); } } } if (fd == use_fd) uv__cloexec(use_fd, 0); else fd = dup2(use_fd, fd); if (fd == -1) { uv__write_int(error_fd, -errno); _exit(127); } if (fd <= 2) uv__nonblock(fd, 0); if (close_fd >= stdio_count) uv__close(close_fd); } for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd >= stdio_count) uv__close(use_fd); } if (options->cwd != NULL && chdir(options->cwd)) { uv__write_int(error_fd, -errno); _exit(127); } if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will * remove any extraneous groups. If we don't call this, then * even though our uid has dropped, we may still have groups * that enable us to do super-user things. This will fail if we * aren't root, so don't bother checking the return value, this * is just done as an optimistic privilege dropping function. */ SAVE_ERRNO(setgroups(0, NULL)); } if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { uv__write_int(error_fd, -errno); _exit(127); } if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { uv__write_int(error_fd, -errno); _exit(127); } if (options->env != NULL) { environ = options->env; } execvp(options->file, options->args); uv__write_int(error_fd, -errno); _exit(127); } #endif int uv_spawn(uv_loop_t* loop, uv_process_t* process, const uv_process_options_t* options) { #if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */ return -ENOSYS; #else int signal_pipe[2] = { -1, -1 }; int (*pipes)[2]; int stdio_count; ssize_t r; pid_t pid; int err; int exec_errorno; int i; int status; assert(options->file != NULL); assert(!(options->flags & ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); QUEUE_INIT(&process->queue); stdio_count = options->stdio_count; if (stdio_count < 3) stdio_count = 3; err = -ENOMEM; pipes = uv__malloc(stdio_count * sizeof(*pipes)); if (pipes == NULL) goto error; for (i = 0; i < stdio_count; i++) { pipes[i][0] = -1; pipes[i][1] = -1; } for (i = 0; i < options->stdio_count; i++) { err = uv__process_init_stdio(options->stdio + i, pipes[i]); if (err) goto error; } /* This pipe is used by the parent to wait until * the child has called `execve()`. We need this * to avoid the following race condition: * * if ((pid = fork()) > 0) { * kill(pid, SIGTERM); * } * else if (pid == 0) { * execve("/bin/cat", argp, envp); * } * * The parent sends a signal immediately after forking. * Since the child may not have called `execve()` yet, * there is no telling what process receives the signal, * our fork or /bin/cat. * * To avoid ambiguity, we create a pipe with both ends * marked close-on-exec. Then, after the call to `fork()`, * the parent polls the read end until it EOFs or errors with EPIPE. */ err = uv__make_pipe(signal_pipe, 0); if (err) goto error; uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD); /* Acquire write lock to prevent opening new fds in worker threads */ uv_rwlock_wrlock(&loop->cloexec_lock); pid = fork(); if (pid == -1) { err = -errno; uv_rwlock_wrunlock(&loop->cloexec_lock); uv__close(signal_pipe[0]); uv__close(signal_pipe[1]); goto error; } if (pid == 0) { uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]); abort(); } /* Release lock in parent process */ uv_rwlock_wrunlock(&loop->cloexec_lock); uv__close(signal_pipe[1]); process->status = 0; exec_errorno = 0; do r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); while (r == -1 && errno == EINTR); if (r == 0) ; /* okay, EOF */ else if (r == sizeof(exec_errorno)) { do err = waitpid(pid, &status, 0); /* okay, read errorno */ while (err == -1 && errno == EINTR); assert(err == pid); } else if (r == -1 && errno == EPIPE) { do err = waitpid(pid, &status, 0); /* okay, got EPIPE */ while (err == -1 && errno == EINTR); assert(err == pid); } else abort(); uv__close(signal_pipe[0]); for (i = 0; i < options->stdio_count; i++) { err = uv__process_open_stream(options->stdio + i, pipes[i], i == 0); if (err == 0) continue; while (i--) uv__process_close_stream(options->stdio + i); goto error; } /* Only activate this handle if exec() happened successfully */ if (exec_errorno == 0) { QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue); uv__handle_start(process); } process->pid = pid; process->exit_cb = options->exit_cb; uv__free(pipes); return exec_errorno; error: if (pipes != NULL) { for (i = 0; i < stdio_count; i++) { if (i < options->stdio_count) if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM)) continue; if (pipes[i][0] != -1) close(pipes[i][0]); if (pipes[i][1] != -1) close(pipes[i][1]); } uv__free(pipes); } return err; #endif } int uv_process_kill(uv_process_t* process, int signum) { return uv_kill(process->pid, signum); } int uv_kill(int pid, int signum) { if (kill(pid, signum)) return -errno; else return 0; } void uv__process_close(uv_process_t* handle) { QUEUE_REMOVE(&handle->queue); uv__handle_stop(handle); if (QUEUE_EMPTY(&handle->loop->process_handles)) uv_signal_stop(&handle->loop->child_watcher); } libuv-v1.8.0/src/unix/proctitle.c000066400000000000000000000054361263361716600167750ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include extern void uv__set_process_title(const char* title); static void* args_mem; static struct { char* str; size_t len; } process_title; char** uv_setup_args(int argc, char** argv) { char** new_argv; size_t size; char* s; int i; if (argc <= 0) return argv; /* Calculate how much memory we need for the argv strings. */ size = 0; for (i = 0; i < argc; i++) size += strlen(argv[i]) + 1; process_title.str = argv[0]; process_title.len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0]; assert(process_title.len + 1 == size); /* argv memory should be adjacent. */ /* Add space for the argv pointers. */ size += (argc + 1) * sizeof(char*); new_argv = uv__malloc(size); if (new_argv == NULL) return argv; args_mem = new_argv; /* Copy over the strings and set up the pointer table. */ s = (char*) &new_argv[argc + 1]; for (i = 0; i < argc; i++) { size = strlen(argv[i]) + 1; memcpy(s, argv[i], size); new_argv[i] = s; s += size; } new_argv[i] = NULL; return new_argv; } int uv_set_process_title(const char* title) { if (process_title.len == 0) return 0; /* No need to terminate, byte after is always '\0'. */ strncpy(process_title.str, title, process_title.len); uv__set_process_title(title); return 0; } int uv_get_process_title(char* buffer, size_t size) { if (process_title.len > 0) strncpy(buffer, process_title.str, size); else if (size > 0) buffer[0] = '\0'; return 0; } UV_DESTRUCTOR(static void free_args_mem(void)) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } libuv-v1.8.0/src/unix/pthread-fixes.c000066400000000000000000000071471263361716600175340ustar00rootroot00000000000000/* Copyright (c) 2013, Sony Mobile Communications AB * Copyright (c) 2012, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Android versions < 4.1 have a broken pthread_sigmask. * Note that this block of code must come before any inclusion of * pthread-fixes.h so that the real pthread_sigmask can be referenced. * */ #include #include #include int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) { static int workaround; if (workaround) { return sigprocmask(how, set, oset); } else if (pthread_sigmask(how, set, oset)) { if (errno == EINVAL && sigprocmask(how, set, oset) == 0) { workaround = 1; return 0; } else { return -1; } } else { return 0; } } /*Android doesn't provide pthread_barrier_t for now.*/ #ifndef PTHREAD_BARRIER_SERIAL_THREAD #include "pthread-fixes.h" int pthread_barrier_init(pthread_barrier_t* barrier, const void* barrier_attr, unsigned count) { barrier->count = count; pthread_mutex_init(&barrier->mutex, NULL); pthread_cond_init(&barrier->cond, NULL); return 0; } int pthread_barrier_wait(pthread_barrier_t* barrier) { /* Lock the mutex*/ pthread_mutex_lock(&barrier->mutex); /* Decrement the count. If this is the first thread to reach 0, wake up waiters, unlock the mutex, then return PTHREAD_BARRIER_SERIAL_THREAD.*/ if (--barrier->count == 0) { /* First thread to reach the barrier */ pthread_cond_broadcast(&barrier->cond); pthread_mutex_unlock(&barrier->mutex); return PTHREAD_BARRIER_SERIAL_THREAD; } /* Otherwise, wait for other threads until the count reaches 0, then return 0 to indicate this is not the first thread.*/ do { pthread_cond_wait(&barrier->cond, &barrier->mutex); } while (barrier->count > 0); pthread_mutex_unlock(&barrier->mutex); return 0; } int pthread_barrier_destroy(pthread_barrier_t *barrier) { barrier->count = 0; pthread_cond_destroy(&barrier->cond); pthread_mutex_destroy(&barrier->mutex); return 0; } #endif /* defined(PTHREAD_BARRIER_SERIAL_THREAD) */ int pthread_yield(void) { sched_yield(); return 0; } libuv-v1.8.0/src/unix/signal.c000066400000000000000000000277211263361716600162460ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include typedef struct { uv_signal_t* handle; int signum; } uv__signal_msg_t; RB_HEAD(uv__signal_tree_s, uv_signal_s); static int uv__signal_unlock(void); static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2); static void uv__signal_stop(uv_signal_t* handle); static pthread_once_t uv__signal_global_init_guard = PTHREAD_ONCE_INIT; static struct uv__signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree); static int uv__signal_lock_pipefd[2]; RB_GENERATE_STATIC(uv__signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare) static void uv__signal_global_init(void) { if (uv__make_pipe(uv__signal_lock_pipefd, 0)) abort(); if (uv__signal_unlock()) abort(); } void uv__signal_global_once_init(void) { pthread_once(&uv__signal_global_init_guard, uv__signal_global_init); } static int uv__signal_lock(void) { int r; char data; do { r = read(uv__signal_lock_pipefd[0], &data, sizeof data); } while (r < 0 && errno == EINTR); return (r < 0) ? -1 : 0; } static int uv__signal_unlock(void) { int r; char data = 42; do { r = write(uv__signal_lock_pipefd[1], &data, sizeof data); } while (r < 0 && errno == EINTR); return (r < 0) ? -1 : 0; } static void uv__signal_block_and_lock(sigset_t* saved_sigmask) { sigset_t new_mask; if (sigfillset(&new_mask)) abort(); if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask)) abort(); if (uv__signal_lock()) abort(); } static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) { if (uv__signal_unlock()) abort(); if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL)) abort(); } static uv_signal_t* uv__signal_first_handle(int signum) { /* This function must be called with the signal lock held. */ uv_signal_t lookup; uv_signal_t* handle; lookup.signum = signum; lookup.loop = NULL; handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup); if (handle != NULL && handle->signum == signum) return handle; return NULL; } static void uv__signal_handler(int signum) { uv__signal_msg_t msg; uv_signal_t* handle; int saved_errno; saved_errno = errno; memset(&msg, 0, sizeof msg); if (uv__signal_lock()) { errno = saved_errno; return; } for (handle = uv__signal_first_handle(signum); handle != NULL && handle->signum == signum; handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) { int r; msg.signum = signum; msg.handle = handle; /* write() should be atomic for small data chunks, so the entire message * should be written at once. In theory the pipe could become full, in * which case the user is out of luck. */ do { r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg); } while (r == -1 && errno == EINTR); assert(r == sizeof msg || (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))); if (r != -1) handle->caught_signals++; } uv__signal_unlock(); errno = saved_errno; } static int uv__signal_register_handler(int signum) { /* When this function is called, the signal lock must be held. */ struct sigaction sa; /* XXX use a separate signal stack? */ memset(&sa, 0, sizeof(sa)); if (sigfillset(&sa.sa_mask)) abort(); sa.sa_handler = uv__signal_handler; /* XXX save old action so we can restore it later on? */ if (sigaction(signum, &sa, NULL)) return -errno; return 0; } static void uv__signal_unregister_handler(int signum) { /* When this function is called, the signal lock must be held. */ struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; /* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a * signal implies that it was successfully registered earlier, so EINVAL * should never happen. */ if (sigaction(signum, &sa, NULL)) abort(); } static int uv__signal_loop_once_init(uv_loop_t* loop) { int err; /* Return if already initialized. */ if (loop->signal_pipefd[0] != -1) return 0; err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK); if (err) return err; uv__io_init(&loop->signal_io_watcher, uv__signal_event, loop->signal_pipefd[0]); uv__io_start(loop, &loop->signal_io_watcher, UV__POLLIN); return 0; } void uv__signal_loop_cleanup(uv_loop_t* loop) { QUEUE* q; /* Stop all the signal watchers that are still attached to this loop. This * ensures that the (shared) signal tree doesn't contain any invalid entries * entries, and that signal handlers are removed when appropriate. * It's safe to use QUEUE_FOREACH here because the handles and the handle * queue are not modified by uv__signal_stop(). */ QUEUE_FOREACH(q, &loop->handle_queue) { uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue); if (handle->type == UV_SIGNAL) uv__signal_stop((uv_signal_t*) handle); } if (loop->signal_pipefd[0] != -1) { uv__close(loop->signal_pipefd[0]); loop->signal_pipefd[0] = -1; } if (loop->signal_pipefd[1] != -1) { uv__close(loop->signal_pipefd[1]); loop->signal_pipefd[1] = -1; } } int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { int err; err = uv__signal_loop_once_init(loop); if (err) return err; uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL); handle->signum = 0; handle->caught_signals = 0; handle->dispatched_signals = 0; return 0; } void uv__signal_close(uv_signal_t* handle) { uv__signal_stop(handle); /* If there are any caught signals "trapped" in the signal pipe, we can't * call the close callback yet. Otherwise, add the handle to the finish_close * queue. */ if (handle->caught_signals == handle->dispatched_signals) { uv__make_close_pending((uv_handle_t*) handle); } } int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { sigset_t saved_sigmask; int err; assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); /* If the user supplies signum == 0, then return an error already. If the * signum is otherwise invalid then uv__signal_register will find out * eventually. */ if (signum == 0) return -EINVAL; /* Short circuit: if the signal watcher is already watching {signum} don't * go through the process of deregistering and registering the handler. * Additionally, this avoids pending signals getting lost in the small time * time frame that handle->signum == 0. */ if (signum == handle->signum) { handle->signal_cb = signal_cb; return 0; } /* If the signal handler was already active, stop it first. */ if (handle->signum != 0) { uv__signal_stop(handle); } uv__signal_block_and_lock(&saved_sigmask); /* If at this point there are no active signal watchers for this signum (in * any of the loops), it's time to try and register a handler for it here. */ if (uv__signal_first_handle(signum) == NULL) { err = uv__signal_register_handler(signum); if (err) { /* Registering the signal handler failed. Must be an invalid signal. */ uv__signal_unlock_and_unblock(&saved_sigmask); return err; } } handle->signum = signum; RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle); uv__signal_unlock_and_unblock(&saved_sigmask); handle->signal_cb = signal_cb; uv__handle_start(handle); return 0; } static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv__signal_msg_t* msg; uv_signal_t* handle; char buf[sizeof(uv__signal_msg_t) * 32]; size_t bytes, end, i; int r; bytes = 0; end = 0; do { r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes); if (r == -1 && errno == EINTR) continue; if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { /* If there are bytes in the buffer already (which really is extremely * unlikely if possible at all) we can't exit the function here. We'll * spin until more bytes are read instead. */ if (bytes > 0) continue; /* Otherwise, there was nothing there. */ return; } /* Other errors really should never happen. */ if (r == -1) abort(); bytes += r; /* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */ end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t); for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) { msg = (uv__signal_msg_t*) (buf + i); handle = msg->handle; if (msg->signum == handle->signum) { assert(!(handle->flags & UV_CLOSING)); handle->signal_cb(handle, handle->signum); } handle->dispatched_signals++; /* If uv_close was called while there were caught signals that were not * yet dispatched, the uv__finish_close was deferred. Make close pending * now if this has happened. */ if ((handle->flags & UV_CLOSING) && (handle->caught_signals == handle->dispatched_signals)) { uv__make_close_pending((uv_handle_t*) handle); } } bytes -= end; /* If there are any "partial" messages left, move them to the start of the * the buffer, and spin. This should not happen. */ if (bytes) { memmove(buf, buf + end, bytes); continue; } } while (end == sizeof buf); } static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { /* Compare signums first so all watchers with the same signnum end up * adjacent. */ if (w1->signum < w2->signum) return -1; if (w1->signum > w2->signum) return 1; /* Sort by loop pointer, so we can easily look up the first item after * { .signum = x, .loop = NULL }. */ if (w1->loop < w2->loop) return -1; if (w1->loop > w2->loop) return 1; if (w1 < w2) return -1; if (w1 > w2) return 1; return 0; } int uv_signal_stop(uv_signal_t* handle) { assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); uv__signal_stop(handle); return 0; } static void uv__signal_stop(uv_signal_t* handle) { uv_signal_t* removed_handle; sigset_t saved_sigmask; /* If the watcher wasn't started, this is a no-op. */ if (handle->signum == 0) return; uv__signal_block_and_lock(&saved_sigmask); removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle); assert(removed_handle == handle); (void) removed_handle; /* Check if there are other active signal watchers observing this signal. If * not, unregister the signal handler. */ if (uv__signal_first_handle(handle->signum) == NULL) uv__signal_unregister_handler(handle->signum); uv__signal_unlock_and_unblock(&saved_sigmask); handle->signum = 0; uv__handle_stop(handle); } libuv-v1.8.0/src/unix/spinlock.h000066400000000000000000000036501263361716600166130ustar00rootroot00000000000000/* Copyright (c) 2013, Ben Noordhuis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef UV_SPINLOCK_H_ #define UV_SPINLOCK_H_ #include "internal.h" /* ACCESS_ONCE, UV_UNUSED */ #include "atomic-ops.h" #define UV_SPINLOCK_INITIALIZER { 0 } typedef struct { int lock; } uv_spinlock_t; UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)); UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)); UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)); UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)); UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) { ACCESS_ONCE(int, spinlock->lock) = 0; } UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) { while (!uv_spinlock_trylock(spinlock)) cpu_relax(); } UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) { ACCESS_ONCE(int, spinlock->lock) = 0; } UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) { /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing. * Not really critical until we have locks that are (frequently) contended * for by several threads. */ return 0 == cmpxchgi(&spinlock->lock, 0, 1); } #endif /* UV_SPINLOCK_H_ */ libuv-v1.8.0/src/unix/stream.c000066400000000000000000001223041263361716600162550ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include /* IOV_MAX */ #if defined(__APPLE__) # include # include # include /* Forward declaration */ typedef struct uv__stream_select_s uv__stream_select_t; struct uv__stream_select_s { uv_stream_t* stream; uv_thread_t thread; uv_sem_t close_sem; uv_sem_t async_sem; uv_async_t async; int events; int fake_fd; int int_fd; int fd; fd_set* sread; size_t sread_sz; fd_set* swrite; size_t swrite_sz; }; #endif /* defined(__APPLE__) */ static void uv__stream_connect(uv_stream_t*); static void uv__write(uv_stream_t* stream); static void uv__read(uv_stream_t* stream); static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); static void uv__write_callbacks(uv_stream_t* stream); static size_t uv__write_req_size(uv_write_t* req); void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type) { int err; uv__handle_init(loop, (uv_handle_t*)stream, type); stream->read_cb = NULL; stream->alloc_cb = NULL; stream->close_cb = NULL; stream->connection_cb = NULL; stream->connect_req = NULL; stream->shutdown_req = NULL; stream->accepted_fd = -1; stream->queued_fds = NULL; stream->delayed_error = 0; QUEUE_INIT(&stream->write_queue); QUEUE_INIT(&stream->write_completed_queue); stream->write_queue_size = 0; if (loop->emfile_fd == -1) { err = uv__open_cloexec("/dev/null", O_RDONLY); if (err < 0) /* In the rare case that "/dev/null" isn't mounted open "/" * instead. */ err = uv__open_cloexec("/", O_RDONLY); if (err >= 0) loop->emfile_fd = err; } #if defined(__APPLE__) stream->select = NULL; #endif /* defined(__APPLE_) */ uv__io_init(&stream->io_watcher, uv__stream_io, -1); } static void uv__stream_osx_interrupt_select(uv_stream_t* stream) { #if defined(__APPLE__) /* Notify select() thread about state change */ uv__stream_select_t* s; int r; s = stream->select; if (s == NULL) return; /* Interrupt select() loop * NOTE: fake_fd and int_fd are socketpair(), thus writing to one will * emit read event on other side */ do r = write(s->fake_fd, "x", 1); while (r == -1 && errno == EINTR); assert(r == 1); #else /* !defined(__APPLE__) */ /* No-op on any other platform */ #endif /* !defined(__APPLE__) */ } #if defined(__APPLE__) static void uv__stream_osx_select(void* arg) { uv_stream_t* stream; uv__stream_select_t* s; char buf[1024]; int events; int fd; int r; int max_fd; stream = arg; s = stream->select; fd = s->fd; if (fd > s->int_fd) max_fd = fd; else max_fd = s->int_fd; while (1) { /* Terminate on semaphore */ if (uv_sem_trywait(&s->close_sem) == 0) break; /* Watch fd using select(2) */ memset(s->sread, 0, s->sread_sz); memset(s->swrite, 0, s->swrite_sz); if (uv__io_active(&stream->io_watcher, UV__POLLIN)) FD_SET(fd, s->sread); if (uv__io_active(&stream->io_watcher, UV__POLLOUT)) FD_SET(fd, s->swrite); FD_SET(s->int_fd, s->sread); /* Wait indefinitely for fd events */ r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL); if (r == -1) { if (errno == EINTR) continue; /* XXX: Possible?! */ abort(); } /* Ignore timeouts */ if (r == 0) continue; /* Empty socketpair's buffer in case of interruption */ if (FD_ISSET(s->int_fd, s->sread)) while (1) { r = read(s->int_fd, buf, sizeof(buf)); if (r == sizeof(buf)) continue; if (r != -1) break; if (errno == EAGAIN || errno == EWOULDBLOCK) break; if (errno == EINTR) continue; abort(); } /* Handle events */ events = 0; if (FD_ISSET(fd, s->sread)) events |= UV__POLLIN; if (FD_ISSET(fd, s->swrite)) events |= UV__POLLOUT; assert(events != 0 || FD_ISSET(s->int_fd, s->sread)); if (events != 0) { ACCESS_ONCE(int, s->events) = events; uv_async_send(&s->async); uv_sem_wait(&s->async_sem); /* Should be processed at this stage */ assert((s->events == 0) || (stream->flags & UV_CLOSING)); } } } static void uv__stream_osx_select_cb(uv_async_t* handle) { uv__stream_select_t* s; uv_stream_t* stream; int events; s = container_of(handle, uv__stream_select_t, async); stream = s->stream; /* Get and reset stream's events */ events = s->events; ACCESS_ONCE(int, s->events) = 0; assert(events != 0); assert(events == (events & (UV__POLLIN | UV__POLLOUT))); /* Invoke callback on event-loop */ if ((events & UV__POLLIN) && uv__io_active(&stream->io_watcher, UV__POLLIN)) uv__stream_io(stream->loop, &stream->io_watcher, UV__POLLIN); if ((events & UV__POLLOUT) && uv__io_active(&stream->io_watcher, UV__POLLOUT)) uv__stream_io(stream->loop, &stream->io_watcher, UV__POLLOUT); if (stream->flags & UV_CLOSING) return; /* NOTE: It is important to do it here, otherwise `select()` might be called * before the actual `uv__read()`, leading to the blocking syscall */ uv_sem_post(&s->async_sem); } static void uv__stream_osx_cb_close(uv_handle_t* async) { uv__stream_select_t* s; s = container_of(async, uv__stream_select_t, async); uv__free(s); } int uv__stream_try_select(uv_stream_t* stream, int* fd) { /* * kqueue doesn't work with some files from /dev mount on osx. * select(2) in separate thread for those fds */ struct kevent filter[1]; struct kevent events[1]; struct timespec timeout; uv__stream_select_t* s; int fds[2]; int err; int ret; int kq; int old_fd; int max_fd; size_t sread_sz; size_t swrite_sz; kq = kqueue(); if (kq == -1) { perror("(libuv) kqueue()"); return -errno; } EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); /* Use small timeout, because we only want to capture EINVALs */ timeout.tv_sec = 0; timeout.tv_nsec = 1; ret = kevent(kq, filter, 1, events, 1, &timeout); uv__close(kq); if (ret == -1) return -errno; if (ret == 0 || (events[0].flags & EV_ERROR) == 0 || events[0].data != EINVAL) return 0; /* At this point we definitely know that this fd won't work with kqueue */ /* * Create fds for io watcher and to interrupt the select() loop. * NOTE: do it ahead of malloc below to allocate enough space for fd_sets */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) return -errno; max_fd = *fd; if (fds[1] > max_fd) max_fd = fds[1]; sread_sz = ROUND_UP(max_fd + 1, sizeof(uint32_t) * NBBY) / NBBY; swrite_sz = sread_sz; s = uv__malloc(sizeof(*s) + sread_sz + swrite_sz); if (s == NULL) { err = -ENOMEM; goto failed_malloc; } s->events = 0; s->fd = *fd; s->sread = (fd_set*) ((char*) s + sizeof(*s)); s->sread_sz = sread_sz; s->swrite = (fd_set*) ((char*) s->sread + sread_sz); s->swrite_sz = swrite_sz; err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb); if (err) goto failed_async_init; s->async.flags |= UV__HANDLE_INTERNAL; uv__handle_unref(&s->async); err = uv_sem_init(&s->close_sem, 0); if (err != 0) goto failed_close_sem_init; err = uv_sem_init(&s->async_sem, 0); if (err != 0) goto failed_async_sem_init; s->fake_fd = fds[0]; s->int_fd = fds[1]; old_fd = *fd; s->stream = stream; stream->select = s; *fd = s->fake_fd; err = uv_thread_create(&s->thread, uv__stream_osx_select, stream); if (err != 0) goto failed_thread_create; return 0; failed_thread_create: s->stream = NULL; stream->select = NULL; *fd = old_fd; uv_sem_destroy(&s->async_sem); failed_async_sem_init: uv_sem_destroy(&s->close_sem); failed_close_sem_init: uv__close(fds[0]); uv__close(fds[1]); uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); return err; failed_async_init: uv__free(s); failed_malloc: uv__close(fds[0]); uv__close(fds[1]); return err; } #endif /* defined(__APPLE__) */ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { #if defined(__APPLE__) int enable; #endif if (!(stream->io_watcher.fd == -1 || stream->io_watcher.fd == fd)) return -EBUSY; assert(fd >= 0); stream->flags |= flags; if (stream->type == UV_TCP) { if ((stream->flags & UV_TCP_NODELAY) && uv__tcp_nodelay(fd, 1)) return -errno; /* TODO Use delay the user passed in. */ if ((stream->flags & UV_TCP_KEEPALIVE) && uv__tcp_keepalive(fd, 1, 60)) return -errno; } #if defined(__APPLE__) enable = 1; if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) && errno != ENOTSOCK && errno != EINVAL) { return -errno; } #endif stream->io_watcher.fd = fd; return 0; } void uv__stream_flush_write_queue(uv_stream_t* stream, int error) { uv_write_t* req; QUEUE* q; while (!QUEUE_EMPTY(&stream->write_queue)) { q = QUEUE_HEAD(&stream->write_queue); QUEUE_REMOVE(q); req = QUEUE_DATA(q, uv_write_t, queue); req->error = error; QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); } } void uv__stream_destroy(uv_stream_t* stream) { assert(!uv__io_active(&stream->io_watcher, UV__POLLIN | UV__POLLOUT)); assert(stream->flags & UV_CLOSED); if (stream->connect_req) { uv__req_unregister(stream->loop, stream->connect_req); stream->connect_req->cb(stream->connect_req, -ECANCELED); stream->connect_req = NULL; } uv__stream_flush_write_queue(stream, -ECANCELED); uv__write_callbacks(stream); if (stream->shutdown_req) { /* The ECANCELED error code is a lie, the shutdown(2) syscall is a * fait accompli at this point. Maybe we should revisit this in v0.11. * A possible reason for leaving it unchanged is that it informs the * callee that the handle has been destroyed. */ uv__req_unregister(stream->loop, stream->shutdown_req); stream->shutdown_req->cb(stream->shutdown_req, -ECANCELED); stream->shutdown_req = NULL; } assert(stream->write_queue_size == 0); } /* Implements a best effort approach to mitigating accept() EMFILE errors. * We have a spare file descriptor stashed away that we close to get below * the EMFILE limit. Next, we accept all pending connections and close them * immediately to signal the clients that we're overloaded - and we are, but * we still keep on trucking. * * There is one caveat: it's not reliable in a multi-threaded environment. * The file descriptor limit is per process. Our party trick fails if another * thread opens a file or creates a socket in the time window between us * calling close() and accept(). */ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { int err; int emfile_fd; if (loop->emfile_fd == -1) return -EMFILE; uv__close(loop->emfile_fd); loop->emfile_fd = -1; do { err = uv__accept(accept_fd); if (err >= 0) uv__close(err); } while (err >= 0 || err == -EINTR); emfile_fd = uv__open_cloexec("/", O_RDONLY); if (emfile_fd >= 0) loop->emfile_fd = emfile_fd; return err; } #if defined(UV_HAVE_KQUEUE) # define UV_DEC_BACKLOG(w) w->rcount--; #else # define UV_DEC_BACKLOG(w) /* no-op */ #endif /* defined(UV_HAVE_KQUEUE) */ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_stream_t* stream; int err; stream = container_of(w, uv_stream_t, io_watcher); assert(events == UV__POLLIN); assert(stream->accepted_fd == -1); assert(!(stream->flags & UV_CLOSING)); uv__io_start(stream->loop, &stream->io_watcher, UV__POLLIN); /* connection_cb can close the server socket while we're * in the loop so check it on each iteration. */ while (uv__stream_fd(stream) != -1) { assert(stream->accepted_fd == -1); #if defined(UV_HAVE_KQUEUE) if (w->rcount <= 0) return; #endif /* defined(UV_HAVE_KQUEUE) */ err = uv__accept(uv__stream_fd(stream)); if (err < 0) { if (err == -EAGAIN || err == -EWOULDBLOCK) return; /* Not an error. */ if (err == -ECONNABORTED) continue; /* Ignore. Nothing we can do about that. */ if (err == -EMFILE || err == -ENFILE) { err = uv__emfile_trick(loop, uv__stream_fd(stream)); if (err == -EAGAIN || err == -EWOULDBLOCK) break; } stream->connection_cb(stream, err); continue; } UV_DEC_BACKLOG(w) stream->accepted_fd = err; stream->connection_cb(stream, 0); if (stream->accepted_fd != -1) { /* The user hasn't yet accepted called uv_accept() */ uv__io_stop(loop, &stream->io_watcher, UV__POLLIN); return; } if (stream->type == UV_TCP && (stream->flags & UV_TCP_SINGLE_ACCEPT)) { /* Give other processes a chance to accept connections. */ struct timespec timeout = { 0, 1 }; nanosleep(&timeout, NULL); } } } #undef UV_DEC_BACKLOG int uv_accept(uv_stream_t* server, uv_stream_t* client) { int err; /* TODO document this */ assert(server->loop == client->loop); if (server->accepted_fd == -1) return -EAGAIN; switch (client->type) { case UV_NAMED_PIPE: case UV_TCP: err = uv__stream_open(client, server->accepted_fd, UV_STREAM_READABLE | UV_STREAM_WRITABLE); if (err) { /* TODO handle error */ uv__close(server->accepted_fd); goto done; } break; case UV_UDP: err = uv_udp_open((uv_udp_t*) client, server->accepted_fd); if (err) { uv__close(server->accepted_fd); goto done; } break; default: return -EINVAL; } done: /* Process queued fds */ if (server->queued_fds != NULL) { uv__stream_queued_fds_t* queued_fds; queued_fds = server->queued_fds; /* Read first */ server->accepted_fd = queued_fds->fds[0]; /* All read, free */ assert(queued_fds->offset > 0); if (--queued_fds->offset == 0) { uv__free(queued_fds); server->queued_fds = NULL; } else { /* Shift rest */ memmove(queued_fds->fds, queued_fds->fds + 1, queued_fds->offset * sizeof(*queued_fds->fds)); } } else { server->accepted_fd = -1; if (err == 0) uv__io_start(server->loop, &server->io_watcher, UV__POLLIN); } return err; } int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { int err; switch (stream->type) { case UV_TCP: err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); break; case UV_NAMED_PIPE: err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); break; default: err = -EINVAL; } if (err == 0) uv__handle_start(stream); return err; } static void uv__drain(uv_stream_t* stream) { uv_shutdown_t* req; int err; assert(QUEUE_EMPTY(&stream->write_queue)); uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLOUT); uv__stream_osx_interrupt_select(stream); /* Shutdown? */ if ((stream->flags & UV_STREAM_SHUTTING) && !(stream->flags & UV_CLOSING) && !(stream->flags & UV_STREAM_SHUT)) { assert(stream->shutdown_req); req = stream->shutdown_req; stream->shutdown_req = NULL; stream->flags &= ~UV_STREAM_SHUTTING; uv__req_unregister(stream->loop, req); err = 0; if (shutdown(uv__stream_fd(stream), SHUT_WR)) err = -errno; if (err == 0) stream->flags |= UV_STREAM_SHUT; if (req->cb != NULL) req->cb(req, err); } } static size_t uv__write_req_size(uv_write_t* req) { size_t size; assert(req->bufs != NULL); size = uv__count_bufs(req->bufs + req->write_index, req->nbufs - req->write_index); assert(req->handle->write_queue_size >= size); return size; } static void uv__write_req_finish(uv_write_t* req) { uv_stream_t* stream = req->handle; /* Pop the req off tcp->write_queue. */ QUEUE_REMOVE(&req->queue); /* Only free when there was no error. On error, we touch up write_queue_size * right before making the callback. The reason we don't do that right away * is that a write_queue_size > 0 is our only way to signal to the user that * they should stop writing - which they should if we got an error. Something * to revisit in future revisions of the libuv API. */ if (req->error == 0) { if (req->bufs != req->bufsml) uv__free(req->bufs); req->bufs = NULL; } /* Add it to the write_completed_queue where it will have its * callback called in the near future. */ QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); uv__io_feed(stream->loop, &stream->io_watcher); } static int uv__handle_fd(uv_handle_t* handle) { switch (handle->type) { case UV_NAMED_PIPE: case UV_TCP: return ((uv_stream_t*) handle)->io_watcher.fd; case UV_UDP: return ((uv_udp_t*) handle)->io_watcher.fd; default: return -1; } } static void uv__write(uv_stream_t* stream) { struct iovec* iov; QUEUE* q; uv_write_t* req; int iovmax; int iovcnt; ssize_t n; start: assert(uv__stream_fd(stream) >= 0); if (QUEUE_EMPTY(&stream->write_queue)) return; q = QUEUE_HEAD(&stream->write_queue); req = QUEUE_DATA(q, uv_write_t, queue); assert(req->handle == stream); /* * Cast to iovec. We had to have our own uv_buf_t instead of iovec * because Windows's WSABUF is not an iovec. */ assert(sizeof(uv_buf_t) == sizeof(struct iovec)); iov = (struct iovec*) &(req->bufs[req->write_index]); iovcnt = req->nbufs - req->write_index; iovmax = uv__getiovmax(); /* Limit iov count to avoid EINVALs from writev() */ if (iovcnt > iovmax) iovcnt = iovmax; /* * Now do the actual writev. Note that we've been updating the pointers * inside the iov each time we write. So there is no need to offset it. */ if (req->send_handle) { struct msghdr msg; struct cmsghdr *cmsg; int fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle); char scratch[64] = {0}; assert(fd_to_send >= 0); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = iovcnt; msg.msg_flags = 0; msg.msg_control = (void*) scratch; msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send)); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send)); /* silence aliasing warning */ { void* pv = CMSG_DATA(cmsg); int* pi = pv; *pi = fd_to_send; } do { n = sendmsg(uv__stream_fd(stream), &msg, 0); } #if defined(__APPLE__) /* * Due to a possible kernel bug at least in OS X 10.10 "Yosemite", * EPROTOTYPE can be returned while trying to write to a socket that is * shutting down. If we retry the write, we should get the expected EPIPE * instead. */ while (n == -1 && (errno == EINTR || errno == EPROTOTYPE)); #else while (n == -1 && errno == EINTR); #endif } else { do { if (iovcnt == 1) { n = write(uv__stream_fd(stream), iov[0].iov_base, iov[0].iov_len); } else { n = writev(uv__stream_fd(stream), iov, iovcnt); } } #if defined(__APPLE__) /* * Due to a possible kernel bug at least in OS X 10.10 "Yosemite", * EPROTOTYPE can be returned while trying to write to a socket that is * shutting down. If we retry the write, we should get the expected EPIPE * instead. */ while (n == -1 && (errno == EINTR || errno == EPROTOTYPE)); #else while (n == -1 && errno == EINTR); #endif } if (n < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { /* Error */ req->error = -errno; uv__write_req_finish(req); uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLOUT); if (!uv__io_active(&stream->io_watcher, UV__POLLIN)) uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); return; } else if (stream->flags & UV_STREAM_BLOCKING) { /* If this is a blocking stream, try again. */ goto start; } } else { /* Successful write */ while (n >= 0) { uv_buf_t* buf = &(req->bufs[req->write_index]); size_t len = buf->len; assert(req->write_index < req->nbufs); if ((size_t)n < len) { buf->base += n; buf->len -= n; stream->write_queue_size -= n; n = 0; /* There is more to write. */ if (stream->flags & UV_STREAM_BLOCKING) { /* * If we're blocking then we should not be enabling the write * watcher - instead we need to try again. */ goto start; } else { /* Break loop and ensure the watcher is pending. */ break; } } else { /* Finished writing the buf at index req->write_index. */ req->write_index++; assert((size_t)n >= len); n -= len; assert(stream->write_queue_size >= len); stream->write_queue_size -= len; if (req->write_index == req->nbufs) { /* Then we're done! */ assert(n == 0); uv__write_req_finish(req); /* TODO: start trying to write the next request. */ return; } } } } /* Either we've counted n down to zero or we've got EAGAIN. */ assert(n == 0 || n == -1); /* Only non-blocking streams should use the write_watcher. */ assert(!(stream->flags & UV_STREAM_BLOCKING)); /* We're not done. */ uv__io_start(stream->loop, &stream->io_watcher, UV__POLLOUT); /* Notify select() thread about state change */ uv__stream_osx_interrupt_select(stream); } static void uv__write_callbacks(uv_stream_t* stream) { uv_write_t* req; QUEUE* q; while (!QUEUE_EMPTY(&stream->write_completed_queue)) { /* Pop a req off write_completed_queue. */ q = QUEUE_HEAD(&stream->write_completed_queue); req = QUEUE_DATA(q, uv_write_t, queue); QUEUE_REMOVE(q); uv__req_unregister(stream->loop, req); if (req->bufs != NULL) { stream->write_queue_size -= uv__write_req_size(req); if (req->bufs != req->bufsml) uv__free(req->bufs); req->bufs = NULL; } /* NOTE: call callback AFTER freeing the request data. */ if (req->cb) req->cb(req, req->error); } assert(QUEUE_EMPTY(&stream->write_completed_queue)); } uv_handle_type uv__handle_type(int fd) { struct sockaddr_storage ss; socklen_t len; int type; memset(&ss, 0, sizeof(ss)); len = sizeof(ss); if (getsockname(fd, (struct sockaddr*)&ss, &len)) return UV_UNKNOWN_HANDLE; len = sizeof type; if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len)) return UV_UNKNOWN_HANDLE; if (type == SOCK_STREAM) { switch (ss.ss_family) { case AF_UNIX: return UV_NAMED_PIPE; case AF_INET: case AF_INET6: return UV_TCP; } } if (type == SOCK_DGRAM && (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)) return UV_UDP; return UV_UNKNOWN_HANDLE; } static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { stream->flags |= UV_STREAM_READ_EOF; uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLIN); if (!uv__io_active(&stream->io_watcher, UV__POLLOUT)) uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb(stream, UV_EOF, buf); stream->flags &= ~UV_STREAM_READING; } static int uv__stream_queue_fd(uv_stream_t* stream, int fd) { uv__stream_queued_fds_t* queued_fds; unsigned int queue_size; queued_fds = stream->queued_fds; if (queued_fds == NULL) { queue_size = 8; queued_fds = uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) + sizeof(*queued_fds)); if (queued_fds == NULL) return -ENOMEM; queued_fds->size = queue_size; queued_fds->offset = 0; stream->queued_fds = queued_fds; /* Grow */ } else if (queued_fds->size == queued_fds->offset) { queue_size = queued_fds->size + 8; queued_fds = uv__realloc(queued_fds, (queue_size - 1) * sizeof(*queued_fds->fds) + sizeof(*queued_fds)); /* * Allocation failure, report back. * NOTE: if it is fatal - sockets will be closed in uv__stream_close */ if (queued_fds == NULL) return -ENOMEM; queued_fds->size = queue_size; stream->queued_fds = queued_fds; } /* Put fd in a queue */ queued_fds->fds[queued_fds->offset++] = fd; return 0; } #define UV__CMSG_FD_COUNT 64 #define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int)) static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) { struct cmsghdr* cmsg; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { char* start; char* end; int err; void* pv; int* pi; unsigned int i; unsigned int count; if (cmsg->cmsg_type != SCM_RIGHTS) { fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n", cmsg->cmsg_type); continue; } /* silence aliasing warning */ pv = CMSG_DATA(cmsg); pi = pv; /* Count available fds */ start = (char*) cmsg; end = (char*) cmsg + cmsg->cmsg_len; count = 0; while (start + CMSG_LEN(count * sizeof(*pi)) < end) count++; assert(start + CMSG_LEN(count * sizeof(*pi)) == end); for (i = 0; i < count; i++) { /* Already has accepted fd, queue now */ if (stream->accepted_fd != -1) { err = uv__stream_queue_fd(stream, pi[i]); if (err != 0) { /* Close rest */ for (; i < count; i++) uv__close(pi[i]); return err; } } else { stream->accepted_fd = pi[i]; } } } return 0; } static void uv__read(uv_stream_t* stream) { uv_buf_t buf; ssize_t nread; struct msghdr msg; char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)]; int count; int err; int is_ipc; stream->flags &= ~UV_STREAM_READ_PARTIAL; /* Prevent loop starvation when the data comes in as fast as (or faster than) * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O. */ count = 32; is_ipc = stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) stream)->ipc; /* XXX: Maybe instead of having UV_STREAM_READING we just test if * tcp->read_cb is NULL or not? */ while (stream->read_cb && (stream->flags & UV_STREAM_READING) && (count-- > 0)) { assert(stream->alloc_cb != NULL); stream->alloc_cb((uv_handle_t*)stream, 64 * 1024, &buf); if (buf.len == 0) { /* User indicates it can't or won't handle the read. */ stream->read_cb(stream, UV_ENOBUFS, &buf); return; } assert(buf.base != NULL); assert(uv__stream_fd(stream) >= 0); if (!is_ipc) { do { nread = read(uv__stream_fd(stream), buf.base, buf.len); } while (nread < 0 && errno == EINTR); } else { /* ipc uses recvmsg */ msg.msg_flags = 0; msg.msg_iov = (struct iovec*) &buf; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; /* Set up to receive a descriptor even if one isn't in the message */ msg.msg_controllen = sizeof(cmsg_space); msg.msg_control = cmsg_space; do { nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); } while (nread < 0 && errno == EINTR); } if (nread < 0) { /* Error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { /* Wait for the next one. */ if (stream->flags & UV_STREAM_READING) { uv__io_start(stream->loop, &stream->io_watcher, UV__POLLIN); uv__stream_osx_interrupt_select(stream); } stream->read_cb(stream, 0, &buf); } else { /* Error. User should call uv_close(). */ stream->read_cb(stream, -errno, &buf); if (stream->flags & UV_STREAM_READING) { stream->flags &= ~UV_STREAM_READING; uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLIN); if (!uv__io_active(&stream->io_watcher, UV__POLLOUT)) uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); } } return; } else if (nread == 0) { uv__stream_eof(stream, &buf); return; } else { /* Successful read */ ssize_t buflen = buf.len; if (is_ipc) { err = uv__stream_recv_cmsg(stream, &msg); if (err != 0) { stream->read_cb(stream, err, &buf); return; } } stream->read_cb(stream, nread, &buf); /* Return if we didn't fill the buffer, there is no more data to read. */ if (nread < buflen) { stream->flags |= UV_STREAM_READ_PARTIAL; return; } } } } #undef UV__CMSG_FD_COUNT #undef UV__CMSG_FD_SIZE int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE) && "uv_shutdown (unix) only supports uv_handle_t right now"); if (!(stream->flags & UV_STREAM_WRITABLE) || stream->flags & UV_STREAM_SHUT || stream->flags & UV_STREAM_SHUTTING || stream->flags & UV_CLOSED || stream->flags & UV_CLOSING) { return -ENOTCONN; } assert(uv__stream_fd(stream) >= 0); /* Initialize request */ uv__req_init(stream->loop, req, UV_SHUTDOWN); req->handle = stream; req->cb = cb; stream->shutdown_req = req; stream->flags |= UV_STREAM_SHUTTING; uv__io_start(stream->loop, &stream->io_watcher, UV__POLLOUT); uv__stream_osx_interrupt_select(stream); return 0; } static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_stream_t* stream; stream = container_of(w, uv_stream_t, io_watcher); assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); assert(!(stream->flags & UV_CLOSING)); if (stream->connect_req) { uv__stream_connect(stream); return; } assert(uv__stream_fd(stream) >= 0); /* Ignore POLLHUP here. Even it it's set, there may still be data to read. */ if (events & (UV__POLLIN | UV__POLLERR | UV__POLLHUP)) uv__read(stream); if (uv__stream_fd(stream) == -1) return; /* read_cb closed stream. */ /* Short-circuit iff POLLHUP is set, the user is still interested in read * events and uv__read() reported a partial read but not EOF. If the EOF * flag is set, uv__read() called read_cb with err=UV_EOF and we don't * have to do anything. If the partial read flag is not set, we can't * report the EOF yet because there is still data to read. */ if ((events & UV__POLLHUP) && (stream->flags & UV_STREAM_READING) && (stream->flags & UV_STREAM_READ_PARTIAL) && !(stream->flags & UV_STREAM_READ_EOF)) { uv_buf_t buf = { NULL, 0 }; uv__stream_eof(stream, &buf); } if (uv__stream_fd(stream) == -1) return; /* read_cb closed stream. */ if (events & (UV__POLLOUT | UV__POLLERR | UV__POLLHUP)) { uv__write(stream); uv__write_callbacks(stream); /* Write queue drained. */ if (QUEUE_EMPTY(&stream->write_queue)) uv__drain(stream); } } /** * We get called here from directly following a call to connect(2). * In order to determine if we've errored out or succeeded must call * getsockopt. */ static void uv__stream_connect(uv_stream_t* stream) { int error; uv_connect_t* req = stream->connect_req; socklen_t errorsize = sizeof(int); assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE); assert(req); if (stream->delayed_error) { /* To smooth over the differences between unixes errors that * were reported synchronously on the first connect can be delayed * until the next tick--which is now. */ error = stream->delayed_error; stream->delayed_error = 0; } else { /* Normal situation: we need to get the socket error from the kernel. */ assert(uv__stream_fd(stream) >= 0); getsockopt(uv__stream_fd(stream), SOL_SOCKET, SO_ERROR, &error, &errorsize); error = -error; } if (error == -EINPROGRESS) return; stream->connect_req = NULL; uv__req_unregister(stream->loop, req); if (error < 0 || QUEUE_EMPTY(&stream->write_queue)) { uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLOUT); } if (req->cb) req->cb(req, error); if (uv__stream_fd(stream) == -1) return; if (error < 0) { uv__stream_flush_write_queue(stream, -ECANCELED); uv__write_callbacks(stream); } } int uv_write2(uv_write_t* req, uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb) { int empty_queue; assert(nbufs > 0); assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY) && "uv_write (unix) does not yet support other types of streams"); if (uv__stream_fd(stream) < 0) return -EBADF; if (send_handle) { if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc) return -EINVAL; /* XXX We abuse uv_write2() to send over UDP handles to child processes. * Don't call uv__stream_fd() on those handles, it's a macro that on OS X * evaluates to a function that operates on a uv_stream_t with a couple of * OS X specific fields. On other Unices it does (handle)->io_watcher.fd, * which works but only by accident. */ if (uv__handle_fd((uv_handle_t*) send_handle) < 0) return -EBADF; } /* It's legal for write_queue_size > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that * will touch up write_queue_size later, see also uv__write_req_finish(). * We could check that write_queue is empty instead but that implies making * a write() syscall when we know that the handle is in error mode. */ empty_queue = (stream->write_queue_size == 0); /* Initialize the req */ uv__req_init(stream->loop, req, UV_WRITE); req->cb = cb; req->handle = stream; req->error = 0; req->send_handle = send_handle; QUEUE_INIT(&req->queue); req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(bufs[0])); if (req->bufs == NULL) return -ENOMEM; memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); req->nbufs = nbufs; req->write_index = 0; stream->write_queue_size += uv__count_bufs(bufs, nbufs); /* Append the request to write_queue. */ QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue); /* If the queue was empty when this function began, we should attempt to * do the write immediately. Otherwise start the write_watcher and wait * for the fd to become writable. */ if (stream->connect_req) { /* Still connecting, do nothing. */ } else if (empty_queue) { uv__write(stream); } else { /* * blocking streams should never have anything in the queue. * if this assert fires then somehow the blocking stream isn't being * sufficiently flushed in uv__write. */ assert(!(stream->flags & UV_STREAM_BLOCKING)); uv__io_start(stream->loop, &stream->io_watcher, UV__POLLOUT); uv__stream_osx_interrupt_select(stream); } return 0; } /* The buffers to be written must remain valid until the callback is called. * This is not required for the uv_buf_t array. */ int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) { return uv_write2(req, handle, bufs, nbufs, NULL, cb); } void uv_try_write_cb(uv_write_t* req, int status) { /* Should not be called */ abort(); } int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { int r; int has_pollout; size_t written; size_t req_size; uv_write_t req; /* Connecting or already writing some data */ if (stream->connect_req != NULL || stream->write_queue_size != 0) return -EAGAIN; has_pollout = uv__io_active(&stream->io_watcher, UV__POLLOUT); r = uv_write(&req, stream, bufs, nbufs, uv_try_write_cb); if (r != 0) return r; /* Remove not written bytes from write queue size */ written = uv__count_bufs(bufs, nbufs); if (req.bufs != NULL) req_size = uv__write_req_size(&req); else req_size = 0; written -= req_size; stream->write_queue_size -= req_size; /* Unqueue request, regardless of immediateness */ QUEUE_REMOVE(&req.queue); uv__req_unregister(stream->loop, &req); if (req.bufs != req.bufsml) uv__free(req.bufs); req.bufs = NULL; /* Do not poll for writable, if we wasn't before calling this */ if (!has_pollout) { uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLOUT); uv__stream_osx_interrupt_select(stream); } if (written == 0 && req_size != 0) return -EAGAIN; else return written; } int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); if (stream->flags & UV_CLOSING) return -EINVAL; /* The UV_STREAM_READING flag is irrelevant of the state of the tcp - it just * expresses the desired state of the user. */ stream->flags |= UV_STREAM_READING; /* TODO: try to do the read inline? */ /* TODO: keep track of tcp state. If we've gotten a EOF then we should * not start the IO watcher. */ assert(uv__stream_fd(stream) >= 0); assert(alloc_cb); stream->read_cb = read_cb; stream->alloc_cb = alloc_cb; uv__io_start(stream->loop, &stream->io_watcher, UV__POLLIN); uv__handle_start(stream); uv__stream_osx_interrupt_select(stream); return 0; } int uv_read_stop(uv_stream_t* stream) { if (!(stream->flags & UV_STREAM_READING)) return 0; stream->flags &= ~UV_STREAM_READING; uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLIN); if (!uv__io_active(&stream->io_watcher, UV__POLLOUT)) uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb = NULL; stream->alloc_cb = NULL; return 0; } int uv_is_readable(const uv_stream_t* stream) { return !!(stream->flags & UV_STREAM_READABLE); } int uv_is_writable(const uv_stream_t* stream) { return !!(stream->flags & UV_STREAM_WRITABLE); } #if defined(__APPLE__) int uv___stream_fd(const uv_stream_t* handle) { const uv__stream_select_t* s; assert(handle->type == UV_TCP || handle->type == UV_TTY || handle->type == UV_NAMED_PIPE); s = handle->select; if (s != NULL) return s->fd; return handle->io_watcher.fd; } #endif /* defined(__APPLE__) */ void uv__stream_close(uv_stream_t* handle) { unsigned int i; uv__stream_queued_fds_t* queued_fds; #if defined(__APPLE__) /* Terminate select loop first */ if (handle->select != NULL) { uv__stream_select_t* s; s = handle->select; uv_sem_post(&s->close_sem); uv_sem_post(&s->async_sem); uv__stream_osx_interrupt_select(handle); uv_thread_join(&s->thread); uv_sem_destroy(&s->close_sem); uv_sem_destroy(&s->async_sem); uv__close(s->fake_fd); uv__close(s->int_fd); uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); handle->select = NULL; } #endif /* defined(__APPLE__) */ uv__io_close(handle->loop, &handle->io_watcher); uv_read_stop(handle); uv__handle_stop(handle); if (handle->io_watcher.fd != -1) { /* Don't close stdio file descriptors. Nothing good comes from it. */ if (handle->io_watcher.fd > STDERR_FILENO) uv__close(handle->io_watcher.fd); handle->io_watcher.fd = -1; } if (handle->accepted_fd != -1) { uv__close(handle->accepted_fd); handle->accepted_fd = -1; } /* Close all queued fds */ if (handle->queued_fds != NULL) { queued_fds = handle->queued_fds; for (i = 0; i < queued_fds->offset; i++) uv__close(queued_fds->fds[i]); uv__free(handle->queued_fds); handle->queued_fds = NULL; } assert(!uv__io_active(&handle->io_watcher, UV__POLLIN | UV__POLLOUT)); } int uv_stream_set_blocking(uv_stream_t* handle, int blocking) { /* Don't need to check the file descriptor, uv__nonblock() * will fail with EBADF if it's not valid. */ return uv__nonblock(uv__stream_fd(handle), !blocking); } libuv-v1.8.0/src/unix/sunos.c000066400000000000000000000431551263361716600161370ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #include #ifndef SUNOS_NO_IFADDRS # include #endif #include #include #include #include #include #include #include #include #include #define PORT_FIRED 0x69 #define PORT_UNUSED 0x0 #define PORT_LOADED 0x99 #define PORT_DELETED -1 #if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64) #define PROCFS_FILE_OFFSET_BITS_HACK 1 #undef _FILE_OFFSET_BITS #else #define PROCFS_FILE_OFFSET_BITS_HACK 0 #endif #include #if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1) #define _FILE_OFFSET_BITS 64 #endif int uv__platform_loop_init(uv_loop_t* loop) { int err; int fd; loop->fs_fd = -1; loop->backend_fd = -1; fd = port_create(); if (fd == -1) return -errno; err = uv__cloexec(fd, 1); if (err) { uv__close(fd); return err; } loop->backend_fd = fd; return 0; } void uv__platform_loop_delete(uv_loop_t* loop) { if (loop->fs_fd != -1) { uv__close(loop->fs_fd); loop->fs_fd = -1; } if (loop->backend_fd != -1) { uv__close(loop->backend_fd); loop->backend_fd = -1; } } void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct port_event* events; uintptr_t i; uintptr_t nfds; assert(loop->watchers != NULL); events = (struct port_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; if (events == NULL) return; /* Invalidate events with same file descriptor */ for (i = 0; i < nfds; i++) if ((int) events[i].portev_object == fd) events[i].portev_object = -1; } void uv__io_poll(uv_loop_t* loop, int timeout) { struct port_event events[1024]; struct port_event* pe; struct timespec spec; QUEUE* q; uv__io_t* w; sigset_t* pset; sigset_t set; uint64_t base; uint64_t diff; unsigned int nfds; unsigned int i; int saved_errno; int nevents; int count; int err; int fd; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); return; } while (!QUEUE_EMPTY(&loop->watcher_queue)) { q = QUEUE_HEAD(&loop->watcher_queue); QUEUE_REMOVE(q); QUEUE_INIT(q); w = QUEUE_DATA(q, uv__io_t, watcher_queue); assert(w->pevents != 0); if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0)) abort(); w->events = w->pevents; } pset = NULL; if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { pset = &set; sigemptyset(pset); sigaddset(pset, SIGPROF); } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; } /* Work around a kernel bug where nfds is not updated. */ events[0].portev_source = 0; nfds = 1; saved_errno = 0; if (pset != NULL) pthread_sigmask(SIG_BLOCK, pset, NULL); err = port_getn(loop->backend_fd, events, ARRAY_SIZE(events), &nfds, timeout == -1 ? NULL : &spec); if (pset != NULL) pthread_sigmask(SIG_UNBLOCK, pset, NULL); if (err) { /* Work around another kernel bug: port_getn() may return events even * on error. */ if (errno == EINTR || errno == ETIME) saved_errno = errno; else abort(); } /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (events[0].portev_source == 0) { if (timeout == 0) return; if (timeout == -1) continue; goto update_timeout; } if (nfds == 0) { assert(timeout != -1); return; } nevents = 0; assert(loop->watchers != NULL); loop->watchers[loop->nwatchers] = (void*) events; loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->portev_object; /* Skip invalidated events, see uv__platform_invalidate_fd */ if (fd == -1) continue; assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); w = loop->watchers[fd]; /* File descriptor that we've stopped watching, ignore. */ if (w == NULL) continue; w->cb(loop, w, pe->portev_events); nevents++; if (w != loop->watchers[fd]) continue; /* Disabled by callback. */ /* Events Ports operates in oneshot mode, rearm timer on next run. */ if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); } loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (saved_errno == ETIME) { assert(timeout != -1); return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); diff = loop->time - base; if (diff >= (uint64_t) timeout) return; timeout -= diff; } } uint64_t uv__hrtime(uv_clocktype_t type) { return gethrtime(); } /* * We could use a static buffer for the path manipulations that we need outside * of the function, but this function could be called by multiple consumers and * we don't want to potentially create a race condition in the use of snprintf. */ int uv_exepath(char* buffer, size_t* size) { ssize_t res; char buf[128]; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; snprintf(buf, sizeof(buf), "/proc/%lu/path/a.out", (unsigned long) getpid()); res = *size - 1; if (res > 0) res = readlink(buf, buffer, res); if (res == -1) return -errno; buffer[res] = '\0'; *size = res; return 0; } uint64_t uv_get_free_memory(void) { return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); } uint64_t uv_get_total_memory(void) { return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); } void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } #if defined(PORT_SOURCE_FILE) static int uv__fs_event_rearm(uv_fs_event_t *handle) { if (handle->fd == -1) return -EBADF; if (port_associate(handle->loop->fs_fd, PORT_SOURCE_FILE, (uintptr_t) &handle->fo, FILE_ATTRIB | FILE_MODIFIED, handle) == -1) { return -errno; } handle->fd = PORT_LOADED; return 0; } static void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { uv_fs_event_t *handle = NULL; timespec_t timeout; port_event_t pe; int events; int r; (void) w; (void) revents; do { uint_t n = 1; /* * Note that our use of port_getn() here (and not port_get()) is deliberate: * there is a bug in event ports (Sun bug 6456558) whereby a zeroed timeout * causes port_get() to return success instead of ETIME when there aren't * actually any events (!); by using port_getn() in lieu of port_get(), * we can at least workaround the bug by checking for zero returned events * and treating it as we would ETIME. */ do { memset(&timeout, 0, sizeof timeout); r = port_getn(loop->fs_fd, &pe, 1, &n, &timeout); } while (r == -1 && errno == EINTR); if ((r == -1 && errno == ETIME) || n == 0) break; handle = (uv_fs_event_t*) pe.portev_user; assert((r == 0) && "unexpected port_get() error"); events = 0; if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED)) events |= UV_CHANGE; if (pe.portev_events & ~(FILE_ATTRIB | FILE_MODIFIED)) events |= UV_RENAME; assert(events != 0); handle->fd = PORT_FIRED; handle->cb(handle, NULL, events, 0); if (handle->fd != PORT_DELETED) { r = uv__fs_event_rearm(handle); if (r != 0) handle->cb(handle, NULL, 0, r); } } while (handle->fd != PORT_DELETED); } int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); return 0; } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) { int portfd; int first_run; int err; if (uv__is_active(handle)) return -EINVAL; first_run = 0; if (handle->loop->fs_fd == -1) { portfd = port_create(); if (portfd == -1) return -errno; handle->loop->fs_fd = portfd; first_run = 1; } uv__handle_start(handle); handle->path = uv__strdup(path); handle->fd = PORT_UNUSED; handle->cb = cb; memset(&handle->fo, 0, sizeof handle->fo); handle->fo.fo_name = handle->path; err = uv__fs_event_rearm(handle); if (err != 0) return err; if (first_run) { uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd); uv__io_start(handle->loop, &handle->loop->fs_event_watcher, UV__POLLIN); } return 0; } int uv_fs_event_stop(uv_fs_event_t* handle) { if (!uv__is_active(handle)) return 0; if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) { port_dissociate(handle->loop->fs_fd, PORT_SOURCE_FILE, (uintptr_t) &handle->fo); } handle->fd = PORT_DELETED; uv__free(handle->path); handle->path = NULL; handle->fo.fo_name = NULL; uv__handle_stop(handle); return 0; } void uv__fs_event_close(uv_fs_event_t* handle) { uv_fs_event_stop(handle); } #else /* !defined(PORT_SOURCE_FILE) */ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { return -ENOSYS; } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* filename, unsigned int flags) { return -ENOSYS; } int uv_fs_event_stop(uv_fs_event_t* handle) { return -ENOSYS; } void uv__fs_event_close(uv_fs_event_t* handle) { UNREACHABLE(); } #endif /* defined(PORT_SOURCE_FILE) */ char** uv_setup_args(int argc, char** argv) { return argv; } int uv_set_process_title(const char* title) { return 0; } int uv_get_process_title(char* buffer, size_t size) { if (size > 0) { buffer[0] = '\0'; } return 0; } int uv_resident_set_memory(size_t* rss) { psinfo_t psinfo; int err; int fd; fd = open("/proc/self/psinfo", O_RDONLY); if (fd == -1) return -errno; /* FIXME(bnoordhuis) Handle EINTR. */ err = -EINVAL; if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { *rss = (size_t)psinfo.pr_rssize * 1024; err = 0; } uv__close(fd); return err; } int uv_uptime(double* uptime) { kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *knp; long hz = sysconf(_SC_CLK_TCK); kc = kstat_open(); if (kc == NULL) return -EPERM; ksp = kstat_lookup(kc, (char*) "unix", 0, (char*) "system_misc"); if (kstat_read(kc, ksp, NULL) == -1) { *uptime = -1; } else { knp = (kstat_named_t*) kstat_data_lookup(ksp, (char*) "clk_intr"); *uptime = knp->value.ul / hz; } kstat_close(kc); return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { int lookup_instance; kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *knp; uv_cpu_info_t* cpu_info; kc = kstat_open(); if (kc == NULL) return -EPERM; /* Get count of cpus */ lookup_instance = 0; while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) { lookup_instance++; } *cpu_infos = uv__malloc(lookup_instance * sizeof(**cpu_infos)); if (!(*cpu_infos)) { kstat_close(kc); return -ENOMEM; } *count = lookup_instance; cpu_info = *cpu_infos; lookup_instance = 0; while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) { if (kstat_read(kc, ksp, NULL) == -1) { cpu_info->speed = 0; cpu_info->model = NULL; } else { knp = kstat_data_lookup(ksp, (char*) "clock_MHz"); assert(knp->data_type == KSTAT_DATA_INT32 || knp->data_type == KSTAT_DATA_INT64); cpu_info->speed = (knp->data_type == KSTAT_DATA_INT32) ? knp->value.i32 : knp->value.i64; knp = kstat_data_lookup(ksp, (char*) "brand"); assert(knp->data_type == KSTAT_DATA_STRING); cpu_info->model = uv__strdup(KSTAT_NAMED_STR_PTR(knp)); } lookup_instance++; cpu_info++; } cpu_info = *cpu_infos; lookup_instance = 0; for (;;) { ksp = kstat_lookup(kc, (char*) "cpu", lookup_instance, (char*) "sys"); if (ksp == NULL) break; if (kstat_read(kc, ksp, NULL) == -1) { cpu_info->cpu_times.user = 0; cpu_info->cpu_times.nice = 0; cpu_info->cpu_times.sys = 0; cpu_info->cpu_times.idle = 0; cpu_info->cpu_times.irq = 0; } else { knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_user"); assert(knp->data_type == KSTAT_DATA_UINT64); cpu_info->cpu_times.user = knp->value.ui64; knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_kernel"); assert(knp->data_type == KSTAT_DATA_UINT64); cpu_info->cpu_times.sys = knp->value.ui64; knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_idle"); assert(knp->data_type == KSTAT_DATA_UINT64); cpu_info->cpu_times.idle = knp->value.ui64; knp = kstat_data_lookup(ksp, (char*) "intr"); assert(knp->data_type == KSTAT_DATA_UINT64); cpu_info->cpu_times.irq = knp->value.ui64; cpu_info->cpu_times.nice = 0; } lookup_instance++; cpu_info++; } kstat_close(kc); return 0; } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { #ifdef SUNOS_NO_IFADDRS return -ENOSYS; #else uv_interface_address_t* address; struct sockaddr_dl* sa_addr; struct ifaddrs* addrs; struct ifaddrs* ent; int i; if (getifaddrs(&addrs)) return -errno; *count = 0; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family == PF_PACKET)) { continue; } (*count)++; } *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return -ENOMEM; } address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) continue; if (ent->ifa_addr == NULL) continue; address->name = uv__strdup(ent->ifa_name); if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); } else { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } if (ent->ifa_netmask->sa_family == AF_INET6) { address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); } else { address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); } address->is_internal = !!((ent->ifa_flags & IFF_PRIVATE) || (ent->ifa_flags & IFF_LOOPBACK)); address++; } /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) || (ent->ifa_addr == NULL) || (ent->ifa_addr->sa_family != AF_LINK)) { continue; } address = *addresses; for (i = 0; i < (*count); i++) { if (strcmp(address->name, ent->ifa_name) == 0) { sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); } address++; } } freeifaddrs(addrs); return 0; #endif /* SUNOS_NO_IFADDRS */ } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { int i; for (i = 0; i < count; i++) { uv__free(addresses[i].name); } uv__free(addresses); } libuv-v1.8.0/src/unix/tcp.c000066400000000000000000000214621263361716600155530ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { int sockfd; int err; if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) { handle->flags |= flags; return 0; } err = uv__socket(domain, SOCK_STREAM, 0); if (err < 0) return err; sockfd = err; err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); if (err) { uv__close(sockfd); return err; } return 0; } int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { int domain; /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) return -EINVAL; if (flags & ~0xFF) return -EINVAL; uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ if (domain != AF_UNSPEC) { int err = maybe_new_socket(tcp, domain, 0); if (err) { QUEUE_REMOVE(&tcp->handle_queue); return err; } } return 0; } int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { return uv_tcp_init_ex(loop, tcp, AF_UNSPEC); } int uv__tcp_bind(uv_tcp_t* tcp, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags) { int err; int on; /* Cannot set IPv6-only mode on non-IPv6 socket. */ if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6) return -EINVAL; err = maybe_new_socket(tcp, addr->sa_family, UV_STREAM_READABLE | UV_STREAM_WRITABLE); if (err) return err; on = 1; if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) return -errno; #ifdef IPV6_V6ONLY if (addr->sa_family == AF_INET6) { on = (flags & UV_TCP_IPV6ONLY) != 0; if (setsockopt(tcp->io_watcher.fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof on) == -1) { return -errno; } } #endif errno = 0; if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) { if (errno == EAFNOSUPPORT) /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a * socket created with AF_INET to an AF_INET6 address or vice versa. */ return -EINVAL; return -errno; } tcp->delayed_error = -errno; if (addr->sa_family == AF_INET6) tcp->flags |= UV_HANDLE_IPV6; return 0; } int uv__tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, uv_connect_cb cb) { int err; int r; assert(handle->type == UV_TCP); if (handle->connect_req != NULL) return -EALREADY; /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */ err = maybe_new_socket(handle, addr->sa_family, UV_STREAM_READABLE | UV_STREAM_WRITABLE); if (err) return err; handle->delayed_error = 0; do r = connect(uv__stream_fd(handle), addr, addrlen); while (r == -1 && errno == EINTR); if (r == -1) { if (errno == EINPROGRESS) ; /* not an error */ else if (errno == ECONNREFUSED) /* If we get a ECONNREFUSED wait until the next tick to report the * error. Solaris wants to report immediately--other unixes want to * wait. */ handle->delayed_error = -errno; else return -errno; } uv__req_init(handle->loop, req, UV_CONNECT); req->cb = cb; req->handle = (uv_stream_t*) handle; QUEUE_INIT(&req->queue); handle->connect_req = req; uv__io_start(handle->loop, &handle->io_watcher, UV__POLLOUT); if (handle->delayed_error) uv__io_feed(handle->loop, &handle->io_watcher); return 0; } int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { int err; err = uv__nonblock(sock, 1); if (err) return err; return uv__stream_open((uv_stream_t*)handle, sock, UV_STREAM_READABLE | UV_STREAM_WRITABLE); } int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { socklen_t socklen; if (handle->delayed_error) return handle->delayed_error; if (uv__stream_fd(handle) < 0) return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ /* sizeof(socklen_t) != sizeof(int) on some systems. */ socklen = (socklen_t) *namelen; if (getsockname(uv__stream_fd(handle), name, &socklen)) return -errno; *namelen = (int) socklen; return 0; } int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { socklen_t socklen; if (handle->delayed_error) return handle->delayed_error; if (uv__stream_fd(handle) < 0) return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ /* sizeof(socklen_t) != sizeof(int) on some systems. */ socklen = (socklen_t) *namelen; if (getpeername(uv__stream_fd(handle), name, &socklen)) return -errno; *namelen = (int) socklen; return 0; } int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { static int single_accept = -1; int err; if (tcp->delayed_error) return tcp->delayed_error; if (single_accept == -1) { const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */ } if (single_accept) tcp->flags |= UV_TCP_SINGLE_ACCEPT; err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE); if (err) return err; if (listen(tcp->io_watcher.fd, backlog)) return -errno; tcp->connection_cb = cb; /* Start listening for connections. */ tcp->io_watcher.cb = uv__server_io; uv__io_start(tcp->loop, &tcp->io_watcher, UV__POLLIN); return 0; } int uv__tcp_nodelay(int fd, int on) { if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on))) return -errno; return 0; } int uv__tcp_keepalive(int fd, int on, unsigned int delay) { if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) return -errno; #ifdef TCP_KEEPIDLE if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) return -errno; #endif /* Solaris/SmartOS, if you don't support keep-alive, * then don't advertise it in your system headers... */ /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */ #if defined(TCP_KEEPALIVE) && !defined(__sun) if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) return -errno; #endif return 0; } int uv_tcp_nodelay(uv_tcp_t* handle, int on) { int err; if (uv__stream_fd(handle) != -1) { err = uv__tcp_nodelay(uv__stream_fd(handle), on); if (err) return err; } if (on) handle->flags |= UV_TCP_NODELAY; else handle->flags &= ~UV_TCP_NODELAY; return 0; } int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { int err; if (uv__stream_fd(handle) != -1) { err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay); if (err) return err; } if (on) handle->flags |= UV_TCP_KEEPALIVE; else handle->flags &= ~UV_TCP_KEEPALIVE; /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge * uv_tcp_t with an int that's almost never used... */ return 0; } int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { if (enable) handle->flags &= ~UV_TCP_SINGLE_ACCEPT; else handle->flags |= UV_TCP_SINGLE_ACCEPT; return 0; } void uv__tcp_close(uv_tcp_t* handle) { uv__stream_close((uv_stream_t*)handle); } libuv-v1.8.0/src/unix/thread.c000066400000000000000000000240701263361716600162320ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) struct thread_ctx { void (*entry)(void* arg); void* arg; }; static void* uv__thread_start(void *arg) { struct thread_ctx *ctx_p; struct thread_ctx ctx; ctx_p = arg; ctx = *ctx_p; uv__free(ctx_p); ctx.entry(ctx.arg); return 0; } int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { struct thread_ctx* ctx; int err; ctx = uv__malloc(sizeof(*ctx)); if (ctx == NULL) return UV_ENOMEM; ctx->entry = entry; ctx->arg = arg; err = pthread_create(tid, NULL, uv__thread_start, ctx); if (err) uv__free(ctx); return -err; } uv_thread_t uv_thread_self(void) { return pthread_self(); } int uv_thread_join(uv_thread_t *tid) { return -pthread_join(*tid, NULL); } int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { return pthread_equal(*t1, *t2); } int uv_mutex_init(uv_mutex_t* mutex) { #if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK) return -pthread_mutex_init(mutex, NULL); #else pthread_mutexattr_t attr; int err; if (pthread_mutexattr_init(&attr)) abort(); if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) abort(); err = pthread_mutex_init(mutex, &attr); if (pthread_mutexattr_destroy(&attr)) abort(); return -err; #endif } void uv_mutex_destroy(uv_mutex_t* mutex) { if (pthread_mutex_destroy(mutex)) abort(); } void uv_mutex_lock(uv_mutex_t* mutex) { if (pthread_mutex_lock(mutex)) abort(); } int uv_mutex_trylock(uv_mutex_t* mutex) { int err; err = pthread_mutex_trylock(mutex); if (err) { if (err != EBUSY && err != EAGAIN) abort(); return -EBUSY; } return 0; } void uv_mutex_unlock(uv_mutex_t* mutex) { if (pthread_mutex_unlock(mutex)) abort(); } int uv_rwlock_init(uv_rwlock_t* rwlock) { return -pthread_rwlock_init(rwlock, NULL); } void uv_rwlock_destroy(uv_rwlock_t* rwlock) { if (pthread_rwlock_destroy(rwlock)) abort(); } void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { if (pthread_rwlock_rdlock(rwlock)) abort(); } int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { int err; err = pthread_rwlock_tryrdlock(rwlock); if (err) { if (err != EBUSY && err != EAGAIN) abort(); return -EBUSY; } return 0; } void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { if (pthread_rwlock_unlock(rwlock)) abort(); } void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { if (pthread_rwlock_wrlock(rwlock)) abort(); } int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { int err; err = pthread_rwlock_trywrlock(rwlock); if (err) { if (err != EBUSY && err != EAGAIN) abort(); return -EBUSY; } return 0; } void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { if (pthread_rwlock_unlock(rwlock)) abort(); } void uv_once(uv_once_t* guard, void (*callback)(void)) { if (pthread_once(guard, callback)) abort(); } #if defined(__APPLE__) && defined(__MACH__) int uv_sem_init(uv_sem_t* sem, unsigned int value) { kern_return_t err; err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value); if (err == KERN_SUCCESS) return 0; if (err == KERN_INVALID_ARGUMENT) return -EINVAL; if (err == KERN_RESOURCE_SHORTAGE) return -ENOMEM; abort(); return -EINVAL; /* Satisfy the compiler. */ } void uv_sem_destroy(uv_sem_t* sem) { if (semaphore_destroy(mach_task_self(), *sem)) abort(); } void uv_sem_post(uv_sem_t* sem) { if (semaphore_signal(*sem)) abort(); } void uv_sem_wait(uv_sem_t* sem) { int r; do r = semaphore_wait(*sem); while (r == KERN_ABORTED); if (r != KERN_SUCCESS) abort(); } int uv_sem_trywait(uv_sem_t* sem) { mach_timespec_t interval; kern_return_t err; interval.tv_sec = 0; interval.tv_nsec = 0; err = semaphore_timedwait(*sem, interval); if (err == KERN_SUCCESS) return 0; if (err == KERN_OPERATION_TIMED_OUT) return -EAGAIN; abort(); return -EINVAL; /* Satisfy the compiler. */ } #else /* !(defined(__APPLE__) && defined(__MACH__)) */ int uv_sem_init(uv_sem_t* sem, unsigned int value) { if (sem_init(sem, 0, value)) return -errno; return 0; } void uv_sem_destroy(uv_sem_t* sem) { if (sem_destroy(sem)) abort(); } void uv_sem_post(uv_sem_t* sem) { if (sem_post(sem)) abort(); } void uv_sem_wait(uv_sem_t* sem) { int r; do r = sem_wait(sem); while (r == -1 && errno == EINTR); if (r) abort(); } int uv_sem_trywait(uv_sem_t* sem) { int r; do r = sem_trywait(sem); while (r == -1 && errno == EINTR); if (r) { if (errno == EAGAIN) return -EAGAIN; abort(); } return 0; } #endif /* defined(__APPLE__) && defined(__MACH__) */ #if defined(__APPLE__) && defined(__MACH__) int uv_cond_init(uv_cond_t* cond) { return -pthread_cond_init(cond, NULL); } #else /* !(defined(__APPLE__) && defined(__MACH__)) */ int uv_cond_init(uv_cond_t* cond) { pthread_condattr_t attr; int err; err = pthread_condattr_init(&attr); if (err) return -err; #if !(defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); if (err) goto error2; #endif err = pthread_cond_init(cond, &attr); if (err) goto error2; err = pthread_condattr_destroy(&attr); if (err) goto error; return 0; error: pthread_cond_destroy(cond); error2: pthread_condattr_destroy(&attr); return -err; } #endif /* defined(__APPLE__) && defined(__MACH__) */ void uv_cond_destroy(uv_cond_t* cond) { if (pthread_cond_destroy(cond)) abort(); } void uv_cond_signal(uv_cond_t* cond) { if (pthread_cond_signal(cond)) abort(); } void uv_cond_broadcast(uv_cond_t* cond) { if (pthread_cond_broadcast(cond)) abort(); } void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { if (pthread_cond_wait(cond, mutex)) abort(); } int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { int r; struct timespec ts; #if defined(__APPLE__) && defined(__MACH__) ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); #else timeout += uv__hrtime(UV_CLOCK_PRECISE); ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; #if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) /* * The bionic pthread implementation doesn't support CLOCK_MONOTONIC, * but has this alternative function instead. */ r = pthread_cond_timedwait_monotonic_np(cond, mutex, &ts); #else r = pthread_cond_timedwait(cond, mutex, &ts); #endif /* __ANDROID__ */ #endif if (r == 0) return 0; if (r == ETIMEDOUT) return -ETIMEDOUT; abort(); return -EINVAL; /* Satisfy the compiler. */ } #if defined(__APPLE__) && defined(__MACH__) int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { int err; barrier->n = count; barrier->count = 0; err = uv_mutex_init(&barrier->mutex); if (err) return -err; err = uv_sem_init(&barrier->turnstile1, 0); if (err) goto error2; err = uv_sem_init(&barrier->turnstile2, 1); if (err) goto error; return 0; error: uv_sem_destroy(&barrier->turnstile1); error2: uv_mutex_destroy(&barrier->mutex); return -err; } void uv_barrier_destroy(uv_barrier_t* barrier) { uv_sem_destroy(&barrier->turnstile2); uv_sem_destroy(&barrier->turnstile1); uv_mutex_destroy(&barrier->mutex); } int uv_barrier_wait(uv_barrier_t* barrier) { int serial_thread; uv_mutex_lock(&barrier->mutex); if (++barrier->count == barrier->n) { uv_sem_wait(&barrier->turnstile2); uv_sem_post(&barrier->turnstile1); } uv_mutex_unlock(&barrier->mutex); uv_sem_wait(&barrier->turnstile1); uv_sem_post(&barrier->turnstile1); uv_mutex_lock(&barrier->mutex); serial_thread = (--barrier->count == 0); if (serial_thread) { uv_sem_wait(&barrier->turnstile1); uv_sem_post(&barrier->turnstile2); } uv_mutex_unlock(&barrier->mutex); uv_sem_wait(&barrier->turnstile2); uv_sem_post(&barrier->turnstile2); return serial_thread; } #else /* !(defined(__APPLE__) && defined(__MACH__)) */ int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { return -pthread_barrier_init(barrier, NULL, count); } void uv_barrier_destroy(uv_barrier_t* barrier) { if (pthread_barrier_destroy(barrier)) abort(); } int uv_barrier_wait(uv_barrier_t* barrier) { int r = pthread_barrier_wait(barrier); if (r && r != PTHREAD_BARRIER_SERIAL_THREAD) abort(); return r == PTHREAD_BARRIER_SERIAL_THREAD; } #endif /* defined(__APPLE__) && defined(__MACH__) */ int uv_key_create(uv_key_t* key) { return -pthread_key_create(key, NULL); } void uv_key_delete(uv_key_t* key) { if (pthread_key_delete(*key)) abort(); } void* uv_key_get(uv_key_t* key) { return pthread_getspecific(*key); } void uv_key_set(uv_key_t* key, void* value) { if (pthread_setspecific(*key, value)) abort(); } libuv-v1.8.0/src/unix/timer.c000066400000000000000000000106101263361716600160760ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include "heap-inl.h" #include #include static int timer_less_than(const struct heap_node* ha, const struct heap_node* hb) { const uv_timer_t* a; const uv_timer_t* b; a = container_of(ha, const uv_timer_t, heap_node); b = container_of(hb, const uv_timer_t, heap_node); if (a->timeout < b->timeout) return 1; if (b->timeout < a->timeout) return 0; /* Compare start_id when both have the same timeout. start_id is * allocated with loop->timer_counter in uv_timer_start(). */ if (a->start_id < b->start_id) return 1; if (b->start_id < a->start_id) return 0; return 0; } int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER); handle->timer_cb = NULL; handle->repeat = 0; return 0; } int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat) { uint64_t clamped_timeout; if (cb == NULL) return -EINVAL; if (uv__is_active(handle)) uv_timer_stop(handle); clamped_timeout = handle->loop->time + timeout; if (clamped_timeout < timeout) clamped_timeout = (uint64_t) -1; handle->timer_cb = cb; handle->timeout = clamped_timeout; handle->repeat = repeat; /* start_id is the second index to be compared in uv__timer_cmp() */ handle->start_id = handle->loop->timer_counter++; heap_insert((struct heap*) &handle->loop->timer_heap, (struct heap_node*) &handle->heap_node, timer_less_than); uv__handle_start(handle); return 0; } int uv_timer_stop(uv_timer_t* handle) { if (!uv__is_active(handle)) return 0; heap_remove((struct heap*) &handle->loop->timer_heap, (struct heap_node*) &handle->heap_node, timer_less_than); uv__handle_stop(handle); return 0; } int uv_timer_again(uv_timer_t* handle) { if (handle->timer_cb == NULL) return -EINVAL; if (handle->repeat) { uv_timer_stop(handle); uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat); } return 0; } void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) { handle->repeat = repeat; } uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { return handle->repeat; } int uv__next_timeout(const uv_loop_t* loop) { const struct heap_node* heap_node; const uv_timer_t* handle; uint64_t diff; heap_node = heap_min((const struct heap*) &loop->timer_heap); if (heap_node == NULL) return -1; /* block indefinitely */ handle = container_of(heap_node, const uv_timer_t, heap_node); if (handle->timeout <= loop->time) return 0; diff = handle->timeout - loop->time; if (diff > INT_MAX) diff = INT_MAX; return diff; } void uv__run_timers(uv_loop_t* loop) { struct heap_node* heap_node; uv_timer_t* handle; for (;;) { heap_node = heap_min((struct heap*) &loop->timer_heap); if (heap_node == NULL) break; handle = container_of(heap_node, uv_timer_t, heap_node); if (handle->timeout > loop->time) break; uv_timer_stop(handle); uv_timer_again(handle); handle->timer_cb(handle); } } void uv__timer_close(uv_timer_t* handle) { uv_timer_stop(handle); } libuv-v1.8.0/src/unix/tty.c000066400000000000000000000164151263361716600156070ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include "spinlock.h" #include #include #include #include #include static int orig_termios_fd = -1; static struct termios orig_termios; static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { uv_handle_type type; int flags; int newfd; int r; /* File descriptors that refer to files cannot be monitored with epoll. * That restriction also applies to character devices like /dev/random * (but obviously not /dev/tty.) */ type = uv_guess_handle(fd); if (type == UV_FILE || type == UV_UNKNOWN_HANDLE) return -EINVAL; flags = 0; newfd = -1; /* Reopen the file descriptor when it refers to a tty. This lets us put the * tty in non-blocking mode without affecting other processes that share it * with us. * * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also * affects fd 1 of `cat` because both file descriptors refer to the same * struct file in the kernel. When we reopen our fd 0, it points to a * different struct file, hence changing its properties doesn't affect * other processes. */ if (type == UV_TTY) { r = uv__open_cloexec("/dev/tty", O_RDWR); if (r < 0) { /* fallback to using blocking writes */ if (!readable) flags |= UV_STREAM_BLOCKING; goto skip; } newfd = r; r = uv__dup2_cloexec(newfd, fd); if (r < 0 && r != -EINVAL) { /* EINVAL means newfd == fd which could conceivably happen if another * thread called close(fd) between our calls to isatty() and open(). * That's a rather unlikely event but let's handle it anyway. */ uv__close(newfd); return r; } fd = newfd; } skip: uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ #if defined(__APPLE__) r = uv__stream_try_select((uv_stream_t*) tty, &fd); if (r) { if (newfd != -1) uv__close(newfd); QUEUE_REMOVE(&tty->handle_queue); return r; } #endif if (readable) flags |= UV_STREAM_READABLE; else flags |= UV_STREAM_WRITABLE; if (!(flags & UV_STREAM_BLOCKING)) uv__nonblock(fd, 1); uv__stream_open((uv_stream_t*) tty, fd, flags); tty->mode = UV_TTY_MODE_NORMAL; return 0; } static void uv__tty_make_raw(struct termios* tio) { assert(tio != NULL); #ifdef __sun /* * This implementation of cfmakeraw for Solaris and derivatives is taken from * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html. */ tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); tio->c_oflag &= ~OPOST; tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tio->c_cflag &= ~(CSIZE | PARENB); tio->c_cflag |= CS8; #else cfmakeraw(tio); #endif /* #ifdef __sun */ } int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { struct termios tmp; int fd; if (tty->mode == (int) mode) return 0; fd = uv__stream_fd(tty); if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { if (tcgetattr(fd, &tty->orig_termios)) return -errno; /* This is used for uv_tty_reset_mode() */ uv_spinlock_lock(&termios_spinlock); if (orig_termios_fd == -1) { orig_termios = tty->orig_termios; orig_termios_fd = fd; } uv_spinlock_unlock(&termios_spinlock); } tmp = tty->orig_termios; switch (mode) { case UV_TTY_MODE_NORMAL: break; case UV_TTY_MODE_RAW: tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); tmp.c_oflag |= (ONLCR); tmp.c_cflag |= (CS8); tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); tmp.c_cc[VMIN] = 1; tmp.c_cc[VTIME] = 0; break; case UV_TTY_MODE_IO: uv__tty_make_raw(&tmp); break; } /* Apply changes after draining */ if (tcsetattr(fd, TCSADRAIN, &tmp)) return -errno; tty->mode = mode; return 0; } int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { struct winsize ws; if (ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws)) return -errno; *width = ws.ws_col; *height = ws.ws_row; return 0; } uv_handle_type uv_guess_handle(uv_file file) { struct sockaddr sa; struct stat s; socklen_t len; int type; if (file < 0) return UV_UNKNOWN_HANDLE; if (isatty(file)) return UV_TTY; if (fstat(file, &s)) return UV_UNKNOWN_HANDLE; if (S_ISREG(s.st_mode)) return UV_FILE; if (S_ISCHR(s.st_mode)) return UV_FILE; /* XXX UV_NAMED_PIPE? */ if (S_ISFIFO(s.st_mode)) return UV_NAMED_PIPE; if (!S_ISSOCK(s.st_mode)) return UV_UNKNOWN_HANDLE; len = sizeof(type); if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len)) return UV_UNKNOWN_HANDLE; len = sizeof(sa); if (getsockname(file, &sa, &len)) return UV_UNKNOWN_HANDLE; if (type == SOCK_DGRAM) if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) return UV_UDP; if (type == SOCK_STREAM) { #if defined(_AIX) /* on AIX the getsockname call returns an empty sa structure * for sockets of type AF_UNIX. For all other types it will * return a properly filled in structure. */ if (len == 0) return UV_NAMED_PIPE; #endif /* defined(_AIX) */ if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) return UV_TCP; if (sa.sa_family == AF_UNIX) return UV_NAMED_PIPE; } return UV_UNKNOWN_HANDLE; } /* This function is async signal-safe, meaning that it's safe to call from * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s * critical section when the signal was raised. */ int uv_tty_reset_mode(void) { int saved_errno; int err; saved_errno = errno; if (!uv_spinlock_trylock(&termios_spinlock)) return -EBUSY; /* In uv_tty_set_mode(). */ err = 0; if (orig_termios_fd != -1) if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios)) err = -errno; uv_spinlock_unlock(&termios_spinlock); errno = saved_errno; return err; } libuv-v1.8.0/src/unix/udp.c000066400000000000000000000551401263361716600155550ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" #include #include #include #include #include #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif #if defined(IPV6_LEAVE_GROUP) && !defined(IPV6_DROP_MEMBERSHIP) # define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif static void uv__udp_run_completed(uv_udp_t* handle); static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents); static void uv__udp_recvmsg(uv_udp_t* handle); static void uv__udp_sendmsg(uv_udp_t* handle); static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags); void uv__udp_close(uv_udp_t* handle) { uv__io_close(handle->loop, &handle->io_watcher); uv__handle_stop(handle); if (handle->io_watcher.fd != -1) { uv__close(handle->io_watcher.fd); handle->io_watcher.fd = -1; } } void uv__udp_finish_close(uv_udp_t* handle) { uv_udp_send_t* req; QUEUE* q; assert(!uv__io_active(&handle->io_watcher, UV__POLLIN | UV__POLLOUT)); assert(handle->io_watcher.fd == -1); while (!QUEUE_EMPTY(&handle->write_queue)) { q = QUEUE_HEAD(&handle->write_queue); QUEUE_REMOVE(q); req = QUEUE_DATA(q, uv_udp_send_t, queue); req->status = -ECANCELED; QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); } uv__udp_run_completed(handle); assert(handle->send_queue_size == 0); assert(handle->send_queue_count == 0); /* Now tear down the handle. */ handle->recv_cb = NULL; handle->alloc_cb = NULL; /* but _do not_ touch close_cb */ } static void uv__udp_run_completed(uv_udp_t* handle) { uv_udp_send_t* req; QUEUE* q; assert(!(handle->flags & UV_UDP_PROCESSING)); handle->flags |= UV_UDP_PROCESSING; while (!QUEUE_EMPTY(&handle->write_completed_queue)) { q = QUEUE_HEAD(&handle->write_completed_queue); QUEUE_REMOVE(q); req = QUEUE_DATA(q, uv_udp_send_t, queue); uv__req_unregister(handle->loop, req); handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs); handle->send_queue_count--; if (req->bufs != req->bufsml) uv__free(req->bufs); req->bufs = NULL; if (req->send_cb == NULL) continue; /* req->status >= 0 == bytes written * req->status < 0 == errno */ if (req->status >= 0) req->send_cb(req, 0); else req->send_cb(req, req->status); } if (QUEUE_EMPTY(&handle->write_queue)) { /* Pending queue and completion queue empty, stop watcher. */ uv__io_stop(handle->loop, &handle->io_watcher, UV__POLLOUT); if (!uv__io_active(&handle->io_watcher, UV__POLLIN)) uv__handle_stop(handle); } handle->flags &= ~UV_UDP_PROCESSING; } static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { uv_udp_t* handle; handle = container_of(w, uv_udp_t, io_watcher); assert(handle->type == UV_UDP); if (revents & UV__POLLIN) uv__udp_recvmsg(handle); if (revents & UV__POLLOUT) { uv__udp_sendmsg(handle); uv__udp_run_completed(handle); } } static void uv__udp_recvmsg(uv_udp_t* handle) { struct sockaddr_storage peer; struct msghdr h; ssize_t nread; uv_buf_t buf; int flags; int count; assert(handle->recv_cb != NULL); assert(handle->alloc_cb != NULL); /* Prevent loop starvation when the data comes in as fast as (or faster than) * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O. */ count = 32; memset(&h, 0, sizeof(h)); h.msg_name = &peer; do { handle->alloc_cb((uv_handle_t*) handle, 64 * 1024, &buf); if (buf.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); return; } assert(buf.base != NULL); h.msg_namelen = sizeof(peer); h.msg_iov = (void*) &buf; h.msg_iovlen = 1; do { nread = recvmsg(handle->io_watcher.fd, &h, 0); } while (nread == -1 && errno == EINTR); if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) handle->recv_cb(handle, 0, &buf, NULL, 0); else handle->recv_cb(handle, -errno, &buf, NULL, 0); } else { const struct sockaddr *addr; if (h.msg_namelen == 0) addr = NULL; else addr = (const struct sockaddr*) &peer; flags = 0; if (h.msg_flags & MSG_TRUNC) flags |= UV_UDP_PARTIAL; handle->recv_cb(handle, nread, &buf, addr, flags); } } /* recv_cb callback may decide to pause or close the handle */ while (nread != -1 && count-- > 0 && handle->io_watcher.fd != -1 && handle->recv_cb != NULL); } static void uv__udp_sendmsg(uv_udp_t* handle) { uv_udp_send_t* req; QUEUE* q; struct msghdr h; ssize_t size; while (!QUEUE_EMPTY(&handle->write_queue)) { q = QUEUE_HEAD(&handle->write_queue); assert(q != NULL); req = QUEUE_DATA(q, uv_udp_send_t, queue); assert(req != NULL); memset(&h, 0, sizeof h); h.msg_name = &req->addr; h.msg_namelen = (req->addr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); h.msg_iov = (struct iovec*) req->bufs; h.msg_iovlen = req->nbufs; do { size = sendmsg(handle->io_watcher.fd, &h, 0); } while (size == -1 && errno == EINTR); if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) break; req->status = (size == -1 ? -errno : size); /* Sending a datagram is an atomic operation: either all data * is written or nothing is (and EMSGSIZE is raised). That is * why we don't handle partial writes. Just pop the request * off the write queue and onto the completed queue, done. */ QUEUE_REMOVE(&req->queue); QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); uv__io_feed(handle->loop, &handle->io_watcher); } } /* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional * refinements for programs that use multicast. * * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that * are different from the BSDs: it _shares_ the port rather than steal it * from the current listener. While useful, it's not something we can emulate * on other platforms so we don't enable it. */ static int uv__set_reuse(int fd) { int yes; #if defined(SO_REUSEPORT) && !defined(__linux__) yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return -errno; #else yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) return -errno; #endif return 0; } int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags) { int err; int yes; int fd; /* Check for bad flags. */ if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) return -EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) return -EINVAL; fd = handle->io_watcher.fd; if (fd == -1) { err = uv__socket(addr->sa_family, SOCK_DGRAM, 0); if (err < 0) return err; fd = err; handle->io_watcher.fd = fd; } if (flags & UV_UDP_REUSEADDR) { err = uv__set_reuse(fd); if (err) goto out; } if (flags & UV_UDP_IPV6ONLY) { #ifdef IPV6_V6ONLY yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) { err = -errno; goto out; } #else err = -ENOTSUP; goto out; #endif } if (bind(fd, addr, addrlen)) { err = -errno; if (errno == EAFNOSUPPORT) /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a * socket created with AF_INET to an AF_INET6 address or vice versa. */ err = -EINVAL; goto out; } if (addr->sa_family == AF_INET6) handle->flags |= UV_HANDLE_IPV6; return 0; out: uv__close(handle->io_watcher.fd); handle->io_watcher.fd = -1; return err; } static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags) { unsigned char taddr[sizeof(struct sockaddr_in6)]; socklen_t addrlen; if (handle->io_watcher.fd != -1) return 0; switch (domain) { case AF_INET: { struct sockaddr_in* addr = (void*)&taddr; memset(addr, 0, sizeof *addr); addr->sin_family = AF_INET; addr->sin_addr.s_addr = INADDR_ANY; addrlen = sizeof *addr; break; } case AF_INET6: { struct sockaddr_in6* addr = (void*)&taddr; memset(addr, 0, sizeof *addr); addr->sin6_family = AF_INET6; addr->sin6_addr = in6addr_any; addrlen = sizeof *addr; break; } default: assert(0 && "unsupported address family"); abort(); } return uv__udp_bind(handle, (const struct sockaddr*) &taddr, addrlen, flags); } int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen, uv_udp_send_cb send_cb) { int err; int empty_queue; assert(nbufs > 0); err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); if (err) return err; /* It's legal for send_queue_count > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that * will touch up send_queue_size/count later. */ empty_queue = (handle->send_queue_count == 0); uv__req_init(handle->loop, req, UV_UDP_SEND); assert(addrlen <= sizeof(req->addr)); memcpy(&req->addr, addr, addrlen); req->send_cb = send_cb; req->handle = handle; req->nbufs = nbufs; req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(bufs[0])); if (req->bufs == NULL) { uv__req_unregister(handle->loop, req); return -ENOMEM; } memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs); handle->send_queue_count++; QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue); uv__handle_start(handle); if (empty_queue && !(handle->flags & UV_UDP_PROCESSING)) { uv__udp_sendmsg(handle); } else { uv__io_start(handle->loop, &handle->io_watcher, UV__POLLOUT); } return 0; } int uv__udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen) { int err; struct msghdr h; ssize_t size; assert(nbufs > 0); /* already sending a message */ if (handle->send_queue_count != 0) return -EAGAIN; err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); if (err) return err; memset(&h, 0, sizeof h); h.msg_name = (struct sockaddr*) addr; h.msg_namelen = addrlen; h.msg_iov = (struct iovec*) bufs; h.msg_iovlen = nbufs; do { size = sendmsg(handle->io_watcher.fd, &h, 0); } while (size == -1 && errno == EINTR); if (size == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN; else return -errno; } return size; } static int uv__udp_set_membership4(uv_udp_t* handle, const struct sockaddr_in* multicast_addr, const char* interface_addr, uv_membership membership) { struct ip_mreq mreq; int optname; int err; memset(&mreq, 0, sizeof mreq); if (interface_addr) { err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); if (err) return err; } else { mreq.imr_interface.s_addr = htonl(INADDR_ANY); } mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; switch (membership) { case UV_JOIN_GROUP: optname = IP_ADD_MEMBERSHIP; break; case UV_LEAVE_GROUP: optname = IP_DROP_MEMBERSHIP; break; default: return -EINVAL; } if (setsockopt(handle->io_watcher.fd, IPPROTO_IP, optname, &mreq, sizeof(mreq))) { return -errno; } return 0; } static int uv__udp_set_membership6(uv_udp_t* handle, const struct sockaddr_in6* multicast_addr, const char* interface_addr, uv_membership membership) { int optname; struct ipv6_mreq mreq; struct sockaddr_in6 addr6; memset(&mreq, 0, sizeof mreq); if (interface_addr) { if (uv_ip6_addr(interface_addr, 0, &addr6)) return -EINVAL; mreq.ipv6mr_interface = addr6.sin6_scope_id; } else { mreq.ipv6mr_interface = 0; } mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr; switch (membership) { case UV_JOIN_GROUP: optname = IPV6_ADD_MEMBERSHIP; break; case UV_LEAVE_GROUP: optname = IPV6_DROP_MEMBERSHIP; break; default: return -EINVAL; } if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, optname, &mreq, sizeof(mreq))) { return -errno; } return 0; } int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { int domain; int err; int fd; /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) return -EINVAL; if (flags & ~0xFF) return -EINVAL; if (domain != AF_UNSPEC) { err = uv__socket(domain, SOCK_DGRAM, 0); if (err < 0) return err; fd = err; } else { fd = -1; } uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); handle->alloc_cb = NULL; handle->recv_cb = NULL; handle->send_queue_size = 0; handle->send_queue_count = 0; uv__io_init(&handle->io_watcher, uv__udp_io, fd); QUEUE_INIT(&handle->write_queue); QUEUE_INIT(&handle->write_completed_queue); return 0; } int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { return uv_udp_init_ex(loop, handle, AF_UNSPEC); } int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int err; /* Check for already active socket. */ if (handle->io_watcher.fd != -1) return -EBUSY; err = uv__nonblock(sock, 1); if (err) return err; err = uv__set_reuse(sock); if (err) return err; handle->io_watcher.fd = sock; return 0; } int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership) { int err; struct sockaddr_in addr4; struct sockaddr_in6 addr6; if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) { err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR); if (err) return err; return uv__udp_set_membership4(handle, &addr4, interface_addr, membership); } else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) { err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR); if (err) return err; return uv__udp_set_membership6(handle, &addr6, interface_addr, membership); } else { return -EINVAL; } } static int uv__setsockopt(uv_udp_t* handle, int option4, int option6, const void* val, size_t size) { int r; if (handle->flags & UV_HANDLE_IPV6) r = setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, option6, val, size); else r = setsockopt(handle->io_watcher.fd, IPPROTO_IP, option4, val, size); if (r) return -errno; return 0; } static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option4, int option6, int val) { #if defined(__sun) || defined(_AIX) char arg = val; #elif defined(__OpenBSD__) unsigned char arg = val; #else int arg = val; #endif if (val < 0 || val > 255) return -EINVAL; return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg)); } int uv_udp_set_broadcast(uv_udp_t* handle, int on) { if (setsockopt(handle->io_watcher.fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))) { return -errno; } return 0; } int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { if (ttl < 1 || ttl > 255) return -EINVAL; /* * On Solaris and derivatives such as SmartOS, the length of socket options * is sizeof(int) for IP_TTL and IPV6_UNICAST_HOPS, * so hardcode the size of these options on this platform, * and use the general uv__setsockopt_maybe_char call on other platforms. */ #if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) return uv__setsockopt(handle, IP_TTL, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); #endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) */ return uv__setsockopt_maybe_char(handle, IP_TTL, IPV6_UNICAST_HOPS, ttl); } int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { /* * On Solaris and derivatives such as SmartOS, the length of socket options * is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for * IP_MULTICAST_TTL, so hardcode the size of the option in the IPv6 case, * and use the general uv__setsockopt_maybe_char call otherwise. */ #if defined(__sun) || defined(_AIX) if (handle->flags & UV_HANDLE_IPV6) return uv__setsockopt(handle, IP_MULTICAST_TTL, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); #endif /* defined(__sun) || defined(_AIX) */ return uv__setsockopt_maybe_char(handle, IP_MULTICAST_TTL, IPV6_MULTICAST_HOPS, ttl); } int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { /* * On Solaris and derivatives such as SmartOS, the length of socket options * is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for * IP_MULTICAST_LOOP, so hardcode the size of the option in the IPv6 case, * and use the general uv__setsockopt_maybe_char call otherwise. */ #if defined(__sun) || defined(_AIX) if (handle->flags & UV_HANDLE_IPV6) return uv__setsockopt(handle, IP_MULTICAST_LOOP, IPV6_MULTICAST_LOOP, &on, sizeof(on)); #endif /* defined(__sun) || defined(_AIX) */ return uv__setsockopt_maybe_char(handle, IP_MULTICAST_LOOP, IPV6_MULTICAST_LOOP, on); } int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { struct sockaddr_storage addr_st; struct sockaddr_in* addr4; struct sockaddr_in6* addr6; addr4 = (struct sockaddr_in*) &addr_st; addr6 = (struct sockaddr_in6*) &addr_st; if (!interface_addr) { memset(&addr_st, 0, sizeof addr_st); if (handle->flags & UV_HANDLE_IPV6) { addr_st.ss_family = AF_INET6; addr6->sin6_scope_id = 0; } else { addr_st.ss_family = AF_INET; addr4->sin_addr.s_addr = htonl(INADDR_ANY); } } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) { /* nothing, address was parsed */ } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) { /* nothing, address was parsed */ } else { return -EINVAL; } if (addr_st.ss_family == AF_INET) { if (setsockopt(handle->io_watcher.fd, IPPROTO_IP, IP_MULTICAST_IF, (void*) &addr4->sin_addr, sizeof(addr4->sin_addr)) == -1) { return -errno; } } else if (addr_st.ss_family == AF_INET6) { if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &addr6->sin6_scope_id, sizeof(addr6->sin6_scope_id)) == -1) { return -errno; } } else { assert(0 && "unexpected address family"); abort(); } return 0; } int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen) { socklen_t socklen; if (handle->io_watcher.fd == -1) return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ /* sizeof(socklen_t) != sizeof(int) on some systems. */ socklen = (socklen_t) *namelen; if (getsockname(handle->io_watcher.fd, name, &socklen)) return -errno; *namelen = (int) socklen; return 0; } int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { int err; if (alloc_cb == NULL || recv_cb == NULL) return -EINVAL; if (uv__io_active(&handle->io_watcher, UV__POLLIN)) return -EALREADY; /* FIXME(bnoordhuis) Should be -EBUSY. */ err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0); if (err) return err; handle->alloc_cb = alloc_cb; handle->recv_cb = recv_cb; uv__io_start(handle->loop, &handle->io_watcher, UV__POLLIN); uv__handle_start(handle); return 0; } int uv__udp_recv_stop(uv_udp_t* handle) { uv__io_stop(handle->loop, &handle->io_watcher, UV__POLLIN); if (!uv__io_active(&handle->io_watcher, UV__POLLOUT)) uv__handle_stop(handle); handle->alloc_cb = NULL; handle->recv_cb = NULL; return 0; } libuv-v1.8.0/src/uv-common.c000066400000000000000000000334701263361716600157240ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "uv-common.h" #include #include #include #include /* NULL */ #include /* malloc */ #include /* memset */ #if defined(_WIN32) # include /* malloc */ #else # include /* if_nametoindex */ #endif typedef struct { uv_malloc_func local_malloc; uv_realloc_func local_realloc; uv_calloc_func local_calloc; uv_free_func local_free; } uv__allocator_t; static uv__allocator_t uv__allocator = { malloc, realloc, calloc, free, }; char* uv__strdup(const char* s) { size_t len = strlen(s) + 1; char* m = uv__malloc(len); if (m == NULL) return NULL; return memcpy(m, s, len); } char* uv__strndup(const char* s, size_t n) { char* m; size_t len = strlen(s); if (n < len) len = n; m = uv__malloc(len + 1); if (m == NULL) return NULL; m[len] = '\0'; return memcpy(m, s, len); } void* uv__malloc(size_t size) { return uv__allocator.local_malloc(size); } void uv__free(void* ptr) { uv__allocator.local_free(ptr); } void* uv__calloc(size_t count, size_t size) { return uv__allocator.local_calloc(count, size); } void* uv__realloc(void* ptr, size_t size) { return uv__allocator.local_realloc(ptr, size); } int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, uv_free_func free_func) { if (malloc_func == NULL || realloc_func == NULL || calloc_func == NULL || free_func == NULL) { return UV_EINVAL; } uv__allocator.local_malloc = malloc_func; uv__allocator.local_realloc = realloc_func; uv__allocator.local_calloc = calloc_func; uv__allocator.local_free = free_func; return 0; } #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t); size_t uv_handle_size(uv_handle_type type) { switch (type) { UV_HANDLE_TYPE_MAP(XX) default: return -1; } } size_t uv_req_size(uv_req_type type) { switch(type) { UV_REQ_TYPE_MAP(XX) default: return -1; } } #undef XX size_t uv_loop_size(void) { return sizeof(uv_loop_t); } uv_buf_t uv_buf_init(char* base, unsigned int len) { uv_buf_t buf; buf.base = base; buf.len = len; return buf; } static const char* uv__unknown_err_code(int err) { char buf[32]; char* copy; snprintf(buf, sizeof(buf), "Unknown system error %d", err); copy = uv__strdup(buf); return copy != NULL ? copy : "Unknown system error"; } #define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name; const char* uv_err_name(int err) { switch (err) { UV_ERRNO_MAP(UV_ERR_NAME_GEN) } return uv__unknown_err_code(err); } #undef UV_ERR_NAME_GEN #define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg; const char* uv_strerror(int err) { switch (err) { UV_ERRNO_MAP(UV_STRERROR_GEN) } return uv__unknown_err_code(err); } #undef UV_STRERROR_GEN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) { memset(addr, 0, sizeof(*addr)); addr->sin_family = AF_INET; addr->sin_port = htons(port); return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr)); } int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) { char address_part[40]; size_t address_part_size; const char* zone_index; memset(addr, 0, sizeof(*addr)); addr->sin6_family = AF_INET6; addr->sin6_port = htons(port); zone_index = strchr(ip, '%'); if (zone_index != NULL) { address_part_size = zone_index - ip; if (address_part_size >= sizeof(address_part)) address_part_size = sizeof(address_part) - 1; memcpy(address_part, ip, address_part_size); address_part[address_part_size] = '\0'; ip = address_part; zone_index++; /* skip '%' */ /* NOTE: unknown interface (id=0) is silently ignored */ #ifdef _WIN32 addr->sin6_scope_id = atoi(zone_index); #else addr->sin6_scope_id = if_nametoindex(zone_index); #endif } return uv_inet_pton(AF_INET6, ip, &addr->sin6_addr); } int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) { return uv_inet_ntop(AF_INET, &src->sin_addr, dst, size); } int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) { return uv_inet_ntop(AF_INET6, &src->sin6_addr, dst, size); } int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags) { unsigned int addrlen; if (handle->type != UV_TCP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__tcp_bind(handle, addr, addrlen, flags); } int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__udp_bind(handle, addr, addrlen, flags); } int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, uv_connect_cb cb) { unsigned int addrlen; if (handle->type != UV_TCP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__tcp_connect(req, handle, addr, addrlen, cb); } int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, uv_udp_send_cb send_cb) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb); } int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen); } int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { if (handle->type != UV_UDP || alloc_cb == NULL || recv_cb == NULL) return UV_EINVAL; else return uv__udp_recv_start(handle, alloc_cb, recv_cb); } int uv_udp_recv_stop(uv_udp_t* handle) { if (handle->type != UV_UDP) return UV_EINVAL; else return uv__udp_recv_stop(handle); } void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { QUEUE queue; QUEUE* q; uv_handle_t* h; QUEUE_MOVE(&loop->handle_queue, &queue); while (!QUEUE_EMPTY(&queue)) { q = QUEUE_HEAD(&queue); h = QUEUE_DATA(q, uv_handle_t, handle_queue); QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->handle_queue, q); if (h->flags & UV__HANDLE_INTERNAL) continue; walk_cb(h, arg); } } static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) { const char* type; QUEUE* q; uv_handle_t* h; if (loop == NULL) loop = uv_default_loop(); QUEUE_FOREACH(q, &loop->handle_queue) { h = QUEUE_DATA(q, uv_handle_t, handle_queue); if (only_active && !uv__is_active(h)) continue; switch (h->type) { #define X(uc, lc) case UV_##uc: type = #lc; break; UV_HANDLE_TYPE_MAP(X) #undef X default: type = ""; } fprintf(stream, "[%c%c%c] %-8s %p\n", "R-"[!(h->flags & UV__HANDLE_REF)], "A-"[!(h->flags & UV__HANDLE_ACTIVE)], "I-"[!(h->flags & UV__HANDLE_INTERNAL)], type, (void*)h); } } void uv_print_all_handles(uv_loop_t* loop, FILE* stream) { uv__print_handles(loop, 0, stream); } void uv_print_active_handles(uv_loop_t* loop, FILE* stream) { uv__print_handles(loop, 1, stream); } void uv_ref(uv_handle_t* handle) { uv__handle_ref(handle); } void uv_unref(uv_handle_t* handle) { uv__handle_unref(handle); } int uv_has_ref(const uv_handle_t* handle) { return uv__has_ref(handle); } void uv_stop(uv_loop_t* loop) { loop->stop_flag = 1; } uint64_t uv_now(const uv_loop_t* loop) { return loop->time; } size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) { unsigned int i; size_t bytes; bytes = 0; for (i = 0; i < nbufs; i++) bytes += (size_t) bufs[i].len; return bytes; } int uv_recv_buffer_size(uv_handle_t* handle, int* value) { return uv__socket_sockopt(handle, SO_RCVBUF, value); } int uv_send_buffer_size(uv_handle_t* handle, int *value) { return uv__socket_sockopt(handle, SO_SNDBUF, value); } int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) { size_t required_len; if (!uv__is_active(handle)) { *size = 0; return UV_EINVAL; } required_len = strlen(handle->path); if (required_len > *size) { *size = required_len; return UV_ENOBUFS; } memcpy(buffer, handle->path, required_len); *size = required_len; return 0; } /* The windows implementation does not have the same structure layout as * the unix implementation (nbufs is not directly inside req but is * contained in a nested union/struct) so this function locates it. */ static unsigned int* uv__get_nbufs(uv_fs_t* req) { #ifdef _WIN32 return &req->fs.info.nbufs; #else return &req->nbufs; #endif } void uv__fs_scandir_cleanup(uv_fs_t* req) { uv__dirent_t** dents; unsigned int* nbufs = uv__get_nbufs(req); dents = req->ptr; if (*nbufs > 0 && *nbufs != (unsigned int) req->result) (*nbufs)--; for (; *nbufs < (unsigned int) req->result; (*nbufs)++) uv__free(dents[*nbufs]); } int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { uv__dirent_t** dents; uv__dirent_t* dent; unsigned int* nbufs = uv__get_nbufs(req); dents = req->ptr; /* Free previous entity */ if (*nbufs > 0) uv__free(dents[*nbufs - 1]); /* End was already reached */ if (*nbufs == (unsigned int) req->result) { uv__free(dents); req->ptr = NULL; return UV_EOF; } dent = dents[(*nbufs)++]; ent->name = dent->d_name; #ifdef HAVE_DIRENT_TYPES switch (dent->d_type) { case UV__DT_DIR: ent->type = UV_DIRENT_DIR; break; case UV__DT_FILE: ent->type = UV_DIRENT_FILE; break; case UV__DT_LINK: ent->type = UV_DIRENT_LINK; break; case UV__DT_FIFO: ent->type = UV_DIRENT_FIFO; break; case UV__DT_SOCKET: ent->type = UV_DIRENT_SOCKET; break; case UV__DT_CHAR: ent->type = UV_DIRENT_CHAR; break; case UV__DT_BLOCK: ent->type = UV_DIRENT_BLOCK; break; default: ent->type = UV_DIRENT_UNKNOWN; } #else ent->type = UV_DIRENT_UNKNOWN; #endif return 0; } int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) { va_list ap; int err; va_start(ap, option); /* Any platform-agnostic options should be handled here. */ err = uv__loop_configure(loop, option, ap); va_end(ap); return err; } static uv_loop_t default_loop_struct; static uv_loop_t* default_loop_ptr; uv_loop_t* uv_default_loop(void) { if (default_loop_ptr != NULL) return default_loop_ptr; if (uv_loop_init(&default_loop_struct)) return NULL; default_loop_ptr = &default_loop_struct; return default_loop_ptr; } uv_loop_t* uv_loop_new(void) { uv_loop_t* loop; loop = uv__malloc(sizeof(*loop)); if (loop == NULL) return NULL; if (uv_loop_init(loop)) { uv__free(loop); return NULL; } return loop; } int uv_loop_close(uv_loop_t* loop) { QUEUE* q; uv_handle_t* h; if (!QUEUE_EMPTY(&(loop)->active_reqs)) return UV_EBUSY; QUEUE_FOREACH(q, &loop->handle_queue) { h = QUEUE_DATA(q, uv_handle_t, handle_queue); if (!(h->flags & UV__HANDLE_INTERNAL)) return UV_EBUSY; } uv__loop_close(loop); #ifndef NDEBUG memset(loop, -1, sizeof(*loop)); #endif if (loop == default_loop_ptr) default_loop_ptr = NULL; return 0; } void uv_loop_delete(uv_loop_t* loop) { uv_loop_t* default_loop; int err; default_loop = default_loop_ptr; err = uv_loop_close(loop); (void) err; /* Squelch compiler warnings. */ assert(err == 0); if (loop != default_loop) uv__free(loop); } libuv-v1.8.0/src/uv-common.h000066400000000000000000000224411263361716600157250ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * This file is private to libuv. It provides common functionality to both * Windows and Unix backends. */ #ifndef UV_COMMON_H_ #define UV_COMMON_H_ #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "stdint-msvc2008.h" #else # include #endif #include "uv.h" #include "tree.h" #include "queue.h" #if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 extern int snprintf(char*, size_t, const char*, ...); #endif #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) #define STATIC_ASSERT(expr) \ void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)]) #ifndef _WIN32 enum { UV__HANDLE_INTERNAL = 0x8000, UV__HANDLE_ACTIVE = 0x4000, UV__HANDLE_REF = 0x2000, UV__HANDLE_CLOSING = 0 /* no-op on unix */ }; #else # define UV__HANDLE_INTERNAL 0x80 # define UV__HANDLE_ACTIVE 0x40 # define UV__HANDLE_REF 0x20 # define UV__HANDLE_CLOSING 0x01 #endif int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); void uv__loop_close(uv_loop_t* loop); int uv__tcp_bind(uv_tcp_t* tcp, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags); int uv__tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, uv_connect_cb cb); int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags); int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen, uv_udp_send_cb send_cb); int uv__udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen); int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb, uv_udp_recv_cb recv_cb); int uv__udp_recv_stop(uv_udp_t* handle); void uv__fs_poll_close(uv_fs_poll_t* handle); int uv__getaddrinfo_translate_error(int sys_err); /* EAI_* error. */ void uv__work_submit(uv_loop_t* loop, struct uv__work *w, void (*work)(struct uv__work *w), void (*done)(struct uv__work *w, int status)); void uv__work_done(uv_async_t* handle); size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs); int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value); void uv__fs_scandir_cleanup(uv_fs_t* req); #define uv__has_active_reqs(loop) \ (QUEUE_EMPTY(&(loop)->active_reqs) == 0) #define uv__req_register(loop, req) \ do { \ QUEUE_INSERT_TAIL(&(loop)->active_reqs, &(req)->active_queue); \ } \ while (0) #define uv__req_unregister(loop, req) \ do { \ assert(uv__has_active_reqs(loop)); \ QUEUE_REMOVE(&(req)->active_queue); \ } \ while (0) #define uv__has_active_handles(loop) \ ((loop)->active_handles > 0) #define uv__active_handle_add(h) \ do { \ (h)->loop->active_handles++; \ } \ while (0) #define uv__active_handle_rm(h) \ do { \ (h)->loop->active_handles--; \ } \ while (0) #define uv__is_active(h) \ (((h)->flags & UV__HANDLE_ACTIVE) != 0) #define uv__is_closing(h) \ (((h)->flags & (UV_CLOSING | UV_CLOSED)) != 0) #define uv__handle_start(h) \ do { \ assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \ if (((h)->flags & UV__HANDLE_ACTIVE) != 0) break; \ (h)->flags |= UV__HANDLE_ACTIVE; \ if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_add(h); \ } \ while (0) #define uv__handle_stop(h) \ do { \ assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \ if (((h)->flags & UV__HANDLE_ACTIVE) == 0) break; \ (h)->flags &= ~UV__HANDLE_ACTIVE; \ if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_rm(h); \ } \ while (0) #define uv__handle_ref(h) \ do { \ if (((h)->flags & UV__HANDLE_REF) != 0) break; \ (h)->flags |= UV__HANDLE_REF; \ if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \ if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_add(h); \ } \ while (0) #define uv__handle_unref(h) \ do { \ if (((h)->flags & UV__HANDLE_REF) == 0) break; \ (h)->flags &= ~UV__HANDLE_REF; \ if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \ if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_rm(h); \ } \ while (0) #define uv__has_ref(h) \ (((h)->flags & UV__HANDLE_REF) != 0) #if defined(_WIN32) # define uv__handle_platform_init(h) ((h)->u.fd = -1) #else # define uv__handle_platform_init(h) ((h)->next_closing = NULL) #endif #define uv__handle_init(loop_, h, type_) \ do { \ (h)->loop = (loop_); \ (h)->type = (type_); \ (h)->flags = UV__HANDLE_REF; /* Ref the loop when active. */ \ QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); \ uv__handle_platform_init(h); \ } \ while (0) /* Allocator prototypes */ void *uv__calloc(size_t count, size_t size); char *uv__strdup(const char* s); char *uv__strndup(const char* s, size_t n); void* uv__malloc(size_t size); void uv__free(void* ptr); void* uv__realloc(void* ptr, size_t size); #endif /* UV_COMMON_H_ */ libuv-v1.8.0/src/version.c000066400000000000000000000033271263361716600154670ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #define UV_STRINGIFY(v) UV_STRINGIFY_HELPER(v) #define UV_STRINGIFY_HELPER(v) #v #define UV_VERSION_STRING_BASE UV_STRINGIFY(UV_VERSION_MAJOR) "." \ UV_STRINGIFY(UV_VERSION_MINOR) "." \ UV_STRINGIFY(UV_VERSION_PATCH) #if UV_VERSION_IS_RELEASE # define UV_VERSION_STRING UV_VERSION_STRING_BASE #else # define UV_VERSION_STRING UV_VERSION_STRING_BASE "-" UV_VERSION_SUFFIX #endif unsigned int uv_version(void) { return UV_VERSION_HEX; } const char* uv_version_string(void) { return UV_VERSION_STRING; } libuv-v1.8.0/src/win/000077500000000000000000000000001263361716600144265ustar00rootroot00000000000000libuv-v1.8.0/src/win/async.c000066400000000000000000000055121263361716600157120ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "uv.h" #include "internal.h" #include "atomicops-inl.h" #include "handle-inl.h" #include "req-inl.h" void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) { if (handle->flags & UV__HANDLE_CLOSING && !handle->async_sent) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } } int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { uv_req_t* req; uv__handle_init(loop, (uv_handle_t*) handle, UV_ASYNC); handle->async_sent = 0; handle->async_cb = async_cb; req = &handle->async_req; uv_req_init(loop, req); req->type = UV_WAKEUP; req->data = handle; uv__handle_start(handle); return 0; } void uv_async_close(uv_loop_t* loop, uv_async_t* handle) { if (!((uv_async_t*)handle)->async_sent) { uv_want_endgame(loop, (uv_handle_t*) handle); } uv__handle_closing(handle); } int uv_async_send(uv_async_t* handle) { uv_loop_t* loop = handle->loop; if (handle->type != UV_ASYNC) { /* Can't set errno because that's not thread-safe. */ return -1; } /* The user should make sure never to call uv_async_send to a closing */ /* or closed handle. */ assert(!(handle->flags & UV__HANDLE_CLOSING)); if (!uv__atomic_exchange_set(&handle->async_sent)) { POST_COMPLETION_FOR_REQ(loop, &handle->async_req); } return 0; } void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, uv_req_t* req) { assert(handle->type == UV_ASYNC); assert(req->type == UV_WAKEUP); handle->async_sent = 0; if (handle->flags & UV__HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*)handle); } else if (handle->async_cb != NULL) { handle->async_cb(handle); } } libuv-v1.8.0/src/win/atomicops-inl.h000066400000000000000000000041731263361716600173620ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_ATOMICOPS_INL_H_ #define UV_WIN_ATOMICOPS_INL_H_ #include "uv.h" /* Atomic set operation on char */ #ifdef _MSC_VER /* MSVC */ /* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */ /* efficient than InterlockedExchange, but InterlockedExchange8 does not */ /* exist, and interlocked operations on larger targets might require the */ /* target to be aligned. */ #pragma intrinsic(_InterlockedOr8) static char __declspec(inline) uv__atomic_exchange_set(char volatile* target) { return _InterlockedOr8(target, 1); } #else /* GCC */ /* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ static inline char uv__atomic_exchange_set(char volatile* target) { const char one = 1; char old_value; __asm__ __volatile__ ("lock xchgb %0, %1\n\t" : "=r"(old_value), "=m"(*target) : "0"(one), "m"(*target) : "memory"); return old_value; } #endif #endif /* UV_WIN_ATOMICOPS_INL_H_ */ libuv-v1.8.0/src/win/core.c000066400000000000000000000270401263361716600155250ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) #include #endif #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" static uv_loop_t default_loop_struct; static uv_loop_t* default_loop_ptr; /* uv_once initialization guards */ static uv_once_t uv_init_guard_ = UV_ONCE_INIT; #if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)) /* Our crt debug report handler allows us to temporarily disable asserts * just for the current thread. */ UV_THREAD_LOCAL int uv__crt_assert_enabled = TRUE; static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_val) { if (uv__crt_assert_enabled || report_type != _CRT_ASSERT) return FALSE; if (ret_val) { /* Set ret_val to 0 to continue with normal execution. * Set ret_val to 1 to trigger a breakpoint. */ if(IsDebuggerPresent()) *ret_val = 1; else *ret_val = 0; } /* Don't call _CrtDbgReport. */ return TRUE; } #else UV_THREAD_LOCAL int uv__crt_assert_enabled = FALSE; #endif #if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800 static void uv__crt_invalid_parameter_handler(const wchar_t* expression, const wchar_t* function, const wchar_t * file, unsigned int line, uintptr_t reserved) { /* No-op. */ } #endif static void uv_init(void) { /* Tell Windows that we will handle critical errors. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); /* Tell the CRT to not exit the application when an invalid parameter is * passed. The main issue is that invalid FDs will trigger this behavior. */ #if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800 _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler); #endif /* We also need to setup our debug report handler because some CRT * functions (eg _get_osfhandle) raise an assert when called with invalid * FDs even though they return the proper error code in the release build. */ #if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)) _CrtSetReportHook(uv__crt_dbg_report_handler); #endif /* Fetch winapi function pointers. This must be done first because other * initialization code might need these function pointers to be loaded. */ uv_winapi_init(); /* Initialize winsock */ uv_winsock_init(); /* Initialize FS */ uv_fs_init(); /* Initialize signal stuff */ uv_signals_init(); /* Initialize console */ uv_console_init(); /* Initialize utilities */ uv__util_init(); } int uv_loop_init(uv_loop_t* loop) { int err; /* Initialize libuv itself first */ uv__once_init(); /* Create an I/O completion port */ loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. */ loop->time = 0; uv_update_time(loop); QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->handle_queue); QUEUE_INIT(&loop->active_reqs); loop->active_handles = 0; loop->pending_reqs_tail = NULL; loop->endgame_handles = NULL; RB_INIT(&loop->timers); loop->check_handles = NULL; loop->prepare_handles = NULL; loop->idle_handles = NULL; loop->next_prepare_handle = NULL; loop->next_check_handle = NULL; loop->next_idle_handle = NULL; memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets); loop->active_tcp_streams = 0; loop->active_udp_streams = 0; loop->timer_counter = 0; loop->stop_flag = 0; err = uv_mutex_init(&loop->wq_mutex); if (err) goto fail_mutex_init; err = uv_async_init(loop, &loop->wq_async, uv__work_done); if (err) goto fail_async_init; uv__handle_unref(&loop->wq_async); loop->wq_async.flags |= UV__HANDLE_INTERNAL; return 0; fail_async_init: uv_mutex_destroy(&loop->wq_mutex); fail_mutex_init: CloseHandle(loop->iocp); loop->iocp = INVALID_HANDLE_VALUE; return err; } void uv__once_init(void) { uv_once(&uv_init_guard_, uv_init); } void uv__loop_close(uv_loop_t* loop) { size_t i; /* close the async handle without needing an extra loop iteration */ assert(!loop->wq_async.async_sent); loop->wq_async.close_cb = NULL; uv__handle_closing(&loop->wq_async); uv__handle_close(&loop->wq_async); for (i = 0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++) { SOCKET sock = loop->poll_peer_sockets[i]; if (sock != 0 && sock != INVALID_SOCKET) closesocket(sock); } uv_mutex_lock(&loop->wq_mutex); assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); assert(!uv__has_active_reqs(loop)); uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); CloseHandle(loop->iocp); } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { return UV_ENOSYS; } int uv_backend_fd(const uv_loop_t* loop) { return -1; } int uv_backend_timeout(const uv_loop_t* loop) { if (loop->stop_flag != 0) return 0; if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) return 0; if (loop->pending_reqs_tail) return 0; if (loop->endgame_handles) return 0; if (loop->idle_handles) return 0; return uv__next_timeout(loop); } static void uv_poll(uv_loop_t* loop, DWORD timeout) { DWORD bytes; ULONG_PTR key; OVERLAPPED* overlapped; uv_req_t* req; GetQueuedCompletionStatus(loop->iocp, &bytes, &key, &overlapped, timeout); if (overlapped) { /* Package was dequeued */ req = uv_overlapped_to_req(overlapped); uv_insert_pending_req(loop, req); /* Some time might have passed waiting for I/O, * so update the loop time here. */ uv_update_time(loop); } else if (GetLastError() != WAIT_TIMEOUT) { /* Serious error */ uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); } else if (timeout > 0) { /* GetQueuedCompletionStatus can occasionally return a little early. * Make sure that the desired timeout is reflected in the loop time. */ uv__time_forward(loop, timeout); } } static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { BOOL success; uv_req_t* req; OVERLAPPED_ENTRY overlappeds[128]; ULONG count; ULONG i; success = pGetQueuedCompletionStatusEx(loop->iocp, overlappeds, ARRAY_SIZE(overlappeds), &count, timeout, FALSE); if (success) { for (i = 0; i < count; i++) { /* Package was dequeued */ req = uv_overlapped_to_req(overlappeds[i].lpOverlapped); uv_insert_pending_req(loop, req); } /* Some time might have passed waiting for I/O, * so update the loop time here. */ uv_update_time(loop); } else if (GetLastError() != WAIT_TIMEOUT) { /* Serious error */ uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx"); } else if (timeout > 0) { /* GetQueuedCompletionStatus can occasionally return a little early. * Make sure that the desired timeout is reflected in the loop time. */ uv__time_forward(loop, timeout); } } static int uv__loop_alive(const uv_loop_t* loop) { return loop->active_handles > 0 || !QUEUE_EMPTY(&loop->active_reqs) || loop->endgame_handles != NULL; } int uv_loop_alive(const uv_loop_t* loop) { return uv__loop_alive(loop); } int uv_run(uv_loop_t *loop, uv_run_mode mode) { DWORD timeout; int r; int ran_pending; void (*poll)(uv_loop_t* loop, DWORD timeout); if (pGetQueuedCompletionStatusEx) poll = &uv_poll_ex; else poll = &uv_poll; r = uv__loop_alive(loop); if (!r) uv_update_time(loop); while (r != 0 && loop->stop_flag == 0) { uv_update_time(loop); uv_process_timers(loop); ran_pending = uv_process_reqs(loop); uv_idle_invoke(loop); uv_prepare_invoke(loop); timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); (*poll)(loop, timeout); uv_check_invoke(loop); uv_process_endgames(loop); if (mode == UV_RUN_ONCE) { /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. * * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ uv_process_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; } /* The if statement lets the compiler compile it to a conditional store. * Avoids dirtying a cache line. */ if (loop->stop_flag != 0) loop->stop_flag = 0; return r; } int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { uv_os_fd_t fd_out; switch (handle->type) { case UV_TCP: fd_out = (uv_os_fd_t)((uv_tcp_t*) handle)->socket; break; case UV_NAMED_PIPE: fd_out = ((uv_pipe_t*) handle)->handle; break; case UV_TTY: fd_out = ((uv_tty_t*) handle)->handle; break; case UV_UDP: fd_out = (uv_os_fd_t)((uv_udp_t*) handle)->socket; break; case UV_POLL: fd_out = (uv_os_fd_t)((uv_poll_t*) handle)->socket; break; default: return UV_EINVAL; } if (uv_is_closing(handle) || fd_out == INVALID_HANDLE_VALUE) return UV_EBADF; *fd = fd_out; return 0; } int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { int r; int len; SOCKET socket; if (handle == NULL || value == NULL) return UV_EINVAL; if (handle->type == UV_TCP) socket = ((uv_tcp_t*) handle)->socket; else if (handle->type == UV_UDP) socket = ((uv_udp_t*) handle)->socket; else return UV_ENOTSUP; len = sizeof(*value); if (*value == 0) r = getsockopt(socket, SOL_SOCKET, optname, (char*) value, &len); else r = setsockopt(socket, SOL_SOCKET, optname, (const char*) value, len); if (r == SOCKET_ERROR) return uv_translate_sys_error(WSAGetLastError()); return 0; } libuv-v1.8.0/src/win/dl.c000066400000000000000000000067301263361716600151770ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "internal.h" static int uv__dlerror(uv_lib_t* lib, int errorno); int uv_dlopen(const char* filename, uv_lib_t* lib) { WCHAR filename_w[32768]; lib->handle = NULL; lib->errmsg = NULL; if (!uv_utf8_to_utf16(filename, filename_w, ARRAY_SIZE(filename_w))) { return uv__dlerror(lib, GetLastError()); } lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (lib->handle == NULL) { return uv__dlerror(lib, GetLastError()); } return 0; } void uv_dlclose(uv_lib_t* lib) { if (lib->errmsg) { LocalFree((void*)lib->errmsg); lib->errmsg = NULL; } if (lib->handle) { /* Ignore errors. No good way to signal them without leaking memory. */ FreeLibrary(lib->handle); lib->handle = NULL; } } int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) { *ptr = (void*) GetProcAddress(lib->handle, name); return uv__dlerror(lib, *ptr ? 0 : GetLastError()); } const char* uv_dlerror(const uv_lib_t* lib) { return lib->errmsg ? lib->errmsg : "no error"; } static void uv__format_fallback_error(uv_lib_t* lib, int errorno){ DWORD_PTR args[1] = { (DWORD_PTR) errorno }; LPSTR fallback_error = "error: %1!d!"; FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER, fallback_error, 0, 0, (LPSTR) &lib->errmsg, 0, (va_list*) args); } static int uv__dlerror(uv_lib_t* lib, int errorno) { DWORD res; if (lib->errmsg) { LocalFree((void*)lib->errmsg); lib->errmsg = NULL; } if (errorno) { res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR) &lib->errmsg, 0, NULL); if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, 0, (LPSTR) &lib->errmsg, 0, NULL); } if (!res) { uv__format_fallback_error(lib, errorno); } } return errorno ? -1 : 0; } libuv-v1.8.0/src/win/error.c000066400000000000000000000202061263361716600157230ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include "uv.h" #include "internal.h" /* * Display an error message and abort the event loop. */ void uv_fatal_error(const int errorno, const char* syscall) { char* buf = NULL; const char* errmsg; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL); if (buf) { errmsg = buf; } else { errmsg = "Unknown error"; } /* FormatMessage messages include a newline character already, */ /* so don't add another. */ if (syscall) { fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg); } else { fprintf(stderr, "(%d) %s", errorno, errmsg); } if (buf) { LocalFree(buf); } *((char*)NULL) = 0xff; /* Force debug break */ abort(); } int uv_translate_sys_error(int sys_errno) { if (sys_errno <= 0) { return sys_errno; /* If < 0 then it's already a libuv error. */ } switch (sys_errno) { case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT; case WSAEWOULDBLOCK: return UV_EAGAIN; case WSAEALREADY: return UV_EALREADY; case ERROR_INVALID_FLAGS: return UV_EBADF; case ERROR_INVALID_HANDLE: return UV_EBADF; case ERROR_LOCK_VIOLATION: return UV_EBUSY; case ERROR_PIPE_BUSY: return UV_EBUSY; case ERROR_SHARING_VIOLATION: return UV_EBUSY; case ERROR_OPERATION_ABORTED: return UV_ECANCELED; case WSAEINTR: return UV_ECANCELED; case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; case ERROR_CONNECTION_ABORTED: return UV_ECONNABORTED; case WSAECONNABORTED: return UV_ECONNABORTED; case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; case WSAECONNREFUSED: return UV_ECONNREFUSED; case ERROR_NETNAME_DELETED: return UV_ECONNRESET; case WSAECONNRESET: return UV_ECONNRESET; case ERROR_ALREADY_EXISTS: return UV_EEXIST; case ERROR_FILE_EXISTS: return UV_EEXIST; case ERROR_BUFFER_OVERFLOW: return UV_EFAULT; case WSAEFAULT: return UV_EFAULT; case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; case ERROR_INVALID_DATA: return UV_EINVAL; case ERROR_INVALID_PARAMETER: return UV_EINVAL; case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL; case WSAEINVAL: return UV_EINVAL; case WSAEPFNOSUPPORT: return UV_EINVAL; case WSAESOCKTNOSUPPORT: return UV_EINVAL; case ERROR_BEGINNING_OF_MEDIA: return UV_EIO; case ERROR_BUS_RESET: return UV_EIO; case ERROR_CRC: return UV_EIO; case ERROR_DEVICE_DOOR_OPEN: return UV_EIO; case ERROR_DEVICE_REQUIRES_CLEANING: return UV_EIO; case ERROR_DISK_CORRUPT: return UV_EIO; case ERROR_EOM_OVERFLOW: return UV_EIO; case ERROR_FILEMARK_DETECTED: return UV_EIO; case ERROR_GEN_FAILURE: return UV_EIO; case ERROR_INVALID_BLOCK_LENGTH: return UV_EIO; case ERROR_IO_DEVICE: return UV_EIO; case ERROR_NO_DATA_DETECTED: return UV_EIO; case ERROR_NO_SIGNAL_SENT: return UV_EIO; case ERROR_OPEN_FAILED: return UV_EIO; case ERROR_SETMARK_DETECTED: return UV_EIO; case ERROR_SIGNAL_REFUSED: return UV_EIO; case WSAEISCONN: return UV_EISCONN; case ERROR_CANT_RESOLVE_FILENAME: return UV_ELOOP; case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; case WSAEMFILE: return UV_EMFILE; case WSAEMSGSIZE: return UV_EMSGSIZE; case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG; case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH; case WSAENETUNREACH: return UV_ENETUNREACH; case WSAENOBUFS: return UV_ENOBUFS; case ERROR_BAD_PATHNAME: return UV_ENOENT; case ERROR_DIRECTORY: return UV_ENOENT; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; case ERROR_INVALID_DRIVE: return UV_ENOENT; case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT; case ERROR_MOD_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; case WSAHOST_NOT_FOUND: return UV_ENOENT; case WSANO_DATA: return UV_ENOENT; case ERROR_NOT_ENOUGH_MEMORY: return UV_ENOMEM; case ERROR_OUTOFMEMORY: return UV_ENOMEM; case ERROR_CANNOT_MAKE: return UV_ENOSPC; case ERROR_DISK_FULL: return UV_ENOSPC; case ERROR_EA_TABLE_FULL: return UV_ENOSPC; case ERROR_END_OF_MEDIA: return UV_ENOSPC; case ERROR_HANDLE_DISK_FULL: return UV_ENOSPC; case ERROR_NOT_CONNECTED: return UV_ENOTCONN; case WSAENOTCONN: return UV_ENOTCONN; case ERROR_DIR_NOT_EMPTY: return UV_ENOTEMPTY; case WSAENOTSOCK: return UV_ENOTSOCK; case ERROR_NOT_SUPPORTED: return UV_ENOTSUP; case ERROR_BROKEN_PIPE: return UV_EOF; case ERROR_ACCESS_DENIED: return UV_EPERM; case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM; case ERROR_BAD_PIPE: return UV_EPIPE; case ERROR_NO_DATA: return UV_EPIPE; case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE; case WSAESHUTDOWN: return UV_EPIPE; case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT; case ERROR_WRITE_PROTECT: return UV_EROFS; case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; case WSAETIMEDOUT: return UV_ETIMEDOUT; case ERROR_NOT_SAME_DEVICE: return UV_EXDEV; case ERROR_INVALID_FUNCTION: return UV_EISDIR; case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG; default: return UV_UNKNOWN; } } libuv-v1.8.0/src/win/fs-event.c000066400000000000000000000410041263361716600163200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" const unsigned int uv_directory_watcher_buffer_size = 4096; static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop, uv_fs_event_t* handle) { assert(handle->dir_handle != INVALID_HANDLE_VALUE); assert(!handle->req_pending); memset(&(handle->req.u.io.overlapped), 0, sizeof(handle->req.u.io.overlapped)); if (!ReadDirectoryChangesW(handle->dir_handle, handle->buffer, uv_directory_watcher_buffer_size, (handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, NULL, &handle->req.u.io.overlapped, NULL)) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(&handle->req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)&handle->req); } handle->req_pending = 1; } static int uv_relative_path(const WCHAR* filename, const WCHAR* dir, WCHAR** relpath) { int dirlen = wcslen(dir); int filelen = wcslen(filename); if (dir[dirlen - 1] == '\\') dirlen--; *relpath = uv__malloc((MAX_PATH + 1) * sizeof(WCHAR)); if (!*relpath) uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); wcsncpy(*relpath, filename + dirlen + 1, filelen - dirlen - 1); (*relpath)[filelen - dirlen - 1] = L'\0'; return 0; } static int uv_split_path(const WCHAR* filename, WCHAR** dir, WCHAR** file) { int len = wcslen(filename); int i = len; while (i > 0 && filename[--i] != '\\' && filename[i] != '/'); if (i == 0) { if (dir) { *dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR)); if (!*dir) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { uv__free(*dir); *dir = NULL; return -1; } } *file = wcsdup(filename); } else { if (dir) { *dir = (WCHAR*)uv__malloc((i + 1) * sizeof(WCHAR)); if (!*dir) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } wcsncpy(*dir, filename, i); (*dir)[i] = L'\0'; } *file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR)); if (!*file) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } wcsncpy(*file, filename + i + 1, len - i - 1); (*file)[len - i - 1] = L'\0'; } return 0; } int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT); handle->dir_handle = INVALID_HANDLE_VALUE; handle->buffer = NULL; handle->req_pending = 0; handle->filew = NULL; handle->short_filew = NULL; handle->dirw = NULL; uv_req_init(loop, (uv_req_t*)&handle->req); handle->req.type = UV_FS_EVENT_REQ; handle->req.data = handle; return 0; } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) { int name_size, is_path_dir; DWORD attr, last_error; WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL; WCHAR short_path[MAX_PATH]; if (uv__is_active(handle)) return UV_EINVAL; handle->cb = cb; handle->path = uv__strdup(path); if (!handle->path) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } uv__handle_start(handle); /* Convert name to UTF16. */ name_size = uv_utf8_to_utf16(path, NULL, 0) * sizeof(WCHAR); pathw = (WCHAR*)uv__malloc(name_size); if (!pathw) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } if (!uv_utf8_to_utf16(path, pathw, name_size / sizeof(WCHAR))) { return uv_translate_sys_error(GetLastError()); } /* Determine whether path is a file or a directory. */ attr = GetFileAttributesW(pathw); if (attr == INVALID_FILE_ATTRIBUTES) { last_error = GetLastError(); goto error; } is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; if (is_path_dir) { /* path is a directory, so that's the directory that we will watch. */ handle->dirw = pathw; dir_to_watch = pathw; } else { /* * path is a file. So we split path into dir & file parts, and * watch the dir directory. */ /* Convert to short path. */ if (!GetShortPathNameW(pathw, short_path, ARRAY_SIZE(short_path))) { last_error = GetLastError(); goto error; } if (uv_split_path(pathw, &dir, &handle->filew) != 0) { last_error = GetLastError(); goto error; } if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) { last_error = GetLastError(); goto error; } dir_to_watch = dir; uv__free(pathw); pathw = NULL; } handle->dir_handle = CreateFileW(dir_to_watch, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (dir) { uv__free(dir); dir = NULL; } if (handle->dir_handle == INVALID_HANDLE_VALUE) { last_error = GetLastError(); goto error; } if (CreateIoCompletionPort(handle->dir_handle, handle->loop->iocp, (ULONG_PTR)handle, 0) == NULL) { last_error = GetLastError(); goto error; } if (!handle->buffer) { handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size); } if (!handle->buffer) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } memset(&(handle->req.u.io.overlapped), 0, sizeof(handle->req.u.io.overlapped)); if (!ReadDirectoryChangesW(handle->dir_handle, handle->buffer, uv_directory_watcher_buffer_size, (flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, NULL, &handle->req.u.io.overlapped, NULL)) { last_error = GetLastError(); goto error; } handle->req_pending = 1; return 0; error: if (handle->path) { uv__free(handle->path); handle->path = NULL; } if (handle->filew) { uv__free(handle->filew); handle->filew = NULL; } if (handle->short_filew) { uv__free(handle->short_filew); handle->short_filew = NULL; } uv__free(pathw); if (handle->dir_handle != INVALID_HANDLE_VALUE) { CloseHandle(handle->dir_handle); handle->dir_handle = INVALID_HANDLE_VALUE; } if (handle->buffer) { uv__free(handle->buffer); handle->buffer = NULL; } return uv_translate_sys_error(last_error); } int uv_fs_event_stop(uv_fs_event_t* handle) { if (!uv__is_active(handle)) return 0; if (handle->dir_handle != INVALID_HANDLE_VALUE) { CloseHandle(handle->dir_handle); handle->dir_handle = INVALID_HANDLE_VALUE; } uv__handle_stop(handle); if (handle->filew) { uv__free(handle->filew); handle->filew = NULL; } if (handle->short_filew) { uv__free(handle->short_filew); handle->short_filew = NULL; } if (handle->path) { uv__free(handle->path); handle->path = NULL; } if (handle->dirw) { uv__free(handle->dirw); handle->dirw = NULL; } return 0; } void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle) { FILE_NOTIFY_INFORMATION* file_info; int err, sizew, size, result; char* filename = NULL; WCHAR* filenamew, *long_filenamew = NULL; DWORD offset = 0; assert(req->type == UV_FS_EVENT_REQ); assert(handle->req_pending); handle->req_pending = 0; /* Don't report any callbacks if: * - We're closing, just push the handle onto the endgame queue * - We are not active, just ignore the callback */ if (!uv__is_active(handle)) { if (handle->flags & UV__HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); } return; } file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset); if (REQ_SUCCESS(req)) { if (req->u.io.overlapped.InternalHigh > 0) { do { file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset); assert(!filename); assert(!long_filenamew); /* * Fire the event only if we were asked to watch a directory, * or if the filename filter matches. */ if (handle->dirw || _wcsnicmp(handle->filew, file_info->FileName, file_info->FileNameLength / sizeof(WCHAR)) == 0 || _wcsnicmp(handle->short_filew, file_info->FileName, file_info->FileNameLength / sizeof(WCHAR)) == 0) { if (handle->dirw) { /* * We attempt to resolve the long form of the file name explicitly. * We only do this for file names that might still exist on disk. * If this fails, we use the name given by ReadDirectoryChangesW. * This may be the long form or the 8.3 short name in some cases. */ if (file_info->Action != FILE_ACTION_REMOVED && file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) { /* Construct a full path to the file. */ size = wcslen(handle->dirw) + file_info->FileNameLength / sizeof(WCHAR) + 2; filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR)); if (!filenamew) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw, file_info->FileNameLength / sizeof(WCHAR), file_info->FileName); filenamew[size - 1] = L'\0'; /* Convert to long name. */ size = GetLongPathNameW(filenamew, NULL, 0); if (size) { long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR)); if (!long_filenamew) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } size = GetLongPathNameW(filenamew, long_filenamew, size); if (size) { long_filenamew[size] = '\0'; } else { uv__free(long_filenamew); long_filenamew = NULL; } } uv__free(filenamew); if (long_filenamew) { /* Get the file name out of the long path. */ result = uv_relative_path(long_filenamew, handle->dirw, &filenamew); uv__free(long_filenamew); if (result == 0) { long_filenamew = filenamew; sizew = -1; } else { long_filenamew = NULL; } } /* * We could not resolve the long form explicitly. * We therefore use the name given by ReadDirectoryChangesW. * This may be the long form or the 8.3 short name in some cases. */ if (!long_filenamew) { filenamew = file_info->FileName; sizew = file_info->FileNameLength / sizeof(WCHAR); } } else { /* * Removed or renamed events cannot be resolved to the long form. * We therefore use the name given by ReadDirectoryChangesW. * This may be the long form or the 8.3 short name in some cases. */ if (!long_filenamew) { filenamew = file_info->FileName; sizew = file_info->FileNameLength / sizeof(WCHAR); } } } else { /* We already have the long name of the file, so just use it. */ filenamew = handle->filew; sizew = -1; } if (filenamew) { /* Convert the filename to utf8. */ size = uv_utf16_to_utf8(filenamew, sizew, NULL, 0); if (size) { filename = (char*)uv__malloc(size + 1); if (!filename) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } size = uv_utf16_to_utf8(filenamew, sizew, filename, size); if (size) { filename[size] = '\0'; } else { uv__free(filename); filename = NULL; } } } switch (file_info->Action) { case FILE_ACTION_ADDED: case FILE_ACTION_REMOVED: case FILE_ACTION_RENAMED_OLD_NAME: case FILE_ACTION_RENAMED_NEW_NAME: handle->cb(handle, filename, UV_RENAME, 0); break; case FILE_ACTION_MODIFIED: handle->cb(handle, filename, UV_CHANGE, 0); break; } uv__free(filename); filename = NULL; uv__free(long_filenamew); long_filenamew = NULL; } offset = file_info->NextEntryOffset; } while (offset && !(handle->flags & UV__HANDLE_CLOSING)); } else { handle->cb(handle, NULL, UV_CHANGE, 0); } } else { err = GET_REQ_ERROR(req); handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); } if (!(handle->flags & UV__HANDLE_CLOSING)) { uv_fs_event_queue_readdirchanges(loop, handle); } else { uv_want_endgame(loop, (uv_handle_t*)handle); } } void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { uv_fs_event_stop(handle); uv__handle_closing(handle); if (!handle->req_pending) { uv_want_endgame(loop, (uv_handle_t*)handle); } } void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { if ((handle->flags & UV__HANDLE_CLOSING) && !handle->req_pending) { assert(!(handle->flags & UV_HANDLE_CLOSED)); if (handle->buffer) { uv__free(handle->buffer); handle->buffer = NULL; } uv__handle_close(handle); } } libuv-v1.8.0/src/win/fs.c000066400000000000000000001753111263361716600152120ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "uv.h" #include "internal.h" #include "req-inl.h" #include "handle-inl.h" #include #define UV_FS_FREE_PATHS 0x0002 #define UV_FS_FREE_PTR 0x0008 #define UV_FS_CLEANEDUP 0x0010 #define QUEUE_FS_TP_JOB(loop, req) \ do { \ uv__req_register(loop, req); \ uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done); \ } while (0) #define SET_REQ_RESULT(req, result_value) \ do { \ req->result = (result_value); \ if (req->result == -1) { \ req->sys_errno_ = _doserrno; \ req->result = uv_translate_sys_error(req->sys_errno_); \ } \ } while (0) #define SET_REQ_WIN32_ERROR(req, sys_errno) \ do { \ req->sys_errno_ = (sys_errno); \ req->result = uv_translate_sys_error(req->sys_errno_); \ } while (0) #define SET_REQ_UV_ERROR(req, uv_errno, sys_errno) \ do { \ req->result = (uv_errno); \ req->sys_errno_ = (sys_errno); \ } while (0) #define VERIFY_FD(fd, req) \ if (fd == -1) { \ req->result = UV_EBADF; \ req->sys_errno_ = ERROR_INVALID_HANDLE; \ return; \ } #define FILETIME_TO_UINT(filetime) \ (*((uint64_t*) &(filetime)) - 116444736000000000ULL) #define FILETIME_TO_TIME_T(filetime) \ (FILETIME_TO_UINT(filetime) / 10000000ULL) #define FILETIME_TO_TIME_NS(filetime, secs) \ ((FILETIME_TO_UINT(filetime) - (secs * 10000000ULL)) * 100) #define FILETIME_TO_TIMESPEC(ts, filetime) \ do { \ (ts).tv_sec = (long) FILETIME_TO_TIME_T(filetime); \ (ts).tv_nsec = (long) FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \ } while(0) #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ uint64_t bigtime = ((int64_t) (time) * 10000000LL) + \ 116444736000000000ULL; \ (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF; \ (filetime_ptr)->dwHighDateTime = bigtime >> 32; \ } while(0) #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ ((c) >= L'A' && (c) <= L'Z')) const WCHAR JUNCTION_PREFIX[] = L"\\??\\"; const WCHAR JUNCTION_PREFIX_LEN = 4; const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\"; const WCHAR LONG_PATH_PREFIX_LEN = 4; const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\"; const WCHAR UNC_PATH_PREFIX_LEN = 8; void uv_fs_init() { _fmode = _O_BINARY; } INLINE static int fs__capture_path(uv_fs_t* req, const char* path, const char* new_path, const int copy_path) { char* buf; char* pos; ssize_t buf_sz = 0, path_len, pathw_len = 0, new_pathw_len = 0; /* new_path can only be set if path is also set. */ assert(new_path == NULL || path != NULL); if (path != NULL) { pathw_len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); if (pathw_len == 0) { return GetLastError(); } buf_sz += pathw_len * sizeof(WCHAR); } if (path != NULL && copy_path) { path_len = 1 + strlen(path); buf_sz += path_len; } if (new_path != NULL) { new_pathw_len = MultiByteToWideChar(CP_UTF8, 0, new_path, -1, NULL, 0); if (new_pathw_len == 0) { return GetLastError(); } buf_sz += new_pathw_len * sizeof(WCHAR); } if (buf_sz == 0) { req->file.pathw = NULL; req->fs.info.new_pathw = NULL; req->path = NULL; return 0; } buf = (char*) uv__malloc(buf_sz); if (buf == NULL) { return ERROR_OUTOFMEMORY; } pos = buf; if (path != NULL) { DWORD r = MultiByteToWideChar(CP_UTF8, 0, path, -1, (WCHAR*) pos, pathw_len); assert(r == (DWORD) pathw_len); req->file.pathw = (WCHAR*) pos; pos += r * sizeof(WCHAR); } else { req->file.pathw = NULL; } if (new_path != NULL) { DWORD r = MultiByteToWideChar(CP_UTF8, 0, new_path, -1, (WCHAR*) pos, new_pathw_len); assert(r == (DWORD) new_pathw_len); req->fs.info.new_pathw = (WCHAR*) pos; pos += r * sizeof(WCHAR); } else { req->fs.info.new_pathw = NULL; } if (!copy_path) { req->path = path; } else if (path) { memcpy(pos, path, path_len); assert(path_len == buf_sz - (pos - buf)); req->path = pos; } else { req->path = NULL; } req->flags |= UV_FS_FREE_PATHS; return 0; } INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, uv_fs_type fs_type, const uv_fs_cb cb) { uv_req_init(loop, (uv_req_t*) req); req->type = UV_FS; req->loop = loop; req->flags = 0; req->fs_type = fs_type; req->result = 0; req->ptr = NULL; req->path = NULL; req->cb = cb; } static int fs__wide_to_utf8(WCHAR* w_source_ptr, DWORD w_source_len, char** target_ptr, uint64_t* target_len_ptr) { int r; int target_len; char* target; target_len = WideCharToMultiByte(CP_UTF8, 0, w_source_ptr, w_source_len, NULL, 0, NULL, NULL); if (target_len == 0) { return -1; } if (target_len_ptr != NULL) { *target_len_ptr = target_len; } if (target_ptr == NULL) { return 0; } target = uv__malloc(target_len + 1); if (target == NULL) { SetLastError(ERROR_OUTOFMEMORY); return -1; } r = WideCharToMultiByte(CP_UTF8, 0, w_source_ptr, w_source_len, target, target_len, NULL, NULL); assert(r == target_len); target[target_len] = '\0'; *target_ptr = target; return 0; } INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, uint64_t* target_len_ptr) { char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer; WCHAR* w_target; DWORD w_target_len; DWORD bytes; if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, sizeof buffer, &bytes, NULL)) { return -1; } if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) { /* Real symlink */ w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)); w_target_len = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR); /* Real symlinks can contain pretty much everything, but the only thing */ /* we really care about is undoing the implicit conversion to an NT */ /* namespaced path that CreateSymbolicLink will perform on absolute */ /* paths. If the path is win32-namespaced then the user must have */ /* explicitly made it so, and we better just return the unmodified */ /* reparse data. */ if (w_target_len >= 4 && w_target[0] == L'\\' && w_target[1] == L'?' && w_target[2] == L'?' && w_target[3] == L'\\') { /* Starts with \??\ */ if (w_target_len >= 6 && ((w_target[4] >= L'A' && w_target[4] <= L'Z') || (w_target[4] >= L'a' && w_target[4] <= L'z')) && w_target[5] == L':' && (w_target_len == 6 || w_target[6] == L'\\')) { /* \??\:\ */ w_target += 4; w_target_len -= 4; } else if (w_target_len >= 8 && (w_target[4] == L'U' || w_target[4] == L'u') && (w_target[5] == L'N' || w_target[5] == L'n') && (w_target[6] == L'C' || w_target[6] == L'c') && w_target[7] == L'\\') { /* \??\UNC\\\ - make sure the final path looks like */ /* \\\\ */ w_target += 6; w_target[0] = L'\\'; w_target_len -= 6; } } } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { /* Junction. */ w_target = reparse_data->MountPointReparseBuffer.PathBuffer + (reparse_data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)); w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); /* Only treat junctions that look like \??\:\ as symlink. */ /* Junctions can also be used as mount points, like \??\Volume{}, */ /* but that's confusing for programs since they wouldn't be able to */ /* actually understand such a path when returned by uv_readlink(). */ /* UNC paths are never valid for junctions so we don't care about them. */ if (!(w_target_len >= 6 && w_target[0] == L'\\' && w_target[1] == L'?' && w_target[2] == L'?' && w_target[3] == L'\\' && ((w_target[4] >= L'A' && w_target[4] <= L'Z') || (w_target[4] >= L'a' && w_target[4] <= L'z')) && w_target[5] == L':' && (w_target_len == 6 || w_target[6] == L'\\'))) { SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); return -1; } /* Remove leading \??\ */ w_target += 4; w_target_len -= 4; } else { /* Reparse tag does not indicate a symlink. */ SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); return -1; } return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr); } void fs__open(uv_fs_t* req) { DWORD access; DWORD share; DWORD disposition; DWORD attributes = 0; HANDLE file; int fd, current_umask; int flags = req->fs.info.file_flags; /* Obtain the active umask. umask() never fails and returns the previous */ /* umask. */ current_umask = umask(0); umask(current_umask); /* convert flags and mode to CreateFile parameters */ switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { case _O_RDONLY: access = FILE_GENERIC_READ; attributes |= FILE_FLAG_BACKUP_SEMANTICS; break; case _O_WRONLY: access = FILE_GENERIC_WRITE; break; case _O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; break; default: goto einval; } if (flags & _O_APPEND) { access &= ~FILE_WRITE_DATA; access |= FILE_APPEND_DATA; attributes &= ~FILE_FLAG_BACKUP_SEMANTICS; } /* * Here is where we deviate significantly from what CRT's _open() * does. We indiscriminately use all the sharing modes, to match * UNIX semantics. In particular, this ensures that the file can * be deleted even whilst it's open, fixing issue #1449. */ share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) { case 0: case _O_EXCL: disposition = OPEN_EXISTING; break; case _O_CREAT: disposition = OPEN_ALWAYS; break; case _O_CREAT | _O_EXCL: case _O_CREAT | _O_TRUNC | _O_EXCL: disposition = CREATE_NEW; break; case _O_TRUNC: case _O_TRUNC | _O_EXCL: disposition = TRUNCATE_EXISTING; break; case _O_CREAT | _O_TRUNC: disposition = CREATE_ALWAYS; break; default: goto einval; } attributes |= FILE_ATTRIBUTE_NORMAL; if (flags & _O_CREAT) { if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) { attributes |= FILE_ATTRIBUTE_READONLY; } } if (flags & _O_TEMPORARY ) { attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY; access |= DELETE; } if (flags & _O_SHORT_LIVED) { attributes |= FILE_ATTRIBUTE_TEMPORARY; } switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) { case 0: break; case _O_SEQUENTIAL: attributes |= FILE_FLAG_SEQUENTIAL_SCAN; break; case _O_RANDOM: attributes |= FILE_FLAG_RANDOM_ACCESS; break; default: goto einval; } /* Setting this flag makes it possible to open a directory. */ attributes |= FILE_FLAG_BACKUP_SEMANTICS; file = CreateFileW(req->file.pathw, access, share, NULL, disposition, attributes, NULL); if (file == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) && !(flags & _O_EXCL)) { /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */ /* specified, it means the path referred to a directory. */ SET_REQ_UV_ERROR(req, UV_EISDIR, error); } else { SET_REQ_WIN32_ERROR(req, GetLastError()); } return; } fd = _open_osfhandle((intptr_t) file, flags); if (fd < 0) { /* The only known failure mode for _open_osfhandle() is EMFILE, in which * case GetLastError() will return zero. However we'll try to handle other * errors as well, should they ever occur. */ if (errno == EMFILE) SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); else if (GetLastError() != ERROR_SUCCESS) SET_REQ_WIN32_ERROR(req, GetLastError()); else SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); CloseHandle(file); return; } SET_REQ_RESULT(req, fd); return; einval: SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); } void fs__close(uv_fs_t* req) { int fd = req->file.fd; int result; VERIFY_FD(fd, req); if (fd > 2) result = _close(fd); else result = 0; /* _close doesn't set _doserrno on failure, but it does always set errno * to EBADF on failure. */ if (result == -1) { assert(errno == EBADF); SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE); } else { req->result = 0; } } void fs__read(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; HANDLE handle; OVERLAPPED overlapped, *overlapped_ptr; LARGE_INTEGER offset_; DWORD bytes; DWORD error; int result; unsigned int index; VERIFY_FD(fd, req); handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); return; } if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; } else { overlapped_ptr = NULL; } index = 0; bytes = 0; do { DWORD incremental_bytes; if (offset != -1) { offset_.QuadPart = offset + bytes; overlapped.Offset = offset_.LowPart; overlapped.OffsetHigh = offset_.HighPart; } result = ReadFile(handle, req->fs.info.bufs[index].base, req->fs.info.bufs[index].len, &incremental_bytes, overlapped_ptr); bytes += incremental_bytes; ++index; } while (result && index < req->fs.info.nbufs); if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { error = GetLastError(); if (error == ERROR_HANDLE_EOF) { SET_REQ_RESULT(req, bytes); } else { SET_REQ_WIN32_ERROR(req, error); } } } void fs__write(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; HANDLE handle; OVERLAPPED overlapped, *overlapped_ptr; LARGE_INTEGER offset_; DWORD bytes; int result; unsigned int index; VERIFY_FD(fd, req); handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); return; } if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; } else { overlapped_ptr = NULL; } index = 0; bytes = 0; do { DWORD incremental_bytes; if (offset != -1) { offset_.QuadPart = offset + bytes; overlapped.Offset = offset_.LowPart; overlapped.OffsetHigh = offset_.HighPart; } result = WriteFile(handle, req->fs.info.bufs[index].base, req->fs.info.bufs[index].len, &incremental_bytes, overlapped_ptr); bytes += incremental_bytes; ++index; } while (result && index < req->fs.info.nbufs); if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { SET_REQ_WIN32_ERROR(req, GetLastError()); } } void fs__rmdir(uv_fs_t* req) { int result = _wrmdir(req->file.pathw); SET_REQ_RESULT(req, result); } void fs__unlink(uv_fs_t* req) { const WCHAR* pathw = req->file.pathw; HANDLE handle; BY_HANDLE_FILE_INFORMATION info; FILE_DISPOSITION_INFORMATION disposition; IO_STATUS_BLOCK iosb; NTSTATUS status; handle = CreateFileW(pathw, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } if (!GetFileInformationByHandle(handle, &info)) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(handle); return; } if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { /* Do not allow deletion of directories, unless it is a symlink. When */ /* the path refers to a non-symlink directory, report EPERM as mandated */ /* by POSIX.1. */ /* Check if it is a reparse point. If it's not, it's a normal directory. */ if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); CloseHandle(handle); return; } /* Read the reparse point and check if it is a valid symlink. */ /* If not, don't unlink. */ if (fs__readlink_handle(handle, NULL, NULL) < 0) { DWORD error = GetLastError(); if (error == ERROR_SYMLINK_NOT_SUPPORTED) error = ERROR_ACCESS_DENIED; SET_REQ_WIN32_ERROR(req, error); CloseHandle(handle); return; } } if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { /* Remove read-only attribute */ FILE_BASIC_INFORMATION basic = { 0 }; basic.FileAttributes = info.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY); status = pNtSetInformationFile(handle, &iosb, &basic, sizeof basic, FileBasicInformation); if (!NT_SUCCESS(status)) { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); CloseHandle(handle); return; } } /* Try to set the delete flag. */ disposition.DeleteFile = TRUE; status = pNtSetInformationFile(handle, &iosb, &disposition, sizeof disposition, FileDispositionInformation); if (NT_SUCCESS(status)) { SET_REQ_SUCCESS(req); } else { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); } CloseHandle(handle); } void fs__mkdir(uv_fs_t* req) { /* TODO: use req->mode. */ int result = _wmkdir(req->file.pathw); SET_REQ_RESULT(req, result); } /* OpenBSD original: lib/libc/stdio/mktemp.c */ void fs__mkdtemp(uv_fs_t* req) { static const WCHAR *tempchars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const size_t num_chars = 62; static const size_t num_x = 6; WCHAR *cp, *ep; unsigned int tries, i; size_t len; HCRYPTPROV h_crypt_prov; uint64_t v; BOOL released; len = wcslen(req->file.pathw); ep = req->file.pathw + len; if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return; } if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } tries = TMP_MAX; do { if (!CryptGenRandom(h_crypt_prov, sizeof(v), (BYTE*) &v)) { SET_REQ_WIN32_ERROR(req, GetLastError()); break; } cp = ep - num_x; for (i = 0; i < num_x; i++) { *cp++ = tempchars[v % num_chars]; v /= num_chars; } if (_wmkdir(req->file.pathw) == 0) { len = strlen(req->path); wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); SET_REQ_RESULT(req, 0); break; } else if (errno != EEXIST) { SET_REQ_RESULT(req, -1); break; } } while (--tries); released = CryptReleaseContext(h_crypt_prov, 0); assert(released); if (tries == 0) { SET_REQ_RESULT(req, -1); } } void fs__scandir(uv_fs_t* req) { static const size_t dirents_initial_size = 32; HANDLE dir_handle = INVALID_HANDLE_VALUE; uv__dirent_t** dirents = NULL; size_t dirents_size = 0; size_t dirents_used = 0; IO_STATUS_BLOCK iosb; NTSTATUS status; /* Buffer to hold directory entries returned by NtQueryDirectoryFile. * It's important that this buffer can hold at least one entry, regardless * of the length of the file names present in the enumerated directory. * A file name is at most 256 WCHARs long. * According to MSDN, the buffer must be aligned at an 8-byte boundary. */ #if _MSC_VER __declspec(align(8)) char buffer[8192]; #else __attribute__ ((aligned (8))) char buffer[8192]; #endif STATIC_ASSERT(sizeof buffer >= sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR)); /* Open the directory. */ dir_handle = CreateFileW(req->file.pathw, FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (dir_handle == INVALID_HANDLE_VALUE) goto win32_error; /* Read the first chunk. */ status = pNtQueryDirectoryFile(dir_handle, NULL, NULL, NULL, &iosb, &buffer, sizeof buffer, FileDirectoryInformation, FALSE, NULL, TRUE); /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER. * This should be reported back as UV_ENOTDIR. */ if (status == STATUS_INVALID_PARAMETER) goto not_a_directory_error; while (NT_SUCCESS(status)) { char* position = buffer; size_t next_entry_offset = 0; do { FILE_DIRECTORY_INFORMATION* info; uv__dirent_t* dirent; size_t wchar_len; size_t utf8_len; /* Obtain a pointer to the current directory entry. */ position += next_entry_offset; info = (FILE_DIRECTORY_INFORMATION*) position; /* Fetch the offset to the next directory entry. */ next_entry_offset = info->NextEntryOffset; /* Compute the length of the filename in WCHARs. */ wchar_len = info->FileNameLength / sizeof info->FileName[0]; /* Skip over '.' and '..' entries. */ if (wchar_len == 1 && info->FileName[0] == L'.') continue; if (wchar_len == 2 && info->FileName[0] == L'.' && info->FileName[1] == L'.') continue; /* Compute the space required to store the filename as UTF-8. */ utf8_len = WideCharToMultiByte( CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL); if (utf8_len == 0) goto win32_error; /* Resize the dirent array if needed. */ if (dirents_used >= dirents_size) { size_t new_dirents_size = dirents_size == 0 ? dirents_initial_size : dirents_size << 1; uv__dirent_t** new_dirents = uv__realloc(dirents, new_dirents_size * sizeof *dirents); if (new_dirents == NULL) goto out_of_memory_error; dirents_size = new_dirents_size; dirents = new_dirents; } /* Allocate space for the uv dirent structure. The dirent structure * includes room for the first character of the filename, but `utf8_len` * doesn't count the NULL terminator at this point. */ dirent = uv__malloc(sizeof *dirent + utf8_len); if (dirent == NULL) goto out_of_memory_error; dirents[dirents_used++] = dirent; /* Convert file name to UTF-8. */ if (WideCharToMultiByte(CP_UTF8, 0, &info->FileName[0], wchar_len, &dirent->d_name[0], utf8_len, NULL, NULL) == 0) goto win32_error; /* Add a null terminator to the filename. */ dirent->d_name[utf8_len] = '\0'; /* Fill out the type field. */ if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE) dirent->d_type = UV__DT_CHAR; else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) dirent->d_type = UV__DT_LINK; else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) dirent->d_type = UV__DT_DIR; else dirent->d_type = UV__DT_FILE; } while (next_entry_offset != 0); /* Read the next chunk. */ status = pNtQueryDirectoryFile(dir_handle, NULL, NULL, NULL, &iosb, &buffer, sizeof buffer, FileDirectoryInformation, FALSE, NULL, FALSE); /* After the first pNtQueryDirectoryFile call, the function may return * STATUS_SUCCESS even if the buffer was too small to hold at least one * directory entry. */ if (status == STATUS_SUCCESS && iosb.Information == 0) status = STATUS_BUFFER_OVERFLOW; } if (status != STATUS_NO_MORE_FILES) goto nt_error; CloseHandle(dir_handle); /* Store the result in the request object. */ req->ptr = dirents; if (dirents != NULL) req->flags |= UV_FS_FREE_PTR; SET_REQ_RESULT(req, dirents_used); /* `nbufs` will be used as index by uv_fs_scandir_next. */ req->fs.info.nbufs = 0; return; nt_error: SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); goto cleanup; win32_error: SET_REQ_WIN32_ERROR(req, GetLastError()); goto cleanup; not_a_directory_error: SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY); goto cleanup; out_of_memory_error: SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); goto cleanup; cleanup: if (dir_handle != INVALID_HANDLE_VALUE) CloseHandle(dir_handle); while (dirents_used > 0) uv__free(dirents[--dirents_used]); if (dirents != NULL) uv__free(dirents); } INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { FILE_ALL_INFORMATION file_info; FILE_FS_VOLUME_INFORMATION volume_info; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; nt_status = pNtQueryInformationFile(handle, &io_status, &file_info, sizeof file_info, FileAllInformation); /* Buffer overflow (a warning status code) is expected here. */ if (NT_ERROR(nt_status)) { SetLastError(pRtlNtStatusToDosError(nt_status)); return -1; } nt_status = pNtQueryVolumeInformationFile(handle, &io_status, &volume_info, sizeof volume_info, FileFsVolumeInformation); /* Buffer overflow (a warning status code) is expected here. */ if (io_status.Status == STATUS_NOT_IMPLEMENTED) { statbuf->st_dev = 0; } else if (NT_ERROR(nt_status)) { SetLastError(pRtlNtStatusToDosError(nt_status)); return -1; } else { statbuf->st_dev = volume_info.VolumeSerialNumber; } /* Todo: st_mode should probably always be 0666 for everyone. We might also * want to report 0777 if the file is a .exe or a directory. * * Currently it's based on whether the 'readonly' attribute is set, which * makes little sense because the semantics are so different: the 'read-only' * flag is just a way for a user to protect against accidental deletion, and * serves no security purpose. Windows uses ACLs for that. * * Also people now use uv_fs_chmod() to take away the writable bit for good * reasons. Windows however just makes the file read-only, which makes it * impossible to delete the file afterwards, since read-only files can't be * deleted. * * IOW it's all just a clusterfuck and we should think of something that * makes slightly more sense. * * And uv_fs_chmod should probably just fail on windows or be a total no-op. * There's nothing sensible it can do anyway. */ statbuf->st_mode = 0; if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { statbuf->st_mode |= S_IFLNK; if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) return -1; } else if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { statbuf->st_mode |= _S_IFDIR; statbuf->st_size = 0; } else { statbuf->st_mode |= _S_IFREG; statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart; } if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6); else statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | ((_S_IREAD | _S_IWRITE) >> 6); FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime); FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime); FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime); FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime); statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart; /* st_blocks contains the on-disk allocation size in 512-byte units. */ statbuf->st_blocks = file_info.StandardInformation.AllocationSize.QuadPart >> 9ULL; statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks; /* The st_blksize is supposed to be the 'optimal' number of bytes for reading * and writing to the disk. That is, for any definition of 'optimal' - it's * supposed to at least avoid read-update-write behavior when writing to the * disk. * * However nobody knows this and even fewer people actually use this value, * and in order to fill it out we'd have to make another syscall to query the * volume for FILE_FS_SECTOR_SIZE_INFORMATION. * * Therefore we'll just report a sensible value that's quite commonly okay * on modern hardware. */ statbuf->st_blksize = 2048; /* Todo: set st_flags to something meaningful. Also provide a wrapper for * chattr(2). */ statbuf->st_flags = 0; /* Windows has nothing sensible to say about these values, so they'll just * remain empty. */ statbuf->st_gid = 0; statbuf->st_uid = 0; statbuf->st_rdev = 0; statbuf->st_gen = 0; return 0; } INLINE static void fs__stat_prepare_path(WCHAR* pathw) { size_t len = wcslen(pathw); /* TODO: ignore namespaced paths. */ if (len > 1 && pathw[len - 2] != L':' && (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) { pathw[len - 1] = '\0'; } } INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { HANDLE handle; DWORD flags; flags = FILE_FLAG_BACKUP_SEMANTICS; if (do_lstat) { flags |= FILE_FLAG_OPEN_REPARSE_POINT; } handle = CreateFileW(req->file.pathw, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } if (fs__stat_handle(handle, &req->statbuf) != 0) { DWORD error = GetLastError(); if (do_lstat && error == ERROR_SYMLINK_NOT_SUPPORTED) { /* We opened a reparse point but it was not a symlink. Try again. */ fs__stat_impl(req, 0); } else { /* Stat failed. */ SET_REQ_WIN32_ERROR(req, GetLastError()); } CloseHandle(handle); return; } req->ptr = &req->statbuf; req->result = 0; CloseHandle(handle); } static void fs__stat(uv_fs_t* req) { fs__stat_prepare_path(req->file.pathw); fs__stat_impl(req, 0); } static void fs__lstat(uv_fs_t* req) { fs__stat_prepare_path(req->file.pathw); fs__stat_impl(req, 1); } static void fs__fstat(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; VERIFY_FD(fd, req); handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); return; } if (fs__stat_handle(handle, &req->statbuf) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } req->ptr = &req->statbuf; req->result = 0; } static void fs__rename(uv_fs_t* req) { if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } SET_REQ_RESULT(req, 0); } INLINE static void fs__sync_impl(uv_fs_t* req) { int fd = req->file.fd; int result; VERIFY_FD(fd, req); result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1; if (result == -1) { SET_REQ_WIN32_ERROR(req, GetLastError()); } else { SET_REQ_RESULT(req, result); } } static void fs__fsync(uv_fs_t* req) { fs__sync_impl(req); } static void fs__fdatasync(uv_fs_t* req) { fs__sync_impl(req); } static void fs__ftruncate(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; NTSTATUS status; IO_STATUS_BLOCK io_status; FILE_END_OF_FILE_INFORMATION eof_info; VERIFY_FD(fd, req); handle = uv__get_osfhandle(fd); eof_info.EndOfFile.QuadPart = req->fs.info.offset; status = pNtSetInformationFile(handle, &io_status, &eof_info, sizeof eof_info, FileEndOfFileInformation); if (NT_SUCCESS(status)) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); } } static void fs__sendfile(uv_fs_t* req) { int fd_in = req->file.fd, fd_out = req->fs.info.fd_out; size_t length = req->fs.info.bufsml[0].len; int64_t offset = req->fs.info.offset; const size_t max_buf_size = 65536; size_t buf_size = length < max_buf_size ? length : max_buf_size; int n, result = 0; int64_t result_offset = 0; char* buf = (char*) uv__malloc(buf_size); if (!buf) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } if (offset != -1) { result_offset = _lseeki64(fd_in, offset, SEEK_SET); } if (result_offset == -1) { result = -1; } else { while (length > 0) { n = _read(fd_in, buf, length < buf_size ? length : buf_size); if (n == 0) { break; } else if (n == -1) { result = -1; break; } length -= n; n = _write(fd_out, buf, n); if (n == -1) { result = -1; break; } result += n; } } uv__free(buf); SET_REQ_RESULT(req, result); } static void fs__access(uv_fs_t* req) { DWORD attr = GetFileAttributesW(req->file.pathw); if (attr == INVALID_FILE_ATTRIBUTES) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } /* * Access is possible if * - write access wasn't requested, * - or the file isn't read-only, * - or it's a directory. * (Directories cannot be read-only on Windows.) */ if (!(req->flags & W_OK) || !(attr & FILE_ATTRIBUTE_READONLY) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, UV_EPERM); } } static void fs__chmod(uv_fs_t* req) { int result = _wchmod(req->file.pathw, req->fs.info.mode); SET_REQ_RESULT(req, result); } static void fs__fchmod(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_BASIC_INFORMATION file_info; VERIFY_FD(fd, req); handle = uv__get_osfhandle(fd); nt_status = pNtQueryInformationFile(handle, &io_status, &file_info, sizeof file_info, FileBasicInformation); if (!NT_SUCCESS(nt_status)) { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); return; } if (req->fs.info.mode & _S_IWRITE) { file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; } else { file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY; } nt_status = pNtSetInformationFile(handle, &io_status, &file_info, sizeof file_info, FileBasicInformation); if (!NT_SUCCESS(nt_status)) { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); return; } SET_REQ_SUCCESS(req); } INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { FILETIME filetime_a, filetime_m; TIME_T_TO_FILETIME((time_t) atime, &filetime_a); TIME_T_TO_FILETIME((time_t) mtime, &filetime_m); if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) { return -1; } return 0; } static void fs__utime(uv_fs_t* req) { HANDLE handle; handle = CreateFileW(req->file.pathw, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(handle); return; } CloseHandle(handle); req->result = 0; } static void fs__futime(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; VERIFY_FD(fd, req); handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); return; } if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } req->result = 0; } static void fs__link(uv_fs_t* req) { DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL); if (r == 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); } else { req->result = 0; } } static void fs__create_junction(uv_fs_t* req, const WCHAR* path, const WCHAR* new_path) { HANDLE handle = INVALID_HANDLE_VALUE; REPARSE_DATA_BUFFER *buffer = NULL; int created = 0; int target_len; int is_absolute, is_long_path; int needed_buf_size, used_buf_size, used_data_size, path_buf_len; int start, len, i; int add_slash; DWORD bytes; WCHAR* path_buf; target_len = wcslen(path); is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0; if (is_long_path) { is_absolute = 1; } else { is_absolute = target_len >= 3 && IS_LETTER(path[0]) && path[1] == L':' && IS_SLASH(path[2]); } if (!is_absolute) { /* Not supporting relative paths */ SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED); return; } /* Do a pessimistic calculation of the required buffer size */ needed_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) + JUNCTION_PREFIX_LEN * sizeof(WCHAR) + 2 * (target_len + 2) * sizeof(WCHAR); /* Allocate the buffer */ buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size); if (!buffer) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } /* Grab a pointer to the part of the buffer where filenames go */ path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer); path_buf_len = 0; /* Copy the substitute (internal) target path */ start = path_buf_len; wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX, JUNCTION_PREFIX_LEN); path_buf_len += JUNCTION_PREFIX_LEN; add_slash = 0; for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) { if (IS_SLASH(path[i])) { add_slash = 1; continue; } if (add_slash) { path_buf[path_buf_len++] = L'\\'; add_slash = 0; } path_buf[path_buf_len++] = path[i]; } path_buf[path_buf_len++] = L'\\'; len = path_buf_len - start; /* Set the info about the substitute name */ buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR); buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR); /* Insert null terminator */ path_buf[path_buf_len++] = L'\0'; /* Copy the print name of the target path */ start = path_buf_len; add_slash = 0; for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) { if (IS_SLASH(path[i])) { add_slash = 1; continue; } if (add_slash) { path_buf[path_buf_len++] = L'\\'; add_slash = 0; } path_buf[path_buf_len++] = path[i]; } len = path_buf_len - start; if (len == 2) { path_buf[path_buf_len++] = L'\\'; len++; } /* Set the info about the print name */ buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR); buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR); /* Insert another null terminator */ path_buf[path_buf_len++] = L'\0'; /* Calculate how much buffer space was actually used */ used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) + path_buf_len * sizeof(WCHAR); used_data_size = used_buf_size - FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer); /* Put general info in the data buffer */ buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; buffer->ReparseDataLength = used_data_size; buffer->Reserved = 0; /* Create a new directory */ if (!CreateDirectoryW(new_path, NULL)) { SET_REQ_WIN32_ERROR(req, GetLastError()); goto error; } created = 1; /* Open the directory */ handle = CreateFileW(new_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); goto error; } /* Create the actual reparse point */ if (!DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, buffer, used_buf_size, NULL, 0, &bytes, NULL)) { SET_REQ_WIN32_ERROR(req, GetLastError()); goto error; } /* Clean up */ CloseHandle(handle); uv__free(buffer); SET_REQ_RESULT(req, 0); return; error: uv__free(buffer); if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } if (created) { RemoveDirectoryW(new_path); } } static void fs__symlink(uv_fs_t* req) { WCHAR* pathw = req->file.pathw; WCHAR* new_pathw = req->fs.info.new_pathw; int flags = req->fs.info.file_flags; int result; if (flags & UV_FS_SYMLINK_JUNCTION) { fs__create_junction(req, pathw, new_pathw); } else if (pCreateSymbolicLinkW) { result = pCreateSymbolicLinkW(new_pathw, pathw, flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; if (result == -1) { SET_REQ_WIN32_ERROR(req, GetLastError()); } else { SET_REQ_RESULT(req, result); } } else { SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); } } static void fs__readlink(uv_fs_t* req) { HANDLE handle; handle = CreateFileW(req->file.pathw, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(handle); return; } req->flags |= UV_FS_FREE_PTR; SET_REQ_RESULT(req, 0); CloseHandle(handle); } static size_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { int r; DWORD w_realpath_len; WCHAR* w_realpath_ptr; WCHAR* w_finalpath_ptr = NULL; w_realpath_len = pGetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS); if (w_realpath_len == 0) { return -1; } w_realpath_ptr = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR)); if (w_realpath_ptr == NULL) { SetLastError(ERROR_OUTOFMEMORY); return -1; } if (pGetFinalPathNameByHandleW(handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) { uv__free(w_realpath_ptr); SetLastError(ERROR_INVALID_HANDLE); return -1; } /* convert UNC path to long path */ if (wcsncmp(w_realpath_ptr, UNC_PATH_PREFIX, UNC_PATH_PREFIX_LEN) == 0) { w_finalpath_ptr = w_realpath_ptr + 6; *w_finalpath_ptr = L'\\'; } else if (wcsncmp(w_realpath_ptr, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0) { w_finalpath_ptr = w_realpath_ptr + 4; } else { uv__free(w_realpath_ptr); SetLastError(ERROR_INVALID_HANDLE); return -1; } r = fs__wide_to_utf8(w_finalpath_ptr, w_realpath_len, realpath_ptr, NULL); uv__free(w_realpath_ptr); return r; } static void fs__realpath(uv_fs_t* req) { HANDLE handle; if (!pGetFinalPathNameByHandleW) { SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); return; } handle = CreateFileW(req->file.pathw, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) { CloseHandle(handle); SET_REQ_WIN32_ERROR(req, GetLastError()); return; } CloseHandle(handle); req->flags |= UV_FS_FREE_PTR; SET_REQ_RESULT(req, 0); } static void fs__chown(uv_fs_t* req) { req->result = 0; } static void fs__fchown(uv_fs_t* req) { req->result = 0; } static void uv__fs_work(struct uv__work* w) { uv_fs_t* req; req = container_of(w, uv_fs_t, work_req); assert(req->type == UV_FS); #define XX(uc, lc) case UV_FS_##uc: fs__##lc(req); break; switch (req->fs_type) { XX(OPEN, open) XX(CLOSE, close) XX(READ, read) XX(WRITE, write) XX(SENDFILE, sendfile) XX(STAT, stat) XX(LSTAT, lstat) XX(FSTAT, fstat) XX(FTRUNCATE, ftruncate) XX(UTIME, utime) XX(FUTIME, futime) XX(ACCESS, access) XX(CHMOD, chmod) XX(FCHMOD, fchmod) XX(FSYNC, fsync) XX(FDATASYNC, fdatasync) XX(UNLINK, unlink) XX(RMDIR, rmdir) XX(MKDIR, mkdir) XX(MKDTEMP, mkdtemp) XX(RENAME, rename) XX(SCANDIR, scandir) XX(LINK, link) XX(SYMLINK, symlink) XX(READLINK, readlink) XX(REALPATH, realpath) XX(CHOWN, chown) XX(FCHOWN, fchown); default: assert(!"bad uv_fs_type"); } } static void uv__fs_done(struct uv__work* w, int status) { uv_fs_t* req; req = container_of(w, uv_fs_t, work_req); uv__req_unregister(req->loop, req); if (status == UV_ECANCELED) { assert(req->result == 0); req->result = UV_ECANCELED; } req->cb(req); } void uv_fs_req_cleanup(uv_fs_t* req) { if (req->flags & UV_FS_CLEANEDUP) return; if (req->flags & UV_FS_FREE_PATHS) uv__free(req->file.pathw); if (req->flags & UV_FS_FREE_PTR) uv__free(req->ptr); req->path = NULL; req->file.pathw = NULL; req->fs.info.new_pathw = NULL; req->ptr = NULL; req->flags |= UV_FS_CLEANEDUP; } int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_OPEN, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.file_flags = flags; req->fs.info.mode = mode; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__open(req); return req->result; } } int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_CLOSE, cb); req->file.fd = fd; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__close(req); return req->result; } } int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file fd, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) { if (bufs == NULL || nbufs == 0) return UV_EINVAL; uv_fs_req_init(loop, req, UV_FS_READ, cb); req->file.fd = fd; req->fs.info.nbufs = nbufs; req->fs.info.bufs = req->fs.info.bufsml; if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); if (req->fs.info.bufs == NULL) return UV_ENOMEM; memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); req->fs.info.offset = offset; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__read(req); return req->result; } } int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file fd, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) { if (bufs == NULL || nbufs == 0) return UV_EINVAL; uv_fs_req_init(loop, req, UV_FS_WRITE, cb); req->file.fd = fd; req->fs.info.nbufs = nbufs; req->fs.info.bufs = req->fs.info.bufsml; if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); if (req->fs.info.bufs == NULL) return UV_ENOMEM; memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); req->fs.info.offset = offset; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__write(req); return req->result; } } int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_UNLINK, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__unlink(req); return req->result; } } int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_MKDIR, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.mode = mode; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__mkdir(req); return req->result; } } int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb); err = fs__capture_path(req, tpl, NULL, TRUE); if (err) return uv_translate_sys_error(err); if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__mkdtemp(req); return req->result; } } int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_RMDIR, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__rmdir(req); return req->result; } } int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_SCANDIR, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.file_flags = flags; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__scandir(req); return req->result; } } int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_LINK, cb); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__link(req); return req->result; } } int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_SYMLINK, cb); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.file_flags = flags; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__symlink(req); return req->result; } } int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_READLINK, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__readlink(req); return req->result; } } int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; if (!req || !path) { return UV_EINVAL; } uv_fs_req_init(loop, req, UV_FS_REALPATH, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__realpath(req); return req->result; } } int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_CHOWN, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__chown(req); return req->result; } } int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FCHOWN, cb); if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__fchown(req); return req->result; } } int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_STAT, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__stat(req); return req->result; } } int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_LSTAT, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__lstat(req); return req->result; } } int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FSTAT, cb); req->file.fd = fd; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__fstat(req); return req->result; } } int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_RENAME, cb); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { return uv_translate_sys_error(err); } if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__rename(req); return req->result; } } int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FSYNC, cb); req->file.fd = fd; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__fsync(req); return req->result; } } int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FDATASYNC, cb); req->file.fd = fd; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__fdatasync(req); return req->result; } } int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int64_t offset, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FTRUNCATE, cb); req->file.fd = fd; req->fs.info.offset = offset; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__ftruncate(req); return req->result; } } int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out, uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_SENDFILE, cb); req->file.fd = fd_in; req->fs.info.fd_out = fd_out; req->fs.info.offset = in_offset; req->fs.info.bufsml[0].len = length; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__sendfile(req); return req->result; } } int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_ACCESS, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) return uv_translate_sys_error(err); req->flags = flags; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } fs__access(req); return req->result; } int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_CHMOD, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.mode = mode; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__chmod(req); return req->result; } } int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FCHMOD, cb); req->file.fd = fd; req->fs.info.mode = mode; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__fchmod(req); return req->result; } } int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) { int err; uv_fs_req_init(loop, req, UV_FS_UTIME, cb); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.time.atime = atime; req->fs.time.mtime = mtime; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__utime(req); return req->result; } } int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, double mtime, uv_fs_cb cb) { uv_fs_req_init(loop, req, UV_FS_FUTIME, cb); req->file.fd = fd; req->fs.time.atime = atime; req->fs.time.mtime = mtime; if (cb) { QUEUE_FS_TP_JOB(loop, req); return 0; } else { fs__futime(req); return req->result; } } libuv-v1.8.0/src/win/getaddrinfo.c000066400000000000000000000261411263361716600170640ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "uv.h" #include "internal.h" #include "req-inl.h" /* EAI_* constants. */ #include int uv__getaddrinfo_translate_error(int sys_err) { switch (sys_err) { case 0: return 0; case WSATRY_AGAIN: return UV_EAI_AGAIN; case WSAEINVAL: return UV_EAI_BADFLAGS; case WSANO_RECOVERY: return UV_EAI_FAIL; case WSAEAFNOSUPPORT: return UV_EAI_FAMILY; case WSA_NOT_ENOUGH_MEMORY: return UV_EAI_MEMORY; case WSAHOST_NOT_FOUND: return UV_EAI_NONAME; case WSATYPE_NOT_FOUND: return UV_EAI_SERVICE; case WSAESOCKTNOSUPPORT: return UV_EAI_SOCKTYPE; default: return uv_translate_sys_error(sys_err); } } /* * MinGW is missing this */ #if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR) typedef struct addrinfoW { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; WCHAR* ai_canonname; struct sockaddr* ai_addr; struct addrinfoW* ai_next; } ADDRINFOW, *PADDRINFOW; DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node, const WCHAR* service, const ADDRINFOW* hints, PADDRINFOW* result); DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo); #endif /* adjust size value to be multiple of 4. Use to keep pointer aligned */ /* Do we need different versions of this for different architectures? */ #define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) static void uv__getaddrinfo_work(struct uv__work* w) { uv_getaddrinfo_t* req; struct addrinfoW* hints; int err; req = container_of(w, uv_getaddrinfo_t, work_req); hints = req->addrinfow; req->addrinfow = NULL; err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow); req->retcode = uv__getaddrinfo_translate_error(err); } /* * Called from uv_run when complete. Call user specified callback * then free returned addrinfo * Returned addrinfo strings are converted from UTF-16 to UTF-8. * * To minimize allocation we calculate total size required, * and copy all structs and referenced strings into the one block. * Each size calculation is adjusted to avoid unaligned pointers. */ static void uv__getaddrinfo_done(struct uv__work* w, int status) { uv_getaddrinfo_t* req; int addrinfo_len = 0; int name_len = 0; size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo)); struct addrinfoW* addrinfow_ptr; struct addrinfo* addrinfo_ptr; char* alloc_ptr = NULL; char* cur_ptr = NULL; req = container_of(w, uv_getaddrinfo_t, work_req); /* release input parameter memory */ uv__free(req->alloc); req->alloc = NULL; if (status == UV_ECANCELED) { assert(req->retcode == 0); req->retcode = UV_EAI_CANCELED; goto complete; } if (req->retcode == 0) { /* convert addrinfoW to addrinfo */ /* first calculate required length */ addrinfow_ptr = req->addrinfow; while (addrinfow_ptr != NULL) { addrinfo_len += addrinfo_struct_len + ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); if (addrinfow_ptr->ai_canonname != NULL) { name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); if (name_len == 0) { req->retcode = uv_translate_sys_error(GetLastError()); goto complete; } addrinfo_len += ALIGNED_SIZE(name_len); } addrinfow_ptr = addrinfow_ptr->ai_next; } /* allocate memory for addrinfo results */ alloc_ptr = (char*)uv__malloc(addrinfo_len); /* do conversions */ if (alloc_ptr != NULL) { cur_ptr = alloc_ptr; addrinfow_ptr = req->addrinfow; while (addrinfow_ptr != NULL) { /* copy addrinfo struct data */ assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len); addrinfo_ptr = (struct addrinfo*)cur_ptr; addrinfo_ptr->ai_family = addrinfow_ptr->ai_family; addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype; addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol; addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags; addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen; addrinfo_ptr->ai_canonname = NULL; addrinfo_ptr->ai_addr = NULL; addrinfo_ptr->ai_next = NULL; cur_ptr += addrinfo_struct_len; /* copy sockaddr */ if (addrinfo_ptr->ai_addrlen > 0) { assert(cur_ptr + addrinfo_ptr->ai_addrlen <= alloc_ptr + addrinfo_len); memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen); addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr; cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen); } /* convert canonical name to UTF-8 */ if (addrinfow_ptr->ai_canonname != NULL) { name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); assert(name_len > 0); assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, cur_ptr, name_len); assert(name_len > 0); addrinfo_ptr->ai_canonname = cur_ptr; cur_ptr += ALIGNED_SIZE(name_len); } assert(cur_ptr <= alloc_ptr + addrinfo_len); /* set next ptr */ addrinfow_ptr = addrinfow_ptr->ai_next; if (addrinfow_ptr != NULL) { addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr; } } req->addrinfo = (struct addrinfo*)alloc_ptr; } else { req->retcode = UV_EAI_MEMORY; } } /* return memory to system */ if (req->addrinfow != NULL) { FreeAddrInfoW(req->addrinfow); req->addrinfow = NULL; } complete: uv__req_unregister(req->loop, req); /* finally do callback with converted result */ if (req->getaddrinfo_cb) req->getaddrinfo_cb(req, req->retcode, req->addrinfo); } void uv_freeaddrinfo(struct addrinfo* ai) { char* alloc_ptr = (char*)ai; /* release copied result memory */ uv__free(alloc_ptr); } /* * Entry point for getaddrinfo * we convert the UTF-8 strings to UNICODE * and save the UNICODE string pointers in the req * We also copy hints so that caller does not need to keep memory until the * callback. * return 0 if a callback will be made * return error code if validation fails * * To minimize allocation we calculate total size required, * and copy all structs and referenced strings into the one block. * Each size calculation is adjusted to avoid unaligned pointers. */ int uv_getaddrinfo(uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb, const char* node, const char* service, const struct addrinfo* hints) { int nodesize = 0; int servicesize = 0; int hintssize = 0; char* alloc_ptr = NULL; int err; if (req == NULL || (node == NULL && service == NULL)) { err = WSAEINVAL; goto error; } uv_req_init(loop, (uv_req_t*)req); req->getaddrinfo_cb = getaddrinfo_cb; req->addrinfo = NULL; req->type = UV_GETADDRINFO; req->loop = loop; req->retcode = 0; /* calculate required memory size for all input values */ if (node != NULL) { nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(WCHAR)); if (nodesize == 0) { err = GetLastError(); goto error; } } if (service != NULL) { servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) * sizeof(WCHAR)); if (servicesize == 0) { err = GetLastError(); goto error; } } if (hints != NULL) { hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); } /* allocate memory for inputs, and partition it as needed */ alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize); if (!alloc_ptr) { err = WSAENOBUFS; goto error; } /* save alloc_ptr now so we can free if error */ req->alloc = (void*)alloc_ptr; /* convert node string to UTF16 into allocated memory and save pointer in */ /* the request. */ if (node != NULL) { req->node = (WCHAR*)alloc_ptr; if (uv_utf8_to_utf16(node, (WCHAR*) alloc_ptr, nodesize / sizeof(WCHAR)) == 0) { err = GetLastError(); goto error; } alloc_ptr += nodesize; } else { req->node = NULL; } /* convert service string to UTF16 into allocated memory and save pointer */ /* in the req. */ if (service != NULL) { req->service = (WCHAR*)alloc_ptr; if (uv_utf8_to_utf16(service, (WCHAR*) alloc_ptr, servicesize / sizeof(WCHAR)) == 0) { err = GetLastError(); goto error; } alloc_ptr += servicesize; } else { req->service = NULL; } /* copy hints to allocated memory and save pointer in req */ if (hints != NULL) { req->addrinfow = (struct addrinfoW*)alloc_ptr; req->addrinfow->ai_family = hints->ai_family; req->addrinfow->ai_socktype = hints->ai_socktype; req->addrinfow->ai_protocol = hints->ai_protocol; req->addrinfow->ai_flags = hints->ai_flags; req->addrinfow->ai_addrlen = 0; req->addrinfow->ai_canonname = NULL; req->addrinfow->ai_addr = NULL; req->addrinfow->ai_next = NULL; } else { req->addrinfow = NULL; } uv__req_register(loop, req); if (getaddrinfo_cb) { uv__work_submit(loop, &req->work_req, uv__getaddrinfo_work, uv__getaddrinfo_done); return 0; } else { uv__getaddrinfo_work(&req->work_req); uv__getaddrinfo_done(&req->work_req, 0); return req->retcode; } error: if (req != NULL) { uv__free(req->alloc); req->alloc = NULL; } return uv_translate_sys_error(err); } libuv-v1.8.0/src/win/getnameinfo.c000066400000000000000000000102741263361716600170720ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" #include "req-inl.h" #ifndef GetNameInfo int WSAAPI GetNameInfoW( const SOCKADDR *pSockaddr, socklen_t SockaddrLength, PWCHAR pNodeBuffer, DWORD NodeBufferSize, PWCHAR pServiceBuffer, DWORD ServiceBufferSize, INT Flags ); #endif static void uv__getnameinfo_work(struct uv__work* w) { uv_getnameinfo_t* req; WCHAR host[NI_MAXHOST]; WCHAR service[NI_MAXSERV]; int ret = 0; req = container_of(w, uv_getnameinfo_t, work_req); if (GetNameInfoW((struct sockaddr*)&req->storage, sizeof(req->storage), host, ARRAY_SIZE(host), service, ARRAY_SIZE(service), req->flags)) { ret = WSAGetLastError(); } req->retcode = uv__getaddrinfo_translate_error(ret); /* convert results to UTF-8 */ WideCharToMultiByte(CP_UTF8, 0, host, -1, req->host, sizeof(req->host), NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, service, -1, req->service, sizeof(req->service), NULL, NULL); } /* * Called from uv_run when complete. */ static void uv__getnameinfo_done(struct uv__work* w, int status) { uv_getnameinfo_t* req; char* host; char* service; req = container_of(w, uv_getnameinfo_t, work_req); uv__req_unregister(req->loop, req); host = service = NULL; if (status == UV_ECANCELED) { assert(req->retcode == 0); req->retcode = UV_EAI_CANCELED; } else if (req->retcode == 0) { host = req->host; service = req->service; } if (req->getnameinfo_cb) req->getnameinfo_cb(req, req->retcode, host, service); } /* * Entry point for getnameinfo * return 0 if a callback will be made * return error code if validation fails */ int uv_getnameinfo(uv_loop_t* loop, uv_getnameinfo_t* req, uv_getnameinfo_cb getnameinfo_cb, const struct sockaddr* addr, int flags) { if (req == NULL || addr == NULL) return UV_EINVAL; if (addr->sa_family == AF_INET) { memcpy(&req->storage, addr, sizeof(struct sockaddr_in)); } else if (addr->sa_family == AF_INET6) { memcpy(&req->storage, addr, sizeof(struct sockaddr_in6)); } else { return UV_EINVAL; } uv_req_init(loop, (uv_req_t*)req); uv__req_register(loop, req); req->getnameinfo_cb = getnameinfo_cb; req->flags = flags; req->type = UV_GETNAMEINFO; req->loop = loop; req->retcode = 0; if (getnameinfo_cb) { uv__work_submit(loop, &req->work_req, uv__getnameinfo_work, uv__getnameinfo_done); return 0; } else { uv__getnameinfo_work(&req->work_req); uv__getnameinfo_done(&req->work_req, 0); return req->retcode; } } libuv-v1.8.0/src/win/handle-inl.h000066400000000000000000000150471263361716600166210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_HANDLE_INL_H_ #define UV_WIN_HANDLE_INL_H_ #include #include #include "uv.h" #include "internal.h" #define DECREASE_ACTIVE_COUNT(loop, handle) \ do { \ if (--(handle)->activecnt == 0 && \ !((handle)->flags & UV__HANDLE_CLOSING)) { \ uv__handle_stop((handle)); \ } \ assert((handle)->activecnt >= 0); \ } while (0) #define INCREASE_ACTIVE_COUNT(loop, handle) \ do { \ if ((handle)->activecnt++ == 0) { \ uv__handle_start((handle)); \ } \ assert((handle)->activecnt > 0); \ } while (0) #define DECREASE_PENDING_REQ_COUNT(handle) \ do { \ assert(handle->reqs_pending > 0); \ handle->reqs_pending--; \ \ if (handle->flags & UV__HANDLE_CLOSING && \ handle->reqs_pending == 0) { \ uv_want_endgame(loop, (uv_handle_t*)handle); \ } \ } while (0) #define uv__handle_closing(handle) \ do { \ assert(!((handle)->flags & UV__HANDLE_CLOSING)); \ \ if (!(((handle)->flags & UV__HANDLE_ACTIVE) && \ ((handle)->flags & UV__HANDLE_REF))) \ uv__active_handle_add((uv_handle_t*) (handle)); \ \ (handle)->flags |= UV__HANDLE_CLOSING; \ (handle)->flags &= ~UV__HANDLE_ACTIVE; \ } while (0) #define uv__handle_close(handle) \ do { \ QUEUE_REMOVE(&(handle)->handle_queue); \ uv__active_handle_rm((uv_handle_t*) (handle)); \ \ (handle)->flags |= UV_HANDLE_CLOSED; \ \ if ((handle)->close_cb) \ (handle)->close_cb((uv_handle_t*) (handle)); \ } while (0) INLINE static void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) { if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) { handle->flags |= UV_HANDLE_ENDGAME_QUEUED; handle->endgame_next = loop->endgame_handles; loop->endgame_handles = handle; } } INLINE static void uv_process_endgames(uv_loop_t* loop) { uv_handle_t* handle; while (loop->endgame_handles) { handle = loop->endgame_handles; loop->endgame_handles = handle->endgame_next; handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; switch (handle->type) { case UV_TCP: uv_tcp_endgame(loop, (uv_tcp_t*) handle); break; case UV_NAMED_PIPE: uv_pipe_endgame(loop, (uv_pipe_t*) handle); break; case UV_TTY: uv_tty_endgame(loop, (uv_tty_t*) handle); break; case UV_UDP: uv_udp_endgame(loop, (uv_udp_t*) handle); break; case UV_POLL: uv_poll_endgame(loop, (uv_poll_t*) handle); break; case UV_TIMER: uv_timer_endgame(loop, (uv_timer_t*) handle); break; case UV_PREPARE: case UV_CHECK: case UV_IDLE: uv_loop_watcher_endgame(loop, handle); break; case UV_ASYNC: uv_async_endgame(loop, (uv_async_t*) handle); break; case UV_SIGNAL: uv_signal_endgame(loop, (uv_signal_t*) handle); break; case UV_PROCESS: uv_process_endgame(loop, (uv_process_t*) handle); break; case UV_FS_EVENT: uv_fs_event_endgame(loop, (uv_fs_event_t*) handle); break; case UV_FS_POLL: uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle); break; default: assert(0); break; } } } INLINE static HANDLE uv__get_osfhandle(int fd) { /* _get_osfhandle() raises an assert in debug builds if the FD is invalid. */ /* But it also correctly checks the FD and returns INVALID_HANDLE_VALUE */ /* for invalid FDs in release builds (or if you let the assert continue). */ /* So this wrapper function disables asserts when calling _get_osfhandle. */ HANDLE handle; UV_BEGIN_DISABLE_CRT_ASSERT(); handle = (HANDLE) _get_osfhandle(fd); UV_END_DISABLE_CRT_ASSERT(); return handle; } #endif /* UV_WIN_HANDLE_INL_H_ */ libuv-v1.8.0/src/win/handle.c000066400000000000000000000074111263361716600160300ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" uv_handle_type uv_guess_handle(uv_file file) { HANDLE handle; DWORD mode; if (file < 0) { return UV_UNKNOWN_HANDLE; } handle = uv__get_osfhandle(file); switch (GetFileType(handle)) { case FILE_TYPE_CHAR: if (GetConsoleMode(handle, &mode)) { return UV_TTY; } else { return UV_FILE; } case FILE_TYPE_PIPE: return UV_NAMED_PIPE; case FILE_TYPE_DISK: return UV_FILE; default: return UV_UNKNOWN_HANDLE; } } int uv_is_active(const uv_handle_t* handle) { return (handle->flags & UV__HANDLE_ACTIVE) && !(handle->flags & UV__HANDLE_CLOSING); } void uv_close(uv_handle_t* handle, uv_close_cb cb) { uv_loop_t* loop = handle->loop; if (handle->flags & UV__HANDLE_CLOSING) { assert(0); return; } handle->close_cb = cb; /* Handle-specific close actions */ switch (handle->type) { case UV_TCP: uv_tcp_close(loop, (uv_tcp_t*)handle); return; case UV_NAMED_PIPE: uv_pipe_close(loop, (uv_pipe_t*) handle); return; case UV_TTY: uv_tty_close((uv_tty_t*) handle); return; case UV_UDP: uv_udp_close(loop, (uv_udp_t*) handle); return; case UV_POLL: uv_poll_close(loop, (uv_poll_t*) handle); return; case UV_TIMER: uv_timer_stop((uv_timer_t*)handle); uv__handle_closing(handle); uv_want_endgame(loop, handle); return; case UV_PREPARE: uv_prepare_stop((uv_prepare_t*)handle); uv__handle_closing(handle); uv_want_endgame(loop, handle); return; case UV_CHECK: uv_check_stop((uv_check_t*)handle); uv__handle_closing(handle); uv_want_endgame(loop, handle); return; case UV_IDLE: uv_idle_stop((uv_idle_t*)handle); uv__handle_closing(handle); uv_want_endgame(loop, handle); return; case UV_ASYNC: uv_async_close(loop, (uv_async_t*) handle); return; case UV_SIGNAL: uv_signal_close(loop, (uv_signal_t*) handle); return; case UV_PROCESS: uv_process_close(loop, (uv_process_t*) handle); return; case UV_FS_EVENT: uv_fs_event_close(loop, (uv_fs_event_t*) handle); return; case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t*) handle); uv__handle_closing(handle); uv_want_endgame(loop, handle); return; default: /* Not supported */ abort(); } } int uv_is_closing(const uv_handle_t* handle) { return !!(handle->flags & (UV__HANDLE_CLOSING | UV_HANDLE_CLOSED)); } libuv-v1.8.0/src/win/internal.h000066400000000000000000000267101263361716600164210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_INTERNAL_H_ #define UV_WIN_INTERNAL_H_ #include "uv.h" #include "../uv-common.h" #include "tree.h" #include "winapi.h" #include "winsock.h" #ifdef _MSC_VER # define INLINE __inline # define UV_THREAD_LOCAL __declspec( thread ) #else # define INLINE inline # define UV_THREAD_LOCAL __thread #endif #ifdef _DEBUG extern UV_THREAD_LOCAL int uv__crt_assert_enabled; #define UV_BEGIN_DISABLE_CRT_ASSERT() \ { \ int uv__saved_crt_assert_enabled = uv__crt_assert_enabled; \ uv__crt_assert_enabled = FALSE; #define UV_END_DISABLE_CRT_ASSERT() \ uv__crt_assert_enabled = uv__saved_crt_assert_enabled; \ } #else #define UV_BEGIN_DISABLE_CRT_ASSERT() #define UV_END_DISABLE_CRT_ASSERT() #endif /* * Handles * (also see handle-inl.h) */ /* Used by all handles. */ #define UV_HANDLE_CLOSED 0x00000002 #define UV_HANDLE_ENDGAME_QUEUED 0x00000008 /* uv-common.h: #define UV__HANDLE_CLOSING 0x00000001 */ /* uv-common.h: #define UV__HANDLE_ACTIVE 0x00000040 */ /* uv-common.h: #define UV__HANDLE_REF 0x00000020 */ /* uv-common.h: #define UV_HANDLE_INTERNAL 0x00000080 */ /* Used by streams and UDP handles. */ #define UV_HANDLE_READING 0x00000100 #define UV_HANDLE_BOUND 0x00000200 #define UV_HANDLE_LISTENING 0x00000800 #define UV_HANDLE_CONNECTION 0x00001000 #define UV_HANDLE_READABLE 0x00008000 #define UV_HANDLE_WRITABLE 0x00010000 #define UV_HANDLE_READ_PENDING 0x00020000 #define UV_HANDLE_SYNC_BYPASS_IOCP 0x00040000 #define UV_HANDLE_ZERO_READ 0x00080000 #define UV_HANDLE_EMULATE_IOCP 0x00100000 #define UV_HANDLE_BLOCKING_WRITES 0x00200000 /* Used by uv_tcp_t and uv_udp_t handles */ #define UV_HANDLE_IPV6 0x01000000 /* Only used by uv_tcp_t handles. */ #define UV_HANDLE_TCP_NODELAY 0x02000000 #define UV_HANDLE_TCP_KEEPALIVE 0x04000000 #define UV_HANDLE_TCP_SINGLE_ACCEPT 0x08000000 #define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING 0x10000000 #define UV_HANDLE_TCP_SOCKET_CLOSED 0x20000000 #define UV_HANDLE_SHARED_TCP_SOCKET 0x40000000 /* Only used by uv_pipe_t handles. */ #define UV_HANDLE_NON_OVERLAPPED_PIPE 0x01000000 #define UV_HANDLE_PIPESERVER 0x02000000 #define UV_HANDLE_PIPE_READ_CANCELABLE 0x04000000 /* Only used by uv_tty_t handles. */ #define UV_HANDLE_TTY_READABLE 0x01000000 #define UV_HANDLE_TTY_RAW 0x02000000 #define UV_HANDLE_TTY_SAVED_POSITION 0x04000000 #define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x08000000 /* Only used by uv_poll_t handles. */ #define UV_HANDLE_POLL_SLOW 0x02000000 /* * Requests: see req-inl.h */ /* * Streams: see stream-inl.h */ /* * TCP */ typedef struct { WSAPROTOCOL_INFOW socket_info; int delayed_error; } uv__ipc_socket_info_ex; int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client); int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs); void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, uv_write_t* req); void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, uv_connect_t* req); void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex, int tcp_connection); int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, LPWSAPROTOCOL_INFOW protocol_info); /* * UDP */ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req); void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, uv_udp_send_t* req); void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle); void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle); /* * Pipes */ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, char* name, size_t nameSize); int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client); int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); int uv_pipe_write2(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb); void uv__pipe_pause_read(uv_pipe_t* handle); void uv__pipe_unpause_read(uv_pipe_t* handle); void uv__pipe_stop_read(uv_pipe_t* handle); void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* req); void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, uv_write_t* req); void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* raw_req); void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, uv_connect_t* req); void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req); void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle); void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle); void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); /* * TTY */ void uv_console_init(); int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_tty_read_stop(uv_tty_t* handle); int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs); void uv_tty_close(uv_tty_t* handle); void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req); void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req); /* TODO: remove me */ void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* raw_req); /* TODO: remove me */ void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, uv_connect_t* req); void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle); /* * Poll watchers */ void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req); int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle); void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle); /* * Timers */ void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); DWORD uv__next_timeout(const uv_loop_t* loop); void uv__time_forward(uv_loop_t* loop, uint64_t msecs); void uv_process_timers(uv_loop_t* loop); /* * Loop watchers */ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle); void uv_prepare_invoke(uv_loop_t* loop); void uv_check_invoke(uv_loop_t* loop); void uv_idle_invoke(uv_loop_t* loop); void uv__once_init(); /* * Async watcher */ void uv_async_close(uv_loop_t* loop, uv_async_t* handle); void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle); void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, uv_req_t* req); /* * Signal watcher */ void uv_signals_init(); int uv__signal_dispatch(int signum); void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle); void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle); void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, uv_req_t* req); /* * Spawn */ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle); void uv_process_close(uv_loop_t* loop, uv_process_t* handle); void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle); /* * Error */ int uv_translate_sys_error(int sys_errno); /* * FS */ void uv_fs_init(); /* * FS Event */ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle); void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle); void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle); /* * Stat poller. */ void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle); /* * Utilities. */ void uv__util_init(); uint64_t uv__hrtime(double scale); int uv_parent_pid(); int uv_current_pid(); __declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall); /* * Process stdio handles. */ int uv__stdio_create(uv_loop_t* loop, const uv_process_options_t* options, BYTE** buffer_ptr); void uv__stdio_destroy(BYTE* buffer); void uv__stdio_noinherit(BYTE* buffer); int uv__stdio_verify(BYTE* buffer, WORD size); WORD uv__stdio_size(BYTE* buffer); HANDLE uv__stdio_handle(BYTE* buffer, int fd); /* * Winapi and ntapi utility functions */ void uv_winapi_init(); /* * Winsock utility functions */ void uv_winsock_init(); int uv_ntstatus_to_winsock_error(NTSTATUS status); BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target); BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target); int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr, int* addr_len, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, AFD_POLL_INFO* info_out, OVERLAPPED* overlapped); /* Whether there are any non-IFS LSPs stacked on TCP */ extern int uv_tcp_non_ifs_lsp_ipv4; extern int uv_tcp_non_ifs_lsp_ipv6; /* Ip address used to bind to any port at any interface */ extern struct sockaddr_in uv_addr_ip4_any_; extern struct sockaddr_in6 uv_addr_ip6_any_; #endif /* UV_WIN_INTERNAL_H_ */ libuv-v1.8.0/src/win/loop-watcher.c000066400000000000000000000174761263361716600172150ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "uv.h" #include "internal.h" #include "handle-inl.h" void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { if (handle->flags & UV__HANDLE_CLOSING) { assert(!(handle->flags & UV_HANDLE_CLOSED)); handle->flags |= UV_HANDLE_CLOSED; uv__handle_close(handle); } } #define UV_LOOP_WATCHER_DEFINE(name, NAME) \ int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \ uv__handle_init(loop, (uv_handle_t*) handle, UV_##NAME); \ \ return 0; \ } \ \ \ int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ uv_loop_t* loop = handle->loop; \ uv_##name##_t* old_head; \ \ assert(handle->type == UV_##NAME); \ \ if (uv__is_active(handle)) \ return 0; \ \ if (cb == NULL) \ return UV_EINVAL; \ \ old_head = loop->name##_handles; \ \ handle->name##_next = old_head; \ handle->name##_prev = NULL; \ \ if (old_head) { \ old_head->name##_prev = handle; \ } \ \ loop->name##_handles = handle; \ \ handle->name##_cb = cb; \ uv__handle_start(handle); \ \ return 0; \ } \ \ \ int uv_##name##_stop(uv_##name##_t* handle) { \ uv_loop_t* loop = handle->loop; \ \ assert(handle->type == UV_##NAME); \ \ if (!uv__is_active(handle)) \ return 0; \ \ /* Update loop head if needed */ \ if (loop->name##_handles == handle) { \ loop->name##_handles = handle->name##_next; \ } \ \ /* Update the iterator-next pointer of needed */ \ if (loop->next_##name##_handle == handle) { \ loop->next_##name##_handle = handle->name##_next; \ } \ \ if (handle->name##_prev) { \ handle->name##_prev->name##_next = handle->name##_next; \ } \ if (handle->name##_next) { \ handle->name##_next->name##_prev = handle->name##_prev; \ } \ \ uv__handle_stop(handle); \ \ return 0; \ } \ \ \ void uv_##name##_invoke(uv_loop_t* loop) { \ uv_##name##_t* handle; \ \ (loop)->next_##name##_handle = (loop)->name##_handles; \ \ while ((loop)->next_##name##_handle != NULL) { \ handle = (loop)->next_##name##_handle; \ (loop)->next_##name##_handle = handle->name##_next; \ \ handle->name##_cb(handle); \ } \ } UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) UV_LOOP_WATCHER_DEFINE(check, CHECK) UV_LOOP_WATCHER_DEFINE(idle, IDLE) libuv-v1.8.0/src/win/pipe.c000066400000000000000000001707111263361716600155360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "stream-inl.h" #include "req-inl.h" typedef struct uv__ipc_queue_item_s uv__ipc_queue_item_t; struct uv__ipc_queue_item_s { /* * NOTE: It is important for socket_info_ex to be the first field, * because we will we assigning it to the pending_ipc_info.socket_info */ uv__ipc_socket_info_ex socket_info_ex; QUEUE member; int tcp_connection; }; /* A zero-size buffer for use by uv_pipe_read */ static char uv_zero_[] = ""; /* Null uv_buf_t */ static const uv_buf_t uv_null_buf_ = { 0, NULL }; /* The timeout that the pipe will wait for the remote end to write data */ /* when the local ends wants to shut it down. */ static const int64_t eof_timeout = 50; /* ms */ static const int default_pending_pipe_instances = 4; /* Pipe prefix */ static char pipe_prefix[] = "\\\\?\\pipe"; static const int pipe_prefix_len = sizeof(pipe_prefix) - 1; /* IPC protocol flags. */ #define UV_IPC_RAW_DATA 0x0001 #define UV_IPC_TCP_SERVER 0x0002 #define UV_IPC_TCP_CONNECTION 0x0004 /* IPC frame header. */ typedef struct { int flags; uint64_t raw_data_length; } uv_ipc_frame_header_t; /* IPC frame, which contains an imported TCP socket stream. */ typedef struct { uv_ipc_frame_header_t header; uv__ipc_socket_info_ex socket_info_ex; } uv_ipc_frame_uv_stream; static void eof_timer_init(uv_pipe_t* pipe); static void eof_timer_start(uv_pipe_t* pipe); static void eof_timer_stop(uv_pipe_t* pipe); static void eof_timer_cb(uv_timer_t* timer); static void eof_timer_destroy(uv_pipe_t* pipe); static void eof_timer_close_cb(uv_handle_t* handle); static void uv_unique_pipe_name(char* ptr, char* name, size_t size) { snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%u", ptr, GetCurrentProcessId()); } int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { uv_stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); handle->reqs_pending = 0; handle->handle = INVALID_HANDLE_VALUE; handle->name = NULL; handle->pipe.conn.ipc_pid = 0; handle->pipe.conn.remaining_ipc_rawdata_bytes = 0; QUEUE_INIT(&handle->pipe.conn.pending_ipc_info.queue); handle->pipe.conn.pending_ipc_info.queue_len = 0; handle->ipc = ipc; handle->pipe.conn.non_overlapped_writes_tail = NULL; handle->pipe.conn.readfile_thread = NULL; uv_req_init(loop, (uv_req_t*) &handle->pipe.conn.ipc_header_write_req); return 0; } static void uv_pipe_connection_init(uv_pipe_t* handle) { uv_connection_init((uv_stream_t*) handle); handle->read_req.data = handle; handle->pipe.conn.eof_timer = NULL; assert(!(handle->flags & UV_HANDLE_PIPESERVER)); if (pCancelSynchronousIo && handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { uv_mutex_init(&handle->pipe.conn.readfile_mutex); handle->flags |= UV_HANDLE_PIPE_READ_CANCELABLE; } } static HANDLE open_named_pipe(const WCHAR* name, DWORD* duplex_flags) { HANDLE pipeHandle; /* * Assume that we have a duplex pipe first, so attempt to * connect with GENERIC_READ | GENERIC_WRITE. */ pipeHandle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { *duplex_flags = UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; return pipeHandle; } /* * If the pipe is not duplex CreateFileW fails with * ERROR_ACCESS_DENIED. In that case try to connect * as a read-only or write-only. */ if (GetLastError() == ERROR_ACCESS_DENIED) { pipeHandle = CreateFileW(name, GENERIC_READ | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { *duplex_flags = UV_HANDLE_READABLE; return pipeHandle; } } if (GetLastError() == ERROR_ACCESS_DENIED) { pipeHandle = CreateFileW(name, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { *duplex_flags = UV_HANDLE_WRITABLE; return pipeHandle; } } return INVALID_HANDLE_VALUE; } static void close_pipe(uv_pipe_t* pipe) { assert(pipe->u.fd == -1 || pipe->u.fd > 2); if (pipe->u.fd == -1) CloseHandle(pipe->handle); else close(pipe->u.fd); pipe->u.fd = -1; pipe->handle = INVALID_HANDLE_VALUE; } int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, char* name, size_t nameSize) { HANDLE pipeHandle; int err; char* ptr = (char*)handle; for (;;) { uv_unique_pipe_name(ptr, name, nameSize); pipeHandle = CreateNamedPipeA(name, access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { /* No name collisions. We're done. */ break; } err = GetLastError(); if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) { goto error; } /* Pipe name collision. Increment the pointer and try again. */ ptr++; } if (CreateIoCompletionPort(pipeHandle, loop->iocp, (ULONG_PTR)handle, 0) == NULL) { err = GetLastError(); goto error; } uv_pipe_connection_init(handle); handle->handle = pipeHandle; return 0; error: if (pipeHandle != INVALID_HANDLE_VALUE) { CloseHandle(pipeHandle); } return err; } static int uv_set_pipe_handle(uv_loop_t* loop, uv_pipe_t* handle, HANDLE pipeHandle, int fd, DWORD duplex_flags) { NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_MODE_INFORMATION mode_info; DWORD mode = PIPE_READMODE_BYTE | PIPE_WAIT; DWORD current_mode = 0; DWORD err = 0; if (!(handle->flags & UV_HANDLE_PIPESERVER) && handle->handle != INVALID_HANDLE_VALUE) return UV_EBUSY; if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { /* * SetNamedPipeHandleState can fail if the handle doesn't have either * GENERIC_WRITE or FILE_WRITE_ATTRIBUTES. * But if the handle already has the desired wait and blocking modes * we can continue. */ if (!GetNamedPipeHandleState(pipeHandle, ¤t_mode, NULL, NULL, NULL, NULL, 0)) { return -1; } else if (current_mode & PIPE_NOWAIT) { SetLastError(ERROR_ACCESS_DENIED); return -1; } } else { /* If this returns ERROR_INVALID_PARAMETER we probably opened * something that is not a pipe. */ if (err == ERROR_INVALID_PARAMETER) { SetLastError(WSAENOTSOCK); } return -1; } } /* Check if the pipe was created with FILE_FLAG_OVERLAPPED. */ nt_status = pNtQueryInformationFile(pipeHandle, &io_status, &mode_info, sizeof(mode_info), FileModeInformation); if (nt_status != STATUS_SUCCESS) { return -1; } if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT || mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) { /* Non-overlapped pipe. */ handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE; } else { /* Overlapped pipe. Try to associate with IOCP. */ if (CreateIoCompletionPort(pipeHandle, loop->iocp, (ULONG_PTR)handle, 0) == NULL) { handle->flags |= UV_HANDLE_EMULATE_IOCP; } } handle->handle = pipeHandle; handle->u.fd = fd; handle->flags |= duplex_flags; return 0; } static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { uv_loop_t* loop; uv_pipe_t* handle; uv_shutdown_t* req; req = (uv_shutdown_t*) parameter; assert(req); handle = (uv_pipe_t*) req->handle; assert(handle); loop = handle->loop; assert(loop); FlushFileBuffers(handle->handle); /* Post completed */ POST_COMPLETION_FOR_REQ(loop, req); return 0; } void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { int err; DWORD result; uv_shutdown_t* req; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_PIPE_LOCAL_INFORMATION pipe_info; uv__ipc_queue_item_t* item; if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { handle->flags &= ~UV_HANDLE_PIPE_READ_CANCELABLE; uv_mutex_destroy(&handle->pipe.conn.readfile_mutex); } if ((handle->flags & UV_HANDLE_CONNECTION) && handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { req = handle->stream.conn.shutdown_req; /* Clear the shutdown_req field so we don't go here again. */ handle->stream.conn.shutdown_req = NULL; if (handle->flags & UV__HANDLE_CLOSING) { UNREGISTER_HANDLE_REQ(loop, handle, req); /* Already closing. Cancel the shutdown. */ if (req->cb) { req->cb(req, UV_ECANCELED); } DECREASE_PENDING_REQ_COUNT(handle); return; } /* Try to avoid flushing the pipe buffer in the thread pool. */ nt_status = pNtQueryInformationFile(handle->handle, &io_status, &pipe_info, sizeof pipe_info, FilePipeLocalInformation); if (nt_status != STATUS_SUCCESS) { /* Failure */ UNREGISTER_HANDLE_REQ(loop, handle, req); handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */ if (req->cb) { err = pRtlNtStatusToDosError(nt_status); req->cb(req, uv_translate_sys_error(err)); } DECREASE_PENDING_REQ_COUNT(handle); return; } if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { /* Short-circuit, no need to call FlushFileBuffers. */ uv_insert_pending_req(loop, (uv_req_t*) req); return; } /* Run FlushFileBuffers in the thread pool. */ result = QueueUserWorkItem(pipe_shutdown_thread_proc, req, WT_EXECUTELONGFUNCTION); if (result) { return; } else { /* Failure. */ UNREGISTER_HANDLE_REQ(loop, handle, req); handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */ if (req->cb) { err = GetLastError(); req->cb(req, uv_translate_sys_error(err)); } DECREASE_PENDING_REQ_COUNT(handle); return; } } if (handle->flags & UV__HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); if (handle->flags & UV_HANDLE_CONNECTION) { /* Free pending sockets */ while (!QUEUE_EMPTY(&handle->pipe.conn.pending_ipc_info.queue)) { QUEUE* q; SOCKET socket; q = QUEUE_HEAD(&handle->pipe.conn.pending_ipc_info.queue); QUEUE_REMOVE(q); item = QUEUE_DATA(q, uv__ipc_queue_item_t, member); /* Materialize socket and close it */ socket = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &item->socket_info_ex.socket_info, 0, WSA_FLAG_OVERLAPPED); uv__free(item); if (socket != INVALID_SOCKET) closesocket(socket); } handle->pipe.conn.pending_ipc_info.queue_len = 0; if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(handle->read_req.wait_handle); handle->read_req.wait_handle = INVALID_HANDLE_VALUE; } if (handle->read_req.event_handle) { CloseHandle(handle->read_req.event_handle); handle->read_req.event_handle = NULL; } } } if (handle->flags & UV_HANDLE_PIPESERVER) { assert(handle->pipe.serv.accept_reqs); uv__free(handle->pipe.serv.accept_reqs); handle->pipe.serv.accept_reqs = NULL; } uv__handle_close(handle); } } void uv_pipe_pending_instances(uv_pipe_t* handle, int count) { if (handle->flags & UV_HANDLE_BOUND) return; handle->pipe.serv.pending_instances = count; handle->flags |= UV_HANDLE_PIPESERVER; } /* Creates a pipe server. */ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { uv_loop_t* loop = handle->loop; int i, err, nameSize; uv_pipe_accept_t* req; if (handle->flags & UV_HANDLE_BOUND) { return UV_EINVAL; } if (!name) { return UV_EINVAL; } if (!(handle->flags & UV_HANDLE_PIPESERVER)) { handle->pipe.serv.pending_instances = default_pending_pipe_instances; } handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*) uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances); if (!handle->pipe.serv.accept_reqs) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } for (i = 0; i < handle->pipe.serv.pending_instances; i++) { req = &handle->pipe.serv.accept_reqs[i]; uv_req_init(loop, (uv_req_t*) req); req->type = UV_ACCEPT; req->data = handle; req->pipeHandle = INVALID_HANDLE_VALUE; req->next_pending = NULL; } /* Convert name to UTF16. */ nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(WCHAR); handle->name = (WCHAR*)uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(WCHAR))) { err = GetLastError(); goto error; } /* * Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE. * If this fails then there's already a pipe server for the given pipe name. */ handle->pipe.serv.accept_reqs[0].pipeHandle = CreateNamedPipeW(handle->name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); if (handle->pipe.serv.accept_reqs[0].pipeHandle == INVALID_HANDLE_VALUE) { err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { err = WSAEADDRINUSE; /* Translates to UV_EADDRINUSE. */ } else if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME) { err = WSAEACCES; /* Translates to UV_EACCES. */ } goto error; } if (uv_set_pipe_handle(loop, handle, handle->pipe.serv.accept_reqs[0].pipeHandle, -1, 0)) { err = GetLastError(); goto error; } handle->pipe.serv.pending_accepts = NULL; handle->flags |= UV_HANDLE_PIPESERVER; handle->flags |= UV_HANDLE_BOUND; return 0; error: if (handle->name) { uv__free(handle->name); handle->name = NULL; } if (handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE) { CloseHandle(handle->pipe.serv.accept_reqs[0].pipeHandle); handle->pipe.serv.accept_reqs[0].pipeHandle = INVALID_HANDLE_VALUE; } return uv_translate_sys_error(err); } static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { uv_loop_t* loop; uv_pipe_t* handle; uv_connect_t* req; HANDLE pipeHandle = INVALID_HANDLE_VALUE; DWORD duplex_flags; req = (uv_connect_t*) parameter; assert(req); handle = (uv_pipe_t*) req->handle; assert(handle); loop = handle->loop; assert(loop); /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. */ /* We wait for the pipe to become available with WaitNamedPipe. */ while (WaitNamedPipeW(handle->name, 30000)) { /* The pipe is now available, try to connect. */ pipeHandle = open_named_pipe(handle->name, &duplex_flags); if (pipeHandle != INVALID_HANDLE_VALUE) { break; } SwitchToThread(); } if (pipeHandle != INVALID_HANDLE_VALUE && !uv_set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) { SET_REQ_SUCCESS(req); } else { SET_REQ_ERROR(req, GetLastError()); } /* Post completed */ POST_COMPLETION_FOR_REQ(loop, req); return 0; } void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; int err, nameSize; HANDLE pipeHandle = INVALID_HANDLE_VALUE; DWORD duplex_flags; uv_req_init(loop, (uv_req_t*) req); req->type = UV_CONNECT; req->handle = (uv_stream_t*) handle; req->cb = cb; /* Convert name to UTF16. */ nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(WCHAR); handle->name = (WCHAR*)uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(WCHAR))) { err = GetLastError(); goto error; } pipeHandle = open_named_pipe(handle->name, &duplex_flags); if (pipeHandle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_PIPE_BUSY) { /* Wait for the server to make a pipe instance available. */ if (!QueueUserWorkItem(&pipe_connect_thread_proc, req, WT_EXECUTELONGFUNCTION)) { err = GetLastError(); goto error; } REGISTER_HANDLE_REQ(loop, handle, req); handle->reqs_pending++; return; } err = GetLastError(); goto error; } assert(pipeHandle != INVALID_HANDLE_VALUE); if (uv_set_pipe_handle(loop, (uv_pipe_t*) req->handle, pipeHandle, -1, duplex_flags)) { err = GetLastError(); goto error; } SET_REQ_SUCCESS(req); uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); return; error: if (handle->name) { uv__free(handle->name); handle->name = NULL; } if (pipeHandle != INVALID_HANDLE_VALUE) { CloseHandle(pipeHandle); } /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, err); uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); return; } void uv__pipe_pause_read(uv_pipe_t* handle) { if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { /* Pause the ReadFile task briefly, to work around the Windows kernel bug that causes any access to a NamedPipe to deadlock if any process has called ReadFile */ HANDLE h; uv_mutex_lock(&handle->pipe.conn.readfile_mutex); h = handle->pipe.conn.readfile_thread; while (h) { /* spinlock: we expect this to finish quickly, or we are probably about to deadlock anyways (in the kernel), so it doesn't matter */ pCancelSynchronousIo(h); SwitchToThread(); /* yield thread control briefly */ h = handle->pipe.conn.readfile_thread; } } } void uv__pipe_unpause_read(uv_pipe_t* handle) { if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { uv_mutex_unlock(&handle->pipe.conn.readfile_mutex); } } void uv__pipe_stop_read(uv_pipe_t* handle) { handle->flags &= ~UV_HANDLE_READING; uv__pipe_pause_read((uv_pipe_t*)handle); uv__pipe_unpause_read((uv_pipe_t*)handle); } /* Cleans up uv_pipe_t (server or connection) and all resources associated */ /* with it. */ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { int i; HANDLE pipeHandle; uv__pipe_stop_read(handle); if (handle->name) { uv__free(handle->name); handle->name = NULL; } if (handle->flags & UV_HANDLE_PIPESERVER) { for (i = 0; i < handle->pipe.serv.pending_instances; i++) { pipeHandle = handle->pipe.serv.accept_reqs[i].pipeHandle; if (pipeHandle != INVALID_HANDLE_VALUE) { CloseHandle(pipeHandle); handle->pipe.serv.accept_reqs[i].pipeHandle = INVALID_HANDLE_VALUE; } } handle->handle = INVALID_HANDLE_VALUE; } if (handle->flags & UV_HANDLE_CONNECTION) { handle->flags &= ~UV_HANDLE_WRITABLE; eof_timer_destroy(handle); } if ((handle->flags & UV_HANDLE_CONNECTION) && handle->handle != INVALID_HANDLE_VALUE) close_pipe(handle); } void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) { if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); } if (handle->flags & UV_HANDLE_LISTENING) { handle->flags &= ~UV_HANDLE_LISTENING; DECREASE_ACTIVE_COUNT(loop, handle); } uv_pipe_cleanup(loop, handle); if (handle->reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); } handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(handle); } static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, uv_pipe_accept_t* req, BOOL firstInstance) { assert(handle->flags & UV_HANDLE_LISTENING); if (!firstInstance) { assert(req->pipeHandle == INVALID_HANDLE_VALUE); req->pipeHandle = CreateNamedPipeW(handle->name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); if (req->pipeHandle == INVALID_HANDLE_VALUE) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; return; } if (uv_set_pipe_handle(loop, handle, req->pipeHandle, -1, 0)) { CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; return; } } assert(req->pipeHandle != INVALID_HANDLE_VALUE); /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (!ConnectNamedPipe(req->pipeHandle, &req->u.io.overlapped) && GetLastError() != ERROR_IO_PENDING) { if (GetLastError() == ERROR_PIPE_CONNECTED) { SET_REQ_SUCCESS(req); } else { CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, GetLastError()); } uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; return; } handle->reqs_pending++; } int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { uv_loop_t* loop = server->loop; uv_pipe_t* pipe_client; uv_pipe_accept_t* req; QUEUE* q; uv__ipc_queue_item_t* item; int err; if (server->ipc) { if (QUEUE_EMPTY(&server->pipe.conn.pending_ipc_info.queue)) { /* No valid pending sockets. */ return WSAEWOULDBLOCK; } q = QUEUE_HEAD(&server->pipe.conn.pending_ipc_info.queue); QUEUE_REMOVE(q); server->pipe.conn.pending_ipc_info.queue_len--; item = QUEUE_DATA(q, uv__ipc_queue_item_t, member); err = uv_tcp_import((uv_tcp_t*)client, &item->socket_info_ex, item->tcp_connection); if (err != 0) return err; uv__free(item); } else { pipe_client = (uv_pipe_t*)client; /* Find a connection instance that has been connected, but not yet */ /* accepted. */ req = server->pipe.serv.pending_accepts; if (!req) { /* No valid connections found, so we error out. */ return WSAEWOULDBLOCK; } /* Initialize the client handle and copy the pipeHandle to the client */ uv_pipe_connection_init(pipe_client); pipe_client->handle = req->pipeHandle; pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; /* Prepare the req to pick up a new connection */ server->pipe.serv.pending_accepts = req->next_pending; req->next_pending = NULL; req->pipeHandle = INVALID_HANDLE_VALUE; if (!(server->flags & UV__HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, server, req, FALSE); } } return 0; } /* Starts listening for connections for the given pipe. */ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { uv_loop_t* loop = handle->loop; int i; if (handle->flags & UV_HANDLE_LISTENING) { handle->stream.serv.connection_cb = cb; } if (!(handle->flags & UV_HANDLE_BOUND)) { return WSAEINVAL; } if (handle->flags & UV_HANDLE_READING) { return WSAEISCONN; } if (!(handle->flags & UV_HANDLE_PIPESERVER)) { return ERROR_NOT_SUPPORTED; } handle->flags |= UV_HANDLE_LISTENING; INCREASE_ACTIVE_COUNT(loop, handle); handle->stream.serv.connection_cb = cb; /* First pipe handle should have already been created in uv_pipe_bind */ assert(handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE); for (i = 0; i < handle->pipe.serv.pending_instances; i++) { uv_pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0); } return 0; } static DWORD WINAPI uv_pipe_zero_readfile_thread_proc(void* parameter) { int result; DWORD bytes; uv_read_t* req = (uv_read_t*) parameter; uv_pipe_t* handle = (uv_pipe_t*) req->data; uv_loop_t* loop = handle->loop; HANDLE hThread = NULL; DWORD err; uv_mutex_t *m = &handle->pipe.conn.readfile_mutex; assert(req != NULL); assert(req->type == UV_READ); assert(handle->type == UV_NAMED_PIPE); if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { uv_mutex_lock(m); /* mutex controls *setting* of readfile_thread */ if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, TRUE, DUPLICATE_SAME_ACCESS)) { handle->pipe.conn.readfile_thread = hThread; } else { hThread = NULL; } uv_mutex_unlock(m); } restart_readfile: result = ReadFile(handle->handle, &uv_zero_, 0, &bytes, NULL); if (!result) { err = GetLastError(); if (err == ERROR_OPERATION_ABORTED && handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { if (handle->flags & UV_HANDLE_READING) { /* just a brief break to do something else */ handle->pipe.conn.readfile_thread = NULL; /* resume after it is finished */ uv_mutex_lock(m); handle->pipe.conn.readfile_thread = hThread; uv_mutex_unlock(m); goto restart_readfile; } else { result = 1; /* successfully stopped reading */ } } } if (hThread) { assert(hThread == handle->pipe.conn.readfile_thread); /* mutex does not control clearing readfile_thread */ handle->pipe.conn.readfile_thread = NULL; uv_mutex_lock(m); /* only when we hold the mutex lock is it safe to open or close the handle */ CloseHandle(hThread); uv_mutex_unlock(m); } if (!result) { SET_REQ_ERROR(req, err); } POST_COMPLETION_FOR_REQ(loop, req); return 0; } static DWORD WINAPI uv_pipe_writefile_thread_proc(void* parameter) { int result; DWORD bytes; uv_write_t* req = (uv_write_t*) parameter; uv_pipe_t* handle = (uv_pipe_t*) req->handle; uv_loop_t* loop = handle->loop; assert(req != NULL); assert(req->type == UV_WRITE); assert(handle->type == UV_NAMED_PIPE); assert(req->write_buffer.base); result = WriteFile(handle->handle, req->write_buffer.base, req->write_buffer.len, &bytes, NULL); if (!result) { SET_REQ_ERROR(req, GetLastError()); } POST_COMPLETION_FOR_REQ(loop, req); return 0; } static void CALLBACK post_completion_read_wait(void* context, BOOLEAN timed_out) { uv_read_t* req; uv_tcp_t* handle; req = (uv_read_t*) context; assert(req != NULL); handle = (uv_tcp_t*)req->data; assert(handle != NULL); assert(!timed_out); if (!PostQueuedCompletionStatus(handle->loop->iocp, req->u.io.overlapped.InternalHigh, 0, &req->u.io.overlapped)) { uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); } } static void CALLBACK post_completion_write_wait(void* context, BOOLEAN timed_out) { uv_write_t* req; uv_tcp_t* handle; req = (uv_write_t*) context; assert(req != NULL); handle = (uv_tcp_t*)req->handle; assert(handle != NULL); assert(!timed_out); if (!PostQueuedCompletionStatus(handle->loop->iocp, req->u.io.overlapped.InternalHigh, 0, &req->u.io.overlapped)) { uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); } } static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { uv_read_t* req; int result; assert(handle->flags & UV_HANDLE_READING); assert(!(handle->flags & UV_HANDLE_READ_PENDING)); assert(handle->handle != INVALID_HANDLE_VALUE); req = &handle->read_req; if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { if (!QueueUserWorkItem(&uv_pipe_zero_readfile_thread_proc, req, WT_EXECUTELONGFUNCTION)) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, GetLastError()); goto error; } } else { memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); } /* Do 0-read */ result = ReadFile(handle->handle, &uv_zero_, 0, NULL, &req->u.io.overlapped); if (!result && GetLastError() != ERROR_IO_PENDING) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, GetLastError()); goto error; } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (!req->event_handle) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); if (!req->event_handle) { uv_fatal_error(GetLastError(), "CreateEvent"); } } if (req->wait_handle == INVALID_HANDLE_VALUE) { if (!RegisterWaitForSingleObject(&req->wait_handle, req->u.io.overlapped.hEvent, post_completion_read_wait, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); goto error; } } } } /* Start the eof timer if there is one */ eof_timer_start(handle); handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; return; error: uv_insert_pending_req(loop, (uv_req_t*)req); handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; } int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; /* If reading was stopped and then started again, there could still be a */ /* read request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) uv_pipe_queue_read(loop, handle); return 0; } static void uv_insert_non_overlapped_write_req(uv_pipe_t* handle, uv_write_t* req) { req->next_req = NULL; if (handle->pipe.conn.non_overlapped_writes_tail) { req->next_req = handle->pipe.conn.non_overlapped_writes_tail->next_req; handle->pipe.conn.non_overlapped_writes_tail->next_req = (uv_req_t*)req; handle->pipe.conn.non_overlapped_writes_tail = req; } else { req->next_req = (uv_req_t*)req; handle->pipe.conn.non_overlapped_writes_tail = req; } } static uv_write_t* uv_remove_non_overlapped_write_req(uv_pipe_t* handle) { uv_write_t* req; if (handle->pipe.conn.non_overlapped_writes_tail) { req = (uv_write_t*)handle->pipe.conn.non_overlapped_writes_tail->next_req; if (req == handle->pipe.conn.non_overlapped_writes_tail) { handle->pipe.conn.non_overlapped_writes_tail = NULL; } else { handle->pipe.conn.non_overlapped_writes_tail->next_req = req->next_req; } return req; } else { /* queue empty */ return NULL; } } static void uv_queue_non_overlapped_write(uv_pipe_t* handle) { uv_write_t* req = uv_remove_non_overlapped_write_req(handle); if (req) { if (!QueueUserWorkItem(&uv_pipe_writefile_thread_proc, req, WT_EXECUTELONGFUNCTION)) { uv_fatal_error(GetLastError(), "QueueUserWorkItem"); } } } static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb) { int err; int result; uv_tcp_t* tcp_send_handle; uv_write_t* ipc_header_req = NULL; uv_ipc_frame_uv_stream ipc_frame; if (nbufs != 1 && (nbufs != 0 || !send_handle)) { return ERROR_NOT_SUPPORTED; } /* Only TCP handles are supported for sharing. */ if (send_handle && ((send_handle->type != UV_TCP) || (!(send_handle->flags & UV_HANDLE_BOUND) && !(send_handle->flags & UV_HANDLE_CONNECTION)))) { return ERROR_NOT_SUPPORTED; } assert(handle->handle != INVALID_HANDLE_VALUE); uv_req_init(loop, (uv_req_t*) req); req->type = UV_WRITE; req->handle = (uv_stream_t*) handle; req->cb = cb; req->ipc_header = 0; req->event_handle = NULL; req->wait_handle = INVALID_HANDLE_VALUE; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); if (handle->ipc) { assert(!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); ipc_frame.header.flags = 0; /* Use the IPC framing protocol. */ if (send_handle) { tcp_send_handle = (uv_tcp_t*)send_handle; if (handle->pipe.conn.ipc_pid == 0) { handle->pipe.conn.ipc_pid = uv_current_pid(); } err = uv_tcp_duplicate_socket(tcp_send_handle, handle->pipe.conn.ipc_pid, &ipc_frame.socket_info_ex.socket_info); if (err) { return err; } ipc_frame.socket_info_ex.delayed_error = tcp_send_handle->delayed_error; ipc_frame.header.flags |= UV_IPC_TCP_SERVER; if (tcp_send_handle->flags & UV_HANDLE_CONNECTION) { ipc_frame.header.flags |= UV_IPC_TCP_CONNECTION; } } if (nbufs == 1) { ipc_frame.header.flags |= UV_IPC_RAW_DATA; ipc_frame.header.raw_data_length = bufs[0].len; } /* * Use the provided req if we're only doing a single write. * If we're doing multiple writes, use ipc_header_write_req to do * the first write, and then use the provided req for the second write. */ if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) { ipc_header_req = req; } else { /* * Try to use the preallocated write req if it's available. * Otherwise allocate a new one. */ if (handle->pipe.conn.ipc_header_write_req.type != UV_WRITE) { ipc_header_req = (uv_write_t*)&handle->pipe.conn.ipc_header_write_req; } else { ipc_header_req = (uv_write_t*)uv__malloc(sizeof(uv_write_t)); if (!ipc_header_req) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } } uv_req_init(loop, (uv_req_t*) ipc_header_req); ipc_header_req->type = UV_WRITE; ipc_header_req->handle = (uv_stream_t*) handle; ipc_header_req->cb = NULL; ipc_header_req->ipc_header = 1; } /* Write the header or the whole frame. */ memset(&ipc_header_req->u.io.overlapped, 0, sizeof(ipc_header_req->u.io.overlapped)); /* Using overlapped IO, but wait for completion before returning. This write is blocking because ipc_frame is on stack. */ ipc_header_req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL); if (!ipc_header_req->u.io.overlapped.hEvent) { uv_fatal_error(GetLastError(), "CreateEvent"); } result = WriteFile(handle->handle, &ipc_frame, ipc_frame.header.flags & UV_IPC_TCP_SERVER ? sizeof(ipc_frame) : sizeof(ipc_frame.header), NULL, &ipc_header_req->u.io.overlapped); if (!result && GetLastError() != ERROR_IO_PENDING) { err = GetLastError(); CloseHandle(ipc_header_req->u.io.overlapped.hEvent); return err; } if (!result) { /* Request not completed immediately. Wait for it.*/ if (WaitForSingleObject(ipc_header_req->u.io.overlapped.hEvent, INFINITE) != WAIT_OBJECT_0) { err = GetLastError(); CloseHandle(ipc_header_req->u.io.overlapped.hEvent); return err; } } ipc_header_req->u.io.queued_bytes = 0; CloseHandle(ipc_header_req->u.io.overlapped.hEvent); ipc_header_req->u.io.overlapped.hEvent = NULL; REGISTER_HANDLE_REQ(loop, handle, ipc_header_req); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; /* If we don't have any raw data to write - we're done. */ if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) { return 0; } } if ((handle->flags & (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) == (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) { DWORD bytes; result = WriteFile(handle->handle, bufs[0].base, bufs[0].len, &bytes, NULL); if (!result) { err = GetLastError(); return err; } else { /* Request completed immediately. */ req->u.io.queued_bytes = 0; } REGISTER_HANDLE_REQ(loop, handle, req); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; POST_COMPLETION_FOR_REQ(loop, req); return 0; } else if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { req->write_buffer = bufs[0]; uv_insert_non_overlapped_write_req(handle, req); if (handle->stream.conn.write_reqs_pending == 0) { uv_queue_non_overlapped_write(handle); } /* Request queued by the kernel. */ req->u.io.queued_bytes = bufs[0].len; handle->write_queue_size += req->u.io.queued_bytes; } else if (handle->flags & UV_HANDLE_BLOCKING_WRITES) { /* Using overlapped IO, but wait for completion before returning */ req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL); if (!req->u.io.overlapped.hEvent) { uv_fatal_error(GetLastError(), "CreateEvent"); } result = WriteFile(handle->handle, bufs[0].base, bufs[0].len, NULL, &req->u.io.overlapped); if (!result && GetLastError() != ERROR_IO_PENDING) { err = GetLastError(); CloseHandle(req->u.io.overlapped.hEvent); return err; } if (result) { /* Request completed immediately. */ req->u.io.queued_bytes = 0; } else { /* Request queued by the kernel. */ req->u.io.queued_bytes = bufs[0].len; handle->write_queue_size += req->u.io.queued_bytes; if (WaitForSingleObject(req->u.io.overlapped.hEvent, INFINITE) != WAIT_OBJECT_0) { err = GetLastError(); CloseHandle(req->u.io.overlapped.hEvent); return uv_translate_sys_error(err); } } CloseHandle(req->u.io.overlapped.hEvent); REGISTER_HANDLE_REQ(loop, handle, req); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; return 0; } else { result = WriteFile(handle->handle, bufs[0].base, bufs[0].len, NULL, &req->u.io.overlapped); if (!result && GetLastError() != ERROR_IO_PENDING) { return GetLastError(); } if (result) { /* Request completed immediately. */ req->u.io.queued_bytes = 0; } else { /* Request queued by the kernel. */ req->u.io.queued_bytes = bufs[0].len; handle->write_queue_size += req->u.io.queued_bytes; } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); if (!req->event_handle) { uv_fatal_error(GetLastError(), "CreateEvent"); } if (!RegisterWaitForSingleObject(&req->wait_handle, req->u.io.overlapped.hEvent, post_completion_write_wait, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { return GetLastError(); } } } REGISTER_HANDLE_REQ(loop, handle, req); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; return 0; } int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) { return uv_pipe_write_impl(loop, req, handle, bufs, nbufs, NULL, cb); } int uv_pipe_write2(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb) { if (!handle->ipc) { return WSAEINVAL; } return uv_pipe_write_impl(loop, req, handle, bufs, nbufs, send_handle, cb); } static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, uv_buf_t buf) { /* If there is an eof timer running, we don't need it any more, */ /* so discard it. */ eof_timer_destroy(handle); handle->flags &= ~UV_HANDLE_READABLE; uv_read_stop((uv_stream_t*) handle); handle->read_cb((uv_stream_t*) handle, UV_EOF, &buf); } static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error, uv_buf_t buf) { /* If there is an eof timer running, we don't need it any more, */ /* so discard it. */ eof_timer_destroy(handle); uv_read_stop((uv_stream_t*) handle); handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(error), &buf); } static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, int error, uv_buf_t buf) { if (error == ERROR_BROKEN_PIPE) { uv_pipe_read_eof(loop, handle, buf); } else { uv_pipe_read_error(loop, handle, error, buf); } } void uv__pipe_insert_pending_socket(uv_pipe_t* handle, uv__ipc_socket_info_ex* info, int tcp_connection) { uv__ipc_queue_item_t* item; item = (uv__ipc_queue_item_t*) uv__malloc(sizeof(*item)); if (item == NULL) uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); memcpy(&item->socket_info_ex, info, sizeof(item->socket_info_ex)); item->tcp_connection = tcp_connection; QUEUE_INSERT_TAIL(&handle->pipe.conn.pending_ipc_info.queue, &item->member); handle->pipe.conn.pending_ipc_info.queue_len++; } void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* req) { DWORD bytes, avail; uv_buf_t buf; uv_ipc_frame_uv_stream ipc_frame; assert(handle->type == UV_NAMED_PIPE); handle->flags &= ~UV_HANDLE_READ_PENDING; eof_timer_stop(handle); if (!REQ_SUCCESS(req)) { /* An error occurred doing the 0-read. */ if (handle->flags & UV_HANDLE_READING) { uv_pipe_read_error_or_eof(loop, handle, GET_REQ_ERROR(req), uv_null_buf_); } } else { /* Do non-blocking reads until the buffer is empty */ while (handle->flags & UV_HANDLE_READING) { if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL)) { uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); break; } if (avail == 0) { /* There is nothing to read after all. */ break; } if (handle->ipc) { /* Use the IPC framing protocol to read the incoming data. */ if (handle->pipe.conn.remaining_ipc_rawdata_bytes == 0) { /* We're reading a new frame. First, read the header. */ assert(avail >= sizeof(ipc_frame.header)); if (!ReadFile(handle->handle, &ipc_frame.header, sizeof(ipc_frame.header), &bytes, NULL)) { uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); break; } assert(bytes == sizeof(ipc_frame.header)); assert(ipc_frame.header.flags <= (UV_IPC_TCP_SERVER | UV_IPC_RAW_DATA | UV_IPC_TCP_CONNECTION)); if (ipc_frame.header.flags & UV_IPC_TCP_SERVER) { assert(avail - sizeof(ipc_frame.header) >= sizeof(ipc_frame.socket_info_ex)); /* Read the TCP socket info. */ if (!ReadFile(handle->handle, &ipc_frame.socket_info_ex, sizeof(ipc_frame) - sizeof(ipc_frame.header), &bytes, NULL)) { uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); break; } assert(bytes == sizeof(ipc_frame) - sizeof(ipc_frame.header)); /* Store the pending socket info. */ uv__pipe_insert_pending_socket( handle, &ipc_frame.socket_info_ex, ipc_frame.header.flags & UV_IPC_TCP_CONNECTION); } if (ipc_frame.header.flags & UV_IPC_RAW_DATA) { handle->pipe.conn.remaining_ipc_rawdata_bytes = ipc_frame.header.raw_data_length; continue; } } else { avail = min(avail, (DWORD)handle->pipe.conn.remaining_ipc_rawdata_bytes); } } handle->alloc_cb((uv_handle_t*) handle, avail, &buf); if (buf.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); break; } assert(buf.base != NULL); if (ReadFile(handle->handle, buf.base, min(buf.len, avail), &bytes, NULL)) { /* Successful read */ if (handle->ipc) { assert(handle->pipe.conn.remaining_ipc_rawdata_bytes >= bytes); handle->pipe.conn.remaining_ipc_rawdata_bytes = handle->pipe.conn.remaining_ipc_rawdata_bytes - bytes; } handle->read_cb((uv_stream_t*)handle, bytes, &buf); /* Read again only if bytes == buf.len */ if (bytes <= buf.len) { break; } } else { uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf); break; } } /* Post another 0-read if still reading and not closing. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { uv_pipe_queue_read(loop, handle); } } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, uv_write_t* req) { int err; assert(handle->type == UV_NAMED_PIPE); assert(handle->write_queue_size >= req->u.io.queued_bytes); handle->write_queue_size -= req->u.io.queued_bytes; UNREGISTER_HANDLE_REQ(loop, handle, req); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (req->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } if (req->event_handle) { CloseHandle(req->event_handle); req->event_handle = NULL; } } if (req->ipc_header) { if (req == &handle->pipe.conn.ipc_header_write_req) { req->type = UV_UNKNOWN_REQ; } else { uv__free(req); } } else { if (req->cb) { err = GET_REQ_ERROR(req); req->cb(req, uv_translate_sys_error(err)); } } handle->stream.conn.write_reqs_pending--; if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE && handle->pipe.conn.non_overlapped_writes_tail) { assert(handle->stream.conn.write_reqs_pending > 0); uv_queue_non_overlapped_write(handle); } if (handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*)handle); } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* raw_req) { uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req; assert(handle->type == UV_NAMED_PIPE); if (handle->flags & UV__HANDLE_CLOSING) { /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */ assert(req->pipeHandle == INVALID_HANDLE_VALUE); DECREASE_PENDING_REQ_COUNT(handle); return; } if (REQ_SUCCESS(req)) { assert(req->pipeHandle != INVALID_HANDLE_VALUE); req->next_pending = handle->pipe.serv.pending_accepts; handle->pipe.serv.pending_accepts = req; if (handle->stream.serv.connection_cb) { handle->stream.serv.connection_cb((uv_stream_t*)handle, 0); } } else { if (req->pipeHandle != INVALID_HANDLE_VALUE) { CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; } if (!(handle->flags & UV__HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, handle, req, FALSE); } } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, uv_connect_t* req) { int err; assert(handle->type == UV_NAMED_PIPE); UNREGISTER_HANDLE_REQ(loop, handle, req); if (req->cb) { err = 0; if (REQ_SUCCESS(req)) { uv_pipe_connection_init(handle); } else { err = GET_REQ_ERROR(req); } req->cb(req, uv_translate_sys_error(err)); } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req) { assert(handle->type == UV_NAMED_PIPE); UNREGISTER_HANDLE_REQ(loop, handle, req); if (handle->flags & UV_HANDLE_READABLE) { /* Initialize and optionally start the eof timer. Only do this if the */ /* pipe is readable and we haven't seen EOF come in ourselves. */ eof_timer_init(handle); /* If reading start the timer right now. */ /* Otherwise uv_pipe_queue_read will start it. */ if (handle->flags & UV_HANDLE_READ_PENDING) { eof_timer_start(handle); } } else { /* This pipe is not readable. We can just close it to let the other end */ /* know that we're done writing. */ close_pipe(handle); } if (req->cb) { req->cb(req, 0); } DECREASE_PENDING_REQ_COUNT(handle); } static void eof_timer_init(uv_pipe_t* pipe) { int r; assert(pipe->pipe.conn.eof_timer == NULL); assert(pipe->flags & UV_HANDLE_CONNECTION); pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer); r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer); assert(r == 0); /* timers can't fail */ pipe->pipe.conn.eof_timer->data = pipe; uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer); } static void eof_timer_start(uv_pipe_t* pipe) { assert(pipe->flags & UV_HANDLE_CONNECTION); if (pipe->pipe.conn.eof_timer != NULL) { uv_timer_start(pipe->pipe.conn.eof_timer, eof_timer_cb, eof_timeout, 0); } } static void eof_timer_stop(uv_pipe_t* pipe) { assert(pipe->flags & UV_HANDLE_CONNECTION); if (pipe->pipe.conn.eof_timer != NULL) { uv_timer_stop(pipe->pipe.conn.eof_timer); } } static void eof_timer_cb(uv_timer_t* timer) { uv_pipe_t* pipe = (uv_pipe_t*) timer->data; uv_loop_t* loop = timer->loop; assert(pipe->type == UV_NAMED_PIPE); /* This should always be true, since we start the timer only */ /* in uv_pipe_queue_read after successfully calling ReadFile, */ /* or in uv_process_pipe_shutdown_req if a read is pending, */ /* and we always immediately stop the timer in */ /* uv_process_pipe_read_req. */ assert(pipe->flags & UV_HANDLE_READ_PENDING); /* If there are many packets coming off the iocp then the timer callback */ /* may be called before the read request is coming off the queue. */ /* Therefore we check here if the read request has completed but will */ /* be processed later. */ if ((pipe->flags & UV_HANDLE_READ_PENDING) && HasOverlappedIoCompleted(&pipe->read_req.u.io.overlapped)) { return; } /* Force both ends off the pipe. */ close_pipe(pipe); /* Stop reading, so the pending read that is going to fail will */ /* not be reported to the user. */ uv_read_stop((uv_stream_t*) pipe); /* Report the eof and update flags. This will get reported even if the */ /* user stopped reading in the meantime. TODO: is that okay? */ uv_pipe_read_eof(loop, pipe, uv_null_buf_); } static void eof_timer_destroy(uv_pipe_t* pipe) { assert(pipe->flags & UV_HANDLE_CONNECTION); if (pipe->pipe.conn.eof_timer) { uv_close((uv_handle_t*) pipe->pipe.conn.eof_timer, eof_timer_close_cb); pipe->pipe.conn.eof_timer = NULL; } } static void eof_timer_close_cb(uv_handle_t* handle) { assert(handle->type == UV_TIMER); uv__free(handle); } int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { HANDLE os_handle = uv__get_osfhandle(file); NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_ACCESS_INFORMATION access; DWORD duplex_flags = 0; if (os_handle == INVALID_HANDLE_VALUE) return UV_EBADF; /* In order to avoid closing a stdio file descriptor 0-2, duplicate the * underlying OS handle and forget about the original fd. * We could also opt to use the original OS handle and just never close it, * but then there would be no reliable way to cancel pending read operations * upon close. */ if (file <= 2) { if (!DuplicateHandle(INVALID_HANDLE_VALUE, os_handle, INVALID_HANDLE_VALUE, &os_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) return uv_translate_sys_error(GetLastError()); file = -1; } /* Determine what kind of permissions we have on this handle. * Cygwin opens the pipe in message mode, but we can support it, * just query the access flags and set the stream flags accordingly. */ nt_status = pNtQueryInformationFile(os_handle, &io_status, &access, sizeof(access), FileAccessInformation); if (nt_status != STATUS_SUCCESS) return UV_EINVAL; if (pipe->ipc) { if (!(access.AccessFlags & FILE_WRITE_DATA) || !(access.AccessFlags & FILE_READ_DATA)) { return UV_EINVAL; } } if (access.AccessFlags & FILE_WRITE_DATA) duplex_flags |= UV_HANDLE_WRITABLE; if (access.AccessFlags & FILE_READ_DATA) duplex_flags |= UV_HANDLE_READABLE; if (os_handle == INVALID_HANDLE_VALUE || uv_set_pipe_handle(pipe->loop, pipe, os_handle, file, duplex_flags) == -1) { return UV_EINVAL; } uv_pipe_connection_init(pipe); if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); pipe->pipe.conn.ipc_pid = uv_parent_pid(); assert(pipe->pipe.conn.ipc_pid != -1); } return 0; } static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) { NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_NAME_INFORMATION tmp_name_info; FILE_NAME_INFORMATION* name_info; WCHAR* name_buf; unsigned int addrlen; unsigned int name_size; unsigned int name_len; int err; name_info = NULL; if (handle->handle == INVALID_HANDLE_VALUE) { *size = 0; return UV_EINVAL; } uv__pipe_pause_read((uv_pipe_t*)handle); /* cast away const warning */ nt_status = pNtQueryInformationFile(handle->handle, &io_status, &tmp_name_info, sizeof tmp_name_info, FileNameInformation); if (nt_status == STATUS_BUFFER_OVERFLOW) { name_size = sizeof(*name_info) + tmp_name_info.FileNameLength; name_info = uv__malloc(name_size); if (!name_info) { *size = 0; err = UV_ENOMEM; goto cleanup; } nt_status = pNtQueryInformationFile(handle->handle, &io_status, name_info, name_size, FileNameInformation); } if (nt_status != STATUS_SUCCESS) { *size = 0; err = uv_translate_sys_error(pRtlNtStatusToDosError(nt_status)); goto error; } if (!name_info) { /* the struct on stack was used */ name_buf = tmp_name_info.FileName; name_len = tmp_name_info.FileNameLength; } else { name_buf = name_info->FileName; name_len = name_info->FileNameLength; } if (name_len == 0) { *size = 0; err = 0; goto error; } name_len /= sizeof(WCHAR); /* check how much space we need */ addrlen = WideCharToMultiByte(CP_UTF8, 0, name_buf, name_len, NULL, 0, NULL, NULL); if (!addrlen) { *size = 0; err = uv_translate_sys_error(GetLastError()); goto error; } else if (pipe_prefix_len + addrlen > *size) { /* "\\\\.\\pipe" + name */ *size = pipe_prefix_len + addrlen; err = UV_ENOBUFS; goto error; } memcpy(buffer, pipe_prefix, pipe_prefix_len); addrlen = WideCharToMultiByte(CP_UTF8, 0, name_buf, name_len, buffer+pipe_prefix_len, *size-pipe_prefix_len, NULL, NULL); if (!addrlen) { *size = 0; err = uv_translate_sys_error(GetLastError()); goto error; } addrlen += pipe_prefix_len; *size = addrlen; err = 0; goto cleanup; error: uv__free(name_info); cleanup: uv__pipe_unpause_read((uv_pipe_t*)handle); /* cast away const warning */ return err; } int uv_pipe_pending_count(uv_pipe_t* handle) { if (!handle->ipc) return 0; return handle->pipe.conn.pending_ipc_info.queue_len; } int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) { if (handle->flags & UV_HANDLE_BOUND) return uv__pipe_getname(handle, buffer, size); if (handle->flags & UV_HANDLE_CONNECTION || handle->handle != INVALID_HANDLE_VALUE) { *size = 0; return 0; } return UV_EBADF; } int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { /* emulate unix behaviour */ if (handle->flags & UV_HANDLE_BOUND) return UV_ENOTCONN; if (handle->handle != INVALID_HANDLE_VALUE) return uv__pipe_getname(handle, buffer, size); return UV_EBADF; } uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { if (!handle->ipc) return UV_UNKNOWN_HANDLE; if (handle->pipe.conn.pending_ipc_info.queue_len == 0) return UV_UNKNOWN_HANDLE; else return UV_TCP; } libuv-v1.8.0/src/win/poll.c000066400000000000000000000432171263361716600155470ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = { {0xe70f1aa0, 0xab8b, 0x11cf, {0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}, {0xf9eab0c0, 0x26d4, 0x11d0, {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}}, {0x9fc48064, 0x7298, 0x43e4, {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}} }; typedef struct uv_single_fd_set_s { unsigned int fd_count; SOCKET fd_array[1]; } uv_single_fd_set_t; static OVERLAPPED overlapped_dummy_; static uv_once_t overlapped_dummy_init_guard_ = UV_ONCE_INIT; static AFD_POLL_INFO afd_poll_info_dummy_; static void uv__init_overlapped_dummy(void) { HANDLE event; event = CreateEvent(NULL, TRUE, TRUE, NULL); if (event == NULL) uv_fatal_error(GetLastError(), "CreateEvent"); memset(&overlapped_dummy_, 0, sizeof overlapped_dummy_); overlapped_dummy_.hEvent = (HANDLE) ((uintptr_t) event | 1); } static OVERLAPPED* uv__get_overlapped_dummy() { uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy); return &overlapped_dummy_; } static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() { return &afd_poll_info_dummy_; } static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { uv_req_t* req; AFD_POLL_INFO* afd_poll_info; DWORD result; /* Find a yet unsubmitted req to submit. */ if (handle->submitted_events_1 == 0) { req = &handle->poll_req_1; afd_poll_info = &handle->afd_poll_info_1; handle->submitted_events_1 = handle->events; handle->mask_events_1 = 0; handle->mask_events_2 = handle->events; } else if (handle->submitted_events_2 == 0) { req = &handle->poll_req_2; afd_poll_info = &handle->afd_poll_info_2; handle->submitted_events_2 = handle->events; handle->mask_events_1 = handle->events; handle->mask_events_2 = 0; } else { assert(0); return; } /* Setting Exclusive to TRUE makes the other poll request return if there */ /* is any. */ afd_poll_info->Exclusive = TRUE; afd_poll_info->NumberOfHandles = 1; afd_poll_info->Timeout.QuadPart = INT64_MAX; afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket; afd_poll_info->Handles[0].Status = 0; afd_poll_info->Handles[0].Events = 0; if (handle->events & UV_READABLE) { afd_poll_info->Handles[0].Events |= AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT; } if (handle->events & UV_WRITABLE) { afd_poll_info->Handles[0].Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL; } memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped); result = uv_msafd_poll((SOCKET) handle->peer_socket, afd_poll_info, afd_poll_info, &req->u.io.overlapped); if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) { /* Queue this req, reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, req); } } static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) { AFD_POLL_INFO afd_poll_info; DWORD result; afd_poll_info.Exclusive = TRUE; afd_poll_info.NumberOfHandles = 1; afd_poll_info.Timeout.QuadPart = INT64_MAX; afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; afd_poll_info.Handles[0].Status = 0; afd_poll_info.Handles[0].Events = AFD_POLL_ALL; result = uv_msafd_poll(handle->socket, &afd_poll_info, uv__get_afd_poll_info_dummy(), uv__get_overlapped_dummy()); if (result == SOCKET_ERROR) { DWORD error = WSAGetLastError(); if (error != WSA_IO_PENDING) return error; } return 0; } static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { unsigned char mask_events; AFD_POLL_INFO* afd_poll_info; if (req == &handle->poll_req_1) { afd_poll_info = &handle->afd_poll_info_1; handle->submitted_events_1 = 0; mask_events = handle->mask_events_1; } else if (req == &handle->poll_req_2) { afd_poll_info = &handle->afd_poll_info_2; handle->submitted_events_2 = 0; mask_events = handle->mask_events_2; } else { assert(0); return; } /* Report an error unless the select was just interrupted. */ if (!REQ_SUCCESS(req)) { DWORD error = GET_REQ_SOCK_ERROR(req); if (error != WSAEINTR && handle->events != 0) { handle->events = 0; /* Stop the watcher */ handle->poll_cb(handle, uv_translate_sys_error(error), 0); } } else if (afd_poll_info->NumberOfHandles >= 1) { unsigned char events = 0; if ((afd_poll_info->Handles[0].Events & (AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT)) != 0) { events |= UV_READABLE; } if ((afd_poll_info->Handles[0].Events & (AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL)) != 0) { events |= UV_WRITABLE; } events &= handle->events & ~mask_events; if (afd_poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) { /* Stop polling. */ handle->events = 0; if (uv__is_active(handle)) uv__handle_stop(handle); } if (events != 0) { handle->poll_cb(handle, 0, events); } } if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__fast_poll_submit_poll_req(loop, handle); } else if ((handle->flags & UV__HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); } } static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { assert(handle->type == UV_POLL); assert(!(handle->flags & UV__HANDLE_CLOSING)); assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); handle->events = events; if (handle->events != 0) { uv__handle_start(handle); } else { uv__handle_stop(handle); } if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__fast_poll_submit_poll_req(handle->loop, handle); } return 0; } static int uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) { handle->events = 0; uv__handle_closing(handle); if (handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); return 0; } else { /* Cancel outstanding poll requests by executing another, unique poll */ /* request that forces the outstanding ones to return. */ return uv__fast_poll_cancel_poll_req(loop, handle); } } static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp, WSAPROTOCOL_INFOW* protocol_info) { SOCKET sock = 0; sock = WSASocketW(protocol_info->iAddressFamily, protocol_info->iSocketType, protocol_info->iProtocol, protocol_info, 0, WSA_FLAG_OVERLAPPED); if (sock == INVALID_SOCKET) { return INVALID_SOCKET; } if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) { goto error; }; if (CreateIoCompletionPort((HANDLE) sock, iocp, (ULONG_PTR) sock, 0) == NULL) { goto error; } return sock; error: closesocket(sock); return INVALID_SOCKET; } static SOCKET uv__fast_poll_get_peer_socket(uv_loop_t* loop, WSAPROTOCOL_INFOW* protocol_info) { int index, i; SOCKET peer_socket; index = -1; for (i = 0; (size_t) i < ARRAY_SIZE(uv_msafd_provider_ids); i++) { if (memcmp((void*) &protocol_info->ProviderId, (void*) &uv_msafd_provider_ids[i], sizeof protocol_info->ProviderId) == 0) { index = i; } } /* Check if the protocol uses an msafd socket. */ if (index < 0) { return INVALID_SOCKET; } /* If we didn't (try) to create a peer socket yet, try to make one. Don't */ /* try again if the peer socket creation failed earlier for the same */ /* protocol. */ peer_socket = loop->poll_peer_sockets[index]; if (peer_socket == 0) { peer_socket = uv__fast_poll_create_peer_socket(loop->iocp, protocol_info); loop->poll_peer_sockets[index] = peer_socket; } return peer_socket; } static DWORD WINAPI uv__slow_poll_thread_proc(void* arg) { uv_req_t* req = (uv_req_t*) arg; uv_poll_t* handle = (uv_poll_t*) req->data; unsigned char reported_events; int r; uv_single_fd_set_t rfds, wfds, efds; struct timeval timeout; assert(handle->type == UV_POLL); assert(req->type == UV_POLL_REQ); if (handle->events & UV_READABLE) { rfds.fd_count = 1; rfds.fd_array[0] = handle->socket; } else { rfds.fd_count = 0; } if (handle->events & UV_WRITABLE) { wfds.fd_count = 1; wfds.fd_array[0] = handle->socket; efds.fd_count = 1; efds.fd_array[0] = handle->socket; } else { wfds.fd_count = 0; efds.fd_count = 0; } /* Make the select() time out after 3 minutes. If select() hangs because */ /* the user closed the socket, we will at least not hang indefinitely. */ timeout.tv_sec = 3 * 60; timeout.tv_usec = 0; r = select(1, (fd_set*) &rfds, (fd_set*) &wfds, (fd_set*) &efds, &timeout); if (r == SOCKET_ERROR) { /* Queue this req, reporting an error. */ SET_REQ_ERROR(&handle->poll_req_1, WSAGetLastError()); POST_COMPLETION_FOR_REQ(handle->loop, req); return 0; } reported_events = 0; if (r > 0) { if (rfds.fd_count > 0) { assert(rfds.fd_count == 1); assert(rfds.fd_array[0] == handle->socket); reported_events |= UV_READABLE; } if (wfds.fd_count > 0) { assert(wfds.fd_count == 1); assert(wfds.fd_array[0] == handle->socket); reported_events |= UV_WRITABLE; } else if (efds.fd_count > 0) { assert(efds.fd_count == 1); assert(efds.fd_array[0] == handle->socket); reported_events |= UV_WRITABLE; } } SET_REQ_SUCCESS(req); req->u.io.overlapped.InternalHigh = (DWORD) reported_events; POST_COMPLETION_FOR_REQ(handle->loop, req); return 0; } static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { uv_req_t* req; /* Find a yet unsubmitted req to submit. */ if (handle->submitted_events_1 == 0) { req = &handle->poll_req_1; handle->submitted_events_1 = handle->events; handle->mask_events_1 = 0; handle->mask_events_2 = handle->events; } else if (handle->submitted_events_2 == 0) { req = &handle->poll_req_2; handle->submitted_events_2 = handle->events; handle->mask_events_1 = handle->events; handle->mask_events_2 = 0; } else { assert(0); return; } if (!QueueUserWorkItem(uv__slow_poll_thread_proc, (void*) req, WT_EXECUTELONGFUNCTION)) { /* Make this req pending, reporting an error. */ SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, req); } } static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { unsigned char mask_events; int err; if (req == &handle->poll_req_1) { handle->submitted_events_1 = 0; mask_events = handle->mask_events_1; } else if (req == &handle->poll_req_2) { handle->submitted_events_2 = 0; mask_events = handle->mask_events_2; } else { assert(0); return; } if (!REQ_SUCCESS(req)) { /* Error. */ if (handle->events != 0) { err = GET_REQ_ERROR(req); handle->events = 0; /* Stop the watcher */ handle->poll_cb(handle, uv_translate_sys_error(err), 0); } } else { /* Got some events. */ int events = req->u.io.overlapped.InternalHigh & handle->events & ~mask_events; if (events != 0) { handle->poll_cb(handle, 0, events); } } if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__slow_poll_submit_poll_req(loop, handle); } else if ((handle->flags & UV__HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); } } static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { assert(handle->type == UV_POLL); assert(!(handle->flags & UV__HANDLE_CLOSING)); assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); handle->events = events; if (handle->events != 0) { uv__handle_start(handle); } else { uv__handle_stop(handle); } if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__slow_poll_submit_poll_req(handle->loop, handle); } return 0; } static int uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) { handle->events = 0; uv__handle_closing(handle); if (handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); } return 0; } int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { return uv_poll_init_socket(loop, handle, (SOCKET) uv__get_osfhandle(fd)); } int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket) { WSAPROTOCOL_INFOW protocol_info; int len; SOCKET peer_socket, base_socket; DWORD bytes; DWORD yes = 1; /* Set the socket to nonblocking mode */ if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) return uv_translate_sys_error(WSAGetLastError()); /* Try to obtain a base handle for the socket. This increases this chances */ /* that we find an AFD handle and are able to use the fast poll mechanism. */ /* This will always fail on windows XP/2k3, since they don't support the */ /* SIO_BASE_HANDLE ioctl. */ #ifndef NDEBUG base_socket = INVALID_SOCKET; #endif if (WSAIoctl(socket, SIO_BASE_HANDLE, NULL, 0, &base_socket, sizeof base_socket, &bytes, NULL, NULL) == 0) { assert(base_socket != 0 && base_socket != INVALID_SOCKET); socket = base_socket; } uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL); handle->socket = socket; handle->events = 0; /* Obtain protocol information about the socket. */ len = sizeof protocol_info; if (getsockopt(socket, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, &len) != 0) { return uv_translate_sys_error(WSAGetLastError()); } /* Get the peer socket that is needed to enable fast poll. If the returned */ /* value is NULL, the protocol is not implemented by MSAFD and we'll have */ /* to use slow mode. */ peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info); if (peer_socket != INVALID_SOCKET) { /* Initialize fast poll specific fields. */ handle->peer_socket = peer_socket; } else { /* Initialize slow poll specific fields. */ handle->flags |= UV_HANDLE_POLL_SLOW; } /* Initialize 2 poll reqs. */ handle->submitted_events_1 = 0; uv_req_init(loop, (uv_req_t*) &(handle->poll_req_1)); handle->poll_req_1.type = UV_POLL_REQ; handle->poll_req_1.data = handle; handle->submitted_events_2 = 0; uv_req_init(loop, (uv_req_t*) &(handle->poll_req_2)); handle->poll_req_2.type = UV_POLL_REQ; handle->poll_req_2.data = handle; return 0; } int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { int err; if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { err = uv__fast_poll_set(handle->loop, handle, events); } else { err = uv__slow_poll_set(handle->loop, handle, events); } if (err) { return uv_translate_sys_error(err); } handle->poll_cb = cb; return 0; } int uv_poll_stop(uv_poll_t* handle) { int err; if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { err = uv__fast_poll_set(handle->loop, handle, 0); } else { err = uv__slow_poll_set(handle->loop, handle, 0); } return uv_translate_sys_error(err); } void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { uv__fast_poll_process_poll_req(loop, handle, req); } else { uv__slow_poll_process_poll_req(loop, handle, req); } } int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { return uv__fast_poll_close(loop, handle); } else { return uv__slow_poll_close(loop, handle); } } void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { assert(handle->flags & UV__HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); assert(handle->submitted_events_1 == 0); assert(handle->submitted_events_2 == 0); uv__handle_close(handle); } libuv-v1.8.0/src/win/process-stdio.c000066400000000000000000000360311263361716600173730ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" /* * The `child_stdio_buffer` buffer has the following layout: * int number_of_fds * unsigned char crt_flags[number_of_fds] * HANDLE os_handle[number_of_fds] */ #define CHILD_STDIO_SIZE(count) \ (sizeof(int) + \ sizeof(unsigned char) * (count) + \ sizeof(uintptr_t) * (count)) #define CHILD_STDIO_COUNT(buffer) \ *((unsigned int*) (buffer)) #define CHILD_STDIO_CRT_FLAGS(buffer, fd) \ *((unsigned char*) (buffer) + sizeof(int) + fd) #define CHILD_STDIO_HANDLE(buffer, fd) \ *((HANDLE*) ((unsigned char*) (buffer) + \ sizeof(int) + \ sizeof(unsigned char) * \ CHILD_STDIO_COUNT((buffer)) + \ sizeof(HANDLE) * (fd))) /* CRT file descriptor mode flags */ #define FOPEN 0x01 #define FEOFLAG 0x02 #define FCRLF 0x04 #define FPIPE 0x08 #define FNOINHERIT 0x10 #define FAPPEND 0x20 #define FDEV 0x40 #define FTEXT 0x80 /* * Clear the HANDLE_FLAG_INHERIT flag from all HANDLEs that were inherited * the parent process. Don't check for errors - the stdio handles may not be * valid, or may be closed already. There is no guarantee that this function * does a perfect job. */ void uv_disable_stdio_inheritance(void) { HANDLE handle; STARTUPINFOW si; /* Make the windows stdio handles non-inheritable. */ handle = GetStdHandle(STD_INPUT_HANDLE); if (handle != NULL && handle != INVALID_HANDLE_VALUE) SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); handle = GetStdHandle(STD_OUTPUT_HANDLE); if (handle != NULL && handle != INVALID_HANDLE_VALUE) SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); handle = GetStdHandle(STD_ERROR_HANDLE); if (handle != NULL && handle != INVALID_HANDLE_VALUE) SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); /* Make inherited CRT FDs non-inheritable. */ GetStartupInfoW(&si); if (uv__stdio_verify(si.lpReserved2, si.cbReserved2)) uv__stdio_noinherit(si.lpReserved2); } static int uv__create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { char pipe_name[64]; SECURITY_ATTRIBUTES sa; DWORD server_access = 0; DWORD client_access = 0; HANDLE child_pipe = INVALID_HANDLE_VALUE; int err; if (flags & UV_READABLE_PIPE) { /* The server needs inbound access too, otherwise CreateNamedPipe() */ /* won't give us the FILE_READ_ATTRIBUTES permission. We need that to */ /* probe the state of the write buffer when we're trying to shutdown */ /* the pipe. */ server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; } if (flags & UV_WRITABLE_PIPE) { server_access |= PIPE_ACCESS_INBOUND; client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; } /* Create server pipe handle. */ err = uv_stdio_pipe_server(loop, server_pipe, server_access, pipe_name, sizeof(pipe_name)); if (err) goto error; /* Create child pipe handle. */ sa.nLength = sizeof sa; sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; child_pipe = CreateFileA(pipe_name, client_access, 0, &sa, OPEN_EXISTING, server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0, NULL); if (child_pipe == INVALID_HANDLE_VALUE) { err = GetLastError(); goto error; } #ifndef NDEBUG /* Validate that the pipe was opened in the right mode. */ { DWORD mode; BOOL r = GetNamedPipeHandleState(child_pipe, &mode, NULL, NULL, NULL, NULL, 0); assert(r == TRUE); assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); } #endif /* Do a blocking ConnectNamedPipe. This should not block because we have */ /* both ends of the pipe created. */ if (!ConnectNamedPipe(server_pipe->handle, NULL)) { if (GetLastError() != ERROR_PIPE_CONNECTED) { err = GetLastError(); goto error; } } /* The server end is now readable and/or writable. */ if (flags & UV_READABLE_PIPE) server_pipe->flags |= UV_HANDLE_WRITABLE; if (flags & UV_WRITABLE_PIPE) server_pipe->flags |= UV_HANDLE_READABLE; *child_pipe_ptr = child_pipe; return 0; error: if (server_pipe->handle != INVALID_HANDLE_VALUE) { uv_pipe_cleanup(loop, server_pipe); } if (child_pipe != INVALID_HANDLE_VALUE) { CloseHandle(child_pipe); } return err; } static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { HANDLE current_process; /* _get_osfhandle will sometimes return -2 in case of an error. This seems */ /* to happen when fd <= 2 and the process' corresponding stdio handle is */ /* set to NULL. Unfortunately DuplicateHandle will happily duplicate */ /* (HANDLE) -2, so this situation goes unnoticed until someone tries to */ /* use the duplicate. Therefore we filter out known-invalid handles here. */ if (handle == INVALID_HANDLE_VALUE || handle == NULL || handle == (HANDLE) -2) { *dup = INVALID_HANDLE_VALUE; return ERROR_INVALID_HANDLE; } current_process = GetCurrentProcess(); if (!DuplicateHandle(current_process, handle, current_process, dup, 0, TRUE, DUPLICATE_SAME_ACCESS)) { *dup = INVALID_HANDLE_VALUE; return GetLastError(); } return 0; } static int uv__duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) { HANDLE handle; if (fd == -1) { *dup = INVALID_HANDLE_VALUE; return ERROR_INVALID_HANDLE; } handle = uv__get_osfhandle(fd); return uv__duplicate_handle(loop, handle, dup); } int uv__create_nul_handle(HANDLE* handle_ptr, DWORD access) { HANDLE handle; SECURITY_ATTRIBUTES sa; sa.nLength = sizeof sa; sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; handle = CreateFileW(L"NUL", access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, NULL); if (handle == INVALID_HANDLE_VALUE) { return GetLastError(); } *handle_ptr = handle; return 0; } int uv__stdio_create(uv_loop_t* loop, const uv_process_options_t* options, BYTE** buffer_ptr) { BYTE* buffer; int count, i; int err; count = options->stdio_count; if (count < 0 || count > 255) { /* Only support FDs 0-255 */ return ERROR_NOT_SUPPORTED; } else if (count < 3) { /* There should always be at least 3 stdio handles. */ count = 3; } /* Allocate the child stdio buffer */ buffer = (BYTE*) uv__malloc(CHILD_STDIO_SIZE(count)); if (buffer == NULL) { return ERROR_OUTOFMEMORY; } /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */ /* clean up on failure. */ CHILD_STDIO_COUNT(buffer) = count; for (i = 0; i < count; i++) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; } for (i = 0; i < count; i++) { uv_stdio_container_t fdopt; if (i < options->stdio_count) { fdopt = options->stdio[i]; } else { fdopt.flags = UV_IGNORE; } switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM)) { case UV_IGNORE: /* Starting a process with no stdin/stout/stderr can confuse it. */ /* So no matter what the user specified, we make sure the first */ /* three FDs are always open in their typical modes, e.g. stdin */ /* be readable and stdout/err should be writable. For FDs > 2, don't */ /* do anything - all handles in the stdio buffer are initialized with */ /* INVALID_HANDLE_VALUE, which should be okay. */ if (i <= 2) { DWORD access = (i == 0) ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i), access); if (err) goto error; CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; } break; case UV_CREATE_PIPE: { /* Create a pair of two connected pipe ends; one end is turned into */ /* an uv_pipe_t for use by the parent. The other one is given to */ /* the child. */ uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream; HANDLE child_pipe = INVALID_HANDLE_VALUE; /* Create a new, connected pipe pair. stdio[i].stream should point */ /* to an uninitialized, but not connected pipe handle. */ assert(fdopt.data.stream->type == UV_NAMED_PIPE); assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION)); assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER)); err = uv__create_stdio_pipe_pair(loop, parent_pipe, &child_pipe, fdopt.flags); if (err) goto error; CHILD_STDIO_HANDLE(buffer, i) = child_pipe; CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; break; } case UV_INHERIT_FD: { /* Inherit a raw FD. */ HANDLE child_handle; /* Make an inheritable duplicate of the handle. */ err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle); if (err) { /* If fdopt.data.fd is not valid and fd fd <= 2, then ignore the */ /* error. */ if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; break; } goto error; } /* Figure out what the type is. */ switch (GetFileType(child_handle)) { case FILE_TYPE_DISK: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN; break; case FILE_TYPE_PIPE: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; case FILE_TYPE_CHAR: case FILE_TYPE_REMOTE: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; break; case FILE_TYPE_UNKNOWN: if (GetLastError() != 0) { err = GetLastError(); CloseHandle(child_handle); goto error; } CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; break; default: assert(0); return -1; } CHILD_STDIO_HANDLE(buffer, i) = child_handle; break; } case UV_INHERIT_STREAM: { /* Use an existing stream as the stdio handle for the child. */ HANDLE stream_handle, child_handle; unsigned char crt_flags; uv_stream_t* stream = fdopt.data.stream; /* Leech the handle out of the stream. */ if (stream->type == UV_TTY) { stream_handle = ((uv_tty_t*) stream)->handle; crt_flags = FOPEN | FDEV; } else if (stream->type == UV_NAMED_PIPE && stream->flags & UV_HANDLE_CONNECTION) { stream_handle = ((uv_pipe_t*) stream)->handle; crt_flags = FOPEN | FPIPE; } else { stream_handle = INVALID_HANDLE_VALUE; crt_flags = 0; } if (stream_handle == NULL || stream_handle == INVALID_HANDLE_VALUE) { /* The handle is already closed, or not yet created, or the */ /* stream type is not supported. */ err = ERROR_NOT_SUPPORTED; goto error; } /* Make an inheritable copy of the handle. */ err = uv__duplicate_handle(loop, stream_handle, &child_handle); if (err) goto error; CHILD_STDIO_HANDLE(buffer, i) = child_handle; CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags; break; } default: assert(0); return -1; } } *buffer_ptr = buffer; return 0; error: uv__stdio_destroy(buffer); return err; } void uv__stdio_destroy(BYTE* buffer) { int i, count; count = CHILD_STDIO_COUNT(buffer); for (i = 0; i < count; i++) { HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } } uv__free(buffer); } void uv__stdio_noinherit(BYTE* buffer) { int i, count; count = CHILD_STDIO_COUNT(buffer); for (i = 0; i < count; i++) { HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); if (handle != INVALID_HANDLE_VALUE) { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); } } } int uv__stdio_verify(BYTE* buffer, WORD size) { unsigned int count; /* Check the buffer pointer. */ if (buffer == NULL) return 0; /* Verify that the buffer is at least big enough to hold the count. */ if (size < CHILD_STDIO_SIZE(0)) return 0; /* Verify if the count is within range. */ count = CHILD_STDIO_COUNT(buffer); if (count > 256) return 0; /* Verify that the buffer size is big enough to hold info for N FDs. */ if (size < CHILD_STDIO_SIZE(count)) return 0; return 1; } WORD uv__stdio_size(BYTE* buffer) { return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer))); } HANDLE uv__stdio_handle(BYTE* buffer, int fd) { return CHILD_STDIO_HANDLE(buffer, fd); } libuv-v1.8.0/src/win/process.c000066400000000000000000001041621263361716600162540ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include /* alloca */ #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" #define SIGKILL 9 typedef struct env_var { const WCHAR* const wide; const WCHAR* const wide_eq; const size_t len; /* including null or '=' */ } env_var_t; #define E_V(str) { L##str, L##str L"=", sizeof(str) } static const env_var_t required_vars[] = { /* keep me sorted */ E_V("HOMEDRIVE"), E_V("HOMEPATH"), E_V("LOGONSERVER"), E_V("PATH"), E_V("SYSTEMDRIVE"), E_V("SYSTEMROOT"), E_V("TEMP"), E_V("USERDOMAIN"), E_V("USERNAME"), E_V("USERPROFILE"), E_V("WINDIR"), }; static size_t n_required_vars = ARRAY_SIZE(required_vars); static HANDLE uv_global_job_handle_; static uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT; static void uv__init_global_job_handle(void) { /* Create a job object and set it up to kill all contained processes when * it's closed. Since this handle is made non-inheritable and we're not * giving it to anyone, we're the only process holding a reference to it. * That means that if this process exits it is closed and all the processes * it contains are killed. All processes created with uv_spawn that are not * spawned with the UV_PROCESS_DETACHED flag are assigned to this job. * * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the * processes that we explicitly add are affected, and *their* subprocesses * are not. This ensures that our child processes are not limited in their * ability to use job control on Windows versions that don't deal with * nested jobs (prior to Windows 8 / Server 2012). It also lets our child * processes created detached processes without explicitly breaking away * from job control (which uv_spawn doesn't, either). */ SECURITY_ATTRIBUTES attr; JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; memset(&attr, 0, sizeof attr); attr.bInheritHandle = FALSE; memset(&info, 0, sizeof info); info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; uv_global_job_handle_ = CreateJobObjectW(&attr, NULL); if (uv_global_job_handle_ == NULL) uv_fatal_error(GetLastError(), "CreateJobObjectW"); if (!SetInformationJobObject(uv_global_job_handle_, JobObjectExtendedLimitInformation, &info, sizeof info)) uv_fatal_error(GetLastError(), "SetInformationJobObject"); } static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { int ws_len, r; WCHAR* ws; ws_len = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0); if (ws_len <= 0) { return GetLastError(); } ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR)); if (ws == NULL) { return ERROR_OUTOFMEMORY; } r = MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, ws_len); assert(r == ws_len); *ws_ptr = ws; return 0; } static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); handle->exit_cb = NULL; handle->pid = 0; handle->exit_signal = 0; handle->wait_handle = INVALID_HANDLE_VALUE; handle->process_handle = INVALID_HANDLE_VALUE; handle->child_stdio_buffer = NULL; handle->exit_cb_pending = 0; uv_req_init(loop, (uv_req_t*)&handle->exit_req); handle->exit_req.type = UV_PROCESS_EXIT; handle->exit_req.data = handle; } /* * Path search functions */ /* * Helper function for search_path */ static WCHAR* search_path_join_test(const WCHAR* dir, size_t dir_len, const WCHAR* name, size_t name_len, const WCHAR* ext, size_t ext_len, const WCHAR* cwd, size_t cwd_len) { WCHAR *result, *result_pos; DWORD attrs; if (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') { /* It's a UNC path so ignore cwd */ cwd_len = 0; } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) { /* It's a full path without drive letter, use cwd's drive letter only */ cwd_len = 2; } else if (dir_len >= 2 && dir[1] == L':' && (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) { /* It's a relative path with drive letter (ext.g. D:../some/file) * Replace drive letter in dir by full cwd if it points to the same drive, * otherwise use the dir only. */ if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) { cwd_len = 0; } else { dir += 2; dir_len -= 2; } } else if (dir_len > 2 && dir[1] == L':') { /* It's an absolute path with drive letter * Don't use the cwd at all */ cwd_len = 0; } /* Allocate buffer for output */ result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) * (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1)); /* Copy cwd */ wcsncpy(result_pos, cwd, cwd_len); result_pos += cwd_len; /* Add a path separator if cwd didn't end with one */ if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { result_pos[0] = L'\\'; result_pos++; } /* Copy dir */ wcsncpy(result_pos, dir, dir_len); result_pos += dir_len; /* Add a separator if the dir didn't end with one */ if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { result_pos[0] = L'\\'; result_pos++; } /* Copy filename */ wcsncpy(result_pos, name, name_len); result_pos += name_len; if (ext_len) { /* Add a dot if the filename didn't end with one */ if (name_len && result_pos[-1] != '.') { result_pos[0] = L'.'; result_pos++; } /* Copy extension */ wcsncpy(result_pos, ext, ext_len); result_pos += ext_len; } /* Null terminator */ result_pos[0] = L'\0'; attrs = GetFileAttributesW(result); if (attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { return result; } uv__free(result); return NULL; } /* * Helper function for search_path */ static WCHAR* path_search_walk_ext(const WCHAR *dir, size_t dir_len, const WCHAR *name, size_t name_len, WCHAR *cwd, size_t cwd_len, int name_has_ext) { WCHAR* result; /* If the name itself has a nonempty extension, try this extension first */ if (name_has_ext) { result = search_path_join_test(dir, dir_len, name, name_len, L"", 0, cwd, cwd_len); if (result != NULL) { return result; } } /* Try .com extension */ result = search_path_join_test(dir, dir_len, name, name_len, L"com", 3, cwd, cwd_len); if (result != NULL) { return result; } /* Try .exe extension */ result = search_path_join_test(dir, dir_len, name, name_len, L"exe", 3, cwd, cwd_len); if (result != NULL) { return result; } return NULL; } /* * search_path searches the system path for an executable filename - * the windows API doesn't provide this as a standalone function nor as an * option to CreateProcess. * * It tries to return an absolute filename. * * Furthermore, it tries to follow the semantics that cmd.exe, with this * exception that PATHEXT environment variable isn't used. Since CreateProcess * can start only .com and .exe files, only those extensions are tried. This * behavior equals that of msvcrt's spawn functions. * * - Do not search the path if the filename already contains a path (either * relative or absolute). * * - If there's really only a filename, check the current directory for file, * then search all path directories. * * - If filename specified has *any* extension, search for the file with the * specified extension first. * * - If the literal filename is not found in a directory, try *appending* * (not replacing) .com first and then .exe. * * - The path variable may contain relative paths; relative paths are relative * to the cwd. * * - Directories in path may or may not end with a trailing backslash. * * - CMD does not trim leading/trailing whitespace from path/pathex entries * nor from the environment variables as a whole. * * - When cmd.exe cannot read a directory, it will just skip it and go on * searching. However, unlike posix-y systems, it will happily try to run a * file that is not readable/executable; if the spawn fails it will not * continue searching. * * UNC path support: we are dealing with UNC paths in both the path and the * filename. This is a deviation from what cmd.exe does (it does not let you * start a program by specifying an UNC path on the command line) but this is * really a pointless restriction. * */ static WCHAR* search_path(const WCHAR *file, WCHAR *cwd, const WCHAR *path) { int file_has_dir; WCHAR* result = NULL; WCHAR *file_name_start; WCHAR *dot; const WCHAR *dir_start, *dir_end, *dir_path; size_t dir_len; int name_has_ext; size_t file_len = wcslen(file); size_t cwd_len = wcslen(cwd); /* If the caller supplies an empty filename, * we're not gonna return c:\windows\.exe -- GFY! */ if (file_len == 0 || (file_len == 1 && file[0] == L'.')) { return NULL; } /* Find the start of the filename so we can split the directory from the */ /* name. */ for (file_name_start = (WCHAR*)file + file_len; file_name_start > file && file_name_start[-1] != L'\\' && file_name_start[-1] != L'/' && file_name_start[-1] != L':'; file_name_start--); file_has_dir = file_name_start != file; /* Check if the filename includes an extension */ dot = wcschr(file_name_start, L'.'); name_has_ext = (dot != NULL && dot[1] != L'\0'); if (file_has_dir) { /* The file has a path inside, don't use path */ result = path_search_walk_ext( file, file_name_start - file, file_name_start, file_len - (file_name_start - file), cwd, cwd_len, name_has_ext); } else { dir_end = path; /* The file is really only a name; look in cwd first, then scan path */ result = path_search_walk_ext(L"", 0, file, file_len, cwd, cwd_len, name_has_ext); while (result == NULL) { if (*dir_end == L'\0') { break; } /* Skip the separator that dir_end now points to */ if (dir_end != path || *path == L';') { dir_end++; } /* Next slice starts just after where the previous one ended */ dir_start = dir_end; /* Slice until the next ; or \0 is found */ dir_end = wcschr(dir_start, L';'); if (dir_end == NULL) { dir_end = wcschr(dir_start, L'\0'); } /* If the slice is zero-length, don't bother */ if (dir_end - dir_start == 0) { continue; } dir_path = dir_start; dir_len = dir_end - dir_start; /* Adjust if the path is quoted. */ if (dir_path[0] == '"' || dir_path[0] == '\'') { ++dir_path; --dir_len; } if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') { --dir_len; } result = path_search_walk_ext(dir_path, dir_len, file, file_len, cwd, cwd_len, name_has_ext); } } return result; } /* * Quotes command line arguments * Returns a pointer to the end (next char to be written) of the buffer */ WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) { size_t len = wcslen(source); size_t i; int quote_hit; WCHAR* start; if (len == 0) { /* Need double quotation for empty argument */ *(target++) = L'"'; *(target++) = L'"'; return target; } if (NULL == wcspbrk(source, L" \t\"")) { /* No quotation needed */ wcsncpy(target, source, len); target += len; return target; } if (NULL == wcspbrk(source, L"\"\\")) { /* * No embedded double quotes or backlashes, so I can just wrap * quote marks around the whole thing. */ *(target++) = L'"'; wcsncpy(target, source, len); target += len; *(target++) = L'"'; return target; } /* * Expected input/output: * input : hello"world * output: "hello\"world" * input : hello""world * output: "hello\"\"world" * input : hello\world * output: hello\world * input : hello\\world * output: hello\\world * input : hello\"world * output: "hello\\\"world" * input : hello\\"world * output: "hello\\\\\"world" * input : hello world\ * output: "hello world\" */ *(target++) = L'"'; start = target; quote_hit = 1; for (i = len; i > 0; --i) { *(target++) = source[i - 1]; if (quote_hit && source[i - 1] == L'\\') { *(target++) = L'\\'; } else if(source[i - 1] == L'"') { quote_hit = 1; *(target++) = L'\\'; } else { quote_hit = 0; } } target[0] = L'\0'; wcsrev(start); *(target++) = L'"'; return target; } int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { char** arg; WCHAR* dst = NULL; WCHAR* temp_buffer = NULL; size_t dst_len = 0; size_t temp_buffer_len = 0; WCHAR* pos; int arg_count = 0; int err = 0; /* Count the required size. */ for (arg = args; *arg; arg++) { DWORD arg_len; arg_len = MultiByteToWideChar(CP_UTF8, 0, *arg, -1, NULL, 0); if (arg_len == 0) { return GetLastError(); } dst_len += arg_len; if (arg_len > temp_buffer_len) temp_buffer_len = arg_len; arg_count++; } /* Adjust for potential quotes. Also assume the worst-case scenario */ /* that every character needs escaping, so we need twice as much space. */ dst_len = dst_len * 2 + arg_count * 2; /* Allocate buffer for the final command line. */ dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR)); if (dst == NULL) { err = ERROR_OUTOFMEMORY; goto error; } /* Allocate temporary working buffer. */ temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR)); if (temp_buffer == NULL) { err = ERROR_OUTOFMEMORY; goto error; } pos = dst; for (arg = args; *arg; arg++) { DWORD arg_len; /* Convert argument to wide char. */ arg_len = MultiByteToWideChar(CP_UTF8, 0, *arg, -1, temp_buffer, (int) (dst + dst_len - pos)); if (arg_len == 0) { err = GetLastError(); goto error; } if (verbatim_arguments) { /* Copy verbatim. */ wcscpy(pos, temp_buffer); pos += arg_len - 1; } else { /* Quote/escape, if needed. */ pos = quote_cmd_arg(temp_buffer, pos); } *pos++ = *(arg + 1) ? L' ' : L'\0'; } uv__free(temp_buffer); *dst_ptr = dst; return 0; error: uv__free(dst); uv__free(temp_buffer); return err; } int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { wchar_t* a_eq; wchar_t* b_eq; wchar_t* A; wchar_t* B; int nb; int r; if (na < 0) { a_eq = wcschr(a, L'='); assert(a_eq); na = (int)(long)(a_eq - a); } else { na--; } b_eq = wcschr(b, L'='); assert(b_eq); nb = b_eq - b; A = alloca((na+1) * sizeof(wchar_t)); B = alloca((nb+1) * sizeof(wchar_t)); r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na); assert(r==na); A[na] = L'\0'; r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb); assert(r==nb); B[nb] = L'\0'; while (1) { wchar_t AA = *A++; wchar_t BB = *B++; if (AA < BB) { return -1; } else if (AA > BB) { return 1; } else if (!AA && !BB) { return 0; } } } static int qsort_wcscmp(const void *a, const void *b) { wchar_t* astr = *(wchar_t* const*)a; wchar_t* bstr = *(wchar_t* const*)b; return env_strncmp(astr, -1, bstr); } /* * The way windows takes environment variables is different than what C does; * Windows wants a contiguous block of null-terminated strings, terminated * with an additional null. * * Windows has a few "essential" environment variables. winsock will fail * to initialize if SYSTEMROOT is not defined; some APIs make reference to * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that * these get defined if the input environment block does not contain any * values for them. * * Also add variables known to Cygwin to be required for correct * subprocess operation in many cases: * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955 * */ int make_program_env(char* env_block[], WCHAR** dst_ptr) { WCHAR* dst; WCHAR* ptr; char** env; size_t env_len = 0; int len; size_t i; DWORD var_size; size_t env_block_count = 1; /* 1 for null-terminator */ WCHAR* dst_copy; WCHAR** ptr_copy; WCHAR** env_copy; DWORD* required_vars_value_len = alloca(n_required_vars * sizeof(DWORD*)); /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { int len; if (strchr(*env, '=')) { len = MultiByteToWideChar(CP_UTF8, 0, *env, -1, NULL, 0); if (len <= 0) { return GetLastError(); } env_len += len; env_block_count++; } } /* second pass: copy to UTF-16 environment block */ dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR)); if (!dst_copy) { return ERROR_OUTOFMEMORY; } env_copy = alloca(env_block_count * sizeof(WCHAR*)); ptr = dst_copy; ptr_copy = env_copy; for (env = env_block; *env; env++) { if (strchr(*env, '=')) { len = MultiByteToWideChar(CP_UTF8, 0, *env, -1, ptr, (int) (env_len - (ptr - dst_copy))); if (len <= 0) { DWORD err = GetLastError(); uv__free(dst_copy); return err; } *ptr_copy++ = ptr; ptr += len; } } *ptr_copy = NULL; assert(env_len == ptr - dst_copy); /* sort our (UTF-16) copy */ qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); /* third pass: check for required variables */ for (ptr_copy = env_copy, i = 0; i < n_required_vars; ) { int cmp; if (!*ptr_copy) { cmp = -1; } else { cmp = env_strncmp(required_vars[i].wide_eq, required_vars[i].len, *ptr_copy); } if (cmp < 0) { /* missing required var */ var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); required_vars_value_len[i] = var_size; if (var_size != 0) { env_len += required_vars[i].len; env_len += var_size; } i++; } else { ptr_copy++; if (cmp == 0) i++; } } /* final pass: copy, in sort order, and inserting required variables */ dst = uv__malloc((1+env_len) * sizeof(WCHAR)); if (!dst) { uv__free(dst_copy); return ERROR_OUTOFMEMORY; } for (ptr = dst, ptr_copy = env_copy, i = 0; *ptr_copy || i < n_required_vars; ptr += len) { int cmp; if (i >= n_required_vars) { cmp = 1; } else if (!*ptr_copy) { cmp = -1; } else { cmp = env_strncmp(required_vars[i].wide_eq, required_vars[i].len, *ptr_copy); } if (cmp < 0) { /* missing required var */ len = required_vars_value_len[i]; if (len) { wcscpy(ptr, required_vars[i].wide_eq); ptr += required_vars[i].len; var_size = GetEnvironmentVariableW(required_vars[i].wide, ptr, (int) (env_len - (ptr - dst))); if (var_size != len-1) { /* race condition? */ uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); } } i++; } else { /* copy var from env_block */ len = wcslen(*ptr_copy) + 1; wmemcpy(ptr, *ptr_copy, len); ptr_copy++; if (cmp == 0) i++; } } /* Terminate with an extra NULL. */ assert(env_len == (ptr - dst)); *ptr = L'\0'; uv__free(dst_copy); *dst_ptr = dst; return 0; } /* * Attempt to find the value of the PATH environment variable in the child's * preprocessed environment. * * If found, a pointer into `env` is returned. If not found, NULL is returned. */ static WCHAR* find_path(WCHAR *env) { for (; env != NULL && *env != 0; env += wcslen(env) + 1) { if (wcsncmp(env, L"PATH=", 5) == 0) return &env[5]; } return NULL; } /* * Called on Windows thread-pool thread to indicate that * a child process has exited. */ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { uv_process_t* process = (uv_process_t*) data; uv_loop_t* loop = process->loop; assert(didTimeout == FALSE); assert(process); assert(!process->exit_cb_pending); process->exit_cb_pending = 1; /* Post completed */ POST_COMPLETION_FOR_REQ(loop, &process->exit_req); } /* Called on main thread after a child process has exited. */ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { int64_t exit_code; DWORD status; assert(handle->exit_cb_pending); handle->exit_cb_pending = 0; /* If we're closing, don't call the exit callback. Just schedule a close */ /* callback now. */ if (handle->flags & UV__HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); return; } /* Unregister from process notification. */ if (handle->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(handle->wait_handle); handle->wait_handle = INVALID_HANDLE_VALUE; } /* Set the handle to inactive: no callbacks will be made after the exit */ /* callback.*/ uv__handle_stop(handle); if (GetExitCodeProcess(handle->process_handle, &status)) { exit_code = status; } else { /* Unable to to obtain the exit code. This should never happen. */ exit_code = uv_translate_sys_error(GetLastError()); } /* Fire the exit callback. */ if (handle->exit_cb) { handle->exit_cb(handle, exit_code, handle->exit_signal); } } void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { uv__handle_closing(handle); if (handle->wait_handle != INVALID_HANDLE_VALUE) { /* This blocks until either the wait was cancelled, or the callback has */ /* completed. */ BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE); if (!r) { /* This should never happen, and if it happens, we can't recover... */ uv_fatal_error(GetLastError(), "UnregisterWaitEx"); } handle->wait_handle = INVALID_HANDLE_VALUE; } if (!handle->exit_cb_pending) { uv_want_endgame(loop, (uv_handle_t*)handle); } } void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { assert(!handle->exit_cb_pending); assert(handle->flags & UV__HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); /* Clean-up the process handle. */ CloseHandle(handle->process_handle); uv__handle_close(handle); } int uv_spawn(uv_loop_t* loop, uv_process_t* process, const uv_process_options_t* options) { int i; int err = 0; WCHAR* path = NULL, *alloc_path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; STARTUPINFOW startup; PROCESS_INFORMATION info; DWORD process_flags; uv_process_init(loop, process); process->exit_cb = options->exit_cb; if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { return UV_ENOTSUP; } if (options->file == NULL || options->args == NULL) { return UV_EINVAL; } assert(options->file != NULL); assert(!(options->flags & ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); err = uv_utf8_to_utf16_alloc(options->file, &application); if (err) goto done; err = make_program_args( options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err) goto done; if (options->env) { err = make_program_env(options->env, &env); if (err) goto done; } if (options->cwd) { /* Explicit cwd */ err = uv_utf8_to_utf16_alloc(options->cwd, &cwd); if (err) goto done; } else { /* Inherit cwd */ DWORD cwd_len, r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { err = GetLastError(); goto done; } cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR)); if (cwd == NULL) { err = ERROR_OUTOFMEMORY; goto done; } r = GetCurrentDirectoryW(cwd_len, cwd); if (r == 0 || r >= cwd_len) { err = GetLastError(); goto done; } } /* Get PATH environment variable. */ path = find_path(env); if (path == NULL) { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); if (path_len == 0) { err = GetLastError(); goto done; } alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); if (alloc_path == NULL) { err = ERROR_OUTOFMEMORY; goto done; } path = alloc_path; r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { err = GetLastError(); goto done; } } err = uv__stdio_create(loop, options, &process->child_stdio_buffer); if (err) goto done; application_path = search_path(application, cwd, path); if (application_path == NULL) { /* Not found. */ err = ERROR_FILE_NOT_FOUND; goto done; } startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); if (options->flags & UV_PROCESS_WINDOWS_HIDE) { /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; } else { startup.wShowWindow = SW_SHOWDEFAULT; } process_flags = CREATE_UNICODE_ENVIRONMENT; if (options->flags & UV_PROCESS_DETACHED) { /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That * means that libuv might not let you create a fully daemonized process * when run under job control. However the type of job control that libuv * itself creates doesn't trickle down to subprocesses so they can still * daemonize. * * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the * CreateProcess call fail if we're under job control that doesn't allow * breakaway. */ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; } if (!CreateProcessW(application_path, arguments, NULL, NULL, 1, process_flags, env, cwd, &startup, &info)) { /* CreateProcessW failed. */ err = GetLastError(); goto done; } /* Spawn succeeded */ /* Beyond this point, failure is reported asynchronously. */ process->process_handle = info.hProcess; process->pid = info.dwProcessId; /* If the process isn't spawned as detached, assign to the global job */ /* object so windows will kill it when the parent process dies. */ if (!(options->flags & UV_PROCESS_DETACHED)) { uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { /* AssignProcessToJobObject might fail if this process is under job * control and the job doesn't have the * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version * that doesn't support nested jobs. * * When that happens we just swallow the error and continue without * establishing a kill-child-on-parent-exit relationship, otherwise * there would be no way for libuv applications run under job control * to spawn processes at all. */ DWORD err = GetLastError(); if (err != ERROR_ACCESS_DENIED) uv_fatal_error(err, "AssignProcessToJobObject"); } } /* Set IPC pid to all IPC pipes. */ for (i = 0; i < options->stdio_count; i++) { const uv_stdio_container_t* fdopt = &options->stdio[i]; if (fdopt->flags & UV_CREATE_PIPE && fdopt->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) fdopt->data.stream)->ipc) { ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_pid = info.dwProcessId; } } /* Setup notifications for when the child process exits. */ result = RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, exit_wait_callback, (void*)process, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); if (!result) { uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); } CloseHandle(info.hThread); assert(!err); /* Make the handle active. It will remain active until the exit callback */ /* is made or the handle is closed, whichever happens first. */ uv__handle_start(process); /* Cleanup, whether we succeeded or failed. */ done: uv__free(application); uv__free(application_path); uv__free(arguments); uv__free(cwd); uv__free(env); uv__free(alloc_path); if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } return uv_translate_sys_error(err); } static int uv__kill(HANDLE process_handle, int signum) { switch (signum) { case SIGTERM: case SIGKILL: case SIGINT: { /* Unconditionally terminate the process. On Windows, killed processes */ /* normally return 1. */ DWORD status; int err; if (TerminateProcess(process_handle, 1)) return 0; /* If the process already exited before TerminateProcess was called, */ /* TerminateProcess will fail with ERROR_ACCESS_DENIED. */ err = GetLastError(); if (err == ERROR_ACCESS_DENIED && GetExitCodeProcess(process_handle, &status) && status != STILL_ACTIVE) { return UV_ESRCH; } return uv_translate_sys_error(err); } case 0: { /* Health check: is the process still alive? */ DWORD status; if (!GetExitCodeProcess(process_handle, &status)) return uv_translate_sys_error(GetLastError()); if (status != STILL_ACTIVE) return UV_ESRCH; return 0; } default: /* Unsupported signal. */ return UV_ENOSYS; } } int uv_process_kill(uv_process_t* process, int signum) { int err; if (process->process_handle == INVALID_HANDLE_VALUE) { return UV_EINVAL; } err = uv__kill(process->process_handle, signum); if (err) { return err; /* err is already translated. */ } process->exit_signal = signum; return 0; } int uv_kill(int pid, int signum) { int err; HANDLE process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, pid); if (process_handle == NULL) { err = GetLastError(); if (err == ERROR_INVALID_PARAMETER) { return UV_ESRCH; } else { return uv_translate_sys_error(err); } } err = uv__kill(process_handle, signum); CloseHandle(process_handle); return err; /* err is already translated. */ } libuv-v1.8.0/src/win/req-inl.h000066400000000000000000000202451263361716600161510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_REQ_INL_H_ #define UV_WIN_REQ_INL_H_ #include #include "uv.h" #include "internal.h" #define SET_REQ_STATUS(req, status) \ (req)->u.io.overlapped.Internal = (ULONG_PTR) (status) #define SET_REQ_ERROR(req, error) \ SET_REQ_STATUS((req), NTSTATUS_FROM_WIN32((error))) #define SET_REQ_SUCCESS(req) \ SET_REQ_STATUS((req), STATUS_SUCCESS) #define GET_REQ_STATUS(req) \ ((NTSTATUS) (req)->u.io.overlapped.Internal) #define REQ_SUCCESS(req) \ (NT_SUCCESS(GET_REQ_STATUS((req)))) #define GET_REQ_ERROR(req) \ (pRtlNtStatusToDosError(GET_REQ_STATUS((req)))) #define GET_REQ_SOCK_ERROR(req) \ (uv_ntstatus_to_winsock_error(GET_REQ_STATUS((req)))) #define REGISTER_HANDLE_REQ(loop, handle, req) \ do { \ INCREASE_ACTIVE_COUNT((loop), (handle)); \ uv__req_register((loop), (req)); \ } while (0) #define UNREGISTER_HANDLE_REQ(loop, handle, req) \ do { \ DECREASE_ACTIVE_COUNT((loop), (handle)); \ uv__req_unregister((loop), (req)); \ } while (0) #define UV_SUCCEEDED_WITHOUT_IOCP(result) \ ((result) && (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP)) #define UV_SUCCEEDED_WITH_IOCP(result) \ ((result) || (GetLastError() == ERROR_IO_PENDING)) #define POST_COMPLETION_FOR_REQ(loop, req) \ if (!PostQueuedCompletionStatus((loop)->iocp, \ 0, \ 0, \ &((req)->u.io.overlapped))) { \ uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); \ } INLINE static void uv_req_init(uv_loop_t* loop, uv_req_t* req) { req->type = UV_UNKNOWN_REQ; SET_REQ_SUCCESS(req); } INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped); } INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) { req->next_req = NULL; if (loop->pending_reqs_tail) { #ifdef _DEBUG /* Ensure the request is not already in the queue, or the queue * will get corrupted. */ uv_req_t* current = loop->pending_reqs_tail; do { assert(req != current); current = current->next_req; } while(current != loop->pending_reqs_tail); #endif req->next_req = loop->pending_reqs_tail->next_req; loop->pending_reqs_tail->next_req = req; loop->pending_reqs_tail = req; } else { req->next_req = req; loop->pending_reqs_tail = req; } } #define DELEGATE_STREAM_REQ(loop, req, method, handle_at) \ do { \ switch (((uv_handle_t*) (req)->handle_at)->type) { \ case UV_TCP: \ uv_process_tcp_##method##_req(loop, \ (uv_tcp_t*) ((req)->handle_at), \ req); \ break; \ \ case UV_NAMED_PIPE: \ uv_process_pipe_##method##_req(loop, \ (uv_pipe_t*) ((req)->handle_at), \ req); \ break; \ \ case UV_TTY: \ uv_process_tty_##method##_req(loop, \ (uv_tty_t*) ((req)->handle_at), \ req); \ break; \ \ default: \ assert(0); \ } \ } while (0) INLINE static int uv_process_reqs(uv_loop_t* loop) { uv_req_t* req; uv_req_t* first; uv_req_t* next; if (loop->pending_reqs_tail == NULL) return 0; first = loop->pending_reqs_tail->next_req; next = first; loop->pending_reqs_tail = NULL; while (next != NULL) { req = next; next = req->next_req != first ? req->next_req : NULL; switch (req->type) { case UV_READ: DELEGATE_STREAM_REQ(loop, req, read, data); break; case UV_WRITE: DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle); break; case UV_ACCEPT: DELEGATE_STREAM_REQ(loop, req, accept, data); break; case UV_CONNECT: DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle); break; case UV_SHUTDOWN: /* Tcp shutdown requests don't come here. */ assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE); uv_process_pipe_shutdown_req( loop, (uv_pipe_t*) ((uv_shutdown_t*) req)->handle, (uv_shutdown_t*) req); break; case UV_UDP_RECV: uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req); break; case UV_UDP_SEND: uv_process_udp_send_req(loop, ((uv_udp_send_t*) req)->handle, (uv_udp_send_t*) req); break; case UV_WAKEUP: uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req); break; case UV_SIGNAL_REQ: uv_process_signal_req(loop, (uv_signal_t*) req->data, req); break; case UV_POLL_REQ: uv_process_poll_req(loop, (uv_poll_t*) req->data, req); break; case UV_PROCESS_EXIT: uv_process_proc_exit(loop, (uv_process_t*) req->data); break; case UV_FS_EVENT_REQ: uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); break; default: assert(0); } } return 1; } #endif /* UV_WIN_REQ_INL_H_ */ libuv-v1.8.0/src/win/req.c000066400000000000000000000022771263361716600153710ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "uv.h" #include "internal.h" libuv-v1.8.0/src/win/signal.c000066400000000000000000000227711263361716600160600ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" RB_HEAD(uv_signal_tree_s, uv_signal_s); static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree); static ssize_t volatile uv__signal_control_handler_refs = 0; static CRITICAL_SECTION uv__signal_lock; void uv_signals_init() { InitializeCriticalSection(&uv__signal_lock); } static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { /* Compare signums first so all watchers with the same signnum end up */ /* adjacent. */ if (w1->signum < w2->signum) return -1; if (w1->signum > w2->signum) return 1; /* Sort by loop pointer, so we can easily look up the first item after */ /* { .signum = x, .loop = NULL } */ if ((uintptr_t) w1->loop < (uintptr_t) w2->loop) return -1; if ((uintptr_t) w1->loop > (uintptr_t) w2->loop) return 1; if ((uintptr_t) w1 < (uintptr_t) w2) return -1; if ((uintptr_t) w1 > (uintptr_t) w2) return 1; return 0; } RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare); /* * Dispatches signal {signum} to all active uv_signal_t watchers in all loops. * Returns 1 if the signal was dispatched to any watcher, or 0 if there were * no active signal watchers observing this signal. */ int uv__signal_dispatch(int signum) { uv_signal_t lookup; uv_signal_t* handle; int dispatched = 0; EnterCriticalSection(&uv__signal_lock); lookup.signum = signum; lookup.loop = NULL; for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup); handle != NULL && handle->signum == signum; handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) { unsigned long previous = InterlockedExchange( (volatile LONG*) &handle->pending_signum, signum); if (!previous) { POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req); } dispatched = 1; } LeaveCriticalSection(&uv__signal_lock); return dispatched; } static BOOL WINAPI uv__signal_control_handler(DWORD type) { switch (type) { case CTRL_C_EVENT: return uv__signal_dispatch(SIGINT); case CTRL_BREAK_EVENT: return uv__signal_dispatch(SIGBREAK); case CTRL_CLOSE_EVENT: if (uv__signal_dispatch(SIGHUP)) { /* Windows will terminate the process after the control handler */ /* returns. After that it will just terminate our process. Therefore */ /* block the signal handler so the main loop has some time to pick */ /* up the signal and do something for a few seconds. */ Sleep(INFINITE); return TRUE; } return FALSE; case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: /* These signals are only sent to services. Services have their own */ /* notification mechanism, so there's no point in handling these. */ default: /* We don't handle these. */ return FALSE; } } static int uv__signal_register_control_handler() { /* When this function is called, the uv__signal_lock must be held. */ /* If the console control handler has already been hooked, just add a */ /* reference. */ if (uv__signal_control_handler_refs > 0) { uv__signal_control_handler_refs++; return 0; } if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) return GetLastError(); uv__signal_control_handler_refs++; return 0; } static void uv__signal_unregister_control_handler() { /* When this function is called, the uv__signal_lock must be held. */ BOOL r; /* Don't unregister if the number of console control handlers exceeds one. */ /* Just remove a reference in that case. */ if (uv__signal_control_handler_refs > 1) { uv__signal_control_handler_refs--; return; } assert(uv__signal_control_handler_refs == 1); r = SetConsoleCtrlHandler(uv__signal_control_handler, FALSE); /* This should never fail; if it does it is probably a bug in libuv. */ assert(r); uv__signal_control_handler_refs--; } static int uv__signal_register(int signum) { switch (signum) { case SIGINT: case SIGBREAK: case SIGHUP: return uv__signal_register_control_handler(); case SIGWINCH: /* SIGWINCH is generated in tty.c. No need to register anything. */ return 0; case SIGILL: case SIGABRT_COMPAT: case SIGFPE: case SIGSEGV: case SIGTERM: case SIGABRT: /* Signal is never raised. */ return 0; default: /* Invalid signal. */ return ERROR_INVALID_PARAMETER; } } static void uv__signal_unregister(int signum) { switch (signum) { case SIGINT: case SIGBREAK: case SIGHUP: uv__signal_unregister_control_handler(); return; case SIGWINCH: /* SIGWINCH is generated in tty.c. No need to unregister anything. */ return; case SIGILL: case SIGABRT_COMPAT: case SIGFPE: case SIGSEGV: case SIGTERM: case SIGABRT: /* Nothing is registered for this signal. */ return; default: /* Libuv bug. */ assert(0 && "Invalid signum"); return; } } int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { uv_req_t* req; uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL); handle->pending_signum = 0; handle->signum = 0; handle->signal_cb = NULL; req = &handle->signal_req; uv_req_init(loop, req); req->type = UV_SIGNAL_REQ; req->data = handle; return 0; } int uv_signal_stop(uv_signal_t* handle) { uv_signal_t* removed_handle; /* If the watcher wasn't started, this is a no-op. */ if (handle->signum == 0) return 0; EnterCriticalSection(&uv__signal_lock); uv__signal_unregister(handle->signum); removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle); assert(removed_handle == handle); LeaveCriticalSection(&uv__signal_lock); handle->signum = 0; uv__handle_stop(handle); return 0; } int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { int err; /* If the user supplies signum == 0, then return an error already. If the */ /* signum is otherwise invalid then uv__signal_register will find out */ /* eventually. */ if (signum == 0) { return UV_EINVAL; } /* Short circuit: if the signal watcher is already watching {signum} don't */ /* go through the process of deregistering and registering the handler. */ /* Additionally, this avoids pending signals getting lost in the (small) */ /* time frame that handle->signum == 0. */ if (signum == handle->signum) { handle->signal_cb = signal_cb; return 0; } /* If the signal handler was already active, stop it first. */ if (handle->signum != 0) { int r = uv_signal_stop(handle); /* uv_signal_stop is infallible. */ assert(r == 0); } EnterCriticalSection(&uv__signal_lock); err = uv__signal_register(signum); if (err) { /* Uh-oh, didn't work. */ LeaveCriticalSection(&uv__signal_lock); return uv_translate_sys_error(err); } handle->signum = signum; RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle); LeaveCriticalSection(&uv__signal_lock); handle->signal_cb = signal_cb; uv__handle_start(handle); return 0; } void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, uv_req_t* req) { long dispatched_signum; assert(handle->type == UV_SIGNAL); assert(req->type == UV_SIGNAL_REQ); dispatched_signum = InterlockedExchange( (volatile LONG*) &handle->pending_signum, 0); assert(dispatched_signum != 0); /* Check if the pending signal equals the signum that we are watching for. */ /* These can get out of sync when the handler is stopped and restarted */ /* while the signal_req is pending. */ if (dispatched_signum == handle->signum) handle->signal_cb(handle, dispatched_signum); if (handle->flags & UV__HANDLE_CLOSING) { /* When it is closing, it must be stopped at this point. */ assert(handle->signum == 0); uv_want_endgame(loop, (uv_handle_t*) handle); } } void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) { uv_signal_stop(handle); uv__handle_closing(handle); if (handle->pending_signum == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); } } void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) { assert(handle->flags & UV__HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); assert(handle->signum == 0); assert(handle->pending_signum == 0); handle->flags |= UV_HANDLE_CLOSED; uv__handle_close(handle); } libuv-v1.8.0/src/win/snprintf.c000066400000000000000000000030121263361716600164310ustar00rootroot00000000000000/* Copyright the libuv project contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if defined(_MSC_VER) && _MSC_VER < 1900 #include #include /* Emulate snprintf() on MSVC<2015, _snprintf() doesn't zero-terminate the buffer * on overflow... */ int snprintf(char* buf, size_t len, const char* fmt, ...) { int n; va_list ap; va_start(ap, fmt); n = _vscprintf(fmt, ap); vsnprintf_s(buf, len, _TRUNCATE, fmt, ap); va_end(ap); return n; } #endif libuv-v1.8.0/src/win/stream-inl.h000066400000000000000000000040101263361716600166450ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_STREAM_INL_H_ #define UV_WIN_STREAM_INL_H_ #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" INLINE static void uv_stream_init(uv_loop_t* loop, uv_stream_t* handle, uv_handle_type type) { uv__handle_init(loop, (uv_handle_t*) handle, type); handle->write_queue_size = 0; handle->activecnt = 0; } INLINE static void uv_connection_init(uv_stream_t* handle) { handle->flags |= UV_HANDLE_CONNECTION; handle->stream.conn.write_reqs_pending = 0; uv_req_init(handle->loop, (uv_req_t*) &(handle->read_req)); handle->read_req.event_handle = NULL; handle->read_req.wait_handle = INVALID_HANDLE_VALUE; handle->read_req.type = UV_READ; handle->read_req.data = handle; handle->stream.conn.shutdown_req = NULL; } #endif /* UV_WIN_STREAM_INL_H_ */ libuv-v1.8.0/src/win/stream.c000066400000000000000000000141201263361716600160630ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { int err; err = ERROR_INVALID_PARAMETER; switch (stream->type) { case UV_TCP: err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); break; case UV_NAMED_PIPE: err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); break; default: assert(0); } return uv_translate_sys_error(err); } int uv_accept(uv_stream_t* server, uv_stream_t* client) { int err; err = ERROR_INVALID_PARAMETER; switch (server->type) { case UV_TCP: err = uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); break; case UV_NAMED_PIPE: err = uv_pipe_accept((uv_pipe_t*)server, client); break; default: assert(0); } return uv_translate_sys_error(err); } int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { int err; if (handle->flags & UV_HANDLE_READING) { return UV_EALREADY; } if (!(handle->flags & UV_HANDLE_READABLE)) { return UV_ENOTCONN; } err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_TCP: err = uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); break; case UV_NAMED_PIPE: err = uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); break; case UV_TTY: err = uv_tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb); break; default: assert(0); } return uv_translate_sys_error(err); } int uv_read_stop(uv_stream_t* handle) { int err; if (!(handle->flags & UV_HANDLE_READING)) return 0; err = 0; if (handle->type == UV_TTY) { err = uv_tty_read_stop((uv_tty_t*) handle); } else { if (handle->type == UV_NAMED_PIPE) { uv__pipe_stop_read((uv_pipe_t*) handle); } else { handle->flags &= ~UV_HANDLE_READING; } DECREASE_ACTIVE_COUNT(handle->loop, handle); } return uv_translate_sys_error(err); } int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) { uv_loop_t* loop = handle->loop; int err; if (!(handle->flags & UV_HANDLE_WRITABLE)) { return UV_EPIPE; } err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_TCP: err = uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb); break; case UV_NAMED_PIPE: err = uv_pipe_write(loop, req, (uv_pipe_t*) handle, bufs, nbufs, cb); break; case UV_TTY: err = uv_tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb); break; default: assert(0); } return uv_translate_sys_error(err); } int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb) { uv_loop_t* loop = handle->loop; int err; if (!(handle->flags & UV_HANDLE_WRITABLE)) { return UV_EPIPE; } err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_NAMED_PIPE: err = uv_pipe_write2(loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb); break; default: assert(0); } return uv_translate_sys_error(err); } int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { if (stream->flags & UV__HANDLE_CLOSING) return UV_EBADF; if (!(stream->flags & UV_HANDLE_WRITABLE)) return UV_EPIPE; switch (stream->type) { case UV_TCP: return uv__tcp_try_write((uv_tcp_t*) stream, bufs, nbufs); case UV_TTY: return uv__tty_try_write((uv_tty_t*) stream, bufs, nbufs); case UV_NAMED_PIPE: return UV_EAGAIN; default: assert(0); return UV_ENOSYS; } } int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; if (!(handle->flags & UV_HANDLE_WRITABLE)) { return UV_EPIPE; } uv_req_init(loop, (uv_req_t*) req); req->type = UV_SHUTDOWN; req->handle = handle; req->cb = cb; handle->flags &= ~UV_HANDLE_WRITABLE; handle->stream.conn.shutdown_req = req; handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); uv_want_endgame(loop, (uv_handle_t*)handle); return 0; } int uv_is_readable(const uv_stream_t* handle) { return !!(handle->flags & UV_HANDLE_READABLE); } int uv_is_writable(const uv_stream_t* handle) { return !!(handle->flags & UV_HANDLE_WRITABLE); } int uv_stream_set_blocking(uv_stream_t* handle, int blocking) { if (handle->type != UV_NAMED_PIPE) return UV_EINVAL; if (blocking != 0) handle->flags |= UV_HANDLE_BLOCKING_WRITES; else handle->flags &= ~UV_HANDLE_BLOCKING_WRITES; return 0; } libuv-v1.8.0/src/win/tcp.c000066400000000000000000001271771263361716600153770ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "stream-inl.h" #include "req-inl.h" /* * Threshold of active tcp streams for which to preallocate tcp read buffers. * (Due to node slab allocator performing poorly under this pattern, * the optimization is temporarily disabled (threshold=0). This will be * revisited once node allocator is improved.) */ const unsigned int uv_active_tcp_streams_threshold = 0; /* * Number of simultaneous pending AcceptEx calls. */ const unsigned int uv_simultaneous_server_accepts = 32; /* A zero-size buffer for use by uv_tcp_read */ static char uv_zero_[] = ""; static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) { if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&enable, sizeof enable) == -1) { return WSAGetLastError(); } return 0; } static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) { if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&enable, sizeof enable) == -1) { return WSAGetLastError(); } if (enable && setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, (const char*)&delay, sizeof delay) == -1) { return WSAGetLastError(); } return 0; } static int uv_tcp_set_socket(uv_loop_t* loop, uv_tcp_t* handle, SOCKET socket, int family, int imported) { DWORD yes = 1; int non_ifs_lsp; int err; if (handle->socket != INVALID_SOCKET) return UV_EBUSY; /* Set the socket to nonblocking mode */ if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { return WSAGetLastError(); } /* Make the socket non-inheritable */ if (!SetHandleInformation((HANDLE) socket, HANDLE_FLAG_INHERIT, 0)) return GetLastError(); /* Associate it with the I/O completion port. */ /* Use uv_handle_t pointer as completion key. */ if (CreateIoCompletionPort((HANDLE)socket, loop->iocp, (ULONG_PTR)socket, 0) == NULL) { if (imported) { handle->flags |= UV_HANDLE_EMULATE_IOCP; } else { return GetLastError(); } } if (family == AF_INET6) { non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv6; } else { non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv4; } if (pSetFileCompletionNotificationModes && !(handle->flags & UV_HANDLE_EMULATE_IOCP) && !non_ifs_lsp) { if (pSetFileCompletionNotificationModes((HANDLE) socket, FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; } else if (GetLastError() != ERROR_INVALID_FUNCTION) { return GetLastError(); } } if (handle->flags & UV_HANDLE_TCP_NODELAY) { err = uv__tcp_nodelay(handle, socket, 1); if (err) return err; } /* TODO: Use stored delay. */ if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) { err = uv__tcp_keepalive(handle, socket, 1, 60); if (err) return err; } handle->socket = socket; if (family == AF_INET6) { handle->flags |= UV_HANDLE_IPV6; } else { assert(!(handle->flags & UV_HANDLE_IPV6)); } return 0; } int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) { int domain; /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) return UV_EINVAL; if (flags & ~0xFF) return UV_EINVAL; uv_stream_init(loop, (uv_stream_t*) handle, UV_TCP); handle->tcp.serv.accept_reqs = NULL; handle->tcp.serv.pending_accepts = NULL; handle->socket = INVALID_SOCKET; handle->reqs_pending = 0; handle->tcp.serv.func_acceptex = NULL; handle->tcp.conn.func_connectex = NULL; handle->tcp.serv.processed_accepts = 0; handle->delayed_error = 0; /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ if (domain != AF_UNSPEC) { SOCKET sock; DWORD err; sock = socket(domain, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { err = WSAGetLastError(); QUEUE_REMOVE(&handle->handle_queue); return uv_translate_sys_error(err); } err = uv_tcp_set_socket(handle->loop, handle, sock, domain, 0); if (err) { closesocket(sock); QUEUE_REMOVE(&handle->handle_queue); return uv_translate_sys_error(err); } } return 0; } int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) { return uv_tcp_init_ex(loop, handle, AF_UNSPEC); } void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { int err; unsigned int i; uv_tcp_accept_t* req; if (handle->flags & UV_HANDLE_CONNECTION && handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); err = 0; if (handle->flags & UV__HANDLE_CLOSING) { err = ERROR_OPERATION_ABORTED; } else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) { err = WSAGetLastError(); } if (handle->stream.conn.shutdown_req->cb) { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, uv_translate_sys_error(err)); } handle->stream.conn.shutdown_req = NULL; DECREASE_PENDING_REQ_COUNT(handle); return; } if (handle->flags & UV__HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); if (!(handle->flags & UV_HANDLE_TCP_SOCKET_CLOSED)) { closesocket(handle->socket); handle->socket = INVALID_SOCKET; handle->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; } if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) { if (handle->flags & UV_HANDLE_EMULATE_IOCP) { for (i = 0; i < uv_simultaneous_server_accepts; i++) { req = &handle->tcp.serv.accept_reqs[i]; if (req->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } if (req->event_handle) { CloseHandle(req->event_handle); req->event_handle = NULL; } } } uv__free(handle->tcp.serv.accept_reqs); handle->tcp.serv.accept_reqs = NULL; } if (handle->flags & UV_HANDLE_CONNECTION && handle->flags & UV_HANDLE_EMULATE_IOCP) { if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(handle->read_req.wait_handle); handle->read_req.wait_handle = INVALID_HANDLE_VALUE; } if (handle->read_req.event_handle) { CloseHandle(handle->read_req.event_handle); handle->read_req.event_handle = NULL; } } uv__handle_close(handle); loop->active_tcp_streams--; } } /* Unlike on Unix, here we don't set SO_REUSEADDR, because it doesn't just * allow binding to addresses that are in use by sockets in TIME_WAIT, it * effectively allows 'stealing' a port which is in use by another application. * * SO_EXCLUSIVEADDRUSE is also not good here because it does check all sockets, * regardless of state, so we'd get an error even if the port is in use by a * socket in TIME_WAIT state. * * See issue #1360. * */ static int uv_tcp_try_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags) { DWORD err; int r; if (handle->socket == INVALID_SOCKET) { SOCKET sock; /* Cannot set IPv6-only mode on non-IPv6 socket. */ if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6) return ERROR_INVALID_PARAMETER; sock = socket(addr->sa_family, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { return WSAGetLastError(); } err = uv_tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0); if (err) { closesocket(sock); return err; } } #ifdef IPV6_V6ONLY if (addr->sa_family == AF_INET6) { int on; on = (flags & UV_TCP_IPV6ONLY) != 0; /* TODO: how to handle errors? This may fail if there is no ipv4 stack */ /* available, or when run on XP/2003 which have no support for dualstack */ /* sockets. For now we're silently ignoring the error. */ setsockopt(handle->socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof on); } #endif r = bind(handle->socket, addr, addrlen); if (r == SOCKET_ERROR) { err = WSAGetLastError(); if (err == WSAEADDRINUSE) { /* Some errors are not to be reported until connect() or listen() */ handle->delayed_error = err; } else { return err; } } handle->flags |= UV_HANDLE_BOUND; return 0; } static void CALLBACK post_completion(void* context, BOOLEAN timed_out) { uv_req_t* req; uv_tcp_t* handle; req = (uv_req_t*) context; assert(req != NULL); handle = (uv_tcp_t*)req->data; assert(handle != NULL); assert(!timed_out); if (!PostQueuedCompletionStatus(handle->loop->iocp, req->u.io.overlapped.InternalHigh, 0, &req->u.io.overlapped)) { uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); } } static void CALLBACK post_write_completion(void* context, BOOLEAN timed_out) { uv_write_t* req; uv_tcp_t* handle; req = (uv_write_t*) context; assert(req != NULL); handle = (uv_tcp_t*)req->handle; assert(handle != NULL); assert(!timed_out); if (!PostQueuedCompletionStatus(handle->loop->iocp, req->u.io.overlapped.InternalHigh, 0, &req->u.io.overlapped)) { uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); } } static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { uv_loop_t* loop = handle->loop; BOOL success; DWORD bytes; SOCKET accept_socket; short family; assert(handle->flags & UV_HANDLE_LISTENING); assert(req->accept_socket == INVALID_SOCKET); /* choose family and extension function */ if (handle->flags & UV_HANDLE_IPV6) { family = AF_INET6; } else { family = AF_INET; } /* Open a socket for the accepted connection. */ accept_socket = socket(family, SOCK_STREAM, 0); if (accept_socket == INVALID_SOCKET) { SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; return; } /* Make the socket non-inheritable */ if (!SetHandleInformation((HANDLE) accept_socket, HANDLE_FLAG_INHERIT, 0)) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; closesocket(accept_socket); return; } /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); } success = handle->tcp.serv.func_acceptex(handle->socket, accept_socket, (void*)req->accept_buffer, 0, sizeof(struct sockaddr_storage), sizeof(struct sockaddr_storage), &bytes, &req->u.io.overlapped); if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ req->accept_socket = accept_socket; handle->reqs_pending++; uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(success)) { /* The req will be processed with IOCP. */ req->accept_socket = accept_socket; handle->reqs_pending++; if (handle->flags & UV_HANDLE_EMULATE_IOCP && req->wait_handle == INVALID_HANDLE_VALUE && !RegisterWaitForSingleObject(&req->wait_handle, req->event_handle, post_completion, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; return; } } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; /* Destroy the preallocated client socket. */ closesocket(accept_socket); /* Destroy the event handle */ if (handle->flags & UV_HANDLE_EMULATE_IOCP) { CloseHandle(req->u.io.overlapped.hEvent); req->event_handle = NULL; } } } static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { uv_read_t* req; uv_buf_t buf; int result; DWORD bytes, flags; assert(handle->flags & UV_HANDLE_READING); assert(!(handle->flags & UV_HANDLE_READ_PENDING)); req = &handle->read_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); /* * Preallocate a read buffer if the number of active streams is below * the threshold. */ if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer); if (handle->tcp.conn.read_buffer.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer); return; } assert(handle->tcp.conn.read_buffer.base != NULL); buf = handle->tcp.conn.read_buffer; } else { handle->flags |= UV_HANDLE_ZERO_READ; buf.base = (char*) &uv_zero_; buf.len = 0; } /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { assert(req->event_handle); req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); } flags = 0; result = WSARecv(handle->socket, (WSABUF*)&buf, 1, &bytes, &flags, &req->u.io.overlapped, NULL); if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Process the req without IOCP. */ handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; handle->reqs_pending++; uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* The req will be processed with IOCP. */ handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; if (handle->flags & UV_HANDLE_EMULATE_IOCP && req->wait_handle == INVALID_HANDLE_VALUE && !RegisterWaitForSingleObject(&req->wait_handle, req->event_handle, post_completion, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); } } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; } } int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { uv_loop_t* loop = handle->loop; unsigned int i, simultaneous_accepts; uv_tcp_accept_t* req; int err; assert(backlog > 0); if (handle->flags & UV_HANDLE_LISTENING) { handle->stream.serv.connection_cb = cb; } if (handle->flags & UV_HANDLE_READING) { return WSAEISCONN; } if (handle->delayed_error) { return handle->delayed_error; } if (!(handle->flags & UV_HANDLE_BOUND)) { err = uv_tcp_try_bind(handle, (const struct sockaddr*) &uv_addr_ip4_any_, sizeof(uv_addr_ip4_any_), 0); if (err) return err; if (handle->delayed_error) return handle->delayed_error; } if (!handle->tcp.serv.func_acceptex) { if (!uv_get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) { return WSAEAFNOSUPPORT; } } if (!(handle->flags & UV_HANDLE_SHARED_TCP_SOCKET) && listen(handle->socket, backlog) == SOCKET_ERROR) { return WSAGetLastError(); } handle->flags |= UV_HANDLE_LISTENING; handle->stream.serv.connection_cb = cb; INCREASE_ACTIVE_COUNT(loop, handle); simultaneous_accepts = handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT ? 1 : uv_simultaneous_server_accepts; if(!handle->tcp.serv.accept_reqs) { handle->tcp.serv.accept_reqs = (uv_tcp_accept_t*) uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t)); if (!handle->tcp.serv.accept_reqs) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } for (i = 0; i < simultaneous_accepts; i++) { req = &handle->tcp.serv.accept_reqs[i]; uv_req_init(loop, (uv_req_t*)req); req->type = UV_ACCEPT; req->accept_socket = INVALID_SOCKET; req->data = handle; req->wait_handle = INVALID_HANDLE_VALUE; if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); if (!req->event_handle) { uv_fatal_error(GetLastError(), "CreateEvent"); } } else { req->event_handle = NULL; } uv_tcp_queue_accept(handle, req); } /* Initialize other unused requests too, because uv_tcp_endgame */ /* doesn't know how how many requests were initialized, so it will */ /* try to clean up {uv_simultaneous_server_accepts} requests. */ for (i = simultaneous_accepts; i < uv_simultaneous_server_accepts; i++) { req = &handle->tcp.serv.accept_reqs[i]; uv_req_init(loop, (uv_req_t*) req); req->type = UV_ACCEPT; req->accept_socket = INVALID_SOCKET; req->data = handle; req->wait_handle = INVALID_HANDLE_VALUE; req->event_handle = NULL; } } return 0; } int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { uv_loop_t* loop = server->loop; int err = 0; int family; uv_tcp_accept_t* req = server->tcp.serv.pending_accepts; if (!req) { /* No valid connections found, so we error out. */ return WSAEWOULDBLOCK; } if (req->accept_socket == INVALID_SOCKET) { return WSAENOTCONN; } if (server->flags & UV_HANDLE_IPV6) { family = AF_INET6; } else { family = AF_INET; } err = uv_tcp_set_socket(client->loop, client, req->accept_socket, family, 0); if (err) { closesocket(req->accept_socket); } else { uv_connection_init((uv_stream_t*) client); /* AcceptEx() implicitly binds the accepted socket. */ client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } /* Prepare the req to pick up a new connection */ server->tcp.serv.pending_accepts = req->next_pending; req->next_pending = NULL; req->accept_socket = INVALID_SOCKET; if (!(server->flags & UV__HANDLE_CLOSING)) { /* Check if we're in a middle of changing the number of pending accepts. */ if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) { uv_tcp_queue_accept(server, req); } else { /* We better be switching to a single pending accept. */ assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT); server->tcp.serv.processed_accepts++; if (server->tcp.serv.processed_accepts >= uv_simultaneous_server_accepts) { server->tcp.serv.processed_accepts = 0; /* * All previously queued accept requests are now processed. * We now switch to queueing just a single accept. */ uv_tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]); server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING; server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; } } } loop->active_tcp_streams++; return err; } int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; handle->flags |= UV_HANDLE_READING; handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; INCREASE_ACTIVE_COUNT(loop, handle); /* If reading was stopped and then started again, there could still be a */ /* read request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) { if (handle->flags & UV_HANDLE_EMULATE_IOCP && !handle->read_req.event_handle) { handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); if (!handle->read_req.event_handle) { uv_fatal_error(GetLastError(), "CreateEvent"); } } uv_tcp_queue_read(loop, handle); } return 0; } static int uv_tcp_try_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; const struct sockaddr* bind_addr; BOOL success; DWORD bytes; int err; if (handle->delayed_error) { return handle->delayed_error; } if (!(handle->flags & UV_HANDLE_BOUND)) { if (addrlen == sizeof(uv_addr_ip4_any_)) { bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; } else if (addrlen == sizeof(uv_addr_ip6_any_)) { bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; } else { abort(); } err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0); if (err) return err; if (handle->delayed_error) return handle->delayed_error; } if (!handle->tcp.conn.func_connectex) { if (!uv_get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) { return WSAEAFNOSUPPORT; } } uv_req_init(loop, (uv_req_t*) req); req->type = UV_CONNECT; req->handle = (uv_stream_t*) handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); success = handle->tcp.conn.func_connectex(handle->socket, addr, addrlen, NULL, 0, &bytes, &req->u.io.overlapped); if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(success)) { /* The req will be processed with IOCP. */ handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); } else { return WSAGetLastError(); } return 0; } int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { int result; if (handle->socket == INVALID_SOCKET) { return UV_EINVAL; } if (handle->delayed_error) { return uv_translate_sys_error(handle->delayed_error); } result = getsockname(handle->socket, name, namelen); if (result != 0) { return uv_translate_sys_error(WSAGetLastError()); } return 0; } int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { int result; if (handle->socket == INVALID_SOCKET) { return UV_EINVAL; } if (handle->delayed_error) { return uv_translate_sys_error(handle->delayed_error); } result = getpeername(handle->socket, name, namelen); if (result != 0) { return uv_translate_sys_error(WSAGetLastError()); } return 0; } int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) { int result; DWORD bytes; uv_req_init(loop, (uv_req_t*) req); req->type = UV_WRITE; req->handle = (uv_stream_t*) handle; req->cb = cb; /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); if (!req->event_handle) { uv_fatal_error(GetLastError(), "CreateEvent"); } req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); req->wait_handle = INVALID_HANDLE_VALUE; } result = WSASend(handle->socket, (WSABUF*) bufs, nbufs, &bytes, 0, &req->u.io.overlapped, NULL); if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Request completed immediately. */ req->u.io.queued_bytes = 0; handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); uv_insert_pending_req(loop, (uv_req_t*) req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* Request queued by the kernel. */ req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); handle->write_queue_size += req->u.io.queued_bytes; if (handle->flags & UV_HANDLE_EMULATE_IOCP && !RegisterWaitForSingleObject(&req->wait_handle, req->event_handle, post_write_completion, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); } } else { /* Send failed due to an error, report it later */ req->u.io.queued_bytes = 0; handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*) req); } return 0; } int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs) { int result; DWORD bytes; if (handle->stream.conn.write_reqs_pending > 0) return UV_EAGAIN; result = WSASend(handle->socket, (WSABUF*) bufs, nbufs, &bytes, 0, NULL, NULL); if (result == SOCKET_ERROR) return uv_translate_sys_error(WSAGetLastError()); else return bytes; } void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req) { DWORD bytes, flags, err; uv_buf_t buf; assert(handle->type == UV_TCP); handle->flags &= ~UV_HANDLE_READ_PENDING; if (!REQ_SUCCESS(req)) { /* An error occurred doing the read. */ if ((handle->flags & UV_HANDLE_READING) || !(handle->flags & UV_HANDLE_ZERO_READ)) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); buf = (handle->flags & UV_HANDLE_ZERO_READ) ? uv_buf_init(NULL, 0) : handle->tcp.conn.read_buffer; err = GET_REQ_SOCK_ERROR(req); if (err == WSAECONNABORTED) { /* * Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix. */ err = WSAECONNRESET; } handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(err), &buf); } } else { if (!(handle->flags & UV_HANDLE_ZERO_READ)) { /* The read was done with a non-zero buffer length. */ if (req->u.io.overlapped.InternalHigh > 0) { /* Successful read */ handle->read_cb((uv_stream_t*)handle, req->u.io.overlapped.InternalHigh, &handle->tcp.conn.read_buffer); /* Read again only if bytes == buf.len */ if (req->u.io.overlapped.InternalHigh < handle->tcp.conn.read_buffer.len) { goto done; } } else { /* Connection closed */ if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); } handle->flags &= ~UV_HANDLE_READABLE; buf.base = 0; buf.len = 0; handle->read_cb((uv_stream_t*)handle, UV_EOF, &handle->tcp.conn.read_buffer); goto done; } } /* Do nonblocking reads until the buffer is empty */ while (handle->flags & UV_HANDLE_READING) { handle->alloc_cb((uv_handle_t*) handle, 65536, &buf); if (buf.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); break; } assert(buf.base != NULL); flags = 0; if (WSARecv(handle->socket, (WSABUF*)&buf, 1, &bytes, &flags, NULL, NULL) != SOCKET_ERROR) { if (bytes > 0) { /* Successful read */ handle->read_cb((uv_stream_t*)handle, bytes, &buf); /* Read again only if bytes == buf.len */ if (bytes < buf.len) { break; } } else { /* Connection closed */ handle->flags &= ~(UV_HANDLE_READING | UV_HANDLE_READABLE); DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*)handle, UV_EOF, &buf); break; } } else { err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { /* Read buffer was completely empty, report a 0-byte read. */ handle->read_cb((uv_stream_t*)handle, 0, &buf); } else { /* Ouch! serious error. */ handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); if (err == WSAECONNABORTED) { /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with */ /* Unix. */ err = WSAECONNRESET; } handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(err), &buf); } break; } } done: /* Post another read if still reading and not closing. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { uv_tcp_queue_read(loop, handle); } } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, uv_write_t* req) { int err; assert(handle->type == UV_TCP); assert(handle->write_queue_size >= req->u.io.queued_bytes); handle->write_queue_size -= req->u.io.queued_bytes; UNREGISTER_HANDLE_REQ(loop, handle, req); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (req->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } if (req->event_handle) { CloseHandle(req->event_handle); req->event_handle = NULL; } } if (req->cb) { err = uv_translate_sys_error(GET_REQ_SOCK_ERROR(req)); if (err == UV_ECONNABORTED) { /* use UV_ECANCELED for consistency with Unix */ err = UV_ECANCELED; } req->cb(req, err); } handle->stream.conn.write_reqs_pending--; if (handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*)handle); } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* raw_req) { uv_tcp_accept_t* req = (uv_tcp_accept_t*) raw_req; int err; assert(handle->type == UV_TCP); /* If handle->accepted_socket is not a valid socket, then */ /* uv_queue_accept must have failed. This is a serious error. We stop */ /* accepting connections and report this error to the connection */ /* callback. */ if (req->accept_socket == INVALID_SOCKET) { if (handle->flags & UV_HANDLE_LISTENING) { handle->flags &= ~UV_HANDLE_LISTENING; DECREASE_ACTIVE_COUNT(loop, handle); if (handle->stream.serv.connection_cb) { err = GET_REQ_SOCK_ERROR(req); handle->stream.serv.connection_cb((uv_stream_t*)handle, uv_translate_sys_error(err)); } } } else if (REQ_SUCCESS(req) && setsockopt(req->accept_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&handle->socket, sizeof(handle->socket)) == 0) { req->next_pending = handle->tcp.serv.pending_accepts; handle->tcp.serv.pending_accepts = req; /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ if (handle->stream.serv.connection_cb) { handle->stream.serv.connection_cb((uv_stream_t*)handle, 0); } } else { /* Error related to accepted socket is ignored because the server */ /* socket may still be healthy. If the server socket is broken */ /* uv_queue_accept will detect it. */ closesocket(req->accept_socket); req->accept_socket = INVALID_SOCKET; if (handle->flags & UV_HANDLE_LISTENING) { uv_tcp_queue_accept(handle, req); } } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, uv_connect_t* req) { int err; assert(handle->type == UV_TCP); UNREGISTER_HANDLE_REQ(loop, handle, req); err = 0; if (REQ_SUCCESS(req)) { if (setsockopt(handle->socket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) == 0) { uv_connection_init((uv_stream_t*)handle); handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; loop->active_tcp_streams++; } else { err = WSAGetLastError(); } } else { err = GET_REQ_SOCK_ERROR(req); } req->cb(req, uv_translate_sys_error(err)); DECREASE_PENDING_REQ_COUNT(handle); } int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex, int tcp_connection) { int err; SOCKET socket = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &socket_info_ex->socket_info, 0, WSA_FLAG_OVERLAPPED); if (socket == INVALID_SOCKET) { return WSAGetLastError(); } err = uv_tcp_set_socket(tcp->loop, tcp, socket, socket_info_ex->socket_info.iAddressFamily, 1); if (err) { closesocket(socket); return err; } if (tcp_connection) { uv_connection_init((uv_stream_t*)tcp); tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } tcp->flags |= UV_HANDLE_BOUND; tcp->flags |= UV_HANDLE_SHARED_TCP_SOCKET; tcp->delayed_error = socket_info_ex->delayed_error; tcp->loop->active_tcp_streams++; return 0; } int uv_tcp_nodelay(uv_tcp_t* handle, int enable) { int err; if (handle->socket != INVALID_SOCKET) { err = uv__tcp_nodelay(handle, handle->socket, enable); if (err) return err; } if (enable) { handle->flags |= UV_HANDLE_TCP_NODELAY; } else { handle->flags &= ~UV_HANDLE_TCP_NODELAY; } return 0; } int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { int err; if (handle->socket != INVALID_SOCKET) { err = uv__tcp_keepalive(handle, handle->socket, enable, delay); if (err) return err; } if (enable) { handle->flags |= UV_HANDLE_TCP_KEEPALIVE; } else { handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE; } /* TODO: Store delay if handle->socket isn't created yet. */ return 0; } int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, LPWSAPROTOCOL_INFOW protocol_info) { if (!(handle->flags & UV_HANDLE_CONNECTION)) { /* * We're about to share the socket with another process. Because * this is a listening socket, we assume that the other process will * be accepting connections on it. So, before sharing the socket * with another process, we call listen here in the parent process. */ if (!(handle->flags & UV_HANDLE_LISTENING)) { if (!(handle->flags & UV_HANDLE_BOUND)) { return ERROR_INVALID_PARAMETER; } if (!(handle->delayed_error)) { if (listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) { handle->delayed_error = WSAGetLastError(); } } } } if (WSADuplicateSocketW(handle->socket, pid, protocol_info)) { return WSAGetLastError(); } handle->flags |= UV_HANDLE_SHARED_TCP_SOCKET; return 0; } int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { if (handle->flags & UV_HANDLE_CONNECTION) { return UV_EINVAL; } /* Check if we're already in the desired mode. */ if ((enable && !(handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) || (!enable && handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { return 0; } /* Don't allow switching from single pending accept to many. */ if (enable) { return UV_ENOTSUP; } /* Check if we're in a middle of changing the number of pending accepts. */ if (handle->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING) { return 0; } handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; /* Flip the changing flag if we have already queued multiple accepts. */ if (handle->flags & UV_HANDLE_LISTENING) { handle->flags |= UV_HANDLE_TCP_ACCEPT_STATE_CHANGING; } return 0; } static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) { SOCKET socket = tcp->socket; int non_ifs_lsp; /* Check if we have any non-IFS LSPs stacked on top of TCP */ non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 : uv_tcp_non_ifs_lsp_ipv4; /* If there are non-ifs LSPs then try to obtain a base handle for the */ /* socket. This will always fail on Windows XP/3k. */ if (non_ifs_lsp) { DWORD bytes; if (WSAIoctl(socket, SIO_BASE_HANDLE, NULL, 0, &socket, sizeof socket, &bytes, NULL, NULL) != 0) { /* Failed. We can't do CancelIo. */ return -1; } } assert(socket != 0 && socket != INVALID_SOCKET); if (!CancelIo((HANDLE) socket)) { return GetLastError(); } /* It worked. */ return 0; } void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { int close_socket = 1; if (tcp->flags & UV_HANDLE_READ_PENDING) { /* In order for winsock to do a graceful close there must not be any */ /* any pending reads, or the socket must be shut down for writing */ if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) { /* Just do shutdown on non-shared sockets, which ensures graceful close. */ shutdown(tcp->socket, SD_SEND); } else if (uv_tcp_try_cancel_io(tcp) == 0) { /* In case of a shared socket, we try to cancel all outstanding I/O, */ /* If that works, don't close the socket yet - wait for the read req to */ /* return and close the socket in uv_tcp_endgame. */ close_socket = 0; } else { /* When cancelling isn't possible - which could happen when an LSP is */ /* present on an old Windows version, we will have to close the socket */ /* with a read pending. That is not nice because trailing sent bytes */ /* may not make it to the other side. */ } } else if ((tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET) && tcp->tcp.serv.accept_reqs != NULL) { /* Under normal circumstances closesocket() will ensure that all pending */ /* accept reqs are canceled. However, when the socket is shared the */ /* presence of another reference to the socket in another process will */ /* keep the accept reqs going, so we have to ensure that these are */ /* canceled. */ if (uv_tcp_try_cancel_io(tcp) != 0) { /* When cancellation is not possible, there is another option: we can */ /* close the incoming sockets, which will also cancel the accept */ /* operations. However this is not cool because we might inadvertently */ /* close a socket that just accepted a new connection, which will */ /* cause the connection to be aborted. */ unsigned int i; for (i = 0; i < uv_simultaneous_server_accepts; i++) { uv_tcp_accept_t* req = &tcp->tcp.serv.accept_reqs[i]; if (req->accept_socket != INVALID_SOCKET && !HasOverlappedIoCompleted(&req->u.io.overlapped)) { closesocket(req->accept_socket); req->accept_socket = INVALID_SOCKET; } } } } if (tcp->flags & UV_HANDLE_READING) { tcp->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, tcp); } if (tcp->flags & UV_HANDLE_LISTENING) { tcp->flags &= ~UV_HANDLE_LISTENING; DECREASE_ACTIVE_COUNT(loop, tcp); } if (close_socket) { closesocket(tcp->socket); tcp->socket = INVALID_SOCKET; tcp->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; } tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(tcp); if (tcp->reqs_pending == 0) { uv_want_endgame(tcp->loop, (uv_handle_t*)tcp); } } int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; int err; /* Detect the address family of the socket. */ opt_len = (int) sizeof protocol_info; if (getsockopt(sock, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, &opt_len) == SOCKET_ERROR) { return uv_translate_sys_error(GetLastError()); } err = uv_tcp_set_socket(handle->loop, handle, sock, protocol_info.iAddressFamily, 1); if (err) { return uv_translate_sys_error(err); } return 0; } /* This function is an egress point, i.e. it returns libuv errors rather than * system errors. */ int uv__tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags) { int err; err = uv_tcp_try_bind(handle, addr, addrlen, flags); if (err) return uv_translate_sys_error(err); return 0; } /* This function is an egress point, i.e. it returns libuv errors rather than * system errors. */ int uv__tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, uv_connect_cb cb) { int err; err = uv_tcp_try_connect(req, handle, addr, addrlen, cb); if (err) return uv_translate_sys_error(err); return 0; } libuv-v1.8.0/src/win/thread.c000066400000000000000000000424531263361716600160510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "uv.h" #include "internal.h" #define HAVE_CONDVAR_API() (pInitializeConditionVariable != NULL) static int uv_cond_fallback_init(uv_cond_t* cond); static void uv_cond_fallback_destroy(uv_cond_t* cond); static void uv_cond_fallback_signal(uv_cond_t* cond); static void uv_cond_fallback_broadcast(uv_cond_t* cond); static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex); static int uv_cond_fallback_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout); static int uv_cond_condvar_init(uv_cond_t* cond); static void uv_cond_condvar_destroy(uv_cond_t* cond); static void uv_cond_condvar_signal(uv_cond_t* cond); static void uv_cond_condvar_broadcast(uv_cond_t* cond); static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex); static int uv_cond_condvar_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout); static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) { DWORD result; HANDLE existing_event, created_event; created_event = CreateEvent(NULL, 1, 0, NULL); if (created_event == 0) { /* Could fail in a low-memory situation? */ uv_fatal_error(GetLastError(), "CreateEvent"); } existing_event = InterlockedCompareExchangePointer(&guard->event, created_event, NULL); if (existing_event == NULL) { /* We won the race */ callback(); result = SetEvent(created_event); assert(result); guard->ran = 1; } else { /* We lost the race. Destroy the event we created and wait for the */ /* existing one to become signaled. */ CloseHandle(created_event); result = WaitForSingleObject(existing_event, INFINITE); assert(result == WAIT_OBJECT_0); } } void uv_once(uv_once_t* guard, void (*callback)(void)) { /* Fast case - avoid WaitForSingleObject. */ if (guard->ran) { return; } uv__once_inner(guard, callback); } /* Verify that uv_thread_t can be stored in a TLS slot. */ STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*)); static uv_key_t uv__current_thread_key; static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT; static void uv__init_current_thread_key(void) { if (uv_key_create(&uv__current_thread_key)) abort(); } struct thread_ctx { void (*entry)(void* arg); void* arg; uv_thread_t self; }; static UINT __stdcall uv__thread_start(void* arg) { struct thread_ctx *ctx_p; struct thread_ctx ctx; ctx_p = arg; ctx = *ctx_p; uv__free(ctx_p); uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); uv_key_set(&uv__current_thread_key, (void*) ctx.self); ctx.entry(ctx.arg); return 0; } int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { struct thread_ctx* ctx; int err; HANDLE thread; ctx = uv__malloc(sizeof(*ctx)); if (ctx == NULL) return UV_ENOMEM; ctx->entry = entry; ctx->arg = arg; /* Create the thread in suspended state so we have a chance to pass * its own creation handle to it */ thread = (HANDLE) _beginthreadex(NULL, 0, uv__thread_start, ctx, CREATE_SUSPENDED, NULL); if (thread == NULL) { err = errno; uv__free(ctx); } else { err = 0; *tid = thread; ctx->self = thread; ResumeThread(thread); } switch (err) { case 0: return 0; case EACCES: return UV_EACCES; case EAGAIN: return UV_EAGAIN; case EINVAL: return UV_EINVAL; } return UV_EIO; } uv_thread_t uv_thread_self(void) { uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); return (uv_thread_t) uv_key_get(&uv__current_thread_key); } int uv_thread_join(uv_thread_t *tid) { if (WaitForSingleObject(*tid, INFINITE)) return uv_translate_sys_error(GetLastError()); else { CloseHandle(*tid); *tid = 0; return 0; } } int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { return *t1 == *t2; } int uv_mutex_init(uv_mutex_t* mutex) { InitializeCriticalSection(mutex); return 0; } void uv_mutex_destroy(uv_mutex_t* mutex) { DeleteCriticalSection(mutex); } void uv_mutex_lock(uv_mutex_t* mutex) { EnterCriticalSection(mutex); } int uv_mutex_trylock(uv_mutex_t* mutex) { if (TryEnterCriticalSection(mutex)) return 0; else return UV_EBUSY; } void uv_mutex_unlock(uv_mutex_t* mutex) { LeaveCriticalSection(mutex); } int uv_rwlock_init(uv_rwlock_t* rwlock) { /* Initialize the semaphore that acts as the write lock. */ HANDLE handle = CreateSemaphoreW(NULL, 1, 1, NULL); if (handle == NULL) return uv_translate_sys_error(GetLastError()); rwlock->state_.write_semaphore_ = handle; /* Initialize the critical section protecting the reader count. */ InitializeCriticalSection(&rwlock->state_.num_readers_lock_); /* Initialize the reader count. */ rwlock->state_.num_readers_ = 0; return 0; } void uv_rwlock_destroy(uv_rwlock_t* rwlock) { DeleteCriticalSection(&rwlock->state_.num_readers_lock_); CloseHandle(rwlock->state_.write_semaphore_); } void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { /* Acquire the lock that protects the reader count. */ EnterCriticalSection(&rwlock->state_.num_readers_lock_); /* Increase the reader count, and lock for write if this is the first * reader. */ if (++rwlock->state_.num_readers_ == 1) { DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE); if (r != WAIT_OBJECT_0) uv_fatal_error(GetLastError(), "WaitForSingleObject"); } /* Release the lock that protects the reader count. */ LeaveCriticalSection(&rwlock->state_.num_readers_lock_); } int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { int err; if (!TryEnterCriticalSection(&rwlock->state_.num_readers_lock_)) return UV_EBUSY; err = 0; if (rwlock->state_.num_readers_ == 0) { /* Currently there are no other readers, which means that the write lock * needs to be acquired. */ DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0); if (r == WAIT_OBJECT_0) rwlock->state_.num_readers_++; else if (r == WAIT_TIMEOUT) err = UV_EBUSY; else if (r == WAIT_FAILED) uv_fatal_error(GetLastError(), "WaitForSingleObject"); } else { /* The write lock has already been acquired because there are other * active readers. */ rwlock->state_.num_readers_++; } LeaveCriticalSection(&rwlock->state_.num_readers_lock_); return err; } void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { EnterCriticalSection(&rwlock->state_.num_readers_lock_); if (--rwlock->state_.num_readers_ == 0) { if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL)) uv_fatal_error(GetLastError(), "ReleaseSemaphore"); } LeaveCriticalSection(&rwlock->state_.num_readers_lock_); } void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE); if (r != WAIT_OBJECT_0) uv_fatal_error(GetLastError(), "WaitForSingleObject"); } int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0); if (r == WAIT_OBJECT_0) return 0; else if (r == WAIT_TIMEOUT) return UV_EBUSY; else uv_fatal_error(GetLastError(), "WaitForSingleObject"); } void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL)) uv_fatal_error(GetLastError(), "ReleaseSemaphore"); } int uv_sem_init(uv_sem_t* sem, unsigned int value) { *sem = CreateSemaphore(NULL, value, INT_MAX, NULL); if (*sem == NULL) return uv_translate_sys_error(GetLastError()); else return 0; } void uv_sem_destroy(uv_sem_t* sem) { if (!CloseHandle(*sem)) abort(); } void uv_sem_post(uv_sem_t* sem) { if (!ReleaseSemaphore(*sem, 1, NULL)) abort(); } void uv_sem_wait(uv_sem_t* sem) { if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0) abort(); } int uv_sem_trywait(uv_sem_t* sem) { DWORD r = WaitForSingleObject(*sem, 0); if (r == WAIT_OBJECT_0) return 0; if (r == WAIT_TIMEOUT) return UV_EAGAIN; abort(); return -1; /* Satisfy the compiler. */ } /* This condition variable implementation is based on the SetEvent solution * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html * We could not use the SignalObjectAndWait solution (section 3.4) because * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. */ static int uv_cond_fallback_init(uv_cond_t* cond) { int err; /* Initialize the count to 0. */ cond->fallback.waiters_count = 0; InitializeCriticalSection(&cond->fallback.waiters_count_lock); /* Create an auto-reset event. */ cond->fallback.signal_event = CreateEvent(NULL, /* no security */ FALSE, /* auto-reset event */ FALSE, /* non-signaled initially */ NULL); /* unnamed */ if (!cond->fallback.signal_event) { err = GetLastError(); goto error2; } /* Create a manual-reset event. */ cond->fallback.broadcast_event = CreateEvent(NULL, /* no security */ TRUE, /* manual-reset */ FALSE, /* non-signaled */ NULL); /* unnamed */ if (!cond->fallback.broadcast_event) { err = GetLastError(); goto error; } return 0; error: CloseHandle(cond->fallback.signal_event); error2: DeleteCriticalSection(&cond->fallback.waiters_count_lock); return uv_translate_sys_error(err); } static int uv_cond_condvar_init(uv_cond_t* cond) { pInitializeConditionVariable(&cond->cond_var); return 0; } int uv_cond_init(uv_cond_t* cond) { uv__once_init(); if (HAVE_CONDVAR_API()) return uv_cond_condvar_init(cond); else return uv_cond_fallback_init(cond); } static void uv_cond_fallback_destroy(uv_cond_t* cond) { if (!CloseHandle(cond->fallback.broadcast_event)) abort(); if (!CloseHandle(cond->fallback.signal_event)) abort(); DeleteCriticalSection(&cond->fallback.waiters_count_lock); } static void uv_cond_condvar_destroy(uv_cond_t* cond) { /* nothing to do */ } void uv_cond_destroy(uv_cond_t* cond) { if (HAVE_CONDVAR_API()) uv_cond_condvar_destroy(cond); else uv_cond_fallback_destroy(cond); } static void uv_cond_fallback_signal(uv_cond_t* cond) { int have_waiters; /* Avoid race conditions. */ EnterCriticalSection(&cond->fallback.waiters_count_lock); have_waiters = cond->fallback.waiters_count > 0; LeaveCriticalSection(&cond->fallback.waiters_count_lock); if (have_waiters) SetEvent(cond->fallback.signal_event); } static void uv_cond_condvar_signal(uv_cond_t* cond) { pWakeConditionVariable(&cond->cond_var); } void uv_cond_signal(uv_cond_t* cond) { if (HAVE_CONDVAR_API()) uv_cond_condvar_signal(cond); else uv_cond_fallback_signal(cond); } static void uv_cond_fallback_broadcast(uv_cond_t* cond) { int have_waiters; /* Avoid race conditions. */ EnterCriticalSection(&cond->fallback.waiters_count_lock); have_waiters = cond->fallback.waiters_count > 0; LeaveCriticalSection(&cond->fallback.waiters_count_lock); if (have_waiters) SetEvent(cond->fallback.broadcast_event); } static void uv_cond_condvar_broadcast(uv_cond_t* cond) { pWakeAllConditionVariable(&cond->cond_var); } void uv_cond_broadcast(uv_cond_t* cond) { if (HAVE_CONDVAR_API()) uv_cond_condvar_broadcast(cond); else uv_cond_fallback_broadcast(cond); } static int uv_cond_wait_helper(uv_cond_t* cond, uv_mutex_t* mutex, DWORD dwMilliseconds) { DWORD result; int last_waiter; HANDLE handles[2] = { cond->fallback.signal_event, cond->fallback.broadcast_event }; /* Avoid race conditions. */ EnterCriticalSection(&cond->fallback.waiters_count_lock); cond->fallback.waiters_count++; LeaveCriticalSection(&cond->fallback.waiters_count_lock); /* It's ok to release the here since Win32 manual-reset events */ /* maintain state when used with . This avoids the "lost wakeup" */ /* bug. */ uv_mutex_unlock(mutex); /* Wait for either event to become signaled due to being */ /* called or being called. */ result = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds); EnterCriticalSection(&cond->fallback.waiters_count_lock); cond->fallback.waiters_count--; last_waiter = result == WAIT_OBJECT_0 + 1 && cond->fallback.waiters_count == 0; LeaveCriticalSection(&cond->fallback.waiters_count_lock); /* Some thread called . */ if (last_waiter) { /* We're the last waiter to be notified or to stop waiting, so reset the */ /* the manual-reset event. */ ResetEvent(cond->fallback.broadcast_event); } /* Reacquire the . */ uv_mutex_lock(mutex); if (result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1) return 0; if (result == WAIT_TIMEOUT) return UV_ETIMEDOUT; abort(); return -1; /* Satisfy the compiler. */ } static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex) { if (uv_cond_wait_helper(cond, mutex, INFINITE)) abort(); } static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex) { if (!pSleepConditionVariableCS(&cond->cond_var, mutex, INFINITE)) abort(); } void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { if (HAVE_CONDVAR_API()) uv_cond_condvar_wait(cond, mutex); else uv_cond_fallback_wait(cond, mutex); } static int uv_cond_fallback_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { return uv_cond_wait_helper(cond, mutex, (DWORD)(timeout / 1e6)); } static int uv_cond_condvar_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { if (pSleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) return 0; if (GetLastError() != ERROR_TIMEOUT) abort(); return UV_ETIMEDOUT; } int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { if (HAVE_CONDVAR_API()) return uv_cond_condvar_timedwait(cond, mutex, timeout); else return uv_cond_fallback_timedwait(cond, mutex, timeout); } int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { int err; barrier->n = count; barrier->count = 0; err = uv_mutex_init(&barrier->mutex); if (err) return err; err = uv_sem_init(&barrier->turnstile1, 0); if (err) goto error2; err = uv_sem_init(&barrier->turnstile2, 1); if (err) goto error; return 0; error: uv_sem_destroy(&barrier->turnstile1); error2: uv_mutex_destroy(&barrier->mutex); return err; } void uv_barrier_destroy(uv_barrier_t* barrier) { uv_sem_destroy(&barrier->turnstile2); uv_sem_destroy(&barrier->turnstile1); uv_mutex_destroy(&barrier->mutex); } int uv_barrier_wait(uv_barrier_t* barrier) { int serial_thread; uv_mutex_lock(&barrier->mutex); if (++barrier->count == barrier->n) { uv_sem_wait(&barrier->turnstile2); uv_sem_post(&barrier->turnstile1); } uv_mutex_unlock(&barrier->mutex); uv_sem_wait(&barrier->turnstile1); uv_sem_post(&barrier->turnstile1); uv_mutex_lock(&barrier->mutex); serial_thread = (--barrier->count == 0); if (serial_thread) { uv_sem_wait(&barrier->turnstile1); uv_sem_post(&barrier->turnstile2); } uv_mutex_unlock(&barrier->mutex); uv_sem_wait(&barrier->turnstile2); uv_sem_post(&barrier->turnstile2); return serial_thread; } int uv_key_create(uv_key_t* key) { key->tls_index = TlsAlloc(); if (key->tls_index == TLS_OUT_OF_INDEXES) return UV_ENOMEM; return 0; } void uv_key_delete(uv_key_t* key) { if (TlsFree(key->tls_index) == FALSE) abort(); key->tls_index = TLS_OUT_OF_INDEXES; } void* uv_key_get(uv_key_t* key) { void* value; value = TlsGetValue(key->tls_index); if (value == NULL) if (GetLastError() != ERROR_SUCCESS) abort(); return value; } void uv_key_set(uv_key_t* key, void* value) { if (TlsSetValue(key->tls_index, value) == FALSE) abort(); } libuv-v1.8.0/src/win/timer.c000066400000000000000000000120721263361716600157140ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" #include "tree.h" #include "handle-inl.h" /* The number of milliseconds in one second. */ #define UV__MILLISEC 1000 void uv_update_time(uv_loop_t* loop) { uint64_t new_time = uv__hrtime(UV__MILLISEC); if (new_time > loop->time) { loop->time = new_time; } } void uv__time_forward(uv_loop_t* loop, uint64_t msecs) { loop->time += msecs; } static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { if (a->due < b->due) return -1; if (a->due > b->due) return 1; /* * compare start_id when both has the same due. start_id is * allocated with loop->timer_counter in uv_timer_start(). */ if (a->start_id < b->start_id) return -1; if (a->start_id > b->start_id) return 1; return 0; } RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_TIMER); handle->timer_cb = NULL; handle->repeat = 0; return 0; } void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) { if (handle->flags & UV__HANDLE_CLOSING) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } } static uint64_t get_clamped_due_time(uint64_t loop_time, uint64_t timeout) { uint64_t clamped_timeout; clamped_timeout = loop_time + timeout; if (clamped_timeout < timeout) clamped_timeout = (uint64_t) -1; return clamped_timeout; } int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, uint64_t timeout, uint64_t repeat) { uv_loop_t* loop = handle->loop; uv_timer_t* old; if (timer_cb == NULL) return UV_EINVAL; if (uv__is_active(handle)) uv_timer_stop(handle); handle->timer_cb = timer_cb; handle->due = get_clamped_due_time(loop->time, timeout); handle->repeat = repeat; uv__handle_start(handle); /* start_id is the second index to be compared in uv__timer_cmp() */ handle->start_id = handle->loop->timer_counter++; old = RB_INSERT(uv_timer_tree_s, &loop->timers, handle); assert(old == NULL); return 0; } int uv_timer_stop(uv_timer_t* handle) { uv_loop_t* loop = handle->loop; if (!uv__is_active(handle)) return 0; RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); uv__handle_stop(handle); return 0; } int uv_timer_again(uv_timer_t* handle) { /* If timer_cb is NULL that means that the timer was never started. */ if (!handle->timer_cb) { return UV_EINVAL; } if (handle->repeat) { uv_timer_stop(handle); uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat); } return 0; } void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) { assert(handle->type == UV_TIMER); handle->repeat = repeat; } uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { assert(handle->type == UV_TIMER); return handle->repeat; } DWORD uv__next_timeout(const uv_loop_t* loop) { uv_timer_t* timer; int64_t delta; /* Check if there are any running timers * Need to cast away const first, since RB_MIN doesn't know what we are * going to do with this return value, it can't be marked const */ timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers); if (timer) { delta = timer->due - loop->time; if (delta >= UINT_MAX - 1) { /* A timeout value of UINT_MAX means infinite, so that's no good. */ return UINT_MAX - 1; } else if (delta < 0) { /* Negative timeout values are not allowed */ return 0; } else { return (DWORD)delta; } } else { /* No timers */ return INFINITE; } } void uv_process_timers(uv_loop_t* loop) { uv_timer_t* timer; /* Call timer callbacks */ for (timer = RB_MIN(uv_timer_tree_s, &loop->timers); timer != NULL && timer->due <= loop->time; timer = RB_MIN(uv_timer_tree_s, &loop->timers)) { uv_timer_stop(timer); uv_timer_again(timer); timer->timer_cb((uv_timer_t*) timer); } } libuv-v1.8.0/src/win/tty.c000066400000000000000000002000201263361716600154040ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "stdint-msvc2008.h" #else # include #endif #ifndef COMMON_LVB_REVERSE_VIDEO # define COMMON_LVB_REVERSE_VIDEO 0x4000 #endif #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "stream-inl.h" #include "req-inl.h" #define UNICODE_REPLACEMENT_CHARACTER (0xfffd) #define ANSI_NORMAL 0x00 #define ANSI_ESCAPE_SEEN 0x02 #define ANSI_CSI 0x04 #define ANSI_ST_CONTROL 0x08 #define ANSI_IGNORE 0x10 #define ANSI_IN_ARG 0x20 #define ANSI_IN_STRING 0x40 #define ANSI_BACKSLASH_SEEN 0x80 #define MAX_INPUT_BUFFER_LENGTH 8192 static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info); static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info); /* Null uv_buf_t */ static const uv_buf_t uv_null_buf_ = { 0, NULL }; /* * The console virtual window. * * Normally cursor movement in windows is relative to the console screen buffer, * e.g. the application is allowed to overwrite the 'history'. This is very * inconvenient, it makes absolute cursor movement pretty useless. There is * also the concept of 'client rect' which is defined by the actual size of * the console window and the scroll position of the screen buffer, but it's * very volatile because it changes when the user scrolls. * * To make cursor movement behave sensibly we define a virtual window to which * cursor movement is confined. The virtual window is always as wide as the * console screen buffer, but it's height is defined by the size of the * console window. The top of the virtual window aligns with the position * of the caret when the first stdout/err handle is created, unless that would * mean that it would extend beyond the bottom of the screen buffer - in that * that case it's located as far down as possible. * * When the user writes a long text or many newlines, such that the output * reaches beyond the bottom of the virtual window, the virtual window is * shifted downwards, but not resized. * * Since all tty i/o happens on the same console, this window is shared * between all stdout/stderr handles. */ static int uv_tty_virtual_offset = -1; static int uv_tty_virtual_height = -1; static int uv_tty_virtual_width = -1; static CRITICAL_SECTION uv_tty_output_lock; static HANDLE uv_tty_output_handle = INVALID_HANDLE_VALUE; static WORD uv_tty_default_text_attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; static char uv_tty_default_fg_color = 7; static char uv_tty_default_bg_color = 0; static char uv_tty_default_fg_bright = 0; static char uv_tty_default_bg_bright = 0; static char uv_tty_default_inverse = 0; void uv_console_init() { InitializeCriticalSection(&uv_tty_output_lock); } int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { HANDLE handle; CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; handle = (HANDLE) uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) return UV_EBADF; if (fd <= 2) { /* In order to avoid closing a stdio file descriptor 0-2, duplicate the * underlying OS handle and forget about the original fd. * We could also opt to use the original OS handle and just never close it, * but then there would be no reliable way to cancel pending read operations * upon close. */ if (!DuplicateHandle(INVALID_HANDLE_VALUE, handle, INVALID_HANDLE_VALUE, &handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) return uv_translate_sys_error(GetLastError()); fd = -1; } if (!readable) { /* Obtain the screen buffer info with the output handle. */ if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) { return uv_translate_sys_error(GetLastError()); } /* Obtain the the tty_output_lock because the virtual window state is */ /* shared between all uv_tty_t handles. */ EnterCriticalSection(&uv_tty_output_lock); /* Store the global tty output handle. This handle is used by TTY read */ /* streams to update the virtual window when a CONSOLE_BUFFER_SIZE_EVENT */ /* is received. */ uv_tty_output_handle = handle; /* Remember the original console text attributes. */ uv_tty_capture_initial_style(&screen_buffer_info); uv_tty_update_virtual_window(&screen_buffer_info); LeaveCriticalSection(&uv_tty_output_lock); } uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY); uv_connection_init((uv_stream_t*) tty); tty->handle = handle; tty->u.fd = fd; tty->reqs_pending = 0; tty->flags |= UV_HANDLE_BOUND; if (readable) { /* Initialize TTY input specific fields. */ tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE; tty->tty.rd.read_line_handle = NULL; tty->tty.rd.read_line_buffer = uv_null_buf_; tty->tty.rd.read_raw_wait = NULL; /* Init keycode-to-vt100 mapper state. */ tty->tty.rd.last_key_len = 0; tty->tty.rd.last_key_offset = 0; tty->tty.rd.last_utf16_high_surrogate = 0; memset(&tty->tty.rd.last_input_record, 0, sizeof tty->tty.rd.last_input_record); } else { /* TTY output specific fields. */ tty->flags |= UV_HANDLE_WRITABLE; /* Init utf8-to-utf16 conversion state. */ tty->tty.wr.utf8_bytes_left = 0; tty->tty.wr.utf8_codepoint = 0; /* Initialize eol conversion state */ tty->tty.wr.previous_eol = 0; /* Init ANSI parser state. */ tty->tty.wr.ansi_parser_state = ANSI_NORMAL; } return 0; } /* Set the default console text attributes based on how the console was * configured when libuv started. */ static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { static int style_captured = 0; /* Only do this once. Assumption: Caller has acquired uv_tty_output_lock. */ if (style_captured) return; /* Save raw win32 attributes. */ uv_tty_default_text_attributes = info->wAttributes; /* Convert black text on black background to use white text. */ if (uv_tty_default_text_attributes == 0) uv_tty_default_text_attributes = 7; /* Convert Win32 attributes to ANSI colors. */ uv_tty_default_fg_color = 0; uv_tty_default_bg_color = 0; uv_tty_default_fg_bright = 0; uv_tty_default_bg_bright = 0; uv_tty_default_inverse = 0; if (uv_tty_default_text_attributes & FOREGROUND_RED) uv_tty_default_fg_color |= 1; if (uv_tty_default_text_attributes & FOREGROUND_GREEN) uv_tty_default_fg_color |= 2; if (uv_tty_default_text_attributes & FOREGROUND_BLUE) uv_tty_default_fg_color |= 4; if (uv_tty_default_text_attributes & BACKGROUND_RED) uv_tty_default_bg_color |= 1; if (uv_tty_default_text_attributes & BACKGROUND_GREEN) uv_tty_default_bg_color |= 2; if (uv_tty_default_text_attributes & BACKGROUND_BLUE) uv_tty_default_bg_color |= 4; if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY) uv_tty_default_fg_bright = 1; if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY) uv_tty_default_bg_bright = 1; if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO) uv_tty_default_inverse = 1; style_captured = 1; } int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { DWORD flags; unsigned char was_reading; uv_alloc_cb alloc_cb; uv_read_cb read_cb; int err; if (!(tty->flags & UV_HANDLE_TTY_READABLE)) { return UV_EINVAL; } if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) { return 0; } switch (mode) { case UV_TTY_MODE_NORMAL: flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; break; case UV_TTY_MODE_RAW: flags = ENABLE_WINDOW_INPUT; break; case UV_TTY_MODE_IO: return UV_ENOTSUP; } if (!SetConsoleMode(tty->handle, flags)) { return uv_translate_sys_error(GetLastError()); } /* If currently reading, stop, and restart reading. */ if (tty->flags & UV_HANDLE_READING) { was_reading = 1; alloc_cb = tty->alloc_cb; read_cb = tty->read_cb; if (was_reading) { err = uv_tty_read_stop(tty); if (err) { return uv_translate_sys_error(err); } } } else { was_reading = 0; } /* Update flag. */ tty->flags &= ~UV_HANDLE_TTY_RAW; tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0; /* If we just stopped reading, restart. */ if (was_reading) { err = uv_tty_read_start(tty, alloc_cb, read_cb); if (err) { return uv_translate_sys_error(err); } } return 0; } int uv_is_tty(uv_file file) { DWORD result; return GetConsoleMode((HANDLE) _get_osfhandle(file), &result) != 0; } int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { CONSOLE_SCREEN_BUFFER_INFO info; if (!GetConsoleScreenBufferInfo(tty->handle, &info)) { return uv_translate_sys_error(GetLastError()); } EnterCriticalSection(&uv_tty_output_lock); uv_tty_update_virtual_window(&info); LeaveCriticalSection(&uv_tty_output_lock); *width = uv_tty_virtual_width; *height = uv_tty_virtual_height; return 0; } static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) { uv_loop_t* loop; uv_tty_t* handle; uv_req_t* req; assert(data); assert(!didTimeout); req = (uv_req_t*) data; handle = (uv_tty_t*) req->data; loop = handle->loop; UnregisterWait(handle->tty.rd.read_raw_wait); handle->tty.rd.read_raw_wait = NULL; SET_REQ_SUCCESS(req); POST_COMPLETION_FOR_REQ(loop, req); } static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) { uv_read_t* req; BOOL r; assert(handle->flags & UV_HANDLE_READING); assert(!(handle->flags & UV_HANDLE_READ_PENDING)); assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE); handle->tty.rd.read_line_buffer = uv_null_buf_; req = &handle->read_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait, handle->handle, uv_tty_post_raw_read, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); if (!r) { handle->tty.rd.read_raw_wait = NULL; SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); } handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; } static DWORD CALLBACK uv_tty_line_read_thread(void* data) { uv_loop_t* loop; uv_tty_t* handle; uv_req_t* req; DWORD bytes, read_bytes; WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3]; DWORD chars, read_chars; assert(data); req = (uv_req_t*) data; handle = (uv_tty_t*) req->data; loop = handle->loop; assert(handle->tty.rd.read_line_buffer.base != NULL); assert(handle->tty.rd.read_line_buffer.len > 0); /* ReadConsole can't handle big buffers. */ if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) { bytes = handle->tty.rd.read_line_buffer.len; } else { bytes = MAX_INPUT_BUFFER_LENGTH; } /* At last, unicode! */ /* One utf-16 codeunit never takes more than 3 utf-8 codeunits to encode */ chars = bytes / 3; if (ReadConsoleW(handle->tty.rd.read_line_handle, (void*) utf16, chars, &read_chars, NULL)) { read_bytes = WideCharToMultiByte(CP_UTF8, 0, utf16, read_chars, handle->tty.rd.read_line_buffer.base, bytes, NULL, NULL); SET_REQ_SUCCESS(req); req->u.io.overlapped.InternalHigh = read_bytes; } else { SET_REQ_ERROR(req, GetLastError()); } POST_COMPLETION_FOR_REQ(loop, req); return 0; } static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { uv_read_t* req; BOOL r; assert(handle->flags & UV_HANDLE_READING); assert(!(handle->flags & UV_HANDLE_READ_PENDING)); assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE); req = &handle->read_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); handle->alloc_cb((uv_handle_t*) handle, 8192, &handle->tty.rd.read_line_buffer); if (handle->tty.rd.read_line_buffer.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tty.rd.read_line_buffer); return; } assert(handle->tty.rd.read_line_buffer.base != NULL); /* Duplicate the console handle, so if we want to cancel the read, we can */ /* just close this handle duplicate. */ if (handle->tty.rd.read_line_handle == NULL) { HANDLE this_process = GetCurrentProcess(); r = DuplicateHandle(this_process, handle->handle, this_process, &handle->tty.rd.read_line_handle, 0, 0, DUPLICATE_SAME_ACCESS); if (!r) { handle->tty.rd.read_line_handle = NULL; SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); goto out; } } r = QueueUserWorkItem(uv_tty_line_read_thread, (void*) req, WT_EXECUTELONGFUNCTION); if (!r) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); } out: handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; } static void uv_tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) { if (handle->flags & UV_HANDLE_TTY_RAW) { uv_tty_queue_read_raw(loop, handle); } else { uv_tty_queue_read_line(loop, handle); } } static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl, size_t* len) { #define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \ case (vk): \ if (shift && ctrl) { \ *len = sizeof shift_ctrl_str; \ return "\033" shift_ctrl_str; \ } else if (shift) { \ *len = sizeof shift_str ; \ return "\033" shift_str; \ } else if (ctrl) { \ *len = sizeof ctrl_str; \ return "\033" ctrl_str; \ } else { \ *len = sizeof normal_str; \ return "\033" normal_str; \ } switch (code) { /* These mappings are the same as Cygwin's. Unmodified and alt-modified */ /* keypad keys comply with linux console, modifiers comply with xterm */ /* modifier usage. F1..f12 and shift-f1..f10 comply with linux console, */ /* f6..f12 with and without modifiers comply with rxvt. */ VK_CASE(VK_INSERT, "[2~", "[2;2~", "[2;5~", "[2;6~") VK_CASE(VK_END, "[4~", "[4;2~", "[4;5~", "[4;6~") VK_CASE(VK_DOWN, "[B", "[1;2B", "[1;5B", "[1;6B") VK_CASE(VK_NEXT, "[6~", "[6;2~", "[6;5~", "[6;6~") VK_CASE(VK_LEFT, "[D", "[1;2D", "[1;5D", "[1;6D") VK_CASE(VK_CLEAR, "[G", "[1;2G", "[1;5G", "[1;6G") VK_CASE(VK_RIGHT, "[C", "[1;2C", "[1;5C", "[1;6C") VK_CASE(VK_UP, "[A", "[1;2A", "[1;5A", "[1;6A") VK_CASE(VK_HOME, "[1~", "[1;2~", "[1;5~", "[1;6~") VK_CASE(VK_PRIOR, "[5~", "[5;2~", "[5;5~", "[5;6~") VK_CASE(VK_DELETE, "[3~", "[3;2~", "[3;5~", "[3;6~") VK_CASE(VK_NUMPAD0, "[2~", "[2;2~", "[2;5~", "[2;6~") VK_CASE(VK_NUMPAD1, "[4~", "[4;2~", "[4;5~", "[4;6~") VK_CASE(VK_NUMPAD2, "[B", "[1;2B", "[1;5B", "[1;6B") VK_CASE(VK_NUMPAD3, "[6~", "[6;2~", "[6;5~", "[6;6~") VK_CASE(VK_NUMPAD4, "[D", "[1;2D", "[1;5D", "[1;6D") VK_CASE(VK_NUMPAD5, "[G", "[1;2G", "[1;5G", "[1;6G") VK_CASE(VK_NUMPAD6, "[C", "[1;2C", "[1;5C", "[1;6C") VK_CASE(VK_NUMPAD7, "[A", "[1;2A", "[1;5A", "[1;6A") VK_CASE(VK_NUMPAD8, "[1~", "[1;2~", "[1;5~", "[1;6~") VK_CASE(VK_NUMPAD9, "[5~", "[5;2~", "[5;5~", "[5;6~") VK_CASE(VK_DECIMAL, "[3~", "[3;2~", "[3;5~", "[3;6~") VK_CASE(VK_F1, "[[A", "[23~", "[11^", "[23^" ) VK_CASE(VK_F2, "[[B", "[24~", "[12^", "[24^" ) VK_CASE(VK_F3, "[[C", "[25~", "[13^", "[25^" ) VK_CASE(VK_F4, "[[D", "[26~", "[14^", "[26^" ) VK_CASE(VK_F5, "[[E", "[28~", "[15^", "[28^" ) VK_CASE(VK_F6, "[17~", "[29~", "[17^", "[29^" ) VK_CASE(VK_F7, "[18~", "[31~", "[18^", "[31^" ) VK_CASE(VK_F8, "[19~", "[32~", "[19^", "[32^" ) VK_CASE(VK_F9, "[20~", "[33~", "[20^", "[33^" ) VK_CASE(VK_F10, "[21~", "[34~", "[21^", "[34^" ) VK_CASE(VK_F11, "[23~", "[23$", "[23^", "[23@" ) VK_CASE(VK_F12, "[24~", "[24$", "[24^", "[24@" ) default: *len = 0; return NULL; } #undef VK_CASE } void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req) { /* Shortcut for handle->tty.rd.last_input_record.Event.KeyEvent. */ #define KEV handle->tty.rd.last_input_record.Event.KeyEvent DWORD records_left, records_read; uv_buf_t buf; off_t buf_used; assert(handle->type == UV_TTY); assert(handle->flags & UV_HANDLE_TTY_READABLE); handle->flags &= ~UV_HANDLE_READ_PENDING; if (!(handle->flags & UV_HANDLE_READING) || !(handle->flags & UV_HANDLE_TTY_RAW)) { goto out; } if (!REQ_SUCCESS(req)) { /* An error occurred while waiting for the event. */ if ((handle->flags & UV_HANDLE_READING)) { handle->flags &= ~UV_HANDLE_READING; handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(GET_REQ_ERROR(req)), &uv_null_buf_); } goto out; } /* Fetch the number of events */ if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(GetLastError()), &uv_null_buf_); goto out; } /* Windows sends a lot of events that we're not interested in, so buf */ /* will be allocated on demand, when there's actually something to emit. */ buf = uv_null_buf_; buf_used = 0; while ((records_left > 0 || handle->tty.rd.last_key_len > 0) && (handle->flags & UV_HANDLE_READING)) { if (handle->tty.rd.last_key_len == 0) { /* Read the next input record */ if (!ReadConsoleInputW(handle->handle, &handle->tty.rd.last_input_record, 1, &records_read)) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*) handle, uv_translate_sys_error(GetLastError()), &buf); goto out; } records_left--; /* If the window was resized, recompute the virtual window size. This */ /* will trigger a SIGWINCH signal if the window size changed in an */ /* way that matters to libuv. */ if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) { CONSOLE_SCREEN_BUFFER_INFO info; EnterCriticalSection(&uv_tty_output_lock); if (uv_tty_output_handle != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(uv_tty_output_handle, &info)) { uv_tty_update_virtual_window(&info); } LeaveCriticalSection(&uv_tty_output_lock); continue; } /* Ignore other events that are not key or resize events. */ if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) { continue; } /* Ignore keyup events, unless the left alt key was held and a valid */ /* unicode character was emitted. */ if (!KEV.bKeyDown && !(((KEV.dwControlKeyState & LEFT_ALT_PRESSED) || KEV.wVirtualKeyCode==VK_MENU) && KEV.uChar.UnicodeChar != 0)) { continue; } /* Ignore keypresses to numpad number keys if the left alt is held */ /* because the user is composing a character, or windows simulating */ /* this. */ if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) && !(KEV.dwControlKeyState & ENHANCED_KEY) && (KEV.wVirtualKeyCode == VK_INSERT || KEV.wVirtualKeyCode == VK_END || KEV.wVirtualKeyCode == VK_DOWN || KEV.wVirtualKeyCode == VK_NEXT || KEV.wVirtualKeyCode == VK_LEFT || KEV.wVirtualKeyCode == VK_CLEAR || KEV.wVirtualKeyCode == VK_RIGHT || KEV.wVirtualKeyCode == VK_HOME || KEV.wVirtualKeyCode == VK_UP || KEV.wVirtualKeyCode == VK_PRIOR || KEV.wVirtualKeyCode == VK_NUMPAD0 || KEV.wVirtualKeyCode == VK_NUMPAD1 || KEV.wVirtualKeyCode == VK_NUMPAD2 || KEV.wVirtualKeyCode == VK_NUMPAD3 || KEV.wVirtualKeyCode == VK_NUMPAD4 || KEV.wVirtualKeyCode == VK_NUMPAD5 || KEV.wVirtualKeyCode == VK_NUMPAD6 || KEV.wVirtualKeyCode == VK_NUMPAD7 || KEV.wVirtualKeyCode == VK_NUMPAD8 || KEV.wVirtualKeyCode == VK_NUMPAD9)) { continue; } if (KEV.uChar.UnicodeChar != 0) { int prefix_len, char_len; /* Character key pressed */ if (KEV.uChar.UnicodeChar >= 0xD800 && KEV.uChar.UnicodeChar < 0xDC00) { /* UTF-16 high surrogate */ handle->tty.rd.last_utf16_high_surrogate = KEV.uChar.UnicodeChar; continue; } /* Prefix with \u033 if alt was held, but alt was not used as part */ /* a compose sequence. */ if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) { handle->tty.rd.last_key[0] = '\033'; prefix_len = 1; } else { prefix_len = 0; } if (KEV.uChar.UnicodeChar >= 0xDC00 && KEV.uChar.UnicodeChar < 0xE000) { /* UTF-16 surrogate pair */ WCHAR utf16_buffer[2] = { handle->tty.rd.last_utf16_high_surrogate, KEV.uChar.UnicodeChar}; char_len = WideCharToMultiByte(CP_UTF8, 0, utf16_buffer, 2, &handle->tty.rd.last_key[prefix_len], sizeof handle->tty.rd.last_key, NULL, NULL); } else { /* Single UTF-16 character */ char_len = WideCharToMultiByte(CP_UTF8, 0, &KEV.uChar.UnicodeChar, 1, &handle->tty.rd.last_key[prefix_len], sizeof handle->tty.rd.last_key, NULL, NULL); } /* Whatever happened, the last character wasn't a high surrogate. */ handle->tty.rd.last_utf16_high_surrogate = 0; /* If the utf16 character(s) couldn't be converted something must */ /* be wrong. */ if (!char_len) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*) handle, uv_translate_sys_error(GetLastError()), &buf); goto out; } handle->tty.rd.last_key_len = (unsigned char) (prefix_len + char_len); handle->tty.rd.last_key_offset = 0; continue; } else { /* Function key pressed */ const char* vt100; size_t prefix_len, vt100_len; vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode, !!(KEV.dwControlKeyState & SHIFT_PRESSED), !!(KEV.dwControlKeyState & ( LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)), &vt100_len); /* If we were unable to map to a vt100 sequence, just ignore. */ if (!vt100) { continue; } /* Prefix with \x033 when the alt key was held. */ if (KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) { handle->tty.rd.last_key[0] = '\033'; prefix_len = 1; } else { prefix_len = 0; } /* Copy the vt100 sequence to the handle buffer. */ assert(prefix_len + vt100_len < sizeof handle->tty.rd.last_key); memcpy(&handle->tty.rd.last_key[prefix_len], vt100, vt100_len); handle->tty.rd.last_key_len = (unsigned char) (prefix_len + vt100_len); handle->tty.rd.last_key_offset = 0; continue; } } else { /* Copy any bytes left from the last keypress to the user buffer. */ if (handle->tty.rd.last_key_offset < handle->tty.rd.last_key_len) { /* Allocate a buffer if needed */ if (buf_used == 0) { handle->alloc_cb((uv_handle_t*) handle, 1024, &buf); if (buf.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); goto out; } assert(buf.base != NULL); } buf.base[buf_used++] = handle->tty.rd.last_key[handle->tty.rd.last_key_offset++]; /* If the buffer is full, emit it */ if ((size_t) buf_used == buf.len) { handle->read_cb((uv_stream_t*) handle, buf_used, &buf); buf = uv_null_buf_; buf_used = 0; } continue; } /* Apply dwRepeat from the last input record. */ if (--KEV.wRepeatCount > 0) { handle->tty.rd.last_key_offset = 0; continue; } handle->tty.rd.last_key_len = 0; continue; } } /* Send the buffer back to the user */ if (buf_used > 0) { handle->read_cb((uv_stream_t*) handle, buf_used, &buf); } out: /* Wait for more input events. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { uv_tty_queue_read(loop, handle); } DECREASE_PENDING_REQ_COUNT(handle); #undef KEV } void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req) { uv_buf_t buf; assert(handle->type == UV_TTY); assert(handle->flags & UV_HANDLE_TTY_READABLE); buf = handle->tty.rd.read_line_buffer; handle->flags &= ~UV_HANDLE_READ_PENDING; handle->tty.rd.read_line_buffer = uv_null_buf_; if (!REQ_SUCCESS(req)) { /* Read was not successful */ if ((handle->flags & UV_HANDLE_READING) && handle->tty.rd.read_line_handle != NULL) { /* Real error */ handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*) handle, uv_translate_sys_error(GET_REQ_ERROR(req)), &buf); } else { /* The read was cancelled, or whatever we don't care */ handle->read_cb((uv_stream_t*) handle, 0, &buf); } } else { /* Read successful */ /* TODO: read unicode, convert to utf-8 */ DWORD bytes = req->u.io.overlapped.InternalHigh; handle->read_cb((uv_stream_t*) handle, bytes, &buf); } /* Wait for more input events. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { uv_tty_queue_read(loop, handle); } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req) { assert(handle->type == UV_TTY); assert(handle->flags & UV_HANDLE_TTY_READABLE); /* If the read_line_buffer member is zero, it must have been an raw read. */ /* Otherwise it was a line-buffered read. */ /* FIXME: This is quite obscure. Use a flag or something. */ if (handle->tty.rd.read_line_buffer.len == 0) { uv_process_tty_read_raw_req(loop, handle, req); } else { uv_process_tty_read_line_req(loop, handle, req); } } int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; if (!(handle->flags & UV_HANDLE_TTY_READABLE)) { return ERROR_INVALID_PARAMETER; } handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; /* If reading was stopped and then started again, there could still be a */ /* read request pending. */ if (handle->flags & UV_HANDLE_READ_PENDING) { return 0; } /* Maybe the user stopped reading half-way while processing key events. */ /* Short-circuit if this could be the case. */ if (handle->tty.rd.last_key_len > 0) { SET_REQ_SUCCESS(&handle->read_req); uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req); return 0; } uv_tty_queue_read(loop, handle); return 0; } int uv_tty_read_stop(uv_tty_t* handle) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(handle->loop, handle); /* Cancel raw read */ if ((handle->flags & UV_HANDLE_READ_PENDING) && (handle->flags & UV_HANDLE_TTY_RAW)) { /* Write some bullshit event to force the console wait to return. */ INPUT_RECORD record; DWORD written; memset(&record, 0, sizeof record); if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) { return GetLastError(); } } /* Cancel line-buffered read */ if (handle->tty.rd.read_line_handle != NULL) { /* Closing this handle will cancel the ReadConsole operation */ CloseHandle(handle->tty.rd.read_line_handle); handle->tty.rd.read_line_handle = NULL; } return 0; } static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { int old_virtual_width = uv_tty_virtual_width; int old_virtual_height = uv_tty_virtual_height; uv_tty_virtual_width = info->dwSize.X; uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; /* Recompute virtual window offset row. */ if (uv_tty_virtual_offset == -1) { uv_tty_virtual_offset = info->dwCursorPosition.Y; } else if (uv_tty_virtual_offset < info->dwCursorPosition.Y - uv_tty_virtual_height + 1) { /* If suddenly find the cursor outside of the virtual window, it must */ /* have somehow scrolled. Update the virtual window offset. */ uv_tty_virtual_offset = info->dwCursorPosition.Y - uv_tty_virtual_height + 1; } if (uv_tty_virtual_offset + uv_tty_virtual_height > info->dwSize.Y) { uv_tty_virtual_offset = info->dwSize.Y - uv_tty_virtual_height; } if (uv_tty_virtual_offset < 0) { uv_tty_virtual_offset = 0; } /* If the virtual window size changed, emit a SIGWINCH signal. Don't emit */ /* if this was the first time the virtual window size was computed. */ if (old_virtual_width != -1 && old_virtual_height != -1 && (uv_tty_virtual_width != old_virtual_width || uv_tty_virtual_height != old_virtual_height)) { uv__signal_dispatch(SIGWINCH); } } static COORD uv_tty_make_real_coord(uv_tty_t* handle, CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y, unsigned char y_relative) { COORD result; uv_tty_update_virtual_window(info); /* Adjust y position */ if (y_relative) { y = info->dwCursorPosition.Y + y; } else { y = uv_tty_virtual_offset + y; } /* Clip y to virtual client rectangle */ if (y < uv_tty_virtual_offset) { y = uv_tty_virtual_offset; } else if (y >= uv_tty_virtual_offset + uv_tty_virtual_height) { y = uv_tty_virtual_offset + uv_tty_virtual_height - 1; } /* Adjust x */ if (x_relative) { x = info->dwCursorPosition.X + x; } /* Clip x */ if (x < 0) { x = 0; } else if (x >= uv_tty_virtual_width) { x = uv_tty_virtual_width - 1; } result.X = (unsigned short) x; result.Y = (unsigned short) y; return result; } static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length, DWORD* error) { DWORD written; if (*error != ERROR_SUCCESS) { return -1; } if (!WriteConsoleW(handle->handle, (void*) buffer, length, &written, NULL)) { *error = GetLastError(); return -1; } return 0; } static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, int y, unsigned char y_relative, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; COORD pos; if (*error != ERROR_SUCCESS) { return -1; } retry: if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { *error = GetLastError(); } pos = uv_tty_make_real_coord(handle, &info, x, x_relative, y, y_relative); if (!SetConsoleCursorPosition(handle->handle, pos)) { if (GetLastError() == ERROR_INVALID_PARAMETER) { /* The console may be resized - retry */ goto retry; } else { *error = GetLastError(); return -1; } } return 0; } static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { const COORD origin = {0, 0}; const WORD char_attrs = uv_tty_default_text_attributes; CONSOLE_SCREEN_BUFFER_INFO info; DWORD count, written; if (*error != ERROR_SUCCESS) { return -1; } /* Reset original text attributes. */ if (!SetConsoleTextAttribute(handle->handle, char_attrs)) { *error = GetLastError(); return -1; } /* Move the cursor position to (0, 0). */ if (!SetConsoleCursorPosition(handle->handle, origin)) { *error = GetLastError(); return -1; } /* Clear the screen buffer. */ retry: if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { *error = GetLastError(); return -1; } count = info.dwSize.X * info.dwSize.Y; if (!(FillConsoleOutputCharacterW(handle->handle, L'\x20', count, origin, &written) && FillConsoleOutputAttribute(handle->handle, char_attrs, written, origin, &written))) { if (GetLastError() == ERROR_INVALID_PARAMETER) { /* The console may be resized - retry */ goto retry; } else { *error = GetLastError(); return -1; } } /* Move the virtual window up to the top. */ uv_tty_virtual_offset = 0; uv_tty_update_virtual_window(&info); return 0; } static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; COORD start, end; DWORD count, written; int x1, x2, y1, y2; int x1r, x2r, y1r, y2r; if (*error != ERROR_SUCCESS) { return -1; } if (dir == 0) { /* Clear from current position */ x1 = 0; x1r = 1; } else { /* Clear from column 0 */ x1 = 0; x1r = 0; } if (dir == 1) { /* Clear to current position */ x2 = 0; x2r = 1; } else { /* Clear to end of row. We pretend the console is 65536 characters wide, */ /* uv_tty_make_real_coord will clip it to the actual console width. */ x2 = 0xffff; x2r = 0; } if (!entire_screen) { /* Stay on our own row */ y1 = y2 = 0; y1r = y2r = 1; } else { /* Apply columns direction to row */ y1 = x1; y1r = x1r; y2 = x2; y2r = x2r; } retry: if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { *error = GetLastError(); return -1; } start = uv_tty_make_real_coord(handle, &info, x1, x1r, y1, y1r); end = uv_tty_make_real_coord(handle, &info, x2, x2r, y2, y2r); count = (end.Y * info.dwSize.X + end.X) - (start.Y * info.dwSize.X + start.X) + 1; if (!(FillConsoleOutputCharacterW(handle->handle, L'\x20', count, start, &written) && FillConsoleOutputAttribute(handle->handle, info.wAttributes, written, start, &written))) { if (GetLastError() == ERROR_INVALID_PARAMETER) { /* The console may be resized - retry */ goto retry; } else { *error = GetLastError(); return -1; } } return 0; } #define FLIP_FGBG \ do { \ WORD fg = info.wAttributes & 0xF; \ WORD bg = info.wAttributes & 0xF0; \ info.wAttributes &= 0xFF00; \ info.wAttributes |= fg << 4; \ info.wAttributes |= bg >> 4; \ } while (0) static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) { unsigned short argc = handle->tty.wr.ansi_csi_argc; unsigned short* argv = handle->tty.wr.ansi_csi_argv; int i; CONSOLE_SCREEN_BUFFER_INFO info; char fg_color = -1, bg_color = -1; char fg_bright = -1, bg_bright = -1; char inverse = -1; if (argc == 0) { /* Reset mode */ fg_color = uv_tty_default_fg_color; bg_color = uv_tty_default_bg_color; fg_bright = uv_tty_default_fg_bright; bg_bright = uv_tty_default_bg_bright; inverse = uv_tty_default_inverse; } for (i = 0; i < argc; i++) { short arg = argv[i]; if (arg == 0) { /* Reset mode */ fg_color = uv_tty_default_fg_color; bg_color = uv_tty_default_bg_color; fg_bright = uv_tty_default_fg_bright; bg_bright = uv_tty_default_bg_bright; inverse = uv_tty_default_inverse; } else if (arg == 1) { /* Foreground bright on */ fg_bright = 1; } else if (arg == 2) { /* Both bright off */ fg_bright = 0; bg_bright = 0; } else if (arg == 5) { /* Background bright on */ bg_bright = 1; } else if (arg == 7) { /* Inverse: on */ inverse = 1; } else if (arg == 21 || arg == 22) { /* Foreground bright off */ fg_bright = 0; } else if (arg == 25) { /* Background bright off */ bg_bright = 0; } else if (arg == 27) { /* Inverse: off */ inverse = 0; } else if (arg >= 30 && arg <= 37) { /* Set foreground color */ fg_color = arg - 30; } else if (arg == 39) { /* Default text color */ fg_color = uv_tty_default_fg_color; fg_bright = uv_tty_default_fg_bright; } else if (arg >= 40 && arg <= 47) { /* Set background color */ bg_color = arg - 40; } else if (arg == 49) { /* Default background color */ bg_color = uv_tty_default_bg_color; bg_bright = uv_tty_default_bg_bright; } else if (arg >= 90 && arg <= 97) { /* Set bold foreground color */ fg_bright = 1; fg_color = arg - 90; } else if (arg >= 100 && arg <= 107) { /* Set bold background color */ bg_bright = 1; bg_color = arg - 100; } } if (fg_color == -1 && bg_color == -1 && fg_bright == -1 && bg_bright == -1 && inverse == -1) { /* Nothing changed */ return 0; } if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { *error = GetLastError(); return -1; } if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) { FLIP_FGBG; } if (fg_color != -1) { info.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); if (fg_color & 1) info.wAttributes |= FOREGROUND_RED; if (fg_color & 2) info.wAttributes |= FOREGROUND_GREEN; if (fg_color & 4) info.wAttributes |= FOREGROUND_BLUE; } if (fg_bright != -1) { if (fg_bright) { info.wAttributes |= FOREGROUND_INTENSITY; } else { info.wAttributes &= ~FOREGROUND_INTENSITY; } } if (bg_color != -1) { info.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); if (bg_color & 1) info.wAttributes |= BACKGROUND_RED; if (bg_color & 2) info.wAttributes |= BACKGROUND_GREEN; if (bg_color & 4) info.wAttributes |= BACKGROUND_BLUE; } if (bg_bright != -1) { if (bg_bright) { info.wAttributes |= BACKGROUND_INTENSITY; } else { info.wAttributes &= ~BACKGROUND_INTENSITY; } } if (inverse != -1) { if (inverse) { info.wAttributes |= COMMON_LVB_REVERSE_VIDEO; } else { info.wAttributes &= ~COMMON_LVB_REVERSE_VIDEO; } } if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) { FLIP_FGBG; } if (!SetConsoleTextAttribute(handle->handle, info.wAttributes)) { *error = GetLastError(); return -1; } return 0; } static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; if (*error != ERROR_SUCCESS) { return -1; } if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { *error = GetLastError(); return -1; } uv_tty_update_virtual_window(&info); handle->tty.wr.saved_position.X = info.dwCursorPosition.X; handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y - uv_tty_virtual_offset; handle->flags |= UV_HANDLE_TTY_SAVED_POSITION; if (save_attributes) { handle->tty.wr.saved_attributes = info.wAttributes & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES; } return 0; } static int uv_tty_restore_state(uv_tty_t* handle, unsigned char restore_attributes, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; WORD new_attributes; if (*error != ERROR_SUCCESS) { return -1; } if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) { if (uv_tty_move_caret(handle, handle->tty.wr.saved_position.X, 0, handle->tty.wr.saved_position.Y, 0, error) != 0) { return -1; } } if (restore_attributes && (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) { if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { *error = GetLastError(); return -1; } new_attributes = info.wAttributes; new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); new_attributes |= handle->tty.wr.saved_attributes; if (!SetConsoleTextAttribute(handle->handle, new_attributes)) { *error = GetLastError(); return -1; } } return 0; } static int uv_tty_set_cursor_visibility(uv_tty_t* handle, BOOL visible, DWORD* error) { CONSOLE_CURSOR_INFO cursor_info; if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) { *error = GetLastError(); return -1; } cursor_info.bVisible = visible; if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) { *error = GetLastError(); return -1; } return 0; } static int uv_tty_write_bufs(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, DWORD* error) { /* We can only write 8k characters at a time. Windows can't handle */ /* much more characters in a single console write anyway. */ WCHAR utf16_buf[8192]; DWORD utf16_buf_used = 0; unsigned int i; #define FLUSH_TEXT() \ do { \ if (utf16_buf_used > 0) { \ uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \ utf16_buf_used = 0; \ } \ } while (0) #define ENSURE_BUFFER_SPACE(wchars_needed) \ if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { \ FLUSH_TEXT(); \ } /* Cache for fast access */ unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left; unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint; unsigned char previous_eol = handle->tty.wr.previous_eol; unsigned char ansi_parser_state = handle->tty.wr.ansi_parser_state; /* Store the error here. If we encounter an error, stop trying to do i/o */ /* but keep parsing the buffer so we leave the parser in a consistent */ /* state. */ *error = ERROR_SUCCESS; EnterCriticalSection(&uv_tty_output_lock); for (i = 0; i < nbufs; i++) { uv_buf_t buf = bufs[i]; unsigned int j; for (j = 0; j < buf.len; j++) { unsigned char c = buf.base[j]; /* Run the character through the utf8 decoder We happily accept non */ /* shortest form encodings and invalid code points - there's no real */ /* harm that can be done. */ if (utf8_bytes_left == 0) { /* Read utf-8 start byte */ DWORD first_zero_bit; unsigned char not_c = ~c; #ifdef _MSC_VER /* msvc */ if (_BitScanReverse(&first_zero_bit, not_c)) { #else /* assume gcc */ if (c != 0) { first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c); #endif if (first_zero_bit == 7) { /* Ascii - pass right through */ utf8_codepoint = (unsigned int) c; } else if (first_zero_bit <= 5) { /* Multibyte sequence */ utf8_codepoint = (0xff >> (8 - first_zero_bit)) & c; utf8_bytes_left = (char) (6 - first_zero_bit); } else { /* Invalid continuation */ utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; } } else { /* 0xff -- invalid */ utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; } } else if ((c & 0xc0) == 0x80) { /* Valid continuation of utf-8 multibyte sequence */ utf8_bytes_left--; utf8_codepoint <<= 6; utf8_codepoint |= ((unsigned int) c & 0x3f); } else { /* Start byte where continuation was expected. */ utf8_bytes_left = 0; utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; /* Patch buf offset so this character will be parsed again as a */ /* start byte. */ j--; } /* Maybe we need to parse more bytes to find a character. */ if (utf8_bytes_left != 0) { continue; } /* Parse vt100/ansi escape codes */ if (ansi_parser_state == ANSI_NORMAL) { switch (utf8_codepoint) { case '\033': ansi_parser_state = ANSI_ESCAPE_SEEN; continue; case 0233: ansi_parser_state = ANSI_CSI; handle->tty.wr.ansi_csi_argc = 0; continue; } } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) { switch (utf8_codepoint) { case '[': ansi_parser_state = ANSI_CSI; handle->tty.wr.ansi_csi_argc = 0; continue; case '^': case '_': case 'P': case ']': /* Not supported, but we'll have to parse until we see a stop */ /* code, e.g. ESC \ or BEL. */ ansi_parser_state = ANSI_ST_CONTROL; continue; case '\033': /* Ignore double escape. */ continue; case 'c': /* Full console reset. */ FLUSH_TEXT(); uv_tty_reset(handle, error); ansi_parser_state = ANSI_NORMAL; continue; case '7': /* Save the cursor position and text attributes. */ FLUSH_TEXT(); uv_tty_save_state(handle, 1, error); ansi_parser_state = ANSI_NORMAL; continue; case '8': /* Restore the cursor position and text attributes */ FLUSH_TEXT(); uv_tty_restore_state(handle, 1, error); ansi_parser_state = ANSI_NORMAL; continue; default: if (utf8_codepoint >= '@' && utf8_codepoint <= '_') { /* Single-char control. */ ansi_parser_state = ANSI_NORMAL; continue; } else { /* Invalid - proceed as normal, */ ansi_parser_state = ANSI_NORMAL; } } } else if (ansi_parser_state & ANSI_CSI) { if (!(ansi_parser_state & ANSI_IGNORE)) { if (utf8_codepoint >= '0' && utf8_codepoint <= '9') { /* Parsing a numerical argument */ if (!(ansi_parser_state & ANSI_IN_ARG)) { /* We were not currently parsing a number */ /* Check for too many arguments */ if (handle->tty.wr.ansi_csi_argc >= ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { ansi_parser_state |= ANSI_IGNORE; continue; } ansi_parser_state |= ANSI_IN_ARG; handle->tty.wr.ansi_csi_argc++; handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = (unsigned short) utf8_codepoint - '0'; continue; } else { /* We were already parsing a number. Parse next digit. */ uint32_t value = 10 * handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1]; /* Check for overflow. */ if (value > UINT16_MAX) { ansi_parser_state |= ANSI_IGNORE; continue; } handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = (unsigned short) value + (utf8_codepoint - '0'); continue; } } else if (utf8_codepoint == ';') { /* Denotes the end of an argument. */ if (ansi_parser_state & ANSI_IN_ARG) { ansi_parser_state &= ~ANSI_IN_ARG; continue; } else { /* If ANSI_IN_ARG is not set, add another argument and */ /* default it to 0. */ /* Check for too many arguments */ if (handle->tty.wr.ansi_csi_argc >= ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { ansi_parser_state |= ANSI_IGNORE; continue; } handle->tty.wr.ansi_csi_argc++; handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0; continue; } } else if (utf8_codepoint == '?' && !(ansi_parser_state & ANSI_IN_ARG) && handle->tty.wr.ansi_csi_argc == 0) { /* Ignores '?' if it is the first character after CSI[ */ /* This is an extension character from the VT100 codeset */ /* that is supported and used by most ANSI terminals today. */ continue; } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~' && (handle->tty.wr.ansi_csi_argc > 0 || utf8_codepoint != '[')) { int x, y, d; /* Command byte */ switch (utf8_codepoint) { case 'A': /* cursor up */ FLUSH_TEXT(); y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, 0, 1, y, 1, error); break; case 'B': /* cursor down */ FLUSH_TEXT(); y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, 0, 1, y, 1, error); break; case 'C': /* cursor forward */ FLUSH_TEXT(); x = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, x, 1, 0, 1, error); break; case 'D': /* cursor back */ FLUSH_TEXT(); x = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, x, 1, 0, 1, error); break; case 'E': /* cursor next line */ FLUSH_TEXT(); y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, 0, 0, y, 1, error); break; case 'F': /* cursor previous line */ FLUSH_TEXT(); y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, 0, 0, y, 1, error); break; case 'G': /* cursor horizontal move absolute */ FLUSH_TEXT(); x = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; uv_tty_move_caret(handle, x, 0, 0, 1, error); break; case 'H': case 'f': /* cursor move absolute */ FLUSH_TEXT(); y = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; x = (handle->tty.wr.ansi_csi_argc >= 2 && handle->tty.wr.ansi_csi_argv[1]) ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0; uv_tty_move_caret(handle, x, 0, y, 0, error); break; case 'J': /* Erase screen */ FLUSH_TEXT(); d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { uv_tty_clear(handle, d, 1, error); } break; case 'K': /* Erase line */ FLUSH_TEXT(); d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { uv_tty_clear(handle, d, 0, error); } break; case 'm': /* Set style */ FLUSH_TEXT(); uv_tty_set_style(handle, error); break; case 's': /* Save the cursor position. */ FLUSH_TEXT(); uv_tty_save_state(handle, 0, error); break; case 'u': /* Restore the cursor position */ FLUSH_TEXT(); uv_tty_restore_state(handle, 0, error); break; case 'l': /* Hide the cursor */ if (handle->tty.wr.ansi_csi_argc == 1 && handle->tty.wr.ansi_csi_argv[0] == 25) { FLUSH_TEXT(); uv_tty_set_cursor_visibility(handle, 0, error); } break; case 'h': /* Show the cursor */ if (handle->tty.wr.ansi_csi_argc == 1 && handle->tty.wr.ansi_csi_argv[0] == 25) { FLUSH_TEXT(); uv_tty_set_cursor_visibility(handle, 1, error); } break; } /* Sequence ended - go back to normal state. */ ansi_parser_state = ANSI_NORMAL; continue; } else { /* We don't support commands that use private mode characters or */ /* intermediaries. Ignore the rest of the sequence. */ ansi_parser_state |= ANSI_IGNORE; continue; } } else { /* We're ignoring this command. Stop only on command character. */ if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { ansi_parser_state = ANSI_NORMAL; } continue; } } else if (ansi_parser_state & ANSI_ST_CONTROL) { /* Unsupported control code */ /* Ignore everything until we see BEL or ESC \ */ if (ansi_parser_state & ANSI_IN_STRING) { if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) { if (utf8_codepoint == '"') { ansi_parser_state &= ~ANSI_IN_STRING; } else if (utf8_codepoint == '\\') { ansi_parser_state |= ANSI_BACKSLASH_SEEN; } } else { ansi_parser_state &= ~ANSI_BACKSLASH_SEEN; } } else { if (utf8_codepoint == '\007' || (utf8_codepoint == '\\' && (ansi_parser_state & ANSI_ESCAPE_SEEN))) { /* End of sequence */ ansi_parser_state = ANSI_NORMAL; } else if (utf8_codepoint == '\033') { /* Escape character */ ansi_parser_state |= ANSI_ESCAPE_SEEN; } else if (utf8_codepoint == '"') { /* String starting */ ansi_parser_state |= ANSI_IN_STRING; ansi_parser_state &= ~ANSI_ESCAPE_SEEN; ansi_parser_state &= ~ANSI_BACKSLASH_SEEN; } else { ansi_parser_state &= ~ANSI_ESCAPE_SEEN; } } continue; } else { /* Inconsistent state */ abort(); } /* We wouldn't mind emitting utf-16 surrogate pairs. Too bad, the */ /* windows console doesn't really support UTF-16, so just emit the */ /* replacement character. */ if (utf8_codepoint > 0xffff) { utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; } if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) { /* EOL conversion - emit \r\n when we see \n. */ if (utf8_codepoint == 0x0a && previous_eol != 0x0d) { /* \n was not preceded by \r; print \r\n. */ ENSURE_BUFFER_SPACE(2); utf16_buf[utf16_buf_used++] = L'\r'; utf16_buf[utf16_buf_used++] = L'\n'; } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) { /* \n was followed by \r; do not print the \r, since */ /* the source was either \r\n\r (so the second \r is */ /* redundant) or was \n\r (so the \n was processed */ /* by the last case and an \r automatically inserted). */ } else { /* \r without \n; print \r as-is. */ ENSURE_BUFFER_SPACE(1); utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint; } previous_eol = (char) utf8_codepoint; } else if (utf8_codepoint <= 0xffff) { /* Encode character into utf-16 buffer. */ ENSURE_BUFFER_SPACE(1); utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint; previous_eol = 0; } } } /* Flush remaining characters */ FLUSH_TEXT(); /* Copy cached values back to struct. */ handle->tty.wr.utf8_bytes_left = utf8_bytes_left; handle->tty.wr.utf8_codepoint = utf8_codepoint; handle->tty.wr.previous_eol = previous_eol; handle->tty.wr.ansi_parser_state = ansi_parser_state; LeaveCriticalSection(&uv_tty_output_lock); if (*error == STATUS_SUCCESS) { return 0; } else { return -1; } #undef FLUSH_TEXT } int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) { DWORD error; uv_req_init(loop, (uv_req_t*) req); req->type = UV_WRITE; req->handle = (uv_stream_t*) handle; req->cb = cb; handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); req->u.io.queued_bytes = 0; if (!uv_tty_write_bufs(handle, bufs, nbufs, &error)) { SET_REQ_SUCCESS(req); } else { SET_REQ_ERROR(req, error); } uv_insert_pending_req(loop, (uv_req_t*) req); return 0; } int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs) { DWORD error; if (handle->stream.conn.write_reqs_pending > 0) return UV_EAGAIN; if (uv_tty_write_bufs(handle, bufs, nbufs, &error)) return uv_translate_sys_error(error); return uv__count_bufs(bufs, nbufs); } void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req) { int err; handle->write_queue_size -= req->u.io.queued_bytes; UNREGISTER_HANDLE_REQ(loop, handle, req); if (req->cb) { err = GET_REQ_ERROR(req); req->cb(req, uv_translate_sys_error(err)); } handle->stream.conn.write_reqs_pending--; if (handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*)handle); } DECREASE_PENDING_REQ_COUNT(handle); } void uv_tty_close(uv_tty_t* handle) { assert(handle->u.fd == -1 || handle->u.fd > 2); if (handle->u.fd == -1) CloseHandle(handle->handle); else close(handle->u.fd); if (handle->flags & UV_HANDLE_READING) uv_tty_read_stop(handle); handle->u.fd = -1; handle->handle = INVALID_HANDLE_VALUE; handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(handle); if (handle->reqs_pending == 0) { uv_want_endgame(handle->loop, (uv_handle_t*) handle); } } void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { if (!(handle->flags & UV_HANDLE_TTY_READABLE) && handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); /* TTY shutdown is really just a no-op */ if (handle->stream.conn.shutdown_req->cb) { if (handle->flags & UV__HANDLE_CLOSING) { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED); } else { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0); } } handle->stream.conn.shutdown_req = NULL; DECREASE_PENDING_REQ_COUNT(handle); return; } if (handle->flags & UV__HANDLE_CLOSING && handle->reqs_pending == 0) { /* The console handle duplicate used for line reading should be destroyed */ /* by uv_tty_read_stop. */ assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || handle->tty.rd.read_line_handle == NULL); /* The wait handle used for raw reading should be unregistered when the */ /* wait callback runs. */ assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || handle->tty.rd.read_raw_wait == NULL); assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } } /* TODO: remove me */ void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* raw_req) { abort(); } /* TODO: remove me */ void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, uv_connect_t* req) { abort(); } int uv_tty_reset_mode(void) { /* Not necessary to do anything. */ return 0; } libuv-v1.8.0/src/win/udp.c000066400000000000000000000676221263361716600153770ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "stream-inl.h" #include "req-inl.h" /* * Threshold of active udp streams for which to preallocate udp read buffers. */ const unsigned int uv_active_udp_streams_threshold = 0; /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen) { int result; if (handle->socket == INVALID_SOCKET) { return UV_EINVAL; } result = getsockname(handle->socket, name, namelen); if (result != 0) { return uv_translate_sys_error(WSAGetLastError()); } return 0; } static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, int family) { DWORD yes = 1; WSAPROTOCOL_INFOW info; int opt_len; if (handle->socket != INVALID_SOCKET) return UV_EBUSY; /* Set the socket to nonblocking mode */ if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { return WSAGetLastError(); } /* Make the socket non-inheritable */ if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { return GetLastError(); } /* Associate it with the I/O completion port. */ /* Use uv_handle_t pointer as completion key. */ if (CreateIoCompletionPort((HANDLE)socket, loop->iocp, (ULONG_PTR)socket, 0) == NULL) { return GetLastError(); } if (pSetFileCompletionNotificationModes) { /* All known Windows that support SetFileCompletionNotificationModes */ /* have a bug that makes it impossible to use this function in */ /* conjunction with datagram sockets. We can work around that but only */ /* if the user is using the default UDP driver (AFD) and has no other */ /* LSPs stacked on top. Here we check whether that is the case. */ opt_len = (int) sizeof info; if (getsockopt(socket, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &info, &opt_len) == SOCKET_ERROR) { return GetLastError(); } if (info.ProtocolChain.ChainLen == 1) { if (pSetFileCompletionNotificationModes((HANDLE)socket, FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; handle->func_wsarecv = uv_wsarecv_workaround; handle->func_wsarecvfrom = uv_wsarecvfrom_workaround; } else if (GetLastError() != ERROR_INVALID_FUNCTION) { return GetLastError(); } } } handle->socket = socket; if (family == AF_INET6) { handle->flags |= UV_HANDLE_IPV6; } else { assert(!(handle->flags & UV_HANDLE_IPV6)); } return 0; } int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { int domain; /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) return UV_EINVAL; if (flags & ~0xFF) return UV_EINVAL; uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP); handle->socket = INVALID_SOCKET; handle->reqs_pending = 0; handle->activecnt = 0; handle->func_wsarecv = WSARecv; handle->func_wsarecvfrom = WSARecvFrom; handle->send_queue_size = 0; handle->send_queue_count = 0; uv_req_init(loop, (uv_req_t*) &(handle->recv_req)); handle->recv_req.type = UV_UDP_RECV; handle->recv_req.data = handle; /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init. */ if (domain != AF_UNSPEC) { SOCKET sock; DWORD err; sock = socket(domain, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) { err = WSAGetLastError(); QUEUE_REMOVE(&handle->handle_queue); return uv_translate_sys_error(err); } err = uv_udp_set_socket(handle->loop, handle, sock, domain); if (err) { closesocket(sock); QUEUE_REMOVE(&handle->handle_queue); return uv_translate_sys_error(err); } } return 0; } int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { return uv_udp_init_ex(loop, handle, AF_UNSPEC); } void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { uv_udp_recv_stop(handle); closesocket(handle->socket); handle->socket = INVALID_SOCKET; uv__handle_closing(handle); if (handle->reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); } } void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { if (handle->flags & UV__HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } } static int uv_udp_maybe_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags) { int r; int err; DWORD no = 0; if (handle->flags & UV_HANDLE_BOUND) return 0; if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) { /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */ return ERROR_INVALID_PARAMETER; } if (handle->socket == INVALID_SOCKET) { SOCKET sock = socket(addr->sa_family, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) { return WSAGetLastError(); } err = uv_udp_set_socket(handle->loop, handle, sock, addr->sa_family); if (err) { closesocket(sock); return err; } } if (flags & UV_UDP_REUSEADDR) { DWORD yes = 1; /* Set SO_REUSEADDR on the socket. */ if (setsockopt(handle->socket, SOL_SOCKET, SO_REUSEADDR, (char*) &yes, sizeof yes) == SOCKET_ERROR) { err = WSAGetLastError(); return err; } } if (addr->sa_family == AF_INET6) handle->flags |= UV_HANDLE_IPV6; if (addr->sa_family == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) { /* On windows IPV6ONLY is on by default. */ /* If the user doesn't specify it libuv turns it off. */ /* TODO: how to handle errors? This may fail if there is no ipv4 stack */ /* available, or when run on XP/2003 which have no support for dualstack */ /* sockets. For now we're silently ignoring the error. */ setsockopt(handle->socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*) &no, sizeof no); } r = bind(handle->socket, addr, addrlen); if (r == SOCKET_ERROR) { return WSAGetLastError(); } handle->flags |= UV_HANDLE_BOUND; return 0; } static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { uv_req_t* req; uv_buf_t buf; DWORD bytes, flags; int result; assert(handle->flags & UV_HANDLE_READING); assert(!(handle->flags & UV_HANDLE_READ_PENDING)); req = &handle->recv_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); /* * Preallocate a read buffer if the number of active streams is below * the threshold. */ if (loop->active_udp_streams < uv_active_udp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->recv_buffer); if (handle->recv_buffer.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0); return; } assert(handle->recv_buffer.base != NULL); buf = handle->recv_buffer; memset(&handle->recv_from, 0, sizeof handle->recv_from); handle->recv_from_len = sizeof handle->recv_from; flags = 0; result = handle->func_wsarecvfrom(handle->socket, (WSABUF*) &buf, 1, &bytes, &flags, (struct sockaddr*) &handle->recv_from, &handle->recv_from_len, &req->u.io.overlapped, NULL); if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Process the req without IOCP. */ handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; handle->reqs_pending++; uv_insert_pending_req(loop, req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* The req will be processed with IOCP. */ handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, req); handle->reqs_pending++; } } else { handle->flags |= UV_HANDLE_ZERO_READ; buf.base = (char*) uv_zero_; buf.len = 0; flags = MSG_PEEK; result = handle->func_wsarecv(handle->socket, (WSABUF*) &buf, 1, &bytes, &flags, &req->u.io.overlapped, NULL); if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Process the req without IOCP. */ handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; handle->reqs_pending++; uv_insert_pending_req(loop, req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* The req will be processed with IOCP. */ handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, req); handle->reqs_pending++; } } } int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { uv_loop_t* loop = handle->loop; int err; if (handle->flags & UV_HANDLE_READING) { return WSAEALREADY; } err = uv_udp_maybe_bind(handle, (const struct sockaddr*) &uv_addr_ip4_any_, sizeof(uv_addr_ip4_any_), 0); if (err) return err; handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); loop->active_udp_streams++; handle->recv_cb = recv_cb; handle->alloc_cb = alloc_cb; /* If reading was stopped and then started again, there could still be a */ /* recv request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) uv_udp_queue_recv(loop, handle); return 0; } int uv__udp_recv_stop(uv_udp_t* handle) { if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; handle->loop->active_udp_streams--; DECREASE_ACTIVE_COUNT(loop, handle); } return 0; } static int uv__send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen, uv_udp_send_cb cb) { uv_loop_t* loop = handle->loop; DWORD result, bytes; uv_req_init(loop, (uv_req_t*) req); req->type = UV_UDP_SEND; req->handle = handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); result = WSASendTo(handle->socket, (WSABUF*)bufs, nbufs, &bytes, 0, addr, addrlen, &req->u.io.overlapped, NULL); if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Request completed immediately. */ req->u.io.queued_bytes = 0; handle->reqs_pending++; handle->send_queue_size += req->u.io.queued_bytes; handle->send_queue_count++; REGISTER_HANDLE_REQ(loop, handle, req); uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* Request queued by the kernel. */ req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs); handle->reqs_pending++; handle->send_queue_size += req->u.io.queued_bytes; handle->send_queue_count++; REGISTER_HANDLE_REQ(loop, handle, req); } else { /* Send failed due to an error. */ return WSAGetLastError(); } return 0; } void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req) { uv_buf_t buf; int partial; assert(handle->type == UV_UDP); handle->flags &= ~UV_HANDLE_READ_PENDING; if (!REQ_SUCCESS(req)) { DWORD err = GET_REQ_SOCK_ERROR(req); if (err == WSAEMSGSIZE) { /* Not a real error, it just indicates that the received packet */ /* was bigger than the receive buffer. */ } else if (err == WSAECONNRESET || err == WSAENETRESET) { /* A previous sendto operation failed; ignore this error. If */ /* zero-reading we need to call WSARecv/WSARecvFrom _without_ the */ /* MSG_PEEK flag to clear out the error queue. For nonzero reads, */ /* immediately queue a new receive. */ if (!(handle->flags & UV_HANDLE_ZERO_READ)) { goto done; } } else { /* A real error occurred. Report the error to the user only if we're */ /* currently reading. */ if (handle->flags & UV_HANDLE_READING) { uv_udp_recv_stop(handle); buf = (handle->flags & UV_HANDLE_ZERO_READ) ? uv_buf_init(NULL, 0) : handle->recv_buffer; handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0); } goto done; } } if (!(handle->flags & UV_HANDLE_ZERO_READ)) { /* Successful read */ partial = !REQ_SUCCESS(req); handle->recv_cb(handle, req->u.io.overlapped.InternalHigh, &handle->recv_buffer, (const struct sockaddr*) &handle->recv_from, partial ? UV_UDP_PARTIAL : 0); } else if (handle->flags & UV_HANDLE_READING) { DWORD bytes, err, flags; struct sockaddr_storage from; int from_len; /* Do a nonblocking receive */ /* TODO: try to read multiple datagrams at once. FIONREAD maybe? */ handle->alloc_cb((uv_handle_t*) handle, 65536, &buf); if (buf.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); goto done; } assert(buf.base != NULL); memset(&from, 0, sizeof from); from_len = sizeof from; flags = 0; if (WSARecvFrom(handle->socket, (WSABUF*)&buf, 1, &bytes, &flags, (struct sockaddr*) &from, &from_len, NULL, NULL) != SOCKET_ERROR) { /* Message received */ handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0); } else { err = WSAGetLastError(); if (err == WSAEMSGSIZE) { /* Message truncated */ handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, UV_UDP_PARTIAL); } else if (err == WSAEWOULDBLOCK) { /* Kernel buffer empty */ handle->recv_cb(handle, 0, &buf, NULL, 0); } else if (err == WSAECONNRESET || err == WSAENETRESET) { /* WSAECONNRESET/WSANETRESET is ignored because this just indicates * that a previous sendto operation failed. */ handle->recv_cb(handle, 0, &buf, NULL, 0); } else { /* Any other error that we want to report back to the user. */ uv_udp_recv_stop(handle); handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0); } } } done: /* Post another read if still reading and not closing. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { uv_udp_queue_recv(loop, handle); } DECREASE_PENDING_REQ_COUNT(handle); } void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, uv_udp_send_t* req) { int err; assert(handle->type == UV_UDP); assert(handle->send_queue_size >= req->u.io.queued_bytes); assert(handle->send_queue_count >= 1); handle->send_queue_size -= req->u.io.queued_bytes; handle->send_queue_count--; UNREGISTER_HANDLE_REQ(loop, handle, req); if (req->cb) { err = 0; if (!REQ_SUCCESS(req)) { err = GET_REQ_SOCK_ERROR(req); } req->cb(req, uv_translate_sys_error(err)); } DECREASE_PENDING_REQ_COUNT(handle); } static int uv__udp_set_membership4(uv_udp_t* handle, const struct sockaddr_in* multicast_addr, const char* interface_addr, uv_membership membership) { int err; int optname; struct ip_mreq mreq; if (handle->flags & UV_HANDLE_IPV6) return UV_EINVAL; /* If the socket is unbound, bind to inaddr_any. */ err = uv_udp_maybe_bind(handle, (const struct sockaddr*) &uv_addr_ip4_any_, sizeof(uv_addr_ip4_any_), UV_UDP_REUSEADDR); if (err) return uv_translate_sys_error(err); memset(&mreq, 0, sizeof mreq); if (interface_addr) { err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); if (err) return err; } else { mreq.imr_interface.s_addr = htonl(INADDR_ANY); } mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; switch (membership) { case UV_JOIN_GROUP: optname = IP_ADD_MEMBERSHIP; break; case UV_LEAVE_GROUP: optname = IP_DROP_MEMBERSHIP; break; default: return UV_EINVAL; } if (setsockopt(handle->socket, IPPROTO_IP, optname, (char*) &mreq, sizeof mreq) == SOCKET_ERROR) { return uv_translate_sys_error(WSAGetLastError()); } return 0; } int uv__udp_set_membership6(uv_udp_t* handle, const struct sockaddr_in6* multicast_addr, const char* interface_addr, uv_membership membership) { int optname; int err; struct ipv6_mreq mreq; struct sockaddr_in6 addr6; if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6)) return UV_EINVAL; err = uv_udp_maybe_bind(handle, (const struct sockaddr*) &uv_addr_ip6_any_, sizeof(uv_addr_ip6_any_), UV_UDP_REUSEADDR); if (err) return uv_translate_sys_error(err); memset(&mreq, 0, sizeof(mreq)); if (interface_addr) { if (uv_ip6_addr(interface_addr, 0, &addr6)) return UV_EINVAL; mreq.ipv6mr_interface = addr6.sin6_scope_id; } else { mreq.ipv6mr_interface = 0; } mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr; switch (membership) { case UV_JOIN_GROUP: optname = IPV6_ADD_MEMBERSHIP; break; case UV_LEAVE_GROUP: optname = IPV6_DROP_MEMBERSHIP; break; default: return UV_EINVAL; } if (setsockopt(handle->socket, IPPROTO_IPV6, optname, (char*) &mreq, sizeof mreq) == SOCKET_ERROR) { return uv_translate_sys_error(WSAGetLastError()); } return 0; } int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership) { struct sockaddr_in addr4; struct sockaddr_in6 addr6; if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) return uv__udp_set_membership4(handle, &addr4, interface_addr, membership); else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) return uv__udp_set_membership6(handle, &addr6, interface_addr, membership); else return UV_EINVAL; } int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { struct sockaddr_storage addr_st; struct sockaddr_in* addr4; struct sockaddr_in6* addr6; addr4 = (struct sockaddr_in*) &addr_st; addr6 = (struct sockaddr_in6*) &addr_st; if (!interface_addr) { memset(&addr_st, 0, sizeof addr_st); if (handle->flags & UV_HANDLE_IPV6) { addr_st.ss_family = AF_INET6; addr6->sin6_scope_id = 0; } else { addr_st.ss_family = AF_INET; addr4->sin_addr.s_addr = htonl(INADDR_ANY); } } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) { /* nothing, address was parsed */ } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) { /* nothing, address was parsed */ } else { return UV_EINVAL; } if (!(handle->flags & UV_HANDLE_BOUND)) return UV_EBADF; if (addr_st.ss_family == AF_INET) { if (setsockopt(handle->socket, IPPROTO_IP, IP_MULTICAST_IF, (char*) &addr4->sin_addr, sizeof(addr4->sin_addr)) == SOCKET_ERROR) { return uv_translate_sys_error(WSAGetLastError()); } } else if (addr_st.ss_family == AF_INET6) { if (setsockopt(handle->socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*) &addr6->sin6_scope_id, sizeof(addr6->sin6_scope_id)) == SOCKET_ERROR) { return uv_translate_sys_error(WSAGetLastError()); } } else { assert(0 && "unexpected address family"); abort(); } return 0; } int uv_udp_set_broadcast(uv_udp_t* handle, int value) { BOOL optval = (BOOL) value; if (!(handle->flags & UV_HANDLE_BOUND)) return UV_EBADF; if (setsockopt(handle->socket, SOL_SOCKET, SO_BROADCAST, (char*) &optval, sizeof optval)) { return uv_translate_sys_error(WSAGetLastError()); } return 0; } int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; int err; /* Detect the address family of the socket. */ opt_len = (int) sizeof protocol_info; if (getsockopt(sock, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, &opt_len) == SOCKET_ERROR) { return uv_translate_sys_error(GetLastError()); } err = uv_udp_set_socket(handle->loop, handle, sock, protocol_info.iAddressFamily); return uv_translate_sys_error(err); } #define SOCKOPT_SETTER(name, option4, option6, validate) \ int uv_udp_set_##name(uv_udp_t* handle, int value) { \ DWORD optval = (DWORD) value; \ \ if (!(validate(value))) { \ return UV_EINVAL; \ } \ \ if (!(handle->flags & UV_HANDLE_BOUND)) \ return UV_EBADF; \ \ if (!(handle->flags & UV_HANDLE_IPV6)) { \ /* Set IPv4 socket option */ \ if (setsockopt(handle->socket, \ IPPROTO_IP, \ option4, \ (char*) &optval, \ sizeof optval)) { \ return uv_translate_sys_error(WSAGetLastError()); \ } \ } else { \ /* Set IPv6 socket option */ \ if (setsockopt(handle->socket, \ IPPROTO_IPV6, \ option6, \ (char*) &optval, \ sizeof optval)) { \ return uv_translate_sys_error(WSAGetLastError()); \ } \ } \ return 0; \ } #define VALIDATE_TTL(value) ((value) >= 1 && (value) <= 255) #define VALIDATE_MULTICAST_TTL(value) ((value) >= -1 && (value) <= 255) #define VALIDATE_MULTICAST_LOOP(value) (1) SOCKOPT_SETTER(ttl, IP_TTL, IPV6_HOPLIMIT, VALIDATE_TTL) SOCKOPT_SETTER(multicast_ttl, IP_MULTICAST_TTL, IPV6_MULTICAST_HOPS, VALIDATE_MULTICAST_TTL) SOCKOPT_SETTER(multicast_loop, IP_MULTICAST_LOOP, IPV6_MULTICAST_LOOP, VALIDATE_MULTICAST_LOOP) #undef SOCKOPT_SETTER #undef VALIDATE_TTL #undef VALIDATE_MULTICAST_TTL #undef VALIDATE_MULTICAST_LOOP /* This function is an egress point, i.e. it returns libuv errors rather than * system errors. */ int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags) { int err; err = uv_udp_maybe_bind(handle, addr, addrlen, flags); if (err) return uv_translate_sys_error(err); return 0; } /* This function is an egress point, i.e. it returns libuv errors rather than * system errors. */ int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen, uv_udp_send_cb send_cb) { const struct sockaddr* bind_addr; int err; if (!(handle->flags & UV_HANDLE_BOUND)) { if (addrlen == sizeof(uv_addr_ip4_any_)) { bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; } else if (addrlen == sizeof(uv_addr_ip6_any_)) { bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; } else { abort(); } err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); if (err) return uv_translate_sys_error(err); } err = uv__send(req, handle, bufs, nbufs, addr, addrlen, send_cb); if (err) return uv_translate_sys_error(err); return 0; } int uv__udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen) { return UV_ENOSYS; } libuv-v1.8.0/src/win/util.c000066400000000000000000001041461263361716600155550ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include "uv.h" #include "internal.h" #include #include #include #include #include #include #include /* * Max title length; the only thing MSDN tells us about the maximum length * of the console title is that it is smaller than 64K. However in practice * it is much smaller, and there is no way to figure out what the exact length * of the title is or can be, at least not on XP. To make it even more * annoying, GetConsoleTitle fails when the buffer to be read into is bigger * than the actual maximum length. So we make a conservative guess here; * just don't put the novel you're writing in the title, unless the plot * survives truncation. */ #define MAX_TITLE_LENGTH 8192 /* The number of nanoseconds in one second. */ #define UV__NANOSEC 1000000000 /* Cached copy of the process title, plus a mutex guarding it. */ static char *process_title; static CRITICAL_SECTION process_title_lock; /* Cached copy of the process id, written once. */ static DWORD current_pid = 0; /* Interval (in seconds) of the high-resolution clock. */ static double hrtime_interval_ = 0; /* * One-time initialization code for functionality defined in util.c. */ void uv__util_init() { LARGE_INTEGER perf_frequency; /* Initialize process title access mutex. */ InitializeCriticalSection(&process_title_lock); /* Retrieve high-resolution timer frequency * and precompute its reciprocal. */ if (QueryPerformanceFrequency(&perf_frequency)) { hrtime_interval_ = 1.0 / perf_frequency.QuadPart; } else { hrtime_interval_= 0; } } int uv_utf16_to_utf8(const WCHAR* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size) { return WideCharToMultiByte(CP_UTF8, 0, utf16Buffer, utf16Size, utf8Buffer, utf8Size, NULL, NULL); } int uv_utf8_to_utf16(const char* utf8Buffer, WCHAR* utf16Buffer, size_t utf16Size) { return MultiByteToWideChar(CP_UTF8, 0, utf8Buffer, -1, utf16Buffer, utf16Size); } int uv_exepath(char* buffer, size_t* size_ptr) { int utf8_len, utf16_buffer_len, utf16_len; WCHAR* utf16_buffer; int err; if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) { return UV_EINVAL; } if (*size_ptr > 32768) { /* Windows paths can never be longer than this. */ utf16_buffer_len = 32768; } else { utf16_buffer_len = (int) *size_ptr; } utf16_buffer = (WCHAR*) uv__malloc(sizeof(WCHAR) * utf16_buffer_len); if (!utf16_buffer) { return UV_ENOMEM; } /* Get the path as UTF-16. */ utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len); if (utf16_len <= 0) { err = GetLastError(); goto error; } /* utf16_len contains the length, *not* including the terminating null. */ utf16_buffer[utf16_len] = L'\0'; /* Convert to UTF-8 */ utf8_len = WideCharToMultiByte(CP_UTF8, 0, utf16_buffer, -1, buffer, *size_ptr > INT_MAX ? INT_MAX : (int) *size_ptr, NULL, NULL); if (utf8_len == 0) { err = GetLastError(); goto error; } uv__free(utf16_buffer); /* utf8_len *does* include the terminating null at this point, but the */ /* returned size shouldn't. */ *size_ptr = utf8_len - 1; return 0; error: uv__free(utf16_buffer); return uv_translate_sys_error(err); } int uv_cwd(char* buffer, size_t* size) { DWORD utf16_len; WCHAR utf16_buffer[MAX_PATH]; int r; if (buffer == NULL || size == NULL) { return UV_EINVAL; } utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); if (utf16_len == 0) { return uv_translate_sys_error(GetLastError()); } else if (utf16_len > MAX_PATH) { /* This should be impossible; however the CRT has a code path to deal */ /* with this scenario, so I added a check anyway. */ return UV_EIO; } /* utf16_len contains the length, *not* including the terminating null. */ utf16_buffer[utf16_len] = L'\0'; /* The returned directory should not have a trailing slash, unless it */ /* points at a drive root, like c:\. Remove it if needed.*/ if (utf16_buffer[utf16_len - 1] == L'\\' && !(utf16_len == 3 && utf16_buffer[1] == L':')) { utf16_len--; utf16_buffer[utf16_len] = L'\0'; } /* Check how much space we need */ r = WideCharToMultiByte(CP_UTF8, 0, utf16_buffer, -1, NULL, 0, NULL, NULL); if (r == 0) { return uv_translate_sys_error(GetLastError()); } else if (r > (int) *size) { *size = r -1; return UV_ENOBUFS; } /* Convert to UTF-8 */ r = WideCharToMultiByte(CP_UTF8, 0, utf16_buffer, -1, buffer, *size > INT_MAX ? INT_MAX : (int) *size, NULL, NULL); if (r == 0) { return uv_translate_sys_error(GetLastError()); } *size = r - 1; return 0; } int uv_chdir(const char* dir) { WCHAR utf16_buffer[MAX_PATH]; size_t utf16_len; WCHAR drive_letter, env_var[4]; if (dir == NULL) { return UV_EINVAL; } if (MultiByteToWideChar(CP_UTF8, 0, dir, -1, utf16_buffer, MAX_PATH) == 0) { DWORD error = GetLastError(); /* The maximum length of the current working directory is 260 chars, */ /* including terminating null. If it doesn't fit, the path name must be */ /* too long. */ if (error == ERROR_INSUFFICIENT_BUFFER) { return UV_ENAMETOOLONG; } else { return uv_translate_sys_error(error); } } if (!SetCurrentDirectoryW(utf16_buffer)) { return uv_translate_sys_error(GetLastError()); } /* Windows stores the drive-local path in an "hidden" environment variable, */ /* which has the form "=C:=C:\Windows". SetCurrentDirectory does not */ /* update this, so we'll have to do it. */ utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); if (utf16_len == 0) { return uv_translate_sys_error(GetLastError()); } else if (utf16_len > MAX_PATH) { return UV_EIO; } /* The returned directory should not have a trailing slash, unless it */ /* points at a drive root, like c:\. Remove it if needed. */ if (utf16_buffer[utf16_len - 1] == L'\\' && !(utf16_len == 3 && utf16_buffer[1] == L':')) { utf16_len--; utf16_buffer[utf16_len] = L'\0'; } if (utf16_len < 2 || utf16_buffer[1] != L':') { /* Doesn't look like a drive letter could be there - probably an UNC */ /* path. TODO: Need to handle win32 namespaces like \\?\C:\ ? */ drive_letter = 0; } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') { drive_letter = utf16_buffer[0]; } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') { /* Convert to uppercase. */ drive_letter = utf16_buffer[0] - L'a' + L'A'; } else { /* Not valid. */ drive_letter = 0; } if (drive_letter != 0) { /* Construct the environment variable name and set it. */ env_var[0] = L'='; env_var[1] = drive_letter; env_var[2] = L':'; env_var[3] = L'\0'; if (!SetEnvironmentVariableW(env_var, utf16_buffer)) { return uv_translate_sys_error(GetLastError()); } } return 0; } void uv_loadavg(double avg[3]) { /* Can't be implemented */ avg[0] = avg[1] = avg[2] = 0; } uint64_t uv_get_free_memory(void) { MEMORYSTATUSEX memory_status; memory_status.dwLength = sizeof(memory_status); if (!GlobalMemoryStatusEx(&memory_status)) { return -1; } return (uint64_t)memory_status.ullAvailPhys; } uint64_t uv_get_total_memory(void) { MEMORYSTATUSEX memory_status; memory_status.dwLength = sizeof(memory_status); if (!GlobalMemoryStatusEx(&memory_status)) { return -1; } return (uint64_t)memory_status.ullTotalPhys; } int uv_parent_pid() { int parent_pid = -1; HANDLE handle; PROCESSENTRY32 pe; DWORD current_pid = GetCurrentProcessId(); pe.dwSize = sizeof(PROCESSENTRY32); handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (Process32First(handle, &pe)) { do { if (pe.th32ProcessID == current_pid) { parent_pid = pe.th32ParentProcessID; break; } } while( Process32Next(handle, &pe)); } CloseHandle(handle); return parent_pid; } int uv_current_pid() { if (current_pid == 0) { current_pid = GetCurrentProcessId(); } return current_pid; } char** uv_setup_args(int argc, char** argv) { return argv; } int uv_set_process_title(const char* title) { int err; int length; WCHAR* title_w = NULL; uv__once_init(); /* Find out how big the buffer for the wide-char title must be */ length = uv_utf8_to_utf16(title, NULL, 0); if (!length) { err = GetLastError(); goto done; } /* Convert to wide-char string */ title_w = (WCHAR*)uv__malloc(sizeof(WCHAR) * length); if (!title_w) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } length = uv_utf8_to_utf16(title, title_w, length); if (!length) { err = GetLastError(); goto done; } /* If the title must be truncated insert a \0 terminator there */ if (length > MAX_TITLE_LENGTH) { title_w[MAX_TITLE_LENGTH - 1] = L'\0'; } if (!SetConsoleTitleW(title_w)) { err = GetLastError(); goto done; } EnterCriticalSection(&process_title_lock); uv__free(process_title); process_title = uv__strdup(title); LeaveCriticalSection(&process_title_lock); err = 0; done: uv__free(title_w); return uv_translate_sys_error(err); } static int uv__get_process_title() { WCHAR title_w[MAX_TITLE_LENGTH]; int length; if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { return -1; } /* Find out what the size of the buffer is that we need */ length = uv_utf16_to_utf8(title_w, -1, NULL, 0); if (!length) { return -1; } assert(!process_title); process_title = (char*)uv__malloc(length); if (!process_title) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } /* Do utf16 -> utf8 conversion here */ if (!uv_utf16_to_utf8(title_w, -1, process_title, length)) { uv__free(process_title); return -1; } return 0; } int uv_get_process_title(char* buffer, size_t size) { uv__once_init(); EnterCriticalSection(&process_title_lock); /* * If the process_title was never read before nor explicitly set, * we must query it with getConsoleTitleW */ if (!process_title && uv__get_process_title() == -1) { LeaveCriticalSection(&process_title_lock); return uv_translate_sys_error(GetLastError()); } assert(process_title); strncpy(buffer, process_title, size); LeaveCriticalSection(&process_title_lock); return 0; } uint64_t uv_hrtime(void) { uv__once_init(); return uv__hrtime(UV__NANOSEC); } uint64_t uv__hrtime(double scale) { LARGE_INTEGER counter; /* If the performance interval is zero, there's no support. */ if (hrtime_interval_ == 0) { return 0; } if (!QueryPerformanceCounter(&counter)) { return 0; } /* Because we have no guarantee about the order of magnitude of the * performance counter interval, integer math could cause this computation * to overflow. Therefore we resort to floating point math. */ return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale); } int uv_resident_set_memory(size_t* rss) { HANDLE current_process; PROCESS_MEMORY_COUNTERS pmc; current_process = GetCurrentProcess(); if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) { return uv_translate_sys_error(GetLastError()); } *rss = pmc.WorkingSetSize; return 0; } int uv_uptime(double* uptime) { BYTE stack_buffer[4096]; BYTE* malloced_buffer = NULL; BYTE* buffer = (BYTE*) stack_buffer; size_t buffer_size = sizeof(stack_buffer); DWORD data_size; PERF_DATA_BLOCK* data_block; PERF_OBJECT_TYPE* object_type; PERF_COUNTER_DEFINITION* counter_definition; DWORD i; for (;;) { LONG result; data_size = (DWORD) buffer_size; result = RegQueryValueExW(HKEY_PERFORMANCE_DATA, L"2", NULL, NULL, buffer, &data_size); if (result == ERROR_SUCCESS) { break; } else if (result != ERROR_MORE_DATA) { *uptime = 0; return uv_translate_sys_error(result); } buffer_size *= 2; /* Don't let the buffer grow infinitely. */ if (buffer_size > 1 << 20) { goto internalError; } uv__free(malloced_buffer); buffer = malloced_buffer = (BYTE*) uv__malloc(buffer_size); if (malloced_buffer == NULL) { *uptime = 0; return UV_ENOMEM; } } if (data_size < sizeof(*data_block)) goto internalError; data_block = (PERF_DATA_BLOCK*) buffer; if (wmemcmp(data_block->Signature, L"PERF", 4) != 0) goto internalError; if (data_size < data_block->HeaderLength + sizeof(*object_type)) goto internalError; object_type = (PERF_OBJECT_TYPE*) (buffer + data_block->HeaderLength); if (object_type->NumInstances != PERF_NO_INSTANCES) goto internalError; counter_definition = (PERF_COUNTER_DEFINITION*) (buffer + data_block->HeaderLength + object_type->HeaderLength); for (i = 0; i < object_type->NumCounters; i++) { if ((BYTE*) counter_definition + sizeof(*counter_definition) > buffer + data_size) { break; } if (counter_definition->CounterNameTitleIndex == 674 && counter_definition->CounterSize == sizeof(uint64_t)) { if (counter_definition->CounterOffset + sizeof(uint64_t) > data_size || !(counter_definition->CounterType & PERF_OBJECT_TIMER)) { goto internalError; } else { BYTE* address = (BYTE*) object_type + object_type->DefinitionLength + counter_definition->CounterOffset; uint64_t value = *((uint64_t*) address); *uptime = (double) (object_type->PerfTime.QuadPart - value) / (double) object_type->PerfFreq.QuadPart; uv__free(malloced_buffer); return 0; } } counter_definition = (PERF_COUNTER_DEFINITION*) ((BYTE*) counter_definition + counter_definition->ByteLength); } /* If we get here, the uptime value was not found. */ uv__free(malloced_buffer); *uptime = 0; return UV_ENOSYS; internalError: uv__free(malloced_buffer); *uptime = 0; return UV_EIO; } int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { uv_cpu_info_t* cpu_infos; SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi; DWORD sppi_size; SYSTEM_INFO system_info; DWORD cpu_count, r, i; NTSTATUS status; ULONG result_size; int err; uv_cpu_info_t* cpu_info; cpu_infos = NULL; cpu_count = 0; sppi = NULL; uv__once_init(); GetSystemInfo(&system_info); cpu_count = system_info.dwNumberOfProcessors; cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos); if (cpu_infos == NULL) { err = ERROR_OUTOFMEMORY; goto error; } sppi_size = cpu_count * sizeof(*sppi); sppi = uv__malloc(sppi_size); if (sppi == NULL) { err = ERROR_OUTOFMEMORY; goto error; } status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, sppi_size, &result_size); if (!NT_SUCCESS(status)) { err = pRtlNtStatusToDosError(status); goto error; } assert(result_size == sppi_size); for (i = 0; i < cpu_count; i++) { WCHAR key_name[128]; HKEY processor_key; DWORD cpu_speed; DWORD cpu_speed_size = sizeof(cpu_speed); WCHAR cpu_brand[256]; DWORD cpu_brand_size = sizeof(cpu_brand); size_t len; len = _snwprintf(key_name, ARRAY_SIZE(key_name), L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", i); assert(len > 0 && len < ARRAY_SIZE(key_name)); r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_QUERY_VALUE, &processor_key); if (r != ERROR_SUCCESS) { err = GetLastError(); goto error; } if (RegQueryValueExW(processor_key, L"~MHz", NULL, NULL, (BYTE*) &cpu_speed, &cpu_speed_size) != ERROR_SUCCESS) { err = GetLastError(); RegCloseKey(processor_key); goto error; } if (RegQueryValueExW(processor_key, L"ProcessorNameString", NULL, NULL, (BYTE*) &cpu_brand, &cpu_brand_size) != ERROR_SUCCESS) { err = GetLastError(); RegCloseKey(processor_key); goto error; } RegCloseKey(processor_key); cpu_info = &cpu_infos[i]; cpu_info->speed = cpu_speed; cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000; cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart - sppi[i].IdleTime.QuadPart) / 10000; cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000; cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000; cpu_info->cpu_times.nice = 0; len = WideCharToMultiByte(CP_UTF8, 0, cpu_brand, cpu_brand_size / sizeof(WCHAR), NULL, 0, NULL, NULL); if (len == 0) { err = GetLastError(); goto error; } assert(len > 0); /* Allocate 1 extra byte for the null terminator. */ cpu_info->model = uv__malloc(len + 1); if (cpu_info->model == NULL) { err = ERROR_OUTOFMEMORY; goto error; } if (WideCharToMultiByte(CP_UTF8, 0, cpu_brand, cpu_brand_size / sizeof(WCHAR), cpu_info->model, len, NULL, NULL) == 0) { err = GetLastError(); goto error; } /* Ensure that cpu_info->model is null terminated. */ cpu_info->model[len] = '\0'; } uv__free(sppi); *cpu_count_ptr = cpu_count; *cpu_infos_ptr = cpu_infos; return 0; error: /* This is safe because the cpu_infos array is zeroed on allocation. */ for (i = 0; i < cpu_count; i++) uv__free(cpu_infos[i].model); uv__free(cpu_infos); uv__free(sppi); return uv_translate_sys_error(err); } void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { int i; for (i = 0; i < count; i++) { uv__free(cpu_infos[i].model); } uv__free(cpu_infos); } static int is_windows_version_or_greater(DWORD os_major, DWORD os_minor, WORD service_pack_major, WORD service_pack_minor) { OSVERSIONINFOEX osvi; DWORDLONG condition_mask = 0; int op = VER_GREATER_EQUAL; /* Initialize the OSVERSIONINFOEX structure. */ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvi.dwMajorVersion = os_major; osvi.dwMinorVersion = os_minor; osvi.wServicePackMajor = service_pack_major; osvi.wServicePackMinor = service_pack_minor; /* Initialize the condition mask. */ VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op); VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op); VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op); VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op); /* Perform the test. */ return (int) VerifyVersionInfo( &osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, condition_mask); } static int address_prefix_match(int family, struct sockaddr* address, struct sockaddr* prefix_address, int prefix_len) { uint8_t* address_data; uint8_t* prefix_address_data; int i; assert(address->sa_family == family); assert(prefix_address->sa_family == family); if (family == AF_INET6) { address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr); prefix_address_data = (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr); } else { address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr); prefix_address_data = (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr); } for (i = 0; i < prefix_len >> 3; i++) { if (address_data[i] != prefix_address_data[i]) return 0; } if (prefix_len % 8) return prefix_address_data[i] == (address_data[i] & (0xff << (8 - prefix_len % 8))); return 1; } int uv_interface_addresses(uv_interface_address_t** addresses_ptr, int* count_ptr) { IP_ADAPTER_ADDRESSES* win_address_buf; ULONG win_address_buf_size; IP_ADAPTER_ADDRESSES* adapter; uv_interface_address_t* uv_address_buf; char* name_buf; size_t uv_address_buf_size; uv_interface_address_t* uv_address; int count; int is_vista_or_greater; ULONG flags; is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0); if (is_vista_or_greater) { flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER; } else { /* We need at least XP SP1. */ if (!is_windows_version_or_greater(5, 1, 1, 0)) return UV_ENOTSUP; flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX; } /* Fetch the size of the adapters reported by windows, and then get the */ /* list itself. */ win_address_buf_size = 0; win_address_buf = NULL; for (;;) { ULONG r; /* If win_address_buf is 0, then GetAdaptersAddresses will fail with */ /* ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in */ /* win_address_buf_size. */ r = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, win_address_buf, &win_address_buf_size); if (r == ERROR_SUCCESS) break; uv__free(win_address_buf); switch (r) { case ERROR_BUFFER_OVERFLOW: /* This happens when win_address_buf is NULL or too small to hold */ /* all adapters. */ win_address_buf = uv__malloc(win_address_buf_size); if (win_address_buf == NULL) return UV_ENOMEM; continue; case ERROR_NO_DATA: { /* No adapters were found. */ uv_address_buf = uv__malloc(1); if (uv_address_buf == NULL) return UV_ENOMEM; *count_ptr = 0; *addresses_ptr = uv_address_buf; return 0; } case ERROR_ADDRESS_NOT_ASSOCIATED: return UV_EAGAIN; case ERROR_INVALID_PARAMETER: /* MSDN says: * "This error is returned for any of the following conditions: the * SizePointer parameter is NULL, the Address parameter is not * AF_INET, AF_INET6, or AF_UNSPEC, or the address information for * the parameters requested is greater than ULONG_MAX." * Since the first two conditions are not met, it must be that the * adapter data is too big. */ return UV_ENOBUFS; default: /* Other (unspecified) errors can happen, but we don't have any */ /* special meaning for them. */ assert(r != ERROR_SUCCESS); return uv_translate_sys_error(r); } } /* Count the number of enabled interfaces and compute how much space is */ /* needed to store their info. */ count = 0; uv_address_buf_size = 0; for (adapter = win_address_buf; adapter != NULL; adapter = adapter->Next) { IP_ADAPTER_UNICAST_ADDRESS* unicast_address; int name_size; /* Interfaces that are not 'up' should not be reported. Also skip */ /* interfaces that have no associated unicast address, as to avoid */ /* allocating space for the name for this interface. */ if (adapter->OperStatus != IfOperStatusUp || adapter->FirstUnicastAddress == NULL) continue; /* Compute the size of the interface name. */ name_size = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, NULL, 0, NULL, FALSE); if (name_size <= 0) { uv__free(win_address_buf); return uv_translate_sys_error(GetLastError()); } uv_address_buf_size += name_size; /* Count the number of addresses associated with this interface, and */ /* compute the size. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) adapter->FirstUnicastAddress; unicast_address != NULL; unicast_address = unicast_address->Next) { count++; uv_address_buf_size += sizeof(uv_interface_address_t); } } /* Allocate space to store interface data plus adapter names. */ uv_address_buf = uv__malloc(uv_address_buf_size); if (uv_address_buf == NULL) { uv__free(win_address_buf); return UV_ENOMEM; } /* Compute the start of the uv_interface_address_t array, and the place in */ /* the buffer where the interface names will be stored. */ uv_address = uv_address_buf; name_buf = (char*) (uv_address_buf + count); /* Fill out the output buffer. */ for (adapter = win_address_buf; adapter != NULL; adapter = adapter->Next) { IP_ADAPTER_UNICAST_ADDRESS* unicast_address; int name_size; size_t max_name_size; if (adapter->OperStatus != IfOperStatusUp || adapter->FirstUnicastAddress == NULL) continue; /* Convert the interface name to UTF8. */ max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf; if (max_name_size > (size_t) INT_MAX) max_name_size = INT_MAX; name_size = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, name_buf, (int) max_name_size, NULL, FALSE); if (name_size <= 0) { uv__free(win_address_buf); uv__free(uv_address_buf); return uv_translate_sys_error(GetLastError()); } /* Add an uv_interface_address_t element for every unicast address. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) adapter->FirstUnicastAddress; unicast_address != NULL; unicast_address = unicast_address->Next) { struct sockaddr* sa; ULONG prefix_len; sa = unicast_address->Address.lpSockaddr; /* XP has no OnLinkPrefixLength field. */ if (is_vista_or_greater) { prefix_len = ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength; } else { /* Prior to Windows Vista the FirstPrefix pointed to the list with * single prefix for each IP address assigned to the adapter. * Order of FirstPrefix does not match order of FirstUnicastAddress, * so we need to find corresponding prefix. */ IP_ADAPTER_PREFIX* prefix; prefix_len = 0; for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) { /* We want the longest matching prefix. */ if (prefix->Address.lpSockaddr->sa_family != sa->sa_family || prefix->PrefixLength <= prefix_len) continue; if (address_prefix_match(sa->sa_family, sa, prefix->Address.lpSockaddr, prefix->PrefixLength)) { prefix_len = prefix->PrefixLength; } } /* If there is no matching prefix information, return a single-host * subnet mask (e.g. 255.255.255.255 for IPv4). */ if (!prefix_len) prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32; } memset(uv_address, 0, sizeof *uv_address); uv_address->name = name_buf; if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) { memcpy(uv_address->phys_addr, adapter->PhysicalAddress, sizeof(uv_address->phys_addr)); } uv_address->is_internal = (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK); if (sa->sa_family == AF_INET6) { uv_address->address.address6 = *((struct sockaddr_in6 *) sa); uv_address->netmask.netmask6.sin6_family = AF_INET6; memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3); /* This check ensures that we don't write past the size of the data. */ if (prefix_len % 8) { uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] = 0xff << (8 - prefix_len % 8); } } else { uv_address->address.address4 = *((struct sockaddr_in *) sa); uv_address->netmask.netmask4.sin_family = AF_INET; uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ? htonl(0xffffffff << (32 - prefix_len)) : 0; } uv_address++; } name_buf += name_size; } uv__free(win_address_buf); *addresses_ptr = uv_address_buf; *count_ptr = count; return 0; } void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { uv__free(addresses); } int uv_getrusage(uv_rusage_t *uv_rusage) { FILETIME createTime, exitTime, kernelTime, userTime; SYSTEMTIME kernelSystemTime, userSystemTime; int ret; ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } ret = FileTimeToSystemTime(&userTime, &userSystemTime); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } memset(uv_rusage, 0, sizeof(*uv_rusage)); uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 + userSystemTime.wMinute * 60 + userSystemTime.wSecond; uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000; uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 + kernelSystemTime.wMinute * 60 + kernelSystemTime.wSecond; uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000; return 0; } int uv_os_homedir(char* buffer, size_t* size) { HANDLE token; wchar_t path[MAX_PATH]; DWORD bufsize; size_t len; int r; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; /* Check if the USERPROFILE environment variable is set first */ len = GetEnvironmentVariableW(L"USERPROFILE", path, MAX_PATH); if (len == 0) { r = GetLastError(); /* Don't return an error if USERPROFILE was not found */ if (r != ERROR_ENVVAR_NOT_FOUND) return uv_translate_sys_error(r); } else if (len > MAX_PATH) { /* This should not be possible */ return UV_EIO; } else { goto convert_buffer; } /* USERPROFILE is not set, so call GetUserProfileDirectoryW() */ if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0) return uv_translate_sys_error(GetLastError()); bufsize = MAX_PATH; if (!GetUserProfileDirectoryW(token, path, &bufsize)) { r = GetLastError(); CloseHandle(token); /* This should not be possible */ if (r == ERROR_INSUFFICIENT_BUFFER) return UV_EIO; return uv_translate_sys_error(r); } CloseHandle(token); convert_buffer: /* Check how much space we need */ bufsize = uv_utf16_to_utf8(path, -1, NULL, 0); if (bufsize == 0) { return uv_translate_sys_error(GetLastError()); } else if (bufsize > *size) { *size = bufsize - 1; return UV_ENOBUFS; } /* Convert to UTF-8 */ bufsize = uv_utf16_to_utf8(path, -1, buffer, *size); if (bufsize == 0) return uv_translate_sys_error(GetLastError()); *size = bufsize - 1; return 0; } libuv-v1.8.0/src/win/winapi.c000066400000000000000000000125351263361716600160670ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "uv.h" #include "internal.h" /* Ntdll function pointers */ sRtlNtStatusToDosError pRtlNtStatusToDosError; sNtDeviceIoControlFile pNtDeviceIoControlFile; sNtQueryInformationFile pNtQueryInformationFile; sNtSetInformationFile pNtSetInformationFile; sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; sNtQueryDirectoryFile pNtQueryDirectoryFile; sNtQuerySystemInformation pNtQuerySystemInformation; /* Kernel32 function pointers */ sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; sCreateSymbolicLinkW pCreateSymbolicLinkW; sCancelIoEx pCancelIoEx; sInitializeConditionVariable pInitializeConditionVariable; sSleepConditionVariableCS pSleepConditionVariableCS; sSleepConditionVariableSRW pSleepConditionVariableSRW; sWakeAllConditionVariable pWakeAllConditionVariable; sWakeConditionVariable pWakeConditionVariable; sCancelSynchronousIo pCancelSynchronousIo; sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW; void uv_winapi_init() { HMODULE ntdll_module; HMODULE kernel32_module; ntdll_module = GetModuleHandleA("ntdll.dll"); if (ntdll_module == NULL) { uv_fatal_error(GetLastError(), "GetModuleHandleA"); } pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress( ntdll_module, "RtlNtStatusToDosError"); if (pRtlNtStatusToDosError == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } pNtDeviceIoControlFile = (sNtDeviceIoControlFile) GetProcAddress( ntdll_module, "NtDeviceIoControlFile"); if (pNtDeviceIoControlFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } pNtQueryInformationFile = (sNtQueryInformationFile) GetProcAddress( ntdll_module, "NtQueryInformationFile"); if (pNtQueryInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress( ntdll_module, "NtSetInformationFile"); if (pNtSetInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } pNtQueryVolumeInformationFile = (sNtQueryVolumeInformationFile) GetProcAddress(ntdll_module, "NtQueryVolumeInformationFile"); if (pNtQueryVolumeInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } pNtQueryDirectoryFile = (sNtQueryDirectoryFile) GetProcAddress(ntdll_module, "NtQueryDirectoryFile"); if (pNtQueryVolumeInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } pNtQuerySystemInformation = (sNtQuerySystemInformation) GetProcAddress( ntdll_module, "NtQuerySystemInformation"); if (pNtQuerySystemInformation == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } kernel32_module = GetModuleHandleA("kernel32.dll"); if (kernel32_module == NULL) { uv_fatal_error(GetLastError(), "GetModuleHandleA"); } pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress( kernel32_module, "GetQueuedCompletionStatusEx"); pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes) GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes"); pCreateSymbolicLinkW = (sCreateSymbolicLinkW) GetProcAddress(kernel32_module, "CreateSymbolicLinkW"); pCancelIoEx = (sCancelIoEx) GetProcAddress(kernel32_module, "CancelIoEx"); pInitializeConditionVariable = (sInitializeConditionVariable) GetProcAddress(kernel32_module, "InitializeConditionVariable"); pSleepConditionVariableCS = (sSleepConditionVariableCS) GetProcAddress(kernel32_module, "SleepConditionVariableCS"); pSleepConditionVariableSRW = (sSleepConditionVariableSRW) GetProcAddress(kernel32_module, "SleepConditionVariableSRW"); pWakeAllConditionVariable = (sWakeAllConditionVariable) GetProcAddress(kernel32_module, "WakeAllConditionVariable"); pWakeConditionVariable = (sWakeConditionVariable) GetProcAddress(kernel32_module, "WakeConditionVariable"); pCancelSynchronousIo = (sCancelSynchronousIo) GetProcAddress(kernel32_module, "CancelSynchronousIo"); pGetFinalPathNameByHandleW = (sGetFinalPathNameByHandleW) GetProcAddress(kernel32_module, "GetFinalPathNameByHandleW"); } libuv-v1.8.0/src/win/winapi.h000066400000000000000000003676131263361716600161060ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_WINAPI_H_ #define UV_WIN_WINAPI_H_ #include /* * Ntdll headers */ #ifndef STATUS_SEVERITY_SUCCESS # define STATUS_SEVERITY_SUCCESS 0x0 #endif #ifndef STATUS_SEVERITY_INFORMATIONAL # define STATUS_SEVERITY_INFORMATIONAL 0x1 #endif #ifndef STATUS_SEVERITY_WARNING # define STATUS_SEVERITY_WARNING 0x2 #endif #ifndef STATUS_SEVERITY_ERROR # define STATUS_SEVERITY_ERROR 0x3 #endif #ifndef FACILITY_NTWIN32 # define FACILITY_NTWIN32 0x7 #endif #ifndef NT_SUCCESS # define NT_SUCCESS(status) (((NTSTATUS) (status)) >= 0) #endif #ifndef NT_INFORMATION # define NT_INFORMATION(status) ((((ULONG) (status)) >> 30) == 1) #endif #ifndef NT_WARNING # define NT_WARNING(status) ((((ULONG) (status)) >> 30) == 2) #endif #ifndef NT_ERROR # define NT_ERROR(status) ((((ULONG) (status)) >> 30) == 3) #endif #ifndef STATUS_SUCCESS # define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) #endif #ifndef STATUS_WAIT_0 # define STATUS_WAIT_0 ((NTSTATUS) 0x00000000L) #endif #ifndef STATUS_WAIT_1 # define STATUS_WAIT_1 ((NTSTATUS) 0x00000001L) #endif #ifndef STATUS_WAIT_2 # define STATUS_WAIT_2 ((NTSTATUS) 0x00000002L) #endif #ifndef STATUS_WAIT_3 # define STATUS_WAIT_3 ((NTSTATUS) 0x00000003L) #endif #ifndef STATUS_WAIT_63 # define STATUS_WAIT_63 ((NTSTATUS) 0x0000003FL) #endif #ifndef STATUS_ABANDONED # define STATUS_ABANDONED ((NTSTATUS) 0x00000080L) #endif #ifndef STATUS_ABANDONED_WAIT_0 # define STATUS_ABANDONED_WAIT_0 ((NTSTATUS) 0x00000080L) #endif #ifndef STATUS_ABANDONED_WAIT_63 # define STATUS_ABANDONED_WAIT_63 ((NTSTATUS) 0x000000BFL) #endif #ifndef STATUS_USER_APC # define STATUS_USER_APC ((NTSTATUS) 0x000000C0L) #endif #ifndef STATUS_KERNEL_APC # define STATUS_KERNEL_APC ((NTSTATUS) 0x00000100L) #endif #ifndef STATUS_ALERTED # define STATUS_ALERTED ((NTSTATUS) 0x00000101L) #endif #ifndef STATUS_TIMEOUT # define STATUS_TIMEOUT ((NTSTATUS) 0x00000102L) #endif #ifndef STATUS_PENDING # define STATUS_PENDING ((NTSTATUS) 0x00000103L) #endif #ifndef STATUS_REPARSE # define STATUS_REPARSE ((NTSTATUS) 0x00000104L) #endif #ifndef STATUS_MORE_ENTRIES # define STATUS_MORE_ENTRIES ((NTSTATUS) 0x00000105L) #endif #ifndef STATUS_NOT_ALL_ASSIGNED # define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS) 0x00000106L) #endif #ifndef STATUS_SOME_NOT_MAPPED # define STATUS_SOME_NOT_MAPPED ((NTSTATUS) 0x00000107L) #endif #ifndef STATUS_OPLOCK_BREAK_IN_PROGRESS # define STATUS_OPLOCK_BREAK_IN_PROGRESS ((NTSTATUS) 0x00000108L) #endif #ifndef STATUS_VOLUME_MOUNTED # define STATUS_VOLUME_MOUNTED ((NTSTATUS) 0x00000109L) #endif #ifndef STATUS_RXACT_COMMITTED # define STATUS_RXACT_COMMITTED ((NTSTATUS) 0x0000010AL) #endif #ifndef STATUS_NOTIFY_CLEANUP # define STATUS_NOTIFY_CLEANUP ((NTSTATUS) 0x0000010BL) #endif #ifndef STATUS_NOTIFY_ENUM_DIR # define STATUS_NOTIFY_ENUM_DIR ((NTSTATUS) 0x0000010CL) #endif #ifndef STATUS_NO_QUOTAS_FOR_ACCOUNT # define STATUS_NO_QUOTAS_FOR_ACCOUNT ((NTSTATUS) 0x0000010DL) #endif #ifndef STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED # define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED ((NTSTATUS) 0x0000010EL) #endif #ifndef STATUS_PAGE_FAULT_TRANSITION # define STATUS_PAGE_FAULT_TRANSITION ((NTSTATUS) 0x00000110L) #endif #ifndef STATUS_PAGE_FAULT_DEMAND_ZERO # define STATUS_PAGE_FAULT_DEMAND_ZERO ((NTSTATUS) 0x00000111L) #endif #ifndef STATUS_PAGE_FAULT_COPY_ON_WRITE # define STATUS_PAGE_FAULT_COPY_ON_WRITE ((NTSTATUS) 0x00000112L) #endif #ifndef STATUS_PAGE_FAULT_GUARD_PAGE # define STATUS_PAGE_FAULT_GUARD_PAGE ((NTSTATUS) 0x00000113L) #endif #ifndef STATUS_PAGE_FAULT_PAGING_FILE # define STATUS_PAGE_FAULT_PAGING_FILE ((NTSTATUS) 0x00000114L) #endif #ifndef STATUS_CACHE_PAGE_LOCKED # define STATUS_CACHE_PAGE_LOCKED ((NTSTATUS) 0x00000115L) #endif #ifndef STATUS_CRASH_DUMP # define STATUS_CRASH_DUMP ((NTSTATUS) 0x00000116L) #endif #ifndef STATUS_BUFFER_ALL_ZEROS # define STATUS_BUFFER_ALL_ZEROS ((NTSTATUS) 0x00000117L) #endif #ifndef STATUS_REPARSE_OBJECT # define STATUS_REPARSE_OBJECT ((NTSTATUS) 0x00000118L) #endif #ifndef STATUS_RESOURCE_REQUIREMENTS_CHANGED # define STATUS_RESOURCE_REQUIREMENTS_CHANGED ((NTSTATUS) 0x00000119L) #endif #ifndef STATUS_TRANSLATION_COMPLETE # define STATUS_TRANSLATION_COMPLETE ((NTSTATUS) 0x00000120L) #endif #ifndef STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY # define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY ((NTSTATUS) 0x00000121L) #endif #ifndef STATUS_NOTHING_TO_TERMINATE # define STATUS_NOTHING_TO_TERMINATE ((NTSTATUS) 0x00000122L) #endif #ifndef STATUS_PROCESS_NOT_IN_JOB # define STATUS_PROCESS_NOT_IN_JOB ((NTSTATUS) 0x00000123L) #endif #ifndef STATUS_PROCESS_IN_JOB # define STATUS_PROCESS_IN_JOB ((NTSTATUS) 0x00000124L) #endif #ifndef STATUS_VOLSNAP_HIBERNATE_READY # define STATUS_VOLSNAP_HIBERNATE_READY ((NTSTATUS) 0x00000125L) #endif #ifndef STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY # define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY ((NTSTATUS) 0x00000126L) #endif #ifndef STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED # define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED ((NTSTATUS) 0x00000127L) #endif #ifndef STATUS_INTERRUPT_STILL_CONNECTED # define STATUS_INTERRUPT_STILL_CONNECTED ((NTSTATUS) 0x00000128L) #endif #ifndef STATUS_PROCESS_CLONED # define STATUS_PROCESS_CLONED ((NTSTATUS) 0x00000129L) #endif #ifndef STATUS_FILE_LOCKED_WITH_ONLY_READERS # define STATUS_FILE_LOCKED_WITH_ONLY_READERS ((NTSTATUS) 0x0000012AL) #endif #ifndef STATUS_FILE_LOCKED_WITH_WRITERS # define STATUS_FILE_LOCKED_WITH_WRITERS ((NTSTATUS) 0x0000012BL) #endif #ifndef STATUS_RESOURCEMANAGER_READ_ONLY # define STATUS_RESOURCEMANAGER_READ_ONLY ((NTSTATUS) 0x00000202L) #endif #ifndef STATUS_RING_PREVIOUSLY_EMPTY # define STATUS_RING_PREVIOUSLY_EMPTY ((NTSTATUS) 0x00000210L) #endif #ifndef STATUS_RING_PREVIOUSLY_FULL # define STATUS_RING_PREVIOUSLY_FULL ((NTSTATUS) 0x00000211L) #endif #ifndef STATUS_RING_PREVIOUSLY_ABOVE_QUOTA # define STATUS_RING_PREVIOUSLY_ABOVE_QUOTA ((NTSTATUS) 0x00000212L) #endif #ifndef STATUS_RING_NEWLY_EMPTY # define STATUS_RING_NEWLY_EMPTY ((NTSTATUS) 0x00000213L) #endif #ifndef STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT # define STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT ((NTSTATUS) 0x00000214L) #endif #ifndef STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE # define STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE ((NTSTATUS) 0x00000215L) #endif #ifndef STATUS_OPLOCK_HANDLE_CLOSED # define STATUS_OPLOCK_HANDLE_CLOSED ((NTSTATUS) 0x00000216L) #endif #ifndef STATUS_WAIT_FOR_OPLOCK # define STATUS_WAIT_FOR_OPLOCK ((NTSTATUS) 0x00000367L) #endif #ifndef STATUS_OBJECT_NAME_EXISTS # define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS) 0x40000000L) #endif #ifndef STATUS_THREAD_WAS_SUSPENDED # define STATUS_THREAD_WAS_SUSPENDED ((NTSTATUS) 0x40000001L) #endif #ifndef STATUS_WORKING_SET_LIMIT_RANGE # define STATUS_WORKING_SET_LIMIT_RANGE ((NTSTATUS) 0x40000002L) #endif #ifndef STATUS_IMAGE_NOT_AT_BASE # define STATUS_IMAGE_NOT_AT_BASE ((NTSTATUS) 0x40000003L) #endif #ifndef STATUS_RXACT_STATE_CREATED # define STATUS_RXACT_STATE_CREATED ((NTSTATUS) 0x40000004L) #endif #ifndef STATUS_SEGMENT_NOTIFICATION # define STATUS_SEGMENT_NOTIFICATION ((NTSTATUS) 0x40000005L) #endif #ifndef STATUS_LOCAL_USER_SESSION_KEY # define STATUS_LOCAL_USER_SESSION_KEY ((NTSTATUS) 0x40000006L) #endif #ifndef STATUS_BAD_CURRENT_DIRECTORY # define STATUS_BAD_CURRENT_DIRECTORY ((NTSTATUS) 0x40000007L) #endif #ifndef STATUS_SERIAL_MORE_WRITES # define STATUS_SERIAL_MORE_WRITES ((NTSTATUS) 0x40000008L) #endif #ifndef STATUS_REGISTRY_RECOVERED # define STATUS_REGISTRY_RECOVERED ((NTSTATUS) 0x40000009L) #endif #ifndef STATUS_FT_READ_RECOVERY_FROM_BACKUP # define STATUS_FT_READ_RECOVERY_FROM_BACKUP ((NTSTATUS) 0x4000000AL) #endif #ifndef STATUS_FT_WRITE_RECOVERY # define STATUS_FT_WRITE_RECOVERY ((NTSTATUS) 0x4000000BL) #endif #ifndef STATUS_SERIAL_COUNTER_TIMEOUT # define STATUS_SERIAL_COUNTER_TIMEOUT ((NTSTATUS) 0x4000000CL) #endif #ifndef STATUS_NULL_LM_PASSWORD # define STATUS_NULL_LM_PASSWORD ((NTSTATUS) 0x4000000DL) #endif #ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH # define STATUS_IMAGE_MACHINE_TYPE_MISMATCH ((NTSTATUS) 0x4000000EL) #endif #ifndef STATUS_RECEIVE_PARTIAL # define STATUS_RECEIVE_PARTIAL ((NTSTATUS) 0x4000000FL) #endif #ifndef STATUS_RECEIVE_EXPEDITED # define STATUS_RECEIVE_EXPEDITED ((NTSTATUS) 0x40000010L) #endif #ifndef STATUS_RECEIVE_PARTIAL_EXPEDITED # define STATUS_RECEIVE_PARTIAL_EXPEDITED ((NTSTATUS) 0x40000011L) #endif #ifndef STATUS_EVENT_DONE # define STATUS_EVENT_DONE ((NTSTATUS) 0x40000012L) #endif #ifndef STATUS_EVENT_PENDING # define STATUS_EVENT_PENDING ((NTSTATUS) 0x40000013L) #endif #ifndef STATUS_CHECKING_FILE_SYSTEM # define STATUS_CHECKING_FILE_SYSTEM ((NTSTATUS) 0x40000014L) #endif #ifndef STATUS_FATAL_APP_EXIT # define STATUS_FATAL_APP_EXIT ((NTSTATUS) 0x40000015L) #endif #ifndef STATUS_PREDEFINED_HANDLE # define STATUS_PREDEFINED_HANDLE ((NTSTATUS) 0x40000016L) #endif #ifndef STATUS_WAS_UNLOCKED # define STATUS_WAS_UNLOCKED ((NTSTATUS) 0x40000017L) #endif #ifndef STATUS_SERVICE_NOTIFICATION # define STATUS_SERVICE_NOTIFICATION ((NTSTATUS) 0x40000018L) #endif #ifndef STATUS_WAS_LOCKED # define STATUS_WAS_LOCKED ((NTSTATUS) 0x40000019L) #endif #ifndef STATUS_LOG_HARD_ERROR # define STATUS_LOG_HARD_ERROR ((NTSTATUS) 0x4000001AL) #endif #ifndef STATUS_ALREADY_WIN32 # define STATUS_ALREADY_WIN32 ((NTSTATUS) 0x4000001BL) #endif #ifndef STATUS_WX86_UNSIMULATE # define STATUS_WX86_UNSIMULATE ((NTSTATUS) 0x4000001CL) #endif #ifndef STATUS_WX86_CONTINUE # define STATUS_WX86_CONTINUE ((NTSTATUS) 0x4000001DL) #endif #ifndef STATUS_WX86_SINGLE_STEP # define STATUS_WX86_SINGLE_STEP ((NTSTATUS) 0x4000001EL) #endif #ifndef STATUS_WX86_BREAKPOINT # define STATUS_WX86_BREAKPOINT ((NTSTATUS) 0x4000001FL) #endif #ifndef STATUS_WX86_EXCEPTION_CONTINUE # define STATUS_WX86_EXCEPTION_CONTINUE ((NTSTATUS) 0x40000020L) #endif #ifndef STATUS_WX86_EXCEPTION_LASTCHANCE # define STATUS_WX86_EXCEPTION_LASTCHANCE ((NTSTATUS) 0x40000021L) #endif #ifndef STATUS_WX86_EXCEPTION_CHAIN # define STATUS_WX86_EXCEPTION_CHAIN ((NTSTATUS) 0x40000022L) #endif #ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE # define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE ((NTSTATUS) 0x40000023L) #endif #ifndef STATUS_NO_YIELD_PERFORMED # define STATUS_NO_YIELD_PERFORMED ((NTSTATUS) 0x40000024L) #endif #ifndef STATUS_TIMER_RESUME_IGNORED # define STATUS_TIMER_RESUME_IGNORED ((NTSTATUS) 0x40000025L) #endif #ifndef STATUS_ARBITRATION_UNHANDLED # define STATUS_ARBITRATION_UNHANDLED ((NTSTATUS) 0x40000026L) #endif #ifndef STATUS_CARDBUS_NOT_SUPPORTED # define STATUS_CARDBUS_NOT_SUPPORTED ((NTSTATUS) 0x40000027L) #endif #ifndef STATUS_WX86_CREATEWX86TIB # define STATUS_WX86_CREATEWX86TIB ((NTSTATUS) 0x40000028L) #endif #ifndef STATUS_MP_PROCESSOR_MISMATCH # define STATUS_MP_PROCESSOR_MISMATCH ((NTSTATUS) 0x40000029L) #endif #ifndef STATUS_HIBERNATED # define STATUS_HIBERNATED ((NTSTATUS) 0x4000002AL) #endif #ifndef STATUS_RESUME_HIBERNATION # define STATUS_RESUME_HIBERNATION ((NTSTATUS) 0x4000002BL) #endif #ifndef STATUS_FIRMWARE_UPDATED # define STATUS_FIRMWARE_UPDATED ((NTSTATUS) 0x4000002CL) #endif #ifndef STATUS_DRIVERS_LEAKING_LOCKED_PAGES # define STATUS_DRIVERS_LEAKING_LOCKED_PAGES ((NTSTATUS) 0x4000002DL) #endif #ifndef STATUS_MESSAGE_RETRIEVED # define STATUS_MESSAGE_RETRIEVED ((NTSTATUS) 0x4000002EL) #endif #ifndef STATUS_SYSTEM_POWERSTATE_TRANSITION # define STATUS_SYSTEM_POWERSTATE_TRANSITION ((NTSTATUS) 0x4000002FL) #endif #ifndef STATUS_ALPC_CHECK_COMPLETION_LIST # define STATUS_ALPC_CHECK_COMPLETION_LIST ((NTSTATUS) 0x40000030L) #endif #ifndef STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION # define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION ((NTSTATUS) 0x40000031L) #endif #ifndef STATUS_ACCESS_AUDIT_BY_POLICY # define STATUS_ACCESS_AUDIT_BY_POLICY ((NTSTATUS) 0x40000032L) #endif #ifndef STATUS_ABANDON_HIBERFILE # define STATUS_ABANDON_HIBERFILE ((NTSTATUS) 0x40000033L) #endif #ifndef STATUS_BIZRULES_NOT_ENABLED # define STATUS_BIZRULES_NOT_ENABLED ((NTSTATUS) 0x40000034L) #endif #ifndef STATUS_GUARD_PAGE_VIOLATION # define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS) 0x80000001L) #endif #ifndef STATUS_DATATYPE_MISALIGNMENT # define STATUS_DATATYPE_MISALIGNMENT ((NTSTATUS) 0x80000002L) #endif #ifndef STATUS_BREAKPOINT # define STATUS_BREAKPOINT ((NTSTATUS) 0x80000003L) #endif #ifndef STATUS_SINGLE_STEP # define STATUS_SINGLE_STEP ((NTSTATUS) 0x80000004L) #endif #ifndef STATUS_BUFFER_OVERFLOW # define STATUS_BUFFER_OVERFLOW ((NTSTATUS) 0x80000005L) #endif #ifndef STATUS_NO_MORE_FILES # define STATUS_NO_MORE_FILES ((NTSTATUS) 0x80000006L) #endif #ifndef STATUS_WAKE_SYSTEM_DEBUGGER # define STATUS_WAKE_SYSTEM_DEBUGGER ((NTSTATUS) 0x80000007L) #endif #ifndef STATUS_HANDLES_CLOSED # define STATUS_HANDLES_CLOSED ((NTSTATUS) 0x8000000AL) #endif #ifndef STATUS_NO_INHERITANCE # define STATUS_NO_INHERITANCE ((NTSTATUS) 0x8000000BL) #endif #ifndef STATUS_GUID_SUBSTITUTION_MADE # define STATUS_GUID_SUBSTITUTION_MADE ((NTSTATUS) 0x8000000CL) #endif #ifndef STATUS_PARTIAL_COPY # define STATUS_PARTIAL_COPY ((NTSTATUS) 0x8000000DL) #endif #ifndef STATUS_DEVICE_PAPER_EMPTY # define STATUS_DEVICE_PAPER_EMPTY ((NTSTATUS) 0x8000000EL) #endif #ifndef STATUS_DEVICE_POWERED_OFF # define STATUS_DEVICE_POWERED_OFF ((NTSTATUS) 0x8000000FL) #endif #ifndef STATUS_DEVICE_OFF_LINE # define STATUS_DEVICE_OFF_LINE ((NTSTATUS) 0x80000010L) #endif #ifndef STATUS_DEVICE_BUSY # define STATUS_DEVICE_BUSY ((NTSTATUS) 0x80000011L) #endif #ifndef STATUS_NO_MORE_EAS # define STATUS_NO_MORE_EAS ((NTSTATUS) 0x80000012L) #endif #ifndef STATUS_INVALID_EA_NAME # define STATUS_INVALID_EA_NAME ((NTSTATUS) 0x80000013L) #endif #ifndef STATUS_EA_LIST_INCONSISTENT # define STATUS_EA_LIST_INCONSISTENT ((NTSTATUS) 0x80000014L) #endif #ifndef STATUS_INVALID_EA_FLAG # define STATUS_INVALID_EA_FLAG ((NTSTATUS) 0x80000015L) #endif #ifndef STATUS_VERIFY_REQUIRED # define STATUS_VERIFY_REQUIRED ((NTSTATUS) 0x80000016L) #endif #ifndef STATUS_EXTRANEOUS_INFORMATION # define STATUS_EXTRANEOUS_INFORMATION ((NTSTATUS) 0x80000017L) #endif #ifndef STATUS_RXACT_COMMIT_NECESSARY # define STATUS_RXACT_COMMIT_NECESSARY ((NTSTATUS) 0x80000018L) #endif #ifndef STATUS_NO_MORE_ENTRIES # define STATUS_NO_MORE_ENTRIES ((NTSTATUS) 0x8000001AL) #endif #ifndef STATUS_FILEMARK_DETECTED # define STATUS_FILEMARK_DETECTED ((NTSTATUS) 0x8000001BL) #endif #ifndef STATUS_MEDIA_CHANGED # define STATUS_MEDIA_CHANGED ((NTSTATUS) 0x8000001CL) #endif #ifndef STATUS_BUS_RESET # define STATUS_BUS_RESET ((NTSTATUS) 0x8000001DL) #endif #ifndef STATUS_END_OF_MEDIA # define STATUS_END_OF_MEDIA ((NTSTATUS) 0x8000001EL) #endif #ifndef STATUS_BEGINNING_OF_MEDIA # define STATUS_BEGINNING_OF_MEDIA ((NTSTATUS) 0x8000001FL) #endif #ifndef STATUS_MEDIA_CHECK # define STATUS_MEDIA_CHECK ((NTSTATUS) 0x80000020L) #endif #ifndef STATUS_SETMARK_DETECTED # define STATUS_SETMARK_DETECTED ((NTSTATUS) 0x80000021L) #endif #ifndef STATUS_NO_DATA_DETECTED # define STATUS_NO_DATA_DETECTED ((NTSTATUS) 0x80000022L) #endif #ifndef STATUS_REDIRECTOR_HAS_OPEN_HANDLES # define STATUS_REDIRECTOR_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000023L) #endif #ifndef STATUS_SERVER_HAS_OPEN_HANDLES # define STATUS_SERVER_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000024L) #endif #ifndef STATUS_ALREADY_DISCONNECTED # define STATUS_ALREADY_DISCONNECTED ((NTSTATUS) 0x80000025L) #endif #ifndef STATUS_LONGJUMP # define STATUS_LONGJUMP ((NTSTATUS) 0x80000026L) #endif #ifndef STATUS_CLEANER_CARTRIDGE_INSTALLED # define STATUS_CLEANER_CARTRIDGE_INSTALLED ((NTSTATUS) 0x80000027L) #endif #ifndef STATUS_PLUGPLAY_QUERY_VETOED # define STATUS_PLUGPLAY_QUERY_VETOED ((NTSTATUS) 0x80000028L) #endif #ifndef STATUS_UNWIND_CONSOLIDATE # define STATUS_UNWIND_CONSOLIDATE ((NTSTATUS) 0x80000029L) #endif #ifndef STATUS_REGISTRY_HIVE_RECOVERED # define STATUS_REGISTRY_HIVE_RECOVERED ((NTSTATUS) 0x8000002AL) #endif #ifndef STATUS_DLL_MIGHT_BE_INSECURE # define STATUS_DLL_MIGHT_BE_INSECURE ((NTSTATUS) 0x8000002BL) #endif #ifndef STATUS_DLL_MIGHT_BE_INCOMPATIBLE # define STATUS_DLL_MIGHT_BE_INCOMPATIBLE ((NTSTATUS) 0x8000002CL) #endif #ifndef STATUS_STOPPED_ON_SYMLINK # define STATUS_STOPPED_ON_SYMLINK ((NTSTATUS) 0x8000002DL) #endif #ifndef STATUS_CANNOT_GRANT_REQUESTED_OPLOCK # define STATUS_CANNOT_GRANT_REQUESTED_OPLOCK ((NTSTATUS) 0x8000002EL) #endif #ifndef STATUS_NO_ACE_CONDITION # define STATUS_NO_ACE_CONDITION ((NTSTATUS) 0x8000002FL) #endif #ifndef STATUS_UNSUCCESSFUL # define STATUS_UNSUCCESSFUL ((NTSTATUS) 0xC0000001L) #endif #ifndef STATUS_NOT_IMPLEMENTED # define STATUS_NOT_IMPLEMENTED ((NTSTATUS) 0xC0000002L) #endif #ifndef STATUS_INVALID_INFO_CLASS # define STATUS_INVALID_INFO_CLASS ((NTSTATUS) 0xC0000003L) #endif #ifndef STATUS_INFO_LENGTH_MISMATCH # define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004L) #endif #ifndef STATUS_ACCESS_VIOLATION # define STATUS_ACCESS_VIOLATION ((NTSTATUS) 0xC0000005L) #endif #ifndef STATUS_IN_PAGE_ERROR # define STATUS_IN_PAGE_ERROR ((NTSTATUS) 0xC0000006L) #endif #ifndef STATUS_PAGEFILE_QUOTA # define STATUS_PAGEFILE_QUOTA ((NTSTATUS) 0xC0000007L) #endif #ifndef STATUS_INVALID_HANDLE # define STATUS_INVALID_HANDLE ((NTSTATUS) 0xC0000008L) #endif #ifndef STATUS_BAD_INITIAL_STACK # define STATUS_BAD_INITIAL_STACK ((NTSTATUS) 0xC0000009L) #endif #ifndef STATUS_BAD_INITIAL_PC # define STATUS_BAD_INITIAL_PC ((NTSTATUS) 0xC000000AL) #endif #ifndef STATUS_INVALID_CID # define STATUS_INVALID_CID ((NTSTATUS) 0xC000000BL) #endif #ifndef STATUS_TIMER_NOT_CANCELED # define STATUS_TIMER_NOT_CANCELED ((NTSTATUS) 0xC000000CL) #endif #ifndef STATUS_INVALID_PARAMETER # define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xC000000DL) #endif #ifndef STATUS_NO_SUCH_DEVICE # define STATUS_NO_SUCH_DEVICE ((NTSTATUS) 0xC000000EL) #endif #ifndef STATUS_NO_SUCH_FILE # define STATUS_NO_SUCH_FILE ((NTSTATUS) 0xC000000FL) #endif #ifndef STATUS_INVALID_DEVICE_REQUEST # define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS) 0xC0000010L) #endif #ifndef STATUS_END_OF_FILE # define STATUS_END_OF_FILE ((NTSTATUS) 0xC0000011L) #endif #ifndef STATUS_WRONG_VOLUME # define STATUS_WRONG_VOLUME ((NTSTATUS) 0xC0000012L) #endif #ifndef STATUS_NO_MEDIA_IN_DEVICE # define STATUS_NO_MEDIA_IN_DEVICE ((NTSTATUS) 0xC0000013L) #endif #ifndef STATUS_UNRECOGNIZED_MEDIA # define STATUS_UNRECOGNIZED_MEDIA ((NTSTATUS) 0xC0000014L) #endif #ifndef STATUS_NONEXISTENT_SECTOR # define STATUS_NONEXISTENT_SECTOR ((NTSTATUS) 0xC0000015L) #endif #ifndef STATUS_MORE_PROCESSING_REQUIRED # define STATUS_MORE_PROCESSING_REQUIRED ((NTSTATUS) 0xC0000016L) #endif #ifndef STATUS_NO_MEMORY # define STATUS_NO_MEMORY ((NTSTATUS) 0xC0000017L) #endif #ifndef STATUS_CONFLICTING_ADDRESSES # define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS) 0xC0000018L) #endif #ifndef STATUS_NOT_MAPPED_VIEW # define STATUS_NOT_MAPPED_VIEW ((NTSTATUS) 0xC0000019L) #endif #ifndef STATUS_UNABLE_TO_FREE_VM # define STATUS_UNABLE_TO_FREE_VM ((NTSTATUS) 0xC000001AL) #endif #ifndef STATUS_UNABLE_TO_DELETE_SECTION # define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS) 0xC000001BL) #endif #ifndef STATUS_INVALID_SYSTEM_SERVICE # define STATUS_INVALID_SYSTEM_SERVICE ((NTSTATUS) 0xC000001CL) #endif #ifndef STATUS_ILLEGAL_INSTRUCTION # define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS) 0xC000001DL) #endif #ifndef STATUS_INVALID_LOCK_SEQUENCE # define STATUS_INVALID_LOCK_SEQUENCE ((NTSTATUS) 0xC000001EL) #endif #ifndef STATUS_INVALID_VIEW_SIZE # define STATUS_INVALID_VIEW_SIZE ((NTSTATUS) 0xC000001FL) #endif #ifndef STATUS_INVALID_FILE_FOR_SECTION # define STATUS_INVALID_FILE_FOR_SECTION ((NTSTATUS) 0xC0000020L) #endif #ifndef STATUS_ALREADY_COMMITTED # define STATUS_ALREADY_COMMITTED ((NTSTATUS) 0xC0000021L) #endif #ifndef STATUS_ACCESS_DENIED # define STATUS_ACCESS_DENIED ((NTSTATUS) 0xC0000022L) #endif #ifndef STATUS_BUFFER_TOO_SMALL # define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xC0000023L) #endif #ifndef STATUS_OBJECT_TYPE_MISMATCH # define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS) 0xC0000024L) #endif #ifndef STATUS_NONCONTINUABLE_EXCEPTION # define STATUS_NONCONTINUABLE_EXCEPTION ((NTSTATUS) 0xC0000025L) #endif #ifndef STATUS_INVALID_DISPOSITION # define STATUS_INVALID_DISPOSITION ((NTSTATUS) 0xC0000026L) #endif #ifndef STATUS_UNWIND # define STATUS_UNWIND ((NTSTATUS) 0xC0000027L) #endif #ifndef STATUS_BAD_STACK # define STATUS_BAD_STACK ((NTSTATUS) 0xC0000028L) #endif #ifndef STATUS_INVALID_UNWIND_TARGET # define STATUS_INVALID_UNWIND_TARGET ((NTSTATUS) 0xC0000029L) #endif #ifndef STATUS_NOT_LOCKED # define STATUS_NOT_LOCKED ((NTSTATUS) 0xC000002AL) #endif #ifndef STATUS_PARITY_ERROR # define STATUS_PARITY_ERROR ((NTSTATUS) 0xC000002BL) #endif #ifndef STATUS_UNABLE_TO_DECOMMIT_VM # define STATUS_UNABLE_TO_DECOMMIT_VM ((NTSTATUS) 0xC000002CL) #endif #ifndef STATUS_NOT_COMMITTED # define STATUS_NOT_COMMITTED ((NTSTATUS) 0xC000002DL) #endif #ifndef STATUS_INVALID_PORT_ATTRIBUTES # define STATUS_INVALID_PORT_ATTRIBUTES ((NTSTATUS) 0xC000002EL) #endif #ifndef STATUS_PORT_MESSAGE_TOO_LONG # define STATUS_PORT_MESSAGE_TOO_LONG ((NTSTATUS) 0xC000002FL) #endif #ifndef STATUS_INVALID_PARAMETER_MIX # define STATUS_INVALID_PARAMETER_MIX ((NTSTATUS) 0xC0000030L) #endif #ifndef STATUS_INVALID_QUOTA_LOWER # define STATUS_INVALID_QUOTA_LOWER ((NTSTATUS) 0xC0000031L) #endif #ifndef STATUS_DISK_CORRUPT_ERROR # define STATUS_DISK_CORRUPT_ERROR ((NTSTATUS) 0xC0000032L) #endif #ifndef STATUS_OBJECT_NAME_INVALID # define STATUS_OBJECT_NAME_INVALID ((NTSTATUS) 0xC0000033L) #endif #ifndef STATUS_OBJECT_NAME_NOT_FOUND # define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS) 0xC0000034L) #endif #ifndef STATUS_OBJECT_NAME_COLLISION # define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS) 0xC0000035L) #endif #ifndef STATUS_PORT_DISCONNECTED # define STATUS_PORT_DISCONNECTED ((NTSTATUS) 0xC0000037L) #endif #ifndef STATUS_DEVICE_ALREADY_ATTACHED # define STATUS_DEVICE_ALREADY_ATTACHED ((NTSTATUS) 0xC0000038L) #endif #ifndef STATUS_OBJECT_PATH_INVALID # define STATUS_OBJECT_PATH_INVALID ((NTSTATUS) 0xC0000039L) #endif #ifndef STATUS_OBJECT_PATH_NOT_FOUND # define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS) 0xC000003AL) #endif #ifndef STATUS_OBJECT_PATH_SYNTAX_BAD # define STATUS_OBJECT_PATH_SYNTAX_BAD ((NTSTATUS) 0xC000003BL) #endif #ifndef STATUS_DATA_OVERRUN # define STATUS_DATA_OVERRUN ((NTSTATUS) 0xC000003CL) #endif #ifndef STATUS_DATA_LATE_ERROR # define STATUS_DATA_LATE_ERROR ((NTSTATUS) 0xC000003DL) #endif #ifndef STATUS_DATA_ERROR # define STATUS_DATA_ERROR ((NTSTATUS) 0xC000003EL) #endif #ifndef STATUS_CRC_ERROR # define STATUS_CRC_ERROR ((NTSTATUS) 0xC000003FL) #endif #ifndef STATUS_SECTION_TOO_BIG # define STATUS_SECTION_TOO_BIG ((NTSTATUS) 0xC0000040L) #endif #ifndef STATUS_PORT_CONNECTION_REFUSED # define STATUS_PORT_CONNECTION_REFUSED ((NTSTATUS) 0xC0000041L) #endif #ifndef STATUS_INVALID_PORT_HANDLE # define STATUS_INVALID_PORT_HANDLE ((NTSTATUS) 0xC0000042L) #endif #ifndef STATUS_SHARING_VIOLATION # define STATUS_SHARING_VIOLATION ((NTSTATUS) 0xC0000043L) #endif #ifndef STATUS_QUOTA_EXCEEDED # define STATUS_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000044L) #endif #ifndef STATUS_INVALID_PAGE_PROTECTION # define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS) 0xC0000045L) #endif #ifndef STATUS_MUTANT_NOT_OWNED # define STATUS_MUTANT_NOT_OWNED ((NTSTATUS) 0xC0000046L) #endif #ifndef STATUS_SEMAPHORE_LIMIT_EXCEEDED # define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000047L) #endif #ifndef STATUS_PORT_ALREADY_SET # define STATUS_PORT_ALREADY_SET ((NTSTATUS) 0xC0000048L) #endif #ifndef STATUS_SECTION_NOT_IMAGE # define STATUS_SECTION_NOT_IMAGE ((NTSTATUS) 0xC0000049L) #endif #ifndef STATUS_SUSPEND_COUNT_EXCEEDED # define STATUS_SUSPEND_COUNT_EXCEEDED ((NTSTATUS) 0xC000004AL) #endif #ifndef STATUS_THREAD_IS_TERMINATING # define STATUS_THREAD_IS_TERMINATING ((NTSTATUS) 0xC000004BL) #endif #ifndef STATUS_BAD_WORKING_SET_LIMIT # define STATUS_BAD_WORKING_SET_LIMIT ((NTSTATUS) 0xC000004CL) #endif #ifndef STATUS_INCOMPATIBLE_FILE_MAP # define STATUS_INCOMPATIBLE_FILE_MAP ((NTSTATUS) 0xC000004DL) #endif #ifndef STATUS_SECTION_PROTECTION # define STATUS_SECTION_PROTECTION ((NTSTATUS) 0xC000004EL) #endif #ifndef STATUS_EAS_NOT_SUPPORTED # define STATUS_EAS_NOT_SUPPORTED ((NTSTATUS) 0xC000004FL) #endif #ifndef STATUS_EA_TOO_LARGE # define STATUS_EA_TOO_LARGE ((NTSTATUS) 0xC0000050L) #endif #ifndef STATUS_NONEXISTENT_EA_ENTRY # define STATUS_NONEXISTENT_EA_ENTRY ((NTSTATUS) 0xC0000051L) #endif #ifndef STATUS_NO_EAS_ON_FILE # define STATUS_NO_EAS_ON_FILE ((NTSTATUS) 0xC0000052L) #endif #ifndef STATUS_EA_CORRUPT_ERROR # define STATUS_EA_CORRUPT_ERROR ((NTSTATUS) 0xC0000053L) #endif #ifndef STATUS_FILE_LOCK_CONFLICT # define STATUS_FILE_LOCK_CONFLICT ((NTSTATUS) 0xC0000054L) #endif #ifndef STATUS_LOCK_NOT_GRANTED # define STATUS_LOCK_NOT_GRANTED ((NTSTATUS) 0xC0000055L) #endif #ifndef STATUS_DELETE_PENDING # define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056L) #endif #ifndef STATUS_CTL_FILE_NOT_SUPPORTED # define STATUS_CTL_FILE_NOT_SUPPORTED ((NTSTATUS) 0xC0000057L) #endif #ifndef STATUS_UNKNOWN_REVISION # define STATUS_UNKNOWN_REVISION ((NTSTATUS) 0xC0000058L) #endif #ifndef STATUS_REVISION_MISMATCH # define STATUS_REVISION_MISMATCH ((NTSTATUS) 0xC0000059L) #endif #ifndef STATUS_INVALID_OWNER # define STATUS_INVALID_OWNER ((NTSTATUS) 0xC000005AL) #endif #ifndef STATUS_INVALID_PRIMARY_GROUP # define STATUS_INVALID_PRIMARY_GROUP ((NTSTATUS) 0xC000005BL) #endif #ifndef STATUS_NO_IMPERSONATION_TOKEN # define STATUS_NO_IMPERSONATION_TOKEN ((NTSTATUS) 0xC000005CL) #endif #ifndef STATUS_CANT_DISABLE_MANDATORY # define STATUS_CANT_DISABLE_MANDATORY ((NTSTATUS) 0xC000005DL) #endif #ifndef STATUS_NO_LOGON_SERVERS # define STATUS_NO_LOGON_SERVERS ((NTSTATUS) 0xC000005EL) #endif #ifndef STATUS_NO_SUCH_LOGON_SESSION # define STATUS_NO_SUCH_LOGON_SESSION ((NTSTATUS) 0xC000005FL) #endif #ifndef STATUS_NO_SUCH_PRIVILEGE # define STATUS_NO_SUCH_PRIVILEGE ((NTSTATUS) 0xC0000060L) #endif #ifndef STATUS_PRIVILEGE_NOT_HELD # define STATUS_PRIVILEGE_NOT_HELD ((NTSTATUS) 0xC0000061L) #endif #ifndef STATUS_INVALID_ACCOUNT_NAME # define STATUS_INVALID_ACCOUNT_NAME ((NTSTATUS) 0xC0000062L) #endif #ifndef STATUS_USER_EXISTS # define STATUS_USER_EXISTS ((NTSTATUS) 0xC0000063L) #endif #ifndef STATUS_NO_SUCH_USER # define STATUS_NO_SUCH_USER ((NTSTATUS) 0xC0000064L) #endif #ifndef STATUS_GROUP_EXISTS # define STATUS_GROUP_EXISTS ((NTSTATUS) 0xC0000065L) #endif #ifndef STATUS_NO_SUCH_GROUP # define STATUS_NO_SUCH_GROUP ((NTSTATUS) 0xC0000066L) #endif #ifndef STATUS_MEMBER_IN_GROUP # define STATUS_MEMBER_IN_GROUP ((NTSTATUS) 0xC0000067L) #endif #ifndef STATUS_MEMBER_NOT_IN_GROUP # define STATUS_MEMBER_NOT_IN_GROUP ((NTSTATUS) 0xC0000068L) #endif #ifndef STATUS_LAST_ADMIN # define STATUS_LAST_ADMIN ((NTSTATUS) 0xC0000069L) #endif #ifndef STATUS_WRONG_PASSWORD # define STATUS_WRONG_PASSWORD ((NTSTATUS) 0xC000006AL) #endif #ifndef STATUS_ILL_FORMED_PASSWORD # define STATUS_ILL_FORMED_PASSWORD ((NTSTATUS) 0xC000006BL) #endif #ifndef STATUS_PASSWORD_RESTRICTION # define STATUS_PASSWORD_RESTRICTION ((NTSTATUS) 0xC000006CL) #endif #ifndef STATUS_LOGON_FAILURE # define STATUS_LOGON_FAILURE ((NTSTATUS) 0xC000006DL) #endif #ifndef STATUS_ACCOUNT_RESTRICTION # define STATUS_ACCOUNT_RESTRICTION ((NTSTATUS) 0xC000006EL) #endif #ifndef STATUS_INVALID_LOGON_HOURS # define STATUS_INVALID_LOGON_HOURS ((NTSTATUS) 0xC000006FL) #endif #ifndef STATUS_INVALID_WORKSTATION # define STATUS_INVALID_WORKSTATION ((NTSTATUS) 0xC0000070L) #endif #ifndef STATUS_PASSWORD_EXPIRED # define STATUS_PASSWORD_EXPIRED ((NTSTATUS) 0xC0000071L) #endif #ifndef STATUS_ACCOUNT_DISABLED # define STATUS_ACCOUNT_DISABLED ((NTSTATUS) 0xC0000072L) #endif #ifndef STATUS_NONE_MAPPED # define STATUS_NONE_MAPPED ((NTSTATUS) 0xC0000073L) #endif #ifndef STATUS_TOO_MANY_LUIDS_REQUESTED # define STATUS_TOO_MANY_LUIDS_REQUESTED ((NTSTATUS) 0xC0000074L) #endif #ifndef STATUS_LUIDS_EXHAUSTED # define STATUS_LUIDS_EXHAUSTED ((NTSTATUS) 0xC0000075L) #endif #ifndef STATUS_INVALID_SUB_AUTHORITY # define STATUS_INVALID_SUB_AUTHORITY ((NTSTATUS) 0xC0000076L) #endif #ifndef STATUS_INVALID_ACL # define STATUS_INVALID_ACL ((NTSTATUS) 0xC0000077L) #endif #ifndef STATUS_INVALID_SID # define STATUS_INVALID_SID ((NTSTATUS) 0xC0000078L) #endif #ifndef STATUS_INVALID_SECURITY_DESCR # define STATUS_INVALID_SECURITY_DESCR ((NTSTATUS) 0xC0000079L) #endif #ifndef STATUS_PROCEDURE_NOT_FOUND # define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS) 0xC000007AL) #endif #ifndef STATUS_INVALID_IMAGE_FORMAT # define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS) 0xC000007BL) #endif #ifndef STATUS_NO_TOKEN # define STATUS_NO_TOKEN ((NTSTATUS) 0xC000007CL) #endif #ifndef STATUS_BAD_INHERITANCE_ACL # define STATUS_BAD_INHERITANCE_ACL ((NTSTATUS) 0xC000007DL) #endif #ifndef STATUS_RANGE_NOT_LOCKED # define STATUS_RANGE_NOT_LOCKED ((NTSTATUS) 0xC000007EL) #endif #ifndef STATUS_DISK_FULL # define STATUS_DISK_FULL ((NTSTATUS) 0xC000007FL) #endif #ifndef STATUS_SERVER_DISABLED # define STATUS_SERVER_DISABLED ((NTSTATUS) 0xC0000080L) #endif #ifndef STATUS_SERVER_NOT_DISABLED # define STATUS_SERVER_NOT_DISABLED ((NTSTATUS) 0xC0000081L) #endif #ifndef STATUS_TOO_MANY_GUIDS_REQUESTED # define STATUS_TOO_MANY_GUIDS_REQUESTED ((NTSTATUS) 0xC0000082L) #endif #ifndef STATUS_GUIDS_EXHAUSTED # define STATUS_GUIDS_EXHAUSTED ((NTSTATUS) 0xC0000083L) #endif #ifndef STATUS_INVALID_ID_AUTHORITY # define STATUS_INVALID_ID_AUTHORITY ((NTSTATUS) 0xC0000084L) #endif #ifndef STATUS_AGENTS_EXHAUSTED # define STATUS_AGENTS_EXHAUSTED ((NTSTATUS) 0xC0000085L) #endif #ifndef STATUS_INVALID_VOLUME_LABEL # define STATUS_INVALID_VOLUME_LABEL ((NTSTATUS) 0xC0000086L) #endif #ifndef STATUS_SECTION_NOT_EXTENDED # define STATUS_SECTION_NOT_EXTENDED ((NTSTATUS) 0xC0000087L) #endif #ifndef STATUS_NOT_MAPPED_DATA # define STATUS_NOT_MAPPED_DATA ((NTSTATUS) 0xC0000088L) #endif #ifndef STATUS_RESOURCE_DATA_NOT_FOUND # define STATUS_RESOURCE_DATA_NOT_FOUND ((NTSTATUS) 0xC0000089L) #endif #ifndef STATUS_RESOURCE_TYPE_NOT_FOUND # define STATUS_RESOURCE_TYPE_NOT_FOUND ((NTSTATUS) 0xC000008AL) #endif #ifndef STATUS_RESOURCE_NAME_NOT_FOUND # define STATUS_RESOURCE_NAME_NOT_FOUND ((NTSTATUS) 0xC000008BL) #endif #ifndef STATUS_ARRAY_BOUNDS_EXCEEDED # define STATUS_ARRAY_BOUNDS_EXCEEDED ((NTSTATUS) 0xC000008CL) #endif #ifndef STATUS_FLOAT_DENORMAL_OPERAND # define STATUS_FLOAT_DENORMAL_OPERAND ((NTSTATUS) 0xC000008DL) #endif #ifndef STATUS_FLOAT_DIVIDE_BY_ZERO # define STATUS_FLOAT_DIVIDE_BY_ZERO ((NTSTATUS) 0xC000008EL) #endif #ifndef STATUS_FLOAT_INEXACT_RESULT # define STATUS_FLOAT_INEXACT_RESULT ((NTSTATUS) 0xC000008FL) #endif #ifndef STATUS_FLOAT_INVALID_OPERATION # define STATUS_FLOAT_INVALID_OPERATION ((NTSTATUS) 0xC0000090L) #endif #ifndef STATUS_FLOAT_OVERFLOW # define STATUS_FLOAT_OVERFLOW ((NTSTATUS) 0xC0000091L) #endif #ifndef STATUS_FLOAT_STACK_CHECK # define STATUS_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000092L) #endif #ifndef STATUS_FLOAT_UNDERFLOW # define STATUS_FLOAT_UNDERFLOW ((NTSTATUS) 0xC0000093L) #endif #ifndef STATUS_INTEGER_DIVIDE_BY_ZERO # define STATUS_INTEGER_DIVIDE_BY_ZERO ((NTSTATUS) 0xC0000094L) #endif #ifndef STATUS_INTEGER_OVERFLOW # define STATUS_INTEGER_OVERFLOW ((NTSTATUS) 0xC0000095L) #endif #ifndef STATUS_PRIVILEGED_INSTRUCTION # define STATUS_PRIVILEGED_INSTRUCTION ((NTSTATUS) 0xC0000096L) #endif #ifndef STATUS_TOO_MANY_PAGING_FILES # define STATUS_TOO_MANY_PAGING_FILES ((NTSTATUS) 0xC0000097L) #endif #ifndef STATUS_FILE_INVALID # define STATUS_FILE_INVALID ((NTSTATUS) 0xC0000098L) #endif #ifndef STATUS_ALLOTTED_SPACE_EXCEEDED # define STATUS_ALLOTTED_SPACE_EXCEEDED ((NTSTATUS) 0xC0000099L) #endif #ifndef STATUS_INSUFFICIENT_RESOURCES # define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS) 0xC000009AL) #endif #ifndef STATUS_DFS_EXIT_PATH_FOUND # define STATUS_DFS_EXIT_PATH_FOUND ((NTSTATUS) 0xC000009BL) #endif #ifndef STATUS_DEVICE_DATA_ERROR # define STATUS_DEVICE_DATA_ERROR ((NTSTATUS) 0xC000009CL) #endif #ifndef STATUS_DEVICE_NOT_CONNECTED # define STATUS_DEVICE_NOT_CONNECTED ((NTSTATUS) 0xC000009DL) #endif #ifndef STATUS_DEVICE_POWER_FAILURE # define STATUS_DEVICE_POWER_FAILURE ((NTSTATUS) 0xC000009EL) #endif #ifndef STATUS_FREE_VM_NOT_AT_BASE # define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS) 0xC000009FL) #endif #ifndef STATUS_MEMORY_NOT_ALLOCATED # define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS) 0xC00000A0L) #endif #ifndef STATUS_WORKING_SET_QUOTA # define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xC00000A1L) #endif #ifndef STATUS_MEDIA_WRITE_PROTECTED # define STATUS_MEDIA_WRITE_PROTECTED ((NTSTATUS) 0xC00000A2L) #endif #ifndef STATUS_DEVICE_NOT_READY # define STATUS_DEVICE_NOT_READY ((NTSTATUS) 0xC00000A3L) #endif #ifndef STATUS_INVALID_GROUP_ATTRIBUTES # define STATUS_INVALID_GROUP_ATTRIBUTES ((NTSTATUS) 0xC00000A4L) #endif #ifndef STATUS_BAD_IMPERSONATION_LEVEL # define STATUS_BAD_IMPERSONATION_LEVEL ((NTSTATUS) 0xC00000A5L) #endif #ifndef STATUS_CANT_OPEN_ANONYMOUS # define STATUS_CANT_OPEN_ANONYMOUS ((NTSTATUS) 0xC00000A6L) #endif #ifndef STATUS_BAD_VALIDATION_CLASS # define STATUS_BAD_VALIDATION_CLASS ((NTSTATUS) 0xC00000A7L) #endif #ifndef STATUS_BAD_TOKEN_TYPE # define STATUS_BAD_TOKEN_TYPE ((NTSTATUS) 0xC00000A8L) #endif #ifndef STATUS_BAD_MASTER_BOOT_RECORD # define STATUS_BAD_MASTER_BOOT_RECORD ((NTSTATUS) 0xC00000A9L) #endif #ifndef STATUS_INSTRUCTION_MISALIGNMENT # define STATUS_INSTRUCTION_MISALIGNMENT ((NTSTATUS) 0xC00000AAL) #endif #ifndef STATUS_INSTANCE_NOT_AVAILABLE # define STATUS_INSTANCE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ABL) #endif #ifndef STATUS_PIPE_NOT_AVAILABLE # define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ACL) #endif #ifndef STATUS_INVALID_PIPE_STATE # define STATUS_INVALID_PIPE_STATE ((NTSTATUS) 0xC00000ADL) #endif #ifndef STATUS_PIPE_BUSY # define STATUS_PIPE_BUSY ((NTSTATUS) 0xC00000AEL) #endif #ifndef STATUS_ILLEGAL_FUNCTION # define STATUS_ILLEGAL_FUNCTION ((NTSTATUS) 0xC00000AFL) #endif #ifndef STATUS_PIPE_DISCONNECTED # define STATUS_PIPE_DISCONNECTED ((NTSTATUS) 0xC00000B0L) #endif #ifndef STATUS_PIPE_CLOSING # define STATUS_PIPE_CLOSING ((NTSTATUS) 0xC00000B1L) #endif #ifndef STATUS_PIPE_CONNECTED # define STATUS_PIPE_CONNECTED ((NTSTATUS) 0xC00000B2L) #endif #ifndef STATUS_PIPE_LISTENING # define STATUS_PIPE_LISTENING ((NTSTATUS) 0xC00000B3L) #endif #ifndef STATUS_INVALID_READ_MODE # define STATUS_INVALID_READ_MODE ((NTSTATUS) 0xC00000B4L) #endif #ifndef STATUS_IO_TIMEOUT # define STATUS_IO_TIMEOUT ((NTSTATUS) 0xC00000B5L) #endif #ifndef STATUS_FILE_FORCED_CLOSED # define STATUS_FILE_FORCED_CLOSED ((NTSTATUS) 0xC00000B6L) #endif #ifndef STATUS_PROFILING_NOT_STARTED # define STATUS_PROFILING_NOT_STARTED ((NTSTATUS) 0xC00000B7L) #endif #ifndef STATUS_PROFILING_NOT_STOPPED # define STATUS_PROFILING_NOT_STOPPED ((NTSTATUS) 0xC00000B8L) #endif #ifndef STATUS_COULD_NOT_INTERPRET # define STATUS_COULD_NOT_INTERPRET ((NTSTATUS) 0xC00000B9L) #endif #ifndef STATUS_FILE_IS_A_DIRECTORY # define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS) 0xC00000BAL) #endif #ifndef STATUS_NOT_SUPPORTED # define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xC00000BBL) #endif #ifndef STATUS_REMOTE_NOT_LISTENING # define STATUS_REMOTE_NOT_LISTENING ((NTSTATUS) 0xC00000BCL) #endif #ifndef STATUS_DUPLICATE_NAME # define STATUS_DUPLICATE_NAME ((NTSTATUS) 0xC00000BDL) #endif #ifndef STATUS_BAD_NETWORK_PATH # define STATUS_BAD_NETWORK_PATH ((NTSTATUS) 0xC00000BEL) #endif #ifndef STATUS_NETWORK_BUSY # define STATUS_NETWORK_BUSY ((NTSTATUS) 0xC00000BFL) #endif #ifndef STATUS_DEVICE_DOES_NOT_EXIST # define STATUS_DEVICE_DOES_NOT_EXIST ((NTSTATUS) 0xC00000C0L) #endif #ifndef STATUS_TOO_MANY_COMMANDS # define STATUS_TOO_MANY_COMMANDS ((NTSTATUS) 0xC00000C1L) #endif #ifndef STATUS_ADAPTER_HARDWARE_ERROR # define STATUS_ADAPTER_HARDWARE_ERROR ((NTSTATUS) 0xC00000C2L) #endif #ifndef STATUS_INVALID_NETWORK_RESPONSE # define STATUS_INVALID_NETWORK_RESPONSE ((NTSTATUS) 0xC00000C3L) #endif #ifndef STATUS_UNEXPECTED_NETWORK_ERROR # define STATUS_UNEXPECTED_NETWORK_ERROR ((NTSTATUS) 0xC00000C4L) #endif #ifndef STATUS_BAD_REMOTE_ADAPTER # define STATUS_BAD_REMOTE_ADAPTER ((NTSTATUS) 0xC00000C5L) #endif #ifndef STATUS_PRINT_QUEUE_FULL # define STATUS_PRINT_QUEUE_FULL ((NTSTATUS) 0xC00000C6L) #endif #ifndef STATUS_NO_SPOOL_SPACE # define STATUS_NO_SPOOL_SPACE ((NTSTATUS) 0xC00000C7L) #endif #ifndef STATUS_PRINT_CANCELLED # define STATUS_PRINT_CANCELLED ((NTSTATUS) 0xC00000C8L) #endif #ifndef STATUS_NETWORK_NAME_DELETED # define STATUS_NETWORK_NAME_DELETED ((NTSTATUS) 0xC00000C9L) #endif #ifndef STATUS_NETWORK_ACCESS_DENIED # define STATUS_NETWORK_ACCESS_DENIED ((NTSTATUS) 0xC00000CAL) #endif #ifndef STATUS_BAD_DEVICE_TYPE # define STATUS_BAD_DEVICE_TYPE ((NTSTATUS) 0xC00000CBL) #endif #ifndef STATUS_BAD_NETWORK_NAME # define STATUS_BAD_NETWORK_NAME ((NTSTATUS) 0xC00000CCL) #endif #ifndef STATUS_TOO_MANY_NAMES # define STATUS_TOO_MANY_NAMES ((NTSTATUS) 0xC00000CDL) #endif #ifndef STATUS_TOO_MANY_SESSIONS # define STATUS_TOO_MANY_SESSIONS ((NTSTATUS) 0xC00000CEL) #endif #ifndef STATUS_SHARING_PAUSED # define STATUS_SHARING_PAUSED ((NTSTATUS) 0xC00000CFL) #endif #ifndef STATUS_REQUEST_NOT_ACCEPTED # define STATUS_REQUEST_NOT_ACCEPTED ((NTSTATUS) 0xC00000D0L) #endif #ifndef STATUS_REDIRECTOR_PAUSED # define STATUS_REDIRECTOR_PAUSED ((NTSTATUS) 0xC00000D1L) #endif #ifndef STATUS_NET_WRITE_FAULT # define STATUS_NET_WRITE_FAULT ((NTSTATUS) 0xC00000D2L) #endif #ifndef STATUS_PROFILING_AT_LIMIT # define STATUS_PROFILING_AT_LIMIT ((NTSTATUS) 0xC00000D3L) #endif #ifndef STATUS_NOT_SAME_DEVICE # define STATUS_NOT_SAME_DEVICE ((NTSTATUS) 0xC00000D4L) #endif #ifndef STATUS_FILE_RENAMED # define STATUS_FILE_RENAMED ((NTSTATUS) 0xC00000D5L) #endif #ifndef STATUS_VIRTUAL_CIRCUIT_CLOSED # define STATUS_VIRTUAL_CIRCUIT_CLOSED ((NTSTATUS) 0xC00000D6L) #endif #ifndef STATUS_NO_SECURITY_ON_OBJECT # define STATUS_NO_SECURITY_ON_OBJECT ((NTSTATUS) 0xC00000D7L) #endif #ifndef STATUS_CANT_WAIT # define STATUS_CANT_WAIT ((NTSTATUS) 0xC00000D8L) #endif #ifndef STATUS_PIPE_EMPTY # define STATUS_PIPE_EMPTY ((NTSTATUS) 0xC00000D9L) #endif #ifndef STATUS_CANT_ACCESS_DOMAIN_INFO # define STATUS_CANT_ACCESS_DOMAIN_INFO ((NTSTATUS) 0xC00000DAL) #endif #ifndef STATUS_CANT_TERMINATE_SELF # define STATUS_CANT_TERMINATE_SELF ((NTSTATUS) 0xC00000DBL) #endif #ifndef STATUS_INVALID_SERVER_STATE # define STATUS_INVALID_SERVER_STATE ((NTSTATUS) 0xC00000DCL) #endif #ifndef STATUS_INVALID_DOMAIN_STATE # define STATUS_INVALID_DOMAIN_STATE ((NTSTATUS) 0xC00000DDL) #endif #ifndef STATUS_INVALID_DOMAIN_ROLE # define STATUS_INVALID_DOMAIN_ROLE ((NTSTATUS) 0xC00000DEL) #endif #ifndef STATUS_NO_SUCH_DOMAIN # define STATUS_NO_SUCH_DOMAIN ((NTSTATUS) 0xC00000DFL) #endif #ifndef STATUS_DOMAIN_EXISTS # define STATUS_DOMAIN_EXISTS ((NTSTATUS) 0xC00000E0L) #endif #ifndef STATUS_DOMAIN_LIMIT_EXCEEDED # define STATUS_DOMAIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00000E1L) #endif #ifndef STATUS_OPLOCK_NOT_GRANTED # define STATUS_OPLOCK_NOT_GRANTED ((NTSTATUS) 0xC00000E2L) #endif #ifndef STATUS_INVALID_OPLOCK_PROTOCOL # define STATUS_INVALID_OPLOCK_PROTOCOL ((NTSTATUS) 0xC00000E3L) #endif #ifndef STATUS_INTERNAL_DB_CORRUPTION # define STATUS_INTERNAL_DB_CORRUPTION ((NTSTATUS) 0xC00000E4L) #endif #ifndef STATUS_INTERNAL_ERROR # define STATUS_INTERNAL_ERROR ((NTSTATUS) 0xC00000E5L) #endif #ifndef STATUS_GENERIC_NOT_MAPPED # define STATUS_GENERIC_NOT_MAPPED ((NTSTATUS) 0xC00000E6L) #endif #ifndef STATUS_BAD_DESCRIPTOR_FORMAT # define STATUS_BAD_DESCRIPTOR_FORMAT ((NTSTATUS) 0xC00000E7L) #endif #ifndef STATUS_INVALID_USER_BUFFER # define STATUS_INVALID_USER_BUFFER ((NTSTATUS) 0xC00000E8L) #endif #ifndef STATUS_UNEXPECTED_IO_ERROR # define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS) 0xC00000E9L) #endif #ifndef STATUS_UNEXPECTED_MM_CREATE_ERR # define STATUS_UNEXPECTED_MM_CREATE_ERR ((NTSTATUS) 0xC00000EAL) #endif #ifndef STATUS_UNEXPECTED_MM_MAP_ERROR # define STATUS_UNEXPECTED_MM_MAP_ERROR ((NTSTATUS) 0xC00000EBL) #endif #ifndef STATUS_UNEXPECTED_MM_EXTEND_ERR # define STATUS_UNEXPECTED_MM_EXTEND_ERR ((NTSTATUS) 0xC00000ECL) #endif #ifndef STATUS_NOT_LOGON_PROCESS # define STATUS_NOT_LOGON_PROCESS ((NTSTATUS) 0xC00000EDL) #endif #ifndef STATUS_LOGON_SESSION_EXISTS # define STATUS_LOGON_SESSION_EXISTS ((NTSTATUS) 0xC00000EEL) #endif #ifndef STATUS_INVALID_PARAMETER_1 # define STATUS_INVALID_PARAMETER_1 ((NTSTATUS) 0xC00000EFL) #endif #ifndef STATUS_INVALID_PARAMETER_2 # define STATUS_INVALID_PARAMETER_2 ((NTSTATUS) 0xC00000F0L) #endif #ifndef STATUS_INVALID_PARAMETER_3 # define STATUS_INVALID_PARAMETER_3 ((NTSTATUS) 0xC00000F1L) #endif #ifndef STATUS_INVALID_PARAMETER_4 # define STATUS_INVALID_PARAMETER_4 ((NTSTATUS) 0xC00000F2L) #endif #ifndef STATUS_INVALID_PARAMETER_5 # define STATUS_INVALID_PARAMETER_5 ((NTSTATUS) 0xC00000F3L) #endif #ifndef STATUS_INVALID_PARAMETER_6 # define STATUS_INVALID_PARAMETER_6 ((NTSTATUS) 0xC00000F4L) #endif #ifndef STATUS_INVALID_PARAMETER_7 # define STATUS_INVALID_PARAMETER_7 ((NTSTATUS) 0xC00000F5L) #endif #ifndef STATUS_INVALID_PARAMETER_8 # define STATUS_INVALID_PARAMETER_8 ((NTSTATUS) 0xC00000F6L) #endif #ifndef STATUS_INVALID_PARAMETER_9 # define STATUS_INVALID_PARAMETER_9 ((NTSTATUS) 0xC00000F7L) #endif #ifndef STATUS_INVALID_PARAMETER_10 # define STATUS_INVALID_PARAMETER_10 ((NTSTATUS) 0xC00000F8L) #endif #ifndef STATUS_INVALID_PARAMETER_11 # define STATUS_INVALID_PARAMETER_11 ((NTSTATUS) 0xC00000F9L) #endif #ifndef STATUS_INVALID_PARAMETER_12 # define STATUS_INVALID_PARAMETER_12 ((NTSTATUS) 0xC00000FAL) #endif #ifndef STATUS_REDIRECTOR_NOT_STARTED # define STATUS_REDIRECTOR_NOT_STARTED ((NTSTATUS) 0xC00000FBL) #endif #ifndef STATUS_REDIRECTOR_STARTED # define STATUS_REDIRECTOR_STARTED ((NTSTATUS) 0xC00000FCL) #endif #ifndef STATUS_STACK_OVERFLOW # define STATUS_STACK_OVERFLOW ((NTSTATUS) 0xC00000FDL) #endif #ifndef STATUS_NO_SUCH_PACKAGE # define STATUS_NO_SUCH_PACKAGE ((NTSTATUS) 0xC00000FEL) #endif #ifndef STATUS_BAD_FUNCTION_TABLE # define STATUS_BAD_FUNCTION_TABLE ((NTSTATUS) 0xC00000FFL) #endif #ifndef STATUS_VARIABLE_NOT_FOUND # define STATUS_VARIABLE_NOT_FOUND ((NTSTATUS) 0xC0000100L) #endif #ifndef STATUS_DIRECTORY_NOT_EMPTY # define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xC0000101L) #endif #ifndef STATUS_FILE_CORRUPT_ERROR # define STATUS_FILE_CORRUPT_ERROR ((NTSTATUS) 0xC0000102L) #endif #ifndef STATUS_NOT_A_DIRECTORY # define STATUS_NOT_A_DIRECTORY ((NTSTATUS) 0xC0000103L) #endif #ifndef STATUS_BAD_LOGON_SESSION_STATE # define STATUS_BAD_LOGON_SESSION_STATE ((NTSTATUS) 0xC0000104L) #endif #ifndef STATUS_LOGON_SESSION_COLLISION # define STATUS_LOGON_SESSION_COLLISION ((NTSTATUS) 0xC0000105L) #endif #ifndef STATUS_NAME_TOO_LONG # define STATUS_NAME_TOO_LONG ((NTSTATUS) 0xC0000106L) #endif #ifndef STATUS_FILES_OPEN # define STATUS_FILES_OPEN ((NTSTATUS) 0xC0000107L) #endif #ifndef STATUS_CONNECTION_IN_USE # define STATUS_CONNECTION_IN_USE ((NTSTATUS) 0xC0000108L) #endif #ifndef STATUS_MESSAGE_NOT_FOUND # define STATUS_MESSAGE_NOT_FOUND ((NTSTATUS) 0xC0000109L) #endif #ifndef STATUS_PROCESS_IS_TERMINATING # define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS) 0xC000010AL) #endif #ifndef STATUS_INVALID_LOGON_TYPE # define STATUS_INVALID_LOGON_TYPE ((NTSTATUS) 0xC000010BL) #endif #ifndef STATUS_NO_GUID_TRANSLATION # define STATUS_NO_GUID_TRANSLATION ((NTSTATUS) 0xC000010CL) #endif #ifndef STATUS_CANNOT_IMPERSONATE # define STATUS_CANNOT_IMPERSONATE ((NTSTATUS) 0xC000010DL) #endif #ifndef STATUS_IMAGE_ALREADY_LOADED # define STATUS_IMAGE_ALREADY_LOADED ((NTSTATUS) 0xC000010EL) #endif #ifndef STATUS_ABIOS_NOT_PRESENT # define STATUS_ABIOS_NOT_PRESENT ((NTSTATUS) 0xC000010FL) #endif #ifndef STATUS_ABIOS_LID_NOT_EXIST # define STATUS_ABIOS_LID_NOT_EXIST ((NTSTATUS) 0xC0000110L) #endif #ifndef STATUS_ABIOS_LID_ALREADY_OWNED # define STATUS_ABIOS_LID_ALREADY_OWNED ((NTSTATUS) 0xC0000111L) #endif #ifndef STATUS_ABIOS_NOT_LID_OWNER # define STATUS_ABIOS_NOT_LID_OWNER ((NTSTATUS) 0xC0000112L) #endif #ifndef STATUS_ABIOS_INVALID_COMMAND # define STATUS_ABIOS_INVALID_COMMAND ((NTSTATUS) 0xC0000113L) #endif #ifndef STATUS_ABIOS_INVALID_LID # define STATUS_ABIOS_INVALID_LID ((NTSTATUS) 0xC0000114L) #endif #ifndef STATUS_ABIOS_SELECTOR_NOT_AVAILABLE # define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE ((NTSTATUS) 0xC0000115L) #endif #ifndef STATUS_ABIOS_INVALID_SELECTOR # define STATUS_ABIOS_INVALID_SELECTOR ((NTSTATUS) 0xC0000116L) #endif #ifndef STATUS_NO_LDT # define STATUS_NO_LDT ((NTSTATUS) 0xC0000117L) #endif #ifndef STATUS_INVALID_LDT_SIZE # define STATUS_INVALID_LDT_SIZE ((NTSTATUS) 0xC0000118L) #endif #ifndef STATUS_INVALID_LDT_OFFSET # define STATUS_INVALID_LDT_OFFSET ((NTSTATUS) 0xC0000119L) #endif #ifndef STATUS_INVALID_LDT_DESCRIPTOR # define STATUS_INVALID_LDT_DESCRIPTOR ((NTSTATUS) 0xC000011AL) #endif #ifndef STATUS_INVALID_IMAGE_NE_FORMAT # define STATUS_INVALID_IMAGE_NE_FORMAT ((NTSTATUS) 0xC000011BL) #endif #ifndef STATUS_RXACT_INVALID_STATE # define STATUS_RXACT_INVALID_STATE ((NTSTATUS) 0xC000011CL) #endif #ifndef STATUS_RXACT_COMMIT_FAILURE # define STATUS_RXACT_COMMIT_FAILURE ((NTSTATUS) 0xC000011DL) #endif #ifndef STATUS_MAPPED_FILE_SIZE_ZERO # define STATUS_MAPPED_FILE_SIZE_ZERO ((NTSTATUS) 0xC000011EL) #endif #ifndef STATUS_TOO_MANY_OPENED_FILES # define STATUS_TOO_MANY_OPENED_FILES ((NTSTATUS) 0xC000011FL) #endif #ifndef STATUS_CANCELLED # define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) #endif #ifndef STATUS_CANNOT_DELETE # define STATUS_CANNOT_DELETE ((NTSTATUS) 0xC0000121L) #endif #ifndef STATUS_INVALID_COMPUTER_NAME # define STATUS_INVALID_COMPUTER_NAME ((NTSTATUS) 0xC0000122L) #endif #ifndef STATUS_FILE_DELETED # define STATUS_FILE_DELETED ((NTSTATUS) 0xC0000123L) #endif #ifndef STATUS_SPECIAL_ACCOUNT # define STATUS_SPECIAL_ACCOUNT ((NTSTATUS) 0xC0000124L) #endif #ifndef STATUS_SPECIAL_GROUP # define STATUS_SPECIAL_GROUP ((NTSTATUS) 0xC0000125L) #endif #ifndef STATUS_SPECIAL_USER # define STATUS_SPECIAL_USER ((NTSTATUS) 0xC0000126L) #endif #ifndef STATUS_MEMBERS_PRIMARY_GROUP # define STATUS_MEMBERS_PRIMARY_GROUP ((NTSTATUS) 0xC0000127L) #endif #ifndef STATUS_FILE_CLOSED # define STATUS_FILE_CLOSED ((NTSTATUS) 0xC0000128L) #endif #ifndef STATUS_TOO_MANY_THREADS # define STATUS_TOO_MANY_THREADS ((NTSTATUS) 0xC0000129L) #endif #ifndef STATUS_THREAD_NOT_IN_PROCESS # define STATUS_THREAD_NOT_IN_PROCESS ((NTSTATUS) 0xC000012AL) #endif #ifndef STATUS_TOKEN_ALREADY_IN_USE # define STATUS_TOKEN_ALREADY_IN_USE ((NTSTATUS) 0xC000012BL) #endif #ifndef STATUS_PAGEFILE_QUOTA_EXCEEDED # define STATUS_PAGEFILE_QUOTA_EXCEEDED ((NTSTATUS) 0xC000012CL) #endif #ifndef STATUS_COMMITMENT_LIMIT # define STATUS_COMMITMENT_LIMIT ((NTSTATUS) 0xC000012DL) #endif #ifndef STATUS_INVALID_IMAGE_LE_FORMAT # define STATUS_INVALID_IMAGE_LE_FORMAT ((NTSTATUS) 0xC000012EL) #endif #ifndef STATUS_INVALID_IMAGE_NOT_MZ # define STATUS_INVALID_IMAGE_NOT_MZ ((NTSTATUS) 0xC000012FL) #endif #ifndef STATUS_INVALID_IMAGE_PROTECT # define STATUS_INVALID_IMAGE_PROTECT ((NTSTATUS) 0xC0000130L) #endif #ifndef STATUS_INVALID_IMAGE_WIN_16 # define STATUS_INVALID_IMAGE_WIN_16 ((NTSTATUS) 0xC0000131L) #endif #ifndef STATUS_LOGON_SERVER_CONFLICT # define STATUS_LOGON_SERVER_CONFLICT ((NTSTATUS) 0xC0000132L) #endif #ifndef STATUS_TIME_DIFFERENCE_AT_DC # define STATUS_TIME_DIFFERENCE_AT_DC ((NTSTATUS) 0xC0000133L) #endif #ifndef STATUS_SYNCHRONIZATION_REQUIRED # define STATUS_SYNCHRONIZATION_REQUIRED ((NTSTATUS) 0xC0000134L) #endif #ifndef STATUS_DLL_NOT_FOUND # define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xC0000135L) #endif #ifndef STATUS_OPEN_FAILED # define STATUS_OPEN_FAILED ((NTSTATUS) 0xC0000136L) #endif #ifndef STATUS_IO_PRIVILEGE_FAILED # define STATUS_IO_PRIVILEGE_FAILED ((NTSTATUS) 0xC0000137L) #endif #ifndef STATUS_ORDINAL_NOT_FOUND # define STATUS_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000138L) #endif #ifndef STATUS_ENTRYPOINT_NOT_FOUND # define STATUS_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000139L) #endif #ifndef STATUS_CONTROL_C_EXIT # define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013AL) #endif #ifndef STATUS_LOCAL_DISCONNECT # define STATUS_LOCAL_DISCONNECT ((NTSTATUS) 0xC000013BL) #endif #ifndef STATUS_REMOTE_DISCONNECT # define STATUS_REMOTE_DISCONNECT ((NTSTATUS) 0xC000013CL) #endif #ifndef STATUS_REMOTE_RESOURCES # define STATUS_REMOTE_RESOURCES ((NTSTATUS) 0xC000013DL) #endif #ifndef STATUS_LINK_FAILED # define STATUS_LINK_FAILED ((NTSTATUS) 0xC000013EL) #endif #ifndef STATUS_LINK_TIMEOUT # define STATUS_LINK_TIMEOUT ((NTSTATUS) 0xC000013FL) #endif #ifndef STATUS_INVALID_CONNECTION # define STATUS_INVALID_CONNECTION ((NTSTATUS) 0xC0000140L) #endif #ifndef STATUS_INVALID_ADDRESS # define STATUS_INVALID_ADDRESS ((NTSTATUS) 0xC0000141L) #endif #ifndef STATUS_DLL_INIT_FAILED # define STATUS_DLL_INIT_FAILED ((NTSTATUS) 0xC0000142L) #endif #ifndef STATUS_MISSING_SYSTEMFILE # define STATUS_MISSING_SYSTEMFILE ((NTSTATUS) 0xC0000143L) #endif #ifndef STATUS_UNHANDLED_EXCEPTION # define STATUS_UNHANDLED_EXCEPTION ((NTSTATUS) 0xC0000144L) #endif #ifndef STATUS_APP_INIT_FAILURE # define STATUS_APP_INIT_FAILURE ((NTSTATUS) 0xC0000145L) #endif #ifndef STATUS_PAGEFILE_CREATE_FAILED # define STATUS_PAGEFILE_CREATE_FAILED ((NTSTATUS) 0xC0000146L) #endif #ifndef STATUS_NO_PAGEFILE # define STATUS_NO_PAGEFILE ((NTSTATUS) 0xC0000147L) #endif #ifndef STATUS_INVALID_LEVEL # define STATUS_INVALID_LEVEL ((NTSTATUS) 0xC0000148L) #endif #ifndef STATUS_WRONG_PASSWORD_CORE # define STATUS_WRONG_PASSWORD_CORE ((NTSTATUS) 0xC0000149L) #endif #ifndef STATUS_ILLEGAL_FLOAT_CONTEXT # define STATUS_ILLEGAL_FLOAT_CONTEXT ((NTSTATUS) 0xC000014AL) #endif #ifndef STATUS_PIPE_BROKEN # define STATUS_PIPE_BROKEN ((NTSTATUS) 0xC000014BL) #endif #ifndef STATUS_REGISTRY_CORRUPT # define STATUS_REGISTRY_CORRUPT ((NTSTATUS) 0xC000014CL) #endif #ifndef STATUS_REGISTRY_IO_FAILED # define STATUS_REGISTRY_IO_FAILED ((NTSTATUS) 0xC000014DL) #endif #ifndef STATUS_NO_EVENT_PAIR # define STATUS_NO_EVENT_PAIR ((NTSTATUS) 0xC000014EL) #endif #ifndef STATUS_UNRECOGNIZED_VOLUME # define STATUS_UNRECOGNIZED_VOLUME ((NTSTATUS) 0xC000014FL) #endif #ifndef STATUS_SERIAL_NO_DEVICE_INITED # define STATUS_SERIAL_NO_DEVICE_INITED ((NTSTATUS) 0xC0000150L) #endif #ifndef STATUS_NO_SUCH_ALIAS # define STATUS_NO_SUCH_ALIAS ((NTSTATUS) 0xC0000151L) #endif #ifndef STATUS_MEMBER_NOT_IN_ALIAS # define STATUS_MEMBER_NOT_IN_ALIAS ((NTSTATUS) 0xC0000152L) #endif #ifndef STATUS_MEMBER_IN_ALIAS # define STATUS_MEMBER_IN_ALIAS ((NTSTATUS) 0xC0000153L) #endif #ifndef STATUS_ALIAS_EXISTS # define STATUS_ALIAS_EXISTS ((NTSTATUS) 0xC0000154L) #endif #ifndef STATUS_LOGON_NOT_GRANTED # define STATUS_LOGON_NOT_GRANTED ((NTSTATUS) 0xC0000155L) #endif #ifndef STATUS_TOO_MANY_SECRETS # define STATUS_TOO_MANY_SECRETS ((NTSTATUS) 0xC0000156L) #endif #ifndef STATUS_SECRET_TOO_LONG # define STATUS_SECRET_TOO_LONG ((NTSTATUS) 0xC0000157L) #endif #ifndef STATUS_INTERNAL_DB_ERROR # define STATUS_INTERNAL_DB_ERROR ((NTSTATUS) 0xC0000158L) #endif #ifndef STATUS_FULLSCREEN_MODE # define STATUS_FULLSCREEN_MODE ((NTSTATUS) 0xC0000159L) #endif #ifndef STATUS_TOO_MANY_CONTEXT_IDS # define STATUS_TOO_MANY_CONTEXT_IDS ((NTSTATUS) 0xC000015AL) #endif #ifndef STATUS_LOGON_TYPE_NOT_GRANTED # define STATUS_LOGON_TYPE_NOT_GRANTED ((NTSTATUS) 0xC000015BL) #endif #ifndef STATUS_NOT_REGISTRY_FILE # define STATUS_NOT_REGISTRY_FILE ((NTSTATUS) 0xC000015CL) #endif #ifndef STATUS_NT_CROSS_ENCRYPTION_REQUIRED # define STATUS_NT_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000015DL) #endif #ifndef STATUS_DOMAIN_CTRLR_CONFIG_ERROR # define STATUS_DOMAIN_CTRLR_CONFIG_ERROR ((NTSTATUS) 0xC000015EL) #endif #ifndef STATUS_FT_MISSING_MEMBER # define STATUS_FT_MISSING_MEMBER ((NTSTATUS) 0xC000015FL) #endif #ifndef STATUS_ILL_FORMED_SERVICE_ENTRY # define STATUS_ILL_FORMED_SERVICE_ENTRY ((NTSTATUS) 0xC0000160L) #endif #ifndef STATUS_ILLEGAL_CHARACTER # define STATUS_ILLEGAL_CHARACTER ((NTSTATUS) 0xC0000161L) #endif #ifndef STATUS_UNMAPPABLE_CHARACTER # define STATUS_UNMAPPABLE_CHARACTER ((NTSTATUS) 0xC0000162L) #endif #ifndef STATUS_UNDEFINED_CHARACTER # define STATUS_UNDEFINED_CHARACTER ((NTSTATUS) 0xC0000163L) #endif #ifndef STATUS_FLOPPY_VOLUME # define STATUS_FLOPPY_VOLUME ((NTSTATUS) 0xC0000164L) #endif #ifndef STATUS_FLOPPY_ID_MARK_NOT_FOUND # define STATUS_FLOPPY_ID_MARK_NOT_FOUND ((NTSTATUS) 0xC0000165L) #endif #ifndef STATUS_FLOPPY_WRONG_CYLINDER # define STATUS_FLOPPY_WRONG_CYLINDER ((NTSTATUS) 0xC0000166L) #endif #ifndef STATUS_FLOPPY_UNKNOWN_ERROR # define STATUS_FLOPPY_UNKNOWN_ERROR ((NTSTATUS) 0xC0000167L) #endif #ifndef STATUS_FLOPPY_BAD_REGISTERS # define STATUS_FLOPPY_BAD_REGISTERS ((NTSTATUS) 0xC0000168L) #endif #ifndef STATUS_DISK_RECALIBRATE_FAILED # define STATUS_DISK_RECALIBRATE_FAILED ((NTSTATUS) 0xC0000169L) #endif #ifndef STATUS_DISK_OPERATION_FAILED # define STATUS_DISK_OPERATION_FAILED ((NTSTATUS) 0xC000016AL) #endif #ifndef STATUS_DISK_RESET_FAILED # define STATUS_DISK_RESET_FAILED ((NTSTATUS) 0xC000016BL) #endif #ifndef STATUS_SHARED_IRQ_BUSY # define STATUS_SHARED_IRQ_BUSY ((NTSTATUS) 0xC000016CL) #endif #ifndef STATUS_FT_ORPHANING # define STATUS_FT_ORPHANING ((NTSTATUS) 0xC000016DL) #endif #ifndef STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT # define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT ((NTSTATUS) 0xC000016EL) #endif #ifndef STATUS_PARTITION_FAILURE # define STATUS_PARTITION_FAILURE ((NTSTATUS) 0xC0000172L) #endif #ifndef STATUS_INVALID_BLOCK_LENGTH # define STATUS_INVALID_BLOCK_LENGTH ((NTSTATUS) 0xC0000173L) #endif #ifndef STATUS_DEVICE_NOT_PARTITIONED # define STATUS_DEVICE_NOT_PARTITIONED ((NTSTATUS) 0xC0000174L) #endif #ifndef STATUS_UNABLE_TO_LOCK_MEDIA # define STATUS_UNABLE_TO_LOCK_MEDIA ((NTSTATUS) 0xC0000175L) #endif #ifndef STATUS_UNABLE_TO_UNLOAD_MEDIA # define STATUS_UNABLE_TO_UNLOAD_MEDIA ((NTSTATUS) 0xC0000176L) #endif #ifndef STATUS_EOM_OVERFLOW # define STATUS_EOM_OVERFLOW ((NTSTATUS) 0xC0000177L) #endif #ifndef STATUS_NO_MEDIA # define STATUS_NO_MEDIA ((NTSTATUS) 0xC0000178L) #endif #ifndef STATUS_NO_SUCH_MEMBER # define STATUS_NO_SUCH_MEMBER ((NTSTATUS) 0xC000017AL) #endif #ifndef STATUS_INVALID_MEMBER # define STATUS_INVALID_MEMBER ((NTSTATUS) 0xC000017BL) #endif #ifndef STATUS_KEY_DELETED # define STATUS_KEY_DELETED ((NTSTATUS) 0xC000017CL) #endif #ifndef STATUS_NO_LOG_SPACE # define STATUS_NO_LOG_SPACE ((NTSTATUS) 0xC000017DL) #endif #ifndef STATUS_TOO_MANY_SIDS # define STATUS_TOO_MANY_SIDS ((NTSTATUS) 0xC000017EL) #endif #ifndef STATUS_LM_CROSS_ENCRYPTION_REQUIRED # define STATUS_LM_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000017FL) #endif #ifndef STATUS_KEY_HAS_CHILDREN # define STATUS_KEY_HAS_CHILDREN ((NTSTATUS) 0xC0000180L) #endif #ifndef STATUS_CHILD_MUST_BE_VOLATILE # define STATUS_CHILD_MUST_BE_VOLATILE ((NTSTATUS) 0xC0000181L) #endif #ifndef STATUS_DEVICE_CONFIGURATION_ERROR # define STATUS_DEVICE_CONFIGURATION_ERROR ((NTSTATUS) 0xC0000182L) #endif #ifndef STATUS_DRIVER_INTERNAL_ERROR # define STATUS_DRIVER_INTERNAL_ERROR ((NTSTATUS) 0xC0000183L) #endif #ifndef STATUS_INVALID_DEVICE_STATE # define STATUS_INVALID_DEVICE_STATE ((NTSTATUS) 0xC0000184L) #endif #ifndef STATUS_IO_DEVICE_ERROR # define STATUS_IO_DEVICE_ERROR ((NTSTATUS) 0xC0000185L) #endif #ifndef STATUS_DEVICE_PROTOCOL_ERROR # define STATUS_DEVICE_PROTOCOL_ERROR ((NTSTATUS) 0xC0000186L) #endif #ifndef STATUS_BACKUP_CONTROLLER # define STATUS_BACKUP_CONTROLLER ((NTSTATUS) 0xC0000187L) #endif #ifndef STATUS_LOG_FILE_FULL # define STATUS_LOG_FILE_FULL ((NTSTATUS) 0xC0000188L) #endif #ifndef STATUS_TOO_LATE # define STATUS_TOO_LATE ((NTSTATUS) 0xC0000189L) #endif #ifndef STATUS_NO_TRUST_LSA_SECRET # define STATUS_NO_TRUST_LSA_SECRET ((NTSTATUS) 0xC000018AL) #endif #ifndef STATUS_NO_TRUST_SAM_ACCOUNT # define STATUS_NO_TRUST_SAM_ACCOUNT ((NTSTATUS) 0xC000018BL) #endif #ifndef STATUS_TRUSTED_DOMAIN_FAILURE # define STATUS_TRUSTED_DOMAIN_FAILURE ((NTSTATUS) 0xC000018CL) #endif #ifndef STATUS_TRUSTED_RELATIONSHIP_FAILURE # define STATUS_TRUSTED_RELATIONSHIP_FAILURE ((NTSTATUS) 0xC000018DL) #endif #ifndef STATUS_EVENTLOG_FILE_CORRUPT # define STATUS_EVENTLOG_FILE_CORRUPT ((NTSTATUS) 0xC000018EL) #endif #ifndef STATUS_EVENTLOG_CANT_START # define STATUS_EVENTLOG_CANT_START ((NTSTATUS) 0xC000018FL) #endif #ifndef STATUS_TRUST_FAILURE # define STATUS_TRUST_FAILURE ((NTSTATUS) 0xC0000190L) #endif #ifndef STATUS_MUTANT_LIMIT_EXCEEDED # define STATUS_MUTANT_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000191L) #endif #ifndef STATUS_NETLOGON_NOT_STARTED # define STATUS_NETLOGON_NOT_STARTED ((NTSTATUS) 0xC0000192L) #endif #ifndef STATUS_ACCOUNT_EXPIRED # define STATUS_ACCOUNT_EXPIRED ((NTSTATUS) 0xC0000193L) #endif #ifndef STATUS_POSSIBLE_DEADLOCK # define STATUS_POSSIBLE_DEADLOCK ((NTSTATUS) 0xC0000194L) #endif #ifndef STATUS_NETWORK_CREDENTIAL_CONFLICT # define STATUS_NETWORK_CREDENTIAL_CONFLICT ((NTSTATUS) 0xC0000195L) #endif #ifndef STATUS_REMOTE_SESSION_LIMIT # define STATUS_REMOTE_SESSION_LIMIT ((NTSTATUS) 0xC0000196L) #endif #ifndef STATUS_EVENTLOG_FILE_CHANGED # define STATUS_EVENTLOG_FILE_CHANGED ((NTSTATUS) 0xC0000197L) #endif #ifndef STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT # define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT ((NTSTATUS) 0xC0000198L) #endif #ifndef STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT # define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT ((NTSTATUS) 0xC0000199L) #endif #ifndef STATUS_NOLOGON_SERVER_TRUST_ACCOUNT # define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT ((NTSTATUS) 0xC000019AL) #endif #ifndef STATUS_DOMAIN_TRUST_INCONSISTENT # define STATUS_DOMAIN_TRUST_INCONSISTENT ((NTSTATUS) 0xC000019BL) #endif #ifndef STATUS_FS_DRIVER_REQUIRED # define STATUS_FS_DRIVER_REQUIRED ((NTSTATUS) 0xC000019CL) #endif #ifndef STATUS_IMAGE_ALREADY_LOADED_AS_DLL # define STATUS_IMAGE_ALREADY_LOADED_AS_DLL ((NTSTATUS) 0xC000019DL) #endif #ifndef STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING # define STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING ((NTSTATUS) 0xC000019EL) #endif #ifndef STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME # define STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME ((NTSTATUS) 0xC000019FL) #endif #ifndef STATUS_SECURITY_STREAM_IS_INCONSISTENT # define STATUS_SECURITY_STREAM_IS_INCONSISTENT ((NTSTATUS) 0xC00001A0L) #endif #ifndef STATUS_INVALID_LOCK_RANGE # define STATUS_INVALID_LOCK_RANGE ((NTSTATUS) 0xC00001A1L) #endif #ifndef STATUS_INVALID_ACE_CONDITION # define STATUS_INVALID_ACE_CONDITION ((NTSTATUS) 0xC00001A2L) #endif #ifndef STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT # define STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT ((NTSTATUS) 0xC00001A3L) #endif #ifndef STATUS_NOTIFICATION_GUID_ALREADY_DEFINED # define STATUS_NOTIFICATION_GUID_ALREADY_DEFINED ((NTSTATUS) 0xC00001A4L) #endif #ifndef STATUS_NETWORK_OPEN_RESTRICTION # define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS) 0xC0000201L) #endif #ifndef STATUS_NO_USER_SESSION_KEY # define STATUS_NO_USER_SESSION_KEY ((NTSTATUS) 0xC0000202L) #endif #ifndef STATUS_USER_SESSION_DELETED # define STATUS_USER_SESSION_DELETED ((NTSTATUS) 0xC0000203L) #endif #ifndef STATUS_RESOURCE_LANG_NOT_FOUND # define STATUS_RESOURCE_LANG_NOT_FOUND ((NTSTATUS) 0xC0000204L) #endif #ifndef STATUS_INSUFF_SERVER_RESOURCES # define STATUS_INSUFF_SERVER_RESOURCES ((NTSTATUS) 0xC0000205L) #endif #ifndef STATUS_INVALID_BUFFER_SIZE # define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS) 0xC0000206L) #endif #ifndef STATUS_INVALID_ADDRESS_COMPONENT # define STATUS_INVALID_ADDRESS_COMPONENT ((NTSTATUS) 0xC0000207L) #endif #ifndef STATUS_INVALID_ADDRESS_WILDCARD # define STATUS_INVALID_ADDRESS_WILDCARD ((NTSTATUS) 0xC0000208L) #endif #ifndef STATUS_TOO_MANY_ADDRESSES # define STATUS_TOO_MANY_ADDRESSES ((NTSTATUS) 0xC0000209L) #endif #ifndef STATUS_ADDRESS_ALREADY_EXISTS # define STATUS_ADDRESS_ALREADY_EXISTS ((NTSTATUS) 0xC000020AL) #endif #ifndef STATUS_ADDRESS_CLOSED # define STATUS_ADDRESS_CLOSED ((NTSTATUS) 0xC000020BL) #endif #ifndef STATUS_CONNECTION_DISCONNECTED # define STATUS_CONNECTION_DISCONNECTED ((NTSTATUS) 0xC000020CL) #endif #ifndef STATUS_CONNECTION_RESET # define STATUS_CONNECTION_RESET ((NTSTATUS) 0xC000020DL) #endif #ifndef STATUS_TOO_MANY_NODES # define STATUS_TOO_MANY_NODES ((NTSTATUS) 0xC000020EL) #endif #ifndef STATUS_TRANSACTION_ABORTED # define STATUS_TRANSACTION_ABORTED ((NTSTATUS) 0xC000020FL) #endif #ifndef STATUS_TRANSACTION_TIMED_OUT # define STATUS_TRANSACTION_TIMED_OUT ((NTSTATUS) 0xC0000210L) #endif #ifndef STATUS_TRANSACTION_NO_RELEASE # define STATUS_TRANSACTION_NO_RELEASE ((NTSTATUS) 0xC0000211L) #endif #ifndef STATUS_TRANSACTION_NO_MATCH # define STATUS_TRANSACTION_NO_MATCH ((NTSTATUS) 0xC0000212L) #endif #ifndef STATUS_TRANSACTION_RESPONDED # define STATUS_TRANSACTION_RESPONDED ((NTSTATUS) 0xC0000213L) #endif #ifndef STATUS_TRANSACTION_INVALID_ID # define STATUS_TRANSACTION_INVALID_ID ((NTSTATUS) 0xC0000214L) #endif #ifndef STATUS_TRANSACTION_INVALID_TYPE # define STATUS_TRANSACTION_INVALID_TYPE ((NTSTATUS) 0xC0000215L) #endif #ifndef STATUS_NOT_SERVER_SESSION # define STATUS_NOT_SERVER_SESSION ((NTSTATUS) 0xC0000216L) #endif #ifndef STATUS_NOT_CLIENT_SESSION # define STATUS_NOT_CLIENT_SESSION ((NTSTATUS) 0xC0000217L) #endif #ifndef STATUS_CANNOT_LOAD_REGISTRY_FILE # define STATUS_CANNOT_LOAD_REGISTRY_FILE ((NTSTATUS) 0xC0000218L) #endif #ifndef STATUS_DEBUG_ATTACH_FAILED # define STATUS_DEBUG_ATTACH_FAILED ((NTSTATUS) 0xC0000219L) #endif #ifndef STATUS_SYSTEM_PROCESS_TERMINATED # define STATUS_SYSTEM_PROCESS_TERMINATED ((NTSTATUS) 0xC000021AL) #endif #ifndef STATUS_DATA_NOT_ACCEPTED # define STATUS_DATA_NOT_ACCEPTED ((NTSTATUS) 0xC000021BL) #endif #ifndef STATUS_NO_BROWSER_SERVERS_FOUND # define STATUS_NO_BROWSER_SERVERS_FOUND ((NTSTATUS) 0xC000021CL) #endif #ifndef STATUS_VDM_HARD_ERROR # define STATUS_VDM_HARD_ERROR ((NTSTATUS) 0xC000021DL) #endif #ifndef STATUS_DRIVER_CANCEL_TIMEOUT # define STATUS_DRIVER_CANCEL_TIMEOUT ((NTSTATUS) 0xC000021EL) #endif #ifndef STATUS_REPLY_MESSAGE_MISMATCH # define STATUS_REPLY_MESSAGE_MISMATCH ((NTSTATUS) 0xC000021FL) #endif #ifndef STATUS_MAPPED_ALIGNMENT # define STATUS_MAPPED_ALIGNMENT ((NTSTATUS) 0xC0000220L) #endif #ifndef STATUS_IMAGE_CHECKSUM_MISMATCH # define STATUS_IMAGE_CHECKSUM_MISMATCH ((NTSTATUS) 0xC0000221L) #endif #ifndef STATUS_LOST_WRITEBEHIND_DATA # define STATUS_LOST_WRITEBEHIND_DATA ((NTSTATUS) 0xC0000222L) #endif #ifndef STATUS_CLIENT_SERVER_PARAMETERS_INVALID # define STATUS_CLIENT_SERVER_PARAMETERS_INVALID ((NTSTATUS) 0xC0000223L) #endif #ifndef STATUS_PASSWORD_MUST_CHANGE # define STATUS_PASSWORD_MUST_CHANGE ((NTSTATUS) 0xC0000224L) #endif #ifndef STATUS_NOT_FOUND # define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L) #endif #ifndef STATUS_NOT_TINY_STREAM # define STATUS_NOT_TINY_STREAM ((NTSTATUS) 0xC0000226L) #endif #ifndef STATUS_RECOVERY_FAILURE # define STATUS_RECOVERY_FAILURE ((NTSTATUS) 0xC0000227L) #endif #ifndef STATUS_STACK_OVERFLOW_READ # define STATUS_STACK_OVERFLOW_READ ((NTSTATUS) 0xC0000228L) #endif #ifndef STATUS_FAIL_CHECK # define STATUS_FAIL_CHECK ((NTSTATUS) 0xC0000229L) #endif #ifndef STATUS_DUPLICATE_OBJECTID # define STATUS_DUPLICATE_OBJECTID ((NTSTATUS) 0xC000022AL) #endif #ifndef STATUS_OBJECTID_EXISTS # define STATUS_OBJECTID_EXISTS ((NTSTATUS) 0xC000022BL) #endif #ifndef STATUS_CONVERT_TO_LARGE # define STATUS_CONVERT_TO_LARGE ((NTSTATUS) 0xC000022CL) #endif #ifndef STATUS_RETRY # define STATUS_RETRY ((NTSTATUS) 0xC000022DL) #endif #ifndef STATUS_FOUND_OUT_OF_SCOPE # define STATUS_FOUND_OUT_OF_SCOPE ((NTSTATUS) 0xC000022EL) #endif #ifndef STATUS_ALLOCATE_BUCKET # define STATUS_ALLOCATE_BUCKET ((NTSTATUS) 0xC000022FL) #endif #ifndef STATUS_PROPSET_NOT_FOUND # define STATUS_PROPSET_NOT_FOUND ((NTSTATUS) 0xC0000230L) #endif #ifndef STATUS_MARSHALL_OVERFLOW # define STATUS_MARSHALL_OVERFLOW ((NTSTATUS) 0xC0000231L) #endif #ifndef STATUS_INVALID_VARIANT # define STATUS_INVALID_VARIANT ((NTSTATUS) 0xC0000232L) #endif #ifndef STATUS_DOMAIN_CONTROLLER_NOT_FOUND # define STATUS_DOMAIN_CONTROLLER_NOT_FOUND ((NTSTATUS) 0xC0000233L) #endif #ifndef STATUS_ACCOUNT_LOCKED_OUT # define STATUS_ACCOUNT_LOCKED_OUT ((NTSTATUS) 0xC0000234L) #endif #ifndef STATUS_HANDLE_NOT_CLOSABLE # define STATUS_HANDLE_NOT_CLOSABLE ((NTSTATUS) 0xC0000235L) #endif #ifndef STATUS_CONNECTION_REFUSED # define STATUS_CONNECTION_REFUSED ((NTSTATUS) 0xC0000236L) #endif #ifndef STATUS_GRACEFUL_DISCONNECT # define STATUS_GRACEFUL_DISCONNECT ((NTSTATUS) 0xC0000237L) #endif #ifndef STATUS_ADDRESS_ALREADY_ASSOCIATED # define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS) 0xC0000238L) #endif #ifndef STATUS_ADDRESS_NOT_ASSOCIATED # define STATUS_ADDRESS_NOT_ASSOCIATED ((NTSTATUS) 0xC0000239L) #endif #ifndef STATUS_CONNECTION_INVALID # define STATUS_CONNECTION_INVALID ((NTSTATUS) 0xC000023AL) #endif #ifndef STATUS_CONNECTION_ACTIVE # define STATUS_CONNECTION_ACTIVE ((NTSTATUS) 0xC000023BL) #endif #ifndef STATUS_NETWORK_UNREACHABLE # define STATUS_NETWORK_UNREACHABLE ((NTSTATUS) 0xC000023CL) #endif #ifndef STATUS_HOST_UNREACHABLE # define STATUS_HOST_UNREACHABLE ((NTSTATUS) 0xC000023DL) #endif #ifndef STATUS_PROTOCOL_UNREACHABLE # define STATUS_PROTOCOL_UNREACHABLE ((NTSTATUS) 0xC000023EL) #endif #ifndef STATUS_PORT_UNREACHABLE # define STATUS_PORT_UNREACHABLE ((NTSTATUS) 0xC000023FL) #endif #ifndef STATUS_REQUEST_ABORTED # define STATUS_REQUEST_ABORTED ((NTSTATUS) 0xC0000240L) #endif #ifndef STATUS_CONNECTION_ABORTED # define STATUS_CONNECTION_ABORTED ((NTSTATUS) 0xC0000241L) #endif #ifndef STATUS_BAD_COMPRESSION_BUFFER # define STATUS_BAD_COMPRESSION_BUFFER ((NTSTATUS) 0xC0000242L) #endif #ifndef STATUS_USER_MAPPED_FILE # define STATUS_USER_MAPPED_FILE ((NTSTATUS) 0xC0000243L) #endif #ifndef STATUS_AUDIT_FAILED # define STATUS_AUDIT_FAILED ((NTSTATUS) 0xC0000244L) #endif #ifndef STATUS_TIMER_RESOLUTION_NOT_SET # define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS) 0xC0000245L) #endif #ifndef STATUS_CONNECTION_COUNT_LIMIT # define STATUS_CONNECTION_COUNT_LIMIT ((NTSTATUS) 0xC0000246L) #endif #ifndef STATUS_LOGIN_TIME_RESTRICTION # define STATUS_LOGIN_TIME_RESTRICTION ((NTSTATUS) 0xC0000247L) #endif #ifndef STATUS_LOGIN_WKSTA_RESTRICTION # define STATUS_LOGIN_WKSTA_RESTRICTION ((NTSTATUS) 0xC0000248L) #endif #ifndef STATUS_IMAGE_MP_UP_MISMATCH # define STATUS_IMAGE_MP_UP_MISMATCH ((NTSTATUS) 0xC0000249L) #endif #ifndef STATUS_INSUFFICIENT_LOGON_INFO # define STATUS_INSUFFICIENT_LOGON_INFO ((NTSTATUS) 0xC0000250L) #endif #ifndef STATUS_BAD_DLL_ENTRYPOINT # define STATUS_BAD_DLL_ENTRYPOINT ((NTSTATUS) 0xC0000251L) #endif #ifndef STATUS_BAD_SERVICE_ENTRYPOINT # define STATUS_BAD_SERVICE_ENTRYPOINT ((NTSTATUS) 0xC0000252L) #endif #ifndef STATUS_LPC_REPLY_LOST # define STATUS_LPC_REPLY_LOST ((NTSTATUS) 0xC0000253L) #endif #ifndef STATUS_IP_ADDRESS_CONFLICT1 # define STATUS_IP_ADDRESS_CONFLICT1 ((NTSTATUS) 0xC0000254L) #endif #ifndef STATUS_IP_ADDRESS_CONFLICT2 # define STATUS_IP_ADDRESS_CONFLICT2 ((NTSTATUS) 0xC0000255L) #endif #ifndef STATUS_REGISTRY_QUOTA_LIMIT # define STATUS_REGISTRY_QUOTA_LIMIT ((NTSTATUS) 0xC0000256L) #endif #ifndef STATUS_PATH_NOT_COVERED # define STATUS_PATH_NOT_COVERED ((NTSTATUS) 0xC0000257L) #endif #ifndef STATUS_NO_CALLBACK_ACTIVE # define STATUS_NO_CALLBACK_ACTIVE ((NTSTATUS) 0xC0000258L) #endif #ifndef STATUS_LICENSE_QUOTA_EXCEEDED # define STATUS_LICENSE_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000259L) #endif #ifndef STATUS_PWD_TOO_SHORT # define STATUS_PWD_TOO_SHORT ((NTSTATUS) 0xC000025AL) #endif #ifndef STATUS_PWD_TOO_RECENT # define STATUS_PWD_TOO_RECENT ((NTSTATUS) 0xC000025BL) #endif #ifndef STATUS_PWD_HISTORY_CONFLICT # define STATUS_PWD_HISTORY_CONFLICT ((NTSTATUS) 0xC000025CL) #endif #ifndef STATUS_PLUGPLAY_NO_DEVICE # define STATUS_PLUGPLAY_NO_DEVICE ((NTSTATUS) 0xC000025EL) #endif #ifndef STATUS_UNSUPPORTED_COMPRESSION # define STATUS_UNSUPPORTED_COMPRESSION ((NTSTATUS) 0xC000025FL) #endif #ifndef STATUS_INVALID_HW_PROFILE # define STATUS_INVALID_HW_PROFILE ((NTSTATUS) 0xC0000260L) #endif #ifndef STATUS_INVALID_PLUGPLAY_DEVICE_PATH # define STATUS_INVALID_PLUGPLAY_DEVICE_PATH ((NTSTATUS) 0xC0000261L) #endif #ifndef STATUS_DRIVER_ORDINAL_NOT_FOUND # define STATUS_DRIVER_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000262L) #endif #ifndef STATUS_DRIVER_ENTRYPOINT_NOT_FOUND # define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000263L) #endif #ifndef STATUS_RESOURCE_NOT_OWNED # define STATUS_RESOURCE_NOT_OWNED ((NTSTATUS) 0xC0000264L) #endif #ifndef STATUS_TOO_MANY_LINKS # define STATUS_TOO_MANY_LINKS ((NTSTATUS) 0xC0000265L) #endif #ifndef STATUS_QUOTA_LIST_INCONSISTENT # define STATUS_QUOTA_LIST_INCONSISTENT ((NTSTATUS) 0xC0000266L) #endif #ifndef STATUS_FILE_IS_OFFLINE # define STATUS_FILE_IS_OFFLINE ((NTSTATUS) 0xC0000267L) #endif #ifndef STATUS_EVALUATION_EXPIRATION # define STATUS_EVALUATION_EXPIRATION ((NTSTATUS) 0xC0000268L) #endif #ifndef STATUS_ILLEGAL_DLL_RELOCATION # define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xC0000269L) #endif #ifndef STATUS_LICENSE_VIOLATION # define STATUS_LICENSE_VIOLATION ((NTSTATUS) 0xC000026AL) #endif #ifndef STATUS_DLL_INIT_FAILED_LOGOFF # define STATUS_DLL_INIT_FAILED_LOGOFF ((NTSTATUS) 0xC000026BL) #endif #ifndef STATUS_DRIVER_UNABLE_TO_LOAD # define STATUS_DRIVER_UNABLE_TO_LOAD ((NTSTATUS) 0xC000026CL) #endif #ifndef STATUS_DFS_UNAVAILABLE # define STATUS_DFS_UNAVAILABLE ((NTSTATUS) 0xC000026DL) #endif #ifndef STATUS_VOLUME_DISMOUNTED # define STATUS_VOLUME_DISMOUNTED ((NTSTATUS) 0xC000026EL) #endif #ifndef STATUS_WX86_INTERNAL_ERROR # define STATUS_WX86_INTERNAL_ERROR ((NTSTATUS) 0xC000026FL) #endif #ifndef STATUS_WX86_FLOAT_STACK_CHECK # define STATUS_WX86_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000270L) #endif #ifndef STATUS_VALIDATE_CONTINUE # define STATUS_VALIDATE_CONTINUE ((NTSTATUS) 0xC0000271L) #endif #ifndef STATUS_NO_MATCH # define STATUS_NO_MATCH ((NTSTATUS) 0xC0000272L) #endif #ifndef STATUS_NO_MORE_MATCHES # define STATUS_NO_MORE_MATCHES ((NTSTATUS) 0xC0000273L) #endif #ifndef STATUS_NOT_A_REPARSE_POINT # define STATUS_NOT_A_REPARSE_POINT ((NTSTATUS) 0xC0000275L) #endif #ifndef STATUS_IO_REPARSE_TAG_INVALID # define STATUS_IO_REPARSE_TAG_INVALID ((NTSTATUS) 0xC0000276L) #endif #ifndef STATUS_IO_REPARSE_TAG_MISMATCH # define STATUS_IO_REPARSE_TAG_MISMATCH ((NTSTATUS) 0xC0000277L) #endif #ifndef STATUS_IO_REPARSE_DATA_INVALID # define STATUS_IO_REPARSE_DATA_INVALID ((NTSTATUS) 0xC0000278L) #endif #ifndef STATUS_IO_REPARSE_TAG_NOT_HANDLED # define STATUS_IO_REPARSE_TAG_NOT_HANDLED ((NTSTATUS) 0xC0000279L) #endif #ifndef STATUS_REPARSE_POINT_NOT_RESOLVED # define STATUS_REPARSE_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000280L) #endif #ifndef STATUS_DIRECTORY_IS_A_REPARSE_POINT # define STATUS_DIRECTORY_IS_A_REPARSE_POINT ((NTSTATUS) 0xC0000281L) #endif #ifndef STATUS_RANGE_LIST_CONFLICT # define STATUS_RANGE_LIST_CONFLICT ((NTSTATUS) 0xC0000282L) #endif #ifndef STATUS_SOURCE_ELEMENT_EMPTY # define STATUS_SOURCE_ELEMENT_EMPTY ((NTSTATUS) 0xC0000283L) #endif #ifndef STATUS_DESTINATION_ELEMENT_FULL # define STATUS_DESTINATION_ELEMENT_FULL ((NTSTATUS) 0xC0000284L) #endif #ifndef STATUS_ILLEGAL_ELEMENT_ADDRESS # define STATUS_ILLEGAL_ELEMENT_ADDRESS ((NTSTATUS) 0xC0000285L) #endif #ifndef STATUS_MAGAZINE_NOT_PRESENT # define STATUS_MAGAZINE_NOT_PRESENT ((NTSTATUS) 0xC0000286L) #endif #ifndef STATUS_REINITIALIZATION_NEEDED # define STATUS_REINITIALIZATION_NEEDED ((NTSTATUS) 0xC0000287L) #endif #ifndef STATUS_DEVICE_REQUIRES_CLEANING # define STATUS_DEVICE_REQUIRES_CLEANING ((NTSTATUS) 0x80000288L) #endif #ifndef STATUS_DEVICE_DOOR_OPEN # define STATUS_DEVICE_DOOR_OPEN ((NTSTATUS) 0x80000289L) #endif #ifndef STATUS_ENCRYPTION_FAILED # define STATUS_ENCRYPTION_FAILED ((NTSTATUS) 0xC000028AL) #endif #ifndef STATUS_DECRYPTION_FAILED # define STATUS_DECRYPTION_FAILED ((NTSTATUS) 0xC000028BL) #endif #ifndef STATUS_RANGE_NOT_FOUND # define STATUS_RANGE_NOT_FOUND ((NTSTATUS) 0xC000028CL) #endif #ifndef STATUS_NO_RECOVERY_POLICY # define STATUS_NO_RECOVERY_POLICY ((NTSTATUS) 0xC000028DL) #endif #ifndef STATUS_NO_EFS # define STATUS_NO_EFS ((NTSTATUS) 0xC000028EL) #endif #ifndef STATUS_WRONG_EFS # define STATUS_WRONG_EFS ((NTSTATUS) 0xC000028FL) #endif #ifndef STATUS_NO_USER_KEYS # define STATUS_NO_USER_KEYS ((NTSTATUS) 0xC0000290L) #endif #ifndef STATUS_FILE_NOT_ENCRYPTED # define STATUS_FILE_NOT_ENCRYPTED ((NTSTATUS) 0xC0000291L) #endif #ifndef STATUS_NOT_EXPORT_FORMAT # define STATUS_NOT_EXPORT_FORMAT ((NTSTATUS) 0xC0000292L) #endif #ifndef STATUS_FILE_ENCRYPTED # define STATUS_FILE_ENCRYPTED ((NTSTATUS) 0xC0000293L) #endif #ifndef STATUS_WAKE_SYSTEM # define STATUS_WAKE_SYSTEM ((NTSTATUS) 0x40000294L) #endif #ifndef STATUS_WMI_GUID_NOT_FOUND # define STATUS_WMI_GUID_NOT_FOUND ((NTSTATUS) 0xC0000295L) #endif #ifndef STATUS_WMI_INSTANCE_NOT_FOUND # define STATUS_WMI_INSTANCE_NOT_FOUND ((NTSTATUS) 0xC0000296L) #endif #ifndef STATUS_WMI_ITEMID_NOT_FOUND # define STATUS_WMI_ITEMID_NOT_FOUND ((NTSTATUS) 0xC0000297L) #endif #ifndef STATUS_WMI_TRY_AGAIN # define STATUS_WMI_TRY_AGAIN ((NTSTATUS) 0xC0000298L) #endif #ifndef STATUS_SHARED_POLICY # define STATUS_SHARED_POLICY ((NTSTATUS) 0xC0000299L) #endif #ifndef STATUS_POLICY_OBJECT_NOT_FOUND # define STATUS_POLICY_OBJECT_NOT_FOUND ((NTSTATUS) 0xC000029AL) #endif #ifndef STATUS_POLICY_ONLY_IN_DS # define STATUS_POLICY_ONLY_IN_DS ((NTSTATUS) 0xC000029BL) #endif #ifndef STATUS_VOLUME_NOT_UPGRADED # define STATUS_VOLUME_NOT_UPGRADED ((NTSTATUS) 0xC000029CL) #endif #ifndef STATUS_REMOTE_STORAGE_NOT_ACTIVE # define STATUS_REMOTE_STORAGE_NOT_ACTIVE ((NTSTATUS) 0xC000029DL) #endif #ifndef STATUS_REMOTE_STORAGE_MEDIA_ERROR # define STATUS_REMOTE_STORAGE_MEDIA_ERROR ((NTSTATUS) 0xC000029EL) #endif #ifndef STATUS_NO_TRACKING_SERVICE # define STATUS_NO_TRACKING_SERVICE ((NTSTATUS) 0xC000029FL) #endif #ifndef STATUS_SERVER_SID_MISMATCH # define STATUS_SERVER_SID_MISMATCH ((NTSTATUS) 0xC00002A0L) #endif #ifndef STATUS_DS_NO_ATTRIBUTE_OR_VALUE # define STATUS_DS_NO_ATTRIBUTE_OR_VALUE ((NTSTATUS) 0xC00002A1L) #endif #ifndef STATUS_DS_INVALID_ATTRIBUTE_SYNTAX # define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX ((NTSTATUS) 0xC00002A2L) #endif #ifndef STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED # define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED ((NTSTATUS) 0xC00002A3L) #endif #ifndef STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS # define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS ((NTSTATUS) 0xC00002A4L) #endif #ifndef STATUS_DS_BUSY # define STATUS_DS_BUSY ((NTSTATUS) 0xC00002A5L) #endif #ifndef STATUS_DS_UNAVAILABLE # define STATUS_DS_UNAVAILABLE ((NTSTATUS) 0xC00002A6L) #endif #ifndef STATUS_DS_NO_RIDS_ALLOCATED # define STATUS_DS_NO_RIDS_ALLOCATED ((NTSTATUS) 0xC00002A7L) #endif #ifndef STATUS_DS_NO_MORE_RIDS # define STATUS_DS_NO_MORE_RIDS ((NTSTATUS) 0xC00002A8L) #endif #ifndef STATUS_DS_INCORRECT_ROLE_OWNER # define STATUS_DS_INCORRECT_ROLE_OWNER ((NTSTATUS) 0xC00002A9L) #endif #ifndef STATUS_DS_RIDMGR_INIT_ERROR # define STATUS_DS_RIDMGR_INIT_ERROR ((NTSTATUS) 0xC00002AAL) #endif #ifndef STATUS_DS_OBJ_CLASS_VIOLATION # define STATUS_DS_OBJ_CLASS_VIOLATION ((NTSTATUS) 0xC00002ABL) #endif #ifndef STATUS_DS_CANT_ON_NON_LEAF # define STATUS_DS_CANT_ON_NON_LEAF ((NTSTATUS) 0xC00002ACL) #endif #ifndef STATUS_DS_CANT_ON_RDN # define STATUS_DS_CANT_ON_RDN ((NTSTATUS) 0xC00002ADL) #endif #ifndef STATUS_DS_CANT_MOD_OBJ_CLASS # define STATUS_DS_CANT_MOD_OBJ_CLASS ((NTSTATUS) 0xC00002AEL) #endif #ifndef STATUS_DS_CROSS_DOM_MOVE_FAILED # define STATUS_DS_CROSS_DOM_MOVE_FAILED ((NTSTATUS) 0xC00002AFL) #endif #ifndef STATUS_DS_GC_NOT_AVAILABLE # define STATUS_DS_GC_NOT_AVAILABLE ((NTSTATUS) 0xC00002B0L) #endif #ifndef STATUS_DIRECTORY_SERVICE_REQUIRED # define STATUS_DIRECTORY_SERVICE_REQUIRED ((NTSTATUS) 0xC00002B1L) #endif #ifndef STATUS_REPARSE_ATTRIBUTE_CONFLICT # define STATUS_REPARSE_ATTRIBUTE_CONFLICT ((NTSTATUS) 0xC00002B2L) #endif #ifndef STATUS_CANT_ENABLE_DENY_ONLY # define STATUS_CANT_ENABLE_DENY_ONLY ((NTSTATUS) 0xC00002B3L) #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS # define STATUS_FLOAT_MULTIPLE_FAULTS ((NTSTATUS) 0xC00002B4L) #endif #ifndef STATUS_FLOAT_MULTIPLE_TRAPS # define STATUS_FLOAT_MULTIPLE_TRAPS ((NTSTATUS) 0xC00002B5L) #endif #ifndef STATUS_DEVICE_REMOVED # define STATUS_DEVICE_REMOVED ((NTSTATUS) 0xC00002B6L) #endif #ifndef STATUS_JOURNAL_DELETE_IN_PROGRESS # define STATUS_JOURNAL_DELETE_IN_PROGRESS ((NTSTATUS) 0xC00002B7L) #endif #ifndef STATUS_JOURNAL_NOT_ACTIVE # define STATUS_JOURNAL_NOT_ACTIVE ((NTSTATUS) 0xC00002B8L) #endif #ifndef STATUS_NOINTERFACE # define STATUS_NOINTERFACE ((NTSTATUS) 0xC00002B9L) #endif #ifndef STATUS_DS_ADMIN_LIMIT_EXCEEDED # define STATUS_DS_ADMIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00002C1L) #endif #ifndef STATUS_DRIVER_FAILED_SLEEP # define STATUS_DRIVER_FAILED_SLEEP ((NTSTATUS) 0xC00002C2L) #endif #ifndef STATUS_MUTUAL_AUTHENTICATION_FAILED # define STATUS_MUTUAL_AUTHENTICATION_FAILED ((NTSTATUS) 0xC00002C3L) #endif #ifndef STATUS_CORRUPT_SYSTEM_FILE # define STATUS_CORRUPT_SYSTEM_FILE ((NTSTATUS) 0xC00002C4L) #endif #ifndef STATUS_DATATYPE_MISALIGNMENT_ERROR # define STATUS_DATATYPE_MISALIGNMENT_ERROR ((NTSTATUS) 0xC00002C5L) #endif #ifndef STATUS_WMI_READ_ONLY # define STATUS_WMI_READ_ONLY ((NTSTATUS) 0xC00002C6L) #endif #ifndef STATUS_WMI_SET_FAILURE # define STATUS_WMI_SET_FAILURE ((NTSTATUS) 0xC00002C7L) #endif #ifndef STATUS_COMMITMENT_MINIMUM # define STATUS_COMMITMENT_MINIMUM ((NTSTATUS) 0xC00002C8L) #endif #ifndef STATUS_REG_NAT_CONSUMPTION # define STATUS_REG_NAT_CONSUMPTION ((NTSTATUS) 0xC00002C9L) #endif #ifndef STATUS_TRANSPORT_FULL # define STATUS_TRANSPORT_FULL ((NTSTATUS) 0xC00002CAL) #endif #ifndef STATUS_DS_SAM_INIT_FAILURE # define STATUS_DS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002CBL) #endif #ifndef STATUS_ONLY_IF_CONNECTED # define STATUS_ONLY_IF_CONNECTED ((NTSTATUS) 0xC00002CCL) #endif #ifndef STATUS_DS_SENSITIVE_GROUP_VIOLATION # define STATUS_DS_SENSITIVE_GROUP_VIOLATION ((NTSTATUS) 0xC00002CDL) #endif #ifndef STATUS_PNP_RESTART_ENUMERATION # define STATUS_PNP_RESTART_ENUMERATION ((NTSTATUS) 0xC00002CEL) #endif #ifndef STATUS_JOURNAL_ENTRY_DELETED # define STATUS_JOURNAL_ENTRY_DELETED ((NTSTATUS) 0xC00002CFL) #endif #ifndef STATUS_DS_CANT_MOD_PRIMARYGROUPID # define STATUS_DS_CANT_MOD_PRIMARYGROUPID ((NTSTATUS) 0xC00002D0L) #endif #ifndef STATUS_SYSTEM_IMAGE_BAD_SIGNATURE # define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE ((NTSTATUS) 0xC00002D1L) #endif #ifndef STATUS_PNP_REBOOT_REQUIRED # define STATUS_PNP_REBOOT_REQUIRED ((NTSTATUS) 0xC00002D2L) #endif #ifndef STATUS_POWER_STATE_INVALID # define STATUS_POWER_STATE_INVALID ((NTSTATUS) 0xC00002D3L) #endif #ifndef STATUS_DS_INVALID_GROUP_TYPE # define STATUS_DS_INVALID_GROUP_TYPE ((NTSTATUS) 0xC00002D4L) #endif #ifndef STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN # define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D5L) #endif #ifndef STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN # define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D6L) #endif #ifndef STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER # define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D7L) #endif #ifndef STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER # define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC00002D8L) #endif #ifndef STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER # define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D9L) #endif #ifndef STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER # define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER ((NTSTATUS) 0xC00002DAL) #endif #ifndef STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER # define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER ((NTSTATUS) 0xC00002DBL) #endif #ifndef STATUS_DS_HAVE_PRIMARY_MEMBERS # define STATUS_DS_HAVE_PRIMARY_MEMBERS ((NTSTATUS) 0xC00002DCL) #endif #ifndef STATUS_WMI_NOT_SUPPORTED # define STATUS_WMI_NOT_SUPPORTED ((NTSTATUS) 0xC00002DDL) #endif #ifndef STATUS_INSUFFICIENT_POWER # define STATUS_INSUFFICIENT_POWER ((NTSTATUS) 0xC00002DEL) #endif #ifndef STATUS_SAM_NEED_BOOTKEY_PASSWORD # define STATUS_SAM_NEED_BOOTKEY_PASSWORD ((NTSTATUS) 0xC00002DFL) #endif #ifndef STATUS_SAM_NEED_BOOTKEY_FLOPPY # define STATUS_SAM_NEED_BOOTKEY_FLOPPY ((NTSTATUS) 0xC00002E0L) #endif #ifndef STATUS_DS_CANT_START # define STATUS_DS_CANT_START ((NTSTATUS) 0xC00002E1L) #endif #ifndef STATUS_DS_INIT_FAILURE # define STATUS_DS_INIT_FAILURE ((NTSTATUS) 0xC00002E2L) #endif #ifndef STATUS_SAM_INIT_FAILURE # define STATUS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002E3L) #endif #ifndef STATUS_DS_GC_REQUIRED # define STATUS_DS_GC_REQUIRED ((NTSTATUS) 0xC00002E4L) #endif #ifndef STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY # define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY ((NTSTATUS) 0xC00002E5L) #endif #ifndef STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS # define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS ((NTSTATUS) 0xC00002E6L) #endif #ifndef STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED # define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED ((NTSTATUS) 0xC00002E7L) #endif #ifndef STATUS_MULTIPLE_FAULT_VIOLATION # define STATUS_MULTIPLE_FAULT_VIOLATION ((NTSTATUS) 0xC00002E8L) #endif #ifndef STATUS_CURRENT_DOMAIN_NOT_ALLOWED # define STATUS_CURRENT_DOMAIN_NOT_ALLOWED ((NTSTATUS) 0xC00002E9L) #endif #ifndef STATUS_CANNOT_MAKE # define STATUS_CANNOT_MAKE ((NTSTATUS) 0xC00002EAL) #endif #ifndef STATUS_SYSTEM_SHUTDOWN # define STATUS_SYSTEM_SHUTDOWN ((NTSTATUS) 0xC00002EBL) #endif #ifndef STATUS_DS_INIT_FAILURE_CONSOLE # define STATUS_DS_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002ECL) #endif #ifndef STATUS_DS_SAM_INIT_FAILURE_CONSOLE # define STATUS_DS_SAM_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002EDL) #endif #ifndef STATUS_UNFINISHED_CONTEXT_DELETED # define STATUS_UNFINISHED_CONTEXT_DELETED ((NTSTATUS) 0xC00002EEL) #endif #ifndef STATUS_NO_TGT_REPLY # define STATUS_NO_TGT_REPLY ((NTSTATUS) 0xC00002EFL) #endif #ifndef STATUS_OBJECTID_NOT_FOUND # define STATUS_OBJECTID_NOT_FOUND ((NTSTATUS) 0xC00002F0L) #endif #ifndef STATUS_NO_IP_ADDRESSES # define STATUS_NO_IP_ADDRESSES ((NTSTATUS) 0xC00002F1L) #endif #ifndef STATUS_WRONG_CREDENTIAL_HANDLE # define STATUS_WRONG_CREDENTIAL_HANDLE ((NTSTATUS) 0xC00002F2L) #endif #ifndef STATUS_CRYPTO_SYSTEM_INVALID # define STATUS_CRYPTO_SYSTEM_INVALID ((NTSTATUS) 0xC00002F3L) #endif #ifndef STATUS_MAX_REFERRALS_EXCEEDED # define STATUS_MAX_REFERRALS_EXCEEDED ((NTSTATUS) 0xC00002F4L) #endif #ifndef STATUS_MUST_BE_KDC # define STATUS_MUST_BE_KDC ((NTSTATUS) 0xC00002F5L) #endif #ifndef STATUS_STRONG_CRYPTO_NOT_SUPPORTED # define STATUS_STRONG_CRYPTO_NOT_SUPPORTED ((NTSTATUS) 0xC00002F6L) #endif #ifndef STATUS_TOO_MANY_PRINCIPALS # define STATUS_TOO_MANY_PRINCIPALS ((NTSTATUS) 0xC00002F7L) #endif #ifndef STATUS_NO_PA_DATA # define STATUS_NO_PA_DATA ((NTSTATUS) 0xC00002F8L) #endif #ifndef STATUS_PKINIT_NAME_MISMATCH # define STATUS_PKINIT_NAME_MISMATCH ((NTSTATUS) 0xC00002F9L) #endif #ifndef STATUS_SMARTCARD_LOGON_REQUIRED # define STATUS_SMARTCARD_LOGON_REQUIRED ((NTSTATUS) 0xC00002FAL) #endif #ifndef STATUS_KDC_INVALID_REQUEST # define STATUS_KDC_INVALID_REQUEST ((NTSTATUS) 0xC00002FBL) #endif #ifndef STATUS_KDC_UNABLE_TO_REFER # define STATUS_KDC_UNABLE_TO_REFER ((NTSTATUS) 0xC00002FCL) #endif #ifndef STATUS_KDC_UNKNOWN_ETYPE # define STATUS_KDC_UNKNOWN_ETYPE ((NTSTATUS) 0xC00002FDL) #endif #ifndef STATUS_SHUTDOWN_IN_PROGRESS # define STATUS_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FEL) #endif #ifndef STATUS_SERVER_SHUTDOWN_IN_PROGRESS # define STATUS_SERVER_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FFL) #endif #ifndef STATUS_NOT_SUPPORTED_ON_SBS # define STATUS_NOT_SUPPORTED_ON_SBS ((NTSTATUS) 0xC0000300L) #endif #ifndef STATUS_WMI_GUID_DISCONNECTED # define STATUS_WMI_GUID_DISCONNECTED ((NTSTATUS) 0xC0000301L) #endif #ifndef STATUS_WMI_ALREADY_DISABLED # define STATUS_WMI_ALREADY_DISABLED ((NTSTATUS) 0xC0000302L) #endif #ifndef STATUS_WMI_ALREADY_ENABLED # define STATUS_WMI_ALREADY_ENABLED ((NTSTATUS) 0xC0000303L) #endif #ifndef STATUS_MFT_TOO_FRAGMENTED # define STATUS_MFT_TOO_FRAGMENTED ((NTSTATUS) 0xC0000304L) #endif #ifndef STATUS_COPY_PROTECTION_FAILURE # define STATUS_COPY_PROTECTION_FAILURE ((NTSTATUS) 0xC0000305L) #endif #ifndef STATUS_CSS_AUTHENTICATION_FAILURE # define STATUS_CSS_AUTHENTICATION_FAILURE ((NTSTATUS) 0xC0000306L) #endif #ifndef STATUS_CSS_KEY_NOT_PRESENT # define STATUS_CSS_KEY_NOT_PRESENT ((NTSTATUS) 0xC0000307L) #endif #ifndef STATUS_CSS_KEY_NOT_ESTABLISHED # define STATUS_CSS_KEY_NOT_ESTABLISHED ((NTSTATUS) 0xC0000308L) #endif #ifndef STATUS_CSS_SCRAMBLED_SECTOR # define STATUS_CSS_SCRAMBLED_SECTOR ((NTSTATUS) 0xC0000309L) #endif #ifndef STATUS_CSS_REGION_MISMATCH # define STATUS_CSS_REGION_MISMATCH ((NTSTATUS) 0xC000030AL) #endif #ifndef STATUS_CSS_RESETS_EXHAUSTED # define STATUS_CSS_RESETS_EXHAUSTED ((NTSTATUS) 0xC000030BL) #endif #ifndef STATUS_PKINIT_FAILURE # define STATUS_PKINIT_FAILURE ((NTSTATUS) 0xC0000320L) #endif #ifndef STATUS_SMARTCARD_SUBSYSTEM_FAILURE # define STATUS_SMARTCARD_SUBSYSTEM_FAILURE ((NTSTATUS) 0xC0000321L) #endif #ifndef STATUS_NO_KERB_KEY # define STATUS_NO_KERB_KEY ((NTSTATUS) 0xC0000322L) #endif #ifndef STATUS_HOST_DOWN # define STATUS_HOST_DOWN ((NTSTATUS) 0xC0000350L) #endif #ifndef STATUS_UNSUPPORTED_PREAUTH # define STATUS_UNSUPPORTED_PREAUTH ((NTSTATUS) 0xC0000351L) #endif #ifndef STATUS_EFS_ALG_BLOB_TOO_BIG # define STATUS_EFS_ALG_BLOB_TOO_BIG ((NTSTATUS) 0xC0000352L) #endif #ifndef STATUS_PORT_NOT_SET # define STATUS_PORT_NOT_SET ((NTSTATUS) 0xC0000353L) #endif #ifndef STATUS_DEBUGGER_INACTIVE # define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354L) #endif #ifndef STATUS_DS_VERSION_CHECK_FAILURE # define STATUS_DS_VERSION_CHECK_FAILURE ((NTSTATUS) 0xC0000355L) #endif #ifndef STATUS_AUDITING_DISABLED # define STATUS_AUDITING_DISABLED ((NTSTATUS) 0xC0000356L) #endif #ifndef STATUS_PRENT4_MACHINE_ACCOUNT # define STATUS_PRENT4_MACHINE_ACCOUNT ((NTSTATUS) 0xC0000357L) #endif #ifndef STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER # define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC0000358L) #endif #ifndef STATUS_INVALID_IMAGE_WIN_32 # define STATUS_INVALID_IMAGE_WIN_32 ((NTSTATUS) 0xC0000359L) #endif #ifndef STATUS_INVALID_IMAGE_WIN_64 # define STATUS_INVALID_IMAGE_WIN_64 ((NTSTATUS) 0xC000035AL) #endif #ifndef STATUS_BAD_BINDINGS # define STATUS_BAD_BINDINGS ((NTSTATUS) 0xC000035BL) #endif #ifndef STATUS_NETWORK_SESSION_EXPIRED # define STATUS_NETWORK_SESSION_EXPIRED ((NTSTATUS) 0xC000035CL) #endif #ifndef STATUS_APPHELP_BLOCK # define STATUS_APPHELP_BLOCK ((NTSTATUS) 0xC000035DL) #endif #ifndef STATUS_ALL_SIDS_FILTERED # define STATUS_ALL_SIDS_FILTERED ((NTSTATUS) 0xC000035EL) #endif #ifndef STATUS_NOT_SAFE_MODE_DRIVER # define STATUS_NOT_SAFE_MODE_DRIVER ((NTSTATUS) 0xC000035FL) #endif #ifndef STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT # define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT ((NTSTATUS) 0xC0000361L) #endif #ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PATH # define STATUS_ACCESS_DISABLED_BY_POLICY_PATH ((NTSTATUS) 0xC0000362L) #endif #ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER # define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER ((NTSTATUS) 0xC0000363L) #endif #ifndef STATUS_ACCESS_DISABLED_BY_POLICY_OTHER # define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER ((NTSTATUS) 0xC0000364L) #endif #ifndef STATUS_FAILED_DRIVER_ENTRY # define STATUS_FAILED_DRIVER_ENTRY ((NTSTATUS) 0xC0000365L) #endif #ifndef STATUS_DEVICE_ENUMERATION_ERROR # define STATUS_DEVICE_ENUMERATION_ERROR ((NTSTATUS) 0xC0000366L) #endif #ifndef STATUS_MOUNT_POINT_NOT_RESOLVED # define STATUS_MOUNT_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000368L) #endif #ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER # define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS) 0xC0000369L) #endif #ifndef STATUS_MCA_OCCURED # define STATUS_MCA_OCCURED ((NTSTATUS) 0xC000036AL) #endif #ifndef STATUS_DRIVER_BLOCKED_CRITICAL # define STATUS_DRIVER_BLOCKED_CRITICAL ((NTSTATUS) 0xC000036BL) #endif #ifndef STATUS_DRIVER_BLOCKED # define STATUS_DRIVER_BLOCKED ((NTSTATUS) 0xC000036CL) #endif #ifndef STATUS_DRIVER_DATABASE_ERROR # define STATUS_DRIVER_DATABASE_ERROR ((NTSTATUS) 0xC000036DL) #endif #ifndef STATUS_SYSTEM_HIVE_TOO_LARGE # define STATUS_SYSTEM_HIVE_TOO_LARGE ((NTSTATUS) 0xC000036EL) #endif #ifndef STATUS_INVALID_IMPORT_OF_NON_DLL # define STATUS_INVALID_IMPORT_OF_NON_DLL ((NTSTATUS) 0xC000036FL) #endif #ifndef STATUS_DS_SHUTTING_DOWN # define STATUS_DS_SHUTTING_DOWN ((NTSTATUS) 0x40000370L) #endif #ifndef STATUS_NO_SECRETS # define STATUS_NO_SECRETS ((NTSTATUS) 0xC0000371L) #endif #ifndef STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY # define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY ((NTSTATUS) 0xC0000372L) #endif #ifndef STATUS_FAILED_STACK_SWITCH # define STATUS_FAILED_STACK_SWITCH ((NTSTATUS) 0xC0000373L) #endif #ifndef STATUS_HEAP_CORRUPTION # define STATUS_HEAP_CORRUPTION ((NTSTATUS) 0xC0000374L) #endif #ifndef STATUS_SMARTCARD_WRONG_PIN # define STATUS_SMARTCARD_WRONG_PIN ((NTSTATUS) 0xC0000380L) #endif #ifndef STATUS_SMARTCARD_CARD_BLOCKED # define STATUS_SMARTCARD_CARD_BLOCKED ((NTSTATUS) 0xC0000381L) #endif #ifndef STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED # define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED ((NTSTATUS) 0xC0000382L) #endif #ifndef STATUS_SMARTCARD_NO_CARD # define STATUS_SMARTCARD_NO_CARD ((NTSTATUS) 0xC0000383L) #endif #ifndef STATUS_SMARTCARD_NO_KEY_CONTAINER # define STATUS_SMARTCARD_NO_KEY_CONTAINER ((NTSTATUS) 0xC0000384L) #endif #ifndef STATUS_SMARTCARD_NO_CERTIFICATE # define STATUS_SMARTCARD_NO_CERTIFICATE ((NTSTATUS) 0xC0000385L) #endif #ifndef STATUS_SMARTCARD_NO_KEYSET # define STATUS_SMARTCARD_NO_KEYSET ((NTSTATUS) 0xC0000386L) #endif #ifndef STATUS_SMARTCARD_IO_ERROR # define STATUS_SMARTCARD_IO_ERROR ((NTSTATUS) 0xC0000387L) #endif #ifndef STATUS_DOWNGRADE_DETECTED # define STATUS_DOWNGRADE_DETECTED ((NTSTATUS) 0xC0000388L) #endif #ifndef STATUS_SMARTCARD_CERT_REVOKED # define STATUS_SMARTCARD_CERT_REVOKED ((NTSTATUS) 0xC0000389L) #endif #ifndef STATUS_ISSUING_CA_UNTRUSTED # define STATUS_ISSUING_CA_UNTRUSTED ((NTSTATUS) 0xC000038AL) #endif #ifndef STATUS_REVOCATION_OFFLINE_C # define STATUS_REVOCATION_OFFLINE_C ((NTSTATUS) 0xC000038BL) #endif #ifndef STATUS_PKINIT_CLIENT_FAILURE # define STATUS_PKINIT_CLIENT_FAILURE ((NTSTATUS) 0xC000038CL) #endif #ifndef STATUS_SMARTCARD_CERT_EXPIRED # define STATUS_SMARTCARD_CERT_EXPIRED ((NTSTATUS) 0xC000038DL) #endif #ifndef STATUS_DRIVER_FAILED_PRIOR_UNLOAD # define STATUS_DRIVER_FAILED_PRIOR_UNLOAD ((NTSTATUS) 0xC000038EL) #endif #ifndef STATUS_SMARTCARD_SILENT_CONTEXT # define STATUS_SMARTCARD_SILENT_CONTEXT ((NTSTATUS) 0xC000038FL) #endif #ifndef STATUS_PER_USER_TRUST_QUOTA_EXCEEDED # define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000401L) #endif #ifndef STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED # define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000402L) #endif #ifndef STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED # define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000403L) #endif #ifndef STATUS_DS_NAME_NOT_UNIQUE # define STATUS_DS_NAME_NOT_UNIQUE ((NTSTATUS) 0xC0000404L) #endif #ifndef STATUS_DS_DUPLICATE_ID_FOUND # define STATUS_DS_DUPLICATE_ID_FOUND ((NTSTATUS) 0xC0000405L) #endif #ifndef STATUS_DS_GROUP_CONVERSION_ERROR # define STATUS_DS_GROUP_CONVERSION_ERROR ((NTSTATUS) 0xC0000406L) #endif #ifndef STATUS_VOLSNAP_PREPARE_HIBERNATE # define STATUS_VOLSNAP_PREPARE_HIBERNATE ((NTSTATUS) 0xC0000407L) #endif #ifndef STATUS_USER2USER_REQUIRED # define STATUS_USER2USER_REQUIRED ((NTSTATUS) 0xC0000408L) #endif #ifndef STATUS_STACK_BUFFER_OVERRUN # define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS) 0xC0000409L) #endif #ifndef STATUS_NO_S4U_PROT_SUPPORT # define STATUS_NO_S4U_PROT_SUPPORT ((NTSTATUS) 0xC000040AL) #endif #ifndef STATUS_CROSSREALM_DELEGATION_FAILURE # define STATUS_CROSSREALM_DELEGATION_FAILURE ((NTSTATUS) 0xC000040BL) #endif #ifndef STATUS_REVOCATION_OFFLINE_KDC # define STATUS_REVOCATION_OFFLINE_KDC ((NTSTATUS) 0xC000040CL) #endif #ifndef STATUS_ISSUING_CA_UNTRUSTED_KDC # define STATUS_ISSUING_CA_UNTRUSTED_KDC ((NTSTATUS) 0xC000040DL) #endif #ifndef STATUS_KDC_CERT_EXPIRED # define STATUS_KDC_CERT_EXPIRED ((NTSTATUS) 0xC000040EL) #endif #ifndef STATUS_KDC_CERT_REVOKED # define STATUS_KDC_CERT_REVOKED ((NTSTATUS) 0xC000040FL) #endif #ifndef STATUS_PARAMETER_QUOTA_EXCEEDED # define STATUS_PARAMETER_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000410L) #endif #ifndef STATUS_HIBERNATION_FAILURE # define STATUS_HIBERNATION_FAILURE ((NTSTATUS) 0xC0000411L) #endif #ifndef STATUS_DELAY_LOAD_FAILED # define STATUS_DELAY_LOAD_FAILED ((NTSTATUS) 0xC0000412L) #endif #ifndef STATUS_AUTHENTICATION_FIREWALL_FAILED # define STATUS_AUTHENTICATION_FIREWALL_FAILED ((NTSTATUS) 0xC0000413L) #endif #ifndef STATUS_VDM_DISALLOWED # define STATUS_VDM_DISALLOWED ((NTSTATUS) 0xC0000414L) #endif #ifndef STATUS_HUNG_DISPLAY_DRIVER_THREAD # define STATUS_HUNG_DISPLAY_DRIVER_THREAD ((NTSTATUS) 0xC0000415L) #endif #ifndef STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE # define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE ((NTSTATUS) 0xC0000416L) #endif #ifndef STATUS_INVALID_CRUNTIME_PARAMETER # define STATUS_INVALID_CRUNTIME_PARAMETER ((NTSTATUS) 0xC0000417L) #endif #ifndef STATUS_NTLM_BLOCKED # define STATUS_NTLM_BLOCKED ((NTSTATUS) 0xC0000418L) #endif #ifndef STATUS_DS_SRC_SID_EXISTS_IN_FOREST # define STATUS_DS_SRC_SID_EXISTS_IN_FOREST ((NTSTATUS) 0xC0000419L) #endif #ifndef STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST # define STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041AL) #endif #ifndef STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST # define STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041BL) #endif #ifndef STATUS_INVALID_USER_PRINCIPAL_NAME # define STATUS_INVALID_USER_PRINCIPAL_NAME ((NTSTATUS) 0xC000041CL) #endif #ifndef STATUS_FATAL_USER_CALLBACK_EXCEPTION # define STATUS_FATAL_USER_CALLBACK_EXCEPTION ((NTSTATUS) 0xC000041DL) #endif #ifndef STATUS_ASSERTION_FAILURE # define STATUS_ASSERTION_FAILURE ((NTSTATUS) 0xC0000420L) #endif #ifndef STATUS_VERIFIER_STOP # define STATUS_VERIFIER_STOP ((NTSTATUS) 0xC0000421L) #endif #ifndef STATUS_CALLBACK_POP_STACK # define STATUS_CALLBACK_POP_STACK ((NTSTATUS) 0xC0000423L) #endif #ifndef STATUS_INCOMPATIBLE_DRIVER_BLOCKED # define STATUS_INCOMPATIBLE_DRIVER_BLOCKED ((NTSTATUS) 0xC0000424L) #endif #ifndef STATUS_HIVE_UNLOADED # define STATUS_HIVE_UNLOADED ((NTSTATUS) 0xC0000425L) #endif #ifndef STATUS_COMPRESSION_DISABLED # define STATUS_COMPRESSION_DISABLED ((NTSTATUS) 0xC0000426L) #endif #ifndef STATUS_FILE_SYSTEM_LIMITATION # define STATUS_FILE_SYSTEM_LIMITATION ((NTSTATUS) 0xC0000427L) #endif #ifndef STATUS_INVALID_IMAGE_HASH # define STATUS_INVALID_IMAGE_HASH ((NTSTATUS) 0xC0000428L) #endif #ifndef STATUS_NOT_CAPABLE # define STATUS_NOT_CAPABLE ((NTSTATUS) 0xC0000429L) #endif #ifndef STATUS_REQUEST_OUT_OF_SEQUENCE # define STATUS_REQUEST_OUT_OF_SEQUENCE ((NTSTATUS) 0xC000042AL) #endif #ifndef STATUS_IMPLEMENTATION_LIMIT # define STATUS_IMPLEMENTATION_LIMIT ((NTSTATUS) 0xC000042BL) #endif #ifndef STATUS_ELEVATION_REQUIRED # define STATUS_ELEVATION_REQUIRED ((NTSTATUS) 0xC000042CL) #endif #ifndef STATUS_NO_SECURITY_CONTEXT # define STATUS_NO_SECURITY_CONTEXT ((NTSTATUS) 0xC000042DL) #endif #ifndef STATUS_PKU2U_CERT_FAILURE # define STATUS_PKU2U_CERT_FAILURE ((NTSTATUS) 0xC000042FL) #endif #ifndef STATUS_BEYOND_VDL # define STATUS_BEYOND_VDL ((NTSTATUS) 0xC0000432L) #endif #ifndef STATUS_ENCOUNTERED_WRITE_IN_PROGRESS # define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS ((NTSTATUS) 0xC0000433L) #endif #ifndef STATUS_PTE_CHANGED # define STATUS_PTE_CHANGED ((NTSTATUS) 0xC0000434L) #endif #ifndef STATUS_PURGE_FAILED # define STATUS_PURGE_FAILED ((NTSTATUS) 0xC0000435L) #endif #ifndef STATUS_CRED_REQUIRES_CONFIRMATION # define STATUS_CRED_REQUIRES_CONFIRMATION ((NTSTATUS) 0xC0000440L) #endif #ifndef STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE # define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE ((NTSTATUS) 0xC0000441L) #endif #ifndef STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER # define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER ((NTSTATUS) 0xC0000442L) #endif #ifndef STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE # define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE ((NTSTATUS) 0xC0000443L) #endif #ifndef STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE # define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE ((NTSTATUS) 0xC0000444L) #endif #ifndef STATUS_CS_ENCRYPTION_FILE_NOT_CSE # define STATUS_CS_ENCRYPTION_FILE_NOT_CSE ((NTSTATUS) 0xC0000445L) #endif #ifndef STATUS_INVALID_LABEL # define STATUS_INVALID_LABEL ((NTSTATUS) 0xC0000446L) #endif #ifndef STATUS_DRIVER_PROCESS_TERMINATED # define STATUS_DRIVER_PROCESS_TERMINATED ((NTSTATUS) 0xC0000450L) #endif #ifndef STATUS_AMBIGUOUS_SYSTEM_DEVICE # define STATUS_AMBIGUOUS_SYSTEM_DEVICE ((NTSTATUS) 0xC0000451L) #endif #ifndef STATUS_SYSTEM_DEVICE_NOT_FOUND # define STATUS_SYSTEM_DEVICE_NOT_FOUND ((NTSTATUS) 0xC0000452L) #endif #ifndef STATUS_RESTART_BOOT_APPLICATION # define STATUS_RESTART_BOOT_APPLICATION ((NTSTATUS) 0xC0000453L) #endif #ifndef STATUS_INSUFFICIENT_NVRAM_RESOURCES # define STATUS_INSUFFICIENT_NVRAM_RESOURCES ((NTSTATUS) 0xC0000454L) #endif #ifndef STATUS_INVALID_TASK_NAME # define STATUS_INVALID_TASK_NAME ((NTSTATUS) 0xC0000500L) #endif #ifndef STATUS_INVALID_TASK_INDEX # define STATUS_INVALID_TASK_INDEX ((NTSTATUS) 0xC0000501L) #endif #ifndef STATUS_THREAD_ALREADY_IN_TASK # define STATUS_THREAD_ALREADY_IN_TASK ((NTSTATUS) 0xC0000502L) #endif #ifndef STATUS_CALLBACK_BYPASS # define STATUS_CALLBACK_BYPASS ((NTSTATUS) 0xC0000503L) #endif #ifndef STATUS_FAIL_FAST_EXCEPTION # define STATUS_FAIL_FAST_EXCEPTION ((NTSTATUS) 0xC0000602L) #endif #ifndef STATUS_IMAGE_CERT_REVOKED # define STATUS_IMAGE_CERT_REVOKED ((NTSTATUS) 0xC0000603L) #endif #ifndef STATUS_PORT_CLOSED # define STATUS_PORT_CLOSED ((NTSTATUS) 0xC0000700L) #endif #ifndef STATUS_MESSAGE_LOST # define STATUS_MESSAGE_LOST ((NTSTATUS) 0xC0000701L) #endif #ifndef STATUS_INVALID_MESSAGE # define STATUS_INVALID_MESSAGE ((NTSTATUS) 0xC0000702L) #endif #ifndef STATUS_REQUEST_CANCELED # define STATUS_REQUEST_CANCELED ((NTSTATUS) 0xC0000703L) #endif #ifndef STATUS_RECURSIVE_DISPATCH # define STATUS_RECURSIVE_DISPATCH ((NTSTATUS) 0xC0000704L) #endif #ifndef STATUS_LPC_RECEIVE_BUFFER_EXPECTED # define STATUS_LPC_RECEIVE_BUFFER_EXPECTED ((NTSTATUS) 0xC0000705L) #endif #ifndef STATUS_LPC_INVALID_CONNECTION_USAGE # define STATUS_LPC_INVALID_CONNECTION_USAGE ((NTSTATUS) 0xC0000706L) #endif #ifndef STATUS_LPC_REQUESTS_NOT_ALLOWED # define STATUS_LPC_REQUESTS_NOT_ALLOWED ((NTSTATUS) 0xC0000707L) #endif #ifndef STATUS_RESOURCE_IN_USE # define STATUS_RESOURCE_IN_USE ((NTSTATUS) 0xC0000708L) #endif #ifndef STATUS_HARDWARE_MEMORY_ERROR # define STATUS_HARDWARE_MEMORY_ERROR ((NTSTATUS) 0xC0000709L) #endif #ifndef STATUS_THREADPOOL_HANDLE_EXCEPTION # define STATUS_THREADPOOL_HANDLE_EXCEPTION ((NTSTATUS) 0xC000070AL) #endif #ifndef STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED # define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070BL) #endif #ifndef STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED # define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070CL) #endif #ifndef STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED # define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070DL) #endif #ifndef STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED # define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070EL) #endif #ifndef STATUS_THREADPOOL_RELEASED_DURING_OPERATION # define STATUS_THREADPOOL_RELEASED_DURING_OPERATION ((NTSTATUS) 0xC000070FL) #endif #ifndef STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING # define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000710L) #endif #ifndef STATUS_APC_RETURNED_WHILE_IMPERSONATING # define STATUS_APC_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000711L) #endif #ifndef STATUS_PROCESS_IS_PROTECTED # define STATUS_PROCESS_IS_PROTECTED ((NTSTATUS) 0xC0000712L) #endif #ifndef STATUS_MCA_EXCEPTION # define STATUS_MCA_EXCEPTION ((NTSTATUS) 0xC0000713L) #endif #ifndef STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE # define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE ((NTSTATUS) 0xC0000714L) #endif #ifndef STATUS_SYMLINK_CLASS_DISABLED # define STATUS_SYMLINK_CLASS_DISABLED ((NTSTATUS) 0xC0000715L) #endif #ifndef STATUS_INVALID_IDN_NORMALIZATION # define STATUS_INVALID_IDN_NORMALIZATION ((NTSTATUS) 0xC0000716L) #endif #ifndef STATUS_NO_UNICODE_TRANSLATION # define STATUS_NO_UNICODE_TRANSLATION ((NTSTATUS) 0xC0000717L) #endif #ifndef STATUS_ALREADY_REGISTERED # define STATUS_ALREADY_REGISTERED ((NTSTATUS) 0xC0000718L) #endif #ifndef STATUS_CONTEXT_MISMATCH # define STATUS_CONTEXT_MISMATCH ((NTSTATUS) 0xC0000719L) #endif #ifndef STATUS_PORT_ALREADY_HAS_COMPLETION_LIST # define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST ((NTSTATUS) 0xC000071AL) #endif #ifndef STATUS_CALLBACK_RETURNED_THREAD_PRIORITY # define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY ((NTSTATUS) 0xC000071BL) #endif #ifndef STATUS_INVALID_THREAD # define STATUS_INVALID_THREAD ((NTSTATUS) 0xC000071CL) #endif #ifndef STATUS_CALLBACK_RETURNED_TRANSACTION # define STATUS_CALLBACK_RETURNED_TRANSACTION ((NTSTATUS) 0xC000071DL) #endif #ifndef STATUS_CALLBACK_RETURNED_LDR_LOCK # define STATUS_CALLBACK_RETURNED_LDR_LOCK ((NTSTATUS) 0xC000071EL) #endif #ifndef STATUS_CALLBACK_RETURNED_LANG # define STATUS_CALLBACK_RETURNED_LANG ((NTSTATUS) 0xC000071FL) #endif #ifndef STATUS_CALLBACK_RETURNED_PRI_BACK # define STATUS_CALLBACK_RETURNED_PRI_BACK ((NTSTATUS) 0xC0000720L) #endif #ifndef STATUS_CALLBACK_RETURNED_THREAD_AFFINITY # define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY ((NTSTATUS) 0xC0000721L) #endif #ifndef STATUS_DISK_REPAIR_DISABLED # define STATUS_DISK_REPAIR_DISABLED ((NTSTATUS) 0xC0000800L) #endif #ifndef STATUS_DS_DOMAIN_RENAME_IN_PROGRESS # define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS ((NTSTATUS) 0xC0000801L) #endif #ifndef STATUS_DISK_QUOTA_EXCEEDED # define STATUS_DISK_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000802L) #endif #ifndef STATUS_DATA_LOST_REPAIR # define STATUS_DATA_LOST_REPAIR ((NTSTATUS) 0x80000803L) #endif #ifndef STATUS_CONTENT_BLOCKED # define STATUS_CONTENT_BLOCKED ((NTSTATUS) 0xC0000804L) #endif #ifndef STATUS_BAD_CLUSTERS # define STATUS_BAD_CLUSTERS ((NTSTATUS) 0xC0000805L) #endif #ifndef STATUS_VOLUME_DIRTY # define STATUS_VOLUME_DIRTY ((NTSTATUS) 0xC0000806L) #endif #ifndef STATUS_FILE_CHECKED_OUT # define STATUS_FILE_CHECKED_OUT ((NTSTATUS) 0xC0000901L) #endif #ifndef STATUS_CHECKOUT_REQUIRED # define STATUS_CHECKOUT_REQUIRED ((NTSTATUS) 0xC0000902L) #endif #ifndef STATUS_BAD_FILE_TYPE # define STATUS_BAD_FILE_TYPE ((NTSTATUS) 0xC0000903L) #endif #ifndef STATUS_FILE_TOO_LARGE # define STATUS_FILE_TOO_LARGE ((NTSTATUS) 0xC0000904L) #endif #ifndef STATUS_FORMS_AUTH_REQUIRED # define STATUS_FORMS_AUTH_REQUIRED ((NTSTATUS) 0xC0000905L) #endif #ifndef STATUS_VIRUS_INFECTED # define STATUS_VIRUS_INFECTED ((NTSTATUS) 0xC0000906L) #endif #ifndef STATUS_VIRUS_DELETED # define STATUS_VIRUS_DELETED ((NTSTATUS) 0xC0000907L) #endif #ifndef STATUS_BAD_MCFG_TABLE # define STATUS_BAD_MCFG_TABLE ((NTSTATUS) 0xC0000908L) #endif #ifndef STATUS_CANNOT_BREAK_OPLOCK # define STATUS_CANNOT_BREAK_OPLOCK ((NTSTATUS) 0xC0000909L) #endif #ifndef STATUS_WOW_ASSERTION # define STATUS_WOW_ASSERTION ((NTSTATUS) 0xC0009898L) #endif #ifndef STATUS_INVALID_SIGNATURE # define STATUS_INVALID_SIGNATURE ((NTSTATUS) 0xC000A000L) #endif #ifndef STATUS_HMAC_NOT_SUPPORTED # define STATUS_HMAC_NOT_SUPPORTED ((NTSTATUS) 0xC000A001L) #endif #ifndef STATUS_AUTH_TAG_MISMATCH # define STATUS_AUTH_TAG_MISMATCH ((NTSTATUS) 0xC000A002L) #endif #ifndef STATUS_IPSEC_QUEUE_OVERFLOW # define STATUS_IPSEC_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A010L) #endif #ifndef STATUS_ND_QUEUE_OVERFLOW # define STATUS_ND_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A011L) #endif #ifndef STATUS_HOPLIMIT_EXCEEDED # define STATUS_HOPLIMIT_EXCEEDED ((NTSTATUS) 0xC000A012L) #endif #ifndef STATUS_PROTOCOL_NOT_SUPPORTED # define STATUS_PROTOCOL_NOT_SUPPORTED ((NTSTATUS) 0xC000A013L) #endif #ifndef STATUS_FASTPATH_REJECTED # define STATUS_FASTPATH_REJECTED ((NTSTATUS) 0xC000A014L) #endif #ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED # define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED ((NTSTATUS) 0xC000A080L) #endif #ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR # define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR ((NTSTATUS) 0xC000A081L) #endif #ifndef STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR # define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR ((NTSTATUS) 0xC000A082L) #endif #ifndef STATUS_XML_PARSE_ERROR # define STATUS_XML_PARSE_ERROR ((NTSTATUS) 0xC000A083L) #endif #ifndef STATUS_XMLDSIG_ERROR # define STATUS_XMLDSIG_ERROR ((NTSTATUS) 0xC000A084L) #endif #ifndef STATUS_WRONG_COMPARTMENT # define STATUS_WRONG_COMPARTMENT ((NTSTATUS) 0xC000A085L) #endif #ifndef STATUS_AUTHIP_FAILURE # define STATUS_AUTHIP_FAILURE ((NTSTATUS) 0xC000A086L) #endif #ifndef STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS # define STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS ((NTSTATUS) 0xC000A087L) #endif #ifndef STATUS_DS_OID_NOT_FOUND # define STATUS_DS_OID_NOT_FOUND ((NTSTATUS) 0xC000A088L) #endif #ifndef STATUS_HASH_NOT_SUPPORTED # define STATUS_HASH_NOT_SUPPORTED ((NTSTATUS) 0xC000A100L) #endif #ifndef STATUS_HASH_NOT_PRESENT # define STATUS_HASH_NOT_PRESENT ((NTSTATUS) 0xC000A101L) #endif /* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the */ /* DDK got it wrong! */ #ifdef NTSTATUS_FROM_WIN32 # undef NTSTATUS_FROM_WIN32 #endif #define NTSTATUS_FROM_WIN32(error) ((NTSTATUS) (error) <= 0 ? \ ((NTSTATUS) (error)) : ((NTSTATUS) (((error) & 0x0000FFFF) | \ (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_WARNING))) #ifndef JOB_OBJECT_LIMIT_PROCESS_MEMORY # define JOB_OBJECT_LIMIT_PROCESS_MEMORY 0x00000100 #endif #ifndef JOB_OBJECT_LIMIT_JOB_MEMORY # define JOB_OBJECT_LIMIT_JOB_MEMORY 0x00000200 #endif #ifndef JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION # define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 0x00000400 #endif #ifndef JOB_OBJECT_LIMIT_BREAKAWAY_OK # define JOB_OBJECT_LIMIT_BREAKAWAY_OK 0x00000800 #endif #ifndef JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK # define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK 0x00001000 #endif #ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE # define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000 #endif /* from winternl.h */ typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; typedef const UNICODE_STRING *PCUNICODE_STRING; /* from ntifs.h */ #ifndef DEVICE_TYPE # define DEVICE_TYPE DWORD #endif /* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does * not. */ #if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; } DUMMYUNIONNAME; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #endif typedef struct _IO_STATUS_BLOCK { union { NTSTATUS Status; PVOID Pointer; } DUMMYUNIONNAME; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; typedef enum _FILE_INFORMATION_CLASS { FileDirectoryInformation = 1, FileFullDirectoryInformation, FileBothDirectoryInformation, FileBasicInformation, FileStandardInformation, FileInternalInformation, FileEaInformation, FileAccessInformation, FileNameInformation, FileRenameInformation, FileLinkInformation, FileNamesInformation, FileDispositionInformation, FilePositionInformation, FileFullEaInformation, FileModeInformation, FileAlignmentInformation, FileAllInformation, FileAllocationInformation, FileEndOfFileInformation, FileAlternateNameInformation, FileStreamInformation, FilePipeInformation, FilePipeLocalInformation, FilePipeRemoteInformation, FileMailslotQueryInformation, FileMailslotSetInformation, FileCompressionInformation, FileObjectIdInformation, FileCompletionInformation, FileMoveClusterInformation, FileQuotaInformation, FileReparsePointInformation, FileNetworkOpenInformation, FileAttributeTagInformation, FileTrackingInformation, FileIdBothDirectoryInformation, FileIdFullDirectoryInformation, FileValidDataLengthInformation, FileShortNameInformation, FileIoCompletionNotificationInformation, FileIoStatusBlockRangeInformation, FileIoPriorityHintInformation, FileSfioReserveInformation, FileSfioVolumeInformation, FileHardLinkInformation, FileProcessIdsUsingFileInformation, FileNormalizedNameInformation, FileNetworkPhysicalNameInformation, FileIdGlobalTxDirectoryInformation, FileIsRemoteDeviceInformation, FileAttributeCacheInformation, FileNumaNodeInformation, FileStandardLinkInformation, FileRemoteProtocolInformation, FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; WCHAR FileName[1]; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; typedef struct _FILE_BOTH_DIR_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaSize; CCHAR ShortNameLength; WCHAR ShortName[12]; WCHAR FileName[1]; } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; typedef struct _FILE_BASIC_INFORMATION { LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; DWORD FileAttributes; } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; typedef struct _FILE_STANDARD_INFORMATION { LARGE_INTEGER AllocationSize; LARGE_INTEGER EndOfFile; ULONG NumberOfLinks; BOOLEAN DeletePending; BOOLEAN Directory; } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; typedef struct _FILE_INTERNAL_INFORMATION { LARGE_INTEGER IndexNumber; } FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; typedef struct _FILE_EA_INFORMATION { ULONG EaSize; } FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; typedef struct _FILE_ACCESS_INFORMATION { ACCESS_MASK AccessFlags; } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; typedef struct _FILE_POSITION_INFORMATION { LARGE_INTEGER CurrentByteOffset; } FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; typedef struct _FILE_MODE_INFORMATION { ULONG Mode; } FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; typedef struct _FILE_ALIGNMENT_INFORMATION { ULONG AlignmentRequirement; } FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; typedef struct _FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; typedef struct _FILE_END_OF_FILE_INFORMATION { LARGE_INTEGER EndOfFile; } FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; typedef struct _FILE_ALL_INFORMATION { FILE_BASIC_INFORMATION BasicInformation; FILE_STANDARD_INFORMATION StandardInformation; FILE_INTERNAL_INFORMATION InternalInformation; FILE_EA_INFORMATION EaInformation; FILE_ACCESS_INFORMATION AccessInformation; FILE_POSITION_INFORMATION PositionInformation; FILE_MODE_INFORMATION ModeInformation; FILE_ALIGNMENT_INFORMATION AlignmentInformation; FILE_NAME_INFORMATION NameInformation; } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; typedef struct _FILE_DISPOSITION_INFORMATION { BOOLEAN DeleteFile; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; typedef struct _FILE_PIPE_LOCAL_INFORMATION { ULONG NamedPipeType; ULONG NamedPipeConfiguration; ULONG MaximumInstances; ULONG CurrentInstances; ULONG InboundQuota; ULONG ReadDataAvailable; ULONG OutboundQuota; ULONG WriteQuotaAvailable; ULONG NamedPipeState; ULONG NamedPipeEnd; } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; #define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 typedef enum _FS_INFORMATION_CLASS { FileFsVolumeInformation = 1, FileFsLabelInformation = 2, FileFsSizeInformation = 3, FileFsDeviceInformation = 4, FileFsAttributeInformation = 5, FileFsControlInformation = 6, FileFsFullSizeInformation = 7, FileFsObjectIdInformation = 8, FileFsDriverPathInformation = 9, FileFsVolumeFlagsInformation = 10, FileFsSectorSizeInformation = 11 } FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; typedef struct _FILE_FS_VOLUME_INFORMATION { LARGE_INTEGER VolumeCreationTime; ULONG VolumeSerialNumber; ULONG VolumeLabelLength; BOOLEAN SupportsObjects; WCHAR VolumeLabel[1]; } FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION; typedef struct _FILE_FS_LABEL_INFORMATION { ULONG VolumeLabelLength; WCHAR VolumeLabel[1]; } FILE_FS_LABEL_INFORMATION, *PFILE_FS_LABEL_INFORMATION; typedef struct _FILE_FS_SIZE_INFORMATION { LARGE_INTEGER TotalAllocationUnits; LARGE_INTEGER AvailableAllocationUnits; ULONG SectorsPerAllocationUnit; ULONG BytesPerSector; } FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION; typedef struct _FILE_FS_DEVICE_INFORMATION { DEVICE_TYPE DeviceType; ULONG Characteristics; } FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION; typedef struct _FILE_FS_ATTRIBUTE_INFORMATION { ULONG FileSystemAttributes; LONG MaximumComponentNameLength; ULONG FileSystemNameLength; WCHAR FileSystemName[1]; } FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION; typedef struct _FILE_FS_CONTROL_INFORMATION { LARGE_INTEGER FreeSpaceStartFiltering; LARGE_INTEGER FreeSpaceThreshold; LARGE_INTEGER FreeSpaceStopFiltering; LARGE_INTEGER DefaultQuotaThreshold; LARGE_INTEGER DefaultQuotaLimit; ULONG FileSystemControlFlags; } FILE_FS_CONTROL_INFORMATION, *PFILE_FS_CONTROL_INFORMATION; typedef struct _FILE_FS_FULL_SIZE_INFORMATION { LARGE_INTEGER TotalAllocationUnits; LARGE_INTEGER CallerAvailableAllocationUnits; LARGE_INTEGER ActualAvailableAllocationUnits; ULONG SectorsPerAllocationUnit; ULONG BytesPerSector; } FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION; typedef struct _FILE_FS_OBJECTID_INFORMATION { UCHAR ObjectId[16]; UCHAR ExtendedInfo[48]; } FILE_FS_OBJECTID_INFORMATION, *PFILE_FS_OBJECTID_INFORMATION; typedef struct _FILE_FS_DRIVER_PATH_INFORMATION { BOOLEAN DriverInPath; ULONG DriverNameLength; WCHAR DriverName[1]; } FILE_FS_DRIVER_PATH_INFORMATION, *PFILE_FS_DRIVER_PATH_INFORMATION; typedef struct _FILE_FS_VOLUME_FLAGS_INFORMATION { ULONG Flags; } FILE_FS_VOLUME_FLAGS_INFORMATION, *PFILE_FS_VOLUME_FLAGS_INFORMATION; typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION { ULONG LogicalBytesPerSector; ULONG PhysicalBytesPerSectorForAtomicity; ULONG PhysicalBytesPerSectorForPerformance; ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity; ULONG Flags; ULONG ByteOffsetForSectorAlignment; ULONG ByteOffsetForPartitionAlignment; } FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION; typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER DpcTime; LARGE_INTEGER InterruptTime; ULONG InterruptCount; } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; #ifndef SystemProcessorPerformanceInformation # define SystemProcessorPerformanceInformation 8 #endif #ifndef FILE_DEVICE_FILE_SYSTEM # define FILE_DEVICE_FILE_SYSTEM 0x00000009 #endif #ifndef FILE_DEVICE_NETWORK # define FILE_DEVICE_NETWORK 0x00000012 #endif #ifndef METHOD_BUFFERED # define METHOD_BUFFERED 0 #endif #ifndef METHOD_IN_DIRECT # define METHOD_IN_DIRECT 1 #endif #ifndef METHOD_OUT_DIRECT # define METHOD_OUT_DIRECT 2 #endif #ifndef METHOD_NEITHER #define METHOD_NEITHER 3 #endif #ifndef METHOD_DIRECT_TO_HARDWARE # define METHOD_DIRECT_TO_HARDWARE METHOD_IN_DIRECT #endif #ifndef METHOD_DIRECT_FROM_HARDWARE # define METHOD_DIRECT_FROM_HARDWARE METHOD_OUT_DIRECT #endif #ifndef FILE_ANY_ACCESS # define FILE_ANY_ACCESS 0 #endif #ifndef FILE_SPECIAL_ACCESS # define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) #endif #ifndef FILE_READ_ACCESS # define FILE_READ_ACCESS 0x0001 #endif #ifndef FILE_WRITE_ACCESS # define FILE_WRITE_ACCESS 0x0002 #endif #ifndef CTL_CODE # define CTL_CODE(device_type, function, method, access) \ (((device_type) << 16) | ((access) << 14) | ((function) << 2) | (method)) #endif #ifndef FSCTL_SET_REPARSE_POINT # define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ 41, \ METHOD_BUFFERED, \ FILE_SPECIAL_ACCESS) #endif #ifndef FSCTL_GET_REPARSE_POINT # define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ 42, \ METHOD_BUFFERED, \ FILE_ANY_ACCESS) #endif #ifndef FSCTL_DELETE_REPARSE_POINT # define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ 43, \ METHOD_BUFFERED, \ FILE_SPECIAL_ACCESS) #endif #ifndef IO_REPARSE_TAG_SYMLINK # define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif typedef VOID (NTAPI *PIO_APC_ROUTINE) (PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG Reserved); typedef ULONG (NTAPI *sRtlNtStatusToDosError) (NTSTATUS Status); typedef NTSTATUS (NTAPI *sNtDeviceIoControlFile) (HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength); typedef NTSTATUS (NTAPI *sNtQueryInformationFile) (HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); typedef NTSTATUS (NTAPI *sNtSetInformationFile) (HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); typedef NTSTATUS (NTAPI *sNtQueryVolumeInformationFile) (HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FsInformation, ULONG Length, FS_INFORMATION_CLASS FsInformationClass); typedef NTSTATUS (NTAPI *sNtQuerySystemInformation) (UINT SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile) (HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, PUNICODE_STRING FileName, BOOLEAN RestartScan ); /* * Kernel32 headers */ #ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS # define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 #endif #ifndef FILE_SKIP_SET_EVENT_ON_HANDLE # define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 #endif #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 #endif #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) typedef struct _OVERLAPPED_ENTRY { ULONG_PTR lpCompletionKey; LPOVERLAPPED lpOverlapped; ULONG_PTR Internal; DWORD dwNumberOfBytesTransferred; } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; #endif /* from wincon.h */ #ifndef ENABLE_INSERT_MODE # define ENABLE_INSERT_MODE 0x20 #endif #ifndef ENABLE_QUICK_EDIT_MODE # define ENABLE_QUICK_EDIT_MODE 0x40 #endif #ifndef ENABLE_EXTENDED_FLAGS # define ENABLE_EXTENDED_FLAGS 0x80 #endif /* from winerror.h */ #ifndef ERROR_SYMLINK_NOT_SUPPORTED # define ERROR_SYMLINK_NOT_SUPPORTED 1464 #endif #ifndef ERROR_MUI_FILE_NOT_FOUND # define ERROR_MUI_FILE_NOT_FOUND 15100 #endif #ifndef ERROR_MUI_INVALID_FILE # define ERROR_MUI_INVALID_FILE 15101 #endif #ifndef ERROR_MUI_INVALID_RC_CONFIG # define ERROR_MUI_INVALID_RC_CONFIG 15102 #endif #ifndef ERROR_MUI_INVALID_LOCALE_NAME # define ERROR_MUI_INVALID_LOCALE_NAME 15103 #endif #ifndef ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME # define ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME 15104 #endif #ifndef ERROR_MUI_FILE_NOT_LOADED # define ERROR_MUI_FILE_NOT_LOADED 15105 #endif typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx) (HANDLE CompletionPort, LPOVERLAPPED_ENTRY lpCompletionPortEntries, ULONG ulCount, PULONG ulNumEntriesRemoved, DWORD dwMilliseconds, BOOL fAlertable); typedef BOOL (WINAPI* sSetFileCompletionNotificationModes) (HANDLE FileHandle, UCHAR Flags); typedef BOOLEAN (WINAPI* sCreateSymbolicLinkW) (LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags); typedef BOOL (WINAPI* sCancelIoEx) (HANDLE hFile, LPOVERLAPPED lpOverlapped); typedef VOID (WINAPI* sInitializeConditionVariable) (PCONDITION_VARIABLE ConditionVariable); typedef BOOL (WINAPI* sSleepConditionVariableCS) (PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds); typedef BOOL (WINAPI* sSleepConditionVariableSRW) (PCONDITION_VARIABLE ConditionVariable, PSRWLOCK SRWLock, DWORD dwMilliseconds, ULONG Flags); typedef VOID (WINAPI* sWakeAllConditionVariable) (PCONDITION_VARIABLE ConditionVariable); typedef VOID (WINAPI* sWakeConditionVariable) (PCONDITION_VARIABLE ConditionVariable); typedef BOOL (WINAPI* sCancelSynchronousIo) (HANDLE hThread); typedef DWORD (WINAPI* sGetFinalPathNameByHandleW) (HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); /* Ntdll function pointers */ extern sRtlNtStatusToDosError pRtlNtStatusToDosError; extern sNtDeviceIoControlFile pNtDeviceIoControlFile; extern sNtQueryInformationFile pNtQueryInformationFile; extern sNtSetInformationFile pNtSetInformationFile; extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; extern sNtQueryDirectoryFile pNtQueryDirectoryFile; extern sNtQuerySystemInformation pNtQuerySystemInformation; /* Kernel32 function pointers */ extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; extern sCreateSymbolicLinkW pCreateSymbolicLinkW; extern sCancelIoEx pCancelIoEx; extern sInitializeConditionVariable pInitializeConditionVariable; extern sSleepConditionVariableCS pSleepConditionVariableCS; extern sSleepConditionVariableSRW pSleepConditionVariableSRW; extern sWakeAllConditionVariable pWakeAllConditionVariable; extern sWakeConditionVariable pWakeConditionVariable; extern sCancelSynchronousIo pCancelSynchronousIo; extern sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW; #endif /* UV_WIN_WINAPI_H_ */ libuv-v1.8.0/src/win/winsock.c000066400000000000000000000361221263361716600162530ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "uv.h" #include "internal.h" /* Whether there are any non-IFS LSPs stacked on TCP */ int uv_tcp_non_ifs_lsp_ipv4; int uv_tcp_non_ifs_lsp_ipv6; /* Ip address used to bind to any port at any interface */ struct sockaddr_in uv_addr_ip4_any_; struct sockaddr_in6 uv_addr_ip6_any_; /* * Retrieves the pointer to a winsock extension function. */ static BOOL uv_get_extension_function(SOCKET socket, GUID guid, void **target) { int result; DWORD bytes; result = WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), (void*)target, sizeof(*target), &bytes, NULL, NULL); if (result == SOCKET_ERROR) { *target = NULL; return FALSE; } else { return TRUE; } } BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) { const GUID wsaid_acceptex = WSAID_ACCEPTEX; return uv_get_extension_function(socket, wsaid_acceptex, (void**)target); } BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) { const GUID wsaid_connectex = WSAID_CONNECTEX; return uv_get_extension_function(socket, wsaid_connectex, (void**)target); } static int error_means_no_support(DWORD error) { return error == WSAEPROTONOSUPPORT || error == WSAESOCKTNOSUPPORT || error == WSAEPFNOSUPPORT || error == WSAEAFNOSUPPORT; } void uv_winsock_init() { WSADATA wsa_data; int errorno; SOCKET dummy; WSAPROTOCOL_INFOW protocol_info; int opt_len; /* Initialize winsock */ errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); if (errorno != 0) { uv_fatal_error(errorno, "WSAStartup"); } /* Set implicit binding address used by connectEx */ if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) { abort(); } if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) { abort(); } /* Detect non-IFS LSPs */ dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (dummy != INVALID_SOCKET) { opt_len = (int) sizeof protocol_info; if (getsockopt(dummy, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, &opt_len) == SOCKET_ERROR) uv_fatal_error(WSAGetLastError(), "getsockopt"); if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)) uv_tcp_non_ifs_lsp_ipv4 = 1; if (closesocket(dummy) == SOCKET_ERROR) uv_fatal_error(WSAGetLastError(), "closesocket"); } else if (!error_means_no_support(WSAGetLastError())) { /* Any error other than "socket type not supported" is fatal. */ uv_fatal_error(WSAGetLastError(), "socket"); } /* Detect IPV6 support and non-IFS LSPs */ dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); if (dummy != INVALID_SOCKET) { opt_len = (int) sizeof protocol_info; if (getsockopt(dummy, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, &opt_len) == SOCKET_ERROR) uv_fatal_error(WSAGetLastError(), "getsockopt"); if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)) uv_tcp_non_ifs_lsp_ipv6 = 1; if (closesocket(dummy) == SOCKET_ERROR) uv_fatal_error(WSAGetLastError(), "closesocket"); } else if (!error_means_no_support(WSAGetLastError())) { /* Any error other than "socket type not supported" is fatal. */ uv_fatal_error(WSAGetLastError(), "socket"); } } int uv_ntstatus_to_winsock_error(NTSTATUS status) { switch (status) { case STATUS_SUCCESS: return ERROR_SUCCESS; case STATUS_PENDING: return ERROR_IO_PENDING; case STATUS_INVALID_HANDLE: case STATUS_OBJECT_TYPE_MISMATCH: return WSAENOTSOCK; case STATUS_INSUFFICIENT_RESOURCES: case STATUS_PAGEFILE_QUOTA: case STATUS_COMMITMENT_LIMIT: case STATUS_WORKING_SET_QUOTA: case STATUS_NO_MEMORY: case STATUS_QUOTA_EXCEEDED: case STATUS_TOO_MANY_PAGING_FILES: case STATUS_REMOTE_RESOURCES: return WSAENOBUFS; case STATUS_TOO_MANY_ADDRESSES: case STATUS_SHARING_VIOLATION: case STATUS_ADDRESS_ALREADY_EXISTS: return WSAEADDRINUSE; case STATUS_LINK_TIMEOUT: case STATUS_IO_TIMEOUT: case STATUS_TIMEOUT: return WSAETIMEDOUT; case STATUS_GRACEFUL_DISCONNECT: return WSAEDISCON; case STATUS_REMOTE_DISCONNECT: case STATUS_CONNECTION_RESET: case STATUS_LINK_FAILED: case STATUS_CONNECTION_DISCONNECTED: case STATUS_PORT_UNREACHABLE: case STATUS_HOPLIMIT_EXCEEDED: return WSAECONNRESET; case STATUS_LOCAL_DISCONNECT: case STATUS_TRANSACTION_ABORTED: case STATUS_CONNECTION_ABORTED: return WSAECONNABORTED; case STATUS_BAD_NETWORK_PATH: case STATUS_NETWORK_UNREACHABLE: case STATUS_PROTOCOL_UNREACHABLE: return WSAENETUNREACH; case STATUS_HOST_UNREACHABLE: return WSAEHOSTUNREACH; case STATUS_CANCELLED: case STATUS_REQUEST_ABORTED: return WSAEINTR; case STATUS_BUFFER_OVERFLOW: case STATUS_INVALID_BUFFER_SIZE: return WSAEMSGSIZE; case STATUS_BUFFER_TOO_SMALL: case STATUS_ACCESS_VIOLATION: return WSAEFAULT; case STATUS_DEVICE_NOT_READY: case STATUS_REQUEST_NOT_ACCEPTED: return WSAEWOULDBLOCK; case STATUS_INVALID_NETWORK_RESPONSE: case STATUS_NETWORK_BUSY: case STATUS_NO_SUCH_DEVICE: case STATUS_NO_SUCH_FILE: case STATUS_OBJECT_PATH_NOT_FOUND: case STATUS_OBJECT_NAME_NOT_FOUND: case STATUS_UNEXPECTED_NETWORK_ERROR: return WSAENETDOWN; case STATUS_INVALID_CONNECTION: return WSAENOTCONN; case STATUS_REMOTE_NOT_LISTENING: case STATUS_CONNECTION_REFUSED: return WSAECONNREFUSED; case STATUS_PIPE_DISCONNECTED: return WSAESHUTDOWN; case STATUS_CONFLICTING_ADDRESSES: case STATUS_INVALID_ADDRESS: case STATUS_INVALID_ADDRESS_COMPONENT: return WSAEADDRNOTAVAIL; case STATUS_NOT_SUPPORTED: case STATUS_NOT_IMPLEMENTED: return WSAEOPNOTSUPP; case STATUS_ACCESS_DENIED: return WSAEACCES; default: if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) && (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) { /* It's a windows error that has been previously mapped to an */ /* ntstatus code. */ return (DWORD) (status & 0xffff); } else { /* The default fallback for unmappable ntstatus codes. */ return WSAEINVAL; } } } /* * This function provides a workaround for a bug in the winsock implementation * of WSARecv. The problem is that when SetFileCompletionNotificationModes is * used to avoid IOCP notifications of completed reads, WSARecv does not * reliably indicate whether we can expect a completion package to be posted * when the receive buffer is smaller than the received datagram. * * However it is desirable to use SetFileCompletionNotificationModes because * it yields a massive performance increase. * * This function provides a workaround for that bug, but it only works for the * specific case that we need it for. E.g. it assumes that the "avoid iocp" * bit has been set, and supports only overlapped operation. It also requires * the user to use the default msafd driver, doesn't work when other LSPs are * stacked on top of it. */ int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) { NTSTATUS status; void* apc_context; IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal; AFD_RECV_INFO info; DWORD error; if (overlapped == NULL || completion_routine != NULL) { WSASetLastError(WSAEINVAL); return SOCKET_ERROR; } info.BufferArray = buffers; info.BufferCount = buffer_count; info.AfdFlags = AFD_OVERLAPPED; info.TdiFlags = TDI_RECEIVE_NORMAL; if (*flags & MSG_PEEK) { info.TdiFlags |= TDI_RECEIVE_PEEK; } if (*flags & MSG_PARTIAL) { info.TdiFlags |= TDI_RECEIVE_PARTIAL; } if (!((intptr_t) overlapped->hEvent & 1)) { apc_context = (void*) overlapped; } else { apc_context = NULL; } iosb->Status = STATUS_PENDING; iosb->Pointer = 0; status = pNtDeviceIoControlFile((HANDLE) socket, overlapped->hEvent, NULL, apc_context, iosb, IOCTL_AFD_RECEIVE, &info, sizeof(info), NULL, 0); *flags = 0; *bytes = (DWORD) iosb->Information; switch (status) { case STATUS_SUCCESS: error = ERROR_SUCCESS; break; case STATUS_PENDING: error = WSA_IO_PENDING; break; case STATUS_BUFFER_OVERFLOW: error = WSAEMSGSIZE; break; case STATUS_RECEIVE_EXPEDITED: error = ERROR_SUCCESS; *flags = MSG_OOB; break; case STATUS_RECEIVE_PARTIAL_EXPEDITED: error = ERROR_SUCCESS; *flags = MSG_PARTIAL | MSG_OOB; break; case STATUS_RECEIVE_PARTIAL: error = ERROR_SUCCESS; *flags = MSG_PARTIAL; break; default: error = uv_ntstatus_to_winsock_error(status); break; } WSASetLastError(error); if (error == ERROR_SUCCESS) { return 0; } else { return SOCKET_ERROR; } } /* See description of uv_wsarecv_workaround. */ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr, int* addr_len, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) { NTSTATUS status; void* apc_context; IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal; AFD_RECV_DATAGRAM_INFO info; DWORD error; if (overlapped == NULL || addr == NULL || addr_len == NULL || completion_routine != NULL) { WSASetLastError(WSAEINVAL); return SOCKET_ERROR; } info.BufferArray = buffers; info.BufferCount = buffer_count; info.AfdFlags = AFD_OVERLAPPED; info.TdiFlags = TDI_RECEIVE_NORMAL; info.Address = addr; info.AddressLength = addr_len; if (*flags & MSG_PEEK) { info.TdiFlags |= TDI_RECEIVE_PEEK; } if (*flags & MSG_PARTIAL) { info.TdiFlags |= TDI_RECEIVE_PARTIAL; } if (!((intptr_t) overlapped->hEvent & 1)) { apc_context = (void*) overlapped; } else { apc_context = NULL; } iosb->Status = STATUS_PENDING; iosb->Pointer = 0; status = pNtDeviceIoControlFile((HANDLE) socket, overlapped->hEvent, NULL, apc_context, iosb, IOCTL_AFD_RECEIVE_DATAGRAM, &info, sizeof(info), NULL, 0); *flags = 0; *bytes = (DWORD) iosb->Information; switch (status) { case STATUS_SUCCESS: error = ERROR_SUCCESS; break; case STATUS_PENDING: error = WSA_IO_PENDING; break; case STATUS_BUFFER_OVERFLOW: error = WSAEMSGSIZE; break; case STATUS_RECEIVE_EXPEDITED: error = ERROR_SUCCESS; *flags = MSG_OOB; break; case STATUS_RECEIVE_PARTIAL_EXPEDITED: error = ERROR_SUCCESS; *flags = MSG_PARTIAL | MSG_OOB; break; case STATUS_RECEIVE_PARTIAL: error = ERROR_SUCCESS; *flags = MSG_PARTIAL; break; default: error = uv_ntstatus_to_winsock_error(status); break; } WSASetLastError(error); if (error == ERROR_SUCCESS) { return 0; } else { return SOCKET_ERROR; } } int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) { IO_STATUS_BLOCK iosb; IO_STATUS_BLOCK* iosb_ptr; HANDLE event = NULL; void* apc_context; NTSTATUS status; DWORD error; if (overlapped != NULL) { /* Overlapped operation. */ iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal; event = overlapped->hEvent; /* Do not report iocp completion if hEvent is tagged. */ if ((uintptr_t) event & 1) { event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1); apc_context = NULL; } else { apc_context = overlapped; } } else { /* Blocking operation. */ iosb_ptr = &iosb; event = CreateEvent(NULL, FALSE, FALSE, NULL); if (event == NULL) { return SOCKET_ERROR; } apc_context = NULL; } iosb_ptr->Status = STATUS_PENDING; status = pNtDeviceIoControlFile((HANDLE) socket, event, NULL, apc_context, iosb_ptr, IOCTL_AFD_POLL, info_in, sizeof *info_in, info_out, sizeof *info_out); if (overlapped == NULL) { /* If this is a blocking operation, wait for the event to become */ /* signaled, and then grab the real status from the io status block. */ if (status == STATUS_PENDING) { DWORD r = WaitForSingleObject(event, INFINITE); if (r == WAIT_FAILED) { DWORD saved_error = GetLastError(); CloseHandle(event); WSASetLastError(saved_error); return SOCKET_ERROR; } status = iosb.Status; } CloseHandle(event); } switch (status) { case STATUS_SUCCESS: error = ERROR_SUCCESS; break; case STATUS_PENDING: error = WSA_IO_PENDING; break; default: error = uv_ntstatus_to_winsock_error(status); break; } WSASetLastError(error); if (error == ERROR_SUCCESS) { return 0; } else { return SOCKET_ERROR; } } libuv-v1.8.0/src/win/winsock.h000066400000000000000000000142001263361716600162510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef UV_WIN_WINSOCK_H_ #define UV_WIN_WINSOCK_H_ #include #include #include #include #include #include "winapi.h" /* * MinGW is missing these too */ #ifndef SO_UPDATE_CONNECT_CONTEXT # define SO_UPDATE_CONNECT_CONTEXT 0x7010 #endif #ifndef TCP_KEEPALIVE # define TCP_KEEPALIVE 3 #endif #ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 #endif #ifndef IPV6_HOPLIMIT # define IPV6_HOPLIMIT 21 #endif #ifndef SIO_BASE_HANDLE # define SIO_BASE_HANDLE 0x48000022 #endif /* * TDI defines that are only in the DDK. * We only need receive flags so far. */ #ifndef TDI_RECEIVE_NORMAL #define TDI_RECEIVE_BROADCAST 0x00000004 #define TDI_RECEIVE_MULTICAST 0x00000008 #define TDI_RECEIVE_PARTIAL 0x00000010 #define TDI_RECEIVE_NORMAL 0x00000020 #define TDI_RECEIVE_EXPEDITED 0x00000040 #define TDI_RECEIVE_PEEK 0x00000080 #define TDI_RECEIVE_NO_RESPONSE_EXP 0x00000100 #define TDI_RECEIVE_COPY_LOOKAHEAD 0x00000200 #define TDI_RECEIVE_ENTIRE_MESSAGE 0x00000400 #define TDI_RECEIVE_AT_DISPATCH_LEVEL 0x00000800 #define TDI_RECEIVE_CONTROL_INFO 0x00001000 #define TDI_RECEIVE_FORCE_INDICATION 0x00002000 #define TDI_RECEIVE_NO_PUSH 0x00004000 #endif /* * The "Auxiliary Function Driver" is the windows kernel-mode driver that does * TCP, UDP etc. Winsock is just a layer that dispatches requests to it. * Having these definitions allows us to bypass winsock and make an AFD kernel * call directly, avoiding a bug in winsock's recvfrom implementation. */ #define AFD_NO_FAST_IO 0x00000001 #define AFD_OVERLAPPED 0x00000002 #define AFD_IMMEDIATE 0x00000004 #define AFD_POLL_RECEIVE_BIT 0 #define AFD_POLL_RECEIVE (1 << AFD_POLL_RECEIVE_BIT) #define AFD_POLL_RECEIVE_EXPEDITED_BIT 1 #define AFD_POLL_RECEIVE_EXPEDITED (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT) #define AFD_POLL_SEND_BIT 2 #define AFD_POLL_SEND (1 << AFD_POLL_SEND_BIT) #define AFD_POLL_DISCONNECT_BIT 3 #define AFD_POLL_DISCONNECT (1 << AFD_POLL_DISCONNECT_BIT) #define AFD_POLL_ABORT_BIT 4 #define AFD_POLL_ABORT (1 << AFD_POLL_ABORT_BIT) #define AFD_POLL_LOCAL_CLOSE_BIT 5 #define AFD_POLL_LOCAL_CLOSE (1 << AFD_POLL_LOCAL_CLOSE_BIT) #define AFD_POLL_CONNECT_BIT 6 #define AFD_POLL_CONNECT (1 << AFD_POLL_CONNECT_BIT) #define AFD_POLL_ACCEPT_BIT 7 #define AFD_POLL_ACCEPT (1 << AFD_POLL_ACCEPT_BIT) #define AFD_POLL_CONNECT_FAIL_BIT 8 #define AFD_POLL_CONNECT_FAIL (1 << AFD_POLL_CONNECT_FAIL_BIT) #define AFD_POLL_QOS_BIT 9 #define AFD_POLL_QOS (1 << AFD_POLL_QOS_BIT) #define AFD_POLL_GROUP_QOS_BIT 10 #define AFD_POLL_GROUP_QOS (1 << AFD_POLL_GROUP_QOS_BIT) #define AFD_NUM_POLL_EVENTS 11 #define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1) typedef struct _AFD_RECV_DATAGRAM_INFO { LPWSABUF BufferArray; ULONG BufferCount; ULONG AfdFlags; ULONG TdiFlags; struct sockaddr* Address; int* AddressLength; } AFD_RECV_DATAGRAM_INFO, *PAFD_RECV_DATAGRAM_INFO; typedef struct _AFD_RECV_INFO { LPWSABUF BufferArray; ULONG BufferCount; ULONG AfdFlags; ULONG TdiFlags; } AFD_RECV_INFO, *PAFD_RECV_INFO; #define _AFD_CONTROL_CODE(operation, method) \ ((FSCTL_AFD_BASE) << 12 | (operation << 2) | method) #define FSCTL_AFD_BASE FILE_DEVICE_NETWORK #define AFD_RECEIVE 5 #define AFD_RECEIVE_DATAGRAM 6 #define AFD_POLL 9 #define IOCTL_AFD_RECEIVE \ _AFD_CONTROL_CODE(AFD_RECEIVE, METHOD_NEITHER) #define IOCTL_AFD_RECEIVE_DATAGRAM \ _AFD_CONTROL_CODE(AFD_RECEIVE_DATAGRAM, METHOD_NEITHER) #define IOCTL_AFD_POLL \ _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED) #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP { /* FIXME: __C89_NAMELESS was removed */ /* __C89_NAMELESS */ union { ULONGLONG Alignment; /* __C89_NAMELESS */ struct { ULONG Length; DWORD Flags; }; }; struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next; SOCKET_ADDRESS Address; IP_PREFIX_ORIGIN PrefixOrigin; IP_SUFFIX_ORIGIN SuffixOrigin; IP_DAD_STATE DadState; ULONG ValidLifetime; ULONG PreferredLifetime; ULONG LeaseLifetime; } IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP; typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH { union { ULONGLONG Alignment; struct { ULONG Length; DWORD Flags; }; }; struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next; SOCKET_ADDRESS Address; IP_PREFIX_ORIGIN PrefixOrigin; IP_SUFFIX_ORIGIN SuffixOrigin; IP_DAD_STATE DadState; ULONG ValidLifetime; ULONG PreferredLifetime; ULONG LeaseLifetime; UINT8 OnLinkPrefixLength; } IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH; #endif #endif /* UV_WIN_WINSOCK_H_ */ libuv-v1.8.0/test/000077500000000000000000000000001263361716600140215ustar00rootroot00000000000000libuv-v1.8.0/test/benchmark-async-pummel.c000066400000000000000000000063151263361716600205340ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #include #include #define NUM_PINGS (1000 * 1000) #define ACCESS_ONCE(type, var) (*(volatile type*) &(var)) static unsigned int callbacks; static volatile int done; static const char running[] = "running"; static const char stop[] = "stop"; static const char stopped[] = "stopped"; static void async_cb(uv_async_t* handle) { if (++callbacks == NUM_PINGS) { /* Tell the pummel thread to stop. */ ACCESS_ONCE(const char*, handle->data) = stop; /* Wait for for the pummel thread to acknowledge that it has stoppped. */ while (ACCESS_ONCE(const char*, handle->data) != stopped) uv_sleep(0); uv_close((uv_handle_t*) handle, NULL); } } static void pummel(void* arg) { uv_async_t* handle = (uv_async_t*) arg; while (ACCESS_ONCE(const char*, handle->data) == running) uv_async_send(handle); /* Acknowledge that we've seen handle->data change. */ ACCESS_ONCE(const char*, handle->data) = stopped; } static int test_async_pummel(int nthreads) { uv_thread_t* tids; uv_async_t handle; uint64_t time; int i; tids = calloc(nthreads, sizeof(tids[0])); ASSERT(tids != NULL); ASSERT(0 == uv_async_init(uv_default_loop(), &handle, async_cb)); ACCESS_ONCE(const char*, handle.data) = running; for (i = 0; i < nthreads; i++) ASSERT(0 == uv_thread_create(tids + i, pummel, &handle)); time = uv_hrtime(); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); time = uv_hrtime() - time; done = 1; for (i = 0; i < nthreads; i++) ASSERT(0 == uv_thread_join(tids + i)); printf("async_pummel_%d: %s callbacks in %.2f seconds (%s/sec)\n", nthreads, fmt(callbacks), time / 1e9, fmt(callbacks / (time / 1e9))); free(tids); MAKE_VALGRIND_HAPPY(); return 0; } BENCHMARK_IMPL(async_pummel_1) { return test_async_pummel(1); } BENCHMARK_IMPL(async_pummel_2) { return test_async_pummel(2); } BENCHMARK_IMPL(async_pummel_4) { return test_async_pummel(4); } BENCHMARK_IMPL(async_pummel_8) { return test_async_pummel(8); } libuv-v1.8.0/test/benchmark-async.c000066400000000000000000000073451263361716600172430ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #include #include #define NUM_PINGS (1000 * 1000) struct ctx { uv_loop_t loop; uv_thread_t thread; uv_async_t main_async; /* wake up main thread */ uv_async_t worker_async; /* wake up worker */ unsigned int nthreads; unsigned int main_sent; unsigned int main_seen; unsigned int worker_sent; unsigned int worker_seen; }; static void worker_async_cb(uv_async_t* handle) { struct ctx* ctx = container_of(handle, struct ctx, worker_async); ASSERT(0 == uv_async_send(&ctx->main_async)); ctx->worker_sent++; ctx->worker_seen++; if (ctx->worker_sent >= NUM_PINGS) uv_close((uv_handle_t*) &ctx->worker_async, NULL); } static void main_async_cb(uv_async_t* handle) { struct ctx* ctx = container_of(handle, struct ctx, main_async); ASSERT(0 == uv_async_send(&ctx->worker_async)); ctx->main_sent++; ctx->main_seen++; if (ctx->main_sent >= NUM_PINGS) uv_close((uv_handle_t*) &ctx->main_async, NULL); } static void worker(void* arg) { struct ctx* ctx = arg; ASSERT(0 == uv_async_send(&ctx->main_async)); ASSERT(0 == uv_run(&ctx->loop, UV_RUN_DEFAULT)); uv_loop_close(&ctx->loop); } static int test_async(int nthreads) { struct ctx* threads; struct ctx* ctx; uint64_t time; int i; threads = calloc(nthreads, sizeof(threads[0])); ASSERT(threads != NULL); for (i = 0; i < nthreads; i++) { ctx = threads + i; ctx->nthreads = nthreads; ASSERT(0 == uv_loop_init(&ctx->loop)); ASSERT(0 == uv_async_init(&ctx->loop, &ctx->worker_async, worker_async_cb)); ASSERT(0 == uv_async_init(uv_default_loop(), &ctx->main_async, main_async_cb)); ASSERT(0 == uv_thread_create(&ctx->thread, worker, ctx)); } time = uv_hrtime(); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); for (i = 0; i < nthreads; i++) ASSERT(0 == uv_thread_join(&threads[i].thread)); time = uv_hrtime() - time; for (i = 0; i < nthreads; i++) { ctx = threads + i; ASSERT(ctx->worker_sent == NUM_PINGS); ASSERT(ctx->worker_seen == NUM_PINGS); ASSERT(ctx->main_sent == (unsigned int) NUM_PINGS); ASSERT(ctx->main_seen == (unsigned int) NUM_PINGS); } printf("async%d: %.2f sec (%s/sec)\n", nthreads, time / 1e9, fmt(NUM_PINGS / (time / 1e9))); free(threads); MAKE_VALGRIND_HAPPY(); return 0; } BENCHMARK_IMPL(async1) { return test_async(1); } BENCHMARK_IMPL(async2) { return test_async(2); } BENCHMARK_IMPL(async4) { return test_async(4); } BENCHMARK_IMPL(async8) { return test_async(8); } libuv-v1.8.0/test/benchmark-fs-stat.c000066400000000000000000000100111263361716600174670ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #include #include #define NUM_SYNC_REQS (10 * 1e5) #define NUM_ASYNC_REQS (1 * (int) 1e5) #define MAX_CONCURRENT_REQS 32 #define sync_stat(req, path) \ do { \ uv_fs_stat(NULL, (req), (path), NULL); \ uv_fs_req_cleanup((req)); \ } \ while (0) struct async_req { const char* path; uv_fs_t fs_req; int* count; }; static void warmup(const char* path) { uv_fs_t reqs[MAX_CONCURRENT_REQS]; unsigned int i; /* warm up the thread pool */ for (i = 0; i < ARRAY_SIZE(reqs); i++) uv_fs_stat(uv_default_loop(), reqs + i, path, uv_fs_req_cleanup); uv_run(uv_default_loop(), UV_RUN_DEFAULT); /* warm up the OS dirent cache */ for (i = 0; i < 16; i++) sync_stat(reqs + 0, path); } static void sync_bench(const char* path) { uint64_t before; uint64_t after; uv_fs_t req; int i; /* do the sync benchmark */ before = uv_hrtime(); for (i = 0; i < NUM_SYNC_REQS; i++) sync_stat(&req, path); after = uv_hrtime(); printf("%s stats (sync): %.2fs (%s/s)\n", fmt(1.0 * NUM_SYNC_REQS), (after - before) / 1e9, fmt((1.0 * NUM_SYNC_REQS) / ((after - before) / 1e9))); fflush(stdout); } static void stat_cb(uv_fs_t* fs_req) { struct async_req* req = container_of(fs_req, struct async_req, fs_req); uv_fs_req_cleanup(&req->fs_req); if (*req->count == 0) return; uv_fs_stat(uv_default_loop(), &req->fs_req, req->path, stat_cb); (*req->count)--; } static void async_bench(const char* path) { struct async_req reqs[MAX_CONCURRENT_REQS]; struct async_req* req; uint64_t before; uint64_t after; int count; int i; for (i = 1; i <= MAX_CONCURRENT_REQS; i++) { count = NUM_ASYNC_REQS; for (req = reqs; req < reqs + i; req++) { req->path = path; req->count = &count; uv_fs_stat(uv_default_loop(), &req->fs_req, req->path, stat_cb); } before = uv_hrtime(); uv_run(uv_default_loop(), UV_RUN_DEFAULT); after = uv_hrtime(); printf("%s stats (%d concurrent): %.2fs (%s/s)\n", fmt(1.0 * NUM_ASYNC_REQS), i, (after - before) / 1e9, fmt((1.0 * NUM_ASYNC_REQS) / ((after - before) / 1e9))); fflush(stdout); } } /* This benchmark aims to measure the overhead of doing I/O syscalls from * the thread pool. The stat() syscall was chosen because its results are * easy for the operating system to cache, taking the actual I/O overhead * out of the equation. */ BENCHMARK_IMPL(fs_stat) { const char path[] = "."; warmup(path); sync_bench(path); async_bench(path); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-getaddrinfo.c000066400000000000000000000047761263361716600204210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #define CONCURRENT_CALLS 10 #define TOTAL_CALLS 10000 static const char* name = "localhost"; static uv_loop_t* loop; static uv_getaddrinfo_t handles[CONCURRENT_CALLS]; static int calls_initiated = 0; static int calls_completed = 0; static int64_t start_time; static int64_t end_time; static void getaddrinfo_initiate(uv_getaddrinfo_t* handle); static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { ASSERT(status == 0); calls_completed++; if (calls_initiated < TOTAL_CALLS) { getaddrinfo_initiate(handle); } uv_freeaddrinfo(res); } static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { int r; calls_initiated++; r = uv_getaddrinfo(loop, handle, &getaddrinfo_cb, name, NULL, NULL); ASSERT(r == 0); } BENCHMARK_IMPL(getaddrinfo) { int i; loop = uv_default_loop(); uv_update_time(loop); start_time = uv_now(loop); for (i = 0; i < CONCURRENT_CALLS; i++) { getaddrinfo_initiate(&handles[i]); } uv_run(loop, UV_RUN_DEFAULT); uv_update_time(loop); end_time = uv_now(loop); ASSERT(calls_initiated == TOTAL_CALLS); ASSERT(calls_completed == TOTAL_CALLS); fprintf(stderr, "getaddrinfo: %.0f req/s\n", (double) calls_completed / (double) (end_time - start_time) * 1000.0); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-list.h000066400000000000000000000132251263361716600171000ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ BENCHMARK_DECLARE (sizes) BENCHMARK_DECLARE (loop_count) BENCHMARK_DECLARE (loop_count_timed) BENCHMARK_DECLARE (ping_pongs) BENCHMARK_DECLARE (tcp_write_batch) BENCHMARK_DECLARE (tcp4_pound_100) BENCHMARK_DECLARE (tcp4_pound_1000) BENCHMARK_DECLARE (pipe_pound_100) BENCHMARK_DECLARE (pipe_pound_1000) BENCHMARK_DECLARE (tcp_pump100_client) BENCHMARK_DECLARE (tcp_pump1_client) BENCHMARK_DECLARE (pipe_pump100_client) BENCHMARK_DECLARE (pipe_pump1_client) BENCHMARK_DECLARE (tcp_multi_accept2) BENCHMARK_DECLARE (tcp_multi_accept4) BENCHMARK_DECLARE (tcp_multi_accept8) /* Run until X packets have been sent/received. */ BENCHMARK_DECLARE (udp_pummel_1v1) BENCHMARK_DECLARE (udp_pummel_1v10) BENCHMARK_DECLARE (udp_pummel_1v100) BENCHMARK_DECLARE (udp_pummel_1v1000) BENCHMARK_DECLARE (udp_pummel_10v10) BENCHMARK_DECLARE (udp_pummel_10v100) BENCHMARK_DECLARE (udp_pummel_10v1000) BENCHMARK_DECLARE (udp_pummel_100v100) BENCHMARK_DECLARE (udp_pummel_100v1000) BENCHMARK_DECLARE (udp_pummel_1000v1000) /* Run until X seconds have elapsed. */ BENCHMARK_DECLARE (udp_timed_pummel_1v1) BENCHMARK_DECLARE (udp_timed_pummel_1v10) BENCHMARK_DECLARE (udp_timed_pummel_1v100) BENCHMARK_DECLARE (udp_timed_pummel_1v1000) BENCHMARK_DECLARE (udp_timed_pummel_10v10) BENCHMARK_DECLARE (udp_timed_pummel_10v100) BENCHMARK_DECLARE (udp_timed_pummel_10v1000) BENCHMARK_DECLARE (udp_timed_pummel_100v100) BENCHMARK_DECLARE (udp_timed_pummel_100v1000) BENCHMARK_DECLARE (udp_timed_pummel_1000v1000) BENCHMARK_DECLARE (getaddrinfo) BENCHMARK_DECLARE (fs_stat) BENCHMARK_DECLARE (async1) BENCHMARK_DECLARE (async2) BENCHMARK_DECLARE (async4) BENCHMARK_DECLARE (async8) BENCHMARK_DECLARE (async_pummel_1) BENCHMARK_DECLARE (async_pummel_2) BENCHMARK_DECLARE (async_pummel_4) BENCHMARK_DECLARE (async_pummel_8) BENCHMARK_DECLARE (spawn) BENCHMARK_DECLARE (thread_create) BENCHMARK_DECLARE (million_async) BENCHMARK_DECLARE (million_timers) HELPER_DECLARE (tcp4_blackhole_server) HELPER_DECLARE (tcp_pump_server) HELPER_DECLARE (pipe_pump_server) HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (pipe_echo_server) HELPER_DECLARE (dns_server) TASK_LIST_START BENCHMARK_ENTRY (sizes) BENCHMARK_ENTRY (loop_count) BENCHMARK_ENTRY (loop_count_timed) BENCHMARK_ENTRY (ping_pongs) BENCHMARK_HELPER (ping_pongs, tcp4_echo_server) BENCHMARK_ENTRY (tcp_write_batch) BENCHMARK_HELPER (tcp_write_batch, tcp4_blackhole_server) BENCHMARK_ENTRY (tcp_pump100_client) BENCHMARK_HELPER (tcp_pump100_client, tcp_pump_server) BENCHMARK_ENTRY (tcp_pump1_client) BENCHMARK_HELPER (tcp_pump1_client, tcp_pump_server) BENCHMARK_ENTRY (tcp4_pound_100) BENCHMARK_HELPER (tcp4_pound_100, tcp4_echo_server) BENCHMARK_ENTRY (tcp4_pound_1000) BENCHMARK_HELPER (tcp4_pound_1000, tcp4_echo_server) BENCHMARK_ENTRY (pipe_pump100_client) BENCHMARK_HELPER (pipe_pump100_client, pipe_pump_server) BENCHMARK_ENTRY (pipe_pump1_client) BENCHMARK_HELPER (pipe_pump1_client, pipe_pump_server) BENCHMARK_ENTRY (pipe_pound_100) BENCHMARK_HELPER (pipe_pound_100, pipe_echo_server) BENCHMARK_ENTRY (pipe_pound_1000) BENCHMARK_HELPER (pipe_pound_1000, pipe_echo_server) BENCHMARK_ENTRY (tcp_multi_accept2) BENCHMARK_ENTRY (tcp_multi_accept4) BENCHMARK_ENTRY (tcp_multi_accept8) BENCHMARK_ENTRY (udp_pummel_1v1) BENCHMARK_ENTRY (udp_pummel_1v10) BENCHMARK_ENTRY (udp_pummel_1v100) BENCHMARK_ENTRY (udp_pummel_1v1000) BENCHMARK_ENTRY (udp_pummel_10v10) BENCHMARK_ENTRY (udp_pummel_10v100) BENCHMARK_ENTRY (udp_pummel_10v1000) BENCHMARK_ENTRY (udp_pummel_100v100) BENCHMARK_ENTRY (udp_pummel_100v1000) BENCHMARK_ENTRY (udp_pummel_1000v1000) BENCHMARK_ENTRY (udp_timed_pummel_1v1) BENCHMARK_ENTRY (udp_timed_pummel_1v10) BENCHMARK_ENTRY (udp_timed_pummel_1v100) BENCHMARK_ENTRY (udp_timed_pummel_1v1000) BENCHMARK_ENTRY (udp_timed_pummel_10v10) BENCHMARK_ENTRY (udp_timed_pummel_10v100) BENCHMARK_ENTRY (udp_timed_pummel_10v1000) BENCHMARK_ENTRY (udp_timed_pummel_100v100) BENCHMARK_ENTRY (udp_timed_pummel_100v1000) BENCHMARK_ENTRY (udp_timed_pummel_1000v1000) BENCHMARK_ENTRY (getaddrinfo) BENCHMARK_ENTRY (fs_stat) BENCHMARK_ENTRY (async1) BENCHMARK_ENTRY (async2) BENCHMARK_ENTRY (async4) BENCHMARK_ENTRY (async8) BENCHMARK_ENTRY (async_pummel_1) BENCHMARK_ENTRY (async_pummel_2) BENCHMARK_ENTRY (async_pummel_4) BENCHMARK_ENTRY (async_pummel_8) BENCHMARK_ENTRY (spawn) BENCHMARK_ENTRY (thread_create) BENCHMARK_ENTRY (million_async) BENCHMARK_ENTRY (million_timers) TASK_LIST_END libuv-v1.8.0/test/benchmark-loop-count.c000066400000000000000000000047151263361716600202230ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #include #include #define NUM_TICKS (2 * 1000 * 1000) static unsigned long ticks; static uv_idle_t idle_handle; static uv_timer_t timer_handle; static void idle_cb(uv_idle_t* handle) { if (++ticks == NUM_TICKS) uv_idle_stop(handle); } static void idle2_cb(uv_idle_t* handle) { ticks++; } static void timer_cb(uv_timer_t* handle) { uv_idle_stop(&idle_handle); uv_timer_stop(&timer_handle); } BENCHMARK_IMPL(loop_count) { uv_loop_t* loop = uv_default_loop(); uint64_t ns; uv_idle_init(loop, &idle_handle); uv_idle_start(&idle_handle, idle_cb); ns = uv_hrtime(); uv_run(loop, UV_RUN_DEFAULT); ns = uv_hrtime() - ns; ASSERT(ticks == NUM_TICKS); fprintf(stderr, "loop_count: %d ticks in %.2fs (%.0f/s)\n", NUM_TICKS, ns / 1e9, NUM_TICKS / (ns / 1e9)); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } BENCHMARK_IMPL(loop_count_timed) { uv_loop_t* loop = uv_default_loop(); uv_idle_init(loop, &idle_handle); uv_idle_start(&idle_handle, idle2_cb); uv_timer_init(loop, &timer_handle); uv_timer_start(&timer_handle, timer_cb, 5000, 0); uv_run(loop, UV_RUN_DEFAULT); fprintf(stderr, "loop_count: %lu ticks (%.0f ticks/s)\n", ticks, ticks / 5.0); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-million-async.c000066400000000000000000000062311263361716600206750ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" struct async_container { unsigned async_events; unsigned handles_seen; uv_async_t async_handles[1024 * 1024]; }; static volatile int done; static uv_thread_t thread_id; static struct async_container* container; static unsigned fastrand(void) { static unsigned g = 0; g = g * 214013 + 2531011; return g; } static void thread_cb(void* arg) { unsigned i; while (done == 0) { i = fastrand() % ARRAY_SIZE(container->async_handles); uv_async_send(container->async_handles + i); } } static void async_cb(uv_async_t* handle) { container->async_events++; handle->data = handle; } static void timer_cb(uv_timer_t* handle) { unsigned i; done = 1; ASSERT(0 == uv_thread_join(&thread_id)); for (i = 0; i < ARRAY_SIZE(container->async_handles); i++) { uv_async_t* handle = container->async_handles + i; if (handle->data != NULL) container->handles_seen++; uv_close((uv_handle_t*) handle, NULL); } uv_close((uv_handle_t*) handle, NULL); } BENCHMARK_IMPL(million_async) { uv_timer_t timer_handle; uv_async_t* handle; uv_loop_t* loop; int timeout; unsigned i; loop = uv_default_loop(); timeout = 5000; container = malloc(sizeof(*container)); ASSERT(container != NULL); container->async_events = 0; container->handles_seen = 0; for (i = 0; i < ARRAY_SIZE(container->async_handles); i++) { handle = container->async_handles + i; ASSERT(0 == uv_async_init(loop, handle, async_cb)); handle->data = NULL; } ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, timeout, 0)); ASSERT(0 == uv_thread_create(&thread_id, thread_cb, NULL)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); printf("%s async events in %.1f seconds (%s/s, %s unique handles seen)\n", fmt(container->async_events), timeout / 1000., fmt(container->async_events / (timeout / 1000.)), fmt(container->handles_seen)); free(container); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-million-timers.c000066400000000000000000000051711263361716600210650ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #define NUM_TIMERS (10 * 1000 * 1000) static int timer_cb_called; static int close_cb_called; static void timer_cb(uv_timer_t* handle) { timer_cb_called++; } static void close_cb(uv_handle_t* handle) { close_cb_called++; } BENCHMARK_IMPL(million_timers) { uv_timer_t* timers; uv_loop_t* loop; uint64_t before_all; uint64_t before_run; uint64_t after_run; uint64_t after_all; int timeout; int i; timers = malloc(NUM_TIMERS * sizeof(timers[0])); ASSERT(timers != NULL); loop = uv_default_loop(); timeout = 0; before_all = uv_hrtime(); for (i = 0; i < NUM_TIMERS; i++) { if (i % 1000 == 0) timeout++; ASSERT(0 == uv_timer_init(loop, timers + i)); ASSERT(0 == uv_timer_start(timers + i, timer_cb, timeout, 0)); } before_run = uv_hrtime(); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); after_run = uv_hrtime(); for (i = 0; i < NUM_TIMERS; i++) uv_close((uv_handle_t*) (timers + i), close_cb); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); after_all = uv_hrtime(); ASSERT(timer_cb_called == NUM_TIMERS); ASSERT(close_cb_called == NUM_TIMERS); free(timers); fprintf(stderr, "%.2f seconds total\n", (after_all - before_all) / 1e9); fprintf(stderr, "%.2f seconds init\n", (before_run - before_all) / 1e9); fprintf(stderr, "%.2f seconds dispatch\n", (after_run - before_run) / 1e9); fprintf(stderr, "%.2f seconds cleanup\n", (after_all - after_run) / 1e9); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-multi-accept.c000066400000000000000000000324071263361716600205120ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #define IPC_PIPE_NAME TEST_PIPENAME #define NUM_CONNECTS (250 * 1000) union stream_handle { uv_pipe_t pipe; uv_tcp_t tcp; }; /* Use as (uv_stream_t *) &handle_storage -- it's kind of clunky but it * avoids aliasing warnings. */ typedef unsigned char handle_storage_t[sizeof(union stream_handle)]; /* Used for passing around the listen handle, not part of the benchmark proper. * We have an overabundance of server types here. It works like this: * * 1. The main thread starts an IPC pipe server. * 2. The worker threads connect to the IPC server and obtain a listen handle. * 3. The worker threads start accepting requests on the listen handle. * 4. The main thread starts connecting repeatedly. * * Step #4 should perhaps be farmed out over several threads. */ struct ipc_server_ctx { handle_storage_t server_handle; unsigned int num_connects; uv_pipe_t ipc_pipe; }; struct ipc_peer_ctx { handle_storage_t peer_handle; uv_write_t write_req; }; struct ipc_client_ctx { uv_connect_t connect_req; uv_stream_t* server_handle; uv_pipe_t ipc_pipe; char scratch[16]; }; /* Used in the actual benchmark. */ struct server_ctx { handle_storage_t server_handle; unsigned int num_connects; uv_async_t async_handle; uv_thread_t thread_id; uv_sem_t semaphore; }; struct client_ctx { handle_storage_t client_handle; unsigned int num_connects; uv_connect_t connect_req; uv_idle_t idle_handle; }; static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status); static void ipc_write_cb(uv_write_t* req, int status); static void ipc_close_cb(uv_handle_t* handle); static void ipc_connect_cb(uv_connect_t* req, int status); static void ipc_read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf); static void ipc_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); static void sv_async_cb(uv_async_t* handle); static void sv_connection_cb(uv_stream_t* server_handle, int status); static void sv_read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf); static void sv_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); static void cl_connect_cb(uv_connect_t* req, int status); static void cl_idle_cb(uv_idle_t* handle); static void cl_close_cb(uv_handle_t* handle); static struct sockaddr_in listen_addr; static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status) { struct ipc_server_ctx* sc; struct ipc_peer_ctx* pc; uv_loop_t* loop; uv_buf_t buf; loop = ipc_pipe->loop; buf = uv_buf_init("PING", 4); sc = container_of(ipc_pipe, struct ipc_server_ctx, ipc_pipe); pc = calloc(1, sizeof(*pc)); ASSERT(pc != NULL); if (ipc_pipe->type == UV_TCP) ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle)); else if (ipc_pipe->type == UV_NAMED_PIPE) ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &pc->peer_handle, 1)); else ASSERT(0); ASSERT(0 == uv_accept(ipc_pipe, (uv_stream_t*) &pc->peer_handle)); ASSERT(0 == uv_write2(&pc->write_req, (uv_stream_t*) &pc->peer_handle, &buf, 1, (uv_stream_t*) &sc->server_handle, ipc_write_cb)); if (--sc->num_connects == 0) uv_close((uv_handle_t*) ipc_pipe, NULL); } static void ipc_write_cb(uv_write_t* req, int status) { struct ipc_peer_ctx* ctx; ctx = container_of(req, struct ipc_peer_ctx, write_req); uv_close((uv_handle_t*) &ctx->peer_handle, ipc_close_cb); } static void ipc_close_cb(uv_handle_t* handle) { struct ipc_peer_ctx* ctx; ctx = container_of(handle, struct ipc_peer_ctx, peer_handle); free(ctx); } static void ipc_connect_cb(uv_connect_t* req, int status) { struct ipc_client_ctx* ctx; ctx = container_of(req, struct ipc_client_ctx, connect_req); ASSERT(0 == status); ASSERT(0 == uv_read_start((uv_stream_t*) &ctx->ipc_pipe, ipc_alloc_cb, ipc_read_cb)); } static void ipc_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { struct ipc_client_ctx* ctx; ctx = container_of(handle, struct ipc_client_ctx, ipc_pipe); buf->base = ctx->scratch; buf->len = sizeof(ctx->scratch); } static void ipc_read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { struct ipc_client_ctx* ctx; uv_loop_t* loop; uv_handle_type type; uv_pipe_t* ipc_pipe; ipc_pipe = (uv_pipe_t*) handle; ctx = container_of(ipc_pipe, struct ipc_client_ctx, ipc_pipe); loop = ipc_pipe->loop; ASSERT(1 == uv_pipe_pending_count(ipc_pipe)); type = uv_pipe_pending_type(ipc_pipe); if (type == UV_TCP) ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) ctx->server_handle)); else if (type == UV_NAMED_PIPE) ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) ctx->server_handle, 0)); else ASSERT(0); ASSERT(0 == uv_accept(handle, ctx->server_handle)); uv_close((uv_handle_t*) &ctx->ipc_pipe, NULL); } /* Set up an IPC pipe server that hands out listen sockets to the worker * threads. It's kind of cumbersome for such a simple operation, maybe we * should revive uv_import() and uv_export(). */ static void send_listen_handles(uv_handle_type type, unsigned int num_servers, struct server_ctx* servers) { struct ipc_server_ctx ctx; uv_loop_t* loop; unsigned int i; loop = uv_default_loop(); ctx.num_connects = num_servers; if (type == UV_TCP) { ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &ctx.server_handle)); ASSERT(0 == uv_tcp_bind((uv_tcp_t*) &ctx.server_handle, (const struct sockaddr*) &listen_addr, 0)); } else ASSERT(0); ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); ASSERT(0 == uv_pipe_bind(&ctx.ipc_pipe, IPC_PIPE_NAME)); ASSERT(0 == uv_listen((uv_stream_t*) &ctx.ipc_pipe, 128, ipc_connection_cb)); for (i = 0; i < num_servers; i++) uv_sem_post(&servers[i].semaphore); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); uv_close((uv_handle_t*) &ctx.server_handle, NULL); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); for (i = 0; i < num_servers; i++) uv_sem_wait(&servers[i].semaphore); } static void get_listen_handle(uv_loop_t* loop, uv_stream_t* server_handle) { struct ipc_client_ctx ctx; ctx.server_handle = server_handle; ctx.server_handle->data = "server handle"; ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); uv_pipe_connect(&ctx.connect_req, &ctx.ipc_pipe, IPC_PIPE_NAME, ipc_connect_cb); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); } static void server_cb(void *arg) { struct server_ctx *ctx; uv_loop_t loop; ctx = arg; ASSERT(0 == uv_loop_init(&loop)); ASSERT(0 == uv_async_init(&loop, &ctx->async_handle, sv_async_cb)); uv_unref((uv_handle_t*) &ctx->async_handle); /* Wait until the main thread is ready. */ uv_sem_wait(&ctx->semaphore); get_listen_handle(&loop, (uv_stream_t*) &ctx->server_handle); uv_sem_post(&ctx->semaphore); /* Now start the actual benchmark. */ ASSERT(0 == uv_listen((uv_stream_t*) &ctx->server_handle, 128, sv_connection_cb)); ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); uv_loop_close(&loop); } static void sv_async_cb(uv_async_t* handle) { struct server_ctx* ctx; ctx = container_of(handle, struct server_ctx, async_handle); uv_close((uv_handle_t*) &ctx->server_handle, NULL); uv_close((uv_handle_t*) &ctx->async_handle, NULL); } static void sv_connection_cb(uv_stream_t* server_handle, int status) { handle_storage_t* storage; struct server_ctx* ctx; ctx = container_of(server_handle, struct server_ctx, server_handle); ASSERT(status == 0); storage = malloc(sizeof(*storage)); ASSERT(storage != NULL); if (server_handle->type == UV_TCP) ASSERT(0 == uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage)); else if (server_handle->type == UV_NAMED_PIPE) ASSERT(0 == uv_pipe_init(server_handle->loop, (uv_pipe_t*) storage, 0)); else ASSERT(0); ASSERT(0 == uv_accept(server_handle, (uv_stream_t*) storage)); ASSERT(0 == uv_read_start((uv_stream_t*) storage, sv_alloc_cb, sv_read_cb)); ctx->num_connects++; } static void sv_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[32]; buf->base = slab; buf->len = sizeof(slab); } static void sv_read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { ASSERT(nread == UV_EOF); uv_close((uv_handle_t*) handle, (uv_close_cb) free); } static void cl_connect_cb(uv_connect_t* req, int status) { struct client_ctx* ctx = container_of(req, struct client_ctx, connect_req); uv_idle_start(&ctx->idle_handle, cl_idle_cb); ASSERT(0 == status); } static void cl_idle_cb(uv_idle_t* handle) { struct client_ctx* ctx = container_of(handle, struct client_ctx, idle_handle); uv_close((uv_handle_t*) &ctx->client_handle, cl_close_cb); uv_idle_stop(&ctx->idle_handle); } static void cl_close_cb(uv_handle_t* handle) { struct client_ctx* ctx; ctx = container_of(handle, struct client_ctx, client_handle); if (--ctx->num_connects == 0) { uv_close((uv_handle_t*) &ctx->idle_handle, NULL); return; } ASSERT(0 == uv_tcp_init(handle->loop, (uv_tcp_t*) &ctx->client_handle)); ASSERT(0 == uv_tcp_connect(&ctx->connect_req, (uv_tcp_t*) &ctx->client_handle, (const struct sockaddr*) &listen_addr, cl_connect_cb)); } static int test_tcp(unsigned int num_servers, unsigned int num_clients) { struct server_ctx* servers; struct client_ctx* clients; uv_loop_t* loop; uv_tcp_t* handle; unsigned int i; double time; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &listen_addr)); loop = uv_default_loop(); servers = calloc(num_servers, sizeof(servers[0])); clients = calloc(num_clients, sizeof(clients[0])); ASSERT(servers != NULL); ASSERT(clients != NULL); /* We're making the assumption here that from the perspective of the * OS scheduler, threads are functionally equivalent to and interchangeable * with full-blown processes. */ for (i = 0; i < num_servers; i++) { struct server_ctx* ctx = servers + i; ASSERT(0 == uv_sem_init(&ctx->semaphore, 0)); ASSERT(0 == uv_thread_create(&ctx->thread_id, server_cb, ctx)); } send_listen_handles(UV_TCP, num_servers, servers); for (i = 0; i < num_clients; i++) { struct client_ctx* ctx = clients + i; ctx->num_connects = NUM_CONNECTS / num_clients; handle = (uv_tcp_t*) &ctx->client_handle; handle->data = "client handle"; ASSERT(0 == uv_tcp_init(loop, handle)); ASSERT(0 == uv_tcp_connect(&ctx->connect_req, handle, (const struct sockaddr*) &listen_addr, cl_connect_cb)); ASSERT(0 == uv_idle_init(loop, &ctx->idle_handle)); } { uint64_t t = uv_hrtime(); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); t = uv_hrtime() - t; time = t / 1e9; } for (i = 0; i < num_servers; i++) { struct server_ctx* ctx = servers + i; uv_async_send(&ctx->async_handle); ASSERT(0 == uv_thread_join(&ctx->thread_id)); uv_sem_destroy(&ctx->semaphore); } printf("accept%u: %.0f accepts/sec (%u total)\n", num_servers, NUM_CONNECTS / time, NUM_CONNECTS); for (i = 0; i < num_servers; i++) { struct server_ctx* ctx = servers + i; printf(" thread #%u: %.0f accepts/sec (%u total, %.1f%%)\n", i, ctx->num_connects / time, ctx->num_connects, ctx->num_connects * 100.0 / NUM_CONNECTS); } free(clients); free(servers); MAKE_VALGRIND_HAPPY(); return 0; } BENCHMARK_IMPL(tcp_multi_accept2) { return test_tcp(2, 40); } BENCHMARK_IMPL(tcp_multi_accept4) { return test_tcp(4, 40); } BENCHMARK_IMPL(tcp_multi_accept8) { return test_tcp(8, 40); } libuv-v1.8.0/test/benchmark-ping-pongs.c000066400000000000000000000122051263361716600201760ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include /* Run the benchmark for this many ms */ #define TIME 5000 typedef struct { int pongs; int state; uv_tcp_t tcp; uv_connect_t connect_req; uv_shutdown_t shutdown_req; } pinger_t; typedef struct buf_s { uv_buf_t uv_buf_t; struct buf_s* next; } buf_t; static char PING[] = "PING\n"; static uv_loop_t* loop; static buf_t* buf_freelist = NULL; static int pinger_shutdown_cb_called; static int completed_pingers = 0; static int64_t start_time; static void buf_alloc(uv_handle_t* tcp, size_t size, uv_buf_t* buf) { buf_t* ab; ab = buf_freelist; if (ab != NULL) buf_freelist = ab->next; else { ab = malloc(size + sizeof(*ab)); ab->uv_buf_t.len = size; ab->uv_buf_t.base = (char*) (ab + 1); } *buf = ab->uv_buf_t; } static void buf_free(const uv_buf_t* buf) { buf_t* ab = (buf_t*) buf->base - 1; ab->next = buf_freelist; buf_freelist = ab; } static void pinger_close_cb(uv_handle_t* handle) { pinger_t* pinger; pinger = (pinger_t*)handle->data; fprintf(stderr, "ping_pongs: %d roundtrips/s\n", (1000 * pinger->pongs) / TIME); fflush(stderr); free(pinger); completed_pingers++; } static void pinger_write_cb(uv_write_t* req, int status) { ASSERT(status == 0); free(req); } static void pinger_write_ping(pinger_t* pinger) { uv_write_t* req; uv_buf_t buf; buf = uv_buf_init(PING, sizeof(PING) - 1); req = malloc(sizeof *req); if (uv_write(req, (uv_stream_t*) &pinger->tcp, &buf, 1, pinger_write_cb)) { FATAL("uv_write failed"); } } static void pinger_shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); pinger_shutdown_cb_called++; /* * The close callback has not been triggered yet. We must wait for EOF * until we close the connection. */ ASSERT(completed_pingers == 0); } static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ssize_t i; pinger_t* pinger; pinger = (pinger_t*)tcp->data; if (nread < 0) { ASSERT(nread == UV_EOF); if (buf->base) { buf_free(buf); } ASSERT(pinger_shutdown_cb_called == 1); uv_close((uv_handle_t*)tcp, pinger_close_cb); return; } /* Now we count the pings */ for (i = 0; i < nread; i++) { ASSERT(buf->base[i] == PING[pinger->state]); pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); if (pinger->state == 0) { pinger->pongs++; if (uv_now(loop) - start_time > TIME) { uv_shutdown(&pinger->shutdown_req, (uv_stream_t*) tcp, pinger_shutdown_cb); break; } else { pinger_write_ping(pinger); } } } buf_free(buf); } static void pinger_connect_cb(uv_connect_t* req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; ASSERT(status == 0); pinger_write_ping(pinger); if (uv_read_start(req->handle, buf_alloc, pinger_read_cb)) { FATAL("uv_read_start failed"); } } static void pinger_new(void) { struct sockaddr_in client_addr; struct sockaddr_in server_addr; pinger_t *pinger; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); pinger->state = 0; pinger->pongs = 0; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(loop, &pinger->tcp); ASSERT(!r); pinger->tcp.data = pinger; ASSERT(0 == uv_tcp_bind(&pinger->tcp, (const struct sockaddr*) &client_addr, 0)); r = uv_tcp_connect(&pinger->connect_req, &pinger->tcp, (const struct sockaddr*) &server_addr, pinger_connect_cb); ASSERT(!r); } BENCHMARK_IMPL(ping_pongs) { loop = uv_default_loop(); start_time = uv_now(loop); pinger_new(); uv_run(loop, UV_RUN_DEFAULT); ASSERT(completed_pingers == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-pound.c000066400000000000000000000174561263361716600172570ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" /* Update this is you're going to run > 1000 concurrent requests. */ #define MAX_CONNS 1000 #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) #undef DEBUG #define DEBUG 0 struct conn_rec_s; typedef void (*setup_fn)(int num, void* arg); typedef void (*make_connect_fn)(struct conn_rec_s* conn); typedef int (*connect_fn)(int num, make_connect_fn make_connect, void* arg); /* Base class for tcp_conn_rec and pipe_conn_rec. * The ordering of fields matters! */ typedef struct conn_rec_s { int i; uv_connect_t conn_req; uv_write_t write_req; make_connect_fn make_connect; uv_stream_t stream; } conn_rec; typedef struct { int i; uv_connect_t conn_req; uv_write_t write_req; make_connect_fn make_connect; uv_tcp_t stream; } tcp_conn_rec; typedef struct { int i; uv_connect_t conn_req; uv_write_t write_req; make_connect_fn make_connect; uv_pipe_t stream; } pipe_conn_rec; static char buffer[] = "QS"; static uv_loop_t* loop; static tcp_conn_rec tcp_conns[MAX_CONNS]; static pipe_conn_rec pipe_conns[MAX_CONNS]; static uint64_t start; /* in ms */ static int closed_streams; static int conns_failed; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); static void connect_cb(uv_connect_t* conn_req, int status); static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); static void close_cb(uv_handle_t* handle); static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; buf->base = slab; buf->len = sizeof(slab); } static void after_write(uv_write_t* req, int status) { if (status != 0) { fprintf(stderr, "write error %s\n", uv_err_name(status)); uv_close((uv_handle_t*)req->handle, close_cb); conns_failed++; return; } } static void connect_cb(uv_connect_t* req, int status) { conn_rec* conn; uv_buf_t buf; int r; if (status != 0) { #if DEBUG fprintf(stderr, "connect error %s\n", uv_err_name(status)); #endif uv_close((uv_handle_t*)req->handle, close_cb); conns_failed++; return; } ASSERT(req != NULL); ASSERT(status == 0); conn = (conn_rec*)req->data; ASSERT(conn != NULL); #if DEBUG printf("connect_cb %d\n", conn->i); #endif r = uv_read_start(&conn->stream, alloc_cb, read_cb); ASSERT(r == 0); buf.base = buffer; buf.len = sizeof(buffer) - 1; r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write); ASSERT(r == 0); } static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { ASSERT(stream != NULL); #if DEBUG printf("read_cb %d\n", p->i); #endif uv_close((uv_handle_t*)stream, close_cb); if (nread < 0) { if (nread == UV_EOF) { ; } else if (nread == UV_ECONNRESET) { conns_failed++; } else { fprintf(stderr, "read error %s\n", uv_err_name(nread)); ASSERT(0); } } } static void close_cb(uv_handle_t* handle) { conn_rec* p = (conn_rec*)handle->data; ASSERT(handle != NULL); closed_streams++; #if DEBUG printf("close_cb %d\n", p->i); #endif if (uv_now(loop) - start < 10000) { p->make_connect(p); } } static void tcp_do_setup(int num, void* arg) { int i; for (i = 0; i < num; i++) { tcp_conns[i].i = i; } } static void pipe_do_setup(int num, void* arg) { int i; for (i = 0; i < num; i++) { pipe_conns[i].i = i; } } static void tcp_make_connect(conn_rec* p) { struct sockaddr_in addr; tcp_conn_rec* tp; int r; tp = (tcp_conn_rec*) p; r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_connect(&tp->conn_req, (uv_tcp_t*) &p->stream, (const struct sockaddr*) &addr, connect_cb); if (r) { fprintf(stderr, "uv_tcp_connect error %s\n", uv_err_name(r)); ASSERT(0); } #if DEBUG printf("make connect %d\n", p->i); #endif p->conn_req.data = p; p->write_req.data = p; p->stream.data = p; } static void pipe_make_connect(conn_rec* p) { int r; r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream, 0); ASSERT(r == 0); uv_pipe_connect(&((pipe_conn_rec*) p)->conn_req, (uv_pipe_t*) &p->stream, TEST_PIPENAME, connect_cb); #if DEBUG printf("make connect %d\n", p->i); #endif p->conn_req.data = p; p->write_req.data = p; p->stream.data = p; } static int tcp_do_connect(int num, make_connect_fn make_connect, void* arg) { int i; for (i = 0; i < num; i++) { tcp_make_connect((conn_rec*)&tcp_conns[i]); tcp_conns[i].make_connect = make_connect; } return 0; } static int pipe_do_connect(int num, make_connect_fn make_connect, void* arg) { int i; for (i = 0; i < num; i++) { pipe_make_connect((conn_rec*)&pipe_conns[i]); pipe_conns[i].make_connect = make_connect; } return 0; } static int pound_it(int concurrency, const char* type, setup_fn do_setup, connect_fn do_connect, make_connect_fn make_connect, void* arg) { double secs; int r; uint64_t start_time; /* in ns */ uint64_t end_time; loop = uv_default_loop(); uv_update_time(loop); start = uv_now(loop); /* Run benchmark for at least five seconds. */ start_time = uv_hrtime(); do_setup(concurrency, arg); r = do_connect(concurrency, make_connect, arg); ASSERT(!r); uv_run(loop, UV_RUN_DEFAULT); end_time = uv_hrtime(); /* Number of fractional seconds it took to run the benchmark. */ secs = (double)(end_time - start_time) / NANOSEC; fprintf(stderr, "%s-conn-pound-%d: %.0f accepts/s (%d failed)\n", type, concurrency, closed_streams / secs, conns_failed); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } BENCHMARK_IMPL(tcp4_pound_100) { return pound_it(100, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL); } BENCHMARK_IMPL(tcp4_pound_1000) { return pound_it(1000, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL); } BENCHMARK_IMPL(pipe_pound_100) { return pound_it(100, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL); } BENCHMARK_IMPL(pipe_pound_1000) { return pound_it(1000, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL); } libuv-v1.8.0/test/benchmark-pump.c000066400000000000000000000237451263361716600171110ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #include #include static int TARGET_CONNECTIONS; #define WRITE_BUFFER_SIZE 8192 #define MAX_SIMULTANEOUS_CONNECTS 100 #define PRINT_STATS 0 #define STATS_INTERVAL 1000 /* msec */ #define STATS_COUNT 5 static void do_write(uv_stream_t*); static void maybe_connect_some(); static uv_req_t* req_alloc(); static void req_free(uv_req_t* uv_req); static void buf_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf); static void buf_free(const uv_buf_t* buf); static uv_loop_t* loop; static uv_tcp_t tcpServer; static uv_pipe_t pipeServer; static uv_stream_t* server; static struct sockaddr_in listen_addr; static struct sockaddr_in connect_addr; static int64_t start_time; static int max_connect_socket = 0; static int max_read_sockets = 0; static int read_sockets = 0; static int write_sockets = 0; static int64_t nrecv = 0; static int64_t nrecv_total = 0; static int64_t nsent = 0; static int64_t nsent_total = 0; static int stats_left = 0; static char write_buffer[WRITE_BUFFER_SIZE]; /* Make this as large as you need. */ #define MAX_WRITE_HANDLES 1000 static stream_type type; static uv_tcp_t tcp_write_handles[MAX_WRITE_HANDLES]; static uv_pipe_t pipe_write_handles[MAX_WRITE_HANDLES]; static uv_timer_t timer_handle; static double gbit(int64_t bytes, int64_t passed_ms) { double gbits = ((double)bytes / (1024 * 1024 * 1024)) * 8; return gbits / ((double)passed_ms / 1000); } static void show_stats(uv_timer_t* handle) { int64_t diff; int i; #if PRINT_STATS fprintf(stderr, "connections: %d, write: %.1f gbit/s\n", write_sockets, gbit(nsent, STATS_INTERVAL)); fflush(stderr); #endif /* Exit if the show is over */ if (!--stats_left) { uv_update_time(loop); diff = uv_now(loop) - start_time; fprintf(stderr, "%s_pump%d_client: %.1f gbit/s\n", type == TCP ? "tcp" : "pipe", write_sockets, gbit(nsent_total, diff)); fflush(stderr); for (i = 0; i < write_sockets; i++) { if (type == TCP) uv_close((uv_handle_t*) &tcp_write_handles[i], NULL); else uv_close((uv_handle_t*) &pipe_write_handles[i], NULL); } exit(0); } /* Reset read and write counters */ nrecv = 0; nsent = 0; } static void read_show_stats(void) { int64_t diff; uv_update_time(loop); diff = uv_now(loop) - start_time; fprintf(stderr, "%s_pump%d_server: %.1f gbit/s\n", type == TCP ? "tcp" : "pipe", max_read_sockets, gbit(nrecv_total, diff)); fflush(stderr); } static void read_sockets_close_cb(uv_handle_t* handle) { free(handle); read_sockets--; /* If it's past the first second and everyone has closed their connection * Then print stats. */ if (uv_now(loop) - start_time > 1000 && read_sockets == 0) { read_show_stats(); uv_close((uv_handle_t*)server, NULL); } } static void start_stats_collection(void) { int r; /* Show-stats timer */ stats_left = STATS_COUNT; r = uv_timer_init(loop, &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, show_stats, STATS_INTERVAL, STATS_INTERVAL); ASSERT(r == 0); uv_update_time(loop); start_time = uv_now(loop); } static void read_cb(uv_stream_t* stream, ssize_t bytes, const uv_buf_t* buf) { if (nrecv_total == 0) { ASSERT(start_time == 0); uv_update_time(loop); start_time = uv_now(loop); } if (bytes < 0) { uv_close((uv_handle_t*)stream, read_sockets_close_cb); return; } buf_free(buf); nrecv += bytes; nrecv_total += bytes; } static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); req_free((uv_req_t*) req); nsent += sizeof write_buffer; nsent_total += sizeof write_buffer; do_write((uv_stream_t*) req->handle); } static void do_write(uv_stream_t* stream) { uv_write_t* req; uv_buf_t buf; int r; buf.base = (char*) &write_buffer; buf.len = sizeof write_buffer; req = (uv_write_t*) req_alloc(); r = uv_write(req, stream, &buf, 1, write_cb); ASSERT(r == 0); } static void connect_cb(uv_connect_t* req, int status) { int i; if (status) { fprintf(stderr, "%s", uv_strerror(status)); fflush(stderr); } ASSERT(status == 0); write_sockets++; req_free((uv_req_t*) req); maybe_connect_some(); if (write_sockets == TARGET_CONNECTIONS) { start_stats_collection(); /* Yay! start writing */ for (i = 0; i < write_sockets; i++) { if (type == TCP) do_write((uv_stream_t*) &tcp_write_handles[i]); else do_write((uv_stream_t*) &pipe_write_handles[i]); } } } static void maybe_connect_some(void) { uv_connect_t* req; uv_tcp_t* tcp; uv_pipe_t* pipe; int r; while (max_connect_socket < TARGET_CONNECTIONS && max_connect_socket < write_sockets + MAX_SIMULTANEOUS_CONNECTS) { if (type == TCP) { tcp = &tcp_write_handles[max_connect_socket++]; r = uv_tcp_init(loop, tcp); ASSERT(r == 0); req = (uv_connect_t*) req_alloc(); r = uv_tcp_connect(req, tcp, (const struct sockaddr*) &connect_addr, connect_cb); ASSERT(r == 0); } else { pipe = &pipe_write_handles[max_connect_socket++]; r = uv_pipe_init(loop, pipe, 0); ASSERT(r == 0); req = (uv_connect_t*) req_alloc(); uv_pipe_connect(req, pipe, TEST_PIPENAME, connect_cb); } } } static void connection_cb(uv_stream_t* s, int status) { uv_stream_t* stream; int r; ASSERT(server == s); ASSERT(status == 0); if (type == TCP) { stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t)); r = uv_tcp_init(loop, (uv_tcp_t*)stream); ASSERT(r == 0); } else { stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t)); r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); ASSERT(r == 0); } r = uv_accept(s, stream); ASSERT(r == 0); r = uv_read_start(stream, buf_alloc, read_cb); ASSERT(r == 0); read_sockets++; max_read_sockets++; } /* * Request allocator */ typedef struct req_list_s { union uv_any_req uv_req; struct req_list_s* next; } req_list_t; static req_list_t* req_freelist = NULL; static uv_req_t* req_alloc(void) { req_list_t* req; req = req_freelist; if (req != NULL) { req_freelist = req->next; return (uv_req_t*) req; } req = (req_list_t*) malloc(sizeof *req); return (uv_req_t*) req; } static void req_free(uv_req_t* uv_req) { req_list_t* req = (req_list_t*) uv_req; req->next = req_freelist; req_freelist = req; } /* * Buffer allocator */ typedef struct buf_list_s { uv_buf_t uv_buf_t; struct buf_list_s* next; } buf_list_t; static buf_list_t* buf_freelist = NULL; static void buf_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf_list_t* ab; ab = buf_freelist; if (ab != NULL) buf_freelist = ab->next; else { ab = malloc(size + sizeof(*ab)); ab->uv_buf_t.len = size; ab->uv_buf_t.base = (char*) (ab + 1); } *buf = ab->uv_buf_t; } static void buf_free(const uv_buf_t* buf) { buf_list_t* ab = (buf_list_t*) buf->base - 1; ab->next = buf_freelist; buf_freelist = ab; } HELPER_IMPL(tcp_pump_server) { int r; type = TCP; loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &listen_addr)); /* Server */ server = (uv_stream_t*)&tcpServer; r = uv_tcp_init(loop, &tcpServer); ASSERT(r == 0); r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &listen_addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); return 0; } HELPER_IMPL(pipe_pump_server) { int r; type = PIPE; loop = uv_default_loop(); /* Server */ server = (uv_stream_t*)&pipeServer; r = uv_pipe_init(loop, &pipeServer, 0); ASSERT(r == 0); r = uv_pipe_bind(&pipeServer, TEST_PIPENAME); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } static void tcp_pump(int n) { ASSERT(n <= MAX_WRITE_HANDLES); TARGET_CONNECTIONS = n; type = TCP; loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &connect_addr)); /* Start making connections */ maybe_connect_some(); uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); } static void pipe_pump(int n) { ASSERT(n <= MAX_WRITE_HANDLES); TARGET_CONNECTIONS = n; type = PIPE; loop = uv_default_loop(); /* Start making connections */ maybe_connect_some(); uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); } BENCHMARK_IMPL(tcp_pump100_client) { tcp_pump(100); return 0; } BENCHMARK_IMPL(tcp_pump1_client) { tcp_pump(1); return 0; } BENCHMARK_IMPL(pipe_pump100_client) { pipe_pump(100); return 0; } BENCHMARK_IMPL(pipe_pump1_client) { pipe_pump(1); return 0; } libuv-v1.8.0/test/benchmark-sizes.c000066400000000000000000000051021263361716600172500ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" BENCHMARK_IMPL(sizes) { fprintf(stderr, "uv_shutdown_t: %u bytes\n", (unsigned int) sizeof(uv_shutdown_t)); fprintf(stderr, "uv_write_t: %u bytes\n", (unsigned int) sizeof(uv_write_t)); fprintf(stderr, "uv_connect_t: %u bytes\n", (unsigned int) sizeof(uv_connect_t)); fprintf(stderr, "uv_udp_send_t: %u bytes\n", (unsigned int) sizeof(uv_udp_send_t)); fprintf(stderr, "uv_tcp_t: %u bytes\n", (unsigned int) sizeof(uv_tcp_t)); fprintf(stderr, "uv_pipe_t: %u bytes\n", (unsigned int) sizeof(uv_pipe_t)); fprintf(stderr, "uv_tty_t: %u bytes\n", (unsigned int) sizeof(uv_tty_t)); fprintf(stderr, "uv_prepare_t: %u bytes\n", (unsigned int) sizeof(uv_prepare_t)); fprintf(stderr, "uv_check_t: %u bytes\n", (unsigned int) sizeof(uv_check_t)); fprintf(stderr, "uv_idle_t: %u bytes\n", (unsigned int) sizeof(uv_idle_t)); fprintf(stderr, "uv_async_t: %u bytes\n", (unsigned int) sizeof(uv_async_t)); fprintf(stderr, "uv_timer_t: %u bytes\n", (unsigned int) sizeof(uv_timer_t)); fprintf(stderr, "uv_fs_poll_t: %u bytes\n", (unsigned int) sizeof(uv_fs_poll_t)); fprintf(stderr, "uv_fs_event_t: %u bytes\n", (unsigned int) sizeof(uv_fs_event_t)); fprintf(stderr, "uv_process_t: %u bytes\n", (unsigned int) sizeof(uv_process_t)); fprintf(stderr, "uv_poll_t: %u bytes\n", (unsigned int) sizeof(uv_poll_t)); fprintf(stderr, "uv_loop_t: %u bytes\n", (unsigned int) sizeof(uv_loop_t)); fflush(stderr); return 0; } libuv-v1.8.0/test/benchmark-spawn.c000066400000000000000000000075721263361716600172600ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* This benchmark spawns itself 1000 times. */ #include "task.h" #include "uv.h" static uv_loop_t* loop; static int N = 1000; static int done; static uv_process_t process; static uv_process_options_t options; static char exepath[1024]; static size_t exepath_size = 1024; static char* args[3]; static uv_pipe_t out; #define OUTPUT_SIZE 1024 static char output[OUTPUT_SIZE]; static int output_used; static int process_open; static int pipe_open; static void spawn(void); static void maybe_spawn(void) { if (process_open == 0 && pipe_open == 0) { done++; if (done < N) { spawn(); } } } static void process_close_cb(uv_handle_t* handle) { ASSERT(process_open == 1); process_open = 0; maybe_spawn(); } static void exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) { ASSERT(exit_status == 42); ASSERT(term_signal == 0); uv_close((uv_handle_t*)process, process_close_cb); } static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = output + output_used; buf->len = OUTPUT_SIZE - output_used; } static void pipe_close_cb(uv_handle_t* pipe) { ASSERT(pipe_open == 1); pipe_open = 0; maybe_spawn(); } static void on_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { ASSERT(pipe_open == 1); output_used += nread; } else if (nread < 0) { if (nread == UV_EOF) { uv_close((uv_handle_t*)pipe, pipe_close_cb); } } } static void spawn(void) { uv_stdio_container_t stdio[2]; int r; ASSERT(process_open == 0); ASSERT(pipe_open == 0); args[0] = exepath; args[1] = "spawn_helper"; args[2] = NULL; options.file = exepath; options.args = args; options.exit_cb = exit_cb; uv_pipe_init(loop, &out, 0); options.stdio = stdio; options.stdio_count = 2; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; r = uv_spawn(loop, &process, &options); ASSERT(r == 0); process_open = 1; pipe_open = 1; output_used = 0; r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); } BENCHMARK_IMPL(spawn) { int r; static int64_t start_time, end_time; loop = uv_default_loop(); r = uv_exepath(exepath, &exepath_size); ASSERT(r == 0); exepath[exepath_size] = '\0'; uv_update_time(loop); start_time = uv_now(loop); spawn(); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); uv_update_time(loop); end_time = uv_now(loop); fprintf(stderr, "spawn: %.0f spawns/s\n", (double) N / (double) (end_time - start_time) * 1000.0); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-tcp-write-batch.c000066400000000000000000000074541263361716600211240ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #define WRITE_REQ_DATA "Hello, world." #define NUM_WRITE_REQS (1000 * 1000) typedef struct { uv_write_t req; uv_buf_t buf; } write_req; static write_req* write_reqs; static uv_tcp_t tcp_client; static uv_connect_t connect_req; static uv_shutdown_t shutdown_req; static int shutdown_cb_called = 0; static int connect_cb_called = 0; static int write_cb_called = 0; static int close_cb_called = 0; static void connect_cb(uv_connect_t* req, int status); static void write_cb(uv_write_t* req, int status); static void shutdown_cb(uv_shutdown_t* req, int status); static void close_cb(uv_handle_t* handle); static void connect_cb(uv_connect_t* req, int status) { write_req* w; int i; int r; ASSERT(req->handle == (uv_stream_t*)&tcp_client); for (i = 0; i < NUM_WRITE_REQS; i++) { w = &write_reqs[i]; r = uv_write(&w->req, req->handle, &w->buf, 1, write_cb); ASSERT(r == 0); } r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); ASSERT(r == 0); connect_cb_called++; } static void write_cb(uv_write_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); write_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(req->handle == (uv_stream_t*)&tcp_client); ASSERT(req->handle->write_queue_size == 0); uv_close((uv_handle_t*)req->handle, close_cb); free(write_reqs); shutdown_cb_called++; } static void close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*)&tcp_client); close_cb_called++; } BENCHMARK_IMPL(tcp_write_batch) { struct sockaddr_in addr; uv_loop_t* loop; uint64_t start; uint64_t stop; int i; int r; write_reqs = malloc(sizeof(*write_reqs) * NUM_WRITE_REQS); ASSERT(write_reqs != NULL); /* Prepare the data to write out. */ for (i = 0; i < NUM_WRITE_REQS; i++) { write_reqs[i].buf = uv_buf_init(WRITE_REQ_DATA, sizeof(WRITE_REQ_DATA) - 1); } loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, &tcp_client); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &tcp_client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); start = uv_hrtime(); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); stop = uv_hrtime(); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == NUM_WRITE_REQS); ASSERT(shutdown_cb_called == 1); ASSERT(close_cb_called == 1); printf("%ld write requests in %.2fs.\n", (long)NUM_WRITE_REQS, (stop - start) / 1e9); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/benchmark-thread.c000066400000000000000000000036231263361716600173700ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #define NUM_THREADS (20 * 1000) static volatile int num_threads; static void thread_entry(void* arg) { ASSERT(arg == (void *) 42); num_threads++; /* FIXME write barrier? */ } BENCHMARK_IMPL(thread_create) { uint64_t start_time; double duration; uv_thread_t tid; int i, r; start_time = uv_hrtime(); for (i = 0; i < NUM_THREADS; i++) { r = uv_thread_create(&tid, thread_entry, (void *) 42); ASSERT(r == 0); r = uv_thread_join(&tid); ASSERT(r == 0); } duration = (uv_hrtime() - start_time) / 1e9; ASSERT(num_threads == NUM_THREADS); printf("%d threads created in %.2f seconds (%.0f/s)\n", NUM_THREADS, duration, NUM_THREADS / duration); return 0; } libuv-v1.8.0/test/benchmark-udp-pummel.c000066400000000000000000000151211263361716600202020ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" #include "uv.h" #include #include #include #define EXPECTED "RANG TANG DING DONG I AM THE JAPANESE SANDMAN" #define TEST_DURATION 5000 /* ms */ #define BASE_PORT 12345 struct sender_state { struct sockaddr_in addr; uv_udp_send_t send_req; uv_udp_t udp_handle; }; struct receiver_state { struct sockaddr_in addr; uv_udp_t udp_handle; }; /* not used in timed mode */ static unsigned int packet_counter = (unsigned int) 1e6; static int n_senders_; static int n_receivers_; static uv_buf_t bufs[5]; static struct sender_state senders[1024]; static struct receiver_state receivers[1024]; static unsigned int send_cb_called; static unsigned int recv_cb_called; static unsigned int close_cb_called; static int timed; static int exiting; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void send_cb(uv_udp_send_t* req, int status) { struct sender_state* s; ASSERT(req != NULL); if (status != 0) { ASSERT(status == UV_ECANCELED); return; } if (exiting) return; s = container_of(req, struct sender_state, send_req); ASSERT(req->handle == &s->udp_handle); if (timed) goto send; if (packet_counter == 0) { uv_close((uv_handle_t*)&s->udp_handle, NULL); return; } packet_counter--; send: ASSERT(0 == uv_udp_send(&s->send_req, &s->udp_handle, bufs, ARRAY_SIZE(bufs), (const struct sockaddr*) &s->addr, send_cb)); send_cb_called++; } static void recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { if (nread == 0) return; if (nread < 0) { ASSERT(nread == UV_ECANCELED); return; } ASSERT(addr->sa_family == AF_INET); ASSERT(!memcmp(buf->base, EXPECTED, nread)); recv_cb_called++; } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void timeout_cb(uv_timer_t* timer) { int i; exiting = 1; for (i = 0; i < n_senders_; i++) uv_close((uv_handle_t*)&senders[i].udp_handle, close_cb); for (i = 0; i < n_receivers_; i++) uv_close((uv_handle_t*)&receivers[i].udp_handle, close_cb); } static int pummel(unsigned int n_senders, unsigned int n_receivers, unsigned long timeout) { uv_timer_t timer_handle; uint64_t duration; uv_loop_t* loop; unsigned int i; ASSERT(n_senders <= ARRAY_SIZE(senders)); ASSERT(n_receivers <= ARRAY_SIZE(receivers)); loop = uv_default_loop(); n_senders_ = n_senders; n_receivers_ = n_receivers; if (timeout) { ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timeout_cb, timeout, 0)); /* Timer should not keep loop alive. */ uv_unref((uv_handle_t*)&timer_handle); timed = 1; } for (i = 0; i < n_receivers; i++) { struct receiver_state* s = receivers + i; struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("0.0.0.0", BASE_PORT + i, &addr)); ASSERT(0 == uv_udp_init(loop, &s->udp_handle)); ASSERT(0 == uv_udp_bind(&s->udp_handle, (const struct sockaddr*) &addr, 0)); ASSERT(0 == uv_udp_recv_start(&s->udp_handle, alloc_cb, recv_cb)); uv_unref((uv_handle_t*)&s->udp_handle); } bufs[0] = uv_buf_init(EXPECTED + 0, 10); bufs[1] = uv_buf_init(EXPECTED + 10, 10); bufs[2] = uv_buf_init(EXPECTED + 20, 10); bufs[3] = uv_buf_init(EXPECTED + 30, 10); bufs[4] = uv_buf_init(EXPECTED + 40, 5); for (i = 0; i < n_senders; i++) { struct sender_state* s = senders + i; ASSERT(0 == uv_ip4_addr("127.0.0.1", BASE_PORT + (i % n_receivers), &s->addr)); ASSERT(0 == uv_udp_init(loop, &s->udp_handle)); ASSERT(0 == uv_udp_send(&s->send_req, &s->udp_handle, bufs, ARRAY_SIZE(bufs), (const struct sockaddr*) &s->addr, send_cb)); } duration = uv_hrtime(); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); duration = uv_hrtime() - duration; /* convert from nanoseconds to milliseconds */ duration = duration / (uint64_t) 1e6; printf("udp_pummel_%dv%d: %.0f/s received, %.0f/s sent. " "%u received, %u sent in %.1f seconds.\n", n_receivers, n_senders, recv_cb_called / (duration / 1000.0), send_cb_called / (duration / 1000.0), recv_cb_called, send_cb_called, duration / 1000.0); MAKE_VALGRIND_HAPPY(); return 0; } #define X(a, b) \ BENCHMARK_IMPL(udp_pummel_##a##v##b) { \ return pummel(a, b, 0); \ } \ BENCHMARK_IMPL(udp_timed_pummel_##a##v##b) { \ return pummel(a, b, TEST_DURATION); \ } X(1, 1) X(1, 10) X(1, 100) X(1, 1000) X(10, 10) X(10, 100) X(10, 1000) X(100, 10) X(100, 100) X(100, 1000) X(1000, 1000) #undef X libuv-v1.8.0/test/blackhole-server.c000066400000000000000000000065401263361716600174220ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include typedef struct { uv_tcp_t handle; uv_shutdown_t shutdown_req; } conn_rec; static uv_tcp_t tcp_server; static void connection_cb(uv_stream_t* stream, int status); static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); static void shutdown_cb(uv_shutdown_t* req, int status); static void close_cb(uv_handle_t* handle); static void connection_cb(uv_stream_t* stream, int status) { conn_rec* conn; int r; ASSERT(status == 0); ASSERT(stream == (uv_stream_t*)&tcp_server); conn = malloc(sizeof *conn); ASSERT(conn != NULL); r = uv_tcp_init(stream->loop, &conn->handle); ASSERT(r == 0); r = uv_accept(stream, (uv_stream_t*)&conn->handle); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&conn->handle, alloc_cb, read_cb); ASSERT(r == 0); } static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; buf->base = slab; buf->len = sizeof(slab); } static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { conn_rec* conn; int r; if (nread >= 0) return; ASSERT(nread == UV_EOF); conn = container_of(stream, conn_rec, handle); r = uv_shutdown(&conn->shutdown_req, stream, shutdown_cb); ASSERT(r == 0); } static void shutdown_cb(uv_shutdown_t* req, int status) { conn_rec* conn = container_of(req, conn_rec, shutdown_req); uv_close((uv_handle_t*)&conn->handle, close_cb); } static void close_cb(uv_handle_t* handle) { conn_rec* conn = container_of(handle, conn_rec, handle); free(conn); } HELPER_IMPL(tcp4_blackhole_server) { struct sockaddr_in addr; uv_loop_t* loop; int r; loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, &tcp_server); ASSERT(r == 0); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcp_server, 128, connection_cb); ASSERT(r == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(0 && "Blackhole server dropped out of event loop."); return 0; } libuv-v1.8.0/test/dns-server.c000066400000000000000000000205761263361716600162670ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include typedef struct { uv_write_t req; uv_buf_t buf; } write_req_t; /* used to track multiple DNS requests received */ typedef struct { char* prevbuf_ptr; int prevbuf_pos; int prevbuf_rem; } dnsstate; /* modify handle to append dnsstate */ typedef struct { uv_tcp_t handle; dnsstate state; } dnshandle; static uv_loop_t* loop; static uv_tcp_t server; static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf); static void on_close(uv_handle_t* peer); static void on_connection(uv_stream_t*, int status); #define WRITE_BUF_LEN (64*1024) #define DNSREC_LEN (4) #define LEN_OFFSET 0 #define QUERYID_OFFSET 2 static unsigned char DNSRsp[] = { 0, 43, 0, 0, 0x81, 0x80, 0, 1, 0, 1, 0, 0, 0, 0 }; static unsigned char qrecord[] = { 5, 'e', 'c', 'h', 'o', 's', 3, 's', 'r', 'v', 0, 0, 1, 0, 1 }; static unsigned char arecord[] = { 0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 5, 0xbd, 0, 4, 10, 0, 1, 1 }; static void after_write(uv_write_t* req, int status) { write_req_t* wr; if (status) { fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); ASSERT(0); } wr = (write_req_t*) req; /* Free the read/write buffer and the request */ free(wr->buf.base); free(wr); } static void after_shutdown(uv_shutdown_t* req, int status) { uv_close((uv_handle_t*) req->handle, on_close); free(req); } static void addrsp(write_req_t* wr, char* hdr) { char * dnsrsp; short int rsplen; short int* reclen; rsplen = sizeof(DNSRsp) + sizeof(qrecord) + sizeof(arecord); ASSERT (rsplen + wr->buf.len < WRITE_BUF_LEN); dnsrsp = wr->buf.base + wr->buf.len; /* copy stock response */ memcpy(dnsrsp, DNSRsp, sizeof(DNSRsp)); memcpy(dnsrsp + sizeof(DNSRsp), qrecord, sizeof(qrecord)); memcpy(dnsrsp + sizeof(DNSRsp) + sizeof(qrecord), arecord, sizeof(arecord)); /* overwrite with network order length and id from request header */ reclen = (short int*)dnsrsp; *reclen = htons(rsplen-2); dnsrsp[QUERYID_OFFSET] = hdr[QUERYID_OFFSET]; dnsrsp[QUERYID_OFFSET+1] = hdr[QUERYID_OFFSET+1]; wr->buf.len += rsplen; } static void process_req(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { write_req_t* wr; dnshandle* dns = (dnshandle*)handle; char hdrbuf[DNSREC_LEN]; int hdrbuf_remaining = DNSREC_LEN; int rec_remaining = 0; int readbuf_remaining; char* dnsreq; char* hdrstart; int usingprev = 0; wr = (write_req_t*) malloc(sizeof *wr); wr->buf.base = (char*)malloc(WRITE_BUF_LEN); wr->buf.len = 0; if (dns->state.prevbuf_ptr != NULL) { dnsreq = dns->state.prevbuf_ptr + dns->state.prevbuf_pos; readbuf_remaining = dns->state.prevbuf_rem; usingprev = 1; } else { dnsreq = buf->base; readbuf_remaining = nread; } hdrstart = dnsreq; while (dnsreq != NULL) { /* something to process */ while (readbuf_remaining > 0) { /* something to process in current buffer */ if (hdrbuf_remaining > 0) { /* process len and id */ if (readbuf_remaining < hdrbuf_remaining) { /* too little to get request header. save for next buffer */ memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], dnsreq, readbuf_remaining); hdrbuf_remaining = DNSREC_LEN - readbuf_remaining; break; } else { /* save header */ memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], dnsreq, hdrbuf_remaining); dnsreq += hdrbuf_remaining; readbuf_remaining -= hdrbuf_remaining; hdrbuf_remaining = 0; /* get record length */ rec_remaining = (unsigned) hdrbuf[0] * 256 + (unsigned) hdrbuf[1]; rec_remaining -= (DNSREC_LEN - 2); } } if (rec_remaining <= readbuf_remaining) { /* prepare reply */ addrsp(wr, hdrbuf); /* move to next record */ dnsreq += rec_remaining; hdrstart = dnsreq; readbuf_remaining -= rec_remaining; rec_remaining = 0; hdrbuf_remaining = DNSREC_LEN; } else { /* otherwise this buffer is done. */ rec_remaining -= readbuf_remaining; break; } } /* If we had to use bytes from prev buffer, start processing the current * one. */ if (usingprev == 1) { /* free previous buffer */ free(dns->state.prevbuf_ptr); dnsreq = buf->base; readbuf_remaining = nread; usingprev = 0; } else { dnsreq = NULL; } } /* send write buffer */ if (wr->buf.len > 0) { if (uv_write((uv_write_t*) &wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } } if (readbuf_remaining > 0) { /* save start of record position, so we can continue on next read */ dns->state.prevbuf_ptr = buf->base; dns->state.prevbuf_pos = hdrstart - buf->base; dns->state.prevbuf_rem = nread - dns->state.prevbuf_pos; } else { /* nothing left in this buffer */ dns->state.prevbuf_ptr = NULL; dns->state.prevbuf_pos = 0; dns->state.prevbuf_rem = 0; free(buf->base); } } static void after_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { uv_shutdown_t* req; if (nread < 0) { /* Error or EOF */ ASSERT(nread == UV_EOF); if (buf->base) { free(buf->base); } req = malloc(sizeof *req); uv_shutdown(req, handle, after_shutdown); return; } if (nread == 0) { /* Everything OK, but nothing read. */ free(buf->base); return; } /* process requests and send responses */ process_req(handle, nread, buf); } static void on_close(uv_handle_t* peer) { free(peer); } static void buf_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } static void on_connection(uv_stream_t* server, int status) { dnshandle* handle; int r; ASSERT(status == 0); handle = (dnshandle*) malloc(sizeof *handle); ASSERT(handle != NULL); /* initialize read buffer state */ handle->state.prevbuf_ptr = 0; handle->state.prevbuf_pos = 0; handle->state.prevbuf_rem = 0; r = uv_tcp_init(loop, (uv_tcp_t*)handle); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)handle); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)handle, buf_alloc, after_read); ASSERT(r == 0); } static int dns_start(int port) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); r = uv_tcp_init(loop, &server); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); return 1; } r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); if (r) { /* TODO: Error codes */ fprintf(stderr, "Bind error\n"); return 1; } r = uv_listen((uv_stream_t*)&server, 128, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); return 1; } return 0; } HELPER_IMPL(dns_server) { loop = uv_default_loop(); if (dns_start(TEST_PORT_2)) return 1; uv_run(loop, UV_RUN_DEFAULT); return 0; } libuv-v1.8.0/test/echo-server.c000066400000000000000000000202041263361716600164050ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include typedef struct { uv_write_t req; uv_buf_t buf; } write_req_t; static uv_loop_t* loop; static int server_closed; static stream_type serverType; static uv_tcp_t tcpServer; static uv_udp_t udpServer; static uv_pipe_t pipeServer; static uv_handle_t* server; static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); static void on_connection(uv_stream_t*, int status); static void after_write(uv_write_t* req, int status) { write_req_t* wr; /* Free the read/write buffer and the request */ wr = (write_req_t*) req; free(wr->buf.base); free(wr); if (status == 0) return; fprintf(stderr, "uv_write error: %s - %s\n", uv_err_name(status), uv_strerror(status)); } static void after_shutdown(uv_shutdown_t* req, int status) { uv_close((uv_handle_t*) req->handle, on_close); free(req); } static void after_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { int i; write_req_t *wr; uv_shutdown_t* sreq; if (nread < 0) { /* Error or EOF */ ASSERT(nread == UV_EOF); free(buf->base); sreq = malloc(sizeof* sreq); ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown)); return; } if (nread == 0) { /* Everything OK, but nothing read. */ free(buf->base); return; } /* * Scan for the letter Q which signals that we should quit the server. * If we get QS it means close the stream. */ if (!server_closed) { for (i = 0; i < nread; i++) { if (buf->base[i] == 'Q') { if (i + 1 < nread && buf->base[i + 1] == 'S') { free(buf->base); uv_close((uv_handle_t*)handle, on_close); return; } else { uv_close(server, on_server_close); server_closed = 1; } } } } wr = (write_req_t*) malloc(sizeof *wr); ASSERT(wr != NULL); wr->buf = uv_buf_init(buf->base, nread); if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } } static void on_close(uv_handle_t* peer) { free(peer); } static void echo_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } static void on_connection(uv_stream_t* server, int status) { uv_stream_t* stream; int r; if (status != 0) { fprintf(stderr, "Connect error %s\n", uv_err_name(status)); } ASSERT(status == 0); switch (serverType) { case TCP: stream = malloc(sizeof(uv_tcp_t)); ASSERT(stream != NULL); r = uv_tcp_init(loop, (uv_tcp_t*)stream); ASSERT(r == 0); break; case PIPE: stream = malloc(sizeof(uv_pipe_t)); ASSERT(stream != NULL); r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); ASSERT(r == 0); break; default: ASSERT(0 && "Bad serverType"); abort(); } /* associate server with stream */ stream->data = server; r = uv_accept(server, stream); ASSERT(r == 0); r = uv_read_start(stream, echo_alloc, after_read); ASSERT(r == 0); } static void on_server_close(uv_handle_t* handle) { ASSERT(handle == server); } static void on_send(uv_udp_send_t* req, int status); static void on_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { uv_udp_send_t* req; uv_buf_t sndbuf; ASSERT(nread > 0); ASSERT(addr->sa_family == AF_INET); req = malloc(sizeof(*req)); ASSERT(req != NULL); sndbuf = *rcvbuf; ASSERT(0 == uv_udp_send(req, handle, &sndbuf, 1, addr, on_send)); } static void on_send(uv_udp_send_t* req, int status) { ASSERT(status == 0); free(req); } static int tcp4_echo_start(int port) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); server = (uv_handle_t*)&tcpServer; serverType = TCP; r = uv_tcp_init(loop, &tcpServer); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); return 1; } r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0); if (r) { /* TODO: Error codes */ fprintf(stderr, "Bind error\n"); return 1; } r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error %s\n", uv_err_name(r)); return 1; } return 0; } static int tcp6_echo_start(int port) { struct sockaddr_in6 addr6; int r; ASSERT(0 == uv_ip6_addr("::1", port, &addr6)); server = (uv_handle_t*)&tcpServer; serverType = TCP; r = uv_tcp_init(loop, &tcpServer); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); return 1; } /* IPv6 is optional as not all platforms support it */ r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr6, 0); if (r) { /* show message but return OK */ fprintf(stderr, "IPv6 not supported\n"); return 0; } r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); return 1; } return 0; } static int udp4_echo_start(int port) { int r; server = (uv_handle_t*)&udpServer; serverType = UDP; r = uv_udp_init(loop, &udpServer); if (r) { fprintf(stderr, "uv_udp_init: %s\n", uv_strerror(r)); return 1; } r = uv_udp_recv_start(&udpServer, echo_alloc, on_recv); if (r) { fprintf(stderr, "uv_udp_recv_start: %s\n", uv_strerror(r)); return 1; } return 0; } static int pipe_echo_start(char* pipeName) { int r; #ifndef _WIN32 { uv_fs_t req; uv_fs_unlink(NULL, &req, pipeName, NULL); uv_fs_req_cleanup(&req); } #endif server = (uv_handle_t*)&pipeServer; serverType = PIPE; r = uv_pipe_init(loop, &pipeServer, 0); if (r) { fprintf(stderr, "uv_pipe_init: %s\n", uv_strerror(r)); return 1; } r = uv_pipe_bind(&pipeServer, pipeName); if (r) { fprintf(stderr, "uv_pipe_bind: %s\n", uv_strerror(r)); return 1; } r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection); if (r) { fprintf(stderr, "uv_pipe_listen: %s\n", uv_strerror(r)); return 1; } return 0; } HELPER_IMPL(tcp4_echo_server) { loop = uv_default_loop(); if (tcp4_echo_start(TEST_PORT)) return 1; uv_run(loop, UV_RUN_DEFAULT); return 0; } HELPER_IMPL(tcp6_echo_server) { loop = uv_default_loop(); if (tcp6_echo_start(TEST_PORT)) return 1; uv_run(loop, UV_RUN_DEFAULT); return 0; } HELPER_IMPL(pipe_echo_server) { loop = uv_default_loop(); if (pipe_echo_start(TEST_PIPENAME)) return 1; uv_run(loop, UV_RUN_DEFAULT); return 0; } HELPER_IMPL(udp4_echo_server) { loop = uv_default_loop(); if (udp4_echo_start(TEST_PORT)) return 1; uv_run(loop, UV_RUN_DEFAULT); return 0; } libuv-v1.8.0/test/fixtures/000077500000000000000000000000001263361716600156725ustar00rootroot00000000000000libuv-v1.8.0/test/fixtures/empty_file000066400000000000000000000000001263361716600177400ustar00rootroot00000000000000libuv-v1.8.0/test/fixtures/load_error.node000066400000000000000000000000071263361716600206660ustar00rootroot00000000000000foobar libuv-v1.8.0/test/run-benchmarks.c000066400000000000000000000037461263361716600171160ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "runner.h" #include "task.h" /* Actual benchmarks and helpers are defined in benchmark-list.h */ #include "benchmark-list.h" static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { if (platform_init(argc, argv)) return EXIT_FAILURE; switch (argc) { case 1: return run_tests(1); case 2: return maybe_run_test(argc, argv); case 3: return run_test_part(argv[1], argv[2]); default: fprintf(stderr, "Too many arguments.\n"); fflush(stderr); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int maybe_run_test(int argc, char **argv) { if (strcmp(argv[1], "--list") == 0) { print_tests(stdout); return 0; } if (strcmp(argv[1], "spawn_helper") == 0) { printf("hello world\n"); return 42; } return run_test(argv[1], 1, 1); } libuv-v1.8.0/test/run-tests.c000066400000000000000000000106241263361716600161340ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #ifdef _WIN32 # include #else # include #endif #include "uv.h" #include "runner.h" #include "task.h" /* Actual tests and helpers are defined in test-list.h */ #include "test-list.h" int ipc_helper(int listen_after_write); int ipc_helper_tcp_connection(void); int ipc_send_recv_helper(void); int ipc_helper_bind_twice(void); int stdio_over_pipes_helper(void); int spawn_stdin_stdout(void); static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { if (platform_init(argc, argv)) return EXIT_FAILURE; argv = uv_setup_args(argc, argv); switch (argc) { case 1: return run_tests(0); case 2: return maybe_run_test(argc, argv); case 3: return run_test_part(argv[1], argv[2]); default: fprintf(stderr, "Too many arguments.\n"); fflush(stderr); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int maybe_run_test(int argc, char **argv) { if (strcmp(argv[1], "--list") == 0) { print_tests(stdout); return 0; } if (strcmp(argv[1], "ipc_helper_listen_before_write") == 0) { return ipc_helper(0); } if (strcmp(argv[1], "ipc_helper_listen_after_write") == 0) { return ipc_helper(1); } if (strcmp(argv[1], "ipc_send_recv_helper") == 0) { return ipc_send_recv_helper(); } if (strcmp(argv[1], "ipc_helper_tcp_connection") == 0) { return ipc_helper_tcp_connection(); } if (strcmp(argv[1], "ipc_helper_bind_twice") == 0) { return ipc_helper_bind_twice(); } if (strcmp(argv[1], "stdio_over_pipes_helper") == 0) { return stdio_over_pipes_helper(); } if (strcmp(argv[1], "spawn_helper1") == 0) { return 1; } if (strcmp(argv[1], "spawn_helper2") == 0) { printf("hello world\n"); return 1; } if (strcmp(argv[1], "spawn_helper3") == 0) { char buffer[256]; ASSERT(buffer == fgets(buffer, sizeof(buffer) - 1, stdin)); buffer[sizeof(buffer) - 1] = '\0'; fputs(buffer, stdout); return 1; } if (strcmp(argv[1], "spawn_helper4") == 0) { /* Never surrender, never return! */ while (1) uv_sleep(10000); } if (strcmp(argv[1], "spawn_helper5") == 0) { const char out[] = "fourth stdio!\n"; #ifdef _WIN32 DWORD bytes; WriteFile((HANDLE) _get_osfhandle(3), out, sizeof(out) - 1, &bytes, NULL); #else { ssize_t r; do r = write(3, out, sizeof(out) - 1); while (r == -1 && errno == EINTR); fsync(3); } #endif return 1; } if (strcmp(argv[1], "spawn_helper6") == 0) { int r; r = fprintf(stdout, "hello world\n"); ASSERT(r > 0); r = fprintf(stderr, "hello errworld\n"); ASSERT(r > 0); return 1; } if (strcmp(argv[1], "spawn_helper7") == 0) { int r; char *test; /* Test if the test value from the parent is still set */ test = getenv("ENV_TEST"); ASSERT(test != NULL); r = fprintf(stdout, "%s", test); ASSERT(r > 0); return 1; } #ifndef _WIN32 if (strcmp(argv[1], "spawn_helper8") == 0) { int fd; ASSERT(sizeof(fd) == read(0, &fd, sizeof(fd))); ASSERT(fd > 2); ASSERT(-1 == write(fd, "x", 1)); return 1; } #endif /* !_WIN32 */ if (strcmp(argv[1], "spawn_helper9") == 0) { return spawn_stdin_stdout(); } return run_test(argv[1], 0, 1); } libuv-v1.8.0/test/runner-unix.c000066400000000000000000000216221263361716600164620ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "runner-unix.h" #include "runner.h" #include #include /* uintptr_t */ #include #include /* readlink, usleep */ #include /* strdup */ #include #include #include #include #include #include #include #include #include #include /* Do platform-specific initialization. */ int platform_init(int argc, char **argv) { const char* tap; tap = getenv("UV_TAP_OUTPUT"); tap_output = (tap != NULL && atoi(tap) > 0); /* Disable stdio output buffering. */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); signal(SIGPIPE, SIG_IGN); if (realpath(argv[0], executable_path) == NULL) { perror("realpath"); return -1; } return 0; } /* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ /* Make sure that all stdio output of the processes is buffered up. */ int process_start(char* name, char* part, process_info_t* p, int is_helper) { FILE* stdout_file; const char* arg; char* args[16]; int n; pid_t pid; stdout_file = tmpfile(); if (!stdout_file) { perror("tmpfile"); return -1; } p->terminated = 0; p->status = 0; pid = fork(); if (pid < 0) { perror("fork"); return -1; } if (pid == 0) { /* child */ arg = getenv("UV_USE_VALGRIND"); n = 0; /* Disable valgrind for helpers, it complains about helpers leaking memory. * They're killed after the test and as such never get a chance to clean up. */ if (is_helper == 0 && arg != NULL && atoi(arg) != 0) { args[n++] = "valgrind"; args[n++] = "--quiet"; args[n++] = "--leak-check=full"; args[n++] = "--show-reachable=yes"; args[n++] = "--error-exitcode=125"; } args[n++] = executable_path; args[n++] = name; args[n++] = part; args[n++] = NULL; dup2(fileno(stdout_file), STDOUT_FILENO); dup2(fileno(stdout_file), STDERR_FILENO); execvp(args[0], args); perror("execvp()"); _exit(127); } /* parent */ p->pid = pid; p->name = strdup(name); p->stdout_file = stdout_file; return 0; } typedef struct { int pipe[2]; process_info_t* vec; int n; } dowait_args; /* This function is run inside a pthread. We do this so that we can possibly * timeout. */ static void* dowait(void* data) { dowait_args* args = data; int i, r; process_info_t* p; for (i = 0; i < args->n; i++) { p = (process_info_t*)(args->vec + i * sizeof(process_info_t)); if (p->terminated) continue; r = waitpid(p->pid, &p->status, 0); if (r < 0) { perror("waitpid"); return NULL; } p->terminated = 1; } if (args->pipe[1] >= 0) { /* Write a character to the main thread to notify it about this. */ ssize_t r; do r = write(args->pipe[1], "", 1); while (r == -1 && errno == EINTR); } return NULL; } /* Wait for all `n` processes in `vec` to terminate. */ /* Time out after `timeout` msec, or never if timeout == -1 */ /* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */ int process_wait(process_info_t* vec, int n, int timeout) { int i; int r; int retval; process_info_t* p; dowait_args args; pthread_t tid; pthread_attr_t attr; unsigned int elapsed_ms; struct timeval timebase; struct timeval tv; fd_set fds; args.vec = vec; args.n = n; args.pipe[0] = -1; args.pipe[1] = -1; /* The simple case is where there is no timeout */ if (timeout == -1) { dowait(&args); return 0; } /* Hard case. Do the wait with a timeout. * * Assumption: we are the only ones making this call right now. Otherwise * we'd need to lock vec. */ r = pipe((int*)&(args.pipe)); if (r) { perror("pipe()"); return -1; } if (pthread_attr_init(&attr)) abort(); if (pthread_attr_setstacksize(&attr, 256 * 1024)) abort(); r = pthread_create(&tid, &attr, dowait, &args); if (pthread_attr_destroy(&attr)) abort(); if (r) { perror("pthread_create()"); retval = -1; goto terminate; } if (gettimeofday(&timebase, NULL)) abort(); tv = timebase; for (;;) { /* Check that gettimeofday() doesn't jump back in time. */ assert(tv.tv_sec == timebase.tv_sec || (tv.tv_sec == timebase.tv_sec && tv.tv_usec >= timebase.tv_usec)); elapsed_ms = (tv.tv_sec - timebase.tv_sec) * 1000 + (tv.tv_usec / 1000) - (timebase.tv_usec / 1000); r = 0; /* Timeout. */ if (elapsed_ms >= (unsigned) timeout) break; tv.tv_sec = (timeout - elapsed_ms) / 1000; tv.tv_usec = (timeout - elapsed_ms) % 1000 * 1000; FD_ZERO(&fds); FD_SET(args.pipe[0], &fds); r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv); if (!(r == -1 && errno == EINTR)) break; if (gettimeofday(&tv, NULL)) abort(); } if (r == -1) { perror("select()"); retval = -1; } else if (r) { /* The thread completed successfully. */ retval = 0; } else { /* Timeout. Kill all the children. */ for (i = 0; i < n; i++) { p = (process_info_t*)(vec + i * sizeof(process_info_t)); kill(p->pid, SIGTERM); } retval = -2; } if (pthread_join(tid, NULL)) abort(); terminate: close(args.pipe[0]); close(args.pipe[1]); return retval; } /* Returns the number of bytes in the stdio output buffer for process `p`. */ long int process_output_size(process_info_t *p) { /* Size of the p->stdout_file */ struct stat buf; int r = fstat(fileno(p->stdout_file), &buf); if (r < 0) { return -1; } return (long)buf.st_size; } /* Copy the contents of the stdio output buffer to `fd`. */ int process_copy_output(process_info_t *p, int fd) { ssize_t nwritten; char buf[1024]; int r; r = fseek(p->stdout_file, 0, SEEK_SET); if (r < 0) { perror("fseek"); return -1; } /* TODO: what if the line is longer than buf */ while (fgets(buf, sizeof(buf), p->stdout_file) != NULL) { /* TODO: what if write doesn't write the whole buffer... */ nwritten = 0; if (tap_output) nwritten += write(fd, "#", 1); nwritten += write(fd, buf, strlen(buf)); if (nwritten < 0) { perror("write"); return -1; } } if (ferror(p->stdout_file)) { perror("read"); return -1; } return 0; } /* Copy the last line of the stdio output buffer to `buffer` */ int process_read_last_line(process_info_t *p, char* buffer, size_t buffer_len) { char* ptr; int r = fseek(p->stdout_file, 0, SEEK_SET); if (r < 0) { perror("fseek"); return -1; } buffer[0] = '\0'; while (fgets(buffer, buffer_len, p->stdout_file) != NULL) { for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++); *ptr = '\0'; } if (ferror(p->stdout_file)) { perror("read"); buffer[0] = '\0'; return -1; } return 0; } /* Return the name that was specified when `p` was started by process_start */ char* process_get_name(process_info_t *p) { return p->name; } /* Terminate process `p`. */ int process_terminate(process_info_t *p) { return kill(p->pid, SIGTERM); } /* Return the exit code of process p. */ /* On error, return -1. */ int process_reap(process_info_t *p) { if (WIFEXITED(p->status)) { return WEXITSTATUS(p->status); } else { return p->status; /* ? */ } } /* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ void process_cleanup(process_info_t *p) { fclose(p->stdout_file); free(p->name); } /* Move the console cursor one line up and back to the first column. */ void rewind_cursor(void) { fprintf(stderr, "\033[2K\r"); } /* Pause the calling thread for a number of milliseconds. */ void uv_sleep(int msec) { usleep(msec * 1000); } libuv-v1.8.0/test/runner-unix.h000066400000000000000000000026061263361716600164700ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef TEST_RUNNER_UNIX_H #define TEST_RUNNER_UNIX_H #include #include /* FILE */ typedef struct { FILE* stdout_file; pid_t pid; char* name; int status; int terminated; } process_info_t; #endif /* TEST_RUNNER_UNIX_H */ libuv-v1.8.0/test/runner-win.c000066400000000000000000000214311263361716600162720ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #if !defined(__MINGW32__) # include #endif #include "task.h" #include "runner.h" /* * Define the stuff that MinGW doesn't have */ #ifndef GetFileSizeEx WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize); #endif /* Do platform-specific initialization. */ int platform_init(int argc, char **argv) { const char* tap; tap = getenv("UV_TAP_OUTPUT"); tap_output = (tap != NULL && atoi(tap) > 0); /* Disable the "application crashed" popup. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); #if !defined(__MINGW32__) _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif _setmode(0, _O_BINARY); _setmode(1, _O_BINARY); _setmode(2, _O_BINARY); /* Disable stdio output buffering. */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); strcpy(executable_path, argv[0]); return 0; } int process_start(char *name, char *part, process_info_t *p, int is_helper) { HANDLE file = INVALID_HANDLE_VALUE; HANDLE nul = INVALID_HANDLE_VALUE; WCHAR path[MAX_PATH], filename[MAX_PATH]; WCHAR image[MAX_PATH + 1]; WCHAR args[MAX_PATH * 2]; STARTUPINFOW si; PROCESS_INFORMATION pi; DWORD result; if (GetTempPathW(sizeof(path) / sizeof(WCHAR), (WCHAR*)&path) == 0) goto error; if (GetTempFileNameW((WCHAR*)&path, L"uv", 0, (WCHAR*)&filename) == 0) goto error; file = CreateFileW((WCHAR*)filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (file == INVALID_HANDLE_VALUE) goto error; if (!SetHandleInformation(file, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) goto error; nul = CreateFileA("nul", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (nul == INVALID_HANDLE_VALUE) goto error; if (!SetHandleInformation(nul, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) goto error; result = GetModuleFileNameW(NULL, (WCHAR*) &image, sizeof(image) / sizeof(WCHAR)); if (result == 0 || result == sizeof(image)) goto error; if (part) { if (_snwprintf((WCHAR*)args, sizeof(args) / sizeof(WCHAR), L"\"%s\" %S %S", image, name, part) < 0) { goto error; } } else { if (_snwprintf((WCHAR*)args, sizeof(args) / sizeof(WCHAR), L"\"%s\" %S", image, name) < 0) { goto error; } } memset((void*)&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = nul; si.hStdOutput = file; si.hStdError = file; if (!CreateProcessW(image, args, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) goto error; CloseHandle(pi.hThread); SetHandleInformation(nul, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(file, HANDLE_FLAG_INHERIT, 0); p->stdio_in = nul; p->stdio_out = file; p->process = pi.hProcess; p->name = part; return 0; error: if (file != INVALID_HANDLE_VALUE) CloseHandle(file); if (nul != INVALID_HANDLE_VALUE) CloseHandle(nul); return -1; } /* Timeout is is msecs. Set timeout < 0 to never time out. */ /* Returns 0 when all processes are terminated, -2 on timeout. */ int process_wait(process_info_t *vec, int n, int timeout) { int i; HANDLE handles[MAXIMUM_WAIT_OBJECTS]; DWORD timeout_api, result; /* If there's nothing to wait for, return immediately. */ if (n == 0) return 0; ASSERT(n <= MAXIMUM_WAIT_OBJECTS); for (i = 0; i < n; i++) handles[i] = vec[i].process; if (timeout >= 0) { timeout_api = (DWORD)timeout; } else { timeout_api = INFINITE; } result = WaitForMultipleObjects(n, handles, TRUE, timeout_api); if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n) { /* All processes are terminated. */ return 0; } if (result == WAIT_TIMEOUT) { return -2; } return -1; } long int process_output_size(process_info_t *p) { LARGE_INTEGER size; if (!GetFileSizeEx(p->stdio_out, &size)) return -1; return (long int)size.QuadPart; } int process_copy_output(process_info_t *p, int fd) { DWORD read; char buf[1024]; char *line, *start; if (SetFilePointer(p->stdio_out, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { return -1; } if (tap_output) write(fd, "#", 1); while (ReadFile(p->stdio_out, (void*)&buf, sizeof(buf), &read, NULL) && read > 0) { if (tap_output) { start = buf; while ((line = strchr(start, '\n')) != NULL) { write(fd, start, line - start + 1); write(fd, "#", 1); start = line + 1; } if (start < buf + read) write(fd, start, buf + read - start); } else { write(fd, buf, read); } } if (tap_output) write(fd, "\n", 1); if (GetLastError() != ERROR_HANDLE_EOF) return -1; return 0; } int process_read_last_line(process_info_t *p, char * buffer, size_t buffer_len) { DWORD size; DWORD read; DWORD start; OVERLAPPED overlapped; ASSERT(buffer_len > 0); size = GetFileSize(p->stdio_out, NULL); if (size == INVALID_FILE_SIZE) return -1; if (size == 0) { buffer[0] = '\0'; return 1; } memset(&overlapped, 0, sizeof overlapped); if (size >= buffer_len) overlapped.Offset = size - buffer_len - 1; if (!ReadFile(p->stdio_out, buffer, buffer_len - 1, &read, &overlapped)) return -1; for (start = read - 1; start >= 0; start--) { if (buffer[start] == '\n' || buffer[start] == '\r') break; } if (start > 0) memmove(buffer, buffer + start, read - start); buffer[read - start] = '\0'; return 0; } char* process_get_name(process_info_t *p) { return p->name; } int process_terminate(process_info_t *p) { if (!TerminateProcess(p->process, 1)) return -1; return 0; } int process_reap(process_info_t *p) { DWORD exitCode; if (!GetExitCodeProcess(p->process, &exitCode)) return -1; return (int)exitCode; } void process_cleanup(process_info_t *p) { CloseHandle(p->process); CloseHandle(p->stdio_in); CloseHandle(p->stdio_out); } static int clear_line() { HANDLE handle; CONSOLE_SCREEN_BUFFER_INFO info; COORD coord; DWORD written; handle = (HANDLE)_get_osfhandle(fileno(stderr)); if (handle == INVALID_HANDLE_VALUE) return -1; if (!GetConsoleScreenBufferInfo(handle, &info)) return -1; coord = info.dwCursorPosition; if (coord.Y <= 0) return -1; coord.X = 0; if (!SetConsoleCursorPosition(handle, coord)) return -1; if (!FillConsoleOutputCharacterW(handle, 0x20, info.dwSize.X, coord, &written)) { return -1; } return 0; } void rewind_cursor() { if (clear_line() == -1) { /* If clear_line fails (stdout is not a console), print a newline. */ fprintf(stderr, "\n"); } } /* Pause the calling thread for a number of milliseconds. */ void uv_sleep(int msec) { Sleep(msec); } libuv-v1.8.0/test/runner-win.h000066400000000000000000000030201263361716600162710ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* Don't complain about write(), fileno() etc. being deprecated. */ #pragma warning(disable : 4996) #include #include #include #if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 extern int snprintf(char*, size_t, const char*, ...); #endif typedef struct { HANDLE process; HANDLE stdio_in; HANDLE stdio_out; char *name; } process_info_t; libuv-v1.8.0/test/runner.c000066400000000000000000000256701263361716600155100ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include "runner.h" #include "task.h" #include "uv.h" char executable_path[sizeof(executable_path)]; int tap_output = 0; static void log_progress(int total, int passed, int failed, int todos, int skipped, const char* name) { int progress; if (total == 0) total = 1; progress = 100 * (passed + failed + skipped + todos) / total; fprintf(stderr, "[%% %3d|+ %3d|- %3d|T %3d|S %3d]: %s", progress, passed, failed, todos, skipped, name); fflush(stderr); } const char* fmt(double d) { static char buf[1024]; static char* p; uint64_t v; if (p == NULL) p = buf; p += 31; if (p >= buf + sizeof(buf)) return ""; v = (uint64_t) d; #if 0 /* works but we don't care about fractional precision */ if (d - v >= 0.01) { *--p = '0' + (uint64_t) (d * 100) % 10; *--p = '0' + (uint64_t) (d * 10) % 10; *--p = '.'; } #endif if (v == 0) *--p = '0'; while (v) { if (v) *--p = '0' + (v % 10), v /= 10; if (v) *--p = '0' + (v % 10), v /= 10; if (v) *--p = '0' + (v % 10), v /= 10; if (v) *--p = ','; } return p; } int run_tests(int benchmark_output) { int total; int passed; int failed; int todos; int skipped; int current; int test_result; task_entry_t* task; /* Count the number of tests. */ total = 0; for (task = TASKS; task->main; task++) { if (!task->is_helper) { total++; } } if (tap_output) { fprintf(stderr, "1..%d\n", total); fflush(stderr); } /* Run all tests. */ passed = 0; failed = 0; todos = 0; skipped = 0; current = 1; for (task = TASKS; task->main; task++) { if (task->is_helper) { continue; } if (!tap_output) rewind_cursor(); if (!benchmark_output && !tap_output) { log_progress(total, passed, failed, todos, skipped, task->task_name); } test_result = run_test(task->task_name, benchmark_output, current); switch (test_result) { case TEST_OK: passed++; break; case TEST_TODO: todos++; break; case TEST_SKIP: skipped++; break; default: failed++; } current++; } if (!tap_output) rewind_cursor(); if (!benchmark_output && !tap_output) { log_progress(total, passed, failed, todos, skipped, "Done.\n"); } return failed; } void log_tap_result(int test_count, const char* test, int status, process_info_t* process) { const char* result; const char* directive; char reason[1024]; switch (status) { case TEST_OK: result = "ok"; directive = ""; break; case TEST_TODO: result = "not ok"; directive = " # TODO "; break; case TEST_SKIP: result = "ok"; directive = " # SKIP "; break; default: result = "not ok"; directive = ""; } if ((status == TEST_SKIP || status == TEST_TODO) && process_output_size(process) > 0) { process_read_last_line(process, reason, sizeof reason); } else { reason[0] = '\0'; } fprintf(stderr, "%s %d - %s%s%s\n", result, test_count, test, directive, reason); fflush(stderr); } int run_test(const char* test, int benchmark_output, int test_count) { char errmsg[1024] = "no error"; process_info_t processes[1024]; process_info_t *main_proc; task_entry_t* task; int process_count; int result; int status; int i; status = 255; main_proc = NULL; process_count = 0; #ifndef _WIN32 /* Clean up stale socket from previous run. */ remove(TEST_PIPENAME); remove(TEST_PIPENAME_2); remove(TEST_PIPENAME_3); #endif /* If it's a helper the user asks for, start it directly. */ for (task = TASKS; task->main; task++) { if (task->is_helper && strcmp(test, task->process_name) == 0) { return task->main(); } } /* Start the helpers first. */ for (task = TASKS; task->main; task++) { if (strcmp(test, task->task_name) != 0) { continue; } /* Skip the test itself. */ if (!task->is_helper) { continue; } if (process_start(task->task_name, task->process_name, &processes[process_count], 1 /* is_helper */) == -1) { snprintf(errmsg, sizeof errmsg, "Process `%s` failed to start.", task->process_name); goto out; } process_count++; } /* Give the helpers time to settle. Race-y, fix this. */ uv_sleep(250); /* Now start the test itself. */ for (task = TASKS; task->main; task++) { if (strcmp(test, task->task_name) != 0) { continue; } if (task->is_helper) { continue; } if (process_start(task->task_name, task->process_name, &processes[process_count], 0 /* !is_helper */) == -1) { snprintf(errmsg, sizeof errmsg, "Process `%s` failed to start.", task->process_name); goto out; } main_proc = &processes[process_count]; process_count++; break; } if (main_proc == NULL) { snprintf(errmsg, sizeof errmsg, "No test with that name: %s", test); goto out; } result = process_wait(main_proc, 1, task->timeout); if (result == -1) { FATAL("process_wait failed"); } else if (result == -2) { /* Don't have to clean up the process, process_wait() has killed it. */ snprintf(errmsg, sizeof errmsg, "timeout"); goto out; } status = process_reap(main_proc); if (status != TEST_OK) { snprintf(errmsg, sizeof errmsg, "exit code %d", status); goto out; } if (benchmark_output) { /* Give the helpers time to clean up their act. */ uv_sleep(1000); } out: /* Reap running processes except the main process, it's already dead. */ for (i = 0; i < process_count - 1; i++) { process_terminate(&processes[i]); } if (process_count > 0 && process_wait(processes, process_count - 1, -1) < 0) { FATAL("process_wait failed"); } if (tap_output) log_tap_result(test_count, test, status, &processes[i]); /* Show error and output from processes if the test failed. */ if (status != 0 || task->show_output) { if (tap_output) { fprintf(stderr, "#"); } else if (status == TEST_TODO) { fprintf(stderr, "\n`%s` todo\n", test); } else if (status == TEST_SKIP) { fprintf(stderr, "\n`%s` skipped\n", test); } else if (status != 0) { fprintf(stderr, "\n`%s` failed: %s\n", test, errmsg); } else { fprintf(stderr, "\n"); } fflush(stderr); for (i = 0; i < process_count; i++) { switch (process_output_size(&processes[i])) { case -1: fprintf(stderr, "Output from process `%s`: (unavailable)\n", process_get_name(&processes[i])); fflush(stderr); break; case 0: fprintf(stderr, "Output from process `%s`: (no output)\n", process_get_name(&processes[i])); fflush(stderr); break; default: fprintf(stderr, "Output from process `%s`:\n", process_get_name(&processes[i])); fflush(stderr); process_copy_output(&processes[i], fileno(stderr)); break; } } if (!tap_output) { fprintf(stderr, "=============================================================\n"); } /* In benchmark mode show concise output from the main process. */ } else if (benchmark_output) { switch (process_output_size(main_proc)) { case -1: fprintf(stderr, "%s: (unavailable)\n", test); fflush(stderr); break; case 0: fprintf(stderr, "%s: (no output)\n", test); fflush(stderr); break; default: for (i = 0; i < process_count; i++) { process_copy_output(&processes[i], fileno(stderr)); } break; } } /* Clean up all process handles. */ for (i = 0; i < process_count; i++) { process_cleanup(&processes[i]); } return status; } /* Returns the status code of the task part * or 255 if no matching task was not found. */ int run_test_part(const char* test, const char* part) { task_entry_t* task; int r; for (task = TASKS; task->main; task++) { if (strcmp(test, task->task_name) == 0 && strcmp(part, task->process_name) == 0) { r = task->main(); return r; } } fprintf(stderr, "No test part with that name: %s:%s\n", test, part); fflush(stderr); return 255; } static int compare_task(const void* va, const void* vb) { const task_entry_t* a = va; const task_entry_t* b = vb; return strcmp(a->task_name, b->task_name); } static int find_helpers(const task_entry_t* task, const task_entry_t** helpers) { const task_entry_t* helper; int n_helpers; for (n_helpers = 0, helper = TASKS; helper->main; helper++) { if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) { *helpers++ = helper; n_helpers++; } } return n_helpers; } void print_tests(FILE* stream) { const task_entry_t* helpers[1024]; const task_entry_t* task; int n_helpers; int n_tasks; int i; for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++); qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task); for (task = TASKS; task->main; task++) { if (task->is_helper) { continue; } n_helpers = find_helpers(task, helpers); if (n_helpers) { printf("%-25s (helpers:", task->task_name); for (i = 0; i < n_helpers; i++) { printf(" %s", helpers[i]->process_name); } printf(")\n"); } else { printf("%s\n", task->task_name); } } } libuv-v1.8.0/test/runner.h000066400000000000000000000123061263361716600155050ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef RUNNER_H_ #define RUNNER_H_ #include /* PATH_MAX */ #include /* FILE */ /* * The maximum number of processes (main + helpers) that a test / benchmark * can have. */ #define MAX_PROCESSES 8 /* * Struct to store both tests and to define helper processes for tasks. */ typedef struct { char *task_name; char *process_name; int (*main)(void); int is_helper; int show_output; /* * The time in milliseconds after which a single test or benchmark times out. */ int timeout; } task_entry_t, bench_entry_t; /* * Macros used by test-list.h and benchmark-list.h. */ #define TASK_LIST_START \ task_entry_t TASKS[] = { #define TASK_LIST_END \ { 0, 0, 0, 0, 0, 0 } \ }; #define TEST_DECLARE(name) \ int run_test_##name(void); #define TEST_ENTRY(name) \ { #name, #name, &run_test_##name, 0, 0, 5000 }, #define TEST_ENTRY_CUSTOM(name, is_helper, show_output, timeout) \ { #name, #name, &run_test_##name, is_helper, show_output, timeout }, #define BENCHMARK_DECLARE(name) \ int run_benchmark_##name(void); #define BENCHMARK_ENTRY(name) \ { #name, #name, &run_benchmark_##name, 0, 0, 60000 }, #define HELPER_DECLARE(name) \ int run_helper_##name(void); #define HELPER_ENTRY(task_name, name) \ { #task_name, #name, &run_helper_##name, 1, 0, 0 }, #define TEST_HELPER HELPER_ENTRY #define BENCHMARK_HELPER HELPER_ENTRY #ifdef PATH_MAX extern char executable_path[PATH_MAX]; #else extern char executable_path[4096]; #endif /* * Include platform-dependent definitions */ #ifdef _WIN32 # include "runner-win.h" #else # include "runner-unix.h" #endif /* The array that is filled by test-list.h or benchmark-list.h */ extern task_entry_t TASKS[]; /* * Run all tests. */ int run_tests(int benchmark_output); /* * Run a single test. Starts up any helpers. */ int run_test(const char* test, int benchmark_output, int test_count); /* * Run a test part, i.e. the test or one of its helpers. */ int run_test_part(const char* test, const char* part); /* * Print tests in sorted order to `stream`. Used by `./run-tests --list`. */ void print_tests(FILE* stream); /* * Stuff that should be implemented by test-runner-.h * All functions return 0 on success, -1 on failure, unless specified * otherwise. */ /* Do platform-specific initialization. */ int platform_init(int argc, char** argv); /* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ /* Make sure that all stdio output of the processes is buffered up. */ int process_start(char *name, char* part, process_info_t *p, int is_helper); /* Wait for all `n` processes in `vec` to terminate. */ /* Time out after `timeout` msec, or never if timeout == -1 */ /* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */ int process_wait(process_info_t *vec, int n, int timeout); /* Returns the number of bytes in the stdio output buffer for process `p`. */ long int process_output_size(process_info_t *p); /* Copy the contents of the stdio output buffer to `fd`. */ int process_copy_output(process_info_t *p, int fd); /* Copy the last line of the stdio output buffer to `buffer` */ int process_read_last_line(process_info_t *p, char * buffer, size_t buffer_len); /* Return the name that was specified when `p` was started by process_start */ char* process_get_name(process_info_t *p); /* Terminate process `p`. */ int process_terminate(process_info_t *p); /* Return the exit code of process p. */ /* On error, return -1. */ int process_reap(process_info_t *p); /* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ void process_cleanup(process_info_t *p); /* Move the console cursor one line up and back to the first column. */ void rewind_cursor(void); /* trigger output as tap */ extern int tap_output; #endif /* RUNNER_H_ */ libuv-v1.8.0/test/task.h000066400000000000000000000170121263361716600151350ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef TASK_H_ #define TASK_H_ #include "uv.h" #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "stdint-msvc2008.h" #else # include #endif #if !defined(_WIN32) # include # include /* setrlimit() */ #endif #ifdef __clang__ # pragma clang diagnostic ignored "-Wvariadic-macros" # pragma clang diagnostic ignored "-Wc99-extensions" #endif #define TEST_PORT 9123 #define TEST_PORT_2 9124 #ifdef _WIN32 # define TEST_PIPENAME "\\\\?\\pipe\\uv-test" # define TEST_PIPENAME_2 "\\\\?\\pipe\\uv-test2" # define TEST_PIPENAME_3 "\\\\?\\pipe\\uv-test3" #else # define TEST_PIPENAME "/tmp/uv-test-sock" # define TEST_PIPENAME_2 "/tmp/uv-test-sock2" # define TEST_PIPENAME_3 "/tmp/uv-test-sock3" #endif #ifdef _WIN32 # include # ifndef S_IRUSR # define S_IRUSR _S_IREAD # endif # ifndef S_IWUSR # define S_IWUSR _S_IWRITE # endif #endif #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) typedef enum { TCP = 0, UDP, PIPE } stream_type; /* Die with fatal error. */ #define FATAL(msg) \ do { \ fprintf(stderr, \ "Fatal error in %s on line %d: %s\n", \ __FILE__, \ __LINE__, \ msg); \ fflush(stderr); \ abort(); \ } while (0) /* Have our own assert, so we are sure it does not get optimized away in * a release build. */ #define ASSERT(expr) \ do { \ if (!(expr)) { \ fprintf(stderr, \ "Assertion failed in %s on line %d: %s\n", \ __FILE__, \ __LINE__, \ #expr); \ abort(); \ } \ } while (0) /* This macro cleans up the main loop. This is used to avoid valgrind * warnings about memory being "leaked" by the main event loop. */ #define MAKE_VALGRIND_HAPPY() \ do { \ close_loop(uv_default_loop()); \ uv_loop_delete(uv_default_loop()); \ } while (0) /* Just sugar for wrapping the main() for a task or helper. */ #define TEST_IMPL(name) \ int run_test_##name(void); \ int run_test_##name(void) #define BENCHMARK_IMPL(name) \ int run_benchmark_##name(void); \ int run_benchmark_##name(void) #define HELPER_IMPL(name) \ int run_helper_##name(void); \ int run_helper_##name(void) /* Pause the calling thread for a number of milliseconds. */ void uv_sleep(int msec); /* Format big numbers nicely. WARNING: leaks memory. */ const char* fmt(double d); /* Reserved test exit codes. */ enum test_status { TEST_OK = 0, TEST_TODO, TEST_SKIP }; #define RETURN_OK() \ do { \ return TEST_OK; \ } while (0) #define RETURN_TODO(explanation) \ do { \ fprintf(stderr, "%s\n", explanation); \ fflush(stderr); \ return TEST_TODO; \ } while (0) #define RETURN_SKIP(explanation) \ do { \ fprintf(stderr, "%s\n", explanation); \ fflush(stderr); \ return TEST_SKIP; \ } while (0) #if !defined(_WIN32) # define TEST_FILE_LIMIT(num) \ do { \ struct rlimit lim; \ lim.rlim_cur = (num); \ lim.rlim_max = lim.rlim_cur; \ if (setrlimit(RLIMIT_NOFILE, &lim)) \ RETURN_SKIP("File descriptor limit too low."); \ } while (0) #else /* defined(_WIN32) */ # define TEST_FILE_LIMIT(num) do {} while (0) #endif #if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 extern int snprintf(char*, size_t, const char*, ...); #endif #if defined(__clang__) || \ defined(__GNUC__) || \ defined(__INTEL_COMPILER) || \ defined(__SUNPRO_C) # define UNUSED __attribute__((unused)) #else # define UNUSED #endif /* Fully close a loop */ static void close_walk_cb(uv_handle_t* handle, void* arg) { if (!uv_is_closing(handle)) uv_close(handle, NULL); } UNUSED static void close_loop(uv_loop_t* loop) { uv_walk(loop, close_walk_cb, NULL); uv_run(loop, UV_RUN_DEFAULT); } UNUSED static int can_ipv6(void) { uv_interface_address_t* addr; int supported; int count; int i; if (uv_interface_addresses(&addr, &count)) return 1; /* Assume IPv6 support on failure. */ supported = 0; for (i = 0; supported == 0 && i < count; i += 1) supported = (AF_INET6 == addr[i].address.address6.sin6_family); uv_free_interface_addresses(addr, count); return supported; } #endif /* TASK_H_ */ libuv-v1.8.0/test/test-active.c000066400000000000000000000047771263361716600164340ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void timer_cb(uv_timer_t* handle) { ASSERT(0 && "timer_cb should not have been called"); } TEST_IMPL(active) { int r; uv_timer_t timer; r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); /* uv_is_active() and uv_is_closing() should always return either 0 or 1. */ ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); r = uv_timer_start(&timer, timer_cb, 1000, 0); ASSERT(r == 0); ASSERT(1 == uv_is_active((uv_handle_t*) &timer)); ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); r = uv_timer_stop(&timer); ASSERT(r == 0); ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); r = uv_timer_start(&timer, timer_cb, 1000, 0); ASSERT(r == 0); ASSERT(1 == uv_is_active((uv_handle_t*) &timer)); ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); uv_close((uv_handle_t*) &timer, close_cb); ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); ASSERT(1 == uv_is_closing((uv_handle_t*) &timer)); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-async-null-cb.c000066400000000000000000000037731263361716600176230ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_async_t async_handle; static uv_check_t check_handle; static int check_cb_called; static uv_thread_t thread; static void thread_cb(void* dummy) { (void) &dummy; uv_async_send(&async_handle); } static void check_cb(uv_check_t* handle) { ASSERT(check_cb_called == 0); uv_close((uv_handle_t*) &async_handle, NULL); uv_close((uv_handle_t*) &check_handle, NULL); check_cb_called++; } TEST_IMPL(async_null_cb) { ASSERT(0 == uv_async_init(uv_default_loop(), &async_handle, NULL)); ASSERT(0 == uv_check_init(uv_default_loop(), &check_handle)); ASSERT(0 == uv_check_start(&check_handle, check_cb)); ASSERT(0 == uv_thread_create(&thread, thread_cb, NULL)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_thread_join(&thread)); ASSERT(1 == check_cb_called); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-async.c000066400000000000000000000066071263361716600162700ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_thread_t thread; static uv_mutex_t mutex; static uv_prepare_t prepare; static uv_async_t async; static volatile int async_cb_called; static int prepare_cb_called; static int close_cb_called; static void thread_cb(void *arg) { int n; int r; for (;;) { uv_mutex_lock(&mutex); n = async_cb_called; uv_mutex_unlock(&mutex); if (n == 3) { break; } r = uv_async_send(&async); ASSERT(r == 0); /* Work around a bug in Valgrind. * * Valgrind runs threads not in parallel but sequentially, i.e. one after * the other. It also doesn't preempt them, instead it depends on threads * yielding voluntarily by making a syscall. * * That never happens here: the pipe that is associated with the async * handle is written to once but that's too early for Valgrind's scheduler * to kick in. Afterwards, the thread busy-loops, starving the main thread. * Therefore, we yield. * * This behavior has been observed with Valgrind 3.7.0 and 3.9.0. */ uv_sleep(0); } } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void async_cb(uv_async_t* handle) { int n; ASSERT(handle == &async); uv_mutex_lock(&mutex); n = ++async_cb_called; uv_mutex_unlock(&mutex); if (n == 3) { uv_close((uv_handle_t*)&async, close_cb); uv_close((uv_handle_t*)&prepare, close_cb); } } static void prepare_cb(uv_prepare_t* handle) { int r; ASSERT(handle == &prepare); if (prepare_cb_called++) return; r = uv_thread_create(&thread, thread_cb, NULL); ASSERT(r == 0); uv_mutex_unlock(&mutex); } TEST_IMPL(async) { int r; r = uv_mutex_init(&mutex); ASSERT(r == 0); uv_mutex_lock(&mutex); r = uv_prepare_init(uv_default_loop(), &prepare); ASSERT(r == 0); r = uv_prepare_start(&prepare, prepare_cb); ASSERT(r == 0); r = uv_async_init(uv_default_loop(), &async, async_cb); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(prepare_cb_called > 0); ASSERT(async_cb_called == 3); ASSERT(close_cb_called == 2); ASSERT(0 == uv_thread_join(&thread)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-barrier.c000066400000000000000000000054731263361716600166010ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include typedef struct { uv_barrier_t barrier; int delay; volatile int posted; int main_barrier_wait_rval; int worker_barrier_wait_rval; } worker_config; static void worker(void* arg) { worker_config* c = arg; if (c->delay) uv_sleep(c->delay); c->worker_barrier_wait_rval = uv_barrier_wait(&c->barrier); } TEST_IMPL(barrier_1) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_sleep(100); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); ASSERT(0 == uv_thread_join(&thread)); uv_barrier_destroy(&wc.barrier); ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; } TEST_IMPL(barrier_2) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); wc.delay = 100; ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); ASSERT(0 == uv_thread_join(&thread)); uv_barrier_destroy(&wc.barrier); ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; } TEST_IMPL(barrier_3) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); ASSERT(0 == uv_thread_join(&thread)); uv_barrier_destroy(&wc.barrier); ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; } libuv-v1.8.0/test/test-callback-order.c000066400000000000000000000043571263361716600200200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int idle_cb_called; static int timer_cb_called; static uv_idle_t idle_handle; static uv_timer_t timer_handle; /* idle_cb should run before timer_cb */ static void idle_cb(uv_idle_t* handle) { ASSERT(idle_cb_called == 0); ASSERT(timer_cb_called == 0); uv_idle_stop(handle); idle_cb_called++; } static void timer_cb(uv_timer_t* handle) { ASSERT(idle_cb_called == 1); ASSERT(timer_cb_called == 0); uv_timer_stop(handle); timer_cb_called++; } static void next_tick(uv_idle_t* handle) { uv_loop_t* loop = handle->loop; uv_idle_stop(handle); uv_idle_init(loop, &idle_handle); uv_idle_start(&idle_handle, idle_cb); uv_timer_init(loop, &timer_handle); uv_timer_start(&timer_handle, timer_cb, 0, 0); } TEST_IMPL(callback_order) { uv_loop_t* loop; uv_idle_t idle; loop = uv_default_loop(); uv_idle_init(loop, &idle); uv_idle_start(&idle, next_tick); ASSERT(idle_cb_called == 0); ASSERT(timer_cb_called == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(idle_cb_called == 1); ASSERT(timer_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-callback-stack.c000066400000000000000000000127441263361716600200110ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * TODO: Add explanation of why we want on_close to be called from fresh * stack. */ #include "uv.h" #include "task.h" static const char MESSAGE[] = "Failure is for the weak. Everyone dies alone."; static uv_tcp_t client; static uv_timer_t timer; static uv_connect_t connect_req; static uv_write_t write_req; static uv_shutdown_t shutdown_req; static int nested = 0; static int close_cb_called = 0; static int connect_cb_called = 0; static int write_cb_called = 0; static int timer_cb_called = 0; static int bytes_received = 0; static int shutdown_cb_called = 0; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->len = size; buf->base = malloc(size); ASSERT(buf->base != NULL); } static void close_cb(uv_handle_t* handle) { ASSERT(nested == 0 && "close_cb must be called from a fresh stack"); close_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack"); shutdown_cb_called++; } static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ASSERT(nested == 0 && "read_cb must be called from a fresh stack"); printf("Read. nread == %d\n", (int)nread); free(buf->base); if (nread == 0) { return; } else if (nread < 0) { ASSERT(nread == UV_EOF); nested++; uv_close((uv_handle_t*)tcp, close_cb); nested--; return; } bytes_received += nread; /* We call shutdown here because when bytes_received == sizeof MESSAGE */ /* there will be no more data sent nor received, so here it would be */ /* possible for a backend to to call shutdown_cb immediately and *not* */ /* from a fresh stack. */ if (bytes_received == sizeof MESSAGE) { nested++; puts("Shutdown"); if (uv_shutdown(&shutdown_req, (uv_stream_t*)tcp, shutdown_cb)) { FATAL("uv_shutdown failed"); } nested--; } } static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer); ASSERT(nested == 0 && "timer_cb must be called from a fresh stack"); puts("Timeout complete. Now read data..."); nested++; if (uv_read_start((uv_stream_t*)&client, alloc_cb, read_cb)) { FATAL("uv_read_start failed"); } nested--; timer_cb_called++; uv_close((uv_handle_t*)handle, close_cb); } static void write_cb(uv_write_t* req, int status) { int r; ASSERT(status == 0); ASSERT(nested == 0 && "write_cb must be called from a fresh stack"); puts("Data written. 500ms timeout..."); /* After the data has been sent, we're going to wait for a while, then */ /* start reading. This makes us certain that the message has been echoed */ /* back to our receive buffer when we start reading. This maximizes the */ /* temptation for the backend to use dirty stack for calling read_cb. */ nested++; r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 500, 0); ASSERT(r == 0); nested--; write_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { uv_buf_t buf; puts("Connected. Write some data to echo server..."); ASSERT(status == 0); ASSERT(nested == 0 && "connect_cb must be called from a fresh stack"); nested++; buf.base = (char*) &MESSAGE; buf.len = sizeof MESSAGE; if (uv_write(&write_req, (uv_stream_t*)req->handle, &buf, 1, write_cb)) { FATAL("uv_write failed"); } nested--; connect_cb_called++; } TEST_IMPL(callback_stack) { struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); if (uv_tcp_init(uv_default_loop(), &client)) { FATAL("uv_tcp_init failed"); } puts("Connecting..."); nested++; if (uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb)) { FATAL("uv_tcp_connect failed"); } nested--; uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(nested == 0); ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once"); ASSERT(write_cb_called == 1 && "write_cb must be called exactly once"); ASSERT(timer_cb_called == 1 && "timer_cb must be called exactly once"); ASSERT(bytes_received == sizeof MESSAGE); ASSERT(shutdown_cb_called == 1 && "shutdown_cb must be called exactly once"); ASSERT(close_cb_called == 2 && "close_cb must be called exactly twice"); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-close-fd.c000066400000000000000000000051401263361716600166360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if !defined(_WIN32) #include "uv.h" #include "task.h" #include #include static unsigned int read_cb_called; static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { static char slab[1]; buf->base = slab; buf->len = sizeof(slab); } static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { switch (++read_cb_called) { case 1: ASSERT(nread == 1); uv_read_stop(handle); break; case 2: ASSERT(nread == UV_EOF); uv_close((uv_handle_t *) handle, NULL); break; default: ASSERT(!"read_cb_called > 2"); } } TEST_IMPL(close_fd) { uv_pipe_t pipe_handle; int fd[2]; ASSERT(0 == pipe(fd)); ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */ ASSERT(1 == write(fd[1], "", 1)); ASSERT(0 == close(fd[1])); fd[1] = -1; ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(1 == read_cb_called); ASSERT(0 == uv_is_active((const uv_handle_t *) &pipe_handle)); ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(2 == read_cb_called); ASSERT(0 != uv_is_closing((const uv_handle_t *) &pipe_handle)); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !defined(_WIN32) */ libuv-v1.8.0/test/test-close-order.c000066400000000000000000000047001263361716600173610ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int check_cb_called; static int timer_cb_called; static int close_cb_called; static uv_check_t check_handle; static uv_timer_t timer_handle1; static uv_timer_t timer_handle2; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } /* check_cb should run before any close_cb */ static void check_cb(uv_check_t* handle) { ASSERT(check_cb_called == 0); ASSERT(timer_cb_called == 1); ASSERT(close_cb_called == 0); uv_close((uv_handle_t*) handle, close_cb); uv_close((uv_handle_t*) &timer_handle2, close_cb); check_cb_called++; } static void timer_cb(uv_timer_t* handle) { uv_close((uv_handle_t*) handle, close_cb); timer_cb_called++; } TEST_IMPL(close_order) { uv_loop_t* loop; loop = uv_default_loop(); uv_check_init(loop, &check_handle); uv_check_start(&check_handle, check_cb); uv_timer_init(loop, &timer_handle1); uv_timer_start(&timer_handle1, timer_cb, 0, 0); uv_timer_init(loop, &timer_handle2); uv_timer_start(&timer_handle2, timer_cb, 100000, 0); ASSERT(check_cb_called == 0); ASSERT(close_cb_called == 0); ASSERT(timer_cb_called == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(check_cb_called == 1); ASSERT(close_cb_called == 3); ASSERT(timer_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-condvar.c000066400000000000000000000101251263361716600165750ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include typedef struct { uv_mutex_t mutex; uv_cond_t cond; int delay; int use_broadcast; volatile int posted; } worker_config; static void worker(void* arg) { worker_config* c = arg; if (c->delay) uv_sleep(c->delay); uv_mutex_lock(&c->mutex); ASSERT(c->posted == 0); c->posted = 1; if (c->use_broadcast) uv_cond_broadcast(&c->cond); else uv_cond_signal(&c->cond); uv_mutex_unlock(&c->mutex); } TEST_IMPL(condvar_1) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); ASSERT(0 == uv_cond_init(&wc.cond)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_mutex_lock(&wc.mutex); uv_sleep(100); uv_cond_wait(&wc.cond, &wc.mutex); ASSERT(wc.posted == 1); uv_mutex_unlock(&wc.mutex); ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_cond_destroy(&wc.cond); return 0; } TEST_IMPL(condvar_2) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); wc.delay = 100; ASSERT(0 == uv_cond_init(&wc.cond)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_mutex_lock(&wc.mutex); uv_cond_wait(&wc.cond, &wc.mutex); uv_mutex_unlock(&wc.mutex); ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_cond_destroy(&wc.cond); return 0; } TEST_IMPL(condvar_3) { uv_thread_t thread; worker_config wc; int r; memset(&wc, 0, sizeof(wc)); wc.delay = 100; ASSERT(0 == uv_cond_init(&wc.cond)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_mutex_lock(&wc.mutex); r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(50 * 1e6)); ASSERT(r == UV_ETIMEDOUT); uv_mutex_unlock(&wc.mutex); ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_cond_destroy(&wc.cond); return 0; } TEST_IMPL(condvar_4) { uv_thread_t thread; worker_config wc; int r; memset(&wc, 0, sizeof(wc)); wc.delay = 100; ASSERT(0 == uv_cond_init(&wc.cond)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_mutex_lock(&wc.mutex); r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(150 * 1e6)); ASSERT(r == 0); uv_mutex_unlock(&wc.mutex); ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_cond_destroy(&wc.cond); return 0; } TEST_IMPL(condvar_5) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); wc.use_broadcast = 1; ASSERT(0 == uv_cond_init(&wc.cond)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_mutex_lock(&wc.mutex); uv_sleep(100); uv_cond_wait(&wc.cond, &wc.mutex); ASSERT(wc.posted == 1); uv_mutex_unlock(&wc.mutex); ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_cond_destroy(&wc.cond); return 0; } libuv-v1.8.0/test/test-connection-fail.c000066400000000000000000000100371263361716600202130ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_tcp_t tcp; static uv_connect_t req; static int connect_cb_calls; static int close_cb_calls; static uv_timer_t timer; static int timer_close_cb_calls; static int timer_cb_calls; static void on_close(uv_handle_t* handle) { close_cb_calls++; } static void timer_close_cb(uv_handle_t* handle) { timer_close_cb_calls++; } static void timer_cb(uv_timer_t* handle) { timer_cb_calls++; /* * These are the important asserts. The connection callback has been made, * but libuv hasn't automatically closed the socket. The user must * uv_close the handle manually. */ ASSERT(close_cb_calls == 0); ASSERT(connect_cb_calls == 1); /* Close the tcp handle. */ uv_close((uv_handle_t*)&tcp, on_close); /* Close the timer. */ uv_close((uv_handle_t*)handle, timer_close_cb); } static void on_connect_with_close(uv_connect_t *req, int status) { ASSERT((uv_stream_t*) &tcp == req->handle); ASSERT(status == UV_ECONNREFUSED); connect_cb_calls++; ASSERT(close_cb_calls == 0); uv_close((uv_handle_t*)req->handle, on_close); } static void on_connect_without_close(uv_connect_t *req, int status) { ASSERT(status == UV_ECONNREFUSED); connect_cb_calls++; uv_timer_start(&timer, timer_cb, 100, 0); ASSERT(close_cb_calls == 0); } static void connection_fail(uv_connect_cb connect_cb) { struct sockaddr_in client_addr, server_addr; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); /* There should be no servers listening on this port. */ ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ ASSERT(0 == uv_tcp_bind(&tcp, (const struct sockaddr*) &client_addr, 0)); r = uv_tcp_connect(&req, &tcp, (const struct sockaddr*) &server_addr, connect_cb); ASSERT(!r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_calls == 1); ASSERT(close_cb_calls == 1); } /* * This test attempts to connect to a port where no server is running. We * expect an error. */ TEST_IMPL(connection_fail) { connection_fail(on_connect_with_close); ASSERT(timer_close_cb_calls == 0); ASSERT(timer_cb_calls == 0); MAKE_VALGRIND_HAPPY(); return 0; } /* * This test is the same as the first except it check that the close * callback of the tcp handle hasn't been made after the failed connection * attempt. */ TEST_IMPL(connection_fail_doesnt_auto_close) { int r; r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); connection_fail(on_connect_without_close); ASSERT(timer_close_cb_calls == 1); ASSERT(timer_cb_calls == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-cwd-and-chdir.c000066400000000000000000000032531263361716600175510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #define PATHMAX 1024 extern char executable_path[]; TEST_IMPL(cwd_and_chdir) { char buffer_orig[PATHMAX]; char buffer_new[PATHMAX]; size_t size1; size_t size2; int err; size1 = sizeof buffer_orig; err = uv_cwd(buffer_orig, &size1); ASSERT(err == 0); err = uv_chdir(buffer_orig); ASSERT(err == 0); size2 = sizeof buffer_new; err = uv_cwd(buffer_new, &size2); ASSERT(err == 0); ASSERT(size1 == size2); ASSERT(strcmp(buffer_orig, buffer_new) == 0); return 0; } libuv-v1.8.0/test/test-default-loop-close.c000066400000000000000000000037271263361716600206510ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int timer_cb_called; static void timer_cb(uv_timer_t* timer) { timer_cb_called++; uv_close((uv_handle_t*) timer, NULL); } TEST_IMPL(default_loop_close) { uv_loop_t* loop; uv_timer_t timer_handle; loop = uv_default_loop(); ASSERT(loop != NULL); ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(1 == timer_cb_called); ASSERT(0 == uv_loop_close(loop)); loop = uv_default_loop(); ASSERT(loop != NULL); ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(2 == timer_cb_called); ASSERT(0 == uv_loop_close(loop)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-delayed-accept.c000066400000000000000000000113641263361716600200130ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int connection_cb_called = 0; static int do_accept_called = 0; static int close_cb_called = 0; static int connect_cb_called = 0; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = malloc(size); buf->len = size; } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); free(handle); close_cb_called++; } static void do_accept(uv_timer_t* timer_handle) { uv_tcp_t* server; uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); int r; ASSERT(timer_handle != NULL); ASSERT(accepted_handle != NULL); r = uv_tcp_init(uv_default_loop(), accepted_handle); ASSERT(r == 0); server = (uv_tcp_t*)timer_handle->data; r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle); ASSERT(r == 0); do_accept_called++; /* Immediately close the accepted handle. */ uv_close((uv_handle_t*)accepted_handle, close_cb); /* After accepting the two clients close the server handle */ if (do_accept_called == 2) { uv_close((uv_handle_t*)server, close_cb); } /* Dispose the timer. */ uv_close((uv_handle_t*)timer_handle, close_cb); } static void connection_cb(uv_stream_t* tcp, int status) { int r; uv_timer_t* timer_handle; ASSERT(status == 0); timer_handle = (uv_timer_t*)malloc(sizeof *timer_handle); ASSERT(timer_handle != NULL); /* Accept the client after 1 second */ r = uv_timer_init(uv_default_loop(), timer_handle); ASSERT(r == 0); timer_handle->data = tcp; r = uv_timer_start(timer_handle, do_accept, 1000, 0); ASSERT(r == 0); connection_cb_called++; } static void start_server(void) { struct sockaddr_in addr; uv_tcp_t* server = (uv_tcp_t*)malloc(sizeof *server); int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); ASSERT(server != NULL); r = uv_tcp_init(uv_default_loop(), server); ASSERT(r == 0); r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)server, 128, connection_cb); ASSERT(r == 0); } static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { /* The server will not send anything, it should close gracefully. */ if (buf->base) { free(buf->base); } if (nread >= 0) { ASSERT(nread == 0); } else { ASSERT(tcp != NULL); ASSERT(nread == UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } } static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT(req != NULL); ASSERT(status == 0); /* Not that the server will send anything, but otherwise we'll never know */ /* when the server closes the connection. */ r = uv_read_start((uv_stream_t*)(req->handle), alloc_cb, read_cb); ASSERT(r == 0); connect_cb_called++; free(req); } static void client_connect(void) { struct sockaddr_in addr; uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof *client); uv_connect_t* connect_req = malloc(sizeof *connect_req); int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(client != NULL); ASSERT(connect_req != NULL); r = uv_tcp_init(uv_default_loop(), client); ASSERT(r == 0); r = uv_tcp_connect(connect_req, client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); } TEST_IMPL(delayed_accept) { start_server(); client_connect(); client_connect(); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connection_cb_called == 2); ASSERT(do_accept_called == 2); ASSERT(connect_cb_called == 2); ASSERT(close_cb_called == 7); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-dlerror.c000066400000000000000000000034751263361716600166240ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include TEST_IMPL(dlerror) { const char* path = "test/fixtures/load_error.node"; const char* dlerror_no_error = "no error"; const char* msg; uv_lib_t lib; int r; lib.errmsg = NULL; lib.handle = NULL; msg = uv_dlerror(&lib); ASSERT(msg != NULL); ASSERT(strstr(msg, dlerror_no_error) != NULL); r = uv_dlopen(path, &lib); ASSERT(r == -1); msg = uv_dlerror(&lib); ASSERT(msg != NULL); ASSERT(strstr(msg, dlerror_no_error) == NULL); /* Should return the same error twice in a row. */ msg = uv_dlerror(&lib); ASSERT(msg != NULL); ASSERT(strstr(msg, dlerror_no_error) == NULL); uv_dlclose(&lib); return 0; } libuv-v1.8.0/test/test-embed.c000066400000000000000000000072301263361716600162200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #ifndef HAVE_KQUEUE # if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # define HAVE_KQUEUE 1 # endif #endif #ifndef HAVE_EPOLL # if defined(__linux__) # define HAVE_EPOLL 1 # endif #endif #if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) #if defined(HAVE_KQUEUE) # include # include # include #endif #if defined(HAVE_EPOLL) # include #endif static uv_thread_t embed_thread; static uv_sem_t embed_sem; static uv_timer_t embed_timer; static uv_async_t embed_async; static volatile int embed_closed; static int embed_timer_called; static void embed_thread_runner(void* arg) { int r; int fd; int timeout; while (!embed_closed) { fd = uv_backend_fd(uv_default_loop()); timeout = uv_backend_timeout(uv_default_loop()); do { #if defined(HAVE_KQUEUE) struct timespec ts; ts.tv_sec = timeout / 1000; ts.tv_nsec = (timeout % 1000) * 1000000; r = kevent(fd, NULL, 0, NULL, 0, &ts); #elif defined(HAVE_EPOLL) { struct epoll_event ev; r = epoll_wait(fd, &ev, 1, timeout); } #endif } while (r == -1 && errno == EINTR); uv_async_send(&embed_async); uv_sem_wait(&embed_sem); } } static void embed_cb(uv_async_t* async) { uv_run(uv_default_loop(), UV_RUN_ONCE); uv_sem_post(&embed_sem); } static void embed_timer_cb(uv_timer_t* timer) { embed_timer_called++; embed_closed = 1; uv_close((uv_handle_t*) &embed_async, NULL); } #endif TEST_IMPL(embed) { #if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) uv_loop_t external; ASSERT(0 == uv_loop_init(&external)); embed_timer_called = 0; embed_closed = 0; uv_async_init(&external, &embed_async, embed_cb); /* Start timer in default loop */ uv_timer_init(uv_default_loop(), &embed_timer); uv_timer_start(&embed_timer, embed_timer_cb, 250, 0); /* Start worker that will interrupt external loop */ uv_sem_init(&embed_sem, 0); uv_thread_create(&embed_thread, embed_thread_runner, NULL); /* But run external loop */ uv_run(&external, UV_RUN_DEFAULT); uv_thread_join(&embed_thread); uv_loop_close(&external); ASSERT(embed_timer_called == 1); #endif return 0; } libuv-v1.8.0/test/test-emfile.c000066400000000000000000000073171263361716600164130ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if !defined(_WIN32) #include "uv.h" #include "task.h" #include #include #include static void connection_cb(uv_stream_t* server_handle, int status); static void connect_cb(uv_connect_t* req, int status); static const int maxfd = 31; static unsigned connect_cb_called; static uv_tcp_t server_handle; static uv_tcp_t client_handle; TEST_IMPL(emfile) { struct sockaddr_in addr; struct rlimit limits; uv_connect_t connect_req; uv_loop_t* loop; int first_fd; /* Lower the file descriptor limit and use up all fds save one. */ limits.rlim_cur = limits.rlim_max = maxfd + 1; if (setrlimit(RLIMIT_NOFILE, &limits)) { ASSERT(errno == EPERM); /* Valgrind blocks the setrlimit() call. */ RETURN_SKIP("setrlimit(RLIMIT_NOFILE) failed, running under valgrind?"); } loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(loop, &server_handle)); ASSERT(0 == uv_tcp_init(loop, &client_handle)); ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 8, connection_cb)); /* Remember the first one so we can clean up afterwards. */ do first_fd = dup(0); while (first_fd == -1 && errno == EINTR); ASSERT(first_fd > 0); while (dup(0) != -1 || errno == EINTR); ASSERT(errno == EMFILE); close(maxfd); /* Now connect and use up the last available file descriptor. The EMFILE * handling logic in src/unix/stream.c should ensure that connect_cb() runs * whereas connection_cb() should *not* run. */ ASSERT(0 == uv_tcp_connect(&connect_req, &client_handle, (const struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(1 == connect_cb_called); /* Close the dups again. Ignore errors in the unlikely event that the * file descriptors were not contiguous. */ while (first_fd < maxfd) { close(first_fd); first_fd += 1; } MAKE_VALGRIND_HAPPY(); return 0; } static void connection_cb(uv_stream_t* server_handle, int status) { ASSERT(0 && "connection_cb should not be called."); } static void connect_cb(uv_connect_t* req, int status) { /* |status| should equal 0 because the connection should have been accepted, * it's just that the server immediately closes it again. */ ASSERT(0 == status); connect_cb_called += 1; uv_close((uv_handle_t*) &server_handle, NULL); uv_close((uv_handle_t*) &client_handle, NULL); } #endif /* !defined(_WIN32) */ libuv-v1.8.0/test/test-error.c000066400000000000000000000035631263361716600163020ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include /* * Synthetic errors (errors that originate from within libuv, not the system) * should produce sensible error messages when run through uv_strerror(). * * See https://github.com/joyent/libuv/issues/210 */ TEST_IMPL(error_message) { /* Cop out. Can't do proper checks on systems with * i18n-ized error messages... */ if (strcmp(uv_strerror(0), "Success") != 0) { printf("i18n error messages detected, skipping test.\n"); return 0; } ASSERT(strstr(uv_strerror(UV_EINVAL), "Success") == NULL); ASSERT(strcmp(uv_strerror(1337), "Unknown error") == 0); ASSERT(strcmp(uv_strerror(-1337), "Unknown error") == 0); return 0; } libuv-v1.8.0/test/test-fail-always.c000066400000000000000000000024441263361716600173570ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" TEST_IMPL(fail_always) { /* This test always fails. It is used to test the test runner. */ FATAL("Yes, it always fails"); return 2; } libuv-v1.8.0/test/test-fs-event.c000066400000000000000000000573241263361716600167040ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #ifndef HAVE_KQUEUE # if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # define HAVE_KQUEUE 1 # endif #endif static uv_fs_event_t fs_event; static const char file_prefix[] = "fsevent-"; static const int fs_event_file_count = 16; #if defined(__APPLE__) || defined(_WIN32) static const char file_prefix_in_subdir[] = "subdir"; #endif static uv_timer_t timer; static int timer_cb_called; static int close_cb_called; static int fs_event_created; static int fs_event_removed; static int fs_event_cb_called; #if defined(PATH_MAX) static char fs_event_filename[PATH_MAX]; #else static char fs_event_filename[1024]; #endif /* defined(PATH_MAX) */ static int timer_cb_touch_called; static void create_dir(const char* name) { int r; uv_fs_t req; r = uv_fs_mkdir(NULL, &req, name, 0755, NULL); ASSERT(r == 0 || r == UV_EEXIST); uv_fs_req_cleanup(&req); } static void create_file(const char* name) { int r; uv_file file; uv_fs_t req; r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); file = r; uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); } static void touch_file(const char* name) { int r; uv_file file; uv_fs_t req; uv_buf_t buf; r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL); ASSERT(r >= 0); file = r; uv_fs_req_cleanup(&req); buf = uv_buf_init("foo", 4); r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL); ASSERT(r >= 0); uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void fail_cb(uv_fs_event_t* handle, const char* path, int events, int status) { ASSERT(0 && "fail_cb called"); } static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_RENAME); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strcmp(filename, "file1") == 0); #else ASSERT(filename == NULL || strcmp(filename, "file1") == 0); #endif ASSERT(0 == uv_fs_event_stop(handle)); uv_close((uv_handle_t*)handle, close_cb); } static const char* fs_event_get_filename(int i) { snprintf(fs_event_filename, sizeof(fs_event_filename), "watch_dir/%s%d", file_prefix, i); return fs_event_filename; } static void fs_event_create_files(uv_timer_t* handle) { /* Make sure we're not attempting to create files we do not intend */ ASSERT(fs_event_created < fs_event_file_count); /* Create the file */ create_file(fs_event_get_filename(fs_event_created)); if (++fs_event_created < fs_event_file_count) { /* Create another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ ASSERT(0 == uv_timer_start(&timer, fs_event_create_files, 1, 0)); } } static void fs_event_unlink_files(uv_timer_t* handle) { int r; int i; /* NOTE: handle might be NULL if invoked not as timer callback */ if (handle == NULL) { /* Unlink all files */ for (i = 0; i < 16; i++) { r = remove(fs_event_get_filename(i)); if (handle != NULL) ASSERT(r == 0); } } else { /* Make sure we're not attempting to remove files we do not intend */ ASSERT(fs_event_removed < fs_event_file_count); /* Remove the file */ ASSERT(0 == remove(fs_event_get_filename(fs_event_removed))); if (++fs_event_removed < fs_event_file_count) { /* Remove another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); } } } static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle, const char* filename, int events, int status) { fs_event_cb_called++; ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE || UV_RENAME); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); #else ASSERT(filename == NULL || strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); #endif if (fs_event_created + fs_event_removed == fs_event_file_count) { /* Once we've processed all create events, delete all files */ ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); } else if (fs_event_cb_called == 2 * fs_event_file_count) { /* Once we've processed all create and delete events, stop watching */ uv_close((uv_handle_t*) &timer, close_cb); uv_close((uv_handle_t*) handle, close_cb); } } #if defined(__APPLE__) || defined(_WIN32) static const char* fs_event_get_filename_in_subdir(int i) { snprintf(fs_event_filename, sizeof(fs_event_filename), "watch_dir/subdir/%s%d", file_prefix, i); return fs_event_filename; } static void fs_event_create_files_in_subdir(uv_timer_t* handle) { /* Make sure we're not attempting to create files we do not intend */ ASSERT(fs_event_created < fs_event_file_count); /* Create the file */ create_file(fs_event_get_filename_in_subdir(fs_event_created)); if (++fs_event_created < fs_event_file_count) { /* Create another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ ASSERT(0 == uv_timer_start(&timer, fs_event_create_files_in_subdir, 1, 0)); } } static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) { int r; int i; /* NOTE: handle might be NULL if invoked not as timer callback */ if (handle == NULL) { /* Unlink all files */ for (i = 0; i < 16; i++) { r = remove(fs_event_get_filename_in_subdir(i)); if (handle != NULL) ASSERT(r == 0); } } else { /* Make sure we're not attempting to remove files we do not intend */ ASSERT(fs_event_removed < fs_event_file_count); /* Remove the file */ ASSERT(0 == remove(fs_event_get_filename_in_subdir(fs_event_removed))); if (++fs_event_removed < fs_event_file_count) { /* Remove another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0)); } } } static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle, const char* filename, int events, int status) { fs_event_cb_called++; ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE || UV_RENAME); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strncmp(filename, file_prefix_in_subdir, sizeof(file_prefix_in_subdir) - 1) == 0); #else ASSERT(filename == NULL || strncmp(filename, file_prefix_in_subdir, sizeof(file_prefix_in_subdir) - 1) == 0); #endif if (fs_event_created + fs_event_removed == fs_event_file_count) { /* Once we've processed all create events, delete all files */ ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0)); } else if (fs_event_cb_called == 2 * fs_event_file_count) { /* Once we've processed all create and delete events, stop watching */ uv_close((uv_handle_t*) &timer, close_cb); uv_close((uv_handle_t*) handle, close_cb); } } #endif static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strcmp(filename, "file2") == 0); #else ASSERT(filename == NULL || strcmp(filename, "file2") == 0); #endif ASSERT(0 == uv_fs_event_stop(handle)); uv_close((uv_handle_t*)handle, close_cb); } static void timer_cb_close_handle(uv_timer_t* timer) { uv_handle_t* handle; ASSERT(timer != NULL); handle = timer->data; uv_close((uv_handle_t*)timer, NULL); uv_close((uv_handle_t*)handle, close_cb); } static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { ASSERT(fs_event_cb_called == 0); ++fs_event_cb_called; ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strcmp(filename, "watch_file") == 0); #else ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); #endif /* Regression test for SunOS: touch should generate just one event. */ { static uv_timer_t timer; uv_timer_init(handle->loop, &timer); timer.data = handle; uv_timer_start(&timer, timer_cb_close_handle, 250, 0); } } static void timer_cb_file(uv_timer_t* handle) { ++timer_cb_called; if (timer_cb_called == 1) { touch_file("watch_dir/file1"); } else { touch_file("watch_dir/file2"); uv_close((uv_handle_t*)handle, close_cb); } } static void timer_cb_touch(uv_timer_t* timer) { uv_close((uv_handle_t*)timer, NULL); touch_file("watch_file"); timer_cb_touch_called++; } static void timer_cb_watch_twice(uv_timer_t* handle) { uv_fs_event_t* handles = handle->data; uv_close((uv_handle_t*) (handles + 0), NULL); uv_close((uv_handle_t*) (handles + 1), NULL); uv_close((uv_handle_t*) handle, NULL); } TEST_IMPL(fs_event_watch_dir) { uv_loop_t* loop = uv_default_loop(); int r; /* Setup */ fs_event_unlink_files(NULL); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0); ASSERT(r == 0); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, fs_event_create_files, 100, 0); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed); ASSERT(close_cb_called == 2); /* Cleanup */ fs_event_unlink_files(NULL); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_watch_dir_recursive) { #if defined(__APPLE__) || defined(_WIN32) uv_loop_t* loop; int r; /* Setup */ loop = uv_default_loop(); fs_event_unlink_files(NULL); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/subdir"); remove("watch_dir/"); create_dir("watch_dir"); create_dir("watch_dir/subdir"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file_in_subdir, "watch_dir", UV_FS_EVENT_RECURSIVE); ASSERT(r == 0); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed); ASSERT(close_cb_called == 2); /* Cleanup */ fs_event_unlink_files_in_subdir(NULL); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/subdir"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; #else RETURN_SKIP("Recursive directory watching not supported on this platform."); #endif } TEST_IMPL(fs_event_watch_file) { uv_loop_t* loop = uv_default_loop(); int r; /* Setup */ remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); create_file("watch_dir/file2"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0); ASSERT(r == 0); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb_file, 100, 100); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fs_event_cb_called == 1); ASSERT(timer_cb_called == 2); ASSERT(close_cb_called == 2); /* Cleanup */ remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_watch_file_twice) { const char path[] = "test/fixtures/empty_file"; uv_fs_event_t watchers[2]; uv_timer_t timer; uv_loop_t* loop; loop = uv_default_loop(); timer.data = watchers; ASSERT(0 == uv_fs_event_init(loop, watchers + 0)); ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0)); ASSERT(0 == uv_fs_event_init(loop, watchers + 1)); ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0)); ASSERT(0 == uv_timer_init(loop, &timer)); ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_watch_file_current_dir) { uv_timer_t timer; uv_loop_t* loop; int r; loop = uv_default_loop(); /* Setup */ remove("watch_file"); create_file("watch_file"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_file_current_dir, "watch_file", 0); ASSERT(r == 0); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb_touch, 10, 0); ASSERT(r == 0); ASSERT(timer_cb_touch_called == 0); ASSERT(fs_event_cb_called == 0); ASSERT(close_cb_called == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(timer_cb_touch_called == 1); ASSERT(fs_event_cb_called == 1); ASSERT(close_cb_called == 1); /* Cleanup */ remove("watch_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_no_callback_after_close) { uv_loop_t* loop = uv_default_loop(); int r; /* Setup */ remove("watch_dir/file1"); remove("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file1", 0); ASSERT(r == 0); uv_close((uv_handle_t*)&fs_event, close_cb); touch_file("watch_dir/file1"); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fs_event_cb_called == 0); ASSERT(close_cb_called == 1); /* Cleanup */ remove("watch_dir/file1"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_no_callback_on_close) { uv_loop_t* loop = uv_default_loop(); int r; /* Setup */ remove("watch_dir/file1"); remove("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file1", 0); ASSERT(r == 0); uv_close((uv_handle_t*)&fs_event, close_cb); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fs_event_cb_called == 0); ASSERT(close_cb_called == 1); /* Cleanup */ remove("watch_dir/file1"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } static void fs_event_fail(uv_fs_event_t* handle, const char* filename, int events, int status) { ASSERT(0 && "should never be called"); } static void timer_cb(uv_timer_t* handle) { int r; r = uv_fs_event_init(handle->loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0); ASSERT(r == 0); uv_close((uv_handle_t*)&fs_event, close_cb); uv_close((uv_handle_t*)handle, close_cb); } TEST_IMPL(fs_event_immediate_close) { uv_timer_t timer; uv_loop_t* loop; int r; loop = uv_default_loop(); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 1, 0); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_close_with_pending_event) { uv_loop_t* loop; int r; loop = uv_default_loop(); create_dir("watch_dir"); create_file("watch_dir/file"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0); ASSERT(r == 0); /* Generate an fs event. */ touch_file("watch_dir/file"); uv_close((uv_handle_t*)&fs_event, close_cb); uv_run(loop, UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); /* Clean up */ remove("watch_dir/file"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } #if defined(HAVE_KQUEUE) /* kqueue doesn't register fs events if you don't have an active watcher. * The file descriptor needs to be part of the kqueue set of interest and * that's not the case until we actually enter the event loop. */ TEST_IMPL(fs_event_close_in_callback) { fprintf(stderr, "Skipping test, doesn't work with kqueue.\n"); return 0; } #else /* !HAVE_KQUEUE */ static void fs_event_cb_close(uv_fs_event_t* handle, const char* filename, int events, int status) { ASSERT(status == 0); ASSERT(fs_event_cb_called < 3); ++fs_event_cb_called; if (fs_event_cb_called == 3) { uv_close((uv_handle_t*) handle, close_cb); } } TEST_IMPL(fs_event_close_in_callback) { uv_loop_t* loop; int r; loop = uv_default_loop(); create_dir("watch_dir"); create_file("watch_dir/file1"); create_file("watch_dir/file2"); create_file("watch_dir/file3"); create_file("watch_dir/file4"); create_file("watch_dir/file5"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0); ASSERT(r == 0); /* Generate a couple of fs events. */ touch_file("watch_dir/file1"); touch_file("watch_dir/file2"); touch_file("watch_dir/file3"); touch_file("watch_dir/file4"); touch_file("watch_dir/file5"); uv_run(loop, UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); ASSERT(fs_event_cb_called == 3); /* Clean up */ remove("watch_dir/file1"); remove("watch_dir/file2"); remove("watch_dir/file3"); remove("watch_dir/file4"); remove("watch_dir/file5"); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* HAVE_KQUEUE */ TEST_IMPL(fs_event_start_and_close) { uv_loop_t* loop; uv_fs_event_t fs_event1; uv_fs_event_t fs_event2; int r; loop = uv_default_loop(); create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event1); ASSERT(r == 0); r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0); ASSERT(r == 0); r = uv_fs_event_init(loop, &fs_event2); ASSERT(r == 0); r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0); ASSERT(r == 0); uv_close((uv_handle_t*) &fs_event2, close_cb); uv_close((uv_handle_t*) &fs_event1, close_cb); uv_run(loop, UV_RUN_DEFAULT); ASSERT(close_cb_called == 2); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_getpath) { uv_loop_t* loop = uv_default_loop(); int r; char buf[1024]; size_t len; create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); len = sizeof buf; r = uv_fs_event_getpath(&fs_event, buf, &len); ASSERT(r == UV_EINVAL); r = uv_fs_event_start(&fs_event, fail_cb, "watch_dir", 0); ASSERT(r == 0); len = sizeof buf; r = uv_fs_event_getpath(&fs_event, buf, &len); ASSERT(r == 0); ASSERT(buf[len - 1] != 0); ASSERT(memcmp(buf, "watch_dir", len) == 0); r = uv_fs_event_stop(&fs_event); ASSERT(r == 0); uv_close((uv_handle_t*) &fs_event, close_cb); uv_run(loop, UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } #if defined(__APPLE__) static int fs_event_error_reported; static void fs_event_error_report_cb(uv_fs_event_t* handle, const char* filename, int events, int status) { if (status != 0) fs_event_error_reported = status; } static void timer_cb_nop(uv_timer_t* handle) { ++timer_cb_called; uv_close((uv_handle_t*) handle, close_cb); } static void fs_event_error_report_close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; /* handle is allocated on-stack, no need to free it */ } TEST_IMPL(fs_event_error_reporting) { unsigned int i; uv_loop_t loops[1024]; uv_fs_event_t events[ARRAY_SIZE(loops)]; uv_loop_t* loop; uv_fs_event_t* event; TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3); remove("watch_dir/"); create_dir("watch_dir"); /* Create a lot of loops, and start FSEventStream in each of them. * Eventually, this should create enough streams to make FSEventStreamStart() * fail. */ for (i = 0; i < ARRAY_SIZE(loops); i++) { loop = &loops[i]; ASSERT(0 == uv_loop_init(loop)); event = &events[i]; timer_cb_called = 0; close_cb_called = 0; ASSERT(0 == uv_fs_event_init(loop, event)); ASSERT(0 == uv_fs_event_start(event, fs_event_error_report_cb, "watch_dir", 0)); uv_unref((uv_handle_t*) event); /* Let loop run for some time */ ASSERT(0 == uv_timer_init(loop, &timer)); ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0)); uv_run(loop, UV_RUN_DEFAULT); ASSERT(1 == timer_cb_called); ASSERT(1 == close_cb_called); if (fs_event_error_reported != 0) break; } /* At least one loop should fail */ ASSERT(fs_event_error_reported == UV_EMFILE); /* Stop and close all events, and destroy loops */ do { loop = &loops[i]; event = &events[i]; ASSERT(0 == uv_fs_event_stop(event)); uv_ref((uv_handle_t*) event); uv_close((uv_handle_t*) event, fs_event_error_report_close_cb); close_cb_called = 0; uv_run(loop, UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); uv_loop_close(loop); } while (i-- != 0); remove("watch_dir/"); MAKE_VALGRIND_HAPPY(); return 0; } #else /* !defined(__APPLE__) */ TEST_IMPL(fs_event_error_reporting) { /* No-op, needed only for FSEvents backend */ MAKE_VALGRIND_HAPPY(); return 0; } #endif /* defined(__APPLE__) */ libuv-v1.8.0/test/test-fs-poll.c000066400000000000000000000121051263361716600165150ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #define FIXTURE "testfile" static void timer_cb(uv_timer_t* handle); static void close_cb(uv_handle_t* handle); static void poll_cb(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr); static void poll_cb_fail(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr); static uv_fs_poll_t poll_handle; static uv_timer_t timer_handle; static uv_loop_t* loop; static int poll_cb_called; static int timer_cb_called; static int close_cb_called; static void touch_file(const char* path) { static int count; FILE* fp; int i; ASSERT((fp = fopen(FIXTURE, "w+"))); /* Need to change the file size because the poller may not pick up * sub-second mtime changes. */ i = ++count; while (i--) fputc('*', fp); fclose(fp); } static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void timer_cb(uv_timer_t* handle) { touch_file(FIXTURE); timer_cb_called++; } static void poll_cb_fail(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr) { ASSERT(0 && "fail_cb called"); } static void poll_cb(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr) { uv_stat_t zero_statbuf; memset(&zero_statbuf, 0, sizeof(zero_statbuf)); ASSERT(handle == &poll_handle); ASSERT(1 == uv_is_active((uv_handle_t*) handle)); ASSERT(prev != NULL); ASSERT(curr != NULL); switch (poll_cb_called++) { case 0: ASSERT(status == UV_ENOENT); ASSERT(0 == memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 == memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); touch_file(FIXTURE); break; case 1: ASSERT(status == 0); ASSERT(0 == memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 20, 0)); break; case 2: ASSERT(status == 0); ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 200, 0)); break; case 3: ASSERT(status == 0); ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); remove(FIXTURE); break; case 4: ASSERT(status == UV_ENOENT); ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); ASSERT(0 == memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); uv_close((uv_handle_t*)handle, close_cb); break; default: ASSERT(0); } } TEST_IMPL(fs_poll) { loop = uv_default_loop(); remove(FIXTURE); ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_fs_poll_init(loop, &poll_handle)); ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb, FIXTURE, 100)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(poll_cb_called == 5); ASSERT(timer_cb_called == 2); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_poll_getpath) { char buf[1024]; size_t len; loop = uv_default_loop(); remove(FIXTURE); ASSERT(0 == uv_fs_poll_init(loop, &poll_handle)); len = sizeof buf; ASSERT(UV_EINVAL == uv_fs_poll_getpath(&poll_handle, buf, &len)); ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); len = sizeof buf; ASSERT(0 == uv_fs_poll_getpath(&poll_handle, buf, &len)); ASSERT(buf[len - 1] != 0); ASSERT(0 == memcmp(buf, FIXTURE, len)); uv_close((uv_handle_t*) &poll_handle, close_cb); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-fs.c000066400000000000000000002010351263361716600155530ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include /* memset */ #include #include /* FIXME we shouldn't need to branch in this file */ #if defined(__unix__) || defined(__POSIX__) || \ defined(__APPLE__) || defined(_AIX) #include /* unlink, rmdir, etc. */ #else # include # include # define unlink _unlink # define rmdir _rmdir # define open _open # define write _write # define close _close # ifndef stat # define stat _stati64 # endif # ifndef lseek # define lseek _lseek # endif #endif #define TOO_LONG_NAME_LENGTH 65536 #define PATHMAX 1024 typedef struct { const char* path; double atime; double mtime; } utime_check_t; static int dummy_cb_count; static int close_cb_count; static int create_cb_count; static int open_cb_count; static int read_cb_count; static int write_cb_count; static int unlink_cb_count; static int mkdir_cb_count; static int mkdtemp_cb_count; static int rmdir_cb_count; static int scandir_cb_count; static int stat_cb_count; static int rename_cb_count; static int fsync_cb_count; static int fdatasync_cb_count; static int ftruncate_cb_count; static int sendfile_cb_count; static int fstat_cb_count; static int access_cb_count; static int chmod_cb_count; static int fchmod_cb_count; static int chown_cb_count; static int fchown_cb_count; static int link_cb_count; static int symlink_cb_count; static int readlink_cb_count; static int realpath_cb_count; static int utime_cb_count; static int futime_cb_count; static uv_loop_t* loop; static uv_fs_t open_req1; static uv_fs_t open_req2; static uv_fs_t read_req; static uv_fs_t write_req; static uv_fs_t unlink_req; static uv_fs_t close_req; static uv_fs_t mkdir_req; static uv_fs_t mkdtemp_req1; static uv_fs_t mkdtemp_req2; static uv_fs_t rmdir_req; static uv_fs_t scandir_req; static uv_fs_t stat_req; static uv_fs_t rename_req; static uv_fs_t fsync_req; static uv_fs_t fdatasync_req; static uv_fs_t ftruncate_req; static uv_fs_t sendfile_req; static uv_fs_t utime_req; static uv_fs_t futime_req; static char buf[32]; static char buf2[32]; static char test_buf[] = "test-buffer\n"; static char test_buf2[] = "second-buffer\n"; static uv_buf_t iov; static void check_permission(const char* filename, unsigned int mode) { int r; uv_fs_t req; uv_stat_t* s; r = uv_fs_stat(NULL, &req, filename, NULL); ASSERT(r == 0); ASSERT(req.result == 0); s = &req.statbuf; #ifdef _WIN32 /* * On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit, * so only testing for the specified flags. */ ASSERT((s->st_mode & 0777) & mode); #else ASSERT((s->st_mode & 0777) == mode); #endif uv_fs_req_cleanup(&req); } static void dummy_cb(uv_fs_t* req) { (void) req; dummy_cb_count++; } static void link_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_LINK); ASSERT(req->result == 0); link_cb_count++; uv_fs_req_cleanup(req); } static void symlink_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_SYMLINK); ASSERT(req->result == 0); symlink_cb_count++; uv_fs_req_cleanup(req); } static void readlink_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_READLINK); ASSERT(req->result == 0); ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0); readlink_cb_count++; uv_fs_req_cleanup(req); } static void realpath_cb(uv_fs_t* req) { char test_file_abs_buf[PATHMAX]; size_t test_file_abs_size = sizeof(test_file_abs_buf); ASSERT(req->fs_type == UV_FS_REALPATH); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() */ if (req->result == UV_ENOSYS) { realpath_cb_count++; uv_fs_req_cleanup(req); return; } #endif ASSERT(req->result == 0); uv_cwd(test_file_abs_buf, &test_file_abs_size); #ifdef _WIN32 strcat(test_file_abs_buf, "\\test_file"); ASSERT(stricmp(req->ptr, test_file_abs_buf) == 0); #else strcat(test_file_abs_buf, "/test_file"); ASSERT(strcmp(req->ptr, test_file_abs_buf) == 0); #endif realpath_cb_count++; uv_fs_req_cleanup(req); } static void access_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_ACCESS); access_cb_count++; uv_fs_req_cleanup(req); } static void fchmod_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_FCHMOD); ASSERT(req->result == 0); fchmod_cb_count++; uv_fs_req_cleanup(req); check_permission("test_file", *(int*)req->data); } static void chmod_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_CHMOD); ASSERT(req->result == 0); chmod_cb_count++; uv_fs_req_cleanup(req); check_permission("test_file", *(int*)req->data); } static void fchown_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_FCHOWN); ASSERT(req->result == 0); fchown_cb_count++; uv_fs_req_cleanup(req); } static void chown_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_CHOWN); ASSERT(req->result == 0); chown_cb_count++; uv_fs_req_cleanup(req); } static void chown_root_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_CHOWN); #ifdef _WIN32 /* On windows, chown is a no-op and always succeeds. */ ASSERT(req->result == 0); #else /* On unix, chown'ing the root directory is not allowed - * unless you're root, of course. */ if (geteuid() == 0) ASSERT(req->result == 0); else ASSERT(req->result == UV_EPERM); #endif chown_cb_count++; uv_fs_req_cleanup(req); } static void unlink_cb(uv_fs_t* req) { ASSERT(req == &unlink_req); ASSERT(req->fs_type == UV_FS_UNLINK); ASSERT(req->result == 0); unlink_cb_count++; uv_fs_req_cleanup(req); } static void fstat_cb(uv_fs_t* req) { uv_stat_t* s = req->ptr; ASSERT(req->fs_type == UV_FS_FSTAT); ASSERT(req->result == 0); ASSERT(s->st_size == sizeof(test_buf)); uv_fs_req_cleanup(req); fstat_cb_count++; } static void close_cb(uv_fs_t* req) { int r; ASSERT(req == &close_req); ASSERT(req->fs_type == UV_FS_CLOSE); ASSERT(req->result == 0); close_cb_count++; uv_fs_req_cleanup(req); if (close_cb_count == 3) { r = uv_fs_unlink(loop, &unlink_req, "test_file2", unlink_cb); ASSERT(r == 0); } } static void ftruncate_cb(uv_fs_t* req) { int r; ASSERT(req == &ftruncate_req); ASSERT(req->fs_type == UV_FS_FTRUNCATE); ASSERT(req->result == 0); ftruncate_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); ASSERT(r == 0); } static void read_cb(uv_fs_t* req) { int r; ASSERT(req == &read_req); ASSERT(req->fs_type == UV_FS_READ); ASSERT(req->result >= 0); /* FIXME(bnoordhuis) Check if requested size? */ read_cb_count++; uv_fs_req_cleanup(req); if (read_cb_count == 1) { ASSERT(strcmp(buf, test_buf) == 0); r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, ftruncate_cb); } else { ASSERT(strcmp(buf, "test-bu") == 0); r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); } ASSERT(r == 0); } static void open_cb(uv_fs_t* req) { int r; ASSERT(req == &open_req1); ASSERT(req->fs_type == UV_FS_OPEN); if (req->result < 0) { fprintf(stderr, "async open error: %d\n", (int) req->result); ASSERT(0); } open_cb_count++; ASSERT(req->path); ASSERT(memcmp(req->path, "test_file2\0", 11) == 0); uv_fs_req_cleanup(req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, read_cb); ASSERT(r == 0); } static void open_cb_simple(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_OPEN); if (req->result < 0) { fprintf(stderr, "async open error: %d\n", (int) req->result); ASSERT(0); } open_cb_count++; ASSERT(req->path); uv_fs_req_cleanup(req); } static void fsync_cb(uv_fs_t* req) { int r; ASSERT(req == &fsync_req); ASSERT(req->fs_type == UV_FS_FSYNC); ASSERT(req->result == 0); fsync_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); ASSERT(r == 0); } static void fdatasync_cb(uv_fs_t* req) { int r; ASSERT(req == &fdatasync_req); ASSERT(req->fs_type == UV_FS_FDATASYNC); ASSERT(req->result == 0); fdatasync_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_fsync(loop, &fsync_req, open_req1.result, fsync_cb); ASSERT(r == 0); } static void write_cb(uv_fs_t* req) { int r; ASSERT(req == &write_req); ASSERT(req->fs_type == UV_FS_WRITE); ASSERT(req->result >= 0); /* FIXME(bnoordhuis) Check if requested size? */ write_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); ASSERT(r == 0); } static void create_cb(uv_fs_t* req) { int r; ASSERT(req == &open_req1); ASSERT(req->fs_type == UV_FS_OPEN); ASSERT(req->result >= 0); create_cb_count++; uv_fs_req_cleanup(req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(loop, &write_req, req->result, &iov, 1, -1, write_cb); ASSERT(r == 0); } static void rename_cb(uv_fs_t* req) { ASSERT(req == &rename_req); ASSERT(req->fs_type == UV_FS_RENAME); ASSERT(req->result == 0); rename_cb_count++; uv_fs_req_cleanup(req); } static void mkdir_cb(uv_fs_t* req) { ASSERT(req == &mkdir_req); ASSERT(req->fs_type == UV_FS_MKDIR); ASSERT(req->result == 0); mkdir_cb_count++; ASSERT(req->path); ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); uv_fs_req_cleanup(req); } static void check_mkdtemp_result(uv_fs_t* req) { int r; ASSERT(req->fs_type == UV_FS_MKDTEMP); ASSERT(req->result == 0); ASSERT(req->path); ASSERT(strlen(req->path) == 15); ASSERT(memcmp(req->path, "test_dir_", 9) == 0); ASSERT(memcmp(req->path + 9, "XXXXXX", 6) != 0); check_permission(req->path, 0700); /* Check if req->path is actually a directory */ r = uv_fs_stat(NULL, &stat_req, req->path, NULL); ASSERT(r == 0); ASSERT(((uv_stat_t*)stat_req.ptr)->st_mode & S_IFDIR); uv_fs_req_cleanup(&stat_req); } static void mkdtemp_cb(uv_fs_t* req) { ASSERT(req == &mkdtemp_req1); check_mkdtemp_result(req); mkdtemp_cb_count++; } static void rmdir_cb(uv_fs_t* req) { ASSERT(req == &rmdir_req); ASSERT(req->fs_type == UV_FS_RMDIR); ASSERT(req->result == 0); rmdir_cb_count++; ASSERT(req->path); ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); uv_fs_req_cleanup(req); } static void assert_is_file_type(uv_dirent_t dent) { #ifdef HAVE_DIRENT_TYPES /* * For Apple and Windows, we know getdents is expected to work but for other * environments, the filesystem dictates whether or not getdents supports * returning the file type. * * See: * http://man7.org/linux/man-pages/man2/getdents.2.html * https://github.com/libuv/libuv/issues/501 */ #if defined(__APPLE__) || defined(_WIN32) ASSERT(dent.type == UV_DIRENT_FILE); #else ASSERT(dent.type == UV_DIRENT_FILE || dent.type == UV_DIRENT_UNKNOWN); #endif #else ASSERT(dent.type == UV_DIRENT_UNKNOWN); #endif } static void scandir_cb(uv_fs_t* req) { uv_dirent_t dent; ASSERT(req == &scandir_req); ASSERT(req->fs_type == UV_FS_SCANDIR); ASSERT(req->result == 2); ASSERT(req->ptr); while (UV_EOF != uv_fs_scandir_next(req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); assert_is_file_type(dent); } scandir_cb_count++; ASSERT(req->path); ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); uv_fs_req_cleanup(req); ASSERT(!req->ptr); } static void empty_scandir_cb(uv_fs_t* req) { uv_dirent_t dent; ASSERT(req == &scandir_req); ASSERT(req->fs_type == UV_FS_SCANDIR); ASSERT(req->result == 0); ASSERT(req->ptr == NULL); ASSERT(UV_EOF == uv_fs_scandir_next(req, &dent)); uv_fs_req_cleanup(req); scandir_cb_count++; } static void file_scandir_cb(uv_fs_t* req) { ASSERT(req == &scandir_req); ASSERT(req->fs_type == UV_FS_SCANDIR); ASSERT(req->result == UV_ENOTDIR); ASSERT(req->ptr == NULL); uv_fs_req_cleanup(req); scandir_cb_count++; } static void stat_cb(uv_fs_t* req) { ASSERT(req == &stat_req); ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT); ASSERT(req->result == 0); ASSERT(req->ptr); stat_cb_count++; uv_fs_req_cleanup(req); ASSERT(!req->ptr); } static void sendfile_cb(uv_fs_t* req) { ASSERT(req == &sendfile_req); ASSERT(req->fs_type == UV_FS_SENDFILE); ASSERT(req->result == 65546); sendfile_cb_count++; uv_fs_req_cleanup(req); } static void open_noent_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_OPEN); ASSERT(req->result == UV_ENOENT); open_cb_count++; uv_fs_req_cleanup(req); } static void open_nametoolong_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_OPEN); ASSERT(req->result == UV_ENAMETOOLONG); open_cb_count++; uv_fs_req_cleanup(req); } static void open_loop_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_OPEN); ASSERT(req->result == UV_ELOOP); open_cb_count++; uv_fs_req_cleanup(req); } TEST_IMPL(fs_file_noent) { uv_fs_t req; int r; loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "does_not_exist", O_RDONLY, 0, NULL); ASSERT(r == UV_ENOENT); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, open_noent_cb); ASSERT(r == 0); ASSERT(open_cb_count == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(open_cb_count == 1); /* TODO add EACCES test */ MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_file_nametoolong) { uv_fs_t req; int r; char name[TOO_LONG_NAME_LENGTH + 1]; loop = uv_default_loop(); memset(name, 'a', TOO_LONG_NAME_LENGTH); name[TOO_LONG_NAME_LENGTH] = 0; r = uv_fs_open(NULL, &req, name, O_RDONLY, 0, NULL); ASSERT(r == UV_ENAMETOOLONG); ASSERT(req.result == UV_ENAMETOOLONG); uv_fs_req_cleanup(&req); r = uv_fs_open(loop, &req, name, O_RDONLY, 0, open_nametoolong_cb); ASSERT(r == 0); ASSERT(open_cb_count == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(open_cb_count == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_file_loop) { uv_fs_t req; int r; loop = uv_default_loop(); unlink("test_symlink"); r = uv_fs_symlink(NULL, &req, "test_symlink", "test_symlink", 0, NULL); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support symlinks; we'll get UV_ENOTSUP. * Starting with vista they are supported, but only when elevated, otherwise * we'll see UV_EPERM. */ if (r == UV_ENOTSUP || r == UV_EPERM) return 0; #endif ASSERT(r == 0); uv_fs_req_cleanup(&req); r = uv_fs_open(NULL, &req, "test_symlink", O_RDONLY, 0, NULL); ASSERT(r == UV_ELOOP); ASSERT(req.result == UV_ELOOP); uv_fs_req_cleanup(&req); r = uv_fs_open(loop, &req, "test_symlink", O_RDONLY, 0, open_loop_cb); ASSERT(r == 0); ASSERT(open_cb_count == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(open_cb_count == 1); unlink("test_symlink"); MAKE_VALGRIND_HAPPY(); return 0; } static void check_utime(const char* path, double atime, double mtime) { uv_stat_t* s; uv_fs_t req; int r; r = uv_fs_stat(loop, &req, path, NULL); ASSERT(r == 0); ASSERT(req.result == 0); s = &req.statbuf; ASSERT(s->st_atim.tv_sec == atime); ASSERT(s->st_mtim.tv_sec == mtime); uv_fs_req_cleanup(&req); } static void utime_cb(uv_fs_t* req) { utime_check_t* c; ASSERT(req == &utime_req); ASSERT(req->result == 0); ASSERT(req->fs_type == UV_FS_UTIME); c = req->data; check_utime(c->path, c->atime, c->mtime); uv_fs_req_cleanup(req); utime_cb_count++; } static void futime_cb(uv_fs_t* req) { utime_check_t* c; ASSERT(req == &futime_req); ASSERT(req->result == 0); ASSERT(req->fs_type == UV_FS_FUTIME); c = req->data; check_utime(c->path, c->atime, c->mtime); uv_fs_req_cleanup(req); futime_cb_count++; } TEST_IMPL(fs_file_async) { int r; /* Setup. */ unlink("test_file"); unlink("test_file2"); loop = uv_default_loop(); r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, create_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(create_cb_count == 1); ASSERT(write_cb_count == 1); ASSERT(fsync_cb_count == 1); ASSERT(fdatasync_cb_count == 1); ASSERT(close_cb_count == 1); r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", rename_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(create_cb_count == 1); ASSERT(write_cb_count == 1); ASSERT(close_cb_count == 1); ASSERT(rename_cb_count == 1); r = uv_fs_open(loop, &open_req1, "test_file2", O_RDWR, 0, open_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(open_cb_count == 1); ASSERT(read_cb_count == 1); ASSERT(close_cb_count == 2); ASSERT(rename_cb_count == 1); ASSERT(create_cb_count == 1); ASSERT(write_cb_count == 1); ASSERT(ftruncate_cb_count == 1); r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, open_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(open_cb_count == 2); ASSERT(read_cb_count == 2); ASSERT(close_cb_count == 3); ASSERT(rename_cb_count == 1); ASSERT(unlink_cb_count == 1); ASSERT(create_cb_count == 1); ASSERT(write_cb_count == 1); ASSERT(ftruncate_cb_count == 1); /* Cleanup. */ unlink("test_file"); unlink("test_file2"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_file_sync) { int r; /* Setup. */ unlink("test_file"); unlink("test_file2"); loop = uv_default_loop(); r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(write_req.result >= 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(read_req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_ftruncate(NULL, &ftruncate_req, open_req1.result, 7, NULL); ASSERT(r == 0); ASSERT(ftruncate_req.result == 0); uv_fs_req_cleanup(&ftruncate_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_rename(NULL, &rename_req, "test_file", "test_file2", NULL); ASSERT(r == 0); ASSERT(rename_req.result == 0); uv_fs_req_cleanup(&rename_req); r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(read_req.result >= 0); ASSERT(strcmp(buf, "test-bu") == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_unlink(NULL, &unlink_req, "test_file2", NULL); ASSERT(r == 0); ASSERT(unlink_req.result == 0); uv_fs_req_cleanup(&unlink_req); /* Cleanup */ unlink("test_file"); unlink("test_file2"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_file_write_null_buffer) { int r; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(NULL, 0); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r == 0); ASSERT(write_req.result == 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_async_dir) { int r; uv_dirent_t dent; /* Setup */ unlink("test_dir/file1"); unlink("test_dir/file2"); rmdir("test_dir"); loop = uv_default_loop(); r = uv_fs_mkdir(loop, &mkdir_req, "test_dir", 0755, mkdir_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(mkdir_cb_count == 1); /* Create 2 files synchronously. */ r = uv_fs_open(NULL, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_scandir(loop, &scandir_req, "test_dir", 0, scandir_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(scandir_cb_count == 1); /* sync uv_fs_scandir */ r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); ASSERT(r == 2); ASSERT(scandir_req.result == 2); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); assert_is_file_type(dent); } uv_fs_req_cleanup(&scandir_req); ASSERT(!scandir_req.ptr); r = uv_fs_stat(loop, &stat_req, "test_dir", stat_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); r = uv_fs_stat(loop, &stat_req, "test_dir/", stat_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); r = uv_fs_lstat(loop, &stat_req, "test_dir", stat_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); r = uv_fs_lstat(loop, &stat_req, "test_dir/", stat_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(stat_cb_count == 4); r = uv_fs_unlink(loop, &unlink_req, "test_dir/file1", unlink_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(unlink_cb_count == 1); r = uv_fs_unlink(loop, &unlink_req, "test_dir/file2", unlink_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(unlink_cb_count == 2); r = uv_fs_rmdir(loop, &rmdir_req, "test_dir", rmdir_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(rmdir_cb_count == 1); /* Cleanup */ unlink("test_dir/file1"); unlink("test_dir/file2"); rmdir("test_dir"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_async_sendfile) { int f, r; struct stat s1, s2; loop = uv_default_loop(); /* Setup. */ unlink("test_file"); unlink("test_file2"); f = open("test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); ASSERT(f != -1); r = write(f, "begin\n", 6); ASSERT(r == 6); r = lseek(f, 65536, SEEK_CUR); ASSERT(r == 65542); r = write(f, "end\n", 4); ASSERT(r != -1); r = close(f); ASSERT(r == 0); /* Test starts here. */ r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_open(NULL, &open_req2, "test_file2", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req2.result >= 0); uv_fs_req_cleanup(&open_req2); r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, 0, 131072, sendfile_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(sendfile_cb_count == 1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_close(NULL, &close_req, open_req2.result, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&close_req); stat("test_file", &s1); stat("test_file2", &s2); ASSERT(65546 == s2.st_size && s1.st_size == s2.st_size); /* Cleanup. */ unlink("test_file"); unlink("test_file2"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_mkdtemp) { int r; const char* path_template = "test_dir_XXXXXX"; loop = uv_default_loop(); r = uv_fs_mkdtemp(loop, &mkdtemp_req1, path_template, mkdtemp_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(mkdtemp_cb_count == 1); /* sync mkdtemp */ r = uv_fs_mkdtemp(NULL, &mkdtemp_req2, path_template, NULL); ASSERT(r == 0); check_mkdtemp_result(&mkdtemp_req2); /* mkdtemp return different values on subsequent calls */ ASSERT(strcmp(mkdtemp_req1.path, mkdtemp_req2.path) != 0); /* Cleanup */ rmdir(mkdtemp_req1.path); rmdir(mkdtemp_req2.path); uv_fs_req_cleanup(&mkdtemp_req1); uv_fs_req_cleanup(&mkdtemp_req2); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_fstat) { int r; uv_fs_t req; uv_file file; uv_stat_t* s; #ifndef _WIN32 struct stat t; #endif /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); ASSERT(r == sizeof(test_buf)); ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); r = uv_fs_fstat(NULL, &req, file, NULL); ASSERT(r == 0); ASSERT(req.result == 0); s = req.ptr; ASSERT(s->st_size == sizeof(test_buf)); #ifndef _WIN32 r = fstat(file, &t); ASSERT(r == 0); ASSERT(s->st_dev == (uint64_t) t.st_dev); ASSERT(s->st_mode == (uint64_t) t.st_mode); ASSERT(s->st_nlink == (uint64_t) t.st_nlink); ASSERT(s->st_uid == (uint64_t) t.st_uid); ASSERT(s->st_gid == (uint64_t) t.st_gid); ASSERT(s->st_rdev == (uint64_t) t.st_rdev); ASSERT(s->st_ino == (uint64_t) t.st_ino); ASSERT(s->st_size == (uint64_t) t.st_size); ASSERT(s->st_blksize == (uint64_t) t.st_blksize); ASSERT(s->st_blocks == (uint64_t) t.st_blocks); #if defined(__APPLE__) ASSERT(s->st_atim.tv_sec == t.st_atimespec.tv_sec); ASSERT(s->st_atim.tv_nsec == t.st_atimespec.tv_nsec); ASSERT(s->st_mtim.tv_sec == t.st_mtimespec.tv_sec); ASSERT(s->st_mtim.tv_nsec == t.st_mtimespec.tv_nsec); ASSERT(s->st_ctim.tv_sec == t.st_ctimespec.tv_sec); ASSERT(s->st_ctim.tv_nsec == t.st_ctimespec.tv_nsec); ASSERT(s->st_birthtim.tv_sec == t.st_birthtimespec.tv_sec); ASSERT(s->st_birthtim.tv_nsec == t.st_birthtimespec.tv_nsec); ASSERT(s->st_flags == t.st_flags); ASSERT(s->st_gen == t.st_gen); #elif defined(_AIX) ASSERT(s->st_atim.tv_sec == t.st_atime); ASSERT(s->st_atim.tv_nsec == 0); ASSERT(s->st_mtim.tv_sec == t.st_mtime); ASSERT(s->st_mtim.tv_nsec == 0); ASSERT(s->st_ctim.tv_sec == t.st_ctime); ASSERT(s->st_ctim.tv_nsec == 0); #elif defined(__sun) || \ defined(_BSD_SOURCE) || \ defined(_SVID_SOURCE) || \ defined(_XOPEN_SOURCE) || \ defined(_DEFAULT_SOURCE) ASSERT(s->st_atim.tv_sec == t.st_atim.tv_sec); ASSERT(s->st_atim.tv_nsec == t.st_atim.tv_nsec); ASSERT(s->st_mtim.tv_sec == t.st_mtim.tv_sec); ASSERT(s->st_mtim.tv_nsec == t.st_mtim.tv_nsec); ASSERT(s->st_ctim.tv_sec == t.st_ctim.tv_sec); ASSERT(s->st_ctim.tv_nsec == t.st_ctim.tv_nsec); # if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) ASSERT(s->st_birthtim.tv_sec == t.st_birthtim.tv_sec); ASSERT(s->st_birthtim.tv_nsec == t.st_birthtim.tv_nsec); ASSERT(s->st_flags == t.st_flags); ASSERT(s->st_gen == t.st_gen); # endif #else ASSERT(s->st_atim.tv_sec == t.st_atime); ASSERT(s->st_atim.tv_nsec == 0); ASSERT(s->st_mtim.tv_sec == t.st_mtime); ASSERT(s->st_mtim.tv_nsec == 0); ASSERT(s->st_ctim.tv_sec == t.st_ctime); ASSERT(s->st_ctim.tv_nsec == 0); #endif #endif uv_fs_req_cleanup(&req); /* Now do the uv_fs_fstat call asynchronously */ r = uv_fs_fstat(loop, &req, file, fstat_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fstat_cb_count == 1); r = uv_fs_close(NULL, &req, file, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_access) { int r; uv_fs_t req; uv_file file; /* Setup. */ unlink("test_file"); rmdir("test_dir"); loop = uv_default_loop(); /* File should not exist */ r = uv_fs_access(NULL, &req, "test_file", F_OK, NULL); ASSERT(r < 0); ASSERT(req.result < 0); uv_fs_req_cleanup(&req); /* File should not exist */ r = uv_fs_access(loop, &req, "test_file", F_OK, access_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(access_cb_count == 1); access_cb_count = 0; /* reset for the next test */ /* Create file */ r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); /* File should exist */ r = uv_fs_access(NULL, &req, "test_file", F_OK, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* File should exist */ r = uv_fs_access(loop, &req, "test_file", F_OK, access_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(access_cb_count == 1); access_cb_count = 0; /* reset for the next test */ /* Close file */ r = uv_fs_close(NULL, &req, file, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* Directory access */ r = uv_fs_mkdir(NULL, &req, "test_dir", 0777, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); r = uv_fs_access(NULL, &req, "test_dir", W_OK, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ unlink("test_file"); rmdir("test_dir"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_chmod) { int r; uv_fs_t req; uv_file file; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); ASSERT(r == sizeof(test_buf)); ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); #ifndef _WIN32 /* Make the file write-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0200, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); check_permission("test_file", 0200); #endif /* Make the file read-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); check_permission("test_file", 0400); /* Make the file read+write with sync uv_fs_fchmod */ r = uv_fs_fchmod(NULL, &req, file, 0600, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); check_permission("test_file", 0600); #ifndef _WIN32 /* async chmod */ { static int mode = 0200; req.data = &mode; } r = uv_fs_chmod(loop, &req, "test_file", 0200, chmod_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(chmod_cb_count == 1); chmod_cb_count = 0; /* reset for the next test */ #endif /* async chmod */ { static int mode = 0400; req.data = &mode; } r = uv_fs_chmod(loop, &req, "test_file", 0400, chmod_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(chmod_cb_count == 1); /* async fchmod */ { static int mode = 0600; req.data = &mode; } r = uv_fs_fchmod(loop, &req, file, 0600, fchmod_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fchmod_cb_count == 1); close(file); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_unlink_readonly) { int r; uv_fs_t req; uv_file file; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); ASSERT(r == sizeof(test_buf)); ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); close(file); /* Make the file read-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); check_permission("test_file", 0400); /* Try to unlink the file */ r = uv_fs_unlink(NULL, &req, "test_file", NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ uv_fs_chmod(NULL, &req, "test_file", 0600, NULL); uv_fs_req_cleanup(&req); unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_chown) { int r; uv_fs_t req; uv_file file; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); /* sync chown */ r = uv_fs_chown(NULL, &req, "test_file", -1, -1, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* sync fchown */ r = uv_fs_fchown(NULL, &req, file, -1, -1, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); /* async chown */ r = uv_fs_chown(loop, &req, "test_file", -1, -1, chown_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(chown_cb_count == 1); /* chown to root (fail) */ chown_cb_count = 0; r = uv_fs_chown(loop, &req, "test_file", 0, 0, chown_root_cb); uv_run(loop, UV_RUN_DEFAULT); ASSERT(chown_cb_count == 1); /* async fchown */ r = uv_fs_fchown(loop, &req, file, -1, -1, fchown_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(fchown_cb_count == 1); close(file); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_link) { int r; uv_fs_t req; uv_file file; uv_file link; /* Setup. */ unlink("test_file"); unlink("test_file_link"); unlink("test_file_link2"); loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); ASSERT(r == sizeof(test_buf)); ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); close(file); /* sync link */ r = uv_fs_link(NULL, &req, "test_file", "test_file_link", NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); r = uv_fs_open(NULL, &req, "test_file_link", O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); close(link); /* async link */ r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(link_cb_count == 1); r = uv_fs_open(NULL, &req, "test_file_link2", O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); close(link); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ unlink("test_file"); unlink("test_file_link"); unlink("test_file_link2"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_readlink) { uv_fs_t req; loop = uv_default_loop(); ASSERT(0 == uv_fs_readlink(loop, &req, "no_such_file", dummy_cb)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(dummy_cb_count == 1); ASSERT(req.ptr == NULL); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); ASSERT(UV_ENOENT == uv_fs_readlink(NULL, &req, "no_such_file", NULL)); ASSERT(req.ptr == NULL); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_realpath) { uv_fs_t req; loop = uv_default_loop(); ASSERT(0 == uv_fs_realpath(loop, &req, "no_such_file", dummy_cb)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(dummy_cb_count == 1); ASSERT(req.ptr == NULL); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() */ if (req.result == UV_ENOSYS) { uv_fs_req_cleanup(&req); RETURN_SKIP("realpath is not supported on Windows XP"); } #endif ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); ASSERT(UV_ENOENT == uv_fs_realpath(NULL, &req, "no_such_file", NULL)); ASSERT(req.ptr == NULL); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_symlink) { int r; uv_fs_t req; uv_file file; uv_file link; char test_file_abs_buf[PATHMAX]; size_t test_file_abs_size; /* Setup. */ unlink("test_file"); unlink("test_file_symlink"); unlink("test_file_symlink2"); unlink("test_file_symlink_symlink"); unlink("test_file_symlink2_symlink"); test_file_abs_size = sizeof(test_file_abs_buf); #ifdef _WIN32 uv_cwd(test_file_abs_buf, &test_file_abs_size); strcat(test_file_abs_buf, "\\test_file"); #else uv_cwd(test_file_abs_buf, &test_file_abs_size); strcat(test_file_abs_buf, "/test_file"); #endif loop = uv_default_loop(); r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); ASSERT(r == sizeof(test_buf)); ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); close(file); /* sync symlink */ r = uv_fs_symlink(NULL, &req, "test_file", "test_file_symlink", 0, NULL); #ifdef _WIN32 if (r < 0) { if (r == UV_ENOTSUP) { /* * Windows doesn't support symlinks on older versions. * We just pass the test and bail out early if we get ENOTSUP. */ return 0; } else if (r == UV_EPERM) { /* * Creating a symlink is only allowed when running elevated. * We pass the test and bail out early if we get UV_EPERM. */ return 0; } } #endif ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); r = uv_fs_open(NULL, &req, "test_file_symlink", O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); close(link); r = uv_fs_symlink(NULL, &req, "test_file_symlink", "test_file_symlink_symlink", 0, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); r = uv_fs_readlink(NULL, &req, "test_file_symlink_symlink", NULL); ASSERT(r == 0); ASSERT(strcmp(req.ptr, "test_file_symlink") == 0); uv_fs_req_cleanup(&req); r = uv_fs_realpath(NULL, &req, "test_file_symlink_symlink", NULL); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() */ if (r == UV_ENOSYS) { uv_fs_req_cleanup(&req); RETURN_SKIP("realpath is not supported on Windows XP"); } #endif ASSERT(r == 0); #ifdef _WIN32 ASSERT(stricmp(req.ptr, test_file_abs_buf) == 0); #else ASSERT(strcmp(req.ptr, test_file_abs_buf) == 0); #endif uv_fs_req_cleanup(&req); /* async link */ r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink2", 0, symlink_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(symlink_cb_count == 1); r = uv_fs_open(NULL, &req, "test_file_symlink2", O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); close(link); r = uv_fs_symlink(NULL, &req, "test_file_symlink2", "test_file_symlink2_symlink", 0, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(readlink_cb_count == 1); r = uv_fs_realpath(loop, &req, "test_file", realpath_cb); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() */ if (r == UV_ENOSYS) { uv_fs_req_cleanup(&req); RETURN_SKIP("realpath is not supported on Windows XP"); } #endif ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(realpath_cb_count == 1); /* * Run the loop just to check we don't have make any extraneous uv_ref() * calls. This should drop out immediately. */ uv_run(loop, UV_RUN_DEFAULT); /* Cleanup. */ unlink("test_file"); unlink("test_file_symlink"); unlink("test_file_symlink_symlink"); unlink("test_file_symlink2"); unlink("test_file_symlink2_symlink"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_symlink_dir) { uv_fs_t req; int r; char* test_dir; uv_dirent_t dent; static char test_dir_abs_buf[PATHMAX]; size_t test_dir_abs_size; /* set-up */ unlink("test_dir/file1"); unlink("test_dir/file2"); rmdir("test_dir"); rmdir("test_dir_symlink"); test_dir_abs_size = sizeof(test_dir_abs_buf); loop = uv_default_loop(); uv_fs_mkdir(NULL, &req, "test_dir", 0777, NULL); uv_fs_req_cleanup(&req); #ifdef _WIN32 strcpy(test_dir_abs_buf, "\\\\?\\"); uv_cwd(test_dir_abs_buf + 4, &test_dir_abs_size); test_dir_abs_size += 4; strcat(test_dir_abs_buf, "\\test_dir\\"); test_dir_abs_size += strlen("\\test_dir\\"); test_dir = test_dir_abs_buf; #else uv_cwd(test_dir_abs_buf, &test_dir_abs_size); strcat(test_dir_abs_buf, "/test_dir"); test_dir_abs_size += strlen("/test_dir"); test_dir = "test_dir"; #endif r = uv_fs_symlink(NULL, &req, test_dir, "test_dir_symlink", UV_FS_SYMLINK_JUNCTION, NULL); fprintf(stderr, "r == %i\n", r); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); r = uv_fs_stat(NULL, &req, "test_dir_symlink", NULL); ASSERT(r == 0); ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFDIR); uv_fs_req_cleanup(&req); r = uv_fs_lstat(NULL, &req, "test_dir_symlink", NULL); ASSERT(r == 0); ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFLNK); #ifdef _WIN32 ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir + 4)); #else ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir)); #endif uv_fs_req_cleanup(&req); r = uv_fs_readlink(NULL, &req, "test_dir_symlink", NULL); ASSERT(r == 0); #ifdef _WIN32 ASSERT(strcmp(req.ptr, test_dir + 4) == 0); #else ASSERT(strcmp(req.ptr, test_dir) == 0); #endif uv_fs_req_cleanup(&req); r = uv_fs_realpath(NULL, &req, "test_dir_symlink", NULL); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() */ if (r == UV_ENOSYS) { uv_fs_req_cleanup(&req); RETURN_SKIP("realpath is not supported on Windows XP"); } #endif ASSERT(r == 0); #ifdef _WIN32 ASSERT(strlen(req.ptr) == test_dir_abs_size - 5); ASSERT(strnicmp(req.ptr, test_dir + 4, test_dir_abs_size - 5) == 0); #else ASSERT(strcmp(req.ptr, test_dir_abs_buf) == 0); #endif uv_fs_req_cleanup(&req); r = uv_fs_open(NULL, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_scandir(NULL, &scandir_req, "test_dir_symlink", 0, NULL); ASSERT(r == 2); ASSERT(scandir_req.result == 2); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); assert_is_file_type(dent); } uv_fs_req_cleanup(&scandir_req); ASSERT(!scandir_req.ptr); /* unlink will remove the directory symlink */ r = uv_fs_unlink(NULL, &req, "test_dir_symlink", NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); r = uv_fs_scandir(NULL, &scandir_req, "test_dir_symlink", 0, NULL); ASSERT(r == UV_ENOENT); uv_fs_req_cleanup(&scandir_req); r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); ASSERT(r == 2); ASSERT(scandir_req.result == 2); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); assert_is_file_type(dent); } uv_fs_req_cleanup(&scandir_req); ASSERT(!scandir_req.ptr); /* clean-up */ unlink("test_dir/file1"); unlink("test_dir/file2"); rmdir("test_dir"); rmdir("test_dir_symlink"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_utime) { utime_check_t checkme; const char* path = "test_file"; double atime; double mtime; uv_fs_t req; int r; /* Setup. */ loop = uv_default_loop(); unlink(path); r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); uv_fs_req_cleanup(&req); close(r); atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ r = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); r = uv_fs_stat(NULL, &req, path, NULL); ASSERT(r == 0); ASSERT(req.result == 0); check_utime(path, atime, mtime); uv_fs_req_cleanup(&req); atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ checkme.path = path; checkme.atime = atime; checkme.mtime = mtime; /* async utime */ utime_req.data = &checkme; r = uv_fs_utime(loop, &utime_req, path, atime, mtime, utime_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(utime_cb_count == 1); /* Cleanup. */ unlink(path); MAKE_VALGRIND_HAPPY(); return 0; } #ifdef _WIN32 TEST_IMPL(fs_stat_root) { int r; uv_loop_t* loop = uv_default_loop(); r = uv_fs_stat(NULL, &stat_req, "\\", NULL); ASSERT(r == 0); r = uv_fs_stat(NULL, &stat_req, "..\\..\\..\\..\\..\\..\\..", NULL); ASSERT(r == 0); r = uv_fs_stat(NULL, &stat_req, "..", NULL); ASSERT(r == 0); r = uv_fs_stat(NULL, &stat_req, "..\\", NULL); ASSERT(r == 0); /* stats the current directory on c: */ r = uv_fs_stat(NULL, &stat_req, "c:", NULL); ASSERT(r == 0); r = uv_fs_stat(NULL, &stat_req, "c:\\", NULL); ASSERT(r == 0); r = uv_fs_stat(NULL, &stat_req, "\\\\?\\C:\\", NULL); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } #endif TEST_IMPL(fs_futime) { utime_check_t checkme; const char* path = "test_file"; double atime; double mtime; uv_file file; uv_fs_t req; int r; /* Setup. */ loop = uv_default_loop(); unlink(path); r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); uv_fs_req_cleanup(&req); close(r); atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ r = uv_fs_open(NULL, &req, path, O_RDWR, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); file = req.result; /* FIXME probably not how it's supposed to be used */ uv_fs_req_cleanup(&req); r = uv_fs_futime(NULL, &req, file, atime, mtime, NULL); ASSERT(r == 0); ASSERT(req.result == 0); uv_fs_req_cleanup(&req); r = uv_fs_stat(NULL, &req, path, NULL); ASSERT(r == 0); ASSERT(req.result == 0); check_utime(path, atime, mtime); uv_fs_req_cleanup(&req); atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ checkme.atime = atime; checkme.mtime = mtime; checkme.path = path; /* async futime */ futime_req.data = &checkme; r = uv_fs_futime(loop, &futime_req, file, atime, mtime, futime_cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(futime_cb_count == 1); /* Cleanup. */ unlink(path); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_stat_missing_path) { uv_fs_t req; int r; loop = uv_default_loop(); r = uv_fs_stat(NULL, &req, "non_existent_file", NULL); ASSERT(r == UV_ENOENT); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_scandir_empty_dir) { const char* path; uv_fs_t req; uv_dirent_t dent; int r; path = "./empty_dir/"; loop = uv_default_loop(); uv_fs_mkdir(NULL, &req, path, 0777, NULL); uv_fs_req_cleanup(&req); /* Fill the req to ensure that required fields are cleaned up */ memset(&req, 0xdb, sizeof(req)); r = uv_fs_scandir(NULL, &req, path, 0, NULL); ASSERT(r == 0); ASSERT(req.result == 0); ASSERT(req.ptr == NULL); ASSERT(UV_EOF == uv_fs_scandir_next(&req, &dent)); uv_fs_req_cleanup(&req); r = uv_fs_scandir(loop, &scandir_req, path, 0, empty_scandir_cb); ASSERT(r == 0); ASSERT(scandir_cb_count == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(scandir_cb_count == 1); uv_fs_rmdir(NULL, &req, path, NULL); uv_fs_req_cleanup(&req); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_scandir_file) { const char* path; int r; path = "test/fixtures/empty_file"; loop = uv_default_loop(); r = uv_fs_scandir(NULL, &scandir_req, path, 0, NULL); ASSERT(r == UV_ENOTDIR); uv_fs_req_cleanup(&scandir_req); r = uv_fs_scandir(loop, &scandir_req, path, 0, file_scandir_cb); ASSERT(r == 0); ASSERT(scandir_cb_count == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(scandir_cb_count == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_open_dir) { const char* path; uv_fs_t req; int r, file; path = "."; loop = uv_default_loop(); r = uv_fs_open(NULL, &req, path, O_RDONLY, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); ASSERT(req.ptr == NULL); file = r; uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); ASSERT(r == 0); r = uv_fs_open(loop, &req, path, O_RDONLY, 0, open_cb_simple); ASSERT(r == 0); ASSERT(open_cb_count == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(open_cb_count == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_file_open_append) { int r; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(write_req.result >= 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | O_APPEND, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(write_req.result >= 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); printf("read = %d\n", r); ASSERT(r == 26); ASSERT(read_req.result == 26); ASSERT(memcmp(buf, "test-buffer\n\0test-buffer\n\0", sizeof("test-buffer\n\0test-buffer\n\0") - 1) == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_rename_to_existing_file) { int r; /* Setup. */ unlink("test_file"); unlink("test_file2"); loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(write_req.result >= 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file2", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_rename(NULL, &rename_req, "test_file", "test_file2", NULL); ASSERT(r == 0); ASSERT(rename_req.result == 0); uv_fs_req_cleanup(&rename_req); r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(read_req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); unlink("test_file2"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_read_file_eof) { int r; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(write_req.result >= 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r >= 0); ASSERT(read_req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, read_req.result, NULL); ASSERT(r == 0); ASSERT(read_req.result == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_write_multiple_bufs) { uv_buf_t iovs[2]; int r; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iovs[0] = uv_buf_init(test_buf, sizeof(test_buf)); iovs[1] = uv_buf_init(test_buf2, sizeof(test_buf2)); r = uv_fs_write(NULL, &write_req, open_req1.result, iovs, 2, 0, NULL); ASSERT(r >= 0); ASSERT(write_req.result >= 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); memset(buf2, 0, sizeof(buf2)); /* Read the strings back to separate buffers. */ iovs[0] = uv_buf_init(buf, sizeof(test_buf)); iovs[1] = uv_buf_init(buf2, sizeof(test_buf2)); r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, 2, 0, NULL); ASSERT(r >= 0); ASSERT(read_req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); ASSERT(strcmp(buf2, test_buf2) == 0); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, read_req.result, NULL); ASSERT(r == 0); ASSERT(read_req.result == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_write_alotof_bufs) { const size_t iovcount = 54321; uv_buf_t* iovs; char* buffer; size_t index; int r; /* Setup. */ unlink("test_file"); loop = uv_default_loop(); iovs = malloc(sizeof(*iovs) * iovcount); ASSERT(iovs != NULL); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, iovs, iovcount, -1, NULL); ASSERT(r >= 0); ASSERT((size_t)write_req.result == sizeof(test_buf) * iovcount); uv_fs_req_cleanup(&write_req); /* Read the strings back to separate buffers. */ buffer = malloc(sizeof(test_buf) * iovcount); ASSERT(buffer != NULL); for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), sizeof(test_buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, iovcount, 0, NULL); ASSERT(r >= 0); ASSERT((size_t)read_req.result == sizeof(test_buf) * iovcount); for (index = 0; index < iovcount; ++index) ASSERT(strncmp(buffer + index * sizeof(test_buf), test_buf, sizeof(test_buf)) == 0); uv_fs_req_cleanup(&read_req); free(buffer); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, read_req.result, NULL); ASSERT(r == 0); ASSERT(read_req.result == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); free(iovs); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_write_alotof_bufs_with_offset) { const size_t iovcount = 54321; uv_buf_t* iovs; char* buffer; size_t index; int r; int64_t offset; char* filler = "0123456789"; int filler_len = strlen(filler); /* Setup. */ unlink("test_file"); loop = uv_default_loop(); iovs = malloc(sizeof(*iovs) * iovcount); ASSERT(iovs != NULL); r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(filler, filler_len); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); ASSERT(r == filler_len); ASSERT(write_req.result == filler_len); uv_fs_req_cleanup(&write_req); offset = (int64_t)r; for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, iovs, iovcount, offset, NULL); ASSERT(r >= 0); ASSERT((size_t)write_req.result == sizeof(test_buf) * iovcount); uv_fs_req_cleanup(&write_req); /* Read the strings back to separate buffers. */ buffer = malloc(sizeof(test_buf) * iovcount); ASSERT(buffer != NULL); for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), sizeof(test_buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, iovcount, offset, NULL); ASSERT(r >= 0); ASSERT(read_req.result == sizeof(test_buf) * iovcount); for (index = 0; index < iovcount; ++index) ASSERT(strncmp(buffer + index * sizeof(test_buf), test_buf, sizeof(test_buf)) == 0); uv_fs_req_cleanup(&read_req); free(buffer); r = uv_fs_stat(NULL, &stat_req, "test_file", NULL); ASSERT(r == 0); ASSERT((int64_t)((uv_stat_t*)stat_req.ptr)->st_size == offset + (int64_t)(iovcount * sizeof(test_buf))); uv_fs_req_cleanup(&stat_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, read_req.result + offset, NULL); ASSERT(r == 0); ASSERT(read_req.result == 0); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); ASSERT(r == 0); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); free(iovs); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_read_write_null_arguments) { int r; r = uv_fs_read(NULL, NULL, 0, NULL, 0, -1, NULL); ASSERT(r == UV_EINVAL); r = uv_fs_write(NULL, NULL, 0, NULL, 0, -1, NULL); ASSERT(r == UV_EINVAL); iov = uv_buf_init(NULL, 0); r = uv_fs_read(NULL, NULL, 0, &iov, 0, -1, NULL); ASSERT(r == UV_EINVAL); iov = uv_buf_init(NULL, 0); r = uv_fs_write(NULL, NULL, 0, &iov, 0, -1, NULL); ASSERT(r == UV_EINVAL); return 0; } libuv-v1.8.0/test/test-get-currentexe.c000066400000000000000000000050631263361716600201070ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #define PATHMAX 1024 extern char executable_path[]; TEST_IMPL(get_currentexe) { char buffer[PATHMAX]; size_t size; char* match; char* path; int r; size = sizeof(buffer) / sizeof(buffer[0]); r = uv_exepath(buffer, &size); ASSERT(!r); /* uv_exepath can return an absolute path on darwin, so if the test runner * was run with a relative prefix of "./", we need to strip that prefix off * executable_path or we'll fail. */ if (executable_path[0] == '.' && executable_path[1] == '/') { path = executable_path + 2; } else { path = executable_path; } match = strstr(buffer, path); /* Verify that the path returned from uv_exepath is a subdirectory of * executable_path. */ ASSERT(match && !strcmp(match, path)); ASSERT(size == strlen(buffer)); /* Negative tests */ size = sizeof(buffer) / sizeof(buffer[0]); r = uv_exepath(NULL, &size); ASSERT(r == UV_EINVAL); r = uv_exepath(buffer, NULL); ASSERT(r == UV_EINVAL); size = 0; r = uv_exepath(buffer, &size); ASSERT(r == UV_EINVAL); memset(buffer, -1, sizeof(buffer)); size = 1; r = uv_exepath(buffer, &size); ASSERT(r == 0); ASSERT(size == 0); ASSERT(buffer[0] == '\0'); memset(buffer, -1, sizeof(buffer)); size = 2; r = uv_exepath(buffer, &size); ASSERT(r == 0); ASSERT(size == 1); ASSERT(buffer[0] != '\0'); ASSERT(buffer[1] == '\0'); return 0; } libuv-v1.8.0/test/test-get-loadavg.c000066400000000000000000000025111263361716600173330ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" TEST_IMPL(get_loadavg) { double avg[3] = {-1, -1, -1}; uv_loadavg(avg); ASSERT(avg[0] >= 0); ASSERT(avg[1] >= 0); ASSERT(avg[2] >= 0); return 0; } libuv-v1.8.0/test/test-get-memory.c000066400000000000000000000027711263361716600172360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" TEST_IMPL(get_memory) { uint64_t free_mem = uv_get_free_memory(); uint64_t total_mem = uv_get_total_memory(); printf("free_mem=%llu, total_mem=%llu\n", (unsigned long long) free_mem, (unsigned long long) total_mem); ASSERT(free_mem > 0); ASSERT(total_mem > 0); ASSERT(total_mem > free_mem); return 0; } libuv-v1.8.0/test/test-getaddrinfo.c000066400000000000000000000116151263361716600174340ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #define CONCURRENT_COUNT 10 static const char* name = "localhost"; static int getaddrinfo_cbs = 0; /* data used for running multiple calls concurrently */ static uv_getaddrinfo_t* getaddrinfo_handle; static uv_getaddrinfo_t getaddrinfo_handles[CONCURRENT_COUNT]; static int callback_counts[CONCURRENT_COUNT]; static int fail_cb_called; static void getaddrinfo_fail_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { ASSERT(fail_cb_called == 0); ASSERT(status < 0); ASSERT(res == NULL); uv_freeaddrinfo(res); /* Should not crash. */ fail_cb_called++; } static void getaddrinfo_basic_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { ASSERT(handle == getaddrinfo_handle); getaddrinfo_cbs++; free(handle); uv_freeaddrinfo(res); } static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { int i; int* data = (int*)handle->data; for (i = 0; i < CONCURRENT_COUNT; i++) { if (&getaddrinfo_handles[i] == handle) { ASSERT(i == *data); callback_counts[i]++; break; } } ASSERT (i < CONCURRENT_COUNT); free(data); uv_freeaddrinfo(res); getaddrinfo_cbs++; } TEST_IMPL(getaddrinfo_fail) { uv_getaddrinfo_t req; /* Use a FQDN by ending in a period */ ASSERT(0 == uv_getaddrinfo(uv_default_loop(), &req, getaddrinfo_fail_cb, "xyzzy.xyzzy.xyzzy.", NULL, NULL)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(fail_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getaddrinfo_fail_sync) { uv_getaddrinfo_t req; /* Use a FQDN by ending in a period */ ASSERT(0 > uv_getaddrinfo(uv_default_loop(), &req, NULL, "xyzzy.xyzzy.xyzzy.", NULL, NULL)); uv_freeaddrinfo(req.addrinfo); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getaddrinfo_basic) { int r; getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); r = uv_getaddrinfo(uv_default_loop(), getaddrinfo_handle, &getaddrinfo_basic_cb, name, NULL, NULL); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(getaddrinfo_cbs == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getaddrinfo_basic_sync) { uv_getaddrinfo_t req; ASSERT(0 == uv_getaddrinfo(uv_default_loop(), &req, NULL, name, NULL, NULL)); uv_freeaddrinfo(req.addrinfo); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getaddrinfo_concurrent) { int i, r; int* data; for (i = 0; i < CONCURRENT_COUNT; i++) { callback_counts[i] = 0; data = (int*)malloc(sizeof(int)); ASSERT(data != NULL); *data = i; getaddrinfo_handles[i].data = data; r = uv_getaddrinfo(uv_default_loop(), &getaddrinfo_handles[i], &getaddrinfo_cuncurrent_cb, name, NULL, NULL); ASSERT(r == 0); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); for (i = 0; i < CONCURRENT_COUNT; i++) { ASSERT(callback_counts[i] == 1); } MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-getnameinfo.c000066400000000000000000000056211263361716600174420ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include static const char* address_ip4 = "127.0.0.1"; static const char* address_ip6 = "::1"; static const int port = 80; static struct sockaddr_in addr4; static struct sockaddr_in6 addr6; static uv_getnameinfo_t req; static void getnameinfo_req(uv_getnameinfo_t* handle, int status, const char* hostname, const char* service) { ASSERT(handle != NULL); ASSERT(status == 0); ASSERT(hostname != NULL); ASSERT(service != NULL); } TEST_IMPL(getnameinfo_basic_ip4) { int r; r = uv_ip4_addr(address_ip4, port, &addr4); ASSERT(r == 0); r = uv_getnameinfo(uv_default_loop(), &req, &getnameinfo_req, (const struct sockaddr*)&addr4, 0); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getnameinfo_basic_ip4_sync) { ASSERT(0 == uv_ip4_addr(address_ip4, port, &addr4)); ASSERT(0 == uv_getnameinfo(uv_default_loop(), &req, NULL, (const struct sockaddr*)&addr4, 0)); ASSERT(req.host[0] != '\0'); ASSERT(req.service[0] != '\0'); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getnameinfo_basic_ip6) { int r; r = uv_ip6_addr(address_ip6, port, &addr6); ASSERT(r == 0); r = uv_getnameinfo(uv_default_loop(), &req, &getnameinfo_req, (const struct sockaddr*)&addr6, 0); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-getsockname.c000066400000000000000000000214311263361716600174430ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include static const int server_port = TEST_PORT; /* Will be updated right after making the uv_connect_call */ static int connect_port = -1; static int getsocknamecount = 0; static int getpeernamecount = 0; static uv_loop_t* loop; static uv_tcp_t tcp; static uv_udp_t udp; static uv_connect_t connect_req; static uv_tcp_t tcpServer; static uv_udp_t udpServer; static uv_udp_send_t send_req; static void alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } static void on_close(uv_handle_t* peer) { free(peer); uv_close((uv_handle_t*)&tcpServer, NULL); } static void after_shutdown(uv_shutdown_t* req, int status) { uv_close((uv_handle_t*) req->handle, on_close); free(req); } static void after_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { uv_shutdown_t* req; int r; if (buf->base) { free(buf->base); } req = (uv_shutdown_t*) malloc(sizeof *req); r = uv_shutdown(req, handle, after_shutdown); ASSERT(r == 0); } static void check_sockname(struct sockaddr* addr, const char* compare_ip, int compare_port, const char* context) { struct sockaddr_in check_addr = *(struct sockaddr_in*) addr; struct sockaddr_in compare_addr; char check_ip[17]; int r; ASSERT(0 == uv_ip4_addr(compare_ip, compare_port, &compare_addr)); /* Both addresses should be ipv4 */ ASSERT(check_addr.sin_family == AF_INET); ASSERT(compare_addr.sin_family == AF_INET); /* Check if the ip matches */ ASSERT(memcmp(&check_addr.sin_addr, &compare_addr.sin_addr, sizeof compare_addr.sin_addr) == 0); /* Check if the port matches. If port == 0 anything goes. */ ASSERT(compare_port == 0 || check_addr.sin_port == compare_addr.sin_port); r = uv_ip4_name(&check_addr, (char*) check_ip, sizeof check_ip); ASSERT(r == 0); printf("%s: %s:%d\n", context, check_ip, ntohs(check_addr.sin_port)); } static void on_connection(uv_stream_t* server, int status) { struct sockaddr sockname, peername; int namelen; uv_tcp_t* handle; int r; if (status != 0) { fprintf(stderr, "Connect error %s\n", uv_err_name(status)); } ASSERT(status == 0); handle = malloc(sizeof(*handle)); ASSERT(handle != NULL); r = uv_tcp_init(loop, handle); ASSERT(r == 0); /* associate server with stream */ handle->data = server; r = uv_accept(server, (uv_stream_t*)handle); ASSERT(r == 0); namelen = sizeof sockname; r = uv_tcp_getsockname(handle, &sockname, &namelen); ASSERT(r == 0); check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket"); getsocknamecount++; namelen = sizeof peername; r = uv_tcp_getpeername(handle, &peername, &namelen); ASSERT(r == 0); check_sockname(&peername, "127.0.0.1", connect_port, "accepted socket peer"); getpeernamecount++; r = uv_read_start((uv_stream_t*)handle, alloc, after_read); ASSERT(r == 0); } static void on_connect(uv_connect_t* req, int status) { struct sockaddr sockname, peername; int r, namelen; ASSERT(status == 0); namelen = sizeof sockname; r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen); ASSERT(r == 0); check_sockname(&sockname, "127.0.0.1", 0, "connected socket"); getsocknamecount++; namelen = sizeof peername; r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen); ASSERT(r == 0); check_sockname(&peername, "127.0.0.1", server_port, "connected socket peer"); getpeernamecount++; uv_close((uv_handle_t*)&tcp, NULL); } static int tcp_listener(void) { struct sockaddr_in addr; struct sockaddr sockname, peername; int namelen; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", server_port, &addr)); r = uv_tcp_init(loop, &tcpServer); if (r) { fprintf(stderr, "Socket creation error\n"); return 1; } r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0); if (r) { fprintf(stderr, "Bind error\n"); return 1; } r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); if (r) { fprintf(stderr, "Listen error\n"); return 1; } memset(&sockname, -1, sizeof sockname); namelen = sizeof sockname; r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen); ASSERT(r == 0); check_sockname(&sockname, "0.0.0.0", server_port, "server socket"); getsocknamecount++; namelen = sizeof sockname; r = uv_tcp_getpeername(&tcpServer, &peername, &namelen); ASSERT(r == UV_ENOTCONN); getpeernamecount++; return 0; } static void tcp_connector(void) { struct sockaddr_in server_addr; struct sockaddr sockname; int r, namelen; ASSERT(0 == uv_ip4_addr("127.0.0.1", server_port, &server_addr)); r = uv_tcp_init(loop, &tcp); tcp.data = &connect_req; ASSERT(!r); r = uv_tcp_connect(&connect_req, &tcp, (const struct sockaddr*) &server_addr, on_connect); ASSERT(!r); /* Fetch the actual port used by the connecting socket. */ namelen = sizeof sockname; r = uv_tcp_getsockname(&tcp, &sockname, &namelen); ASSERT(!r); ASSERT(sockname.sa_family == AF_INET); connect_port = ntohs(((struct sockaddr_in*) &sockname)->sin_port); ASSERT(connect_port > 0); } static void udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { struct sockaddr sockname; int namelen; int r; ASSERT(nread >= 0); free(buf->base); if (nread == 0) { return; } memset(&sockname, -1, sizeof sockname); namelen = sizeof(sockname); r = uv_udp_getsockname(&udp, &sockname, &namelen); ASSERT(r == 0); check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket"); getsocknamecount++; uv_close((uv_handle_t*) &udp, NULL); uv_close((uv_handle_t*) handle, NULL); } static void udp_send(uv_udp_send_t* req, int status) { } static int udp_listener(void) { struct sockaddr_in addr; struct sockaddr sockname; int namelen; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", server_port, &addr)); r = uv_udp_init(loop, &udpServer); if (r) { fprintf(stderr, "Socket creation error\n"); return 1; } r = uv_udp_bind(&udpServer, (const struct sockaddr*) &addr, 0); if (r) { fprintf(stderr, "Bind error\n"); return 1; } memset(&sockname, -1, sizeof sockname); namelen = sizeof sockname; r = uv_udp_getsockname(&udpServer, &sockname, &namelen); ASSERT(r == 0); check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket"); getsocknamecount++; r = uv_udp_recv_start(&udpServer, alloc, udp_recv); ASSERT(r == 0); return 0; } static void udp_sender(void) { struct sockaddr_in server_addr; uv_buf_t buf; int r; r = uv_udp_init(loop, &udp); ASSERT(!r); buf = uv_buf_init("PING", 4); ASSERT(0 == uv_ip4_addr("127.0.0.1", server_port, &server_addr)); r = uv_udp_send(&send_req, &udp, &buf, 1, (const struct sockaddr*) &server_addr, udp_send); ASSERT(!r); } TEST_IMPL(getsockname_tcp) { loop = uv_default_loop(); if (tcp_listener()) return 1; tcp_connector(); uv_run(loop, UV_RUN_DEFAULT); ASSERT(getsocknamecount == 3); ASSERT(getpeernamecount == 3); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(getsockname_udp) { loop = uv_default_loop(); if (udp_listener()) return 1; udp_sender(); uv_run(loop, UV_RUN_DEFAULT); ASSERT(getsocknamecount == 2); ASSERT(udp.send_queue_size == 0); ASSERT(udpServer.send_queue_size == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-handle-fileno.c000066400000000000000000000072271263361716600176570ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int get_tty_fd(void) { /* Make sure we have an FD that refers to a tty */ #ifdef _WIN32 HANDLE handle; handle = CreateFileA("conout$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) return -1; return _open_osfhandle((intptr_t) handle, 0); #else /* unix */ return open("/dev/tty", O_RDONLY, 0); #endif } TEST_IMPL(handle_fileno) { int r; int tty_fd; struct sockaddr_in addr; uv_os_fd_t fd; uv_tcp_t tcp; uv_udp_t udp; uv_pipe_t pipe; uv_tty_t tty; uv_idle_t idle; uv_loop_t* loop; loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_idle_init(loop, &idle); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &idle, &fd); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*) &idle, NULL); r = uv_tcp_init(loop, &tcp); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &tcp, &fd); ASSERT(r == UV_EBADF); r = uv_tcp_bind(&tcp, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &tcp, &fd); ASSERT(r == 0); uv_close((uv_handle_t*) &tcp, NULL); r = uv_fileno((uv_handle_t*) &tcp, &fd); ASSERT(r == UV_EBADF); r = uv_udp_init(loop, &udp); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &udp, &fd); ASSERT(r == UV_EBADF); r = uv_udp_bind(&udp, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &udp, &fd); ASSERT(r == 0); uv_close((uv_handle_t*) &udp, NULL); r = uv_fileno((uv_handle_t*) &udp, &fd); ASSERT(r == UV_EBADF); r = uv_pipe_init(loop, &pipe, 0); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &pipe, &fd); ASSERT(r == UV_EBADF); r = uv_pipe_bind(&pipe, TEST_PIPENAME); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &pipe, &fd); ASSERT(r == 0); uv_close((uv_handle_t*) &pipe, NULL); r = uv_fileno((uv_handle_t*) &pipe, &fd); ASSERT(r == UV_EBADF); tty_fd = get_tty_fd(); if (tty_fd < 0) { fprintf(stderr, "Cannot open a TTY fd"); fflush(stderr); } else { r = uv_tty_init(loop, &tty, tty_fd, 0); ASSERT(r == 0); r = uv_fileno((uv_handle_t*) &tty, &fd); ASSERT(r == 0); uv_close((uv_handle_t*) &tty, NULL); r = uv_fileno((uv_handle_t*) &tty, &fd); ASSERT(r == UV_EBADF); } uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-homedir.c000066400000000000000000000016721263361716600165770ustar00rootroot00000000000000#include "uv.h" #include "task.h" #include #define PATHMAX 1024 #define SMALLPATH 1 TEST_IMPL(homedir) { char homedir[PATHMAX]; size_t len; char last; int r; /* Test the normal case */ len = sizeof homedir; homedir[0] = '\0'; ASSERT(strlen(homedir) == 0); r = uv_os_homedir(homedir, &len); ASSERT(r == 0); ASSERT(strlen(homedir) == len); ASSERT(len > 0); ASSERT(homedir[len] == '\0'); if (len > 1) { last = homedir[len - 1]; #ifdef _WIN32 ASSERT(last != '\\'); #else ASSERT(last != '/'); #endif } /* Test the case where the buffer is too small */ len = SMALLPATH; r = uv_os_homedir(homedir, &len); ASSERT(r == UV_ENOBUFS); ASSERT(len > SMALLPATH); /* Test invalid inputs */ r = uv_os_homedir(NULL, &len); ASSERT(r == UV_EINVAL); r = uv_os_homedir(homedir, NULL); ASSERT(r == UV_EINVAL); len = 0; r = uv_os_homedir(homedir, &len); ASSERT(r == UV_EINVAL); return 0; } libuv-v1.8.0/test/test-hrtime.c000066400000000000000000000035061263361716600164360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #ifndef MILLISEC # define MILLISEC 1000 #endif #ifndef NANOSEC # define NANOSEC ((uint64_t) 1e9) #endif TEST_IMPL(hrtime) { uint64_t a, b, diff; int i = 75; while (i > 0) { a = uv_hrtime(); uv_sleep(45); b = uv_hrtime(); diff = b - a; /* printf("i= %d diff = %llu\n", i, (unsigned long long int) diff); */ /* The windows Sleep() function has only a resolution of 10-20 ms. */ /* Check that the difference between the two hrtime values is somewhat in */ /* the range we expect it to be. */ ASSERT(diff > (uint64_t) 25 * NANOSEC / MILLISEC); ASSERT(diff < (uint64_t) 80 * NANOSEC / MILLISEC); --i; } return 0; } libuv-v1.8.0/test/test-idle.c000066400000000000000000000054041263361716600160620ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_idle_t idle_handle; static uv_check_t check_handle; static uv_timer_t timer_handle; static int idle_cb_called = 0; static int check_cb_called = 0; static int timer_cb_called = 0; static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer_handle); uv_close((uv_handle_t*) &idle_handle, close_cb); uv_close((uv_handle_t*) &check_handle, close_cb); uv_close((uv_handle_t*) &timer_handle, close_cb); timer_cb_called++; fprintf(stderr, "timer_cb %d\n", timer_cb_called); fflush(stderr); } static void idle_cb(uv_idle_t* handle) { ASSERT(handle == &idle_handle); idle_cb_called++; fprintf(stderr, "idle_cb %d\n", idle_cb_called); fflush(stderr); } static void check_cb(uv_check_t* handle) { ASSERT(handle == &check_handle); check_cb_called++; fprintf(stderr, "check_cb %d\n", check_cb_called); fflush(stderr); } TEST_IMPL(idle_starvation) { int r; r = uv_idle_init(uv_default_loop(), &idle_handle); ASSERT(r == 0); r = uv_idle_start(&idle_handle, idle_cb); ASSERT(r == 0); r = uv_check_init(uv_default_loop(), &check_handle); ASSERT(r == 0); r = uv_check_start(&check_handle, check_cb); ASSERT(r == 0); r = uv_timer_init(uv_default_loop(), &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, timer_cb, 50, 0); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(idle_cb_called > 0); ASSERT(timer_cb_called == 1); ASSERT(close_cb_called == 3); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-ip4-addr.c000066400000000000000000000034641263361716600165550ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include TEST_IMPL(ip4_addr) { struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_ip4_addr("255.255.255.255", TEST_PORT, &addr)); ASSERT(UV_EINVAL == uv_ip4_addr("255.255.255*000", TEST_PORT, &addr)); ASSERT(UV_EINVAL == uv_ip4_addr("255.255.255.256", TEST_PORT, &addr)); ASSERT(UV_EINVAL == uv_ip4_addr("2555.0.0.0", TEST_PORT, &addr)); ASSERT(UV_EINVAL == uv_ip4_addr("255", TEST_PORT, &addr)); /* for broken address family */ ASSERT(UV_EAFNOSUPPORT == uv_inet_pton(42, "127.0.0.1", &addr.sin_addr.s_addr)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-ip6-addr.c000066400000000000000000000123261263361716600165540ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #ifdef __linux__ # include # include #endif TEST_IMPL(ip6_addr_link_local) { char string_address[INET6_ADDRSTRLEN]; uv_interface_address_t* addresses; uv_interface_address_t* address; struct sockaddr_in6 addr; unsigned int iface_index; const char* device_name; /* 40 bytes address, 16 bytes device name, plus reserve. */ char scoped_addr[128]; int count; int ix; ASSERT(0 == uv_interface_addresses(&addresses, &count)); for (ix = 0; ix < count; ix++) { address = addresses + ix; if (address->address.address6.sin6_family != AF_INET6) continue; ASSERT(0 == uv_inet_ntop(AF_INET6, &address->address.address6.sin6_addr, string_address, sizeof(string_address))); /* Skip addresses that are not link-local. */ if (strncmp(string_address, "fe80::", 6) != 0) continue; iface_index = address->address.address6.sin6_scope_id; device_name = address->name; #ifdef _WIN32 snprintf(scoped_addr, sizeof(scoped_addr), "%s%%%d", string_address, iface_index); #else snprintf(scoped_addr, sizeof(scoped_addr), "%s%%%s", string_address, device_name); #endif fprintf(stderr, "Testing link-local address %s " "(iface_index: 0x%02x, device_name: %s)\n", scoped_addr, iface_index, device_name); fflush(stderr); ASSERT(0 == uv_ip6_addr(scoped_addr, TEST_PORT, &addr)); fprintf(stderr, "Got scope_id 0x%02x\n", addr.sin6_scope_id); fflush(stderr); ASSERT(iface_index == addr.sin6_scope_id); } uv_free_interface_addresses(addresses, count); MAKE_VALGRIND_HAPPY(); return 0; } #define GOOD_ADDR_LIST(X) \ X("::") \ X("::1") \ X("fe80::1") \ X("fe80::") \ X("fe80::2acf:daff:fedd:342a") \ X("fe80:0:0:0:2acf:daff:fedd:342a") \ X("fe80:0:0:0:2acf:daff:1.2.3.4") \ X("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") \ #define BAD_ADDR_LIST(X) \ X(":::1") \ X("abcde::1") \ X("fe80:0:0:0:2acf:daff:fedd:342a:5678") \ X("fe80:0:0:0:2acf:daff:abcd:1.2.3.4") \ X("fe80:0:0:2acf:daff:1.2.3.4.5") \ X("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255.255") \ #define TEST_GOOD(ADDR) \ ASSERT(0 == uv_inet_pton(AF_INET6, ADDR, &addr)); \ ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ #define TEST_BAD(ADDR) \ ASSERT(0 != uv_inet_pton(AF_INET6, ADDR, &addr)); \ ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ TEST_IMPL(ip6_pton) { struct in6_addr addr; GOOD_ADDR_LIST(TEST_GOOD) BAD_ADDR_LIST(TEST_BAD) MAKE_VALGRIND_HAPPY(); return 0; } #undef GOOD_ADDR_LIST #undef BAD_ADDR_LIST libuv-v1.8.0/test/test-ipc-send-recv.c000066400000000000000000000235361263361716600176120ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include /* See test-ipc.ctx */ void spawn_helper(uv_pipe_t* channel, uv_process_t* process, const char* helper); void ipc_send_recv_helper_threadproc(void* arg); union handles { uv_handle_t handle; uv_stream_t stream; uv_pipe_t pipe; uv_tcp_t tcp; uv_tty_t tty; }; struct test_ctx { uv_pipe_t channel; uv_connect_t connect_req; uv_write_t write_req; uv_write_t write_req2; uv_handle_type expected_type; union handles send; union handles send2; union handles recv; union handles recv2; }; struct echo_ctx { uv_pipe_t listen; uv_pipe_t channel; uv_write_t write_req; uv_write_t write_req2; uv_handle_type expected_type; union handles recv; union handles recv2; }; static struct test_ctx ctx; static struct echo_ctx ctx2; /* Used in write2_cb to decide if we need to cleanup or not */ static int is_child_process; static int is_in_process; static int read_cb_called; static int recv_cb_called; static int write2_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { /* we're not actually reading anything so a small buffer is okay */ static char slab[8]; buf->base = slab; buf->len = sizeof(slab); } static void recv_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { uv_handle_type pending; uv_pipe_t* pipe; int r; union handles* recv; if (++recv_cb_called == 1) { recv = &ctx.recv; } else { recv = &ctx.recv2; } pipe = (uv_pipe_t*) handle; ASSERT(pipe == &ctx.channel); /* Depending on the OS, the final recv_cb can be called after the child * process has terminated which can result in nread being UV_EOF instead of * the number of bytes read. Since the other end of the pipe has closed this * UV_EOF is an acceptable value. */ if (nread == UV_EOF) { /* UV_EOF is only acceptable for the final recv_cb call */ ASSERT(recv_cb_called == 2); } else { ASSERT(nread >= 0); ASSERT(1 == uv_pipe_pending_count(pipe)); pending = uv_pipe_pending_type(pipe); ASSERT(pending == ctx.expected_type); if (pending == UV_NAMED_PIPE) r = uv_pipe_init(ctx.channel.loop, &recv->pipe, 0); else if (pending == UV_TCP) r = uv_tcp_init(ctx.channel.loop, &recv->tcp); else abort(); ASSERT(r == 0); r = uv_accept(handle, &recv->stream); ASSERT(r == 0); } /* Close after two writes received */ if (recv_cb_called == 2) { uv_close((uv_handle_t*)&ctx.channel, NULL); } } static void connect_cb(uv_connect_t* req, int status) { int r; uv_buf_t buf; ASSERT(req == &ctx.connect_req); ASSERT(status == 0); buf = uv_buf_init(".", 1); r = uv_write2(&ctx.write_req, (uv_stream_t*)&ctx.channel, &buf, 1, &ctx.send.stream, NULL); ASSERT(r == 0); /* Perform two writes to the same pipe to make sure that on Windows we are * not running into issue 505: * https://github.com/libuv/libuv/issues/505 */ buf = uv_buf_init(".", 1); r = uv_write2(&ctx.write_req2, (uv_stream_t*)&ctx.channel, &buf, 1, &ctx.send2.stream, NULL); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&ctx.channel, alloc_cb, recv_cb); ASSERT(r == 0); } static int run_test(int inprocess) { uv_process_t process; uv_thread_t tid; int r; if (inprocess) { r = uv_thread_create(&tid, ipc_send_recv_helper_threadproc, (void *) 42); ASSERT(r == 0); uv_sleep(1000); r = uv_pipe_init(uv_default_loop(), &ctx.channel, 1); ASSERT(r == 0); uv_pipe_connect(&ctx.connect_req, &ctx.channel, TEST_PIPENAME_3, connect_cb); } else { spawn_helper(&ctx.channel, &process, "ipc_send_recv_helper"); connect_cb(&ctx.connect_req, 0); } r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(recv_cb_called == 2); if (inprocess) { r = uv_thread_join(&tid); ASSERT(r == 0); } return 0; } static int run_ipc_send_recv_pipe(int inprocess) { int r; ctx.expected_type = UV_NAMED_PIPE; r = uv_pipe_init(uv_default_loop(), &ctx.send.pipe, 1); ASSERT(r == 0); r = uv_pipe_bind(&ctx.send.pipe, TEST_PIPENAME); ASSERT(r == 0); r = uv_pipe_init(uv_default_loop(), &ctx.send2.pipe, 1); ASSERT(r == 0); r = uv_pipe_bind(&ctx.send2.pipe, TEST_PIPENAME_2); ASSERT(r == 0); r = run_test(inprocess); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(ipc_send_recv_pipe) { return run_ipc_send_recv_pipe(0); } TEST_IMPL(ipc_send_recv_pipe_inprocess) { return run_ipc_send_recv_pipe(1); } static int run_ipc_send_recv_tcp(int inprocess) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ctx.expected_type = UV_TCP; r = uv_tcp_init(uv_default_loop(), &ctx.send.tcp); ASSERT(r == 0); r = uv_tcp_init(uv_default_loop(), &ctx.send2.tcp); ASSERT(r == 0); r = uv_tcp_bind(&ctx.send.tcp, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_tcp_bind(&ctx.send2.tcp, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = run_test(inprocess); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(ipc_send_recv_tcp) { return run_ipc_send_recv_tcp(0); } TEST_IMPL(ipc_send_recv_tcp_inprocess) { return run_ipc_send_recv_tcp(1); } /* Everything here runs in a child process or second thread. */ static void write2_cb(uv_write_t* req, int status) { ASSERT(status == 0); /* After two successful writes in the child process, allow the child * process to be closed. */ if (++write2_cb_called == 2 && (is_child_process || is_in_process)) { uv_close(&ctx2.recv.handle, NULL); uv_close(&ctx2.recv2.handle, NULL); uv_close((uv_handle_t*)&ctx2.channel, NULL); uv_close((uv_handle_t*)&ctx2.listen, NULL); } } static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* rdbuf) { uv_buf_t wrbuf; uv_pipe_t* pipe; uv_handle_type pending; int r; union handles* recv; uv_write_t* write_req; if (nread == UV__EOF || nread == UV__ECONNABORTED) { return; } if (++read_cb_called == 2) { recv = &ctx2.recv; write_req = &ctx2.write_req; } else { recv = &ctx2.recv2; write_req = &ctx2.write_req2; } pipe = (uv_pipe_t*) handle; ASSERT(pipe == &ctx2.channel); ASSERT(nread >= 0); ASSERT(1 == uv_pipe_pending_count(pipe)); pending = uv_pipe_pending_type(pipe); ASSERT(pending == UV_NAMED_PIPE || pending == UV_TCP); if (pending == UV_NAMED_PIPE) r = uv_pipe_init(ctx2.channel.loop, &recv->pipe, 0); else if (pending == UV_TCP) r = uv_tcp_init(ctx2.channel.loop, &recv->tcp); else abort(); ASSERT(r == 0); r = uv_accept(handle, &recv->stream); ASSERT(r == 0); wrbuf = uv_buf_init(".", 1); r = uv_write2(write_req, (uv_stream_t*)&ctx2.channel, &wrbuf, 1, &recv->stream, write2_cb); ASSERT(r == 0); } static void send_recv_start() { int r; ASSERT(1 == uv_is_readable((uv_stream_t*)&ctx2.channel)); ASSERT(1 == uv_is_writable((uv_stream_t*)&ctx2.channel)); ASSERT(0 == uv_is_closing((uv_handle_t*)&ctx2.channel)); r = uv_read_start((uv_stream_t*)&ctx2.channel, alloc_cb, read_cb); ASSERT(r == 0); } static void listen_cb(uv_stream_t* handle, int status) { int r; ASSERT(handle == (uv_stream_t*)&ctx2.listen); ASSERT(status == 0); r = uv_accept((uv_stream_t*)&ctx2.listen, (uv_stream_t*)&ctx2.channel); ASSERT(r == 0); send_recv_start(); } int run_ipc_send_recv_helper(uv_loop_t* loop, int inprocess) { int r; is_in_process = inprocess; memset(&ctx2, 0, sizeof(ctx2)); r = uv_pipe_init(loop, &ctx2.listen, 0); ASSERT(r == 0); r = uv_pipe_init(loop, &ctx2.channel, 1); ASSERT(r == 0); if (inprocess) { r = uv_pipe_bind(&ctx2.listen, TEST_PIPENAME_3); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&ctx2.listen, SOMAXCONN, listen_cb); ASSERT(r == 0); } else { r = uv_pipe_open(&ctx2.channel, 0); ASSERT(r == 0); send_recv_start(); } r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); return 0; } /* stdin is a duplex channel over which a handle is sent. * We receive it and send it back where it came from. */ int ipc_send_recv_helper(void) { int r; r = run_ipc_send_recv_helper(uv_default_loop(), 0); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } void ipc_send_recv_helper_threadproc(void* arg) { int r; uv_loop_t loop; r = uv_loop_init(&loop); ASSERT(r == 0); r = run_ipc_send_recv_helper(&loop, 1); ASSERT(r == 0); r = uv_loop_close(&loop); ASSERT(r == 0); } libuv-v1.8.0/test/test-ipc.c000066400000000000000000000465341263361716600157310ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_pipe_t channel; static uv_tcp_t tcp_server; static uv_tcp_t tcp_server2; static uv_tcp_t tcp_connection; static int exit_cb_called; static int read_cb_called; static int tcp_write_cb_called; static int tcp_read_cb_called; static int on_pipe_read_called; static int local_conn_accepted; static int remote_conn_accepted; static int tcp_server_listening; static uv_write_t write_req; static uv_write_t conn_notify_req; static int close_cb_called; static int connection_accepted; static int tcp_conn_read_cb_called; static int tcp_conn_write_cb_called; typedef struct { uv_connect_t conn_req; uv_write_t tcp_write_req; uv_tcp_t conn; } tcp_conn; #define CONN_COUNT 100 #define BACKLOG 128 static void close_server_conn_cb(uv_handle_t* handle) { free(handle); } static void on_connection(uv_stream_t* server, int status) { uv_tcp_t* conn; int r; if (!local_conn_accepted) { /* Accept the connection and close it. Also and close the server. */ ASSERT(status == 0); ASSERT((uv_stream_t*)&tcp_server == server); conn = malloc(sizeof(*conn)); ASSERT(conn); r = uv_tcp_init(server->loop, conn); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)conn); ASSERT(r == 0); uv_close((uv_handle_t*)conn, close_server_conn_cb); uv_close((uv_handle_t*)server, NULL); local_conn_accepted = 1; } } static void exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) { printf("exit_cb\n"); exit_cb_called++; ASSERT(exit_status == 0); uv_close((uv_handle_t*)process, NULL); } static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } static void close_client_conn_cb(uv_handle_t* handle) { tcp_conn* p = (tcp_conn*)handle->data; free(p); } static void connect_cb(uv_connect_t* req, int status) { uv_close((uv_handle_t*)req->handle, close_client_conn_cb); } static void make_many_connections(void) { tcp_conn* conn; struct sockaddr_in addr; int r, i; for (i = 0; i < CONN_COUNT; i++) { conn = malloc(sizeof(*conn)); ASSERT(conn); r = uv_tcp_init(uv_default_loop(), &conn->conn); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_connect(&conn->conn_req, (uv_tcp_t*) &conn->conn, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); conn->conn.data = conn; } } static void on_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { int r; uv_pipe_t* pipe; uv_handle_type pending; uv_buf_t outbuf; pipe = (uv_pipe_t*) handle; if (nread == 0) { /* Everything OK, but nothing read. */ free(buf->base); return; } if (nread < 0) { if (nread == UV_EOF) { free(buf->base); return; } printf("error recving on channel: %s\n", uv_strerror(nread)); abort(); } fprintf(stderr, "got %d bytes\n", (int)nread); pending = uv_pipe_pending_type(pipe); if (!tcp_server_listening) { ASSERT(1 == uv_pipe_pending_count(pipe)); ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); read_cb_called++; /* Accept the pending TCP server, and start listening on it. */ ASSERT(pending == UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server); ASSERT(r == 0); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); ASSERT(r == 0); tcp_server_listening = 1; /* Make sure that the expected data is correctly multiplexed. */ ASSERT(memcmp("hello\n", buf->base, nread) == 0); outbuf = uv_buf_init("world\n", 6); r = uv_write(&write_req, (uv_stream_t*)pipe, &outbuf, 1, NULL); ASSERT(r == 0); /* Create a bunch of connections to get both servers to accept. */ make_many_connections(); } else if (memcmp("accepted_connection\n", buf->base, nread) == 0) { /* Remote server has accepted a connection. Close the channel. */ ASSERT(0 == uv_pipe_pending_count(pipe)); ASSERT(pending == UV_UNKNOWN_HANDLE); remote_conn_accepted = 1; uv_close((uv_handle_t*)&channel, NULL); } free(buf->base); } #ifdef _WIN32 static void on_read_listen_after_bound_twice(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { int r; uv_pipe_t* pipe; uv_handle_type pending; pipe = (uv_pipe_t*) handle; if (nread == 0) { /* Everything OK, but nothing read. */ free(buf->base); return; } if (nread < 0) { if (nread == UV_EOF) { free(buf->base); return; } printf("error recving on channel: %s\n", uv_strerror(nread)); abort(); } fprintf(stderr, "got %d bytes\n", (int)nread); ASSERT(uv_pipe_pending_count(pipe) > 0); pending = uv_pipe_pending_type(pipe); ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); read_cb_called++; if (read_cb_called == 1) { /* Accept the first TCP server, and start listening on it. */ ASSERT(pending == UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server); ASSERT(r == 0); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); ASSERT(r == 0); } else if (read_cb_called == 2) { /* Accept the second TCP server, and start listening on it. */ ASSERT(pending == UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server2); ASSERT(r == 0); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server2); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcp_server2, BACKLOG, on_connection); ASSERT(r == UV_EADDRINUSE); uv_close((uv_handle_t*)&tcp_server, NULL); uv_close((uv_handle_t*)&tcp_server2, NULL); ASSERT(0 == uv_pipe_pending_count(pipe)); uv_close((uv_handle_t*)&channel, NULL); } free(buf->base); } #endif void spawn_helper(uv_pipe_t* channel, uv_process_t* process, const char* helper) { uv_process_options_t options; size_t exepath_size; char exepath[1024]; char* args[3]; int r; uv_stdio_container_t stdio[1]; r = uv_pipe_init(uv_default_loop(), channel, 1); ASSERT(r == 0); ASSERT(channel->ipc); exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); ASSERT(r == 0); exepath[exepath_size] = '\0'; args[0] = exepath; args[1] = (char*)helper; args[2] = NULL; memset(&options, 0, sizeof(options)); options.file = exepath; options.args = args; options.exit_cb = exit_cb; options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*)channel; options.stdio_count = 1; r = uv_spawn(uv_default_loop(), process, &options); ASSERT(r == 0); } static void on_tcp_write(uv_write_t* req, int status) { ASSERT(status == 0); ASSERT(req->handle == (uv_stream_t*)&tcp_connection); tcp_write_cb_called++; } static void on_read_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } static void on_tcp_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ASSERT(nread > 0); ASSERT(memcmp("hello again\n", buf->base, nread) == 0); ASSERT(tcp == (uv_stream_t*)&tcp_connection); free(buf->base); tcp_read_cb_called++; uv_close((uv_handle_t*)tcp, NULL); uv_close((uv_handle_t*)&channel, NULL); } static void on_read_connection(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { int r; uv_buf_t outbuf; uv_pipe_t* pipe; uv_handle_type pending; pipe = (uv_pipe_t*) handle; if (nread == 0) { /* Everything OK, but nothing read. */ free(buf->base); return; } if (nread < 0) { if (nread == UV_EOF) { free(buf->base); return; } printf("error recving on channel: %s\n", uv_strerror(nread)); abort(); } fprintf(stderr, "got %d bytes\n", (int)nread); ASSERT(1 == uv_pipe_pending_count(pipe)); pending = uv_pipe_pending_type(pipe); ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); read_cb_called++; /* Accept the pending TCP connection */ ASSERT(pending == UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_connection); ASSERT(r == 0); r = uv_accept(handle, (uv_stream_t*)&tcp_connection); ASSERT(r == 0); /* Make sure that the expected data is correctly multiplexed. */ ASSERT(memcmp("hello\n", buf->base, nread) == 0); /* Write/read to/from the connection */ outbuf = uv_buf_init("world\n", 6); r = uv_write(&write_req, (uv_stream_t*)&tcp_connection, &outbuf, 1, on_tcp_write); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&tcp_connection, on_read_alloc, on_tcp_read); ASSERT(r == 0); free(buf->base); } static int run_ipc_test(const char* helper, uv_read_cb read_cb) { uv_process_t process; int r; spawn_helper(&channel, &process, helper); uv_read_start((uv_stream_t*)&channel, on_alloc, read_cb); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(ipc_listen_before_write) { int r = run_ipc_test("ipc_helper_listen_before_write", on_read); ASSERT(local_conn_accepted == 1); ASSERT(remote_conn_accepted == 1); ASSERT(read_cb_called == 1); ASSERT(exit_cb_called == 1); return r; } TEST_IMPL(ipc_listen_after_write) { int r = run_ipc_test("ipc_helper_listen_after_write", on_read); ASSERT(local_conn_accepted == 1); ASSERT(remote_conn_accepted == 1); ASSERT(read_cb_called == 1); ASSERT(exit_cb_called == 1); return r; } TEST_IMPL(ipc_tcp_connection) { int r = run_ipc_test("ipc_helper_tcp_connection", on_read_connection); ASSERT(read_cb_called == 1); ASSERT(tcp_write_cb_called == 1); ASSERT(tcp_read_cb_called == 1); ASSERT(exit_cb_called == 1); return r; } #ifdef _WIN32 TEST_IMPL(listen_with_simultaneous_accepts) { uv_tcp_t server; int r; struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_tcp_simultaneous_accepts(&server, 1); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); ASSERT(r == 0); ASSERT(server.reqs_pending == 32); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(listen_no_simultaneous_accepts) { uv_tcp_t server; int r; struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_tcp_simultaneous_accepts(&server, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); ASSERT(r == 0); ASSERT(server.reqs_pending == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(ipc_listen_after_bind_twice) { int r = run_ipc_test("ipc_helper_bind_twice", on_read_listen_after_bound_twice); ASSERT(read_cb_called == 2); ASSERT(exit_cb_called == 1); return r; } #endif /* Everything here runs in a child process. */ static tcp_conn conn; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void conn_notify_write_cb(uv_write_t* req, int status) { uv_close((uv_handle_t*)&tcp_server, close_cb); uv_close((uv_handle_t*)&channel, close_cb); } static void tcp_connection_write_cb(uv_write_t* req, int status) { ASSERT((uv_handle_t*)&conn.conn == (uv_handle_t*)req->handle); uv_close((uv_handle_t*)req->handle, close_cb); uv_close((uv_handle_t*)&channel, close_cb); uv_close((uv_handle_t*)&tcp_server, close_cb); tcp_conn_write_cb_called++; } static void on_tcp_child_process_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { uv_buf_t outbuf; int r; if (nread < 0) { if (nread == UV_EOF) { free(buf->base); return; } printf("error recving on tcp connection: %s\n", uv_strerror(nread)); abort(); } ASSERT(nread > 0); ASSERT(memcmp("world\n", buf->base, nread) == 0); on_pipe_read_called++; free(buf->base); /* Write to the socket */ outbuf = uv_buf_init("hello again\n", 12); r = uv_write(&conn.tcp_write_req, tcp, &outbuf, 1, tcp_connection_write_cb); ASSERT(r == 0); tcp_conn_read_cb_called++; } static void connect_child_process_cb(uv_connect_t* req, int status) { int r; ASSERT(status == 0); r = uv_read_start(req->handle, on_read_alloc, on_tcp_child_process_read); ASSERT(r == 0); } static void ipc_on_connection(uv_stream_t* server, int status) { int r; uv_buf_t buf; if (!connection_accepted) { /* * Accept the connection and close it. Also let the other * side know. */ ASSERT(status == 0); ASSERT((uv_stream_t*)&tcp_server == server); r = uv_tcp_init(server->loop, &conn.conn); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)&conn.conn); ASSERT(r == 0); uv_close((uv_handle_t*)&conn.conn, close_cb); buf = uv_buf_init("accepted_connection\n", 20); r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1, NULL, conn_notify_write_cb); ASSERT(r == 0); connection_accepted = 1; } } static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) { int r; uv_buf_t buf; uv_tcp_t* conn; ASSERT(status == 0); ASSERT((uv_stream_t*)&tcp_server == server); conn = malloc(sizeof(*conn)); ASSERT(conn); r = uv_tcp_init(server->loop, conn); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)conn); ASSERT(r == 0); /* Send the accepted connection to the other process */ buf = uv_buf_init("hello\n", 6); r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)conn, NULL); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) conn, on_read_alloc, on_tcp_child_process_read); ASSERT(r == 0); uv_close((uv_handle_t*)conn, close_cb); } int ipc_helper(int listen_after_write) { /* * This is launched from test-ipc.c. stdin is a duplex channel that we * over which a handle will be transmitted. */ struct sockaddr_in addr; uv_write_t write_req; int r; uv_buf_t buf; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_pipe_init(uv_default_loop(), &channel, 1); ASSERT(r == 0); uv_pipe_open(&channel, 0); ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); r = uv_tcp_init(uv_default_loop(), &tcp_server); ASSERT(r == 0); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); if (!listen_after_write) { r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); ASSERT(r == 0); } buf = uv_buf_init("hello\n", 6); r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server, NULL); ASSERT(r == 0); if (listen_after_write) { r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); ASSERT(r == 0); } r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(connection_accepted == 1); ASSERT(close_cb_called == 3); MAKE_VALGRIND_HAPPY(); return 0; } int ipc_helper_tcp_connection(void) { /* * This is launched from test-ipc.c. stdin is a duplex channel * over which a handle will be transmitted. */ int r; struct sockaddr_in addr; r = uv_pipe_init(uv_default_loop(), &channel, 1); ASSERT(r == 0); uv_pipe_open(&channel, 0); ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); r = uv_tcp_init(uv_default_loop(), &tcp_server); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection_tcp_conn); ASSERT(r == 0); /* Make a connection to the server */ r = uv_tcp_init(uv_default_loop(), &conn.conn); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_connect(&conn.conn_req, (uv_tcp_t*) &conn.conn, (const struct sockaddr*) &addr, connect_child_process_cb); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(tcp_conn_read_cb_called == 1); ASSERT(tcp_conn_write_cb_called == 1); ASSERT(close_cb_called == 4); MAKE_VALGRIND_HAPPY(); return 0; } int ipc_helper_bind_twice(void) { /* * This is launched from test-ipc.c. stdin is a duplex channel * over which two handles will be transmitted. */ struct sockaddr_in addr; uv_write_t write_req; uv_write_t write_req2; int r; uv_buf_t buf; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_pipe_init(uv_default_loop(), &channel, 1); ASSERT(r == 0); uv_pipe_open(&channel, 0); ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); buf = uv_buf_init("hello\n", 6); r = uv_tcp_init(uv_default_loop(), &tcp_server); ASSERT(r == 0); r = uv_tcp_init(uv_default_loop(), &tcp_server2); ASSERT(r == 0); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_tcp_bind(&tcp_server2, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server, NULL); ASSERT(r == 0); r = uv_write2(&write_req2, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server2, NULL); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-list.h000066400000000000000000000546771263361716600161450ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ TEST_DECLARE (platform_output) TEST_DECLARE (callback_order) TEST_DECLARE (close_order) TEST_DECLARE (run_once) TEST_DECLARE (run_nowait) TEST_DECLARE (loop_alive) TEST_DECLARE (loop_close) TEST_DECLARE (loop_stop) TEST_DECLARE (loop_update_time) TEST_DECLARE (loop_backend_timeout) TEST_DECLARE (loop_configure) TEST_DECLARE (default_loop_close) TEST_DECLARE (barrier_1) TEST_DECLARE (barrier_2) TEST_DECLARE (barrier_3) TEST_DECLARE (condvar_1) TEST_DECLARE (condvar_2) TEST_DECLARE (condvar_3) TEST_DECLARE (condvar_4) TEST_DECLARE (condvar_5) TEST_DECLARE (semaphore_1) TEST_DECLARE (semaphore_2) TEST_DECLARE (semaphore_3) TEST_DECLARE (tty) TEST_DECLARE (tty_file) TEST_DECLARE (stdio_over_pipes) TEST_DECLARE (ip6_pton) TEST_DECLARE (ipc_listen_before_write) TEST_DECLARE (ipc_listen_after_write) #ifndef _WIN32 TEST_DECLARE (ipc_send_recv_pipe) TEST_DECLARE (ipc_send_recv_pipe_inprocess) #endif TEST_DECLARE (ipc_send_recv_tcp) TEST_DECLARE (ipc_send_recv_tcp_inprocess) TEST_DECLARE (ipc_tcp_connection) TEST_DECLARE (tcp_ping_pong) TEST_DECLARE (tcp_ping_pong_v6) TEST_DECLARE (pipe_ping_pong) TEST_DECLARE (delayed_accept) TEST_DECLARE (multiple_listen) #ifndef _WIN32 TEST_DECLARE (tcp_write_after_connect) #endif TEST_DECLARE (tcp_writealot) TEST_DECLARE (tcp_write_fail) TEST_DECLARE (tcp_try_write) TEST_DECLARE (tcp_write_queue_order) TEST_DECLARE (tcp_open) TEST_DECLARE (tcp_open_twice) TEST_DECLARE (tcp_connect_error_after_write) TEST_DECLARE (tcp_shutdown_after_write) TEST_DECLARE (tcp_bind_error_addrinuse) TEST_DECLARE (tcp_bind_error_addrnotavail_1) TEST_DECLARE (tcp_bind_error_addrnotavail_2) TEST_DECLARE (tcp_bind_error_fault) TEST_DECLARE (tcp_bind_error_inval) TEST_DECLARE (tcp_bind_localhost_ok) TEST_DECLARE (tcp_bind_invalid_flags) TEST_DECLARE (tcp_listen_without_bind) TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) TEST_DECLARE (tcp_create_early) TEST_DECLARE (tcp_create_early_bad_bind) TEST_DECLARE (tcp_create_early_bad_domain) TEST_DECLARE (tcp_create_early_accept) #ifndef _WIN32 TEST_DECLARE (tcp_close_accept) TEST_DECLARE (tcp_oob) #endif TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_read_stop) TEST_DECLARE (tcp_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_fault) TEST_DECLARE (tcp_bind6_error_inval) TEST_DECLARE (tcp_bind6_localhost_ok) TEST_DECLARE (udp_bind) TEST_DECLARE (udp_bind_reuseaddr) TEST_DECLARE (udp_create_early) TEST_DECLARE (udp_create_early_bad_bind) TEST_DECLARE (udp_create_early_bad_domain) TEST_DECLARE (udp_send_and_recv) TEST_DECLARE (udp_send_immediate) TEST_DECLARE (udp_send_unreachable) TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join6) TEST_DECLARE (udp_multicast_ttl) TEST_DECLARE (udp_multicast_interface) TEST_DECLARE (udp_multicast_interface6) TEST_DECLARE (udp_dgram_too_big) TEST_DECLARE (udp_dual_stack) TEST_DECLARE (udp_ipv6_only) TEST_DECLARE (udp_options) TEST_DECLARE (udp_options6) TEST_DECLARE (udp_no_autobind) TEST_DECLARE (udp_open) TEST_DECLARE (udp_open_twice) TEST_DECLARE (udp_try_send) TEST_DECLARE (pipe_bind_error_addrinuse) TEST_DECLARE (pipe_bind_error_addrnotavail) TEST_DECLARE (pipe_bind_error_inval) TEST_DECLARE (pipe_connect_multiple) TEST_DECLARE (pipe_listen_without_bind) TEST_DECLARE (pipe_connect_bad_name) TEST_DECLARE (pipe_connect_to_file) TEST_DECLARE (pipe_connect_on_prepare) TEST_DECLARE (pipe_getsockname) TEST_DECLARE (pipe_getsockname_abstract) TEST_DECLARE (pipe_getsockname_blocking) TEST_DECLARE (pipe_pending_instances) TEST_DECLARE (pipe_sendmsg) TEST_DECLARE (pipe_server_close) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) TEST_DECLARE (shutdown_close_tcp) TEST_DECLARE (shutdown_close_pipe) TEST_DECLARE (shutdown_eof) TEST_DECLARE (shutdown_twice) TEST_DECLARE (callback_stack) TEST_DECLARE (error_message) TEST_DECLARE (timer) TEST_DECLARE (timer_init) TEST_DECLARE (timer_again) TEST_DECLARE (timer_start_twice) TEST_DECLARE (timer_order) TEST_DECLARE (timer_huge_timeout) TEST_DECLARE (timer_huge_repeat) TEST_DECLARE (timer_run_once) TEST_DECLARE (timer_from_check) TEST_DECLARE (timer_null_callback) TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (get_loadavg) TEST_DECLARE (walk_handles) TEST_DECLARE (watcher_cross_stop) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) TEST_DECLARE (prepare_ref) TEST_DECLARE (check_ref) TEST_DECLARE (unref_in_prepare_cb) TEST_DECLARE (timer_ref) TEST_DECLARE (timer_ref2) TEST_DECLARE (fs_event_ref) TEST_DECLARE (fs_poll_ref) TEST_DECLARE (tcp_ref) TEST_DECLARE (tcp_ref2) TEST_DECLARE (tcp_ref2b) TEST_DECLARE (tcp_ref3) TEST_DECLARE (tcp_ref4) TEST_DECLARE (udp_ref) TEST_DECLARE (udp_ref2) TEST_DECLARE (udp_ref3) TEST_DECLARE (pipe_ref) TEST_DECLARE (pipe_ref2) TEST_DECLARE (pipe_ref3) TEST_DECLARE (pipe_ref4) #ifndef _WIN32 TEST_DECLARE (pipe_close_stdout_read_stdin) #endif TEST_DECLARE (pipe_set_non_blocking) TEST_DECLARE (process_ref) TEST_DECLARE (has_ref) TEST_DECLARE (active) TEST_DECLARE (embed) TEST_DECLARE (async) TEST_DECLARE (async_null_cb) TEST_DECLARE (get_currentexe) TEST_DECLARE (process_title) TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (get_memory) TEST_DECLARE (handle_fileno) TEST_DECLARE (homedir) TEST_DECLARE (hrtime) TEST_DECLARE (getaddrinfo_fail) TEST_DECLARE (getaddrinfo_fail_sync) TEST_DECLARE (getaddrinfo_basic) TEST_DECLARE (getaddrinfo_basic_sync) TEST_DECLARE (getaddrinfo_concurrent) TEST_DECLARE (getnameinfo_basic_ip4) TEST_DECLARE (getnameinfo_basic_ip4_sync) TEST_DECLARE (getnameinfo_basic_ip6) TEST_DECLARE (getsockname_tcp) TEST_DECLARE (getsockname_udp) TEST_DECLARE (fail_always) TEST_DECLARE (pass_always) TEST_DECLARE (socket_buffer_size) TEST_DECLARE (spawn_fails) #ifndef _WIN32 TEST_DECLARE (spawn_fails_check_for_waitpid_cleanup) #endif TEST_DECLARE (spawn_exit_code) TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) TEST_DECLARE (spawn_stdio_greater_than_3) TEST_DECLARE (spawn_ignored_stdio) TEST_DECLARE (spawn_and_kill) TEST_DECLARE (spawn_detached) TEST_DECLARE (spawn_and_kill_with_std) TEST_DECLARE (spawn_and_ping) TEST_DECLARE (spawn_preserve_env) TEST_DECLARE (spawn_setuid_fails) TEST_DECLARE (spawn_setgid_fails) TEST_DECLARE (spawn_stdout_to_file) TEST_DECLARE (spawn_stdout_and_stderr_to_file) TEST_DECLARE (spawn_stdout_and_stderr_to_file2) TEST_DECLARE (spawn_stdout_and_stderr_to_file_swap) TEST_DECLARE (spawn_auto_unref) TEST_DECLARE (spawn_closed_process_io) TEST_DECLARE (spawn_reads_child_path) TEST_DECLARE (spawn_inherit_streams) TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (kill) TEST_DECLARE (fs_file_noent) TEST_DECLARE (fs_file_nametoolong) TEST_DECLARE (fs_file_loop) TEST_DECLARE (fs_file_async) TEST_DECLARE (fs_file_sync) TEST_DECLARE (fs_file_write_null_buffer) TEST_DECLARE (fs_async_dir) TEST_DECLARE (fs_async_sendfile) TEST_DECLARE (fs_mkdtemp) TEST_DECLARE (fs_fstat) TEST_DECLARE (fs_access) TEST_DECLARE (fs_chmod) TEST_DECLARE (fs_unlink_readonly) TEST_DECLARE (fs_chown) TEST_DECLARE (fs_link) TEST_DECLARE (fs_readlink) TEST_DECLARE (fs_realpath) TEST_DECLARE (fs_symlink) TEST_DECLARE (fs_symlink_dir) TEST_DECLARE (fs_utime) TEST_DECLARE (fs_futime) TEST_DECLARE (fs_file_open_append) TEST_DECLARE (fs_stat_missing_path) TEST_DECLARE (fs_read_file_eof) TEST_DECLARE (fs_event_watch_dir) TEST_DECLARE (fs_event_watch_dir_recursive) TEST_DECLARE (fs_event_watch_file) TEST_DECLARE (fs_event_watch_file_twice) TEST_DECLARE (fs_event_watch_file_current_dir) TEST_DECLARE (fs_event_no_callback_after_close) TEST_DECLARE (fs_event_no_callback_on_close) TEST_DECLARE (fs_event_immediate_close) TEST_DECLARE (fs_event_close_with_pending_event) TEST_DECLARE (fs_event_close_in_callback) TEST_DECLARE (fs_event_start_and_close) TEST_DECLARE (fs_event_error_reporting) TEST_DECLARE (fs_event_getpath) TEST_DECLARE (fs_scandir_empty_dir) TEST_DECLARE (fs_scandir_file) TEST_DECLARE (fs_open_dir) TEST_DECLARE (fs_rename_to_existing_file) TEST_DECLARE (fs_write_multiple_bufs) TEST_DECLARE (fs_read_write_null_arguments) TEST_DECLARE (fs_write_alotof_bufs) TEST_DECLARE (fs_write_alotof_bufs_with_offset) TEST_DECLARE (threadpool_queue_work_simple) TEST_DECLARE (threadpool_queue_work_einval) TEST_DECLARE (threadpool_multiple_event_loops) TEST_DECLARE (threadpool_cancel_getaddrinfo) TEST_DECLARE (threadpool_cancel_getnameinfo) TEST_DECLARE (threadpool_cancel_work) TEST_DECLARE (threadpool_cancel_fs) TEST_DECLARE (threadpool_cancel_single) TEST_DECLARE (thread_local_storage) TEST_DECLARE (thread_mutex) TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_rwlock_trylock) TEST_DECLARE (thread_create) TEST_DECLARE (thread_equal) TEST_DECLARE (dlerror) TEST_DECLARE (poll_duplex) TEST_DECLARE (poll_unidirectional) TEST_DECLARE (poll_close) TEST_DECLARE (ip4_addr) TEST_DECLARE (ip6_addr_link_local) #ifdef _WIN32 TEST_DECLARE (poll_close_doesnt_corrupt_stack) TEST_DECLARE (poll_closesocket) TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) #if !defined(USING_UV_SHARED) TEST_DECLARE (argument_escaping) TEST_DECLARE (environment_creation) #endif TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) TEST_DECLARE (spawn_with_an_odd_path) TEST_DECLARE (ipc_listen_after_bind_twice) #else TEST_DECLARE (emfile) TEST_DECLARE (close_fd) TEST_DECLARE (spawn_fs_open) TEST_DECLARE (spawn_setuid_setgid) TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signals) TEST_DECLARE (signal_multiple_loops) TEST_DECLARE (closed_fd_events) #endif #ifdef __APPLE__ TEST_DECLARE (osx_select) TEST_DECLARE (osx_select_many_fds) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) HELPER_DECLARE (udp4_echo_server) HELPER_DECLARE (pipe_echo_server) TEST_DECLARE (queue_foreach_delete) TASK_LIST_START TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000) #if 0 TEST_ENTRY (callback_order) #endif TEST_ENTRY (close_order) TEST_ENTRY (run_once) TEST_ENTRY (run_nowait) TEST_ENTRY (loop_alive) TEST_ENTRY (loop_close) TEST_ENTRY (loop_stop) TEST_ENTRY (loop_update_time) TEST_ENTRY (loop_backend_timeout) TEST_ENTRY (loop_configure) TEST_ENTRY (default_loop_close) TEST_ENTRY (barrier_1) TEST_ENTRY (barrier_2) TEST_ENTRY (barrier_3) TEST_ENTRY (condvar_1) TEST_ENTRY (condvar_2) TEST_ENTRY (condvar_3) TEST_ENTRY (condvar_4) TEST_ENTRY (condvar_5) TEST_ENTRY (semaphore_1) TEST_ENTRY (semaphore_2) TEST_ENTRY (semaphore_3) TEST_ENTRY (pipe_connect_bad_name) TEST_ENTRY (pipe_connect_to_file) TEST_ENTRY (pipe_connect_on_prepare) TEST_ENTRY (pipe_server_close) #ifndef _WIN32 TEST_ENTRY (pipe_close_stdout_read_stdin) #endif TEST_ENTRY (pipe_set_non_blocking) TEST_ENTRY (tty) TEST_ENTRY (tty_file) TEST_ENTRY (stdio_over_pipes) TEST_ENTRY (ip6_pton) TEST_ENTRY (ipc_listen_before_write) TEST_ENTRY (ipc_listen_after_write) #ifndef _WIN32 TEST_ENTRY (ipc_send_recv_pipe) TEST_ENTRY (ipc_send_recv_pipe_inprocess) #endif TEST_ENTRY (ipc_send_recv_tcp) TEST_ENTRY (ipc_send_recv_tcp_inprocess) TEST_ENTRY (ipc_tcp_connection) TEST_ENTRY (tcp_ping_pong) TEST_HELPER (tcp_ping_pong, tcp4_echo_server) TEST_ENTRY (tcp_ping_pong_v6) TEST_HELPER (tcp_ping_pong_v6, tcp6_echo_server) TEST_ENTRY (pipe_ping_pong) TEST_HELPER (pipe_ping_pong, pipe_echo_server) TEST_ENTRY (delayed_accept) TEST_ENTRY (multiple_listen) #ifndef _WIN32 TEST_ENTRY (tcp_write_after_connect) #endif TEST_ENTRY (tcp_writealot) TEST_HELPER (tcp_writealot, tcp4_echo_server) TEST_ENTRY (tcp_write_fail) TEST_HELPER (tcp_write_fail, tcp4_echo_server) TEST_ENTRY (tcp_try_write) TEST_ENTRY (tcp_write_queue_order) TEST_ENTRY (tcp_open) TEST_HELPER (tcp_open, tcp4_echo_server) TEST_ENTRY (tcp_open_twice) TEST_ENTRY (tcp_shutdown_after_write) TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server) TEST_ENTRY (tcp_connect_error_after_write) TEST_ENTRY (tcp_bind_error_addrinuse) TEST_ENTRY (tcp_bind_error_addrnotavail_1) TEST_ENTRY (tcp_bind_error_addrnotavail_2) TEST_ENTRY (tcp_bind_error_fault) TEST_ENTRY (tcp_bind_error_inval) TEST_ENTRY (tcp_bind_localhost_ok) TEST_ENTRY (tcp_bind_invalid_flags) TEST_ENTRY (tcp_listen_without_bind) TEST_ENTRY (tcp_connect_error_fault) TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) TEST_ENTRY (tcp_create_early) TEST_ENTRY (tcp_create_early_bad_bind) TEST_ENTRY (tcp_create_early_bad_domain) TEST_ENTRY (tcp_create_early_accept) #ifndef _WIN32 TEST_ENTRY (tcp_close_accept) TEST_ENTRY (tcp_oob) #endif TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) TEST_ENTRY (tcp_read_stop) TEST_HELPER (tcp_read_stop, tcp4_echo_server) TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrnotavail) TEST_ENTRY (tcp_bind6_error_fault) TEST_ENTRY (tcp_bind6_error_inval) TEST_ENTRY (tcp_bind6_localhost_ok) TEST_ENTRY (udp_bind) TEST_ENTRY (udp_bind_reuseaddr) TEST_ENTRY (udp_create_early) TEST_ENTRY (udp_create_early_bad_bind) TEST_ENTRY (udp_create_early_bad_domain) TEST_ENTRY (udp_send_and_recv) TEST_ENTRY (udp_send_immediate) TEST_ENTRY (udp_send_unreachable) TEST_ENTRY (udp_dgram_too_big) TEST_ENTRY (udp_dual_stack) TEST_ENTRY (udp_ipv6_only) TEST_ENTRY (udp_options) TEST_ENTRY (udp_options6) TEST_ENTRY (udp_no_autobind) TEST_ENTRY (udp_multicast_interface) TEST_ENTRY (udp_multicast_interface6) TEST_ENTRY (udp_multicast_join) TEST_ENTRY (udp_multicast_join6) TEST_ENTRY (udp_multicast_ttl) TEST_ENTRY (udp_try_send) TEST_ENTRY (udp_open) TEST_HELPER (udp_open, udp4_echo_server) TEST_ENTRY (udp_open_twice) TEST_ENTRY (pipe_bind_error_addrinuse) TEST_ENTRY (pipe_bind_error_addrnotavail) TEST_ENTRY (pipe_bind_error_inval) TEST_ENTRY (pipe_connect_multiple) TEST_ENTRY (pipe_listen_without_bind) TEST_ENTRY (pipe_getsockname) TEST_ENTRY (pipe_getsockname_abstract) TEST_ENTRY (pipe_getsockname_blocking) TEST_ENTRY (pipe_pending_instances) TEST_ENTRY (pipe_sendmsg) TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) TEST_ENTRY (shutdown_close_tcp) TEST_HELPER (shutdown_close_tcp, tcp4_echo_server) TEST_ENTRY (shutdown_close_pipe) TEST_HELPER (shutdown_close_pipe, pipe_echo_server) TEST_ENTRY (shutdown_eof) TEST_HELPER (shutdown_eof, tcp4_echo_server) TEST_ENTRY (shutdown_twice) TEST_HELPER (shutdown_twice, tcp4_echo_server) TEST_ENTRY (callback_stack) TEST_HELPER (callback_stack, tcp4_echo_server) TEST_ENTRY (error_message) TEST_ENTRY (timer) TEST_ENTRY (timer_init) TEST_ENTRY (timer_again) TEST_ENTRY (timer_start_twice) TEST_ENTRY (timer_order) TEST_ENTRY (timer_huge_timeout) TEST_ENTRY (timer_huge_repeat) TEST_ENTRY (timer_run_once) TEST_ENTRY (timer_from_check) TEST_ENTRY (timer_null_callback) TEST_ENTRY (idle_starvation) TEST_ENTRY (ref) TEST_ENTRY (idle_ref) TEST_ENTRY (fs_poll_ref) TEST_ENTRY (async_ref) TEST_ENTRY (prepare_ref) TEST_ENTRY (check_ref) TEST_ENTRY (unref_in_prepare_cb) TEST_ENTRY (timer_ref) TEST_ENTRY (timer_ref2) TEST_ENTRY (fs_event_ref) TEST_ENTRY (tcp_ref) TEST_ENTRY (tcp_ref2) TEST_ENTRY (tcp_ref2b) TEST_ENTRY (tcp_ref3) TEST_HELPER (tcp_ref3, tcp4_echo_server) TEST_ENTRY (tcp_ref4) TEST_HELPER (tcp_ref4, tcp4_echo_server) TEST_ENTRY (udp_ref) TEST_ENTRY (udp_ref2) TEST_ENTRY (udp_ref3) TEST_HELPER (udp_ref3, udp4_echo_server) TEST_ENTRY (pipe_ref) TEST_ENTRY (pipe_ref2) TEST_ENTRY (pipe_ref3) TEST_HELPER (pipe_ref3, pipe_echo_server) TEST_ENTRY (pipe_ref4) TEST_HELPER (pipe_ref4, pipe_echo_server) TEST_ENTRY (process_ref) TEST_ENTRY (has_ref) TEST_ENTRY (loop_handles) TEST_ENTRY (walk_handles) TEST_ENTRY (watcher_cross_stop) TEST_ENTRY (active) TEST_ENTRY (embed) TEST_ENTRY (async) TEST_ENTRY (async_null_cb) TEST_ENTRY (get_currentexe) TEST_ENTRY (process_title) TEST_ENTRY (cwd_and_chdir) TEST_ENTRY (get_memory) TEST_ENTRY (get_loadavg) TEST_ENTRY (handle_fileno) TEST_ENTRY (homedir) TEST_ENTRY (hrtime) TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000) TEST_ENTRY (getaddrinfo_fail_sync) TEST_ENTRY (getaddrinfo_basic) TEST_ENTRY (getaddrinfo_basic_sync) TEST_ENTRY (getaddrinfo_concurrent) TEST_ENTRY (getnameinfo_basic_ip4) TEST_ENTRY (getnameinfo_basic_ip4_sync) TEST_ENTRY (getnameinfo_basic_ip6) TEST_ENTRY (getsockname_tcp) TEST_ENTRY (getsockname_udp) TEST_ENTRY (poll_duplex) TEST_ENTRY (poll_unidirectional) TEST_ENTRY (poll_close) TEST_ENTRY (socket_buffer_size) TEST_ENTRY (spawn_fails) #ifndef _WIN32 TEST_ENTRY (spawn_fails_check_for_waitpid_cleanup) #endif TEST_ENTRY (spawn_exit_code) TEST_ENTRY (spawn_stdout) TEST_ENTRY (spawn_stdin) TEST_ENTRY (spawn_stdio_greater_than_3) TEST_ENTRY (spawn_ignored_stdio) TEST_ENTRY (spawn_and_kill) TEST_ENTRY (spawn_detached) TEST_ENTRY (spawn_and_kill_with_std) TEST_ENTRY (spawn_and_ping) TEST_ENTRY (spawn_preserve_env) TEST_ENTRY (spawn_setuid_fails) TEST_ENTRY (spawn_setgid_fails) TEST_ENTRY (spawn_stdout_to_file) TEST_ENTRY (spawn_stdout_and_stderr_to_file) TEST_ENTRY (spawn_stdout_and_stderr_to_file2) TEST_ENTRY (spawn_stdout_and_stderr_to_file_swap) TEST_ENTRY (spawn_auto_unref) TEST_ENTRY (spawn_closed_process_io) TEST_ENTRY (spawn_reads_child_path) TEST_ENTRY (spawn_inherit_streams) TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (kill) #ifdef _WIN32 TEST_ENTRY (poll_close_doesnt_corrupt_stack) TEST_ENTRY (poll_closesocket) TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) #if !defined(USING_UV_SHARED) TEST_ENTRY (argument_escaping) TEST_ENTRY (environment_creation) # endif TEST_ENTRY (listen_with_simultaneous_accepts) TEST_ENTRY (listen_no_simultaneous_accepts) TEST_ENTRY (fs_stat_root) TEST_ENTRY (spawn_with_an_odd_path) TEST_ENTRY (ipc_listen_after_bind_twice) #else TEST_ENTRY (emfile) TEST_ENTRY (close_fd) TEST_ENTRY (spawn_fs_open) TEST_ENTRY (spawn_setuid_setgid) TEST_ENTRY (we_get_signal) TEST_ENTRY (we_get_signals) TEST_ENTRY (signal_multiple_loops) TEST_ENTRY (closed_fd_events) #endif #ifdef __APPLE__ TEST_ENTRY (osx_select) TEST_ENTRY (osx_select_many_fds) #endif TEST_ENTRY (fs_file_noent) TEST_ENTRY (fs_file_nametoolong) TEST_ENTRY (fs_file_loop) TEST_ENTRY (fs_file_async) TEST_ENTRY (fs_file_sync) TEST_ENTRY (fs_file_write_null_buffer) TEST_ENTRY (fs_async_dir) TEST_ENTRY (fs_async_sendfile) TEST_ENTRY (fs_mkdtemp) TEST_ENTRY (fs_fstat) TEST_ENTRY (fs_access) TEST_ENTRY (fs_chmod) TEST_ENTRY (fs_unlink_readonly) TEST_ENTRY (fs_chown) TEST_ENTRY (fs_utime) TEST_ENTRY (fs_futime) TEST_ENTRY (fs_readlink) TEST_ENTRY (fs_realpath) TEST_ENTRY (fs_symlink) TEST_ENTRY (fs_symlink_dir) TEST_ENTRY (fs_stat_missing_path) TEST_ENTRY (fs_read_file_eof) TEST_ENTRY (fs_file_open_append) TEST_ENTRY (fs_event_watch_dir) TEST_ENTRY (fs_event_watch_dir_recursive) TEST_ENTRY (fs_event_watch_file) TEST_ENTRY (fs_event_watch_file_twice) TEST_ENTRY (fs_event_watch_file_current_dir) TEST_ENTRY (fs_event_no_callback_after_close) TEST_ENTRY (fs_event_no_callback_on_close) TEST_ENTRY (fs_event_immediate_close) TEST_ENTRY (fs_event_close_with_pending_event) TEST_ENTRY (fs_event_close_in_callback) TEST_ENTRY (fs_event_start_and_close) TEST_ENTRY (fs_event_error_reporting) TEST_ENTRY (fs_event_getpath) TEST_ENTRY (fs_scandir_empty_dir) TEST_ENTRY (fs_scandir_file) TEST_ENTRY (fs_open_dir) TEST_ENTRY (fs_rename_to_existing_file) TEST_ENTRY (fs_write_multiple_bufs) TEST_ENTRY (fs_write_alotof_bufs) TEST_ENTRY (fs_write_alotof_bufs_with_offset) TEST_ENTRY (fs_read_write_null_arguments) TEST_ENTRY (threadpool_queue_work_simple) TEST_ENTRY (threadpool_queue_work_einval) TEST_ENTRY (threadpool_multiple_event_loops) TEST_ENTRY (threadpool_cancel_getaddrinfo) TEST_ENTRY (threadpool_cancel_getnameinfo) TEST_ENTRY (threadpool_cancel_work) TEST_ENTRY (threadpool_cancel_fs) TEST_ENTRY (threadpool_cancel_single) TEST_ENTRY (thread_local_storage) TEST_ENTRY (thread_mutex) TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_rwlock_trylock) TEST_ENTRY (thread_create) TEST_ENTRY (thread_equal) TEST_ENTRY (dlerror) TEST_ENTRY (ip4_addr) TEST_ENTRY (ip6_addr_link_local) TEST_ENTRY (queue_foreach_delete) #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) TEST_ENTRY (pass_always) #endif TASK_LIST_END libuv-v1.8.0/test/test-loop-alive.c000066400000000000000000000041461263361716600172160ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_timer_t timer_handle; static void timer_cb(uv_timer_t* handle) { ASSERT(handle); } static uv_work_t work_req; static void work_cb(uv_work_t* req) { ASSERT(req); } static void after_work_cb(uv_work_t* req, int status) { ASSERT(req); ASSERT(status == 0); } TEST_IMPL(loop_alive) { int r; ASSERT(!uv_loop_alive(uv_default_loop())); /* loops with handles are alive */ uv_timer_init(uv_default_loop(), &timer_handle); uv_timer_start(&timer_handle, timer_cb, 100, 0); ASSERT(uv_loop_alive(uv_default_loop())); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(!uv_loop_alive(uv_default_loop())); /* loops with requests are alive */ r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); ASSERT(r == 0); ASSERT(uv_loop_alive(uv_default_loop())); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(!uv_loop_alive(uv_default_loop())); return 0; } libuv-v1.8.0/test/test-loop-close.c000066400000000000000000000033031263361716600172150ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_timer_t timer_handle; static void timer_cb(uv_timer_t* handle) { ASSERT(handle); uv_stop(handle->loop); } TEST_IMPL(loop_close) { int r; uv_loop_t loop; ASSERT(0 == uv_loop_init(&loop)); uv_timer_init(&loop, &timer_handle); uv_timer_start(&timer_handle, timer_cb, 100, 100); ASSERT(UV_EBUSY == uv_loop_close(&loop)); uv_run(&loop, UV_RUN_DEFAULT); uv_close((uv_handle_t*) &timer_handle, NULL); r = uv_run(&loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(0 == uv_loop_close(&loop)); return 0; } libuv-v1.8.0/test/test-loop-configure.c000066400000000000000000000026041263361716600200740ustar00rootroot00000000000000/* Copyright (c) 2014, Ben Noordhuis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "uv.h" #include "task.h" static void timer_cb(uv_timer_t* handle) { uv_close((uv_handle_t*) handle, NULL); } TEST_IMPL(loop_configure) { uv_timer_t timer_handle; uv_loop_t loop; ASSERT(0 == uv_loop_init(&loop)); #ifdef _WIN32 ASSERT(UV_ENOSYS == uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, 0)); #else ASSERT(0 == uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, SIGPROF)); #endif ASSERT(0 == uv_timer_init(&loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 10, 0)); ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); ASSERT(0 == uv_loop_close(&loop)); return 0; } libuv-v1.8.0/test/test-loop-handles.c000066400000000000000000000233571263361716600175410ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* Tests commented out with XXX are ones that are failing on Linux */ /* * Purpose of this test is to check semantics of starting and stopping * prepare, check and idle watchers. * * - A watcher must be able to safely stop or close itself; * - Once a watcher is stopped or closed its callback should never be called. * - If a watcher is closed, it is implicitly stopped and its close_cb should * be called exactly once. * - A watcher can safely start and stop other watchers of the same type. * - Prepare and check watchers are called once per event loop iterations. * - All active idle watchers are queued when the event loop has no more work * to do. This is done repeatedly until all idle watchers are inactive. * - If a watcher starts another watcher of the same type its callback is not * immediately queued. For check and prepare watchers, that means that if * a watcher makes another of the same type active, it'll not be called until * the next event loop iteration. For idle. watchers this means that the * newly activated idle watcher might not be queued immediately. * - Prepare, check, idle watchers keep the event loop alive even when they're * not active. * * This is what the test globally does: * * - prepare_1 is always active and counts event loop iterations. It also * creates and starts prepare_2 every other iteration. Finally it verifies * that no idle watchers are active before polling. * - prepare_2 is started by prepare_1 every other iteration. It immediately * stops itself. It verifies that a watcher is not queued immediately * if created by another watcher of the same type. * - There's a check watcher that stops the event loop after a certain number * of iterations. It starts a varying number of idle_1 watchers. * - Idle_1 watchers stop themselves after being called a few times. All idle_1 * watchers try to start the idle_2 watcher if it is not already started or * awaiting its close callback. * - The idle_2 watcher always exists but immediately closes itself after * being started by a check_1 watcher. It verifies that a watcher is * implicitly stopped when closed, and that a watcher can close itself * safely. * - There is a repeating timer. It does not keep the event loop alive * (ev_unref) but makes sure that the loop keeps polling the system for * events. */ #include "uv.h" #include "task.h" #include #define IDLE_COUNT 7 #define ITERATIONS 21 #define TIMEOUT 100 static uv_prepare_t prepare_1_handle; static uv_prepare_t prepare_2_handle; static uv_check_t check_handle; static uv_idle_t idle_1_handles[IDLE_COUNT]; static uv_idle_t idle_2_handle; static uv_timer_t timer_handle; static int loop_iteration = 0; static int prepare_1_cb_called = 0; static int prepare_1_close_cb_called = 0; static int prepare_2_cb_called = 0; static int prepare_2_close_cb_called = 0; static int check_cb_called = 0; static int check_close_cb_called = 0; static int idle_1_cb_called = 0; static int idle_1_close_cb_called = 0; static int idles_1_active = 0; static int idle_2_cb_called = 0; static int idle_2_close_cb_called = 0; static int idle_2_cb_started = 0; static int idle_2_is_active = 0; static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer_handle); } static void idle_2_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "IDLE_2_CLOSE_CB\n"); fflush(stderr); ASSERT(handle == (uv_handle_t*)&idle_2_handle); ASSERT(idle_2_is_active); idle_2_close_cb_called++; idle_2_is_active = 0; } static void idle_2_cb(uv_idle_t* handle) { fprintf(stderr, "%s", "IDLE_2_CB\n"); fflush(stderr); ASSERT(handle == &idle_2_handle); idle_2_cb_called++; uv_close((uv_handle_t*)handle, idle_2_close_cb); } static void idle_1_cb(uv_idle_t* handle) { int r; fprintf(stderr, "%s", "IDLE_1_CB\n"); fflush(stderr); ASSERT(handle != NULL); ASSERT(idles_1_active > 0); /* Init idle_2 and make it active */ if (!idle_2_is_active && !uv_is_closing((uv_handle_t*)&idle_2_handle)) { r = uv_idle_init(uv_default_loop(), &idle_2_handle); ASSERT(r == 0); r = uv_idle_start(&idle_2_handle, idle_2_cb); ASSERT(r == 0); idle_2_is_active = 1; idle_2_cb_started++; } idle_1_cb_called++; if (idle_1_cb_called % 5 == 0) { r = uv_idle_stop((uv_idle_t*)handle); ASSERT(r == 0); idles_1_active--; } } static void idle_1_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "IDLE_1_CLOSE_CB\n"); fflush(stderr); ASSERT(handle != NULL); idle_1_close_cb_called++; } static void prepare_1_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "PREPARE_1_CLOSE_CB"); fflush(stderr); ASSERT(handle == (uv_handle_t*)&prepare_1_handle); prepare_1_close_cb_called++; } static void check_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "CHECK_CLOSE_CB\n"); fflush(stderr); ASSERT(handle == (uv_handle_t*)&check_handle); check_close_cb_called++; } static void prepare_2_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "PREPARE_2_CLOSE_CB\n"); fflush(stderr); ASSERT(handle == (uv_handle_t*)&prepare_2_handle); prepare_2_close_cb_called++; } static void check_cb(uv_check_t* handle) { int i, r; fprintf(stderr, "%s", "CHECK_CB\n"); fflush(stderr); ASSERT(handle == &check_handle); if (loop_iteration < ITERATIONS) { /* Make some idle watchers active */ for (i = 0; i < 1 + (loop_iteration % IDLE_COUNT); i++) { r = uv_idle_start(&idle_1_handles[i], idle_1_cb); ASSERT(r == 0); idles_1_active++; } } else { /* End of the test - close all handles */ uv_close((uv_handle_t*)&prepare_1_handle, prepare_1_close_cb); uv_close((uv_handle_t*)&check_handle, check_close_cb); uv_close((uv_handle_t*)&prepare_2_handle, prepare_2_close_cb); for (i = 0; i < IDLE_COUNT; i++) { uv_close((uv_handle_t*)&idle_1_handles[i], idle_1_close_cb); } /* This handle is closed/recreated every time, close it only if it is */ /* active.*/ if (idle_2_is_active) { uv_close((uv_handle_t*)&idle_2_handle, idle_2_close_cb); } } check_cb_called++; } static void prepare_2_cb(uv_prepare_t* handle) { int r; fprintf(stderr, "%s", "PREPARE_2_CB\n"); fflush(stderr); ASSERT(handle == &prepare_2_handle); /* prepare_2 gets started by prepare_1 when (loop_iteration % 2 == 0), */ /* and it stops itself immediately. A started watcher is not queued */ /* until the next round, so when this callback is made */ /* (loop_iteration % 2 == 0) cannot be true. */ ASSERT(loop_iteration % 2 != 0); r = uv_prepare_stop((uv_prepare_t*)handle); ASSERT(r == 0); prepare_2_cb_called++; } static void prepare_1_cb(uv_prepare_t* handle) { int r; fprintf(stderr, "%s", "PREPARE_1_CB\n"); fflush(stderr); ASSERT(handle == &prepare_1_handle); if (loop_iteration % 2 == 0) { r = uv_prepare_start(&prepare_2_handle, prepare_2_cb); ASSERT(r == 0); } prepare_1_cb_called++; loop_iteration++; printf("Loop iteration %d of %d.\n", loop_iteration, ITERATIONS); } TEST_IMPL(loop_handles) { int i; int r; r = uv_prepare_init(uv_default_loop(), &prepare_1_handle); ASSERT(r == 0); r = uv_prepare_start(&prepare_1_handle, prepare_1_cb); ASSERT(r == 0); r = uv_check_init(uv_default_loop(), &check_handle); ASSERT(r == 0); r = uv_check_start(&check_handle, check_cb); ASSERT(r == 0); /* initialize only, prepare_2 is started by prepare_1_cb */ r = uv_prepare_init(uv_default_loop(), &prepare_2_handle); ASSERT(r == 0); for (i = 0; i < IDLE_COUNT; i++) { /* initialize only, idle_1 handles are started by check_cb */ r = uv_idle_init(uv_default_loop(), &idle_1_handles[i]); ASSERT(r == 0); } /* don't init or start idle_2, both is done by idle_1_cb */ /* the timer callback is there to keep the event loop polling */ /* unref it as it is not supposed to keep the loop alive */ r = uv_timer_init(uv_default_loop(), &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, timer_cb, TIMEOUT, TIMEOUT); ASSERT(r == 0); uv_unref((uv_handle_t*)&timer_handle); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(loop_iteration == ITERATIONS); ASSERT(prepare_1_cb_called == ITERATIONS); ASSERT(prepare_1_close_cb_called == 1); ASSERT(prepare_2_cb_called == floor(ITERATIONS / 2.0)); ASSERT(prepare_2_close_cb_called == 1); ASSERT(check_cb_called == ITERATIONS); ASSERT(check_close_cb_called == 1); /* idle_1_cb should be called a lot */ ASSERT(idle_1_close_cb_called == IDLE_COUNT); ASSERT(idle_2_close_cb_called == idle_2_cb_started); ASSERT(idle_2_is_active == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-loop-stop.c000066400000000000000000000044211263361716600170770ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_prepare_t prepare_handle; static uv_timer_t timer_handle; static int prepare_called = 0; static int timer_called = 0; static int num_ticks = 10; static void prepare_cb(uv_prepare_t* handle) { ASSERT(handle == &prepare_handle); prepare_called++; if (prepare_called == num_ticks) uv_prepare_stop(handle); } static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer_handle); timer_called++; if (timer_called == 1) uv_stop(uv_default_loop()); else if (timer_called == num_ticks) uv_timer_stop(handle); } TEST_IMPL(loop_stop) { int r; uv_prepare_init(uv_default_loop(), &prepare_handle); uv_prepare_start(&prepare_handle, prepare_cb); uv_timer_init(uv_default_loop(), &timer_handle); uv_timer_start(&timer_handle, timer_cb, 100, 100); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r != 0); ASSERT(timer_called == 1); r = uv_run(uv_default_loop(), UV_RUN_NOWAIT); ASSERT(r != 0); ASSERT(prepare_called > 1); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(timer_called == 10); ASSERT(prepare_called == 10); return 0; } libuv-v1.8.0/test/test-loop-time.c000066400000000000000000000040131263361716600170450ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" TEST_IMPL(loop_update_time) { uint64_t start; start = uv_now(uv_default_loop()); while (uv_now(uv_default_loop()) - start < 1000) ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); MAKE_VALGRIND_HAPPY(); return 0; } static void cb(uv_timer_t* timer) { uv_close((uv_handle_t*)timer, NULL); } TEST_IMPL(loop_backend_timeout) { uv_loop_t *loop = uv_default_loop(); uv_timer_t timer; int r; r = uv_timer_init(loop, &timer); ASSERT(r == 0); ASSERT(!uv_loop_alive(loop)); ASSERT(uv_backend_timeout(loop) == 0); r = uv_timer_start(&timer, cb, 1000, 0); /* 1 sec */ ASSERT(r == 0); ASSERT(uv_backend_timeout(loop) > 100); /* 0.1 sec */ ASSERT(uv_backend_timeout(loop) <= 1000); /* 1 sec */ r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(uv_backend_timeout(loop) == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-multiple-listen.c000066400000000000000000000056421263361716600203000ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int connection_cb_called = 0; static int close_cb_called = 0; static int connect_cb_called = 0; static uv_tcp_t server; static uv_tcp_t client; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void connection_cb(uv_stream_t* tcp, int status) { ASSERT(status == 0); uv_close((uv_handle_t*)&server, close_cb); connection_cb_called++; } static void start_server(void) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server, 128, connection_cb); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server, 128, connection_cb); ASSERT(r == 0); } static void connect_cb(uv_connect_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); free(req); uv_close((uv_handle_t*)&client, close_cb); connect_cb_called++; } static void client_connect(void) { struct sockaddr_in addr; uv_connect_t* connect_req = malloc(sizeof *connect_req); int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(connect_req != NULL); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_tcp_connect(connect_req, &client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); } TEST_IMPL(multiple_listen) { start_server(); client_connect(); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connection_cb_called == 1); ASSERT(connect_cb_called == 1); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-mutexes.c000066400000000000000000000104621263361716600166370ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_cond_t condvar; static uv_mutex_t mutex; static uv_rwlock_t rwlock; static int step; /* The mutex and rwlock tests are really poor. * They're very basic sanity checks and nothing more. * Apologies if that rhymes. */ TEST_IMPL(thread_mutex) { uv_mutex_t mutex; int r; r = uv_mutex_init(&mutex); ASSERT(r == 0); uv_mutex_lock(&mutex); uv_mutex_unlock(&mutex); uv_mutex_destroy(&mutex); return 0; } TEST_IMPL(thread_rwlock) { uv_rwlock_t rwlock; int r; r = uv_rwlock_init(&rwlock); ASSERT(r == 0); uv_rwlock_rdlock(&rwlock); uv_rwlock_rdunlock(&rwlock); uv_rwlock_wrlock(&rwlock); uv_rwlock_wrunlock(&rwlock); uv_rwlock_destroy(&rwlock); return 0; } /* Call when holding |mutex|. */ static void synchronize_nowait(void) { step += 1; uv_cond_signal(&condvar); } /* Call when holding |mutex|. */ static void synchronize(void) { int current; synchronize_nowait(); /* Wait for the other thread. Guard against spurious wakeups. */ for (current = step; current == step; uv_cond_wait(&condvar, &mutex)); ASSERT(step == current + 1); } static void thread_rwlock_trylock_peer(void* unused) { (void) &unused; uv_mutex_lock(&mutex); /* Write lock held by other thread. */ ASSERT(UV_EBUSY == uv_rwlock_tryrdlock(&rwlock)); ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Read lock held by other thread. */ ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); uv_rwlock_rdunlock(&rwlock); ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Acquire write lock. */ ASSERT(0 == uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Release write lock and acquire read lock. */ uv_rwlock_wrunlock(&rwlock); ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); synchronize(); uv_rwlock_rdunlock(&rwlock); synchronize_nowait(); /* Signal main thread we're going away. */ uv_mutex_unlock(&mutex); } TEST_IMPL(thread_rwlock_trylock) { uv_thread_t thread; ASSERT(0 == uv_cond_init(&condvar)); ASSERT(0 == uv_mutex_init(&mutex)); ASSERT(0 == uv_rwlock_init(&rwlock)); uv_mutex_lock(&mutex); ASSERT(0 == uv_thread_create(&thread, thread_rwlock_trylock_peer, NULL)); /* Hold write lock. */ ASSERT(0 == uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Releases the mutex to the other thread. */ /* Release write lock and acquire read lock. Pthreads doesn't support * the notion of upgrading or downgrading rwlocks, so neither do we. */ uv_rwlock_wrunlock(&rwlock); ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); synchronize(); /* Release read lock. */ uv_rwlock_rdunlock(&rwlock); synchronize(); /* Write lock held by other thread. */ ASSERT(UV_EBUSY == uv_rwlock_tryrdlock(&rwlock)); ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Read lock held by other thread. */ ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); uv_rwlock_rdunlock(&rwlock); ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); synchronize(); ASSERT(0 == uv_thread_join(&thread)); uv_rwlock_destroy(&rwlock); uv_mutex_unlock(&mutex); uv_mutex_destroy(&mutex); uv_cond_destroy(&condvar); return 0; } libuv-v1.8.0/test/test-osx-select.c000066400000000000000000000070351263361716600172350ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #ifdef __APPLE__ #include #include static int read_count; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static char slab[1024]; buf->base = slab; buf->len = sizeof(slab); } static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { fprintf(stdout, "got data %d\n", ++read_count); fflush(stdout); if (read_count == 3) uv_close((uv_handle_t*) stream, NULL); } TEST_IMPL(osx_select) { int r; int fd; size_t i; size_t len; const char* str; uv_tty_t tty; fd = open("/dev/tty", O_RDONLY); if (fd < 0) { fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); fflush(stderr); return TEST_SKIP; } r = uv_tty_init(uv_default_loop(), &tty, fd, 1); ASSERT(r == 0); uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); /* Emulate user-input */ str = "got some input\n" "with a couple of lines\n" "feel pretty happy\n"; for (i = 0, len = strlen(str); i < len; i++) { r = ioctl(fd, TIOCSTI, str + i); ASSERT(r == 0); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(read_count == 3); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(osx_select_many_fds) { int r; int fd; size_t i; size_t len; const char* str; struct sockaddr_in addr; uv_tty_t tty; uv_tcp_t tcps[1500]; TEST_FILE_LIMIT(ARRAY_SIZE(tcps) + 100); r = uv_ip4_addr("127.0.0.1", 0, &addr); ASSERT(r == 0); for (i = 0; i < ARRAY_SIZE(tcps); i++) { r = uv_tcp_init(uv_default_loop(), &tcps[i]); ASSERT(r == 0); r = uv_tcp_bind(&tcps[i], (const struct sockaddr *) &addr, 0); ASSERT(r == 0); uv_unref((uv_handle_t*) &tcps[i]); } fd = open("/dev/tty", O_RDONLY); if (fd < 0) { fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); fflush(stderr); return TEST_SKIP; } r = uv_tty_init(uv_default_loop(), &tty, fd, 1); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); ASSERT(r == 0); /* Emulate user-input */ str = "got some input\n" "with a couple of lines\n" "feel pretty happy\n"; for (i = 0, len = strlen(str); i < len; i++) { r = ioctl(fd, TIOCSTI, str + i); ASSERT(r == 0); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(read_count == 3); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* __APPLE__ */ libuv-v1.8.0/test/test-pass-always.c000066400000000000000000000024041263361716600174060ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "task.h" TEST_IMPL(pass_always) { /* This test always passes. It is used to test the test runner. */ return 0; } libuv-v1.8.0/test/test-ping-pong.c000066400000000000000000000150501263361716600170410ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int completed_pingers = 0; #define NUM_PINGS 1000 /* 64 bytes is enough for a pinger */ #define BUFSIZE 10240 static char PING[] = "PING\n"; static int pinger_on_connect_count; typedef struct { int pongs; int state; union { uv_tcp_t tcp; uv_pipe_t pipe; } stream; uv_connect_t connect_req; char read_buffer[BUFSIZE]; } pinger_t; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = malloc(size); buf->len = size; } static void pinger_on_close(uv_handle_t* handle) { pinger_t* pinger = (pinger_t*)handle->data; ASSERT(NUM_PINGS == pinger->pongs); free(pinger); completed_pingers++; } static void pinger_after_write(uv_write_t *req, int status) { ASSERT(status == 0); free(req); } static void pinger_write_ping(pinger_t* pinger) { uv_write_t *req; uv_buf_t buf; buf = uv_buf_init(PING, sizeof(PING) - 1); req = malloc(sizeof(*req)); if (uv_write(req, (uv_stream_t*) &pinger->stream.tcp, &buf, 1, pinger_after_write)) { FATAL("uv_write failed"); } puts("PING"); } static void pinger_read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { ssize_t i; pinger_t* pinger; pinger = (pinger_t*)stream->data; if (nread < 0) { ASSERT(nread == UV_EOF); puts("got EOF"); free(buf->base); uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); return; } /* Now we count the pings */ for (i = 0; i < nread; i++) { ASSERT(buf->base[i] == PING[pinger->state]); pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); if (pinger->state != 0) continue; printf("PONG %d\n", pinger->pongs); pinger->pongs++; if (pinger->pongs < NUM_PINGS) { pinger_write_ping(pinger); } else { uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); break; } } free(buf->base); } static void pinger_on_connect(uv_connect_t *req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; pinger_on_connect_count++; ASSERT(status == 0); ASSERT(1 == uv_is_readable(req->handle)); ASSERT(1 == uv_is_writable(req->handle)); ASSERT(0 == uv_is_closing((uv_handle_t *) req->handle)); pinger_write_ping(pinger); uv_read_start((uv_stream_t*)(req->handle), alloc_cb, pinger_read_cb); } /* same ping-pong test, but using IPv6 connection */ static void tcp_pinger_v6_new(void) { int r; struct sockaddr_in6 server_addr; pinger_t *pinger; ASSERT(0 ==uv_ip6_addr("::1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->state = 0; pinger->pongs = 0; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); pinger->stream.tcp.data = pinger; ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ r = uv_tcp_connect(&pinger->connect_req, &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); ASSERT(!r); /* Synchronous connect callbacks are not allowed. */ ASSERT(pinger_on_connect_count == 0); } static void tcp_pinger_new(void) { int r; struct sockaddr_in server_addr; pinger_t *pinger; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->state = 0; pinger->pongs = 0; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); pinger->stream.tcp.data = pinger; ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ r = uv_tcp_connect(&pinger->connect_req, &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); ASSERT(!r); /* Synchronous connect callbacks are not allowed. */ ASSERT(pinger_on_connect_count == 0); } static void pipe_pinger_new(void) { int r; pinger_t *pinger; pinger = (pinger_t*)malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->state = 0; pinger->pongs = 0; /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0); pinger->stream.pipe.data = pinger; ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ uv_pipe_connect(&pinger->connect_req, &pinger->stream.pipe, TEST_PIPENAME, pinger_on_connect); /* Synchronous connect callbacks are not allowed. */ ASSERT(pinger_on_connect_count == 0); } TEST_IMPL(tcp_ping_pong) { tcp_pinger_new(); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(completed_pingers == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_ping_pong_v6) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); tcp_pinger_v6_new(); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(completed_pingers == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_ping_pong) { pipe_pinger_new(); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(completed_pingers == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-bind-error.c000066400000000000000000000065471263361716600201540ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #ifdef _WIN32 # define BAD_PIPENAME "bad-pipe" #else # define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" #endif static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } TEST_IMPL(pipe_bind_error_addrinuse) { uv_pipe_t server1, server2; int r; r = uv_pipe_init(uv_default_loop(), &server1, 0); ASSERT(r == 0); r = uv_pipe_bind(&server1, TEST_PIPENAME); ASSERT(r == 0); r = uv_pipe_init(uv_default_loop(), &server2, 0); ASSERT(r == 0); r = uv_pipe_bind(&server2, TEST_PIPENAME); ASSERT(r == UV_EADDRINUSE); r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_bind_error_addrnotavail) { uv_pipe_t server; int r; r = uv_pipe_init(uv_default_loop(), &server, 0); ASSERT(r == 0); r = uv_pipe_bind(&server, BAD_PIPENAME); ASSERT(r == UV_EACCES); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_bind_error_inval) { uv_pipe_t server; int r; r = uv_pipe_init(uv_default_loop(), &server, 0); ASSERT(r == 0); r = uv_pipe_bind(&server, TEST_PIPENAME); ASSERT(r == 0); r = uv_pipe_bind(&server, TEST_PIPENAME_2); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_listen_without_bind) { uv_pipe_t server; int r; r = uv_pipe_init(uv_default_loop(), &server, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-close-stdout-read-stdin.c000066400000000000000000000056641263361716600225650ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef _WIN32 #include #include #include #include #include "uv.h" #include "task.h" void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t* buf) { static char buffer[1024]; buf->base = buffer; buf->len = sizeof(buffer); } void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t* buf) { if (nread < 0) { uv_close((uv_handle_t*)stream, NULL); return; } } /* * This test is a reproduction of joyent/libuv#1419 . */ TEST_IMPL(pipe_close_stdout_read_stdin) { int r = -1; int pid; int fd[2]; int status; uv_pipe_t stdin_pipe; r = pipe(fd); ASSERT(r == 0); if ((pid = fork()) == 0) { /* * Make the read side of the pipe our stdin. * The write side will be closed by the parent process. */ close(fd[1]); close(0); r = dup(fd[0]); ASSERT(r != -1); /* Create a stream that reads from the pipe. */ r = uv_pipe_init(uv_default_loop(), (uv_pipe_t *)&stdin_pipe, 0); ASSERT(r == 0); r = uv_pipe_open((uv_pipe_t *)&stdin_pipe, 0); ASSERT(r == 0); r = uv_read_start((uv_stream_t *)&stdin_pipe, alloc_buffer, read_stdin); ASSERT(r == 0); /* * Because the other end of the pipe was closed, there should * be no event left to process after one run of the event loop. * Otherwise, it means that events were not processed correctly. */ ASSERT(uv_run(uv_default_loop(), UV_RUN_NOWAIT) == 0); } else { /* * Close both ends of the pipe so that the child * get a POLLHUP event when it tries to read from * the other end. */ close(fd[1]); close(fd[0]); waitpid(pid, &status, 0); ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0); } MAKE_VALGRIND_HAPPY(); return 0; } #endif /* ifndef _WIN32 */ libuv-v1.8.0/test/test-pipe-connect-error.c000066400000000000000000000052241263361716600206600ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #ifdef _WIN32 # define BAD_PIPENAME "bad-pipe" #else # define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" #endif static int close_cb_called = 0; static int connect_cb_called = 0; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void connect_cb(uv_connect_t* connect_req, int status) { ASSERT(status == UV_ENOENT); uv_close((uv_handle_t*)connect_req->handle, close_cb); connect_cb_called++; } static void connect_cb_file(uv_connect_t* connect_req, int status) { ASSERT(status == UV_ENOTSOCK || status == UV_ECONNREFUSED); uv_close((uv_handle_t*)connect_req->handle, close_cb); connect_cb_called++; } TEST_IMPL(pipe_connect_bad_name) { uv_pipe_t client; uv_connect_t req; int r; r = uv_pipe_init(uv_default_loop(), &client, 0); ASSERT(r == 0); uv_pipe_connect(&req, &client, BAD_PIPENAME, connect_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); ASSERT(connect_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_connect_to_file) { const char* path = "test/fixtures/empty_file"; uv_pipe_t client; uv_connect_t req; int r; r = uv_pipe_init(uv_default_loop(), &client, 0); ASSERT(r == 0); uv_pipe_connect(&req, &client, path, connect_cb_file); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); ASSERT(connect_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-connect-multiple.c000066400000000000000000000055561263361716600213720ustar00rootroot00000000000000/* Copyright (c) 2015 Saúl Ibarra Corretgé . * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int connection_cb_called = 0; static int connect_cb_called = 0; #define NUM_CLIENTS 4 typedef struct { uv_pipe_t pipe_handle; uv_connect_t conn_req; } client_t; static uv_pipe_t server_handle; static client_t clients[NUM_CLIENTS]; static uv_pipe_t connections[NUM_CLIENTS]; static void connection_cb(uv_stream_t* server, int status) { int r; uv_pipe_t* conn; ASSERT(status == 0); conn = &connections[connection_cb_called]; r = uv_pipe_init(server->loop, conn, 0); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)conn); ASSERT(r == 0); if (++connection_cb_called == NUM_CLIENTS && connect_cb_called == NUM_CLIENTS) { uv_stop(server->loop); } } static void connect_cb(uv_connect_t* connect_req, int status) { ASSERT(status == 0); if (++connect_cb_called == NUM_CLIENTS && connection_cb_called == NUM_CLIENTS) { uv_stop(connect_req->handle->loop); } } TEST_IMPL(pipe_connect_multiple) { int i; int r; uv_loop_t* loop; loop = uv_default_loop(); r = uv_pipe_init(loop, &server_handle, 0); ASSERT(r == 0); r = uv_pipe_bind(&server_handle, TEST_PIPENAME); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server_handle, 128, connection_cb); ASSERT(r == 0); for (i = 0; i < NUM_CLIENTS; i++) { r = uv_pipe_init(loop, &clients[i].pipe_handle, 0); ASSERT(r == 0); uv_pipe_connect(&clients[i].conn_req, &clients[i].pipe_handle, TEST_PIPENAME, connect_cb); } uv_run(loop, UV_RUN_DEFAULT); ASSERT(connection_cb_called == NUM_CLIENTS); ASSERT(connect_cb_called == NUM_CLIENTS); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-connect-prepare.c000066400000000000000000000046471263361716600211750ustar00rootroot00000000000000/* Copyright (c) 2015 Saúl Ibarra Corretgé . * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #ifdef _WIN32 # define BAD_PIPENAME "bad-pipe" #else # define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" #endif static int close_cb_called = 0; static int connect_cb_called = 0; static uv_pipe_t pipe_handle; static uv_prepare_t prepare_handle; static uv_connect_t conn_req; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void connect_cb(uv_connect_t* connect_req, int status) { ASSERT(status == UV_ENOENT); connect_cb_called++; uv_close((uv_handle_t*)&prepare_handle, close_cb); uv_close((uv_handle_t*)&pipe_handle, close_cb); } static void prepare_cb(uv_prepare_t* handle) { ASSERT(handle == &prepare_handle); uv_pipe_connect(&conn_req, &pipe_handle, BAD_PIPENAME, connect_cb); } TEST_IMPL(pipe_connect_on_prepare) { int r; r = uv_pipe_init(uv_default_loop(), &pipe_handle, 0); ASSERT(r == 0); r = uv_prepare_init(uv_default_loop(), &prepare_handle); ASSERT(r == 0); r = uv_prepare_start(&prepare_handle, prepare_cb); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 2); ASSERT(connect_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-getsockname.c000066400000000000000000000146251263361716600204050ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #if defined(__linux__) #include #include #endif #ifndef _WIN32 # include /* close */ #else # include #endif static uv_pipe_t pipe_client; static uv_pipe_t pipe_server; static uv_connect_t connect_req; static int pipe_close_cb_called = 0; static int pipe_client_connect_cb_called = 0; static void pipe_close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*) &pipe_client || handle == (uv_handle_t*) &pipe_server); pipe_close_cb_called++; } static void pipe_client_connect_cb(uv_connect_t* req, int status) { char buf[1024]; size_t len; int r; ASSERT(req == &connect_req); ASSERT(status == 0); len = sizeof buf; r = uv_pipe_getpeername(&pipe_client, buf, &len); ASSERT(r == 0); ASSERT(buf[len - 1] != 0); ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); len = sizeof buf; r = uv_pipe_getsockname(&pipe_client, buf, &len); ASSERT(r == 0 && len == 0); pipe_client_connect_cb_called++; uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); } static void pipe_server_connection_cb(uv_stream_t* handle, int status) { /* This function *may* be called, depending on whether accept or the * connection callback is called first. */ ASSERT(status == 0); } TEST_IMPL(pipe_getsockname) { uv_loop_t* loop; char buf[1024]; size_t len; int r; loop = uv_default_loop(); ASSERT(loop != NULL); r = uv_pipe_init(loop, &pipe_server, 0); ASSERT(r == 0); len = sizeof buf; r = uv_pipe_getsockname(&pipe_server, buf, &len); ASSERT(r == UV_EBADF); len = sizeof buf; r = uv_pipe_getpeername(&pipe_server, buf, &len); ASSERT(r == UV_EBADF); r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); ASSERT(r == 0); len = sizeof buf; r = uv_pipe_getsockname(&pipe_server, buf, &len); ASSERT(r == 0); ASSERT(buf[len - 1] != 0); ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); len = sizeof buf; r = uv_pipe_getpeername(&pipe_server, buf, &len); ASSERT(r == UV_ENOTCONN); r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); ASSERT(r == 0); r = uv_pipe_init(loop, &pipe_client, 0); ASSERT(r == 0); len = sizeof buf; r = uv_pipe_getsockname(&pipe_client, buf, &len); ASSERT(r == UV_EBADF); len = sizeof buf; r = uv_pipe_getpeername(&pipe_client, buf, &len); ASSERT(r == UV_EBADF); uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); len = sizeof buf; r = uv_pipe_getsockname(&pipe_client, buf, &len); ASSERT(r == 0 && len == 0); len = sizeof buf; r = uv_pipe_getpeername(&pipe_client, buf, &len); ASSERT(r == 0); ASSERT(buf[len - 1] != 0); ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(pipe_client_connect_cb_called == 1); ASSERT(pipe_close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_getsockname_abstract) { #if defined(__linux__) char buf[1024]; size_t len; int r; int sock; struct sockaddr_un sun; socklen_t sun_len; char abstract_pipe[] = "\0test-pipe"; sock = socket(AF_LOCAL, SOCK_STREAM, 0); ASSERT(sock != -1); sun_len = sizeof sun; memset(&sun, 0, sun_len); sun.sun_family = AF_UNIX; memcpy(sun.sun_path, abstract_pipe, sizeof abstract_pipe); r = bind(sock, (struct sockaddr*)&sun, sun_len); ASSERT(r == 0); r = uv_pipe_init(uv_default_loop(), &pipe_server, 0); ASSERT(r == 0); r = uv_pipe_open(&pipe_server, sock); ASSERT(r == 0); len = sizeof buf; r = uv_pipe_getsockname(&pipe_server, buf, &len); ASSERT(r == 0); ASSERT(memcmp(buf, abstract_pipe, sizeof abstract_pipe) == 0); uv_close((uv_handle_t*)&pipe_server, pipe_close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); close(sock); ASSERT(pipe_close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; #else MAKE_VALGRIND_HAPPY(); return 0; #endif } TEST_IMPL(pipe_getsockname_blocking) { #ifdef _WIN32 HANDLE readh, writeh; int readfd; char buf1[1024], buf2[1024]; size_t len1, len2; int r; r = CreatePipe(&readh, &writeh, NULL, 65536); ASSERT(r != 0); r = uv_pipe_init(uv_default_loop(), &pipe_client, 0); ASSERT(r == 0); readfd = _open_osfhandle((intptr_t)readh, _O_RDONLY); ASSERT(r != -1); r = uv_pipe_open(&pipe_client, readfd); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); ASSERT(r == 0); Sleep(100); r = uv_read_stop((uv_stream_t*)&pipe_client); ASSERT(r == 0); len1 = sizeof buf1; r = uv_pipe_getsockname(&pipe_client, buf1, &len1); ASSERT(r == 0); ASSERT(buf1[len1 - 1] != 0); r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); ASSERT(r == 0); Sleep(100); len2 = sizeof buf2; r = uv_pipe_getsockname(&pipe_client, buf2, &len2); ASSERT(r == 0); ASSERT(buf2[len2 - 1] != 0); r = uv_read_stop((uv_stream_t*)&pipe_client); ASSERT(r == 0); ASSERT(len1 == len2); ASSERT(memcmp(buf1, buf2, len1) == 0); pipe_close_cb_called = 0; uv_close((uv_handle_t*)&pipe_client, pipe_close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(pipe_close_cb_called == 1); _close(readfd); CloseHandle(writeh); #endif MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-pending-instances.c000066400000000000000000000035331263361716600215120ustar00rootroot00000000000000/* Copyright (c) 2015 Saúl Ibarra Corretgé . * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static void connection_cb(uv_stream_t* server, int status) { ASSERT(0 && "this will never be called"); } TEST_IMPL(pipe_pending_instances) { int r; uv_pipe_t pipe_handle; uv_loop_t* loop; loop = uv_default_loop(); r = uv_pipe_init(loop, &pipe_handle, 0); ASSERT(r == 0); uv_pipe_pending_instances(&pipe_handle, 8); r = uv_pipe_bind(&pipe_handle, TEST_PIPENAME); ASSERT(r == 0); uv_pipe_pending_instances(&pipe_handle, 16); r = uv_listen((uv_stream_t*)&pipe_handle, 128, connection_cb); ASSERT(r == 0); uv_close((uv_handle_t*)&pipe_handle, NULL); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-sendmsg.c000066400000000000000000000105021263361716600175330ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #ifndef _WIN32 #include #include #include #include #include #include #include /* NOTE: size should be divisible by 2 */ static uv_pipe_t incoming[4]; static unsigned int incoming_count; static unsigned int close_called; static void set_nonblocking(uv_os_sock_t sock) { int r; #ifdef _WIN32 unsigned long on = 1; r = ioctlsocket(sock, FIONBIO, &on); ASSERT(r == 0); #else int flags = fcntl(sock, F_GETFL, 0); ASSERT(flags >= 0); r = fcntl(sock, F_SETFL, flags | O_NONBLOCK); ASSERT(r >= 0); #endif } static void close_cb(uv_handle_t* handle) { close_called++; } static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static char base[1]; buf->base = base; buf->len = sizeof(base); } static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { uv_pipe_t* p; uv_pipe_t* inc; uv_handle_type pending; unsigned int i; p = (uv_pipe_t*) handle; ASSERT(nread >= 0); while (uv_pipe_pending_count(p) != 0) { pending = uv_pipe_pending_type(p); ASSERT(pending == UV_NAMED_PIPE); ASSERT(incoming_count < ARRAY_SIZE(incoming)); inc = &incoming[incoming_count++]; ASSERT(0 == uv_pipe_init(p->loop, inc, 0)); ASSERT(0 == uv_accept(handle, (uv_stream_t*) inc)); } if (incoming_count != ARRAY_SIZE(incoming)) return; ASSERT(0 == uv_read_stop((uv_stream_t*) p)); uv_close((uv_handle_t*) p, close_cb); for (i = 0; i < ARRAY_SIZE(incoming); i++) uv_close((uv_handle_t*) &incoming[i], close_cb); } TEST_IMPL(pipe_sendmsg) { uv_pipe_t p; int r; int fds[2]; int send_fds[ARRAY_SIZE(incoming)]; struct msghdr msg; char scratch[64]; struct cmsghdr *cmsg; unsigned int i; uv_buf_t buf; ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fds)); for (i = 0; i < ARRAY_SIZE(send_fds); i += 2) ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, send_fds + i)); ASSERT(i == ARRAY_SIZE(send_fds)); ASSERT(0 == uv_pipe_init(uv_default_loop(), &p, 1)); ASSERT(0 == uv_pipe_open(&p, fds[1])); buf = uv_buf_init("X", 1); memset(&msg, 0, sizeof(msg)); msg.msg_iov = (struct iovec*) &buf; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = (void*) scratch; msg.msg_controllen = CMSG_LEN(sizeof(send_fds)); ASSERT(sizeof(scratch) >= msg.msg_controllen); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = msg.msg_controllen; /* silence aliasing warning */ { void* pv = CMSG_DATA(cmsg); int* pi = pv; for (i = 0; i < ARRAY_SIZE(send_fds); i++) pi[i] = send_fds[i]; } set_nonblocking(fds[1]); ASSERT(0 == uv_read_start((uv_stream_t*) &p, alloc_cb, read_cb)); do r = sendmsg(fds[0], &msg, 0); while (r == -1 && errno == EINTR); ASSERT(r == 1); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(ARRAY_SIZE(incoming) == incoming_count); ASSERT(ARRAY_SIZE(incoming) + 1 == close_called); close(fds[0]); MAKE_VALGRIND_HAPPY(); return 0; } #else /* !_WIN32 */ TEST_IMPL(pipe_sendmsg) { MAKE_VALGRIND_HAPPY(); return 0; } #endif /* _WIN32 */ libuv-v1.8.0/test/test-pipe-server-close.c000066400000000000000000000052731263361716600205150ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_pipe_t pipe_client; static uv_pipe_t pipe_server; static uv_connect_t connect_req; static int pipe_close_cb_called = 0; static int pipe_client_connect_cb_called = 0; static void pipe_close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*) &pipe_client || handle == (uv_handle_t*) &pipe_server); pipe_close_cb_called++; } static void pipe_client_connect_cb(uv_connect_t* req, int status) { ASSERT(req == &connect_req); ASSERT(status == 0); pipe_client_connect_cb_called++; uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); } static void pipe_server_connection_cb(uv_stream_t* handle, int status) { /* This function *may* be called, depending on whether accept or the * connection callback is called first. */ ASSERT(status == 0); } TEST_IMPL(pipe_server_close) { uv_loop_t* loop; int r; loop = uv_default_loop(); ASSERT(loop != NULL); r = uv_pipe_init(loop, &pipe_server, 0); ASSERT(r == 0); r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); ASSERT(r == 0); r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); ASSERT(r == 0); r = uv_pipe_init(loop, &pipe_client, 0); ASSERT(r == 0); uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(pipe_client_connect_cb_called == 1); ASSERT(pipe_close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-pipe-set-non-blocking.c000066400000000000000000000052051263361716600212500ustar00rootroot00000000000000/* Copyright (c) 2015, Ben Noordhuis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "uv.h" #include "task.h" #ifdef _WIN32 TEST_IMPL(pipe_set_non_blocking) { RETURN_SKIP("Test not implemented on Windows."); } #else /* !_WIN32 */ #include #include #include #include #include #include struct thread_ctx { uv_barrier_t barrier; int fd; }; static void thread_main(void* arg) { struct thread_ctx* ctx; char buf[4096]; ssize_t n; ctx = arg; uv_barrier_wait(&ctx->barrier); do n = read(ctx->fd, buf, sizeof(buf)); while (n > 0 || (n == -1 && errno == EINTR)); ASSERT(n == 0); } TEST_IMPL(pipe_set_non_blocking) { struct thread_ctx ctx; uv_pipe_t pipe_handle; uv_thread_t thread; size_t nwritten; char data[4096]; uv_buf_t buf; int fd[2]; int n; ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1)); ctx.fd = fd[1]; ASSERT(0 == uv_barrier_init(&ctx.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); uv_barrier_wait(&ctx.barrier); buf.len = sizeof(data); buf.base = data; memset(data, '.', sizeof(data)); nwritten = 0; while (nwritten < 10 << 20) { /* The stream is in blocking mode so uv_try_write() should always succeed * with the exact number of bytes that we wanted written. */ n = uv_try_write((uv_stream_t*) &pipe_handle, &buf, 1); ASSERT(n == sizeof(data)); nwritten += n; } uv_close((uv_handle_t*) &pipe_handle, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_thread_join(&thread)); ASSERT(0 == close(fd[1])); /* fd[0] is closed by uv_close(). */ uv_barrier_destroy(&ctx.barrier); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !_WIN32 */ libuv-v1.8.0/test/test-platform-output.c000066400000000000000000000110041263361716600203200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include TEST_IMPL(platform_output) { char buffer[512]; size_t rss; size_t size; double uptime; uv_rusage_t rusage; uv_cpu_info_t* cpus; uv_interface_address_t* interfaces; int count; int i; int err; err = uv_get_process_title(buffer, sizeof(buffer)); ASSERT(err == 0); printf("uv_get_process_title: %s\n", buffer); size = sizeof(buffer); err = uv_cwd(buffer, &size); ASSERT(err == 0); printf("uv_cwd: %s\n", buffer); err = uv_resident_set_memory(&rss); ASSERT(err == 0); printf("uv_resident_set_memory: %llu\n", (unsigned long long) rss); err = uv_uptime(&uptime); ASSERT(err == 0); ASSERT(uptime > 0); printf("uv_uptime: %f\n", uptime); err = uv_getrusage(&rusage); ASSERT(err == 0); ASSERT(rusage.ru_utime.tv_sec >= 0); ASSERT(rusage.ru_utime.tv_usec >= 0); ASSERT(rusage.ru_stime.tv_sec >= 0); ASSERT(rusage.ru_stime.tv_usec >= 0); printf("uv_getrusage:\n"); printf(" user: %llu sec %llu microsec\n", (unsigned long long) rusage.ru_utime.tv_sec, (unsigned long long) rusage.ru_utime.tv_usec); printf(" system: %llu sec %llu microsec\n", (unsigned long long) rusage.ru_stime.tv_sec, (unsigned long long) rusage.ru_stime.tv_usec); err = uv_cpu_info(&cpus, &count); ASSERT(err == 0); printf("uv_cpu_info:\n"); for (i = 0; i < count; i++) { printf(" model: %s\n", cpus[i].model); printf(" speed: %d\n", cpus[i].speed); printf(" times.sys: %llu\n", (unsigned long long) cpus[i].cpu_times.sys); printf(" times.user: %llu\n", (unsigned long long) cpus[i].cpu_times.user); printf(" times.idle: %llu\n", (unsigned long long) cpus[i].cpu_times.idle); printf(" times.irq: %llu\n", (unsigned long long) cpus[i].cpu_times.irq); printf(" times.nice: %llu\n", (unsigned long long) cpus[i].cpu_times.nice); } uv_free_cpu_info(cpus, count); err = uv_interface_addresses(&interfaces, &count); ASSERT(err == 0); printf("uv_interface_addresses:\n"); for (i = 0; i < count; i++) { printf(" name: %s\n", interfaces[i].name); printf(" internal: %d\n", interfaces[i].is_internal); printf(" physical address: "); printf("%02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned char)interfaces[i].phys_addr[0], (unsigned char)interfaces[i].phys_addr[1], (unsigned char)interfaces[i].phys_addr[2], (unsigned char)interfaces[i].phys_addr[3], (unsigned char)interfaces[i].phys_addr[4], (unsigned char)interfaces[i].phys_addr[5]); if (interfaces[i].address.address4.sin_family == AF_INET) { uv_ip4_name(&interfaces[i].address.address4, buffer, sizeof(buffer)); } else if (interfaces[i].address.address4.sin_family == AF_INET6) { uv_ip6_name(&interfaces[i].address.address6, buffer, sizeof(buffer)); } printf(" address: %s\n", buffer); if (interfaces[i].netmask.netmask4.sin_family == AF_INET) { uv_ip4_name(&interfaces[i].netmask.netmask4, buffer, sizeof(buffer)); printf(" netmask: %s\n", buffer); } else if (interfaces[i].netmask.netmask4.sin_family == AF_INET6) { uv_ip6_name(&interfaces[i].netmask.netmask6, buffer, sizeof(buffer)); printf(" netmask: %s\n", buffer); } else { printf(" netmask: none\n"); } } uv_free_interface_addresses(interfaces, count); return 0; } libuv-v1.8.0/test/test-poll-close-doesnt-corrupt-stack.c000066400000000000000000000056361263361716600233160ustar00rootroot00000000000000/* Copyright Bert Belder, and other libuv contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifdef _WIN32 #include #include #include "uv.h" #include "task.h" #ifdef _MSC_VER /* msvc */ # define NO_INLINE __declspec(noinline) #else /* gcc */ # define NO_INLINE __attribute__ ((noinline)) #endif uv_os_sock_t sock; uv_poll_t handle; static int close_cb_called = 0; static void close_cb(uv_handle_t* h) { close_cb_called++; } static void poll_cb(uv_poll_t* h, int status, int events) { ASSERT(0 && "should never get here"); } static void NO_INLINE close_socket_and_verify_stack() { const uint32_t MARKER = 0xDEADBEEF; const int VERIFY_AFTER = 10; /* ms */ int r; volatile uint32_t data[65536]; size_t i; for (i = 0; i < ARRAY_SIZE(data); i++) data[i] = MARKER; r = closesocket(sock); ASSERT(r == 0); uv_sleep(VERIFY_AFTER); for (i = 0; i < ARRAY_SIZE(data); i++) ASSERT(data[i] == MARKER); } TEST_IMPL(poll_close_doesnt_corrupt_stack) { struct WSAData wsa_data; int r; unsigned long on; struct sockaddr_in addr; r = WSAStartup(MAKEWORD(2, 2), &wsa_data); ASSERT(r == 0); sock = socket(AF_INET, SOCK_STREAM, 0); ASSERT(sock != INVALID_SOCKET); on = 1; r = ioctlsocket(sock, FIONBIO, &on); ASSERT(r == 0); r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); ASSERT(r == 0); r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); ASSERT(r != 0); ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); r = uv_poll_init_socket(uv_default_loop(), &handle, sock); ASSERT(r == 0); r = uv_poll_start(&handle, UV_READABLE | UV_WRITABLE, poll_cb); ASSERT(r == 0); uv_close((uv_handle_t*) &handle, close_cb); close_socket_and_verify_stack(); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* _WIN32 */ libuv-v1.8.0/test/test-poll-close.c000066400000000000000000000041251263361716600172150ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #ifndef _WIN32 # include # include # include #endif #include "uv.h" #include "task.h" #define NUM_SOCKETS 64 static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { close_cb_called++; } TEST_IMPL(poll_close) { uv_os_sock_t sockets[NUM_SOCKETS]; uv_poll_t poll_handles[NUM_SOCKETS]; int i; #ifdef _WIN32 { struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); ASSERT(r == 0); } #endif for (i = 0; i < NUM_SOCKETS; i++) { sockets[i] = socket(AF_INET, SOCK_STREAM, 0); uv_poll_init_socket(uv_default_loop(), &poll_handles[i], sockets[i]); uv_poll_start(&poll_handles[i], UV_READABLE | UV_WRITABLE, NULL); } for (i = 0; i < NUM_SOCKETS; i++) { uv_close((uv_handle_t*) &poll_handles[i], close_cb); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == NUM_SOCKETS); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-poll-closesocket.c000066400000000000000000000045651263361716600204360ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifdef _WIN32 #include #include "uv.h" #include "task.h" uv_os_sock_t sock; uv_poll_t handle; static int close_cb_called = 0; static void close_cb(uv_handle_t* h) { close_cb_called++; } static void poll_cb(uv_poll_t* h, int status, int events) { int r; ASSERT(status == 0); ASSERT(h == &handle); r = uv_poll_start(&handle, UV_READABLE, poll_cb); ASSERT(r == 0); closesocket(sock); uv_close((uv_handle_t*) &handle, close_cb); } TEST_IMPL(poll_closesocket) { struct WSAData wsa_data; int r; unsigned long on; struct sockaddr_in addr; r = WSAStartup(MAKEWORD(2, 2), &wsa_data); ASSERT(r == 0); sock = socket(AF_INET, SOCK_STREAM, 0); ASSERT(sock != INVALID_SOCKET); on = 1; r = ioctlsocket(sock, FIONBIO, &on); ASSERT(r == 0); r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); ASSERT(r == 0); r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); ASSERT(r != 0); ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); r = uv_poll_init_socket(uv_default_loop(), &handle, sock); ASSERT(r == 0); r = uv_poll_start(&handle, UV_WRITABLE, poll_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } #endif libuv-v1.8.0/test/test-poll.c000066400000000000000000000333451263361716600161200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #ifndef _WIN32 # include # include #endif #include "uv.h" #include "task.h" #define NUM_CLIENTS 5 #define TRANSFER_BYTES (1 << 16) #undef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)); typedef enum { UNIDIRECTIONAL, DUPLEX } test_mode_t; typedef struct connection_context_s { uv_poll_t poll_handle; uv_timer_t timer_handle; uv_os_sock_t sock; size_t read, sent; int is_server_connection; int open_handles; int got_fin, sent_fin; unsigned int events, delayed_events; } connection_context_t; typedef struct server_context_s { uv_poll_t poll_handle; uv_os_sock_t sock; int connections; } server_context_t; static void delay_timer_cb(uv_timer_t* timer); static test_mode_t test_mode = DUPLEX; static int closed_connections = 0; static int valid_writable_wakeups = 0; static int spurious_writable_wakeups = 0; static int got_eagain(void) { #ifdef _WIN32 return WSAGetLastError() == WSAEWOULDBLOCK; #else return errno == EAGAIN || errno == EINPROGRESS #ifdef EWOULDBLOCK || errno == EWOULDBLOCK; #endif ; #endif } static uv_os_sock_t create_bound_socket (struct sockaddr_in bind_addr) { uv_os_sock_t sock; int r; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); #ifdef _WIN32 ASSERT(sock != INVALID_SOCKET); #else ASSERT(sock >= 0); #endif #ifndef _WIN32 { /* Allow reuse of the port. */ int yes = 1; r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); ASSERT(r == 0); } #endif r = bind(sock, (const struct sockaddr*) &bind_addr, sizeof bind_addr); ASSERT(r == 0); return sock; } static void close_socket(uv_os_sock_t sock) { int r; #ifdef _WIN32 r = closesocket(sock); #else r = close(sock); #endif ASSERT(r == 0); } static connection_context_t* create_connection_context( uv_os_sock_t sock, int is_server_connection) { int r; connection_context_t* context; context = (connection_context_t*) malloc(sizeof *context); ASSERT(context != NULL); context->sock = sock; context->is_server_connection = is_server_connection; context->read = 0; context->sent = 0; context->open_handles = 0; context->events = 0; context->delayed_events = 0; context->got_fin = 0; context->sent_fin = 0; r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock); context->open_handles++; context->poll_handle.data = context; ASSERT(r == 0); r = uv_timer_init(uv_default_loop(), &context->timer_handle); context->open_handles++; context->timer_handle.data = context; ASSERT(r == 0); return context; } static void connection_close_cb(uv_handle_t* handle) { connection_context_t* context = (connection_context_t*) handle->data; if (--context->open_handles == 0) { if (test_mode == DUPLEX || context->is_server_connection) { ASSERT(context->read == TRANSFER_BYTES); } else { ASSERT(context->read == 0); } if (test_mode == DUPLEX || !context->is_server_connection) { ASSERT(context->sent == TRANSFER_BYTES); } else { ASSERT(context->sent == 0); } closed_connections++; free(context); } } static void destroy_connection_context(connection_context_t* context) { uv_close((uv_handle_t*) &context->poll_handle, connection_close_cb); uv_close((uv_handle_t*) &context->timer_handle, connection_close_cb); } static void connection_poll_cb(uv_poll_t* handle, int status, int events) { connection_context_t* context = (connection_context_t*) handle->data; unsigned int new_events; int r; ASSERT(status == 0); ASSERT(events & context->events); ASSERT(!(events & ~context->events)); new_events = context->events; if (events & UV_READABLE) { int action = rand() % 7; switch (action) { case 0: case 1: { /* Read a couple of bytes. */ static char buffer[74]; r = recv(context->sock, buffer, sizeof buffer, 0); ASSERT(r >= 0); if (r > 0) { context->read += r; } else { /* Got FIN. */ context->got_fin = 1; new_events &= ~UV_READABLE; } break; } case 2: case 3: { /* Read until EAGAIN. */ static char buffer[931]; r = recv(context->sock, buffer, sizeof buffer, 0); ASSERT(r >= 0); while (r > 0) { context->read += r; r = recv(context->sock, buffer, sizeof buffer, 0); } if (r == 0) { /* Got FIN. */ context->got_fin = 1; new_events &= ~UV_READABLE; } else { ASSERT(got_eagain()); } break; } case 4: /* Ignore. */ break; case 5: /* Stop reading for a while. Restart in timer callback. */ new_events &= ~UV_READABLE; if (!uv_is_active((uv_handle_t*) &context->timer_handle)) { context->delayed_events = UV_READABLE; uv_timer_start(&context->timer_handle, delay_timer_cb, 10, 0); } else { context->delayed_events |= UV_READABLE; } break; case 6: /* Fudge with the event mask. */ uv_poll_start(&context->poll_handle, UV_WRITABLE, connection_poll_cb); uv_poll_start(&context->poll_handle, UV_READABLE, connection_poll_cb); context->events = UV_READABLE; break; default: ASSERT(0); } } if (events & UV_WRITABLE) { if (context->sent < TRANSFER_BYTES && !(test_mode == UNIDIRECTIONAL && context->is_server_connection)) { /* We have to send more bytes. */ int action = rand() % 7; switch (action) { case 0: case 1: { /* Send a couple of bytes. */ static char buffer[103]; int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); ASSERT(send_bytes > 0); r = send(context->sock, buffer, send_bytes, 0); if (r < 0) { ASSERT(got_eagain()); spurious_writable_wakeups++; break; } ASSERT(r > 0); context->sent += r; valid_writable_wakeups++; break; } case 2: case 3: { /* Send until EAGAIN. */ static char buffer[1234]; int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); ASSERT(send_bytes > 0); r = send(context->sock, buffer, send_bytes, 0); if (r < 0) { ASSERT(got_eagain()); spurious_writable_wakeups++; break; } ASSERT(r > 0); valid_writable_wakeups++; context->sent += r; while (context->sent < TRANSFER_BYTES) { send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); ASSERT(send_bytes > 0); r = send(context->sock, buffer, send_bytes, 0); if (r <= 0) break; context->sent += r; } ASSERT(r > 0 || got_eagain()); break; } case 4: /* Ignore. */ break; case 5: /* Stop sending for a while. Restart in timer callback. */ new_events &= ~UV_WRITABLE; if (!uv_is_active((uv_handle_t*) &context->timer_handle)) { context->delayed_events = UV_WRITABLE; uv_timer_start(&context->timer_handle, delay_timer_cb, 100, 0); } else { context->delayed_events |= UV_WRITABLE; } break; case 6: /* Fudge with the event mask. */ uv_poll_start(&context->poll_handle, UV_READABLE, connection_poll_cb); uv_poll_start(&context->poll_handle, UV_WRITABLE, connection_poll_cb); context->events = UV_WRITABLE; break; default: ASSERT(0); } } else { /* Nothing more to write. Send FIN. */ int r; #ifdef _WIN32 r = shutdown(context->sock, SD_SEND); #else r = shutdown(context->sock, SHUT_WR); #endif ASSERT(r == 0); context->sent_fin = 1; new_events &= ~UV_WRITABLE; } } if (context->got_fin && context->sent_fin) { /* Sent and received FIN. Close and destroy context. */ close_socket(context->sock); destroy_connection_context(context); context->events = 0; } else if (new_events != context->events) { /* Poll mask changed. Call uv_poll_start again. */ context->events = new_events; uv_poll_start(handle, new_events, connection_poll_cb); } /* Assert that uv_is_active works correctly for poll handles. */ if (context->events != 0) { ASSERT(1 == uv_is_active((uv_handle_t*) handle)); } else { ASSERT(0 == uv_is_active((uv_handle_t*) handle)); } } static void delay_timer_cb(uv_timer_t* timer) { connection_context_t* context = (connection_context_t*) timer->data; int r; /* Timer should auto stop. */ ASSERT(0 == uv_is_active((uv_handle_t*) timer)); /* Add the requested events to the poll mask. */ ASSERT(context->delayed_events != 0); context->events |= context->delayed_events; context->delayed_events = 0; r = uv_poll_start(&context->poll_handle, context->events, connection_poll_cb); ASSERT(r == 0); } static server_context_t* create_server_context( uv_os_sock_t sock) { int r; server_context_t* context; context = (server_context_t*) malloc(sizeof *context); ASSERT(context != NULL); context->sock = sock; context->connections = 0; r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock); context->poll_handle.data = context; ASSERT(r == 0); return context; } static void server_close_cb(uv_handle_t* handle) { server_context_t* context = (server_context_t*) handle->data; free(context); } static void destroy_server_context(server_context_t* context) { uv_close((uv_handle_t*) &context->poll_handle, server_close_cb); } static void server_poll_cb(uv_poll_t* handle, int status, int events) { server_context_t* server_context = (server_context_t*) handle->data; connection_context_t* connection_context; struct sockaddr_in addr; socklen_t addr_len; uv_os_sock_t sock; int r; addr_len = sizeof addr; sock = accept(server_context->sock, (struct sockaddr*) &addr, &addr_len); #ifdef _WIN32 ASSERT(sock != INVALID_SOCKET); #else ASSERT(sock >= 0); #endif connection_context = create_connection_context(sock, 1); connection_context->events = UV_READABLE | UV_WRITABLE; r = uv_poll_start(&connection_context->poll_handle, UV_READABLE | UV_WRITABLE, connection_poll_cb); ASSERT(r == 0); if (++server_context->connections == NUM_CLIENTS) { close_socket(server_context->sock); destroy_server_context(server_context); } } static void start_server(void) { server_context_t* context; struct sockaddr_in addr; uv_os_sock_t sock; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); sock = create_bound_socket(addr); context = create_server_context(sock); r = listen(sock, 100); ASSERT(r == 0); r = uv_poll_start(&context->poll_handle, UV_READABLE, server_poll_cb); ASSERT(r == 0); } static void start_client(void) { uv_os_sock_t sock; connection_context_t* context; struct sockaddr_in server_addr; struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &addr)); sock = create_bound_socket(addr); context = create_connection_context(sock, 0); context->events = UV_READABLE | UV_WRITABLE; r = uv_poll_start(&context->poll_handle, UV_READABLE | UV_WRITABLE, connection_poll_cb); ASSERT(r == 0); r = connect(sock, (struct sockaddr*) &server_addr, sizeof server_addr); ASSERT(r == 0 || got_eagain()); } static void start_poll_test(void) { int i, r; #ifdef _WIN32 { struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); ASSERT(r == 0); } #endif start_server(); for (i = 0; i < NUM_CLIENTS; i++) start_client(); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); /* Assert that at most five percent of the writable wakeups was spurious. */ ASSERT(spurious_writable_wakeups == 0 || (valid_writable_wakeups + spurious_writable_wakeups) / spurious_writable_wakeups > 20); ASSERT(closed_connections == NUM_CLIENTS * 2); MAKE_VALGRIND_HAPPY(); } TEST_IMPL(poll_duplex) { test_mode = DUPLEX; start_poll_test(); return 0; } TEST_IMPL(poll_unidirectional) { test_mode = UNIDIRECTIONAL; start_poll_test(); return 0; } libuv-v1.8.0/test/test-process-title.c000066400000000000000000000034361263361716600177450ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include static void set_title(const char* title) { char buffer[512]; int err; err = uv_get_process_title(buffer, sizeof(buffer)); ASSERT(err == 0); err = uv_set_process_title(title); ASSERT(err == 0); err = uv_get_process_title(buffer, sizeof(buffer)); ASSERT(err == 0); ASSERT(strcmp(buffer, title) == 0); } TEST_IMPL(process_title) { #if defined(__sun) || defined(_AIX) RETURN_SKIP("uv_(get|set)_process_title is not implemented."); #else /* Check for format string vulnerabilities. */ set_title("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"); set_title("new title"); return 0; #endif } libuv-v1.8.0/test/test-queue-foreach-delete.c000066400000000000000000000206511263361716600211370ustar00rootroot00000000000000/* Copyright The libuv project and contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include /* * The idea behind the test is as follows. * Certain handle types are stored in a queue internally. * Extra care should be taken for removal of a handle from the queue while iterating over the queue. * (i.e., QUEUE_REMOVE() called within QUEUE_FOREACH()) * This usually happens when someone closes or stops a handle from within its callback. * So we need to check that we haven't screwed the queue on close/stop. * To do so we do the following (for each handle type): * 1. Create and start 3 handles (#0, #1, and #2). * * The queue after the start() calls: * ..=> [queue head] <=> [handle] <=> [handle #1] <=> [handle] <=.. * * 2. Trigger handles to fire (for uv_idle_t, uv_prepare_t, and uv_check_t there is nothing to do). * * 3. In the callback for the first-executed handle (#0 or #2 depending on handle type) * stop the handle and the next one (#1). * (for uv_idle_t, uv_prepare_t, and uv_check_t callbacks are executed in the reverse order as they are start()'ed, * so callback for handle #2 will be called first) * * The queue after the stop() calls: * correct foreach "next" | * \/ * ..=> [queue head] <==============================> [handle] <=.. * [ ] <- [handle] <=> [handle #1] -> [ ] * /\ * wrong foreach "next" | * * 4. The callback for handle #1 shouldn't be called because the handle #1 is stopped in the previous step. * However, if QUEUE_REMOVE() is not handled properly within QUEUE_FOREACH(), the callback _will_ be called. */ static const unsigned first_handle_number_idle = 2; static const unsigned first_handle_number_prepare = 2; static const unsigned first_handle_number_check = 2; #ifdef __linux__ static const unsigned first_handle_number_fs_event = 0; #endif #define DEFINE_GLOBALS_AND_CBS(name) \ static uv_##name##_t (name)[3]; \ static unsigned name##_cb_calls[3]; \ \ static void name##2_cb(uv_##name##_t* handle) { \ ASSERT(handle == &(name)[2]); \ if (first_handle_number_##name == 2) { \ uv_close((uv_handle_t*)&(name)[2], NULL); \ uv_close((uv_handle_t*)&(name)[1], NULL); \ } \ name##_cb_calls[2]++; \ } \ \ static void name##1_cb(uv_##name##_t* handle) { \ ASSERT(handle == &(name)[1]); \ ASSERT(0 && "Shouldn't be called" && (&name[0])); \ } \ \ static void name##0_cb(uv_##name##_t* handle) { \ ASSERT(handle == &(name)[0]); \ if (first_handle_number_##name == 0) { \ uv_close((uv_handle_t*)&(name)[0], NULL); \ uv_close((uv_handle_t*)&(name)[1], NULL); \ } \ name##_cb_calls[0]++; \ } \ \ static const uv_##name##_cb name##_cbs[] = { \ (uv_##name##_cb)name##0_cb, \ (uv_##name##_cb)name##1_cb, \ (uv_##name##_cb)name##2_cb, \ }; #define INIT_AND_START(name, loop) \ do { \ size_t i; \ for (i = 0; i < ARRAY_SIZE(name); i++) { \ int r; \ r = uv_##name##_init((loop), &(name)[i]); \ ASSERT(r == 0); \ \ r = uv_##name##_start(&(name)[i], name##_cbs[i]); \ ASSERT(r == 0); \ } \ } while (0) #define END_ASSERTS(name) \ do { \ ASSERT(name##_cb_calls[0] == 1); \ ASSERT(name##_cb_calls[1] == 0); \ ASSERT(name##_cb_calls[2] == 1); \ } while (0) DEFINE_GLOBALS_AND_CBS(idle) DEFINE_GLOBALS_AND_CBS(prepare) DEFINE_GLOBALS_AND_CBS(check) #ifdef __linux__ DEFINE_GLOBALS_AND_CBS(fs_event) static const char watched_dir[] = "."; static uv_timer_t timer; static unsigned helper_timer_cb_calls; static void init_and_start_fs_events(uv_loop_t* loop) { size_t i; for (i = 0; i < ARRAY_SIZE(fs_event); i++) { int r; r = uv_fs_event_init(loop, &fs_event[i]); ASSERT(r == 0); r = uv_fs_event_start(&fs_event[i], (uv_fs_event_cb)fs_event_cbs[i], watched_dir, 0); ASSERT(r == 0); } } static void helper_timer_cb(uv_timer_t* thandle) { int r; uv_fs_t fs_req; /* fire all fs_events */ r = uv_fs_utime(thandle->loop, &fs_req, watched_dir, 0, 0, NULL); ASSERT(r == 0); ASSERT(fs_req.result == 0); ASSERT(fs_req.fs_type == UV_FS_UTIME); ASSERT(strcmp(fs_req.path, watched_dir) == 0); uv_fs_req_cleanup(&fs_req); helper_timer_cb_calls++; } #endif TEST_IMPL(queue_foreach_delete) { uv_loop_t* loop; int r; loop = uv_default_loop(); INIT_AND_START(idle, loop); INIT_AND_START(prepare, loop); INIT_AND_START(check, loop); #ifdef __linux__ init_and_start_fs_events(loop); /* helper timer to trigger async and fs_event callbacks */ r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, helper_timer_cb, 0, 0); ASSERT(r == 0); #endif r = uv_run(loop, UV_RUN_NOWAIT); ASSERT(r == 1); END_ASSERTS(idle); END_ASSERTS(prepare); END_ASSERTS(check); #ifdef __linux__ ASSERT(helper_timer_cb_calls == 1); #endif MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-ref.c000066400000000000000000000242551263361716600157260ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_write_t write_req; static uv_shutdown_t shutdown_req; static uv_connect_t connect_req; static char buffer[32767]; static int req_cb_called; static int connect_cb_called; static int write_cb_called; static int shutdown_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void do_close(void* handle) { close_cb_called = 0; uv_close((uv_handle_t*)handle, close_cb); ASSERT(close_cb_called == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); } static void fail_cb(void) { FATAL("fail_cb should not have been called"); } static void fail_cb2(void) { ASSERT(0 && "fail_cb2 should not have been called"); } static void req_cb(uv_handle_t* req, int status) { req_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(req == &shutdown_req); shutdown_cb_called++; } static void write_cb(uv_write_t* req, int status) { ASSERT(req == &write_req); uv_shutdown(&shutdown_req, req->handle, shutdown_cb); write_cb_called++; } static void connect_and_write(uv_connect_t* req, int status) { uv_buf_t buf = uv_buf_init(buffer, sizeof buffer); ASSERT(req == &connect_req); ASSERT(status == 0); uv_write(&write_req, req->handle, &buf, 1, write_cb); connect_cb_called++; } static void connect_and_shutdown(uv_connect_t* req, int status) { ASSERT(req == &connect_req); ASSERT(status == 0); uv_shutdown(&shutdown_req, req->handle, shutdown_cb); connect_cb_called++; } TEST_IMPL(ref) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(idle_ref) { uv_idle_t h; uv_idle_init(uv_default_loop(), &h); uv_idle_start(&h, (uv_idle_cb) fail_cb2); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(async_ref) { uv_async_t h; uv_async_init(uv_default_loop(), &h, NULL); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(prepare_ref) { uv_prepare_t h; uv_prepare_init(uv_default_loop(), &h); uv_prepare_start(&h, (uv_prepare_cb) fail_cb2); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(check_ref) { uv_check_t h; uv_check_init(uv_default_loop(), &h); uv_check_start(&h, (uv_check_cb) fail_cb2); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } static void prepare_cb(uv_prepare_t* h) { ASSERT(h != NULL); uv_unref((uv_handle_t*)h); } TEST_IMPL(unref_in_prepare_cb) { uv_prepare_t h; uv_prepare_init(uv_default_loop(), &h); uv_prepare_start(&h, prepare_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(timer_ref) { uv_timer_t h; uv_timer_init(uv_default_loop(), &h); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(timer_ref2) { uv_timer_t h; uv_timer_init(uv_default_loop(), &h); uv_timer_start(&h, (uv_timer_cb)fail_cb, 42, 42); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_event_ref) { uv_fs_event_t h; uv_fs_event_init(uv_default_loop(), &h); uv_fs_event_start(&h, (uv_fs_event_cb)fail_cb, ".", 0); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(fs_poll_ref) { uv_fs_poll_t h; uv_fs_poll_init(uv_default_loop(), &h); uv_fs_poll_start(&h, NULL, ".", 999); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_ref) { uv_tcp_t h; uv_tcp_init(uv_default_loop(), &h); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_ref2) { uv_tcp_t h; uv_tcp_init(uv_default_loop(), &h); uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_ref2b) { uv_tcp_t h; uv_tcp_init(uv_default_loop(), &h); uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); uv_unref((uv_handle_t*)&h); uv_close((uv_handle_t*)&h, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_ref3) { struct sockaddr_in addr; uv_tcp_t h; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_tcp_init(uv_default_loop(), &h); uv_tcp_connect(&connect_req, &h, (const struct sockaddr*) &addr, connect_and_shutdown); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 1); ASSERT(shutdown_cb_called == 1); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_ref4) { struct sockaddr_in addr; uv_tcp_t h; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_tcp_init(uv_default_loop(), &h); uv_tcp_connect(&connect_req, &h, (const struct sockaddr*) &addr, connect_and_write); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == 1); ASSERT(shutdown_cb_called == 1); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_ref) { uv_udp_t h; uv_udp_init(uv_default_loop(), &h); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_ref2) { struct sockaddr_in addr; uv_udp_t h; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_udp_init(uv_default_loop(), &h); uv_udp_bind(&h, (const struct sockaddr*) &addr, 0); uv_udp_recv_start(&h, (uv_alloc_cb)fail_cb, (uv_udp_recv_cb)fail_cb); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_ref3) { struct sockaddr_in addr; uv_buf_t buf = uv_buf_init("PING", 4); uv_udp_send_t req; uv_udp_t h; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_udp_init(uv_default_loop(), &h); uv_udp_send(&req, &h, &buf, 1, (const struct sockaddr*) &addr, (uv_udp_send_cb) req_cb); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(req_cb_called == 1); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_ref) { uv_pipe_t h; uv_pipe_init(uv_default_loop(), &h, 0); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_ref2) { uv_pipe_t h; uv_pipe_init(uv_default_loop(), &h, 0); uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_ref3) { uv_pipe_t h; uv_pipe_init(uv_default_loop(), &h, 0); uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_and_shutdown); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 1); ASSERT(shutdown_cb_called == 1); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(pipe_ref4) { uv_pipe_t h; uv_pipe_init(uv_default_loop(), &h, 0); uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_and_write); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == 1); ASSERT(shutdown_cb_called == 1); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(process_ref) { /* spawn_helper4 blocks indefinitely. */ char *argv[] = { NULL, "spawn_helper4", NULL }; uv_process_options_t options; size_t exepath_size; char exepath[256]; uv_process_t h; int r; memset(&options, 0, sizeof(options)); exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); ASSERT(r == 0); argv[0] = exepath; options.file = exepath; options.args = argv; options.exit_cb = NULL; r = uv_spawn(uv_default_loop(), &h, &options); ASSERT(r == 0); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); r = uv_process_kill(&h, /* SIGTERM */ 15); ASSERT(r == 0); do_close(&h); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(has_ref) { uv_idle_t h; uv_idle_init(uv_default_loop(), &h); uv_ref((uv_handle_t*)&h); ASSERT(uv_has_ref((uv_handle_t*)&h) == 1); uv_unref((uv_handle_t*)&h); ASSERT(uv_has_ref((uv_handle_t*)&h) == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-run-nowait.c000066400000000000000000000031041263361716600172430ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_timer_t timer_handle; static int timer_called = 0; static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer_handle); timer_called = 1; } TEST_IMPL(run_nowait) { int r; uv_timer_init(uv_default_loop(), &timer_handle); uv_timer_start(&timer_handle, timer_cb, 100, 100); r = uv_run(uv_default_loop(), UV_RUN_NOWAIT); ASSERT(r != 0); ASSERT(timer_called == 0); return 0; } libuv-v1.8.0/test/test-run-once.c000066400000000000000000000031621263361716600166720ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #define NUM_TICKS 64 static uv_idle_t idle_handle; static int idle_counter; static void idle_cb(uv_idle_t* handle) { ASSERT(handle == &idle_handle); if (++idle_counter == NUM_TICKS) uv_idle_stop(handle); } TEST_IMPL(run_once) { uv_idle_init(uv_default_loop(), &idle_handle); uv_idle_start(&idle_handle, idle_cb); while (uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT(idle_counter == NUM_TICKS); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-semaphore.c000066400000000000000000000055311263361716600171310ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include typedef struct { uv_mutex_t mutex; uv_sem_t sem; int delay; volatile int posted; } worker_config; static void worker(void* arg) { worker_config* c = arg; if (c->delay) uv_sleep(c->delay); uv_mutex_lock(&c->mutex); ASSERT(c->posted == 0); uv_sem_post(&c->sem); c->posted = 1; uv_mutex_unlock(&c->mutex); } TEST_IMPL(semaphore_1) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); ASSERT(0 == uv_sem_init(&wc.sem, 0)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_sleep(100); uv_mutex_lock(&wc.mutex); ASSERT(wc.posted == 1); uv_sem_wait(&wc.sem); /* should not block */ uv_mutex_unlock(&wc.mutex); /* ergo, it should be ok to unlock after wait */ ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_sem_destroy(&wc.sem); return 0; } TEST_IMPL(semaphore_2) { uv_thread_t thread; worker_config wc; memset(&wc, 0, sizeof(wc)); wc.delay = 100; ASSERT(0 == uv_sem_init(&wc.sem, 0)); ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_sem_wait(&wc.sem); ASSERT(0 == uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_sem_destroy(&wc.sem); return 0; } TEST_IMPL(semaphore_3) { uv_sem_t sem; ASSERT(0 == uv_sem_init(&sem, 3)); uv_sem_wait(&sem); /* should not block */ uv_sem_wait(&sem); /* should not block */ ASSERT(0 == uv_sem_trywait(&sem)); ASSERT(UV_EAGAIN == uv_sem_trywait(&sem)); uv_sem_post(&sem); ASSERT(0 == uv_sem_trywait(&sem)); ASSERT(UV_EAGAIN == uv_sem_trywait(&sem)); uv_sem_destroy(&sem); return 0; } libuv-v1.8.0/test/test-shutdown-close.c000066400000000000000000000060371263361716600201260ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * These tests verify that the uv_shutdown callback is always made, even when * it is immediately followed by an uv_close call. */ #include "uv.h" #include "task.h" static uv_shutdown_t shutdown_req; static uv_connect_t connect_req; static int connect_cb_called = 0; static int shutdown_cb_called = 0; static int close_cb_called = 0; static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(req == &shutdown_req); ASSERT(status == 0 || status == UV_ECANCELED); shutdown_cb_called++; } static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT(req == &connect_req); ASSERT(status == 0); r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); ASSERT(r == 0); ASSERT(0 == uv_is_closing((uv_handle_t*) req->handle)); uv_close((uv_handle_t*) req->handle, close_cb); ASSERT(1 == uv_is_closing((uv_handle_t*) req->handle)); connect_cb_called++; } TEST_IMPL(shutdown_close_tcp) { struct sockaddr_in addr; uv_tcp_t h; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &h); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &h, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(connect_cb_called == 1); ASSERT(shutdown_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(shutdown_close_pipe) { uv_pipe_t h; int r; r = uv_pipe_init(uv_default_loop(), &h, 0); ASSERT(r == 0); uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_cb); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(connect_cb_called == 1); ASSERT(shutdown_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-shutdown-eof.c000066400000000000000000000113261263361716600175670ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static uv_timer_t timer; static uv_tcp_t tcp; static uv_connect_t connect_req; static uv_write_t write_req; static uv_shutdown_t shutdown_req; static uv_buf_t qbuf; static int got_q; static int got_eof; static int called_connect_cb; static int called_shutdown_cb; static int called_tcp_close_cb; static int called_timer_close_cb; static int called_timer_cb; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = malloc(size); buf->len = size; } static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { ASSERT((uv_tcp_t*)t == &tcp); if (nread == 0) { free(buf->base); return; } if (!got_q) { ASSERT(nread == 1); ASSERT(!got_eof); ASSERT(buf->base[0] == 'Q'); free(buf->base); got_q = 1; puts("got Q"); } else { ASSERT(nread == UV_EOF); if (buf->base) { free(buf->base); } got_eof = 1; puts("got EOF"); } } static void shutdown_cb(uv_shutdown_t *req, int status) { ASSERT(req == &shutdown_req); ASSERT(called_connect_cb == 1); ASSERT(!got_eof); ASSERT(called_tcp_close_cb == 0); ASSERT(called_timer_close_cb == 0); ASSERT(called_timer_cb == 0); called_shutdown_cb++; } static void connect_cb(uv_connect_t *req, int status) { ASSERT(status == 0); ASSERT(req == &connect_req); /* Start reading from our connection so we can receive the EOF. */ uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb); /* * Write the letter 'Q' to gracefully kill the echo-server. This will not * effect our connection. */ uv_write(&write_req, (uv_stream_t*) &tcp, &qbuf, 1, NULL); /* Shutdown our end of the connection. */ uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb); called_connect_cb++; ASSERT(called_shutdown_cb == 0); } static void tcp_close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*) &tcp); ASSERT(called_connect_cb == 1); ASSERT(got_q); ASSERT(got_eof); ASSERT(called_timer_cb == 1); called_tcp_close_cb++; } static void timer_close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*) &timer); called_timer_close_cb++; } static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer); uv_close((uv_handle_t*) handle, timer_close_cb); /* * The most important assert of the test: we have not received * tcp_close_cb yet. */ ASSERT(called_tcp_close_cb == 0); uv_close((uv_handle_t*) &tcp, tcp_close_cb); called_timer_cb++; } /* * This test has a client which connects to the echo_server and immediately * issues a shutdown. The echo-server, in response, will also shutdown their * connection. We check, with a timer, that libuv is not automatically * calling uv_close when the client receives the EOF from echo-server. */ TEST_IMPL(shutdown_eof) { struct sockaddr_in server_addr; int r; qbuf.base = "Q"; qbuf.len = 1; r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); uv_timer_start(&timer, timer_cb, 100, 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); r = uv_tcp_connect(&connect_req, &tcp, (const struct sockaddr*) &server_addr, connect_cb); ASSERT(!r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(called_connect_cb == 1); ASSERT(called_shutdown_cb == 1); ASSERT(got_eof); ASSERT(got_q); ASSERT(called_tcp_close_cb == 1); ASSERT(called_timer_close_cb == 1); ASSERT(called_timer_cb == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-shutdown-twice.c000066400000000000000000000045711263361716600201350ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * This is a regression test for issue #1113 (calling uv_shutdown twice will * leave a ghost request in the system) */ #include "uv.h" #include "task.h" static uv_shutdown_t req1; static uv_shutdown_t req2; static int shutdown_cb_called = 0; static void close_cb(uv_handle_t* handle) { } static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(req == &req1); ASSERT(status == 0); shutdown_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT(status == 0); r = uv_shutdown(&req1, req->handle, shutdown_cb); ASSERT(r == 0); r = uv_shutdown(&req2, req->handle, shutdown_cb); ASSERT(r != 0); } TEST_IMPL(shutdown_twice) { struct sockaddr_in addr; uv_loop_t* loop; int r; uv_tcp_t h; uv_connect_t connect_req; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_tcp_init(loop, &h); r = uv_tcp_connect(&connect_req, &h, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(shutdown_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-signal-multiple-loops.c000066400000000000000000000203321263361716600214020ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* This test does not pretend to be cross-platform. */ #ifndef _WIN32 #include "uv.h" #include "task.h" #include #include #include #include #include #include #include /* The value of NUM_SIGNAL_HANDLING_THREADS is not arbitrary; it needs to be a * multiple of three for reasons that will become clear when you scroll down. * We're basically creating three different thread groups. The total needs * to be divisible by three in order for the numbers in the final check to * match up. */ #define NUM_SIGNAL_HANDLING_THREADS 24 #define NUM_LOOP_CREATING_THREADS 10 enum signal_action { ONLY_SIGUSR1, ONLY_SIGUSR2, SIGUSR1_AND_SIGUSR2 }; static uv_sem_t sem; static uv_mutex_t counter_lock; static volatile int stop = 0; static volatile int signal1_cb_counter = 0; static volatile int signal2_cb_counter = 0; static volatile int loop_creation_counter = 0; static void increment_counter(volatile int* counter) { uv_mutex_lock(&counter_lock); ++(*counter); uv_mutex_unlock(&counter_lock); } static void signal1_cb(uv_signal_t* handle, int signum) { ASSERT(signum == SIGUSR1); increment_counter(&signal1_cb_counter); uv_signal_stop(handle); } static void signal2_cb(uv_signal_t* handle, int signum) { ASSERT(signum == SIGUSR2); increment_counter(&signal2_cb_counter); uv_signal_stop(handle); } static void signal_handling_worker(void* context) { enum signal_action action; uv_signal_t signal1a; uv_signal_t signal1b; uv_signal_t signal2; uv_loop_t loop; int r; action = (enum signal_action) (uintptr_t) context; ASSERT(0 == uv_loop_init(&loop)); /* Setup the signal watchers and start them. */ if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_init(&loop, &signal1a); ASSERT(r == 0); r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1); ASSERT(r == 0); r = uv_signal_init(&loop, &signal1b); ASSERT(r == 0); r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1); ASSERT(r == 0); } if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_init(&loop, &signal2); ASSERT(r == 0); r = uv_signal_start(&signal2, signal2_cb, SIGUSR2); ASSERT(r == 0); } /* Signal watchers are now set up. */ uv_sem_post(&sem); /* Wait for all signals. The signal callbacks stop the watcher, so uv_run * will return when all signal watchers caught a signal. */ r = uv_run(&loop, UV_RUN_DEFAULT); ASSERT(r == 0); /* Restart the signal watchers. */ if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1); ASSERT(r == 0); r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1); ASSERT(r == 0); } if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_start(&signal2, signal2_cb, SIGUSR2); ASSERT(r == 0); } /* Wait for signals once more. */ uv_sem_post(&sem); r = uv_run(&loop, UV_RUN_DEFAULT); ASSERT(r == 0); /* Close the watchers. */ if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { uv_close((uv_handle_t*) &signal1a, NULL); uv_close((uv_handle_t*) &signal1b, NULL); } if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { uv_close((uv_handle_t*) &signal2, NULL); } /* Wait for the signal watchers to close. */ r = uv_run(&loop, UV_RUN_DEFAULT); ASSERT(r == 0); uv_loop_close(&loop); } static void signal_unexpected_cb(uv_signal_t* handle, int signum) { ASSERT(0 && "signal_unexpected_cb should never be called"); } static void loop_creating_worker(void* context) { (void) context; do { uv_loop_t *loop; uv_signal_t signal; int r; loop = malloc(sizeof(*loop)); ASSERT(loop != NULL); ASSERT(0 == uv_loop_init(loop)); r = uv_signal_init(loop, &signal); ASSERT(r == 0); r = uv_signal_start(&signal, signal_unexpected_cb, SIGTERM); ASSERT(r == 0); uv_close((uv_handle_t*) &signal, NULL); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); uv_loop_close(loop); free(loop); increment_counter(&loop_creation_counter); } while (!stop); } TEST_IMPL(signal_multiple_loops) { uv_thread_t loop_creating_threads[NUM_LOOP_CREATING_THREADS]; uv_thread_t signal_handling_threads[NUM_SIGNAL_HANDLING_THREADS]; enum signal_action action; sigset_t sigset; int i; int r; r = uv_sem_init(&sem, 0); ASSERT(r == 0); r = uv_mutex_init(&counter_lock); ASSERT(r == 0); /* Create a couple of threads that create a destroy loops continuously. */ for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) { r = uv_thread_create(&loop_creating_threads[i], loop_creating_worker, NULL); ASSERT(r == 0); } /* Create a couple of threads that actually handle signals. */ for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) { switch (i % 3) { case 0: action = ONLY_SIGUSR1; break; case 1: action = ONLY_SIGUSR2; break; case 2: action = SIGUSR1_AND_SIGUSR2; break; } r = uv_thread_create(&signal_handling_threads[i], signal_handling_worker, (void*) (uintptr_t) action); ASSERT(r == 0); } /* Wait until all threads have started and set up their signal watchers. */ for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) uv_sem_wait(&sem); r = kill(getpid(), SIGUSR1); ASSERT(r == 0); r = kill(getpid(), SIGUSR2); ASSERT(r == 0); /* Wait for all threads to handle these signals. */ for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) uv_sem_wait(&sem); /* Block all signals to this thread, so we are sure that from here the signal * handler runs in another thread. This is is more likely to catch thread and * signal safety issues if there are any. */ sigfillset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); r = kill(getpid(), SIGUSR1); ASSERT(r == 0); r = kill(getpid(), SIGUSR2); ASSERT(r == 0); /* Wait for all signal handling threads to exit. */ for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) { r = uv_thread_join(&signal_handling_threads[i]); ASSERT(r == 0); } /* Tell all loop creating threads to stop. */ stop = 1; /* Wait for all loop creating threads to exit. */ for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) { r = uv_thread_join(&loop_creating_threads[i]); ASSERT(r == 0); } printf("signal1_cb calls: %d\n", signal1_cb_counter); printf("signal2_cb calls: %d\n", signal2_cb_counter); printf("loops created and destroyed: %d\n", loop_creation_counter); /* The division by three reflects the fact that we spawn three different * thread groups of (NUM_SIGNAL_HANDLING_THREADS / 3) threads each. */ ASSERT(signal1_cb_counter == 8 * (NUM_SIGNAL_HANDLING_THREADS / 3)); ASSERT(signal2_cb_counter == 4 * (NUM_SIGNAL_HANDLING_THREADS / 3)); /* We don't know exactly how much loops will be created and destroyed, but at * least there should be 1 for every loop creating thread. */ ASSERT(loop_creation_counter >= NUM_LOOP_CREATING_THREADS); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !_WIN32 */ libuv-v1.8.0/test/test-signal.c000066400000000000000000000102071263361716600164170ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* This test does not pretend to be cross-platform. */ #ifndef _WIN32 #include "uv.h" #include "task.h" #include #include #include #include #include #include #include #define NSIGNALS 10 struct timer_ctx { unsigned int ncalls; uv_timer_t handle; int signum; }; struct signal_ctx { enum { CLOSE, STOP } stop_or_close; unsigned int ncalls; uv_signal_t handle; int signum; }; static void signal_cb(uv_signal_t* handle, int signum) { struct signal_ctx* ctx = container_of(handle, struct signal_ctx, handle); ASSERT(signum == ctx->signum); if (++ctx->ncalls == NSIGNALS) { if (ctx->stop_or_close == STOP) uv_signal_stop(handle); else if (ctx->stop_or_close == CLOSE) uv_close((uv_handle_t*)handle, NULL); else ASSERT(0); } } static void timer_cb(uv_timer_t* handle) { struct timer_ctx* ctx = container_of(handle, struct timer_ctx, handle); raise(ctx->signum); if (++ctx->ncalls == NSIGNALS) uv_close((uv_handle_t*)handle, NULL); } static void start_watcher(uv_loop_t* loop, int signum, struct signal_ctx* ctx) { ctx->ncalls = 0; ctx->signum = signum; ctx->stop_or_close = CLOSE; ASSERT(0 == uv_signal_init(loop, &ctx->handle)); ASSERT(0 == uv_signal_start(&ctx->handle, signal_cb, signum)); } static void start_timer(uv_loop_t* loop, int signum, struct timer_ctx* ctx) { ctx->ncalls = 0; ctx->signum = signum; ASSERT(0 == uv_timer_init(loop, &ctx->handle)); ASSERT(0 == uv_timer_start(&ctx->handle, timer_cb, 5, 5)); } TEST_IMPL(we_get_signal) { struct signal_ctx sc; struct timer_ctx tc; uv_loop_t* loop; loop = uv_default_loop(); start_timer(loop, SIGCHLD, &tc); start_watcher(loop, SIGCHLD, &sc); sc.stop_or_close = STOP; /* stop, don't close the signal handle */ ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(tc.ncalls == NSIGNALS); ASSERT(sc.ncalls == NSIGNALS); start_timer(loop, SIGCHLD, &tc); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(tc.ncalls == NSIGNALS); ASSERT(sc.ncalls == NSIGNALS); sc.ncalls = 0; sc.stop_or_close = CLOSE; /* now close it when it's done */ uv_signal_start(&sc.handle, signal_cb, SIGCHLD); start_timer(loop, SIGCHLD, &tc); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(tc.ncalls == NSIGNALS); ASSERT(sc.ncalls == NSIGNALS); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(we_get_signals) { struct signal_ctx sc[4]; struct timer_ctx tc[2]; uv_loop_t* loop; unsigned int i; loop = uv_default_loop(); start_watcher(loop, SIGUSR1, sc + 0); start_watcher(loop, SIGUSR1, sc + 1); start_watcher(loop, SIGUSR2, sc + 2); start_watcher(loop, SIGUSR2, sc + 3); start_timer(loop, SIGUSR1, tc + 0); start_timer(loop, SIGUSR2, tc + 1); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); for (i = 0; i < ARRAY_SIZE(sc); i++) ASSERT(sc[i].ncalls == NSIGNALS); for (i = 0; i < ARRAY_SIZE(tc); i++) ASSERT(tc[i].ncalls == NSIGNALS); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* _WIN32 */ libuv-v1.8.0/test/test-socket-buffer-size.c000066400000000000000000000045641263361716600206620ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include static uv_udp_t udp; static uv_tcp_t tcp; static int close_cb_called; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void check_buffer_size(uv_handle_t* handle) { int value; value = 0; ASSERT(0 == uv_recv_buffer_size(handle, &value)); ASSERT(value > 0); value = 10000; ASSERT(0 == uv_recv_buffer_size(handle, &value)); value = 0; ASSERT(0 == uv_recv_buffer_size(handle, &value)); /* linux sets double the value */ ASSERT(value == 10000 || value == 20000); } TEST_IMPL(socket_buffer_size) { struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(uv_default_loop(), &tcp)); ASSERT(0 == uv_tcp_bind(&tcp, (struct sockaddr*) &addr, 0)); check_buffer_size((uv_handle_t*) &tcp); uv_close((uv_handle_t*) &tcp, close_cb); ASSERT(0 == uv_udp_init(uv_default_loop(), &udp)); ASSERT(0 == uv_udp_bind(&udp, (struct sockaddr*) &addr, 0)); check_buffer_size((uv_handle_t*) &udp); uv_close((uv_handle_t*) &udp, close_cb); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-spawn.c000066400000000000000000001233571263361716600163050ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #include #include #ifdef _WIN32 # if defined(__MINGW32__) # include # endif # include # include #else # include # include #endif static int close_cb_called; static int exit_cb_called; static uv_process_t process; static uv_timer_t timer; static uv_process_options_t options; static char exepath[1024]; static size_t exepath_size = 1024; static char* args[3]; static int no_term_signal; static int timer_counter; #define OUTPUT_SIZE 1024 static char output[OUTPUT_SIZE]; static int output_used; static void close_cb(uv_handle_t* handle) { printf("close_cb\n"); close_cb_called++; } static void exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) { printf("exit_cb\n"); exit_cb_called++; ASSERT(exit_status == 1); ASSERT(term_signal == 0); uv_close((uv_handle_t*)process, close_cb); } static void fail_cb(uv_process_t* process, int64_t exit_status, int term_signal) { ASSERT(0 && "fail_cb called"); } static void kill_cb(uv_process_t* process, int64_t exit_status, int term_signal) { int err; printf("exit_cb\n"); exit_cb_called++; #ifdef _WIN32 ASSERT(exit_status == 1); #else ASSERT(exit_status == 0); #endif ASSERT(no_term_signal || term_signal == 15); uv_close((uv_handle_t*)process, close_cb); /* * Sending signum == 0 should check if the * child process is still alive, not kill it. * This process should be dead. */ err = uv_kill(process->pid, 0); ASSERT(err == UV_ESRCH); } static void detach_failure_cb(uv_process_t* process, int64_t exit_status, int term_signal) { printf("detach_cb\n"); exit_cb_called++; } static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = output + output_used; buf->len = OUTPUT_SIZE - output_used; } static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { output_used += nread; } else if (nread < 0) { ASSERT(nread == UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } } static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { uv_read_stop(tcp); on_read(tcp, nread, buf); } static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); uv_close((uv_handle_t*)req->handle, close_cb); } static void init_process_options(char* test, uv_exit_cb exit_cb) { /* Note spawn_helper1 defined in test/run-tests.c */ int r = uv_exepath(exepath, &exepath_size); ASSERT(r == 0); exepath[exepath_size] = '\0'; args[0] = exepath; args[1] = test; args[2] = NULL; options.file = exepath; options.args = args; options.exit_cb = exit_cb; options.flags = 0; } static void timer_cb(uv_timer_t* handle) { uv_process_kill(&process, /* SIGTERM */ 15); uv_close((uv_handle_t*)handle, close_cb); } static void timer_counter_cb(uv_timer_t* handle) { ++timer_counter; } TEST_IMPL(spawn_fails) { int r; init_process_options("", fail_cb); options.file = options.args[0] = "program-that-had-better-not-exist"; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOENT || r == UV_EACCES); ASSERT(0 == uv_is_active((uv_handle_t*) &process)); uv_close((uv_handle_t*) &process, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } #ifndef _WIN32 TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) { int r; int status; int err; init_process_options("", fail_cb); options.file = options.args[0] = "program-that-had-better-not-exist"; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOENT || r == UV_EACCES); ASSERT(0 == uv_is_active((uv_handle_t*) &process)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); /* verify the child is successfully cleaned up within libuv */ do err = waitpid(process.pid, &status, 0); while (err == -1 && errno == EINTR); ASSERT(err == -1); ASSERT(errno == ECHILD); uv_close((uv_handle_t*) &process, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } #endif TEST_IMPL(spawn_exit_code) { int r; init_process_options("spawn_helper1", exit_cb); r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_stdout) { int r; uv_pipe_t out; uv_stdio_container_t stdio[2]; init_process_options("spawn_helper2", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ printf("output is: %s", output); ASSERT(strcmp("hello world\n", output) == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_stdout_to_file) { int r; uv_file file; uv_fs_t fs_req; uv_stdio_container_t stdio[2]; uv_buf_t buf; /* Setup. */ unlink("stdout_file"); init_process_options("spawn_helper2", exit_cb); r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); ASSERT(r != -1); uv_fs_req_cleanup(&fs_req); file = r; options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_INHERIT_FD; options.stdio[1].data.fd = file; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); buf = uv_buf_init(output, sizeof(output)); r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); ASSERT(r == 12); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); ASSERT(strcmp("hello world\n", output) == 0); /* Cleanup. */ unlink("stdout_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_stdout_and_stderr_to_file) { int r; uv_file file; uv_fs_t fs_req; uv_stdio_container_t stdio[3]; uv_buf_t buf; /* Setup. */ unlink("stdout_file"); init_process_options("spawn_helper6", exit_cb); r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); ASSERT(r != -1); uv_fs_req_cleanup(&fs_req); file = r; options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_INHERIT_FD; options.stdio[1].data.fd = file; options.stdio[2].flags = UV_INHERIT_FD; options.stdio[2].data.fd = file; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); buf = uv_buf_init(output, sizeof(output)); r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); ASSERT(r == 27); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); /* Cleanup. */ unlink("stdout_file"); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_stdout_and_stderr_to_file2) { #ifndef _WIN32 int r; uv_file file; uv_fs_t fs_req; uv_stdio_container_t stdio[3]; uv_buf_t buf; /* Setup. */ unlink("stdout_file"); init_process_options("spawn_helper6", exit_cb); /* Replace stderr with our file */ r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); ASSERT(r != -1); uv_fs_req_cleanup(&fs_req); file = dup2(r, STDERR_FILENO); ASSERT(file != -1); options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_INHERIT_FD; options.stdio[1].data.fd = file; options.stdio[2].flags = UV_INHERIT_FD; options.stdio[2].data.fd = file; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); buf = uv_buf_init(output, sizeof(output)); r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); ASSERT(r == 27); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); /* Cleanup. */ unlink("stdout_file"); MAKE_VALGRIND_HAPPY(); return 0; #else RETURN_SKIP("Unix only test"); #endif } TEST_IMPL(spawn_stdout_and_stderr_to_file_swap) { #ifndef _WIN32 int r; uv_file stdout_file; uv_file stderr_file; uv_fs_t fs_req; uv_stdio_container_t stdio[3]; uv_buf_t buf; /* Setup. */ unlink("stdout_file"); unlink("stderr_file"); init_process_options("spawn_helper6", exit_cb); /* open 'stdout_file' and replace STDOUT_FILENO with it */ r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); ASSERT(r != -1); uv_fs_req_cleanup(&fs_req); stdout_file = dup2(r, STDOUT_FILENO); ASSERT(stdout_file != -1); /* open 'stderr_file' and replace STDERR_FILENO with it */ r = uv_fs_open(NULL, &fs_req, "stderr_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); ASSERT(r != -1); uv_fs_req_cleanup(&fs_req); stderr_file = dup2(r, STDERR_FILENO); ASSERT(stderr_file != -1); /* now we're going to swap them: the child process' stdout will be our * stderr_file and vice versa */ options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_INHERIT_FD; options.stdio[1].data.fd = stderr_file; options.stdio[2].flags = UV_INHERIT_FD; options.stdio[2].data.fd = stdout_file; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); buf = uv_buf_init(output, sizeof(output)); /* check the content of stdout_file */ r = uv_fs_read(NULL, &fs_req, stdout_file, &buf, 1, 0, NULL); ASSERT(r >= 15); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, stdout_file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); ASSERT(strncmp("hello errworld\n", output, 15) == 0); /* check the content of stderr_file */ r = uv_fs_read(NULL, &fs_req, stderr_file, &buf, 1, 0, NULL); ASSERT(r >= 12); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, stderr_file, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); ASSERT(strncmp("hello world\n", output, 12) == 0); /* Cleanup. */ unlink("stdout_file"); unlink("stderr_file"); MAKE_VALGRIND_HAPPY(); return 0; #else RETURN_SKIP("Unix only test"); #endif } TEST_IMPL(spawn_stdin) { int r; uv_pipe_t out; uv_pipe_t in; uv_write_t write_req; uv_buf_t buf; uv_stdio_container_t stdio[2]; char buffer[] = "hello-from-spawn_stdin"; init_process_options("spawn_helper3", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*)∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); buf.base = buffer; buf.len = sizeof(buffer); r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */ ASSERT(strcmp(buffer, output) == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_stdio_greater_than_3) { int r; uv_pipe_t pipe; uv_stdio_container_t stdio[4]; init_process_options("spawn_helper5", exit_cb); uv_pipe_init(uv_default_loop(), &pipe, 0); options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_IGNORE; options.stdio[2].flags = UV_IGNORE; options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[3].data.stream = (uv_stream_t*)&pipe; options.stdio_count = 4; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ printf("output from stdio[3] is: %s", output); ASSERT(strcmp("fourth stdio!\n", output) == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_ignored_stdio) { int r; init_process_options("spawn_helper6", exit_cb); options.stdio = NULL; options.stdio_count = 0; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_and_kill) { int r; init_process_options("spawn_helper4", kill_cb); r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 500, 0); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* Once for process and once for timer. */ MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_preserve_env) { int r; uv_pipe_t out; uv_stdio_container_t stdio[2]; init_process_options("spawn_helper7", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = putenv("ENV_TEST=testval"); ASSERT(r == 0); /* Explicitly set options.env to NULL to test for env clobbering. */ options.env = NULL; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); printf("output is: %s", output); ASSERT(strcmp("testval", output) == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_detached) { int r; init_process_options("spawn_helper4", detach_failure_cb); options.flags |= UV_PROCESS_DETACHED; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); uv_unref((uv_handle_t*)&process); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 0); r = uv_kill(process.pid, 0); ASSERT(r == 0); r = uv_kill(process.pid, 15); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_and_kill_with_std) { int r; uv_pipe_t in, out, err; uv_write_t write; char message[] = "Nancy's joining me because the message this evening is " "not my message but ours."; uv_buf_t buf; uv_stdio_container_t stdio[3]; init_process_options("spawn_helper4", kill_cb); r = uv_pipe_init(uv_default_loop(), &in, 0); ASSERT(r == 0); r = uv_pipe_init(uv_default_loop(), &out, 0); ASSERT(r == 0); r = uv_pipe_init(uv_default_loop(), &err, 0); ASSERT(r == 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*)∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[2].data.stream = (uv_stream_t*)&err; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); buf = uv_buf_init(message, sizeof message); r = uv_write(&write, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &err, on_alloc, on_read); ASSERT(r == 0); r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 500, 0); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 5); /* process x 1, timer x 1, stdio x 3. */ MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_and_ping) { uv_write_t write_req; uv_pipe_t in, out; uv_buf_t buf; uv_stdio_container_t stdio[2]; int r; init_process_options("spawn_helper3", exit_cb); buf = uv_buf_init("TEST", 4); uv_pipe_init(uv_default_loop(), &out, 0); uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*)∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ r = uv_process_kill(&process, 0); ASSERT(r == 0); r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); ASSERT(r == 0); ASSERT(exit_cb_called == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(strcmp(output, "TEST") == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_same_stdout_stderr) { uv_write_t write_req; uv_pipe_t in, out; uv_buf_t buf; uv_stdio_container_t stdio[3]; int r; init_process_options("spawn_helper3", exit_cb); buf = uv_buf_init("TEST", 4); uv_pipe_init(uv_default_loop(), &out, 0); uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*)∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ r = uv_process_kill(&process, 0); ASSERT(r == 0); r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); ASSERT(r == 0); ASSERT(exit_cb_called == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(strcmp(output, "TEST") == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_closed_process_io) { uv_pipe_t in; uv_write_t write_req; uv_buf_t buf; uv_stdio_container_t stdio[2]; static char buffer[] = "hello-from-spawn_stdin\n"; init_process_options("spawn_helper3", exit_cb); uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio_count = 1; close(0); /* Close process stdin. */ ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); buf = uv_buf_init(buffer, sizeof(buffer)); ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* process, child stdin */ MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(kill) { int r; #ifdef _WIN32 no_term_signal = 1; #endif init_process_options("spawn_helper4", kill_cb); r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ r = uv_kill(process.pid, 0); ASSERT(r == 0); /* Kill the process. */ r = uv_kill(process.pid, /* SIGTERM */ 15); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } #ifdef _WIN32 TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { int r; uv_pipe_t out; char name[64]; HANDLE pipe_handle; uv_stdio_container_t stdio[2]; init_process_options("spawn_helper2", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio_count = 2; /* Create a pipe that'll cause a collision. */ snprintf(name, sizeof(name), "\\\\.\\pipe\\uv\\%p-%d", &out, GetCurrentProcessId()); pipe_handle = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 10, 65536, 65536, 0, NULL); ASSERT(pipe_handle != INVALID_HANDLE_VALUE); r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ printf("output is: %s", output); ASSERT(strcmp("hello world\n", output) == 0); MAKE_VALGRIND_HAPPY(); return 0; } #if !defined(USING_UV_SHARED) int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr); WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target); TEST_IMPL(argument_escaping) { const WCHAR* test_str[] = { L"", L"HelloWorld", L"Hello World", L"Hello\"World", L"Hello World\\", L"Hello\\\"World", L"Hello\\World", L"Hello\\\\World", L"Hello World\\", L"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"" }; const int count = sizeof(test_str) / sizeof(*test_str); WCHAR** test_output; WCHAR* command_line; WCHAR** cracked; size_t total_size = 0; int i; int num_args; int result; char* verbatim[] = { "cmd.exe", "/c", "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"", NULL }; WCHAR* verbatim_output; WCHAR* non_verbatim_output; test_output = calloc(count, sizeof(WCHAR*)); ASSERT(test_output != NULL); for (i = 0; i < count; ++i) { test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(WCHAR)); quote_cmd_arg(test_str[i], test_output[i]); wprintf(L"input : %s\n", test_str[i]); wprintf(L"output: %s\n", test_output[i]); total_size += wcslen(test_output[i]) + 1; } command_line = calloc(total_size + 1, sizeof(WCHAR)); ASSERT(command_line != NULL); for (i = 0; i < count; ++i) { wcscat(command_line, test_output[i]); wcscat(command_line, L" "); } command_line[total_size - 1] = L'\0'; wprintf(L"command_line: %s\n", command_line); cracked = CommandLineToArgvW(command_line, &num_args); for (i = 0; i < num_args; ++i) { wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]); ASSERT(wcscmp(test_str[i], cracked[i]) == 0); } LocalFree(cracked); for (i = 0; i < count; ++i) { free(test_output[i]); } result = make_program_args(verbatim, 1, &verbatim_output); ASSERT(result == 0); result = make_program_args(verbatim, 0, &non_verbatim_output); ASSERT(result == 0); wprintf(L" verbatim_output: %s\n", verbatim_output); wprintf(L"non_verbatim_output: %s\n", non_verbatim_output); ASSERT(wcscmp(verbatim_output, L"cmd.exe /c c:\\path\\to\\node.exe --eval " L"\"require('c:\\\\path\\\\to\\\\test.js')\"") == 0); ASSERT(wcscmp(non_verbatim_output, L"cmd.exe /c \"c:\\path\\to\\node.exe --eval " L"\\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0); free(verbatim_output); free(non_verbatim_output); return 0; } int make_program_env(char** env_block, WCHAR** dst_ptr); TEST_IMPL(environment_creation) { int i; char* environment[] = { "FOO=BAR", "SYSTEM=ROOT", /* substring of a supplied var name */ "SYSTEMROOTED=OMG", /* supplied var name is a substring */ "TEMP=C:\\Temp", "INVALID", "BAZ=QUX", "B_Z=QUX", "B\xe2\x82\xacZ=QUX", "B\xf0\x90\x80\x82Z=QUX", "B\xef\xbd\xa1Z=QUX", "B\xf0\xa3\x91\x96Z=QUX", "BAZ", /* repeat, invalid variable */ NULL }; WCHAR* wenvironment[] = { L"BAZ=QUX", L"B_Z=QUX", L"B\x20acZ=QUX", L"B\xd800\xdc02Z=QUX", L"B\xd84d\xdc56Z=QUX", L"B\xff61Z=QUX", L"FOO=BAR", L"SYSTEM=ROOT", /* substring of a supplied var name */ L"SYSTEMROOTED=OMG", /* supplied var name is a substring */ L"TEMP=C:\\Temp", }; WCHAR* from_env[] = { /* list should be kept in sync with list * in process.c, minus variables in wenvironment */ L"HOMEDRIVE", L"HOMEPATH", L"LOGONSERVER", L"PATH", L"USERDOMAIN", L"USERNAME", L"USERPROFILE", L"SYSTEMDRIVE", L"SYSTEMROOT", L"WINDIR", /* test for behavior in the absence of a * required-environment variable: */ L"ZTHIS_ENV_VARIABLE_DOES_NOT_EXIST", }; int found_in_loc_env[ARRAY_SIZE(wenvironment)] = {0}; int found_in_usr_env[ARRAY_SIZE(from_env)] = {0}; WCHAR *expected[ARRAY_SIZE(from_env)]; int result; WCHAR* str; WCHAR* prev; WCHAR* env; for (i = 0; i < ARRAY_SIZE(from_env); i++) { /* copy expected additions to environment locally */ size_t len = GetEnvironmentVariableW(from_env[i], NULL, 0); if (len == 0) { found_in_usr_env[i] = 1; str = malloc(1 * sizeof(WCHAR)); *str = 0; expected[i] = str; } else { size_t name_len = wcslen(from_env[i]); str = malloc((name_len+1+len) * sizeof(WCHAR)); wmemcpy(str, from_env[i], name_len); expected[i] = str; str += name_len; *str++ = L'='; GetEnvironmentVariableW(from_env[i], str, len); } } result = make_program_env(environment, &env); ASSERT(result == 0); for (str = env, prev = NULL; *str; prev = str, str += wcslen(str) + 1) { int found = 0; #if 0 _cputws(str); putchar('\n'); #endif for (i = 0; i < ARRAY_SIZE(wenvironment) && !found; i++) { if (!wcscmp(str, wenvironment[i])) { ASSERT(!found_in_loc_env[i]); found_in_loc_env[i] = 1; found = 1; } } for (i = 0; i < ARRAY_SIZE(expected) && !found; i++) { if (!wcscmp(str, expected[i])) { ASSERT(!found_in_usr_env[i]); found_in_usr_env[i] = 1; found = 1; } } if (prev) { /* verify sort order -- requires Vista */ #if _WIN32_WINNT >= 0x0600 && \ (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)) ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1); #endif } ASSERT(found); /* verify that we expected this variable */ } /* verify that we found all expected variables */ for (i = 0; i < ARRAY_SIZE(wenvironment); i++) { ASSERT(found_in_loc_env[i]); } for (i = 0; i < ARRAY_SIZE(expected); i++) { ASSERT(found_in_usr_env[i]); } return 0; } #endif /* Regression test for issue #909 */ TEST_IMPL(spawn_with_an_odd_path) { int r; char newpath[2048]; char *path = getenv("PATH"); ASSERT(path != NULL); snprintf(newpath, 2048, ";.;%s", path); SetEnvironmentVariable("PATH", newpath); init_process_options("", exit_cb); options.file = options.args[0] = "program-that-had-better-not-exist"; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOENT || r == UV_EACCES); ASSERT(0 == uv_is_active((uv_handle_t*) &process)); uv_close((uv_handle_t*) &process, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } #endif #ifndef _WIN32 TEST_IMPL(spawn_setuid_setgid) { int r; struct passwd* pw; /* if not root, then this will fail. */ uv_uid_t uid = getuid(); if (uid != 0) { fprintf(stderr, "spawn_setuid_setgid skipped: not root\n"); return 0; } init_process_options("spawn_helper1", exit_cb); /* become the "nobody" user. */ pw = getpwnam("nobody"); ASSERT(pw != NULL); options.uid = pw->pw_uid; options.gid = pw->pw_gid; options.flags = UV_PROCESS_SETUID | UV_PROCESS_SETGID; r = uv_spawn(uv_default_loop(), &process, &options); if (r == UV_EACCES) RETURN_SKIP("user 'nobody' cannot access the test runner"); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } #endif #ifndef _WIN32 TEST_IMPL(spawn_setuid_fails) { int r; /* if root, become nobody. */ uv_uid_t uid = getuid(); if (uid == 0) { struct passwd* pw; pw = getpwnam("nobody"); ASSERT(pw != NULL); ASSERT(0 == setgid(pw->pw_gid)); ASSERT(0 == setuid(pw->pw_uid)); } init_process_options("spawn_helper1", fail_cb); options.flags |= UV_PROCESS_SETUID; options.uid = 0; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_EPERM); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_setgid_fails) { int r; /* if root, become nobody. */ uv_uid_t uid = getuid(); if (uid == 0) { struct passwd* pw; pw = getpwnam("nobody"); ASSERT(pw != NULL); ASSERT(0 == setgid(pw->pw_gid)); ASSERT(0 == setuid(pw->pw_uid)); } init_process_options("spawn_helper1", fail_cb); options.flags |= UV_PROCESS_SETGID; options.gid = 0; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_EPERM); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 0); MAKE_VALGRIND_HAPPY(); return 0; } #endif #ifdef _WIN32 static void exit_cb_unexpected(uv_process_t* process, int64_t exit_status, int term_signal) { ASSERT(0 && "should not have been called"); } TEST_IMPL(spawn_setuid_fails) { int r; init_process_options("spawn_helper1", exit_cb_unexpected); options.flags |= UV_PROCESS_SETUID; options.uid = (uv_uid_t) -42424242; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOTSUP); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(spawn_setgid_fails) { int r; init_process_options("spawn_helper1", exit_cb_unexpected); options.flags |= UV_PROCESS_SETGID; options.gid = (uv_gid_t) -42424242; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOTSUP); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(close_cb_called == 0); MAKE_VALGRIND_HAPPY(); return 0; } #endif TEST_IMPL(spawn_auto_unref) { init_process_options("spawn_helper1", NULL); ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_is_closing((uv_handle_t*) &process)); uv_close((uv_handle_t*) &process, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(1 == uv_is_closing((uv_handle_t*) &process)); MAKE_VALGRIND_HAPPY(); return 0; } #ifndef _WIN32 TEST_IMPL(spawn_fs_open) { int fd; uv_fs_t fs_req; uv_pipe_t in; uv_write_t write_req; uv_buf_t buf; uv_stdio_container_t stdio[1]; fd = uv_fs_open(NULL, &fs_req, "/dev/null", O_RDWR, 0, NULL); ASSERT(fd >= 0); uv_fs_req_cleanup(&fs_req); init_process_options("spawn_helper8", exit_cb); ASSERT(0 == uv_pipe_init(uv_default_loop(), &in, 0)); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio_count = 1; ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); buf = uv_buf_init((char*) &fd, sizeof(fd)); ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_fs_close(NULL, &fs_req, fd, NULL)); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* One for `in`, one for process */ MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !_WIN32 */ #ifndef _WIN32 TEST_IMPL(closed_fd_events) { uv_stdio_container_t stdio[3]; uv_pipe_t pipe_handle; int fd[2]; /* create a pipe and share it with a child process */ ASSERT(0 == pipe(fd)); /* spawn_helper4 blocks indefinitely. */ init_process_options("spawn_helper4", exit_cb); options.stdio_count = 3; options.stdio = stdio; options.stdio[0].flags = UV_INHERIT_FD; options.stdio[0].data.fd = fd[0]; options.stdio[1].flags = UV_IGNORE; options.stdio[2].flags = UV_IGNORE; ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); uv_unref((uv_handle_t*) &process); /* read from the pipe with uv */ ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); fd[0] = -1; ASSERT(0 == uv_read_start((uv_stream_t*) &pipe_handle, on_alloc, on_read_once)); ASSERT(1 == write(fd[1], "", 1)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* should have received just one byte */ ASSERT(output_used == 1); /* close the pipe and see if we still get events */ uv_close((uv_handle_t*) &pipe_handle, close_cb); ASSERT(1 == write(fd[1], "", 1)); ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); ASSERT(0 == uv_timer_start(&timer, timer_counter_cb, 10, 0)); /* see if any spurious events interrupt the timer */ if (1 == uv_run(uv_default_loop(), UV_RUN_ONCE)) /* have to run again to really trigger the timer */ ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT(timer_counter == 1); /* cleanup */ ASSERT(0 == uv_process_kill(&process, /* SIGTERM */ 15)); ASSERT(0 == close(fd[1])); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !_WIN32 */ TEST_IMPL(spawn_reads_child_path) { int r; int len; char file[64]; char path[1024]; char* env[3]; /* Need to carry over the dynamic linker path when the test runner is * linked against libuv.so, see https://github.com/libuv/libuv/issues/85. */ #if defined(__APPLE__) static const char dyld_path_var[] = "DYLD_LIBRARY_PATH"; #else static const char dyld_path_var[] = "LD_LIBRARY_PATH"; #endif /* Set up the process, but make sure that the file to run is relative and */ /* requires a lookup into PATH */ init_process_options("spawn_helper1", exit_cb); /* Set up the PATH env variable */ for (len = strlen(exepath); exepath[len - 1] != '/' && exepath[len - 1] != '\\'; len--); strcpy(file, exepath + len); exepath[len] = 0; strcpy(path, "PATH="); strcpy(path + 5, exepath); env[0] = path; env[1] = getenv(dyld_path_var); env[2] = NULL; if (env[1] != NULL) { static char buf[1024 + sizeof(dyld_path_var)]; snprintf(buf, sizeof(buf), "%s=%s", dyld_path_var, env[1]); env[1] = buf; } options.file = file; options.args[0] = file; options.env = env; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } #ifndef _WIN32 static int mpipe(int *fds) { if (pipe(fds) == -1) return -1; if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { close(fds[0]); close(fds[1]); return -1; } return 0; } #else static int mpipe(int *fds) { SECURITY_ATTRIBUTES attr; HANDLE readh, writeh; attr.nLength = sizeof(attr); attr.lpSecurityDescriptor = NULL; attr.bInheritHandle = FALSE; if (!CreatePipe(&readh, &writeh, &attr, 0)) return -1; fds[0] = _open_osfhandle((intptr_t)readh, 0); fds[1] = _open_osfhandle((intptr_t)writeh, 0); if (fds[0] == -1 || fds[1] == -1) { CloseHandle(readh); CloseHandle(writeh); return -1; } return 0; } #endif /* !_WIN32 */ TEST_IMPL(spawn_inherit_streams) { uv_process_t child_req; uv_stdio_container_t child_stdio[2]; int fds_stdin[2]; int fds_stdout[2]; uv_pipe_t pipe_stdin_child; uv_pipe_t pipe_stdout_child; uv_pipe_t pipe_stdin_parent; uv_pipe_t pipe_stdout_parent; unsigned char ubuf[OUTPUT_SIZE - 1]; uv_buf_t buf; unsigned int i; int r; uv_write_t write_req; uv_loop_t* loop; init_process_options("spawn_helper9", exit_cb); loop = uv_default_loop(); ASSERT(uv_pipe_init(loop, &pipe_stdin_child, 0) == 0); ASSERT(uv_pipe_init(loop, &pipe_stdout_child, 0) == 0); ASSERT(uv_pipe_init(loop, &pipe_stdin_parent, 0) == 0); ASSERT(uv_pipe_init(loop, &pipe_stdout_parent, 0) == 0); ASSERT(mpipe(fds_stdin) != -1); ASSERT(mpipe(fds_stdout) != -1); ASSERT(uv_pipe_open(&pipe_stdin_child, fds_stdin[0]) == 0); ASSERT(uv_pipe_open(&pipe_stdout_child, fds_stdout[1]) == 0); ASSERT(uv_pipe_open(&pipe_stdin_parent, fds_stdin[1]) == 0); ASSERT(uv_pipe_open(&pipe_stdout_parent, fds_stdout[0]) == 0); child_stdio[0].flags = UV_INHERIT_STREAM; child_stdio[0].data.stream = (uv_stream_t *)&pipe_stdin_child; child_stdio[1].flags = UV_INHERIT_STREAM; child_stdio[1].data.stream = (uv_stream_t *)&pipe_stdout_child; options.stdio = child_stdio; options.stdio_count = 2; ASSERT(uv_spawn(loop, &child_req, &options) == 0); uv_close((uv_handle_t*)&pipe_stdin_child, NULL); uv_close((uv_handle_t*)&pipe_stdout_child, NULL); buf = uv_buf_init((char*)ubuf, sizeof ubuf); for (i = 0; i < sizeof ubuf; ++i) ubuf[i] = i & 255u; memset(output, 0, sizeof ubuf); r = uv_write(&write_req, (uv_stream_t*)&pipe_stdin_parent, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&pipe_stdout_parent, on_alloc, on_read); ASSERT(r == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 3); r = memcmp(ubuf, output, sizeof ubuf); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } /* Helper for child process of spawn_inherit_streams */ #ifndef _WIN32 int spawn_stdin_stdout(void) { char buf[1024]; char* pbuf; for (;;) { ssize_t r, w, c; do { r = read(0, buf, sizeof buf); } while (r == -1 && errno == EINTR); if (r == 0) { return 1; } ASSERT(r > 0); c = r; pbuf = buf; while (c) { do { w = write(1, pbuf, (size_t)c); } while (w == -1 && errno == EINTR); ASSERT(w >= 0); pbuf = pbuf + w; c = c - w; } } return 2; } #else int spawn_stdin_stdout(void) { char buf[1024]; char* pbuf; HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE); HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); ASSERT(h_stdin != INVALID_HANDLE_VALUE); ASSERT(h_stdout != INVALID_HANDLE_VALUE); for (;;) { DWORD n_read; DWORD n_written; DWORD to_write; if (!ReadFile(h_stdin, buf, sizeof buf, &n_read, NULL)) { ASSERT(GetLastError() == ERROR_BROKEN_PIPE); return 1; } to_write = n_read; pbuf = buf; while (to_write) { ASSERT(WriteFile(h_stdout, pbuf, to_write, &n_written, NULL)); to_write -= n_written; pbuf += n_written; } } return 2; } #endif /* !_WIN32 */ libuv-v1.8.0/test/test-stdio-over-pipes.c000066400000000000000000000150141263361716600203540ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static char exepath[1024]; static size_t exepath_size = 1024; static char* args[3]; static uv_process_options_t options; static int close_cb_called; static int exit_cb_called; static int on_read_cb_called; static int after_write_cb_called; static uv_pipe_t in; static uv_pipe_t out; static uv_loop_t* loop; #define OUTPUT_SIZE 1024 static char output[OUTPUT_SIZE]; static int output_used; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) { printf("exit_cb\n"); exit_cb_called++; ASSERT(exit_status == 0); ASSERT(term_signal == 0); uv_close((uv_handle_t*)process, close_cb); uv_close((uv_handle_t*)&in, close_cb); uv_close((uv_handle_t*)&out, close_cb); } static void init_process_options(char* test, uv_exit_cb exit_cb) { int r = uv_exepath(exepath, &exepath_size); ASSERT(r == 0); exepath[exepath_size] = '\0'; args[0] = exepath; args[1] = test; args[2] = NULL; options.file = exepath; options.args = args; options.exit_cb = exit_cb; } static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = output + output_used; buf->len = OUTPUT_SIZE - output_used; } static void after_write(uv_write_t* req, int status) { if (status) { fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); ASSERT(0); } /* Free the read/write buffer and the request */ free(req); after_write_cb_called++; } static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* rdbuf) { uv_write_t* req; uv_buf_t wrbuf; int r; ASSERT(nread > 0 || nread == UV_EOF); if (nread > 0) { output_used += nread; if (output_used == 12) { ASSERT(memcmp("hello world\n", output, 12) == 0); wrbuf = uv_buf_init(output, output_used); req = malloc(sizeof(*req)); r = uv_write(req, (uv_stream_t*)&in, &wrbuf, 1, after_write); ASSERT(r == 0); } } on_read_cb_called++; } TEST_IMPL(stdio_over_pipes) { int r; uv_process_t process; uv_stdio_container_t stdio[2]; loop = uv_default_loop(); init_process_options("stdio_over_pipes_helper", exit_cb); uv_pipe_init(loop, &out, 0); uv_pipe_init(loop, &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; options.stdio[0].data.stream = (uv_stream_t*)∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; options.stdio[1].data.stream = (uv_stream_t*)&out; options.stdio_count = 2; r = uv_spawn(loop, &process, &options); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(on_read_cb_called > 1); ASSERT(after_write_cb_called == 1); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 3); ASSERT(memcmp("hello world\n", output, 12) == 0); ASSERT(output_used == 12); MAKE_VALGRIND_HAPPY(); return 0; } /* Everything here runs in a child process. */ static int on_pipe_read_called; static int after_write_called; static uv_pipe_t stdin_pipe; static uv_pipe_t stdout_pipe; static void on_pipe_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ASSERT(nread > 0); ASSERT(memcmp("hello world\n", buf->base, nread) == 0); on_pipe_read_called++; free(buf->base); uv_close((uv_handle_t*)&stdin_pipe, close_cb); uv_close((uv_handle_t*)&stdout_pipe, close_cb); } static void after_pipe_write(uv_write_t* req, int status) { ASSERT(status == 0); after_write_called++; } static void on_read_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } int stdio_over_pipes_helper(void) { /* Write several buffers to test that the write order is preserved. */ char* buffers[] = { "he", "ll", "o ", "wo", "rl", "d", "\n" }; uv_write_t write_req[ARRAY_SIZE(buffers)]; uv_buf_t buf[ARRAY_SIZE(buffers)]; unsigned int i; int r; uv_loop_t* loop = uv_default_loop(); ASSERT(UV_NAMED_PIPE == uv_guess_handle(0)); ASSERT(UV_NAMED_PIPE == uv_guess_handle(1)); r = uv_pipe_init(loop, &stdin_pipe, 0); ASSERT(r == 0); r = uv_pipe_init(loop, &stdout_pipe, 0); ASSERT(r == 0); uv_pipe_open(&stdin_pipe, 0); uv_pipe_open(&stdout_pipe, 1); /* Unref both stdio handles to make sure that all writes complete. */ uv_unref((uv_handle_t*)&stdin_pipe); uv_unref((uv_handle_t*)&stdout_pipe); for (i = 0; i < ARRAY_SIZE(buffers); i++) { buf[i] = uv_buf_init((char*)buffers[i], strlen(buffers[i])); } for (i = 0; i < ARRAY_SIZE(buffers); i++) { r = uv_write(&write_req[i], (uv_stream_t*)&stdout_pipe, &buf[i], 1, after_pipe_write); ASSERT(r == 0); } uv_run(loop, UV_RUN_DEFAULT); ASSERT(after_write_called == 7); ASSERT(on_pipe_read_called == 0); ASSERT(close_cb_called == 0); uv_ref((uv_handle_t*)&stdout_pipe); uv_ref((uv_handle_t*)&stdin_pipe); r = uv_read_start((uv_stream_t*)&stdin_pipe, on_read_alloc, on_pipe_read); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(after_write_called == 7); ASSERT(on_pipe_read_called == 1); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-bind-error.c000066400000000000000000000125161263361716600177760ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } TEST_IMPL(tcp_bind_error_addrinuse) { struct sockaddr_in addr; uv_tcp_t server1, server2; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server1); ASSERT(r == 0); r = uv_tcp_bind(&server1, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_tcp_init(uv_default_loop(), &server2); ASSERT(r == 0); r = uv_tcp_bind(&server2, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind_error_addrnotavail_1) { struct sockaddr_in addr; uv_tcp_t server; int r; ASSERT(0 == uv_ip4_addr("127.255.255.255", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); /* It seems that Linux is broken here - bind succeeds. */ r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0 || r == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind_error_addrnotavail_2) { struct sockaddr_in addr; uv_tcp_t server; int r; ASSERT(0 == uv_ip4_addr("4.4.4.4", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind_error_fault) { char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; struct sockaddr_in* garbage_addr; uv_tcp_t server; int r; garbage_addr = (struct sockaddr_in*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) garbage_addr, 0); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } /* Notes: On Linux uv_bind(server, NULL) will segfault the program. */ TEST_IMPL(tcp_bind_error_inval) { struct sockaddr_in addr1; struct sockaddr_in addr2; uv_tcp_t server; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr1)); ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT_2, &addr2)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr1, 0); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr2, 0); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind_localhost_ok) { struct sockaddr_in addr; uv_tcp_t server; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind_invalid_flags) { struct sockaddr_in addr; uv_tcp_t server; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, UV_TCP_IPV6ONLY); ASSERT(r == UV_EINVAL); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_listen_without_bind) { int r; uv_tcp_t server; r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server, 128, NULL); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-bind6-error.c000066400000000000000000000107441263361716600200650ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } TEST_IMPL(tcp_bind6_error_addrinuse) { struct sockaddr_in6 addr; uv_tcp_t server1, server2; int r; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server1); ASSERT(r == 0); r = uv_tcp_bind(&server1, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_tcp_init(uv_default_loop(), &server2); ASSERT(r == 0); r = uv_tcp_bind(&server2, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind6_error_addrnotavail) { struct sockaddr_in6 addr; uv_tcp_t server; int r; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("4:4:4:4:4:4:4:4", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind6_error_fault) { char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; struct sockaddr_in6* garbage_addr; uv_tcp_t server; int r; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); garbage_addr = (struct sockaddr_in6*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) garbage_addr, 0); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } /* Notes: On Linux uv_bind6(server, NULL) will segfault the program. */ TEST_IMPL(tcp_bind6_error_inval) { struct sockaddr_in6 addr1; struct sockaddr_in6 addr2; uv_tcp_t server; int r; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr1)); ASSERT(0 == uv_ip6_addr("::", TEST_PORT_2, &addr2)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr1, 0); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr2, 0); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_bind6_localhost_ok) { struct sockaddr_in6 addr; uv_tcp_t server; int r; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-close-accept.c000066400000000000000000000140431263361716600202720ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* this test is Unix only */ #ifndef _WIN32 #include "uv.h" #include "task.h" #include #include static struct sockaddr_in addr; static uv_tcp_t tcp_server; static uv_tcp_t tcp_outgoing[2]; static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; static uv_tcp_t tcp_check; static uv_connect_t tcp_check_req; static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; static unsigned int got_connections; static unsigned int close_cb_called; static unsigned int write_cb_called; static unsigned int read_cb_called; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); write_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { unsigned int i; uv_buf_t buf; uv_stream_t* outgoing; if (req == &tcp_check_req) { ASSERT(status != 0); /* Close check and incoming[0], time to finish test */ uv_close((uv_handle_t*) &tcp_incoming[0], close_cb); uv_close((uv_handle_t*) &tcp_check, close_cb); return; } ASSERT(status == 0); ASSERT(connect_reqs <= req); ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); i = req - connect_reqs; buf = uv_buf_init("x", 1); outgoing = (uv_stream_t*) &tcp_outgoing[i]; ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); } static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static char slab[1]; buf->base = slab; buf->len = sizeof(slab); } static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { uv_loop_t* loop; unsigned int i; /* Only first stream should receive read events */ ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]); ASSERT(0 == uv_read_stop(stream)); ASSERT(1 == nread); loop = stream->loop; read_cb_called++; /* Close all active incomings, except current one */ for (i = 1; i < got_connections; i++) uv_close((uv_handle_t*) &tcp_incoming[i], close_cb); /* Create new fd that should be one of the closed incomings */ ASSERT(0 == uv_tcp_init(loop, &tcp_check)); ASSERT(0 == uv_tcp_connect(&tcp_check_req, &tcp_check, (const struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); /* Close server, so no one will connect to it */ uv_close((uv_handle_t*) &tcp_server, close_cb); } static void connection_cb(uv_stream_t* server, int status) { unsigned int i; uv_tcp_t* incoming; ASSERT(server == (uv_stream_t*) &tcp_server); /* Ignore tcp_check connection */ if (got_connections == ARRAY_SIZE(tcp_incoming)) return; /* Accept everyone */ incoming = &tcp_incoming[got_connections++]; ASSERT(0 == uv_tcp_init(server->loop, incoming)); ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); if (got_connections != ARRAY_SIZE(tcp_incoming)) return; /* Once all clients are accepted - start reading */ for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { incoming = &tcp_incoming[i]; ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); } } TEST_IMPL(tcp_close_accept) { unsigned int i; uv_loop_t* loop; uv_tcp_t* client; /* * A little explanation of what goes on below: * * We'll create server and connect to it using two clients, each writing one * byte once connected. * * When all clients will be accepted by server - we'll start reading from them * and, on first client's first byte, will close second client and server. * After that, we'll immediately initiate new connection to server using * tcp_check handle (thus, reusing fd from second client). * * In this situation uv__io_poll()'s event list should still contain read * event for second client, and, if not cleaned up properly, `tcp_check` will * receive stale event of second incoming and invoke `connect_cb` with zero * status. */ loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(loop, &tcp_server)); ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0)); ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, ARRAY_SIZE(tcp_outgoing), connection_cb)); for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { client = tcp_outgoing + i; ASSERT(0 == uv_tcp_init(loop, client)); ASSERT(0 == uv_tcp_connect(&connect_reqs[i], client, (const struct sockaddr*) &addr, connect_cb)); } uv_run(loop, UV_RUN_DEFAULT); ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); ASSERT(1 == read_cb_called); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !_WIN32 */ libuv-v1.8.0/test/test-tcp-close-while-connecting.c000066400000000000000000000053231263361716600222710ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_timer_t timer1_handle; static uv_timer_t timer2_handle; static uv_tcp_t tcp_handle; static int connect_cb_called; static int timer1_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { ASSERT(status == UV_ECANCELED); uv_timer_stop(&timer2_handle); connect_cb_called++; } static void timer1_cb(uv_timer_t* handle) { uv_close((uv_handle_t*)handle, close_cb); uv_close((uv_handle_t*)&tcp_handle, close_cb); timer1_cb_called++; } static void timer2_cb(uv_timer_t* handle) { ASSERT(0 && "should not be called"); } TEST_IMPL(tcp_close_while_connecting) { uv_connect_t connect_req; struct sockaddr_in addr; uv_loop_t* loop; int r; loop = uv_default_loop(); ASSERT(0 == uv_ip4_addr("1.2.3.4", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(loop, &tcp_handle)); r = uv_tcp_connect(&connect_req, &tcp_handle, (const struct sockaddr*) &addr, connect_cb); if (r == UV_ENETUNREACH) RETURN_SKIP("Network unreachable."); ASSERT(r == 0); ASSERT(0 == uv_timer_init(loop, &timer1_handle)); ASSERT(0 == uv_timer_start(&timer1_handle, timer1_cb, 50, 0)); ASSERT(0 == uv_timer_init(loop, &timer2_handle)); ASSERT(0 == uv_timer_start(&timer2_handle, timer2_cb, 86400 * 1000, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(connect_cb_called == 1); ASSERT(timer1_cb_called == 1); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-close.c000066400000000000000000000072001263361716600170320ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include /* memset */ #define NUM_WRITE_REQS 32 static uv_tcp_t tcp_handle; static uv_connect_t connect_req; static int write_cb_called; static int close_cb_called; static void connect_cb(uv_connect_t* req, int status); static void write_cb(uv_write_t* req, int status); static void close_cb(uv_handle_t* handle); static void connect_cb(uv_connect_t* conn_req, int status) { uv_write_t* req; uv_buf_t buf; int i, r; buf = uv_buf_init("PING", 4); for (i = 0; i < NUM_WRITE_REQS; i++) { req = malloc(sizeof *req); ASSERT(req != NULL); r = uv_write(req, (uv_stream_t*)&tcp_handle, &buf, 1, write_cb); ASSERT(r == 0); } uv_close((uv_handle_t*)&tcp_handle, close_cb); } static void write_cb(uv_write_t* req, int status) { /* write callbacks should run before the close callback */ ASSERT(close_cb_called == 0); ASSERT(req->handle == (uv_stream_t*)&tcp_handle); write_cb_called++; free(req); } static void close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*)&tcp_handle); close_cb_called++; } static void connection_cb(uv_stream_t* server, int status) { ASSERT(status == 0); } static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, handle); ASSERT(r == 0); r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)handle, 128, connection_cb); ASSERT(r == 0); uv_unref((uv_handle_t*)handle); } /* Check that pending write requests have their callbacks * invoked when the handle is closed. */ TEST_IMPL(tcp_close) { struct sockaddr_in addr; uv_tcp_t tcp_server; uv_loop_t* loop; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); /* We can't use the echo server, it doesn't handle ECONNRESET. */ start_server(loop, &tcp_server); r = uv_tcp_init(loop, &tcp_handle); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &tcp_handle, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); ASSERT(write_cb_called == 0); ASSERT(close_cb_called == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); printf("%d of %d write reqs seen\n", write_cb_called, NUM_WRITE_REQS); ASSERT(write_cb_called == NUM_WRITE_REQS); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-connect-error-after-write.c000066400000000000000000000056021263361716600227400ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include static int connect_cb_called; static int write_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { ASSERT(status < 0); connect_cb_called++; uv_close((uv_handle_t*)req->handle, close_cb); } static void write_cb(uv_write_t* req, int status) { ASSERT(status < 0); write_cb_called++; } /* * Try to connect to an address on which nothing listens, get ECONNREFUSED * (uv errno 12) and get connect_cb() called once with status != 0. * Related issue: https://github.com/joyent/libuv/issues/443 */ TEST_IMPL(tcp_connect_error_after_write) { uv_connect_t connect_req; struct sockaddr_in addr; uv_write_t write_req; uv_tcp_t conn; uv_buf_t buf; int r; #ifdef _WIN32 fprintf(stderr, "This test is disabled on Windows for now.\n"); fprintf(stderr, "See https://github.com/joyent/libuv/issues/444\n"); return 0; /* windows slackers... */ #endif ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); buf = uv_buf_init("TEST", 4); r = uv_tcp_init(uv_default_loop(), &conn); ASSERT(r == 0); r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); ASSERT(r == UV_EBADF); r = uv_tcp_connect(&connect_req, &conn, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-connect-error.c000066400000000000000000000042631263361716600205130ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int connect_cb_called = 0; static int close_cb_called = 0; static void connect_cb(uv_connect_t* handle, int status) { ASSERT(handle != NULL); connect_cb_called++; } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } TEST_IMPL(tcp_connect_error_fault) { const char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; const struct sockaddr_in* garbage_addr; uv_tcp_t server; int r; uv_connect_t req; garbage_addr = (const struct sockaddr_in*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_connect(&req, &server, (const struct sockaddr*) garbage_addr, connect_cb); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 0); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-connect-timeout.c000066400000000000000000000052471263361716600210530ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int connect_cb_called; static int close_cb_called; static uv_connect_t connect_req; static uv_timer_t timer; static uv_tcp_t conn; static void connect_cb(uv_connect_t* req, int status); static void timer_cb(uv_timer_t* handle); static void close_cb(uv_handle_t* handle); static void connect_cb(uv_connect_t* req, int status) { ASSERT(req == &connect_req); ASSERT(status == UV_ECANCELED); connect_cb_called++; } static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer); uv_close((uv_handle_t*)&conn, close_cb); uv_close((uv_handle_t*)&timer, close_cb); } static void close_cb(uv_handle_t* handle) { ASSERT(handle == (uv_handle_t*)&conn || handle == (uv_handle_t*)&timer); close_cb_called++; } /* Verify that connecting to an unreachable address or port doesn't hang * the event loop. */ TEST_IMPL(tcp_connect_timeout) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("8.8.8.8", 9999, &addr)); r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 50, 0); ASSERT(r == 0); r = uv_tcp_init(uv_default_loop(), &conn); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &conn, (const struct sockaddr*) &addr, connect_cb); if (r == UV_ENETUNREACH) RETURN_SKIP("Network unreachable."); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-connect6-error.c000066400000000000000000000042641263361716600206020ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static int connect_cb_called = 0; static int close_cb_called = 0; static void connect_cb(uv_connect_t* handle, int status) { ASSERT(handle != NULL); connect_cb_called++; } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } TEST_IMPL(tcp_connect6_error_fault) { const char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; const struct sockaddr_in6* garbage_addr; uv_tcp_t server; int r; uv_connect_t req; garbage_addr = (const struct sockaddr_in6*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_connect(&req, &server, (const struct sockaddr*) garbage_addr, connect_cb); ASSERT(r == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 0); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-create-socket-early.c000066400000000000000000000122371263361716600215760ustar00rootroot00000000000000/* Copyright (c) 2015 Saúl Ibarra Corretgé . * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #ifdef _WIN32 # define INVALID_FD (INVALID_HANDLE_VALUE) #else # define INVALID_FD (-1) #endif static void on_connect(uv_connect_t* req, int status) { ASSERT(status == 0); uv_close((uv_handle_t*) req->handle, NULL); } static void on_connection(uv_stream_t* server, int status) { uv_tcp_t* handle; int r; ASSERT(status == 0); handle = malloc(sizeof(*handle)); ASSERT(handle != NULL); r = uv_tcp_init_ex(server->loop, handle, AF_INET); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)handle); ASSERT(r == UV_EBUSY); uv_close((uv_handle_t*) server, NULL); uv_close((uv_handle_t*) handle, (uv_close_cb)free); } static void tcp_listener(uv_loop_t* loop, uv_tcp_t* server) { struct sockaddr_in addr; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(loop, server); ASSERT(r == 0); r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*) server, 128, on_connection); ASSERT(r == 0); } static void tcp_connector(uv_loop_t* loop, uv_tcp_t* client, uv_connect_t* req) { struct sockaddr_in server_addr; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); r = uv_tcp_init(loop, client); ASSERT(r == 0); r = uv_tcp_connect(req, client, (const struct sockaddr*) &server_addr, on_connect); ASSERT(r == 0); } TEST_IMPL(tcp_create_early) { struct sockaddr_in addr; struct sockaddr_in sockname; uv_tcp_t client; uv_os_fd_t fd; int r, namelen; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init_ex(uv_default_loop(), &client, AF_INET); ASSERT(r == 0); r = uv_fileno((const uv_handle_t*) &client, &fd); ASSERT(r == 0); ASSERT(fd != INVALID_FD); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 namelen = sizeof sockname; r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); ASSERT(r == 0); ASSERT(sockname.sin_family == AF_INET); #endif r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); namelen = sizeof sockname; r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); ASSERT(r == 0); ASSERT(memcmp(&addr.sin_addr, &sockname.sin_addr, sizeof(addr.sin_addr)) == 0); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_create_early_bad_bind) { struct sockaddr_in addr; uv_tcp_t client; uv_os_fd_t fd; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init_ex(uv_default_loop(), &client, AF_INET6); ASSERT(r == 0); r = uv_fileno((const uv_handle_t*) &client, &fd); ASSERT(r == 0); ASSERT(fd != INVALID_FD); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 { int namelen; struct sockaddr_in6 sockname; namelen = sizeof sockname; r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); ASSERT(r == 0); ASSERT(sockname.sin6_family == AF_INET6); } #endif r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); #ifndef _WIN32 ASSERT(r == UV_EINVAL); #else ASSERT(r == UV_EFAULT); #endif uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_create_early_bad_domain) { uv_tcp_t client; int r; r = uv_tcp_init_ex(uv_default_loop(), &client, 47); ASSERT(r == UV_EINVAL); r = uv_tcp_init_ex(uv_default_loop(), &client, 1024); ASSERT(r == UV_EINVAL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_create_early_accept) { uv_tcp_t client, server; uv_connect_t connect_req; tcp_listener(uv_default_loop(), &server); tcp_connector(uv_default_loop(), &client, &connect_req); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-flags.c000066400000000000000000000031411263361716600170210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include TEST_IMPL(tcp_flags) { uv_loop_t* loop; uv_tcp_t handle; int r; loop = uv_default_loop(); r = uv_tcp_init(loop, &handle); ASSERT(r == 0); r = uv_tcp_nodelay(&handle, 1); ASSERT(r == 0); r = uv_tcp_keepalive(&handle, 1, 60); ASSERT(r == 0); uv_close((uv_handle_t*)&handle, NULL); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-oob.c000066400000000000000000000075421263361716600165150ustar00rootroot00000000000000/* Copyright Fedor Indutny. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if !defined(_WIN32) #include "uv.h" #include "task.h" #include #include #include static uv_tcp_t server_handle; static uv_tcp_t client_handle; static uv_tcp_t peer_handle; static uv_idle_t idle; static uv_connect_t connect_req; static int ticks; static const int kMaxTicks = 10; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char storage[1024]; *buf = uv_buf_init(storage, sizeof(storage)); } static void idle_cb(uv_idle_t* idle) { if (++ticks < kMaxTicks) return; uv_close((uv_handle_t*) &server_handle, NULL); uv_close((uv_handle_t*) &client_handle, NULL); uv_close((uv_handle_t*) &peer_handle, NULL); uv_close((uv_handle_t*) idle, NULL); } static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { ASSERT(nread > 0); ASSERT(0 == uv_idle_start(&idle, idle_cb)); } static void connect_cb(uv_connect_t* req, int status) { ASSERT(req->handle == (uv_stream_t*) &client_handle); ASSERT(0 == status); } static void connection_cb(uv_stream_t* handle, int status) { int r; uv_os_fd_t fd; ASSERT(0 == status); ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); /* Send some OOB data */ ASSERT(0 == uv_fileno((uv_handle_t*) &client_handle, &fd)); ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &client_handle, 1)); /* The problem triggers only on a second message, it seem that xnu is not * triggering `kevent()` for the first one */ do { r = send(fd, "hello", 5, MSG_OOB); } while (r < 0 && errno == EINTR); ASSERT(5 == r); do { r = send(fd, "hello", 5, MSG_OOB); } while (r < 0 && errno == EINTR); ASSERT(5 == r); ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &client_handle, 0)); } TEST_IMPL(tcp_oob) { struct sockaddr_in addr; uv_loop_t* loop; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); ASSERT(0 == uv_tcp_init(loop, &server_handle)); ASSERT(0 == uv_tcp_init(loop, &client_handle)); ASSERT(0 == uv_tcp_init(loop, &peer_handle)); ASSERT(0 == uv_idle_init(loop, &idle)); ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); /* Ensure two separate packets */ ASSERT(0 == uv_tcp_nodelay(&client_handle, 1)); ASSERT(0 == uv_tcp_connect(&connect_req, &client_handle, (const struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(ticks == kMaxTicks); MAKE_VALGRIND_HAPPY(); return 0; } #endif libuv-v1.8.0/test/test-tcp-open.c000066400000000000000000000115421263361716600166720ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #ifndef _WIN32 # include #endif static int shutdown_cb_called = 0; static int connect_cb_called = 0; static int write_cb_called = 0; static int close_cb_called = 0; static uv_connect_t connect_req; static uv_shutdown_t shutdown_req; static uv_write_t write_req; static void startup(void) { #ifdef _WIN32 struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); ASSERT(r == 0); #endif } static uv_os_sock_t create_tcp_socket(void) { uv_os_sock_t sock; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); #ifdef _WIN32 ASSERT(sock != INVALID_SOCKET); #else ASSERT(sock >= 0); #endif #ifndef _WIN32 { /* Allow reuse of the port. */ int yes = 1; int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); ASSERT(r == 0); } #endif return sock; } static void close_socket(uv_os_sock_t sock) { int r; #ifdef _WIN32 r = closesocket(sock); #else r = close(sock); #endif ASSERT(r == 0); } static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(req == &shutdown_req); ASSERT(status == 0); /* Now we wait for the EOF */ shutdown_cb_called++; } static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ASSERT(tcp != NULL); if (nread >= 0) { ASSERT(nread == 4); ASSERT(memcmp("PING", buf->base, nread) == 0); } else { ASSERT(nread == UV_EOF); printf("GOT EOF\n"); uv_close((uv_handle_t*)tcp, close_cb); } } static void write_cb(uv_write_t* req, int status) { ASSERT(req != NULL); if (status) { fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); ASSERT(0); } write_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { uv_buf_t buf = uv_buf_init("PING", 4); uv_stream_t* stream; int r; ASSERT(req == &connect_req); ASSERT(status == 0); stream = req->handle; connect_cb_called++; r = uv_write(&write_req, stream, &buf, 1, write_cb); ASSERT(r == 0); /* Shutdown on drain. */ r = uv_shutdown(&shutdown_req, stream, shutdown_cb); ASSERT(r == 0); /* Start reading */ r = uv_read_start(stream, alloc_cb, read_cb); ASSERT(r == 0); } TEST_IMPL(tcp_open) { struct sockaddr_in addr; uv_tcp_t client; uv_os_sock_t sock; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_tcp_socket(); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_tcp_open(&client, sock); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(shutdown_cb_called == 1); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tcp_open_twice) { uv_tcp_t client; uv_os_sock_t sock1, sock2; int r; startup(); sock1 = create_tcp_socket(); sock2 = create_tcp_socket(); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_tcp_open(&client, sock1); ASSERT(r == 0); r = uv_tcp_open(&client, sock2); ASSERT(r == UV_EBUSY); close_socket(sock2); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-read-stop.c000066400000000000000000000051641263361716600176320ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_timer_t timer_handle; static uv_tcp_t tcp_handle; static uv_write_t write_req; static void fail_cb(void) { ASSERT(0 && "fail_cb called"); } static void write_cb(uv_write_t* req, int status) { uv_close((uv_handle_t*) &timer_handle, NULL); uv_close((uv_handle_t*) &tcp_handle, NULL); } static void timer_cb(uv_timer_t* handle) { uv_buf_t buf = uv_buf_init("PING", 4); ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &tcp_handle, &buf, 1, write_cb)); ASSERT(0 == uv_read_stop((uv_stream_t*) &tcp_handle)); } static void connect_cb(uv_connect_t* req, int status) { ASSERT(0 == status); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_handle, (uv_alloc_cb) fail_cb, (uv_read_cb) fail_cb)); } TEST_IMPL(tcp_read_stop) { uv_connect_t connect_req; struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); ASSERT(0 == uv_tcp_init(uv_default_loop(), &tcp_handle)); ASSERT(0 == uv_tcp_connect(&connect_req, &tcp_handle, (const struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-shutdown-after-write.c000066400000000000000000000071461263361716600220400ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static void write_cb(uv_write_t* req, int status); static void shutdown_cb(uv_shutdown_t* req, int status); static uv_tcp_t conn; static uv_timer_t timer; static uv_connect_t connect_req; static uv_write_t write_req; static uv_shutdown_t shutdown_req; static int connect_cb_called; static int write_cb_called; static int shutdown_cb_called; static int conn_close_cb_called; static int timer_close_cb_called; static void close_cb(uv_handle_t* handle) { if (handle == (uv_handle_t*)&conn) conn_close_cb_called++; else if (handle == (uv_handle_t*)&timer) timer_close_cb_called++; else ASSERT(0 && "bad handle in close_cb"); } static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[64]; buf->base = slab; buf->len = sizeof(slab); } static void timer_cb(uv_timer_t* handle) { uv_buf_t buf; int r; uv_close((uv_handle_t*)handle, close_cb); buf = uv_buf_init("TEST", 4); r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); ASSERT(r == 0); r = uv_shutdown(&shutdown_req, (uv_stream_t*)&conn, shutdown_cb); ASSERT(r == 0); } static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { } static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT(status == 0); connect_cb_called++; r = uv_read_start((uv_stream_t*)&conn, alloc_cb, read_cb); ASSERT(r == 0); } static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); write_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); shutdown_cb_called++; uv_close((uv_handle_t*)&conn, close_cb); } TEST_IMPL(tcp_shutdown_after_write) { struct sockaddr_in addr; uv_loop_t* loop; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 125, 0); ASSERT(r == 0); r = uv_tcp_init(loop, &conn); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &conn, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == 1); ASSERT(shutdown_cb_called == 1); ASSERT(conn_close_cb_called == 1); ASSERT(timer_close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-try-write.c000066400000000000000000000072171263361716600177030ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define MAX_BYTES 1024 * 1024 static uv_tcp_t server; static uv_tcp_t client; static uv_tcp_t incoming; static int connect_cb_called; static int close_cb_called; static int connection_cb_called; static int bytes_read; static int bytes_written; static void close_cb(uv_handle_t* handle) { close_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { int r; uv_buf_t buf; ASSERT(status == 0); connect_cb_called++; do { buf = uv_buf_init("PING", 4); r = uv_try_write((uv_stream_t*) &client, &buf, 1); ASSERT(r > 0 || r == UV_EAGAIN); if (r > 0) { bytes_written += r; break; } } while (1); do { buf = uv_buf_init("", 0); r = uv_try_write((uv_stream_t*) &client, &buf, 1); } while (r != 0); uv_close((uv_handle_t*) &client, close_cb); } static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static char base[1024]; buf->base = base; buf->len = sizeof(base); } static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { if (nread < 0) { uv_close((uv_handle_t*) tcp, close_cb); uv_close((uv_handle_t*) &server, close_cb); return; } bytes_read += nread; } static void connection_cb(uv_stream_t* tcp, int status) { ASSERT(status == 0); ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); connection_cb_called++; ASSERT(0 == uv_read_start((uv_stream_t*) &incoming, alloc_cb, read_cb)); } static void start_server(void) { struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); } TEST_IMPL(tcp_try_write) { uv_connect_t connect_req; struct sockaddr_in addr; start_server(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); ASSERT(0 == uv_tcp_connect(&connect_req, &client, (struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(connect_cb_called == 1); ASSERT(close_cb_called == 3); ASSERT(connection_cb_called == 1); ASSERT(bytes_read == bytes_written); ASSERT(bytes_written > 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-unexpected-read.c000066400000000000000000000075631263361716600210160ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_check_t check_handle; static uv_timer_t timer_handle; static uv_tcp_t server_handle; static uv_tcp_t client_handle; static uv_tcp_t peer_handle; static uv_write_t write_req; static uv_connect_t connect_req; static unsigned long ticks; /* event loop ticks */ static void check_cb(uv_check_t* handle) { ticks++; } static void timer_cb(uv_timer_t* handle) { uv_close((uv_handle_t*) &check_handle, NULL); uv_close((uv_handle_t*) &timer_handle, NULL); uv_close((uv_handle_t*) &server_handle, NULL); uv_close((uv_handle_t*) &client_handle, NULL); uv_close((uv_handle_t*) &peer_handle, NULL); } static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { ASSERT(0 && "alloc_cb should not have been called"); } static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { ASSERT(0 && "read_cb should not have been called"); } static void connect_cb(uv_connect_t* req, int status) { ASSERT(req->handle == (uv_stream_t*) &client_handle); ASSERT(0 == status); } static void write_cb(uv_write_t* req, int status) { ASSERT(req->handle == (uv_stream_t*) &peer_handle); ASSERT(0 == status); } static void connection_cb(uv_stream_t* handle, int status) { uv_buf_t buf; buf = uv_buf_init("PING", 4); ASSERT(0 == status); ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &peer_handle, &buf, 1, write_cb)); } TEST_IMPL(tcp_unexpected_read) { struct sockaddr_in addr; uv_loop_t* loop; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1000, 0)); ASSERT(0 == uv_check_init(loop, &check_handle)); ASSERT(0 == uv_check_start(&check_handle, check_cb)); ASSERT(0 == uv_tcp_init(loop, &server_handle)); ASSERT(0 == uv_tcp_init(loop, &client_handle)); ASSERT(0 == uv_tcp_init(loop, &peer_handle)); ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); ASSERT(0 == uv_tcp_connect(&connect_req, &client_handle, (const struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); /* This is somewhat inexact but the idea is that the event loop should not * start busy looping when the server sends a message and the client isn't * reading. */ ASSERT(ticks <= 20); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-write-after-connect.c000066400000000000000000000042751263361716600216160ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef _WIN32 #include "uv.h" #include "task.h" uv_loop_t loop; uv_tcp_t tcp_client; uv_connect_t connection_request; uv_write_t write_request; uv_buf_t buf = { "HELLO", 4 }; static void write_cb(uv_write_t *req, int status) { ASSERT(status == UV_ECANCELED); uv_close((uv_handle_t*) req->handle, NULL); } static void connect_cb(uv_connect_t *req, int status) { ASSERT(status == UV_ECONNREFUSED); } TEST_IMPL(tcp_write_after_connect) { struct sockaddr_in sa; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); ASSERT(0 == uv_loop_init(&loop)); ASSERT(0 == uv_tcp_init(&loop, &tcp_client)); ASSERT(0 == uv_tcp_connect(&connection_request, &tcp_client, (const struct sockaddr *) &sa, connect_cb)); ASSERT(0 == uv_write(&write_request, (uv_stream_t *)&tcp_client, &buf, 1, write_cb)); uv_run(&loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } #endif libuv-v1.8.0/test/test-tcp-write-fail.c000066400000000000000000000056251263361716600200010ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #ifndef _WIN32 # include #endif static int connect_cb_called = 0; static int write_cb_called = 0; static int close_cb_called = 0; static uv_connect_t connect_req; static uv_write_t write_req; static void close_socket(uv_tcp_t* sock) { uv_os_fd_t fd; int r; r = uv_fileno((uv_handle_t*)sock, &fd); ASSERT(r == 0); #ifdef _WIN32 r = closesocket((uv_os_sock_t)fd); #else r = close(fd); #endif ASSERT(r == 0); } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void write_cb(uv_write_t* req, int status) { ASSERT(req != NULL); ASSERT(status != 0); fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); write_cb_called++; uv_close((uv_handle_t*)(req->handle), close_cb); } static void connect_cb(uv_connect_t* req, int status) { uv_buf_t buf; uv_stream_t* stream; int r; ASSERT(req == &connect_req); ASSERT(status == 0); stream = req->handle; connect_cb_called++; /* close the socket, the hard way */ close_socket((uv_tcp_t*)stream); buf = uv_buf_init("hello\n", 6); r = uv_write(&write_req, stream, &buf, 1, write_cb); ASSERT(r == 0); } TEST_IMPL(tcp_write_fail) { struct sockaddr_in addr; uv_tcp_t client; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-write-queue-order.c000066400000000000000000000074771263361716600213320ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "uv.h" #include "task.h" #define REQ_COUNT 10000 static uv_timer_t timer; static uv_tcp_t server; static uv_tcp_t client; static uv_tcp_t incoming; static int connect_cb_called; static int close_cb_called; static int connection_cb_called; static int write_callbacks; static int write_cancelled_callbacks; static int write_error_callbacks; static uv_write_t write_requests[REQ_COUNT]; static void close_cb(uv_handle_t* handle) { close_cb_called++; } void timer_cb(uv_timer_t* handle) { uv_close((uv_handle_t*) &client, close_cb); uv_close((uv_handle_t*) &server, close_cb); uv_close((uv_handle_t*) &incoming, close_cb); } void write_cb(uv_write_t* req, int status) { if (status == 0) write_callbacks++; else if (status == UV_ECANCELED) write_cancelled_callbacks++; else write_error_callbacks++; } static void connect_cb(uv_connect_t* req, int status) { static char base[1024]; int r; int i; uv_buf_t buf; ASSERT(status == 0); connect_cb_called++; buf = uv_buf_init(base, sizeof(base)); for (i = 0; i < REQ_COUNT; i++) { r = uv_write(&write_requests[i], req->handle, &buf, 1, write_cb); ASSERT(r == 0); } } static void connection_cb(uv_stream_t* tcp, int status) { ASSERT(status == 0); ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); connection_cb_called++; } static void start_server(void) { struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); } TEST_IMPL(tcp_write_queue_order) { uv_connect_t connect_req; struct sockaddr_in addr; start_server(); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); ASSERT(0 == uv_tcp_connect(&connect_req, &client, (struct sockaddr*) &addr, connect_cb)); ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); ASSERT(0 == uv_timer_start(&timer, timer_cb, 100, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(connect_cb_called == 1); ASSERT(connection_cb_called == 1); ASSERT(write_callbacks > 0); ASSERT(write_cancelled_callbacks > 0); ASSERT(write_callbacks + write_error_callbacks + write_cancelled_callbacks == REQ_COUNT); ASSERT(close_cb_called == 3); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-write-to-half-open-connection.c000066400000000000000000000076211263361716600235120ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include static void connection_cb(uv_stream_t* server, int status); static void connect_cb(uv_connect_t* req, int status); static void write_cb(uv_write_t* req, int status); static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); static uv_tcp_t tcp_server; static uv_tcp_t tcp_client; static uv_tcp_t tcp_peer; /* client socket as accept()-ed by server */ static uv_connect_t connect_req; static uv_write_t write_req; static int write_cb_called; static int read_cb_called; static void connection_cb(uv_stream_t* server, int status) { int r; uv_buf_t buf; ASSERT(server == (uv_stream_t*)&tcp_server); ASSERT(status == 0); r = uv_tcp_init(server->loop, &tcp_peer); ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)&tcp_peer); ASSERT(r == 0); r = uv_read_start((uv_stream_t*)&tcp_peer, alloc_cb, read_cb); ASSERT(r == 0); buf.base = "hello\n"; buf.len = 6; r = uv_write(&write_req, (uv_stream_t*)&tcp_peer, &buf, 1, write_cb); ASSERT(r == 0); } static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[1024]; buf->base = slab; buf->len = sizeof(slab); } static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { if (nread < 0) { fprintf(stderr, "read_cb error: %s\n", uv_err_name(nread)); ASSERT(nread == UV_ECONNRESET || nread == UV_EOF); uv_close((uv_handle_t*)&tcp_server, NULL); uv_close((uv_handle_t*)&tcp_peer, NULL); } read_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { ASSERT(req == &connect_req); ASSERT(status == 0); /* Close the client. */ uv_close((uv_handle_t*)&tcp_client, NULL); } static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); write_cb_called++; } TEST_IMPL(tcp_write_to_half_open_connection) { struct sockaddr_in addr; uv_loop_t* loop; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); ASSERT(loop != NULL); r = uv_tcp_init(loop, &tcp_server); ASSERT(r == 0); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_listen((uv_stream_t*)&tcp_server, 1, connection_cb); ASSERT(r == 0); r = uv_tcp_init(loop, &tcp_client); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &tcp_client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(write_cb_called > 0); ASSERT(read_cb_called > 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tcp-writealot.c000066400000000000000000000107311263361716600177420ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #define WRITES 3 #define CHUNKS_PER_WRITE 4096 #define CHUNK_SIZE 10024 /* 10 kb */ #define TOTAL_BYTES (WRITES * CHUNKS_PER_WRITE * CHUNK_SIZE) static char* send_buffer; static int shutdown_cb_called = 0; static int connect_cb_called = 0; static int write_cb_called = 0; static int close_cb_called = 0; static size_t bytes_sent = 0; static size_t bytes_sent_done = 0; static size_t bytes_received_done = 0; static uv_connect_t connect_req; static uv_shutdown_t shutdown_req; static uv_write_t write_reqs[WRITES]; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = malloc(size); buf->len = size; } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { uv_tcp_t* tcp; ASSERT(req == &shutdown_req); ASSERT(status == 0); tcp = (uv_tcp_t*)(req->handle); /* The write buffer should be empty by now. */ ASSERT(tcp->write_queue_size == 0); /* Now we wait for the EOF */ shutdown_cb_called++; /* We should have had all the writes called already. */ ASSERT(write_cb_called == WRITES); } static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ASSERT(tcp != NULL); if (nread >= 0) { bytes_received_done += nread; } else { ASSERT(nread == UV_EOF); printf("GOT EOF\n"); uv_close((uv_handle_t*)tcp, close_cb); } free(buf->base); } static void write_cb(uv_write_t* req, int status) { ASSERT(req != NULL); if (status) { fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); ASSERT(0); } bytes_sent_done += CHUNKS_PER_WRITE * CHUNK_SIZE; write_cb_called++; } static void connect_cb(uv_connect_t* req, int status) { uv_buf_t send_bufs[CHUNKS_PER_WRITE]; uv_stream_t* stream; int i, j, r; ASSERT(req == &connect_req); ASSERT(status == 0); stream = req->handle; connect_cb_called++; /* Write a lot of data */ for (i = 0; i < WRITES; i++) { uv_write_t* write_req = write_reqs + i; for (j = 0; j < CHUNKS_PER_WRITE; j++) { send_bufs[j] = uv_buf_init(send_buffer + bytes_sent, CHUNK_SIZE); bytes_sent += CHUNK_SIZE; } r = uv_write(write_req, stream, send_bufs, CHUNKS_PER_WRITE, write_cb); ASSERT(r == 0); } /* Shutdown on drain. */ r = uv_shutdown(&shutdown_req, stream, shutdown_cb); ASSERT(r == 0); /* Start reading */ r = uv_read_start(stream, alloc_cb, read_cb); ASSERT(r == 0); } TEST_IMPL(tcp_writealot) { struct sockaddr_in addr; uv_tcp_t client; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); send_buffer = calloc(1, TOTAL_BYTES); ASSERT(send_buffer != NULL); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(shutdown_cb_called == 1); ASSERT(connect_cb_called == 1); ASSERT(write_cb_called == WRITES); ASSERT(close_cb_called == 1); ASSERT(bytes_sent == TOTAL_BYTES); ASSERT(bytes_sent_done == TOTAL_BYTES); ASSERT(bytes_received_done == TOTAL_BYTES); free(send_buffer); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-thread-equal.c000066400000000000000000000036071263361716600175240ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" uv_thread_t main_thread_id; uv_thread_t subthreads[2]; static void check_thread(void* arg) { uv_thread_t *thread_id = arg; uv_thread_t self_id = uv_thread_self(); ASSERT(uv_thread_equal(&main_thread_id, &self_id) == 0); *thread_id = uv_thread_self(); } TEST_IMPL(thread_equal) { uv_thread_t threads[2]; main_thread_id = uv_thread_self(); ASSERT(0 != uv_thread_equal(&main_thread_id, &main_thread_id)); ASSERT(0 == uv_thread_create(threads + 0, check_thread, subthreads + 0)); ASSERT(0 == uv_thread_create(threads + 1, check_thread, subthreads + 1)); ASSERT(0 == uv_thread_join(threads + 0)); ASSERT(0 == uv_thread_join(threads + 1)); ASSERT(0 == uv_thread_equal(subthreads + 0, subthreads + 1)); return 0; } libuv-v1.8.0/test/test-thread.c000066400000000000000000000121611263361716600164120ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include /* memset */ struct getaddrinfo_req { uv_thread_t thread_id; unsigned int counter; uv_loop_t* loop; uv_getaddrinfo_t handle; }; struct fs_req { uv_thread_t thread_id; unsigned int counter; uv_loop_t* loop; uv_fs_t handle; }; struct test_thread { uv_thread_t thread_id; volatile int thread_called; }; static void getaddrinfo_do(struct getaddrinfo_req* req); static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res); static void fs_do(struct fs_req* req); static void fs_cb(uv_fs_t* handle); static volatile int thread_called; static uv_key_t tls_key; static void getaddrinfo_do(struct getaddrinfo_req* req) { int r; r = uv_getaddrinfo(req->loop, &req->handle, getaddrinfo_cb, "localhost", NULL, NULL); ASSERT(r == 0); } static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { struct getaddrinfo_req* req; ASSERT(status == 0); req = container_of(handle, struct getaddrinfo_req, handle); uv_freeaddrinfo(res); if (--req->counter) getaddrinfo_do(req); } static void fs_do(struct fs_req* req) { int r; r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb); ASSERT(r == 0); } static void fs_cb(uv_fs_t* handle) { struct fs_req* req = container_of(handle, struct fs_req, handle); uv_fs_req_cleanup(handle); if (--req->counter) fs_do(req); } static void do_work(void* arg) { struct getaddrinfo_req getaddrinfo_reqs[16]; struct fs_req fs_reqs[16]; uv_loop_t* loop; size_t i; int r; struct test_thread* thread = arg; loop = malloc(sizeof *loop); ASSERT(loop != NULL); ASSERT(0 == uv_loop_init(loop)); for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) { struct getaddrinfo_req* req = getaddrinfo_reqs + i; req->counter = 16; req->loop = loop; getaddrinfo_do(req); } for (i = 0; i < ARRAY_SIZE(fs_reqs); i++) { struct fs_req* req = fs_reqs + i; req->counter = 16; req->loop = loop; fs_do(req); } r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(0 == uv_loop_close(loop)); free(loop); thread->thread_called = 1; } static void thread_entry(void* arg) { ASSERT(arg == (void *) 42); thread_called++; } TEST_IMPL(thread_create) { uv_thread_t tid; int r; r = uv_thread_create(&tid, thread_entry, (void *) 42); ASSERT(r == 0); r = uv_thread_join(&tid); ASSERT(r == 0); ASSERT(thread_called == 1); return 0; } /* Hilariously bad test name. Run a lot of tasks in the thread pool and verify * that each "finished" callback is run in its originating thread. */ TEST_IMPL(threadpool_multiple_event_loops) { struct test_thread threads[8]; size_t i; int r; memset(threads, 0, sizeof(threads)); for (i = 0; i < ARRAY_SIZE(threads); i++) { r = uv_thread_create(&threads[i].thread_id, do_work, &threads[i]); ASSERT(r == 0); } for (i = 0; i < ARRAY_SIZE(threads); i++) { r = uv_thread_join(&threads[i].thread_id); ASSERT(r == 0); ASSERT(threads[i].thread_called); } return 0; } static void tls_thread(void* arg) { ASSERT(NULL == uv_key_get(&tls_key)); uv_key_set(&tls_key, arg); ASSERT(arg == uv_key_get(&tls_key)); uv_key_set(&tls_key, NULL); ASSERT(NULL == uv_key_get(&tls_key)); } TEST_IMPL(thread_local_storage) { char name[] = "main"; uv_thread_t threads[2]; ASSERT(0 == uv_key_create(&tls_key)); ASSERT(NULL == uv_key_get(&tls_key)); uv_key_set(&tls_key, name); ASSERT(name == uv_key_get(&tls_key)); ASSERT(0 == uv_thread_create(threads + 0, tls_thread, threads + 0)); ASSERT(0 == uv_thread_create(threads + 1, tls_thread, threads + 1)); ASSERT(0 == uv_thread_join(threads + 0)); ASSERT(0 == uv_thread_join(threads + 1)); uv_key_delete(&tls_key); return 0; } libuv-v1.8.0/test/test-threadpool-cancel.c000066400000000000000000000242361263361716600205350ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #define INIT_CANCEL_INFO(ci, what) \ do { \ (ci)->reqs = (what); \ (ci)->nreqs = ARRAY_SIZE(what); \ (ci)->stride = sizeof((what)[0]); \ } \ while (0) struct cancel_info { void* reqs; unsigned nreqs; unsigned stride; uv_timer_t timer_handle; }; static uv_cond_t signal_cond; static uv_mutex_t signal_mutex; static uv_mutex_t wait_mutex; static unsigned num_threads; static unsigned fs_cb_called; static unsigned work_cb_called; static unsigned done_cb_called; static unsigned done2_cb_called; static unsigned timer_cb_called; static void work_cb(uv_work_t* req) { uv_mutex_lock(&signal_mutex); uv_cond_signal(&signal_cond); uv_mutex_unlock(&signal_mutex); uv_mutex_lock(&wait_mutex); uv_mutex_unlock(&wait_mutex); work_cb_called++; } static void done_cb(uv_work_t* req, int status) { done_cb_called++; free(req); } static void saturate_threadpool(void) { uv_work_t* req; ASSERT(0 == uv_cond_init(&signal_cond)); ASSERT(0 == uv_mutex_init(&signal_mutex)); ASSERT(0 == uv_mutex_init(&wait_mutex)); uv_mutex_lock(&signal_mutex); uv_mutex_lock(&wait_mutex); for (num_threads = 0; /* empty */; num_threads++) { req = malloc(sizeof(*req)); ASSERT(req != NULL); ASSERT(0 == uv_queue_work(uv_default_loop(), req, work_cb, done_cb)); /* Expect to get signalled within 350 ms, otherwise assume that * the thread pool is saturated. As with any timing dependent test, * this is obviously not ideal. */ if (uv_cond_timedwait(&signal_cond, &signal_mutex, (uint64_t) (350 * 1e6))) { ASSERT(0 == uv_cancel((uv_req_t*) req)); break; } } } static void unblock_threadpool(void) { uv_mutex_unlock(&signal_mutex); uv_mutex_unlock(&wait_mutex); } static void cleanup_threadpool(void) { ASSERT(done_cb_called == num_threads + 1); /* +1 == cancelled work req. */ ASSERT(work_cb_called == num_threads); uv_cond_destroy(&signal_cond); uv_mutex_destroy(&signal_mutex); uv_mutex_destroy(&wait_mutex); } static void fs_cb(uv_fs_t* req) { ASSERT(req->result == UV_ECANCELED); uv_fs_req_cleanup(req); fs_cb_called++; } static void getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { ASSERT(status == UV_EAI_CANCELED); ASSERT(res == NULL); uv_freeaddrinfo(res); /* Should not crash. */ } static void getnameinfo_cb(uv_getnameinfo_t* handle, int status, const char* hostname, const char* service) { ASSERT(status == UV_EAI_CANCELED); ASSERT(hostname == NULL); ASSERT(service == NULL); } static void work2_cb(uv_work_t* req) { ASSERT(0 && "work2_cb called"); } static void done2_cb(uv_work_t* req, int status) { ASSERT(status == UV_ECANCELED); done2_cb_called++; } static void timer_cb(uv_timer_t* handle) { struct cancel_info* ci; uv_req_t* req; unsigned i; ci = container_of(handle, struct cancel_info, timer_handle); for (i = 0; i < ci->nreqs; i++) { req = (uv_req_t*) ((char*) ci->reqs + i * ci->stride); ASSERT(0 == uv_cancel(req)); } uv_close((uv_handle_t*) &ci->timer_handle, NULL); unblock_threadpool(); timer_cb_called++; } static void nop_work_cb(uv_work_t* req) { } static void nop_done_cb(uv_work_t* req, int status) { req->data = "OK"; } TEST_IMPL(threadpool_cancel_getaddrinfo) { uv_getaddrinfo_t reqs[4]; struct cancel_info ci; struct addrinfo hints; uv_loop_t* loop; int r; INIT_CANCEL_INFO(&ci, reqs); loop = uv_default_loop(); saturate_threadpool(); r = uv_getaddrinfo(loop, reqs + 0, getaddrinfo_cb, "fail", NULL, NULL); ASSERT(r == 0); r = uv_getaddrinfo(loop, reqs + 1, getaddrinfo_cb, NULL, "fail", NULL); ASSERT(r == 0); r = uv_getaddrinfo(loop, reqs + 2, getaddrinfo_cb, "fail", "fail", NULL); ASSERT(r == 0); r = uv_getaddrinfo(loop, reqs + 3, getaddrinfo_cb, "fail", NULL, &hints); ASSERT(r == 0); ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(1 == timer_cb_called); cleanup_threadpool(); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(threadpool_cancel_getnameinfo) { uv_getnameinfo_t reqs[4]; struct sockaddr_in addr4; struct cancel_info ci; uv_loop_t* loop; int r; r = uv_ip4_addr("127.0.0.1", 80, &addr4); ASSERT(r == 0); INIT_CANCEL_INFO(&ci, reqs); loop = uv_default_loop(); saturate_threadpool(); r = uv_getnameinfo(loop, reqs + 0, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); ASSERT(r == 0); r = uv_getnameinfo(loop, reqs + 1, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); ASSERT(r == 0); r = uv_getnameinfo(loop, reqs + 2, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); ASSERT(r == 0); r = uv_getnameinfo(loop, reqs + 3, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); ASSERT(r == 0); ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(1 == timer_cb_called); cleanup_threadpool(); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(threadpool_cancel_work) { struct cancel_info ci; uv_work_t reqs[16]; uv_loop_t* loop; unsigned i; INIT_CANCEL_INFO(&ci, reqs); loop = uv_default_loop(); saturate_threadpool(); for (i = 0; i < ARRAY_SIZE(reqs); i++) ASSERT(0 == uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(1 == timer_cb_called); ASSERT(ARRAY_SIZE(reqs) == done2_cb_called); cleanup_threadpool(); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(threadpool_cancel_fs) { struct cancel_info ci; uv_fs_t reqs[26]; uv_loop_t* loop; unsigned n; uv_buf_t iov; INIT_CANCEL_INFO(&ci, reqs); loop = uv_default_loop(); saturate_threadpool(); iov = uv_buf_init(NULL, 0); /* Needs to match ARRAY_SIZE(fs_reqs). */ n = 0; ASSERT(0 == uv_fs_chmod(loop, reqs + n++, "/", 0, fs_cb)); ASSERT(0 == uv_fs_chown(loop, reqs + n++, "/", 0, 0, fs_cb)); ASSERT(0 == uv_fs_close(loop, reqs + n++, 0, fs_cb)); ASSERT(0 == uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); ASSERT(0 == uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); ASSERT(0 == uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); ASSERT(0 == uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); ASSERT(0 == uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); ASSERT(0 == uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); ASSERT(0 == uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); ASSERT(0 == uv_fs_link(loop, reqs + n++, "/", "/", fs_cb)); ASSERT(0 == uv_fs_lstat(loop, reqs + n++, "/", fs_cb)); ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); ASSERT(0 == uv_fs_open(loop, reqs + n++, "/", 0, 0, fs_cb)); ASSERT(0 == uv_fs_read(loop, reqs + n++, 0, &iov, 1, 0, fs_cb)); ASSERT(0 == uv_fs_scandir(loop, reqs + n++, "/", 0, fs_cb)); ASSERT(0 == uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); ASSERT(0 == uv_fs_realpath(loop, reqs + n++, "/", fs_cb)); ASSERT(0 == uv_fs_rename(loop, reqs + n++, "/", "/", fs_cb)); ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); ASSERT(0 == uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); ASSERT(0 == uv_fs_stat(loop, reqs + n++, "/", fs_cb)); ASSERT(0 == uv_fs_symlink(loop, reqs + n++, "/", "/", 0, fs_cb)); ASSERT(0 == uv_fs_unlink(loop, reqs + n++, "/", fs_cb)); ASSERT(0 == uv_fs_utime(loop, reqs + n++, "/", 0, 0, fs_cb)); ASSERT(0 == uv_fs_write(loop, reqs + n++, 0, &iov, 1, 0, fs_cb)); ASSERT(n == ARRAY_SIZE(reqs)); ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(n == fs_cb_called); ASSERT(1 == timer_cb_called); cleanup_threadpool(); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(threadpool_cancel_single) { uv_loop_t* loop; uv_work_t req; int cancelled; int i; loop = uv_default_loop(); for (i = 0; i < 5000; i++) { req.data = NULL; ASSERT(0 == uv_queue_work(loop, &req, nop_work_cb, nop_done_cb)); cancelled = uv_cancel((uv_req_t*) &req); if (cancelled == 0) break; ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); } if (cancelled != 0) { fputs("Failed to cancel a work req in 5,000 iterations, giving up.\n", stderr); return 1; } ASSERT(req.data == NULL); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); ASSERT(req.data != NULL); /* Should have been updated by nop_done_cb(). */ MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-threadpool.c000066400000000000000000000042711263361716600173070ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int work_cb_count; static int after_work_cb_count; static uv_work_t work_req; static char data; static void work_cb(uv_work_t* req) { ASSERT(req == &work_req); ASSERT(req->data == &data); work_cb_count++; } static void after_work_cb(uv_work_t* req, int status) { ASSERT(status == 0); ASSERT(req == &work_req); ASSERT(req->data == &data); after_work_cb_count++; } TEST_IMPL(threadpool_queue_work_simple) { int r; work_req.data = &data; r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(work_cb_count == 1); ASSERT(after_work_cb_count == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(threadpool_queue_work_einval) { int r; work_req.data = &data; r = uv_queue_work(uv_default_loop(), &work_req, NULL, after_work_cb); ASSERT(r == UV_EINVAL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(work_cb_count == 0); ASSERT(after_work_cb_count == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-timer-again.c000066400000000000000000000100611263361716600173350ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int close_cb_called = 0; static int repeat_1_cb_called = 0; static int repeat_2_cb_called = 0; static int repeat_2_cb_allowed = 0; static uv_timer_t dummy, repeat_1, repeat_2; static uint64_t start_time; static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void repeat_1_cb(uv_timer_t* handle) { int r; ASSERT(handle == &repeat_1); ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50); fprintf(stderr, "repeat_1_cb called after %ld ms\n", (long int)(uv_now(uv_default_loop()) - start_time)); fflush(stderr); repeat_1_cb_called++; r = uv_timer_again(&repeat_2); ASSERT(r == 0); if (repeat_1_cb_called == 10) { uv_close((uv_handle_t*)handle, close_cb); /* We're not calling uv_timer_again on repeat_2 any more, so after this */ /* timer_2_cb is expected. */ repeat_2_cb_allowed = 1; return; } } static void repeat_2_cb(uv_timer_t* handle) { ASSERT(handle == &repeat_2); ASSERT(repeat_2_cb_allowed); fprintf(stderr, "repeat_2_cb called after %ld ms\n", (long int)(uv_now(uv_default_loop()) - start_time)); fflush(stderr); repeat_2_cb_called++; if (uv_timer_get_repeat(&repeat_2) == 0) { ASSERT(0 == uv_is_active((uv_handle_t*) handle)); uv_close((uv_handle_t*)handle, close_cb); return; } fprintf(stderr, "uv_timer_get_repeat %ld ms\n", (long int)uv_timer_get_repeat(&repeat_2)); fflush(stderr); ASSERT(uv_timer_get_repeat(&repeat_2) == 100); /* This shouldn't take effect immediately. */ uv_timer_set_repeat(&repeat_2, 0); } TEST_IMPL(timer_again) { int r; start_time = uv_now(uv_default_loop()); ASSERT(0 < start_time); /* Verify that it is not possible to uv_timer_again a never-started timer. */ r = uv_timer_init(uv_default_loop(), &dummy); ASSERT(r == 0); r = uv_timer_again(&dummy); ASSERT(r == UV_EINVAL); uv_unref((uv_handle_t*)&dummy); /* Start timer repeat_1. */ r = uv_timer_init(uv_default_loop(), &repeat_1); ASSERT(r == 0); r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0); ASSERT(r == 0); ASSERT(uv_timer_get_repeat(&repeat_1) == 0); /* Actually make repeat_1 repeating. */ uv_timer_set_repeat(&repeat_1, 50); ASSERT(uv_timer_get_repeat(&repeat_1) == 50); /* * Start another repeating timer. It'll be again()ed by the repeat_1 so * it should not time out until repeat_1 stops. */ r = uv_timer_init(uv_default_loop(), &repeat_2); ASSERT(r == 0); r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100); ASSERT(r == 0); ASSERT(uv_timer_get_repeat(&repeat_2) == 100); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(repeat_1_cb_called == 10); ASSERT(repeat_2_cb_called == 2); ASSERT(close_cb_called == 2); fprintf(stderr, "Test took %ld ms (expected ~700 ms)\n", (long int)(uv_now(uv_default_loop()) - start_time)); fflush(stderr); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-timer-from-check.c000066400000000000000000000056231263361716600203040ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static uv_prepare_t prepare_handle; static uv_check_t check_handle; static uv_timer_t timer_handle; static int prepare_cb_called; static int check_cb_called; static int timer_cb_called; static void prepare_cb(uv_prepare_t* handle) { ASSERT(0 == uv_prepare_stop(&prepare_handle)); ASSERT(0 == prepare_cb_called); ASSERT(1 == check_cb_called); ASSERT(0 == timer_cb_called); prepare_cb_called++; } static void timer_cb(uv_timer_t* handle) { ASSERT(0 == uv_timer_stop(&timer_handle)); ASSERT(1 == prepare_cb_called); ASSERT(1 == check_cb_called); ASSERT(0 == timer_cb_called); timer_cb_called++; } static void check_cb(uv_check_t* handle) { ASSERT(0 == uv_check_stop(&check_handle)); ASSERT(0 == uv_timer_stop(&timer_handle)); /* Runs before timer_cb. */ ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); ASSERT(0 == uv_prepare_start(&prepare_handle, prepare_cb)); ASSERT(0 == prepare_cb_called); ASSERT(0 == check_cb_called); ASSERT(0 == timer_cb_called); check_cb_called++; } TEST_IMPL(timer_from_check) { ASSERT(0 == uv_prepare_init(uv_default_loop(), &prepare_handle)); ASSERT(0 == uv_check_init(uv_default_loop(), &check_handle)); ASSERT(0 == uv_check_start(&check_handle, check_cb)); ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(1 == prepare_cb_called); ASSERT(1 == check_cb_called); ASSERT(1 == timer_cb_called); uv_close((uv_handle_t*) &prepare_handle, NULL); uv_close((uv_handle_t*) &check_handle, NULL); uv_close((uv_handle_t*) &timer_handle, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-timer.c000066400000000000000000000176261263361716600162760ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" static int once_cb_called = 0; static int once_close_cb_called = 0; static int repeat_cb_called = 0; static int repeat_close_cb_called = 0; static int order_cb_called = 0; static uint64_t start_time; static uv_timer_t tiny_timer; static uv_timer_t huge_timer1; static uv_timer_t huge_timer2; static void once_close_cb(uv_handle_t* handle) { printf("ONCE_CLOSE_CB\n"); ASSERT(handle != NULL); ASSERT(0 == uv_is_active(handle)); once_close_cb_called++; } static void once_cb(uv_timer_t* handle) { printf("ONCE_CB %d\n", once_cb_called); ASSERT(handle != NULL); ASSERT(0 == uv_is_active((uv_handle_t*) handle)); once_cb_called++; uv_close((uv_handle_t*)handle, once_close_cb); /* Just call this randomly for the code coverage. */ uv_update_time(uv_default_loop()); } static void repeat_close_cb(uv_handle_t* handle) { printf("REPEAT_CLOSE_CB\n"); ASSERT(handle != NULL); repeat_close_cb_called++; } static void repeat_cb(uv_timer_t* handle) { printf("REPEAT_CB\n"); ASSERT(handle != NULL); ASSERT(1 == uv_is_active((uv_handle_t*) handle)); repeat_cb_called++; if (repeat_cb_called == 5) { uv_close((uv_handle_t*)handle, repeat_close_cb); } } static void never_cb(uv_timer_t* handle) { FATAL("never_cb should never be called"); } TEST_IMPL(timer) { uv_timer_t once_timers[10]; uv_timer_t *once; uv_timer_t repeat, never; unsigned int i; int r; start_time = uv_now(uv_default_loop()); ASSERT(0 < start_time); /* Let 10 timers time out in 500 ms total. */ for (i = 0; i < ARRAY_SIZE(once_timers); i++) { once = once_timers + i; r = uv_timer_init(uv_default_loop(), once); ASSERT(r == 0); r = uv_timer_start(once, once_cb, i * 50, 0); ASSERT(r == 0); } /* The 11th timer is a repeating timer that runs 4 times */ r = uv_timer_init(uv_default_loop(), &repeat); ASSERT(r == 0); r = uv_timer_start(&repeat, repeat_cb, 100, 100); ASSERT(r == 0); /* The 12th timer should not do anything. */ r = uv_timer_init(uv_default_loop(), &never); ASSERT(r == 0); r = uv_timer_start(&never, never_cb, 100, 100); ASSERT(r == 0); r = uv_timer_stop(&never); ASSERT(r == 0); uv_unref((uv_handle_t*)&never); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(once_cb_called == 10); ASSERT(once_close_cb_called == 10); printf("repeat_cb_called %d\n", repeat_cb_called); ASSERT(repeat_cb_called == 5); ASSERT(repeat_close_cb_called == 1); ASSERT(500 <= uv_now(uv_default_loop()) - start_time); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(timer_start_twice) { uv_timer_t once; int r; r = uv_timer_init(uv_default_loop(), &once); ASSERT(r == 0); r = uv_timer_start(&once, never_cb, 86400 * 1000, 0); ASSERT(r == 0); r = uv_timer_start(&once, once_cb, 10, 0); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(once_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(timer_init) { uv_timer_t handle; ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); ASSERT(0 == uv_timer_get_repeat(&handle)); ASSERT(0 == uv_is_active((uv_handle_t*) &handle)); MAKE_VALGRIND_HAPPY(); return 0; } static void order_cb_a(uv_timer_t *handle) { ASSERT(order_cb_called++ == *(int*)handle->data); } static void order_cb_b(uv_timer_t *handle) { ASSERT(order_cb_called++ == *(int*)handle->data); } TEST_IMPL(timer_order) { int first; int second; uv_timer_t handle_a; uv_timer_t handle_b; first = 0; second = 1; ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_a)); ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_b)); /* Test for starting handle_a then handle_b */ handle_a.data = &first; ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0)); handle_b.data = &second; ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(order_cb_called == 2); ASSERT(0 == uv_timer_stop(&handle_a)); ASSERT(0 == uv_timer_stop(&handle_b)); /* Test for starting handle_b then handle_a */ order_cb_called = 0; handle_b.data = &first; ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0)); handle_a.data = &second; ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(order_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } static void tiny_timer_cb(uv_timer_t* handle) { ASSERT(handle == &tiny_timer); uv_close((uv_handle_t*) &tiny_timer, NULL); uv_close((uv_handle_t*) &huge_timer1, NULL); uv_close((uv_handle_t*) &huge_timer2, NULL); } TEST_IMPL(timer_huge_timeout) { ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer)); ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1)); ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer2)); ASSERT(0 == uv_timer_start(&tiny_timer, tiny_timer_cb, 1, 0)); ASSERT(0 == uv_timer_start(&huge_timer1, tiny_timer_cb, 0xffffffffffffLL, 0)); ASSERT(0 == uv_timer_start(&huge_timer2, tiny_timer_cb, (uint64_t) -1, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } static void huge_repeat_cb(uv_timer_t* handle) { static int ncalls; if (ncalls == 0) ASSERT(handle == &huge_timer1); else ASSERT(handle == &tiny_timer); if (++ncalls == 10) { uv_close((uv_handle_t*) &tiny_timer, NULL); uv_close((uv_handle_t*) &huge_timer1, NULL); } } TEST_IMPL(timer_huge_repeat) { ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer)); ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1)); ASSERT(0 == uv_timer_start(&tiny_timer, huge_repeat_cb, 2, 2)); ASSERT(0 == uv_timer_start(&huge_timer1, huge_repeat_cb, 1, (uint64_t) -1)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } static unsigned int timer_run_once_timer_cb_called; static void timer_run_once_timer_cb(uv_timer_t* handle) { timer_run_once_timer_cb_called++; } TEST_IMPL(timer_run_once) { uv_timer_t timer_handle; ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 0, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT(1 == timer_run_once_timer_cb_called); ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 1, 0)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT(2 == timer_run_once_timer_cb_called); uv_close((uv_handle_t*) &timer_handle, NULL); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(timer_null_callback) { uv_timer_t handle; ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); ASSERT(UV_EINVAL == uv_timer_start(&handle, NULL, 100, 100)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-tty.c000066400000000000000000000120611263361716600157620ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #ifdef _WIN32 # include # include #else /* Unix */ # include # include #endif #include #include TEST_IMPL(tty) { int r, width, height; int ttyin_fd, ttyout_fd; uv_tty_t tty_in, tty_out; uv_loop_t* loop = uv_default_loop(); /* Make sure we have an FD that refers to a tty */ #ifdef _WIN32 HANDLE handle; handle = CreateFileA("conin$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ASSERT(handle != INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); handle = CreateFileA("conout$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ASSERT(handle != INVALID_HANDLE_VALUE); ttyout_fd = _open_osfhandle((intptr_t) handle, 0); #else /* unix */ ttyin_fd = open("/dev/tty", O_RDONLY, 0); if (ttyin_fd < 0) { fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); fflush(stderr); return TEST_SKIP; } ttyout_fd = open("/dev/tty", O_WRONLY, 0); if (ttyout_fd < 0) { fprintf(stderr, "Cannot open /dev/tty as write-only: %s\n", strerror(errno)); fflush(stderr); return TEST_SKIP; } #endif ASSERT(ttyin_fd >= 0); ASSERT(ttyout_fd >= 0); ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1)); ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ ASSERT(r == 0); r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ ASSERT(r == 0); r = uv_tty_get_winsize(&tty_out, &width, &height); ASSERT(r == 0); printf("width=%d height=%d\n", width, height); if (width == 0 && height == 0) { /* Some environments such as containers or Jenkins behave like this * sometimes */ MAKE_VALGRIND_HAPPY(); return TEST_SKIP; } /* * Is it a safe assumption that most people have terminals larger than * 10x10? */ ASSERT(width > 10); ASSERT(height > 10); /* Turn on raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); ASSERT(r == 0); /* Turn off raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_NORMAL); ASSERT(r == 0); /* Calling uv_tty_reset_mode() repeatedly should not clobber errno. */ errno = 0; ASSERT(0 == uv_tty_reset_mode()); ASSERT(0 == uv_tty_reset_mode()); ASSERT(0 == uv_tty_reset_mode()); ASSERT(0 == errno); /* TODO check the actual mode! */ uv_close((uv_handle_t*) &tty_in, NULL); uv_close((uv_handle_t*) &tty_out, NULL); uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(tty_file) { #ifndef _WIN32 uv_loop_t loop; uv_tty_t tty; int fd; ASSERT(0 == uv_loop_init(&loop)); fd = open("test/fixtures/empty_file", O_RDONLY); if (fd != -1) { ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); ASSERT(0 == close(fd)); } /* Bug on AIX where '/dev/random' returns 1 from isatty() */ #ifndef _AIX fd = open("/dev/random", O_RDONLY); if (fd != -1) { ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); ASSERT(0 == close(fd)); } #endif /* _AIX */ fd = open("/dev/zero", O_RDONLY); if (fd != -1) { ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); ASSERT(0 == close(fd)); } fd = open("/dev/tty", O_RDONLY); if (fd != -1) { ASSERT(0 == uv_tty_init(&loop, &tty, fd, 1)); ASSERT(0 == close(fd)); uv_close((uv_handle_t*) &tty, NULL); } ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); ASSERT(0 == uv_loop_close(&loop)); MAKE_VALGRIND_HAPPY(); #endif return 0; } libuv-v1.8.0/test/test-udp-bind.c000066400000000000000000000047341263361716600166540ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include TEST_IMPL(udp_bind) { struct sockaddr_in addr; uv_loop_t* loop; uv_udp_t h1, h2; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_udp_init(loop, &h1); ASSERT(r == 0); r = uv_udp_init(loop, &h2); ASSERT(r == 0); r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, 0); ASSERT(r == UV_EADDRINUSE); uv_close((uv_handle_t*) &h1, NULL); uv_close((uv_handle_t*) &h2, NULL); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_bind_reuseaddr) { struct sockaddr_in addr; uv_loop_t* loop; uv_udp_t h1, h2; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_udp_init(loop, &h1); ASSERT(r == 0); r = uv_udp_init(loop, &h2); ASSERT(r == 0); r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR); ASSERT(r == 0); r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR); ASSERT(r == 0); uv_close((uv_handle_t*) &h1, NULL); uv_close((uv_handle_t*) &h2, NULL); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-create-socket-early.c000066400000000000000000000071101263361716600215720ustar00rootroot00000000000000/* Copyright (c) 2015 Saúl Ibarra Corretgé . * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #ifdef _WIN32 # define INVALID_FD (INVALID_HANDLE_VALUE) #else # define INVALID_FD (-1) #endif TEST_IMPL(udp_create_early) { struct sockaddr_in addr; struct sockaddr_in sockname; uv_udp_t client; uv_os_fd_t fd; int r, namelen; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init_ex(uv_default_loop(), &client, AF_INET); ASSERT(r == 0); r = uv_fileno((const uv_handle_t*) &client, &fd); ASSERT(r == 0); ASSERT(fd != INVALID_FD); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 namelen = sizeof sockname; r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); ASSERT(r == 0); ASSERT(sockname.sin_family == AF_INET); #endif r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); namelen = sizeof sockname; r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); ASSERT(r == 0); ASSERT(memcmp(&addr.sin_addr, &sockname.sin_addr, sizeof(addr.sin_addr)) == 0); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_create_early_bad_bind) { struct sockaddr_in addr; uv_udp_t client; uv_os_fd_t fd; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init_ex(uv_default_loop(), &client, AF_INET6); ASSERT(r == 0); r = uv_fileno((const uv_handle_t*) &client, &fd); ASSERT(r == 0); ASSERT(fd != INVALID_FD); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 { int namelen; struct sockaddr_in6 sockname; namelen = sizeof sockname; r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); ASSERT(r == 0); ASSERT(sockname.sin6_family == AF_INET6); } #endif r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); #ifndef _WIN32 ASSERT(r == UV_EINVAL); #else ASSERT(r == UV_EFAULT); #endif uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_create_early_bad_domain) { uv_udp_t client; int r; r = uv_udp_init_ex(uv_default_loop(), &client, 47); ASSERT(r == UV_EINVAL); r = uv_udp_init_ex(uv_default_loop(), &client, 1024); ASSERT(r == UV_EINVAL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-dgram-too-big.c000066400000000000000000000050171263361716600203630ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &handle_) #define CHECK_REQ(req) \ ASSERT((req) == &req_); static uv_udp_t handle_; static uv_udp_send_t req_; static int send_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void send_cb(uv_udp_send_t* req, int status) { CHECK_REQ(req); CHECK_HANDLE(req->handle); ASSERT(status == UV_EMSGSIZE); uv_close((uv_handle_t*)req->handle, close_cb); send_cb_called++; } TEST_IMPL(udp_dgram_too_big) { char dgram[65536]; /* 64K MTU is unlikely, even on localhost */ struct sockaddr_in addr; uv_buf_t buf; int r; memset(dgram, 42, sizeof dgram); /* silence valgrind */ r = uv_udp_init(uv_default_loop(), &handle_); ASSERT(r == 0); buf = uv_buf_init(dgram, sizeof dgram); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_send(&req_, &handle_, &buf, 1, (const struct sockaddr*) &addr, send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(send_cb_called == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(send_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-ipv6.c000066400000000000000000000113461263361716600166210ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #ifdef __FreeBSD__ #include #endif #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server \ || (uv_udp_t*)(handle) == &client \ || (uv_timer_t*)(handle) == &timeout) #define CHECK_REQ(req) \ ASSERT((req) == &req_); static uv_udp_t client; static uv_udp_t server; static uv_udp_send_t req_; static uv_timer_t timeout; static int send_cb_called; static int recv_cb_called; static int close_cb_called; #ifdef __FreeBSD__ static int can_ipv6_ipv4_dual() { int v6only; size_t size = sizeof(int); if (sysctlbyname("net.inet6.ip6.v6only", &v6only, &size, NULL, 0)) return 0; return v6only != 1; } #endif static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void send_cb(uv_udp_send_t* req, int status) { CHECK_REQ(req); CHECK_HANDLE(req->handle); ASSERT(status == 0); send_cb_called++; } static void ipv6_recv_fail(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { ASSERT(0 && "this function should not have been called"); } static void ipv6_recv_ok(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); ASSERT(nread >= 0); if (nread) recv_cb_called++; } static void timeout_cb(uv_timer_t* timer) { uv_close((uv_handle_t*)&server, close_cb); uv_close((uv_handle_t*)&client, close_cb); uv_close((uv_handle_t*)&timeout, close_cb); } static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { struct sockaddr_in6 addr6; struct sockaddr_in addr; uv_buf_t buf; int r; ASSERT(0 == uv_ip6_addr("::0", TEST_PORT, &addr6)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_udp_bind(&server, (const struct sockaddr*) &addr6, bind_flags); ASSERT(r == 0); r = uv_udp_recv_start(&server, alloc_cb, recv_cb); ASSERT(r == 0); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); buf = uv_buf_init("PING", 4); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_send(&req_, &client, &buf, 1, (const struct sockaddr*) &addr, send_cb); ASSERT(r == 0); r = uv_timer_init(uv_default_loop(), &timeout); ASSERT(r == 0); r = uv_timer_start(&timeout, timeout_cb, 500, 0); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(send_cb_called == 0); ASSERT(recv_cb_called == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 3); MAKE_VALGRIND_HAPPY(); } TEST_IMPL(udp_dual_stack) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); #ifdef __FreeBSD__ if (!can_ipv6_ipv4_dual()) RETURN_SKIP("IPv6-IPv4 dual stack not supported"); #endif do_test(ipv6_recv_ok, 0); ASSERT(recv_cb_called == 1); ASSERT(send_cb_called == 1); return 0; } TEST_IMPL(udp_ipv6_only) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); ASSERT(recv_cb_called == 0); ASSERT(send_cb_called == 1); return 0; } libuv-v1.8.0/test/test-udp-multicast-interface.c000066400000000000000000000054771263361716600217100ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int sv_send_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0 || status == UV_ENETUNREACH); CHECK_HANDLE(req->handle); sv_send_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } TEST_IMPL(udp_multicast_interface) { int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in addr; struct sockaddr_in baddr; ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &baddr)); r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); ASSERT(r == 0); r = uv_udp_set_multicast_interface(&server, "0.0.0.0"); ASSERT(r == 0); /* server sends "PING" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*)&addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 1); ASSERT(client.send_queue_size == 0); ASSERT(server.send_queue_size == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-multicast-interface6.c000066400000000000000000000055661263361716600217750ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int sv_send_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); sv_send_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } TEST_IMPL(udp_multicast_interface6) { int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in6 addr; struct sockaddr_in6 baddr; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); ASSERT(0 == uv_ip6_addr("::", 0, &baddr)); r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); ASSERT(r == 0); #if defined(__APPLE__) || defined(__FreeBSD__) r = uv_udp_set_multicast_interface(&server, "::1%lo0"); #else r = uv_udp_set_multicast_interface(&server, NULL); #endif ASSERT(r == 0); /* server sends "PING" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*)&addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-multicast-join.c000066400000000000000000000077101263361716600206770ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int cl_recv_cb_called; static int sv_send_cb_called; static int close_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); sv_send_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } static void cl_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); ASSERT(flags == 0); cl_recv_cb_called++; if (nread < 0) { ASSERT(0 && "unexpected error"); } if (nread == 0) { /* Returning unused buffer */ /* Don't count towards cl_recv_cb_called */ ASSERT(addr == NULL); return; } ASSERT(addr != NULL); ASSERT(nread == 4); ASSERT(!memcmp("PING", buf->base, nread)); /* we are done with the client handle, we can close it */ uv_close((uv_handle_t*) &client, close_cb); } TEST_IMPL(udp_multicast_join) { int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); /* bind to the desired port */ r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); /* join the multicast channel */ r = uv_udp_set_membership(&client, "239.255.0.1", NULL, UV_JOIN_GROUP); if (r == UV_ENODEV) RETURN_SKIP("No multicast support."); ASSERT(r == 0); r = uv_udp_recv_start(&client, alloc_cb, cl_recv_cb); ASSERT(r == 0); buf = uv_buf_init("PING", 4); /* server sends "PING" */ r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*) &addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(cl_recv_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(cl_recv_cb_called == 1); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-multicast-join6.c000066400000000000000000000102401263361716600207550ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int cl_recv_cb_called; static int sv_send_cb_called; static int close_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); sv_send_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } static void cl_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); ASSERT(flags == 0); cl_recv_cb_called++; if (nread < 0) { ASSERT(0 && "unexpected error"); } if (nread == 0) { /* Returning unused buffer */ /* Don't count towards cl_recv_cb_called */ ASSERT(addr == NULL); return; } ASSERT(addr != NULL); ASSERT(nread == 4); ASSERT(!memcmp("PING", buf->base, nread)); /* we are done with the client handle, we can close it */ uv_close((uv_handle_t*) &client, close_cb); } TEST_IMPL(udp_multicast_join6) { int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in6 addr; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); /* bind to the desired port */ r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); /* join the multicast channel */ #if defined(__APPLE__) || defined(_AIX) r = uv_udp_set_membership(&client, "ff02::1", "::1%lo0", UV_JOIN_GROUP); #else r = uv_udp_set_membership(&client, "ff02::1", NULL, UV_JOIN_GROUP); #endif if (r == UV_ENODEV) { MAKE_VALGRIND_HAPPY(); RETURN_SKIP("No ipv6 multicast route"); } ASSERT(r == 0); r = uv_udp_recv_start(&client, alloc_cb, cl_recv_cb); ASSERT(r == 0); buf = uv_buf_init("PING", 4); /* server sends "PING" */ r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*) &addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(cl_recv_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(cl_recv_cb_called == 1); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-multicast-ttl.c000066400000000000000000000053001263361716600205340ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int sv_send_cb_called; static int close_cb_called; static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); close_cb_called++; } static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0 || status == UV_ENETUNREACH); CHECK_HANDLE(req->handle); sv_send_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } TEST_IMPL(udp_multicast_ttl) { int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in addr; r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &addr)); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_udp_set_multicast_ttl(&server, 32); ASSERT(r == 0); /* server sends "PING" */ buf = uv_buf_init("PING", 4); ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*) &addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-open.c000066400000000000000000000107701263361716600166760ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #ifndef _WIN32 # include #endif static int send_cb_called = 0; static int close_cb_called = 0; static uv_udp_send_t send_req; static void startup(void) { #ifdef _WIN32 struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); ASSERT(r == 0); #endif } static uv_os_sock_t create_udp_socket(void) { uv_os_sock_t sock; sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); #ifdef _WIN32 ASSERT(sock != INVALID_SOCKET); #else ASSERT(sock >= 0); #endif #ifndef _WIN32 { /* Allow reuse of the port. */ int yes = 1; int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); ASSERT(r == 0); } #endif return sock; } static void close_socket(uv_os_sock_t sock) { int r; #ifdef _WIN32 r = closesocket(sock); #else r = close(sock); #endif ASSERT(r == 0); } static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); close_cb_called++; } static void recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { int r; if (nread < 0) { ASSERT(0 && "unexpected error"); } if (nread == 0) { /* Returning unused buffer */ /* Don't count towards sv_recv_cb_called */ ASSERT(addr == NULL); return; } ASSERT(flags == 0); ASSERT(addr != NULL); ASSERT(nread == 4); ASSERT(memcmp("PING", buf->base, nread) == 0); r = uv_udp_recv_stop(handle); ASSERT(r == 0); uv_close((uv_handle_t*) handle, close_cb); } static void send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); send_cb_called++; } TEST_IMPL(udp_open) { struct sockaddr_in addr; uv_buf_t buf = uv_buf_init("PING", 4); uv_udp_t client; uv_os_sock_t sock; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_udp_socket(); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_udp_open(&client, sock); ASSERT(r == 0); r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_udp_recv_start(&client, alloc_cb, recv_cb); ASSERT(r == 0); r = uv_udp_send(&send_req, &client, &buf, 1, (const struct sockaddr*) &addr, send_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(send_cb_called == 1); ASSERT(close_cb_called == 1); ASSERT(client.send_queue_size == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_open_twice) { uv_udp_t client; uv_os_sock_t sock1, sock2; int r; startup(); sock1 = create_udp_socket(); sock2 = create_udp_socket(); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_udp_open(&client, sock1); ASSERT(r == 0); r = uv_udp_open(&client, sock2); ASSERT(r == UV_EBUSY); close_socket(sock2); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-options.c000066400000000000000000000066761263361716600174420ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include static int udp_options_test(const struct sockaddr* addr) { static int invalid_ttls[] = { -1, 0, 256 }; uv_loop_t* loop; uv_udp_t h; int i, r; loop = uv_default_loop(); r = uv_udp_init(loop, &h); ASSERT(r == 0); uv_unref((uv_handle_t*)&h); /* don't keep the loop alive */ r = uv_udp_bind(&h, addr, 0); ASSERT(r == 0); r = uv_udp_set_broadcast(&h, 1); r |= uv_udp_set_broadcast(&h, 1); r |= uv_udp_set_broadcast(&h, 0); r |= uv_udp_set_broadcast(&h, 0); ASSERT(r == 0); /* values 1-255 should work */ for (i = 1; i <= 255; i++) { r = uv_udp_set_ttl(&h, i); ASSERT(r == 0); } for (i = 0; i < (int) ARRAY_SIZE(invalid_ttls); i++) { r = uv_udp_set_ttl(&h, invalid_ttls[i]); ASSERT(r == UV_EINVAL); } r = uv_udp_set_multicast_loop(&h, 1); r |= uv_udp_set_multicast_loop(&h, 1); r |= uv_udp_set_multicast_loop(&h, 0); r |= uv_udp_set_multicast_loop(&h, 0); ASSERT(r == 0); /* values 0-255 should work */ for (i = 0; i <= 255; i++) { r = uv_udp_set_multicast_ttl(&h, i); ASSERT(r == 0); } /* anything >255 should fail */ r = uv_udp_set_multicast_ttl(&h, 256); ASSERT(r == UV_EINVAL); /* don't test ttl=-1, it's a valid value on some platforms */ r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); return 0; } TEST_IMPL(udp_options) { struct sockaddr_in addr; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); return udp_options_test((const struct sockaddr*) &addr); } TEST_IMPL(udp_options6) { struct sockaddr_in6 addr; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); return udp_options_test((const struct sockaddr*) &addr); } TEST_IMPL(udp_no_autobind) { uv_loop_t* loop; uv_udp_t h; loop = uv_default_loop(); ASSERT(0 == uv_udp_init(loop, &h)); ASSERT(UV_EBADF == uv_udp_set_multicast_ttl(&h, 32)); ASSERT(UV_EBADF == uv_udp_set_broadcast(&h, 1)); ASSERT(UV_EBADF == uv_udp_set_ttl(&h, 1)); ASSERT(UV_EBADF == uv_udp_set_multicast_loop(&h, 1)); ASSERT(UV_EBADF == uv_udp_set_multicast_interface(&h, "0.0.0.0")); uv_close((uv_handle_t*) &h, NULL); ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-send-and-recv.c000066400000000000000000000125121263361716600203570ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int cl_send_cb_called; static int cl_recv_cb_called; static int sv_send_cb_called; static int sv_recv_cb_called; static int close_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); ASSERT(1 == uv_is_closing(handle)); close_cb_called++; } static void cl_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); ASSERT(flags == 0); if (nread < 0) { ASSERT(0 && "unexpected error"); } if (nread == 0) { /* Returning unused buffer */ /* Don't count towards cl_recv_cb_called */ ASSERT(addr == NULL); return; } ASSERT(addr != NULL); ASSERT(nread == 4); ASSERT(!memcmp("PONG", buf->base, nread)); cl_recv_cb_called++; uv_close((uv_handle_t*) handle, close_cb); } static void cl_send_cb(uv_udp_send_t* req, int status) { int r; ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); r = uv_udp_recv_start(req->handle, alloc_cb, cl_recv_cb); ASSERT(r == 0); cl_send_cb_called++; } static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); uv_close((uv_handle_t*) req->handle, close_cb); free(req); sv_send_cb_called++; } static void sv_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { uv_udp_send_t* req; uv_buf_t sndbuf; int r; if (nread < 0) { ASSERT(0 && "unexpected error"); } if (nread == 0) { /* Returning unused buffer */ /* Don't count towards sv_recv_cb_called */ ASSERT(addr == NULL); return; } CHECK_HANDLE(handle); ASSERT(flags == 0); ASSERT(addr != NULL); ASSERT(nread == 4); ASSERT(!memcmp("PING", rcvbuf->base, nread)); /* FIXME? `uv_udp_recv_stop` does what it says: recv_cb is not called * anymore. That's problematic because the read buffer won't be returned * either... Not sure I like that but it's consistent with `uv_read_stop`. */ r = uv_udp_recv_stop(handle); ASSERT(r == 0); req = malloc(sizeof *req); ASSERT(req != NULL); sndbuf = uv_buf_init("PONG", 4); r = uv_udp_send(req, handle, &sndbuf, 1, addr, sv_send_cb); ASSERT(r == 0); sv_recv_cb_called++; } TEST_IMPL(udp_send_and_recv) { struct sockaddr_in addr; uv_udp_send_t req; uv_buf_t buf; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); /* client sends "PING", expects "PONG" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req, &client, &buf, 1, (const struct sockaddr*) &addr, cl_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(cl_send_cb_called == 0); ASSERT(cl_recv_cb_called == 0); ASSERT(sv_send_cb_called == 0); ASSERT(sv_recv_cb_called == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(cl_send_cb_called == 1); ASSERT(cl_recv_cb_called == 1); ASSERT(sv_send_cb_called == 1); ASSERT(sv_recv_cb_called == 1); ASSERT(close_cb_called == 2); ASSERT(client.send_queue_size == 0); ASSERT(server.send_queue_size == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-send-immediate.c000066400000000000000000000076471263361716600206330ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int cl_send_cb_called; static int sv_recv_cb_called; static int close_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); ASSERT(1 == uv_is_closing(handle)); close_cb_called++; } static void cl_send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); cl_send_cb_called++; } static void sv_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { if (nread < 0) { ASSERT(0 && "unexpected error"); } if (nread == 0) { /* Returning unused buffer */ /* Don't count towards sv_recv_cb_called */ ASSERT(addr == NULL); return; } CHECK_HANDLE(handle); ASSERT(flags == 0); ASSERT(addr != NULL); ASSERT(nread == 4); ASSERT(memcmp("PING", rcvbuf->base, nread) == 0 || memcmp("PANG", rcvbuf->base, nread) == 0); if (++sv_recv_cb_called == 2) { uv_close((uv_handle_t*) &server, close_cb); uv_close((uv_handle_t*) &client, close_cb); } } TEST_IMPL(udp_send_immediate) { struct sockaddr_in addr; uv_udp_send_t req1, req2; uv_buf_t buf; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); /* client sends "PING", then "PANG" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req1, &client, &buf, 1, (const struct sockaddr*) &addr, cl_send_cb); ASSERT(r == 0); buf = uv_buf_init("PANG", 4); r = uv_udp_send(&req2, &client, &buf, 1, (const struct sockaddr*) &addr, cl_send_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(cl_send_cb_called == 2); ASSERT(sv_recv_cb_called == 2); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-send-unreachable.c000066400000000000000000000076761263361716600211500ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &client) static uv_udp_t client; static uv_timer_t timer; static int send_cb_called; static int recv_cb_called; static int close_cb_called; static int alloc_cb_called; static int timer_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); alloc_cb_called++; } static void close_cb(uv_handle_t* handle) { ASSERT(1 == uv_is_closing(handle)); close_cb_called++; } static void send_cb(uv_udp_send_t* req, int status) { ASSERT(req != NULL); ASSERT(status == 0); CHECK_HANDLE(req->handle); send_cb_called++; } static void recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); recv_cb_called++; if (nread < 0) { ASSERT(0 && "unexpected error"); } else if (nread == 0) { /* Returning unused buffer */ ASSERT(addr == NULL); } else { ASSERT(addr != NULL); } } static void timer_cb(uv_timer_t* h) { ASSERT(h == &timer); timer_cb_called++; uv_close((uv_handle_t*) &client, close_cb); uv_close((uv_handle_t*) h, close_cb); } TEST_IMPL(udp_send_unreachable) { struct sockaddr_in addr; struct sockaddr_in addr2; uv_udp_send_t req1, req2; uv_buf_t buf; int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2)); r = uv_timer_init( uv_default_loop(), &timer ); ASSERT(r == 0); r = uv_timer_start( &timer, timer_cb, 1000, 0 ); ASSERT(r == 0); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); r = uv_udp_bind(&client, (const struct sockaddr*) &addr2, 0); ASSERT(r == 0); r = uv_udp_recv_start(&client, alloc_cb, recv_cb); ASSERT(r == 0); /* client sends "PING", then "PANG" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req1, &client, &buf, 1, (const struct sockaddr*) &addr, send_cb); ASSERT(r == 0); buf = uv_buf_init("PANG", 4); r = uv_udp_send(&req2, &client, &buf, 1, (const struct sockaddr*) &addr, send_cb); ASSERT(r == 0); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(send_cb_called == 2); ASSERT(recv_cb_called == alloc_cb_called); ASSERT(timer_cb_called == 1); ASSERT(close_cb_called == 2); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-udp-try-send.c000066400000000000000000000067141263361716600175050ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include #include #ifdef _WIN32 TEST_IMPL(udp_try_send) { MAKE_VALGRIND_HAPPY(); return 0; } #else /* !_WIN32 */ #define CHECK_HANDLE(handle) \ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) static uv_udp_t server; static uv_udp_t client; static int sv_recv_cb_called; static int close_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); ASSERT(suggested_size <= sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); ASSERT(uv_is_closing(handle)); close_cb_called++; } static void sv_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { ASSERT(nread > 0); if (nread == 0) { ASSERT(addr == NULL); return; } ASSERT(nread == 4); ASSERT(addr != NULL); ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); uv_close((uv_handle_t*) handle, close_cb); uv_close((uv_handle_t*) &client, close_cb); sv_recv_cb_called++; } TEST_IMPL(udp_try_send) { struct sockaddr_in addr; static char buffer[64 * 1024]; uv_buf_t buf; int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); ASSERT(r == 0); buf = uv_buf_init(buffer, sizeof(buffer)); r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); ASSERT(r == UV_EMSGSIZE); buf = uv_buf_init("EXIT", 4); r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); ASSERT(r == 4); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(close_cb_called == 2); ASSERT(sv_recv_cb_called == 1); ASSERT(client.send_queue_size == 0); ASSERT(server.send_queue_size == 0); MAKE_VALGRIND_HAPPY(); return 0; } #endif /* !_WIN32 */ libuv-v1.8.0/test/test-walk-handles.c000066400000000000000000000044111263361716600175140ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include static char magic_cookie[] = "magic cookie"; static int seen_timer_handle; static uv_timer_t timer; static void walk_cb(uv_handle_t* handle, void* arg) { ASSERT(arg == (void*)magic_cookie); if (handle == (uv_handle_t*)&timer) { seen_timer_handle++; } else { ASSERT(0 && "unexpected handle"); } } static void timer_cb(uv_timer_t* handle) { ASSERT(handle == &timer); uv_walk(handle->loop, walk_cb, magic_cookie); uv_close((uv_handle_t*)handle, NULL); } TEST_IMPL(walk_handles) { uv_loop_t* loop; int r; loop = uv_default_loop(); r = uv_timer_init(loop, &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 1, 0); ASSERT(r == 0); /* Start event loop, expect to see the timer handle in walk_cb. */ ASSERT(seen_timer_handle == 0); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(r == 0); ASSERT(seen_timer_handle == 1); /* Loop is finished, walk_cb should not see our timer handle. */ seen_timer_handle = 0; uv_walk(loop, walk_cb, magic_cookie); ASSERT(seen_timer_handle == 0); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/test/test-watcher-cross-stop.c000066400000000000000000000063261263361716600207200ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uv.h" #include "task.h" #include #include /* NOTE: Number should be big enough to trigger this problem */ static uv_udp_t sockets[2500]; static uv_udp_send_t reqs[ARRAY_SIZE(sockets)]; static char slab[1]; static unsigned int recv_cb_called; static unsigned int send_cb_called; static unsigned int close_cb_called; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = slab; buf->len = sizeof(slab); } static void recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { recv_cb_called++; } static void send_cb(uv_udp_send_t* req, int status) { send_cb_called++; } static void close_cb(uv_handle_t* handle) { close_cb_called++; } TEST_IMPL(watcher_cross_stop) { uv_loop_t* loop = uv_default_loop(); unsigned int i; struct sockaddr_in addr; uv_buf_t buf; char big_string[1024]; TEST_FILE_LIMIT(ARRAY_SIZE(sockets) + 32); ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); memset(big_string, 'A', sizeof(big_string)); buf = uv_buf_init(big_string, sizeof(big_string)); for (i = 0; i < ARRAY_SIZE(sockets); i++) { ASSERT(0 == uv_udp_init(loop, &sockets[i])); ASSERT(0 == uv_udp_bind(&sockets[i], (const struct sockaddr*) &addr, UV_UDP_REUSEADDR)); ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); ASSERT(0 == uv_udp_send(&reqs[i], &sockets[i], &buf, 1, (const struct sockaddr*) &addr, send_cb)); } while (recv_cb_called == 0) uv_run(loop, UV_RUN_ONCE); for (i = 0; i < ARRAY_SIZE(sockets); i++) uv_close((uv_handle_t*) &sockets[i], close_cb); ASSERT(recv_cb_called > 0); uv_run(loop, UV_RUN_DEFAULT); ASSERT(ARRAY_SIZE(sockets) == send_cb_called); ASSERT(ARRAY_SIZE(sockets) == close_cb_called); MAKE_VALGRIND_HAPPY(); return 0; } libuv-v1.8.0/uv.gyp000066400000000000000000000360461263361716600142260ustar00rootroot00000000000000{ 'target_defaults': { 'conditions': [ ['OS != "win"', { 'defines': [ '_LARGEFILE_SOURCE', '_FILE_OFFSET_BITS=64', ], 'conditions': [ ['OS=="solaris"', { 'cflags': [ '-pthreads' ], }], ['OS not in "solaris android"', { 'cflags': [ '-pthread' ], }], ], }], ], 'xcode_settings': { 'WARNING_CFLAGS': [ '-Wall', '-Wextra', '-Wno-unused-parameter' ], 'OTHER_CFLAGS': [ '-g', '--std=gnu89', '-pedantic' ], } }, 'targets': [ { 'target_name': 'libuv', 'type': '<(uv_library)', 'include_dirs': [ 'include', 'src/', ], 'direct_dependent_settings': { 'include_dirs': [ 'include' ], 'conditions': [ ['OS != "win"', { 'defines': [ '_LARGEFILE_SOURCE', '_FILE_OFFSET_BITS=64', ], }], ['OS in "mac ios"', { 'defines': [ '_DARWIN_USE_64_BIT_INODE=1' ], }], ['OS == "linux"', { 'defines': [ '_POSIX_C_SOURCE=200112' ], }], ], }, 'sources': [ 'common.gypi', 'include/uv.h', 'include/tree.h', 'include/uv-errno.h', 'include/uv-threadpool.h', 'include/uv-version.h', 'src/fs-poll.c', 'src/heap-inl.h', 'src/inet.c', 'src/queue.h', 'src/threadpool.c', 'src/uv-common.c', 'src/uv-common.h', 'src/version.c' ], 'conditions': [ [ 'OS=="win"', { 'defines': [ '_WIN32_WINNT=0x0600', '_GNU_SOURCE', ], 'sources': [ 'include/uv-win.h', 'src/win/async.c', 'src/win/atomicops-inl.h', 'src/win/core.c', 'src/win/dl.c', 'src/win/error.c', 'src/win/fs.c', 'src/win/fs-event.c', 'src/win/getaddrinfo.c', 'src/win/getnameinfo.c', 'src/win/handle.c', 'src/win/handle-inl.h', 'src/win/internal.h', 'src/win/loop-watcher.c', 'src/win/pipe.c', 'src/win/thread.c', 'src/win/poll.c', 'src/win/process.c', 'src/win/process-stdio.c', 'src/win/req.c', 'src/win/req-inl.h', 'src/win/signal.c', 'src/win/stream.c', 'src/win/stream-inl.h', 'src/win/tcp.c', 'src/win/tty.c', 'src/win/timer.c', 'src/win/udp.c', 'src/win/util.c', 'src/win/winapi.c', 'src/win/winapi.h', 'src/win/winsock.c', 'src/win/winsock.h', ], 'conditions': [ ['MSVS_VERSION < "2015"', { 'sources': [ 'src/win/snprintf.c' ] }] ], 'link_settings': { 'libraries': [ '-ladvapi32', '-liphlpapi', '-lpsapi', '-lshell32', '-luserenv', '-lws2_32' ], }, }, { # Not Windows i.e. POSIX 'cflags': [ '-g', '--std=gnu89', '-pedantic', '-Wall', '-Wextra', '-Wno-unused-parameter', ], 'sources': [ 'include/uv-unix.h', 'include/uv-linux.h', 'include/uv-sunos.h', 'include/uv-darwin.h', 'include/uv-bsd.h', 'include/uv-aix.h', 'src/unix/async.c', 'src/unix/atomic-ops.h', 'src/unix/core.c', 'src/unix/dl.c', 'src/unix/fs.c', 'src/unix/getaddrinfo.c', 'src/unix/getnameinfo.c', 'src/unix/internal.h', 'src/unix/loop.c', 'src/unix/loop-watcher.c', 'src/unix/pipe.c', 'src/unix/poll.c', 'src/unix/process.c', 'src/unix/signal.c', 'src/unix/spinlock.h', 'src/unix/stream.c', 'src/unix/tcp.c', 'src/unix/thread.c', 'src/unix/timer.c', 'src/unix/tty.c', 'src/unix/udp.c', ], 'link_settings': { 'libraries': [ '-lm' ], 'conditions': [ ['OS=="solaris"', { 'ldflags': [ '-pthreads' ], }], ['OS != "solaris" and OS != "android"', { 'ldflags': [ '-pthread' ], }], ], }, 'conditions': [ ['uv_library=="shared_library"', { 'cflags': [ '-fPIC' ], }], ['uv_library=="shared_library" and OS!="mac"', { # This will cause gyp to set soname # Must correspond with UV_VERSION_MAJOR # in include/uv-version.h 'product_extension': 'so.1', }], ], }], [ 'OS in "linux mac ios android"', { 'sources': [ 'src/unix/proctitle.c' ], }], [ 'OS in "mac ios"', { 'sources': [ 'src/unix/darwin.c', 'src/unix/fsevents.c', 'src/unix/darwin-proctitle.c', ], 'defines': [ '_DARWIN_USE_64_BIT_INODE=1', '_DARWIN_UNLIMITED_SELECT=1', ] }], [ 'OS!="mac"', { # Enable on all platforms except OS X. The antique gcc/clang that # ships with Xcode emits waaaay too many false positives. 'cflags': [ '-Wstrict-aliasing' ], }], [ 'OS=="linux"', { 'defines': [ '_GNU_SOURCE' ], 'sources': [ 'src/unix/linux-core.c', 'src/unix/linux-inotify.c', 'src/unix/linux-syscalls.c', 'src/unix/linux-syscalls.h', ], 'link_settings': { 'libraries': [ '-ldl', '-lrt' ], }, }], [ 'OS=="android"', { 'sources': [ 'src/unix/linux-core.c', 'src/unix/linux-inotify.c', 'src/unix/linux-syscalls.c', 'src/unix/linux-syscalls.h', 'src/unix/pthread-fixes.c', 'src/unix/android-ifaddrs.c' ], 'link_settings': { 'libraries': [ '-ldl' ], }, }], [ 'OS=="solaris"', { 'sources': [ 'src/unix/sunos.c' ], 'defines': [ '__EXTENSIONS__', '_XOPEN_SOURCE=500', ], 'link_settings': { 'libraries': [ '-lkstat', '-lnsl', '-lsendfile', '-lsocket', ], }, }], [ 'OS=="aix"', { 'sources': [ 'src/unix/aix.c' ], 'defines': [ '_ALL_SOURCE', '_XOPEN_SOURCE=500', '_LINUX_SOURCE_COMPAT', ], 'link_settings': { 'libraries': [ '-lperfstat', ], }, }], [ 'OS=="freebsd" or OS=="dragonflybsd"', { 'sources': [ 'src/unix/freebsd.c' ], }], [ 'OS=="openbsd"', { 'sources': [ 'src/unix/openbsd.c' ], }], [ 'OS=="netbsd"', { 'sources': [ 'src/unix/netbsd.c' ], }], [ 'OS in "freebsd dragonflybsd openbsd netbsd".split()', { 'link_settings': { 'libraries': [ '-lkvm' ], }, }], [ 'OS in "ios mac freebsd dragonflybsd openbsd netbsd".split()', { 'sources': [ 'src/unix/kqueue.c' ], }], ['uv_library=="shared_library"', { 'defines': [ 'BUILDING_UV_SHARED=1' ] }], ] }, { 'target_name': 'run-tests', 'type': 'executable', 'dependencies': [ 'libuv' ], 'sources': [ 'test/blackhole-server.c', 'test/echo-server.c', 'test/run-tests.c', 'test/runner.c', 'test/runner.h', 'test/test-get-loadavg.c', 'test/task.h', 'test/test-active.c', 'test/test-async.c', 'test/test-async-null-cb.c', 'test/test-callback-stack.c', 'test/test-callback-order.c', 'test/test-close-fd.c', 'test/test-close-order.c', 'test/test-connection-fail.c', 'test/test-cwd-and-chdir.c', 'test/test-default-loop-close.c', 'test/test-delayed-accept.c', 'test/test-error.c', 'test/test-embed.c', 'test/test-emfile.c', 'test/test-fail-always.c', 'test/test-fs.c', 'test/test-fs-event.c', 'test/test-get-currentexe.c', 'test/test-get-memory.c', 'test/test-getaddrinfo.c', 'test/test-getnameinfo.c', 'test/test-getsockname.c', 'test/test-handle-fileno.c', 'test/test-homedir.c', 'test/test-hrtime.c', 'test/test-idle.c', 'test/test-ip6-addr.c', 'test/test-ipc.c', 'test/test-ipc-send-recv.c', 'test/test-list.h', 'test/test-loop-handles.c', 'test/test-loop-alive.c', 'test/test-loop-close.c', 'test/test-loop-stop.c', 'test/test-loop-time.c', 'test/test-loop-configure.c', 'test/test-walk-handles.c', 'test/test-watcher-cross-stop.c', 'test/test-multiple-listen.c', 'test/test-osx-select.c', 'test/test-pass-always.c', 'test/test-ping-pong.c', 'test/test-pipe-bind-error.c', 'test/test-pipe-connect-error.c', 'test/test-pipe-connect-multiple.c', 'test/test-pipe-connect-prepare.c', 'test/test-pipe-getsockname.c', 'test/test-pipe-pending-instances.c', 'test/test-pipe-sendmsg.c', 'test/test-pipe-server-close.c', 'test/test-pipe-close-stdout-read-stdin.c', 'test/test-pipe-set-non-blocking.c', 'test/test-platform-output.c', 'test/test-poll.c', 'test/test-poll-close.c', 'test/test-poll-close-doesnt-corrupt-stack.c', 'test/test-poll-closesocket.c', 'test/test-process-title.c', 'test/test-queue-foreach-delete.c', 'test/test-ref.c', 'test/test-run-nowait.c', 'test/test-run-once.c', 'test/test-semaphore.c', 'test/test-shutdown-close.c', 'test/test-shutdown-eof.c', 'test/test-shutdown-twice.c', 'test/test-signal.c', 'test/test-signal-multiple-loops.c', 'test/test-socket-buffer-size.c', 'test/test-spawn.c', 'test/test-fs-poll.c', 'test/test-stdio-over-pipes.c', 'test/test-tcp-bind-error.c', 'test/test-tcp-bind6-error.c', 'test/test-tcp-close.c', 'test/test-tcp-close-accept.c', 'test/test-tcp-close-while-connecting.c', 'test/test-tcp-create-socket-early.c', 'test/test-tcp-connect-error-after-write.c', 'test/test-tcp-shutdown-after-write.c', 'test/test-tcp-flags.c', 'test/test-tcp-connect-error.c', 'test/test-tcp-connect-timeout.c', 'test/test-tcp-connect6-error.c', 'test/test-tcp-open.c', 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-write-after-connect.c', 'test/test-tcp-writealot.c', 'test/test-tcp-write-fail.c', 'test/test-tcp-try-write.c', 'test/test-tcp-unexpected-read.c', 'test/test-tcp-oob.c', 'test/test-tcp-read-stop.c', 'test/test-tcp-write-queue-order.c', 'test/test-threadpool.c', 'test/test-threadpool-cancel.c', 'test/test-thread-equal.c', 'test/test-mutexes.c', 'test/test-thread.c', 'test/test-barrier.c', 'test/test-condvar.c', 'test/test-timer-again.c', 'test/test-timer-from-check.c', 'test/test-timer.c', 'test/test-tty.c', 'test/test-udp-bind.c', 'test/test-udp-create-socket-early.c', 'test/test-udp-dgram-too-big.c', 'test/test-udp-ipv6.c', 'test/test-udp-open.c', 'test/test-udp-options.c', 'test/test-udp-send-and-recv.c', 'test/test-udp-send-immediate.c', 'test/test-udp-send-unreachable.c', 'test/test-udp-multicast-join.c', 'test/test-udp-multicast-join6.c', 'test/test-dlerror.c', 'test/test-udp-multicast-ttl.c', 'test/test-ip4-addr.c', 'test/test-ip6-addr.c', 'test/test-udp-multicast-interface.c', 'test/test-udp-multicast-interface6.c', 'test/test-udp-try-send.c', ], 'conditions': [ [ 'OS=="win"', { 'sources': [ 'test/runner-win.c', 'test/runner-win.h' ], 'libraries': [ '-lws2_32' ] }, { # POSIX 'defines': [ '_GNU_SOURCE' ], 'sources': [ 'test/runner-unix.c', 'test/runner-unix.h', ], }], [ 'OS=="solaris"', { # make test-fs.c compile, needs _POSIX_C_SOURCE 'defines': [ '__EXTENSIONS__', '_XOPEN_SOURCE=500', ], }], [ 'OS=="aix"', { # make test-fs.c compile, needs _POSIX_C_SOURCE 'defines': [ '_ALL_SOURCE', '_XOPEN_SOURCE=500', ], }], ['uv_library=="shared_library"', { 'defines': [ 'USING_UV_SHARED=1' ] }], ], 'msvs-settings': { 'VCLinkerTool': { 'SubSystem': 1, # /subsystem:console }, }, }, { 'target_name': 'run-benchmarks', 'type': 'executable', 'dependencies': [ 'libuv' ], 'sources': [ 'test/benchmark-async.c', 'test/benchmark-async-pummel.c', 'test/benchmark-fs-stat.c', 'test/benchmark-getaddrinfo.c', 'test/benchmark-list.h', 'test/benchmark-loop-count.c', 'test/benchmark-million-async.c', 'test/benchmark-million-timers.c', 'test/benchmark-multi-accept.c', 'test/benchmark-ping-pongs.c', 'test/benchmark-pound.c', 'test/benchmark-pump.c', 'test/benchmark-sizes.c', 'test/benchmark-spawn.c', 'test/benchmark-thread.c', 'test/benchmark-tcp-write-batch.c', 'test/benchmark-udp-pummel.c', 'test/dns-server.c', 'test/echo-server.c', 'test/blackhole-server.c', 'test/run-benchmarks.c', 'test/runner.c', 'test/runner.h', 'test/task.h', ], 'conditions': [ [ 'OS=="win"', { 'sources': [ 'test/runner-win.c', 'test/runner-win.h', ], 'libraries': [ '-lws2_32' ] }, { # POSIX 'defines': [ '_GNU_SOURCE' ], 'sources': [ 'test/runner-unix.c', 'test/runner-unix.h', ] }], ['uv_library=="shared_library"', { 'defines': [ 'USING_UV_SHARED=1' ] }], ], 'msvs-settings': { 'VCLinkerTool': { 'SubSystem': 1, # /subsystem:console }, }, }, ] } libuv-v1.8.0/vcbuild.bat000066400000000000000000000113521263361716600151640ustar00rootroot00000000000000@echo off cd %~dp0 if /i "%1"=="help" goto help if /i "%1"=="--help" goto help if /i "%1"=="-help" goto help if /i "%1"=="/help" goto help if /i "%1"=="?" goto help if /i "%1"=="-?" goto help if /i "%1"=="--?" goto help if /i "%1"=="/?" goto help @rem Process arguments. set config= set target=Build set noprojgen= set nobuild= set run= set target_arch=ia32 set vs_toolset=x86 set platform=WIN32 set library=static_library :next-arg if "%1"=="" goto args-done if /i "%1"=="debug" set config=Debug&goto arg-ok if /i "%1"=="release" set config=Release&goto arg-ok if /i "%1"=="test" set run=run-tests.exe&goto arg-ok if /i "%1"=="bench" set run=run-benchmarks.exe&goto arg-ok if /i "%1"=="clean" set target=Clean&goto arg-ok if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok if /i "%1"=="nobuild" set nobuild=1&goto arg-ok if /i "%1"=="x86" set target_arch=ia32&set platform=WIN32&set vs_toolset=x86&goto arg-ok if /i "%1"=="ia32" set target_arch=ia32&set platform=WIN32&set vs_toolset=x86&goto arg-ok if /i "%1"=="x64" set target_arch=x64&set platform=x64&set vs_toolset=x64&goto arg-ok if /i "%1"=="shared" set library=shared_library&goto arg-ok if /i "%1"=="static" set library=static_library&goto arg-ok :arg-ok shift goto next-arg :args-done if defined WindowsSDKDir goto select-target if defined VCINSTALLDIR goto select-target @rem Look for Visual Studio 2015 if not defined VS140COMNTOOLS goto vc-set-2013 if not exist "%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2013 call "%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% set GYP_MSVS_VERSION=2015 goto select-target :vc-set-2013 @rem Look for Visual Studio 2013 if not defined VS120COMNTOOLS goto vc-set-2012 if not exist "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2012 call "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% set GYP_MSVS_VERSION=2013 goto select-target :vc-set-2012 @rem Look for Visual Studio 2012 if not defined VS110COMNTOOLS goto vc-set-2010 if not exist "%VS110COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2010 call "%VS110COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% set GYP_MSVS_VERSION=2012 goto select-target :vc-set-2010 @rem Look for Visual Studio 2010 if not defined VS100COMNTOOLS goto vc-set-2008 if not exist "%VS100COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2008 call "%VS100COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% set GYP_MSVS_VERSION=2010 goto select-target :vc-set-2008 @rem Look for Visual Studio 2008 if not defined VS90COMNTOOLS goto vc-set-notfound if not exist "%VS90COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-notfound call "%VS90COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% set GYP_MSVS_VERSION=2008 goto select-target :vc-set-notfound echo Warning: Visual Studio not found :select-target if not "%config%"=="" goto project-gen if "%run%"=="run-tests.exe" set config=Debug& goto project-gen if "%run%"=="run-benchmarks.exe" set config=Release& goto project-gen set config=Debug :project-gen @rem Skip project generation if requested. if defined noprojgen goto msbuild @rem Generate the VS project. if exist build\gyp goto have_gyp echo git clone https://chromium.googlesource.com/external/gyp build/gyp git clone https://chromium.googlesource.com/external/gyp build/gyp if errorlevel 1 goto gyp_install_failed goto have_gyp :gyp_install_failed echo Failed to download gyp. Make sure you have git installed, or echo manually install gyp into %~dp0build\gyp. exit /b 1 :have_gyp if not defined PYTHON set PYTHON=python "%PYTHON%" gyp_uv.py -Dtarget_arch=%target_arch% -Duv_library=%library% if errorlevel 1 goto create-msvs-files-failed if not exist uv.sln goto create-msvs-files-failed echo Project files generated. :msbuild @rem Skip project generation if requested. if defined nobuild goto run @rem Check if VS build env is available if defined VCINSTALLDIR goto msbuild-found if defined WindowsSDKDir goto msbuild-found echo Build skipped. To build, this file needs to run from VS cmd prompt. goto run @rem Build the sln with msbuild. :msbuild-found msbuild uv.sln /t:%target% /p:Configuration=%config% /p:Platform="%platform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 exit /b 1 :run @rem Run tests if requested. if "%run%"=="" goto exit if not exist %config%\%run% goto exit echo running '%config%\%run%' %config%\%run% goto exit :create-msvs-files-failed echo Failed to create vc project files. exit /b 1 :help echo vcbuild.bat [debug/release] [test/bench] [clean] [noprojgen] [nobuild] [x86/x64] [static/shared] echo Examples: echo vcbuild.bat : builds debug build echo vcbuild.bat test : builds debug build and runs tests echo vcbuild.bat release bench: builds release build and runs benchmarks goto exit :exit