Z-IO 0.1.1.2 → 0.1.2.0
raw patch · 66 files changed
+17590/−217 lines, 66 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Z.IO.FileSystem.Threaded: checkFileTClosed :: HasCallStack => FileT -> (UVFD -> IO a) -> IO a
- Z.IO.Network.SocketAddr: ProtocolNumber :: CInt -> ProtocolNumber
- Z.IO.Network.SocketAddr: SocketFamily :: CInt -> SocketFamily
- Z.IO.Network.SocketAddr: SocketType :: CInt -> SocketType
- Z.IO.Network.SocketAddr: newtype ProtocolNumber
- Z.IO.Network.SocketAddr: newtype SocketFamily
- Z.IO.Network.SocketAddr: newtype SocketType
- Z.IO.UV.FFI: pattern ACCEPT_BUFFER_SIZE :: Int
+ Z.IO.FileSystem: data FilePtr
+ Z.IO.FileSystem: getFileOffset :: FilePtr -> IO Int64
+ Z.IO.FileSystem: instance Z.IO.Buffered.Input Z.IO.FileSystem.FilePtr
+ Z.IO.FileSystem: instance Z.IO.Buffered.Output Z.IO.FileSystem.FilePtr
+ Z.IO.FileSystem: newFilePtr :: File -> Int64 -> IO FilePtr
+ Z.IO.FileSystem: setFileOffset :: FilePtr -> Int64 -> IO ()
+ Z.IO.FileSystem.Threaded: data FilePtrT
+ Z.IO.FileSystem.Threaded: getFileOffset :: FilePtrT -> IO Int64
+ Z.IO.FileSystem.Threaded: instance Z.IO.Buffered.Input Z.IO.FileSystem.Threaded.FilePtrT
+ Z.IO.FileSystem.Threaded: instance Z.IO.Buffered.Output Z.IO.FileSystem.Threaded.FilePtrT
+ Z.IO.FileSystem.Threaded: newFilePtrT :: FileT -> Int64 -> IO FilePtrT
+ Z.IO.FileSystem.Threaded: setFileOffset :: FilePtrT -> Int64 -> IO ()
+ Z.IO.Network.SocketAddr: data ProtocolNumber
+ Z.IO.Network.SocketAddr: data SocketFamily
+ Z.IO.Network.SocketAddr: data SocketType
Files
- ChangeLog.md +7/−0
- README.md +4/−14
- Z-IO.cabal +130/−63
- Z/Compression/Zlib.hsc +10/−13
- Z/IO/Buffered.hs +4/−4
- Z/IO/FileSystem.hs +39/−0
- Z/IO/FileSystem/Threaded.hs +40/−2
- Z/IO/Network/IPC.hs +9/−8
- Z/IO/Network/SocketAddr.hsc +3/−16
- Z/IO/Network/TCP.hs +13/−10
- Z/IO/UV/FFI.hsc +0/−2
- Z/IO/UV/Manager.hs +7/−7
- cbits/hs_uv_stream.c +2/−2
- cbits/hs_zlib.c +0/−1
- include/hs_uv.h +2/−3
- test/Z/IO/FileSystem/ThreadedSpec.hs +27/−10
- test/Z/IO/FileSystemSpec.hs +22/−10
- test/Z/IO/LowResTimerSpec.hs +1/−1
- test/Z/IO/Network/TCPSpec.hs +8/−9
- third_party/libuv/include/uv/aix.h +32/−0
- third_party/libuv/include/uv/android-ifaddrs.h +54/−0
- third_party/libuv/include/uv/bsd.h +34/−0
- third_party/libuv/include/uv/darwin.h +61/−0
- third_party/libuv/include/uv/linux.h +34/−0
- third_party/libuv/include/uv/os390.h +33/−0
- third_party/libuv/include/uv/posix.h +31/−0
- third_party/libuv/include/uv/sunos.h +44/−0
- third_party/libuv/include/uv/unix.h +505/−0
- third_party/libuv/src/random.c +123/−0
- third_party/libuv/src/unix/async.c +253/−0
- third_party/libuv/src/unix/atomic-ops.h +61/−0
- third_party/libuv/src/unix/bsd-ifaddrs.c +161/−0
- third_party/libuv/src/unix/core.c +1613/−0
- third_party/libuv/src/unix/darwin-proctitle.c +192/−0
- third_party/libuv/src/unix/darwin-stub.h +113/−0
- third_party/libuv/src/unix/darwin.c +371/−0
- third_party/libuv/src/unix/dl.c +80/−0
- third_party/libuv/src/unix/fs.c +2121/−0
- third_party/libuv/src/unix/fsevents.c +923/−0
- third_party/libuv/src/unix/getaddrinfo.c +255/−0
- third_party/libuv/src/unix/getnameinfo.c +121/−0
- third_party/libuv/src/unix/internal.h +351/−0
- third_party/libuv/src/unix/kqueue.c +585/−0
- third_party/libuv/src/unix/linux-core.c +1145/−0
- third_party/libuv/src/unix/linux-inotify.c +327/−0
- third_party/libuv/src/unix/linux-syscalls.c +245/−0
- third_party/libuv/src/unix/linux-syscalls.h +81/−0
- third_party/libuv/src/unix/loop-watcher.c +68/−0
- third_party/libuv/src/unix/loop.c +228/−0
- third_party/libuv/src/unix/os390-syscalls.h +74/−0
- third_party/libuv/src/unix/pipe.c +381/−0
- third_party/libuv/src/unix/poll.c +150/−0
- third_party/libuv/src/unix/process.c +595/−0
- third_party/libuv/src/unix/procfs-exepath.c +45/−0
- third_party/libuv/src/unix/proctitle.c +159/−0
- third_party/libuv/src/unix/random-devurandom.c +93/−0
- third_party/libuv/src/unix/random-getrandom.c +88/−0
- third_party/libuv/src/unix/random-sysctl-linux.c +99/−0
- third_party/libuv/src/unix/signal.c +558/−0
- third_party/libuv/src/unix/spinlock.h +53/−0
- third_party/libuv/src/unix/stream.c +1693/−0
- third_party/libuv/src/unix/tcp.c +461/−0
- third_party/libuv/src/unix/thread.c +842/−0
- third_party/libuv/src/unix/tty.c +402/−0
- third_party/libuv/src/unix/udp.c +1324/−0
- third_party/libuv/src/win/snprintf.c +0/−42
ChangeLog.md view
@@ -1,5 +1,12 @@ # Revision history for Z-IO +## 0.1.2.0 -- 2020-09-28++* Add file offset interface back, see `FilePtr` and `FilePtrT`.+* Remove `checkFileTClosed` from `Z.IO.FileSystem.Threaded`.+* Take c source file list from libuv Makefile, remove lib requirements on linux and osx.+* Fix `uv_check_t` initiate order in accept loop, which cause a segfault.+ ## 0.1.1.2 -- 2020-09-25 * Fix macOS build problem caused by missing zconf.h.
README.md view
@@ -4,9 +4,7 @@ ## Z-IO -[](https://github.com/haskell-Z/z-io/actions)--[](https://github.com/haskell-Z/z-io/actions)+[](https://github.com/haskell-Z/z-io/actions) [](https://github.com/haskell-Z/z-io/actions) [](https://github.com/haskell-Z/z-io/actions) This package provides basic IO operations: @@ -17,17 +15,11 @@ * High performance logger * High performance timer -## Dependencies--On *nix system, libuv >= 1.32 are required to build this library, e.g.+## Requirements -```base-# on ubuntu-sudo apt-get install libuv1 libuv1-dev-# on mac-brew install libuv-```+* A working haskell compiler system, GHC(>=8.10.2), cabal-install(>=3.4). +* Tests need [hspec-discover](https://hackage.haskell.org/package/hspec-discover). ## Example usage @@ -69,8 +61,6 @@ ## Dev guide -+ GHC(>=8.10.2) -+ cabal-install(>=3.4) ```bash # get code
Z-IO.cabal view
@@ -1,5 +1,5 @@ name: Z-IO-version: 0.1.1.2+version: 0.1.2.0 synopsis: Simple and high performance IO toolkit for Haskell description: Simple and high performance IO toolkit for Haskell, including file system, network, ipc and more!@@ -30,63 +30,46 @@ third_party/zlib/zutil.h third_party/zlib/gzguts.h - -- libuv C sources (for Windows)+ -- libuv C sources third_party/libuv/include/uv.h- third_party/libuv/include/uv/win.h- third_party/libuv/include/uv/tree.h+ third_party/libuv/include/uv/aix.h+ third_party/libuv/include/uv/android-ifaddrs.h+ third_party/libuv/include/uv/bsd.h+ third_party/libuv/include/uv/darwin.h third_party/libuv/include/uv/errno.h+ third_party/libuv/include/uv/linux.h+ third_party/libuv/include/uv/os390.h+ third_party/libuv/include/uv/posix.h+ third_party/libuv/include/uv/stdint-msvc2008.h+ third_party/libuv/include/uv/sunos.h third_party/libuv/include/uv/threadpool.h+ third_party/libuv/include/uv/tree.h+ third_party/libuv/include/uv/unix.h third_party/libuv/include/uv/version.h- third_party/libuv/include/uv/stdint-msvc2008.h+ third_party/libuv/include/uv/win.h - third_party/libuv/src/fs-poll.c third_party/libuv/src/heap-inl.h- third_party/libuv/src/idna.c third_party/libuv/src/idna.h- third_party/libuv/src/inet.c third_party/libuv/src/queue.h- third_party/libuv/src/strscpy.c third_party/libuv/src/strscpy.h- third_party/libuv/src/threadpool.c- third_party/libuv/src/timer.c- third_party/libuv/src/uv-common.c third_party/libuv/src/uv-common.h- third_party/libuv/src/uv-data-getter-setters.c- third_party/libuv/src/version.c - third_party/libuv/src/win/async.c third_party/libuv/src/win/atomicops-inl.h- third_party/libuv/src/win/core.c- third_party/libuv/src/win/detect-wakeup.c- third_party/libuv/src/win/dl.c- third_party/libuv/src/win/error.c- third_party/libuv/src/win/fs.c- third_party/libuv/src/win/fs-event.c- third_party/libuv/src/win/getaddrinfo.c- third_party/libuv/src/win/getnameinfo.c- third_party/libuv/src/win/handle.c third_party/libuv/src/win/handle-inl.h third_party/libuv/src/win/internal.h- third_party/libuv/src/win/loop-watcher.c- third_party/libuv/src/win/pipe.c- third_party/libuv/src/win/poll.c- third_party/libuv/src/win/process.c- third_party/libuv/src/win/process-stdio.c third_party/libuv/src/win/req-inl.h- third_party/libuv/src/win/signal.c- third_party/libuv/src/win/snprintf.c- third_party/libuv/src/win/stream.c third_party/libuv/src/win/stream-inl.h- third_party/libuv/src/win/tcp.c- third_party/libuv/src/win/thread.c- third_party/libuv/src/win/tty.c- third_party/libuv/src/win/udp.c- third_party/libuv/src/win/util.c- third_party/libuv/src/win/winapi.c third_party/libuv/src/win/winapi.h- third_party/libuv/src/win/winsock.c third_party/libuv/src/win/winsock.h + third_party/libuv/src/unix/atomic-ops.h+ third_party/libuv/src/unix/darwin-stub.h+ third_party/libuv/src/unix/darwin-stub.h+ third_party/libuv/src/unix/internal.h+ third_party/libuv/src/unix/linux-syscalls.h+ third_party/libuv/src/unix/spinlock.h+ third_party/libuv/src/unix/os390-syscalls.h+ source-repository head type: git location: git://github.com/haskell-Z/Z-IO.git@@ -127,6 +110,10 @@ , time >= 1.8 && < 2.0 , stm == 2.5.* + default-language: Haskell2010+ build-tools: hsc2hs, hspec-discover+ ghc-options: -Wall+ include-dirs: include third_party/zlib @@ -158,26 +145,23 @@ third_party/zlib/uncompr.c third_party/zlib/zutil.c - -- Note: The c-sources list is taken from libuv's Makefile.mingw, needs to be- -- updated when we bump up libuv's version.- -- header files are ignored here, otherwise will confuse linker- if os(windows)- c-sources: third_party/libuv/src/fs-poll.c- -- third_party/libuv/src/heap-inl.h+ third_party/libuv/src/fs-poll.c third_party/libuv/src/idna.c third_party/libuv/src/inet.c- -- third_party/libuv/src/queue.h+ third_party/libuv/src/random.c third_party/libuv/src/strscpy.c- -- third_party/libuv/src/strscpy.h third_party/libuv/src/threadpool.c third_party/libuv/src/timer.c third_party/libuv/src/uv-data-getter-setters.c third_party/libuv/src/uv-common.c- -- third_party/libuv/src/uv-common.h third_party/libuv/src/version.c + -- Note: The c-sources list is taken from libuv's Makefile.mingw, needs to be+ -- updated when we bump up libuv's version.+ -- header files are ignored here, otherwise will confuse linker+ if os(windows)+ c-sources: third_party/libuv/src/win/async.c- -- third_party/libuv/src/win/atomicops-inl.h third_party/libuv/src/win/core.c third_party/libuv/src/win/detect-wakeup.c third_party/libuv/src/win/dl.c@@ -187,43 +171,126 @@ third_party/libuv/src/win/getaddrinfo.c third_party/libuv/src/win/getnameinfo.c third_party/libuv/src/win/handle.c- -- third_party/libuv/src/win/handle-inl.h- -- third_party/libuv/src/win/internal.h third_party/libuv/src/win/loop-watcher.c third_party/libuv/src/win/pipe.c third_party/libuv/src/win/poll.c third_party/libuv/src/win/process-stdio.c third_party/libuv/src/win/process.c- -- third_party/libuv/src/win/req-inl.h third_party/libuv/src/win/signal.c third_party/libuv/src/win/stream.c- -- third_party/libuv/src/win/stream-inl.h third_party/libuv/src/win/tcp.c third_party/libuv/src/win/thread.c third_party/libuv/src/win/tty.c third_party/libuv/src/win/udp.c third_party/libuv/src/win/util.c third_party/libuv/src/win/winapi.c- -- third_party/libuv/src/win/winapi.h third_party/libuv/src/win/winsock.c- -- third_party/libuv/src/win/winsock.h - cc-options: -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600 -march=native- cpp-options: -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600+ cc-options: -Wall -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600 -O -march=native+ cpp-options: -Wall -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600 include-dirs: third_party/libuv/include third_party/libuv/src -- The C runtime dependencies are imposed by libuv. extra-libraries: psapi, Iphlpapi, userenv, Ws2_32 else- if flag(no-pkg-config)- extra-libraries: uv+ if os(linux)+ c-sources: third_party/libuv/src/fs-poll.c+ third_party/libuv/src/idna.c+ third_party/libuv/src/inet.c+ third_party/libuv/src/random.c+ third_party/libuv/src/strscpy.c+ third_party/libuv/src/threadpool.c+ third_party/libuv/src/timer.c+ third_party/libuv/src/uv-data-getter-setters.c+ third_party/libuv/src/uv-common.c+ third_party/libuv/src/version.c+++ third_party/libuv/src/unix/async.c+ third_party/libuv/src/unix/core.c+ third_party/libuv/src/unix/dl.c+ third_party/libuv/src/unix/fs.c+ third_party/libuv/src/unix/getaddrinfo.c+ third_party/libuv/src/unix/getnameinfo.c+ third_party/libuv/src/unix/loop.c+ third_party/libuv/src/unix/loop-watcher.c+ third_party/libuv/src/unix/pipe.c+ third_party/libuv/src/unix/poll.c+ third_party/libuv/src/unix/process.c+ third_party/libuv/src/unix/random-devurandom.c+ third_party/libuv/src/unix/signal.c+ third_party/libuv/src/unix/stream.c+ third_party/libuv/src/unix/tcp.c+ third_party/libuv/src/unix/thread.c+ third_party/libuv/src/unix/tty.c+ third_party/libuv/src/unix/udp.c++ third_party/libuv/src/unix/linux-core.c+ third_party/libuv/src/unix/linux-inotify.c+ third_party/libuv/src/unix/linux-syscalls.c+ third_party/libuv/src/unix/procfs-exepath.c+ third_party/libuv/src/unix/proctitle.c+ third_party/libuv/src/unix/random-getrandom.c+ third_party/libuv/src/unix/random-sysctl-linux.c++ include-dirs: third_party/libuv/include+ third_party/libuv/src/unix+ third_party/libuv/src+ cc-options: -Wall -O -march=native else- pkgconfig-depends: libuv >= 1.32+ if os(osx)+ c-sources: third_party/libuv/src/fs-poll.c+ third_party/libuv/src/idna.c+ third_party/libuv/src/inet.c+ third_party/libuv/src/random.c+ third_party/libuv/src/strscpy.c+ third_party/libuv/src/threadpool.c+ third_party/libuv/src/timer.c+ third_party/libuv/src/uv-data-getter-setters.c+ third_party/libuv/src/uv-common.c+ third_party/libuv/src/version.c - default-language: Haskell2010- build-tools: hsc2hs, hspec-discover- ghc-options: -Wall++ third_party/libuv/src/unix/async.c+ third_party/libuv/src/unix/core.c+ third_party/libuv/src/unix/dl.c+ third_party/libuv/src/unix/fs.c+ third_party/libuv/src/unix/getaddrinfo.c+ third_party/libuv/src/unix/getnameinfo.c+ third_party/libuv/src/unix/loop.c+ third_party/libuv/src/unix/loop-watcher.c+ third_party/libuv/src/unix/pipe.c+ third_party/libuv/src/unix/poll.c+ third_party/libuv/src/unix/process.c+ third_party/libuv/src/unix/random-devurandom.c+ third_party/libuv/src/unix/signal.c+ third_party/libuv/src/unix/stream.c+ third_party/libuv/src/unix/tcp.c+ third_party/libuv/src/unix/thread.c+ third_party/libuv/src/unix/tty.c+ third_party/libuv/src/unix/udp.c++ third_party/libuv/src/unix/bsd-ifaddrs.c+ third_party/libuv/src/unix/darwin-proctitle.c+ third_party/libuv/src/unix/darwin.c+ third_party/libuv/src/unix/fsevents.c+ third_party/libuv/src/unix/kqueue.c+ third_party/libuv/src/unix/proctitle.c+ third_party/libuv/src/unix/random-getrandom.c+ + include-dirs: third_party/libuv/include+ third_party/libuv/src/unix+ third_party/libuv/src+ cc-options: -Wall -O -march=native++ else+ if flag(no-pkg-config)+ extra-libraries: uv+ else+ pkgconfig-depends: libuv >= 1.32+ + test-suite Z-IO-Test type: exitcode-stdio-1.0
Z/Compression/Zlib.hsc view
@@ -25,16 +25,8 @@ , defaultCompressConfig , compress , compressSink- , Strategy- , pattern Z_FILTERED - , pattern Z_HUFFMAN_ONLY - , pattern Z_RLE - , pattern Z_FIXED - , pattern Z_DEFAULT_STRATEGY- , CompressLevel- , pattern Z_BEST_SPEED - , pattern Z_BEST_COMPRESSION - , pattern Z_DEFAULT_COMPRESSION+ , Strategy (Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED, Z_DEFAULT_STRATEGY)+ , CompressLevel(Z_BEST_SPEED, Z_BEST_COMPRESSION, Z_DEFAULT_COMPRESSION) , WindowBits , defaultWindowBits , MemLevel@@ -65,6 +57,11 @@ newtype Strategy = Strategy CInt deriving (Eq, Ord, Show, Typeable) +pattern Z_FILTERED :: Strategy+pattern Z_HUFFMAN_ONLY :: Strategy+pattern Z_RLE :: Strategy+pattern Z_FIXED :: Strategy+pattern Z_DEFAULT_STRATEGY :: Strategy pattern Z_FILTERED = Strategy (#const Z_FILTERED ) pattern Z_HUFFMAN_ONLY = Strategy (#const Z_HUFFMAN_ONLY ) pattern Z_RLE = Strategy (#const Z_RLE )@@ -75,6 +72,9 @@ newtype CompressLevel = CompressLevel CInt deriving (Eq, Ord, Show, Typeable) -- pattern Z_NO_COMPRESSION = CompressLevel (#const Z_NO_COMPRESSION )+pattern Z_BEST_SPEED :: CompressLevel+pattern Z_BEST_COMPRESSION :: CompressLevel+pattern Z_DEFAULT_COMPRESSION :: CompressLevel pattern Z_BEST_SPEED = CompressLevel (#const Z_BEST_SPEED ) pattern Z_BEST_COMPRESSION = CompressLevel (#const Z_BEST_COMPRESSION ) pattern Z_DEFAULT_COMPRESSION = CompressLevel (#const Z_DEFAULT_COMPRESSION)@@ -318,9 +318,6 @@ foreign import ccall unsafe deflate :: Ptr ZStream -> CInt -> IO CInt--foreign import ccall unsafe- deflateEnd :: Ptr ZStream -> IO CInt foreign import ccall unsafe inflate_init2 :: Ptr ZStream -> WindowBits -> IO CInt
Z/IO/Buffered.hs view
@@ -242,7 +242,7 @@ -- -- Either during parsing or before parsing, reach EOF will result in 'P.ParseError'. readParser :: (HasCallStack, Input i) => P.Parser a -> BufferedInput i -> IO (Either P.ParseError a)-readParser p i@BufferedInput{..} = do+readParser p i = do bs <- readBuffer i (rest, r) <- P.parseChunks p (readBuffer i) bs unReadBuffer rest i@@ -409,8 +409,8 @@ -- | Source a list streamly. sourceFromList :: [a] -> IO (Source a) {-# INLINABLE sourceFromList #-}-sourceFromList xs = do- xsRef <- newIORef xs+sourceFromList xs0 = do+ xsRef <- newIORef xs0 return (popper xsRef) where popper xsRef = do@@ -474,7 +474,7 @@ -- | Connect list of streams, after one stream reach EOF, draw element from next. concatSource :: [Source a] -> IO (Source a) {-# INLINABLE concatSource #-}-concatSource ss = newIORef ss >>= return . loop+concatSource ss0 = newIORef ss0 >>= return . loop where loop ref = do ss <- readIORef ref
Z/IO/FileSystem.hs view
@@ -21,6 +21,8 @@ module Z.IO.FileSystem ( -- * regular file devices File, initFile, readFile, writeFile+ -- * file offset bundle+ , FilePtr, newFilePtr, getFileOffset, setFileOffset -- * opening constant , FileMode(DEFAULT_MODE, S_IRWXU, S_IRUSR, S_IWUSR , S_IXUSR, S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXO, S_IROTH@@ -61,6 +63,7 @@ import Foreign.Storable (peekElemOff) import Foreign.Marshal.Alloc (allocaBytes) import Z.Data.CBytes as CBytes+import Z.Data.PrimRef.PrimIORef import Z.Foreign import Z.IO.Buffered import Z.IO.Exception@@ -131,6 +134,42 @@ go' fd (buf `plusPtr` written) (bufSiz-written) (off+fromIntegral written)++-- | File bundled with offset.+--+-- Reading or writing using 'Input' \/ 'Output' instance will automatically increase offset.+-- 'FilePtr' and its operations are NOT thread safe, use 'MVar' 'FilePtr' in multiple threads.+--+data FilePtr = FilePtr {-# UNPACK #-} !File+ {-# UNPACK #-} !(PrimIORef Int64)++-- | Create a file offset bundle from an 'File'.+--+newFilePtr :: File -- ^ the file we're reading+ -> Int64 -- ^ initial offset+ -> IO FilePtr+newFilePtr uvf off = FilePtr uvf <$> newPrimIORef off++-- | Get current offset.+getFileOffset :: FilePtr -> IO Int64+getFileOffset (FilePtr _ offsetRef) = readPrimIORef offsetRef++-- | Change current offset.+setFileOffset :: FilePtr -> Int64 -> IO ()+setFileOffset (FilePtr _ offsetRef) = writePrimIORef offsetRef++instance Input FilePtr where+ readInput (FilePtr file offsetRef) buf bufSiz =+ readPrimIORef offsetRef >>= \ off -> do+ l <- readFile file buf bufSiz off+ writePrimIORef offsetRef (off + fromIntegral l)+ return l++instance Output FilePtr where+ writeOutput (FilePtr file offsetRef) buf bufSiz =+ readPrimIORef offsetRef >>= \ off -> do+ writeFile file buf bufSiz off+ writePrimIORef offsetRef (off + fromIntegral bufSiz) --------------------------------------------------------------------------------
Z/IO/FileSystem/Threaded.hs view
@@ -25,8 +25,9 @@ module Z.IO.FileSystem.Threaded ( -- * regular file devices- FileT, checkFileTClosed- , initFileT, readFileT, writeFileT+ FileT, initFileT, readFileT, writeFileT+ -- * file offset bundle+ , FilePtrT, newFilePtrT, getFileOffset, setFileOffset -- * opening constant , FileMode(DEFAULT_MODE, S_IRWXU, S_IRUSR, S_IWUSR , S_IXUSR, S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXO, S_IROTH@@ -64,6 +65,7 @@ import Data.Int import Data.IORef import Z.Data.CBytes as CBytes+import Z.Data.PrimRef.PrimIORef import Foreign.Ptr import Foreign.Storable (peekElemOff) import Foreign.Marshal.Alloc (allocaBytes)@@ -146,6 +148,42 @@ go' fd (buf `plusPtr` written) (bufSiz-written) (off+fromIntegral written)++-- | File bundled with offset.+--+-- Reading or writing using 'Input' \/ 'Output' instance will automatically increase offset.+-- 'FilePtrT' and its operations are NOT thread safe, use 'MVar' 'FilePtrT' in multiple threads.+--+data FilePtrT = FilePtrT {-# UNPACK #-} !FileT+ {-# UNPACK #-} !(PrimIORef Int64)++-- | Create a file offset bundle from an 'File'.+--+newFilePtrT :: FileT -- ^ the file we're reading+ -> Int64 -- ^ initial offset+ -> IO FilePtrT+newFilePtrT uvf off = FilePtrT uvf <$> newPrimIORef off++-- | Get current offset.+getFileOffset :: FilePtrT -> IO Int64+getFileOffset (FilePtrT _ offsetRef) = readPrimIORef offsetRef++-- | Change current offset.+setFileOffset :: FilePtrT -> Int64 -> IO ()+setFileOffset (FilePtrT _ offsetRef) = writePrimIORef offsetRef++instance Input FilePtrT where+ readInput (FilePtrT file offsetRef) buf bufSiz =+ readPrimIORef offsetRef >>= \ off -> do+ l <- readFileT file buf bufSiz off+ writePrimIORef offsetRef (off + fromIntegral l)+ return l++instance Output FilePtrT where+ writeOutput (FilePtrT file offsetRef) buf bufSiz =+ readPrimIORef offsetRef >>= \ off -> do+ writeFileT file buf bufSiz off+ writePrimIORef offsetRef (off + fromIntegral bufSiz) --------------------------------------------------------------------------------
Z/IO/Network/IPC.hs view
@@ -128,30 +128,31 @@ -- if backLog is reached, we resume receiving from haskell side. -- -- Step 1.--- we allocate a new uv_check_t for given uv_stream_t, with predefined checking callback--- see hs_accept_check_cb in hs_uv_stream.c- throwUVIfMinus_ $ hs_uv_accept_check_init check- m <- getBlockMVar serverUVManager serverSlot--- Step 2. -- we allocate a buffer to hold accepted FDs, pass it just like a normal reading buffer. -- then we can start listening. acceptBuf <- newPinnedPrimArray backLog let acceptBufPtr = coerce (mutablePrimArrayContents acceptBuf :: Ptr UVFD) withUVManager' serverUVManager $ do- -- We use buffersize as accepted fd counter, so we write 0 here+ -- We use buffersize as accepted fd count(count backwards) pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1)- throwUVIfMinus_ (hs_uv_listen serverHandle (max 4 (fromIntegral backLog)))+ throwUVIfMinus_ (hs_uv_listen serverHandle (fromIntegral backLog))+-- Step 2.+-- we initiate a uv_check_t for given uv_stream_t, with predefined checking callback+-- see hs_accept_check_cb in hs_uv_stream.c+ throwUVIfMinus_ $ hs_uv_accept_check_init check + m <- getBlockMVar serverUVManager serverSlot forever $ do -- wait until accept some FDs- !acceptCountDown <- takeMVar m+ _ <- takeMVar m -- Step 3. -- After uv loop finishes, if we got some FDs, copy the FD buffer, fetch accepted FDs and fork worker threads. -- we lock uv manager here in case of next uv_run overwrite current accept buffer acceptBufCopy <- withUVManager' serverUVManager $ do _ <- tryTakeMVar m+ acceptCountDown <- peekBufferTable serverUVManager serverSlot pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1) -- if acceptCountDown count to -1, we should resume on haskell side
Z/IO/Network/SocketAddr.hsc view
@@ -53,22 +53,9 @@ , PortNumber(..) , portAny -- * family, type, protocol- , SocketFamily(..)- , pattern AF_UNSPEC- , pattern AF_INET- , pattern AF_INET6- , SocketType(..)- , pattern SOCK_DGRAM- , pattern SOCK_STREAM- , pattern SOCK_SEQPACKET- , pattern SOCK_RAW- , pattern SOCK_RDM- , pattern SOCK_ANY- , ProtocolNumber(..)- , pattern IPPROTO_DEFAULT- , pattern IPPROTO_IP- , pattern IPPROTO_TCP- , pattern IPPROTO_UDP+ , SocketFamily(AF_UNSPEC, AF_INET, AF_INET6)+ , SocketType(SOCK_DGRAM, SOCK_STREAM, SOCK_SEQPACKET, SOCK_RAW, SOCK_RDM, SOCK_ANY)+ , ProtocolNumber(IPPROTO_DEFAULT, IPPROTO_IP, IPPROTO_TCP, IPPROTO_UDP) -- * Internal helper , peekSocketAddr , pokeSocketAddr
Z/IO/Network/TCP.hs view
@@ -36,6 +36,7 @@ ) where import Control.Concurrent.MVar+import Control.Monad.Primitive import Control.Monad import Control.Monad.IO.Class import Data.Primitive.PrimArray@@ -107,7 +108,7 @@ defaultTCPServerConfig :: TCPServerConfig defaultTCPServerConfig = TCPServerConfig (SocketAddrInet 8888 inetAny)- 256+ 128 True 30 (\ uvs -> writeOutput uvs (Ptr "hello world"#) 11)@@ -142,29 +143,31 @@ -- if backLog is reached, we resume receiving from haskell side. -- -- Step 1.--- we allocate a new uv_check_t for given uv_stream_t, with predefined checking callback--- see hs_accept_check_cb in hs_uv_stream.c- throwUVIfMinus_ $ hs_uv_accept_check_init check- m <- getBlockMVar serverUVManager serverSlot--- Step 2. -- we allocate a buffer to hold accepted FDs, pass it just like a normal reading buffer. -- then we can start listening. acceptBuf <- newPinnedPrimArray backLog let acceptBufPtr = coerce (mutablePrimArrayContents acceptBuf :: Ptr UVFD)+ withUVManager' serverUVManager $ do -- We use buffersize as accepted fd count(count backwards) pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1)- throwUVIfMinus_ (hs_uv_listen serverHandle (max 4 (fromIntegral backLog)))-+ throwUVIfMinus_ (hs_uv_listen serverHandle (fromIntegral backLog))+-- Step 2.+-- we initiate a uv_check_t for given uv_stream_t, with predefined checking callback+-- see hs_accept_check_cb in hs_uv_stream.c+ throwUVIfMinus_ $ hs_uv_accept_check_init check+--+ m <- getBlockMVar serverUVManager serverSlot forever $ do -- wait until accept some FDs- !acceptCountDown <- takeMVar m+ _ <- takeMVar m -- Step 3. -- After uv loop finishes, if we got some FDs, copy the FD buffer, fetch accepted FDs and fork worker threads. -- we lock uv manager here in case of next uv_run overwrite current accept buffer acceptBufCopy <- withUVManager' serverUVManager $ do _ <- tryTakeMVar m+ acceptCountDown <- peekBufferTable serverUVManager serverSlot pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1) -- if acceptCountDown count to -1, we should resume on haskell side@@ -188,8 +191,8 @@ withResource (initUVStream (\ loop hdl -> do throwUVIfMinus_ (uv_tcp_init loop hdl) throwUVIfMinus_ (hs_uv_tcp_open hdl fd)) uvm) $ \ uvs -> do+ -- safe without withUVManager when tcpServerWorkerNoDelay . throwUVIfMinus_ $- -- safe without withUVManager uv_tcp_nodelay (uvsHandle uvs) 1 when (tcpServerWorkerKeepAlive > 0) . throwUVIfMinus_ $ uv_tcp_keepalive (uvsHandle uvs) 1 tcpServerWorkerKeepAlive
Z/IO/UV/FFI.hsc view
@@ -55,8 +55,6 @@ -------------------------------------------------------------------------------- -- CONSTANT -pattern ACCEPT_BUFFER_SIZE :: Int-pattern ACCEPT_BUFFER_SIZE = #const ACCEPT_BUFFER_SIZE pattern SO_REUSEPORT_LOAD_BALANCE :: Int pattern SO_REUSEPORT_LOAD_BALANCE = #const SO_REUSEPORT_LOAD_BALANCE pattern INIT_LOOP_SIZE :: Int
Z/IO/UV/Manager.hs view
@@ -16,12 +16,12 @@ The main procedures for doing event IO is: - * Allocate a slot number using 'allocSlot'.- * Prepare you IO buffer and write them to uv loop with 'pokeBufferTable'(both read and write).- * Block your thread with a 'MVar', using 'getBlockMVar' to get it.- * Read the result with 'getResult', for read it's the read bytes number, for write it will be zero.- Use 'E.throwIfError' to guard error situations.- * Return the slot back uv manager with 'freeSlot'.+ * Allocate uv_handle in C side, get its slot number with 'getUVSlot', or allocate uv_request with 'withUVRequest'.+ * Prepare you IO buffer with 'pokeBufferTable'(both read and write).+ * Call C side IO functions with predefined callbacks.+ * Block your thread with the 'MVar' from 'getBlockMVar'.+ * Read the result by 'takeMVar' on that 'MVar', it will be the value pushed on C side.+ * Slot is freed on C side, either via callbacks, or when handle is closed. Usually slots are cache in the IO device so that you don't have to allocate new one before each IO operation. Check "System.IO.Socket.TCP" as an example.@@ -180,7 +180,7 @@ withUVManager (UVManager _ loop loopData runningLock _) f = go where go = do- r <- withMVar runningLock $ \ running ->+ r <- withMVar runningLock $ \ running -> do if running then do -- if uv_run is running, it will stop
cbits/hs_uv_stream.c view
@@ -126,7 +126,7 @@ } int hs_uv_pipe_open(uv_pipe_t* handle, int32_t file) {- int r = uv_pipe_open(handle, (uv_file_t)file);+ int r = uv_pipe_open(handle, (uv_file)file); if (r == 0) { hs_uv_connection_init((uv_stream_t*)handle); handle->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;@@ -338,7 +338,7 @@ uv_stream_t* server=(uv_stream_t*)check->data; HsInt slot = (HsInt)server->data; hs_loop_data* loop_data = server->loop->data;- // This relays on GHC ByteArray# memory layout+ // This relys on GHC ByteArray# memory layout, ByteArray# length is recorded before content. HsInt* buffer_ptr = (HsInt*)loop_data->buffer_table[slot]; HsInt backlog = *(buffer_ptr-1); backlog = backlog / 4; // int32_t fd
cbits/hs_zlib.c view
@@ -66,4 +66,3 @@ inflateEnd(stream); free(stream); }-
include/hs_uv.h view
@@ -28,11 +28,11 @@ * SUCH DAMAGE. */ -#include <uv.h>-#include <assert.h> #include <HsFFI.h> // for HsInt+#include <assert.h> #include <stdlib.h> // for malloc, free, etc. #include <string.h> // for fs path+#include <uv.h> #if !defined(_WIN32) @@ -55,7 +55,6 @@ //////////////////////////////////////////////////////////////////////////////// // CONSTANT-#define ACCEPT_BUFFER_SIZE 1020 #define INIT_LOOP_SIZE 128 #define INIT_LOOP_SIZE_BIT 7
test/Z/IO/FileSystem/ThreadedSpec.hs view
@@ -43,6 +43,16 @@ writeBuffer o content flushBuffer o + withResource (initFileT filename flags mode) $ \ file -> do+ i <- newBufferedInput 4096 file+ written <- readExactly size i+ written @?= content++ fr <- newFilePtrT file 0+ i <- newBufferedInput 4096 fr+ written <- readExactly size i+ written @=? content+ unlink filename it "Opens and writes a file II" $ do@@ -61,6 +71,12 @@ Just firstLine <- readLine i firstLine @=? fst (V.break (== V.c2w '\n') content2) + fr <- newFilePtrT file (fromIntegral $ size2 `div` 2)+ i <- newBufferedInput 4096 fr+ replicateM_ 512 $ do+ Just firstLine <- readLine i+ firstLine @=? fst (V.break (== V.c2w '\n') content2)+ unlink filename @@ -78,7 +94,7 @@ symlinkname = "test-symlink" symlinkname2 = "test-symlink2" - it "link stat should be equal to target file" $ do+ it "link stat should be equal to target file" $ ( do withResource (initFileT filename flags mode) $ \ file -> return () @@ -94,18 +110,19 @@ s2 <- stat symlinkname s2' <- stat symlinkname2 - s0 @?= s1 {stNlink = 1} -- update hard link number- s0 @?= s2 {stNlink = 1}- s0 @?= s2' {stNlink = 1}+ s0 @=? s1 {stNlink = 1, stCtim = stCtim s0} -- update hard link number+ s0 @=? s2 {stNlink = 1, stCtim = stCtim s0}+ s0 @=? s2' {stNlink = 1, stCtim = stCtim s0} withResource (initFileT filename flags mode) $ \ file -> do s4 <- fstat file- s0 @?= s4 {stNlink = 1}-- unlink filename- unlink linkname- unlink symlinkname- unlink symlinkname2+ s0 @?= s4 {stNlink = 1, stCtim = stCtim s0}+ ) `finally` ( do+ unlink filename+ unlink linkname+ unlink symlinkname+ unlink symlinkname2+ ) it "utime result in stat change" $ do withResource (initFileT filename flags mode) $ \ file -> return ()
test/Z/IO/FileSystemSpec.hs view
@@ -49,7 +49,12 @@ written <- readExactly size i written @?= content + fr <- newFilePtr file 0+ i <- newBufferedInput 4096 fr+ written <- readExactly size i+ written @=? content + unlink filename it "Opens and writes a file II" $ do@@ -68,6 +73,12 @@ Just firstLine <- readLine i firstLine @?= fst (V.break (== V.c2w '\n') content2) + fr <- newFilePtr file (fromIntegral $ size2 `div` 2)+ i <- newBufferedInput 4096 fr+ replicateM_ 512 $ do+ Just firstLine <- readLine i+ firstLine @=? fst (V.break (== V.c2w '\n') content2)+ unlink filename @@ -85,7 +96,7 @@ symlinkname = "test-symlink" symlinkname2 = "test-symlink2" - it "link stat should be equal to target file" $ do+ it "link stat should be equal to target file" $ ( do withResource (initFile filename flags mode) $ \ file -> return () @@ -101,18 +112,19 @@ s2 <- stat symlinkname s2' <- stat symlinkname2 - s0 @=? s1 {stNlink = 1} -- update hard link number- s0 @=? s2 {stNlink = 1}- s0 @=? s2' {stNlink = 1}+ s0 @=? s1 {stNlink = 1, stCtim = stCtim s0} -- update hard link number, stCtim could have some small diff+ s0 @=? s2 {stNlink = 1, stCtim = stCtim s0}+ s0 @=? s2' {stNlink = 1, stCtim = stCtim s0} withResource (initFile filename flags mode) $ \ file -> do s4 <- fstat file- s0 @=? s4 {stNlink = 1}-- unlink filename- unlink linkname- unlink symlinkname- unlink symlinkname2+ s0 @=? s4 {stNlink = 1, stCtim = stCtim s0}+ ) `finally` ( do+ unlink filename+ unlink linkname+ unlink symlinkname+ unlink symlinkname2+ ) it "utime result in stat change" $ do withResource (initFile filename flags mode) $ \ file -> return ()
test/Z/IO/LowResTimerSpec.hs view
@@ -21,7 +21,7 @@ running <- isLowResTimerManagerRunning lrtm assertEqual "timer manager should start" True running - threadDelay 1200000 -- make sure all timers are fired+ threadDelay 1500000 -- make sure all timers are fired c' <- readPrimIORef c assertEqual "timers registration counter" 100000 c'
test/Z/IO/Network/TCPSpec.hs view
@@ -34,19 +34,18 @@ threadDelay 100000 - replicateM_ 1000 . forkIO $+ replicateM_ 512 . forkIO $ withResource (initTCPClient defaultTCPClientConfig{tcpRemoteAddr = addr}) $ \ tcp -> do i <- newBufferedInput defaultChunkSize tcp o <- newBufferedOutput defaultChunkSize tcp - replicateM_ 1000 . forkIO $ do- writeBuffer o testMsg >> flushBuffer o- testMsg' <- readAll' i- testMsg' @=? testMsg+ writeBuffer o testMsg >> flushBuffer o+ testMsg' <- readExactly' (V.length testMsg) i+ testMsg' @=? testMsg - replicateM_ 1000 . forkIO $ do- writeBuffer o longMsg >> flushBuffer o- longMsg' <- readAll' i- longMsg' @=? longMsg+ writeBuffer o longMsg >> flushBuffer o+ longMsg' <- readExactly' (V.length longMsg) i+ longMsg' @=? longMsg + threadDelay 5000000 killThread serverThread
+ third_party/libuv/include/uv/aix.h view
@@ -0,0 +1,32 @@+/* 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 */
+ third_party/libuv/include/uv/android-ifaddrs.h view
@@ -0,0 +1,54 @@+/*+ * 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 <net/if.h>. Note that if <net/if.h> 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 <sys/cdefs.h>++__BEGIN_DECLS+extern int getifaddrs(struct ifaddrs **ifap);+extern void freeifaddrs(struct ifaddrs *ifa);+__END_DECLS++#endif
+ third_party/libuv/include/uv/bsd.h view
@@ -0,0 +1,34 @@+/* 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 */
+ third_party/libuv/include/uv/darwin.h view
@@ -0,0 +1,61 @@+/* 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 <mach/mach.h>+# include <mach/task.h>+# include <mach/semaphore.h>+# include <TargetConditionals.h>+# 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 */
+ third_party/libuv/include/uv/linux.h view
@@ -0,0 +1,34 @@+/* 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 */
+ third_party/libuv/include/uv/os390.h view
@@ -0,0 +1,33 @@+/* Copyright 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.+ */++#ifndef UV_MVS_H+#define UV_MVS_H++#define UV_PLATFORM_SEM_T long++#define UV_PLATFORM_LOOP_FIELDS \+ void* ep; \++#define UV_PLATFORM_FS_EVENT_FIELDS \+ char rfis_rftok[8]; \++#endif /* UV_MVS_H */
+ third_party/libuv/include/uv/posix.h view
@@ -0,0 +1,31 @@+/* Copyright 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.+ */++#ifndef UV_POSIX_H+#define UV_POSIX_H++#define UV_PLATFORM_LOOP_FIELDS \+ struct pollfd* poll_fds; \+ size_t poll_fds_used; \+ size_t poll_fds_size; \+ unsigned char poll_fds_iterating; \++#endif /* UV_POSIX_H */
+ third_party/libuv/include/uv/sunos.h view
@@ -0,0 +1,44 @@+/* 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 <sys/port.h>+#include <port.h>++/* 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 */
+ third_party/libuv/include/uv/unix.h view
@@ -0,0 +1,505 @@+/* 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 <sys/types.h>+#include <sys/stat.h>+#include <fcntl.h>+#include <dirent.h>++#include <sys/socket.h>+#include <netinet/in.h>+#include <netinet/tcp.h>+#include <arpa/inet.h>+#include <netdb.h> /* MAXHOSTNAMELEN on Solaris */++#include <termios.h>+#include <pwd.h>++#if !defined(__MVS__)+#include <semaphore.h>+#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */+#endif+#include <pthread.h>+#include <signal.h>++#include "uv/threadpool.h"++#if defined(__linux__)+# include "uv/linux.h"+#elif defined (__MVS__)+# include "uv/os390.h"+#elif defined(__PASE__) /* __PASE__ and _AIX are both defined on IBM i */+# include "uv/posix.h" /* IBM i needs uv/posix.h, not uv/aix.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(__FreeBSD_kernel__) || \+ defined(__OpenBSD__) || \+ defined(__NetBSD__)+# include "uv/bsd.h"+#elif defined(__CYGWIN__) || \+ defined(__MSYS__) || \+ defined(__GNU__)+# include "uv/posix.h"+#elif defined(__HAIKU__)+# include "uv/posix.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_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+};++#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;+typedef pid_t uv_pid_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;++/* Note: guard clauses should match uv_barrier_init's in src/unix/thread.c. */+#if defined(_AIX) || \+ defined(__OpenBSD__) || \+ !defined(PTHREAD_BARRIER_SERIAL_THREAD)+/* TODO(bnoordhuis) Merge into uv_barrier_t in v2. */+struct _uv_barrier {+ uv_mutex_t mutex;+ uv_cond_t cond;+ unsigned threshold;+ unsigned in;+ unsigned out;+};++typedef struct {+ struct _uv_barrier* b;+# if defined(PTHREAD_BARRIER_SERIAL_THREAD)+ /* TODO(bnoordhuis) Remove padding in v2. */+ char pad[sizeof(pthread_barrier_t) - sizeof(struct _uv_barrier*)];+# endif+} uv_barrier_t;+#else+typedef pthread_barrier_t uv_barrier_t;+#endif++/* 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;++#define UV_DIR_PRIVATE_FIELDS \+ DIR* dir;++#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]; \+ void (*async_unused)(void); /* TODO(bnoordhuis) Remove in libuv v2. */ \+ uv__io_t async_io_watcher; \+ int async_wfd; \+ 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 \++/* fs open() flags supported on this platform: */+#if defined(O_APPEND)+# define UV_FS_O_APPEND O_APPEND+#else+# define UV_FS_O_APPEND 0+#endif+#if defined(O_CREAT)+# define UV_FS_O_CREAT O_CREAT+#else+# define UV_FS_O_CREAT 0+#endif++#if defined(__linux__) && defined(__arm__)+# define UV_FS_O_DIRECT 0x10000+#elif defined(__linux__) && defined(__m68k__)+# define UV_FS_O_DIRECT 0x10000+#elif defined(__linux__) && defined(__mips__)+# define UV_FS_O_DIRECT 0x08000+#elif defined(__linux__) && defined(__powerpc__)+# define UV_FS_O_DIRECT 0x20000+#elif defined(__linux__) && defined(__s390x__)+# define UV_FS_O_DIRECT 0x04000+#elif defined(__linux__) && defined(__x86_64__)+# define UV_FS_O_DIRECT 0x04000+#elif defined(O_DIRECT)+# define UV_FS_O_DIRECT O_DIRECT+#else+# define UV_FS_O_DIRECT 0+#endif++#if defined(O_DIRECTORY)+# define UV_FS_O_DIRECTORY O_DIRECTORY+#else+# define UV_FS_O_DIRECTORY 0+#endif+#if defined(O_DSYNC)+# define UV_FS_O_DSYNC O_DSYNC+#else+# define UV_FS_O_DSYNC 0+#endif+#if defined(O_EXCL)+# define UV_FS_O_EXCL O_EXCL+#else+# define UV_FS_O_EXCL 0+#endif+#if defined(O_EXLOCK)+# define UV_FS_O_EXLOCK O_EXLOCK+#else+# define UV_FS_O_EXLOCK 0+#endif+#if defined(O_NOATIME)+# define UV_FS_O_NOATIME O_NOATIME+#else+# define UV_FS_O_NOATIME 0+#endif+#if defined(O_NOCTTY)+# define UV_FS_O_NOCTTY O_NOCTTY+#else+# define UV_FS_O_NOCTTY 0+#endif+#if defined(O_NOFOLLOW)+# define UV_FS_O_NOFOLLOW O_NOFOLLOW+#else+# define UV_FS_O_NOFOLLOW 0+#endif+#if defined(O_NONBLOCK)+# define UV_FS_O_NONBLOCK O_NONBLOCK+#else+# define UV_FS_O_NONBLOCK 0+#endif+#if defined(O_RDONLY)+# define UV_FS_O_RDONLY O_RDONLY+#else+# define UV_FS_O_RDONLY 0+#endif+#if defined(O_RDWR)+# define UV_FS_O_RDWR O_RDWR+#else+# define UV_FS_O_RDWR 0+#endif+#if defined(O_SYMLINK)+# define UV_FS_O_SYMLINK O_SYMLINK+#else+# define UV_FS_O_SYMLINK 0+#endif+#if defined(O_SYNC)+# define UV_FS_O_SYNC O_SYNC+#else+# define UV_FS_O_SYNC 0+#endif+#if defined(O_TRUNC)+# define UV_FS_O_TRUNC O_TRUNC+#else+# define UV_FS_O_TRUNC 0+#endif+#if defined(O_WRONLY)+# define UV_FS_O_WRONLY O_WRONLY+#else+# define UV_FS_O_WRONLY 0+#endif++/* fs open() flags supported on other platforms: */+#define UV_FS_O_FILEMAP 0+#define UV_FS_O_RANDOM 0+#define UV_FS_O_SHORT_LIVED 0+#define UV_FS_O_SEQUENTIAL 0+#define UV_FS_O_TEMPORARY 0++#endif /* UV_UNIX_H */
+ third_party/libuv/src/random.c view
@@ -0,0 +1,123 @@+/* Copyright 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.+ */++#include "uv.h"+#include "uv-common.h"++#ifdef _WIN32+# include "win/internal.h"+#else+# include "unix/internal.h"+#endif++static int uv__random(void* buf, size_t buflen) {+ int rc;++#if defined(__PASE__)+ rc = uv__random_readpath("/dev/urandom", buf, buflen);+#elif defined(_AIX)+ rc = uv__random_readpath("/dev/random", buf, buflen);+#elif defined(__APPLE__) || defined(__OpenBSD__) || \+ (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)+ rc = uv__random_getentropy(buf, buflen);+ if (rc == UV_ENOSYS)+ rc = uv__random_devurandom(buf, buflen);+#elif defined(__NetBSD__)+ rc = uv__random_sysctl(buf, buflen);+#elif defined(__FreeBSD__) || defined(__linux__)+ rc = uv__random_getrandom(buf, buflen);+ if (rc == UV_ENOSYS)+ rc = uv__random_devurandom(buf, buflen);+# if defined(__linux__)+ switch (rc) {+ case UV_EACCES:+ case UV_EIO:+ case UV_ELOOP:+ case UV_EMFILE:+ case UV_ENFILE:+ case UV_ENOENT:+ case UV_EPERM:+ rc = uv__random_sysctl(buf, buflen);+ break;+ }+# endif+#elif defined(_WIN32)+ uv__once_init();+ rc = uv__random_rtlgenrandom(buf, buflen);+#else+ rc = uv__random_devurandom(buf, buflen);+#endif++ return rc;+}+++static void uv__random_work(struct uv__work* w) {+ uv_random_t* req;++ req = container_of(w, uv_random_t, work_req);+ req->status = uv__random(req->buf, req->buflen);+}+++static void uv__random_done(struct uv__work* w, int status) {+ uv_random_t* req;++ req = container_of(w, uv_random_t, work_req);+ uv__req_unregister(req->loop, req);++ if (status == 0)+ status = req->status;++ req->cb(req, status, req->buf, req->buflen);+}+++int uv_random(uv_loop_t* loop,+ uv_random_t* req,+ void *buf,+ size_t buflen,+ unsigned flags,+ uv_random_cb cb) {+ if (buflen > 0x7FFFFFFFu)+ return UV_E2BIG;++ if (flags != 0)+ return UV_EINVAL;++ if (cb == NULL)+ return uv__random(buf, buflen);++ uv__req_init(loop, req, UV_RANDOM);+ req->loop = loop;+ req->status = 0;+ req->cb = cb;+ req->buf = buf;+ req->buflen = buflen;++ uv__work_submit(loop,+ &req->work_req,+ UV__WORK_CPU,+ uv__random_work,+ uv__random_done);++ return 0;+}
+ third_party/libuv/src/unix/async.c view
@@ -0,0 +1,253 @@+/* 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 <errno.h>+#include <stdio.h> /* snprintf() */+#include <assert.h>+#include <stdlib.h>+#include <string.h>+#include <unistd.h>+#include <sched.h> /* sched_yield() */++#ifdef __linux__+#include <sys/eventfd.h>+#endif++static void uv__async_send(uv_loop_t* loop);+static int uv__async_start(uv_loop_t* loop);+++int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {+ int err;++ err = uv__async_start(loop);+ 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;++ /* Tell the other thread we're busy with the handle. */+ if (cmpxchgi(&handle->pending, 0, 1) != 0)+ return 0;++ /* Wake up the other thread's event loop. */+ uv__async_send(handle->loop);++ /* Tell the other thread we're done. */+ if (cmpxchgi(&handle->pending, 1, 2) != 1)+ abort();++ return 0;+}+++/* Only call this from the event loop thread. */+static int uv__async_spin(uv_async_t* handle) {+ int i;+ int rc;++ for (;;) {+ /* 997 is not completely chosen at random. It's a prime number, acyclical+ * by nature, and should therefore hopefully dampen sympathetic resonance.+ */+ for (i = 0; i < 997; i++) {+ /* rc=0 -- handle is not pending.+ * rc=1 -- handle is pending, other thread is still working with it.+ * rc=2 -- handle is pending, other thread is done.+ */+ rc = cmpxchgi(&handle->pending, 2, 0);++ if (rc != 1)+ return rc;++ /* Other thread is busy with this handle, spin until it's done. */+ cpu_relax();+ }++ /* Yield the CPU. We may have preempted the other thread while it's+ * inside the critical section and if it's running on the same CPU+ * as us, we'll just burn CPU cycles until the end of our time slice.+ */+ sched_yield();+ }+}+++void uv__async_close(uv_async_t* handle) {+ uv__async_spin(handle);+ QUEUE_REMOVE(&handle->queue);+ uv__handle_stop(handle);+}+++static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {+ char buf[1024];+ ssize_t r;+ QUEUE queue;+ QUEUE* q;+ uv_async_t* h;++ assert(w == &loop->async_io_watcher);++ for (;;) {+ r = read(w->fd, buf, sizeof(buf));++ if (r == sizeof(buf))+ continue;++ if (r != -1)+ break;++ if (errno == EAGAIN || errno == EWOULDBLOCK)+ break;++ if (errno == EINTR)+ continue;++ abort();+ }++ 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 (0 == uv__async_spin(h))+ continue; /* Not pending. */++ if (h->async_cb == NULL)+ continue;++ h->async_cb(h);+ }+}+++static void uv__async_send(uv_loop_t* loop) {+ const void* buf;+ ssize_t len;+ int fd;+ int r;++ buf = "";+ len = 1;+ fd = loop->async_wfd;++#if defined(__linux__)+ if (fd == -1) {+ static const uint64_t val = 1;+ buf = &val;+ len = sizeof(val);+ fd = loop->async_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();+}+++static int uv__async_start(uv_loop_t* loop) {+ int pipefd[2];+ int err;++ if (loop->async_io_watcher.fd != -1)+ return 0;++#ifdef __linux__+ err = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);+ if (err < 0)+ return UV__ERR(errno);++ pipefd[0] = err;+ pipefd[1] = -1;+#else+ err = uv__make_pipe(pipefd, UV__F_NONBLOCK);+ if (err < 0)+ return err;+#endif++ uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]);+ uv__io_start(loop, &loop->async_io_watcher, POLLIN);+ loop->async_wfd = pipefd[1];++ return 0;+}+++int uv__async_fork(uv_loop_t* loop) {+ if (loop->async_io_watcher.fd == -1) /* never started */+ return 0;++ uv__async_stop(loop);++ return uv__async_start(loop);+}+++void uv__async_stop(uv_loop_t* loop) {+ if (loop->async_io_watcher.fd == -1)+ return;++ if (loop->async_wfd != -1) {+ if (loop->async_wfd != loop->async_io_watcher.fd)+ uv__close(loop->async_wfd);+ loop->async_wfd = -1;+ }++ uv__io_stop(loop, &loop->async_io_watcher, POLLIN);+ uv__close(loop->async_io_watcher.fd);+ loop->async_io_watcher.fd = -1;+}
+ third_party/libuv/src/unix/atomic-ops.h view
@@ -0,0 +1,61 @@+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>+ *+ * 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 <atomic.h>+#endif++UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int 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(__MVS__)+ unsigned int op4;+ if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,+ (unsigned int*) ptr, *ptr, &op4))+ return oldval;+ else+ return op4;+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)+ return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);+#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 */+#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)+ __asm__ volatile("yield");+#endif+}++#endif /* UV_ATOMIC_OPS_H_ */
+ third_party/libuv/src/unix/bsd-ifaddrs.c view
@@ -0,0 +1,161 @@+/* Copyright 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.+ */++#include "uv.h"+#include "internal.h"++#include <errno.h>+#include <stddef.h>++#include <ifaddrs.h>+#include <net/if.h>+#if !defined(__CYGWIN__) && !defined(__MSYS__)+#include <net/if_dl.h>+#endif++#if defined(__HAIKU__)+#define IFF_RUNNING IFF_LINK+#endif++static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {+ if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))+ return 1;+ if (ent->ifa_addr == NULL)+ return 1;+#if !defined(__CYGWIN__) && !defined(__MSYS__)+ /*+ * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family`+ * equals to `AF_LINK` or not. Otherwise, the result depends on the operation+ * system with `AF_LINK` or `PF_INET`.+ */+ if (exclude_type == UV__EXCLUDE_IFPHYS)+ return (ent->ifa_addr->sa_family != AF_LINK);+#endif+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \+ defined(__HAIKU__)+ /*+ * On BSD getifaddrs returns information related to the raw underlying+ * devices. We're not interested in this information.+ */+ if (ent->ifa_addr->sa_family == AF_LINK)+ return 1;+#elif defined(__NetBSD__) || defined(__OpenBSD__)+ if (ent->ifa_addr->sa_family != PF_INET &&+ ent->ifa_addr->sa_family != PF_INET6)+ return 1;+#endif+ return 0;+}++int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {+ struct ifaddrs* addrs;+ struct ifaddrs* ent;+ uv_interface_address_t* address;+#if !(defined(__CYGWIN__) || defined(__MSYS__))+ int i;+#endif++ *count = 0;+ *addresses = NULL;++ if (getifaddrs(&addrs) != 0)+ return UV__ERR(errno);++ /* Count the number of interfaces */+ for (ent = addrs; ent != NULL; ent = ent->ifa_next) {+ if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))+ continue;+ (*count)++;+ }++ if (*count == 0) {+ freeifaddrs(addrs);+ return 0;+ }++ /* Make sure the memory is initiallized to zero using calloc() */+ *addresses = uv__calloc(*count, sizeof(**addresses));++ if (*addresses == NULL) {+ freeifaddrs(addrs);+ return UV_ENOMEM;+ }++ address = *addresses;++ for (ent = addrs; ent != NULL; ent = ent->ifa_next) {+ if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))+ 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++;+ }++#if !(defined(__CYGWIN__) || defined(__MSYS__))+ /* Fill in physical addresses for each interface */+ for (ent = addrs; ent != NULL; ent = ent->ifa_next) {+ if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))+ continue;++ address = *addresses;++ for (i = 0; i < *count; i++) {+ if (strcmp(address->name, ent->ifa_name) == 0) {+ struct sockaddr_dl* sa_addr;+ sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);+ memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));+ }+ address++;+ }+ }+#endif++ 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);+}
+ third_party/libuv/src/unix/core.c view
@@ -0,0 +1,1613 @@+/* 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 <stddef.h> /* NULL */+#include <stdio.h> /* printf */+#include <stdlib.h>+#include <string.h> /* strerror */+#include <errno.h>+#include <assert.h>+#include <unistd.h>+#include <sys/types.h>+#include <sys/stat.h>+#include <fcntl.h> /* O_CLOEXEC */+#include <sys/ioctl.h>+#include <sys/socket.h>+#include <sys/un.h>+#include <netinet/in.h>+#include <arpa/inet.h>+#include <limits.h> /* INT_MAX, PATH_MAX, IOV_MAX */+#include <sys/uio.h> /* writev */+#include <sys/resource.h> /* getrusage */+#include <pwd.h>+#include <sys/utsname.h>+#include <sys/time.h>++#ifdef __sun+# include <sys/filio.h>+# include <sys/types.h>+# include <sys/wait.h>+#endif++#if defined(__APPLE__)+# include <sys/filio.h>+# endif /* defined(__APPLE__) */+++#if defined(__APPLE__) && !TARGET_OS_IPHONE+# include <crt_externs.h>+# include <mach-o/dyld.h> /* _NSGetExecutablePath */+# define environ (*_NSGetEnviron())+#else /* defined(__APPLE__) && !TARGET_OS_IPHONE */+extern char** environ;+#endif /* !(defined(__APPLE__) && !TARGET_OS_IPHONE) */+++#if defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__) || \+ defined(__NetBSD__) || \+ defined(__OpenBSD__)+# include <sys/sysctl.h>+# include <sys/filio.h>+# include <sys/wait.h>+# if defined(__FreeBSD__)+# define uv__accept4 accept4+# endif+# if defined(__NetBSD__)+# define uv__accept4(a, b, c, d) paccept((a), (b), (c), NULL, (d))+# endif+#endif++#if defined(__MVS__)+#include <sys/ioctl.h>+#endif++#if defined(__linux__)+# include <sys/syscall.h>+# define uv__accept4 accept4+#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(!uv__is_closing(handle));++ handle->flags |= UV_HANDLE_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);+ /* Poll handles use file system requests, and one of them may still be+ * running. The poll code will call uv__make_close_pending() for us. */+ return;++ case UV_SIGNAL:+ uv__signal_close((uv_signal_t*) handle);+ break;++ 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 UV_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 UV_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 UV__ERR(errno);++ return 0;+}++void uv__make_close_pending(uv_handle_t* handle) {+ assert(handle->flags & UV_HANDLE_CLOSING);+ assert(!(handle->flags & UV_HANDLE_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_cached = -1;+ int iovmax;++ iovmax = uv__load_relaxed(&iovmax_cached);+ if (iovmax != -1)+ return iovmax;++ /* 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.+ */+ iovmax = sysconf(_SC_IOV_MAX);+ if (iovmax == -1)+ iovmax = 1;++ uv__store_relaxed(&iovmax_cached, iovmax);++ return iovmax;+#else+ return 1024;+#endif+}+++static void uv__finish_close(uv_handle_t* handle) {+ uv_signal_t* sh;++ /* Note: while the handle is in the UV_HANDLE_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_HANDLE_CLOSING);+ assert(!(handle->flags & UV_HANDLE_CLOSED));+ handle->flags |= UV_HANDLE_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:+ break;++ case UV_SIGNAL:+ /* If there are any caught signals "trapped" in the signal pipe,+ * we can't call the close callback yet. Reinserting the handle+ * into the closing queue makes the event loop spin but that's+ * okay because we only need to deliver the pending events.+ */+ sh = (uv_signal_t*) handle;+ if (sh->caught_signals > sh->dispatched_signals) {+ handle->flags ^= UV_HANDLE_CLOSED;+ uv__make_close_pending(handle); /* Back into the queue. */+ return;+ }+ 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);++ /* Run one final update on the provider_idle_time in case uv__io_poll+ * returned because the timeout expired, but no events were received. This+ * call will be ignored if the provider_entry_time was either never set (if+ * the timeout == 0) or was already updated b/c an event was received.+ */+ uv__metrics_update_idle_time(loop);++ 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 UV__ERR(errno);+#endif++ sockfd = socket(domain, type, protocol);+ if (sockfd == -1)+ return UV__ERR(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;+}++/* get a file pointer to a file in read-only and close-on-exec mode */+FILE* uv__open_file(const char* path) {+ int fd;+ FILE* fp;++ fd = uv__open_cloexec(path, O_RDONLY);+ if (fd < 0)+ return NULL;++ fp = fdopen(fd, "r");+ if (fp == NULL)+ uv__close(fd);++ return fp;+}+++int uv__accept(int sockfd) {+ int peerfd;+ int err;++ (void) &err;+ assert(sockfd >= 0);++ do+#ifdef uv__accept4+ peerfd = uv__accept4(sockfd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);+#else+ peerfd = accept(sockfd, NULL, NULL);+#endif+ while (peerfd == -1 && errno == EINTR);++ if (peerfd == -1)+ return UV__ERR(errno);++#ifndef uv__accept4+ err = uv__cloexec(peerfd, 1);+ if (err == 0)+ err = uv__nonblock(peerfd, 1);++ if (err != 0) {+ uv__close(peerfd);+ return err;+ }+#endif++ return peerfd;+}+++/* close() on macos has the "interesting" quirk that it fails with EINTR+ * without closing the file descriptor when a thread is in the cancel state.+ * That's why libuv calls close$NOCANCEL() instead.+ *+ * glibc on linux has a similar issue: close() is a cancellation point and+ * will unwind the thread when it's in the cancel state. Work around that+ * by making the system call directly. Musl libc is unaffected.+ */+int uv__close_nocancel(int fd) {+#if defined(__APPLE__)+#pragma GCC diagnostic push+#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"+#if defined(__LP64__) || TARGET_OS_IPHONE+ extern int close$NOCANCEL(int);+ return close$NOCANCEL(fd);+#else+ extern int close$NOCANCEL$UNIX2003(int);+ return close$NOCANCEL$UNIX2003(fd);+#endif+#pragma GCC diagnostic pop+#elif defined(__linux__)+ return syscall(SYS_close, fd);+#else+ return close(fd);+#endif+}+++int uv__close_nocheckstdio(int fd) {+ int saved_errno;+ int rc;++ assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */++ saved_errno = errno;+ rc = uv__close_nocancel(fd);+ if (rc == -1) {+ rc = UV__ERR(errno);+ if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS))+ rc = 0; /* The close is in progress, not an error. */+ errno = saved_errno;+ }++ return rc;+}+++int uv__close(int fd) {+ assert(fd > STDERR_FILENO); /* Catch stdio close bugs. */+#if defined(__MVS__)+ SAVE_ERRNO(epoll_file_close(fd));+#endif+ return uv__close_nocheckstdio(fd);+}+++int uv__nonblock_ioctl(int fd, int set) {+ int r;++ do+ r = ioctl(fd, FIONBIO, &set);+ while (r == -1 && errno == EINTR);++ if (r)+ return UV__ERR(errno);++ return 0;+}+++#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__)+int uv__cloexec_ioctl(int fd, int set) {+ int r;++ do+ r = ioctl(fd, set ? FIOCLEX : FIONCLEX);+ while (r == -1 && errno == EINTR);++ if (r)+ return UV__ERR(errno);++ return 0;+}+#endif+++int uv__nonblock_fcntl(int fd, int set) {+ int flags;+ int r;++ do+ r = fcntl(fd, F_GETFL);+ while (r == -1 && errno == EINTR);++ if (r == -1)+ return UV__ERR(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 UV__ERR(errno);++ return 0;+}+++int uv__cloexec_fcntl(int fd, int set) {+ int flags;+ int r;++ do+ r = fcntl(fd, F_GETFD);+ while (r == -1 && errno == EINTR);++ if (r == -1)+ return UV__ERR(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 UV__ERR(errno);++ return 0;+}+++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 (0 == uv__load_relaxed(&no_msg_cmsg_cloexec)) {+ rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */+ if (rc != -1)+ return rc;+ if (errno != EINVAL)+ return UV__ERR(errno);+ rc = recvmsg(fd, msg, flags);+ if (rc == -1)+ return UV__ERR(errno);+ uv__store_relaxed(&no_msg_cmsg_cloexec, 1);+ } else {+ rc = recvmsg(fd, msg, flags);+ }+#else+ rc = recvmsg(fd, msg, flags);+#endif+ if (rc == -1)+ return UV__ERR(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) {+ char scratch[1 + UV__PATH_MAX];++ if (buffer == NULL || size == NULL)+ return UV_EINVAL;++ /* Try to read directly into the user's buffer first... */+ if (getcwd(buffer, *size) != NULL)+ goto fixup;++ if (errno != ERANGE)+ return UV__ERR(errno);++ /* ...or into scratch space if the user's buffer is too small+ * so we can report how much space to provide on the next try.+ */+ if (getcwd(scratch, sizeof(scratch)) == NULL)+ return UV__ERR(errno);++ buffer = scratch;++fixup:++ *size = strlen(buffer);++ if (*size > 1 && buffer[*size - 1] == '/') {+ *size -= 1;+ buffer[*size] = '\0';+ }++ if (buffer == scratch) {+ *size += 1;+ return UV_ENOBUFS;+ }++ return 0;+}+++int uv_chdir(const char* dir) {+ if (chdir(dir))+ return UV__ERR(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 UV_EINVAL;+ }++ if (uv__is_closing(handle) || fd_out == -1)+ return UV_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, 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__reallocf(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 & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));+ 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)+ 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 & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));+ 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, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);+ QUEUE_REMOVE(&w->pending_queue);++ /* Remove stale events for this file descriptor */+ if (w->fd != -1)+ 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 & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));+ assert(0 != events);+ return 0 != (w->pevents & events);+}+++int uv__fd_exists(uv_loop_t* loop, int fd) {+ return (unsigned) fd < loop->nwatchers && loop->watchers[fd] != NULL;+}+++int uv_getrusage(uv_rusage_t* rusage) {+ struct rusage usage;++ if (getrusage(RUSAGE_SELF, &usage))+ return UV__ERR(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;++#if !defined(__MVS__) && !defined(__HAIKU__)+ 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;+#endif++ return 0;+}+++int uv__open_cloexec(const char* path, int flags) {+#if defined(O_CLOEXEC)+ int fd;++ fd = open(path, flags | O_CLOEXEC);+ if (fd == -1)+ return UV__ERR(errno);++ return fd;+#else /* O_CLOEXEC */+ int err;+ int fd;++ fd = open(path, flags);+ if (fd == -1)+ return UV__ERR(errno);++ err = uv__cloexec(fd, 1);+ if (err) {+ uv__close(fd);+ return err;+ }++ return fd;+#endif /* O_CLOEXEC */+}+++int uv__dup2_cloexec(int oldfd, int newfd) {+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__linux__)+ int r;++ r = dup3(oldfd, newfd, O_CLOEXEC);+ if (r == -1)+ return UV__ERR(errno);++ return r;+#else+ int err;+ int r;++ r = dup2(oldfd, newfd); /* Never retry. */+ if (r == -1)+ return UV__ERR(errno);++ err = uv__cloexec(newfd, 1);+ if (err != 0) {+ uv__close(newfd);+ return err;+ }++ return r;+#endif+}+++int uv_os_homedir(char* buffer, size_t* size) {+ uv_passwd_t pwd;+ size_t len;+ int r;++ /* Check if the HOME environment variable is set first. The task of+ performing input validation on buffer and size is taken care of by+ uv_os_getenv(). */+ r = uv_os_getenv("HOME", buffer, size);++ if (r != UV_ENOENT)+ return r;++ /* HOME is not set, so call uv__getpwuid_r() */+ r = uv__getpwuid_r(&pwd);++ if (r != 0) {+ return r;+ }++ len = strlen(pwd.homedir);++ if (len >= *size) {+ *size = len + 1;+ uv_os_free_passwd(&pwd);+ return UV_ENOBUFS;+ }++ memcpy(buffer, pwd.homedir, len + 1);+ *size = len;+ uv_os_free_passwd(&pwd);++ return 0;+}+++int uv_os_tmpdir(char* buffer, size_t* size) {+ const char* buf;+ size_t len;++ if (buffer == NULL || size == NULL || *size == 0)+ return UV_EINVAL;++#define CHECK_ENV_VAR(name) \+ do { \+ buf = getenv(name); \+ if (buf != NULL) \+ goto return_buffer; \+ } \+ while (0)++ /* Check the TMPDIR, TMP, TEMP, and TEMPDIR environment variables in order */+ CHECK_ENV_VAR("TMPDIR");+ CHECK_ENV_VAR("TMP");+ CHECK_ENV_VAR("TEMP");+ CHECK_ENV_VAR("TEMPDIR");++#undef CHECK_ENV_VAR++ /* No temp environment variables defined */+ #if defined(__ANDROID__)+ buf = "/data/local/tmp";+ #else+ buf = "/tmp";+ #endif++return_buffer:+ len = strlen(buf);++ if (len >= *size) {+ *size = len + 1;+ return UV_ENOBUFS;+ }++ /* The returned directory should not have a trailing slash. */+ if (len > 1 && buf[len - 1] == '/') {+ len--;+ }++ memcpy(buffer, buf, len + 1);+ buffer[len] = '\0';+ *size = len;++ return 0;+}+++int uv__getpwuid_r(uv_passwd_t* pwd) {+ struct passwd pw;+ struct passwd* result;+ char* buf;+ uid_t uid;+ size_t bufsize;+ size_t name_size;+ size_t homedir_size;+ size_t shell_size;+ long initsize;+ int r;++ if (pwd == NULL)+ return UV_EINVAL;++ initsize = sysconf(_SC_GETPW_R_SIZE_MAX);++ if (initsize <= 0)+ bufsize = 4096;+ else+ bufsize = (size_t) initsize;++ uid = geteuid();+ buf = NULL;++ for (;;) {+ uv__free(buf);+ buf = uv__malloc(bufsize);++ if (buf == NULL)+ return UV_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 UV_ENOENT;+ }++ /* Allocate memory for the username, shell, and home directory */+ name_size = strlen(pw.pw_name) + 1;+ homedir_size = strlen(pw.pw_dir) + 1;+ shell_size = strlen(pw.pw_shell) + 1;+ pwd->username = uv__malloc(name_size + homedir_size + shell_size);++ if (pwd->username == NULL) {+ uv__free(buf);+ return UV_ENOMEM;+ }++ /* Copy the username */+ memcpy(pwd->username, pw.pw_name, name_size);++ /* Copy the home directory */+ pwd->homedir = pwd->username + name_size;+ memcpy(pwd->homedir, pw.pw_dir, homedir_size);++ /* Copy the shell */+ pwd->shell = pwd->homedir + homedir_size;+ memcpy(pwd->shell, pw.pw_shell, shell_size);++ /* Copy the uid and gid */+ pwd->uid = pw.pw_uid;+ pwd->gid = pw.pw_gid;++ uv__free(buf);++ return 0;+}+++void uv_os_free_passwd(uv_passwd_t* pwd) {+ if (pwd == NULL)+ return;++ /*+ The memory for name, shell, and homedir are allocated in a single+ uv__malloc() call. The base of the pointer is stored in pwd->username, so+ that is the field that needs to be freed.+ */+ uv__free(pwd->username);+ pwd->username = NULL;+ pwd->shell = NULL;+ pwd->homedir = NULL;+}+++int uv_os_get_passwd(uv_passwd_t* pwd) {+ return uv__getpwuid_r(pwd);+}+++int uv_translate_sys_error(int sys_errno) {+ /* If < 0 then it's already a libuv error. */+ return sys_errno <= 0 ? sys_errno : -sys_errno;+}+++int uv_os_environ(uv_env_item_t** envitems, int* count) {+ int i, j, cnt;+ uv_env_item_t* envitem;++ *envitems = NULL;+ *count = 0;++ for (i = 0; environ[i] != NULL; i++);++ *envitems = uv__calloc(i, sizeof(**envitems));++ if (*envitems == NULL)+ return UV_ENOMEM;++ for (j = 0, cnt = 0; j < i; j++) {+ char* buf;+ char* ptr;++ if (environ[j] == NULL)+ break;++ buf = uv__strdup(environ[j]);+ if (buf == NULL)+ goto fail;++ ptr = strchr(buf, '=');+ if (ptr == NULL) {+ uv__free(buf);+ continue;+ }++ *ptr = '\0';++ envitem = &(*envitems)[cnt];+ envitem->name = buf;+ envitem->value = ptr + 1;++ cnt++;+ }++ *count = cnt;+ return 0;++fail:+ for (i = 0; i < cnt; i++) {+ envitem = &(*envitems)[cnt];+ uv__free(envitem->name);+ }+ uv__free(*envitems);++ *envitems = NULL;+ *count = 0;+ return UV_ENOMEM;+}+++int uv_os_getenv(const char* name, char* buffer, size_t* size) {+ char* var;+ size_t len;++ if (name == NULL || buffer == NULL || size == NULL || *size == 0)+ return UV_EINVAL;++ var = getenv(name);++ if (var == NULL)+ return UV_ENOENT;++ len = strlen(var);++ if (len >= *size) {+ *size = len + 1;+ return UV_ENOBUFS;+ }++ memcpy(buffer, var, len + 1);+ *size = len;++ return 0;+}+++int uv_os_setenv(const char* name, const char* value) {+ if (name == NULL || value == NULL)+ return UV_EINVAL;++ if (setenv(name, value, 1) != 0)+ return UV__ERR(errno);++ return 0;+}+++int uv_os_unsetenv(const char* name) {+ if (name == NULL)+ return UV_EINVAL;++ if (unsetenv(name) != 0)+ return UV__ERR(errno);++ return 0;+}+++int uv_os_gethostname(char* buffer, size_t* size) {+ /*+ On some platforms, if the input buffer is not large enough, gethostname()+ succeeds, but truncates the result. libuv can detect this and return ENOBUFS+ instead by creating a large enough buffer and comparing the hostname length+ to the size input.+ */+ char buf[UV_MAXHOSTNAMESIZE];+ size_t len;++ if (buffer == NULL || size == NULL || *size == 0)+ return UV_EINVAL;++ if (gethostname(buf, sizeof(buf)) != 0)+ return UV__ERR(errno);++ buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */+ len = strlen(buf);++ if (len >= *size) {+ *size = len + 1;+ return UV_ENOBUFS;+ }++ memcpy(buffer, buf, len + 1);+ *size = len;+ return 0;+}+++uv_os_fd_t uv_get_osfhandle(int fd) {+ return fd;+}++int uv_open_osfhandle(uv_os_fd_t os_fd) {+ return os_fd;+}++uv_pid_t uv_os_getpid(void) {+ return getpid();+}+++uv_pid_t uv_os_getppid(void) {+ return getppid();+}+++int uv_os_getpriority(uv_pid_t pid, int* priority) {+ int r;++ if (priority == NULL)+ return UV_EINVAL;++ errno = 0;+ r = getpriority(PRIO_PROCESS, (int) pid);++ if (r == -1 && errno != 0)+ return UV__ERR(errno);++ *priority = r;+ return 0;+}+++int uv_os_setpriority(uv_pid_t pid, int priority) {+ if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW)+ return UV_EINVAL;++ if (setpriority(PRIO_PROCESS, (int) pid, priority) != 0)+ return UV__ERR(errno);++ return 0;+}+++int uv_os_uname(uv_utsname_t* buffer) {+ struct utsname buf;+ int r;++ if (buffer == NULL)+ return UV_EINVAL;++ if (uname(&buf) == -1) {+ r = UV__ERR(errno);+ goto error;+ }++ r = uv__strscpy(buffer->sysname, buf.sysname, sizeof(buffer->sysname));+ if (r == UV_E2BIG)+ goto error;++#ifdef _AIX+ r = snprintf(buffer->release,+ sizeof(buffer->release),+ "%s.%s",+ buf.version,+ buf.release);+ if (r >= sizeof(buffer->release)) {+ r = UV_E2BIG;+ goto error;+ }+#else+ r = uv__strscpy(buffer->release, buf.release, sizeof(buffer->release));+ if (r == UV_E2BIG)+ goto error;+#endif++ r = uv__strscpy(buffer->version, buf.version, sizeof(buffer->version));+ if (r == UV_E2BIG)+ goto error;++#if defined(_AIX) || defined(__PASE__)+ r = uv__strscpy(buffer->machine, "ppc64", sizeof(buffer->machine));+#else+ r = uv__strscpy(buffer->machine, buf.machine, sizeof(buffer->machine));+#endif++ if (r == UV_E2BIG)+ goto error;++ return 0;++error:+ buffer->sysname[0] = '\0';+ buffer->release[0] = '\0';+ buffer->version[0] = '\0';+ buffer->machine[0] = '\0';+ return r;+}++int uv__getsockpeername(const uv_handle_t* handle,+ uv__peersockfunc func,+ struct sockaddr* name,+ int* namelen) {+ socklen_t socklen;+ uv_os_fd_t fd;+ int r;++ r = uv_fileno(handle, &fd);+ if (r < 0)+ return r;++ /* sizeof(socklen_t) != sizeof(int) on some systems. */+ socklen = (socklen_t) *namelen;++ if (func(fd, name, &socklen))+ return UV__ERR(errno);++ *namelen = (int) socklen;+ return 0;+}++int uv_gettimeofday(uv_timeval64_t* tv) {+ struct timeval time;++ if (tv == NULL)+ return UV_EINVAL;++ if (gettimeofday(&time, NULL) != 0)+ return UV__ERR(errno);++ tv->tv_sec = (int64_t) time.tv_sec;+ tv->tv_usec = (int32_t) time.tv_usec;+ return 0;+}++void uv_sleep(unsigned int msec) {+ struct timespec timeout;+ int rc;++ timeout.tv_sec = msec / 1000;+ timeout.tv_nsec = (msec % 1000) * 1000 * 1000;++ do+ rc = nanosleep(&timeout, &timeout);+ while (rc == -1 && errno == EINTR);++ assert(rc == 0);+}++int uv__search_path(const char* prog, char* buf, size_t* buflen) {+ char abspath[UV__PATH_MAX];+ size_t abspath_size;+ char trypath[UV__PATH_MAX];+ char* cloned_path;+ char* path_env;+ char* token;++ if (buf == NULL || buflen == NULL || *buflen == 0)+ return UV_EINVAL;++ /*+ * Possibilities for prog:+ * 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(prog, '/') != NULL) {+ if (realpath(prog, abspath) != abspath)+ return UV__ERR(errno);++ abspath_size = strlen(abspath);++ *buflen -= 1;+ if (*buflen > abspath_size)+ *buflen = abspath_size;++ memcpy(buf, abspath, *buflen);+ buf[*buflen] = '\0';++ return 0;+ } ++ /* Case iii). Search PATH environment variable */+ cloned_path = NULL;+ token = NULL;+ path_env = getenv("PATH");++ if (path_env == NULL)+ return UV_EINVAL;++ cloned_path = uv__strdup(path_env);+ if (cloned_path == NULL)+ return UV_ENOMEM;++ token = strtok(cloned_path, ":");+ while (token != NULL) {+ snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog);+ if (realpath(trypath, abspath) == abspath) {+ /* Check the match is executable */+ if (access(abspath, X_OK) == 0) {+ abspath_size = strlen(abspath);++ *buflen -= 1;+ if (*buflen > abspath_size)+ *buflen = abspath_size;++ memcpy(buf, abspath, *buflen);+ buf[*buflen] = '\0';++ uv__free(cloned_path);+ return 0;+ }+ }+ token = strtok(NULL, ":");+ }+ uv__free(cloned_path);++ /* Out of tokens (path entries), and no match found */+ return UV_EINVAL;+}
+ third_party/libuv/src/unix/darwin-proctitle.c view
@@ -0,0 +1,192 @@+/* 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 <dlfcn.h>+#include <errno.h>+#include <pthread.h>+#include <stdlib.h>+#include <string.h>++#include <TargetConditionals.h>++#if !TARGET_OS_IPHONE+#include "darwin-stub.h"+#endif+++static int uv__pthread_setname_np(const char* name) {+ char namebuf[64]; /* MAXTHREADNAMESIZE */+ int err;++ strncpy(namebuf, name, sizeof(namebuf) - 1);+ namebuf[sizeof(namebuf) - 1] = '\0';++ err = pthread_setname_np(namebuf);+ if (err)+ return UV__ERR(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);+ CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef);+ void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t,+ void*);+ CFTypeRef asn;+ int err;++ err = UV_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;++ *(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName(+ launch_services_bundle,+ S("_LSApplicationCheckIn"));++ if (pLSApplicationCheckIn == NULL)+ goto out;++ *(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) =+ pCFBundleGetFunctionPointerForName(+ launch_services_bundle,+ S("_LSSetApplicationLaunchServicesServerConnectionStatus"));++ if (pLSSetApplicationLaunchServicesServerConnectionStatus == NULL)+ goto out;++ pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL);++ /* Check into process manager?! */+ pLSApplicationCheckIn(-2,+ pCFBundleGetInfoDictionary(pCFBundleGetMainBundle()));++ asn = pLSGetCurrentApplicationASN();++ err = UV_EBUSY;+ if (asn == NULL)+ goto out;++ err = UV_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 */+}
+ third_party/libuv/src/unix/darwin-stub.h view
@@ -0,0 +1,113 @@+/* Copyright 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.+ */++#ifndef UV_DARWIN_STUB_H_+#define UV_DARWIN_STUB_H_++#include <stdint.h>++struct CFArrayCallBacks;+struct CFRunLoopSourceContext;+struct FSEventStreamContext;+struct CFRange;++typedef double CFAbsoluteTime;+typedef double CFTimeInterval;+typedef int FSEventStreamEventFlags;+typedef int OSStatus;+typedef long CFIndex;+typedef struct CFArrayCallBacks CFArrayCallBacks;+typedef struct CFRunLoopSourceContext CFRunLoopSourceContext;+typedef struct FSEventStreamContext FSEventStreamContext;+typedef uint32_t FSEventStreamCreateFlags;+typedef uint64_t FSEventStreamEventId;+typedef unsigned CFStringEncoding;+typedef void* CFAllocatorRef;+typedef void* CFArrayRef;+typedef void* CFBundleRef;+typedef void* CFDataRef;+typedef void* CFDictionaryRef;+typedef void* CFMutableDictionaryRef;+typedef struct CFRange CFRange;+typedef void* CFRunLoopRef;+typedef void* CFRunLoopSourceRef;+typedef void* CFStringRef;+typedef void* CFTypeRef;+typedef void* FSEventStreamRef;++typedef uint32_t IOOptionBits;+typedef unsigned int io_iterator_t;+typedef unsigned int io_object_t;+typedef unsigned int io_service_t;+typedef unsigned int io_registry_entry_t;+++typedef void (*FSEventStreamCallback)(const FSEventStreamRef,+ void*,+ size_t,+ void*,+ const FSEventStreamEventFlags*,+ const FSEventStreamEventId*);++struct CFRunLoopSourceContext {+ CFIndex version;+ void* info;+ void* pad[7];+ void (*perform)(void*);+};++struct FSEventStreamContext {+ CFIndex version;+ void* info;+ void* pad[3];+};++struct CFRange {+ CFIndex location;+ CFIndex length;+};++static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100;+static const OSStatus noErr = 0;++static const FSEventStreamEventId kFSEventStreamEventIdSinceNow = -1;++static const int kFSEventStreamCreateFlagNoDefer = 2;+static const int kFSEventStreamCreateFlagFileEvents = 16;++static const int kFSEventStreamEventFlagEventIdsWrapped = 8;+static const int kFSEventStreamEventFlagHistoryDone = 16;+static const int kFSEventStreamEventFlagItemChangeOwner = 0x4000;+static const int kFSEventStreamEventFlagItemCreated = 0x100;+static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x2000;+static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x400;+static const int kFSEventStreamEventFlagItemIsDir = 0x20000;+static const int kFSEventStreamEventFlagItemModified = 0x1000;+static const int kFSEventStreamEventFlagItemRemoved = 0x200;+static const int kFSEventStreamEventFlagItemRenamed = 0x800;+static const int kFSEventStreamEventFlagItemXattrMod = 0x8000;+static const int kFSEventStreamEventFlagKernelDropped = 4;+static const int kFSEventStreamEventFlagMount = 64;+static const int kFSEventStreamEventFlagRootChanged = 32;+static const int kFSEventStreamEventFlagUnmount = 128;+static const int kFSEventStreamEventFlagUserDropped = 2;++#endif /* UV_DARWIN_STUB_H_ */
+ third_party/libuv/src/unix/darwin.c view
@@ -0,0 +1,371 @@+/* 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 <assert.h>+#include <stdint.h>+#include <errno.h>++#include <dlfcn.h>+#include <mach/mach.h>+#include <mach/mach_time.h>+#include <mach-o/dyld.h> /* _NSGetExecutablePath */+#include <sys/resource.h>+#include <sys/sysctl.h>+#include <unistd.h> /* sysconf */++#if !TARGET_OS_IPHONE+#include "darwin-stub.h"+#endif++static uv_once_t once = UV_ONCE_INIT;+static uint64_t (*time_func)(void);+static mach_timebase_info_data_t timebase;++typedef unsigned char UInt8;++int uv__platform_loop_init(uv_loop_t* loop) {+ loop->cf_state = NULL;++ if (uv__kqueue_init(loop))+ return UV__ERR(errno);++ return 0;+}+++void uv__platform_loop_delete(uv_loop_t* loop) {+ uv__fsevents_loop_delete(loop);+}+++static void uv__hrtime_init_once(void) {+ if (KERN_SUCCESS != mach_timebase_info(&timebase))+ abort();++ time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");+ if (time_func == NULL)+ time_func = mach_absolute_time;+}+++uint64_t uv__hrtime(uv_clocktype_t type) {+ uv_once(&once, uv__hrtime_init_once);+ return time_func() * timebase.numer / timebase.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 UV_EINVAL;++ exepath_size = sizeof(exepath);+ if (_NSGetExecutablePath(exepath, &exepath_size))+ return UV_EIO;++ if (realpath(exepath, abspath) != abspath)+ return UV__ERR(errno);++ abspath_size = strlen(abspath);+ if (abspath_size == 0)+ return UV_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 UV_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, ARRAY_SIZE(which), &info, &size, NULL, 0))+ return UV__ERR(errno);++ return (uint64_t) info;+}+++uint64_t uv_get_constrained_memory(void) {+ return 0; /* Memory constraints are unknown. */+}+++void uv_loadavg(double avg[3]) {+ struct loadavg info;+ size_t size = sizeof(info);+ int which[] = {CTL_VM, VM_LOADAVG};++ if (sysctl(which, ARRAY_SIZE(which), &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, ARRAY_SIZE(which), &info, &size, NULL, 0))+ return UV__ERR(errno);++ now = time(NULL);+ *uptime = now - info.tv_sec;++ return 0;+}++static int uv__get_cpu_speed(uint64_t* speed) {+ /* IOKit */+ void (*pIOObjectRelease)(io_object_t);+ kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*);+ CFMutableDictionaryRef (*pIOServiceMatching)(const char*);+ kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t,+ CFMutableDictionaryRef,+ io_iterator_t*);+ io_service_t (*pIOIteratorNext)(io_iterator_t);+ CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t,+ CFStringRef,+ CFAllocatorRef,+ IOOptionBits);++ /* CoreFoundation */+ CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,+ const char*,+ CFStringEncoding);+ CFStringEncoding (*pCFStringGetSystemEncoding)(void);+ UInt8 *(*pCFDataGetBytePtr)(CFDataRef);+ CFIndex (*pCFDataGetLength)(CFDataRef);+ void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*);+ void (*pCFRelease)(CFTypeRef);++ void* core_foundation_handle;+ void* iokit_handle;+ int err;++ kern_return_t kr;+ mach_port_t mach_port;+ io_iterator_t it;+ io_object_t service;++ mach_port = 0;++ err = UV_ENOENT;+ core_foundation_handle = dlopen("/System/Library/Frameworks/"+ "CoreFoundation.framework/"+ "Versions/A/CoreFoundation",+ RTLD_LAZY | RTLD_LOCAL);+ iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/"+ "Versions/A/IOKit",+ RTLD_LAZY | RTLD_LOCAL);++ if (core_foundation_handle == NULL || iokit_handle == NULL)+ goto out;++#define V(handle, symbol) \+ do { \+ *(void **)(&p ## symbol) = dlsym((handle), #symbol); \+ if (p ## symbol == NULL) \+ goto out; \+ } \+ while (0)+ V(iokit_handle, IOMasterPort);+ V(iokit_handle, IOServiceMatching);+ V(iokit_handle, IOServiceGetMatchingServices);+ V(iokit_handle, IOIteratorNext);+ V(iokit_handle, IOObjectRelease);+ V(iokit_handle, IORegistryEntryCreateCFProperty);+ V(core_foundation_handle, CFStringCreateWithCString);+ V(core_foundation_handle, CFStringGetSystemEncoding);+ V(core_foundation_handle, CFDataGetBytePtr);+ V(core_foundation_handle, CFDataGetLength);+ V(core_foundation_handle, CFDataGetBytes);+ V(core_foundation_handle, CFRelease);+#undef V++#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)++ kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);+ assert(kr == KERN_SUCCESS);+ CFMutableDictionaryRef classes_to_match+ = pIOServiceMatching("IOPlatformDevice");+ kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it);+ assert(kr == KERN_SUCCESS);+ service = pIOIteratorNext(it);++ CFStringRef device_type_str = S("device_type");+ CFStringRef clock_frequency_str = S("clock-frequency");++ while (service != 0) {+ CFDataRef data;+ data = pIORegistryEntryCreateCFProperty(service,+ device_type_str,+ NULL,+ 0);+ if (data) {+ const UInt8* raw = pCFDataGetBytePtr(data);+ if (strncmp((char*)raw, "cpu", 3) == 0 ||+ strncmp((char*)raw, "processor", 9) == 0) {+ CFDataRef freq_ref;+ freq_ref = pIORegistryEntryCreateCFProperty(service,+ clock_frequency_str,+ NULL,+ 0);+ if (freq_ref) {+ uint32_t freq;+ CFIndex len = pCFDataGetLength(freq_ref);+ CFRange range;+ range.location = 0;+ range.length = len;++ pCFDataGetBytes(freq_ref, range, (UInt8*)&freq);+ *speed = freq;+ pCFRelease(freq_ref);+ pCFRelease(data);+ break;+ }+ }+ pCFRelease(data);+ }++ service = pIOIteratorNext(it);+ }++ pIOObjectRelease(it);++ err = 0;+out:+ if (core_foundation_handle != NULL)+ dlclose(core_foundation_handle);++ if (iokit_handle != NULL)+ dlclose(iokit_handle);++ mach_port_deallocate(mach_task_self(), mach_port);++ return err;+}++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];+ 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;+ uint64_t cpuspeed;+ int err;++ size = sizeof(model);+ if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&+ sysctlbyname("hw.model", &model, &size, NULL, 0)) {+ return UV__ERR(errno);+ }++ err = uv__get_cpu_speed(&cpuspeed);+ if (err < 0)+ return err;++ if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,+ (processor_info_array_t*)&info,+ &msg_type) != KERN_SUCCESS) {+ return UV_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 UV_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;+}
+ third_party/libuv/src/unix/dl.c view
@@ -0,0 +1,80 @@+/* 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 <dlfcn.h>+#include <errno.h>+#include <string.h>+#include <locale.h>++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;+ }+}
+ third_party/libuv/src/unix/fs.c view
@@ -0,0 +1,2121 @@+/* 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 <errno.h>+#include <dlfcn.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <limits.h> /* PATH_MAX */++#include <sys/types.h>+#include <sys/socket.h>+#include <sys/stat.h>+#include <sys/time.h>+#include <sys/uio.h>+#include <pthread.h>+#include <unistd.h>+#include <fcntl.h>+#include <poll.h>++#if defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__) || \+ defined(__OpenBSD__) || \+ defined(__NetBSD__)+# define HAVE_PREADV 1+#else+# define HAVE_PREADV 0+#endif++#if defined(__linux__) || defined(__sun)+# include <sys/sendfile.h>+#endif++#if defined(__APPLE__)+# include <sys/sysctl.h>+#elif defined(__linux__) && !defined(FICLONE)+# include <sys/ioctl.h>+# define FICLONE _IOW(0x94, 9, int)+#endif++#if defined(_AIX) && !defined(_AIX71)+# include <utime.h>+#endif++#if defined(__APPLE__) || \+ defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__) || \+ defined(__OpenBSD__) || \+ defined(__NetBSD__)+# include <sys/param.h>+# include <sys/mount.h>+#elif defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__)+# include <sys/statvfs.h>+#else+# include <sys/statfs.h>+#endif++#if defined(_AIX) && _XOPEN_SOURCE <= 600+extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */+#endif++#define INIT(subtype) \+ do { \+ if (req == NULL) \+ return UV_EINVAL; \+ UV_REQ_INIT(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->bufs = 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) \+ return UV_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) \+ return UV_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__req_register(loop, req); \+ uv__work_submit(loop, \+ &req->work_req, \+ UV__WORK_FAST_IO, \+ uv__fs_work, \+ uv__fs_done); \+ return 0; \+ } \+ else { \+ uv__fs_work(&req->work_req); \+ return req->result; \+ } \+ } \+ while (0)+++static int uv__fs_close(int fd) {+ int rc;++ rc = uv__close_nocancel(fd);+ if (rc == -1)+ if (errno == EINTR || errno == EINPROGRESS)+ rc = 0; /* The close is in progress, not an error. */++ return rc;+}+++static ssize_t uv__fs_fsync(uv_fs_t* req) {+#if defined(__APPLE__)+ /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache+ * to the drive platters. This is in contrast to Linux's fdatasync and fsync+ * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent+ * for flushing buffered data to permanent storage. If F_FULLFSYNC is not+ * supported by the file system we fall back to F_BARRIERFSYNC or fsync().+ * This is the same approach taken by sqlite, except sqlite does not issue+ * an F_BARRIERFSYNC call.+ */+ int r;++ r = fcntl(req->file, F_FULLFSYNC);+ if (r != 0)+ r = fcntl(req->file, 85 /* F_BARRIERFSYNC */); /* fsync + barrier */+ if (r != 0)+ r = fsync(req->file);+ return r;+#else+ return fsync(req->file);+#endif+}+++static ssize_t uv__fs_fdatasync(uv_fs_t* req) {+#if defined(__linux__) || defined(__sun) || defined(__NetBSD__)+ return fdatasync(req->file);+#elif defined(__APPLE__)+ /* See the comment in uv__fs_fsync. */+ return uv__fs_fsync(req);+#else+ return fsync(req->file);+#endif+}+++UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) {+ struct timespec ts;+ ts.tv_sec = time;+ ts.tv_nsec = (uint64_t)(time * 1000000) % 1000000 * 1000;+ return ts;+}++UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) {+ struct timeval tv;+ tv.tv_sec = time;+ tv.tv_usec = (uint64_t)(time * 1000000) % 1000000;+ return tv;+}++static ssize_t uv__fs_futime(uv_fs_t* req) {+#if defined(__linux__) \+ || defined(_AIX71) \+ || defined(__HAIKU__)+ /* utimesat() has nanosecond resolution but we stick to microseconds+ * for the sake of consistency with other platforms.+ */+ struct timespec ts[2];+ ts[0] = uv__fs_to_timespec(req->atime);+ ts[1] = uv__fs_to_timespec(req->mtime);+ return futimens(req->file, ts);+#elif defined(__APPLE__) \+ || defined(__DragonFly__) \+ || defined(__FreeBSD__) \+ || defined(__FreeBSD_kernel__) \+ || defined(__NetBSD__) \+ || defined(__OpenBSD__) \+ || defined(__sun)+ struct timeval tv[2];+ tv[0] = uv__fs_to_timeval(req->atime);+ tv[1] = uv__fs_to_timeval(req->mtime);+# if defined(__sun)+ return futimesat(req->file, NULL, tv);+# else+ return futimes(req->file, tv);+# endif+#elif defined(__MVS__)+ attrib_t atr;+ memset(&atr, 0, sizeof(atr));+ atr.att_mtimechg = 1;+ atr.att_atimechg = 1;+ atr.att_mtime = req->mtime;+ atr.att_atime = req->atime;+ return __fchattr(req->file, &atr, sizeof(atr));+#else+ errno = ENOSYS;+ return -1;+#endif+}+++static ssize_t uv__fs_mkdtemp(uv_fs_t* req) {+ return mkdtemp((char*) req->path) ? 0 : -1;+}+++static int (*uv__mkostemp)(char*, int);+++static void uv__mkostemp_initonce(void) {+ /* z/os doesn't have RTLD_DEFAULT but that's okay+ * because it doesn't have mkostemp(O_CLOEXEC) either.+ */+#ifdef RTLD_DEFAULT+ uv__mkostemp = (int (*)(char*, int)) dlsym(RTLD_DEFAULT, "mkostemp");++ /* We don't care about errors, but we do want to clean them up.+ * If there has been no error, then dlerror() will just return+ * NULL.+ */+ dlerror();+#endif /* RTLD_DEFAULT */+}+++static int uv__fs_mkstemp(uv_fs_t* req) {+ static uv_once_t once = UV_ONCE_INIT;+ int r;+#ifdef O_CLOEXEC+ static int no_cloexec_support;+#endif+ static const char pattern[] = "XXXXXX";+ static const size_t pattern_size = sizeof(pattern) - 1;+ char* path;+ size_t path_length;++ path = (char*) req->path;+ path_length = strlen(path);++ /* EINVAL can be returned for 2 reasons:+ 1. The template's last 6 characters were not XXXXXX+ 2. open() didn't support O_CLOEXEC+ We want to avoid going to the fallback path in case+ of 1, so it's manually checked before. */+ if (path_length < pattern_size ||+ strcmp(path + path_length - pattern_size, pattern)) {+ errno = EINVAL;+ r = -1;+ goto clobber;+ }++ uv_once(&once, uv__mkostemp_initonce);++#ifdef O_CLOEXEC+ if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) {+ r = uv__mkostemp(path, O_CLOEXEC);++ if (r >= 0)+ return r;++ /* If mkostemp() returns EINVAL, it means the kernel doesn't+ support O_CLOEXEC, so we just fallback to mkstemp() below. */+ if (errno != EINVAL)+ goto clobber;++ /* We set the static variable so that next calls don't even+ try to use mkostemp. */+ uv__store_relaxed(&no_cloexec_support, 1);+ }+#endif /* O_CLOEXEC */++ if (req->cb != NULL)+ uv_rwlock_rdlock(&req->loop->cloexec_lock);++ r = mkstemp(path);++ /* 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)+ abort();+ r = -1;+ }++ if (req->cb != NULL)+ uv_rwlock_rdunlock(&req->loop->cloexec_lock);++clobber:+ if (r < 0)+ path[0] = '\0';+ return r;+}+++static ssize_t uv__fs_open(uv_fs_t* req) {+#ifdef O_CLOEXEC+ return open(req->path, req->flags | O_CLOEXEC, req->mode);+#else /* O_CLOEXEC */+ int r;++ 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)+ abort();+ r = -1;+ }++ if (req->cb != NULL)+ uv_rwlock_rdunlock(&req->loop->cloexec_lock);++ return r;+#endif /* O_CLOEXEC */+}+++#if !HAVE_PREADV+static ssize_t uv__fs_preadv(uv_file fd,+ uv_buf_t* bufs,+ unsigned int nbufs,+ off_t off) {+ uv_buf_t* buf;+ uv_buf_t* end;+ ssize_t result;+ ssize_t rc;+ size_t pos;++ assert(nbufs > 0);++ result = 0;+ pos = 0;+ buf = bufs + 0;+ end = bufs + nbufs;++ for (;;) {+ do+ rc = pread(fd, buf->base + pos, buf->len - pos, off + result);+ while (rc == -1 && errno == EINTR);++ if (rc == 0)+ break;++ if (rc == -1 && result == 0)+ return UV__ERR(errno);++ if (rc == -1)+ break; /* We read some data so return that, ignore the error. */++ pos += rc;+ result += rc;++ if (pos < buf->len)+ continue;++ pos = 0;+ buf += 1;++ if (buf == end)+ break;+ }++ return result;+}+#endif+++static ssize_t uv__fs_read(uv_fs_t* req) {+#if defined(__linux__)+ static int no_preadv;+#endif+ unsigned int iovmax;+ ssize_t result;++ iovmax = uv__getiovmax();+ if (req->nbufs > iovmax)+ req->nbufs = iovmax;++ 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 (uv__load_relaxed(&no_preadv)) retry:+# endif+ {+ result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off);+ }+# if defined(__linux__)+ else {+ result = uv__preadv(req->file,+ (struct iovec*)req->bufs,+ req->nbufs,+ req->off);+ if (result == -1 && errno == ENOSYS) {+ uv__store_relaxed(&no_preadv, 1);+ goto retry;+ }+ }+# endif+#endif+ }++done:+ /* Early cleanup of bufs allocation, since we're done with it. */+ if (req->bufs != req->bufsml)+ uv__free(req->bufs);++ req->bufs = NULL;+ req->nbufs = 0;++#ifdef __PASE__+ /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */+ if (result == -1 && errno == EOPNOTSUPP) {+ struct stat buf;+ ssize_t rc;+ rc = fstat(req->file, &buf);+ if (rc == 0 && S_ISDIR(buf.st_mode)) {+ errno = EISDIR;+ }+ }+#endif++ return result;+}+++#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8)+#define UV_CONST_DIRENT uv__dirent_t+#else+#define UV_CONST_DIRENT const uv__dirent_t+#endif+++static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) {+ return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0;+}+++static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) {+ return strcmp((*a)->d_name, (*b)->d_name);+}+++static ssize_t uv__fs_scandir(uv_fs_t* req) {+ uv__dirent_t** dents;+ int n;++ dents = NULL;+ n = scandir(req->path, &dents, uv__fs_scandir_filter, uv__fs_scandir_sort);++ /* NOTE: We will use nbufs as an index field */+ req->nbufs = 0;++ if (n == 0) {+ /* OS X still needs to deallocate some memory.+ * Memory was allocated using the system allocator, so use free() here.+ */+ free(dents);+ dents = NULL;+ } else if (n == -1) {+ return n;+ }++ req->ptr = dents;++ return n;+}++static int uv__fs_opendir(uv_fs_t* req) {+ uv_dir_t* dir;++ dir = uv__malloc(sizeof(*dir));+ if (dir == NULL)+ goto error;++ dir->dir = opendir(req->path);+ if (dir->dir == NULL)+ goto error;++ req->ptr = dir;+ return 0;++error:+ uv__free(dir);+ req->ptr = NULL;+ return -1;+}++static int uv__fs_readdir(uv_fs_t* req) {+ uv_dir_t* dir;+ uv_dirent_t* dirent;+ struct dirent* res;+ unsigned int dirent_idx;+ unsigned int i;++ dir = req->ptr;+ dirent_idx = 0;++ while (dirent_idx < dir->nentries) {+ /* readdir() returns NULL on end of directory, as well as on error. errno+ is used to differentiate between the two conditions. */+ errno = 0;+ res = readdir(dir->dir);++ if (res == NULL) {+ if (errno != 0)+ goto error;+ break;+ }++ if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0)+ continue;++ dirent = &dir->dirents[dirent_idx];+ dirent->name = uv__strdup(res->d_name);++ if (dirent->name == NULL)+ goto error;++ dirent->type = uv__fs_get_dirent_type(res);+ ++dirent_idx;+ }++ return dirent_idx;++error:+ for (i = 0; i < dirent_idx; ++i) {+ uv__free((char*) dir->dirents[i].name);+ dir->dirents[i].name = NULL;+ }++ return -1;+}++static int uv__fs_closedir(uv_fs_t* req) {+ uv_dir_t* dir;++ dir = req->ptr;++ if (dir->dir != NULL) {+ closedir(dir->dir);+ dir->dir = NULL;+ }++ uv__free(req->ptr);+ req->ptr = NULL;+ return 0;+}++static int uv__fs_statfs(uv_fs_t* req) {+ uv_statfs_t* stat_fs;+#if defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__)+ struct statvfs buf;++ if (0 != statvfs(req->path, &buf))+#else+ struct statfs buf;++ if (0 != statfs(req->path, &buf))+#endif /* defined(__sun) */+ return -1;++ stat_fs = uv__malloc(sizeof(*stat_fs));+ if (stat_fs == NULL) {+ errno = ENOMEM;+ return -1;+ }++#if defined(__sun) || defined(__MVS__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)+ stat_fs->f_type = 0; /* f_type is not supported. */+#else+ stat_fs->f_type = buf.f_type;+#endif+ stat_fs->f_bsize = buf.f_bsize;+ stat_fs->f_blocks = buf.f_blocks;+ stat_fs->f_bfree = buf.f_bfree;+ stat_fs->f_bavail = buf.f_bavail;+ stat_fs->f_files = buf.f_files;+ stat_fs->f_ffree = buf.f_ffree;+ req->ptr = stat_fs;+ return 0;+}++static ssize_t uv__fs_pathmax_size(const char* path) {+ ssize_t pathmax;++ pathmax = pathconf(path, _PC_PATH_MAX);++ if (pathmax == -1)+ pathmax = UV__PATH_MAX;++ return pathmax;+}++static ssize_t uv__fs_readlink(uv_fs_t* req) {+ ssize_t maxlen;+ ssize_t len;+ char* buf;++#if defined(_POSIX_PATH_MAX) || defined(PATH_MAX)+ maxlen = uv__fs_pathmax_size(req->path);+#else+ /* We may not have a real PATH_MAX. Read size of link. */+ struct stat st;+ int ret;+ ret = lstat(req->path, &st);+ if (ret != 0)+ return -1;+ if (!S_ISLNK(st.st_mode)) {+ errno = EINVAL;+ return -1;+ }++ maxlen = st.st_size;++ /* According to readlink(2) lstat can report st_size == 0+ for some symlinks, such as those in /proc or /sys. */+ if (maxlen == 0)+ maxlen = uv__fs_pathmax_size(req->path);+#endif++ buf = uv__malloc(maxlen);++ if (buf == NULL) {+ errno = ENOMEM;+ return -1;+ }++#if defined(__MVS__)+ len = os390_readlink(req->path, buf, maxlen);+#else+ len = readlink(req->path, buf, maxlen);+#endif++ if (len == -1) {+ uv__free(buf);+ return -1;+ }++ /* Uncommon case: resize to make room for the trailing nul byte. */+ if (len == maxlen) {+ buf = uv__reallocf(buf, len + 1);++ if (buf == NULL)+ return -1;+ }++ buf[len] = '\0';+ req->ptr = buf;++ return 0;+}++static ssize_t uv__fs_realpath(uv_fs_t* req) {+ char* buf;++#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L+ buf = realpath(req->path, NULL);+ if (buf == NULL)+ return -1;+#else+ ssize_t len;++ 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;+ }+#endif++ 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;++#ifdef __linux__+ {+ static int copy_file_range_support = 1;++ if (copy_file_range_support) {+ r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0);++ if (r == -1 && errno == ENOSYS) {+ errno = 0;+ copy_file_range_support = 0;+ } else {+ goto ok;+ }+ }+ }+#endif++ r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len);++ok:+ /* 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(__APPLE__) || \+ defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__)+ {+ 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);+#elif defined(__FreeBSD_kernel__)+ len = 0;+ r = bsd_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) {+#if defined(__linux__) \+ || defined(_AIX71) \+ || defined(__sun) \+ || defined(__HAIKU__)+ /* utimesat() has nanosecond resolution but we stick to microseconds+ * for the sake of consistency with other platforms.+ */+ struct timespec ts[2];+ ts[0] = uv__fs_to_timespec(req->atime);+ ts[1] = uv__fs_to_timespec(req->mtime);+ return utimensat(AT_FDCWD, req->path, ts, 0);+#elif defined(__APPLE__) \+ || defined(__DragonFly__) \+ || defined(__FreeBSD__) \+ || defined(__FreeBSD_kernel__) \+ || defined(__NetBSD__) \+ || defined(__OpenBSD__)+ struct timeval tv[2];+ tv[0] = uv__fs_to_timeval(req->atime);+ tv[1] = uv__fs_to_timeval(req->mtime);+ return utimes(req->path, tv);+#elif defined(_AIX) \+ && !defined(_AIX71)+ struct utimbuf buf;+ buf.actime = req->atime;+ buf.modtime = req->mtime;+ return utime(req->path, &buf);+#elif defined(__MVS__)+ attrib_t atr;+ memset(&atr, 0, sizeof(atr));+ atr.att_mtimechg = 1;+ atr.att_atimechg = 1;+ atr.att_mtime = req->mtime;+ atr.att_atime = req->atime;+ return __lchattr((char*) req->path, &atr, sizeof(atr));+#else+ errno = ENOSYS;+ return -1;+#endif+}+++static ssize_t uv__fs_lutime(uv_fs_t* req) {+#if defined(__linux__) || \+ defined(_AIX71) || \+ defined(__sun) || \+ defined(__HAIKU__)+ struct timespec ts[2];+ ts[0] = uv__fs_to_timespec(req->atime);+ ts[1] = uv__fs_to_timespec(req->mtime);+ return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW);+#elif defined(__APPLE__) || \+ defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__) || \+ defined(__NetBSD__)+ struct timeval tv[2];+ tv[0] = uv__fs_to_timeval(req->atime);+ tv[1] = uv__fs_to_timeval(req->mtime);+ return lutimes(req->path, tv);+#else+ errno = ENOSYS;+ return -1;+#endif+}+++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+ {+ r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);+ }+# 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 ssize_t uv__fs_copyfile(uv_fs_t* req) {+ uv_fs_t fs_req;+ uv_file srcfd;+ uv_file dstfd;+ struct stat src_statsbuf;+ struct stat dst_statsbuf;+ int dst_flags;+ int result;+ int err;+ off_t bytes_to_send;+ off_t in_offset;+ off_t bytes_written;+ size_t bytes_chunk;++ dstfd = -1;+ err = 0;++ /* Open the source file. */+ srcfd = uv_fs_open(NULL, &fs_req, req->path, O_RDONLY, 0, NULL);+ uv_fs_req_cleanup(&fs_req);++ if (srcfd < 0)+ return srcfd;++ /* Get the source file's mode. */+ if (fstat(srcfd, &src_statsbuf)) {+ err = UV__ERR(errno);+ goto out;+ }++ dst_flags = O_WRONLY | O_CREAT;++ if (req->flags & UV_FS_COPYFILE_EXCL)+ dst_flags |= O_EXCL;++ /* Open the destination file. */+ dstfd = uv_fs_open(NULL,+ &fs_req,+ req->new_path,+ dst_flags,+ src_statsbuf.st_mode,+ NULL);+ uv_fs_req_cleanup(&fs_req);++ if (dstfd < 0) {+ err = dstfd;+ goto out;+ }++ /* If the file is not being opened exclusively, verify that the source and+ destination are not the same file. If they are the same, bail out early. */+ if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) {+ /* Get the destination file's mode. */+ if (fstat(dstfd, &dst_statsbuf)) {+ err = UV__ERR(errno);+ goto out;+ }++ /* Check if srcfd and dstfd refer to the same file */+ if (src_statsbuf.st_dev == dst_statsbuf.st_dev &&+ src_statsbuf.st_ino == dst_statsbuf.st_ino) {+ goto out;+ }++ /* Truncate the file in case the destination already existed. */+ if (ftruncate(dstfd, 0) != 0) {+ err = UV__ERR(errno);+ goto out;+ }+ }++ if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {+ err = UV__ERR(errno);+#ifdef __linux__+ if (err != UV_EPERM)+ goto out;++ {+ struct statfs s;++ /* fchmod() on CIFS shares always fails with EPERM unless the share is+ * mounted with "noperm". As fchmod() is a meaningless operation on such+ * shares anyway, detect that condition and squelch the error.+ */+ if (fstatfs(dstfd, &s) == -1)+ goto out;++ if (s.f_type != /* CIFS */ 0xFF534D42u)+ goto out;+ }++ err = 0;+#else /* !__linux__ */+ goto out;+#endif /* !__linux__ */+ }++#ifdef FICLONE+ if (req->flags & UV_FS_COPYFILE_FICLONE ||+ req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {+ if (ioctl(dstfd, FICLONE, srcfd) == 0) {+ /* ioctl() with FICLONE succeeded. */+ goto out;+ }+ /* If an error occurred and force was set, return the error to the caller;+ * fall back to sendfile() when force was not set. */+ if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {+ err = UV__ERR(errno);+ goto out;+ }+ }+#else+ if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {+ err = UV_ENOSYS;+ goto out;+ }+#endif++ bytes_to_send = src_statsbuf.st_size;+ in_offset = 0;+ while (bytes_to_send != 0) {+ bytes_chunk = SSIZE_MAX;+ if (bytes_to_send < (off_t) bytes_chunk)+ bytes_chunk = bytes_to_send;+ uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_chunk, NULL);+ bytes_written = fs_req.result;+ uv_fs_req_cleanup(&fs_req);++ if (bytes_written < 0) {+ err = bytes_written;+ break;+ }++ bytes_to_send -= bytes_written;+ in_offset += bytes_written;+ }++out:+ if (err < 0)+ result = err;+ else+ result = 0;++ /* Close the source file. */+ err = uv__close_nocheckstdio(srcfd);++ /* Don't overwrite any existing errors. */+ if (err != 0 && result == 0)+ result = err;++ /* Close the destination file if it is open. */+ if (dstfd >= 0) {+ err = uv__close_nocheckstdio(dstfd);++ /* Don't overwrite any existing errors. */+ if (err != 0 && result == 0)+ result = err;++ /* Remove the destination file if something went wrong. */+ if (result != 0) {+ uv_fs_unlink(NULL, &fs_req, req->new_path, NULL);+ /* Ignore the unlink return value, as an error already happened. */+ uv_fs_req_cleanup(&fs_req);+ }+ }++ if (result == 0)+ return 0;++ errno = UV__ERR(result);+ return -1;+}++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_atimensec;+ dst->st_mtim.tv_sec = src->st_mtime;+ dst->st_mtim.tv_nsec = src->st_mtimensec;+ dst->st_ctim.tv_sec = src->st_ctime;+ dst->st_ctim.tv_nsec = src->st_ctimensec;+ dst->st_birthtim.tv_sec = src->st_ctime;+ dst->st_birthtim.tv_nsec = src->st_ctimensec;+ dst->st_flags = 0;+ dst->st_gen = 0;+#elif !defined(_AIX) && ( \+ defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__OpenBSD__) || \+ defined(__NetBSD__) || \+ defined(_GNU_SOURCE) || \+ 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(__FreeBSD__) || \+ 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_statx(int fd,+ const char* path,+ int is_fstat,+ int is_lstat,+ uv_stat_t* buf) {+ STATIC_ASSERT(UV_ENOSYS != -1);+#ifdef __linux__+ static int no_statx;+ struct uv__statx statxbuf;+ int dirfd;+ int flags;+ int mode;+ int rc;++ if (uv__load_relaxed(&no_statx))+ return UV_ENOSYS;++ dirfd = AT_FDCWD;+ flags = 0; /* AT_STATX_SYNC_AS_STAT */+ mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */++ if (is_fstat) {+ dirfd = fd;+ flags |= 0x1000; /* AT_EMPTY_PATH */+ }++ if (is_lstat)+ flags |= AT_SYMLINK_NOFOLLOW;++ rc = uv__statx(dirfd, path, flags, mode, &statxbuf);++ switch (rc) {+ case 0:+ break;+ case -1:+ /* EPERM happens when a seccomp filter rejects the system call.+ * Has been observed with libseccomp < 2.3.3 and docker < 18.04.+ */+ if (errno != EINVAL && errno != EPERM && errno != ENOSYS)+ return -1;+ /* Fall through. */+ default:+ /* Normally on success, zero is returned and On error, -1 is returned.+ * Observed on S390 RHEL running in a docker container with statx not+ * implemented, rc might return 1 with 0 set as the error code in which+ * case we return ENOSYS.+ */+ uv__store_relaxed(&no_statx, 1);+ return UV_ENOSYS;+ }++ buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor;+ buf->st_mode = statxbuf.stx_mode;+ buf->st_nlink = statxbuf.stx_nlink;+ buf->st_uid = statxbuf.stx_uid;+ buf->st_gid = statxbuf.stx_gid;+ buf->st_rdev = statxbuf.stx_rdev_major;+ buf->st_ino = statxbuf.stx_ino;+ buf->st_size = statxbuf.stx_size;+ buf->st_blksize = statxbuf.stx_blksize;+ buf->st_blocks = statxbuf.stx_blocks;+ buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;+ buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;+ buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;+ buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;+ buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;+ buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;+ buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec;+ buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec;+ buf->st_flags = 0;+ buf->st_gen = 0;++ return 0;+#else+ return UV_ENOSYS;+#endif /* __linux__ */+}+++static int uv__fs_stat(const char *path, uv_stat_t *buf) {+ struct stat pbuf;+ int ret;++ ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf);+ if (ret != UV_ENOSYS)+ return ret;++ ret = stat(path, &pbuf);+ if (ret == 0)+ 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 = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf);+ if (ret != UV_ENOSYS)+ return ret;++ ret = lstat(path, &pbuf);+ if (ret == 0)+ uv__to_stat(&pbuf, buf);++ return ret;+}+++static int uv__fs_fstat(int fd, uv_stat_t *buf) {+ struct stat pbuf;+ int ret;++ ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf);+ if (ret != UV_ENOSYS)+ return ret;++ ret = fstat(fd, &pbuf);+ if (ret == 0)+ uv__to_stat(&pbuf, buf);++ return ret;+}++static size_t uv__fs_buf_offset(uv_buf_t* bufs, size_t size) {+ size_t offset;+ /* Figure out which bufs are done */+ for (offset = 0; size > 0 && bufs[offset].len <= size; ++offset)+ size -= bufs[offset].len;++ /* Fix a partial read/write */+ if (size > 0) {+ bufs[offset].base += size;+ bufs[offset].len -= size;+ }+ return offset;+}++static ssize_t uv__fs_write_all(uv_fs_t* req) {+ 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;++ do+ result = uv__fs_write(req);+ while (result < 0 && errno == EINTR);++ if (result <= 0) {+ if (total == 0)+ total = result;+ break;+ }++ if (req->off >= 0)+ req->off += result;++ req->nbufs = uv__fs_buf_offset(req->bufs, result);+ req->bufs += req->nbufs;+ nbufs -= req->nbufs;+ total += result;+ }++ if (bufs != req->bufsml)+ uv__free(bufs);++ req->bufs = NULL;+ req->nbufs = 0;++ 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 ||+ req->fs_type == UV_FS_READ);++ 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, uv__fs_close(req->file));+ X(COPYFILE, uv__fs_copyfile(req));+ X(FCHMOD, fchmod(req->file, req->mode));+ X(FCHOWN, fchown(req->file, req->uid, req->gid));+ X(LCHOWN, lchown(req->path, req->uid, req->gid));+ X(FDATASYNC, uv__fs_fdatasync(req));+ X(FSTAT, uv__fs_fstat(req->file, &req->statbuf));+ X(FSYNC, uv__fs_fsync(req));+ X(FTRUNCATE, ftruncate(req->file, req->off));+ X(FUTIME, uv__fs_futime(req));+ X(LUTIME, uv__fs_lutime(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(MKSTEMP, uv__fs_mkstemp(req));+ X(OPEN, uv__fs_open(req));+ X(READ, uv__fs_read(req));+ X(SCANDIR, uv__fs_scandir(req));+ X(OPENDIR, uv__fs_opendir(req));+ X(READDIR, uv__fs_readdir(req));+ X(CLOSEDIR, uv__fs_closedir(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(STATFS, uv__fs_statfs(req));+ X(SYMLINK, symlink(req->path, req->new_path));+ X(UNLINK, unlink(req->path));+ X(UTIME, uv__fs_utime(req));+ X(WRITE, uv__fs_write_all(req));+ default: abort();+ }+#undef X+ } while (r == -1 && errno == EINTR && retry_on_eintr);++ if (r == -1)+ req->result = UV__ERR(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 == UV_ECANCELED) {+ assert(req->result == 0);+ req->result = UV_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_lchown(uv_loop_t* loop,+ uv_fs_t* req,+ const char* path,+ uv_uid_t uid,+ uv_gid_t gid,+ uv_fs_cb cb) {+ INIT(LCHOWN);+ PATH;+ 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_lutime(uv_loop_t* loop,+ uv_fs_t* req,+ const char* path,+ double atime,+ double mtime,+ uv_fs_cb cb) {+ INIT(LUTIME);+ PATH;+ 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)+ return UV_ENOMEM;+ POST;+}+++int uv_fs_mkstemp(uv_loop_t* loop,+ uv_fs_t* req,+ const char* tpl,+ uv_fs_cb cb) {+ INIT(MKSTEMP);+ req->path = uv__strdup(tpl);+ if (req->path == NULL)+ return UV_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) {+ INIT(READ);++ if (bufs == NULL || nbufs == 0)+ return UV_EINVAL;++ 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)+ return UV_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_opendir(uv_loop_t* loop,+ uv_fs_t* req,+ const char* path,+ uv_fs_cb cb) {+ INIT(OPENDIR);+ PATH;+ POST;+}++int uv_fs_readdir(uv_loop_t* loop,+ uv_fs_t* req,+ uv_dir_t* dir,+ uv_fs_cb cb) {+ INIT(READDIR);++ if (dir == NULL || dir->dir == NULL || dir->dirents == NULL)+ return UV_EINVAL;++ req->ptr = dir;+ POST;+}++int uv_fs_closedir(uv_loop_t* loop,+ uv_fs_t* req,+ uv_dir_t* dir,+ uv_fs_cb cb) {+ INIT(CLOSEDIR);++ if (dir == NULL)+ return UV_EINVAL;++ req->ptr = dir;+ 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) {+ INIT(WRITE);++ if (bufs == NULL || nbufs == 0)+ return UV_EINVAL;++ 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)+ return UV_ENOMEM;++ memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));++ req->off = off;+ POST;+}+++void uv_fs_req_cleanup(uv_fs_t* req) {+ if (req == NULL)+ return;++ /* 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 and+ * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory.+ */+ if (req->path != NULL &&+ (req->cb != NULL ||+ req->fs_type == UV_FS_MKDTEMP || req->fs_type == UV_FS_MKSTEMP))+ 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_READDIR && req->ptr != NULL)+ uv__fs_readdir_cleanup(req);++ if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)+ uv__fs_scandir_cleanup(req);++ if (req->bufs != req->bufsml)+ uv__free(req->bufs);+ req->bufs = NULL;++ if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf)+ uv__free(req->ptr);+ req->ptr = NULL;+}+++int uv_fs_copyfile(uv_loop_t* loop,+ uv_fs_t* req,+ const char* path,+ const char* new_path,+ int flags,+ uv_fs_cb cb) {+ INIT(COPYFILE);++ if (flags & ~(UV_FS_COPYFILE_EXCL |+ UV_FS_COPYFILE_FICLONE |+ UV_FS_COPYFILE_FICLONE_FORCE)) {+ return UV_EINVAL;+ }++ PATH2;+ req->flags = flags;+ POST;+}+++int uv_fs_statfs(uv_loop_t* loop,+ uv_fs_t* req,+ const char* path,+ uv_fs_cb cb) {+ INIT(STATFS);+ PATH;+ POST;+}++int uv_fs_get_system_error(const uv_fs_t* req) {+ return -req->result;+}
+ third_party/libuv/src/unix/fsevents.c view
@@ -0,0 +1,923 @@+/* 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 || MAC_OS_X_VERSION_MAX_ALLOWED < 1070++/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */+/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */++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 "darwin-stub.h"++#include <dlfcn.h>+#include <assert.h>+#include <stdlib.h>+#include <pthread.h>++static const int kFSEventsModified =+ kFSEventStreamEventFlagItemChangeOwner |+ kFSEventStreamEventFlagItemFinderInfoMod |+ kFSEventStreamEventFlagItemInodeMetaMod |+ kFSEventStreamEventFlagItemModified |+ kFSEventStreamEventFlagItemXattrMod;++static const int kFSEventsRenamed =+ kFSEventStreamEventFlagItemCreated |+ kFSEventStreamEventFlagItemRemoved |+ kFSEventStreamEventFlagItemRenamed;++static const int 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 int (*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(const FSEventStreamRef 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;+ FSEventStreamEventFlags flags;+ 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++) {+ flags = eventFlags[i];++ /* Ignore system events */+ if (flags & kFSEventsSystem)+ continue;++ path = paths[i];+ len = strlen(path);++ if (handle->realpath_len == 0)+ continue; /* This should be unreachable */++ /* Filter out paths that are outside handle's request */+ if (len < handle->realpath_len)+ continue;++ /* Make sure that realpath actually named a directory,+ * (unless watching root, which alone keeps a trailing slash on the realpath)+ * or that we matched the whole string */+ if (handle->realpath_len != len &&+ handle->realpath_len > 1 &&+ path[handle->realpath_len] != '/')+ continue;++ if (memcmp(path, handle->realpath, handle->realpath_len) != 0)+ continue;++ if (!(handle->realpath_len == 1 && handle->realpath[0] == '/')) {+ /* Remove common prefix, unless the watched folder is "/" */+ path += handle->realpath_len;+ len -= handle->realpath_len;++ /* Ignore events with path equal to directory itself */+ if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))+ continue;++ if (len == 0) {+ /* Since we're using fsevents to watch the file itself,+ * realpath == path, and we now need to get the basename of the file back+ * (for commonality with other codepaths and platforms). */+ while (len < handle->realpath_len && path[-1] != '/') {+ path--;+ len++;+ }+ /* Created and Removed seem to be always set, but don't make sense */+ flags &= ~kFSEventsRenamed;+ } else {+ /* Skip forward slash */+ path++;+ len--;+ }+ }++ /* 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;+ }++ event = uv__malloc(sizeof(*event) + len);+ if (event == NULL)+ break;++ memset(event, 0, sizeof(*event));+ memcpy(event->path, path, len + 1);+ event->events = UV_RENAME;++ if (0 == (flags & kFSEventsRenamed)) {+ if (0 != (flags & kFSEventsModified) ||+ 0 == (flags & kFSEventStreamEventFlagItemIsDir))+ event->events = UV_CHANGE;+ }++ 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 */+ memset(&ctx, 0, sizeof(ctx));+ ctx.info = loop;++ 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 UV_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;++ /* 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 = UV_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 = UV_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 = UV_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 = UV_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 UV_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 = UV_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 = UV__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);++ state->loop = NULL;++ 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 UV_ENOMEM;++ item->handle = handle;+ item->type = type;++ uv_mutex_lock(&loop->cf_mutex);+ QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);++ state = loop->cf_state;+ assert(state != NULL);+ pCFRunLoopSourceSignal(state->signal_source);+ pCFRunLoopWakeUp(state->loop);++ uv_mutex_unlock(&loop->cf_mutex);++ 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 UV__ERR(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 = UV_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 UV_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 UV__ERR(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 */
+ third_party/libuv/src/unix/getaddrinfo.c view
@@ -0,0 +1,255 @@+/* 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 "idna.h"++#include <errno.h>+#include <stddef.h> /* NULL */+#include <stdlib.h>+#include <string.h>+#include <net/if.h> /* if_indextoname() */++/* EAI_* constants. */+#include <netdb.h>+++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 UV__ERR(errno);+#endif+ }+ assert(!"unknown EAI_* error code");+ abort();+#ifndef __SUNPRO_C+ return 0; /* Pacify compiler. */+#endif+}+++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 == UV_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) {+ char hostname_ascii[256];+ size_t hostname_len;+ size_t service_len;+ size_t hints_len;+ size_t len;+ char* buf;+ long rc;++ if (req == NULL || (hostname == NULL && service == NULL))+ return UV_EINVAL;++ /* FIXME(bnoordhuis) IDNA does not seem to work z/OS,+ * probably because it uses EBCDIC rather than ASCII.+ */+#ifdef __MVS__+ (void) &hostname_ascii;+#else+ if (hostname != NULL) {+ rc = uv__idna_toascii(hostname,+ hostname + strlen(hostname),+ hostname_ascii,+ hostname_ascii + sizeof(hostname_ascii));+ if (rc < 0)+ return rc;+ hostname = hostname_ascii;+ }+#endif++ 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 UV_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__WORK_SLOW_IO,+ 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);+}+++int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {+ char ifname_buf[UV_IF_NAMESIZE];+ size_t len;++ if (buffer == NULL || size == NULL || *size == 0)+ return UV_EINVAL;++ if (if_indextoname(ifindex, ifname_buf) == NULL)+ return UV__ERR(errno);++ len = strnlen(ifname_buf, sizeof(ifname_buf));++ if (*size <= len) {+ *size = len + 1;+ return UV_ENOBUFS;+ }++ memcpy(buffer, ifname_buf, len);+ buffer[len] = '\0';+ *size = len;++ return 0;+}++int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {+ return uv_if_indextoname(ifindex, buffer, size);+}
+ third_party/libuv/src/unix/getnameinfo.c view
@@ -0,0 +1,121 @@+/* 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 <assert.h>+#include <stdlib.h>+#include <stdio.h>+#include <string.h>++#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 == 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_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__WORK_SLOW_IO,+ 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;+ }+}
+ third_party/libuv/src/unix/internal.h view
@@ -0,0 +1,351 @@+/* 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 <assert.h>+#include <limits.h> /* _POSIX_PATH_MAX, PATH_MAX */+#include <stdlib.h> /* abort */+#include <string.h> /* strrchr */+#include <fcntl.h> /* O_CLOEXEC and O_NONBLOCK, if supported. */+#include <stdio.h>+#include <errno.h>+#include <sys/socket.h>++#if defined(__STRICT_ANSI__)+# define inline __inline+#endif++#if defined(__linux__)+# include "linux-syscalls.h"+#endif /* __linux__ */++#if defined(__MVS__)+# include "os390-syscalls.h"+#endif /* __MVS__ */++#if defined(__sun)+# include <sys/port.h>+# include <port.h>+#endif /* __sun */++#if defined(_AIX)+# define reqevents events+# define rtnevents revents+# include <sys/poll.h>+#else+# include <poll.h>+#endif /* _AIX */++#if defined(__APPLE__) && !TARGET_OS_IPHONE+# include <AvailabilityMacros.h>+#endif++#if defined(PATH_MAX)+# define UV__PATH_MAX PATH_MAX+#else+# define UV__PATH_MAX 8192+#endif++#if defined(__ANDROID__)+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++#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)+# define UV_UNUSED(declaration) __attribute__((unused)) declaration+#else+# define UV_UNUSED(declaration) declaration+#endif++/* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */+#ifdef POLLRDHUP+# define UV__POLLRDHUP POLLRDHUP+#else+# define UV__POLLRDHUP 0x2000+#endif++#ifdef POLLPRI+# define UV__POLLPRI POLLPRI+#else+# define UV__POLLPRI 0+#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;++/* loop flags */+enum {+ UV_LOOP_BLOCK_SIGPROF = 1+};++/* flags of excluding ifaddr */+enum {+ UV__EXCLUDE_IFPHYS,+ UV__EXCLUDE_IFADDR+};++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];+};+++#if defined(_AIX) || \+ defined(__APPLE__) || \+ defined(__DragonFly__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__) || \+ defined(__linux__) || \+ defined(__OpenBSD__) || \+ defined(__NetBSD__)+#define uv__cloexec uv__cloexec_ioctl+#define uv__nonblock uv__nonblock_ioctl+#else+#define uv__cloexec uv__cloexec_fcntl+#define uv__nonblock uv__nonblock_fcntl+#endif++/* On Linux, uv__nonblock_fcntl() and uv__nonblock_ioctl() do not commute+ * when O_NDELAY is not equal to O_NONBLOCK. Case in point: linux/sparc32+ * and linux/sparc64, where O_NDELAY is O_NONBLOCK + another bit.+ *+ * Libuv uses uv__nonblock_fcntl() directly sometimes so ensure that it+ * commutes with uv__nonblock().+ */+#if defined(__linux__) && O_NDELAY != O_NONBLOCK+#undef uv__nonblock+#define uv__nonblock uv__nonblock_fcntl+#endif++/* core */+int uv__cloexec_ioctl(int fd, int set);+int uv__cloexec_fcntl(int fd, int set);+int uv__nonblock_ioctl(int fd, int set);+int uv__nonblock_fcntl(int fd, int set);+int uv__close(int fd); /* preserves errno */+int uv__close_nocheckstdio(int fd);+int uv__close_nocancel(int fd);+int uv__socket(int domain, int type, int protocol);+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);+int uv__io_check_fd(uv_loop_t* loop, int fd);+void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */+int uv__io_fork(uv_loop_t* loop);+int uv__fd_exists(uv_loop_t* loop, int fd);++/* async */+void uv__async_stop(uv_loop_t* loop);+int uv__async_fork(uv_loop_t* loop);+++/* 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);++/* 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);+int uv__signal_loop_fork(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__udp_close(uv_udp_t* handle);+void uv__udp_finish_close(uv_udp_t* handle);+uv_handle_type uv__handle_type(int fd);+FILE* uv__open_file(const char* path);+int uv__getpwuid_r(uv_passwd_t* pwd);+int uv__search_path(const char* prog, char* buf, size_t* buflen);++/* random */+int uv__random_devurandom(void* buf, size_t buflen);+int uv__random_getrandom(void* buf, size_t buflen);+int uv__random_getentropy(void* buf, size_t buflen);+int uv__random_readpath(const char* path, void* buf, size_t buflen);+int uv__random_sysctl(void* buf, size_t buflen);++#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 O_NONBLOCK+# define UV__F_NONBLOCK O_NONBLOCK+#else+# define UV__F_NONBLOCK 1+#endif++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);++#endif /* defined(__APPLE__) */++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;+}++#if defined(__linux__)+int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);+#endif++typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);++int uv__getsockpeername(const uv_handle_t* handle,+ uv__peersockfunc func,+ struct sockaddr* name,+ int* namelen);++#if defined(__linux__) || \+ defined(__FreeBSD__) || \+ defined(__FreeBSD_kernel__)+#define HAVE_MMSG 1+struct uv__mmsghdr {+ struct msghdr msg_hdr;+ unsigned int msg_len;+};++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);+#else+#define HAVE_MMSG 0+#endif+++#endif /* UV_UNIX_INTERNAL_H_ */
+ third_party/libuv/src/unix/kqueue.c view
@@ -0,0 +1,585 @@+/* 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 <assert.h>+#include <stdlib.h>+#include <string.h>+#include <errno.h>++#include <sys/sysctl.h>+#include <sys/types.h>+#include <sys/event.h>+#include <sys/time.h>+#include <unistd.h>+#include <fcntl.h>+#include <time.h>++/*+ * Required on+ * - Until at least FreeBSD 11.0+ * - Older versions of Mac OS X+ *+ * http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp+ */+#ifndef EV_OOBAND+#define EV_OOBAND EV_FLAG1+#endif++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 UV__ERR(errno);++ uv__cloexec(loop->backend_fd, 1);++ return 0;+}+++#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070+static int uv__has_forked_with_cfrunloop;+#endif++int uv__io_fork(uv_loop_t* loop) {+ int err;+ loop->backend_fd = -1;+ err = uv__kqueue_init(loop);+ if (err)+ return err;++#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070+ if (loop->cf_state != NULL) {+ /* We cannot start another CFRunloop and/or thread in the child+ process; CF aborts if you try or if you try to touch the thread+ at all to kill it. So the best we can do is ignore it from now+ on. This means we can't watch directories in the same way+ anymore (like other BSDs). It also means we cannot properly+ clean up the allocated resources; calling+ uv__fsevents_loop_delete from uv_loop_close will crash the+ process. So we sidestep the issue by pretending like we never+ started it in the first place.+ */+ uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1);+ uv__free(loop->cf_state);+ loop->cf_state = NULL;+ }+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */+ return err;+}+++int uv__io_check_fd(uv_loop_t* loop, int fd) {+ struct kevent ev;+ int rc;++ rc = 0;+ EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);+ if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))+ rc = UV__ERR(errno);++ EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);+ if (rc == 0)+ if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))+ abort();++ return rc;+}+++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 have_signals;+ int filter;+ int fflags;+ int count;+ int nfds;+ int fd;+ int op;+ int i;+ int user_timeout;+ int reset_timeout;++ 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 & POLLIN) == 0 && (w->pevents & 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 & POLLOUT) == 0 && (w->pevents & 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;+ }+ }++ if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {+ EV_SET(events + nevents, w->fd, EV_OOBAND, 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. */++ if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {+ reset_timeout = 1;+ user_timeout = timeout;+ timeout = 0;+ } else {+ reset_timeout = 0;+ }++ for (;; nevents = 0) {+ /* Only need to set the provider_entry_time if timeout != 0. The function+ * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.+ */+ if (timeout != 0)+ uv__metrics_set_provider_entry_time(loop);++ 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) {+ if (reset_timeout != 0) {+ timeout = user_timeout;+ reset_timeout = 0;+ if (timeout == -1)+ continue;+ if (timeout > 0)+ goto update_timeout;+ }++ assert(timeout != -1);+ return;+ }++ if (nfds == -1) {+ if (errno != EINTR)+ abort();++ if (reset_timeout != 0) {+ timeout = user_timeout;+ reset_timeout = 0;+ }++ if (timeout == 0)+ return;++ if (timeout == -1)+ continue;++ /* Interrupted by a signal. Update timeout and poll again. */+ goto update_timeout;+ }++ have_signals = 0;+ 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 == POLLIN);+ assert(w->pevents == POLLIN);+ uv__metrics_update_idle_time(loop);+ w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */+ nevents++;+ continue;+ }++ revents = 0;++ if (ev->filter == EVFILT_READ) {+ if (w->pevents & POLLIN) {+ revents |= 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 == EV_OOBAND) {+ if (w->pevents & UV__POLLPRI) {+ revents |= UV__POLLPRI;+ 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 & POLLOUT) {+ revents |= 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 |= POLLERR;++ if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))+ revents |= UV__POLLRDHUP;++ if (revents == 0)+ continue;++ /* Run signal watchers last. This also affects child process watchers+ * because those are implemented in terms of signal watchers.+ */+ if (w == &loop->signal_io_watcher) {+ have_signals = 1;+ } else {+ uv__metrics_update_idle_time(loop);+ w->cb(loop, w, revents);+ }++ nevents++;+ }++ if (reset_timeout != 0) {+ timeout = user_timeout;+ reset_timeout = 0;+ }++ if (have_signals != 0) {+ uv__metrics_update_idle_time(loop);+ loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);+ }++ loop->watchers[loop->nwatchers] = NULL;+ loop->watchers[loop->nwatchers + 1] = NULL;++ if (have_signals != 0)+ return; /* Event loop should cycle now so don't poll again. */++ 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);+ assert(fd >= 0);++ 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) {+ int fd;+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070+ struct stat statbuf;+#endif++ if (uv__is_active(handle))+ return UV_EINVAL;++ handle->cb = cb;+ handle->path = uv__strdup(path);+ if (handle->path == NULL)+ return UV_ENOMEM;++ /* TODO open asynchronously - but how do we report back errors? */+ fd = open(handle->path, O_RDONLY);+ if (fd == -1) {+ uv__free(handle->path);+ handle->path = NULL;+ return UV__ERR(errno);+ }++#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070+ /* 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;++ if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) {+ int r;+ /* The fallback fd is no longer needed */+ uv__close_nocheckstdio(fd);+ handle->event_watcher.fd = -1;+ r = uv__fsevents_init(handle);+ if (r == 0) {+ uv__handle_start(handle);+ } else {+ uv__free(handle->path);+ handle->path = NULL;+ }+ return r;+ }+fallback:+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */++ uv__handle_start(handle);+ uv__io_init(&handle->event_watcher, uv__fs_event, fd);+ uv__io_start(handle->loop, &handle->event_watcher, POLLIN);++ return 0;+}+++int uv_fs_event_stop(uv_fs_event_t* handle) {+ int r;+ r = 0;++ if (!uv__is_active(handle))+ return 0;++ uv__handle_stop(handle);++#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070+ if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop))+ if (handle->cf_cb != NULL)+ r = uv__fsevents_close(handle);+#endif++ if (handle->event_watcher.fd != -1) {+ uv__io_close(handle->loop, &handle->event_watcher);+ uv__close(handle->event_watcher.fd);+ handle->event_watcher.fd = -1;+ }++ uv__free(handle->path);+ handle->path = NULL;++ return r;+}+++void uv__fs_event_close(uv_fs_event_t* handle) {+ uv_fs_event_stop(handle);+}
+ third_party/libuv/src/unix/linux-core.c view
@@ -0,0 +1,1145 @@+/* 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.+ */++/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their+ * EPOLL* counterparts. We use the POLL* variants in this file because that+ * is what libuv uses elsewhere.+ */++#include "uv.h"+#include "internal.h"++#include <inttypes.h>+#include <stdint.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <assert.h>+#include <errno.h>++#include <net/if.h>+#include <sys/epoll.h>+#include <sys/param.h>+#include <sys/prctl.h>+#include <sys/sysinfo.h>+#include <unistd.h>+#include <fcntl.h>+#include <time.h>++#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 "uv/android-ifaddrs.h"+# else+# include <ifaddrs.h>+# endif+# include <sys/socket.h>+# include <net/ethernet.h>+# include <netpacket/packet.h>+#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 <linux/time.h> but we can't+ * include that file because it conflicts with <time.h>. 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(FILE* statfile_fp,+ unsigned int numcpus,+ uv_cpu_info_t* ci);+static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);+static uint64_t read_cpufreq(unsigned int cpunum);+++int uv__platform_loop_init(uv_loop_t* loop) {+ int fd;+ fd = epoll_create1(O_CLOEXEC);++ /* epoll_create1() can fail either because it's not implemented (old kernel)+ * or because it doesn't understand the O_CLOEXEC flag.+ */+ if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {+ fd = 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 UV__ERR(errno);++ return 0;+}+++int uv__io_fork(uv_loop_t* loop) {+ int err;+ void* old_watchers;++ old_watchers = loop->inotify_watchers;++ uv__close(loop->backend_fd);+ loop->backend_fd = -1;+ uv__platform_loop_delete(loop);++ err = uv__platform_loop_init(loop);+ if (err)+ return err;++ return uv__inotify_fork(loop, old_watchers);+}+++void uv__platform_loop_delete(uv_loop_t* loop) {+ if (loop->inotify_fd == -1) return;+ uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);+ uv__close(loop->inotify_fd);+ loop->inotify_fd = -1;+}+++void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {+ struct epoll_event* events;+ struct epoll_event dummy;+ uintptr_t i;+ uintptr_t nfds;++ assert(loop->watchers != NULL);+ assert(fd >= 0);++ events = (struct 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 (events[i].data.fd == fd)+ events[i].data.fd = -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));+ epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);+ }+}+++int uv__io_check_fd(uv_loop_t* loop, int fd) {+ struct epoll_event e;+ int rc;++ memset(&e, 0, sizeof(e));+ e.events = POLLIN;+ e.data.fd = -1;++ rc = 0;+ if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e))+ if (errno != EEXIST)+ rc = UV__ERR(errno);++ if (rc == 0)+ if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e))+ abort();++ return rc;+}+++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_cached;+ static int no_epoll_wait_cached;+ int no_epoll_pwait;+ int no_epoll_wait;+ struct epoll_event events[1024];+ struct epoll_event* pe;+ struct epoll_event e;+ int real_timeout;+ QUEUE* q;+ uv__io_t* w;+ sigset_t sigset;+ uint64_t sigmask;+ uint64_t base;+ int have_signals;+ int nevents;+ int count;+ int nfds;+ int fd;+ int op;+ int i;+ int user_timeout;+ int reset_timeout;++ if (loop->nfds == 0) {+ assert(QUEUE_EMPTY(&loop->watcher_queue));+ return;+ }++ memset(&e, 0, sizeof(e));++ 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.fd = w->fd;++ if (w->events == 0)+ op = EPOLL_CTL_ADD;+ else+ op = 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 (epoll_ctl(loop->backend_fd, op, w->fd, &e)) {+ if (errno != EEXIST)+ abort();++ assert(op == EPOLL_CTL_ADD);++ /* We've reactivated a file descriptor that's been watched before. */+ if (epoll_ctl(loop->backend_fd, 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;++ if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {+ reset_timeout = 1;+ user_timeout = timeout;+ timeout = 0;+ } else {+ reset_timeout = 0;+ }++ /* You could argue there is a dependency between these two but+ * ultimately we don't care about their ordering with respect+ * to one another. Worst case, we make a few system calls that+ * could have been avoided because another thread already knows+ * they fail with ENOSYS. Hardly the end of the world.+ */+ no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached);+ no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached);++ for (;;) {+ /* Only need to set the provider_entry_time if timeout != 0. The function+ * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.+ */+ if (timeout != 0)+ uv__metrics_set_provider_entry_time(loop);++ /* 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 = epoll_pwait(loop->backend_fd,+ events,+ ARRAY_SIZE(events),+ timeout,+ &sigset);+ if (nfds == -1 && errno == ENOSYS) {+ uv__store_relaxed(&no_epoll_pwait_cached, 1);+ no_epoll_pwait = 1;+ }+ } else {+ nfds = epoll_wait(loop->backend_fd,+ events,+ ARRAY_SIZE(events),+ timeout);+ if (nfds == -1 && errno == ENOSYS) {+ uv__store_relaxed(&no_epoll_wait_cached, 1);+ 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);++ if (reset_timeout != 0) {+ timeout = user_timeout;+ reset_timeout = 0;+ }++ if (timeout == -1)+ continue;++ if (timeout == 0)+ return;++ /* We may have been inside the system call for longer than |timeout|+ * milliseconds so we need to update the timestamp to avoid drift.+ */+ goto update_timeout;+ }++ 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 (reset_timeout != 0) {+ timeout = user_timeout;+ reset_timeout = 0;+ }++ if (timeout == -1)+ continue;++ if (timeout == 0)+ return;++ /* Interrupted by a signal. Update timeout and poll again. */+ goto update_timeout;+ }++ have_signals = 0;+ nevents = 0;++ {+ /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */+ union {+ struct epoll_event* events;+ uv__io_t* watchers;+ } x;++ x.events = events;+ assert(loop->watchers != NULL);+ loop->watchers[loop->nwatchers] = x.watchers;+ loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;+ }++ for (i = 0; i < nfds; i++) {+ pe = events + i;+ fd = pe->data.fd;++ /* 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.+ */+ epoll_ctl(loop->backend_fd, 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 | POLLERR | 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 == POLLERR || pe->events == POLLHUP)+ pe->events |=+ w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);++ if (pe->events != 0) {+ /* Run signal watchers last. This also affects child process watchers+ * because those are implemented in terms of signal watchers.+ */+ if (w == &loop->signal_io_watcher) {+ have_signals = 1;+ } else {+ uv__metrics_update_idle_time(loop);+ w->cb(loop, w, pe->events);+ }++ nevents++;+ }+ }++ if (reset_timeout != 0) {+ timeout = user_timeout;+ reset_timeout = 0;+ }++ if (have_signals != 0) {+ uv__metrics_update_idle_time(loop);+ loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);+ }++ loop->watchers[loop->nwatchers] = NULL;+ loop->watchers[loop->nwatchers + 1] = NULL;++ if (have_signals != 0)+ return; /* Event loop should cycle now so don't poll again. */++ 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).+ */+ clock_id = CLOCK_MONOTONIC;+ if (type != UV_CLOCK_FAST)+ goto done;++ clock_id = uv__load_relaxed(&fast_clock_id);+ if (clock_id != -1)+ goto done;++ clock_id = CLOCK_MONOTONIC;+ if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t))+ if (t.tv_nsec <= 1 * 1000 * 1000)+ clock_id = CLOCK_MONOTONIC_COARSE;++ uv__store_relaxed(&fast_clock_id, clock_id);++done:++ if (clock_gettime(clock_id, &t))+ return 0; /* Not really possible. */++ return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;+}+++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 UV__ERR(errno);++ do+ n = read(fd, buf, sizeof(buf) - 1);+ while (n == -1 && errno == EINTR);++ uv__close(fd);+ if (n == -1)+ return UV__ERR(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 UV_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 UV__ERR(errno);++ *uptime = now.tv_sec;+ return 0;+}+++static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) {+ unsigned int num;+ char buf[1024];++ if (!fgets(buf, sizeof(buf), statfile_fp))+ return UV_EIO;++ num = 0;+ while (fgets(buf, sizeof(buf), statfile_fp)) {+ if (strncmp(buf, "cpu", 3))+ break;+ num++;+ }++ if (num == 0)+ return UV_EIO;++ *numcpus = num;+ return 0;+}+++int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {+ unsigned int numcpus;+ uv_cpu_info_t* ci;+ int err;+ FILE* statfile_fp;++ *cpu_infos = NULL;+ *count = 0;++ statfile_fp = uv__open_file("/proc/stat");+ if (statfile_fp == NULL)+ return UV__ERR(errno);++ err = uv__cpu_num(statfile_fp, &numcpus);+ if (err < 0)+ goto out;++ err = UV_ENOMEM;+ ci = uv__calloc(numcpus, sizeof(*ci));+ if (ci == NULL)+ goto out;++ err = read_models(numcpus, ci);+ if (err == 0)+ err = read_times(statfile_fp, numcpus, ci);++ if (err) {+ uv_free_cpu_info(ci, numcpus);+ goto out;+ }++ /* 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;+ err = 0;++out:++ if (fclose(statfile_fp))+ if (errno != EINTR && errno != EINPROGRESS)+ abort();++ return err;+}+++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 = uv__open_file("/proc/cpuinfo");+ if (fp == NULL)+ return UV__ERR(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 UV_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 UV_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 UV_ENOMEM;+ ci[model_idx++].model = model;+ }++ return 0;+}+++static int read_times(FILE* statfile_fp,+ unsigned int numcpus,+ uv_cpu_info_t* ci) {+ struct uv_cpu_times_s ts;+ unsigned int ticks;+ unsigned int multiplier;+ uint64_t user;+ uint64_t nice;+ uint64_t sys;+ uint64_t idle;+ uint64_t dummy;+ uint64_t irq;+ uint64_t num;+ uint64_t len;+ char buf[1024];++ ticks = (unsigned int)sysconf(_SC_CLK_TCK);+ multiplier = ((uint64_t)1000L / ticks);+ assert(ticks != (unsigned int) -1);+ assert(ticks != 0);++ rewind(statfile_fp);++ if (!fgets(buf, sizeof(buf), statfile_fp))+ abort();++ num = 0;++ while (fgets(buf, sizeof(buf), statfile_fp)) {+ if (num >= numcpus)+ break;++ if (strncmp(buf, "cpu", 3))+ break;++ /* skip "cpu<num> " 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,+ "%" PRIu64 " %" PRIu64 " %" PRIu64+ "%" PRIu64 " %" PRIu64 " %" PRIu64,+ &user,+ &nice,+ &sys,+ &idle,+ &dummy,+ &irq))+ abort();++ ts.user = user * multiplier;+ ts.nice = nice * multiplier;+ ts.sys = sys * multiplier;+ ts.idle = idle * multiplier;+ ts.irq = irq * multiplier;+ ci[num++].cpu_times = ts;+ }+ assert(num == numcpus);++ return 0;+}+++static uint64_t read_cpufreq(unsigned int cpunum) {+ uint64_t val;+ char buf[1024];+ FILE* fp;++ snprintf(buf,+ sizeof(buf),+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",+ cpunum);++ fp = uv__open_file(buf);+ if (fp == NULL)+ return 0;++ if (fscanf(fp, "%" PRIu64, &val) != 1)+ val = 0;++ fclose(fp);++ return val;+}+++static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {+ if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))+ return 1;+ if (ent->ifa_addr == NULL)+ return 1;+ /*+ * 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)+ return exclude_type;+ return !exclude_type;+}++int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {+#ifndef HAVE_IFADDRS_H+ *count = 0;+ *addresses = NULL;+ return UV_ENOSYS;+#else+ struct ifaddrs *addrs, *ent;+ uv_interface_address_t* address;+ int i;+ struct sockaddr_ll *sll;++ *count = 0;+ *addresses = NULL;++ if (getifaddrs(&addrs))+ return UV__ERR(errno);++ /* Count the number of interfaces */+ for (ent = addrs; ent != NULL; ent = ent->ifa_next) {+ if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))+ continue;++ (*count)++;+ }++ if (*count == 0) {+ freeifaddrs(addrs);+ return 0;+ }++ /* Make sure the memory is initiallized to zero using calloc() */+ *addresses = uv__calloc(*count, sizeof(**addresses));+ if (!(*addresses)) {+ freeifaddrs(addrs);+ return UV_ENOMEM;+ }++ address = *addresses;++ for (ent = addrs; ent != NULL; ent = ent->ifa_next) {+ if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))+ 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 (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))+ continue;++ address = *addresses;++ for (i = 0; i < (*count); i++) {+ size_t namelen = strlen(ent->ifa_name);+ /* Alias interface share the same physical address */+ if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&+ (address->name[namelen] == 0 || address->name[namelen] == ':')) {+ 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+}+++static int uv__slurp(const char* filename, char* buf, size_t len) {+ ssize_t n;+ int fd;++ assert(len > 0);++ fd = uv__open_cloexec(filename, O_RDONLY);+ if (fd < 0)+ return fd;++ do+ n = read(fd, buf, len - 1);+ while (n == -1 && errno == EINTR);++ if (uv__close_nocheckstdio(fd))+ abort();++ if (n < 0)+ return UV__ERR(errno);++ buf[n] = '\0';++ return 0;+}+++static uint64_t uv__read_proc_meminfo(const char* what) {+ uint64_t rc;+ char* p;+ char buf[4096]; /* Large enough to hold all of /proc/meminfo. */++ if (uv__slurp("/proc/meminfo", buf, sizeof(buf)))+ return 0;++ p = strstr(buf, what);++ if (p == NULL)+ return 0;++ p += strlen(what);++ rc = 0;+ sscanf(p, "%" PRIu64 " kB", &rc);++ return rc * 1024;+}+++uint64_t uv_get_free_memory(void) {+ struct sysinfo info;+ uint64_t rc;++ rc = uv__read_proc_meminfo("MemFree:");++ if (rc != 0)+ return rc;++ if (0 == sysinfo(&info))+ return (uint64_t) info.freeram * info.mem_unit;++ return 0;+}+++uint64_t uv_get_total_memory(void) {+ struct sysinfo info;+ uint64_t rc;++ rc = uv__read_proc_meminfo("MemTotal:");++ if (rc != 0)+ return rc;++ if (0 == sysinfo(&info))+ return (uint64_t) info.totalram * info.mem_unit;++ return 0;+}+++static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) {+ char filename[256];+ char buf[32]; /* Large enough to hold an encoded uint64_t. */+ uint64_t rc;++ rc = 0;+ snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param);+ if (0 == uv__slurp(filename, buf, sizeof(buf)))+ sscanf(buf, "%" PRIu64, &rc);++ return rc;+}+++uint64_t uv_get_constrained_memory(void) {+ /*+ * This might return 0 if there was a problem getting the memory limit from+ * cgroups. This is OK because a return value of 0 signifies that the memory+ * limit is unknown.+ */+ return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes");+}+++void uv_loadavg(double avg[3]) {+ struct sysinfo info;+ char buf[128]; /* Large enough to hold all of /proc/loadavg. */++ if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf)))+ if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]))+ return;++ 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;+}
+ third_party/libuv/src/unix/linux-inotify.c view
@@ -0,0 +1,327 @@+/* 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/tree.h"+#include "internal.h"++#include <stdint.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <assert.h>+#include <errno.h>++#include <sys/inotify.h>+#include <sys/types.h>+#include <unistd.h>++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 void maybe_free_watcher_list(struct watcher_list* w,+ uv_loop_t* loop);++static int init_inotify(uv_loop_t* loop) {+ int fd;++ if (loop->inotify_fd != -1)+ return 0;++ fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);+ if (fd < 0)+ return UV__ERR(errno);++ loop->inotify_fd = fd;+ uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);+ uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);++ return 0;+}+++int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) {+ /* Open the inotify_fd, and re-arm all the inotify watchers. */+ int err;+ struct watcher_list* tmp_watcher_list_iter;+ struct watcher_list* watcher_list;+ struct watcher_list tmp_watcher_list;+ QUEUE queue;+ QUEUE* q;+ uv_fs_event_t* handle;+ char* tmp_path;++ if (old_watchers != NULL) {+ /* We must restore the old watcher list to be able to close items+ * out of it.+ */+ loop->inotify_watchers = old_watchers;++ QUEUE_INIT(&tmp_watcher_list.watchers);+ /* Note that the queue we use is shared with the start and stop()+ * functions, making QUEUE_FOREACH unsafe to use. So we use the+ * QUEUE_MOVE trick to safely iterate. Also don't free the watcher+ * list until we're done iterating. c.f. uv__inotify_read.+ */+ RB_FOREACH_SAFE(watcher_list, watcher_root,+ CAST(&old_watchers), tmp_watcher_list_iter) {+ watcher_list->iterating = 1;+ QUEUE_MOVE(&watcher_list->watchers, &queue);+ while (!QUEUE_EMPTY(&queue)) {+ q = QUEUE_HEAD(&queue);+ handle = QUEUE_DATA(q, uv_fs_event_t, watchers);+ /* It's critical to keep a copy of path here, because it+ * will be set to NULL by stop() and then deallocated by+ * maybe_free_watcher_list+ */+ tmp_path = uv__strdup(handle->path);+ assert(tmp_path != NULL);+ QUEUE_REMOVE(q);+ QUEUE_INSERT_TAIL(&watcher_list->watchers, q);+ uv_fs_event_stop(handle);++ QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers);+ handle->path = tmp_path;+ }+ watcher_list->iterating = 0;+ maybe_free_watcher_list(watcher_list, loop);+ }++ QUEUE_MOVE(&tmp_watcher_list.watchers, &queue);+ while (!QUEUE_EMPTY(&queue)) {+ q = QUEUE_HEAD(&queue);+ QUEUE_REMOVE(q);+ handle = QUEUE_DATA(q, uv_fs_event_t, watchers);+ tmp_path = handle->path;+ handle->path = NULL;+ err = uv_fs_event_start(handle, handle->cb, tmp_path, 0);+ uv__free(tmp_path);+ if (err)+ return err;+ }+ }++ 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);+ 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 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 inotify_event*) p;++ events = 0;+ if (e->mask & (IN_ATTRIB|IN_MODIFY))+ events |= UV_CHANGE;+ if (e->mask & ~(IN_ATTRIB|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;+ size_t len;+ int events;+ int err;+ int wd;++ if (uv__is_active(handle))+ return UV_EINVAL;++ err = init_inotify(handle->loop);+ if (err)+ return err;++ events = IN_ATTRIB+ | IN_CREATE+ | IN_MODIFY+ | IN_DELETE+ | IN_DELETE_SELF+ | IN_MOVE_SELF+ | IN_MOVED_FROM+ | IN_MOVED_TO;++ wd = inotify_add_watch(handle->loop->inotify_fd, path, events);+ if (wd == -1)+ return UV__ERR(errno);++ w = find_watcher(handle->loop, wd);+ if (w)+ goto no_insert;++ len = strlen(path) + 1;+ w = uv__malloc(sizeof(*w) + len);+ if (w == NULL)+ return UV_ENOMEM;++ w->wd = wd;+ w->path = memcpy(w + 1, path, len);+ 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);+}
+ third_party/libuv/src/unix/linux-syscalls.c view
@@ -0,0 +1,245 @@+/* 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 <unistd.h>+#include <signal.h>+#include <sys/syscall.h>+#include <sys/types.h>+#include <errno.h>++#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_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 */++#ifndef __NR_copy_file_range+# if defined(__x86_64__)+# define __NR_copy_file_range 326+# elif defined(__i386__)+# define __NR_copy_file_range 377+# elif defined(__s390__)+# define __NR_copy_file_range 375+# elif defined(__arm__)+# define __NR_copy_file_range (UV_SYSCALL_BASE + 391)+# elif defined(__aarch64__)+# define __NR_copy_file_range 285+# elif defined(__powerpc__)+# define __NR_copy_file_range 379+# elif defined(__arc__)+# define __NR_copy_file_range 285+# endif+#endif /* __NR_copy_file_range */++#ifndef __NR_statx+# if defined(__x86_64__)+# define __NR_statx 332+# elif defined(__i386__)+# define __NR_statx 383+# elif defined(__aarch64__)+# define __NR_statx 397+# elif defined(__arm__)+# define __NR_statx (UV_SYSCALL_BASE + 397)+# elif defined(__ppc__)+# define __NR_statx 383+# elif defined(__s390__)+# define __NR_statx 379+# endif+#endif /* __NR_statx */++#ifndef __NR_getrandom+# if defined(__x86_64__)+# define __NR_getrandom 318+# elif defined(__i386__)+# define __NR_getrandom 355+# elif defined(__aarch64__)+# define __NR_getrandom 384+# elif defined(__arm__)+# define __NR_getrandom (UV_SYSCALL_BASE + 384)+# elif defined(__ppc__)+# define __NR_getrandom 359+# elif defined(__s390__)+# define __NR_getrandom 349+# endif+#endif /* __NR_getrandom */++struct uv__mmsghdr;++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+}+++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+}+++ssize_t+uv__fs_copy_file_range(int fd_in,+ ssize_t* off_in,+ int fd_out,+ ssize_t* off_out,+ size_t len,+ unsigned int flags)+{+#ifdef __NR_copy_file_range+ return syscall(__NR_copy_file_range,+ fd_in,+ off_in,+ fd_out,+ off_out,+ len,+ flags);+#else+ return errno = ENOSYS, -1;+#endif+}+++int uv__statx(int dirfd,+ const char* path,+ int flags,+ unsigned int mask,+ struct uv__statx* statxbuf) {+ /* __NR_statx make Android box killed by SIGSYS.+ * That looks like a seccomp2 sandbox filter rejecting the system call.+ */+#if defined(__NR_statx) && !defined(__ANDROID__)+ return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);+#else+ return errno = ENOSYS, -1;+#endif+}+++ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) {+#if defined(__NR_getrandom)+ return syscall(__NR_getrandom, buf, buflen, flags);+#else+ return errno = ENOSYS, -1;+#endif+}
+ third_party/libuv/src/unix/linux-syscalls.h view
@@ -0,0 +1,81 @@+/* 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 <stdint.h>+#include <signal.h>+#include <sys/types.h>+#include <sys/time.h>+#include <sys/socket.h>++struct uv__statx_timestamp {+ int64_t tv_sec;+ uint32_t tv_nsec;+ int32_t unused0;+};++struct uv__statx {+ uint32_t stx_mask;+ uint32_t stx_blksize;+ uint64_t stx_attributes;+ uint32_t stx_nlink;+ uint32_t stx_uid;+ uint32_t stx_gid;+ uint16_t stx_mode;+ uint16_t unused0;+ uint64_t stx_ino;+ uint64_t stx_size;+ uint64_t stx_blocks;+ uint64_t stx_attributes_mask;+ struct uv__statx_timestamp stx_atime;+ struct uv__statx_timestamp stx_btime;+ struct uv__statx_timestamp stx_ctime;+ struct uv__statx_timestamp stx_mtime;+ uint32_t stx_rdev_major;+ uint32_t stx_rdev_minor;+ uint32_t stx_dev_major;+ uint32_t stx_dev_minor;+ uint64_t unused1[14];+};++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);+ssize_t+uv__fs_copy_file_range(int fd_in,+ ssize_t* off_in,+ int fd_out,+ ssize_t* off_out,+ size_t len,+ unsigned int flags);+int uv__statx(int dirfd,+ const char* path,+ int flags,+ unsigned int mask,+ struct uv__statx* statxbuf);+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags);++#endif /* UV_LINUX_SYSCALL_H_ */
+ third_party/libuv/src/unix/loop-watcher.c view
@@ -0,0 +1,68 @@+/* 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 UV_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)
+ third_party/libuv/src/unix/loop.c view
@@ -0,0 +1,228 @@+/* 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/tree.h"+#include "internal.h"+#include "heap-inl.h"+#include <stdlib.h>+#include <string.h>+#include <unistd.h>++int uv_loop_init(uv_loop_t* loop) {+ uv__loop_internal_fields_t* lfields;+ void* saved_data;+ int err;+++ saved_data = loop->data;+ memset(loop, 0, sizeof(*loop));+ loop->data = saved_data;++ lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields));+ if (lfields == NULL)+ return UV_ENOMEM;+ loop->internal_fields = lfields;++ err = uv_mutex_init(&lfields->loop_metrics.lock);+ if (err)+ goto fail_metrics_mutex_init;++ heap_init((struct heap*) &loop->timer_heap);+ QUEUE_INIT(&loop->wq);+ 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->active_handles = 0;+ loop->active_reqs.count = 0;+ 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);+ loop->async_io_watcher.fd = -1;+ loop->async_wfd = -1;+ 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)+ goto fail_platform_init;++ uv__signal_global_once_init();+ 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);++fail_platform_init:+ uv_mutex_destroy(&lfields->loop_metrics.lock);++fail_metrics_mutex_init:+ uv__free(lfields);+ loop->internal_fields = NULL;++ uv__free(loop->watchers);+ loop->nwatchers = 0;+ return err;+}+++int uv_loop_fork(uv_loop_t* loop) {+ int err;+ unsigned int i;+ uv__io_t* w;++ err = uv__io_fork(loop);+ if (err)+ return err;++ err = uv__async_fork(loop);+ if (err)+ return err;++ err = uv__signal_loop_fork(loop);+ if (err)+ return err;++ /* Rearm all the watchers that aren't re-queued by the above. */+ for (i = 0; i < loop->nwatchers; i++) {+ w = loop->watchers[i];+ if (w == NULL)+ continue;++ if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) {+ w->events = 0; /* Force re-registration in uv__io_poll. */+ QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);+ }+ }++ return 0;+}+++void uv__loop_close(uv_loop_t* loop) {+ uv__loop_internal_fields_t* lfields;++ uv__signal_loop_cleanup(loop);+ uv__platform_loop_delete(loop);+ uv__async_stop(loop);++ 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;++ lfields = uv__get_internal_fields(loop);+ uv_mutex_destroy(&lfields->loop_metrics.lock);+ uv__free(lfields);+ loop->internal_fields = NULL;+}+++int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {+ uv__loop_internal_fields_t* lfields;++ lfields = uv__get_internal_fields(loop);+ if (option == UV_METRICS_IDLE_TIME) {+ lfields->flags |= UV_METRICS_IDLE_TIME;+ return 0;+ }++ 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;+}
+ third_party/libuv/src/unix/os390-syscalls.h view
@@ -0,0 +1,74 @@+/* Copyright 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.+ */+++#ifndef UV_OS390_SYSCALL_H_+#define UV_OS390_SYSCALL_H_++#include "uv.h"+#include "internal.h"+#include <dirent.h>+#include <poll.h>+#include <pthread.h>++#define EPOLL_CTL_ADD 1+#define EPOLL_CTL_DEL 2+#define EPOLL_CTL_MOD 3+#define MAX_EPOLL_INSTANCES 256+#define MAX_ITEMS_PER_EPOLL 1024++#define UV__O_CLOEXEC 0x80000++struct epoll_event {+ int events;+ int fd;+ int is_msg;+};++typedef struct {+ QUEUE member;+ struct pollfd* items;+ unsigned long size;+ int msg_queue;+} uv__os390_epoll;++/* epoll api */+uv__os390_epoll* epoll_create1(int flags);+int epoll_ctl(uv__os390_epoll* ep, int op, int fd, struct epoll_event *event);+int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, int timeout);+int epoll_file_close(int fd);++/* utility functions */+int nanosleep(const struct timespec* req, struct timespec* rem);+int scandir(const char* maindir, struct dirent*** namelist,+ int (*filter)(const struct dirent *),+ int (*compar)(const struct dirent **,+ const struct dirent **));+char *mkdtemp(char* path);+ssize_t os390_readlink(const char* path, char* buf, size_t len);+size_t strnlen(const char* str, size_t maxlen);+int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value);+int sem_destroy(UV_PLATFORM_SEM_T* semid);+int sem_post(UV_PLATFORM_SEM_T* semid);+int sem_trywait(UV_PLATFORM_SEM_T* semid);+int sem_wait(UV_PLATFORM_SEM_T* semid);++#endif /* UV_OS390_SYSCALL_H_ */
+ third_party/libuv/src/unix/pipe.c view
@@ -0,0 +1,381 @@+/* 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 <assert.h>+#include <errno.h>+#include <string.h>+#include <sys/un.h>+#include <unistd.h>+#include <stdlib.h>+++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;++ /* Already bound? */+ if (uv__stream_fd(handle) >= 0)+ return UV_EINVAL;++ /* Make a copy of the file name, it outlives this function's scope. */+ pipe_fname = uv__strdup(name);+ if (pipe_fname == NULL)+ return UV_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);+ uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path));+ saddr.sun_family = AF_UNIX;++ if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {+ err = UV__ERR(errno);+ /* Convert ENOENT to EACCES for compatibility with Windows. */+ if (err == UV_ENOENT)+ err = UV_EACCES;++ uv__close(sockfd);+ goto err_socket;+ }++ /* Success. */+ handle->flags |= UV_HANDLE_BOUND;+ handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */+ handle->io_watcher.fd = sockfd;+ return 0;++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 UV_EINVAL;++ if (handle->ipc)+ return UV_EINVAL;++#if defined(__MVS__) || defined(__PASE__)+ /* On zOS, backlog=0 has undefined behaviour */+ /* On IBMi PASE, backlog=0 leads to "Connection refused" error */+ if (backlog == 0)+ backlog = 1;+ else if (backlog < 0)+ backlog = SOMAXCONN;+#endif++ if (listen(uv__stream_fd(handle), backlog))+ return UV__ERR(errno);++ handle->connection_cb = cb;+ handle->io_watcher.cb = uv__server_io;+ uv__io_start(handle->loop, &handle->io_watcher, 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 flags;+ int mode;+ int err;+ flags = 0;++ if (uv__fd_exists(handle->loop, fd))+ return UV_EEXIST;++ do+ mode = fcntl(fd, F_GETFL);+ while (mode == -1 && errno == EINTR);++ if (mode == -1)+ return UV__ERR(errno); /* according to docs, must be EBADF */++ 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__) */++ mode &= O_ACCMODE;+ if (mode != O_WRONLY)+ flags |= UV_HANDLE_READABLE;+ if (mode != O_RDONLY)+ flags |= UV_HANDLE_WRITABLE;++ return uv__stream_open((uv_stream_t*)handle, fd, flags);+}+++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);+ uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path));+ 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 = UV__ERR(errno);+#if defined(__CYGWIN__) || defined(__MSYS__)+ /* EBADF is supposed to mean that the socket fd is bad, but+ Cygwin reports EBADF instead of ENOTSOCK when the file is+ not a socket. We do not expect to see a bad fd here+ (e.g. due to new_sock), so translate the error. */+ if (err == UV_EBADF)+ err = UV_ENOTSOCK;+#endif+ goto out;+ }++ err = 0;+ if (new_sock) {+ err = uv__stream_open((uv_stream_t*)handle,+ uv__stream_fd(handle),+ UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);+ }++ if (err == 0)+ uv__io_start(handle->loop, &handle->io_watcher, 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);++}+++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 = uv__getsockpeername((const uv_handle_t*) handle,+ func,+ (struct sockaddr*) &sa,+ (int*) &addrlen);+ if (err < 0) {+ *size = 0;+ return err;+ }++#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 ((size_t)addrlen >= *size) {+ *size = addrlen + 1;+ return UV_ENOBUFS;+ }++ memcpy(buffer, sa.sun_path, addrlen);+ *size = addrlen;++ /* only null-terminate if it's not an abstract socket */+ if (buffer[0] != '\0')+ buffer[addrlen] = '\0';++ 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);+}+++int uv_pipe_chmod(uv_pipe_t* handle, int mode) {+ unsigned desired_mode;+ struct stat pipe_stat;+ char* name_buffer;+ size_t name_len;+ int r;++ if (handle == NULL || uv__stream_fd(handle) == -1)+ return UV_EBADF;++ if (mode != UV_READABLE &&+ mode != UV_WRITABLE &&+ mode != (UV_WRITABLE | UV_READABLE))+ return UV_EINVAL;++ /* Unfortunately fchmod does not work on all platforms, we will use chmod. */+ name_len = 0;+ r = uv_pipe_getsockname(handle, NULL, &name_len);+ if (r != UV_ENOBUFS)+ return r;++ name_buffer = uv__malloc(name_len);+ if (name_buffer == NULL)+ return UV_ENOMEM;++ r = uv_pipe_getsockname(handle, name_buffer, &name_len);+ if (r != 0) {+ uv__free(name_buffer);+ return r;+ }++ /* stat must be used as fstat has a bug on Darwin */+ if (stat(name_buffer, &pipe_stat) == -1) {+ uv__free(name_buffer);+ return -errno;+ }++ desired_mode = 0;+ if (mode & UV_READABLE)+ desired_mode |= S_IRUSR | S_IRGRP | S_IROTH;+ if (mode & UV_WRITABLE)+ desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;++ /* Exit early if pipe already has desired mode. */+ if ((pipe_stat.st_mode & desired_mode) == desired_mode) {+ uv__free(name_buffer);+ return 0;+ }++ pipe_stat.st_mode |= desired_mode;++ r = chmod(name_buffer, pipe_stat.st_mode);+ uv__free(name_buffer);++ return r != -1 ? 0 : UV__ERR(errno);+}
+ third_party/libuv/src/unix/poll.c view
@@ -0,0 +1,150 @@+/* 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 <unistd.h>+#include <assert.h>+#include <errno.h>+++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);++ /*+ * As documented in the kernel source fs/kernfs/file.c #780+ * poll will return POLLERR|POLLPRI in case of sysfs+ * polling. This does not happen in case of out-of-band+ * TCP messages.+ *+ * The above is the case on (at least) FreeBSD and Linux.+ *+ * So to properly determine a POLLPRI or a POLLERR we need+ * to check for both.+ */+ if ((events & POLLERR) && !(events & UV__POLLPRI)) {+ uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);+ uv__handle_stop(handle);+ handle->poll_cb(handle, UV_EBADF, 0);+ return;+ }++ pevents = 0;+ if (events & POLLIN)+ pevents |= UV_READABLE;+ if (events & UV__POLLPRI)+ pevents |= UV_PRIORITIZED;+ if (events & POLLOUT)+ pevents |= UV_WRITABLE;+ if (events & UV__POLLRDHUP)+ pevents |= UV_DISCONNECT;++ handle->poll_cb(handle, 0, pevents);+}+++int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {+ int err;++ if (uv__fd_exists(loop, fd))+ return UV_EEXIST;++ err = uv__io_check_fd(loop, fd);+ if (err)+ return err;++ /* If ioctl(FIONBIO) reports ENOTTY, try fcntl(F_GETFL) + fcntl(F_SETFL).+ * Workaround for e.g. kqueue fds not supporting ioctls.+ */+ err = uv__nonblock(fd, 1);+ if (err == UV_ENOTTY)+ if (uv__nonblock == uv__nonblock_ioctl)+ err = uv__nonblock_fcntl(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,+ POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);+ uv__handle_stop(handle);+ uv__platform_invalidate_fd(handle->loop, handle->io_watcher.fd);+}+++int uv_poll_stop(uv_poll_t* handle) {+ assert(!uv__is_closing(handle));+ 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 | UV_DISCONNECT |+ UV_PRIORITIZED)) == 0);+ assert(!uv__is_closing(handle));++ uv__poll_stop(handle);++ if (pevents == 0)+ return 0;++ events = 0;+ if (pevents & UV_READABLE)+ events |= POLLIN;+ if (pevents & UV_PRIORITIZED)+ events |= UV__POLLPRI;+ if (pevents & UV_WRITABLE)+ events |= POLLOUT;+ if (pevents & UV_DISCONNECT)+ events |= UV__POLLRDHUP;++ 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);+}
+ third_party/libuv/src/unix/process.c view
@@ -0,0 +1,595 @@+/* 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 <stdio.h>+#include <stdlib.h>+#include <assert.h>+#include <errno.h>++#include <sys/types.h>+#include <sys/wait.h>+#include <unistd.h>+#include <fcntl.h>+#include <poll.h>++#if defined(__APPLE__) && !TARGET_OS_IPHONE+# include <crt_externs.h>+# define environ (*_NSGetEnviron())+#else+extern char **environ;+#endif++#if defined(__linux__) || defined(__GLIBC__)+# include <grp.h>+#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));+}+++static int uv__make_socketpair(int fds[2]) {+#if defined(__FreeBSD__) || defined(__linux__)+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds))+ return UV__ERR(errno);++ return 0;+#else+ int err;++ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))+ return UV__ERR(errno);++ err = uv__cloexec(fds[0], 1);+ if (err == 0)+ err = uv__cloexec(fds[1], 1);++ if (err != 0) {+ uv__close(fds[0]);+ uv__close(fds[1]);+ return UV__ERR(errno);+ }++ return 0;+#endif+}+++int uv__make_pipe(int fds[2], int flags) {+#if defined(__FreeBSD__) || defined(__linux__)+ if (pipe2(fds, flags | O_CLOEXEC))+ return UV__ERR(errno);++ return 0;+#else+ if (pipe(fds))+ return UV__ERR(errno);++ if (uv__cloexec(fds[0], 1))+ goto fail;++ if (uv__cloexec(fds[1], 1))+ goto fail;++ if (flags & UV__F_NONBLOCK) {+ if (uv__nonblock(fds[0], 1))+ goto fail;++ if (uv__nonblock(fds[1], 1))+ goto fail;+ }++ return 0;++fail:+ uv__close(fds[0]);+ uv__close(fds[1]);+ return UV__ERR(errno);+#endif+}+++/*+ * 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 UV_EINVAL;+ else+ return uv__make_socketpair(fds);++ 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 UV_EINVAL;++ fds[1] = fd;+ return 0;++ default:+ assert(0 && "Unexpected flags");+ return UV_EINVAL;+ }+}+++static int uv__process_open_stream(uv_stdio_container_t* container,+ int pipefds[2]) {+ int flags;+ int err;++ if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0)+ return 0;++ err = uv__close(pipefds[1]);+ if (err != 0)+ abort();++ pipefds[1] = -1;+ uv__nonblock(pipefds[0], 1);++ flags = 0;+ if (container->flags & UV_WRITABLE_PIPE)+ flags |= UV_HANDLE_READABLE;+ if (container->flags & UV_READABLE_PIPE)+ flags |= UV_HANDLE_WRITABLE;++ 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(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) {+ sigset_t set;+ int close_fd;+ int use_fd;+ int err;+ int fd;+ int n;++ 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, UV__ERR(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 < 0) {+ uv__write_int(error_fd, UV__ERR(errno));+ _exit(127);+ }+ }+ }++ if (fd == use_fd)+ uv__cloexec_fcntl(use_fd, 0);+ else+ fd = dup2(use_fd, fd);++ if (fd == -1) {+ uv__write_int(error_fd, UV__ERR(errno));+ _exit(127);+ }++ if (fd <= 2)+ uv__nonblock_fcntl(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, UV__ERR(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, UV__ERR(errno));+ _exit(127);+ }++ if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) {+ uv__write_int(error_fd, UV__ERR(errno));+ _exit(127);+ }++ if (options->env != NULL) {+ environ = options->env;+ }++ /* Reset signal disposition. Use a hard-coded limit because NSIG+ * is not fixed on Linux: it's either 32, 34 or 64, depending on+ * whether RT signals are enabled. We are not allowed to touch+ * RT signal handlers, glibc uses them internally.+ */+ for (n = 1; n < 32; n += 1) {+ if (n == SIGKILL || n == SIGSTOP)+ continue; /* Can't be changed. */++#if defined(__HAIKU__)+ if (n == SIGKILLTHR)+ continue; /* Can't be changed. */+#endif++ if (SIG_ERR != signal(n, SIG_DFL))+ continue;++ uv__write_int(error_fd, UV__ERR(errno));+ _exit(127);+ }++ /* Reset signal mask. */+ sigemptyset(&set);+ err = pthread_sigmask(SIG_SETMASK, &set, NULL);++ if (err != 0) {+ uv__write_int(error_fd, UV__ERR(err));+ _exit(127);+ }++ execvp(options->file, options->args);+ uv__write_int(error_fd, UV__ERR(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 UV_ENOSYS;+#else+ int signal_pipe[2] = { -1, -1 };+ int pipes_storage[8][2];+ 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_HIDE_CONSOLE |+ UV_PROCESS_WINDOWS_HIDE_GUI |+ 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 = UV_ENOMEM;+ pipes = pipes_storage;+ if (stdio_count > (int) ARRAY_SIZE(pipes_storage))+ 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 = UV__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_nocheckstdio(signal_pipe[0]);++ for (i = 0; i < options->stdio_count; i++) {+ err = uv__process_open_stream(options->stdio + i, pipes[i]);+ 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;++ if (pipes != pipes_storage)+ 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)+ uv__close_nocheckstdio(pipes[i][0]);+ if (pipes[i][1] != -1)+ uv__close_nocheckstdio(pipes[i][1]);+ }++ if (pipes != pipes_storage)+ 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 UV__ERR(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);+}
+ third_party/libuv/src/unix/procfs-exepath.c view
@@ -0,0 +1,45 @@+/* Copyright 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.+ */++#include "uv.h"+#include "internal.h"++#include <stddef.h>+#include <unistd.h>++int uv_exepath(char* buffer, size_t* size) {+ ssize_t n;++ if (buffer == NULL || size == NULL || *size == 0)+ return UV_EINVAL;++ n = *size - 1;+ if (n > 0)+ n = readlink("/proc/self/exe", buffer, n);++ if (n == -1)+ return UV__ERR(errno);++ buffer[n] = '\0';+ *size = n;++ return 0;+}
+ third_party/libuv/src/unix/proctitle.c view
@@ -0,0 +1,159 @@+/* 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 <stdlib.h>+#include <string.h>++struct uv__process_title {+ char* str;+ size_t len; /* Length of the current process title. */+ size_t cap; /* Maximum capacity. Computed once in uv_setup_args(). */+};++extern void uv__set_process_title(const char* title);++static uv_mutex_t process_title_mutex;+static uv_once_t process_title_mutex_once = UV_ONCE_INIT;+static struct uv__process_title process_title;+static void* args_mem;+++static void init_process_title_mutex_once(void) {+ uv_mutex_init(&process_title_mutex);+}+++char** uv_setup_args(int argc, char** argv) {+ struct uv__process_title pt;+ char** new_argv;+ size_t size;+ char* s;+ int i;++ if (argc <= 0)+ return argv;++ pt.str = argv[0];+ pt.len = strlen(argv[0]);+ pt.cap = pt.len + 1;++ /* Calculate how much memory we need for the argv strings. */+ size = pt.cap;+ for (i = 1; i < argc; i++)+ size += strlen(argv[i]) + 1;++ /* Add space for the argv pointers. */+ size += (argc + 1) * sizeof(char*);++ new_argv = uv__malloc(size);+ if (new_argv == NULL)+ return argv;++ /* Copy over the strings and set up the pointer table. */+ i = 0;+ s = (char*) &new_argv[argc + 1];+ size = pt.cap;+ goto loop;++ for (/* empty */; i < argc; i++) {+ size = strlen(argv[i]) + 1;+ loop:+ memcpy(s, argv[i], size);+ new_argv[i] = s;+ s += size;+ }+ new_argv[i] = NULL;++ /* argv is not adjacent on z/os, we use just argv[0] on that platform. */+#ifndef __MVS__+ pt.cap = argv[i - 1] + size - argv[0];+#endif++ args_mem = new_argv;+ process_title = pt;++ return new_argv;+}+++int uv_set_process_title(const char* title) {+ struct uv__process_title* pt;+ size_t len;++ /* If uv_setup_args wasn't called or failed, we can't continue. */+ if (args_mem == NULL)+ return UV_ENOBUFS;++ pt = &process_title;+ len = strlen(title);++ uv_once(&process_title_mutex_once, init_process_title_mutex_once);+ uv_mutex_lock(&process_title_mutex);++ if (len >= pt->cap) {+ len = 0;+ if (pt->cap > 0)+ len = pt->cap - 1;+ }++ memcpy(pt->str, title, len);+ memset(pt->str + len, '\0', pt->cap - len);+ pt->len = len;++ uv_mutex_unlock(&process_title_mutex);++ return 0;+}+++int uv_get_process_title(char* buffer, size_t size) {+ if (buffer == NULL || size == 0)+ return UV_EINVAL;++ /* If uv_setup_args wasn't called or failed, we can't continue. */+ if (args_mem == NULL)+ return UV_ENOBUFS;++ uv_once(&process_title_mutex_once, init_process_title_mutex_once);+ uv_mutex_lock(&process_title_mutex);++ if (size <= process_title.len) {+ uv_mutex_unlock(&process_title_mutex);+ return UV_ENOBUFS;+ }++ if (process_title.len != 0)+ memcpy(buffer, process_title.str, process_title.len + 1);++ buffer[process_title.len] = '\0';++ uv_mutex_unlock(&process_title_mutex);++ return 0;+}+++void uv__process_title_cleanup(void) {+ uv__free(args_mem); /* Keep valgrind happy. */+ args_mem = NULL;+}
+ third_party/libuv/src/unix/random-devurandom.c view
@@ -0,0 +1,93 @@+/* Copyright 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.+ */++#include "uv.h"+#include "internal.h"++#include <sys/stat.h>+#include <unistd.h>++static uv_once_t once = UV_ONCE_INIT;+static int status;+++int uv__random_readpath(const char* path, void* buf, size_t buflen) {+ struct stat s;+ size_t pos;+ ssize_t n;+ int fd;++ fd = uv__open_cloexec(path, O_RDONLY);++ if (fd < 0)+ return fd;++ if (fstat(fd, &s)) {+ uv__close(fd);+ return UV__ERR(errno);+ }++ if (!S_ISCHR(s.st_mode)) {+ uv__close(fd);+ return UV_EIO;+ }++ for (pos = 0; pos != buflen; pos += n) {+ do+ n = read(fd, (char*) buf + pos, buflen - pos);+ while (n == -1 && errno == EINTR);++ if (n == -1) {+ uv__close(fd);+ return UV__ERR(errno);+ }++ if (n == 0) {+ uv__close(fd);+ return UV_EIO;+ }+ }++ uv__close(fd);+ return 0;+}+++static void uv__random_devurandom_init(void) {+ char c;++ /* Linux's random(4) man page suggests applications should read at least+ * once from /dev/random before switching to /dev/urandom in order to seed+ * the system RNG. Reads from /dev/random can of course block indefinitely+ * until entropy is available but that's the point.+ */+ status = uv__random_readpath("/dev/random", &c, 1);+}+++int uv__random_devurandom(void* buf, size_t buflen) {+ uv_once(&once, uv__random_devurandom_init);++ if (status != 0)+ return status;++ return uv__random_readpath("/dev/urandom", buf, buflen);+}
+ third_party/libuv/src/unix/random-getrandom.c view
@@ -0,0 +1,88 @@+/* Copyright 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.+ */++#include "uv.h"+#include "internal.h"++#ifdef __linux__++#include "linux-syscalls.h"++#define uv__random_getrandom_init() 0++#else /* !__linux__ */++#include <stddef.h>+#include <dlfcn.h>++typedef ssize_t (*uv__getrandom_cb)(void *, size_t, unsigned);++static uv__getrandom_cb uv__getrandom;+static uv_once_t once = UV_ONCE_INIT;++static void uv__random_getrandom_init_once(void) {+ uv__getrandom = (uv__getrandom_cb) dlsym(RTLD_DEFAULT, "getrandom");+}++static int uv__random_getrandom_init(void) {+ uv_once(&once, uv__random_getrandom_init_once);++ if (uv__getrandom == NULL)+ return UV_ENOSYS;++ return 0;+}++#endif /* !__linux__ */++int uv__random_getrandom(void* buf, size_t buflen) {+ ssize_t n;+ size_t pos;+ int rc;++ rc = uv__random_getrandom_init();+ if (rc != 0)+ return rc;++ for (pos = 0; pos != buflen; pos += n) {+ do {+ n = buflen - pos;++ /* Most getrandom() implementations promise that reads <= 256 bytes+ * will always succeed and won't be interrupted by signals.+ * It's therefore useful to split it up in smaller reads because+ * one big read may, in theory, continuously fail with EINTR.+ */+ if (n > 256)+ n = 256;++ n = uv__getrandom((char *) buf + pos, n, 0);+ } while (n == -1 && errno == EINTR);++ if (n == -1)+ return UV__ERR(errno);++ if (n == 0)+ return UV_EIO;+ }++ return 0;+}
+ third_party/libuv/src/unix/random-sysctl-linux.c view
@@ -0,0 +1,99 @@+/* Copyright 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.+ */++#include "uv.h"+#include "internal.h"++#include <errno.h>+#include <string.h>++#include <syscall.h>+#include <unistd.h>+++struct uv__sysctl_args {+ int* name;+ int nlen;+ void* oldval;+ size_t* oldlenp;+ void* newval;+ size_t newlen;+ unsigned long unused[4];+};+++int uv__random_sysctl(void* buf, size_t buflen) {+ static int name[] = {1 /*CTL_KERN*/, 40 /*KERN_RANDOM*/, 6 /*RANDOM_UUID*/};+ struct uv__sysctl_args args;+ char uuid[16];+ char* p;+ char* pe;+ size_t n;++ p = buf;+ pe = p + buflen;++ while (p < pe) {+ memset(&args, 0, sizeof(args));++ args.name = name;+ args.nlen = ARRAY_SIZE(name);+ args.oldval = uuid;+ args.oldlenp = &n;+ n = sizeof(uuid);++ /* Emits a deprecation warning with some kernels but that seems like+ * an okay trade-off for the fallback of the fallback: this function is+ * only called when neither getrandom(2) nor /dev/urandom are available.+ * Fails with ENOSYS on kernels configured without CONFIG_SYSCTL_SYSCALL.+ * At least arm64 never had a _sysctl system call and therefore doesn't+ * have a SYS__sysctl define either.+ */+#ifdef SYS__sysctl+ if (syscall(SYS__sysctl, &args) == -1)+ return UV__ERR(errno);+#else+ {+ (void) &args;+ return UV_ENOSYS;+ }+#endif++ if (n != sizeof(uuid))+ return UV_EIO; /* Can't happen. */++ /* uuid[] is now a type 4 UUID. Bytes 6 and 8 (counting from zero) contain+ * 4 and 5 bits of entropy, respectively. For ease of use, we skip those+ * and only use 14 of the 16 bytes.+ */+ uuid[6] = uuid[14];+ uuid[8] = uuid[15];++ n = pe - p;+ if (n > 14)+ n = 14;++ memcpy(p, uuid, n);+ p += n;+ }++ return 0;+}
+ third_party/libuv/src/unix/signal.c view
@@ -0,0 +1,558 @@+/* 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 <assert.h>+#include <errno.h>+#include <signal.h>+#include <stdlib.h>+#include <string.h>+#include <unistd.h>++#ifndef SA_RESTART+# define SA_RESTART 0+#endif++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 int uv__signal_start(uv_signal_t* handle,+ uv_signal_cb signal_cb,+ int signum,+ int oneshot);+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 void uv__signal_unregister_handler(int signum);+++static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT;+static struct uv__signal_tree_s uv__signal_tree =+ RB_INITIALIZER(uv__signal_tree);+static int uv__signal_lock_pipefd[2] = { -1, -1 };++RB_GENERATE_STATIC(uv__signal_tree_s,+ uv_signal_s, tree_entry,+ uv__signal_compare)++static void uv__signal_global_reinit(void);++static void uv__signal_global_init(void) {+ if (uv__signal_lock_pipefd[0] == -1)+ /* pthread_atfork can register before and after handlers, one+ * for each child. This only registers one for the child. That+ * state is both persistent and cumulative, so if we keep doing+ * it the handler functions will be called multiple times. Thus+ * we only want to do it once.+ */+ if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit))+ abort();++ uv__signal_global_reinit();+}+++void uv__signal_cleanup(void) {+ /* We can only use signal-safe functions here.+ * That includes read/write and close, fortunately.+ * We do all of this directly here instead of resetting+ * uv__signal_global_init_guard because+ * uv__signal_global_once_init is only called from uv_loop_init+ * and this needs to function in existing loops.+ */+ if (uv__signal_lock_pipefd[0] != -1) {+ uv__close(uv__signal_lock_pipefd[0]);+ uv__signal_lock_pipefd[0] = -1;+ }++ if (uv__signal_lock_pipefd[1] != -1) {+ uv__close(uv__signal_lock_pipefd[1]);+ uv__signal_lock_pipefd[1] = -1;+ }+}+++static void uv__signal_global_reinit(void) {+ uv__signal_cleanup();++ if (uv__make_pipe(uv__signal_lock_pipefd, 0))+ abort();++ if (uv__signal_unlock())+ abort();+}+++void uv__signal_global_once_init(void) {+ uv_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();++ /* to shut up valgrind */+ sigemptyset(saved_sigmask);+ 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.flags = 0;+ 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, int oneshot) {+ /* 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;+ sa.sa_flags = SA_RESTART;+ if (oneshot)+ sa.sa_flags |= SA_RESETHAND;++ /* XXX save old action so we can restore it later on? */+ if (sigaction(signum, &sa, NULL))+ return UV__ERR(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, POLLIN);++ return 0;+}+++int uv__signal_loop_fork(uv_loop_t* loop) {+ uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);+ uv__close(loop->signal_pipefd[0]);+ uv__close(loop->signal_pipefd[1]);+ loop->signal_pipefd[0] = -1;+ loop->signal_pipefd[1] = -1;+ return uv__signal_loop_once_init(loop);+}+++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);+}+++int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {+ return uv__signal_start(handle, signal_cb, signum, 0);+}+++int uv_signal_start_oneshot(uv_signal_t* handle,+ uv_signal_cb signal_cb,+ int signum) {+ return uv__signal_start(handle, signal_cb, signum, 1);+}+++static int uv__signal_start(uv_signal_t* handle,+ uv_signal_cb signal_cb,+ int signum,+ int oneshot) {+ sigset_t saved_sigmask;+ int err;+ uv_signal_t* first_handle;++ assert(!uv__is_closing(handle));++ /* 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) {+ 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.+ * Also in case there's only one-shot handlers and a regular handler comes in.+ */+ first_handle = uv__signal_first_handle(signum);+ if (first_handle == NULL ||+ (!oneshot && (first_handle->flags & UV_SIGNAL_ONE_SHOT))) {+ err = uv__signal_register_handler(signum, oneshot);+ if (err) {+ /* Registering the signal handler failed. Must be an invalid signal. */+ uv__signal_unlock_and_unblock(&saved_sigmask);+ return err;+ }+ }++ handle->signum = signum;+ if (oneshot)+ handle->flags |= UV_SIGNAL_ONE_SHOT;++ 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_HANDLE_CLOSING));+ handle->signal_cb(handle, handle->signum);+ }++ handle->dispatched_signals++;++ if (handle->flags & UV_SIGNAL_ONE_SHOT)+ uv__signal_stop(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) {+ int f1;+ int f2;+ /* 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;++ /* Handlers without UV_SIGNAL_ONE_SHOT set will come first, so if the first+ * handler returned is a one-shot handler, the rest will be too.+ */+ f1 = w1->flags & UV_SIGNAL_ONE_SHOT;+ f2 = w2->flags & UV_SIGNAL_ONE_SHOT;+ if (f1 < f2) return -1;+ if (f1 > f2) 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(!uv__is_closing(handle));+ uv__signal_stop(handle);+ return 0;+}+++static void uv__signal_stop(uv_signal_t* handle) {+ uv_signal_t* removed_handle;+ sigset_t saved_sigmask;+ uv_signal_t* first_handle;+ int rem_oneshot;+ int first_oneshot;+ int ret;++ /* 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.+ */+ first_handle = uv__signal_first_handle(handle->signum);+ if (first_handle == NULL) {+ uv__signal_unregister_handler(handle->signum);+ } else {+ rem_oneshot = handle->flags & UV_SIGNAL_ONE_SHOT;+ first_oneshot = first_handle->flags & UV_SIGNAL_ONE_SHOT;+ if (first_oneshot && !rem_oneshot) {+ ret = uv__signal_register_handler(handle->signum, 1);+ assert(ret == 0);+ (void)ret;+ }+ }++ uv__signal_unlock_and_unblock(&saved_sigmask);++ handle->signum = 0;+ uv__handle_stop(handle);+}
+ third_party/libuv/src/unix/spinlock.h view
@@ -0,0 +1,53 @@+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>+ *+ * 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_ */
+ third_party/libuv/src/unix/stream.c view
@@ -0,0 +1,1693 @@+/* 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 <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <assert.h>+#include <errno.h>++#include <sys/types.h>+#include <sys/socket.h>+#include <sys/uio.h>+#include <sys/un.h>+#include <unistd.h>+#include <limits.h> /* IOV_MAX */++#if defined(__APPLE__)+# include <sys/event.h>+# include <sys/time.h>+# include <sys/select.h>++/* 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;+};++/* 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.+ */+# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR || errno == EPROTOTYPE)+# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \+ (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || \+ (errno == EMSGSIZE && send_handle != NULL))+#else+# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR)+# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \+ (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)+#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, POLLIN))+ FD_SET(fd, s->sread);+ if (uv__io_active(&stream->io_watcher, 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 |= POLLIN;+ if (FD_ISSET(fd, s->swrite))+ events |= 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_HANDLE_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 & (POLLIN | POLLOUT)));++ /* Invoke callback on event-loop */+ if ((events & POLLIN) && uv__io_active(&stream->io_watcher, POLLIN))+ uv__stream_io(stream->loop, &stream->io_watcher, POLLIN);++ if ((events & POLLOUT) && uv__io_active(&stream->io_watcher, POLLOUT))+ uv__stream_io(stream->loop, &stream->io_watcher, POLLOUT);++ if (stream->flags & UV_HANDLE_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 UV__ERR(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;++ do+ ret = kevent(kq, filter, 1, events, 1, &timeout);+ while (ret == -1 && errno == EINTR);++ uv__close(kq);++ if (ret == -1)+ return UV__ERR(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 UV__ERR(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 = UV_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 UV_EBUSY;++ assert(fd >= 0);+ stream->flags |= flags;++ if (stream->type == UV_TCP) {+ if ((stream->flags & UV_HANDLE_TCP_NODELAY) && uv__tcp_nodelay(fd, 1))+ return UV__ERR(errno);++ /* TODO Use delay the user passed in. */+ if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) &&+ uv__tcp_keepalive(fd, 1, 60)) {+ return UV__ERR(errno);+ }+ }++#if defined(__APPLE__)+ enable = 1;+ if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) &&+ errno != ENOTSOCK &&+ errno != EINVAL) {+ return UV__ERR(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, POLLIN | POLLOUT));+ assert(stream->flags & UV_HANDLE_CLOSED);++ if (stream->connect_req) {+ uv__req_unregister(stream->loop, stream->connect_req);+ stream->connect_req->cb(stream->connect_req, UV_ECANCELED);+ stream->connect_req = NULL;+ }++ uv__stream_flush_write_queue(stream, UV_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, UV_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 UV_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 == UV_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 & POLLIN);+ assert(stream->accepted_fd == -1);+ assert(!(stream->flags & UV_HANDLE_CLOSING));++ uv__io_start(stream->loop, &stream->io_watcher, 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 == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))+ return; /* Not an error. */++ if (err == UV_ECONNABORTED)+ continue; /* Ignore. Nothing we can do about that. */++ if (err == UV_EMFILE || err == UV_ENFILE) {+ err = uv__emfile_trick(loop, uv__stream_fd(stream));+ if (err == UV_EAGAIN || err == UV__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, POLLIN);+ return;+ }++ if (stream->type == UV_TCP &&+ (stream->flags & UV_HANDLE_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;++ assert(server->loop == client->loop);++ if (server->accepted_fd == -1)+ return UV_EAGAIN;++ switch (client->type) {+ case UV_NAMED_PIPE:+ case UV_TCP:+ err = uv__stream_open(client,+ server->accepted_fd,+ UV_HANDLE_READABLE | UV_HANDLE_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 UV_EINVAL;+ }++ client->flags |= UV_HANDLE_BOUND;++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, 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 = UV_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, POLLOUT);+ uv__stream_osx_interrupt_select(stream);++ /* Shutdown? */+ if ((stream->flags & UV_HANDLE_SHUTTING) &&+ !(stream->flags & UV_HANDLE_CLOSING) &&+ !(stream->flags & UV_HANDLE_SHUT)) {+ assert(stream->shutdown_req);++ req = stream->shutdown_req;+ stream->shutdown_req = NULL;+ stream->flags &= ~UV_HANDLE_SHUTTING;+ uv__req_unregister(stream->loop, req);++ err = 0;+ if (shutdown(uv__stream_fd(stream), SHUT_WR))+ err = UV__ERR(errno);++ if (err == 0)+ stream->flags |= UV_HANDLE_SHUT;++ if (req->cb != NULL)+ req->cb(req, err);+ }+}+++static ssize_t uv__writev(int fd, struct iovec* vec, size_t n) {+ if (n == 1)+ return write(fd, vec->iov_base, vec->iov_len);+ else+ return writev(fd, vec, n);+}+++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;+}+++/* Returns 1 if all write request data has been written, or 0 if there is still+ * more data to write.+ *+ * Note: the return value only says something about the *current* request.+ * There may still be other write requests sitting in the queue.+ */+static int uv__write_req_update(uv_stream_t* stream,+ uv_write_t* req,+ size_t n) {+ uv_buf_t* buf;+ size_t len;++ assert(n <= stream->write_queue_size);+ stream->write_queue_size -= n;++ buf = req->bufs + req->write_index;++ do {+ len = n < buf->len ? n : buf->len;+ buf->base += len;+ buf->len -= len;+ buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */+ n -= len;+ } while (n > 0);++ req->write_index = buf - req->bufs;++ return req->write_index == req->nbufs;+}+++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;+ int err;++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) {+ int fd_to_send;+ struct msghdr msg;+ struct cmsghdr *cmsg;+ union {+ char data[64];+ struct cmsghdr alias;+ } scratch;++ if (uv__is_closing(req->send_handle)) {+ err = UV_EBADF;+ goto error;+ }++ fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle);++ memset(&scratch, 0, sizeof(scratch));++ 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 = &scratch.alias;+ 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);+ while (n == -1 && RETRY_ON_WRITE_ERROR(errno));++ /* Ensure the handle isn't sent again in case this is a partial write. */+ if (n >= 0)+ req->send_handle = NULL;+ } else {+ do+ n = uv__writev(uv__stream_fd(stream), iov, iovcnt);+ while (n == -1 && RETRY_ON_WRITE_ERROR(errno));+ }++ if (n == -1 && !IS_TRANSIENT_WRITE_ERROR(errno, req->send_handle)) {+ err = UV__ERR(errno);+ goto error;+ }++ if (n >= 0 && uv__write_req_update(stream, req, n)) {+ uv__write_req_finish(req);+ return; /* TODO(bnoordhuis) Start trying to write the next request. */+ }++ /* If this is a blocking stream, try again. */+ if (stream->flags & UV_HANDLE_BLOCKING_WRITES)+ goto start;++ /* We're not done. */+ uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);++ /* Notify select() thread about state change */+ uv__stream_osx_interrupt_select(stream);++ return;++error:+ req->error = err;+ uv__write_req_finish(req);+ uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);+ if (!uv__io_active(&stream->io_watcher, POLLIN))+ uv__handle_stop(stream);+ uv__stream_osx_interrupt_select(stream);+}+++static void uv__write_callbacks(uv_stream_t* stream) {+ uv_write_t* req;+ QUEUE* q;+ QUEUE pq;++ if (QUEUE_EMPTY(&stream->write_completed_queue))+ return;++ QUEUE_MOVE(&stream->write_completed_queue, &pq);++ while (!QUEUE_EMPTY(&pq)) {+ /* Pop a req off write_completed_queue. */+ q = QUEUE_HEAD(&pq);+ 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);+ }+}+++uv_handle_type uv__handle_type(int fd) {+ struct sockaddr_storage ss;+ socklen_t sslen;+ socklen_t len;+ int type;++ memset(&ss, 0, sizeof(ss));+ sslen = sizeof(ss);++ if (getsockname(fd, (struct sockaddr*)&ss, &sslen))+ return UV_UNKNOWN_HANDLE;++ len = sizeof type;++ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len))+ return UV_UNKNOWN_HANDLE;++ if (type == SOCK_STREAM) {+#if defined(_AIX) || defined(__DragonFly__)+ /* on AIX/DragonFly 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 (sslen == 0)+ return UV_NAMED_PIPE;+#endif+ 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_HANDLE_READ_EOF;+ stream->flags &= ~UV_HANDLE_READING;+ uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);+ if (!uv__io_active(&stream->io_watcher, POLLOUT))+ uv__handle_stop(stream);+ uv__stream_osx_interrupt_select(stream);+ stream->read_cb(stream, UV_EOF, buf);+}+++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 UV_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 UV_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;+}+++#if defined(__PASE__)+/* on IBMi PASE the control message length can not exceed 256. */+# define UV__CMSG_FD_COUNT 60+#else+# define UV__CMSG_FD_COUNT 64+#endif+#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;+}+++#ifdef __clang__+# pragma clang diagnostic push+# pragma clang diagnostic ignored "-Wgnu-folding-constant"+# pragma clang diagnostic ignored "-Wvla-extension"+#endif++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_HANDLE_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_HANDLE_READING we just test if+ * tcp->read_cb is NULL or not?+ */+ while (stream->read_cb+ && (stream->flags & UV_HANDLE_READING)+ && (count-- > 0)) {+ assert(stream->alloc_cb != NULL);++ buf = uv_buf_init(NULL, 0);+ stream->alloc_cb((uv_handle_t*)stream, 64 * 1024, &buf);+ if (buf.base == NULL || 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_HANDLE_READING) {+ uv__io_start(stream->loop, &stream->io_watcher, POLLIN);+ uv__stream_osx_interrupt_select(stream);+ }+ stream->read_cb(stream, 0, &buf);+#if defined(__CYGWIN__) || defined(__MSYS__)+ } else if (errno == ECONNRESET && stream->type == UV_NAMED_PIPE) {+ uv__stream_eof(stream, &buf);+ return;+#endif+ } else {+ /* Error. User should call uv_close(). */+ stream->read_cb(stream, UV__ERR(errno), &buf);+ if (stream->flags & UV_HANDLE_READING) {+ stream->flags &= ~UV_HANDLE_READING;+ uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);+ if (!uv__io_active(&stream->io_watcher, 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;+ }+ }++#if defined(__MVS__)+ if (is_ipc && msg.msg_controllen > 0) {+ uv_buf_t blankbuf;+ int nread;+ struct iovec *old;++ blankbuf.base = 0;+ blankbuf.len = 0;+ old = msg.msg_iov;+ msg.msg_iov = (struct iovec*) &blankbuf;+ nread = 0;+ do {+ nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);+ err = uv__stream_recv_cmsg(stream, &msg);+ if (err != 0) {+ stream->read_cb(stream, err, &buf);+ msg.msg_iov = old;+ return;+ }+ } while (nread == 0 && msg.msg_controllen > 0);+ msg.msg_iov = old;+ }+#endif+ 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_HANDLE_READ_PARTIAL;+ return;+ }+ }+ }+}+++#ifdef __clang__+# pragma clang diagnostic pop+#endif++#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_TTY ||+ stream->type == UV_NAMED_PIPE);++ if (!(stream->flags & UV_HANDLE_WRITABLE) ||+ stream->flags & UV_HANDLE_SHUT ||+ stream->flags & UV_HANDLE_SHUTTING ||+ uv__is_closing(stream)) {+ return UV_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_HANDLE_SHUTTING;++ uv__io_start(stream->loop, &stream->io_watcher, 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_HANDLE_CLOSING));++ if (stream->connect_req) {+ uv__stream_connect(stream);+ return;+ }++ assert(uv__stream_fd(stream) >= 0);++ /* Ignore POLLHUP here. Even if it's set, there may still be data to read. */+ if (events & (POLLIN | POLLERR | 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 & POLLHUP) &&+ (stream->flags & UV_HANDLE_READING) &&+ (stream->flags & UV_HANDLE_READ_PARTIAL) &&+ !(stream->flags & UV_HANDLE_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 & (POLLOUT | POLLERR | 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 = UV__ERR(error);+ }++ if (error == UV__ERR(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, POLLOUT);+ }++ if (req->cb)+ req->cb(req, error);++ if (uv__stream_fd(stream) == -1)+ return;++ if (error < 0) {+ uv__stream_flush_write_queue(stream, UV_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 UV_EBADF;++ if (!(stream->flags & UV_HANDLE_WRITABLE))+ return UV_EPIPE;++ if (send_handle) {+ if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc)+ return UV_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 UV_EBADF;++#if defined(__CYGWIN__) || defined(__MSYS__)+ /* Cygwin recvmsg always sets msg_controllen to zero, so we cannot send it.+ See https://github.com/mirror/newlib-cygwin/blob/86fc4bf0/winsup/cygwin/fhandler_socket.cc#L1736-L1743 */+ return UV_ENOSYS;+#endif+ }++ /* 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 UV_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_HANDLE_BLOCKING_WRITES));+ uv__io_start(stream->loop, &stream->io_watcher, 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 UV_EAGAIN;++ has_pollout = uv__io_active(&stream->io_watcher, 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, POLLOUT);+ uv__stream_osx_interrupt_select(stream);+ }++ if (written == 0 && req_size != 0)+ return req.error < 0 ? req.error : UV_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_HANDLE_CLOSING)+ return UV_EINVAL;++ if (!(stream->flags & UV_HANDLE_READABLE))+ return UV_ENOTCONN;++ /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just+ * expresses the desired state of the user.+ */+ stream->flags |= UV_HANDLE_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, 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_HANDLE_READING))+ return 0;++ stream->flags &= ~UV_HANDLE_READING;+ uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);+ if (!uv__io_active(&stream->io_watcher, 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_HANDLE_READABLE);+}+++int uv_is_writable(const uv_stream_t* stream) {+ return !!(stream->flags & UV_HANDLE_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);+ handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);++ 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, POLLIN | 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);+}
+ third_party/libuv/src/unix/tcp.c view
@@ -0,0 +1,461 @@+/* 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 <stdlib.h>+#include <unistd.h>+#include <assert.h>+#include <errno.h>+++static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {+ struct sockaddr_storage saddr;+ socklen_t slen;+ int sockfd;+ int err;++ 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;+ }++ if (flags & UV_HANDLE_BOUND) {+ /* Bind this new socket to an arbitrary port */+ slen = sizeof(saddr);+ memset(&saddr, 0, sizeof(saddr));+ if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) {+ uv__close(sockfd);+ return UV__ERR(errno);+ }++ if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) {+ uv__close(sockfd);+ return UV__ERR(errno);+ }+ }++ return 0;+}+++static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {+ struct sockaddr_storage saddr;+ socklen_t slen;++ if (domain == AF_UNSPEC) {+ handle->flags |= flags;+ return 0;+ }++ if (uv__stream_fd(handle) != -1) {++ if (flags & UV_HANDLE_BOUND) {++ if (handle->flags & UV_HANDLE_BOUND) {+ /* It is already bound to a port. */+ handle->flags |= flags;+ return 0;+ }++ /* Query to see if tcp socket is bound. */+ slen = sizeof(saddr);+ memset(&saddr, 0, sizeof(saddr));+ if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen))+ return UV__ERR(errno);++ if ((saddr.ss_family == AF_INET6 &&+ ((struct sockaddr_in6*) &saddr)->sin6_port != 0) ||+ (saddr.ss_family == AF_INET &&+ ((struct sockaddr_in*) &saddr)->sin_port != 0)) {+ /* Handle is already bound to a port. */+ handle->flags |= flags;+ return 0;+ }++ /* Bind to arbitrary port */+ if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen))+ return UV__ERR(errno);+ }++ handle->flags |= flags;+ return 0;+ }++ return new_socket(handle, domain, flags);+}+++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 UV_EINVAL;++ if (flags & ~0xFF)+ return UV_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 UV_EINVAL;++ err = maybe_new_socket(tcp, addr->sa_family, 0);+ if (err)+ return err;++ on = 1;+ if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))+ return UV__ERR(errno);++#ifndef __OpenBSD__+#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) {+#if defined(__MVS__)+ if (errno == EOPNOTSUPP)+ return UV_EINVAL;+#endif+ return UV__ERR(errno);+ }+ }+#endif+#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 UV_EINVAL;+ return UV__ERR(errno);+ }+ tcp->delayed_error = UV__ERR(errno);++ tcp->flags |= UV_HANDLE_BOUND;+ 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 UV_EALREADY; /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */++ err = maybe_new_socket(handle,+ addr->sa_family,+ UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);+ if (err)+ return err;++ handle->delayed_error = 0;++ do {+ errno = 0;+ r = connect(uv__stream_fd(handle), addr, addrlen);+ } while (r == -1 && errno == EINTR);++ /* We not only check the return value, but also check the errno != 0.+ * Because in rare cases connect() will return -1 but the errno+ * is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227)+ * and actually the tcp three-way handshake is completed.+ */+ if (r == -1 && errno != 0) {+ if (errno == EINPROGRESS)+ ; /* not an error */+ else if (errno == ECONNREFUSED+#if defined(__OpenBSD__)+ || errno == EINVAL+#endif+ )+ /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the+ * next tick to report the error. Solaris and OpenBSD wants to report+ * immediately -- other unixes want to wait.+ */+ handle->delayed_error = UV__ERR(ECONNREFUSED);+ else+ return UV__ERR(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, 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;++ if (uv__fd_exists(handle->loop, sock))+ return UV_EEXIST;++ err = uv__nonblock(sock, 1);+ if (err)+ return err;++ return uv__stream_open((uv_stream_t*)handle,+ sock,+ UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);+}+++int uv_tcp_getsockname(const uv_tcp_t* handle,+ struct sockaddr* name,+ int* namelen) {++ if (handle->delayed_error)+ return handle->delayed_error;++ return uv__getsockpeername((const uv_handle_t*) handle,+ getsockname,+ name,+ namelen);+}+++int uv_tcp_getpeername(const uv_tcp_t* handle,+ struct sockaddr* name,+ int* namelen) {++ if (handle->delayed_error)+ return handle->delayed_error;++ return uv__getsockpeername((const uv_handle_t*) handle,+ getpeername,+ name,+ namelen);+}+++int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {+ int fd;+ struct linger l = { 1, 0 };++ /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */+ if (handle->flags & UV_HANDLE_SHUTTING)+ return UV_EINVAL;++ fd = uv__stream_fd(handle);+ if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)))+ return UV__ERR(errno);++ uv_close((uv_handle_t*) handle, close_cb);+ return 0;+}+++int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {+ static int single_accept_cached = -1;+ unsigned long flags;+ int single_accept;+ int err;++ if (tcp->delayed_error)+ return tcp->delayed_error;++ single_accept = uv__load_relaxed(&single_accept_cached);+ if (single_accept == -1) {+ const char* val = getenv("UV_TCP_SINGLE_ACCEPT");+ single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */+ uv__store_relaxed(&single_accept_cached, single_accept);+ }++ if (single_accept)+ tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;++ flags = 0;+#if defined(__MVS__)+ /* on zOS the listen call does not bind automatically+ if the socket is unbound. Hence the manual binding to+ an arbitrary port is required to be done manually+ */+ flags |= UV_HANDLE_BOUND;+#endif+ err = maybe_new_socket(tcp, AF_INET, flags);+ if (err)+ return err;++ if (listen(tcp->io_watcher.fd, backlog))+ return UV__ERR(errno);++ tcp->connection_cb = cb;+ tcp->flags |= UV_HANDLE_BOUND;++ /* Start listening for connections. */+ tcp->io_watcher.cb = uv__server_io;+ uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);++ return 0;+}+++int uv__tcp_nodelay(int fd, int on) {+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)))+ return UV__ERR(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 UV__ERR(errno);++#ifdef TCP_KEEPIDLE+ if (on) {+ int intvl = 1; /* 1 second; same as default on Win32 */+ int cnt = 10; /* 10 retries; same as hardcoded on Win32 */+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))+ return UV__ERR(errno);+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))+ return UV__ERR(errno);+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))+ return UV__ERR(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 UV__ERR(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_HANDLE_TCP_NODELAY;+ else+ handle->flags &= ~UV_HANDLE_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_HANDLE_TCP_KEEPALIVE;+ else+ handle->flags &= ~UV_HANDLE_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_HANDLE_TCP_SINGLE_ACCEPT;+ else+ handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;+ return 0;+}+++void uv__tcp_close(uv_tcp_t* handle) {+ uv__stream_close((uv_stream_t*)handle);+}
+ third_party/libuv/src/unix/thread.c view
@@ -0,0 +1,842 @@+/* 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 <pthread.h>+#include <assert.h>+#include <errno.h>++#include <sys/time.h>+#include <sys/resource.h> /* getrlimit() */+#include <unistd.h> /* getpagesize() */++#include <limits.h>++#ifdef __MVS__+#include <sys/ipc.h>+#include <sys/sem.h>+#endif++#if defined(__GLIBC__) && !defined(__UCLIBC__)+#include <gnu/libc-version.h> /* gnu_get_libc_version() */+#endif++#undef NANOSEC+#define NANOSEC ((uint64_t) 1e9)++#if defined(PTHREAD_BARRIER_SERIAL_THREAD)+STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));+#endif++/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */+#if defined(_AIX) || \+ defined(__OpenBSD__) || \+ !defined(PTHREAD_BARRIER_SERIAL_THREAD)+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {+ struct _uv_barrier* b;+ int rc;++ if (barrier == NULL || count == 0)+ return UV_EINVAL;++ b = uv__malloc(sizeof(*b));+ if (b == NULL)+ return UV_ENOMEM;++ b->in = 0;+ b->out = 0;+ b->threshold = count;++ rc = uv_mutex_init(&b->mutex);+ if (rc != 0)+ goto error2;++ rc = uv_cond_init(&b->cond);+ if (rc != 0)+ goto error;++ barrier->b = b;+ return 0;++error:+ uv_mutex_destroy(&b->mutex);+error2:+ uv__free(b);+ return rc;+}+++int uv_barrier_wait(uv_barrier_t* barrier) {+ struct _uv_barrier* b;+ int last;++ if (barrier == NULL || barrier->b == NULL)+ return UV_EINVAL;++ b = barrier->b;+ uv_mutex_lock(&b->mutex);++ if (++b->in == b->threshold) {+ b->in = 0;+ b->out = b->threshold;+ uv_cond_signal(&b->cond);+ } else {+ do+ uv_cond_wait(&b->cond, &b->mutex);+ while (b->in != 0);+ }++ last = (--b->out == 0);+ if (!last)+ uv_cond_signal(&b->cond); /* Not needed for last thread. */++ uv_mutex_unlock(&b->mutex);+ return last;+}+++void uv_barrier_destroy(uv_barrier_t* barrier) {+ struct _uv_barrier* b;++ b = barrier->b;+ uv_mutex_lock(&b->mutex);++ assert(b->in == 0);+ assert(b->out == 0);++ if (b->in != 0 || b->out != 0)+ abort();++ uv_mutex_unlock(&b->mutex);+ uv_mutex_destroy(&b->mutex);+ uv_cond_destroy(&b->cond);++ uv__free(barrier->b);+ barrier->b = NULL;+}++#else++int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {+ return UV__ERR(pthread_barrier_init(barrier, NULL, count));+}+++int uv_barrier_wait(uv_barrier_t* barrier) {+ int rc;++ rc = pthread_barrier_wait(barrier);+ if (rc != 0)+ if (rc != PTHREAD_BARRIER_SERIAL_THREAD)+ abort();++ return rc == PTHREAD_BARRIER_SERIAL_THREAD;+}+++void uv_barrier_destroy(uv_barrier_t* barrier) {+ if (pthread_barrier_destroy(barrier))+ abort();+}++#endif+++/* On MacOS, threads other than the main thread are created with a reduced+ * stack size by default. Adjust to RLIMIT_STACK aligned to the page size.+ *+ * On Linux, threads created by musl have a much smaller stack than threads+ * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency.+ */+static size_t thread_stack_size(void) {+#if defined(__APPLE__) || defined(__linux__)+ struct rlimit lim;++ /* getrlimit() can fail on some aarch64 systems due to a glibc bug where+ * the system call wrapper invokes the wrong system call. Don't treat+ * that as fatal, just use the default stack size instead.+ */+ if (0 == getrlimit(RLIMIT_STACK, &lim) && lim.rlim_cur != RLIM_INFINITY) {+ /* pthread_attr_setstacksize() expects page-aligned values. */+ lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();++ /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is+ * too small to safely receive signals on.+ *+ * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has+ * the largest MINSIGSTKSZ of the architectures that musl supports) so+ * let's use that as a lower bound.+ *+ * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ+ * is between 28 and 133 KB when compiling against glibc, depending+ * on the architecture.+ */+ if (lim.rlim_cur >= 8192)+ if (lim.rlim_cur >= PTHREAD_STACK_MIN)+ return lim.rlim_cur;+ }+#endif++#if !defined(__linux__)+ return 0;+#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__)+ return 4 << 20; /* glibc default. */+#else+ return 2 << 20; /* glibc default. */+#endif+}+++int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {+ uv_thread_options_t params;+ params.flags = UV_THREAD_NO_FLAGS;+ return uv_thread_create_ex(tid, ¶ms, entry, arg);+}++int uv_thread_create_ex(uv_thread_t* tid,+ const uv_thread_options_t* params,+ void (*entry)(void *arg),+ void *arg) {+ int err;+ pthread_attr_t* attr;+ pthread_attr_t attr_storage;+ size_t pagesize;+ size_t stack_size;++ /* Used to squelch a -Wcast-function-type warning. */+ union {+ void (*in)(void*);+ void* (*out)(void*);+ } f;++ stack_size =+ params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;++ attr = NULL;+ if (stack_size == 0) {+ stack_size = thread_stack_size();+ } else {+ pagesize = (size_t)getpagesize();+ /* Round up to the nearest page boundary. */+ stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);+#ifdef PTHREAD_STACK_MIN+ if (stack_size < PTHREAD_STACK_MIN)+ stack_size = PTHREAD_STACK_MIN;+#endif+ }++ if (stack_size > 0) {+ attr = &attr_storage;++ if (pthread_attr_init(attr))+ abort();++ if (pthread_attr_setstacksize(attr, stack_size))+ abort();+ }++ f.in = entry;+ err = pthread_create(tid, attr, f.out, arg);++ if (attr != NULL)+ pthread_attr_destroy(attr);++ return UV__ERR(err);+}+++uv_thread_t uv_thread_self(void) {+ return pthread_self();+}++int uv_thread_join(uv_thread_t *tid) {+ return UV__ERR(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 UV__ERR(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 UV__ERR(err);+#endif+}+++int uv_mutex_init_recursive(uv_mutex_t* mutex) {+ pthread_mutexattr_t attr;+ int err;++ if (pthread_mutexattr_init(&attr))+ abort();++ if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))+ abort();++ err = pthread_mutex_init(mutex, &attr);++ if (pthread_mutexattr_destroy(&attr))+ abort();++ return UV__ERR(err);+}+++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 UV_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 UV__ERR(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 UV_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 UV_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 UV_EINVAL;+ if (err == KERN_RESOURCE_SHORTAGE)+ return UV_ENOMEM;++ abort();+ return UV_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 UV_EAGAIN;++ abort();+ return UV_EINVAL; /* Satisfy the compiler. */+}++#else /* !(defined(__APPLE__) && defined(__MACH__)) */++#if defined(__GLIBC__) && !defined(__UCLIBC__)++/* Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674+ * by providing a custom implementation for glibc < 2.21 in terms of other+ * concurrency primitives.+ * Refs: https://github.com/nodejs/node/issues/19903 */++/* To preserve ABI compatibility, we treat the uv_sem_t as storage for+ * a pointer to the actual struct we're using underneath. */++static uv_once_t glibc_version_check_once = UV_ONCE_INIT;+static int platform_needs_custom_semaphore = 0;++static void glibc_version_check(void) {+ const char* version = gnu_get_libc_version();+ platform_needs_custom_semaphore =+ version[0] == '2' && version[1] == '.' &&+ atoi(version + 2) < 21;+}++#elif defined(__MVS__)++#define platform_needs_custom_semaphore 1++#else /* !defined(__GLIBC__) && !defined(__MVS__) */++#define platform_needs_custom_semaphore 0++#endif++typedef struct uv_semaphore_s {+ uv_mutex_t mutex;+ uv_cond_t cond;+ unsigned int value;+} uv_semaphore_t;++#if (defined(__GLIBC__) && !defined(__UCLIBC__)) || \+ platform_needs_custom_semaphore+STATIC_ASSERT(sizeof(uv_sem_t) >= sizeof(uv_semaphore_t*));+#endif++static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) {+ int err;+ uv_semaphore_t* sem;++ sem = uv__malloc(sizeof(*sem));+ if (sem == NULL)+ return UV_ENOMEM;++ if ((err = uv_mutex_init(&sem->mutex)) != 0) {+ uv__free(sem);+ return err;+ }++ if ((err = uv_cond_init(&sem->cond)) != 0) {+ uv_mutex_destroy(&sem->mutex);+ uv__free(sem);+ return err;+ }++ sem->value = value;+ *(uv_semaphore_t**)sem_ = sem;+ return 0;+}+++static void uv__custom_sem_destroy(uv_sem_t* sem_) {+ uv_semaphore_t* sem;++ sem = *(uv_semaphore_t**)sem_;+ uv_cond_destroy(&sem->cond);+ uv_mutex_destroy(&sem->mutex);+ uv__free(sem);+}+++static void uv__custom_sem_post(uv_sem_t* sem_) {+ uv_semaphore_t* sem;++ sem = *(uv_semaphore_t**)sem_;+ uv_mutex_lock(&sem->mutex);+ sem->value++;+ if (sem->value == 1)+ uv_cond_signal(&sem->cond);+ uv_mutex_unlock(&sem->mutex);+}+++static void uv__custom_sem_wait(uv_sem_t* sem_) {+ uv_semaphore_t* sem;++ sem = *(uv_semaphore_t**)sem_;+ uv_mutex_lock(&sem->mutex);+ while (sem->value == 0)+ uv_cond_wait(&sem->cond, &sem->mutex);+ sem->value--;+ uv_mutex_unlock(&sem->mutex);+}+++static int uv__custom_sem_trywait(uv_sem_t* sem_) {+ uv_semaphore_t* sem;++ sem = *(uv_semaphore_t**)sem_;+ if (uv_mutex_trylock(&sem->mutex) != 0)+ return UV_EAGAIN;++ if (sem->value == 0) {+ uv_mutex_unlock(&sem->mutex);+ return UV_EAGAIN;+ }++ sem->value--;+ uv_mutex_unlock(&sem->mutex);++ return 0;+}++static int uv__sem_init(uv_sem_t* sem, unsigned int value) {+ if (sem_init(sem, 0, value))+ return UV__ERR(errno);+ return 0;+}+++static void uv__sem_destroy(uv_sem_t* sem) {+ if (sem_destroy(sem))+ abort();+}+++static void uv__sem_post(uv_sem_t* sem) {+ if (sem_post(sem))+ abort();+}+++static void uv__sem_wait(uv_sem_t* sem) {+ int r;++ do+ r = sem_wait(sem);+ while (r == -1 && errno == EINTR);++ if (r)+ abort();+}+++static 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 UV_EAGAIN;+ abort();+ }++ return 0;+}++int uv_sem_init(uv_sem_t* sem, unsigned int value) {+#if defined(__GLIBC__) && !defined(__UCLIBC__)+ uv_once(&glibc_version_check_once, glibc_version_check);+#endif++ if (platform_needs_custom_semaphore)+ return uv__custom_sem_init(sem, value);+ else+ return uv__sem_init(sem, value);+}+++void uv_sem_destroy(uv_sem_t* sem) {+ if (platform_needs_custom_semaphore)+ uv__custom_sem_destroy(sem);+ else+ uv__sem_destroy(sem);+}+++void uv_sem_post(uv_sem_t* sem) {+ if (platform_needs_custom_semaphore)+ uv__custom_sem_post(sem);+ else+ uv__sem_post(sem);+}+++void uv_sem_wait(uv_sem_t* sem) {+ if (platform_needs_custom_semaphore)+ uv__custom_sem_wait(sem);+ else+ uv__sem_wait(sem);+}+++int uv_sem_trywait(uv_sem_t* sem) {+ if (platform_needs_custom_semaphore)+ return uv__custom_sem_trywait(sem);+ else+ return uv__sem_trywait(sem);+}++#endif /* defined(__APPLE__) && defined(__MACH__) */+++#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__)++int uv_cond_init(uv_cond_t* cond) {+ return UV__ERR(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 UV__ERR(err);++ err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);+ if (err)+ goto error2;++ 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 UV__ERR(err);+}++#endif /* defined(__APPLE__) && defined(__MACH__) */++void uv_cond_destroy(uv_cond_t* cond) {+#if defined(__APPLE__) && defined(__MACH__)+ /* It has been reported that destroying condition variables that have been+ * signalled but not waited on can sometimes result in application crashes.+ * See https://codereview.chromium.org/1323293005.+ */+ pthread_mutex_t mutex;+ struct timespec ts;+ int err;++ if (pthread_mutex_init(&mutex, NULL))+ abort();++ if (pthread_mutex_lock(&mutex))+ abort();++ ts.tv_sec = 0;+ ts.tv_nsec = 1;++ err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);+ if (err != 0 && err != ETIMEDOUT)+ abort();++ if (pthread_mutex_unlock(&mutex))+ abort();++ if (pthread_mutex_destroy(&mutex))+ abort();+#endif /* defined(__APPLE__) && defined(__MACH__) */++ 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(__MVS__)+ struct timeval tv;+#endif++#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+#if defined(__MVS__)+ if (gettimeofday(&tv, NULL))+ abort();+ timeout += tv.tv_sec * NANOSEC + tv.tv_usec * 1e3;+#else+ timeout += uv__hrtime(UV_CLOCK_PRECISE);+#endif+ ts.tv_sec = timeout / NANOSEC;+ ts.tv_nsec = timeout % NANOSEC;+ r = pthread_cond_timedwait(cond, mutex, &ts);+#endif+++ if (r == 0)+ return 0;++ if (r == ETIMEDOUT)+ return UV_ETIMEDOUT;++ abort();+#ifndef __SUNPRO_C+ return UV_EINVAL; /* Satisfy the compiler. */+#endif+}+++int uv_key_create(uv_key_t* key) {+ return UV__ERR(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();+}
+ third_party/libuv/src/unix/tty.c view
@@ -0,0 +1,402 @@+/* 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 <stdlib.h>+#include <assert.h>+#include <unistd.h>+#include <termios.h>+#include <errno.h>+#include <sys/ioctl.h>++#if defined(__MVS__) && !defined(IMAXBEL)+#define IMAXBEL 0+#endif++#if defined(__PASE__)+/* On IBM i PASE, for better compatibility with running interactive programs in+ * a 5250 environment, isatty() will return true for the stdin/stdout/stderr+ * streams created by QSH/QP2TERM.+ *+ * For more, see docs on PASE_STDIO_ISATTY in+ * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/apis/pase_environ.htm+ *+ * This behavior causes problems for Node as it expects that if isatty() returns+ * true that TTY ioctls will be supported by that fd (which is not an+ * unreasonable expectation) and when they don't it crashes with assertion+ * errors.+ *+ * Here, we create our own version of isatty() that uses ioctl() to identify+ * whether the fd is *really* a TTY or not.+ */+static int isreallyatty(int file) {+ int rc;+ + rc = !ioctl(file, TXISATTY + 0x81, NULL);+ if (!rc && errno != EBADF)+ errno = ENOTTY;++ return rc;+}+#define isatty(fd) isreallyatty(fd)+#endif++static int orig_termios_fd = -1;+static struct termios orig_termios;+static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;++static int uv__tty_is_slave(const int fd) {+ int result;+#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)+ int dummy;++ result = ioctl(fd, TIOCGPTN, &dummy) != 0;+#elif defined(__APPLE__)+ char dummy[256];++ result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;+#elif defined(__NetBSD__)+ /*+ * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave+ * device name for both descriptors, the master one and slave one.+ *+ * Implement function to compare major device number with pts devices.+ *+ * The major numbers are machine-dependent, on NetBSD/amd64 they are+ * respectively:+ * - master tty: ptc - major 6+ * - slave tty: pts - major 5+ */++ struct stat sb;+ /* Lookup device's major for the pts driver and cache it. */+ static devmajor_t pts = NODEVMAJOR;++ if (pts == NODEVMAJOR) {+ pts = getdevmajor("pts", S_IFCHR);+ if (pts == NODEVMAJOR)+ abort();+ }++ /* Lookup stat structure behind the file descriptor. */+ if (fstat(fd, &sb) != 0)+ abort();++ /* Assert character device. */+ if (!S_ISCHR(sb.st_mode))+ abort();++ /* Assert valid major. */+ if (major(sb.st_rdev) == NODEVMAJOR)+ abort();++ result = (pts == major(sb.st_rdev));+#else+ /* Fallback to ptsname+ */+ result = ptsname(fd) == NULL;+#endif+ return result;+}++int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) {+ uv_handle_type type;+ int flags;+ int newfd;+ int r;+ int saved_flags;+ int mode;+ char path[256];+ (void)unused; /* deprecated parameter is no longer needed */++ /* 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 UV_EINVAL;++ flags = 0;+ newfd = -1;++ /* Save the fd flags in case we need to restore them due to an error. */+ do+ saved_flags = fcntl(fd, F_GETFL);+ while (saved_flags == -1 && errno == EINTR);++ if (saved_flags == -1)+ return UV__ERR(errno);+ mode = saved_flags & O_ACCMODE;++ /* 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) {+ /* Reopening a pty in master mode won't work either because the reopened+ * pty will be in slave mode (*BSD) or reopening will allocate a new+ * master/slave pair (Linux). Therefore check if the fd points to a+ * slave device.+ */+ if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)+ r = uv__open_cloexec(path, mode | O_NOCTTY);+ else+ r = -1;++ if (r < 0) {+ /* fallback to using blocking writes */+ if (mode != O_RDONLY)+ flags |= UV_HANDLE_BLOCKING_WRITES;+ goto skip;+ }++ newfd = r;++ r = uv__dup2_cloexec(newfd, fd);+ if (r < 0 && r != UV_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 (!(flags & UV_HANDLE_BLOCKING_WRITES))+ uv__nonblock(fd, 1);++#if defined(__APPLE__)+ r = uv__stream_try_select((uv_stream_t*) tty, &fd);+ if (r) {+ int rc = r;+ if (newfd != -1)+ uv__close(newfd);+ QUEUE_REMOVE(&tty->handle_queue);+ do+ r = fcntl(fd, F_SETFL, saved_flags);+ while (r == -1 && errno == EINTR);+ return rc;+ }+#endif++ if (mode != O_WRONLY)+ flags |= UV_HANDLE_READABLE;+ if (mode != O_RDONLY)+ flags |= UV_HANDLE_WRITABLE;++ 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);++#if defined __sun || defined __MVS__+ /*+ * 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 UV__ERR(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 UV__ERR(errno);++ tty->mode = mode;+ return 0;+}+++int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {+ struct winsize ws;+ int err;++ do+ err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws);+ while (err == -1 && errno == EINTR);++ if (err == -1)+ return UV__ERR(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) || defined(__DragonFly__)+ /* on AIX/DragonFly 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) || defined(__DragonFly__) */++ 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 UV_EBUSY; /* In uv_tty_set_mode(). */++ err = 0;+ if (orig_termios_fd != -1)+ if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios))+ err = UV__ERR(errno);++ uv_spinlock_unlock(&termios_spinlock);+ errno = saved_errno;++ return err;+}++void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {+}++int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {+ return UV_ENOTSUP;+}
+ third_party/libuv/src/unix/udp.c view
@@ -0,0 +1,1324 @@+/* 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 <assert.h>+#include <string.h>+#include <errno.h>+#include <stdlib.h>+#include <unistd.h>+#if defined(__MVS__)+#include <xti.h>+#endif+#include <sys/un.h>++#define UV__UDP_DGRAM_MAXSIZE (64 * 1024)++#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++union uv__sockaddr {+ struct sockaddr_in6 in6;+ struct sockaddr_in in;+ struct sockaddr addr;+};++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);++#if HAVE_MMSG++#define UV__MMSG_MAXWIDTH 20++static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf);+static void uv__udp_sendmmsg(uv_udp_t* handle);++static int uv__recvmmsg_avail;+static int uv__sendmmsg_avail;+static uv_once_t once = UV_ONCE_INIT;++static void uv__udp_mmsg_init(void) {+ int ret;+ int s;+ s = uv__socket(AF_INET, SOCK_DGRAM, 0);+ if (s < 0)+ return;+ ret = uv__sendmmsg(s, NULL, 0, 0);+ if (ret == 0 || errno != ENOSYS) {+ uv__sendmmsg_avail = 1;+ uv__recvmmsg_avail = 1;+ } else {+ ret = uv__recvmmsg(s, NULL, 0, 0, NULL);+ if (ret == 0 || errno != ENOSYS)+ uv__recvmmsg_avail = 1;+ }+ uv__close(s);+}++#endif++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, POLLIN | 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 = UV_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_HANDLE_UDP_PROCESSING));+ handle->flags |= UV_HANDLE_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, POLLOUT);+ if (!uv__io_active(&handle->io_watcher, POLLIN))+ uv__handle_stop(handle);+ }++ handle->flags &= ~UV_HANDLE_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 & POLLIN)+ uv__udp_recvmsg(handle);++ if (revents & POLLOUT) {+ uv__udp_sendmsg(handle);+ uv__udp_run_completed(handle);+ }+}++#if HAVE_MMSG+static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {+ struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH];+ struct iovec iov[UV__MMSG_MAXWIDTH];+ struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH];+ ssize_t nread;+ uv_buf_t chunk_buf;+ size_t chunks;+ int flags;+ size_t k;++ /* prepare structures for recvmmsg */+ chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;+ if (chunks > ARRAY_SIZE(iov))+ chunks = ARRAY_SIZE(iov);+ for (k = 0; k < chunks; ++k) {+ iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE;+ iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE;+ msgs[k].msg_hdr.msg_iov = iov + k;+ msgs[k].msg_hdr.msg_iovlen = 1;+ msgs[k].msg_hdr.msg_name = peers + k;+ msgs[k].msg_hdr.msg_namelen = sizeof(peers[0]);+ msgs[k].msg_hdr.msg_control = NULL;+ msgs[k].msg_hdr.msg_controllen = 0;+ msgs[k].msg_hdr.msg_flags = 0;+ }++ do+ nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);+ while (nread == -1 && errno == EINTR);++ if (nread < 1) {+ if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)+ handle->recv_cb(handle, 0, buf, NULL, 0);+ else+ handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0);+ } else {+ /* pass each chunk to the application */+ for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) {+ flags = UV_UDP_MMSG_CHUNK;+ if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC)+ flags |= UV_UDP_PARTIAL;++ chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len);+ handle->recv_cb(handle,+ msgs[k].msg_len,+ &chunk_buf,+ msgs[k].msg_hdr.msg_name,+ flags);+ }++ /* one last callback so the original buffer is freed */+ if (handle->recv_cb != NULL)+ handle->recv_cb(handle, 0, buf, NULL, 0);+ }+ return nread;+}+#endif++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;++ do {+ buf = uv_buf_init(NULL, 0);+ handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);+ if (buf.base == NULL || buf.len == 0) {+ handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);+ return;+ }+ assert(buf.base != NULL);++#if HAVE_MMSG+ if (uv_udp_using_recvmmsg(handle)) {+ nread = uv__udp_recvmmsg(handle, &buf);+ if (nread > 0)+ count -= nread;+ continue;+ }+#endif++ memset(&h, 0, sizeof(h));+ memset(&peer, 0, sizeof(peer));+ h.msg_name = &peer;+ 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, UV__ERR(errno), &buf, NULL, 0);+ }+ else {+ flags = 0;+ if (h.msg_flags & MSG_TRUNC)+ flags |= UV_UDP_PARTIAL;++ handle->recv_cb(handle, nread, &buf, (const struct sockaddr*) &peer, flags);+ }+ count--;+ }+ /* 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);+}++#if HAVE_MMSG+static void uv__udp_sendmmsg(uv_udp_t* handle) {+ uv_udp_send_t* req;+ struct uv__mmsghdr h[UV__MMSG_MAXWIDTH];+ struct uv__mmsghdr *p;+ QUEUE* q;+ ssize_t npkts;+ size_t pkts;+ size_t i;++ if (QUEUE_EMPTY(&handle->write_queue))+ return;++write_queue_drain:+ for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue);+ pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue;+ ++pkts, q = QUEUE_HEAD(q)) {+ assert(q != NULL);+ req = QUEUE_DATA(q, uv_udp_send_t, queue);+ assert(req != NULL);++ p = &h[pkts];+ memset(p, 0, sizeof(*p));+ if (req->addr.ss_family == AF_UNSPEC) {+ p->msg_hdr.msg_name = NULL;+ p->msg_hdr.msg_namelen = 0;+ } else {+ p->msg_hdr.msg_name = &req->addr;+ if (req->addr.ss_family == AF_INET6)+ p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);+ else if (req->addr.ss_family == AF_INET)+ p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in);+ else if (req->addr.ss_family == AF_UNIX)+ p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un);+ else {+ assert(0 && "unsupported address family");+ abort();+ }+ }+ h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs;+ h[pkts].msg_hdr.msg_iovlen = req->nbufs;+ }++ do+ npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts, 0);+ while (npkts == -1 && errno == EINTR);++ if (npkts < 1) {+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)+ return;+ for (i = 0, q = QUEUE_HEAD(&handle->write_queue);+ i < pkts && q != &handle->write_queue;+ ++i, q = QUEUE_HEAD(&handle->write_queue)) {+ assert(q != NULL);+ req = QUEUE_DATA(q, uv_udp_send_t, queue);+ assert(req != NULL);++ req->status = UV__ERR(errno);+ QUEUE_REMOVE(&req->queue);+ QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);+ }+ uv__io_feed(handle->loop, &handle->io_watcher);+ return;+ }++ for (i = 0, q = QUEUE_HEAD(&handle->write_queue);+ i < pkts && q != &handle->write_queue;+ ++i, q = QUEUE_HEAD(&handle->write_queue)) {+ assert(q != NULL);+ req = QUEUE_DATA(q, uv_udp_send_t, queue);+ assert(req != NULL);++ req->status = req->bufs[0].len;++ /* 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);+ }++ /* couldn't batch everything, continue sending (jump to avoid stack growth) */+ if (!QUEUE_EMPTY(&handle->write_queue))+ goto write_queue_drain;+ uv__io_feed(handle->loop, &handle->io_watcher);+ return;+}+#endif++static void uv__udp_sendmsg(uv_udp_t* handle) {+ uv_udp_send_t* req;+ struct msghdr h;+ QUEUE* q;+ ssize_t size;++#if HAVE_MMSG+ uv_once(&once, uv__udp_mmsg_init);+ if (uv__sendmmsg_avail) {+ uv__udp_sendmmsg(handle);+ return;+ }+#endif++ 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);+ if (req->addr.ss_family == AF_UNSPEC) {+ h.msg_name = NULL;+ h.msg_namelen = 0;+ } else {+ h.msg_name = &req->addr;+ if (req->addr.ss_family == AF_INET6)+ h.msg_namelen = sizeof(struct sockaddr_in6);+ else if (req->addr.ss_family == AF_INET)+ h.msg_namelen = sizeof(struct sockaddr_in);+ else if (req->addr.ss_family == AF_UNIX)+ h.msg_namelen = sizeof(struct sockaddr_un);+ else {+ assert(0 && "unsupported address family");+ abort();+ }+ }+ 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) {+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)+ break;+ }++ req->status = (size == -1 ? UV__ERR(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.+ *+ * zOS does not support getsockname with SO_REUSEPORT option when using+ * AF_UNIX.+ */+static int uv__set_reuse(int fd) {+ int yes;+ yes = 1;++#if defined(SO_REUSEPORT) && defined(__MVS__)+ struct sockaddr_in sockfd;+ unsigned int sockfd_len = sizeof(sockfd);+ if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1)+ return UV__ERR(errno);+ if (sockfd.sin_family == AF_UNIX) {+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))+ return UV__ERR(errno);+ } else {+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))+ return UV__ERR(errno);+ }+#elif defined(SO_REUSEPORT) && !defined(__linux__)+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))+ return UV__ERR(errno);+#else+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))+ return UV__ERR(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 UV_EINVAL;++ /* Cannot set IPv6-only mode on non-IPv6 socket. */+ if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6)+ return UV_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)+ return err;+ }++ if (flags & UV_UDP_IPV6ONLY) {+#ifdef IPV6_V6ONLY+ yes = 1;+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) {+ err = UV__ERR(errno);+ return err;+ }+#else+ err = UV_ENOTSUP;+ return err;+#endif+ }++ if (bind(fd, addr, addrlen)) {+ err = UV__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 = UV_EINVAL;+ return err;+ }++ if (addr->sa_family == AF_INET6)+ handle->flags |= UV_HANDLE_IPV6;++ handle->flags |= UV_HANDLE_BOUND;+ return 0;+}+++static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,+ int domain,+ unsigned int flags) {+ union uv__sockaddr taddr;+ socklen_t addrlen;++ if (handle->io_watcher.fd != -1)+ return 0;++ switch (domain) {+ case AF_INET:+ {+ struct sockaddr_in* addr = &taddr.in;+ 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 = &taddr.in6;+ 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, &taddr.addr, addrlen, flags);+}+++int uv__udp_connect(uv_udp_t* handle,+ const struct sockaddr* addr,+ unsigned int addrlen) {+ int err;++ err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);+ if (err)+ return err;++ do {+ errno = 0;+ err = connect(handle->io_watcher.fd, addr, addrlen);+ } while (err == -1 && errno == EINTR);++ if (err)+ return UV__ERR(errno);++ handle->flags |= UV_HANDLE_UDP_CONNECTED;++ return 0;+}+++int uv__udp_disconnect(uv_udp_t* handle) {+ int r;+ struct sockaddr addr;++ memset(&addr, 0, sizeof(addr));++ addr.sa_family = AF_UNSPEC;++ do {+ errno = 0;+ r = connect(handle->io_watcher.fd, &addr, sizeof(addr));+ } while (r == -1 && errno == EINTR);++ if (r == -1 && errno != EAFNOSUPPORT)+ return UV__ERR(errno);++ handle->flags &= ~UV_HANDLE_UDP_CONNECTED;+ return 0;+}+++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);++ if (addr) {+ 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));+ if (addr == NULL)+ req->addr.ss_family = AF_UNSPEC;+ else+ 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 UV_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_HANDLE_UDP_PROCESSING)) {+ uv__udp_sendmsg(handle);++ /* `uv__udp_sendmsg` may not be able to do non-blocking write straight+ * away. In such cases the `io_watcher` has to be queued for asynchronous+ * write.+ */+ if (!QUEUE_EMPTY(&handle->write_queue))+ uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);+ } else {+ uv__io_start(handle->loop, &handle->io_watcher, 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 UV_EAGAIN;++ if (addr) {+ err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);+ if (err)+ return err;+ } else {+ assert(handle->flags & UV_HANDLE_UDP_CONNECTED);+ }++ 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 || errno == ENOBUFS)+ return UV_EAGAIN;+ else+ return UV__ERR(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 UV_EINVAL;+ }++ if (setsockopt(handle->io_watcher.fd,+ IPPROTO_IP,+ optname,+ &mreq,+ sizeof(mreq))) {+#if defined(__MVS__)+ if (errno == ENXIO)+ return UV_ENODEV;+#endif+ return UV__ERR(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 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->io_watcher.fd,+ IPPROTO_IPV6,+ optname,+ &mreq,+ sizeof(mreq))) {+#if defined(__MVS__)+ if (errno == ENXIO)+ return UV_ENODEV;+#endif+ return UV__ERR(errno);+ }++ return 0;+}+++#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__ANDROID__)+static int uv__udp_set_source_membership4(uv_udp_t* handle,+ const struct sockaddr_in* multicast_addr,+ const char* interface_addr,+ const struct sockaddr_in* source_addr,+ uv_membership membership) {+ struct ip_mreq_source mreq;+ int optname;+ int err;++ err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);+ if (err)+ return err;++ memset(&mreq, 0, sizeof(mreq));++ if (interface_addr != NULL) {+ 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;+ mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr;++ if (membership == UV_JOIN_GROUP)+ optname = IP_ADD_SOURCE_MEMBERSHIP;+ else if (membership == UV_LEAVE_GROUP)+ optname = IP_DROP_SOURCE_MEMBERSHIP;+ else+ return UV_EINVAL;++ if (setsockopt(handle->io_watcher.fd,+ IPPROTO_IP,+ optname,+ &mreq,+ sizeof(mreq))) {+ return UV__ERR(errno);+ }++ return 0;+}+++static int uv__udp_set_source_membership6(uv_udp_t* handle,+ const struct sockaddr_in6* multicast_addr,+ const char* interface_addr,+ const struct sockaddr_in6* source_addr,+ uv_membership membership) {+ struct group_source_req mreq;+ struct sockaddr_in6 addr6;+ int optname;+ int err;++ err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);+ if (err)+ return err;++ memset(&mreq, 0, sizeof(mreq));++ if (interface_addr != NULL) {+ err = uv_ip6_addr(interface_addr, 0, &addr6);+ if (err)+ return err;+ mreq.gsr_interface = addr6.sin6_scope_id;+ } else {+ mreq.gsr_interface = 0;+ }++ STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr));+ STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr));+ memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr));+ memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr));++ if (membership == UV_JOIN_GROUP)+ optname = MCAST_JOIN_SOURCE_GROUP;+ else if (membership == UV_LEAVE_GROUP)+ optname = MCAST_LEAVE_SOURCE_GROUP;+ else+ return UV_EINVAL;++ if (setsockopt(handle->io_watcher.fd,+ IPPROTO_IPV6,+ optname,+ &mreq,+ sizeof(mreq))) {+ return UV__ERR(errno);+ }++ return 0;+}+#endif+++int uv__udp_init_ex(uv_loop_t* loop,+ uv_udp_t* handle,+ unsigned flags,+ int domain) {+ int fd;++ fd = -1;+ if (domain != AF_UNSPEC) {+ fd = uv__socket(domain, SOCK_DGRAM, 0);+ if (fd < 0)+ return fd;+ }++ 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_using_recvmmsg(const uv_udp_t* handle) {+#if HAVE_MMSG+ if (handle->flags & UV_HANDLE_UDP_RECVMMSG) {+ uv_once(&once, uv__udp_mmsg_init);+ return uv__recvmmsg_avail;+ }+#endif+ return 0;+}+++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 UV_EBUSY;++ if (uv__fd_exists(handle->loop, sock))+ return UV_EEXIST;++ err = uv__nonblock(sock, 1);+ if (err)+ return err;++ err = uv__set_reuse(sock);+ if (err)+ return err;++ handle->io_watcher.fd = sock;+ if (uv__udp_is_connected(handle))+ handle->flags |= UV_HANDLE_UDP_CONNECTED;++ 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 UV_EINVAL;+ }+}+++int uv_udp_set_source_membership(uv_udp_t* handle,+ const char* multicast_addr,+ const char* interface_addr,+ const char* source_addr,+ uv_membership membership) {+#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__ANDROID__)+ int err;+ union uv__sockaddr mcast_addr;+ union uv__sockaddr src_addr;++ err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.in);+ if (err) {+ err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.in6);+ if (err)+ return err;+ err = uv_ip6_addr(source_addr, 0, &src_addr.in6);+ if (err)+ return err;+ return uv__udp_set_source_membership6(handle,+ &mcast_addr.in6,+ interface_addr,+ &src_addr.in6,+ membership);+ }++ err = uv_ip4_addr(source_addr, 0, &src_addr.in);+ if (err)+ return err;+ return uv__udp_set_source_membership4(handle,+ &mcast_addr.in,+ interface_addr,+ &src_addr.in,+ membership);+#else+ return UV_ENOSYS;+#endif+}+++static int uv__setsockopt(uv_udp_t* handle,+ int option4,+ int option6,+ const void* val,+ socklen_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 UV__ERR(errno);++ return 0;+}++static int uv__setsockopt_maybe_char(uv_udp_t* handle,+ int option4,+ int option6,+ int val) {+#if defined(__sun) || defined(_AIX) || defined(__MVS__)+ char arg = val;+#elif defined(__OpenBSD__)+ unsigned char arg = val;+#else+ int arg = val;+#endif++ if (val < 0 || val > 255)+ return UV_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 UV__ERR(errno);+ }++ return 0;+}+++int uv_udp_set_ttl(uv_udp_t* handle, int ttl) {+ if (ttl < 1 || ttl > 255)+ return UV_EINVAL;++#if defined(__MVS__)+ if (!(handle->flags & UV_HANDLE_IPV6))+ return UV_ENOTSUP; /* zOS does not support setting ttl for IPv4 */+#endif++/*+ * 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__) || \+ defined(__MVS__)++ return uv__setsockopt(handle,+ IP_TTL,+ IPV6_UNICAST_HOPS,+ &ttl,+ sizeof(ttl));++#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||+ defined(__MVS__)) */++ return uv__setsockopt_maybe_char(handle,+ IP_TTL,+ IPV6_UNICAST_HOPS,+ ttl);++#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||+ defined(__MVS__) */+}+++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) || defined(__OpenBSD__) || \+ defined(__MVS__)+ if (handle->flags & UV_HANDLE_IPV6)+ return uv__setsockopt(handle,+ IP_MULTICAST_TTL,+ IPV6_MULTICAST_HOPS,+ &ttl,+ sizeof(ttl));+#endif /* defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \+ defined(__MVS__) */++ 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) || defined(__OpenBSD__) || \+ defined(__MVS__)+ if (handle->flags & UV_HANDLE_IPV6)+ return uv__setsockopt(handle,+ IP_MULTICAST_LOOP,+ IPV6_MULTICAST_LOOP,+ &on,+ sizeof(on));+#endif /* defined(__sun) || defined(_AIX) ||defined(__OpenBSD__) ||+ defined(__MVS__) */++ 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 UV_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 UV__ERR(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 UV__ERR(errno);+ }+ } else {+ assert(0 && "unexpected address family");+ abort();+ }++ return 0;+}++int uv_udp_getpeername(const uv_udp_t* handle,+ struct sockaddr* name,+ int* namelen) {++ return uv__getsockpeername((const uv_handle_t*) handle,+ getpeername,+ name,+ namelen);+}++int uv_udp_getsockname(const uv_udp_t* handle,+ struct sockaddr* name,+ int* namelen) {++ return uv__getsockpeername((const uv_handle_t*) handle,+ getsockname,+ name,+ namelen);+}+++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 UV_EINVAL;++ if (uv__io_active(&handle->io_watcher, POLLIN))+ return UV_EALREADY; /* FIXME(bnoordhuis) Should be UV_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, POLLIN);+ uv__handle_start(handle);++ return 0;+}+++int uv__udp_recv_stop(uv_udp_t* handle) {+ uv__io_stop(handle->loop, &handle->io_watcher, POLLIN);++ if (!uv__io_active(&handle->io_watcher, POLLOUT))+ uv__handle_stop(handle);++ handle->alloc_cb = NULL;+ handle->recv_cb = NULL;++ return 0;+}
− third_party/libuv/src/win/snprintf.c
@@ -1,42 +0,0 @@-/* 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 <stdio.h>-#include <stdarg.h>--/* 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