// Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qdbdevice.h" #include "qdbconstants.h" #include "qdbtr.h" #include "qdbutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; using namespace RemoteLinux; using namespace Utils; namespace Qdb::Internal { class QdbProcessImpl : public SshProcessInterface { public: QdbProcessImpl(const IDevice::ConstPtr &device) : SshProcessInterface(device) {} ~QdbProcessImpl() { killIfRunning(); } private: void handleSendControlSignal(ControlSignal controlSignal) final { QTC_ASSERT(controlSignal != ControlSignal::Interrupt, return); QTC_ASSERT(controlSignal != ControlSignal::KickOff, return); if (m_setup.m_commandLine.executable().path() == Constants::AppcontrollerFilepath) { runInShell({Constants::AppcontrollerFilepath, {"--stop"}}); return; } SshProcessInterface::handleSendControlSignal(controlSignal); } }; class DeviceApplicationObserver : public QObject { public: DeviceApplicationObserver(const IDevice::ConstPtr &device, const CommandLine &command) { connect(&m_appRunner, &Process::done, this, &DeviceApplicationObserver::handleDone); QTC_ASSERT(device, return); m_deviceName = device->displayName(); m_appRunner.setCommand(command); m_appRunner.start(); showMessage(Tr::tr("Starting command \"%1\" on device \"%2\".") .arg(command.toUserOutput(), m_deviceName)); } private: void handleDone() { const QString stdOut = m_appRunner.cleanedStdOut(); const QString stdErr = m_appRunner.cleanedStdErr(); // FIXME: Needed in a post-adb world? // adb does not forward exit codes and all stderr goes to stdout. const bool failure = m_appRunner.result() != ProcessResult::FinishedWithSuccess || stdOut.contains("fail") || stdOut.contains("error") || stdOut.contains("not found"); if (failure) { QString errorString; if (!m_appRunner.errorString().isEmpty()) { errorString = Tr::tr("Command failed on device \"%1\": %2") .arg(m_deviceName, m_appRunner.errorString()); } else { errorString = Tr::tr("Command failed on device \"%1\".").arg(m_deviceName); } showMessage(errorString, true); if (!stdOut.isEmpty()) showMessage(Tr::tr("stdout was: \"%1\".").arg(stdOut)); if (!stdErr.isEmpty()) showMessage(Tr::tr("stderr was: \"%1\".").arg(stdErr)); } else { showMessage(Tr::tr("Commands on device \"%1\" finished successfully.") .arg(m_deviceName)); } deleteLater(); } Process m_appRunner; QString m_deviceName; }; // QdbDevice QdbDevice::QdbDevice() { setDisplayType(Tr::tr("Boot to Qt Device")); setType(Constants::QdbLinuxOsType); setMachineType(IDevice::Hardware); setExtraData(ProjectExplorer::Constants::SUPPORTS_RSYNC, true); setExtraData(ProjectExplorer::Constants::SUPPORTS_SFTP, true); sourceProfile.setDefaultValue(true); addDeviceAction({Tr::tr("Reboot Device"), [](const IDevice::Ptr &device) { (void) new DeviceApplicationObserver(device, CommandLine{device->filePath("reboot")}); }}); addDeviceAction({Tr::tr("Restore Default App"), [](const IDevice::Ptr &device) { (void) new DeviceApplicationObserver(device, {device->filePath("appcontroller"), {"--remove-default"}}); }}); } ProjectExplorer::IDeviceWidget *QdbDevice::createWidget() { ProjectExplorer::IDeviceWidget *w = RemoteLinux::LinuxDevice::createWidget(); return w; } ProcessInterface *QdbDevice::createProcessInterface() const { return new QdbProcessImpl(shared_from_this()); } void QdbDevice::setupDefaultNetworkSettings(const QString &host) { setFreePorts(PortList::fromString("10000-10100")); SshParameters parameters = sshParameters(); parameters.setHost(host); parameters.setUserName("root"); parameters.setPort(22); parameters.setTimeout(10); parameters.setAuthenticationType(SshParameters::AuthenticationTypeAll); parameters.setHostKeyCheckingMode(ProjectExplorer::SshHostKeyCheckingNone); setDefaultSshParameters(parameters); } // QdbDeviceWizard class QdbSettingsPage : public QWizardPage { public: QdbSettingsPage() { setWindowTitle(Tr::tr("WizardPage")); setTitle(Tr::tr("Device Settings")); nameLineEdit = new QLineEdit(this); nameLineEdit->setPlaceholderText(Tr::tr("A short, free-text description.")); addressLineEdit = new QLineEdit(this); addressLineEdit->setPlaceholderText(Tr::tr("Host name or IP address")); auto usbWarningLabel = new QLabel(this); usbWarningLabel->setText(QString("%1

%2

") .arg("Note:") .arg("Do not use this wizard for devices connected via USB.
" "Those will be auto-detected.

" "

The connectivity to the device is tested after finishing.")); auto formLayout = new QFormLayout(this); formLayout->addRow(Tr::tr("Device name:"), nameLineEdit); formLayout->addRow(Tr::tr("Device address:"), addressLineEdit); formLayout->addRow(usbWarningLabel); connect(nameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); connect(addressLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); } QString deviceName() const { return nameLineEdit->text().trimmed(); } QString deviceAddress() const { return addressLineEdit->text().trimmed(); } private: bool isComplete() const final { return !deviceName().isEmpty() && !deviceAddress().isEmpty(); } QLineEdit *nameLineEdit; QLineEdit *addressLineEdit; }; class QdbDeviceWizard : public QWizard { public: QdbDeviceWizard(QWidget *parent) : QWizard(parent) { setWindowTitle(Tr::tr("Boot to Qt Network Device Setup")); settingsPage.setCommitPage(true); enum { SettingsPageId }; setPage(SettingsPageId, &settingsPage); } ProjectExplorer::IDevice::Ptr device() { QdbDevice::Ptr device = QdbDevice::create(); device->setDisplayName(settingsPage.deviceName()); device->setupId(ProjectExplorer::IDevice::ManuallyAdded, Utils::Id()); device->setType(Constants::QdbLinuxOsType); device->setMachineType(ProjectExplorer::IDevice::Hardware); device->setupDefaultNetworkSettings(settingsPage.deviceAddress()); return device; } private: QdbSettingsPage settingsPage; }; // Device factory class QdbLinuxDeviceFactory final : public IDeviceFactory { public: QdbLinuxDeviceFactory() : IDeviceFactory(Constants::QdbLinuxOsType) { setDisplayName(Tr::tr("Boot to Qt Device")); setCombinedIcon(":/qdb/images/qdbdevicesmall.png", ":/qdb/images/qdbdevice.png"); setQuickCreationAllowed(true); setConstructionFunction(&QdbDevice::create); setCreator([] { QdbDeviceWizard wizard(Core::ICore::dialogParent()); if (!creatorTheme()->preferredStyles().isEmpty()) wizard.setWizardStyle(QWizard::ModernStyle); if (wizard.exec() != QDialog::Accepted) return IDevice::Ptr(); return wizard.device(); }); } }; void setupQdbLinuxDevice() { static QdbLinuxDeviceFactory theQdbLinuxSeviceFactory; } } // Qdb::Internal