<commit_before>/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "tst_blackboxjava.h"

#include "../shared.h"
#include <tools/hostosinfo.h>
#include <tools/profile.h>
#include <tools/qttools.h>

#include <QtCore/qjsondocument.h>
#include <QtCore/qtemporarydir.h>

using qbs::Internal::HostOsInfo;
using qbs::Profile;

TestBlackboxJava::TestBlackboxJava() : TestBlackboxBase (SRCDIR "/testdata-java", "blackbox-java")
{
}

static QProcessEnvironment processEnvironmentWithCurrentDirectoryInLibraryPath()
{
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    env.insert(HostOsInfo::libraryPathEnvironmentVariable(),
               (QStringList() << env.value(HostOsInfo::libraryPathEnvironmentVariable()) << ".")
               .join(HostOsInfo::pathListSeparator()));
    return env;
}

void TestBlackboxJava::java()
{
#if defined(Q_OS_WIN32) && !defined(Q_OS_WIN64)
    QSKIP("QTBUG-3845");
#endif

    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());

    int status;
    const auto jdkTools = findJdkTools(&status);
    QCOMPARE(status, 0);

    QDir::setCurrent(testDataDir + "/java");

    status = runQbs();
    if (p.value("java.jdkPath").toString().isEmpty()
            && status != 0 && m_qbsStderr.contains("jdkPath")) {
        QSKIP("java.jdkPath not set and automatic detection failed");
    }

    QCOMPARE(status, 0);

    const QStringList classFiles =
            QStringList() << "Jet" << "Ship" << "Vehicles";
    QStringList classFiles1 = QStringList(classFiles) << "io/qt/qbs/HelloWorld" << "NoPackage";
    for (QString &classFile : classFiles1) {
        classFile = relativeProductBuildDir("cc") + "/classes/" + classFile + ".class";
        QVERIFY2(regularFileExists(classFile), qPrintable(classFile));
    }

    for (const QString &classFile : classFiles) {
        const QString filePath = relativeProductBuildDir("jar_file") + "/classes/" + classFile
                + ".class";
        QVERIFY2(regularFileExists(filePath), qPrintable(filePath));
    }
    const QString jarFilePath = relativeProductBuildDir("jar_file") + '/' + "jar_file.jar";
    QVERIFY2(regularFileExists(jarFilePath), qPrintable(jarFilePath));

    // Now check whether we correctly predicted the class file output paths.
    QCOMPARE(runQbs(QbsRunParameters("clean")), 0);
    for (const QString &classFile : qAsConst(classFiles1)) {
        QVERIFY2(!regularFileExists(classFile), qPrintable(classFile));
    }

    // This tests various things: java.manifestClassPath, JNI, etc.
    QDir::setCurrent(relativeBuildDir() + "/install-root");
    QProcess process;
    process.setProcessEnvironment(processEnvironmentWithCurrentDirectoryInLibraryPath());
    process.start(HostOsInfo::appendExecutableSuffix(jdkTools["java"]),
            QStringList() << "-jar" << "jar_file.jar");
    if (process.waitForStarted()) {
        QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
        QVERIFY2(process.exitCode() == 0, process.readAllStandardError().constData());
        const QByteArray stdOut = process.readAllStandardOutput();
        QVERIFY2(stdOut.contains("Driving!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Flying!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Flying (this is a space ship)!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Sailing!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Native code performing complex internal combustion process ("),
                 stdOut.constData());
    }

    process.start("unzip", QStringList() << "-p" << "jar_file.jar");
    if (process.waitForStarted()) {
        QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
        const QByteArray stdOut = process.readAllStandardOutput();
        QVERIFY2(stdOut.contains("Class-Path: random_stuff.jar car_jar.jar"), stdOut.constData());
        QVERIFY2(stdOut.contains("Main-Class: Vehicles"), stdOut.constData());
        QVERIFY2(stdOut.contains("Some-Property: Some-Value"), stdOut.constData());
        QVERIFY2(stdOut.contains("Additional-Property: Additional-Value"), stdOut.constData());
        QVERIFY2(stdOut.contains("Extra-Property: Crazy-Value"), stdOut.constData());
    }
}

static QString dpkgArch(const QString &prefix = QString())
{
    QProcess dpkg;
    dpkg.start("/usr/bin/dpkg", QStringList() << "--print-architecture");
    dpkg.waitForFinished();
    if (dpkg.exitStatus() == QProcess::NormalExit && dpkg.exitCode() == 0)
        return prefix + QString::fromLocal8Bit(dpkg.readAllStandardOutput().trimmed());
    return {};
}

void TestBlackboxJava::javaDependencyTracking()
{
    QFETCH(QString, jdkPath);
    QFETCH(QString, javaVersion);

    QDir::setCurrent(testDataDir + "/java");
    QbsRunParameters rp;
    rp.arguments.push_back("--check-outputs");
    if (!jdkPath.isEmpty())
        rp.arguments << ("modules.java.jdkPath:" + jdkPath);
    if (!javaVersion.isEmpty())
        rp.arguments << ("modules.java.languageVersion:'" + javaVersion + "'");
    rmDirR(relativeBuildDir());
    const bool defaultJdkPossiblyTooOld = jdkPath.isEmpty() && !javaVersion.isEmpty();
    rp.expectFailure = defaultJdkPossiblyTooOld;
    QVERIFY(runQbs(rp) == 0
            || (defaultJdkPossiblyTooOld && m_qbsStderr.contains("invalid source release")));
}

void TestBlackboxJava::javaDependencyTracking_data()
{
    QTest::addColumn<QString>("jdkPath");
    QTest::addColumn<QString>("javaVersion");

    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());

    auto getSpecificJdkVersion = [](const QString &jdkVersion) -> QString {
        if (HostOsInfo::isMacosHost()) {
            QProcess java_home;
            java_home.start("/usr/libexec/java_home", QStringList() << "--version" << jdkVersion);
            java_home.waitForFinished();
            if (java_home.exitStatus() == QProcess::NormalExit && java_home.exitCode() == 0)
                return QString::fromLocal8Bit(java_home.readAllStandardOutput().trimmed());
        } else if (HostOsInfo::isWindowsHost()) {
            QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\"
                               + jdkVersion, QSettings::NativeFormat);
            return settings.value("JavaHome").toString();
        } else {
            QString minorVersion = jdkVersion;
            if (minorVersion.startsWith("1."))
                minorVersion.remove(0, 2);

            const QStringList searchPaths = {
                "/usr/lib/jvm/java-" + minorVersion + "-openjdk" + dpkgArch("-"), // Debian
                "/usr/lib/jvm/java-" + minorVersion + "-openjdk", // Arch
                "/usr/lib/jvm/jre-1." + minorVersion + ".0-openjdk", // Fedora
            };
            for (const QString &searchPath : searchPaths) {
                if (QFile::exists(searchPath + "/bin/javac"))
                    return searchPath;
            }
        }

        return {};
    };

    static const auto knownJdkVersions = QStringList() << "1.6" << "1.7" << "1.8" << "1.9"
                                                       << QString(); // default JDK;
    QStringList seenJdkVersions;
    for (const auto &jdkVersion : knownJdkVersions) {
        QString specificJdkPath = getSpecificJdkVersion(jdkVersion);
        if (jdkVersion.isEmpty() || !specificJdkPath.isEmpty()) {
            const auto jdkPath = jdkVersion.isEmpty() ? jdkVersion : specificJdkPath;

            if (!jdkVersion.isEmpty())
                seenJdkVersions << jdkVersion;

            if (!seenJdkVersions.empty()) {
                const auto javaVersions = QStringList()
                    << knownJdkVersions.mid(0, knownJdkVersions.indexOf(seenJdkVersions.last()) + 1)
                    << QString(); // also test with no explicitly specified source version

                for (const auto &currentJavaVersion : javaVersions) {
                    const QString rowName = (!jdkPath.isEmpty() ? jdkPath : "default JDK")
                            + QStringLiteral(", ")
                            + (!currentJavaVersion.isEmpty()
                               ? ("Java " + currentJavaVersion)
                               : "default Java version");
                    QTest::newRow(rowName.toLatin1().constData())
                            << jdkPath << currentJavaVersion;
                }
            }
        }
    }

    if (seenJdkVersions.empty())
        QSKIP("No JDKs installed");
}

void TestBlackboxJava::javaDependencyTrackingInnerClass()
{
    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());

    QDir::setCurrent(testDataDir + "/java/inner-class");
    QbsRunParameters params;
    int status = runQbs(params);
    if (p.value("java.jdkPath").toString().isEmpty()
            && status != 0 && m_qbsStderr.contains("jdkPath")) {
        QSKIP("java.jdkPath not set and automatic detection failed");
    }
    QCOMPARE(status, 0);
}

QTEST_MAIN(TestBlackboxJava)
<commit_msg>Tests: Fix expected path for jdk<commit_after>/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "tst_blackboxjava.h"

#include "../shared.h"
#include <tools/hostosinfo.h>
#include <tools/profile.h>
#include <tools/qttools.h>

#include <QtCore/qjsondocument.h>
#include <QtCore/qtemporarydir.h>

using qbs::Internal::HostOsInfo;
using qbs::Profile;

TestBlackboxJava::TestBlackboxJava() : TestBlackboxBase (SRCDIR "/testdata-java", "blackbox-java")
{
}

static QProcessEnvironment processEnvironmentWithCurrentDirectoryInLibraryPath()
{
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    env.insert(HostOsInfo::libraryPathEnvironmentVariable(),
               (QStringList() << env.value(HostOsInfo::libraryPathEnvironmentVariable()) << ".")
               .join(HostOsInfo::pathListSeparator()));
    return env;
}

