diff --git a/src/tools/iostool/CMakeLists.txt b/src/tools/iostool/CMakeLists.txt index 8e465e56b6f..230f5b91f43 100644 --- a/src/tools/iostool/CMakeLists.txt +++ b/src/tools/iostool/CMakeLists.txt @@ -11,8 +11,13 @@ add_qtc_executable(iostool ${FWSystemConfiguration} SOURCES Info.plist - main.cpp + gdbrunner.cpp gdbrunner.h iosdevicemanager.cpp iosdevicemanager.h + iostool.cpp iostool.h + iostooltypes.h + main.cpp + mobiledevicelib.cpp mobiledevicelib.h + relayserver.cpp relayserver.h ) if (TARGET iostool) diff --git a/src/tools/iostool/gdbrunner.cpp b/src/tools/iostool/gdbrunner.cpp new file mode 100644 index 00000000000..1234a472c3f --- /dev/null +++ b/src/tools/iostool/gdbrunner.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "gdbrunner.h" + +#include "iostool.h" + +#ifdef Q_OS_UNIX +#include +#include +#endif + +namespace Ios { + +GdbRunner::GdbRunner(IosTool *iosTool, int gdbFd) : + QObject(nullptr), + m_iosTool(iosTool), + m_gdbFd(gdbFd) +{ +} + +void GdbRunner::run() +{ + { + QMutexLocker l(&m_iosTool->m_xmlMutex); + if (!m_iosTool->splitAppOutput) { + m_iosTool->out.writeStartElement(QLatin1String("app_output")); + m_iosTool->inAppOutput = true; + } + m_iosTool->outFile.flush(); + } + Ios::IosDeviceManager::instance()->processGdbServer(m_gdbFd); + { + QMutexLocker l(&m_iosTool->m_xmlMutex); + if (!m_iosTool->splitAppOutput) { + m_iosTool->inAppOutput = false; + m_iosTool->out.writeEndElement(); + } + m_iosTool->outFile.flush(); + } + close(m_gdbFd); + m_iosTool->doExit(); + emit finished(); +} + +void GdbRunner::stop(int phase) +{ + Ios::IosDeviceManager::instance()->stopGdbServer(m_gdbFd, phase); +} + +} diff --git a/src/tools/iostool/gdbrunner.h b/src/tools/iostool/gdbrunner.h new file mode 100644 index 00000000000..27eea4beaf4 --- /dev/null +++ b/src/tools/iostool/gdbrunner.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace Ios { +class IosTool; +class GdbRunner: public QObject +{ + Q_OBJECT + +public: + GdbRunner(IosTool *iosTool, int gdbFd); + void stop(int phase); + void run(); + +signals: + void finished(); + +private: + IosTool *m_iosTool; + int m_gdbFd; +}; +} diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp index 7003710d9d1..1d9b1874ce1 100644 --- a/src/tools/iostool/iosdevicemanager.cpp +++ b/src/tools/iostool/iosdevicemanager.cpp @@ -24,45 +24,27 @@ ****************************************************************************/ #include "iosdevicemanager.h" -#include + +#include "mobiledevicelib.h" + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* // annoying to import, do without -#include -*/ -/* standard calling convention under Win32 is __stdcall */ -/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ -/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ -#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) -#define MDEV_API __stdcall -#else -#define MDEV_API -#endif - -#include -#include -#include -#include - #include #include +#include +#include +#include #include #include +#include +#include +#include +#include +#include #include -#ifdef MOBILE_DEV_DIRECT_LINK -#include "MobileDevice.h" -#endif +#include + +#include static const bool debugGdbServer = false; static const bool debugAll = false; @@ -82,72 +64,7 @@ enum ADNCI_MSG { }; #endif -extern "C" { -typedef unsigned int ServiceSocket; // match_port_t (i.e. natural_t) or socket (on windows, i.e sock_t) -typedef unsigned int *ServiceConnRef; -typedef unsigned int am_res_t; // mach_error_t -#ifndef MOBILE_DEV_DIRECT_LINK -class AMDeviceNotification; -typedef const AMDeviceNotification *AMDeviceNotificationRef; -class AMDevice; - -struct AMDeviceNotificationCallbackInfo { - AMDevice *_device; - unsigned int _message; - AMDeviceNotification *_subscription; -}; - -enum DeviceInterfaceType { - UNKNOWN = 0, - WIRED, - WIFI -}; - -typedef void (MDEV_API *AMDeviceNotificationCallback)(AMDeviceNotificationCallbackInfo *, void *); -typedef am_res_t (MDEV_API *AMDeviceInstallApplicationCallback)(CFDictionaryRef, void *); -typedef mach_error_t (MDEV_API *AMDeviceSecureInstallApplicationCallback)(CFDictionaryRef, int); - - -typedef AMDevice *AMDeviceRef; -#endif -typedef void (MDEV_API *AMDeviceMountImageCallback)(CFDictionaryRef, int); - - - -typedef void (MDEV_API *AMDSetLogLevelPtr)(int); -typedef am_res_t (MDEV_API *AMDeviceNotificationSubscribePtr)(AMDeviceNotificationCallback, - unsigned int, unsigned int, void *, - const AMDeviceNotification **); -typedef am_res_t (MDEV_API *AMDeviceNotificationUnsubscribePtr)(void *); -typedef int (MDEV_API* AMDeviceGetInterfaceTypePtr)(AMDeviceRef device); -typedef CFPropertyListRef (MDEV_API *AMDeviceCopyValuePtr)(AMDeviceRef,CFStringRef,CFStringRef); -typedef unsigned int (MDEV_API *AMDeviceGetConnectionIDPtr)(AMDeviceRef); -typedef CFStringRef (MDEV_API *AMDeviceCopyDeviceIdentifierPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceConnectPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDevicePairPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceIsPairedPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceValidatePairingPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceStartSessionPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceStopSessionPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceDisconnectPtr)(AMDeviceRef); -typedef am_res_t (MDEV_API *AMDeviceMountImagePtr)(AMDeviceRef, CFStringRef, CFDictionaryRef, - AMDeviceMountImageCallback, void *); -typedef am_res_t (MDEV_API *AMDeviceUninstallApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef, - AMDeviceInstallApplicationCallback, - void*); -typedef am_res_t (MDEV_API *AMDeviceLookupApplicationsPtr)(AMDeviceRef, CFDictionaryRef, CFDictionaryRef *); -typedef char * (MDEV_API *AMDErrorStringPtr)(am_res_t); -typedef CFStringRef (MDEV_API *MISCopyErrorStringForErrorCodePtr)(am_res_t); -typedef am_res_t (MDEV_API *USBMuxConnectByPortPtr)(unsigned int, int, ServiceSocket*); -// secure Api's -typedef am_res_t (MDEV_API *AMDeviceSecureStartServicePtr)(AMDeviceRef, CFStringRef, unsigned int *, ServiceConnRef *); -typedef int (MDEV_API *AMDeviceSecureTransferPathPtr)(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int); -typedef int (MDEV_API *AMDeviceSecureInstallApplicationPtr)(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int); -typedef int (MDEV_API *AMDServiceConnectionGetSocketPtr)(ServiceConnRef); - - -} // extern C } // anonymous namespace @@ -155,82 +72,9 @@ namespace Ios { namespace Internal { static const am_res_t kAMDMobileImageMounterImageMountFailed = 0xe8000076; +static const QString DebugServiceName = "com.apple.debugserver"; +static const QString DebugSecureServiceName = "com.apple.debugserver.DVTSecureSocketProxy"; -class MobileDeviceLib { -public : - MobileDeviceLib(); - - bool load(); - bool isLoaded(); - QStringList errors(); -// - - void setLogLevel(int i) ; - am_res_t deviceNotificationSubscribe(AMDeviceNotificationCallback callback, - unsigned int v1, unsigned int v2, void *v3, - const AMDeviceNotification **handle); - am_res_t deviceNotificationUnsubscribe(void *handle); - int deviceGetInterfaceType(AMDeviceRef device); - CFPropertyListRef deviceCopyValue(AMDeviceRef,CFStringRef,CFStringRef); - unsigned int deviceGetConnectionID(AMDeviceRef); - CFStringRef deviceCopyDeviceIdentifier(AMDeviceRef); - am_res_t deviceConnect(AMDeviceRef); - am_res_t devicePair(AMDeviceRef); - am_res_t deviceIsPaired(AMDeviceRef); - am_res_t deviceValidatePairing(AMDeviceRef); - am_res_t deviceStartSession(AMDeviceRef); - am_res_t deviceStopSession(AMDeviceRef); - am_res_t deviceDisconnect(AMDeviceRef); - am_res_t deviceMountImage(AMDeviceRef, CFStringRef, CFDictionaryRef, - AMDeviceMountImageCallback, void *); - am_res_t deviceUninstallApplication(int, CFStringRef, CFDictionaryRef, - AMDeviceInstallApplicationCallback, - void*); - am_res_t deviceLookupApplications(AMDeviceRef, CFDictionaryRef, CFDictionaryRef *); - char *errorString(am_res_t error); - CFStringRef misErrorStringForErrorCode(am_res_t error); - am_res_t connectByPort(unsigned int connectionId, int port, ServiceSocket *resFd); - - void addError(const QString &msg); - void addError(const char *msg); - - // Secure API's - am_res_t deviceSecureStartService(AMDeviceRef, CFStringRef, ServiceConnRef *); - int deviceConnectionGetSocket(ServiceConnRef); - int deviceSecureTransferApplicationPath(int, AMDeviceRef, CFURLRef, - CFDictionaryRef, AMDeviceSecureInstallApplicationCallback callback, int); - int deviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, - CFDictionaryRef options, AMDeviceSecureInstallApplicationCallback callback, int arg); - - QStringList m_errors; -private: - QLibrary lib; - QList deps; - AMDSetLogLevelPtr m_AMDSetLogLevel; - AMDeviceNotificationSubscribePtr m_AMDeviceNotificationSubscribe; - AMDeviceNotificationUnsubscribePtr m_AMDeviceNotificationUnsubscribe; - AMDeviceGetInterfaceTypePtr m_AMDeviceGetInterfaceType; - AMDeviceCopyValuePtr m_AMDeviceCopyValue; - AMDeviceGetConnectionIDPtr m_AMDeviceGetConnectionID; - AMDeviceCopyDeviceIdentifierPtr m_AMDeviceCopyDeviceIdentifier; - AMDeviceConnectPtr m_AMDeviceConnect; - AMDevicePairPtr m_AMDevicePair; - AMDeviceIsPairedPtr m_AMDeviceIsPaired; - AMDeviceValidatePairingPtr m_AMDeviceValidatePairing; - AMDeviceStartSessionPtr m_AMDeviceStartSession; - AMDeviceStopSessionPtr m_AMDeviceStopSession; - AMDeviceDisconnectPtr m_AMDeviceDisconnect; - AMDeviceMountImagePtr m_AMDeviceMountImage; - AMDeviceSecureStartServicePtr m_AMDeviceSecureStartService; - AMDeviceSecureTransferPathPtr m_AMDeviceSecureTransferPath; - AMDeviceSecureInstallApplicationPtr m_AMDeviceSecureInstallApplication; - AMDServiceConnectionGetSocketPtr m_AMDServiceConnectionGetSocket; - AMDeviceUninstallApplicationPtr m_AMDeviceUninstallApplication; - AMDeviceLookupApplicationsPtr m_AMDeviceLookupApplications; - AMDErrorStringPtr m_AMDErrorString; - MISCopyErrorStringForErrorCodePtr m_MISCopyErrorStringForErrorCode; - USBMuxConnectByPortPtr m_USBMuxConnectByPort; -}; static QString mobileDeviceErrorString(MobileDeviceLib *lib, am_res_t code) { @@ -262,7 +106,7 @@ static QString mobileDeviceErrorString(MobileDeviceLib *lib, am_res_t code) return s; } -qint64 toBuildNumber(const QString &versionStr) +static qint64 toBuildNumber(const QString &versionStr) { QString buildNumber; const QRegularExpression re("\\s\\((\\X+)\\)"); @@ -498,7 +342,7 @@ DeviceSession::~DeviceSession() namespace { // ------- callbacks -------- -extern "C" void deviceNotificationCallback(AMDeviceNotificationCallbackInfo *info, void *user) +extern "C" void deviceNotificationCallback(Ios::AMDeviceNotificationCallbackInfo *info, void *user) { if (info == 0) Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("null info in deviceNotificationCallback")); @@ -526,7 +370,7 @@ extern "C" void deviceNotificationCallback(AMDeviceNotificationCallbackInfo *inf } } -extern "C" void deviceAvailableSessionCallback(QString deviceId, AMDeviceRef device, void *userData) +extern "C" void deviceAvailableSessionCallback(QString deviceId, Ios::AMDeviceRef device, void *userData) { if (debugAll) qDebug() << "deviceAvailableSessionCallback" << QThread::currentThread(); @@ -538,7 +382,7 @@ extern "C" void deviceAvailableSessionCallback(QString deviceId, AMDeviceRef dev session->internalDeviceAvailableCallback(deviceId, device); } -extern "C" am_res_t appTransferSessionCallback(CFDictionaryRef dict, void *userData) +extern "C" Ios::am_res_t appTransferSessionCallback(CFDictionaryRef dict, void *userData) { if (debugAll) { qDebug() << "appTransferSessionCallback" << QThread::currentThread(); @@ -584,7 +428,7 @@ extern "C" mach_error_t appSecureTransferSessionCallback(CFDictionaryRef dict, i return 0; } -extern "C" am_res_t appInstallSessionCallback(CFDictionaryRef dict, void *userData) +extern "C" Ios::am_res_t appInstallSessionCallback(CFDictionaryRef dict, void *userData) { if (debugAll) { qDebug() << "appInstallSessionCallback" << QThread::currentThread(); @@ -616,9 +460,10 @@ bool IosDeviceManagerPrivate::watchDevices() { if (!m_lib.load()) addError(QLatin1String("Error loading MobileDevice.framework")); - if (!m_lib.errors().isEmpty()) + if (!m_lib.errors().isEmpty()) { foreach (const QString &msg, m_lib.errors()) addError(msg); + } m_lib.setLogLevel(5); am_res_t e = m_lib.deviceNotificationSubscribe(&deviceNotificationCallback, 0, 0, 0, &m_notification); @@ -1655,318 +1500,7 @@ void DevInfoSession::deviceCallbackReturned() // ------- MobileDeviceLib implementation -------- -MobileDeviceLib::MobileDeviceLib() { } -bool MobileDeviceLib::load() -{ -#ifdef MOBILE_DEV_DIRECT_LINK - m_AMDSetLogLevel = &AMDSetLogLevel; - m_AMDeviceNotificationSubscribe = &AMDeviceNotificationSubscribe; - //m_AMDeviceNotificationUnsubscribe = &AMDeviceNotificationUnsubscribe; - m_AMDeviceCopyValue = &AMDeviceCopyValue; - m_AMDeviceGetConnectionID = &AMDeviceGetConnectionID; - m_AMDeviceCopyDeviceIdentifier = &AMDeviceCopyDeviceIdentifier; - m_AMDeviceConnect = &AMDeviceConnect; - //m_AMDevicePair = &AMDevicePair; - m_AMDeviceIsPaired = &AMDeviceIsPaired; - m_AMDeviceValidatePairing = &AMDeviceValidatePairing; - m_AMDeviceStartSession = &AMDeviceStartSession; - m_AMDeviceStopSession = &AMDeviceStopSession; - m_AMDeviceDisconnect = &AMDeviceDisconnect; - m_AMDeviceMountImage = &AMDeviceMountImage; - m_AMDeviceStartService = &AMDeviceStartService; - m_AMDeviceTransferApplication = &AMDeviceTransferApplication; - m_AMDeviceInstallApplication = &AMDeviceInstallApplication; - //m_AMDeviceUninstallApplication = &AMDeviceUninstallApplication; - //m_AMDeviceLookupApplications = &AMDeviceLookupApplications; - m_USBMuxConnectByPort = &USBMuxConnectByPort; -#else - QLibrary *libAppleFSCompression = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/AppleFSCompression.framework/AppleFSCompression")); - if (!libAppleFSCompression->load()) - addError("MobileDevice dependency AppleFSCompression failed to load"); - deps << libAppleFSCompression; - QLibrary *libBom = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/Bom.framework/Bom")); - if (!libBom->load()) - addError("MobileDevice dependency Bom failed to load"); - deps << libBom; - lib.setFileName(QLatin1String("/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice")); - if (!lib.load()) - return false; - m_AMDSetLogLevel = reinterpret_cast(lib.resolve("AMDSetLogLevel")); - if (m_AMDSetLogLevel == 0) - addError("MobileDeviceLib does not define AMDSetLogLevel"); - m_AMDeviceNotificationSubscribe = reinterpret_cast(lib.resolve("AMDeviceNotificationSubscribe")); - if (m_AMDeviceNotificationSubscribe == 0) - addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe"); - m_AMDeviceNotificationUnsubscribe = reinterpret_cast(lib.resolve("AMDeviceNotificationUnsubscribe")); - if (m_AMDeviceNotificationUnsubscribe == 0) - addError("MobileDeviceLib does not define AMDeviceNotificationUnsubscribe"); - m_AMDeviceGetInterfaceType = reinterpret_cast(lib.resolve("AMDeviceGetInterfaceType")); - if (m_AMDeviceGetInterfaceType == 0) - addError("MobileDeviceLib does not define AMDeviceGetInterfaceType"); - m_AMDeviceCopyValue = reinterpret_cast(lib.resolve("AMDeviceCopyValue")); - if (m_AMDSetLogLevel == 0) - addError("MobileDeviceLib does not define AMDSetLogLevel"); - m_AMDeviceGetConnectionID = reinterpret_cast(lib.resolve("AMDeviceGetConnectionID")); - if (m_AMDeviceGetConnectionID == 0) - addError("MobileDeviceLib does not define AMDeviceGetConnectionID"); - m_AMDeviceCopyDeviceIdentifier = reinterpret_cast(lib.resolve("AMDeviceCopyDeviceIdentifier")); - if (m_AMDeviceCopyDeviceIdentifier == 0) - addError("MobileDeviceLib does not define AMDeviceCopyDeviceIdentifier"); - m_AMDeviceConnect = reinterpret_cast(lib.resolve("AMDeviceConnect")); - if (m_AMDeviceConnect == 0) - addError("MobileDeviceLib does not define AMDeviceConnect"); - m_AMDevicePair = reinterpret_cast(lib.resolve("AMDevicePair")); - if (m_AMDevicePair == 0) - addError("MobileDeviceLib does not define AMDevicePair"); - m_AMDeviceIsPaired = reinterpret_cast(lib.resolve("AMDeviceIsPaired")); - if (m_AMDeviceIsPaired == 0) - addError("MobileDeviceLib does not define AMDeviceIsPaired"); - m_AMDeviceValidatePairing = reinterpret_cast(lib.resolve("AMDeviceValidatePairing")); - if (m_AMDeviceValidatePairing == 0) - addError("MobileDeviceLib does not define AMDeviceValidatePairing"); - m_AMDeviceStartSession = reinterpret_cast(lib.resolve("AMDeviceStartSession")); - if (m_AMDeviceStartSession == 0) - addError("MobileDeviceLib does not define AMDeviceStartSession"); - m_AMDeviceStopSession = reinterpret_cast(lib.resolve("AMDeviceStopSession")); - if (m_AMDeviceStopSession == 0) - addError("MobileDeviceLib does not define AMDeviceStopSession"); - m_AMDeviceDisconnect = reinterpret_cast(lib.resolve("AMDeviceDisconnect")); - if (m_AMDeviceDisconnect == 0) - addError("MobileDeviceLib does not define AMDeviceDisconnect"); - m_AMDeviceMountImage = reinterpret_cast(lib.resolve("AMDeviceMountImage")); - if (m_AMDeviceMountImage == 0) - addError("MobileDeviceLib does not define AMDeviceMountImage"); - m_AMDeviceSecureStartService = reinterpret_cast(lib.resolve("AMDeviceSecureStartService")); - if (m_AMDeviceSecureStartService == 0) - addError("MobileDeviceLib does not define AMDeviceSecureStartService"); - m_AMDeviceSecureTransferPath = reinterpret_cast(lib.resolve("AMDeviceSecureTransferPath")); - if (m_AMDeviceSecureTransferPath == 0) - addError("MobileDeviceLib does not define AMDeviceSecureTransferPath"); - m_AMDeviceSecureInstallApplication = reinterpret_cast(lib.resolve("AMDeviceSecureInstallApplication")); - if (m_AMDeviceSecureInstallApplication == 0) - addError("MobileDeviceLib does not define AMDeviceSecureInstallApplication"); - m_AMDServiceConnectionGetSocket = reinterpret_cast(lib.resolve("AMDServiceConnectionGetSocket")); - if (m_AMDServiceConnectionGetSocket == nullptr) - addError("MobileDeviceLib does not define AMDServiceConnectionGetSocket"); - m_AMDeviceUninstallApplication = reinterpret_cast(lib.resolve("AMDeviceUninstallApplication")); - if (m_AMDeviceUninstallApplication == 0) - addError("MobileDeviceLib does not define AMDeviceUninstallApplication"); - m_AMDeviceLookupApplications = reinterpret_cast(lib.resolve("AMDeviceLookupApplications")); - if (m_AMDeviceLookupApplications == 0) - addError("MobileDeviceLib does not define AMDeviceLookupApplications"); - m_AMDErrorString = reinterpret_cast(lib.resolve("AMDErrorString")); - if (m_AMDErrorString == 0) - addError("MobileDeviceLib does not define AMDErrorString"); - m_MISCopyErrorStringForErrorCode = reinterpret_cast(lib.resolve("MISCopyErrorStringForErrorCode")); - if (m_MISCopyErrorStringForErrorCode == 0) - addError("MobileDeviceLib does not define MISCopyErrorStringForErrorCode"); - m_USBMuxConnectByPort = reinterpret_cast(lib.resolve("USBMuxConnectByPort")); - if (m_USBMuxConnectByPort == 0) - addError("MobileDeviceLib does not define USBMuxConnectByPort"); -#endif - return true; -} - -bool MobileDeviceLib::isLoaded() -{ - return lib.isLoaded(); -} - -QStringList MobileDeviceLib::errors() -{ - return m_errors; -} - -void MobileDeviceLib::setLogLevel(int i) -{ - if (m_AMDSetLogLevel) - m_AMDSetLogLevel(i); -} - -am_res_t MobileDeviceLib::deviceNotificationSubscribe(AMDeviceNotificationCallback callback, - unsigned int v1, unsigned int v2, void *callbackArgs, - const AMDeviceNotification **handle) -{ - if (m_AMDeviceNotificationSubscribe) - return m_AMDeviceNotificationSubscribe(callback,v1,v2,callbackArgs,handle); - return -1; -} - -am_res_t MobileDeviceLib::deviceNotificationUnsubscribe(void *handle) -{ - if (m_AMDeviceNotificationUnsubscribe) - return m_AMDeviceNotificationUnsubscribe(handle); - return -1; -} - -int MobileDeviceLib::deviceGetInterfaceType(AMDeviceRef device) -{ - if (m_AMDeviceGetInterfaceType) - return m_AMDeviceGetInterfaceType(device); - return DeviceInterfaceType::UNKNOWN; -} - -CFPropertyListRef MobileDeviceLib::deviceCopyValue(AMDeviceRef device,CFStringRef group,CFStringRef key) -{ - if (m_AMDeviceCopyValue) - return m_AMDeviceCopyValue(device, group, key); - return 0; -} - -unsigned int MobileDeviceLib::deviceGetConnectionID(AMDeviceRef device) -{ - if (m_AMDeviceGetConnectionID) - return m_AMDeviceGetConnectionID(device); - return -1; -} - -CFStringRef MobileDeviceLib::deviceCopyDeviceIdentifier(AMDeviceRef device) -{ - if (m_AMDeviceCopyDeviceIdentifier) - return m_AMDeviceCopyDeviceIdentifier(device); - return 0; -} - -am_res_t MobileDeviceLib::deviceConnect(AMDeviceRef device) -{ - if (m_AMDeviceConnect) - return m_AMDeviceConnect(device); - return -1; -} - -am_res_t MobileDeviceLib::devicePair(AMDeviceRef device) -{ - if (m_AMDevicePair) - return m_AMDevicePair(device); - return -1; -} - -am_res_t MobileDeviceLib::deviceIsPaired(AMDeviceRef device) -{ - if (m_AMDeviceIsPaired) - return m_AMDeviceIsPaired(device); - return -1; -} - -am_res_t MobileDeviceLib::deviceValidatePairing(AMDeviceRef device) -{ - if (m_AMDeviceValidatePairing) - return m_AMDeviceValidatePairing(device); - return -1; -} - -am_res_t MobileDeviceLib::deviceStartSession(AMDeviceRef device) -{ - if (m_AMDeviceStartSession) - return m_AMDeviceStartSession(device); - return -1; -} - -am_res_t MobileDeviceLib::deviceStopSession(AMDeviceRef device) -{ - if (m_AMDeviceStopSession) - return m_AMDeviceStopSession(device); - return -1; -} - -am_res_t MobileDeviceLib::deviceDisconnect(AMDeviceRef device) -{ - if (m_AMDeviceDisconnect) - return m_AMDeviceDisconnect(device); - return -1; -} - -am_res_t MobileDeviceLib::deviceMountImage(AMDeviceRef device, CFStringRef imagePath, - CFDictionaryRef options, - AMDeviceMountImageCallback callback, - void *callbackExtraArgs) -{ - if (m_AMDeviceMountImage) - return m_AMDeviceMountImage(device, imagePath, options, callback, callbackExtraArgs); - return -1; -} - -am_res_t MobileDeviceLib::deviceUninstallApplication(int serviceFd, CFStringRef bundleId, - CFDictionaryRef options, - AMDeviceInstallApplicationCallback callback, - void *callbackExtraArgs) -{ - if (m_AMDeviceUninstallApplication) - return m_AMDeviceUninstallApplication(serviceFd, bundleId, options, callback, callbackExtraArgs); - return -1; -} - -am_res_t MobileDeviceLib::deviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, - CFDictionaryRef *res) -{ - if (m_AMDeviceLookupApplications) - return m_AMDeviceLookupApplications(device, options, res); - return -1; -} - -char *MobileDeviceLib::errorString(am_res_t error) -{ - if (m_AMDErrorString) - return m_AMDErrorString(error); - return 0; -} - -CFStringRef MobileDeviceLib::misErrorStringForErrorCode(am_res_t error) -{ - if (m_MISCopyErrorStringForErrorCode) - return m_MISCopyErrorStringForErrorCode(error); - return NULL; -} - -am_res_t MobileDeviceLib::connectByPort(unsigned int connectionId, int port, ServiceSocket *resFd) -{ - if (m_USBMuxConnectByPort) - return m_USBMuxConnectByPort(connectionId, port, resFd); - return -1; -} - -void MobileDeviceLib::addError(const QString &msg) -{ - qDebug() << "MobileDeviceLib ERROR:" << msg; - m_errors << QLatin1String("MobileDeviceLib ERROR:") << msg; -} - -void MobileDeviceLib::addError(const char *msg) -{ - addError(QLatin1String(msg)); -} - -am_res_t MobileDeviceLib::deviceSecureStartService(AMDeviceRef device, CFStringRef serviceName, ServiceConnRef *fdRef) -{ - if (m_AMDeviceSecureStartService) - return m_AMDeviceSecureStartService(device, serviceName, nullptr, fdRef); - return 0; -} - -int MobileDeviceLib::deviceSecureTransferApplicationPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef dict, AMDeviceSecureInstallApplicationCallback callback, int args) -{ - int returnCode = -1; - if (m_AMDeviceSecureTransferPath) - returnCode = m_AMDeviceSecureTransferPath(zero, device, url, dict, callback, args); - return returnCode; -} - -int MobileDeviceLib::deviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, AMDeviceSecureInstallApplicationCallback callback, int arg) -{ - int returnCode = -1; - if (m_AMDeviceSecureInstallApplication) { - returnCode = m_AMDeviceSecureInstallApplication(zero, device, url, options, callback, arg); - } - return returnCode; -} - -int MobileDeviceLib::deviceConnectionGetSocket(ServiceConnRef ref) { - int fd = 0; - if (m_AMDServiceConnectionGetSocket) - fd = m_AMDServiceConnectionGetSocket(ref); - return fd; -} void CommandSession::internalDeviceAvailableCallback(QString deviceId, AMDeviceRef device) { diff --git a/src/tools/iostool/iosdevicemanager.h b/src/tools/iostool/iosdevicemanager.h index 5d6c489b7d6..a16403010a9 100644 --- a/src/tools/iostool/iosdevicemanager.h +++ b/src/tools/iostool/iosdevicemanager.h @@ -25,17 +25,12 @@ #pragma once +#include "iostooltypes.h" + #include #include #include #include -#include -#include -#include -#include -#include - -#include namespace Ios { namespace Internal { @@ -43,13 +38,13 @@ class DevInfoSession; class IosDeviceManagerPrivate; } // namespace Internal -typedef unsigned int ServiceSocket; class DeviceSession; class IosDeviceManager : public QObject { Q_OBJECT + public: typedef QMap Dict; enum OpStatus { @@ -72,6 +67,7 @@ public: int processGdbServer(int fd); void stopGdbServer(int fd, int phase); QStringList errors(); + signals: void deviceAdded(const QString &deviceId); void deviceRemoved(const QString &deviceId); @@ -85,6 +81,7 @@ signals: void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info); void appOutput(const QString &output); void errorMsg(const QString &msg); + private: friend class Internal::IosDeviceManagerPrivate; friend class Internal::DevInfoSession; diff --git a/src/tools/iostool/iostool.cpp b/src/tools/iostool/iostool.cpp new file mode 100644 index 00000000000..9123dcec73a --- /dev/null +++ b/src/tools/iostool/iostool.cpp @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "iostool.h" + +#include "relayserver.h" +#include "gdbrunner.h" + +#include +#include +#include +#include + +namespace Ios { + +IosTool::IosTool(QObject *parent): + QObject(parent), + maxProgress(0), + opLeft(0), + debug(false), + inAppOutput(false), + splitAppOutput(true), + appOp(IosDeviceManager::None), + outFile(), + out(&outFile), + gdbServer(0), + qmlServer(0) +{ + outFile.open(stdout, QIODevice::WriteOnly, QFileDevice::DontCloseHandle); + out.setAutoFormatting(true); +} + +IosTool::~IosTool() +{ +} + +void IosTool::run(const QStringList &args) +{ + IosDeviceManager *manager = IosDeviceManager::instance(); + QString deviceId; + QString bundlePath; + bool deviceInfo = false; + bool printHelp = false; + int timeout = 1000; + QStringList extraArgs; + + out.writeStartDocument(); + out.writeStartElement(QLatin1String("query_result")); + for (int iarg = 1; iarg < args.size(); ++iarg) { + const QString &arg = args[iarg]; + if (arg == QLatin1String("-i") || arg == QLatin1String("--id")) { + if (++iarg == args.size()) { + writeMsg(QStringLiteral("missing device id value after ") + arg); + printHelp = true; + } + deviceId = args.value(iarg); + } else if (arg == QLatin1String("-b") || arg == QLatin1String("--bundle")) { + if (++iarg == args.size()) { + writeMsg(QStringLiteral("missing bundle path after ") + arg); + printHelp = true; + } + bundlePath = args.value(iarg); + } else if (arg == QLatin1String("--install")) { + appOp = IosDeviceManager::AppOp(appOp | IosDeviceManager::Install); + } else if (arg == QLatin1String("--run")) { + appOp = IosDeviceManager::AppOp(appOp | IosDeviceManager::Run); + } else if (arg == QLatin1String("--noninteractive")) { + // ignored for compatibility + } else if (arg == QLatin1String("-v") || arg == QLatin1String("--verbose")) { + m_echoRelays = true; + } else if (arg == QLatin1String("-d") || arg == QLatin1String("--debug")) { + appOp = IosDeviceManager::AppOp(appOp | IosDeviceManager::Run); + debug = true; + } else if (arg == QLatin1String("--device-info")) { + deviceInfo = true; + } else if (arg == QLatin1String("-t") || arg == QLatin1String("--timeout")) { + if (++iarg == args.size()) { + writeMsg(QStringLiteral("missing timeout value after ") + arg); + printHelp = true; + } + bool ok = false; + int tOut = args.value(iarg).toInt(&ok); + if (ok && tOut > 0) { + timeout = tOut; + } else { + writeMsg("timeout value should be an integer"); + printHelp = true; + } + } else if (arg == QLatin1String("-a") || arg == QLatin1String("--args")) { + extraArgs = args.mid(iarg + 1, args.size() - iarg - 1); + iarg = args.size(); + } else if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { + printHelp = true; + } else { + writeMsg(QString::fromLatin1("unexpected argument \"%1\"").arg(arg)); + } + } + if (printHelp) { + out.writeStartElement(QLatin1String("msg")); + out.writeCharacters(QLatin1String("iostool [--id ] [--bundle ] [--install] [--run] [--debug]\n")); + out.writeCharacters(QLatin1String(" [--device-info] [--timeout ] [--verbose]\n")); // to do pass in env as stub does + out.writeCharacters(QLatin1String(" [--args ]")); + out.writeEndElement(); + doExit(-1); + return; + } + outFile.flush(); + connect(manager, &IosDeviceManager::isTransferringApp, this, &IosTool::isTransferringApp); + connect(manager, &IosDeviceManager::didTransferApp, this, &IosTool::didTransferApp); + connect(manager, &IosDeviceManager::didStartApp, this, &IosTool::didStartApp); + connect(manager, &IosDeviceManager::deviceInfo, this, &IosTool::deviceInfo); + connect(manager, &IosDeviceManager::appOutput, this, &IosTool::appOutput); + connect(manager, &IosDeviceManager::errorMsg, this, &IosTool::errorMsg); + manager->watchDevices(); + const QRegularExpression qmlPortRe(QLatin1String("-qmljsdebugger=port:([0-9]+)")); + for (const QString &arg : extraArgs) { + const QRegularExpressionMatch match = qmlPortRe.match(arg); + if (match.hasMatch()) { + bool ok; + int qmlPort = match.captured(1).toInt(&ok); + if (ok && qmlPort > 0 && qmlPort <= 0xFFFF) + m_qmlPort = match.captured(1); + break; + } + } + if (deviceInfo) { + if (!bundlePath.isEmpty()) + writeMsg("--device-info overrides --bundle"); + ++opLeft; + manager->requestDeviceInfo(deviceId, timeout); + } else if (!bundlePath.isEmpty()) { + switch (appOp) { + case IosDeviceManager::None: + break; + case IosDeviceManager::Install: + case IosDeviceManager::Run: + ++opLeft; + break; + case IosDeviceManager::InstallAndRun: + opLeft += 2; + break; + } + maxProgress = 200; + manager->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout); + } + if (opLeft == 0) + doExit(0); +} + +void IosTool::stopXml(int errorCode) +{ + QMutexLocker l(&m_xmlMutex); + out.writeEmptyElement(QLatin1String("exit")); + out.writeAttribute(QLatin1String("code"), QString::number(errorCode)); + out.writeEndElement(); // result element (hopefully) + out.writeEndDocument(); + outFile.flush(); +} + +void IosTool::doExit(int errorCode) +{ + stopXml(errorCode); + QCoreApplication::exit(errorCode); // sometime does not really exit + exit(errorCode); +} + +void IosTool::isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress, + const QString &info) +{ + Q_UNUSED(bundlePath) + Q_UNUSED(deviceId) + QMutexLocker l(&m_xmlMutex); + out.writeStartElement(QLatin1String("status")); + out.writeAttribute(QLatin1String("progress"), QString::number(progress)); + out.writeAttribute(QLatin1String("max_progress"), QString::number(maxProgress)); + out.writeCharacters(info); + out.writeEndElement(); + outFile.flush(); +} + +void IosTool::didTransferApp(const QString &bundlePath, const QString &deviceId, + IosDeviceManager::OpStatus status) +{ + Q_UNUSED(bundlePath) + Q_UNUSED(deviceId) + { + QMutexLocker l(&m_xmlMutex); + if (status == IosDeviceManager::Success) { + out.writeStartElement(QLatin1String("status")); + out.writeAttribute(QLatin1String("progress"), QString::number(maxProgress)); + out.writeAttribute(QLatin1String("max_progress"), QString::number(maxProgress)); + out.writeCharacters(QLatin1String("App Transferred")); + out.writeEndElement(); + } + out.writeEmptyElement(QLatin1String("app_transfer")); + out.writeAttribute(QLatin1String("status"), + (status == IosDeviceManager::Success) ? + QLatin1String("SUCCESS") : + QLatin1String("FAILURE")); + //out.writeCharacters(QString()); // trigger a complete closing of the empty element + outFile.flush(); + } + if (status != IosDeviceManager::Success || --opLeft == 0) + doExit((status == IosDeviceManager::Success) ? 0 : -1); +} + +void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, + IosDeviceManager::OpStatus status, int gdbFd, + DeviceSession *deviceSession) +{ + Q_UNUSED(bundlePath) + Q_UNUSED(deviceId) + { + QMutexLocker l(&m_xmlMutex); + out.writeEmptyElement(QLatin1String("app_started")); + out.writeAttribute(QLatin1String("status"), + (status == IosDeviceManager::Success) ? + QLatin1String("SUCCESS") : + QLatin1String("FAILURE")); + //out.writeCharacters(QString()); // trigger a complete closing of the empty element + outFile.flush(); + } + if (status != IosDeviceManager::Success || appOp == IosDeviceManager::Install) { + doExit(); + return; + } + if (gdbFd <= 0) { + writeMsg("no gdb connection"); + doExit(-2); + return; + } + if (appOp != IosDeviceManager::InstallAndRun && appOp != IosDeviceManager::Run) { + writeMsg(QString::fromLatin1("unexpected appOp value %1").arg(appOp)); + doExit(-3); + return; + } + if (deviceSession) { + int qmlPort = deviceSession->qmljsDebugPort(); + if (qmlPort) { + qmlServer = new GenericRelayServer(this, qmlPort, deviceSession); + qmlServer->startServer(); + } + } + if (debug) { + gdbServer = new SingleRelayServer(this, gdbFd); + if (!gdbServer->startServer()) { + doExit(-4); + return; + } + } + { + QMutexLocker l(&m_xmlMutex); + out.writeStartElement(QLatin1String("server_ports")); + out.writeAttribute(QLatin1String("gdb_server"), + QString::number(gdbServer ? gdbServer->serverPort() : -1)); + out.writeAttribute(QLatin1String("qml_server"), + QString::number(qmlServer ? qmlServer->serverPort() : -1)); + out.writeEndElement(); + outFile.flush(); + } + if (!debug) { + gdbRunner = new GdbRunner(this, gdbFd); + // we should not stop the event handling of the main thread + // all output moves to the new thread (other option would be to signal it back) + QThread *gdbProcessThread = new QThread(); + gdbRunner->moveToThread(gdbProcessThread); + QObject::connect(gdbProcessThread, &QThread::started, gdbRunner, &GdbRunner::run); + QObject::connect(gdbRunner, &GdbRunner::finished, gdbProcessThread, &QThread::quit); + QObject::connect(gdbProcessThread, &QThread::finished, + gdbProcessThread, &QObject::deleteLater); + gdbProcessThread->start(); + + new std::thread([this]() -> void { readStdin();}); + } +} + +void IosTool::writeMsg(const char *msg) +{ + writeMsg(QString::fromLatin1(msg)); +} + +void IosTool::writeMsg(const QString &msg) +{ + QMutexLocker l(&m_xmlMutex); + out.writeStartElement(QLatin1String("msg")); + writeTextInElement(msg); + out.writeCharacters(QLatin1String("\n")); + out.writeEndElement(); + outFile.flush(); +} + +void IosTool::writeMaybeBin(const QString &extraMsg, const char *msg, quintptr len) +{ + char *buf2 = new char[len * 2 + 4]; + buf2[0] = '['; + const char toHex[] = "0123456789abcdef"; + for (quintptr i = 0; i < len; ++i) { + buf2[2 * i + 1] = toHex[(0xF & (msg[i] >> 4))]; + buf2[2 * i + 2] = toHex[(0xF & msg[i])]; + } + buf2[2 * len + 1] = ']'; + buf2[2 * len + 2] = ' '; + buf2[2 * len + 3] = 0; + + QMutexLocker l(&m_xmlMutex); + out.writeStartElement(QLatin1String("msg")); + out.writeCharacters(extraMsg); + out.writeCharacters(QLatin1String(buf2)); + for (quintptr i = 0; i < len; ++i) { + if (msg[i] < 0x20 || msg[i] > 0x7f) + buf2[i] = '_'; + else + buf2[i] = msg[i]; + } + buf2[len] = 0; + out.writeCharacters(QLatin1String(buf2)); + delete[] buf2; + out.writeEndElement(); + outFile.flush(); +} + +void IosTool::deviceInfo(const QString &deviceId, const IosDeviceManager::Dict &devInfo) +{ + Q_UNUSED(deviceId) + { + QMutexLocker l(&m_xmlMutex); + out.writeTextElement(QLatin1String("device_id"), deviceId); + out.writeStartElement(QLatin1String("device_info")); + for (auto i = devInfo.cbegin(); i != devInfo.cend(); ++i) { + out.writeStartElement(QLatin1String("item")); + out.writeTextElement(QLatin1String("key"), i.key()); + out.writeTextElement(QLatin1String("value"), i.value()); + out.writeEndElement(); + } + out.writeEndElement(); + outFile.flush(); + } + doExit(); +} + +void IosTool::writeTextInElement(const QString &output) +{ + const QRegularExpression controlCharRe(QLatin1String("[\x01-\x08]|\x0B|\x0C|[\x0E-\x1F]|\\0000")); + int pos = 0; + int oldPos = 0; + + while ((pos = output.indexOf(controlCharRe, pos)) != -1) { + QMutexLocker l(&m_xmlMutex); + out.writeCharacters(output.mid(oldPos, pos - oldPos)); + out.writeEmptyElement(QLatin1String("control_char")); + out.writeAttribute(QLatin1String("code"), QString::number(output.at(pos).toLatin1())); + pos += 1; + oldPos = pos; + } + out.writeCharacters(output.mid(oldPos, output.length() - oldPos)); +} + +void IosTool::appOutput(const QString &output) +{ + QMutexLocker l(&m_xmlMutex); + if (!inAppOutput) + out.writeStartElement(QLatin1String("app_output")); + writeTextInElement(output); + if (!inAppOutput) + out.writeEndElement(); + outFile.flush(); +} + +void IosTool::readStdin() +{ + int c = getchar(); + if (c == 'k') { + QMetaObject::invokeMethod(this, "stopGdbRunner"); + errorMsg(QLatin1String("iostool: Killing inferior.\n")); + } else if (c != EOF) { + errorMsg(QLatin1String("iostool: Unexpected character in stdin, stop listening.\n")); + } +} + +void IosTool::errorMsg(const QString &msg) +{ + writeMsg(msg); +} + +void IosTool::stopGdbRunner() +{ + if (gdbRunner) { + gdbRunner->stop(0); + QTimer::singleShot(100, this, &IosTool::stopGdbRunner2); + } +} + +void IosTool::stopGdbRunner2() +{ + if (gdbRunner) + gdbRunner->stop(1); +} + +void IosTool::stopRelayServers(int errorCode) +{ + if (echoRelays()) + writeMsg("gdbServerStops"); + if (qmlServer) + qmlServer->stopServer(); + if (gdbServer) + gdbServer->stopServer(); + doExit(errorCode); +} + +} diff --git a/src/tools/iostool/iostool.h b/src/tools/iostool/iostool.h new file mode 100644 index 00000000000..24e78c66810 --- /dev/null +++ b/src/tools/iostool/iostool.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "iosdevicemanager.h" + +#include +#include +#include +#include + +namespace Ios { +class GdbRunner; +class SingleRelayServer; +class GenericRelayServer; + +class IosTool: public QObject +{ + Q_OBJECT + +public: + IosTool(QObject *parent = 0); + virtual ~IosTool(); + void run(const QStringList &args); + void doExit(int errorCode = 0); + void writeMsg(const char *msg); + void writeMsg(const QString &msg); + void stopXml(int errorCode); + void writeTextInElement(const QString &output); + void stopRelayServers(int errorCode = 0); + void writeMaybeBin(const QString &extraMsg, const char *msg, quintptr len); + void errorMsg(const QString &msg); + Q_INVOKABLE void stopGdbRunner(); + bool echoRelays() const { return m_echoRelays; } + +private: + void stopGdbRunner2(); + void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress, + const QString &info); + void didTransferApp(const QString &bundlePath, const QString &deviceId, + Ios::IosDeviceManager::OpStatus status); + void didStartApp(const QString &bundlePath, const QString &deviceId, + IosDeviceManager::OpStatus status, int gdbFd, + DeviceSession *deviceSession); + void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info); + void appOutput(const QString &output); + void readStdin(); + + QRecursiveMutex m_xmlMutex; + int maxProgress; + int opLeft; + bool debug; + bool inAppOutput; + bool splitAppOutput; // as QXmlStreamReader reports the text attributes atomically it is better to split + Ios::IosDeviceManager::AppOp appOp; + QFile outFile; + QString m_qmlPort; + QXmlStreamWriter out; + SingleRelayServer *gdbServer; + GenericRelayServer *qmlServer; + GdbRunner *gdbRunner; + bool m_echoRelays = false; + friend class GdbRunner; +}; +} diff --git a/src/tools/iostool/iostool.pro b/src/tools/iostool/iostool.pro index 4ecd607ac22..da866fbffff 100644 --- a/src/tools/iostool/iostool.pro +++ b/src/tools/iostool/iostool.pro @@ -24,10 +24,19 @@ RPATH_BASE = $$DESTDIR include(../../rpath.pri) SOURCES += main.cpp \ - iosdevicemanager.cpp + gdbrunner.cpp \ + iosdevicemanager.cpp \ + iostool.cpp \ + mobiledevicelib.cpp \ + relayserver.cpp HEADERS += \ - iosdevicemanager.h + gdbrunner.h \ + iosdevicemanager.h \ + iostool.h \ + iostooltypes.h \ + mobiledevicelib.h \ + relayserver.h DISTFILES += Info.plist diff --git a/src/tools/iostool/iostool.qbs b/src/tools/iostool/iostool.qbs index 1d5e282104b..a39ace4dba1 100644 --- a/src/tools/iostool/iostool.qbs +++ b/src/tools/iostool/iostool.qbs @@ -12,9 +12,18 @@ QtcTool { files: [ "Info.plist", - "main.cpp", + "gdbrunner.cpp", + "gdbrunner.h", "iosdevicemanager.cpp", - "iosdevicemanager.h" + "iosdevicemanager.h", + "iostool.cpp", + "iostool.h", + "iostooltypes.h", + "main.cpp", + "mobiledevicelib.cpp", + "mobiledevicelib.h", + "relayserver.cpp", + "relayserver.h" ] cpp.frameworks: base.concat(["CoreFoundation", "CoreServices", "IOKit", "Security", "SystemConfiguration"]) diff --git a/src/tools/iostool/iostooltypes.h b/src/tools/iostool/iostooltypes.h new file mode 100644 index 00000000000..766afe143d2 --- /dev/null +++ b/src/tools/iostool/iostooltypes.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + + +extern "C" { +typedef unsigned int ServiceSocket; // match_port_t (i.e. natural_t) or socket (on windows, i.e sock_t) +typedef unsigned int *ServiceConnRef; +} // extern C diff --git a/src/tools/iostool/main.cpp b/src/tools/iostool/main.cpp index c8bf5658166..a453010eb92 100644 --- a/src/tools/iostool/main.cpp +++ b/src/tools/iostool/main.cpp @@ -23,881 +23,10 @@ ** ****************************************************************************/ -#include "iosdevicemanager.h" +#include "iostool.h" -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#ifdef Q_OS_UNIX -#include -#include -#endif -#include - -// avoid utils dependency -#define QTC_CHECK(cond) if (cond) {} else { qWarning() << "assert failed " << #cond << " " \ - << __FILE__ << ":" << __LINE__; } do {} while (0) - -static bool echoRelays = false; - -class IosTool; -class RelayServer; -class GenericRelayServer; - -class Relayer: public QObject -{ - Q_OBJECT -public: - Relayer(RelayServer *parent, QTcpSocket *clientSocket); - ~Relayer(); - void setClientSocket(QTcpSocket *clientSocket); - bool startRelay(int serverFileDescriptor); - - void handleSocketHasData(int socket); - void handleClientHasData(); - void handleClientHasError(QAbstractSocket::SocketError error); -protected: - IosTool *iosTool(); - RelayServer *server(); - Ios::ServiceSocket m_serverFileDescriptor; - QTcpSocket *m_clientSocket; - QSocketNotifier *m_serverNotifier; -}; - -class RemotePortRelayer: public Relayer -{ - Q_OBJECT -public: - static const int reconnectMsecDelay = 500; - static const int maxReconnectAttempts = 2*60*5; // 5 min - RemotePortRelayer(GenericRelayServer *parent, QTcpSocket *clientSocket); - void tryRemoteConnect(); -signals: - void didConnect(GenericRelayServer *serv); -private: - QTimer m_remoteConnectTimer; -}; - -class RelayServer: public QObject -{ - Q_OBJECT -public: - RelayServer(IosTool *parent); - ~RelayServer(); - bool startServer(); - void stopServer(); - quint16 serverPort(); - IosTool *iosTool(); - - void handleNewRelayConnection(); - void removeRelayConnection(Relayer *relayer); -protected: - virtual void newRelayConnection() = 0; - - QTcpServer m_ipv4Server; - QTcpServer m_ipv6Server; - quint16 m_port = 0; - QList m_connections; -}; - -class SingleRelayServer: public RelayServer -{ - Q_OBJECT -public: - SingleRelayServer(IosTool *parent, int serverFileDescriptor); -protected: - void newRelayConnection() override; -private: - int m_serverFileDescriptor; -}; - -class GenericRelayServer: public RelayServer { - Q_OBJECT -public: - GenericRelayServer(IosTool *parent, int remotePort, - Ios::DeviceSession *deviceSession); -protected: - void newRelayConnection() override; -private: - int m_remotePort; - Ios::DeviceSession *m_deviceSession; - friend class RemotePortRelayer; -}; - -class GdbRunner: public QObject -{ - Q_OBJECT -public: - GdbRunner(IosTool *iosTool, int gdbFd); - void stop(int phase); - void run(); -signals: - void finished(); -private: - IosTool *m_iosTool; - int m_gdbFd; -}; - -class IosTool: public QObject -{ - Q_OBJECT -public: - IosTool(QObject *parent = 0); - virtual ~IosTool(); - void run(const QStringList &args); - void doExit(int errorCode = 0); - void writeMsg(const char *msg); - void writeMsg(const QString &msg); - void stopXml(int errorCode); - void writeTextInElement(const QString &output); - void stopRelayServers(int errorCode = 0); - void writeMaybeBin(const QString &extraMsg, const char *msg, quintptr len); - void errorMsg(const QString &msg); - Q_INVOKABLE void stopGdbRunner(); -private: - void stopGdbRunner2(); - void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress, - const QString &info); - void didTransferApp(const QString &bundlePath, const QString &deviceId, - Ios::IosDeviceManager::OpStatus status); - void didStartApp(const QString &bundlePath, const QString &deviceId, - Ios::IosDeviceManager::OpStatus status, int gdbFd, - Ios::DeviceSession *deviceSession); - void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info); - void appOutput(const QString &output); - void readStdin(); - - QRecursiveMutex m_xmlMutex; - int maxProgress; - int opLeft; - bool debug; - bool inAppOutput; - bool splitAppOutput; // as QXmlStreamReader reports the text attributes atomically it is better to split - Ios::IosDeviceManager::AppOp appOp; - QFile outFile; - QString m_qmlPort; - QXmlStreamWriter out; - SingleRelayServer *gdbServer; - GenericRelayServer *qmlServer; - GdbRunner *gdbRunner; - friend class GdbRunner; -}; - -Relayer::Relayer(RelayServer *parent, QTcpSocket *clientSocket) : - QObject(parent), m_serverFileDescriptor(0), m_clientSocket(0), m_serverNotifier(0) -{ - setClientSocket(clientSocket); -} - -Relayer::~Relayer() -{ - if (m_serverFileDescriptor > 0) { - ::close(m_serverFileDescriptor); - m_serverFileDescriptor = -1; - if (m_serverNotifier) - delete m_serverNotifier; - } - if (m_clientSocket->isOpen()) - m_clientSocket->close(); - delete m_clientSocket; -} - -void Relayer::setClientSocket(QTcpSocket *clientSocket) -{ - QTC_CHECK(!m_clientSocket); - m_clientSocket = clientSocket; - if (m_clientSocket) { -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - const auto errorOccurred = QOverload::of(&QAbstractSocket::error); -#else - const auto errorOccurred = &QAbstractSocket::errorOccurred; -#endif - connect(m_clientSocket, errorOccurred, this, &Relayer::handleClientHasError); - connect(m_clientSocket, &QAbstractSocket::disconnected, - this, [this](){server()->removeRelayConnection(this);}); - } -} - -bool Relayer::startRelay(int serverFileDescriptor) -{ - QTC_CHECK(!m_serverFileDescriptor); - m_serverFileDescriptor = serverFileDescriptor; - if (!m_clientSocket || m_serverFileDescriptor <= 0) - return false; - fcntl(serverFileDescriptor,F_SETFL, fcntl(serverFileDescriptor, F_GETFL) | O_NONBLOCK); - connect(m_clientSocket, &QIODevice::readyRead, this, &Relayer::handleClientHasData); - m_serverNotifier = new QSocketNotifier(m_serverFileDescriptor, QSocketNotifier::Read, this); - connect(m_serverNotifier, &QSocketNotifier::activated, this, &Relayer::handleSocketHasData); - // no way to check if an error did happen? - if (m_clientSocket->bytesAvailable() > 0) - handleClientHasData(); - return true; -} - -void Relayer::handleSocketHasData(int socket) -{ - m_serverNotifier->setEnabled(false); - char buf[255]; - while (true) { - qptrdiff rRead = read(socket, &buf, sizeof(buf)-1); - if (rRead == -1) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) { - m_serverNotifier->setEnabled(true); - return; - } - iosTool()->errorMsg(qt_error_string(errno)); - close(socket); - iosTool()->stopRelayServers(-1); - return; - } - if (rRead == 0) { - iosTool()->stopRelayServers(0); - return; - } - if (echoRelays) { - iosTool()->writeMaybeBin(QString::fromLatin1("%1 serverReplies:") - .arg((quintptr)(void *)this), buf, rRead); - } - qint64 pos = 0; - while (true) { - qint64 writtenNow = m_clientSocket->write(buf + int(pos), rRead); - if (writtenNow == -1) { - iosTool()->writeMsg(m_clientSocket->errorString()); - iosTool()->stopRelayServers(-1); - return; - } - if (writtenNow < rRead) { - pos += writtenNow; - rRead -= qptrdiff(writtenNow); - } else { - break; - } - } - m_clientSocket->flush(); - } -} - -void Relayer::handleClientHasData() -{ - char buf[255]; - while (true) { - qint64 toRead = m_clientSocket->bytesAvailable(); - if (qint64(sizeof(buf)-1) < toRead) - toRead = sizeof(buf)-1; - qint64 rRead = m_clientSocket->read(buf, toRead); - if (rRead == -1) { - iosTool()->errorMsg(m_clientSocket->errorString()); - iosTool()->stopRelayServers(); - return; - } - if (rRead == 0) { - if (!m_clientSocket->isOpen()) - iosTool()->stopRelayServers(); - return; - } - int pos = 0; - int irep = 0; - if (echoRelays) { - iosTool()->writeMaybeBin(QString::fromLatin1("%1 clientAsks:") - .arg((quintptr)(void *)this), buf, rRead); - } - while (true) { - qptrdiff written = write(m_serverFileDescriptor, buf + pos, rRead); - if (written == -1) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) { - if (++irep > 10) { - sleep(1); - irep = 0; - } - continue; - } - iosTool()->errorMsg(qt_error_string(errno)); - iosTool()->stopRelayServers(); - return; - } - if (written == 0) { - iosTool()->stopRelayServers(); - return; - } - if (written < rRead) { - pos += written; - rRead -= written; - } else { - break; - } - } - } -} - -void Relayer::handleClientHasError(QAbstractSocket::SocketError error) -{ - iosTool()->errorMsg(tr("iOS Debugging connection to creator failed with error %1").arg(error)); - server()->removeRelayConnection(this); -} - -IosTool *Relayer::iosTool() -{ - return (server() ? server()->iosTool() : 0); -} - -RelayServer *Relayer::server() -{ - return qobject_cast(parent()); -} - -RemotePortRelayer::RemotePortRelayer(GenericRelayServer *parent, QTcpSocket *clientSocket) : - Relayer(parent, clientSocket) -{ - m_remoteConnectTimer.setSingleShot(true); - m_remoteConnectTimer.setInterval(reconnectMsecDelay); - connect(&m_remoteConnectTimer, &QTimer::timeout, this, &RemotePortRelayer::tryRemoteConnect); -} - -void RemotePortRelayer::tryRemoteConnect() -{ - iosTool()->errorMsg(QLatin1String("tryRemoteConnect")); - if (m_serverFileDescriptor > 0) - return; - Ios::ServiceSocket ss; - GenericRelayServer *grServer = qobject_cast(server()); - if (!grServer) - return; - if (grServer->m_deviceSession->connectToPort(grServer->m_remotePort, &ss)) { - if (ss > 0) { - iosTool()->errorMsg(QString::fromLatin1("tryRemoteConnect *succeeded* on remote port %1") - .arg(grServer->m_remotePort)); - startRelay(ss); - emit didConnect(grServer); - return; - } - } - iosTool()->errorMsg(QString::fromLatin1("tryRemoteConnect *failed* on remote port %1") - .arg(grServer->m_remotePort)); - m_remoteConnectTimer.start(); -} - -RelayServer::RelayServer(IosTool *parent): - QObject(parent) -{ } - -RelayServer::~RelayServer() -{ - stopServer(); -} - -bool RelayServer::startServer() -{ - QTC_CHECK(!m_ipv4Server.isListening()); - QTC_CHECK(!m_ipv6Server.isListening()); - - connect(&m_ipv4Server, &QTcpServer::newConnection, - this, &RelayServer::handleNewRelayConnection); - connect(&m_ipv6Server, &QTcpServer::newConnection, - this, &RelayServer::handleNewRelayConnection); - - m_port = 0; - if (m_ipv4Server.listen(QHostAddress(QHostAddress::LocalHost), 0)) - m_port = m_ipv4Server.serverPort(); - if (m_ipv6Server.listen(QHostAddress(QHostAddress::LocalHostIPv6), m_port)) - m_port = m_ipv6Server.serverPort(); - - return m_port > 0; -} - -void RelayServer::stopServer() -{ - foreach (Relayer *connection, m_connections) - delete connection; - if (m_ipv4Server.isListening()) - m_ipv4Server.close(); - if (m_ipv6Server.isListening()) - m_ipv6Server.close(); -} - -quint16 RelayServer::serverPort() -{ - return m_port; -} - -IosTool *RelayServer::iosTool() -{ - return qobject_cast(parent()); -} - -void RelayServer::handleNewRelayConnection() -{ - iosTool()->errorMsg(QLatin1String("handleNewRelayConnection")); - newRelayConnection(); -} - -void RelayServer::removeRelayConnection(Relayer *relayer) -{ - m_connections.removeAll(relayer); - relayer->deleteLater(); -} - -SingleRelayServer::SingleRelayServer(IosTool *parent, - int serverFileDescriptor) : - RelayServer(parent) -{ - m_serverFileDescriptor = serverFileDescriptor; - if (m_serverFileDescriptor > 0) - fcntl(m_serverFileDescriptor, F_SETFL, fcntl(m_serverFileDescriptor, F_GETFL, 0) | O_NONBLOCK); -} - -void SingleRelayServer::newRelayConnection() -{ - QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() - ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); - if (m_connections.size() > 0) { - delete clientSocket; - return; - } - if (clientSocket) { - Relayer *newConnection = new Relayer(this, clientSocket); - m_connections.append(newConnection); - newConnection->startRelay(m_serverFileDescriptor); - } -} - -GenericRelayServer::GenericRelayServer(IosTool *parent, int remotePort, - Ios::DeviceSession *deviceSession) : - RelayServer(parent), - m_remotePort(remotePort), - m_deviceSession(deviceSession) -{ - parent->errorMsg(QLatin1String("created qml server")); -} - - -void GenericRelayServer::newRelayConnection() -{ - QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() - ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); - if (clientSocket) { - iosTool()->errorMsg(QString::fromLatin1("setting up relayer for new connection")); - RemotePortRelayer *newConnection = new RemotePortRelayer(this, clientSocket); - m_connections.append(newConnection); - newConnection->tryRemoteConnect(); - } -} - -IosTool::IosTool(QObject *parent): - QObject(parent), - maxProgress(0), - opLeft(0), - debug(false), - inAppOutput(false), - splitAppOutput(true), - appOp(Ios::IosDeviceManager::None), - outFile(), - out(&outFile), - gdbServer(0), - qmlServer(0) -{ - outFile.open(stdout, QIODevice::WriteOnly, QFileDevice::DontCloseHandle); - out.setAutoFormatting(true); -} - -IosTool::~IosTool() -{ -} - -void IosTool::run(const QStringList &args) -{ - Ios::IosDeviceManager *manager = Ios::IosDeviceManager::instance(); - QString deviceId; - QString bundlePath; - bool deviceInfo = false; - bool printHelp = false; - int timeout = 1000; - QStringList extraArgs; - - out.writeStartDocument(); - out.writeStartElement(QLatin1String("query_result")); - for (int iarg = 1; iarg < args.size(); ++iarg) { - const QString &arg = args[iarg]; - if (arg == QLatin1String("-i") || arg == QLatin1String("--id")) { - if (++iarg == args.size()) { - writeMsg(QStringLiteral("missing device id value after ") + arg); - printHelp = true; - } - deviceId = args.value(iarg); - } else if (arg == QLatin1String("-b") || arg == QLatin1String("--bundle")) { - if (++iarg == args.size()) { - writeMsg(QStringLiteral("missing bundle path after ") + arg); - printHelp = true; - } - bundlePath = args.value(iarg); - } else if (arg == QLatin1String("--install")) { - appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Install); - } else if (arg == QLatin1String("--run")) { - appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run); - } else if (arg == QLatin1String("--noninteractive")) { - // ignored for compatibility - } else if (arg == QLatin1String("-v") || arg == QLatin1String("--verbose")) { - echoRelays = true; - } else if (arg == QLatin1String("-d") || arg == QLatin1String("--debug")) { - appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run); - debug = true; - } else if (arg == QLatin1String("--device-info")) { - deviceInfo = true; - } else if (arg == QLatin1String("-t") || arg == QLatin1String("--timeout")) { - if (++iarg == args.size()) { - writeMsg(QStringLiteral("missing timeout value after ") + arg); - printHelp = true; - } - bool ok = false; - int tOut = args.value(iarg).toInt(&ok); - if (ok && tOut > 0) { - timeout = tOut; - } else { - writeMsg("timeout value should be an integer"); - printHelp = true; - } - } else if (arg == QLatin1String("-a") || arg == QLatin1String("--args")) { - extraArgs = args.mid(iarg + 1, args.size() - iarg - 1); - iarg = args.size(); - } else if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { - printHelp = true; - } else { - writeMsg(QString::fromLatin1("unexpected argument \"%1\"").arg(arg)); - } - } - if (printHelp) { - out.writeStartElement(QLatin1String("msg")); - out.writeCharacters(QLatin1String("iostool [--id ] [--bundle ] [--install] [--run] [--debug]\n")); - out.writeCharacters(QLatin1String(" [--device-info] [--timeout ] [--verbose]\n")); // to do pass in env as stub does - out.writeCharacters(QLatin1String(" [--args ]")); - out.writeEndElement(); - doExit(-1); - return; - } - outFile.flush(); - connect(manager,&Ios::IosDeviceManager::isTransferringApp, this, &IosTool::isTransferringApp); - connect(manager,&Ios::IosDeviceManager::didTransferApp, this, &IosTool::didTransferApp); - connect(manager,&Ios::IosDeviceManager::didStartApp, this, &IosTool::didStartApp); - connect(manager,&Ios::IosDeviceManager::deviceInfo, this, &IosTool::deviceInfo); - connect(manager,&Ios::IosDeviceManager::appOutput, this, &IosTool::appOutput); - connect(manager,&Ios::IosDeviceManager::errorMsg, this, &IosTool::errorMsg); - manager->watchDevices(); - const QRegularExpression qmlPortRe(QLatin1String("-qmljsdebugger=port:([0-9]+)")); - for (const QString &arg : extraArgs) { - const QRegularExpressionMatch match = qmlPortRe.match(arg); - if (match.hasMatch()) { - bool ok; - int qmlPort = match.captured(1).toInt(&ok); - if (ok && qmlPort > 0 && qmlPort <= 0xFFFF) - m_qmlPort = match.captured(1); - break; - } - } - if (deviceInfo) { - if (!bundlePath.isEmpty()) - writeMsg("--device-info overrides --bundle"); - ++opLeft; - manager->requestDeviceInfo(deviceId, timeout); - } else if (!bundlePath.isEmpty()) { - switch (appOp) { - case Ios::IosDeviceManager::None: - break; - case Ios::IosDeviceManager::Install: - case Ios::IosDeviceManager::Run: - ++opLeft; - break; - case Ios::IosDeviceManager::InstallAndRun: - opLeft += 2; - break; - } - maxProgress = 200; - manager->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout); - } - if (opLeft == 0) - doExit(0); -} - -void IosTool::stopXml(int errorCode) -{ - QMutexLocker l(&m_xmlMutex); - out.writeEmptyElement(QLatin1String("exit")); - out.writeAttribute(QLatin1String("code"), QString::number(errorCode)); - out.writeEndElement(); // result element (hopefully) - out.writeEndDocument(); - outFile.flush(); -} - -void IosTool::doExit(int errorCode) -{ - stopXml(errorCode); - QCoreApplication::exit(errorCode); // sometime does not really exit - exit(errorCode); -} - -void IosTool::isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress, - const QString &info) -{ - Q_UNUSED(bundlePath) - Q_UNUSED(deviceId) - QMutexLocker l(&m_xmlMutex); - out.writeStartElement(QLatin1String("status")); - out.writeAttribute(QLatin1String("progress"), QString::number(progress)); - out.writeAttribute(QLatin1String("max_progress"), QString::number(maxProgress)); - out.writeCharacters(info); - out.writeEndElement(); - outFile.flush(); -} - -void IosTool::didTransferApp(const QString &bundlePath, const QString &deviceId, - Ios::IosDeviceManager::OpStatus status) -{ - Q_UNUSED(bundlePath) - Q_UNUSED(deviceId) - { - QMutexLocker l(&m_xmlMutex); - if (status == Ios::IosDeviceManager::Success) { - out.writeStartElement(QLatin1String("status")); - out.writeAttribute(QLatin1String("progress"), QString::number(maxProgress)); - out.writeAttribute(QLatin1String("max_progress"), QString::number(maxProgress)); - out.writeCharacters(QLatin1String("App Transferred")); - out.writeEndElement(); - } - out.writeEmptyElement(QLatin1String("app_transfer")); - out.writeAttribute(QLatin1String("status"), - (status == Ios::IosDeviceManager::Success) ? - QLatin1String("SUCCESS") : - QLatin1String("FAILURE")); - //out.writeCharacters(QString()); // trigger a complete closing of the empty element - outFile.flush(); - } - if (status != Ios::IosDeviceManager::Success || --opLeft == 0) - doExit((status == Ios::IosDeviceManager::Success) ? 0 : -1); -} - -void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, - Ios::IosDeviceManager::OpStatus status, int gdbFd, - Ios::DeviceSession *deviceSession) -{ - Q_UNUSED(bundlePath) - Q_UNUSED(deviceId) - { - QMutexLocker l(&m_xmlMutex); - out.writeEmptyElement(QLatin1String("app_started")); - out.writeAttribute(QLatin1String("status"), - (status == Ios::IosDeviceManager::Success) ? - QLatin1String("SUCCESS") : - QLatin1String("FAILURE")); - //out.writeCharacters(QString()); // trigger a complete closing of the empty element - outFile.flush(); - } - if (status != Ios::IosDeviceManager::Success || appOp == Ios::IosDeviceManager::Install) { - doExit(); - return; - } - if (gdbFd <= 0) { - writeMsg("no gdb connection"); - doExit(-2); - return; - } - if (appOp != Ios::IosDeviceManager::InstallAndRun && appOp != Ios::IosDeviceManager::Run) { - writeMsg(QString::fromLatin1("unexpected appOp value %1").arg(appOp)); - doExit(-3); - return; - } - if (deviceSession) { - int qmlPort = deviceSession->qmljsDebugPort(); - if (qmlPort) { - qmlServer = new GenericRelayServer(this, qmlPort, deviceSession); - qmlServer->startServer(); - } - } - if (debug) { - gdbServer = new SingleRelayServer(this, gdbFd); - if (!gdbServer->startServer()) { - doExit(-4); - return; - } - } - { - QMutexLocker l(&m_xmlMutex); - out.writeStartElement(QLatin1String("server_ports")); - out.writeAttribute(QLatin1String("gdb_server"), - QString::number(gdbServer ? gdbServer->serverPort() : -1)); - out.writeAttribute(QLatin1String("qml_server"), - QString::number(qmlServer ? qmlServer->serverPort() : -1)); - out.writeEndElement(); - outFile.flush(); - } - if (!debug) { - gdbRunner = new GdbRunner(this, gdbFd); - // we should not stop the event handling of the main thread - // all output moves to the new thread (other option would be to signal it back) - QThread *gdbProcessThread = new QThread(); - gdbRunner->moveToThread(gdbProcessThread); - QObject::connect(gdbProcessThread, &QThread::started, gdbRunner, &GdbRunner::run); - QObject::connect(gdbRunner, &GdbRunner::finished, gdbProcessThread, &QThread::quit); - QObject::connect(gdbProcessThread, &QThread::finished, - gdbProcessThread, &QObject::deleteLater); - gdbProcessThread->start(); - - new std::thread([this]() -> void { readStdin();}); - } -} - -void IosTool::writeMsg(const char *msg) -{ - writeMsg(QString::fromLatin1(msg)); -} - -void IosTool::writeMsg(const QString &msg) -{ - QMutexLocker l(&m_xmlMutex); - out.writeStartElement(QLatin1String("msg")); - writeTextInElement(msg); - out.writeCharacters(QLatin1String("\n")); - out.writeEndElement(); - outFile.flush(); -} - -void IosTool::writeMaybeBin(const QString &extraMsg, const char *msg, quintptr len) -{ - char *buf2 = new char[len * 2 + 4]; - buf2[0] = '['; - const char toHex[] = "0123456789abcdef"; - for (quintptr i = 0; i < len; ++i) { - buf2[2 * i + 1] = toHex[(0xF & (msg[i] >> 4))]; - buf2[2 * i + 2] = toHex[(0xF & msg[i])]; - } - buf2[2 * len + 1] = ']'; - buf2[2 * len + 2] = ' '; - buf2[2 * len + 3] = 0; - - QMutexLocker l(&m_xmlMutex); - out.writeStartElement(QLatin1String("msg")); - out.writeCharacters(extraMsg); - out.writeCharacters(QLatin1String(buf2)); - for (quintptr i = 0; i < len; ++i) { - if (msg[i] < 0x20 || msg[i] > 0x7f) - buf2[i] = '_'; - else - buf2[i] = msg[i]; - } - buf2[len] = 0; - out.writeCharacters(QLatin1String(buf2)); - delete[] buf2; - out.writeEndElement(); - outFile.flush(); -} - -void IosTool::deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &devInfo) -{ - Q_UNUSED(deviceId) - { - QMutexLocker l(&m_xmlMutex); - out.writeTextElement(QLatin1String("device_id"), deviceId); - out.writeStartElement(QLatin1String("device_info")); - for (auto i = devInfo.cbegin(); i != devInfo.cend(); ++i) { - out.writeStartElement(QLatin1String("item")); - out.writeTextElement(QLatin1String("key"), i.key()); - out.writeTextElement(QLatin1String("value"), i.value()); - out.writeEndElement(); - } - out.writeEndElement(); - outFile.flush(); - } - doExit(); -} - -void IosTool::writeTextInElement(const QString &output) -{ - const QRegularExpression controlCharRe(QLatin1String("[\x01-\x08]|\x0B|\x0C|[\x0E-\x1F]|\\0000")); - int pos = 0; - int oldPos = 0; - - while ((pos = output.indexOf(controlCharRe, pos)) != -1) { - QMutexLocker l(&m_xmlMutex); - out.writeCharacters(output.mid(oldPos, pos - oldPos)); - out.writeEmptyElement(QLatin1String("control_char")); - out.writeAttribute(QLatin1String("code"), QString::number(output.at(pos).toLatin1())); - pos += 1; - oldPos = pos; - } - out.writeCharacters(output.mid(oldPos, output.length() - oldPos)); -} - -void IosTool::appOutput(const QString &output) -{ - QMutexLocker l(&m_xmlMutex); - if (!inAppOutput) - out.writeStartElement(QLatin1String("app_output")); - writeTextInElement(output); - if (!inAppOutput) - out.writeEndElement(); - outFile.flush(); -} - -void IosTool::readStdin() -{ - int c = getchar(); - if (c == 'k') { - QMetaObject::invokeMethod(this, "stopGdbRunner"); - errorMsg(QLatin1String("iostool: Killing inferior.\n")); - } else if (c != EOF) { - errorMsg(QLatin1String("iostool: Unexpected character in stdin, stop listening.\n")); - } -} - -void IosTool::errorMsg(const QString &msg) -{ - writeMsg(msg); -} - -void IosTool::stopGdbRunner() -{ - if (gdbRunner) { - gdbRunner->stop(0); - QTimer::singleShot(100, this, &IosTool::stopGdbRunner2); - } -} - -void IosTool::stopGdbRunner2() -{ - if (gdbRunner) - gdbRunner->stop(1); -} - -void IosTool::stopRelayServers(int errorCode) -{ - if (echoRelays) - writeMsg("gdbServerStops"); - if (qmlServer) - qmlServer->stopServer(); - if (gdbServer) - gdbServer->stopServer(); - doExit(errorCode); -} +#include int main(int argc, char *argv[]) { @@ -916,44 +45,9 @@ int main(int argc, char *argv[]) } QGuiApplication a(qtArgc, &qtArg); - IosTool tool; + Ios::IosTool tool; tool.run(args); int res = a.exec(); exit(res); } -GdbRunner::GdbRunner(IosTool *iosTool, int gdbFd) : - QObject(0), m_iosTool(iosTool), m_gdbFd(gdbFd) -{ -} - -void GdbRunner::run() -{ - { - QMutexLocker l(&m_iosTool->m_xmlMutex); - if (!m_iosTool->splitAppOutput) { - m_iosTool->out.writeStartElement(QLatin1String("app_output")); - m_iosTool->inAppOutput = true; - } - m_iosTool->outFile.flush(); - } - Ios::IosDeviceManager::instance()->processGdbServer(m_gdbFd); - { - QMutexLocker l(&m_iosTool->m_xmlMutex); - if (!m_iosTool->splitAppOutput) { - m_iosTool->inAppOutput = false; - m_iosTool->out.writeEndElement(); - } - m_iosTool->outFile.flush(); - } - close(m_gdbFd); - m_iosTool->doExit(); - emit finished(); -} - -void GdbRunner::stop(int phase) -{ - Ios::IosDeviceManager::instance()->stopGdbServer(m_gdbFd, phase); -} - -#include "main.moc" diff --git a/src/tools/iostool/mobiledevicelib.cpp b/src/tools/iostool/mobiledevicelib.cpp new file mode 100644 index 00000000000..87ce4fd8d34 --- /dev/null +++ b/src/tools/iostool/mobiledevicelib.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "mobiledevicelib.h" + +#include + + +#ifdef MOBILE_DEV_DIRECT_LINK +#include "MobileDevice.h" +#endif + +namespace Ios { +MobileDeviceLib::MobileDeviceLib() +{ +} + +bool MobileDeviceLib::load() +{ +#ifdef MOBILE_DEV_DIRECT_LINK + m_AMDSetLogLevel = &AMDSetLogLevel; + m_AMDeviceNotificationSubscribe = &AMDeviceNotificationSubscribe; + //m_AMDeviceNotificationUnsubscribe = &AMDeviceNotificationUnsubscribe; + m_AMDeviceCopyValue = &AMDeviceCopyValue; + m_AMDeviceGetConnectionID = &AMDeviceGetConnectionID; + m_AMDeviceCopyDeviceIdentifier = &AMDeviceCopyDeviceIdentifier; + m_AMDeviceConnect = &AMDeviceConnect; + //m_AMDevicePair = &AMDevicePair; + m_AMDeviceIsPaired = &AMDeviceIsPaired; + m_AMDeviceValidatePairing = &AMDeviceValidatePairing; + m_AMDeviceStartSession = &AMDeviceStartSession; + m_AMDeviceStopSession = &AMDeviceStopSession; + m_AMDeviceDisconnect = &AMDeviceDisconnect; + m_AMDeviceMountImage = &AMDeviceMountImage; + m_AMDeviceStartService = &AMDeviceStartService; + m_AMDeviceTransferApplication = &AMDeviceTransferApplication; + m_AMDeviceInstallApplication = &AMDeviceInstallApplication; + //m_AMDeviceUninstallApplication = &AMDeviceUninstallApplication; + //m_AMDeviceLookupApplications = &AMDeviceLookupApplications; + m_USBMuxConnectByPort = &USBMuxConnectByPort; +#else + QLibrary *libAppleFSCompression = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/AppleFSCompression.framework/AppleFSCompression")); + if (!libAppleFSCompression->load()) + addError("MobileDevice dependency AppleFSCompression failed to load"); + deps << libAppleFSCompression; + QLibrary *libBom = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/Bom.framework/Bom")); + if (!libBom->load()) + addError("MobileDevice dependency Bom failed to load"); + deps << libBom; + lib.setFileName(QLatin1String("/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice")); + if (!lib.load()) + return false; + m_AMDSetLogLevel = reinterpret_cast(lib.resolve("AMDSetLogLevel")); + if (m_AMDSetLogLevel == 0) + addError("MobileDeviceLib does not define AMDSetLogLevel"); + m_AMDeviceNotificationSubscribe = reinterpret_cast(lib.resolve("AMDeviceNotificationSubscribe")); + if (m_AMDeviceNotificationSubscribe == 0) + addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe"); + m_AMDeviceNotificationUnsubscribe = reinterpret_cast(lib.resolve("AMDeviceNotificationUnsubscribe")); + if (m_AMDeviceNotificationUnsubscribe == 0) + addError("MobileDeviceLib does not define AMDeviceNotificationUnsubscribe"); + m_AMDeviceGetInterfaceType = reinterpret_cast(lib.resolve("AMDeviceGetInterfaceType")); + if (m_AMDeviceGetInterfaceType == 0) + addError("MobileDeviceLib does not define AMDeviceGetInterfaceType"); + m_AMDeviceCopyValue = reinterpret_cast(lib.resolve("AMDeviceCopyValue")); + if (m_AMDSetLogLevel == 0) + addError("MobileDeviceLib does not define AMDSetLogLevel"); + m_AMDeviceGetConnectionID = reinterpret_cast(lib.resolve("AMDeviceGetConnectionID")); + if (m_AMDeviceGetConnectionID == 0) + addError("MobileDeviceLib does not define AMDeviceGetConnectionID"); + m_AMDeviceCopyDeviceIdentifier = reinterpret_cast(lib.resolve("AMDeviceCopyDeviceIdentifier")); + if (m_AMDeviceCopyDeviceIdentifier == 0) + addError("MobileDeviceLib does not define AMDeviceCopyDeviceIdentifier"); + m_AMDeviceConnect = reinterpret_cast(lib.resolve("AMDeviceConnect")); + if (m_AMDeviceConnect == 0) + addError("MobileDeviceLib does not define AMDeviceConnect"); + m_AMDevicePair = reinterpret_cast(lib.resolve("AMDevicePair")); + if (m_AMDevicePair == 0) + addError("MobileDeviceLib does not define AMDevicePair"); + m_AMDeviceIsPaired = reinterpret_cast(lib.resolve("AMDeviceIsPaired")); + if (m_AMDeviceIsPaired == 0) + addError("MobileDeviceLib does not define AMDeviceIsPaired"); + m_AMDeviceValidatePairing = reinterpret_cast(lib.resolve("AMDeviceValidatePairing")); + if (m_AMDeviceValidatePairing == 0) + addError("MobileDeviceLib does not define AMDeviceValidatePairing"); + m_AMDeviceStartSession = reinterpret_cast(lib.resolve("AMDeviceStartSession")); + if (m_AMDeviceStartSession == 0) + addError("MobileDeviceLib does not define AMDeviceStartSession"); + m_AMDeviceStopSession = reinterpret_cast(lib.resolve("AMDeviceStopSession")); + if (m_AMDeviceStopSession == 0) + addError("MobileDeviceLib does not define AMDeviceStopSession"); + m_AMDeviceDisconnect = reinterpret_cast(lib.resolve("AMDeviceDisconnect")); + if (m_AMDeviceDisconnect == 0) + addError("MobileDeviceLib does not define AMDeviceDisconnect"); + m_AMDeviceMountImage = reinterpret_cast(lib.resolve("AMDeviceMountImage")); + if (m_AMDeviceMountImage == 0) + addError("MobileDeviceLib does not define AMDeviceMountImage"); + m_AMDeviceSecureStartService = reinterpret_cast(lib.resolve("AMDeviceSecureStartService")); + if (m_AMDeviceSecureStartService == 0) + addError("MobileDeviceLib does not define AMDeviceSecureStartService"); + m_AMDeviceSecureTransferPath = reinterpret_cast(lib.resolve("AMDeviceSecureTransferPath")); + if (m_AMDeviceSecureTransferPath == 0) + addError("MobileDeviceLib does not define AMDeviceSecureTransferPath"); + m_AMDeviceSecureInstallApplication = reinterpret_cast(lib.resolve("AMDeviceSecureInstallApplication")); + if (m_AMDeviceSecureInstallApplication == 0) + addError("MobileDeviceLib does not define AMDeviceSecureInstallApplication"); + m_AMDServiceConnectionGetSocket = reinterpret_cast(lib.resolve("AMDServiceConnectionGetSocket")); + if (m_AMDServiceConnectionGetSocket == nullptr) + addError("MobileDeviceLib does not define AMDServiceConnectionGetSocket"); + m_AMDeviceUninstallApplication = reinterpret_cast(lib.resolve("AMDeviceUninstallApplication")); + if (m_AMDeviceUninstallApplication == 0) + addError("MobileDeviceLib does not define AMDeviceUninstallApplication"); + m_AMDeviceLookupApplications = reinterpret_cast(lib.resolve("AMDeviceLookupApplications")); + if (m_AMDeviceLookupApplications == 0) + addError("MobileDeviceLib does not define AMDeviceLookupApplications"); + m_AMDErrorString = reinterpret_cast(lib.resolve("AMDErrorString")); + if (m_AMDErrorString == 0) + addError("MobileDeviceLib does not define AMDErrorString"); + m_MISCopyErrorStringForErrorCode = reinterpret_cast(lib.resolve("MISCopyErrorStringForErrorCode")); + if (m_MISCopyErrorStringForErrorCode == 0) + addError("MobileDeviceLib does not define MISCopyErrorStringForErrorCode"); + m_USBMuxConnectByPort = reinterpret_cast(lib.resolve("USBMuxConnectByPort")); + if (m_USBMuxConnectByPort == 0) + addError("MobileDeviceLib does not define USBMuxConnectByPort"); +#endif + return true; +} + +bool MobileDeviceLib::isLoaded() +{ + return lib.isLoaded(); +} + +QStringList MobileDeviceLib::errors() +{ + return m_errors; +} + +void MobileDeviceLib::setLogLevel(int i) +{ + if (m_AMDSetLogLevel) + m_AMDSetLogLevel(i); +} + +am_res_t MobileDeviceLib::deviceNotificationSubscribe(AMDeviceNotificationCallback callback, + unsigned int v1, unsigned int v2, void *callbackArgs, + const AMDeviceNotification **handle) +{ + if (m_AMDeviceNotificationSubscribe) + return m_AMDeviceNotificationSubscribe(callback,v1,v2,callbackArgs,handle); + return -1; +} + +am_res_t MobileDeviceLib::deviceNotificationUnsubscribe(void *handle) +{ + if (m_AMDeviceNotificationUnsubscribe) + return m_AMDeviceNotificationUnsubscribe(handle); + return -1; +} + +int MobileDeviceLib::deviceGetInterfaceType(AMDeviceRef device) +{ + if (m_AMDeviceGetInterfaceType) + return m_AMDeviceGetInterfaceType(device); + return DeviceInterfaceType::UNKNOWN; +} + +CFPropertyListRef MobileDeviceLib::deviceCopyValue(AMDeviceRef device,CFStringRef group,CFStringRef key) +{ + if (m_AMDeviceCopyValue) + return m_AMDeviceCopyValue(device, group, key); + return 0; +} + +unsigned int MobileDeviceLib::deviceGetConnectionID(AMDeviceRef device) +{ + if (m_AMDeviceGetConnectionID) + return m_AMDeviceGetConnectionID(device); + return -1; +} + +CFStringRef MobileDeviceLib::deviceCopyDeviceIdentifier(AMDeviceRef device) +{ + if (m_AMDeviceCopyDeviceIdentifier) + return m_AMDeviceCopyDeviceIdentifier(device); + return 0; +} + +am_res_t MobileDeviceLib::deviceConnect(AMDeviceRef device) +{ + if (m_AMDeviceConnect) + return m_AMDeviceConnect(device); + return -1; +} + +am_res_t MobileDeviceLib::devicePair(AMDeviceRef device) +{ + if (m_AMDevicePair) + return m_AMDevicePair(device); + return -1; +} + +am_res_t MobileDeviceLib::deviceIsPaired(AMDeviceRef device) +{ + if (m_AMDeviceIsPaired) + return m_AMDeviceIsPaired(device); + return -1; +} + +am_res_t MobileDeviceLib::deviceValidatePairing(AMDeviceRef device) +{ + if (m_AMDeviceValidatePairing) + return m_AMDeviceValidatePairing(device); + return -1; +} + +am_res_t MobileDeviceLib::deviceStartSession(AMDeviceRef device) +{ + if (m_AMDeviceStartSession) + return m_AMDeviceStartSession(device); + return -1; +} + +am_res_t MobileDeviceLib::deviceStopSession(AMDeviceRef device) +{ + if (m_AMDeviceStopSession) + return m_AMDeviceStopSession(device); + return -1; +} + +am_res_t MobileDeviceLib::deviceDisconnect(AMDeviceRef device) +{ + if (m_AMDeviceDisconnect) + return m_AMDeviceDisconnect(device); + return -1; +} + +am_res_t MobileDeviceLib::deviceMountImage(AMDeviceRef device, CFStringRef imagePath, + CFDictionaryRef options, + AMDeviceMountImageCallback callback, + void *callbackExtraArgs) +{ + if (m_AMDeviceMountImage) + return m_AMDeviceMountImage(device, imagePath, options, callback, callbackExtraArgs); + return -1; +} + +am_res_t MobileDeviceLib::deviceUninstallApplication(int serviceFd, CFStringRef bundleId, + CFDictionaryRef options, + AMDeviceInstallApplicationCallback callback, + void *callbackExtraArgs) +{ + if (m_AMDeviceUninstallApplication) + return m_AMDeviceUninstallApplication(serviceFd, bundleId, options, callback, callbackExtraArgs); + return -1; +} + +am_res_t MobileDeviceLib::deviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, + CFDictionaryRef *res) +{ + if (m_AMDeviceLookupApplications) + return m_AMDeviceLookupApplications(device, options, res); + return -1; +} + +char *MobileDeviceLib::errorString(am_res_t error) +{ + if (m_AMDErrorString) + return m_AMDErrorString(error); + return 0; +} + +CFStringRef MobileDeviceLib::misErrorStringForErrorCode(am_res_t error) +{ + if (m_MISCopyErrorStringForErrorCode) + return m_MISCopyErrorStringForErrorCode(error); + return NULL; +} + +am_res_t MobileDeviceLib::connectByPort(unsigned int connectionId, int port, ServiceSocket *resFd) +{ + if (m_USBMuxConnectByPort) + return m_USBMuxConnectByPort(connectionId, port, resFd); + return -1; +} + +void MobileDeviceLib::addError(const QString &msg) +{ + qDebug() << "MobileDeviceLib ERROR:" << msg; + m_errors << QLatin1String("MobileDeviceLib ERROR:") << msg; +} + +void MobileDeviceLib::addError(const char *msg) +{ + addError(QLatin1String(msg)); +} + +am_res_t MobileDeviceLib::deviceSecureStartService(AMDeviceRef device, CFStringRef serviceName, ServiceConnRef *fdRef) +{ + if (m_AMDeviceSecureStartService) + return m_AMDeviceSecureStartService(device, serviceName, nullptr, fdRef); + return 0; +} + +int MobileDeviceLib::deviceSecureTransferApplicationPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef dict, AMDeviceSecureInstallApplicationCallback callback, int args) +{ + int returnCode = -1; + if (m_AMDeviceSecureTransferPath) + returnCode = m_AMDeviceSecureTransferPath(zero, device, url, dict, callback, args); + return returnCode; +} + +int MobileDeviceLib::deviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, AMDeviceSecureInstallApplicationCallback callback, int arg) +{ + int returnCode = -1; + if (m_AMDeviceSecureInstallApplication) { + returnCode = m_AMDeviceSecureInstallApplication(zero, device, url, options, callback, arg); + } + return returnCode; +} + +int MobileDeviceLib::deviceConnectionGetSocket(ServiceConnRef ref) { + int fd = 0; + if (m_AMDServiceConnectionGetSocket) + fd = m_AMDServiceConnectionGetSocket(ref); + return fd; +} +} // IOS diff --git a/src/tools/iostool/mobiledevicelib.h b/src/tools/iostool/mobiledevicelib.h new file mode 100644 index 00000000000..7fdac229038 --- /dev/null +++ b/src/tools/iostool/mobiledevicelib.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "iostooltypes.h" + +#include +#include + + +/* // annoying to import, do without +#include +*/ +/* standard calling convention under Win32 is __stdcall */ +/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ +/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ +#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) +#define MDEV_API __stdcall +#else +#define MDEV_API +#endif + +namespace Ios { +extern "C" { + +#ifndef MOBILE_DEV_DIRECT_LINK +class AMDeviceNotification; +typedef const AMDeviceNotification *AMDeviceNotificationRef; +class AMDevice; +struct AMDeviceNotificationCallbackInfo { + AMDevice *_device; + unsigned int _message; + AMDeviceNotification *_subscription; +}; + +enum DeviceInterfaceType { + UNKNOWN = 0, + WIRED, + WIFI +}; + +typedef unsigned int am_res_t; // mach_error_t + +typedef void (MDEV_API *AMDeviceNotificationCallback)(AMDeviceNotificationCallbackInfo *, void *); +typedef am_res_t (MDEV_API *AMDeviceInstallApplicationCallback)(CFDictionaryRef, void *); +typedef mach_error_t (MDEV_API *AMDeviceSecureInstallApplicationCallback)(CFDictionaryRef, int); + + +typedef AMDevice *AMDeviceRef; +#endif +typedef void (MDEV_API *AMDeviceMountImageCallback)(CFDictionaryRef, int); + +typedef void (MDEV_API *AMDSetLogLevelPtr)(int); +typedef am_res_t (MDEV_API *AMDeviceNotificationSubscribePtr)(AMDeviceNotificationCallback, + unsigned int, unsigned int, void *, + const AMDeviceNotification **); +typedef am_res_t (MDEV_API *AMDeviceNotificationUnsubscribePtr)(void *); +typedef int (MDEV_API* AMDeviceGetInterfaceTypePtr)(AMDeviceRef device); +typedef CFPropertyListRef (MDEV_API *AMDeviceCopyValuePtr)(AMDeviceRef,CFStringRef,CFStringRef); +typedef unsigned int (MDEV_API *AMDeviceGetConnectionIDPtr)(AMDeviceRef); +typedef CFStringRef (MDEV_API *AMDeviceCopyDeviceIdentifierPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceConnectPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDevicePairPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceIsPairedPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceValidatePairingPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceStartSessionPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceStopSessionPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceDisconnectPtr)(AMDeviceRef); +typedef am_res_t (MDEV_API *AMDeviceMountImagePtr)(AMDeviceRef, CFStringRef, CFDictionaryRef, + AMDeviceMountImageCallback, void *); +typedef am_res_t (MDEV_API *AMDeviceUninstallApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef, + AMDeviceInstallApplicationCallback, + void*); +typedef am_res_t (MDEV_API *AMDeviceLookupApplicationsPtr)(AMDeviceRef, CFDictionaryRef, CFDictionaryRef *); +typedef char * (MDEV_API *AMDErrorStringPtr)(am_res_t); +typedef CFStringRef (MDEV_API *MISCopyErrorStringForErrorCodePtr)(am_res_t); +typedef am_res_t (MDEV_API *USBMuxConnectByPortPtr)(unsigned int, int, ServiceSocket*); +// secure Api's +typedef am_res_t (MDEV_API *AMDeviceSecureStartServicePtr)(AMDeviceRef, CFStringRef, unsigned int *, ServiceConnRef *); +typedef int (MDEV_API *AMDeviceSecureTransferPathPtr)(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int); +typedef int (MDEV_API *AMDeviceSecureInstallApplicationPtr)(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int); +typedef int (MDEV_API *AMDServiceConnectionGetSocketPtr)(ServiceConnRef); +} + +class MobileDeviceLib { +public : + MobileDeviceLib(); + + bool load(); + bool isLoaded(); + QStringList errors(); +// + + void setLogLevel(int i) ; + am_res_t deviceNotificationSubscribe(AMDeviceNotificationCallback callback, + unsigned int v1, unsigned int v2, void *v3, + const AMDeviceNotification **handle); + am_res_t deviceNotificationUnsubscribe(void *handle); + int deviceGetInterfaceType(AMDeviceRef device); + CFPropertyListRef deviceCopyValue(AMDeviceRef,CFStringRef,CFStringRef); + unsigned int deviceGetConnectionID(AMDeviceRef); + CFStringRef deviceCopyDeviceIdentifier(AMDeviceRef); + am_res_t deviceConnect(AMDeviceRef); + am_res_t devicePair(AMDeviceRef); + am_res_t deviceIsPaired(AMDeviceRef); + am_res_t deviceValidatePairing(AMDeviceRef); + am_res_t deviceStartSession(AMDeviceRef); + am_res_t deviceStopSession(AMDeviceRef); + am_res_t deviceDisconnect(AMDeviceRef); + am_res_t deviceMountImage(AMDeviceRef, CFStringRef, CFDictionaryRef, + AMDeviceMountImageCallback, void *); + am_res_t deviceUninstallApplication(int, CFStringRef, CFDictionaryRef, + AMDeviceInstallApplicationCallback, + void*); + am_res_t deviceLookupApplications(AMDeviceRef, CFDictionaryRef, CFDictionaryRef *); + char *errorString(am_res_t error); + CFStringRef misErrorStringForErrorCode(am_res_t error); + am_res_t connectByPort(unsigned int connectionId, int port, ServiceSocket *resFd); + + void addError(const QString &msg); + void addError(const char *msg); + + // Secure API's + am_res_t deviceSecureStartService(AMDeviceRef, CFStringRef, ServiceConnRef *); + int deviceConnectionGetSocket(ServiceConnRef); + int deviceSecureTransferApplicationPath(int, AMDeviceRef, CFURLRef, + CFDictionaryRef, AMDeviceSecureInstallApplicationCallback callback, int); + int deviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, + CFDictionaryRef options, AMDeviceSecureInstallApplicationCallback callback, int arg); + + QStringList m_errors; + +private: + QLibrary lib; + QList deps; + AMDSetLogLevelPtr m_AMDSetLogLevel; + AMDeviceNotificationSubscribePtr m_AMDeviceNotificationSubscribe; + AMDeviceNotificationUnsubscribePtr m_AMDeviceNotificationUnsubscribe; + AMDeviceGetInterfaceTypePtr m_AMDeviceGetInterfaceType; + AMDeviceCopyValuePtr m_AMDeviceCopyValue; + AMDeviceGetConnectionIDPtr m_AMDeviceGetConnectionID; + AMDeviceCopyDeviceIdentifierPtr m_AMDeviceCopyDeviceIdentifier; + AMDeviceConnectPtr m_AMDeviceConnect; + AMDevicePairPtr m_AMDevicePair; + AMDeviceIsPairedPtr m_AMDeviceIsPaired; + AMDeviceValidatePairingPtr m_AMDeviceValidatePairing; + AMDeviceStartSessionPtr m_AMDeviceStartSession; + AMDeviceStopSessionPtr m_AMDeviceStopSession; + AMDeviceDisconnectPtr m_AMDeviceDisconnect; + AMDeviceMountImagePtr m_AMDeviceMountImage; + AMDeviceSecureStartServicePtr m_AMDeviceSecureStartService; + AMDeviceSecureTransferPathPtr m_AMDeviceSecureTransferPath; + AMDeviceSecureInstallApplicationPtr m_AMDeviceSecureInstallApplication; + AMDServiceConnectionGetSocketPtr m_AMDServiceConnectionGetSocket; + AMDeviceUninstallApplicationPtr m_AMDeviceUninstallApplication; + AMDeviceLookupApplicationsPtr m_AMDeviceLookupApplications; + AMDErrorStringPtr m_AMDErrorString; + MISCopyErrorStringForErrorCodePtr m_MISCopyErrorStringForErrorCode; + USBMuxConnectByPortPtr m_USBMuxConnectByPort; +}; +} diff --git a/src/tools/iostool/relayserver.cpp b/src/tools/iostool/relayserver.cpp new file mode 100644 index 00000000000..f952560752a --- /dev/null +++ b/src/tools/iostool/relayserver.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "relayserver.h" + +#include "iostool.h" + +#ifdef Q_OS_UNIX +#include +#include +#endif + +// avoid utils dependency +#define QTC_CHECK(cond) if (cond) {} else { qWarning() << "assert failed " << #cond << " " \ + << __FILE__ << ":" << __LINE__; } do {} while (0) + +namespace Ios { +Relayer::Relayer(RelayServer *parent, QTcpSocket *clientSocket) : + QObject(parent), m_serverFileDescriptor(0), m_clientSocket(0), m_serverNotifier(0) +{ + setClientSocket(clientSocket); +} + +Relayer::~Relayer() +{ + if (m_serverFileDescriptor > 0) { + ::close(m_serverFileDescriptor); + m_serverFileDescriptor = -1; + if (m_serverNotifier) + delete m_serverNotifier; + } + if (m_clientSocket->isOpen()) + m_clientSocket->close(); + delete m_clientSocket; +} + +void Relayer::setClientSocket(QTcpSocket *clientSocket) +{ + QTC_CHECK(!m_clientSocket); + m_clientSocket = clientSocket; + if (m_clientSocket) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + const auto errorOccurred = QOverload::of(&QAbstractSocket::error); +#else + const auto errorOccurred = &QAbstractSocket::errorOccurred; +#endif + connect(m_clientSocket, errorOccurred, this, &Relayer::handleClientHasError); + connect(m_clientSocket, &QAbstractSocket::disconnected, + this, [this](){server()->removeRelayConnection(this);}); + } +} + +bool Relayer::startRelay(int serverFileDescriptor) +{ + QTC_CHECK(!m_serverFileDescriptor); + m_serverFileDescriptor = serverFileDescriptor; + if (!m_clientSocket || m_serverFileDescriptor <= 0) + return false; + fcntl(serverFileDescriptor,F_SETFL, fcntl(serverFileDescriptor, F_GETFL) | O_NONBLOCK); + connect(m_clientSocket, &QIODevice::readyRead, this, &Relayer::handleClientHasData); + m_serverNotifier = new QSocketNotifier(m_serverFileDescriptor, QSocketNotifier::Read, this); + connect(m_serverNotifier, &QSocketNotifier::activated, this, &Relayer::handleSocketHasData); + // no way to check if an error did happen? + if (m_clientSocket->bytesAvailable() > 0) + handleClientHasData(); + return true; +} + +void Relayer::handleSocketHasData(int socket) +{ + m_serverNotifier->setEnabled(false); + char buf[255]; + while (true) { + qptrdiff rRead = read(socket, &buf, sizeof(buf)-1); + if (rRead == -1) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) { + m_serverNotifier->setEnabled(true); + return; + } + iosTool()->errorMsg(qt_error_string(errno)); + close(socket); + iosTool()->stopRelayServers(-1); + return; + } + if (rRead == 0) { + iosTool()->stopRelayServers(0); + return; + } + if (iosTool()->echoRelays()) { + iosTool()->writeMaybeBin(QString::fromLatin1("%1 serverReplies:") + .arg((quintptr)(void *)this), buf, rRead); + } + qint64 pos = 0; + while (true) { + qint64 writtenNow = m_clientSocket->write(buf + int(pos), rRead); + if (writtenNow == -1) { + iosTool()->writeMsg(m_clientSocket->errorString()); + iosTool()->stopRelayServers(-1); + return; + } + if (writtenNow < rRead) { + pos += writtenNow; + rRead -= qptrdiff(writtenNow); + } else { + break; + } + } + m_clientSocket->flush(); + } +} + +void Relayer::handleClientHasData() +{ + char buf[255]; + while (true) { + qint64 toRead = m_clientSocket->bytesAvailable(); + if (qint64(sizeof(buf)-1) < toRead) + toRead = sizeof(buf)-1; + qint64 rRead = m_clientSocket->read(buf, toRead); + if (rRead == -1) { + iosTool()->errorMsg(m_clientSocket->errorString()); + iosTool()->stopRelayServers(); + return; + } + if (rRead == 0) { + if (!m_clientSocket->isOpen()) + iosTool()->stopRelayServers(); + return; + } + int pos = 0; + int irep = 0; + if (iosTool()->echoRelays()) { + iosTool()->writeMaybeBin(QString::fromLatin1("%1 clientAsks:") + .arg((quintptr)(void *)this), buf, rRead); + } + while (true) { + qptrdiff written = write(m_serverFileDescriptor, buf + pos, rRead); + if (written == -1) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) { + if (++irep > 10) { + sleep(1); + irep = 0; + } + continue; + } + iosTool()->errorMsg(qt_error_string(errno)); + iosTool()->stopRelayServers(); + return; + } + if (written == 0) { + iosTool()->stopRelayServers(); + return; + } + if (written < rRead) { + pos += written; + rRead -= written; + } else { + break; + } + } + } +} + +void Relayer::handleClientHasError(QAbstractSocket::SocketError error) +{ + iosTool()->errorMsg(tr("iOS Debugging connection to creator failed with error %1").arg(error)); + server()->removeRelayConnection(this); +} + +IosTool *Relayer::iosTool() const +{ + return (server() ? server()->iosTool() : 0); +} + +RelayServer *Relayer::server() const +{ + return qobject_cast(parent()); +} + +RemotePortRelayer::RemotePortRelayer(GenericRelayServer *parent, QTcpSocket *clientSocket) : + Relayer(parent, clientSocket) +{ + m_remoteConnectTimer.setSingleShot(true); + m_remoteConnectTimer.setInterval(reconnectMsecDelay); + connect(&m_remoteConnectTimer, &QTimer::timeout, this, &RemotePortRelayer::tryRemoteConnect); +} + +void RemotePortRelayer::tryRemoteConnect() +{ + iosTool()->errorMsg(QLatin1String("tryRemoteConnect")); + if (m_serverFileDescriptor > 0) + return; + ServiceSocket ss; + GenericRelayServer *grServer = qobject_cast(server()); + if (!grServer) + return; + if (grServer->m_deviceSession->connectToPort(grServer->m_remotePort, &ss)) { + if (ss > 0) { + iosTool()->errorMsg(QString::fromLatin1("tryRemoteConnect *succeeded* on remote port %1") + .arg(grServer->m_remotePort)); + startRelay(ss); + emit didConnect(grServer); + return; + } + } + iosTool()->errorMsg(QString::fromLatin1("tryRemoteConnect *failed* on remote port %1") + .arg(grServer->m_remotePort)); + m_remoteConnectTimer.start(); +} + +RelayServer::RelayServer(IosTool *parent): + QObject(parent) +{ } + +RelayServer::~RelayServer() +{ + stopServer(); +} + +bool RelayServer::startServer() +{ + QTC_CHECK(!m_ipv4Server.isListening()); + QTC_CHECK(!m_ipv6Server.isListening()); + + connect(&m_ipv4Server, &QTcpServer::newConnection, + this, &RelayServer::handleNewRelayConnection); + connect(&m_ipv6Server, &QTcpServer::newConnection, + this, &RelayServer::handleNewRelayConnection); + + m_port = 0; + if (m_ipv4Server.listen(QHostAddress(QHostAddress::LocalHost), 0)) + m_port = m_ipv4Server.serverPort(); + if (m_ipv6Server.listen(QHostAddress(QHostAddress::LocalHostIPv6), m_port)) + m_port = m_ipv6Server.serverPort(); + + return m_port > 0; +} + +void RelayServer::stopServer() +{ + foreach (Relayer *connection, m_connections) + delete connection; + if (m_ipv4Server.isListening()) + m_ipv4Server.close(); + if (m_ipv6Server.isListening()) + m_ipv6Server.close(); +} + +quint16 RelayServer::serverPort() const +{ + return m_port; +} + +IosTool *RelayServer::iosTool() const +{ + return qobject_cast(parent()); +} + +void RelayServer::handleNewRelayConnection() +{ + iosTool()->errorMsg(QLatin1String("handleNewRelayConnection")); + newRelayConnection(); +} + +void RelayServer::removeRelayConnection(Relayer *relayer) +{ + m_connections.removeAll(relayer); + relayer->deleteLater(); +} + +SingleRelayServer::SingleRelayServer(IosTool *parent, + int serverFileDescriptor) : + RelayServer(parent) +{ + m_serverFileDescriptor = serverFileDescriptor; + if (m_serverFileDescriptor > 0) + fcntl(m_serverFileDescriptor, F_SETFL, fcntl(m_serverFileDescriptor, F_GETFL, 0) | O_NONBLOCK); +} + +void SingleRelayServer::newRelayConnection() +{ + QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() + ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); + if (m_connections.size() > 0) { + delete clientSocket; + return; + } + if (clientSocket) { + Relayer *newConnection = new Relayer(this, clientSocket); + m_connections.append(newConnection); + newConnection->startRelay(m_serverFileDescriptor); + } +} + +GenericRelayServer::GenericRelayServer(IosTool *parent, int remotePort, + Ios::DeviceSession *deviceSession) : + RelayServer(parent), + m_remotePort(remotePort), + m_deviceSession(deviceSession) +{ + parent->errorMsg(QLatin1String("created qml server")); +} + + +void GenericRelayServer::newRelayConnection() +{ + QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() + ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); + if (clientSocket) { + iosTool()->errorMsg(QString::fromLatin1("setting up relayer for new connection")); + RemotePortRelayer *newConnection = new RemotePortRelayer(this, clientSocket); + m_connections.append(newConnection); + newConnection->tryRemoteConnect(); + } +} +} diff --git a/src/tools/iostool/relayserver.h b/src/tools/iostool/relayserver.h new file mode 100644 index 00000000000..d41932f47d2 --- /dev/null +++ b/src/tools/iostool/relayserver.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "iostooltypes.h" + +#include + +#include +#include +#include +#include + +namespace Ios { +class DeviceSession; +class IosTool; +class RelayServer; +class GenericRelayServer; + +class Relayer: public QObject +{ + Q_OBJECT + +public: + Relayer(RelayServer *parent, QTcpSocket *clientSocket); + ~Relayer(); + void setClientSocket(QTcpSocket *clientSocket); + bool startRelay(int serverFileDescriptor); + + void handleSocketHasData(int socket); + void handleClientHasData(); + void handleClientHasError(QAbstractSocket::SocketError error); + +protected: + IosTool *iosTool() const; + RelayServer *server() const; + ServiceSocket m_serverFileDescriptor; + QTcpSocket *m_clientSocket; + QSocketNotifier *m_serverNotifier; +}; + +class RemotePortRelayer: public Relayer +{ + Q_OBJECT + +public: + static const int reconnectMsecDelay = 500; + static const int maxReconnectAttempts = 2*60*5; // 5 min + RemotePortRelayer(GenericRelayServer *parent, QTcpSocket *clientSocket); + void tryRemoteConnect(); + +signals: + void didConnect(GenericRelayServer *serv); + +private: + QTimer m_remoteConnectTimer; +}; + +class RelayServer: public QObject +{ + Q_OBJECT + +public: + RelayServer(IosTool *parent); + ~RelayServer(); + bool startServer(); + void stopServer(); + quint16 serverPort() const; + IosTool *iosTool() const; + + void handleNewRelayConnection(); + void removeRelayConnection(Relayer *relayer); + +protected: + virtual void newRelayConnection() = 0; + + QTcpServer m_ipv4Server; + QTcpServer m_ipv6Server; + quint16 m_port = 0; + QList m_connections; +}; + +class SingleRelayServer: public RelayServer +{ + Q_OBJECT + +public: + SingleRelayServer(IosTool *parent, int serverFileDescriptor); + +protected: + void newRelayConnection() override; + +private: + int m_serverFileDescriptor; +}; + +class GenericRelayServer: public RelayServer +{ + Q_OBJECT + +public: + GenericRelayServer(IosTool *parent, int remotePort, + Ios::DeviceSession *deviceSession); + +protected: + void newRelayConnection() override; + +private: + int m_remotePort; + DeviceSession *m_deviceSession; + friend class RemotePortRelayer; +}; +}