// 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(); }
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)); }
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); } } }
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); }