void TestBlackboxJava::java()
{
#if defined(Q_OS_WIN32) && !defined(Q_OS_WIN64)
    QSKIP("QTBUG-3845");
#endif

    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());

    int status;
    const auto jdkTools = findJdkTools(&status);
    QCOMPARE(status, 0);

    QDir::setCurrent(testDataDir + "/java");

    status = runQbs();
    if (p.value("java.jdkPath").toString().isEmpty()
            && status != 0 && m_qbsStderr.contains("jdkPath")) {
        QSKIP("java.jdkPath not set and automatic detection failed");
    }

    QCOMPARE(status, 0);

    const QStringList classFiles =
            QStringList() << "Jet" << "Ship" << "Vehicles";
    QStringList classFiles1 = QStringList(classFiles) << "io/qt/qbs/HelloWorld" << "NoPackage";
    for (QString &classFile : classFiles1) {
        classFile = relativeProductBuildDir("cc") + "/classes/" + classFile + ".class";
        QVERIFY2(regularFileExists(classFile), qPrintable(classFile));
    }

    for (const QString &classFile : classFiles) {
        const QString filePath = relativeProductBuildDir("jar_file") + "/classes/" + classFile
                + ".class";
        QVERIFY2(regularFileExists(filePath), qPrintable(filePath));
    }
    const QString jarFilePath = relativeProductBuildDir("jar_file") + '/' + "jar_file.jar";
    QVERIFY2(regularFileExists(jarFilePath), qPrintable(jarFilePath));

    // Now check whether we correctly predicted the class file output paths.
    QCOMPARE(runQbs(QbsRunParameters("clean")), 0);
    for (const QString &classFile : qAsConst(classFiles1)) {
        QVERIFY2(!regularFileExists(classFile), qPrintable(classFile));
    }

    // This tests various things: java.manifestClassPath, JNI, etc.
    QDir::setCurrent(relativeBuildDir() + "/install-root");
    QProcess process;
    process.setProcessEnvironment(processEnvironmentWithCurrentDirectoryInLibraryPath());
    process.start(HostOsInfo::appendExecutableSuffix(jdkTools["java"]),
            QStringList() << "-jar" << "jar_file.jar");
    if (process.waitForStarted()) {
        QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
        QVERIFY2(process.exitCode() == 0, process.readAllStandardError().constData());
        const QByteArray stdOut = process.readAllStandardOutput();
        QVERIFY2(stdOut.contains("Driving!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Flying!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Flying (this is a space ship)!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Sailing!"), stdOut.constData());
        QVERIFY2(stdOut.contains("Native code performing complex internal combustion process ("),
                 stdOut.constData());
    }

    process.start("unzip", QStringList() << "-p" << "jar_file.jar");
    if (process.waitForStarted()) {
        QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
        const QByteArray stdOut = process.readAllStandardOutput();
        QVERIFY2(stdOut.contains("Class-Path: random_stuff.jar car_jar.jar"), stdOut.constData());
        QVERIFY2(stdOut.contains("Main-Class: Vehicles"), stdOut.constData());
        QVERIFY2(stdOut.contains("Some-Property: Some-Value"), stdOut.constData());
        QVERIFY2(stdOut.contains("Additional-Property: Additional-Value"), stdOut.constData());
        QVERIFY2(stdOut.contains("Extra-Property: Crazy-Value"), stdOut.constData());
    }
}

static QString dpkgArch(const QString &prefix = QString())
{
    QProcess dpkg;
    dpkg.start("/usr/bin/dpkg", QStringList() << "--print-architecture");
    dpkg.waitForFinished();
    if (dpkg.exitStatus() == QProcess::NormalExit && dpkg.exitCode() == 0)
        return prefix + QString::fromLocal8Bit(dpkg.readAllStandardOutput().trimmed());
    return {};
}

void TestBlackboxJava::javaDependencyTracking()
{
    QFETCH(QString, jdkPath);
    QFETCH(QString, javaVersion);

    QDir::setCurrent(testDataDir + "/java");
    QbsRunParameters rp;
    rp.arguments.push_back("--check-outputs");
    if (!jdkPath.isEmpty())
        rp.arguments << ("modules.java.jdkPath:" + jdkPath);
    if (!javaVersion.isEmpty())
        rp.arguments << ("modules.java.languageVersion:'" + javaVersion + "'");
    rmDirR(relativeBuildDir());
    const bool defaultJdkPossiblyTooOld = jdkPath.isEmpty() && !javaVersion.isEmpty();
    rp.expectFailure = defaultJdkPossiblyTooOld;
    QVERIFY(runQbs(rp) == 0
            || (defaultJdkPossiblyTooOld && m_qbsStderr.contains("invalid source release")));
}

void TestBlackboxJava::javaDependencyTracking_data()
{
    QTest::addColumn<QString>("jdkPath");
    QTest::addColumn<QString>("javaVersion");

    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());

    auto getSpecificJdkVersion = [](const QString &jdkVersion) -> QString {
        if (HostOsInfo::isMacosHost()) {
            QProcess java_home;
            java_home.start("/usr/libexec/java_home", QStringList() << "--version" << jdkVersion);
            java_home.waitForFinished();
            if (java_home.exitStatus() == QProcess::NormalExit && java_home.exitCode() == 0)
                return QString::fromLocal8Bit(java_home.readAllStandardOutput().trimmed());
        } else if (HostOsInfo::isWindowsHost()) {
            QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\"
                               + jdkVersion, QSettings::NativeFormat);
            return settings.value("JavaHome").toString();
        } else {
            QString minorVersion = jdkVersion;
            if (minorVersion.startsWith("1."))
                minorVersion.remove(0, 2);

            const QStringList searchPaths = {
                "/usr/lib/jvm/java-" + minorVersion + "-openjdk" + dpkgArch("-"), // Debian
                "/usr/lib/jvm/java-" + minorVersion + "-openjdk", // Arch
                "/usr/lib/jvm/jre-1." + minorVersion + ".0-openjdk", // Fedora
                "/usr/lib64/jvm/java-1." + minorVersion + ".0-openjdk", // OpenSuSE
            };
            for (const QString &searchPath : searchPaths) {
                if (QFile::exists(searchPath + "/bin/javac"))
                    return searchPath;
            }
        }

        return {};
    };

    static const auto knownJdkVersions = QStringList() << "1.6" << "1.7" << "1.8" << "1.9"
                                                       << QString(); // default JDK;
    QStringList seenJdkVersions;
    for (const auto &jdkVersion : knownJdkVersions) {
        QString specificJdkPath = getSpecificJdkVersion(jdkVersion);
        if (jdkVersion.isEmpty() || !specificJdkPath.isEmpty()) {
            const auto jdkPath = jdkVersion.isEmpty() ? jdkVersion : specificJdkPath;

            if (!jdkVersion.isEmpty())
                seenJdkVersions << jdkVersion;

            if (!seenJdkVersions.empty()) {
                const auto javaVersions = QStringList()
                    << knownJdkVersions.mid(0, knownJdkVersions.indexOf(seenJdkVersions.last()) + 1)
                    << QString(); // also test with no explicitly specified source version

                for (const auto &currentJavaVersion : javaVersions) {
                    const QString rowName = (!jdkPath.isEmpty() ? jdkPath : "default JDK")
                            + QStringLiteral(", ")
                            + (!currentJavaVersion.isEmpty()
                               ? ("Java " + currentJavaVersion)
                               : "default Java version");
                    QTest::newRow(rowName.toLatin1().constData())
                            << jdkPath << currentJavaVersion;
                }
            }
        }
    }

    if (seenJdkVersions.empty())
        QSKIP("No JDKs installed");
}

void TestBlackboxJava::javaDependencyTrackingInnerClass()
{
    const SettingsPtr s = settings();
    Profile p(profileName(), s.get());

    QDir::setCurrent(testDataDir + "/java/inner-class");
    QbsRunParameters params;
    int status = runQbs(params);
    if (p.value("java.jdkPath").toString().isEmpty()
            && status != 0 && m_qbsStderr.contains("jdkPath")) {
        QSKIP("java.jdkPath not set and automatic detection failed");
    }
    QCOMPARE(status, 0);
}

QTEST_MAIN(TestBlackboxJava)
<|endoftext|>


<commit_before>#include "mikemap.h"
#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>

//#define TEST_MIKEMAP

#ifdef TEST_MIKEMAP
#include <assert.h>

#define DEFAULT_TEST_VALUE 99
using namespace mikemap;

int main(int argc, char *argv[])
{
  MikeMap mm;
  char input[] = "[100,1,101,0,10,0,9,0,14,0,11,0,15,0]";
  MAP_KEY_TYPE key = DEFAULT_TEST_VALUE;
  MAP_VALUE_TYPE value = DEFAULT_TEST_VALUE;
  int converted = 0;

  assert(mm.get_len() == 0);
  for (int i = 0; i < MAX_MAP_LEN - 4; i++)
    mm.set(i, i);
  assert(mm.get_len() == MAX_MAP_LEN - 4);
  mm.del(1);
  assert(mm.get_len() == MAX_MAP_LEN - 5);
  assert(mm.get(4) == 4);
  assert(mm.has(1) == false);
  assert(mm.has(2) == true);
  mm.get_at(MAX_MAP_LEN + 4, &key, &value);
  assert(key == DEFAULT_TEST_VALUE);
  assert(value == DEFAULT_TEST_VALUE);
  mm.get_at(3, &key, &value);
  assert(key == 4);
  assert(value == 4);
  mm.clear();
  key = DEFAULT_TEST_VALUE;
  value = DEFAULT_TEST_VALUE;
  assert(mm.get_len() == 0);
  for (int i = 0; i < MAX_MAP_LEN - 4; i++)
    mm.set(i, i);
  assert(mm.get_len() == MAX_MAP_LEN - 4);
  mm.del(1);
  assert(mm.get_len() == MAX_MAP_LEN - 5);
  assert(mm.get(4) == 4);
  assert(mm.get(10) == 10);
  assert(mm.has(1) == false);
  assert(mm.has(2) == true);
  mm.get_at(MAX_MAP_LEN + 4, &key, &value);
  assert(key == DEFAULT_TEST_VALUE);
  assert(value == DEFAULT_TEST_VALUE);
  mm.get_at(3, &key, &value);
  assert(key == 4);
  assert(value == 4);
  mm.del(2);
  mm.del(5);
  mm.set(2, 20);
  assert(mm.has(2) == true);
  assert(mm.get_len() == MAX_MAP_LEN - 6);
  mm.get_at(MAX_MAP_LEN - 7, &key, &value);
  assert(key == 2);
  assert(value == 20);
  assert(mm.has(5) == false);
  mm.clear();
  assert(mm.from_string(input, &converted) == 0);
  assert(converted == 14);
  mm.get_at(0, &key, &value);
  printf("k0: %d  v0: %d\n", key, value);
  assert(key == 100);
  assert(value == 1);
  mm.get_at(4, &key, &value);
  assert(key == 14);
  assert(value == 0);
}
#endif

namespace mikemap
{

  MikeMap::MikeMap()
  {
    this->mikemap_len = 0;
  }

