Example #1
0
 // Intended usage is e.g. Interop.Error.EFAIL.Info() for brevity
 // vs. new Interop.ErrorInfo(Interop.Error.EFAIL) for synthesizing
 // errors. Errors originated from the system should be obtained
 // via GetLastErrorInfo(), not GetLastError().Info() as that will
 // convert twice, which is not only inefficient but also lossy if
 // we ever encounter a raw errno that no equivalent in the Error
 // enum.
 public static Interop.ErrorInfo Info(this Interop.Error error)
 {
     return(new Interop.ErrorInfo(error));
 }
 internal static bool TryGetNativeErrorForSocketError(SocketError error, out Interop.Error errno)
 {
     return(s_socketErrorToNativeError.TryGetValue(error, out errno));
 }
        private void CheckForExit(bool blockingAllowed = false)
        {
            Debug.Assert(Monitor.IsEntered(_gate));
            Debug.Assert(!blockingAllowed); // see "PERF NOTE" comment in WaitForExit

            // Try to get the state of the (child) process
            int status;
            int waitResult = Interop.Sys.WaitPid(_processId, out status,
                                                 blockingAllowed ? Interop.Sys.WaitPidOptions.None : Interop.Sys.WaitPidOptions.WNOHANG);

            if (waitResult == _processId)
            {
                // Process has exited
                if (Interop.Sys.WIfExited(status))
                {
                    _exitCode = Interop.Sys.WExitStatus(status);
                }
                else if (Interop.Sys.WIfSignaled(status))
                {
                    const int ExitCodeSignalOffset = 128;
                    _exitCode = ExitCodeSignalOffset + Interop.Sys.WTermSig(status);
                }
                SetExited();
                return;
            }
            else if (waitResult == 0)
            {
                // Process is still running
                return;
            }
            else if (waitResult == -1)
            {
                // Something went wrong, e.g. it's not a child process,
                // or waitpid was already called for this child, or
                // that the call was interrupted by a signal.
                Interop.Error errno = Interop.Sys.GetLastError();
                if (errno == Interop.Error.ECHILD)
                {
                    // waitpid was used with a non-child process.  We won't be
                    // able to get an exit code, but we'll at least be able
                    // to determine if the process is still running (assuming
                    // there's not a race on its id).
                    int killResult = Interop.Sys.Kill(_processId, Interop.Sys.Signals.None); // None means don't send a signal
                    if (killResult == 0)
                    {
                        // Process is still running.  This could also be a defunct process that has completed
                        // its work but still has an entry in the processes table due to its parent not yet
                        // having waited on it to clean it up.
                        return;
                    }
                    else // error from kill
                    {
                        errno = Interop.Sys.GetLastError();
                        if (errno == Interop.Error.ESRCH)
                        {
                            // Couldn't find the process; assume it's exited
                            SetExited();
                            return;
                        }
                        else if (errno == Interop.Error.EPERM)
                        {
                            // Don't have permissions to the process; assume it's alive
                            return;
                        }
                        else
                        {
                            Debug.Fail("Unexpected errno value from kill");
                        }
                    }
                }
                else
                {
                    Debug.Fail("Unexpected errno value from waitpid");
                }
            }
            else
            {
                Debug.Fail("Unexpected process ID from waitpid.");
            }

            SetExited();
        }
Example #4
0
 public bool TryRegister(SafeCloseSocket socket, out Interop.Error error)
 {
     Debug.Assert(WasAllocated, "Expected WasAllocated to be true");
     return(_engine.TryRegister(socket, _handle, out error));
 }
        public bool TryRegister(int fileDescriptor, Interop.Sys.SocketEvents current, Interop.Sys.SocketEvents events, GCHandle handle, out Interop.Error error)
        {
            if (current == events)
            {
                error = Interop.Error.SUCCESS;
                return(true);
            }

            error = Interop.Sys.TryChangeSocketEventRegistration(_port, fileDescriptor, current, events, (IntPtr)handle);
            return(error == Interop.Error.SUCCESS);
        }
        private unsafe SocketError DoCloseHandle(bool abortive)
        {
            Interop.Error errorCode = Interop.Error.SUCCESS;

            if (!IsSocket)
            {
                return(SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle)));
            }

            // If abortive is not set, we're not running on the finalizer thread, so it's safe to block here.
            // We can honor the linger options set on the socket.  It also means closesocket() might return
            // EWOULDBLOCK, in which case we need to do some recovery.
            if (!abortive)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(this, $"handle:{handle} Following 'non-abortive' branch.");
                }

                // Close, and if its errno is other than EWOULDBLOCK, there's nothing more to do - we either succeeded or failed.
                errorCode = CloseHandle(handle);
                if (errorCode != Interop.Error.EWOULDBLOCK)
                {
                    return(SocketPal.GetSocketErrorForErrorCode(errorCode));
                }

                // The socket must be non-blocking with a linger timeout set.
                // We have to set the socket to blocking.
                if (Interop.Sys.Fcntl.DangerousSetIsNonBlocking(handle, 0) == 0)
                {
                    // The socket successfully made blocking; retry the close().
                    return(SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle)));
                }

                // The socket could not be made blocking; fall through to the regular abortive close.
            }

            // By default or if the non-abortive path failed, set linger timeout to zero to get an abortive close (RST).
            var linger = new Interop.Sys.LingerOption
            {
                OnOff   = 1,
                Seconds = 0
            };

            errorCode = Interop.Sys.SetLingerOption(handle, &linger);
