diff --git a/Sources/BluetoothLinux/DeviceCommand.swift b/Sources/BluetoothLinux/DeviceCommand.swift index a3cfd77..cf09ca8 100644 --- a/Sources/BluetoothLinux/DeviceCommand.swift +++ b/Sources/BluetoothLinux/DeviceCommand.swift @@ -47,5 +47,5 @@ internal func HCISendCommand (_ deviceDescriptor: CInt, // write to device descriptor socket guard write(deviceDescriptor, &data, data.count) >= 0 // should we check if all data was written? - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } } diff --git a/Sources/BluetoothLinux/DevicePollEvent.swift b/Sources/BluetoothLinux/DevicePollEvent.swift index 5098029..0a4261c 100644 --- a/Sources/BluetoothLinux/DevicePollEvent.swift +++ b/Sources/BluetoothLinux/DevicePollEvent.swift @@ -44,7 +44,7 @@ internal func HCIPollEvent(_ deviceDescriptor: CInt, guard withUnsafeMutablePointer(to: &oldFilter, { let pointer = UnsafeMutableRawPointer($0) return getsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, pointer, &oldFilterLength) == 0 - }) else { throw POSIXError.fromErrno! } + }) else { throw POSIXError.fromErrno() } var newFilter = HCIFilter() newFilter.clear() @@ -56,7 +56,7 @@ internal func HCIPollEvent(_ deviceDescriptor: CInt, guard withUnsafeMutablePointer(to: &newFilter, { let pointer = UnsafeMutableRawPointer($0) return setsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, pointer, newFilterLength) == 0 - }) else { throw POSIXError.fromErrno! } + }) else { throw POSIXError.fromErrno() } // restore old filter in case of error func restoreFilter(_ error: Error) -> Error { @@ -64,7 +64,7 @@ internal func HCIPollEvent(_ deviceDescriptor: CInt, guard withUnsafeMutablePointer(to: &oldFilter, { let pointer = UnsafeMutableRawPointer($0) return setsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, pointer, newFilterLength) == 0 - }) else { return BluetoothHostControllerError.couldNotRestoreFilter(error, POSIXError.fromErrno!) } + }) else { return BluetoothHostControllerError.couldNotRestoreFilter(error, POSIXError.fromErrno()) } return error } @@ -102,7 +102,7 @@ internal func HCIPollEvent(_ deviceDescriptor: CInt, } else { // attempt to restore filter and throw - throw restoreFilter(POSIXError.fromErrno!) + throw restoreFilter(POSIXError.fromErrno()) } } diff --git a/Sources/BluetoothLinux/DeviceRequest.swift b/Sources/BluetoothLinux/DeviceRequest.swift index abe0d55..6c91b37 100644 --- a/Sources/BluetoothLinux/DeviceRequest.swift +++ b/Sources/BluetoothLinux/DeviceRequest.swift @@ -208,7 +208,7 @@ internal func HCISendRequest (_ deviceDescriptor: CInt, // get old filter guard getsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, oldFilterPointer, &filterLength) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } // configure new filter newFilter.clear() @@ -222,13 +222,13 @@ internal func HCISendRequest (_ deviceDescriptor: CInt, // set new filter guard setsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, newFilterPointer, filterLength) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } // restore old filter in case of error func restoreFilter(_ error: Error) -> Error { guard setsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, oldFilterPointer, filterLength) == 0 - else { return BluetoothHostControllerError.couldNotRestoreFilter(error, POSIXError.fromErrno!) } + else { return BluetoothHostControllerError.couldNotRestoreFilter(error, POSIXError.fromErrno()) } return error } @@ -268,7 +268,7 @@ internal func HCISendRequest (_ deviceDescriptor: CInt, } else { // attempt to restore filter and throw - throw restoreFilter(POSIXError.fromErrno!) + throw restoreFilter(POSIXError.fromErrno()) } } @@ -298,7 +298,7 @@ internal func HCISendRequest (_ deviceDescriptor: CInt, } else { // attempt to restore filter and throw - throw restoreFilter(POSIXError.fromErrno!) + throw restoreFilter(POSIXError.fromErrno()) } } @@ -317,7 +317,7 @@ internal func HCISendRequest (_ deviceDescriptor: CInt, func done() throws { guard setsockopt(deviceDescriptor, SOL_HCI, HCISocketOption.Filter.rawValue, oldFilterPointer, filterLength) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } } switch eventHeader.event { diff --git a/Sources/BluetoothLinux/HostController.swift b/Sources/BluetoothLinux/HostController.swift index 2aa05d8..3e2f799 100644 --- a/Sources/BluetoothLinux/HostController.swift +++ b/Sources/BluetoothLinux/HostController.swift @@ -121,7 +121,7 @@ internal func HCIOpenDevice(_ deviceIdentifier: UInt16) throws -> CInt { // Create HCI socket let hciSocket = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BluetoothProtocol.hci.rawValue) - guard hciSocket >= 0 else { throw POSIXError.fromErrno! } + guard hciSocket >= 0 else { throw POSIXError.fromErrno() } // Bind socket to the HCI device var address = HCISocketAddress() @@ -136,7 +136,7 @@ internal func HCIOpenDevice(_ deviceIdentifier: UInt16) throws -> CInt { guard didBind else { close(hciSocket) - throw POSIXError.fromErrno! + throw POSIXError.fromErrno() } return hciSocket @@ -147,7 +147,7 @@ internal func HCIRequestDeviceList (_ response: (_ hciSocket: CInt, _ list: // open HCI socket let hciSocket = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BluetoothProtocol.hci.rawValue) - guard hciSocket >= 0 else { throw POSIXError.fromErrno! } + guard hciSocket >= 0 else { throw POSIXError.fromErrno() } defer { close(hciSocket) } @@ -160,7 +160,7 @@ internal func HCIRequestDeviceList (_ response: (_ hciSocket: CInt, _ list: IOControl(hciSocket, HCI.IOCTL.GetDeviceList, $0) } - guard ioctlValue >= 0 else { throw POSIXError.fromErrno! } + guard ioctlValue >= 0 else { throw POSIXError.fromErrno() } return try response(hciSocket, &deviceList) } @@ -216,7 +216,7 @@ internal func HCIGetRoute(_ address: BluetoothAddress? = nil) throws -> UInt16? guard withUnsafeMutablePointer(to: &deviceInfo, { IOControl(CInt(dd), HCI.IOCTL.GetDeviceInfo, UnsafeMutableRawPointer($0)) }) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } return deviceInfo.address == address } @@ -228,7 +228,7 @@ internal func HCIDeviceInfo(_ deviceIdentifier: UInt16) throws -> HCIDeviceInfor let hciSocket = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BluetoothProtocol.hci.rawValue) - guard hciSocket >= 0 else { throw POSIXError.fromErrno! } + guard hciSocket >= 0 else { throw POSIXError.fromErrno() } defer { close(hciSocket) } @@ -237,7 +237,7 @@ internal func HCIDeviceInfo(_ deviceIdentifier: UInt16) throws -> HCIDeviceInfor guard withUnsafeMutablePointer(to: &deviceInfo, { IOControl(hciSocket, HCI.IOCTL.GetDeviceInfo, UnsafeMutableRawPointer($0)) }) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } return deviceInfo } diff --git a/Sources/BluetoothLinux/L2CAP.swift b/Sources/BluetoothLinux/L2CAP.swift index 3d4ea7b..c2c68c6 100644 --- a/Sources/BluetoothLinux/L2CAP.swift +++ b/Sources/BluetoothLinux/L2CAP.swift @@ -114,7 +114,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { var optionLength = socklen_t(MemoryLayout.size) guard getsockopt(fileDescriptor, SOL_SOCKET, socketOption, &optionValue, &optionLength) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } return optionValue } @@ -141,7 +141,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { // error creating socket guard internalSocket >= 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } // set source address var localAddress = sockaddr_l2() @@ -157,7 +157,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { $0.withMemoryRebound(to: sockaddr.self, capacity: 1, { bind(internalSocket, $0, socklen_t(MemoryLayout.size)) == 0 }) - }) else { close(internalSocket); throw POSIXError.fromErrno! } + }) else { close(internalSocket); throw POSIXError.fromErrno() } return (internalSocket, localAddress) } @@ -200,7 +200,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { security.level = securityLevel.rawValue guard setsockopt(internalSocket, SOL_BLUETOOTH, BT_SECURITY, &security, socklen_t(MemoryLayout.size)) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } self.securityLevel = securityLevel } @@ -210,7 +210,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { // put socket into listening mode guard listen(internalSocket, Int32(queueLimit)) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } } /// Blocks the caller until a new connection is recieved. @@ -228,7 +228,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { }) // error accepting new connection - guard client >= 0 else { throw POSIXError.fromErrno! } + guard client >= 0 else { throw POSIXError.fromErrno() } let newSocket = L2CAPSocket(clientSocket: client, remoteAddress: remoteAddress, @@ -257,7 +257,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { $0.withMemoryRebound(to: sockaddr.self, capacity: 1, { connect(internalSocket, $0, socklen_t(MemoryLayout.size)) == 0 }) - }) else { throw POSIXError.fromErrno! } + }) else { throw POSIXError.fromErrno() } // make socket non-blocking try setNonblocking() @@ -276,13 +276,9 @@ public final class L2CAPSocket: L2CAPSocketProtocol { let actualByteCount = read(internalSocket, &buffer, bufferSize) guard actualByteCount >= 0 else { - if let error = POSIXError.fromErrno { - throw error - } else { - return nil - } + throw POSIXError.fromErrno() } - + let actualBytes = Array(buffer.prefix(actualByteCount)) return Data(actualBytes) @@ -299,7 +295,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { let fdCount = select(internalSocket + 1, &readSockets, nil, nil, &time) guard fdCount != -1 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } return fdCount > 0 } @@ -309,12 +305,12 @@ public final class L2CAPSocket: L2CAPSocketProtocol { var flags = fcntl(internalSocket, F_GETFL, 0) guard flags != -1 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } flags = fcntl(internalSocket, F_SETFL, flags | O_NONBLOCK); guard flags != -1 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } } /// Write to the socket. @@ -325,7 +321,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { let actualByteCount = write(internalSocket, &buffer, buffer.count) guard actualByteCount >= 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } guard actualByteCount == buffer.count else { throw L2CAPSocketError.sentLessBytes(actualByteCount) } @@ -338,7 +334,7 @@ public final class L2CAPSocket: L2CAPSocketProtocol { var optionLength = socklen_t(MemoryLayout.size) guard getsockopt(internalSocket, SOL_L2CAP, L2CAP_OPTIONS, &optionValue, &optionLength) == 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } return optionValue } diff --git a/Sources/BluetoothLinux/POSIXError.swift b/Sources/BluetoothLinux/POSIXError.swift index e664322..50dfd38 100755 --- a/Sources/BluetoothLinux/POSIXError.swift +++ b/Sources/BluetoothLinux/POSIXError.swift @@ -17,16 +17,26 @@ import Glibc internal extension POSIXError { /// Creates error from C ```errno```. - static var fromErrno: POSIXError? { + static func fromErrno(file: StaticString = #file, + line: UInt = #line, + function: StaticString = #function) -> POSIXError { guard let code = POSIXErrorCode(rawValue: errno) - else { return nil } + else { fatalError("Invalid POSIX Error \(errno)") } - return POSIXError(code) + return POSIXError(code, function: function, file: file, line: line) } - init(_ code: POSIXErrorCode) { - self.init(_nsError: NSPOSIXError(code)) + init(_ code: POSIXErrorCode, + function: StaticString = #function, + file: StaticString = #file, + line: UInt = #line) { + + self.init(_nsError: NSPOSIXError(code, function: function, file: file, line: line)) + } + + var debugInformation: String? { + return userInfo[NSPOSIXError.debugInformationKey] as? String } } @@ -47,7 +57,7 @@ extension POSIXError: CustomStringConvertible { } #if os(macOS) -extension POSIXErrorCode { +extension POSIXErrorCode: CustomStringConvertible { public var description: String { return rawValue.description } @@ -64,15 +74,26 @@ extension POSIXError: LocalizedError { // MARK: - Supporting Types +/// NSError subclass for POSIX Errors internal final class NSPOSIXError: NSError { let posixError: POSIXErrorCode - init(_ code: POSIXErrorCode) { + init(_ code: POSIXErrorCode, + function: StaticString = #function, + file: StaticString = #file, + line: UInt = #line) { + + var userInfo = [String: Any](minimumCapacity: 1) + userInfo[NSPOSIXError.debugInformationKey] = NSPOSIXError.debugInformation( + function: function, + file: file, + line: line) + self.posixError = code super.init(domain: NSPOSIXErrorDomain, code: Int(code.rawValue), - userInfo: nil) + userInfo: userInfo) } required init?(coder decoder: NSCoder) { @@ -96,6 +117,30 @@ internal final class NSPOSIXError: NSError { override var description: String { return "\(posixError.errorMessage) (\(posixError))" } + + var debugInformation: String? { + return userInfo[NSPOSIXError.debugInformationKey] as? String + } +} + +internal extension NSPOSIXError { + + /// Contains + static let debugInformationKey: String = "NSPOSIXErrorDebugInformation" +} + +private extension NSPOSIXError { + + static let module = NSStringFromClass(NSPOSIXError.self).components(separatedBy:".")[0] + + static func debugInformation(function: StaticString, + file: StaticString, + line: UInt) -> String { + + let file = "\(file)" + let fileName = file.components(separatedBy: "/").last ?? file + return "\(module):\(fileName):\(function):\(line)" + } } #if !swift(>=5.1) && (os(Linux) || os(Android)) diff --git a/Sources/BluetoothLinux/Scan.swift b/Sources/BluetoothLinux/Scan.swift index 0994f61..33d1666 100644 --- a/Sources/BluetoothLinux/Scan.swift +++ b/Sources/BluetoothLinux/Scan.swift @@ -59,7 +59,7 @@ public extension HostController { defer { nameBuffer.deallocateCapacity(maxNameLength) } guard hci_read_remote_name(internalSocket, &address, CInt(maxNameLength), nameBuffer, CInt(timeout)) == CInt(0) - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } let name = String.fromCString(nameBuffer) @@ -113,7 +113,7 @@ internal func HCIInquiry(_ deviceIdentifier: UInt16, let deviceDescriptor = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BluetoothProtocol.hci.rawValue) - guard deviceDescriptor >= 0 else { throw POSIXError.fromErrno! } + guard deviceDescriptor >= 0 else { throw POSIXError.fromErrno() } defer { close(deviceDescriptor) } @@ -136,7 +136,7 @@ internal func HCIInquiry(_ deviceIdentifier: UInt16, } guard IOControl(deviceDescriptor, HCI.IOCTL.Inquiry, UnsafeMutableRawPointer(buffer) ) >= 0 - else { throw POSIXError.fromErrno! } + else { throw POSIXError.fromErrno() } let resultCount = buffer.withMemoryRebound(to: HCIInquiryRequest.self, capacity: 1) { (inquiryRequest) in Int(inquiryRequest.pointee.responseCount) diff --git a/Tests/BluetoothLinuxTests/BluetoothLinuxTests.swift b/Tests/BluetoothLinuxTests/BluetoothLinuxTests.swift index 3a917f8..2bb3513 100644 --- a/Tests/BluetoothLinuxTests/BluetoothLinuxTests.swift +++ b/Tests/BluetoothLinuxTests/BluetoothLinuxTests.swift @@ -43,12 +43,21 @@ final class BluetoothLinuxTests: XCTestCase { XCTAssertEqual(error._nsError.domain, NSPOSIXErrorDomain) XCTAssertEqual(error._nsError.code, Int(errorCode.rawValue)) + #if Xcode + print("Description:", error.description) + print("Debug Information:", error.debugInformation ?? "") + #endif + do { throw error } // deal with protocol and not concrete type catch { + XCTAssert("\(error)".contains(string)) + XCTAssertNotNil((error as? POSIXError)?.userInfo[NSPOSIXError.debugInformationKey]) #if os(macOS) XCTAssertEqual(error.localizedDescription, string) #endif - XCTAssert("\(error)".contains(string)) + #if Xcode + print("Error:", error) + #endif } } }