  void MikeMap::set(MAP_KEY_TYPE key, MAP_VALUE_TYPE value)
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        mikemap_values[i] = value;
        return;
      }
    }
    if (mikemap_len == MAX_MAP_LEN)
      return;
    mikemap_keys[mikemap_len] = key;
    mikemap_values[mikemap_len] = value;
    mikemap_len++;
  }

  bool MikeMap::has(MAP_KEY_TYPE key) const
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        return true;
      }
    }
    return false;
  }

  MAP_VALUE_TYPE MikeMap::get(MAP_KEY_TYPE key) const
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        return mikemap_values[i];
      }
    }
    return 0;
  }

  void MikeMap::to_string(char *ptr)
  {
    int offset = 0;
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (i > 0)
      {
        ptr[offset] = ',';
        offset++;
      }
      offset += sprintf(&ptr[offset], "%d", mikemap_keys[i]);
      ptr[offset] = ',';
      offset++;
      offset += sprintf(&ptr[offset], "%ld", mikemap_values[i]);
    }
  }

  long int MikeMap::to_int(const char *data, int len)
  {
    long int val = 0;
    while (len)
    {
      long int new_val = ((int)*data - (int)'0');
      val = val * 10 + new_val;
      len--;
      data++;
    }
#ifdef TEST_MIKEMAP
    printf("val: %ld\n", val);
#endif
    return val;
  }

  int MikeMap::from_string(const char *data_start, int *converted)
  {
    const char *data_ptr = data_start;

    this->clear();
    // the map is supposed to start with "["
    if (*data_ptr != '[')
      return MM_ERR_WRONG_START;
    data_ptr++;

    while (1)
    {
      int len_key = 0;
      const char *key_start = data_ptr;
      {
        while (len_key < MAX_SINGLE_DATA_LEN && *data_ptr != ',' && *data_ptr != ']')
        {
          if (*data_ptr < '0' || *data_ptr > '9')
            return MM_ERR_NO_DIGIT;
          len_key++;
          data_ptr++;
        }
        if (len_key == 0 || len_key == MAX_SINGLE_DATA_LEN)
          return MM_ERR_ZERO_MAX_LEN_KEY;
        if (*data_ptr == ']')
          return MM_ERR_MISSING_VAL;
      }
      int k = this->to_int(key_start, len_key);
      (*converted)++;
      data_ptr++;
      int len_val = 0;
      const char *val_start = data_ptr;
      {
        while (len_val < MAX_SINGLE_DATA_LEN && *data_ptr != ',' && *data_ptr != ']')
        {
          if (*data_ptr < '0' || *data_ptr > '9')
            return MM_ERR_NO_DIGIT;
          len_val++;
          data_ptr++;
        }
        if (len_val == 0 || len_val == MAX_SINGLE_DATA_LEN)
          return MM_ERR_ZERO_MAX_LEN_VAL;
      }
      int v = this->to_int(val_start, len_val);
      (*converted)++;
      this->set(k, v);
      if (*data_ptr == ']')
        break;
      else
        data_ptr++;
    }

    return MM_OK;
  }

  void MikeMap::del(MAP_KEY_TYPE key)
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        for (unsigned int si = i; si < (mikemap_len - 1); si++)
        {
          mikemap_keys[si] = mikemap_keys[si + 1];
          mikemap_values[si] = mikemap_values[si + 1];
        }
        mikemap_keys[mikemap_len] = 0;
        mikemap_values[mikemap_len] = 0;
        mikemap_len--;
        return;
      }
    }
  }

  unsigned int MikeMap::get_len() const
  {
    return mikemap_len;
  }

  void MikeMap::clear()
  {
    mikemap_len = 0;
  }

  void MikeMap::get_at(unsigned int at, MAP_KEY_TYPE *key, MAP_VALUE_TYPE *value) const
  {
    if (at < mikemap_len)
    {
      *key = mikemap_keys[at];
      *value = mikemap_values[at];
    }
  }
}
<commit_msg>adjusted types<commit_after>#include "mikemap.h"
#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>

//#define TEST_MIKEMAP

#ifdef TEST_MIKEMAP
#include <assert.h>

#define DEFAULT_TEST_VALUE 99
using namespace mikemap;

int main(int argc, char *argv[])
{
  MikeMap mm;
  char input[] = "[100,1,101,0,10,0,9,0,14,0,11,0,15,0]";
  MAP_KEY_TYPE key = DEFAULT_TEST_VALUE;
  MAP_VALUE_TYPE value = DEFAULT_TEST_VALUE;
  int converted = 0;

  assert(mm.get_len() == 0);
  for (int i = 0; i < MAX_MAP_LEN - 4; i++)
    mm.set(i, i);
  assert(mm.get_len() == MAX_MAP_LEN - 4);
  mm.del(1);
  assert(mm.get_len() == MAX_MAP_LEN - 5);
  assert(mm.get(4) == 4);
  assert(mm.has(1) == false);
  assert(mm.has(2) == true);
  mm.get_at(MAX_MAP_LEN + 4, &key, &value);
  assert(key == DEFAULT_TEST_VALUE);
  assert(value == DEFAULT_TEST_VALUE);
  mm.get_at(3, &key, &value);
  assert(key == 4);
  assert(value == 4);
  mm.clear();
  key = DEFAULT_TEST_VALUE;
  value = DEFAULT_TEST_VALUE;
  assert(mm.get_len() == 0);
  for (int i = 0; i < MAX_MAP_LEN - 4; i++)
    mm.set(i, i);
  assert(mm.get_len() == MAX_MAP_LEN - 4);
  mm.del(1);
  assert(mm.get_len() == MAX_MAP_LEN - 5);
  assert(mm.get(4) == 4);
  assert(mm.get(10) == 10);
  assert(mm.has(1) == false);
  assert(mm.has(2) == true);
  mm.get_at(MAX_MAP_LEN + 4, &key, &value);
  assert(key == DEFAULT_TEST_VALUE);
  assert(value == DEFAULT_TEST_VALUE);
  mm.get_at(3, &key, &value);
  assert(key == 4);
  assert(value == 4);
  mm.del(2);
  mm.del(5);
  mm.set(2, 20);
  assert(mm.has(2) == true);
  assert(mm.get_len() == MAX_MAP_LEN - 6);
  mm.get_at(MAX_MAP_LEN - 7, &key, &value);
  assert(key == 2);
  assert(value == 20);
  assert(mm.has(5) == false);
  mm.clear();
  assert(mm.from_string(input, &converted) == 0);
  assert(converted == 14);
  mm.get_at(0, &key, &value);
  printf("k0: %d  v0: %d\n", key, value);
  assert(key == 100);
  assert(value == 1);
  mm.get_at(4, &key, &value);
  assert(key == 14);
  assert(value == 0);
}
#endif

namespace mikemap
{

  MikeMap::MikeMap()
  {
    this->mikemap_len = 0;
  }

  void MikeMap::set(MAP_KEY_TYPE key, MAP_VALUE_TYPE value)
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        mikemap_values[i] = value;
        return;
      }
    }
    if (mikemap_len == MAX_MAP_LEN)
      return;
    mikemap_keys[mikemap_len] = key;
    mikemap_values[mikemap_len] = value;
    mikemap_len++;
  }

  bool MikeMap::has(MAP_KEY_TYPE key) const
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        return true;
      }
    }
    return false;
  }

  MAP_VALUE_TYPE MikeMap::get(MAP_KEY_TYPE key) const
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        return mikemap_values[i];
      }
    }
    return 0;
  }

  void MikeMap::to_string(char *ptr)
  {
    int offset = 0;
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (i > 0)
      {
        ptr[offset] = ',';
        offset++;
      }
      offset += sprintf(&ptr[offset], "%d", mikemap_keys[i]);
      ptr[offset] = ',';
      offset++;
      offset += sprintf(&ptr[offset], "%ld", mikemap_values[i]);
    }
  }

  long int MikeMap::to_int(const char *data, int len)
  {
    long int val = 0;
    while (len)
    {
      long int new_val = ((int)*data - (int)'0');
      val = val * 10 + new_val;
      len--;
      data++;
    }
#ifdef TEST_MIKEMAP
    printf("val: %ld\n", val);
#endif
    return val;
  }

  int MikeMap::from_string(const char *data_start, int *converted)
  {
    const char *data_ptr = data_start;

    this->clear();
    // the map is supposed to start with "["
    if (*data_ptr != '[')
      return MM_ERR_WRONG_START;
    data_ptr++;

    while (1)
    {
      int len_key = 0;
      const char *key_start = data_ptr;
      {
        while (len_key < MAX_SINGLE_DATA_LEN && *data_ptr != ',' && *data_ptr != ']')
        {
          if (*data_ptr < '0' || *data_ptr > '9')
            return MM_ERR_NO_DIGIT;
          len_key++;
          data_ptr++;
        }
        if (len_key == 0 || len_key == MAX_SINGLE_DATA_LEN)
          return MM_ERR_ZERO_MAX_LEN_KEY;
        if (*data_ptr == ']')
          return MM_ERR_MISSING_VAL;
      }
      MAP_KEY_TYPE k = this->to_int(key_start, len_key);
      (*converted)++;
      data_ptr++;
      int len_val = 0;
      const char *val_start = data_ptr;
      {
        while (len_val < MAX_SINGLE_DATA_LEN && *data_ptr != ',' && *data_ptr != ']')
        {
          if (*data_ptr < '0' || *data_ptr > '9')
            return MM_ERR_NO_DIGIT;
          len_val++;
          data_ptr++;
        }
        if (len_val == 0 || len_val == MAX_SINGLE_DATA_LEN)
          return MM_ERR_ZERO_MAX_LEN_VAL;
      }
      MAP_VALUE_TYPE v = this->to_int(val_start, len_val);
      (*converted)++;
      this->set(k, v);
      if (*data_ptr == ']')
        break;
      else
        data_ptr++;
    }

    return MM_OK;
  }

  void MikeMap::del(MAP_KEY_TYPE key)
  {
    for (unsigned int i = 0; i < mikemap_len; i++)
    {
      if (mikemap_keys[i] == key)
      {
        for (unsigned int si = i; si < (mikemap_len - 1); si++)
        {
          mikemap_keys[si] = mikemap_keys[si + 1];
          mikemap_values[si] = mikemap_values[si + 1];
        }
        mikemap_keys[mikemap_len] = 0;
        mikemap_values[mikemap_len] = 0;
        mikemap_len--;
        return;
      }
    }
  }

  unsigned int MikeMap::get_len() const
  {
    return mikemap_len;
  }

  void MikeMap::clear()
  {
    mikemap_len = 0;
  }

  void MikeMap::get_at(unsigned int at, MAP_KEY_TYPE *key, MAP_VALUE_TYPE *value) const
  {
    if (at < mikemap_len)
    {
      *key = mikemap_keys[at];
      *value = mikemap_values[at];
    }
  }
}
<|endoftext|>


<commit_before>/*
 *  modules.cpp
 *  StatusSpec project
 *
 *  Copyright (c) 2014 thesupremecommander
 *  BSD 2-Clause License
 *  http://opensource.org/licenses/BSD-2-Clause
 *
 */

#pragma once

#include "stdafx.h"

#include "modules.h"

void ModuleManager::UnloadAllModules() {
	for (auto iterator = modules.begin(); iterator != modules.end(); ++iterator) {
		delete iterator->second;
	}

	modules.clear();
}<commit_msg>Add a message when unloading a module.<commit_after>/*
 *  modules.cpp
 *  StatusSpec project
 *
 *  Copyright (c) 2014 thesupremecommander
 *  BSD 2-Clause License
 *  http://opensource.org/licenses/BSD-2-Clause
 *
 */

#pragma once

#include "stdafx.h"

#include "modules.h"

void ModuleManager::UnloadAllModules() {
	for (auto iterator = modules.begin(); iterator != modules.end(); ++iterator) {
		PRINT_TAG();
		ConColorMsg(Color(0, 255, 0, 255), "Module %s unloaded!\n", iterator->first.c_str());

		delete iterator->second;
	}

	modules.clear();
}<|endoftext|>


<commit_before>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * https://lxqt.org
 *
 * Copyright: 2015-2018 LXQt team
 * Authors:
 *   Palo Kisa <palo.kisa@gmail.com>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "sudo.h"
#include "passworddialog.h"

#include <LXQt/Application>

#include <QTextStream>
#include <QMessageBox>
#include <QFileInfo>
#include <QSocketNotifier>
#include <QDebug>
#include <QThread>
#include <QProcessEnvironment>
#include <QTimer>
#include <pty.h>
#include <unistd.h>
#include <memory>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
#include <iostream>
#include <thread>