#if DEBUG
            _closeSocketLinger = SocketPal.GetSocketErrorForErrorCode(errorCode);
#endif
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this, $"handle:{handle}, setsockopt():{errorCode}");
            }

            switch (errorCode)
            {
            case Interop.Error.SUCCESS:
            case Interop.Error.EINVAL:
            case Interop.Error.ENOPROTOOPT:
            case Interop.Error.ENOTSOCK:
                errorCode = CloseHandle(handle);
                break;

                // For other errors, it's too dangerous to try closesocket() - it might block!
            }

            return(SocketPal.GetSocketErrorForErrorCode(errorCode));
        }
Example #7
0
        private unsafe void SetAccessOrWriteTimeCore(SafeFileHandle?handle, string?path, DateTimeOffset time, bool isAccessTime, bool checkCreationTime, bool asDirectory)
        {
            // This api is used to set creation time on non OSX platforms, and as a fallback for OSX platforms.
            // The reason why we use it to set 'creation time' is the below comment:
            // Unix provides APIs to update the last access time (atime) and last modification time (mtime).
            // There is no API to update the CreationTime.
            // Some platforms (e.g. Linux) don't store a creation time. On those platforms, the creation time
            // is synthesized as the oldest of last status change time (ctime) and last modification time (mtime).
            // We update the LastWriteTime (mtime).
            // This triggers a metadata change for FileSystemWatcher NotifyFilters.CreationTime.
            // Updating the mtime, causes the ctime to be set to 'now'. So, on platforms that don't store a
            // CreationTime, GetCreationTime will return the value that was previously set (when that value
            // wasn't in the future).

            // force a refresh so that we have an up-to-date times for values not being overwritten
            InvalidateCaches();
            EnsureCachesInitialized(handle, path);

            if (!EntryExists)
            {
                FileSystemInfo.ThrowNotFound(path);
            }

            // we use utimes()/utimensat() to set the accessTime and writeTime
            Interop.Sys.TimeSpec *buf = stackalloc Interop.Sys.TimeSpec[2];

            long seconds     = time.ToUnixTimeSeconds();
            long nanoseconds = UnixTimeSecondsToNanoseconds(time, seconds);

#if TARGET_BROWSER
            buf[0].TvSec  = seconds;
            buf[0].TvNsec = nanoseconds;
            buf[1].TvSec  = seconds;
            buf[1].TvNsec = nanoseconds;
#else
            if (isAccessTime)
            {
                buf[0].TvSec  = seconds;
                buf[0].TvNsec = nanoseconds;
                buf[1].TvSec  = _fileCache.MTime;
                buf[1].TvNsec = _fileCache.MTimeNsec;
            }
            else
            {
                buf[0].TvSec  = _fileCache.ATime;
                buf[0].TvNsec = _fileCache.ATimeNsec;
                buf[1].TvSec  = seconds;
                buf[1].TvNsec = nanoseconds;
            }
#endif
            int rv = handle is not null
                ? Interop.Sys.FUTimens(handle, buf)
                : Interop.Sys.UTimensat(path !, buf);

            Interop.CheckIo(rv, path, asDirectory);

            // On OSX-like platforms, when the modification time is less than the creation time (including
            // when the modification time is already less than but access time is being set), the creation
            // time is set to the modification time due to the api we're currently using; this is not
            // desirable behaviour since it is inconsistent with windows behaviour and is not logical to
            // the programmer (ie. we'd have to document it), so these api calls revert the creation time
            // when it shouldn't be set (since we're setting modification time and access time here).
            // checkCreationTime is only true on OSX-like platforms.
            // allowFallbackToLastWriteTime is ignored on non OSX-like platforms.
            bool updateCreationTime = checkCreationTime && (_fileCache.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0 &&
                                      (buf[1].TvSec < _fileCache.BirthTime || (buf[1].TvSec == _fileCache.BirthTime && buf[1].TvNsec < _fileCache.BirthTimeNsec));

            InvalidateCaches();

            if (updateCreationTime)
            {
                Interop.Error error = SetCreationTimeCore(handle, path, _fileCache.BirthTime, _fileCache.BirthTimeNsec);
                if (error != Interop.Error.SUCCESS && error != Interop.Error.ENOTSUP)
                {
                    Interop.CheckIo(error, path, asDirectory);
                }
            }
        }
Example #8
0
        private bool TryRegister(SafeCloseSocket socket, Interop.Sys.SocketEvents current, Interop.Sys.SocketEvents events, IntPtr handle, out Interop.Error error)
        {
            if (current == events)
            {
                error = Interop.Error.SUCCESS;
                return(true);
            }

            error = Interop.Sys.TryChangeSocketEventRegistration(_port, socket, current, events, handle);
            return(error == Interop.Error.SUCCESS);
        }
        public bool TryRegister(SafeCloseSocket socket, Interop.Sys.SocketEvents current, Interop.Sys.SocketEvents events, GCHandle handle, out Interop.Error error)
        {
            if (current == events)
            {
                error = Interop.Error.SUCCESS;
                return(true);
            }

            //
            // @TODO: work out a better way to handle this.  For now, just do the "dangerous" thing; this is called
            // from SafeCloseSocket.ReleaseHandle, so it can't access the file descriptor in the normal way.
            //
            int fd = (int)socket.DangerousGetHandle();

            error = Interop.Sys.DangerousTryChangeSocketEventRegistration(_port, fd, current, events, (IntPtr)handle);
            return(error == Interop.Error.SUCCESS);
        }
        public unsafe bool TryRegister(int fileDescriptor, SocketAsyncEvents current, SocketAsyncEvents events, GCHandle handle, out Interop.Error error)
        {
            Debug.Assert(current != events);

            int op = Interop.libc.EPOLL_CTL_MOD;

            if (current == SocketAsyncEvents.None)
            {
                // This context was not listening for events, add it
                op = Interop.libc.EPOLL_CTL_ADD;
            }
            else if (events == SocketAsyncEvents.None)
            {
                // This context will no longer be listening for events, remove it
                op = Interop.libc.EPOLL_CTL_DEL;
            }

            // Register events
            var evt = new Interop.libc.epoll_event {
                events = GetEPollEvents(events) | Interop.libc.EPOLLET, data = (IntPtr)handle
            };
            int err = Interop.libc.epoll_ctl(_epollFd, op, fileDescriptor, &evt);

            if (err == 0)
            {
                error = Interop.Error.SUCCESS;
                return(true);
            }

            error = Interop.Sys.GetLastError();
            return(false);
        }
        public unsafe bool TryRegister(int fileDescriptor, SocketAsyncEvents current, SocketAsyncEvents events, GCHandle handle, out Interop.Error error)
        {
            const ushort AddFlags    = Interop.libc.EV_ADD | Interop.libc.EV_CLEAR | Interop.libc.EV_RECEIPT;
            const ushort RemoveFlags = Interop.libc.EV_DELETE | Interop.libc.EV_RECEIPT;

            Debug.Assert(current != events);

            SocketAsyncEvents changed = current ^ events;
            bool readChanged          = (changed & SocketAsyncEvents.Read) != 0;
            bool writeChanged         = (changed & SocketAsyncEvents.Write) != 0;

            int evtCount = (readChanged ? 1 : 0) + (writeChanged ? 1 : 0);
            var kevents  = stackalloc Interop.libc.kevent64_s[evtCount];

            int i = 0;

            if (readChanged)
            {
                kevents[0].ident  = unchecked ((ulong)fileDescriptor);
                kevents[0].filter = Interop.libc.EVFILT_READ;
                kevents[0].flags  = (events & SocketAsyncEvents.Read) == 0 ? RemoveFlags : AddFlags;
                kevents[0].fflags = 0;
                kevents[0].data   = 0;
                kevents[0].udata  = (ulong)(IntPtr)handle;
                i = 1;
            }
            if (writeChanged)
            {
                kevents[i].ident  = unchecked ((ulong)fileDescriptor);
                kevents[i].filter = Interop.libc.EVFILT_WRITE;
                kevents[i].flags  = (events & SocketAsyncEvents.Write) == 0 ? RemoveFlags : AddFlags;
                kevents[i].fflags = 0;
                kevents[i].data   = 0;
                kevents[i].udata  = (ulong)(IntPtr)handle;
            }

            int err = Interop.libc.kevent64(_kqueueFd, kevents, evtCount, null, 0, 0, null);

            if (err == 0)
            {
                error = Interop.Error.SUCCESS;
                return(true);
            }

            error = Interop.Sys.GetLastError();
            return(false);
        }