diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/posix/posix.nim | 7 | ||||
-rw-r--r-- | lib/posix/posix_linux_amd64_consts.nim | 3 | ||||
-rw-r--r-- | lib/posix/posix_macos_amd64.nim | 3 | ||||
-rw-r--r-- | lib/posix/posix_openbsd_amd64.nim | 2 | ||||
-rw-r--r-- | lib/posix/posix_other.nim | 3 | ||||
-rw-r--r-- | lib/posix/posix_other_consts.nim | 2 | ||||
-rw-r--r-- | lib/posix/posix_utils.nim | 6 | ||||
-rw-r--r-- | lib/pure/ioselects/ioselectors_epoll.nim | 10 | ||||
-rw-r--r-- | lib/pure/ioselects/ioselectors_kqueue.nim | 8 | ||||
-rw-r--r-- | lib/pure/memfiles.nim | 2 | ||||
-rw-r--r-- | lib/pure/nativesockets.nim | 76 | ||||
-rw-r--r-- | lib/pure/net.nim | 37 | ||||
-rw-r--r-- | lib/system/io.nim | 90 | ||||
-rw-r--r-- | lib/windows/winlean.nim | 5 |
14 files changed, 210 insertions, 44 deletions
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index d1f2faca5..3e423c64b 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -896,6 +896,10 @@ proc `==`*(x, y: SocketHandle): bool {.borrow.} proc accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen): SocketHandle {. importc, header: "<sys/socket.h>", sideEffect.} +when defined(linux) or defined(bsd): + proc accept4*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen, + flags: cint): SocketHandle {.importc, header: "<sys/socket.h>".} + proc bindSocket*(a1: SocketHandle, a2: ptr SockAddr, a3: SockLen): cint {. importc: "bind", header: "<sys/socket.h>".} ## is Posix's ``bind``, because ``bind`` is a reserved word @@ -1036,6 +1040,9 @@ proc mkstemp*(tmpl: cstring): cint {.importc, header: "<stdlib.h>", sideEffect.} proc mkdtemp*(tmpl: cstring): pointer {.importc, header: "<stdlib.h>", sideEffect.} +when defined(linux) or defined(bsd): + proc mkostemp*(tmpl: cstring, oflags: cint): cint {.importc, header: "<stdlib.h>", sideEffect.} + proc utimes*(path: cstring, times: ptr array[2, Timeval]): int {. importc: "utimes", header: "<sys/time.h>", sideEffect.} ## Sets file access and modification times. diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim index 7352e8e35..84296eb9b 100644 --- a/lib/posix/posix_linux_amd64_consts.nim +++ b/lib/posix/posix_linux_amd64_consts.nim @@ -100,6 +100,7 @@ const EXDEV* = cint(18) # <fcntl.h> const F_DUPFD* = cint(0) +const F_DUPFD_CLOEXEC* = cint(1030) const F_GETFD* = cint(1) const F_SETFD* = cint(2) const F_GETFL* = cint(3) @@ -126,6 +127,7 @@ const O_ACCMODE* = cint(3) const O_RDONLY* = cint(0) const O_RDWR* = cint(2) const O_WRONLY* = cint(1) +const O_CLOEXEC* = cint(524288) const POSIX_FADV_NORMAL* = cint(0) const POSIX_FADV_SEQUENTIAL* = cint(2) const POSIX_FADV_RANDOM* = cint(1) @@ -469,6 +471,7 @@ const SOCK_DGRAM* = cint(2) const SOCK_RAW* = cint(3) const SOCK_SEQPACKET* = cint(5) const SOCK_STREAM* = cint(1) +const SOCK_CLOEXEC* = cint(524288) const SOL_SOCKET* = cint(1) const SOMAXCONN* = cint(128) const SO_REUSEPORT* = cint(15) diff --git a/lib/posix/posix_macos_amd64.nim b/lib/posix/posix_macos_amd64.nim index 536d51be5..304993dcb 100644 --- a/lib/posix/posix_macos_amd64.nim +++ b/lib/posix/posix_macos_amd64.nim @@ -557,6 +557,9 @@ when defined(linux) or defined(nimdoc): else: var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint +when defined(linux) or defined(bsd): + var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint + when defined(macosx): # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect # Instead we should use SO_NOSIGPIPE in setsockopt diff --git a/lib/posix/posix_openbsd_amd64.nim b/lib/posix/posix_openbsd_amd64.nim index 408e42255..1ff636517 100644 --- a/lib/posix/posix_openbsd_amd64.nim +++ b/lib/posix/posix_openbsd_amd64.nim @@ -532,6 +532,8 @@ when defined(nimdoc): else: var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint +var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint + var MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint when hasSpawnH: diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 8213f796c..204cc3d9a 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -562,6 +562,9 @@ when defined(linux) or defined(nimdoc): else: var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint +when defined(linux) or defined(bsd): + var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint + when defined(macosx): # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect # Instead we should use SO_NOSIGPIPE in setsockopt diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index 9fcbe425d..f43407b40 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -99,6 +99,7 @@ var EXDEV* {.importc: "EXDEV", header: "<errno.h>".}: cint # <fcntl.h> var F_DUPFD* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint +var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint var F_GETFD* {.importc: "F_GETFD", header: "<fcntl.h>".}: cint var F_SETFD* {.importc: "F_SETFD", header: "<fcntl.h>".}: cint var F_GETFL* {.importc: "F_GETFL", header: "<fcntl.h>".}: cint @@ -125,6 +126,7 @@ var O_ACCMODE* {.importc: "O_ACCMODE", header: "<fcntl.h>".}: cint var O_RDONLY* {.importc: "O_RDONLY", header: "<fcntl.h>".}: cint var O_RDWR* {.importc: "O_RDWR", header: "<fcntl.h>".}: cint var O_WRONLY* {.importc: "O_WRONLY", header: "<fcntl.h>".}: cint +var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "<fcntl.h>".}: cint var POSIX_FADV_NORMAL* {.importc: "POSIX_FADV_NORMAL", header: "<fcntl.h>".}: cint var POSIX_FADV_SEQUENTIAL* {.importc: "POSIX_FADV_SEQUENTIAL", header: "<fcntl.h>".}: cint var POSIX_FADV_RANDOM* {.importc: "POSIX_FADV_RANDOM", header: "<fcntl.h>".}: cint diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index 5dbb163ca..2d0288187 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -83,7 +83,11 @@ proc mkstemp*(prefix: string): (string, File) = ## The file is created with perms 0600. ## Returns the filename and a file opened in r/w mode. var tmpl = cstring(prefix & "XXXXXX") - let fd = mkstemp(tmpl) + let fd = + when declared(mkostemp): + mkostemp(tmpl, O_CLOEXEC) + else: + mkstemp(tmpl) var f: File if open(f, fd, fmReadWrite): return ($tmpl, f) diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index bf13cc83e..83f6001ea 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -81,7 +81,7 @@ proc newSelector*[T](): Selector[T] = # Start with a reasonable size, checkFd() will grow this on demand const numFD = 1024 - var epollFD = epoll_create(MAX_EPOLL_EVENTS) + var epollFD = epoll_create1(O_CLOEXEC) if epollFD < 0: raiseOSError(osLastError()) @@ -110,7 +110,7 @@ proc close*[T](s: Selector[T]) = raiseIOSelectorsError(osLastError()) proc newSelectEvent*(): SelectEvent = - let fdci = eventfd(0, 0) + let fdci = eventfd(0, O_CLOEXEC) if fdci == -1: raiseIOSelectorsError(osLastError()) setNonBlocking(fdci) @@ -269,7 +269,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool, var newTs: Itimerspec oldTs: Itimerspec - let fdi = timerfd_create(CLOCK_MONOTONIC, 0).int + let fdi = timerfd_create(CLOCK_MONOTONIC, O_CLOEXEC).int if fdi == -1: raiseIOSelectorsError(osLastError()) setNonBlocking(fdi.cint) @@ -314,7 +314,7 @@ when not defined(android): discard sigaddset(nmask, cint(signal)) blockSignals(nmask, omask) - let fdi = signalfd(-1, nmask, 0).int + let fdi = signalfd(-1, nmask, O_CLOEXEC).int if fdi == -1: raiseIOSelectorsError(osLastError()) setNonBlocking(fdi.cint) @@ -341,7 +341,7 @@ when not defined(android): discard sigaddset(nmask, posix.SIGCHLD) blockSignals(nmask, omask) - let fdi = signalfd(-1, nmask, 0).int + let fdi = signalfd(-1, nmask, O_CLOEXEC).int if fdi == -1: raiseIOSelectorsError(osLastError()) setNonBlocking(fdi.cint) diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index 83e15d479..7635a04d5 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -9,7 +9,7 @@ # This module implements BSD kqueue(). -import posix, times, kqueue +import posix, times, kqueue, nativesockets const # Maximum number of events that can be returned. @@ -76,7 +76,7 @@ type proc getUnique[T](s: Selector[T]): int {.inline.} = # we create duplicated handles to get unique indexes for our `fds` array. - result = posix.fcntl(s.sock, F_DUPFD, s.sock) + result = posix.fcntl(s.sock, F_DUPFD_CLOEXEC, s.sock) if result == -1: raiseIOSelectorsError(osLastError()) @@ -96,8 +96,8 @@ proc newSelector*[T](): owned(Selector[T]) = # we allocating empty socket to duplicate it handle in future, to get unique # indexes for `fds` array. This is needed to properly identify # {Event.Timer, Event.Signal, Event.Process} events. - let usock = posix.socket(posix.AF_INET, posix.SOCK_STREAM, - posix.IPPROTO_TCP).cint + let usock = createNativeSocket(posix.AF_INET, posix.SOCK_STREAM, + posix.IPPROTO_TCP).cint if usock == -1: let err = osLastError() discard posix.close(kqFD) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 152b03ec6..dab5483e4 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -228,7 +228,7 @@ proc open*(filename: string, mode: FileMode = fmRead, if result.handle != -1: discard close(result.handle) raiseOSError(errCode) - var flags = if readonly: O_RDONLY else: O_RDWR + var flags = (if readonly: O_RDONLY else: O_RDWR) or O_CLOEXEC if newFileSize != -1: flags = flags or O_CREAT or O_TRUNC diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index d9256a921..8d0d6426d 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -185,19 +185,53 @@ proc toSockType*(protocol: Protocol): SockType = of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6: SOCK_RAW -proc createNativeSocket*(domain: Domain = AF_INET, - sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): SocketHandle = - ## Creates a new socket; returns `osInvalidSocket` if an error occurs. - socket(toInt(domain), toInt(sockType), toInt(protocol)) +proc close*(socket: SocketHandle) = + ## closes a socket. + when useWinVersion: + discard winlean.closesocket(socket) + else: + discard posix.close(socket) + # TODO: These values should not be discarded. An OSError should be raised. + # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times -proc createNativeSocket*(domain: cint, sockType: cint, - protocol: cint): SocketHandle = +when declared(setInheritable) or defined(nimdoc): + proc setInheritable*(s: SocketHandle, inheritable: bool): bool {.inline.} = + ## Set whether a socket is inheritable by child processes. Returns `true` + ## on success. + ## + ## This function is not implemented on all platform, test for availability + ## with `declared() <system.html#declared,untyped>`. + setInheritable(FileHandle s, inheritable) + +proc createNativeSocket*(domain: cint, sockType: cint, protocol: cint, + inheritable: bool = defined(nimInheritHandles)): SocketHandle = ## Creates a new socket; returns `osInvalidSocket` if an error occurs. ## + ## `inheritable` decides if the resulting SocketHandle can be inherited + ## by child processes. + ## ## Use this overload if one of the enums specified above does ## not contain what you need. - socket(domain, sockType, protocol) + let sockType = + when (defined(linux) or defined(bsd)) and not defined(nimdoc): + if inheritable: sockType and not SOCK_CLOEXEC else: sockType or SOCK_CLOEXEC + else: + sockType + result = socket(domain, sockType, protocol) + when declared(setInheritable) and not (defined(linux) or defined(bsd)): + if not setInheritable(result, inheritable): + close result + return osInvalidSocket + +proc createNativeSocket*(domain: Domain = AF_INET, + sockType: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP, + inheritable: bool = defined(nimInheritHandles)): SocketHandle = + ## Creates a new socket; returns `osInvalidSocket` if an error occurs. + ## + ## `inheritable` decides if the resulting SocketHandle can be inherited + ## by child processes. + createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol)) proc newNativeSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, @@ -215,15 +249,6 @@ proc newNativeSocket*(domain: cint, sockType: cint, ## not contain what you need. createNativeSocket(domain, sockType, protocol) -proc close*(socket: SocketHandle) = - ## closes a socket. - when useWinVersion: - discard winlean.closesocket(socket) - else: - discard posix.close(socket) - # TODO: These values should not be discarded. An OSError should be raised. - # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times - proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint = result = bindSocket(socket, name, namelen) @@ -652,14 +677,25 @@ proc selectWrite*(writefds: var seq[SocketHandle], pruneSocketSet(writefds, (wr)) -proc accept*(fd: SocketHandle): (SocketHandle, string) = +proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) = ## Accepts a new client connection. ## + ## `inheritable` decides if the resulting SocketHandle can be inherited by + ## child processes. + ## ## Returns (osInvalidSocket, "") if an error occurred. var sockAddress: Sockaddr_in var addrLen = sizeof(sockAddress).SockLen - var sock = accept(fd, cast[ptr SockAddr](addr(sockAddress)), - addr(addrLen)) + var sock = + when (defined(linux) or defined(bsd)) and not defined(nimdoc): + accept4(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen), + if inheritable: 0 else: SOCK_CLOEXEC) + else: + accept(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) + when declared(setInheritable) and not (defined(linux) or defined(bsd)): + if not setInheritable(sock, inheritable): + close sock + sock = osInvalidSocket if sock == osInvalidSocket: return (osInvalidSocket, "") else: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 593e5c76e..30355226c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -212,22 +212,32 @@ proc newSocket*(fd: SocketHandle, domain: Domain = AF_INET, when defined(macosx) and not defined(nimdoc): setSockOptInt(fd, SOL_SOCKET, SO_NOSIGPIPE, 1) -proc newSocket*(domain, sockType, protocol: cint, buffered = true): owned(Socket) = +proc newSocket*(domain, sockType, protocol: cint, buffered = true, + inheritable = defined(nimInheritHandles)): owned(Socket) = ## Creates a new socket. ## + ## The SocketHandle associated with the resulting Socket will not be + ## inheritable by child processes by default. This can be changed via + ## the `inheritable` parameter. + ## ## If an error occurs OSError will be raised. - let fd = createNativeSocket(domain, sockType, protocol) + let fd = createNativeSocket(domain, sockType, protocol, inheritable) if fd == osInvalidSocket: raiseOSError(osLastError()) result = newSocket(fd, domain.Domain, sockType.SockType, protocol.Protocol, buffered) proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP, buffered = true): owned(Socket) = + protocol: Protocol = IPPROTO_TCP, buffered = true, + inheritable = defined(nimInheritHandles)): owned(Socket) = ## Creates a new socket. ## + ## The SocketHandle associated with the resulting Socket will not be + ## inheritable by child processes by default. This can be changed via + ## the `inheritable` parameter. + ## ## If an error occurs OSError will be raised. - let fd = createNativeSocket(domain, sockType, protocol) + let fd = createNativeSocket(domain, sockType, protocol, inheritable) if fd == osInvalidSocket: raiseOSError(osLastError()) result = newSocket(fd, domain, sockType, protocol, buffered) @@ -872,7 +882,8 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. freeaddrinfo(aiList) proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, - flags = {SocketFlag.SafeDisconn}) {. + flags = {SocketFlag.SafeDisconn}, + inheritable = defined(nimInheritHandles)) {. tags: [ReadIOEffect], gcsafe, locks: 0.} = ## Blocks until a connection is being made from a client. When a connection ## is made sets ``client`` to the client socket and ``address`` to the address @@ -882,19 +893,23 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, ## The resulting client will inherit any properties of the server socket. For ## example: whether the socket is buffered or not. ## + ## The SocketHandle associated with the resulting client will not be + ## inheritable by child processes by default. This can be changed via + ## the `inheritable` parameter. + ## ## The ``accept`` call may result in an error if the connecting socket ## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` ## flag is specified then this error will not be raised and instead ## accept will be called again. if client.isNil: new(client) - let ret = accept(server.fd) + let ret = accept(server.fd, inheritable) let sock = ret[0] if sock == osInvalidSocket: let err = osLastError() if flags.isDisconnectionError(err): - acceptAddr(server, client, address, flags) + acceptAddr(server, client, address, flags, inheritable) raiseOSError(err) else: address = ret[1] @@ -964,10 +979,16 @@ when false: #defineSsl: doHandshake() proc accept*(server: Socket, client: var owned(Socket), - flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} = + flags = {SocketFlag.SafeDisconn}, + inheritable = defined(nimInheritHandles)) + {.tags: [ReadIOEffect].} = ## Equivalent to ``acceptAddr`` but doesn't return the address, only the ## socket. ## + ## The SocketHandle associated with the resulting client will not be + ## inheritable by child processes by default. This can be changed via + ## the `inheritable` parameter. + ## ## The ``accept`` call may result in an error if the connecting socket ## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` ## flag is specified then this error will not be raised and instead diff --git a/lib/system/io.nim b/lib/system/io.nim index 9a4502cba..f8358962f 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -259,6 +259,31 @@ else: IOFBF {.importc: "_IOFBF", nodecl.}: cint IONBF {.importc: "_IONBF", nodecl.}: cint +const SupportIoctlInheritCtl = (defined(linux) or defined(bsd)) and + not defined(nimscript) +when SupportIoctlInheritCtl: + var + FIOCLEX {.importc, header: "<sys/ioctl.h>".}: cint + FIONCLEX {.importc, header: "<sys/ioctl.h>".}: cint + + proc c_ioctl(fd: cint, request: cint): cint {. + importc: "ioctl", header: "<sys/ioctl.h>", varargs.} +elif defined(posix) and not defined(nimscript): + var + F_GETFD {.importc, header: "<fcntl.h>".}: cint + F_SETFD {.importc, header: "<fcntl.h>".}: cint + FD_CLOEXEC {.importc, header: "<fcntl.h>".}: cint + + proc c_fcntl(fd: cint, cmd: cint): cint {. + importc: "fcntl", header: "<fcntl.h>", varargs.} +elif defined(windows): + const HANDLE_FLAG_INHERIT = culong 0x1 + proc getOsfhandle(fd: cint): FileHandle {. + importc: "_get_osfhandle", header: "<io.h>".} + + proc setHandleInformation(handle: FileHandle, mask, flags: culong): cint {. + importc: "SetHandleInformation", header: "<handleapi.h>".} + const BufSize = 4000 @@ -292,12 +317,29 @@ proc getOsFileHandle*(f: File): FileHandle = ## returns the OS file handle of the file ``f``. This is only useful for ## platform specific programming. when defined(windows): - proc getOsfhandle(fd: cint): FileHandle {. - importc: "_get_osfhandle", header: "<io.h>".} result = getOsfhandle(getFileHandle(f)) else: result = c_fileno(f) +when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(windows): + proc setInheritable*(f: FileHandle, inheritable: bool): bool = + ## control whether a file handle can be inherited by child processes. Returns + ## ``true`` on success. This requires the OS file handle, which can be + ## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_. + ## + ## This procedure is not guaranteed to be available for all platforms. Test for + ## availability with `declared() <system.html#declared,untyped>`. + when SupportIoctlInheritCtl: + result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1 + elif defined(posix): + var flags = c_fcntl(f, F_GETFD) + if flags == -1: + return false + flags = if inheritable: flags and not FD_CLOEXEC else: flags or FD_CLOEXEC + result = c_fcntl(f, F_SETFD, flags) != -1 + else: + result = setHandleInformation(f, HANDLE_FLAG_INHERIT, culong inheritable) != 0 + proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], benign.} = ## reads a line of text from the file `f` into `line`. May throw an IO @@ -501,7 +543,21 @@ else: importc: "freopen", nodecl.} const - FormatOpen: array[FileMode, string] = ["rb", "wb", "w+b", "r+b", "ab"] + NoInheritFlag = + # Platform specific flag for creating a File without inheritance. + when not defined(nimInheritHandles): + when defined(windows): + "N" + elif defined(linux) or defined(bsd): + "e" + else: + "" + else: + "" + FormatOpen: array[FileMode, string] = [ + "rb" & NoInheritFlag, "wb" & NoInheritFlag, "w+b" & NoInheritFlag, + "r+b" & NoInheritFlag, "ab" & NoInheritFlag + ] #"rt", "wt", "w+t", "r+t", "at" # we always use binary here as for Nim the OS line ending # should not be translated. @@ -544,17 +600,25 @@ proc open*(f: var File, filename: string, ## ## Default mode is readonly. Returns true iff the file could be opened. ## This throws no exception if the file could not be opened. + ## + ## The file handle associated with the resulting ``File`` is not inheritable. var p = fopen(filename, FormatOpen[mode]) if p != nil: + var f2 = cast[File](p) when defined(posix) and not defined(nimscript): # How `fopen` handles opening a directory is not specified in ISO C and # POSIX. We do not want to handle directories as regular files that can # be opened. - var f2 = cast[File](p) var res: Stat if c_fstat(getFileHandle(f2), res) >= 0'i32 and modeIsDir(res.st_mode): close(f2) return false + when not defined(nimInheritHandles) and declared(setInheritable) and + NoInheritFlag.len == 0: + if not setInheritable(getOsFileHandle(f2), false): + close(f2) + return false + result = true f = cast[File](p) if bufSize > 0 and bufSize <= high(cint).int: @@ -569,13 +633,27 @@ proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. ## file variables. ## ## Default mode is readonly. Returns true iff the file could be reopened. - result = freopen(filename, FormatOpen[mode], f) != nil + ## + ## The file handle associated with `f` won't be inheritable. + if freopen(filename, FormatOpen[mode], f) != nil: + when not defined(nimInheritHandles) and declared(setInheritable) and + NoInheritFlag.len == 0: + if not setInheritable(getOsFileHandle(f), false): + close(f) + return false + result = true proc open*(f: var File, filehandle: FileHandle, mode: FileMode = fmRead): bool {.tags: [], raises: [], benign.} = ## Creates a ``File`` from a `filehandle` with given `mode`. ## ## Default mode is readonly. Returns true iff the file could be opened. + ## + ## The passed file handle will no longer be inheritable. + when not defined(nimInheritHandles) and declared(setInheritable): + let oshandle = when defined(windows): getOsfhandle(filehandle) else: filehandle + if not setInheritable(oshandle, false): + return false f = c_fdopen(filehandle, FormatOpen[mode]) result = f != nil @@ -585,6 +663,8 @@ proc open*(filename: string, ## ## Default mode is readonly. Raises an ``IOError`` if the file ## could not be opened. + ## + ## The file handle associated with the resulting ``File`` is not inheritable. if not open(result, filename, mode, bufSize): sysFatal(IOError, "cannot open: " & filename) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index ee8c2343a..f749c508d 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -126,6 +126,8 @@ const CREATE_NO_WINDOW* = 0x08000000'i32 + HANDLE_FLAG_INHERIT* = 0x00000001'i32 + proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {. stdcall, dynlib: "kernel32", importc: "GetVersionExW", sideEffect.} proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {. @@ -708,6 +710,9 @@ proc duplicateHandle*(hSourceProcessHandle: Handle, hSourceHandle: Handle, dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "DuplicateHandle".} +proc getHandleInformation*(hObject: Handle, lpdwFlags: ptr DWORD): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "GetHandleInformation".} + proc setHandleInformation*(hObject: Handle, dwMask: DWORD, dwFlags: DWORD): WINBOOL {.stdcall, dynlib: "kernel32", importc: "SetHandleInformation".} |