namespace
{
    const QString app_master{QStringLiteral(LXQTSUDO)};
    const QString app_version{QStringLiteral(LXQT_VERSION)};
    const QString app_lxsu{QStringLiteral(LXQTSUDO_LXSU)};
    const QString app_lxsudo{QStringLiteral(LXQTSUDO_LXSUDO)};

    const QString su_prog{QStringLiteral(LXQTSUDO_SU)};
    const QString sudo_prog{QStringLiteral(LXQTSUDO_SUDO)};
    const QString pwd_prompt_end{QStringLiteral(": ")};
    const QChar nl{QLatin1Char('\n')};

    void usage(QString const & err = QString())
    {
        if (!err.isEmpty())
            QTextStream(stderr) << err << '\n';
        QTextStream(stdout)
            << QObject::tr("Usage: %1 option [command [arguments...]]\n\n"
                    "GUI frontend for %2/%3\n\n"
                    "Arguments:\n"
                    "  option:\n"
                    "    -h|--help      Print this help.\n"
                    "    -v|--version   Print version information.\n"
                    "    -s|--su        Use %3(1) as backend.\n"
                    "    -d|--sudo      Use %2(8) as backend.\n"
                    "  command          Command to run.\n"
                    "  arguments        Optional arguments for command.\n\n").arg(app_master).arg(sudo_prog).arg(su_prog);
        if (!err.isEmpty())
            QMessageBox(QMessageBox::Critical, app_master, err, QMessageBox::Ok).exec();
    }

    void version()
    {
        QTextStream(stdout)
            << QObject::tr("%1 version %2\n").arg(app_master).arg(app_version);
    }

    //Note: array must be sorted to allow usage of binary search
    static constexpr char const * const ALLOWED_VARS[] = {
        "DISPLAY"
            , "LANG", "LANGUAGE", "LC_ADDRESS", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_IDENTIFICATION", "LC_MEASUREMENT"
            , "LC_MESSAGES", "LC_MONETARY", "LC_NAME", "LC_NUMERIC", "LC_PAPER", "LC_TELEPHONE", "LC_TIME"
            , "PATH", "QT_PLATFORM_PLUGIN", "QT_QPA_PLATFORMTHEME", "WAYLAND_DISPLAY", "XAUTHORITY"
    };
    static constexpr char const * const * const ALLOWED_END = ALLOWED_VARS + sizeof (ALLOWED_VARS) / sizeof (ALLOWED_VARS[0]);
    struct assert_helper
    {
        assert_helper()
        {
            Q_ASSERT(std::is_sorted(ALLOWED_VARS, ALLOWED_END
                        , [] (char const * const a, char const * const b) { return strcmp(a, b) < 0; }));
        }
    };
    assert_helper h;

    inline void env_workarounds()
    {
        std::cerr << LXQTSUDO << ": Stripping child environment except for: ";
        std::copy(ALLOWED_VARS, ALLOWED_END - 1, std::ostream_iterator<const char *>{std::cerr, ", "});
        std::cerr << *(ALLOWED_END - 1) << '\n'; // printing the last separately to avoid trailing comma
        // cleanup environment, because e.g.:
        // - pcmanfm-qt will not start if the DBUS_SESSION_BUS_ADDRESS is preserved
        // - Qt apps may change user's config files permissions if the XDG_* are preserved
        for (auto const & key : QProcessEnvironment::systemEnvironment().keys())
        {
            auto const & i = std::lower_bound(ALLOWED_VARS, ALLOWED_END, key, [] (char const * const a, QString const & b) {
                return b > a;
            });
            if (i == ALLOWED_END || key != *i)
            {
                unsetenv(key.toStdString().c_str());
            }
        }
    }
}

Sudo::Sudo()
    : mArgs{lxqtApp->arguments()}
    , mBackend{BACK_NONE}
{
    QString cmd = QFileInfo(mArgs[0]).fileName();
    mArgs.removeAt(0);
    if (app_lxsu == cmd)
        mBackend = BACK_SU;
    else if (app_lxsudo == cmd || app_master == cmd)
        mBackend = BACK_SUDO;
}

Sudo::~Sudo()
{
}

