forked from qt-creator/qt-creator
iOS: Refactor the ios tool
Move classes into logical compilation units Task-number: QTCREATORBUG-24672 Change-Id: Ia7c0866fa16dca0df05fc70418f8142ff74a5d59 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -11,8 +11,13 @@ add_qtc_executable(iostool
|
|||||||
${FWSystemConfiguration}
|
${FWSystemConfiguration}
|
||||||
SOURCES
|
SOURCES
|
||||||
Info.plist
|
Info.plist
|
||||||
main.cpp
|
gdbrunner.cpp gdbrunner.h
|
||||||
iosdevicemanager.cpp iosdevicemanager.h
|
iosdevicemanager.cpp iosdevicemanager.h
|
||||||
|
iostool.cpp iostool.h
|
||||||
|
iostooltypes.h
|
||||||
|
main.cpp
|
||||||
|
mobiledevicelib.cpp mobiledevicelib.h
|
||||||
|
relayserver.cpp relayserver.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if (TARGET iostool)
|
if (TARGET iostool)
|
||||||
|
73
src/tools/iostool/gdbrunner.cpp
Normal file
73
src/tools/iostool/gdbrunner.cpp
Normal file
@@ -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 <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/tools/iostool/gdbrunner.h
Normal file
48
src/tools/iostool/gdbrunner.h
Normal file
@@ -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 <QObject>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
@@ -24,45 +24,27 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "iosdevicemanager.h"
|
#include "iosdevicemanager.h"
|
||||||
#include <QLibrary>
|
|
||||||
|
#include "mobiledevicelib.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QStringList>
|
|
||||||
#include <QString>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QMultiHash>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <mach/error.h>
|
|
||||||
|
|
||||||
/* // annoying to import, do without
|
|
||||||
#include <QtCore/private/qcore_mac_p.h>
|
|
||||||
*/
|
|
||||||
/* 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 <CoreFoundation/CoreFoundation.h>
|
|
||||||
#include <CoreFoundation/CFRunLoop.h>
|
|
||||||
#include <CoreFoundation/CFDictionary.h>
|
|
||||||
#include <CoreFoundation/CFData.h>
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QLibrary>
|
||||||
|
#include <QMultiHash>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
#include <QVersionNumber>
|
#include <QVersionNumber>
|
||||||
|
|
||||||
#ifdef MOBILE_DEV_DIRECT_LINK
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include "MobileDevice.h"
|
|
||||||
#endif
|
#include <mach/error.h>
|
||||||
|
|
||||||
static const bool debugGdbServer = false;
|
static const bool debugGdbServer = false;
|
||||||
static const bool debugAll = false;
|
static const bool debugAll = false;
|
||||||
@@ -82,72 +64,7 @@ enum ADNCI_MSG {
|
|||||||
};
|
};
|
||||||
#endif
|
#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
|
} // anonymous namespace
|
||||||
|
|
||||||
@@ -155,82 +72,9 @@ namespace Ios {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
static const am_res_t kAMDMobileImageMounterImageMountFailed = 0xe8000076;
|
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<QLibrary *> 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)
|
static QString mobileDeviceErrorString(MobileDeviceLib *lib, am_res_t code)
|
||||||
{
|
{
|
||||||
@@ -262,7 +106,7 @@ static QString mobileDeviceErrorString(MobileDeviceLib *lib, am_res_t code)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 toBuildNumber(const QString &versionStr)
|
static qint64 toBuildNumber(const QString &versionStr)
|
||||||
{
|
{
|
||||||
QString buildNumber;
|
QString buildNumber;
|
||||||
const QRegularExpression re("\\s\\((\\X+)\\)");
|
const QRegularExpression re("\\s\\((\\X+)\\)");
|
||||||
@@ -498,7 +342,7 @@ DeviceSession::~DeviceSession()
|
|||||||
namespace {
|
namespace {
|
||||||
// ------- callbacks --------
|
// ------- callbacks --------
|
||||||
|
|
||||||
extern "C" void deviceNotificationCallback(AMDeviceNotificationCallbackInfo *info, void *user)
|
extern "C" void deviceNotificationCallback(Ios::AMDeviceNotificationCallbackInfo *info, void *user)
|
||||||
{
|
{
|
||||||
if (info == 0)
|
if (info == 0)
|
||||||
Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("null info in deviceNotificationCallback"));
|
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)
|
if (debugAll)
|
||||||
qDebug() << "deviceAvailableSessionCallback" << QThread::currentThread();
|
qDebug() << "deviceAvailableSessionCallback" << QThread::currentThread();
|
||||||
@@ -538,7 +382,7 @@ extern "C" void deviceAvailableSessionCallback(QString deviceId, AMDeviceRef dev
|
|||||||
session->internalDeviceAvailableCallback(deviceId, device);
|
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) {
|
if (debugAll) {
|
||||||
qDebug() << "appTransferSessionCallback" << QThread::currentThread();
|
qDebug() << "appTransferSessionCallback" << QThread::currentThread();
|
||||||
@@ -584,7 +428,7 @@ extern "C" mach_error_t appSecureTransferSessionCallback(CFDictionaryRef dict, i
|
|||||||
return 0;
|
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) {
|
if (debugAll) {
|
||||||
qDebug() << "appInstallSessionCallback" << QThread::currentThread();
|
qDebug() << "appInstallSessionCallback" << QThread::currentThread();
|
||||||
@@ -616,9 +460,10 @@ bool IosDeviceManagerPrivate::watchDevices()
|
|||||||
{
|
{
|
||||||
if (!m_lib.load())
|
if (!m_lib.load())
|
||||||
addError(QLatin1String("Error loading MobileDevice.framework"));
|
addError(QLatin1String("Error loading MobileDevice.framework"));
|
||||||
if (!m_lib.errors().isEmpty())
|
if (!m_lib.errors().isEmpty()) {
|
||||||
foreach (const QString &msg, m_lib.errors())
|
foreach (const QString &msg, m_lib.errors())
|
||||||
addError(msg);
|
addError(msg);
|
||||||
|
}
|
||||||
m_lib.setLogLevel(5);
|
m_lib.setLogLevel(5);
|
||||||
am_res_t e = m_lib.deviceNotificationSubscribe(&deviceNotificationCallback, 0, 0,
|
am_res_t e = m_lib.deviceNotificationSubscribe(&deviceNotificationCallback, 0, 0,
|
||||||
0, &m_notification);
|
0, &m_notification);
|
||||||
@@ -1655,318 +1500,7 @@ void DevInfoSession::deviceCallbackReturned()
|
|||||||
|
|
||||||
// ------- MobileDeviceLib implementation --------
|
// ------- 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<AMDSetLogLevelPtr>(lib.resolve("AMDSetLogLevel"));
|
|
||||||
if (m_AMDSetLogLevel == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDSetLogLevel");
|
|
||||||
m_AMDeviceNotificationSubscribe = reinterpret_cast<AMDeviceNotificationSubscribePtr>(lib.resolve("AMDeviceNotificationSubscribe"));
|
|
||||||
if (m_AMDeviceNotificationSubscribe == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe");
|
|
||||||
m_AMDeviceNotificationUnsubscribe = reinterpret_cast<AMDeviceNotificationUnsubscribePtr>(lib.resolve("AMDeviceNotificationUnsubscribe"));
|
|
||||||
if (m_AMDeviceNotificationUnsubscribe == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceNotificationUnsubscribe");
|
|
||||||
m_AMDeviceGetInterfaceType = reinterpret_cast<AMDeviceGetInterfaceTypePtr>(lib.resolve("AMDeviceGetInterfaceType"));
|
|
||||||
if (m_AMDeviceGetInterfaceType == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceGetInterfaceType");
|
|
||||||
m_AMDeviceCopyValue = reinterpret_cast<AMDeviceCopyValuePtr>(lib.resolve("AMDeviceCopyValue"));
|
|
||||||
if (m_AMDSetLogLevel == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDSetLogLevel");
|
|
||||||
m_AMDeviceGetConnectionID = reinterpret_cast<AMDeviceGetConnectionIDPtr>(lib.resolve("AMDeviceGetConnectionID"));
|
|
||||||
if (m_AMDeviceGetConnectionID == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceGetConnectionID");
|
|
||||||
m_AMDeviceCopyDeviceIdentifier = reinterpret_cast<AMDeviceCopyDeviceIdentifierPtr>(lib.resolve("AMDeviceCopyDeviceIdentifier"));
|
|
||||||
if (m_AMDeviceCopyDeviceIdentifier == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceCopyDeviceIdentifier");
|
|
||||||
m_AMDeviceConnect = reinterpret_cast<AMDeviceConnectPtr>(lib.resolve("AMDeviceConnect"));
|
|
||||||
if (m_AMDeviceConnect == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceConnect");
|
|
||||||
m_AMDevicePair = reinterpret_cast<AMDevicePairPtr>(lib.resolve("AMDevicePair"));
|
|
||||||
if (m_AMDevicePair == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDevicePair");
|
|
||||||
m_AMDeviceIsPaired = reinterpret_cast<AMDeviceIsPairedPtr>(lib.resolve("AMDeviceIsPaired"));
|
|
||||||
if (m_AMDeviceIsPaired == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceIsPaired");
|
|
||||||
m_AMDeviceValidatePairing = reinterpret_cast<AMDeviceValidatePairingPtr>(lib.resolve("AMDeviceValidatePairing"));
|
|
||||||
if (m_AMDeviceValidatePairing == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceValidatePairing");
|
|
||||||
m_AMDeviceStartSession = reinterpret_cast<AMDeviceStartSessionPtr>(lib.resolve("AMDeviceStartSession"));
|
|
||||||
if (m_AMDeviceStartSession == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceStartSession");
|
|
||||||
m_AMDeviceStopSession = reinterpret_cast<AMDeviceStopSessionPtr>(lib.resolve("AMDeviceStopSession"));
|
|
||||||
if (m_AMDeviceStopSession == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceStopSession");
|
|
||||||
m_AMDeviceDisconnect = reinterpret_cast<AMDeviceDisconnectPtr>(lib.resolve("AMDeviceDisconnect"));
|
|
||||||
if (m_AMDeviceDisconnect == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceDisconnect");
|
|
||||||
m_AMDeviceMountImage = reinterpret_cast<AMDeviceMountImagePtr>(lib.resolve("AMDeviceMountImage"));
|
|
||||||
if (m_AMDeviceMountImage == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceMountImage");
|
|
||||||
m_AMDeviceSecureStartService = reinterpret_cast<AMDeviceSecureStartServicePtr>(lib.resolve("AMDeviceSecureStartService"));
|
|
||||||
if (m_AMDeviceSecureStartService == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceSecureStartService");
|
|
||||||
m_AMDeviceSecureTransferPath = reinterpret_cast<AMDeviceSecureTransferPathPtr>(lib.resolve("AMDeviceSecureTransferPath"));
|
|
||||||
if (m_AMDeviceSecureTransferPath == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceSecureTransferPath");
|
|
||||||
m_AMDeviceSecureInstallApplication = reinterpret_cast<AMDeviceSecureInstallApplicationPtr>(lib.resolve("AMDeviceSecureInstallApplication"));
|
|
||||||
if (m_AMDeviceSecureInstallApplication == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceSecureInstallApplication");
|
|
||||||
m_AMDServiceConnectionGetSocket = reinterpret_cast<AMDServiceConnectionGetSocketPtr>(lib.resolve("AMDServiceConnectionGetSocket"));
|
|
||||||
if (m_AMDServiceConnectionGetSocket == nullptr)
|
|
||||||
addError("MobileDeviceLib does not define AMDServiceConnectionGetSocket");
|
|
||||||
m_AMDeviceUninstallApplication = reinterpret_cast<AMDeviceUninstallApplicationPtr>(lib.resolve("AMDeviceUninstallApplication"));
|
|
||||||
if (m_AMDeviceUninstallApplication == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceUninstallApplication");
|
|
||||||
m_AMDeviceLookupApplications = reinterpret_cast<AMDeviceLookupApplicationsPtr>(lib.resolve("AMDeviceLookupApplications"));
|
|
||||||
if (m_AMDeviceLookupApplications == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDeviceLookupApplications");
|
|
||||||
m_AMDErrorString = reinterpret_cast<AMDErrorStringPtr>(lib.resolve("AMDErrorString"));
|
|
||||||
if (m_AMDErrorString == 0)
|
|
||||||
addError("MobileDeviceLib does not define AMDErrorString");
|
|
||||||
m_MISCopyErrorStringForErrorCode = reinterpret_cast<MISCopyErrorStringForErrorCodePtr>(lib.resolve("MISCopyErrorStringForErrorCode"));
|
|
||||||
if (m_MISCopyErrorStringForErrorCode == 0)
|
|
||||||
addError("MobileDeviceLib does not define MISCopyErrorStringForErrorCode");
|
|
||||||
m_USBMuxConnectByPort = reinterpret_cast<USBMuxConnectByPortPtr>(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)
|
void CommandSession::internalDeviceAvailableCallback(QString deviceId, AMDeviceRef device)
|
||||||
{
|
{
|
||||||
|
@@ -25,17 +25,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "iostooltypes.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <qglobal.h>
|
|
||||||
|
|
||||||
namespace Ios {
|
namespace Ios {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -43,13 +38,13 @@ class DevInfoSession;
|
|||||||
class IosDeviceManagerPrivate;
|
class IosDeviceManagerPrivate;
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|
||||||
typedef unsigned int ServiceSocket;
|
|
||||||
|
|
||||||
class DeviceSession;
|
class DeviceSession;
|
||||||
|
|
||||||
class IosDeviceManager : public QObject
|
class IosDeviceManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef QMap<QString,QString> Dict;
|
typedef QMap<QString,QString> Dict;
|
||||||
enum OpStatus {
|
enum OpStatus {
|
||||||
@@ -72,6 +67,7 @@ public:
|
|||||||
int processGdbServer(int fd);
|
int processGdbServer(int fd);
|
||||||
void stopGdbServer(int fd, int phase);
|
void stopGdbServer(int fd, int phase);
|
||||||
QStringList errors();
|
QStringList errors();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void deviceAdded(const QString &deviceId);
|
void deviceAdded(const QString &deviceId);
|
||||||
void deviceRemoved(const QString &deviceId);
|
void deviceRemoved(const QString &deviceId);
|
||||||
@@ -85,6 +81,7 @@ signals:
|
|||||||
void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info);
|
void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info);
|
||||||
void appOutput(const QString &output);
|
void appOutput(const QString &output);
|
||||||
void errorMsg(const QString &msg);
|
void errorMsg(const QString &msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Internal::IosDeviceManagerPrivate;
|
friend class Internal::IosDeviceManagerPrivate;
|
||||||
friend class Internal::DevInfoSession;
|
friend class Internal::DevInfoSession;
|
||||||
|
432
src/tools/iostool/iostool.cpp
Normal file
432
src/tools/iostool/iostool.cpp
Normal file
@@ -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 <QCoreApplication>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
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 <device_id>] [--bundle <bundle.app>] [--install] [--run] [--debug]\n"));
|
||||||
|
out.writeCharacters(QLatin1String(" [--device-info] [--timeout <timeout_in_ms>] [--verbose]\n")); // to do pass in env as stub does
|
||||||
|
out.writeCharacters(QLatin1String(" [--args <arguments for the target app>]"));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
88
src/tools/iostool/iostool.h
Normal file
88
src/tools/iostool/iostool.h
Normal file
@@ -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 <QFile>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
@@ -24,10 +24,19 @@ RPATH_BASE = $$DESTDIR
|
|||||||
include(../../rpath.pri)
|
include(../../rpath.pri)
|
||||||
|
|
||||||
SOURCES += main.cpp \
|
SOURCES += main.cpp \
|
||||||
iosdevicemanager.cpp
|
gdbrunner.cpp \
|
||||||
|
iosdevicemanager.cpp \
|
||||||
|
iostool.cpp \
|
||||||
|
mobiledevicelib.cpp \
|
||||||
|
relayserver.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
iosdevicemanager.h
|
gdbrunner.h \
|
||||||
|
iosdevicemanager.h \
|
||||||
|
iostool.h \
|
||||||
|
iostooltypes.h \
|
||||||
|
mobiledevicelib.h \
|
||||||
|
relayserver.h
|
||||||
|
|
||||||
DISTFILES += Info.plist
|
DISTFILES += Info.plist
|
||||||
|
|
||||||
|
@@ -12,9 +12,18 @@ QtcTool {
|
|||||||
|
|
||||||
files: [
|
files: [
|
||||||
"Info.plist",
|
"Info.plist",
|
||||||
"main.cpp",
|
"gdbrunner.cpp",
|
||||||
|
"gdbrunner.h",
|
||||||
"iosdevicemanager.cpp",
|
"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",
|
cpp.frameworks: base.concat(["CoreFoundation", "CoreServices", "IOKit", "Security",
|
||||||
"SystemConfiguration"])
|
"SystemConfiguration"])
|
||||||
|
38
src/tools/iostool/iostooltypes.h
Normal file
38
src/tools/iostool/iostooltypes.h
Normal file
@@ -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 <CoreFoundation/CFDictionary.h>
|
||||||
|
#include <CoreFoundation/CFData.h>
|
||||||
|
#include <CoreFoundation/CFURL.h>
|
||||||
|
|
||||||
|
#include <mach/error.h>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
@@ -23,881 +23,10 @@
|
|||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "iosdevicemanager.h"
|
#include "iostool.h"
|
||||||
|
|
||||||
#include <qglobal.h>
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QTextStream>
|
#include <QStringList>
|
||||||
#include <QDebug>
|
|
||||||
#include <QXmlStreamWriter>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QScopedArrayPointer>
|
|
||||||
#include <QTcpServer>
|
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include <QTcpSocket>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QMutexLocker>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#ifdef Q_OS_UNIX
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#endif
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
// 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<Relayer *> 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<QAbstractSocket::SocketError>::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<RelayServer *>(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<GenericRelayServer *>(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<IosTool *>(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 <device_id>] [--bundle <bundle.app>] [--install] [--run] [--debug]\n"));
|
|
||||||
out.writeCharacters(QLatin1String(" [--device-info] [--timeout <timeout_in_ms>] [--verbose]\n")); // to do pass in env as stub does
|
|
||||||
out.writeCharacters(QLatin1String(" [--args <arguments for the target app>]"));
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@@ -916,44 +45,9 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
QGuiApplication a(qtArgc, &qtArg);
|
QGuiApplication a(qtArgc, &qtArg);
|
||||||
IosTool tool;
|
Ios::IosTool tool;
|
||||||
tool.run(args);
|
tool.run(args);
|
||||||
int res = a.exec();
|
int res = a.exec();
|
||||||
exit(res);
|
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"
|
|
||||||
|
350
src/tools/iostool/mobiledevicelib.cpp
Normal file
350
src/tools/iostool/mobiledevicelib.cpp
Normal file
@@ -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 <QDebug>
|
||||||
|
|
||||||
|
|
||||||
|
#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<AMDSetLogLevelPtr>(lib.resolve("AMDSetLogLevel"));
|
||||||
|
if (m_AMDSetLogLevel == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDSetLogLevel");
|
||||||
|
m_AMDeviceNotificationSubscribe = reinterpret_cast<AMDeviceNotificationSubscribePtr>(lib.resolve("AMDeviceNotificationSubscribe"));
|
||||||
|
if (m_AMDeviceNotificationSubscribe == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe");
|
||||||
|
m_AMDeviceNotificationUnsubscribe = reinterpret_cast<AMDeviceNotificationUnsubscribePtr>(lib.resolve("AMDeviceNotificationUnsubscribe"));
|
||||||
|
if (m_AMDeviceNotificationUnsubscribe == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceNotificationUnsubscribe");
|
||||||
|
m_AMDeviceGetInterfaceType = reinterpret_cast<AMDeviceGetInterfaceTypePtr>(lib.resolve("AMDeviceGetInterfaceType"));
|
||||||
|
if (m_AMDeviceGetInterfaceType == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceGetInterfaceType");
|
||||||
|
m_AMDeviceCopyValue = reinterpret_cast<AMDeviceCopyValuePtr>(lib.resolve("AMDeviceCopyValue"));
|
||||||
|
if (m_AMDSetLogLevel == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDSetLogLevel");
|
||||||
|
m_AMDeviceGetConnectionID = reinterpret_cast<AMDeviceGetConnectionIDPtr>(lib.resolve("AMDeviceGetConnectionID"));
|
||||||
|
if (m_AMDeviceGetConnectionID == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceGetConnectionID");
|
||||||
|
m_AMDeviceCopyDeviceIdentifier = reinterpret_cast<AMDeviceCopyDeviceIdentifierPtr>(lib.resolve("AMDeviceCopyDeviceIdentifier"));
|
||||||
|
if (m_AMDeviceCopyDeviceIdentifier == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceCopyDeviceIdentifier");
|
||||||
|
m_AMDeviceConnect = reinterpret_cast<AMDeviceConnectPtr>(lib.resolve("AMDeviceConnect"));
|
||||||
|
if (m_AMDeviceConnect == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceConnect");
|
||||||
|
m_AMDevicePair = reinterpret_cast<AMDevicePairPtr>(lib.resolve("AMDevicePair"));
|
||||||
|
if (m_AMDevicePair == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDevicePair");
|
||||||
|
m_AMDeviceIsPaired = reinterpret_cast<AMDeviceIsPairedPtr>(lib.resolve("AMDeviceIsPaired"));
|
||||||
|
if (m_AMDeviceIsPaired == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceIsPaired");
|
||||||
|
m_AMDeviceValidatePairing = reinterpret_cast<AMDeviceValidatePairingPtr>(lib.resolve("AMDeviceValidatePairing"));
|
||||||
|
if (m_AMDeviceValidatePairing == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceValidatePairing");
|
||||||
|
m_AMDeviceStartSession = reinterpret_cast<AMDeviceStartSessionPtr>(lib.resolve("AMDeviceStartSession"));
|
||||||
|
if (m_AMDeviceStartSession == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceStartSession");
|
||||||
|
m_AMDeviceStopSession = reinterpret_cast<AMDeviceStopSessionPtr>(lib.resolve("AMDeviceStopSession"));
|
||||||
|
if (m_AMDeviceStopSession == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceStopSession");
|
||||||
|
m_AMDeviceDisconnect = reinterpret_cast<AMDeviceDisconnectPtr>(lib.resolve("AMDeviceDisconnect"));
|
||||||
|
if (m_AMDeviceDisconnect == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceDisconnect");
|
||||||
|
m_AMDeviceMountImage = reinterpret_cast<AMDeviceMountImagePtr>(lib.resolve("AMDeviceMountImage"));
|
||||||
|
if (m_AMDeviceMountImage == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceMountImage");
|
||||||
|
m_AMDeviceSecureStartService = reinterpret_cast<AMDeviceSecureStartServicePtr>(lib.resolve("AMDeviceSecureStartService"));
|
||||||
|
if (m_AMDeviceSecureStartService == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceSecureStartService");
|
||||||
|
m_AMDeviceSecureTransferPath = reinterpret_cast<AMDeviceSecureTransferPathPtr>(lib.resolve("AMDeviceSecureTransferPath"));
|
||||||
|
if (m_AMDeviceSecureTransferPath == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceSecureTransferPath");
|
||||||
|
m_AMDeviceSecureInstallApplication = reinterpret_cast<AMDeviceSecureInstallApplicationPtr>(lib.resolve("AMDeviceSecureInstallApplication"));
|
||||||
|
if (m_AMDeviceSecureInstallApplication == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceSecureInstallApplication");
|
||||||
|
m_AMDServiceConnectionGetSocket = reinterpret_cast<AMDServiceConnectionGetSocketPtr>(lib.resolve("AMDServiceConnectionGetSocket"));
|
||||||
|
if (m_AMDServiceConnectionGetSocket == nullptr)
|
||||||
|
addError("MobileDeviceLib does not define AMDServiceConnectionGetSocket");
|
||||||
|
m_AMDeviceUninstallApplication = reinterpret_cast<AMDeviceUninstallApplicationPtr>(lib.resolve("AMDeviceUninstallApplication"));
|
||||||
|
if (m_AMDeviceUninstallApplication == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceUninstallApplication");
|
||||||
|
m_AMDeviceLookupApplications = reinterpret_cast<AMDeviceLookupApplicationsPtr>(lib.resolve("AMDeviceLookupApplications"));
|
||||||
|
if (m_AMDeviceLookupApplications == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDeviceLookupApplications");
|
||||||
|
m_AMDErrorString = reinterpret_cast<AMDErrorStringPtr>(lib.resolve("AMDErrorString"));
|
||||||
|
if (m_AMDErrorString == 0)
|
||||||
|
addError("MobileDeviceLib does not define AMDErrorString");
|
||||||
|
m_MISCopyErrorStringForErrorCode = reinterpret_cast<MISCopyErrorStringForErrorCodePtr>(lib.resolve("MISCopyErrorStringForErrorCode"));
|
||||||
|
if (m_MISCopyErrorStringForErrorCode == 0)
|
||||||
|
addError("MobileDeviceLib does not define MISCopyErrorStringForErrorCode");
|
||||||
|
m_USBMuxConnectByPort = reinterpret_cast<USBMuxConnectByPortPtr>(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
|
184
src/tools/iostool/mobiledevicelib.h
Normal file
184
src/tools/iostool/mobiledevicelib.h
Normal file
@@ -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 <QLibrary>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
|
||||||
|
/* // annoying to import, do without
|
||||||
|
#include <QtCore/private/qcore_mac_p.h>
|
||||||
|
*/
|
||||||
|
/* 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<QLibrary *> 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;
|
||||||
|
};
|
||||||
|
}
|
342
src/tools/iostool/relayserver.cpp
Normal file
342
src/tools/iostool/relayserver.cpp
Normal file
@@ -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 <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#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<QAbstractSocket::SocketError>::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<RelayServer *>(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<GenericRelayServer *>(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<IosTool *>(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
src/tools/iostool/relayserver.h
Normal file
136
src/tools/iostool/relayserver.h
Normal file
@@ -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 <QObject>
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTcpServer>
|
||||||
|
|
||||||
|
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<Relayer *> 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;
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user