int Sudo::main()
{
    if (0 < mArgs.size())
    {
        //simple option check
        QString const & arg1 = mArgs[0];
        if ("-h" == arg1 || "--help" == arg1)
        {
            usage();
            return 0;
        } else if ("-v" == arg1 || "--version" == arg1)
        {
            version();
            return 0;
        } else if ("-s" == arg1 || "--su" == arg1)
        {
            mBackend = BACK_SU;
            mArgs.removeAt(0);
        } else if ("-d" == arg1 || "--sudo" == arg1)
        {
            mBackend = BACK_SUDO;
            mArgs.removeAt(0);
        }
    }
    //any other arguments we simply forward to su/sudo

    if (1 > mArgs.size())
    {
        usage(tr("%1: no command to run provided!").arg(app_master));
        return 1;
    }

    if (BACK_NONE == mBackend)
    {
        //we were invoked through unknown link (or renamed binary)
        usage(tr("%1: no backend chosen!").arg(app_master));
        return 1;
    }

    mArgs.replaceInStrings(QStringLiteral("'"), QStringLiteral("'\\''"));
    mSquashedArgs = mArgs.replaceInStrings(QRegExp(QStringLiteral("^(.*)$")), "'\\1'").join(QStringLiteral(" "));

    mChildPid = forkpty(&mPwdFd, nullptr, nullptr, nullptr);
    if (0 == mChildPid)
    {
        child(); // never returns
        return 1; // but for sure
    }

    mDlg.reset(new PasswordDialog{mSquashedArgs});
    mDlg->setModal(true);
    lxqtApp->setActiveWindow(mDlg.data());

    if (-1 == mChildPid)
        QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                , tr("Syscall error, failed to fork: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
    else
        return parent();

    return 1;
}

void Sudo::child()
{
    int params_cnt = 3 //1. su/sudo & "shell command" & last nullptr
        + (BACK_SU == mBackend ? 1 : 3); //-c for su | -E /bin/sh -c for sudo
    std::unique_ptr<char const *[]> params{new char const *[params_cnt]};
    const char ** param_arg = params.get() + 1;

    std::string program;
    if (BACK_SU == mBackend)
    {
        program = su_prog.toStdString();
    } else
    {
        program = sudo_prog.toStdString();
        *(param_arg++) = "-E"; //preserve environment
        *(param_arg++) = "/bin/sh";
    }
    *(param_arg++) = "-c"; //run command

    params[0] = program.c_str();

    // Note: we force the su/sudo to communicate with us in the simplest
    // locale and then set the locale back for the command
    char const * const env_lc_all = getenv("LC_ALL");
    setenv("LC_ALL", "C", 1);
    std::string command;
    if (env_lc_all == nullptr)
    {
        command = "unset LC_ALL; ";
    } else
    {
        // Note: we need to check if someone is not trying to inject commands
        // for privileged execution via the LC_ALL
        if (nullptr != strchr(env_lc_all, '\''))
        {
            QTextStream{stderr, QIODevice::WriteOnly} << tr("%1: Detected attempt to inject privileged command via LC_ALL env(%2). Exitting!\n").arg(app_master).arg(env_lc_all);
            exit(1);
        }
        command = "LC_ALL='";
        command += env_lc_all;
        command += "' ";
    }
    command += "exec ";
    command += mSquashedArgs.toStdString();
    *(param_arg++) = command.c_str();

    *param_arg = nullptr;

    env_workarounds();

    setsid(); //session leader
    execvp(params[0], const_cast<char **>(params.get()));

    //exec never returns in case of success
    QTextStream{stderr, QIODevice::WriteOnly} << tr("%1: Failed to exec '%2': %3\n").arg(app_master).arg(params[0]).arg(strerror(errno));
    exit(1);
}

void Sudo::stopChild()
{
    kill(mChildPid, SIGINT);
    int res, status;
    for (int cnt = 10; 0 == (res = waitpid(mChildPid, &status, WNOHANG)) && 0 < cnt; --cnt)
        QThread::msleep(100);

    if (0 == res)
    {
        kill(mChildPid, SIGKILL);
    }
}

int Sudo::parent()
{
    //set the FD as non-blocking
    if (0 != fcntl(mPwdFd, F_SETFL, O_NONBLOCK))
    {
        QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                , tr("Syscall error, failed to bring pty to non-block mode: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
        return 1;
    }

    FILE * pwd_f = fdopen(mPwdFd, "r+");
    if (nullptr == pwd_f)
    {
        QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                , tr("Syscall error, failed to fdopen pty: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
        return 1;
    }

    QTextStream child_str{pwd_f};

    QObject::connect(mDlg.data(), &QDialog::finished, [&] (int result)
        {
            if (QDialog::Accepted == result)
            {
                child_str << mDlg->password().append(nl);
                child_str.flush();
            } else
            {
                stopChild();
            }
        });

    QString last_line;
    QScopedPointer<QSocketNotifier> pwd_watcher{new QSocketNotifier{mPwdFd, QSocketNotifier::Read}};
    auto reader = [&]
        {
            QString line = child_str.readAll();
            if (line.isEmpty())
            {
                pwd_watcher.reset(nullptr); //stop the notifications events

                QString const & prog = BACK_SU == mBackend ? su_prog : sudo_prog;
                if (last_line.startsWith(QStringLiteral("%1:").arg(prog)))
                {
                    QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                            , tr("Child '%1' process failed!\n%2").arg(prog).arg(last_line), QMessageBox::Ok).exec();
                }
            } else
            {
                if (line.endsWith(pwd_prompt_end))
                {
                    //if now echo is turned off, su/sudo requests password
                    struct termios tios;
                    //loop to be sure we don't miss the flag (we can afford such small delay in "normal" output processing)
                    for (size_t cnt = 10; 0 < cnt && 0 == tcgetattr(mPwdFd, &tios) && (ECHO & tios.c_lflag); --cnt)
                        QThread::msleep(10);
                    if (!(ECHO & tios.c_lflag))
                    {
                        mDlg->show();
                        return;
                    }
                }
                QTextStream{stderr, QIODevice::WriteOnly} << line;
                //assuming text oriented output
                QStringList lines = line.split(nl, QString::SkipEmptyParts);
                last_line = lines.isEmpty() ? QString() : lines.back();
            }

        };

    QObject::connect(pwd_watcher.data(), &QSocketNotifier::activated, reader);

    std::unique_ptr<std::thread> child_waiter;
    QTimer::singleShot(0, [&child_waiter, this] {
            child_waiter.reset(new std::thread{[this] {
                    int res, status;
                    res = waitpid(mChildPid, &status, 0);
                    mRet = (mChildPid == res && WIFEXITED(status)) ? WEXITSTATUS(status) : 1;
                    lxqtApp->quit();
                }
            });
        });

    lxqtApp->exec();

    child_waiter->join();

    // try to read the last line(s)
    reader();

    fclose(pwd_f);
    close(mPwdFd);

    return mRet;
}

<commit_msg>Fix typo in text string.<commit_after>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * https://lxqt.org
 *
 * Copyright: 2015-2018 LXQt team
 * Authors:
 *   Palo Kisa <palo.kisa@gmail.com>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "sudo.h"
#include "passworddialog.h"

#include <LXQt/Application>

#include <QTextStream>
#include <QMessageBox>
#include <QFileInfo>
#include <QSocketNotifier>
#include <QDebug>
#include <QThread>
#include <QProcessEnvironment>
#include <QTimer>
#include <pty.h>
#include <unistd.h>
#include <memory>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
#include <iostream>
#include <thread>

namespace
{
    const QString app_master{QStringLiteral(LXQTSUDO)};
    const QString app_version{QStringLiteral(LXQT_VERSION)};
    const QString app_lxsu{QStringLiteral(LXQTSUDO_LXSU)};
    const QString app_lxsudo{QStringLiteral(LXQTSUDO_LXSUDO)};

    const QString su_prog{QStringLiteral(LXQTSUDO_SU)};
    const QString sudo_prog{QStringLiteral(LXQTSUDO_SUDO)};
    const QString pwd_prompt_end{QStringLiteral(": ")};
    const QChar nl{QLatin1Char('\n')};

    void usage(QString const & err = QString())
    {
        if (!err.isEmpty())
            QTextStream(stderr) << err << '\n';
        QTextStream(stdout)
            << QObject::tr("Usage: %1 option [command [arguments...]]\n\n"
                    "GUI frontend for %2/%3\n\n"
                    "Arguments:\n"
                    "  option:\n"
                    "    -h|--help      Print this help.\n"
                    "    -v|--version   Print version information.\n"
                    "    -s|--su        Use %3(1) as backend.\n"
                    "    -d|--sudo      Use %2(8) as backend.\n"
                    "  command          Command to run.\n"
                    "  arguments        Optional arguments for command.\n\n").arg(app_master).arg(sudo_prog).arg(su_prog);
        if (!err.isEmpty())
            QMessageBox(QMessageBox::Critical, app_master, err, QMessageBox::Ok).exec();
    }

    void version()
    {
        QTextStream(stdout)
            << QObject::tr("%1 version %2\n").arg(app_master).arg(app_version);
    }

    //Note: array must be sorted to allow usage of binary search
    static constexpr char const * const ALLOWED_VARS[] = {
        "DISPLAY"
            , "LANG", "LANGUAGE", "LC_ADDRESS", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_IDENTIFICATION", "LC_MEASUREMENT"
            , "LC_MESSAGES", "LC_MONETARY", "LC_NAME", "LC_NUMERIC", "LC_PAPER", "LC_TELEPHONE", "LC_TIME"
            , "PATH", "QT_PLATFORM_PLUGIN", "QT_QPA_PLATFORMTHEME", "WAYLAND_DISPLAY", "XAUTHORITY"
    };
    static constexpr char const * const * const ALLOWED_END = ALLOWED_VARS + sizeof (ALLOWED_VARS) / sizeof (ALLOWED_VARS[0]);
    struct assert_helper
    {
        assert_helper()
        {
            Q_ASSERT(std::is_sorted(ALLOWED_VARS, ALLOWED_END
                        , [] (char const * const a, char const * const b) { return strcmp(a, b) < 0; }));
        }
    };
    assert_helper h;

    inline void env_workarounds()
    {
        std::cerr << LXQTSUDO << ": Stripping child environment except for: ";
        std::copy(ALLOWED_VARS, ALLOWED_END - 1, std::ostream_iterator<const char *>{std::cerr, ", "});
        std::cerr << *(ALLOWED_END - 1) << '\n'; // printing the last separately to avoid trailing comma
        // cleanup environment, because e.g.:
        // - pcmanfm-qt will not start if the DBUS_SESSION_BUS_ADDRESS is preserved
        // - Qt apps may change user's config files permissions if the XDG_* are preserved
        for (auto const & key : QProcessEnvironment::systemEnvironment().keys())
        {
            auto const & i = std::lower_bound(ALLOWED_VARS, ALLOWED_END, key, [] (char const * const a, QString const & b) {
                return b > a;
            });
            if (i == ALLOWED_END || key != *i)
            {
                unsetenv(key.toStdString().c_str());
            }
        }
    }
}

Sudo::Sudo()
    : mArgs{lxqtApp->arguments()}
    , mBackend{BACK_NONE}
{
    QString cmd = QFileInfo(mArgs[0]).fileName();
    mArgs.removeAt(0);
    if (app_lxsu == cmd)
        mBackend = BACK_SU;
    else if (app_lxsudo == cmd || app_master == cmd)
        mBackend = BACK_SUDO;
}

Sudo::~Sudo()
{
}

int Sudo::main()
{
    if (0 < mArgs.size())
    {
        //simple option check
        QString const & arg1 = mArgs[0];
        if ("-h" == arg1 || "--help" == arg1)
        {
            usage();
            return 0;
        } else if ("-v" == arg1 || "--version" == arg1)
        {
            version();
            return 0;
        } else if ("-s" == arg1 || "--su" == arg1)
        {
            mBackend = BACK_SU;
            mArgs.removeAt(0);
        } else if ("-d" == arg1 || "--sudo" == arg1)
        {
            mBackend = BACK_SUDO;
            mArgs.removeAt(0);
        }
    }
    //any other arguments we simply forward to su/sudo

    if (1 > mArgs.size())
    {
        usage(tr("%1: no command to run provided!").arg(app_master));
        return 1;
    }

    if (BACK_NONE == mBackend)
    {
        //we were invoked through unknown link (or renamed binary)
        usage(tr("%1: no backend chosen!").arg(app_master));
        return 1;
    }

    mArgs.replaceInStrings(QStringLiteral("'"), QStringLiteral("'\\''"));
    mSquashedArgs = mArgs.replaceInStrings(QRegExp(QStringLiteral("^(.*)$")), "'\\1'").join(QStringLiteral(" "));

    mChildPid = forkpty(&mPwdFd, nullptr, nullptr, nullptr);
    if (0 == mChildPid)
    {
        child(); // never returns
        return 1; // but for sure
    }

    mDlg.reset(new PasswordDialog{mSquashedArgs});
    mDlg->setModal(true);
    lxqtApp->setActiveWindow(mDlg.data());

    if (-1 == mChildPid)
        QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                , tr("Syscall error, failed to fork: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
    else
        return parent();

    return 1;
}

void Sudo::child()
{
    int params_cnt = 3 //1. su/sudo & "shell command" & last nullptr
        + (BACK_SU == mBackend ? 1 : 3); //-c for su | -E /bin/sh -c for sudo
    std::unique_ptr<char const *[]> params{new char const *[params_cnt]};
    const char ** param_arg = params.get() + 1;

    std::string program;
    if (BACK_SU == mBackend)
    {
        program = su_prog.toStdString();
    } else
    {
        program = sudo_prog.toStdString();
        *(param_arg++) = "-E"; //preserve environment
        *(param_arg++) = "/bin/sh";
    }
    *(param_arg++) = "-c"; //run command

    params[0] = program.c_str();

    // Note: we force the su/sudo to communicate with us in the simplest
    // locale and then set the locale back for the command
    char const * const env_lc_all = getenv("LC_ALL");
    setenv("LC_ALL", "C", 1);
    std::string command;
    if (env_lc_all == nullptr)
    {
        command = "unset LC_ALL; ";
    } else
    {
        // Note: we need to check if someone is not trying to inject commands
        // for privileged execution via the LC_ALL
        if (nullptr != strchr(env_lc_all, '\''))
        {
            QTextStream{stderr, QIODevice::WriteOnly} << tr("%1: Detected attempt to inject privileged command via LC_ALL env(%2). Exiting!\n").arg(app_master).arg(env_lc_all);
            exit(1);
        }
        command = "LC_ALL='";
        command += env_lc_all;
        command += "' ";
    }
    command += "exec ";
    command += mSquashedArgs.toStdString();
    *(param_arg++) = command.c_str();

    *param_arg = nullptr;

    env_workarounds();

    setsid(); //session leader
    execvp(params[0], const_cast<char **>(params.get()));

    //exec never returns in case of success
    QTextStream{stderr, QIODevice::WriteOnly} << tr("%1: Failed to exec '%2': %3\n").arg(app_master).arg(params[0]).arg(strerror(errno));
    exit(1);
}

void Sudo::stopChild()
{
    kill(mChildPid, SIGINT);
    int res, status;
    for (int cnt = 10; 0 == (res = waitpid(mChildPid, &status, WNOHANG)) && 0 < cnt; --cnt)
        QThread::msleep(100);

    if (0 == res)
    {
        kill(mChildPid, SIGKILL);
    }
}

int Sudo::parent()
{
    //set the FD as non-blocking
    if (0 != fcntl(mPwdFd, F_SETFL, O_NONBLOCK))
    {
        QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                , tr("Syscall error, failed to bring pty to non-block mode: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
        return 1;
    }

    FILE * pwd_f = fdopen(mPwdFd, "r+");
    if (nullptr == pwd_f)
    {
        QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                , tr("Syscall error, failed to fdopen pty: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
        return 1;
    }

    QTextStream child_str{pwd_f};

    QObject::connect(mDlg.data(), &QDialog::finished, [&] (int result)
        {
            if (QDialog::Accepted == result)
            {
                child_str << mDlg->password().append(nl);
                child_str.flush();
            } else
            {
                stopChild();
            }
        });

    QString last_line;
    QScopedPointer<QSocketNotifier> pwd_watcher{new QSocketNotifier{mPwdFd, QSocketNotifier::Read}};
    auto reader = [&]
        {
            QString line = child_str.readAll();
            if (line.isEmpty())
            {
                pwd_watcher.reset(nullptr); //stop the notifications events

                QString const & prog = BACK_SU == mBackend ? su_prog : sudo_prog;
                if (last_line.startsWith(QStringLiteral("%1:").arg(prog)))
                {
                    QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
                            , tr("Child '%1' process failed!\n%2").arg(prog).arg(last_line), QMessageBox::Ok).exec();
                }
            } else
            {
                if (line.endsWith(pwd_prompt_end))
                {
                    //if now echo is turned off, su/sudo requests password
                    struct termios tios;
                    //loop to be sure we don't miss the flag (we can afford such small delay in "normal" output processing)
                    for (size_t cnt = 10; 0 < cnt && 0 == tcgetattr(mPwdFd, &tios) && (ECHO & tios.c_lflag); --cnt)
                        QThread::msleep(10);
                    if (!(ECHO & tios.c_lflag))
                    {
                        mDlg->show();
                        return;
                    }
                }
                QTextStream{stderr, QIODevice::WriteOnly} << line;
                //assuming text oriented output
                QStringList lines = line.split(nl, QString::SkipEmptyParts);
                last_line = lines.isEmpty() ? QString() : lines.back();
            }

        };

    QObject::connect(pwd_watcher.data(), &QSocketNotifier::activated, reader);

    std::unique_ptr<std::thread> child_waiter;
    QTimer::singleShot(0, [&child_waiter, this] {
            child_waiter.reset(new std::thread{[this] {
                    int res, status;
                    res = waitpid(mChildPid, &status, 0);
                    mRet = (mChildPid == res && WIFEXITED(status)) ? WEXITSTATUS(status) : 1;
                    lxqtApp->quit();
                }
            });
        });

    lxqtApp->exec();

    child_waiter->join();

    // try to read the last line(s)
    reader();

    fclose(pwd_f);
    close(mPwdFd);

    return mRet;
}

<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
#include <thrift/lib/cpp/async/TAsyncUDPSocket.h>

#include <thrift/lib/cpp/async/TEventBase.h>

#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
using apache::thrift::transport::TTransportException;

namespace apache { namespace thrift { namespace async {

TAsyncUDPSocket::TAsyncUDPSocket(TEventBase* evb)
    : TEventHandler(CHECK_NOTNULL(evb)),
      eventBase_(evb),
      fd_(-1),
      readCallback_(nullptr) {
  DCHECK(evb->isInEventBaseThread());
}

TAsyncUDPSocket::~TAsyncUDPSocket() {
  if (fd_ != -1) {
    close();
  }
}

void TAsyncUDPSocket::bind(const transport::TSocketAddress& address) {
  int socket = ::socket(address.getFamily(), SOCK_DGRAM, IPPROTO_UDP);
  if (socket == -1) {
    throw TTransportException(TTransportException::NOT_OPEN,
                              "error creating async udp socket",
                              errno);
  }

  auto g = folly::makeGuard([&] { ::close(socket); });

  // put the socket in non-blocking mode
  int ret = fcntl(socket, F_SETFL, O_NONBLOCK);
  if (ret != 0) {
    throw TTransportException(TTransportException::NOT_OPEN,
                              "failed to put socket in non-blocking mode",
                              errno);
  }

  // put the socket in reuse mode
  int value = 1;
  if (setsockopt(socket,
                 SOL_SOCKET,
                 SO_REUSEADDR,
                 &value,
                 sizeof(value)) != 0) {
    throw TTransportException(TTransportException::NOT_OPEN,
                              "failed to put socket in reuse mode",
                              errno);
  }

  // bind to the address
  sockaddr_storage addrStorage;
  address.getAddress(&addrStorage);
  sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage);
  if (::bind(socket, saddr, address.getActualSize()) != 0) {
    throw TTransportException(
        TTransportException::NOT_OPEN,
        "failed to bind the async udp socket for:" + address.describe(),
        errno);
  }

  // success
  g.dismiss();
  fd_ = socket;
  ownership_ = FDOwnership::OWNS;

  // attach to TEventHandler
  TEventHandler::changeHandlerFD(fd_);

  if (address.getPort() != 0) {
    localAddress_ = address;
  } else {
    localAddress_.setFromLocalAddress(fd_);
  }
}

void TAsyncUDPSocket::setFD(int fd, FDOwnership ownership) {
  CHECK_EQ(-1, fd_) << "Already bound to another FD";

  fd_ = fd;
  ownership_ = ownership;

  TEventHandler::changeHandlerFD(fd_);
  localAddress_.setFromLocalAddress(fd_);
}

ssize_t TAsyncUDPSocket::write(const transport::TSocketAddress& address,
                               const std::unique_ptr<folly::IOBuf>& buf) {
  CHECK_NE(-1, fd_) << "Socket not yet bound";

  // XXX: Use `sendmsg` instead of coalescing here
  buf->coalesce();

  sockaddr_storage addrStorage;
  address.getAddress(&addrStorage);
  sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage);

  return ::sendto(fd_,
                  buf->data(),
                  buf->length(),
                  MSG_DONTWAIT,
                  saddr,
                  address.getActualSize());
}

void TAsyncUDPSocket::resumeRead(ReadCallback* cob) {
  CHECK(!readCallback_) << "Another read callback already installed";
  CHECK_NE(-1, fd_) << "UDP server socket not yet bind to an address";

  readCallback_ = CHECK_NOTNULL(cob);
  if (!updateRegistration()) {
    TTransportException ex(TTransportException::NOT_OPEN,
                           "failed to register for accept events");

    readCallback_ = nullptr;
    cob->onReadError(ex);
    return;
  }
}

void TAsyncUDPSocket::pauseRead() {
  // It is ok to pause an already paused socket
  readCallback_ = nullptr;
  updateRegistration();
}

void TAsyncUDPSocket::close() {
  DCHECK(eventBase_->isInEventBaseThread());

  if (readCallback_) {
    auto cob = readCallback_;
    readCallback_ = nullptr;

    cob->onReadClosed();
  }

  // Unregister any events we are registered for
  unregisterHandler();

  if (fd_ != -1 && ownership_ == FDOwnership::OWNS) {
    ::close(fd_);
  }

  fd_ = -1;
}

void TAsyncUDPSocket::handlerReady(uint16_t events) noexcept {
  if (events & TEventHandler::READ) {
    DCHECK(readCallback_);
    handleRead();
  }
}

void TAsyncUDPSocket::handleRead() noexcept {
  void* buf{nullptr};
  size_t len{0};

  readCallback_->getReadBuffer(&buf, &len);
  if (buf == nullptr || len == 0) {
    TTransportException ex(
        TTransportException::BAD_ARGS,
        "TAsyncUDPSocket::getReadBuffer() returned empty buffer");

    auto cob = readCallback_;
    readCallback_ = nullptr;

    cob->onReadError(ex);
    updateRegistration();
    return;
  }

  sockaddr_storage addrStorage;
  struct sockaddr* rawAddr = reinterpret_cast<sockaddr*>(&addrStorage);
  rawAddr->sa_family = localAddress_.getFamily();
  socklen_t addrLen = sizeof(rawAddr);

  ssize_t bytesRead = ::recvfrom(fd_, buf, len, MSG_TRUNC, rawAddr, &addrLen);
  if (bytesRead >= 0) {
    clientAddress_.setFromSockaddr(rawAddr, addrLen);

    if (bytesRead > 0) {
      bool truncated = false;
      if (bytesRead > len) {
        truncated = true;
        bytesRead = len;
      }

      readCallback_->onDataAvailable(clientAddress_, bytesRead, truncated);
    }
  } else {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      // No data could be read without blocking the socket
      return;
    }

    TTransportException ex(TTransportException::INTERNAL_ERROR,
                           "::recvfrom() failed",
                           errno);

    // In case of UDP we can continue reading from the socket
    // even if the current request fails. We notify the user
    // so that he can do some logging/stats collection if he wants.
    auto cob = readCallback_;
    readCallback_ = nullptr;

    cob->onReadError(ex);
    updateRegistration();
  }
}

bool TAsyncUDPSocket::updateRegistration() noexcept {
  uint16_t flags = NONE;

  if (readCallback_) {
    flags |= READ;
  }

  return registerHandler(flags | PERSIST);
}

}}}
<commit_msg>initialize addrLen to sizeof sockaddr rather its pointer<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
#include <thrift/lib/cpp/async/TAsyncUDPSocket.h>

#include <thrift/lib/cpp/async/TEventBase.h>

#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
using apache::thrift::transport::TTransportException;

namespace apache { namespace thrift { namespace async {

TAsyncUDPSocket::TAsyncUDPSocket(TEventBase* evb)
    : TEventHandler(CHECK_NOTNULL(evb)),
      eventBase_(evb),
      fd_(-1),
      readCallback_(nullptr) {
  DCHECK(evb->isInEventBaseThread());
}

TAsyncUDPSocket::~TAsyncUDPSocket() {
  if (fd_ != -1) {
    close();
  }
}

void TAsyncUDPSocket::bind(const transport::TSocketAddress& address) {
  int socket = ::socket(address.getFamily(), SOCK_DGRAM, IPPROTO_UDP);
  if (socket == -1) {
    throw TTransportException(TTransportException::NOT_OPEN,
                              "error creating async udp socket",
                              errno);
  }

  auto g = folly::makeGuard([&] { ::close(socket); });

  // put the socket in non-blocking mode
  int ret = fcntl(socket, F_SETFL, O_NONBLOCK);
  if (ret != 0) {
    throw TTransportException(TTransportException::NOT_OPEN,
                              "failed to put socket in non-blocking mode",
                              errno);
  }

  // put the socket in reuse mode
  int value = 1;
  if (setsockopt(socket,
                 SOL_SOCKET,
                 SO_REUSEADDR,
                 &value,
                 sizeof(value)) != 0) {
    throw TTransportException(TTransportException::NOT_OPEN,
                              "failed to put socket in reuse mode",
                              errno);
  }

  // bind to the address
  sockaddr_storage addrStorage;
  address.getAddress(&addrStorage);
  sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage);
  if (::bind(socket, saddr, address.getActualSize()) != 0) {
    throw TTransportException(
        TTransportException::NOT_OPEN,
        "failed to bind the async udp socket for:" + address.describe(),
        errno);
  }

  // success
  g.dismiss();
  fd_ = socket;
  ownership_ = FDOwnership::OWNS;

  // attach to TEventHandler
  TEventHandler::changeHandlerFD(fd_);

  if (address.getPort() != 0) {
    localAddress_ = address;
  } else {
    localAddress_.setFromLocalAddress(fd_);
  }
}

void TAsyncUDPSocket::setFD(int fd, FDOwnership ownership) {
  CHECK_EQ(-1, fd_) << "Already bound to another FD";

  fd_ = fd;
  ownership_ = ownership;

  TEventHandler::changeHandlerFD(fd_);
  localAddress_.setFromLocalAddress(fd_);
}

ssize_t TAsyncUDPSocket::write(const transport::TSocketAddress& address,
                               const std::unique_ptr<folly::IOBuf>& buf) {
  CHECK_NE(-1, fd_) << "Socket not yet bound";

  // XXX: Use `sendmsg` instead of coalescing here
  buf->coalesce();

  sockaddr_storage addrStorage;
  address.getAddress(&addrStorage);
  sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage);

  return ::sendto(fd_,
                  buf->data(),
                  buf->length(),
                  MSG_DONTWAIT,
                  saddr,
                  address.getActualSize());
}

void TAsyncUDPSocket::resumeRead(ReadCallback* cob) {
  CHECK(!readCallback_) << "Another read callback already installed";
  CHECK_NE(-1, fd_) << "UDP server socket not yet bind to an address";

  readCallback_ = CHECK_NOTNULL(cob);
  if (!updateRegistration()) {
    TTransportException ex(TTransportException::NOT_OPEN,
                           "failed to register for accept events");

    readCallback_ = nullptr;
    cob->onReadError(ex);
    return;
  }
}

void TAsyncUDPSocket::pauseRead() {
  // It is ok to pause an already paused socket
  readCallback_ = nullptr;
  updateRegistration();
}

void TAsyncUDPSocket::close() {
  DCHECK(eventBase_->isInEventBaseThread());

  if (readCallback_) {
    auto cob = readCallback_;
    readCallback_ = nullptr;

    cob->onReadClosed();
  }

  // Unregister any events we are registered for
  unregisterHandler();

  if (fd_ != -1 && ownership_ == FDOwnership::OWNS) {
    ::close(fd_);
  }

  fd_ = -1;
}

void TAsyncUDPSocket::handlerReady(uint16_t events) noexcept {
  if (events & TEventHandler::READ) {
    DCHECK(readCallback_);
    handleRead();
  }
}

void TAsyncUDPSocket::handleRead() noexcept {
  void* buf{nullptr};
  size_t len{0};

  readCallback_->getReadBuffer(&buf, &len);
  if (buf == nullptr || len == 0) {
    TTransportException ex(
        TTransportException::BAD_ARGS,
        "TAsyncUDPSocket::getReadBuffer() returned empty buffer");

    auto cob = readCallback_;
    readCallback_ = nullptr;

    cob->onReadError(ex);
    updateRegistration();
    return;
  }

  sockaddr_storage addrStorage;
  struct sockaddr* rawAddr = reinterpret_cast<sockaddr*>(&addrStorage);
  rawAddr->sa_family = localAddress_.getFamily();
  socklen_t addrLen = sizeof(*rawAddr);

  ssize_t bytesRead = ::recvfrom(fd_, buf, len, MSG_TRUNC, rawAddr, &addrLen);
  if (bytesRead >= 0) {
    clientAddress_.setFromSockaddr(rawAddr, addrLen);

    if (bytesRead > 0) {
      bool truncated = false;
      if (bytesRead > len) {
        truncated = true;
        bytesRead = len;
      }

      readCallback_->onDataAvailable(clientAddress_, bytesRead, truncated);
    }
  } else {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      // No data could be read without blocking the socket
      return;
    }

    TTransportException ex(TTransportException::INTERNAL_ERROR,
                           "::recvfrom() failed",
                           errno);

    // In case of UDP we can continue reading from the socket
    // even if the current request fails. We notify the user
    // so that he can do some logging/stats collection if he wants.
    auto cob = readCallback_;
    readCallback_ = nullptr;

    cob->onReadError(ex);
    updateRegistration();
  }
}

bool TAsyncUDPSocket::updateRegistration() noexcept {
  uint16_t flags = NONE;

  if (readCallback_) {
    flags |= READ;
  }

  return registerHandler(flags | PERSIST);
}

}}}
<|endoftext|>


<commit_before>///
/// @file  phi_sum.cpp
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesum-internal.hpp>
#include <primesum.hpp>
#include <generate.hpp>
#include <fast_div.hpp>

#include <stdint.h>

using namespace std;
using namespace primesum;

namespace {

const int primes_[] = { 0, 2, 3, 5, 7, 11, 13, 17, 19, 23 };

template <int SIGN>
maxint_t F(maxint_t u)
{
  maxint_t u1 = u;
  u1 += 1;
  u1 *= u;
  u1 >>= 1;
  return u1;
}

template <>
maxint_t F<-1>(maxint_t u)
{
  maxint_t u1 = u;
  u1 += 1;
  u1 *= u;
  u1 >>= 1;
  u1 = -u1;
  return u1;
}

template <int SIGN>
maxint_t phi_sum_tiny(int128_t x, int64_t a)
{
  maxint_t phi;
  maxint_t sum = 0;

  for (; a > 0; a--)
  {
    if (x <= primes_[a])
      return sum + SIGN;

    int128_t x2 = fast_div(x, primes_[a]);
    phi = phi_sum_tiny<-SIGN>(x2, a - 1);
    phi *= primes_[a];
    sum += phi;
  }

  sum += F<SIGN>(x);

  return sum;
}

template <int SIGN>
maxint_t phi_sum(int128_t x,
                 int64_t a,
                 vector<int>& primes)
{
  maxint_t phi;
  maxint_t sum = 0;

  for (; a > 0; a--)
  {
    if (x <= primes[a])
      return sum + SIGN;

    int128_t x2 = fast_div(x, primes[a]);
    phi = phi_sum<-SIGN>(x2, a - 1, primes);
    phi *= primes[a];
    sum += phi;
  }

  sum += F<SIGN>(x);

  return sum;
}

}

namespace primesum {

maxint_t phi_sum(int128_t x, int64_t a)
{
  if (x < 1)
    return 0;

  if (a < 10)
    return phi_sum_tiny<1>(x, a);

  vector<int> primes = generate_n_primes(a);
  return ::phi_sum<1>(x, a, primes);
}

} // namespace
<commit_msg>Minor speedup<commit_after>///
/// @file  phi_sum.cpp
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesum-internal.hpp>
#include <primesum.hpp>
#include <generate.hpp>
#include <fast_div.hpp>

#include <stdint.h>

using namespace std;
using namespace primesum;

namespace {

const int primes_[] = { 0, 2, 3, 5, 7, 11, 13, 17, 19, 23 };

template <int SIGN>
maxint_t F(maxint_t u)
{
  maxint_t u1 = u * u;
  u1 += u;
  u1 >>= 1;
  return u1;
}

template <>
maxint_t F<-1>(maxint_t u)
{
  maxint_t u1 = u * u;
  u1 += u;
  u1 >>= 1;
  u1 = -u1;
  return u1;
}

template <int SIGN>
maxint_t phi_sum_tiny(int128_t x, int64_t a)
{
  maxint_t phi;
  maxint_t sum = 0;

  for (; a > 0; a--)
  {
    if (x <= primes_[a])
      return sum + SIGN;

    int128_t x2 = fast_div(x, primes_[a]);
    phi = phi_sum_tiny<-SIGN>(x2, a - 1);
    phi *= primes_[a];
    sum += phi;
  }

  sum += F<SIGN>(x);

  return sum;
}

template <int SIGN>
maxint_t phi_sum(int128_t x,
                 int64_t a,
                 vector<int>& primes)
{
  maxint_t phi;
  maxint_t sum = 0;

  for (; a > 0; a--)
  {
    if (x <= primes[a])
      return sum + SIGN;

    int128_t x2 = fast_div(x, primes[a]);
    phi = phi_sum<-SIGN>(x2, a - 1, primes);
    phi *= primes[a];
    sum += phi;
  }

  sum += F<SIGN>(x);

  return sum;
}

}

namespace primesum {

maxint_t phi_sum(int128_t x, int64_t a)
{
  if (x < 1)
    return 0;

  if (a < 10)
    return phi_sum_tiny<1>(x, a);

  vector<int> primes = generate_n_primes(a);
  return ::phi_sum<1>(x, a, primes);
}

} // namespace
<|endoftext|>


<commit_before>// Copyright 2011 The Avalon Project Authors. All rights reserved.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the LICENSE file.
// Steffen Grundmann, June 2011

#include "helmsman/boat_model.h"
#include "helmsman/ship_control.h"

#include <math.h>
#include <stdio.h>
#include "common/convert.h"
#include "common/delta_angle.h"
#include "common/polar.h"

#include "lib/testing/testing.h"

namespace {
const double kSamplingPeriod = 0.1;
}

extern int debug;

static int print_model = 0;
void NormalControllerTest(double wind_direction_deg,
                          double expected_min_speed_m_s,
                          double wind_strength_m_s = 10) {
  BoatModel model(kSamplingPeriod,
                  0,                // omega_ / rad, turning rate, + turns right
                  0,                // phi_z_ / rad, heading relative to North, + turns right
                  0,                // v_x_ / m/s,   speed
                  -M_PI / 2);       // gamma_sail_ / rad, e.g. -90 degrees here
  DriveReferenceValuesRad ref;
  Polar true_wind(Deg2Rad(wind_direction_deg), wind_strength_m_s);  // forward wind, 10m/s
  ControllerInput in;
  ControllerOutput out;
  in.alpha_star_rad = Deg2Rad(90);         // want to go east

  ShipControl::Reset();
  ShipControl::Docking();              // The consequential Entry into the
                                       // Initial state allows to reset the
                                       // Initial controller.
  ShipControl::Run(in, &out);
  ShipControl::Normal();
  printf("\nWind direction: %6.0f degree, Wind speed: %4.2f m/s\n",
          wind_direction_deg, wind_strength_m_s);
  double t;
  double t_final = wind_strength_m_s < 5 ? 450 : 240; // weak winds make the slow tack fail.
                                                      // Better make jibes only for slow winds?.

  if (print_model)
    model.PrintHeader();
  for (t = 0; t < t_final; t += kSamplingPeriod) {
    model.Simulate(out.drives_reference,
                   true_wind,
                   &in);
    ShipControl::Run(in, &out);
    if ((print_model /*&& t > 99.0*/) || debug)
      model.Print(t);
  }
  printf("\n");
  model.PrintHeader();
  model.Print(t);
  EXPECT_LT(expected_min_speed_m_s, model.GetSpeed());
  if (fabs(model.GetPhiZ() - in.alpha_star_rad) > Deg2Rad(5))
    printf("final heading %g for wind direction %g\n", Rad2Deg(model.GetPhiZ()), wind_direction_deg);
}

TEST(SimShip, Wind_1) {
  NormalControllerTest(-165,   // wind vector direction, in degrees
                       0.8);   // minimum final boat speed, m/s
  // All initial wind directions are handled correctly.
  for (double wind_direction = -180; wind_direction < 180; wind_direction += 0.3) {
    if (fabs(wind_direction - -90) < 20)  // Not sailable.
      continue;
    NormalControllerTest(wind_direction, 0.8);  // speeds vary from 0.8 to 2.1 m/s
  }
}

TEST(SimShip, Wind_2) {
  // Cannot sail with less than 0.65m/s wind strength.
  for (double wind_strength = 2; wind_strength < 20; wind_strength *= 1.15)
  // All initial wind directions are handled correctly.
  for (double wind_direction = -180; wind_direction < 180; wind_direction += 0.3) {
    if (fabs(wind_direction - -90) < 20)  // Not sailable.
      continue;
    /*
    if (fabs(wind_direction - -180 ) < 0.1 && fabs(wind_strength - 2.6) < 0.1) {
      //debug = 1;
      print_model = 1;
      //continue;
    } else {
      print_model = 0;
    }
    */
    // speeds vary due to different wind strength and headings.
    // If our model was correct, we would get the polar diagram here.
    NormalControllerTest(wind_direction, std::min(0.06 * wind_strength, 2.5), wind_strength);
  }
}

TEST(SimShip, Wind_Strong) {
  for (double wind_strength = 10; wind_strength < 20; wind_strength *= 1.5)
    // All initial wind directions are handled correctly.
    for (double wind_direction = -180; wind_direction < 180; wind_direction += 0.3) {
      if (fabs(wind_direction - -90) < 20)  // Not sailable.
        continue;
      /*
      if (fabs(wind_direction - 31.8 ) < 0.1 && fabs(wind_strength - 20) < 0.1) {
        debug = 1;
        print_model = 1;
      } else {
        debug = 0;
        print_model = 0;
      }
      */
      // speeds vary due to different wind strength and headings.
      // If our model was correct, we would get the polar diagram here.
      NormalControllerTest(wind_direction, std::min(0.06 * wind_strength, 2.5), wind_strength);
      debug = 0;
    }
}

TEST(SimShip, Wind_Debug) {
  debug = 0;
  NormalControllerTest(31.8,           // wind vector direction, in degrees
                       1.5,            // minimum final boat speed, m/s
                       16.2741232583); // wind speed, m/s
  debug = 0;
}

int main(int argc, char* argv[]) {
  debug = 0;
  SimShip_Wind_2();
  SimShip_Wind_Strong();
  SimShip_Wind_Debug();
  SimShip_Wind_1();
  return 0;
}

<commit_msg>Timing of the controller runtimes.<commit_after>// Copyright 2011 The Avalon Project Authors. All rights reserved.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the LICENSE file.
// Steffen Grundmann, June 2011

// Test with runtime measurements.
// ==============================

#include "helmsman/boat_model.h"
#include "helmsman/ship_control.h"

#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include "common/convert.h"
#include "common/delta_angle.h"
#include "common/polar.h"
#include "common/now.h"
#include "lib/testing/testing.h"
#include "lib/util/stopwatch.h"

namespace {
const double kSamplingPeriod = 0.1;
}

extern int debug;

static int print_model = 0;
void NormalControllerTest(double wind_direction_deg,
                          double expected_min_speed_m_s,
                          double wind_strength_m_s = 10) {
  BoatModel model(kSamplingPeriod,
                  0,                // omega_ / rad, turning rate, + turns right
                  0,                // phi_z_ / rad, heading relative to North, + turns right
                  0,                // v_x_ / m/s,   speed
                  -M_PI / 2);       // gamma_sail_ / rad, e.g. -90 degrees here
  DriveReferenceValuesRad ref;
  Polar true_wind(Deg2Rad(wind_direction_deg), wind_strength_m_s);  // forward wind, 10m/s
  ControllerInput in;
  ControllerOutput out;
  in.alpha_star_rad = Deg2Rad(90);         // want to go east

  ShipControl::Reset();
  ShipControl::Docking();              // The consequential Entry into the
                                       // Initial state allows to reset the
                                       // Initial controller.
  ShipControl::Run(in, &out);
  ShipControl::Normal();
  printf("\nWind direction: %6.0f degree, Wind speed: %4.2f m/s\n",
          wind_direction_deg, wind_strength_m_s);
  int64_t micros_max = 0;
  int64_t micros_sum = 0;
  int rounds = 0;
  double t;
  double t_final = wind_strength_m_s < 5 ? 450 : 240; // weak winds make the slow tack fail.
                                                      // Better make jibes only for slow winds?.

  if (print_model)
    model.PrintHeader();
  for (t = 0; t < t_final; t += kSamplingPeriod) {
    model.Simulate(out.drives_reference,
                   true_wind,
                   &in);
    int64_t start = now_micros();
    ShipControl::Run(in, &out);
    int64_t micros = now_micros() - start;
    ++rounds;
    micros_sum += micros;
    micros_max = std::max(micros_max, micros);
    if ((print_model /*&& t > 99.0*/) || debug)
      model.Print(t);
  }
  printf("\n");
  model.PrintHeader();
  model.Print(t);
  EXPECT_LT(expected_min_speed_m_s, model.GetSpeed());
  if (fabs(model.GetPhiZ() - in.alpha_star_rad) > Deg2Rad(5))
    printf("final heading %g for wind direction %g\n", Rad2Deg(model.GetPhiZ()), wind_direction_deg);
  printf("\nRuntimes/microseconds\n=================\n");
  printf("Average:    %6.4f micros\n", micros_sum / static_cast<double>(rounds));
  printf("Max:        %lld micros\n", micros_max);
  printf("\n");
}

TEST(SimShip, Wind_1) {
  NormalControllerTest(-165,   // wind vector direction, in degrees
                       0.8);   // minimum final boat speed, m/s
  // All initial wind directions are handled correctly.
  for (double wind_direction = -180; wind_direction < 180; wind_direction += 0.3) {
    if (fabs(wind_direction - -90) < 20)  // Not sailable.
      continue;
    NormalControllerTest(wind_direction, 0.8);  // speeds vary from 0.8 to 2.1 m/s
  }
}

TEST(SimShip, Wind_2) {
  // Cannot sail with less than 0.65m/s wind strength.
  for (double wind_strength = 2; wind_strength < 20; wind_strength *= 1.15)
  // All initial wind directions are handled correctly.
  for (double wind_direction = -180; wind_direction < 180; wind_direction += 0.3) {
    if (fabs(wind_direction - -90) < 20)  // Not sailable.
      continue;
    /*
    if (fabs(wind_direction - -180 ) < 0.1 && fabs(wind_strength - 2.6) < 0.1) {
      //debug = 1;
      print_model = 1;
      //continue;
    } else {
      print_model = 0;
    }
    */
    // speeds vary due to different wind strength and headings.
    // If our model was correct, we would get the polar diagram here.
    NormalControllerTest(wind_direction, std::min(0.06 * wind_strength, 2.5), wind_strength);
  }
}

TEST(SimShip, Wind_Strong) {
  for (double wind_strength = 10; wind_strength < 20; wind_strength *= 1.5)
    // All initial wind directions are handled correctly.
    for (double wind_direction = -180; wind_direction < 180; wind_direction += 0.3) {
      if (fabs(wind_direction - -90) < 20)  // Not sailable.
        continue;
      /*
      if (fabs(wind_direction - 31.8 ) < 0.1 && fabs(wind_strength - 20) < 0.1) {
        debug = 1;
        print_model = 1;
      } else {
        debug = 0;
        print_model = 0;
      }
      */
      // speeds vary due to different wind strength and headings.
      // If our model was correct, we would get the polar diagram here.
      NormalControllerTest(wind_direction, std::min(0.06 * wind_strength, 2.5), wind_strength);
      debug = 0;
    }
}

TEST(SimShip, Wind_Debug) {
  debug = 0;
  NormalControllerTest(31.8,           // wind vector direction, in degrees
                       1.5,            // minimum final boat speed, m/s
                       16.2741232583); // wind speed, m/s
  debug = 0;
}

int main(int argc, char* argv[]) {
  debug = 0;
  SimShip_Wind_2();
  SimShip_Wind_Strong();
  SimShip_Wind_Debug();
  SimShip_Wind_1();
  return 0;
}
<|endoftext|>


<commit_before>#ifndef SRC_CORE_CONNECTABLES_HPP_
#define SRC_CORE_CONNECTABLES_HPP_

#include <cmath>
#include <cassert>

namespace fc
{

/**
 * A collection of different useful connectables.
 * A lot of them are names for simple lambdas to make code less verbose.
 */

/// Increments input using prefix operator ++.
auto increment = [](auto in) { return ++in; };
/// Decrements input using prefix operator --.
auto decrement = [](auto in) { return --in; };
/// Returns input unchanged.
auto identity = [](auto in) { return in; };

/// Adds a constant addend to inputs.
auto add_to = [](const auto summand)
{
	return [summand](auto in){ return in + summand; };
};

/// Subtracts a constant subtrahend from inputs.
auto subtract_value = [](const auto subtrahend)
{
	return [subtrahend](auto in){ return in - subtrahend; };
};

/// Multiples input by a constant factor.
auto multiply_with = [](const auto factor)
{
	return [factor](auto in) { return factor * in; };
};

/// Divides inputs by a constant divisor.
auto divide_by = [](const auto divisor)
{
	return [divisor](auto in) { return in / divisor; };
};

/// Returns absolute value on input using std::abs.
auto absolute = [](auto in) { return std::abs(in); };

/// Negates input using unary -.
auto negate = [](auto in) { return -in; };

/// Returns logical not (operator !) of input.
auto logical_not = [](auto in) { return !in; };

/**
 * \brief  Clamps input to closed range [min, max].
 *
 * \pre min <= max
 * \post output >= min && output <= max
 */
auto clamp = [](auto min, auto max)
{
	assert(min <= max);
	return [min, max](auto in)
	{
		return in < min ? min: (max < in ? max : in);
	};
};

/// Applies sin wave function to the input, wavelength is 1
double sin_wave = [](double in) { return std::sin(in * 2 * M_PI); };

/// Applies a saw wave function to the input, wavelength is 1
double saw_wave = [](double in)
	{
		in = (in < 2 ? 2 : 0) + fmod(in, 2);
		return 2*(2*in - floor(in)) - 1;
	};

/// Applies a triangular wave function to the input, wavelength is 1
double tri_wave = [](double in) { return 2*std::abs(saw_wave(in)) - 1; };

/// Applies a rectangular wave function to the input, wavelength is 1
double rec_wave = [](double in)
	{
		in = (in < 0 ? 2 : 0), + fmod(in, 2);
		return in > 1 ? -1 : 1;
	};

}  // namespace fc

#endif /* SRC_CORE_CONNECTABLES_HPP_ */
<commit_msg>Fixed bug with last commit<commit_after>#ifndef SRC_CORE_CONNECTABLES_HPP_
#define SRC_CORE_CONNECTABLES_HPP_

#include <cmath>
#include <cassert>

namespace fc
{

/**
 * A collection of different useful connectables.
 * A lot of them are names for simple lambdas to make code less verbose.
 */

/// Increments input using prefix operator ++.
auto increment = [](auto in) { return ++in; };
/// Decrements input using prefix operator --.
auto decrement = [](auto in) { return --in; };
/// Returns input unchanged.
auto identity = [](auto in) { return in; };

/// Adds a constant addend to inputs.
auto add_to = [](const auto summand)
{
	return [summand](auto in){ return in + summand; };
};

/// Subtracts a constant subtrahend from inputs.
auto subtract_value = [](const auto subtrahend)
{
	return [subtrahend](auto in){ return in - subtrahend; };
};

/// Multiples input by a constant factor.
auto multiply_with = [](const auto factor)
{
	return [factor](auto in) { return factor * in; };
};

/// Divides inputs by a constant divisor.
auto divide_by = [](const auto divisor)
{
	return [divisor](auto in) { return in / divisor; };
};

/// Returns absolute value on input using std::abs.
auto absolute = [](auto in) { return std::abs(in); };

/// Negates input using unary -.
auto negate = [](auto in) { return -in; };

/// Returns logical not (operator !) of input.
auto logical_not = [](auto in) { return !in; };

/**
 * \brief  Clamps input to closed range [min, max].
 *
 * \pre min <= max
 * \post output >= min && output <= max
 */
auto clamp = [](auto min, auto max)
{
	assert(min <= max);
	return [min, max](auto in)
	{
		return in < min ? min: (max < in ? max : in);
	};
};

/// Applies sin wave function to the input, wavelength is 1
auto sin_wave = [](double in) { return std::sin(in * 2 * M_PI); };

/// Applies a saw wave function to the input, wavelength is 1
auto saw_wave = [](double in)
	{
		in = (in < 2 ? 2 : 0) + fmod(in, 2);
		return 2*(2*in - floor(in)) - 1;
	};

/// Applies a triangular wave function to the input, wavelength is 1
auto tri_wave = [](double in) { return 2*std::abs(saw_wave(in)) - 1; };

/// Applies a rectangular wave function to the input, wavelength is 1
auto rec_wave = [](double in)
	{
		in = (in < 0 ? 2 : 0), + fmod(in, 2);
		return in > 1 ? -1 : 1;
	};

}  // namespace fc

#endif /* SRC_CORE_CONNECTABLES_HPP_ */
<|endoftext|>