Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9d83fab6c | ||
|
|
edcce32f24 | ||
|
|
68d265f0e7 | ||
|
|
b985bb5757 | ||
|
|
f030816385 | ||
|
|
6f9f694eff | ||
|
|
a0d7f0e172 | ||
|
|
9f98e438cb | ||
|
|
f029ca7b64 | ||
|
|
ca6c6dd6d3 | ||
|
|
4ba4ea5602 | ||
|
|
ca5f66da9f | ||
|
|
92d053d850 | ||
|
|
001ae80723 | ||
|
|
58d9a6d0b5 | ||
|
|
261316cb45 | ||
|
|
2ad4d2e672 | ||
|
|
1d9fa929fa | ||
|
|
26bb6236a0 | ||
|
|
b2acf5a623 | ||
|
|
bea88bb3a9 | ||
|
|
def2af2bdd | ||
|
|
6f6d705f22 | ||
|
|
e9c33104d3 | ||
|
|
985897f8ba | ||
|
|
4251b28e88 | ||
|
|
76540a211c | ||
|
|
904fc5a7dd | ||
|
|
4aebc159d5 | ||
|
|
f89227fc1a | ||
|
|
f50f383cc0 | ||
|
|
706ab387de | ||
|
|
cddfdb7d1d | ||
|
|
3c64578848 | ||
|
|
e1fe9b45e0 | ||
|
|
8fdc1d306f | ||
|
|
6e0c39bb68 | ||
|
|
f20b416cfe | ||
|
|
47e06b5216 | ||
|
|
bfcdf22705 | ||
|
|
d0db407faa | ||
|
|
d95b1c0441 | ||
|
|
837ba86d57 | ||
|
|
0cb1b463e1 |
@@ -286,6 +286,7 @@ else()
|
|||||||
find_package(ZLIB 1.2.4 REQUIRED)
|
find_package(ZLIB 1.2.4 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(LibLZMA REQUIRED)
|
||||||
find_package(CURL REQUIRED)
|
find_package(CURL REQUIRED)
|
||||||
|
|
||||||
if (SYSTEM_EXPAT)
|
if (SYSTEM_EXPAT)
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||||
|
|
||||||
|
#[=======================================================================[.rst:
|
||||||
|
FindLibLZMA
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Find LZMA compression algorithm headers and library.
|
||||||
|
|
||||||
|
|
||||||
|
Imported Targets
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This module defines :prop_tgt:`IMPORTED` target ``LibLZMA::LibLZMA``, if
|
||||||
|
liblzma has been found.
|
||||||
|
|
||||||
|
Result variables
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This module will set the following variables in your project:
|
||||||
|
|
||||||
|
``LIBLZMA_FOUND``
|
||||||
|
True if liblzma headers and library were found.
|
||||||
|
``LIBLZMA_INCLUDE_DIRS``
|
||||||
|
Directory where liblzma headers are located.
|
||||||
|
``LIBLZMA_LIBRARIES``
|
||||||
|
Lzma libraries to link against.
|
||||||
|
``LIBLZMA_HAS_AUTO_DECODER``
|
||||||
|
True if lzma_auto_decoder() is found (required).
|
||||||
|
``LIBLZMA_HAS_EASY_ENCODER``
|
||||||
|
True if lzma_easy_encoder() is found (required).
|
||||||
|
``LIBLZMA_HAS_LZMA_PRESET``
|
||||||
|
True if lzma_lzma_preset() is found (required).
|
||||||
|
``LIBLZMA_VERSION_MAJOR``
|
||||||
|
The major version of lzma
|
||||||
|
``LIBLZMA_VERSION_MINOR``
|
||||||
|
The minor version of lzma
|
||||||
|
``LIBLZMA_VERSION_PATCH``
|
||||||
|
The patch version of lzma
|
||||||
|
``LIBLZMA_VERSION_STRING``
|
||||||
|
version number as a string (ex: "5.0.3")
|
||||||
|
#]=======================================================================]
|
||||||
|
|
||||||
|
find_path(LIBLZMA_INCLUDE_DIR lzma.h )
|
||||||
|
if(NOT LIBLZMA_LIBRARY)
|
||||||
|
find_library(LIBLZMA_LIBRARY_RELEASE NAMES lzma liblzma NAMES_PER_DIR PATH_SUFFIXES lib)
|
||||||
|
find_library(LIBLZMA_LIBRARY_DEBUG NAMES lzmad liblzmad NAMES_PER_DIR PATH_SUFFIXES lib)
|
||||||
|
include(SelectLibraryConfigurations)
|
||||||
|
select_library_configurations(LIBLZMA)
|
||||||
|
else()
|
||||||
|
file(TO_CMAKE_PATH "${LIBLZMA_LIBRARY}" LIBLZMA_LIBRARY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(LIBLZMA_INCLUDE_DIR AND EXISTS "${LIBLZMA_INCLUDE_DIR}/lzma/version.h")
|
||||||
|
file(STRINGS "${LIBLZMA_INCLUDE_DIR}/lzma/version.h" LIBLZMA_HEADER_CONTENTS REGEX "#define LZMA_VERSION_[A-Z]+ [0-9]+")
|
||||||
|
|
||||||
|
string(REGEX REPLACE ".*#define LZMA_VERSION_MAJOR ([0-9]+).*" "\\1" LIBLZMA_VERSION_MAJOR "${LIBLZMA_HEADER_CONTENTS}")
|
||||||
|
string(REGEX REPLACE ".*#define LZMA_VERSION_MINOR ([0-9]+).*" "\\1" LIBLZMA_VERSION_MINOR "${LIBLZMA_HEADER_CONTENTS}")
|
||||||
|
string(REGEX REPLACE ".*#define LZMA_VERSION_PATCH ([0-9]+).*" "\\1" LIBLZMA_VERSION_PATCH "${LIBLZMA_HEADER_CONTENTS}")
|
||||||
|
|
||||||
|
set(LIBLZMA_VERSION_STRING "${LIBLZMA_VERSION_MAJOR}.${LIBLZMA_VERSION_MINOR}.${LIBLZMA_VERSION_PATCH}")
|
||||||
|
unset(LIBLZMA_HEADER_CONTENTS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# We're using new code known now as XZ, even library still been called LZMA
|
||||||
|
# it can be found in http://tukaani.org/xz/
|
||||||
|
# Avoid using old codebase
|
||||||
|
if (LIBLZMA_LIBRARY)
|
||||||
|
include(CheckLibraryExists)
|
||||||
|
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
|
||||||
|
set(CMAKE_REQUIRED_QUIET ${LibLZMA_FIND_QUIETLY})
|
||||||
|
if(NOT LIBLZMA_LIBRARY_RELEASE AND NOT LIBLZMA_LIBRARY_DEBUG)
|
||||||
|
set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY})
|
||||||
|
elseif(LIBLZMA_LIBRARY_RELEASE)
|
||||||
|
set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY_RELEASE})
|
||||||
|
elseif(LIBLZMA_LIBRARY_DEBUG)
|
||||||
|
set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY_DEBUG})
|
||||||
|
endif()
|
||||||
|
CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_auto_decoder "" LIBLZMA_HAS_AUTO_DECODER)
|
||||||
|
CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_easy_encoder "" LIBLZMA_HAS_EASY_ENCODER)
|
||||||
|
CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_lzma_preset "" LIBLZMA_HAS_LZMA_PRESET)
|
||||||
|
unset(LIBLZMA_LIBRARY_check)
|
||||||
|
set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(LibLZMA REQUIRED_VARS LIBLZMA_LIBRARY
|
||||||
|
LIBLZMA_INCLUDE_DIR
|
||||||
|
LIBLZMA_HAS_AUTO_DECODER
|
||||||
|
LIBLZMA_HAS_EASY_ENCODER
|
||||||
|
LIBLZMA_HAS_LZMA_PRESET
|
||||||
|
VERSION_VAR LIBLZMA_VERSION_STRING
|
||||||
|
)
|
||||||
|
mark_as_advanced( LIBLZMA_INCLUDE_DIR LIBLZMA_LIBRARY )
|
||||||
|
|
||||||
|
if (LIBLZMA_FOUND)
|
||||||
|
set(LIBLZMA_LIBRARIES ${LIBLZMA_LIBRARY})
|
||||||
|
set(LIBLZMA_INCLUDE_DIRS ${LIBLZMA_INCLUDE_DIR})
|
||||||
|
if(NOT TARGET LibLZMA::LibLZMA)
|
||||||
|
add_library(LibLZMA::LibLZMA UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(LibLZMA::LibLZMA PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES ${LIBLZMA_INCLUDE_DIR}
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES C)
|
||||||
|
|
||||||
|
if(LIBLZMA_LIBRARY_RELEASE)
|
||||||
|
set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY
|
||||||
|
IMPORTED_CONFIGURATIONS RELEASE)
|
||||||
|
set_target_properties(LibLZMA::LibLZMA PROPERTIES
|
||||||
|
IMPORTED_LOCATION_RELEASE "${LIBLZMA_LIBRARY_RELEASE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(LIBLZMA_LIBRARY_DEBUG)
|
||||||
|
set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY
|
||||||
|
IMPORTED_CONFIGURATIONS DEBUG)
|
||||||
|
set_target_properties(LibLZMA::LibLZMA PROPERTIES
|
||||||
|
IMPORTED_LOCATION_DEBUG "${LIBLZMA_LIBRARY_DEBUG}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT LIBLZMA_LIBRARY_RELEASE AND NOT LIBLZMA_LIBRARY_DEBUG)
|
||||||
|
set_target_properties(LibLZMA::LibLZMA PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${LIBLZMA_LIBRARY}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
include(CMakeFindDependencyMacro)
|
include(CMakeFindDependencyMacro)
|
||||||
|
|
||||||
find_dependency(ZLIB)
|
find_dependency(ZLIB)
|
||||||
|
find_dependency(LibLZMA)
|
||||||
find_dependency(Threads)
|
find_dependency(Threads)
|
||||||
|
|
||||||
# OSG
|
# OSG
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2020.3.2
|
2020.3.7
|
||||||
|
|||||||
@@ -168,7 +168,9 @@ target_link_libraries(SimGearCore PRIVATE
|
|||||||
${COCOA_LIBRARY}
|
${COCOA_LIBRARY}
|
||||||
${CURL_LIBRARIES}
|
${CURL_LIBRARIES}
|
||||||
${WINSOCK_LIBRARY}
|
${WINSOCK_LIBRARY}
|
||||||
${SHLWAPI_LIBRARY})
|
${SHLWAPI_LIBRARY}
|
||||||
|
LibLZMA::LibLZMA
|
||||||
|
)
|
||||||
|
|
||||||
if(SYSTEM_EXPAT)
|
if(SYSTEM_EXPAT)
|
||||||
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})
|
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})
|
||||||
|
|||||||
@@ -691,6 +691,10 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
_sampling_dirty = true;
|
_sampling_dirty = true;
|
||||||
}
|
}
|
||||||
|
else if( name == "anisotropy" )
|
||||||
|
{
|
||||||
|
_texture.setMaxAnisotropy( node->getFloatValue() );
|
||||||
|
}
|
||||||
else if( name == "additive-blend" )
|
else if( name == "additive-blend" )
|
||||||
{
|
{
|
||||||
_texture.useAdditiveBlend( node->getBoolValue() );
|
_texture.useAdditiveBlend( node->getBoolValue() );
|
||||||
|
|||||||
@@ -202,6 +202,12 @@ namespace canvas
|
|||||||
updateSampling();
|
updateSampling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void ODGauge::setMaxAnisotropy(float anis)
|
||||||
|
{
|
||||||
|
texture->setMaxAnisotropy(anis);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::setRender(bool render)
|
void ODGauge::setRender(bool render)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ namespace canvas
|
|||||||
int coverage_samples = 0,
|
int coverage_samples = 0,
|
||||||
int color_samples = 0 );
|
int color_samples = 0 );
|
||||||
|
|
||||||
|
void setMaxAnisotropy(float anis);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/Disable updating the texture (If disabled the contents of the
|
* Enable/Disable updating the texture (If disabled the contents of the
|
||||||
* texture remains with the outcome of the last rendering pass)
|
* texture remains with the outcome of the last rendering pass)
|
||||||
|
|||||||
@@ -218,6 +218,9 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
|||||||
#define SG_OBJECT_RANGE_ROUGH 9000.0
|
#define SG_OBJECT_RANGE_ROUGH 9000.0
|
||||||
#define SG_OBJECT_RANGE_DETAILED 1500.0
|
#define SG_OBJECT_RANGE_DETAILED 1500.0
|
||||||
|
|
||||||
|
/** Minimum expiry time of PagedLOD within the Tile. Overridden by /sim/rendering/plod-minimum-expiry-time-secs **/
|
||||||
|
#define SG_TILE_MIN_EXPIRY 180.0
|
||||||
|
|
||||||
/** Radius of scenery tiles in m **/
|
/** Radius of scenery tiles in m **/
|
||||||
#define SG_TILE_RADIUS 14000.0
|
#define SG_TILE_RADIUS 14000.0
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ include (SimGearComponent)
|
|||||||
|
|
||||||
set(HEADERS debug_types.h
|
set(HEADERS debug_types.h
|
||||||
logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx
|
logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx
|
||||||
LogCallback.hxx LogEntry.hxx)
|
LogCallback.hxx LogEntry.hxx
|
||||||
|
ErrorReportingCallback.hxx)
|
||||||
|
|
||||||
set(SOURCES logstream.cxx BufferedLogCallback.cxx
|
set(SOURCES logstream.cxx BufferedLogCallback.cxx
|
||||||
LogCallback.cxx LogEntry.cxx)
|
LogCallback.cxx LogEntry.cxx
|
||||||
|
ErrorReportingCallback.cxx
|
||||||
|
)
|
||||||
|
|
||||||
simgear_component(debug debug "${SOURCES}" "${HEADERS}")
|
simgear_component(debug debug "${SOURCES}" "${HEADERS}")
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 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
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <simgear_config.h>
|
||||||
|
|
||||||
|
#include "ErrorReportingCallback.hxx"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
namespace simgear {
|
||||||
|
|
||||||
|
static ErrorReportCallback static_callback;
|
||||||
|
|
||||||
|
void setErrorReportCallback(ErrorReportCallback cb)
|
||||||
|
{
|
||||||
|
static_callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void reportError(const std::string& msg, const std::string& more)
|
||||||
|
{
|
||||||
|
if (!static_callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
static_callback(msg, more, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reportFatalError(const std::string& msg, const std::string& more)
|
||||||
|
{
|
||||||
|
if (!static_callback)
|
||||||
|
return;
|
||||||
|
static_callback(msg, more, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 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
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace simgear {
|
||||||
|
|
||||||
|
void reportError(const std::string& msg, const std::string& more = {});
|
||||||
|
|
||||||
|
void reportFatalError(const std::string& msg, const std::string& more = {});
|
||||||
|
|
||||||
|
using ErrorReportCallback = std::function<void(const std::string& msg, const std::string& more, bool isFatal)>;
|
||||||
|
|
||||||
|
void setErrorReportCallback(ErrorReportCallback cb);
|
||||||
|
|
||||||
|
} // namespace simgear
|
||||||
@@ -45,14 +45,6 @@ namespace simgear
|
|||||||
std::atomic<int> receiveDepth;
|
std::atomic<int> receiveDepth;
|
||||||
std::atomic<int> sentMessageCount;
|
std::atomic<int> sentMessageCount;
|
||||||
|
|
||||||
void UnlockList()
|
|
||||||
{
|
|
||||||
_lock.unlock();
|
|
||||||
}
|
|
||||||
void LockList()
|
|
||||||
{
|
|
||||||
_lock.lock();
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
Transmitter() : receiveDepth(0), sentMessageCount(0)
|
Transmitter() : receiveDepth(0), sentMessageCount(0)
|
||||||
{
|
{
|
||||||
@@ -69,20 +61,17 @@ namespace simgear
|
|||||||
// most recently registered recipients should process the messages/events first.
|
// most recently registered recipients should process the messages/events first.
|
||||||
virtual void Register(IReceiver& r)
|
virtual void Register(IReceiver& r)
|
||||||
{
|
{
|
||||||
LockList();
|
std::lock_guard<std::mutex> scopeLock(_lock);
|
||||||
recipient_list.push_back(&r);
|
recipient_list.push_back(&r);
|
||||||
r.OnRegisteredAtTransmitter(this);
|
r.OnRegisteredAtTransmitter(this);
|
||||||
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
|
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
|
||||||
deleted_recipients.remove(&r);
|
deleted_recipients.remove(&r);
|
||||||
|
|
||||||
UnlockList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes an object from receving message from this transmitter
|
// Removes an object from receving message from this transmitter
|
||||||
virtual void DeRegister(IReceiver& R)
|
virtual void DeRegister(IReceiver& R)
|
||||||
{
|
{
|
||||||
LockList();
|
std::lock_guard<std::mutex> scopeLock(_lock);
|
||||||
//printf("Remove %x\n", &R);
|
|
||||||
if (recipient_list.size())
|
if (recipient_list.size())
|
||||||
{
|
{
|
||||||
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
|
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
|
||||||
@@ -93,7 +82,6 @@ namespace simgear
|
|||||||
deleted_recipients.push_back(&R);
|
deleted_recipients.push_back(&R);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UnlockList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify all registered recipients. Stop when receipt status of abort or finished are received.
|
// Notify all registered recipients. Stop when receipt status of abort or finished are received.
|
||||||
@@ -107,69 +95,68 @@ namespace simgear
|
|||||||
ReceiptStatus return_status = ReceiptStatusNotProcessed;
|
ReceiptStatus return_status = ReceiptStatusNotProcessed;
|
||||||
|
|
||||||
sentMessageCount++;
|
sentMessageCount++;
|
||||||
try
|
|
||||||
|
std::vector<IReceiver*> temp;
|
||||||
{
|
{
|
||||||
LockList();
|
std::lock_guard<std::mutex> scopeLock(_lock);
|
||||||
if (receiveDepth == 0)
|
if (receiveDepth == 0)
|
||||||
deleted_recipients.clear();
|
deleted_recipients.clear();
|
||||||
receiveDepth++;
|
receiveDepth++;
|
||||||
std::vector<IReceiver*> temp(recipient_list.size());
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
|
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
|
||||||
{
|
{
|
||||||
temp[idx++] = *i;
|
temp.push_back(*i);
|
||||||
}
|
}
|
||||||
UnlockList();
|
}
|
||||||
int tempSize = temp.size();
|
int tempSize = temp.size();
|
||||||
for (int index = 0; index < tempSize; index++)
|
for (int index = 0; index < tempSize; index++)
|
||||||
|
{
|
||||||
|
IReceiver* R = temp[index];
|
||||||
{
|
{
|
||||||
IReceiver* R = temp[index];
|
std::lock_guard<std::mutex> scopeLock(_lock);
|
||||||
LockList();
|
|
||||||
if (deleted_recipients.size())
|
if (deleted_recipients.size())
|
||||||
{
|
{
|
||||||
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
|
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
|
||||||
{
|
{
|
||||||
UnlockList();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UnlockList();
|
|
||||||
if (R)
|
|
||||||
{
|
|
||||||
ReceiptStatus rstat = R->Receive(M);
|
|
||||||
switch (rstat)
|
|
||||||
{
|
|
||||||
case ReceiptStatusFail:
|
|
||||||
return_status = ReceiptStatusFail;
|
|
||||||
break;
|
|
||||||
case ReceiptStatusPending:
|
|
||||||
return_status = ReceiptStatusPending;
|
|
||||||
break;
|
|
||||||
case ReceiptStatusPendingFinished:
|
|
||||||
return rstat;
|
|
||||||
|
|
||||||
case ReceiptStatusNotProcessed:
|
|
||||||
break;
|
|
||||||
case ReceiptStatusOK:
|
|
||||||
if (return_status == ReceiptStatusNotProcessed)
|
|
||||||
return_status = rstat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ReceiptStatusAbort:
|
|
||||||
return ReceiptStatusAbort;
|
|
||||||
|
|
||||||
case ReceiptStatusFinished:
|
|
||||||
return ReceiptStatusOK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if (R)
|
||||||
|
{
|
||||||
|
ReceiptStatus rstat = R->Receive(M);
|
||||||
|
switch (rstat)
|
||||||
|
{
|
||||||
|
case ReceiptStatusFail:
|
||||||
|
return_status = ReceiptStatusFail;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReceiptStatusPending:
|
||||||
|
return_status = ReceiptStatusPending;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReceiptStatusPendingFinished:
|
||||||
|
return rstat;
|
||||||
|
|
||||||
|
case ReceiptStatusNotProcessed:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReceiptStatusOK:
|
||||||
|
if (return_status == ReceiptStatusNotProcessed)
|
||||||
|
return_status = rstat;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReceiptStatusAbort:
|
||||||
|
return ReceiptStatusAbort;
|
||||||
|
|
||||||
|
case ReceiptStatusFinished:
|
||||||
|
return ReceiptStatusOK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
// return_status = ReceiptStatusAbort;
|
|
||||||
}
|
|
||||||
receiveDepth--;
|
receiveDepth--;
|
||||||
return return_status;
|
return return_status;
|
||||||
}
|
}
|
||||||
@@ -177,9 +164,8 @@ namespace simgear
|
|||||||
// number of currently registered recipients
|
// number of currently registered recipients
|
||||||
virtual int Count()
|
virtual int Count()
|
||||||
{
|
{
|
||||||
LockList();
|
std::lock_guard<std::mutex> scopeLock(_lock);
|
||||||
return recipient_list.size();
|
return recipient_list.size();
|
||||||
UnlockList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of sent messages.
|
// number of sent messages.
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/math/sg_random.h>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
#include "metar.hxx"
|
#include "metar.hxx"
|
||||||
@@ -646,16 +647,21 @@ bool SGMetar::scanWind()
|
|||||||
double gust = NaN;
|
double gust = NaN;
|
||||||
if (*m == 'G') {
|
if (*m == 'G') {
|
||||||
m++;
|
m++;
|
||||||
if (!scanNumber(&m, &i, 2, 3))
|
if (!strncmp(m, "//", 2)) // speed not measurable
|
||||||
|
m += 2, i = -1;
|
||||||
|
else if (!scanNumber(&m, &i, 2, 3))
|
||||||
return false;
|
return false;
|
||||||
gust = i;
|
|
||||||
|
if (i != -1)
|
||||||
|
gust = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
double factor;
|
double factor;
|
||||||
if (!strncmp(m, "KT", 2))
|
if (!strncmp(m, "KT", 2))
|
||||||
m += 2, factor = SG_KT_TO_MPS;
|
m += 2, factor = SG_KT_TO_MPS;
|
||||||
else if (!strncmp(m, "KMH", 3))
|
else if (!strncmp(m, "KMH", 3)) // invalid Km/h
|
||||||
m += 3, factor = SG_KMH_TO_MPS;
|
m += 3, factor = SG_KMH_TO_MPS;
|
||||||
else if (!strncmp(m, "KPH", 3)) // ??
|
else if (!strncmp(m, "KPH", 3)) // invalid Km/h
|
||||||
m += 3, factor = SG_KMH_TO_MPS;
|
m += 3, factor = SG_KMH_TO_MPS;
|
||||||
else if (!strncmp(m, "MPS", 3))
|
else if (!strncmp(m, "MPS", 3))
|
||||||
m += 3, factor = 1.0;
|
m += 3, factor = 1.0;
|
||||||
@@ -680,18 +686,28 @@ bool SGMetar::scanVariability()
|
|||||||
{
|
{
|
||||||
char *m = _m;
|
char *m = _m;
|
||||||
int from, to;
|
int from, to;
|
||||||
if (!scanNumber(&m, &from, 3))
|
|
||||||
|
if (!strncmp(m, "///", 3)) // direction not measurable
|
||||||
|
m += 3, from = -1;
|
||||||
|
else if (!scanNumber(&m, &from, 3))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (*m++ != 'V')
|
if (*m++ != 'V')
|
||||||
return false;
|
return false;
|
||||||
if (!scanNumber(&m, &to, 3))
|
|
||||||
|
if (!strncmp(m, "///", 3)) // direction not measurable
|
||||||
|
m += 3, to = -1;
|
||||||
|
else if (!scanNumber(&m, &to, 3))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!scanBoundary(&m))
|
if (!scanBoundary(&m))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_m = m;
|
_m = m;
|
||||||
_wind_range_from = from;
|
_wind_range_from = from;
|
||||||
_wind_range_to = to;
|
_wind_range_to = to;
|
||||||
_grpcount++;
|
_grpcount++;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1053,9 +1069,12 @@ bool SGMetar::scanSkyCondition()
|
|||||||
return false;
|
return false;
|
||||||
m += i;
|
m += i;
|
||||||
|
|
||||||
if (!strncmp(m, "///", 3)) // vis not measurable (e.g. because of heavy snowing)
|
if (!strncmp(m, "///", 3)) { // vis not measurable (e.g. because of heavy snowing)
|
||||||
m += 3, i = -1;
|
m += 3, i = -1;
|
||||||
else if (scanBoundary(&m)) {
|
sg_srandom_time();
|
||||||
|
// randomize the base height to avoid the black sky issue
|
||||||
|
i = 50 + static_cast<int>(sg_random() * 250.0); // range [5,000, 30,000]
|
||||||
|
} else if (scanBoundary(&m)) {
|
||||||
_m = m;
|
_m = m;
|
||||||
return true; // ignore single OVC/BKN/...
|
return true; // ignore single OVC/BKN/...
|
||||||
} else if (!scanNumber(&m, &i, 3))
|
} else if (!scanNumber(&m, &i, 3))
|
||||||
|
|||||||
@@ -81,6 +81,15 @@ void test_sensor_failure_wind()
|
|||||||
SGMetar m1("2020/10/23 16:55 LIVD 231655Z /////KT 9999 OVC025 10/08 Q1020 RMK OVC VIS MIN 9999 BLU");
|
SGMetar m1("2020/10/23 16:55 LIVD 231655Z /////KT 9999 OVC025 10/08 Q1020 RMK OVC VIS MIN 9999 BLU");
|
||||||
SG_CHECK_EQUAL(m1.getWindDir(), -1);
|
SG_CHECK_EQUAL(m1.getWindDir(), -1);
|
||||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), -1, TEST_EPSILON);
|
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), -1, TEST_EPSILON);
|
||||||
|
|
||||||
|
SGMetar m2("2020/10/21 16:55 LIVD 211655Z /////KT CAVOK 07/03 Q1023 RMK SKC VIS MIN 9999 BLU");
|
||||||
|
SG_CHECK_EQUAL(m2.getWindDir(), -1);
|
||||||
|
SG_CHECK_EQUAL_EP2(m2.getWindSpeed_kt(), -1, TEST_EPSILON);
|
||||||
|
|
||||||
|
SGMetar m3("2020/11/17 16:00 CYAZ 171600Z 14040G//KT 10SM -RA OVC012 12/11 A2895 RMK NS8 VIA CYXY SLP806 DENSITY ALT 900FT");
|
||||||
|
SG_CHECK_EQUAL(m3.getWindDir(), 140);
|
||||||
|
SG_CHECK_EQUAL_EP2(m3.getWindSpeed_kt(), 40, TEST_EPSILON);
|
||||||
|
SG_CHECK_EQUAL_EP2(m3.getGustSpeed_kt(), SGMetarNaN, TEST_EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_wind_unit_not_specified()
|
void test_wind_unit_not_specified()
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (C) 2021 James Turner - <james@flightgear.org>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 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
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "untar.hxx"
|
||||||
|
|
||||||
|
namespace simgear {
|
||||||
|
|
||||||
|
class ArchiveExtractorPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArchiveExtractorPrivate(ArchiveExtractor* o) : outer(o)
|
||||||
|
{
|
||||||
|
assert(outer);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ArchiveExtractorPrivate() = default;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INVALID = 0,
|
||||||
|
READING_HEADER,
|
||||||
|
READING_FILE,
|
||||||
|
READING_PADDING,
|
||||||
|
READING_PAX_GLOBAL_ATTRIBUTES,
|
||||||
|
READING_PAX_FILE_ATTRIBUTES,
|
||||||
|
PRE_END_OF_ARCHVE,
|
||||||
|
END_OF_ARCHIVE,
|
||||||
|
ERROR_STATE, ///< states above this are error conditions
|
||||||
|
BAD_ARCHIVE,
|
||||||
|
BAD_DATA,
|
||||||
|
FILTER_STOPPED
|
||||||
|
} State;
|
||||||
|
|
||||||
|
State state = INVALID;
|
||||||
|
ArchiveExtractor* outer = nullptr;
|
||||||
|
|
||||||
|
virtual void extractBytes(const uint8_t* bytes, size_t count) = 0;
|
||||||
|
|
||||||
|
virtual void flush() = 0;
|
||||||
|
|
||||||
|
SGPath extractRootPath()
|
||||||
|
{
|
||||||
|
return outer->_rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveExtractor::PathResult filterPath(std::string& pathToExtract)
|
||||||
|
{
|
||||||
|
return outer->filterPath(pathToExtract);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isSafePath(const std::string& p) const
|
||||||
|
{
|
||||||
|
if (p.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject absolute paths
|
||||||
|
if (p.at(0) == '/') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject paths containing '..'
|
||||||
|
size_t doubleDot = p.find("..");
|
||||||
|
if (doubleDot != std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on POSIX could use realpath to sanity check
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace simgear
|
||||||
+85
-47
@@ -61,6 +61,10 @@ public:
|
|||||||
|
|
||||||
struct dns_ctx * ctx;
|
struct dns_ctx * ctx;
|
||||||
static size_t instanceCounter;
|
static size_t instanceCounter;
|
||||||
|
|
||||||
|
using RequestVec = std::vector<Request_ptr>;
|
||||||
|
|
||||||
|
RequestVec _activeRequests;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t Client::ClientPrivate::instanceCounter = 0;
|
size_t Client::ClientPrivate::instanceCounter = 0;
|
||||||
@@ -78,6 +82,11 @@ Request::~Request()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Request::cancel()
|
||||||
|
{
|
||||||
|
_cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Request::isTimeout() const
|
bool Request::isTimeout() const
|
||||||
{
|
{
|
||||||
return (time(NULL) - _start) > _timeout_secs;
|
return (time(NULL) - _start) > _timeout_secs;
|
||||||
@@ -114,18 +123,20 @@ static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
|
|||||||
{
|
{
|
||||||
SRVRequest * r = static_cast<SRVRequest*>(data);
|
SRVRequest * r = static_cast<SRVRequest*>(data);
|
||||||
if (result) {
|
if (result) {
|
||||||
r->cname = result->dnssrv_cname;
|
if (!r->isCancelled()) {
|
||||||
r->qname = result->dnssrv_qname;
|
r->cname = result->dnssrv_cname;
|
||||||
r->ttl = result->dnssrv_ttl;
|
r->qname = result->dnssrv_qname;
|
||||||
for (int i = 0; i < result->dnssrv_nrr; i++) {
|
r->ttl = result->dnssrv_ttl;
|
||||||
SRVRequest::SRV_ptr srv(new SRVRequest::SRV);
|
for (int i = 0; i < result->dnssrv_nrr; i++) {
|
||||||
r->entries.push_back(srv);
|
SRVRequest::SRV_ptr srv(new SRVRequest::SRV);
|
||||||
srv->priority = result->dnssrv_srv[i].priority;
|
r->entries.push_back(srv);
|
||||||
srv->weight = result->dnssrv_srv[i].weight;
|
srv->priority = result->dnssrv_srv[i].priority;
|
||||||
srv->port = result->dnssrv_srv[i].port;
|
srv->weight = result->dnssrv_srv[i].weight;
|
||||||
srv->target = result->dnssrv_srv[i].name;
|
srv->port = result->dnssrv_srv[i].port;
|
||||||
|
srv->target = result->dnssrv_srv[i].name;
|
||||||
|
}
|
||||||
|
std::sort(r->entries.begin(), r->entries.end(), sortSRV);
|
||||||
}
|
}
|
||||||
std::sort( r->entries.begin(), r->entries.end(), sortSRV );
|
|
||||||
free(result);
|
free(result);
|
||||||
}
|
}
|
||||||
r->setComplete();
|
r->setComplete();
|
||||||
@@ -134,11 +145,16 @@ static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
|
|||||||
void SRVRequest::submit( Client * client )
|
void SRVRequest::submit( Client * client )
|
||||||
{
|
{
|
||||||
// if service is defined, pass service and protocol
|
// if service is defined, pass service and protocol
|
||||||
if (!dns_submit_srv(client->d->ctx, getDn().c_str(), _service.empty() ? NULL : _service.c_str(), _service.empty() ? NULL : _protocol.c_str(), 0, dnscbSRV, this )) {
|
auto q = dns_submit_srv(client->d->ctx, getDn().c_str(), _service.empty() ? NULL : _service.c_str(),
|
||||||
|
_service.empty() ? NULL : _protocol.c_str(),
|
||||||
|
0, dnscbSRV, this);
|
||||||
|
|
||||||
|
if (!q) {
|
||||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_start = time(NULL);
|
_start = time(NULL);
|
||||||
|
_query = q;
|
||||||
}
|
}
|
||||||
|
|
||||||
TXTRequest::TXTRequest( const std::string & dn ) :
|
TXTRequest::TXTRequest( const std::string & dn ) :
|
||||||
@@ -151,22 +167,24 @@ static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
|
|||||||
{
|
{
|
||||||
TXTRequest * r = static_cast<TXTRequest*>(data);
|
TXTRequest * r = static_cast<TXTRequest*>(data);
|
||||||
if (result) {
|
if (result) {
|
||||||
r->cname = result->dnstxt_cname;
|
if (!r->isCancelled()) {
|
||||||
r->qname = result->dnstxt_qname;
|
r->cname = result->dnstxt_cname;
|
||||||
r->ttl = result->dnstxt_ttl;
|
r->qname = result->dnstxt_qname;
|
||||||
for (int i = 0; i < result->dnstxt_nrr; i++) {
|
r->ttl = result->dnstxt_ttl;
|
||||||
//TODO: interprete the .len field of dnstxt_txt?
|
for (int i = 0; i < result->dnstxt_nrr; i++) {
|
||||||
auto rawTxt = reinterpret_cast<char*>(result->dnstxt_txt[i].txt);
|
//TODO: interprete the .len field of dnstxt_txt?
|
||||||
if (!rawTxt) {
|
auto rawTxt = reinterpret_cast<char*>(result->dnstxt_txt[i].txt);
|
||||||
continue;
|
if (!rawTxt) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const string txt{rawTxt};
|
const string txt{rawTxt};
|
||||||
r->entries.push_back(txt);
|
r->entries.push_back(txt);
|
||||||
string_list tokens = simgear::strutils::split( txt, "=", 1 );
|
string_list tokens = simgear::strutils::split(txt, "=", 1);
|
||||||
if( tokens.size() == 2 ) {
|
if (tokens.size() == 2) {
|
||||||
r->attributes[tokens[0]] = tokens[1];
|
r->attributes[tokens[0]] = tokens[1];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(result);
|
free(result);
|
||||||
}
|
}
|
||||||
@@ -176,11 +194,13 @@ static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
|
|||||||
void TXTRequest::submit( Client * client )
|
void TXTRequest::submit( Client * client )
|
||||||
{
|
{
|
||||||
// protocol and service an already encoded in DN so pass in NULL for both
|
// protocol and service an already encoded in DN so pass in NULL for both
|
||||||
if (!dns_submit_txt(client->d->ctx, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this )) {
|
auto q = dns_submit_txt(client->d->ctx, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this);
|
||||||
|
if (!q) {
|
||||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_start = time(NULL);
|
_start = time(NULL);
|
||||||
|
_query = q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -195,27 +215,29 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
|
|||||||
{
|
{
|
||||||
NAPTRRequest * r = static_cast<NAPTRRequest*>(data);
|
NAPTRRequest * r = static_cast<NAPTRRequest*>(data);
|
||||||
if (result) {
|
if (result) {
|
||||||
r->cname = result->dnsnaptr_cname;
|
if (!r->isCancelled()) {
|
||||||
r->qname = result->dnsnaptr_qname;
|
r->cname = result->dnsnaptr_cname;
|
||||||
r->ttl = result->dnsnaptr_ttl;
|
r->qname = result->dnsnaptr_qname;
|
||||||
for (int i = 0; i < result->dnsnaptr_nrr; i++) {
|
r->ttl = result->dnsnaptr_ttl;
|
||||||
if( !r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service )
|
for (int i = 0; i < result->dnsnaptr_nrr; i++) {
|
||||||
continue;
|
if (!r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service)
|
||||||
|
continue;
|
||||||
|
|
||||||
//TODO: case ignore and result flags may have more than one flag
|
//TODO: case ignore and result flags may have more than one flag
|
||||||
if( !r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags )
|
if (!r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
|
NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
|
||||||
r->entries.push_back(naptr);
|
r->entries.push_back(naptr);
|
||||||
naptr->order = result->dnsnaptr_naptr[i].order;
|
naptr->order = result->dnsnaptr_naptr[i].order;
|
||||||
naptr->preference = result->dnsnaptr_naptr[i].preference;
|
naptr->preference = result->dnsnaptr_naptr[i].preference;
|
||||||
naptr->flags = result->dnsnaptr_naptr[i].flags;
|
naptr->flags = result->dnsnaptr_naptr[i].flags;
|
||||||
naptr->service = result->dnsnaptr_naptr[i].service;
|
naptr->service = result->dnsnaptr_naptr[i].service;
|
||||||
naptr->regexp = result->dnsnaptr_naptr[i].regexp;
|
naptr->regexp = result->dnsnaptr_naptr[i].regexp;
|
||||||
naptr->replacement = result->dnsnaptr_naptr[i].replacement;
|
naptr->replacement = result->dnsnaptr_naptr[i].replacement;
|
||||||
|
}
|
||||||
|
std::sort(r->entries.begin(), r->entries.end(), sortNAPTR);
|
||||||
}
|
}
|
||||||
std::sort( r->entries.begin(), r->entries.end(), sortNAPTR );
|
|
||||||
free(result);
|
free(result);
|
||||||
}
|
}
|
||||||
r->setComplete();
|
r->setComplete();
|
||||||
@@ -223,11 +245,13 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
|
|||||||
|
|
||||||
void NAPTRRequest::submit( Client * client )
|
void NAPTRRequest::submit( Client * client )
|
||||||
{
|
{
|
||||||
if (!dns_submit_naptr(client->d->ctx, getDn().c_str(), 0, dnscbNAPTR, this )) {
|
auto q = dns_submit_naptr(client->d->ctx, getDn().c_str(), 0, dnscbNAPTR, this);
|
||||||
|
if (!q) {
|
||||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_start = time(NULL);
|
_start = time(NULL);
|
||||||
|
_query = q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -242,6 +266,7 @@ Client::Client() :
|
|||||||
|
|
||||||
void Client::makeRequest(const Request_ptr& r)
|
void Client::makeRequest(const Request_ptr& r)
|
||||||
{
|
{
|
||||||
|
d->_activeRequests.push_back(r);
|
||||||
r->submit(this);
|
r->submit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +277,19 @@ void Client::update(int waitTimeout)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
dns_ioevent(d->ctx, now);
|
dns_ioevent(d->ctx, now);
|
||||||
|
|
||||||
|
// drop our owning ref to completed requests,
|
||||||
|
// and cancel any which timed out
|
||||||
|
auto it = std::remove_if(d->_activeRequests.begin(), d->_activeRequests.end(),
|
||||||
|
[this](const Request_ptr& r) {
|
||||||
|
if (r->isTimeout()) {
|
||||||
|
dns_cancel(d->ctx, reinterpret_cast<struct dns_query*>(r->_query));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r->isComplete();
|
||||||
|
});
|
||||||
|
d->_activeRequests.erase(it, d->_activeRequests.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // of namespace DNS
|
} // of namespace DNS
|
||||||
|
|||||||
@@ -40,28 +40,38 @@ namespace DNS
|
|||||||
{
|
{
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
|
|
||||||
|
using UDNSQueryPtr = void*;
|
||||||
|
|
||||||
class Request : public SGReferenced
|
class Request : public SGReferenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Request( const std::string & dn );
|
Request( const std::string & dn );
|
||||||
virtual ~Request();
|
virtual ~Request();
|
||||||
std::string getDn() const { return _dn; }
|
const std::string& getDn() const { return _dn; }
|
||||||
int getType() const { return _type; }
|
int getType() const { return _type; }
|
||||||
bool isComplete() const { return _complete; }
|
bool isComplete() const { return _complete; }
|
||||||
bool isTimeout() const;
|
bool isTimeout() const;
|
||||||
void setComplete( bool b = true ) { _complete = b; }
|
void setComplete( bool b = true ) { _complete = b; }
|
||||||
|
bool isCancelled() const { return _cancelled; }
|
||||||
|
|
||||||
virtual void submit( Client * client) = 0;
|
virtual void submit( Client * client) = 0;
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
|
||||||
std::string cname;
|
std::string cname;
|
||||||
std::string qname;
|
std::string qname;
|
||||||
unsigned ttl;
|
unsigned ttl;
|
||||||
protected:
|
protected:
|
||||||
|
friend class Client;
|
||||||
|
|
||||||
|
UDNSQueryPtr _query = nullptr;
|
||||||
std::string _dn;
|
std::string _dn;
|
||||||
int _type;
|
int _type;
|
||||||
bool _complete;
|
bool _complete;
|
||||||
time_t _timeout_secs;
|
time_t _timeout_secs;
|
||||||
time_t _start;
|
time_t _start;
|
||||||
|
bool _cancelled = false;
|
||||||
};
|
};
|
||||||
typedef SGSharedPtr<Request> Request_ptr;
|
typedef SGSharedPtr<Request> Request_ptr;
|
||||||
|
|
||||||
@@ -69,7 +79,7 @@ class NAPTRRequest : public Request
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NAPTRRequest( const std::string & dn );
|
NAPTRRequest( const std::string & dn );
|
||||||
virtual void submit( Client * client );
|
void submit(Client* client) override;
|
||||||
|
|
||||||
struct NAPTR : SGReferenced {
|
struct NAPTR : SGReferenced {
|
||||||
int order;
|
int order;
|
||||||
@@ -92,7 +102,7 @@ class SRVRequest : public Request
|
|||||||
public:
|
public:
|
||||||
SRVRequest( const std::string & dn );
|
SRVRequest( const std::string & dn );
|
||||||
SRVRequest( const std::string & dn, const string & service, const string & protocol );
|
SRVRequest( const std::string & dn, const string & service, const string & protocol );
|
||||||
virtual void submit( Client * client );
|
void submit(Client* client) override;
|
||||||
|
|
||||||
struct SRV : SGReferenced {
|
struct SRV : SGReferenced {
|
||||||
int priority;
|
int priority;
|
||||||
@@ -112,7 +122,7 @@ class TXTRequest : public Request
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TXTRequest( const std::string & dn );
|
TXTRequest( const std::string & dn );
|
||||||
virtual void submit( Client * client );
|
void submit(Client* client) override;
|
||||||
|
|
||||||
typedef std::vector<string> TXT_list;
|
typedef std::vector<string> TXT_list;
|
||||||
typedef std::map<std::string,std::string> TXT_Attribute_map;
|
typedef std::map<std::string,std::string> TXT_Attribute_map;
|
||||||
|
|||||||
+16
-16
@@ -81,21 +81,8 @@ void Client::ClientPrivate::createCurlMulti() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::Client() :
|
Client::Client()
|
||||||
d(new ClientPrivate)
|
|
||||||
{
|
{
|
||||||
d->proxyPort = 0;
|
|
||||||
d->maxConnections = 4;
|
|
||||||
d->maxHostConnections = 4;
|
|
||||||
d->bytesTransferred = 0;
|
|
||||||
d->lastTransferRate = 0;
|
|
||||||
d->timeTransferSample.stamp();
|
|
||||||
d->totalBytesDownloaded = 0;
|
|
||||||
d->maxPipelineDepth = 5;
|
|
||||||
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
|
|
||||||
|
|
||||||
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
|
|
||||||
|
|
||||||
static bool didInitCurlGlobal = false;
|
static bool didInitCurlGlobal = false;
|
||||||
static std::mutex initMutex;
|
static std::mutex initMutex;
|
||||||
|
|
||||||
@@ -105,7 +92,7 @@ Client::Client() :
|
|||||||
didInitCurlGlobal = true;
|
didInitCurlGlobal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
d->createCurlMulti();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client()
|
Client::~Client()
|
||||||
@@ -139,8 +126,21 @@ void Client::setMaxPipelineDepth(unsigned int depth)
|
|||||||
|
|
||||||
void Client::reset()
|
void Client::reset()
|
||||||
{
|
{
|
||||||
curl_multi_cleanup(d->curlMulti);
|
if (d.get()) {
|
||||||
|
curl_multi_cleanup(d->curlMulti);
|
||||||
|
}
|
||||||
|
|
||||||
d.reset(new ClientPrivate);
|
d.reset(new ClientPrivate);
|
||||||
|
|
||||||
|
d->proxyPort = 0;
|
||||||
|
d->maxConnections = 4;
|
||||||
|
d->maxHostConnections = 4;
|
||||||
|
d->bytesTransferred = 0;
|
||||||
|
d->lastTransferRate = 0;
|
||||||
|
d->timeTransferSample.stamp();
|
||||||
|
d->totalBytesDownloaded = 0;
|
||||||
|
d->maxPipelineDepth = 5;
|
||||||
|
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
|
||||||
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
|
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
|
||||||
d->createCurlMulti();
|
d->createCurlMulti();
|
||||||
}
|
}
|
||||||
|
|||||||
+199
-143
@@ -20,15 +20,15 @@
|
|||||||
|
|
||||||
#include "HTTPRepository.hxx"
|
#include "HTTPRepository.hxx"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <fstream>
|
#include <sstream>
|
||||||
#include <limits>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
@@ -143,8 +143,8 @@ class HTTPDirectory
|
|||||||
|
|
||||||
HTTPRepository::EntryType type;
|
HTTPRepository::EntryType type;
|
||||||
std::string name, hash;
|
std::string name, hash;
|
||||||
size_t sizeInBytes = 0;
|
size_t sizeInBytes = 0;
|
||||||
SGPath path; // absolute path on disk
|
SGPath path; // absolute path on disk
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<ChildInfo> ChildInfoList;
|
typedef std::vector<ChildInfo> ChildInfoList;
|
||||||
@@ -218,53 +218,66 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* buf = nullptr;
|
char* buf = nullptr;
|
||||||
size_t bufSize = 0;
|
size_t bufSize = 0;
|
||||||
|
|
||||||
for (auto &child : children) {
|
for (auto& child : children) {
|
||||||
if (child.type != HTTPRepository::FileType)
|
if (child.type != HTTPRepository::FileType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (child.path.exists())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SGPath cp = _repository->installedCopyPath;
|
||||||
|
cp.append(relativePath());
|
||||||
|
cp.append(child.name);
|
||||||
|
if (!cp.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGBinaryFile src(cp);
|
||||||
|
SGBinaryFile dst(child.path);
|
||||||
|
src.open(SG_IO_IN);
|
||||||
|
dst.open(SG_IO_OUT);
|
||||||
|
|
||||||
|
if (bufSize < cp.sizeInBytes()) {
|
||||||
|
bufSize = cp.sizeInBytes();
|
||||||
|
free(buf);
|
||||||
|
buf = (char*)malloc(bufSize);
|
||||||
|
if (!buf) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (child.path.exists())
|
src.read(buf, cp.sizeInBytes());
|
||||||
continue;
|
dst.write(buf, cp.sizeInBytes());
|
||||||
|
src.close();
|
||||||
|
dst.close();
|
||||||
|
|
||||||
SGPath cp = _repository->installedCopyPath;
|
// reset caching
|
||||||
cp.append(relativePath());
|
child.path.set_cached(false);
|
||||||
cp.append(child.name);
|
child.path.set_cached(true);
|
||||||
if (!cp.exists()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SGBinaryFile src(cp);
|
std::string hash = computeHashForPath(child.path);
|
||||||
SGBinaryFile dst(child.path);
|
updatedFileContents(child.path, hash);
|
||||||
src.open(SG_IO_IN);
|
|
||||||
dst.open(SG_IO_OUT);
|
|
||||||
|
|
||||||
if (bufSize < cp.sizeInBytes()) {
|
|
||||||
bufSize = cp.sizeInBytes();
|
|
||||||
free(buf);
|
|
||||||
buf = (char *)malloc(bufSize);
|
|
||||||
if (!buf) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
src.read(buf, cp.sizeInBytes());
|
|
||||||
dst.write(buf, cp.sizeInBytes());
|
|
||||||
src.close();
|
|
||||||
dst.close();
|
|
||||||
|
|
||||||
// reset caching
|
|
||||||
child.path.set_cached(false);
|
|
||||||
child.path.set_cached(true);
|
|
||||||
|
|
||||||
std::string hash = computeHashForPath(child.path);
|
|
||||||
updatedFileContents(child.path, hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// helper to check and erase 'fooBar' from paths, if passed fooBar.zip, fooBar.tgz, etc.
|
||||||
|
void removeExtractedDirectoryFromList(PathList& paths, const std::string& tarballName)
|
||||||
|
{
|
||||||
|
const auto directoryName = SGPath::fromUtf8(tarballName).file_base();
|
||||||
|
auto it = std::find_if(paths.begin(), paths.end(), [directoryName](const SGPath& p) {
|
||||||
|
return p.isDir() && (p.file() == directoryName);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != paths.end()) {
|
||||||
|
paths.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateChildrenBasedOnHash()
|
void updateChildrenBasedOnHash()
|
||||||
{
|
{
|
||||||
using SAct = HTTPRepository::SyncAction;
|
using SAct = HTTPRepository::SyncAction;
|
||||||
@@ -298,6 +311,11 @@ public:
|
|||||||
orphans.end());
|
orphans.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure the extracted directory corresponding to a tarball, is *not* considered an orphan
|
||||||
|
if (c.type == HTTPRepository::TarballType) {
|
||||||
|
removeExtractedDirectoryFromList(orphans, c.name);
|
||||||
|
}
|
||||||
|
|
||||||
if (_repository->syncPredicate) {
|
if (_repository->syncPredicate) {
|
||||||
const auto pathOnDisk = isNew ? absolutePath() / c.name : *p;
|
const auto pathOnDisk = isNew ? absolutePath() / c.name : *p;
|
||||||
// never handle deletes here, do them at the end
|
// never handle deletes here, do them at the end
|
||||||
@@ -320,7 +338,6 @@ public:
|
|||||||
toBeUpdated.push_back(c);
|
toBeUpdated.push_back(c);
|
||||||
} else {
|
} else {
|
||||||
// File/Directory exists and hash is valid.
|
// File/Directory exists and hash is valid.
|
||||||
|
|
||||||
if (c.type == HTTPRepository::DirectoryType) {
|
if (c.type == HTTPRepository::DirectoryType) {
|
||||||
// If it's a directory,perform a recursive check.
|
// If it's a directory,perform a recursive check.
|
||||||
HTTPDirectory *childDir = childDirectory(c.name);
|
HTTPDirectory *childDir = childDirectory(c.name);
|
||||||
@@ -432,41 +449,54 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compressedBytes = p.sizeInBytes();
|
||||||
buffer = (uint8_t *)malloc(bufferSize);
|
buffer = (uint8_t *)malloc(bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveExtractTask(const ArchiveExtractTask &) = delete;
|
ArchiveExtractTask(const ArchiveExtractTask &) = delete;
|
||||||
|
|
||||||
HTTPRepoPrivate::ProcessResult run(HTTPRepoPrivate *repo) {
|
HTTPRepoPrivate::ProcessResult run(HTTPRepoPrivate* repo)
|
||||||
size_t rd = file.read((char *)buffer, bufferSize);
|
{
|
||||||
extractor.extractBytes(buffer, rd);
|
if (!buffer) {
|
||||||
|
return HTTPRepoPrivate::ProcessFailed;
|
||||||
if (file.eof()) {
|
|
||||||
extractor.flush();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (!extractor.isAtEndOfArchive()) {
|
|
||||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Corrupt tarball " << relativePath);
|
|
||||||
repo->failedToUpdateChild(relativePath,
|
|
||||||
HTTPRepository::REPO_ERROR_IO);
|
|
||||||
return HTTPRepoPrivate::ProcessFailed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extractor.hasError()) {
|
size_t rd = file.read((char*)buffer, bufferSize);
|
||||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Error extracting " << relativePath);
|
repo->bytesExtracted += rd;
|
||||||
repo->failedToUpdateChild(relativePath,
|
extractor.extractBytes(buffer, rd);
|
||||||
HTTPRepository::REPO_ERROR_IO);
|
|
||||||
return HTTPRepoPrivate::ProcessFailed;
|
if (file.eof()) {
|
||||||
|
extractor.flush();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (!extractor.isAtEndOfArchive()) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_ALERT, "Corrupt tarball " << relativePath);
|
||||||
|
repo->failedToUpdateChild(relativePath,
|
||||||
|
HTTPRepository::REPO_ERROR_IO);
|
||||||
|
return HTTPRepoPrivate::ProcessFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extractor.hasError()) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_ALERT, "Error extracting " << relativePath);
|
||||||
|
repo->failedToUpdateChild(relativePath,
|
||||||
|
HTTPRepository::REPO_ERROR_IO);
|
||||||
|
return HTTPRepoPrivate::ProcessFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HTTPRepoPrivate::ProcessDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HTTPRepoPrivate::ProcessDone;
|
return HTTPRepoPrivate::ProcessContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HTTPRepoPrivate::ProcessContinue;
|
size_t archiveSizeBytes() const
|
||||||
|
{
|
||||||
|
return compressedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
~ArchiveExtractTask() { free(buffer); }
|
~ArchiveExtractTask() { free(buffer); }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// intentionally small so we extract incrementally on Windows
|
// intentionally small so we extract incrementally on Windows
|
||||||
// where Defender throttles many small files, sorry
|
// where Defender throttles many small files, sorry
|
||||||
@@ -478,6 +508,7 @@ public:
|
|||||||
uint8_t *buffer = nullptr;
|
uint8_t *buffer = nullptr;
|
||||||
SGBinaryFile file;
|
SGBinaryFile file;
|
||||||
ArchiveExtractor extractor;
|
ArchiveExtractor extractor;
|
||||||
|
std::size_t compressedBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ArchiveExtractTaskPtr = std::shared_ptr<ArchiveExtractTask>;
|
using ArchiveExtractTaskPtr = std::shared_ptr<ArchiveExtractTask>;
|
||||||
@@ -504,42 +535,42 @@ public:
|
|||||||
_repository->totalDownloaded += sz;
|
_repository->totalDownloaded += sz;
|
||||||
SGPath p = SGPath(absolutePath(), file);
|
SGPath p = SGPath(absolutePath(), file);
|
||||||
|
|
||||||
if ((p.extension() == "tgz") || (p.extension() == "zip")) {
|
if (it->type == HTTPRepository::TarballType) {
|
||||||
// We require that any compressed files have the same filename as the file or directory
|
// We require that any compressed files have the same filename as the file or directory
|
||||||
// they expand to, so we can remove the old file/directory before extracting the new
|
// they expand to, so we can remove the old file/directory before extracting the new
|
||||||
// data.
|
// data.
|
||||||
SGPath removePath = SGPath(p.base());
|
SGPath removePath = SGPath(p.base());
|
||||||
bool pathAvailable = true;
|
bool pathAvailable = true;
|
||||||
if (removePath.exists()) {
|
if (removePath.exists()) {
|
||||||
if (removePath.isDir()) {
|
if (removePath.isDir()) {
|
||||||
simgear::Dir pd(removePath);
|
simgear::Dir pd(removePath);
|
||||||
pathAvailable = pd.removeChildren();
|
pathAvailable = pd.removeChildren();
|
||||||
} else {
|
} else {
|
||||||
pathAvailable = removePath.remove();
|
pathAvailable = removePath.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (pathAvailable) {
|
if (pathAvailable) {
|
||||||
// we use a Task helper to extract tarballs incrementally.
|
// we use a Task helper to extract tarballs incrementally.
|
||||||
// without this, archive extraction blocks here, which
|
// without this, archive extraction blocks here, which
|
||||||
// prevents other repositories downloading / updating.
|
// prevents other repositories downloading / updating.
|
||||||
// Unfortunately due Windows AV (Defender, etc) we cna block
|
// Unfortunately due Windows AV (Defender, etc) we cna block
|
||||||
// here for many minutes.
|
// here for many minutes.
|
||||||
|
|
||||||
// use a lambda to own this shared_ptr; this means when the
|
// use a lambda to own this shared_ptr; this means when the
|
||||||
// lambda is destroyed, the ArchiveExtraTask will get
|
// lambda is destroyed, the ArchiveExtraTask will get
|
||||||
// cleaned up.
|
// cleaned up.
|
||||||
ArchiveExtractTaskPtr t =
|
ArchiveExtractTaskPtr t =
|
||||||
std::make_shared<ArchiveExtractTask>(p, _relativePath);
|
std::make_shared<ArchiveExtractTask>(p, _relativePath);
|
||||||
auto cb = [t](HTTPRepoPrivate *repo) {
|
auto cb = [t](HTTPRepoPrivate* repo) {
|
||||||
return t->run(repo);
|
return t->run(repo);
|
||||||
};
|
};
|
||||||
|
_repository->bytesToExtract += t->archiveSizeBytes();
|
||||||
_repository->addTask(cb);
|
_repository->addTask(cb);
|
||||||
} else {
|
} else {
|
||||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to remove old file/directory " << removePath);
|
SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to remove old file/directory " << removePath);
|
||||||
} // of pathAvailable
|
} // of pathAvailable
|
||||||
} // of handling tgz files
|
} // of handling archive files
|
||||||
} // of hash matches
|
} // of hash matches
|
||||||
} // of found in child list
|
} // of found in child list
|
||||||
}
|
}
|
||||||
@@ -731,11 +762,9 @@ private:
|
|||||||
std::string hashForChild(const ChildInfo& child) const
|
std::string hashForChild(const ChildInfo& child) const
|
||||||
{
|
{
|
||||||
SGPath p(child.path);
|
SGPath p(child.path);
|
||||||
if (child.type == HTTPRepository::DirectoryType)
|
if (child.type == HTTPRepository::DirectoryType) {
|
||||||
p.append(".dirindex");
|
p.append(".dirindex");
|
||||||
if (child.type == HTTPRepository::TarballType)
|
}
|
||||||
p.concat(
|
|
||||||
".tgz"); // For tarballs the hash is against the tarball file itself
|
|
||||||
return hashForPath(p);
|
return hashForPath(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,6 +961,11 @@ size_t HTTPRepository::bytesDownloaded() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t HTTPRepository::bytesToExtract() const
|
||||||
|
{
|
||||||
|
return _d->bytesToExtract - _d->bytesExtracted;
|
||||||
|
}
|
||||||
|
|
||||||
void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath)
|
void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath)
|
||||||
{
|
{
|
||||||
_d->installedCopyPath = copyPath;
|
_d->installedCopyPath = copyPath;
|
||||||
@@ -976,49 +1010,71 @@ HTTPRepository::failure() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void gotBodyData(const char *s, int n) override {
|
void gotBodyData(const char* s, int n) override
|
||||||
if (!file.get()) {
|
{
|
||||||
file.reset(new SGBinaryFile(pathInRepo));
|
if (!file.get()) {
|
||||||
if (!file->open(SG_IO_OUT)) {
|
const bool ok = createOutputFile();
|
||||||
SG_LOG(SG_TERRASYNC, SG_WARN,
|
if (!ok) {
|
||||||
"unable to create file " << pathInRepo);
|
_directory->repository()->http->cancelRequest(
|
||||||
_directory->repository()->http->cancelRequest(
|
this, "Unable to create output file:" + pathInRepo.utf8Str());
|
||||||
this, "Unable to create output file:" + pathInRepo.utf8Str());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_init(&hashContext);
|
sha1_write(&hashContext, s, n);
|
||||||
|
file->write(s, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_write(&hashContext, s, n);
|
bool createOutputFile()
|
||||||
file->write(s, n);
|
{
|
||||||
}
|
file.reset(new SGBinaryFile(pathInRepo));
|
||||||
|
if (!file->open(SG_IO_OUT)) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_WARN,
|
||||||
|
"unable to create file " << pathInRepo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void onDone() override {
|
sha1_init(&hashContext);
|
||||||
file->close();
|
return true;
|
||||||
if (responseCode() == 200) {
|
|
||||||
std::string hash =
|
|
||||||
strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
|
|
||||||
_directory->didUpdateFile(fileName, hash, contentSize());
|
|
||||||
} else if (responseCode() == 404) {
|
|
||||||
SG_LOG(SG_TERRASYNC, SG_WARN,
|
|
||||||
"terrasync file not found on server: "
|
|
||||||
<< fileName << " for " << _directory->absolutePath());
|
|
||||||
_directory->didFailToUpdateFile(
|
|
||||||
fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
|
|
||||||
} else {
|
|
||||||
SG_LOG(SG_TERRASYNC, SG_WARN,
|
|
||||||
"terrasync file download error on server: "
|
|
||||||
<< fileName << " for " << _directory->absolutePath()
|
|
||||||
<< "\n\tserver responded: " << responseCode() << "/"
|
|
||||||
<< responseReason());
|
|
||||||
_directory->didFailToUpdateFile(fileName,
|
|
||||||
HTTPRepository::REPO_ERROR_HTTP);
|
|
||||||
// should we every retry here?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_directory->repository()->finishedRequest(
|
void onDone() override
|
||||||
this, HTTPRepoPrivate::RequestFinish::Done);
|
{
|
||||||
}
|
const bool is200Response = (responseCode() == 200);
|
||||||
|
if (!file && is200Response) {
|
||||||
|
// if the server defines a zero-byte file, we will never call
|
||||||
|
// gotBodyData, so create the file here
|
||||||
|
// this ensures all the logic below works as expected
|
||||||
|
createOutputFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
file->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is200Response) {
|
||||||
|
std::string hash =
|
||||||
|
strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
|
||||||
|
_directory->didUpdateFile(fileName, hash, contentSize());
|
||||||
|
} else if (responseCode() == 404) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_WARN,
|
||||||
|
"terrasync file not found on server: "
|
||||||
|
<< fileName << " for " << _directory->absolutePath());
|
||||||
|
_directory->didFailToUpdateFile(
|
||||||
|
fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_WARN,
|
||||||
|
"terrasync file download error on server: "
|
||||||
|
<< fileName << " for " << _directory->absolutePath()
|
||||||
|
<< "\n\tserver responded: " << responseCode() << "/"
|
||||||
|
<< responseReason());
|
||||||
|
_directory->didFailToUpdateFile(fileName,
|
||||||
|
HTTPRepository::REPO_ERROR_HTTP);
|
||||||
|
// should we every retry here?
|
||||||
|
}
|
||||||
|
|
||||||
|
_directory->repository()->finishedRequest(
|
||||||
|
this, HTTPRepoPrivate::RequestFinish::Done);
|
||||||
|
}
|
||||||
|
|
||||||
void onFail() override {
|
void onFail() override {
|
||||||
HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET;
|
HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET;
|
||||||
@@ -1266,7 +1322,7 @@ HTTPRepository::failure() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
Dir dir(absPath);
|
Dir dir(absPath);
|
||||||
bool result = dir.remove(true);
|
bool result = dir.remove(true);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ public:
|
|||||||
|
|
||||||
virtual size_t bytesDownloaded() const;
|
virtual size_t bytesDownloaded() const;
|
||||||
|
|
||||||
|
virtual size_t bytesToExtract() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* optionally provide the location of an installer copy of this
|
* optionally provide the location of an installer copy of this
|
||||||
* repository. When a file is missing it will be copied from this tree.
|
* repository. When a file is missing it will be copied from this tree.
|
||||||
|
|||||||
@@ -60,22 +60,23 @@ public:
|
|||||||
HTTPRepository::FailureVec failures;
|
HTTPRepository::FailureVec failures;
|
||||||
int maxPermittedFailures = 16;
|
int maxPermittedFailures = 16;
|
||||||
|
|
||||||
HTTPRepoPrivate(HTTPRepository *parent)
|
HTTPRepoPrivate(HTTPRepository* parent)
|
||||||
: p(parent), isUpdating(false), status(HTTPRepository::REPO_NO_ERROR),
|
: p(parent)
|
||||||
totalDownloaded(0) {
|
{
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~HTTPRepoPrivate();
|
~HTTPRepoPrivate();
|
||||||
|
|
||||||
HTTPRepository *p; // link back to outer
|
HTTPRepository *p; // link back to outer
|
||||||
HTTP::Client *http;
|
HTTP::Client* http = nullptr;
|
||||||
std::string baseUrl;
|
std::string baseUrl;
|
||||||
SGPath basePath;
|
SGPath basePath;
|
||||||
bool isUpdating;
|
bool isUpdating = false;
|
||||||
HTTPRepository::ResultCode status;
|
HTTPRepository::ResultCode status = HTTPRepository::REPO_NO_ERROR;
|
||||||
HTTPDirectory_ptr rootDir;
|
HTTPDirectory_ptr rootDir;
|
||||||
size_t totalDownloaded;
|
size_t totalDownloaded = 0;
|
||||||
|
size_t bytesToExtract = 0;
|
||||||
|
size_t bytesExtracted = 0;
|
||||||
HTTPRepository::SyncPredicate syncPredicate;
|
HTTPRepository::SyncPredicate syncPredicate;
|
||||||
|
|
||||||
HTTP::Request_ptr updateFile(HTTPDirectory *dir, const std::string &name,
|
HTTP::Request_ptr updateFile(HTTPDirectory *dir, const std::string &name,
|
||||||
|
|||||||
@@ -566,8 +566,10 @@ bool SGBinObject::read_bin( const SGPath& file ) {
|
|||||||
sgReadUInt( fp, &header );
|
sgReadUInt( fp, &header );
|
||||||
|
|
||||||
if (sgReadError()) {
|
if (sgReadError()) {
|
||||||
|
int code = 0;
|
||||||
|
const char* gzErrorString = gzerror(fp, &code);
|
||||||
gzclose(fp);
|
gzclose(fp);
|
||||||
throw sg_io_exception("Unable to read BTG header: " + simgear::strutils::error_string(errno), sg_location(file));
|
throw sg_io_exception("Unable to read BTG header: " + string{gzErrorString} + ", code =" + std::to_string(code), sg_location(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ((header & 0xFF000000) >> 24) == 'S' &&
|
if ( ((header & 0xFF000000) >> 24) == 'S' &&
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ std::string SGFile::computeHash()
|
|||||||
[](char* p) { free(p); }};
|
[](char* p) { free(p); }};
|
||||||
|
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
SG_LOG(SG_IO, SG_ALERT, "Failed to malloc buffer for SHA1 check");
|
// @TODO report out of memory error
|
||||||
|
SG_LOG(SG_IO, SG_ALERT, "Failed to malloc buffer for SHA1 check:" << file_name);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t readLen;
|
size_t readLen;
|
||||||
|
|||||||
Binary file not shown.
@@ -31,6 +31,10 @@ using TestApi = simgear::HTTP::TestApi;
|
|||||||
|
|
||||||
std::string dataForFile(const std::string& parentName, const std::string& name, int revision)
|
std::string dataForFile(const std::string& parentName, const std::string& name, int revision)
|
||||||
{
|
{
|
||||||
|
if (name == "zeroByteFile") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
// random content but which definitely depends on our tree location
|
// random content but which definitely depends on our tree location
|
||||||
// and revision.
|
// and revision.
|
||||||
@@ -446,6 +450,7 @@ void testBasicClone(HTTP::Client* cl)
|
|||||||
verifyFileState(p, "fileA");
|
verifyFileState(p, "fileA");
|
||||||
verifyFileState(p, "dirA/subdirA/fileAAA");
|
verifyFileState(p, "dirA/subdirA/fileAAA");
|
||||||
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
|
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
|
||||||
|
verifyFileState(p, "dirA/subdirA/zeroByteFile");
|
||||||
|
|
||||||
global_repo->findEntry("fileA")->revision++;
|
global_repo->findEntry("fileA")->revision++;
|
||||||
global_repo->findEntry("dirB/subdirA/fileBAA")->revision++;
|
global_repo->findEntry("dirB/subdirA/fileBAA")->revision++;
|
||||||
@@ -896,6 +901,27 @@ void testPersistentSocketFailure(HTTP::Client *cl) {
|
|||||||
verifyRequestCount("dirD/subdirDB/fileDBA", 1);
|
verifyRequestCount("dirD/subdirDB/fileDBA", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testHashOnEmptyFile()
|
||||||
|
{
|
||||||
|
std::unique_ptr<HTTPRepository> repo;
|
||||||
|
SGPath p(simgear::Dir::current().path());
|
||||||
|
p.append("sgfile_compute_hash");
|
||||||
|
simgear::Dir pd(p);
|
||||||
|
if (pd.exists()) {
|
||||||
|
pd.removeChildren();
|
||||||
|
} else {
|
||||||
|
pd.create(0700);
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath fPath = p / "test_empty_file";
|
||||||
|
|
||||||
|
SGFile file(fPath);
|
||||||
|
file.open(SG_IO_OUT);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
const auto hash = file.computeHash();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
sglog().setLogLevels( SG_ALL, SG_INFO );
|
sglog().setLogLevels( SG_ALL, SG_INFO );
|
||||||
@@ -911,6 +937,8 @@ int main(int argc, char* argv[])
|
|||||||
global_repo->defineFile("dirA/fileAC");
|
global_repo->defineFile("dirA/fileAC");
|
||||||
global_repo->defineFile("dirA/subdirA/fileAAA");
|
global_repo->defineFile("dirA/subdirA/fileAAA");
|
||||||
global_repo->defineFile("dirA/subdirA/fileAAB");
|
global_repo->defineFile("dirA/subdirA/fileAAB");
|
||||||
|
global_repo->defineFile("dirA/subdirA/zeroByteFile");
|
||||||
|
|
||||||
global_repo->defineFile("dirB/subdirA/fileBAA");
|
global_repo->defineFile("dirB/subdirA/fileBAA");
|
||||||
global_repo->defineFile("dirB/subdirA/fileBAB");
|
global_repo->defineFile("dirB/subdirA/fileBAB");
|
||||||
global_repo->defineFile("dirB/subdirA/fileBAC");
|
global_repo->defineFile("dirB/subdirA/fileBAC");
|
||||||
@@ -944,6 +972,8 @@ int main(int argc, char* argv[])
|
|||||||
testRetryAfterSocketFailure(&cl);
|
testRetryAfterSocketFailure(&cl);
|
||||||
testPersistentSocketFailure(&cl);
|
testPersistentSocketFailure(&cl);
|
||||||
|
|
||||||
|
testHashOnEmptyFile();
|
||||||
|
|
||||||
std::cout << "all tests passed ok" << std::endl;
|
std::cout << "all tests passed ok" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ void testTarGz()
|
|||||||
uint8_t* buf = (uint8_t*) alloca(8192);
|
uint8_t* buf = (uint8_t*) alloca(8192);
|
||||||
size_t bufSize = f.read((char*) buf, 8192);
|
size_t bufSize = f.read((char*) buf, 8192);
|
||||||
|
|
||||||
SG_VERIFY(ArchiveExtractor::determineType(buf, bufSize) == ArchiveExtractor::TarData);
|
SG_VERIFY(ArchiveExtractor::determineType(buf, bufSize) == ArchiveExtractor::GZData);
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
}
|
}
|
||||||
@@ -174,6 +174,34 @@ void testPAXAttributes()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testExtractXZ()
|
||||||
|
{
|
||||||
|
SGPath p = SGPath(SRC_DIR);
|
||||||
|
p.append("test.tar.xz");
|
||||||
|
|
||||||
|
SGBinaryFile f(p);
|
||||||
|
f.open(SG_IO_IN);
|
||||||
|
|
||||||
|
SGPath extractDir = simgear::Dir::current().path() / "test_extract_xz";
|
||||||
|
simgear::Dir pd(extractDir);
|
||||||
|
pd.removeChildren();
|
||||||
|
|
||||||
|
ArchiveExtractor ex(extractDir);
|
||||||
|
|
||||||
|
uint8_t* buf = (uint8_t*)alloca(128);
|
||||||
|
while (!f.eof()) {
|
||||||
|
size_t bufSize = f.read((char*)buf, 128);
|
||||||
|
ex.extractBytes(buf, bufSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ex.flush();
|
||||||
|
SG_VERIFY(ex.isAtEndOfArchive());
|
||||||
|
SG_VERIFY(ex.hasError() == false);
|
||||||
|
|
||||||
|
SG_VERIFY((extractDir / "testDir/hello.c").exists());
|
||||||
|
SG_VERIFY((extractDir / "testDir/foo.txt").exists());
|
||||||
|
}
|
||||||
|
|
||||||
int main(int ac, char ** av)
|
int main(int ac, char ** av)
|
||||||
{
|
{
|
||||||
testTarGz();
|
testTarGz();
|
||||||
@@ -181,6 +209,7 @@ int main(int ac, char ** av)
|
|||||||
testFilterTar();
|
testFilterTar();
|
||||||
testExtractStreamed();
|
testExtractStreamed();
|
||||||
testExtractZip();
|
testExtractZip();
|
||||||
|
testExtractXZ();
|
||||||
|
|
||||||
// disabled to avoiding checking in large PAX archive
|
// disabled to avoiding checking in large PAX archive
|
||||||
// testPAXAttributes();
|
// testPAXAttributes();
|
||||||
|
|||||||
+430
-394
@@ -38,82 +38,11 @@
|
|||||||
#include <simgear/package/unzip.h>
|
#include <simgear/package/unzip.h>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
|
#include "ArchiveExtractor_private.hxx"
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
class ArchiveExtractorPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArchiveExtractorPrivate(ArchiveExtractor* o) :
|
|
||||||
outer(o)
|
|
||||||
{
|
|
||||||
assert(outer);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ArchiveExtractorPrivate() = default;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
INVALID = 0,
|
|
||||||
READING_HEADER,
|
|
||||||
READING_FILE,
|
|
||||||
READING_PADDING,
|
|
||||||
READING_PAX_GLOBAL_ATTRIBUTES,
|
|
||||||
READING_PAX_FILE_ATTRIBUTES,
|
|
||||||
PRE_END_OF_ARCHVE,
|
|
||||||
END_OF_ARCHIVE,
|
|
||||||
ERROR_STATE, ///< states above this are error conditions
|
|
||||||
BAD_ARCHIVE,
|
|
||||||
BAD_DATA,
|
|
||||||
FILTER_STOPPED
|
|
||||||
} State;
|
|
||||||
|
|
||||||
State state = INVALID;
|
|
||||||
ArchiveExtractor* outer = nullptr;
|
|
||||||
|
|
||||||
virtual void extractBytes(const uint8_t* bytes, size_t count) = 0;
|
|
||||||
|
|
||||||
virtual void flush() = 0;
|
|
||||||
|
|
||||||
SGPath extractRootPath()
|
|
||||||
{
|
|
||||||
return outer->_rootPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArchiveExtractor::PathResult filterPath(std::string& pathToExtract)
|
|
||||||
{
|
|
||||||
return outer->filterPath(pathToExtract);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool isSafePath(const std::string& p) const
|
|
||||||
{
|
|
||||||
if (p.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reject absolute paths
|
|
||||||
if (p.at(0) == '/') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reject paths containing '..'
|
|
||||||
size_t doubleDot = p.find("..");
|
|
||||||
if (doubleDot != std::string::npos) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on POSIX could use realpath to sanity check
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
|
|
||||||
const int ZLIB_INFLATE_WINDOW_BITS = MAX_WBITS;
|
|
||||||
const int ZLIB_DECODE_GZIP_HEADER = 16;
|
|
||||||
|
|
||||||
/* tar Header Block, from POSIX 1003.1-1990. */
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -153,320 +82,410 @@ typedef struct
|
|||||||
#define FIFOTYPE '6' /* FIFO special */
|
#define FIFOTYPE '6' /* FIFO special */
|
||||||
#define CONTTYPE '7' /* reserved */
|
#define CONTTYPE '7' /* reserved */
|
||||||
|
|
||||||
const char PAX_GLOBAL_HEADER = 'g';
|
const char PAX_GLOBAL_HEADER = 'g';
|
||||||
const char PAX_FILE_ATTRIBUTES = 'x';
|
const char PAX_FILE_ATTRIBUTES = 'x';
|
||||||
|
|
||||||
|
|
||||||
class TarExtractorPrivate : public ArchiveExtractorPrivate
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
|
||||||
|
const int ZLIB_INFLATE_WINDOW_BITS = MAX_WBITS;
|
||||||
|
const int ZLIB_DECODE_GZIP_HEADER = 16;
|
||||||
|
|
||||||
|
/* tar Header Block, from POSIX 1003.1-1990. */
|
||||||
|
|
||||||
|
|
||||||
|
class TarExtractorPrivate : public ArchiveExtractorPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
UstarHeaderBlock header;
|
||||||
|
uint8_t headerBytes[TAR_HEADER_BLOCK_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t bytesRemaining;
|
||||||
|
std::unique_ptr<SGFile> currentFile;
|
||||||
|
size_t currentFileSize;
|
||||||
|
|
||||||
|
uint8_t* headerPtr;
|
||||||
|
bool skipCurrentEntry = false;
|
||||||
|
std::string paxAttributes;
|
||||||
|
std::string paxPathName;
|
||||||
|
|
||||||
|
TarExtractorPrivate(ArchiveExtractor* o) : ArchiveExtractorPrivate(o)
|
||||||
|
{
|
||||||
|
setState(TarExtractorPrivate::READING_HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
|
~TarExtractorPrivate() = default;
|
||||||
|
|
||||||
|
void readPaddingIfRequired()
|
||||||
|
{
|
||||||
|
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
|
||||||
|
if (pad) {
|
||||||
|
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
|
||||||
|
setState(READING_PADDING);
|
||||||
|
} else {
|
||||||
|
setState(READING_HEADER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkEndOfState()
|
||||||
|
{
|
||||||
|
if (bytesRemaining > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == READING_FILE) {
|
||||||
|
if (currentFile) {
|
||||||
|
currentFile->close();
|
||||||
|
currentFile.reset();
|
||||||
|
}
|
||||||
|
readPaddingIfRequired();
|
||||||
|
} else if (state == READING_HEADER) {
|
||||||
|
processHeader();
|
||||||
|
} else if (state == PRE_END_OF_ARCHVE) {
|
||||||
|
if (headerIsAllZeros()) {
|
||||||
|
setState(END_OF_ARCHIVE);
|
||||||
|
} else {
|
||||||
|
// what does the spec say here?
|
||||||
|
}
|
||||||
|
} else if (state == READING_PAX_GLOBAL_ATTRIBUTES) {
|
||||||
|
parsePAXAttributes(true);
|
||||||
|
readPaddingIfRequired();
|
||||||
|
} else if (state == READING_PAX_FILE_ATTRIBUTES) {
|
||||||
|
parsePAXAttributes(false);
|
||||||
|
readPaddingIfRequired();
|
||||||
|
} else if (state == READING_PADDING) {
|
||||||
|
setState(READING_HEADER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setState(State newState)
|
||||||
|
{
|
||||||
|
if ((newState == READING_HEADER) || (newState == PRE_END_OF_ARCHVE)) {
|
||||||
|
bytesRemaining = TAR_HEADER_BLOCK_SIZE;
|
||||||
|
headerPtr = headerBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void extractBytes(const uint8_t* bytes, size_t count) override
|
||||||
|
{
|
||||||
|
// uncompressed, just pass through directly
|
||||||
|
processBytes((const char*)bytes, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush() override
|
||||||
|
{
|
||||||
|
// no-op for tar files, we process everything greedily
|
||||||
|
}
|
||||||
|
|
||||||
|
void processHeader()
|
||||||
|
{
|
||||||
|
if (headerIsAllZeros()) {
|
||||||
|
if (state == PRE_END_OF_ARCHVE) {
|
||||||
|
setState(END_OF_ARCHIVE);
|
||||||
|
} else {
|
||||||
|
setState(PRE_END_OF_ARCHVE);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(header.magic, TMAGIC, TMAGLEN) != 0) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Untar: magic is wrong");
|
||||||
|
state = BAD_ARCHIVE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipCurrentEntry = false;
|
||||||
|
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
|
||||||
|
if (!paxPathName.empty()) {
|
||||||
|
tarPath = paxPathName;
|
||||||
|
paxPathName.clear(); // clear for next file
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSafePath(tarPath)) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "unsafe tar path, skipping::" << tarPath);
|
||||||
|
skipCurrentEntry = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = filterPath(tarPath);
|
||||||
|
if (result == ArchiveExtractor::Stop) {
|
||||||
|
setState(FILTER_STOPPED);
|
||||||
|
return;
|
||||||
|
} else if (result == ArchiveExtractor::Skipped) {
|
||||||
|
skipCurrentEntry = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath p = extractRootPath() / tarPath;
|
||||||
|
if (header.typeflag == DIRTYPE) {
|
||||||
|
if (!skipCurrentEntry) {
|
||||||
|
Dir dir(p);
|
||||||
|
dir.create(0755);
|
||||||
|
}
|
||||||
|
setState(READING_HEADER);
|
||||||
|
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
|
||||||
|
currentFileSize = ::strtol(header.size, NULL, 8);
|
||||||
|
bytesRemaining = currentFileSize;
|
||||||
|
if (!skipCurrentEntry) {
|
||||||
|
currentFile.reset(new SGBinaryFile(p));
|
||||||
|
currentFile->open(SG_IO_OUT);
|
||||||
|
}
|
||||||
|
setState(READING_FILE);
|
||||||
|
} else if (header.typeflag == PAX_GLOBAL_HEADER) {
|
||||||
|
setState(READING_PAX_GLOBAL_ATTRIBUTES);
|
||||||
|
currentFileSize = ::strtol(header.size, NULL, 8);
|
||||||
|
bytesRemaining = currentFileSize;
|
||||||
|
paxAttributes.clear();
|
||||||
|
} else if (header.typeflag == PAX_FILE_ATTRIBUTES) {
|
||||||
|
setState(READING_PAX_FILE_ATTRIBUTES);
|
||||||
|
currentFileSize = ::strtol(header.size, NULL, 8);
|
||||||
|
bytesRemaining = currentFileSize;
|
||||||
|
paxAttributes.clear();
|
||||||
|
} else if ((header.typeflag == SYMTYPE) || (header.typeflag == LNKTYPE)) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Tarball contains a link or symlink, will be skipped:" << tarPath);
|
||||||
|
skipCurrentEntry = true;
|
||||||
|
setState(READING_HEADER);
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
|
||||||
|
state = BAD_ARCHIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processBytes(const char* bytes, size_t count)
|
||||||
|
{
|
||||||
|
if ((state >= ERROR_STATE) || (state == END_OF_ARCHIVE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t curBytes = std::min(bytesRemaining, count);
|
||||||
|
if (state == READING_FILE) {
|
||||||
|
if (currentFile) {
|
||||||
|
currentFile->write(bytes, curBytes);
|
||||||
|
}
|
||||||
|
bytesRemaining -= curBytes;
|
||||||
|
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
|
||||||
|
memcpy(headerPtr, bytes, curBytes);
|
||||||
|
bytesRemaining -= curBytes;
|
||||||
|
headerPtr += curBytes;
|
||||||
|
} else if (state == READING_PADDING) {
|
||||||
|
bytesRemaining -= curBytes;
|
||||||
|
} else if ((state == READING_PAX_FILE_ATTRIBUTES) || (state == READING_PAX_GLOBAL_ATTRIBUTES)) {
|
||||||
|
bytesRemaining -= curBytes;
|
||||||
|
paxAttributes.append(bytes, curBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEndOfState();
|
||||||
|
if (count > curBytes) {
|
||||||
|
// recurse with unprocessed bytes
|
||||||
|
processBytes(bytes + curBytes, count - curBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool headerIsAllZeros() const
|
||||||
|
{
|
||||||
|
char* headerAsChar = (char*)&header;
|
||||||
|
for (size_t i = 0; i < offsetof(UstarHeaderBlock, magic); ++i) {
|
||||||
|
if (*headerAsChar++ != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm#paxex
|
||||||
|
void parsePAXAttributes(bool areGlobal)
|
||||||
|
{
|
||||||
|
auto lineStart = 0;
|
||||||
|
for (;;) {
|
||||||
|
auto firstSpace = paxAttributes.find(' ', lineStart);
|
||||||
|
auto firstEq = paxAttributes.find('=', lineStart);
|
||||||
|
if ((firstEq == std::string::npos) || (firstSpace == std::string::npos)) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Malfroemd PAX attributes in tarfile");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lengthBytes = std::stoul(paxAttributes.substr(lineStart, firstSpace));
|
||||||
|
uint32_t dataBytes = lengthBytes - (firstEq + 1);
|
||||||
|
std::string name = paxAttributes.substr(firstSpace + 1, firstEq - (firstSpace + 1));
|
||||||
|
|
||||||
|
// dataBytes - 1 here to trim off the trailing newline
|
||||||
|
std::string data = paxAttributes.substr(firstEq + 1, dataBytes - 1);
|
||||||
|
|
||||||
|
processPAXAttribute(areGlobal, name, data);
|
||||||
|
|
||||||
|
lineStart += lengthBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processPAXAttribute(bool isGlobalAttr, const std::string& attrName, const std::string& data)
|
||||||
|
{
|
||||||
|
if (!isGlobalAttr && (attrName == "path")) {
|
||||||
|
// data is UTF-8 encoded path name
|
||||||
|
paxPathName = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
class GZTarExtractor : public TarExtractorPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
GZTarExtractor(ArchiveExtractor* outer) : TarExtractorPrivate(outer)
|
||||||
union {
|
|
||||||
UstarHeaderBlock header;
|
|
||||||
uint8_t headerBytes[TAR_HEADER_BLOCK_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t bytesRemaining;
|
|
||||||
std::unique_ptr<SGFile> currentFile;
|
|
||||||
size_t currentFileSize;
|
|
||||||
z_stream zlibStream;
|
|
||||||
uint8_t* zlibOutput;
|
|
||||||
bool haveInitedZLib = false;
|
|
||||||
bool uncompressedData = false; // set if reading a plain .tar (not tar.gz)
|
|
||||||
uint8_t* headerPtr;
|
|
||||||
bool skipCurrentEntry = false;
|
|
||||||
std::string paxAttributes;
|
|
||||||
std::string paxPathName;
|
|
||||||
|
|
||||||
TarExtractorPrivate(ArchiveExtractor* o) :
|
|
||||||
ArchiveExtractorPrivate(o)
|
|
||||||
{
|
{
|
||||||
memset(&zlibStream, 0, sizeof(z_stream));
|
memset(&zlibStream, 0, sizeof(z_stream));
|
||||||
zlibOutput = (unsigned char*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
|
zlibOutput = (unsigned char*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
|
||||||
zlibStream.zalloc = Z_NULL;
|
zlibStream.zalloc = Z_NULL;
|
||||||
zlibStream.zfree = Z_NULL;
|
zlibStream.zfree = Z_NULL;
|
||||||
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
||||||
zlibStream.next_out = zlibOutput;
|
zlibStream.next_out = zlibOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
~TarExtractorPrivate()
|
~GZTarExtractor()
|
||||||
{
|
{
|
||||||
free(zlibOutput);
|
free(zlibOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readPaddingIfRequired()
|
void extractBytes(const uint8_t* bytes, size_t count) override
|
||||||
{
|
{
|
||||||
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
|
zlibStream.next_in = (uint8_t*)bytes;
|
||||||
if (pad) {
|
zlibStream.avail_in = count;
|
||||||
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
|
|
||||||
setState(READING_PADDING);
|
|
||||||
} else {
|
|
||||||
setState(READING_HEADER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkEndOfState()
|
if (!haveInitedZLib) {
|
||||||
{
|
// now we have data, see if we're dealing with GZ-compressed data or not
|
||||||
if (bytesRemaining > 0) {
|
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
|
||||||
return;
|
// GZIP identification bytes
|
||||||
}
|
if (inflateInit2(&zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
|
||||||
if (state == READING_FILE) {
|
state = TarExtractorPrivate::BAD_DATA;
|
||||||
if (currentFile) {
|
return;
|
||||||
currentFile->close();
|
}
|
||||||
currentFile.reset();
|
|
||||||
}
|
|
||||||
readPaddingIfRequired();
|
|
||||||
} else if (state == READING_HEADER) {
|
|
||||||
processHeader();
|
|
||||||
} else if (state == PRE_END_OF_ARCHVE) {
|
|
||||||
if (headerIsAllZeros()) {
|
|
||||||
setState(END_OF_ARCHIVE);
|
|
||||||
} else {
|
} else {
|
||||||
// what does the spec say here?
|
// set error state
|
||||||
|
state = TarExtractorPrivate::BAD_DATA;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if (state == READING_PAX_GLOBAL_ATTRIBUTES) {
|
|
||||||
parsePAXAttributes(true);
|
|
||||||
readPaddingIfRequired();
|
|
||||||
} else if (state == READING_PAX_FILE_ATTRIBUTES) {
|
|
||||||
parsePAXAttributes(false);
|
|
||||||
readPaddingIfRequired();
|
|
||||||
} else if (state == READING_PADDING) {
|
|
||||||
setState(READING_HEADER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setState(State newState)
|
haveInitedZLib = true;
|
||||||
{
|
setState(TarExtractorPrivate::READING_HEADER);
|
||||||
if ((newState == READING_HEADER) || (newState == PRE_END_OF_ARCHVE)) {
|
} // of init on first-bytes case
|
||||||
bytesRemaining = TAR_HEADER_BLOCK_SIZE;
|
|
||||||
headerPtr = headerBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = newState;
|
size_t writtenSize;
|
||||||
}
|
// loop, running zlib() inflate and sending output bytes to
|
||||||
|
// our request body handler. Keep calling inflate until no bytes are
|
||||||
|
// written, and ZLIB has consumed all available input
|
||||||
|
do {
|
||||||
|
zlibStream.next_out = zlibOutput;
|
||||||
|
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
||||||
|
int result = inflate(&zlibStream, Z_NO_FLUSH);
|
||||||
|
if (result == Z_OK || result == Z_STREAM_END) {
|
||||||
|
// nothing to do
|
||||||
|
|
||||||
void extractBytes(const uint8_t* bytes, size_t count) override
|
} else if (result == Z_BUF_ERROR) {
|
||||||
{
|
// transient error, fall through
|
||||||
zlibStream.next_in = (uint8_t*) bytes;
|
|
||||||
zlibStream.avail_in = count;
|
|
||||||
|
|
||||||
if (!haveInitedZLib) {
|
|
||||||
// now we have data, see if we're dealing with GZ-compressed data or not
|
|
||||||
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
|
|
||||||
// GZIP identification bytes
|
|
||||||
if (inflateInit2(&zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
|
|
||||||
state = TarExtractorPrivate::BAD_DATA;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
UstarHeaderBlock* header = (UstarHeaderBlock*)bytes;
|
|
||||||
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "didn't find tar magic in header");
|
|
||||||
state = TarExtractorPrivate::BAD_DATA;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uncompressedData = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
haveInitedZLib = true;
|
|
||||||
setState(TarExtractorPrivate::READING_HEADER);
|
|
||||||
} // of init on first-bytes case
|
|
||||||
|
|
||||||
if (uncompressedData) {
|
|
||||||
processBytes((const char*) bytes, count);
|
|
||||||
} else {
|
|
||||||
size_t writtenSize;
|
|
||||||
// loop, running zlib() inflate and sending output bytes to
|
|
||||||
// our request body handler. Keep calling inflate until no bytes are
|
|
||||||
// written, and ZLIB has consumed all available input
|
|
||||||
do {
|
|
||||||
zlibStream.next_out = zlibOutput;
|
|
||||||
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
|
||||||
int result = inflate(&zlibStream, Z_NO_FLUSH);
|
|
||||||
if (result == Z_OK || result == Z_STREAM_END) {
|
|
||||||
// nothing to do
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (result == Z_BUF_ERROR) {
|
|
||||||
// transient error, fall through
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// _error = result;
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << zlibStream.msg);
|
|
||||||
state = TarExtractorPrivate::BAD_DATA;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlibStream.avail_out;
|
|
||||||
if (writtenSize > 0) {
|
|
||||||
processBytes((const char*) zlibOutput, writtenSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == Z_STREAM_END) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while ((zlibStream.avail_in > 0) || (writtenSize > 0));
|
|
||||||
} // of Zlib-compressed data
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() override
|
|
||||||
{
|
|
||||||
// no-op for tar files, we process everything greedily
|
|
||||||
}
|
|
||||||
|
|
||||||
void processHeader()
|
|
||||||
{
|
|
||||||
if (headerIsAllZeros()) {
|
|
||||||
if (state == PRE_END_OF_ARCHVE) {
|
|
||||||
setState(END_OF_ARCHIVE);
|
|
||||||
} else {
|
} else {
|
||||||
setState(PRE_END_OF_ARCHVE);
|
// _error = result;
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << zlibStream.msg);
|
||||||
|
state = TarExtractorPrivate::BAD_DATA;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(header.magic, TMAGIC, TMAGLEN) != 0) {
|
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlibStream.avail_out;
|
||||||
SG_LOG(SG_IO, SG_WARN, "Untar: magic is wrong");
|
if (writtenSize > 0) {
|
||||||
state = BAD_ARCHIVE;
|
processBytes((const char*)zlibOutput, writtenSize);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
skipCurrentEntry = false;
|
|
||||||
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
|
|
||||||
if (!paxPathName.empty()) {
|
|
||||||
tarPath = paxPathName;
|
|
||||||
paxPathName.clear(); // clear for next file
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSafePath(tarPath)) {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "unsafe tar path, skipping::" << tarPath);
|
|
||||||
skipCurrentEntry = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = filterPath(tarPath);
|
|
||||||
if (result == ArchiveExtractor::Stop) {
|
|
||||||
setState(FILTER_STOPPED);
|
|
||||||
return;
|
|
||||||
} else if (result == ArchiveExtractor::Skipped) {
|
|
||||||
skipCurrentEntry = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SGPath p = extractRootPath() / tarPath;
|
|
||||||
if (header.typeflag == DIRTYPE) {
|
|
||||||
if (!skipCurrentEntry) {
|
|
||||||
Dir dir(p);
|
|
||||||
dir.create(0755);
|
|
||||||
}
|
}
|
||||||
setState(READING_HEADER);
|
|
||||||
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
|
|
||||||
currentFileSize = ::strtol(header.size, NULL, 8);
|
|
||||||
bytesRemaining = currentFileSize;
|
|
||||||
if (!skipCurrentEntry) {
|
|
||||||
currentFile.reset(new SGBinaryFile(p));
|
|
||||||
currentFile->open(SG_IO_OUT);
|
|
||||||
}
|
|
||||||
setState(READING_FILE);
|
|
||||||
} else if (header.typeflag == PAX_GLOBAL_HEADER) {
|
|
||||||
setState(READING_PAX_GLOBAL_ATTRIBUTES);
|
|
||||||
currentFileSize = ::strtol(header.size, NULL, 8);
|
|
||||||
bytesRemaining = currentFileSize;
|
|
||||||
paxAttributes.clear();
|
|
||||||
} else if (header.typeflag == PAX_FILE_ATTRIBUTES) {
|
|
||||||
setState(READING_PAX_FILE_ATTRIBUTES);
|
|
||||||
currentFileSize = ::strtol(header.size, NULL, 8);
|
|
||||||
bytesRemaining = currentFileSize;
|
|
||||||
paxAttributes.clear();
|
|
||||||
} else if ((header.typeflag == SYMTYPE) || (header.typeflag == LNKTYPE)) {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "Tarball contains a link or symlink, will be skipped:" << tarPath);
|
|
||||||
skipCurrentEntry = true;
|
|
||||||
setState(READING_HEADER);
|
|
||||||
} else {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
|
|
||||||
state = BAD_ARCHIVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void processBytes(const char* bytes, size_t count)
|
if (result == Z_STREAM_END) {
|
||||||
{
|
|
||||||
if ((state >= ERROR_STATE) || (state == END_OF_ARCHIVE)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t curBytes = std::min(bytesRemaining, count);
|
|
||||||
if (state == READING_FILE) {
|
|
||||||
if (currentFile) {
|
|
||||||
currentFile->write(bytes, curBytes);
|
|
||||||
}
|
|
||||||
bytesRemaining -= curBytes;
|
|
||||||
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
|
|
||||||
memcpy(headerPtr, bytes, curBytes);
|
|
||||||
bytesRemaining -= curBytes;
|
|
||||||
headerPtr += curBytes;
|
|
||||||
} else if (state == READING_PADDING) {
|
|
||||||
bytesRemaining -= curBytes;
|
|
||||||
} else if ((state == READING_PAX_FILE_ATTRIBUTES) || (state == READING_PAX_GLOBAL_ATTRIBUTES)) {
|
|
||||||
bytesRemaining -= curBytes;
|
|
||||||
paxAttributes.append(bytes, curBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkEndOfState();
|
|
||||||
if (count > curBytes) {
|
|
||||||
// recurse with unprocessed bytes
|
|
||||||
processBytes(bytes + curBytes, count - curBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool headerIsAllZeros() const
|
|
||||||
{
|
|
||||||
char* headerAsChar = (char*) &header;
|
|
||||||
for (size_t i=0; i < offsetof(UstarHeaderBlock, magic); ++i) {
|
|
||||||
if (*headerAsChar++ != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm#paxex
|
|
||||||
void parsePAXAttributes(bool areGlobal)
|
|
||||||
{
|
|
||||||
auto lineStart = 0;
|
|
||||||
for (;;) {
|
|
||||||
auto firstSpace = paxAttributes.find(' ', lineStart);
|
|
||||||
auto firstEq = paxAttributes.find('=', lineStart);
|
|
||||||
if ((firstEq == std::string::npos) || (firstSpace == std::string::npos)) {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "Malfroemd PAX attributes in tarfile");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} while ((zlibStream.avail_in > 0) || (writtenSize > 0));
|
||||||
uint32_t lengthBytes = std::stoul(paxAttributes.substr(lineStart, firstSpace));
|
|
||||||
uint32_t dataBytes = lengthBytes - (firstEq + 1);
|
|
||||||
std::string name = paxAttributes.substr(firstSpace+1, firstEq - (firstSpace + 1));
|
|
||||||
|
|
||||||
// dataBytes - 1 here to trim off the trailing newline
|
|
||||||
std::string data = paxAttributes.substr(firstEq+1, dataBytes - 1);
|
|
||||||
|
|
||||||
processPAXAttribute(areGlobal, name, data);
|
|
||||||
|
|
||||||
lineStart += lengthBytes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void processPAXAttribute(bool isGlobalAttr, const std::string& attrName, const std::string& data)
|
private:
|
||||||
{
|
z_stream zlibStream;
|
||||||
if (!isGlobalAttr && (attrName == "path")) {
|
uint8_t* zlibOutput;
|
||||||
// data is UTF-8 encoded path name
|
bool haveInitedZLib = false;
|
||||||
paxPathName = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#if 1 || defined(HAVE_XZ)
|
||||||
|
|
||||||
|
#include <lzma.h>
|
||||||
|
|
||||||
|
class XZTarExtractor : public TarExtractorPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XZTarExtractor(ArchiveExtractor* outer) : TarExtractorPrivate(outer)
|
||||||
|
{
|
||||||
|
_xzStream = LZMA_STREAM_INIT;
|
||||||
|
_outputBuffer = (uint8_t*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
|
||||||
|
|
||||||
|
|
||||||
|
auto ret = lzma_stream_decoder(&_xzStream, UINT64_MAX, LZMA_TELL_ANY_CHECK);
|
||||||
|
if (ret != LZMA_OK) {
|
||||||
|
setState(BAD_ARCHIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~XZTarExtractor()
|
||||||
|
{
|
||||||
|
free(_outputBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void extractBytes(const uint8_t* bytes, size_t count) override
|
||||||
|
{
|
||||||
|
lzma_action action = LZMA_RUN;
|
||||||
|
_xzStream.next_in = bytes;
|
||||||
|
_xzStream.avail_in = count;
|
||||||
|
|
||||||
|
size_t writtenSize;
|
||||||
|
do {
|
||||||
|
_xzStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
||||||
|
_xzStream.next_out = _outputBuffer;
|
||||||
|
|
||||||
|
const auto ret = lzma_code(&_xzStream, action);
|
||||||
|
|
||||||
|
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - _xzStream.avail_out;
|
||||||
|
if (writtenSize > 0) {
|
||||||
|
processBytes((const char*)_outputBuffer, writtenSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == LZMA_GET_CHECK) {
|
||||||
|
//
|
||||||
|
} else if (ret == LZMA_STREAM_END) {
|
||||||
|
setState(END_OF_ARCHIVE);
|
||||||
|
break;
|
||||||
|
} else if (ret != LZMA_OK) {
|
||||||
|
setState(BAD_ARCHIVE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ((_xzStream.avail_in > 0) || (writtenSize > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush() override
|
||||||
|
{
|
||||||
|
const auto ret = lzma_code(&_xzStream, LZMA_FINISH);
|
||||||
|
if (ret != LZMA_STREAM_END) {
|
||||||
|
setState(BAD_ARCHIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
lzma_stream _xzStream;
|
||||||
|
uint8_t* _outputBuffer = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void fill_memory_filefunc(zlib_filefunc_def*);
|
void fill_memory_filefunc(zlib_filefunc_def*);
|
||||||
}
|
}
|
||||||
@@ -511,32 +530,34 @@ public:
|
|||||||
const size_t BUFFER_SIZE = 1024 * 1024;
|
const size_t BUFFER_SIZE = 1024 * 1024;
|
||||||
void* buf = malloc(BUFFER_SIZE);
|
void* buf = malloc(BUFFER_SIZE);
|
||||||
|
|
||||||
try {
|
int result = unzGoToFirstFile(zip);
|
||||||
int result = unzGoToFirstFile(zip);
|
if (result != UNZ_OK) {
|
||||||
if (result != UNZ_OK) {
|
SG_LOG(SG_IO, SG_ALERT, outer->rootPath() << "failed to go to first file in archive:" << result);
|
||||||
throw sg_exception("failed to go to first file in archive");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
extractCurrentFile(zip, (char*)buf, BUFFER_SIZE);
|
|
||||||
if (state == FILTER_STOPPED) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = unzGoToNextFile(zip);
|
|
||||||
if (result == UNZ_END_OF_LIST_OF_FILE) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (result != UNZ_OK) {
|
|
||||||
throw sg_io_exception("failed to go to next file in the archive");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state = END_OF_ARCHIVE;
|
|
||||||
}
|
|
||||||
catch (sg_exception&) {
|
|
||||||
state = BAD_ARCHIVE;
|
state = BAD_ARCHIVE;
|
||||||
|
free(buf);
|
||||||
|
unzClose(zip);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
extractCurrentFile(zip, (char*)buf, BUFFER_SIZE);
|
||||||
|
if (state == FILTER_STOPPED) {
|
||||||
|
state = END_OF_ARCHIVE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = unzGoToNextFile(zip);
|
||||||
|
if (result == UNZ_END_OF_LIST_OF_FILE) {
|
||||||
|
state = END_OF_ARCHIVE;
|
||||||
|
break;
|
||||||
|
} else if (result != UNZ_OK) {
|
||||||
|
SG_LOG(SG_IO, SG_ALERT, outer->rootPath() << "failed to go to next file in archive:" << result);
|
||||||
|
state = BAD_ARCHIVE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
unzClose(zip);
|
unzClose(zip);
|
||||||
}
|
}
|
||||||
@@ -635,17 +656,19 @@ void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
|
|||||||
|
|
||||||
if (r == TarData) {
|
if (r == TarData) {
|
||||||
d.reset(new TarExtractorPrivate(this));
|
d.reset(new TarExtractorPrivate(this));
|
||||||
}
|
} else if (r == GZData) {
|
||||||
else if (r == ZipData) {
|
d.reset(new GZTarExtractor(this));
|
||||||
d.reset(new ZipExtractorPrivate(this));
|
} else if (r == XZData) {
|
||||||
}
|
d.reset(new XZTarExtractor(this));
|
||||||
else {
|
} else if (r == ZipData) {
|
||||||
SG_LOG(SG_IO, SG_WARN, "Invalid archive type");
|
d.reset(new ZipExtractorPrivate(this));
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Invalid archive type");
|
||||||
_invalidDataType = true;
|
_invalidDataType = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if hit here, we created the extractor. Feed the prefbuffer
|
// if hit here, we created the extractor. Feed the prefbuffer
|
||||||
// bytes through it
|
// bytes through it
|
||||||
d->extractBytes((uint8_t*) _prebuffer.data(), _prebuffer.size());
|
d->extractBytes((uint8_t*) _prebuffer.data(), _prebuffer.size());
|
||||||
_prebuffer.clear();
|
_prebuffer.clear();
|
||||||
@@ -697,9 +720,18 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::determineType(const uint8_t*
|
|||||||
return ZipData;
|
return ZipData;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto r = isTarData(bytes, count);
|
if (count < 6) {
|
||||||
if ((r == TarData) || (r == InsufficientData))
|
return InsufficientData;
|
||||||
return r;
|
}
|
||||||
|
|
||||||
|
const uint8_t XZ_HEADER[6] = {0xFD, '7', 'z', 'X', 'Z', 0x00};
|
||||||
|
if (memcmp(bytes, XZ_HEADER, 6) == 0) {
|
||||||
|
return XZData;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r = isTarData(bytes, count);
|
||||||
|
if ((r == TarData) || (r == InsufficientData) || (r == GZData))
|
||||||
|
return r;
|
||||||
|
|
||||||
return Invalid;
|
return Invalid;
|
||||||
}
|
}
|
||||||
@@ -712,6 +744,8 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
UstarHeaderBlock* header = 0;
|
UstarHeaderBlock* header = 0;
|
||||||
|
DetermineResult result = InsufficientData;
|
||||||
|
|
||||||
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
|
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
|
||||||
// GZIP identification bytes
|
// GZIP identification bytes
|
||||||
z_stream z;
|
z_stream z;
|
||||||
@@ -729,11 +763,11 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
|
|||||||
return Invalid;
|
return Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = inflate(&z, Z_SYNC_FLUSH);
|
int zResult = inflate(&z, Z_SYNC_FLUSH);
|
||||||
if ((result == Z_OK) || (result == Z_STREAM_END)) {
|
if ((zResult == Z_OK) || (zResult == Z_STREAM_END)) {
|
||||||
// all good
|
// all good
|
||||||
} else {
|
} else {
|
||||||
SG_LOG(SG_IO, SG_WARN, "isTarData: Zlib inflate failed:" << result);
|
SG_LOG(SG_IO, SG_WARN, "isTarData: Zlib inflate failed:" << zResult);
|
||||||
inflateEnd(&z);
|
inflateEnd(&z);
|
||||||
return Invalid; // not tar data
|
return Invalid; // not tar data
|
||||||
}
|
}
|
||||||
@@ -746,6 +780,7 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
|
|||||||
|
|
||||||
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
|
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
|
||||||
inflateEnd(&z);
|
inflateEnd(&z);
|
||||||
|
result = GZData;
|
||||||
} else {
|
} else {
|
||||||
// uncompressed tar
|
// uncompressed tar
|
||||||
if (count < TAR_HEADER_BLOCK_SIZE) {
|
if (count < TAR_HEADER_BLOCK_SIZE) {
|
||||||
@@ -753,13 +788,14 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
header = (UstarHeaderBlock*) bytes;
|
header = (UstarHeaderBlock*) bytes;
|
||||||
|
result = TarData;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
|
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
|
||||||
return Invalid;
|
return Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TarData;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArchiveExtractor::extractLocalFile(const SGPath& archiveFile)
|
void ArchiveExtractor::extractLocalFile(const SGPath& archiveFile)
|
||||||
|
|||||||
+14
-8
@@ -36,15 +36,16 @@ public:
|
|||||||
ArchiveExtractor(const SGPath& rootPath);
|
ArchiveExtractor(const SGPath& rootPath);
|
||||||
virtual ~ArchiveExtractor();
|
virtual ~ArchiveExtractor();
|
||||||
|
|
||||||
enum DetermineResult
|
enum DetermineResult {
|
||||||
{
|
Invalid,
|
||||||
Invalid,
|
InsufficientData,
|
||||||
InsufficientData,
|
TarData,
|
||||||
TarData,
|
ZipData,
|
||||||
ZipData
|
GZData, // Gzipped-tar
|
||||||
};
|
XZData // XZ (aka LZMA) tar
|
||||||
|
};
|
||||||
|
|
||||||
static DetermineResult determineType(const uint8_t* bytes, size_t count);
|
static DetermineResult determineType(const uint8_t* bytes, size_t count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief API to extract a local zip or tar.gz
|
* @brief API to extract a local zip or tar.gz
|
||||||
@@ -70,6 +71,11 @@ public:
|
|||||||
Stop
|
Stop
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SGPath rootPath() const
|
||||||
|
{
|
||||||
|
return _rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ public:
|
|||||||
/// Default constructor, initializes the instance to lat = lon = elev = 0
|
/// Default constructor, initializes the instance to lat = lon = elev = 0
|
||||||
SGGeod(void);
|
SGGeod(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
return an SGGeod for which isValid() returns false.
|
||||||
|
This is necessaerby becuase for historical reasons, ther defaulrt constructor above initialsies to zero,zero,zero
|
||||||
|
which *is*
|
||||||
|
*/
|
||||||
|
static SGGeod invalid();
|
||||||
|
|
||||||
/// Factory from angular values in radians and elevation is 0
|
/// Factory from angular values in radians and elevation is 0
|
||||||
static SGGeod fromRad(double lon, double lat);
|
static SGGeod fromRad(double lon, double lat);
|
||||||
/// Factory from angular values in degrees and elevation is 0
|
/// Factory from angular values in degrees and elevation is 0
|
||||||
|
|||||||
@@ -663,3 +663,8 @@ SGGeodesy::radialIntersection(const SGGeod& a, double aRadial,
|
|||||||
result = SGGeod::fromGeoc(r);
|
result = SGGeod::fromGeoc(r);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SGGeod SGGeod::invalid()
|
||||||
|
{
|
||||||
|
return SGGeod::fromDeg(-999.9, -999.0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ void naFreeContext(naContext c)
|
|||||||
// than I have right now. So instead I'm clearing the stack tops here, so
|
// than I have right now. So instead I'm clearing the stack tops here, so
|
||||||
// a freed context looks the same as a new one returned by initContext.
|
// a freed context looks the same as a new one returned by initContext.
|
||||||
|
|
||||||
c->fTop = c->opTop = c->markTop = 0;
|
c->fTop = c->opTop = c->markTop = c->ntemps = 0;
|
||||||
|
|
||||||
c->nextFree = globals->freeContexts;
|
c->nextFree = globals->freeContexts;
|
||||||
globals->freeContexts = c;
|
globals->freeContexts = c;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace nasal
|
|||||||
|
|
||||||
class NasalMainLoopRecipient : public simgear::Emesary::IReceiver {
|
class NasalMainLoopRecipient : public simgear::Emesary::IReceiver {
|
||||||
public:
|
public:
|
||||||
NasalMainLoopRecipient() : receiveCount(0) {
|
NasalMainLoopRecipient() : receiveCount(0), Active(false), CanWait(false) {
|
||||||
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
|
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
|
||||||
}
|
}
|
||||||
virtual ~NasalMainLoopRecipient() {
|
virtual ~NasalMainLoopRecipient() {
|
||||||
|
|||||||
@@ -100,7 +100,14 @@ naRef naNewHash(struct Context* c)
|
|||||||
|
|
||||||
naRef naNewCode(struct Context* c)
|
naRef naNewCode(struct Context* c)
|
||||||
{
|
{
|
||||||
return naNew(c, T_CODE);
|
naRef r = naNew(c, T_CODE);
|
||||||
|
// naNew can return a previously used naCode. naCodeGen will init
|
||||||
|
// all these members but a GC can occur inside naCodeGen, so we see
|
||||||
|
// partially initalized state here. To avoid this, clear out the values
|
||||||
|
// which mark() cares about.
|
||||||
|
PTR(r).code->srcFile = naNil();
|
||||||
|
PTR(r).code->nConstants = 0;
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
naRef naNewCCode(struct Context* c, naCFunction fptr)
|
naRef naNewCCode(struct Context* c, naCFunction fptr)
|
||||||
|
|||||||
@@ -133,7 +133,9 @@ public:
|
|||||||
|
|
||||||
void fireRefreshStatus(CatalogRef catalog, Delegate::StatusCode status)
|
void fireRefreshStatus(CatalogRef catalog, Delegate::StatusCode status)
|
||||||
{
|
{
|
||||||
for (auto d : delegates) {
|
// take a copy of delegates since firing this can modify the data
|
||||||
|
const auto currentDelegates = delegates;
|
||||||
|
for (auto d : currentDelegates) {
|
||||||
d->catalogRefreshed(catalog, status);
|
d->catalogRefreshed(catalog, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -722,12 +724,6 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
|
|||||||
auto catIt = d->catalogs.find(aCat->id());
|
auto catIt = d->catalogs.find(aCat->id());
|
||||||
d->fireRefreshStatus(aCat, aReason);
|
d->fireRefreshStatus(aCat, aReason);
|
||||||
|
|
||||||
if (aReason == Delegate::STATUS_IN_PROGRESS) {
|
|
||||||
d->refreshing.insert(aCat);
|
|
||||||
} else {
|
|
||||||
d->refreshing.erase(aCat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aCat->isUserEnabled() &&
|
if (aCat->isUserEnabled() &&
|
||||||
(aReason == Delegate::STATUS_REFRESHED) &&
|
(aReason == Delegate::STATUS_REFRESHED) &&
|
||||||
(catIt == d->catalogs.end()))
|
(catIt == d->catalogs.end()))
|
||||||
@@ -761,6 +757,17 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
|
|||||||
}
|
}
|
||||||
} // of catalog is disabled
|
} // of catalog is disabled
|
||||||
|
|
||||||
|
// remove from refreshing /after/ checking for enable / disabled, since for
|
||||||
|
// new catalogs, the reference in d->refreshing might be our /only/
|
||||||
|
// reference to the catalog. Once the refresh is done (either failed or
|
||||||
|
// succeeded) the Catalog will be in either d->catalogs or
|
||||||
|
// d->disabledCatalogs
|
||||||
|
if (aReason == Delegate::STATUS_IN_PROGRESS) {
|
||||||
|
d->refreshing.insert(aCat);
|
||||||
|
} else {
|
||||||
|
d->refreshing.erase(aCat);
|
||||||
|
}
|
||||||
|
|
||||||
if (d->refreshing.empty()) {
|
if (d->refreshing.empty()) {
|
||||||
d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
|
d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
|
||||||
d->firePackagesChanged();
|
d->firePackagesChanged();
|
||||||
|
|||||||
+25
-14
@@ -19,6 +19,10 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <exception> // can't use sg_exception becuase of PROPS_STANDALONE
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -51,6 +55,10 @@ using namespace simgear;
|
|||||||
|
|
||||||
struct SGPropertyNodeListeners
|
struct SGPropertyNodeListeners
|
||||||
{
|
{
|
||||||
|
/* Protect _num_iterators and _items. We use a recursive mutex to allow
|
||||||
|
nested access to work as normal. */
|
||||||
|
std::recursive_mutex _rmutex;
|
||||||
|
|
||||||
/* This keeps a count of the current number of nested invocations of
|
/* This keeps a count of the current number of nested invocations of
|
||||||
forEachListener(). If non-zero, other code higher up the stack is iterating
|
forEachListener(). If non-zero, other code higher up the stack is iterating
|
||||||
_items[] so for example code must not erase items in the vector. */
|
_items[] so for example code must not erase items in the vector. */
|
||||||
@@ -558,23 +566,15 @@ find_node (SGPropertyNode * current,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
template<typename Range>
|
template <typename Range>
|
||||||
SGPropertyNode*
|
SGPropertyNode *find_node(SGPropertyNode *current, const Range &path,
|
||||||
find_node (SGPropertyNode * current,
|
bool create, int last_index = -1) {
|
||||||
const Range& path,
|
|
||||||
bool create,
|
|
||||||
int last_index = -1)
|
|
||||||
{
|
|
||||||
using namespace boost;
|
using namespace boost;
|
||||||
typedef split_iterator<typename range_result_iterator<Range>::type>
|
auto itr = make_split_iterator(path, first_finder("/", is_equal()));
|
||||||
PathSplitIterator;
|
|
||||||
|
|
||||||
PathSplitIterator itr
|
|
||||||
= make_split_iterator(path, first_finder("/", is_equal()));
|
|
||||||
if (*path.begin() == '/')
|
if (*path.begin() == '/')
|
||||||
return find_node_aux(current->getRootNode(), itr, create, last_index);
|
return find_node_aux(current->getRootNode(), itr, create, last_index);
|
||||||
else
|
else
|
||||||
return find_node_aux(current, itr, create, last_index);
|
return find_node_aux(current, itr, create, last_index);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2406,6 +2406,7 @@ SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
|
|||||||
if (_listeners == 0)
|
if (_listeners == 0)
|
||||||
_listeners = new SGPropertyNodeListeners;
|
_listeners = new SGPropertyNodeListeners;
|
||||||
|
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(_listeners->_rmutex);
|
||||||
/* If there's a nullptr entry (a listener that was unregistered), we
|
/* If there's a nullptr entry (a listener that was unregistered), we
|
||||||
overwrite it. This ensures that listeners that routinely unregister+register
|
overwrite it. This ensures that listeners that routinely unregister+register
|
||||||
themselves don't make _listeners->_items grow unnecessarily. Otherwise simply
|
themselves don't make _listeners->_items grow unnecessarily. Otherwise simply
|
||||||
@@ -2429,9 +2430,13 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
|
|||||||
{
|
{
|
||||||
if (_listeners == 0)
|
if (_listeners == 0)
|
||||||
return;
|
return;
|
||||||
|
/* We use a std::unique_lock rather than a std::lock_guard because we may
|
||||||
|
need to unlock early. */
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(_listeners->_rmutex);
|
||||||
vector<SGPropertyChangeListener*>::iterator it =
|
vector<SGPropertyChangeListener*>::iterator it =
|
||||||
find(_listeners->_items.begin(), _listeners->_items.end(), listener);
|
find(_listeners->_items.begin(), _listeners->_items.end(), listener);
|
||||||
if (it != _listeners->_items.end()) {
|
if (it != _listeners->_items.end()) {
|
||||||
|
assert(_listeners->_num_iterators >= 0);
|
||||||
if (_listeners->_num_iterators) {
|
if (_listeners->_num_iterators) {
|
||||||
/* _listeners._items is currently being iterated further up the stack in
|
/* _listeners._items is currently being iterated further up the stack in
|
||||||
this thread by one or more nested invocations of forEachListener(), so
|
this thread by one or more nested invocations of forEachListener(), so
|
||||||
@@ -2450,6 +2455,7 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
|
|||||||
_listeners->_items.erase(it);
|
_listeners->_items.erase(it);
|
||||||
listener->unregister_property(this);
|
listener->unregister_property(this);
|
||||||
if (_listeners->_items.empty()) {
|
if (_listeners->_items.empty()) {
|
||||||
|
lock.unlock();
|
||||||
delete _listeners;
|
delete _listeners;
|
||||||
_listeners = 0;
|
_listeners = 0;
|
||||||
}
|
}
|
||||||
@@ -2512,6 +2518,8 @@ static void forEachListener(
|
|||||||
{
|
{
|
||||||
if (!_listeners) return;
|
if (!_listeners) return;
|
||||||
|
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(_listeners->_rmutex);
|
||||||
|
assert(_listeners->_num_iterators >= 0);
|
||||||
_listeners->_num_iterators += 1;
|
_listeners->_num_iterators += 1;
|
||||||
|
|
||||||
/* We need to use an index here when iterating _listeners->_items, not an
|
/* We need to use an index here when iterating _listeners->_items, not an
|
||||||
@@ -2530,8 +2538,10 @@ static void forEachListener(
|
|||||||
}
|
}
|
||||||
|
|
||||||
_listeners->_num_iterators -= 1;
|
_listeners->_num_iterators -= 1;
|
||||||
|
assert(_listeners->_num_iterators >= 0);
|
||||||
|
|
||||||
if (_listeners->_num_iterators == 0) {
|
if (_listeners->_num_iterators == 0) {
|
||||||
|
|
||||||
/* Remove any items that have been set to nullptr. */
|
/* Remove any items that have been set to nullptr. */
|
||||||
_listeners->_items.erase(
|
_listeners->_items.erase(
|
||||||
std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr),
|
std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr),
|
||||||
@@ -2547,6 +2557,7 @@ static void forEachListener(
|
|||||||
int SGPropertyNode::nListeners() const
|
int SGPropertyNode::nListeners() const
|
||||||
{
|
{
|
||||||
if (!_listeners) return 0;
|
if (!_listeners) return 0;
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(_listeners->_rmutex);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (auto listener: _listeners->_items) {
|
for (auto listener: _listeners->_items) {
|
||||||
if (listener) n += 1;
|
if (listener) n += 1;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
#include <simgear/structure/SGExpression.hxx>
|
#include <simgear/structure/SGExpression.hxx>
|
||||||
#include <simgear/props/props_io.hxx>
|
#include <simgear/props/props_io.hxx>
|
||||||
#include <simgear/props/vectorPropTemplates.hxx>
|
#include <simgear/props/vectorPropTemplates.hxx>
|
||||||
|
#include <simgear/debug/ErrorReportingCallback.hxx>
|
||||||
#include <simgear/io/iostreams/sgstream.hxx>
|
#include <simgear/io/iostreams/sgstream.hxx>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
@@ -928,7 +928,8 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
|
|||||||
// FIXME orig: const string& shaderName = shaderKey.first;
|
// FIXME orig: const string& shaderName = shaderKey.first;
|
||||||
string shaderName = shaderKey.first;
|
string shaderName = shaderKey.first;
|
||||||
Shader::Type stype = (Shader::Type)shaderKey.second;
|
Shader::Type stype = (Shader::Type)shaderKey.second;
|
||||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) &&
|
const bool compositorEnabled = getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false);
|
||||||
|
if (compositorEnabled &&
|
||||||
shaderName.substr(0, shaderName.find("/")) == "Shaders") {
|
shaderName.substr(0, shaderName.find("/")) == "Shaders") {
|
||||||
shaderName = "Compositor/" + shaderName;
|
shaderName = "Compositor/" + shaderName;
|
||||||
}
|
}
|
||||||
@@ -936,7 +937,9 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
|
|||||||
if (fileName.empty())
|
if (fileName.empty())
|
||||||
{
|
{
|
||||||
SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader" << shaderName);
|
SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader" << shaderName);
|
||||||
|
if (!compositorEnabled) {
|
||||||
|
reportError("Missing shader", shaderName);
|
||||||
|
}
|
||||||
|
|
||||||
throw BuilderException(string("couldn't find shader ") +
|
throw BuilderException(string("couldn't find shader ") +
|
||||||
shaderName);
|
shaderName);
|
||||||
@@ -1481,10 +1484,8 @@ void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options)
|
|||||||
|
|
||||||
// Walk the techniques property tree, building techniques and
|
// Walk the techniques property tree, building techniques and
|
||||||
// passes.
|
// passes.
|
||||||
static std::mutex realizeTechniques_lock;
|
|
||||||
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
|
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> g(realizeTechniques_lock);
|
|
||||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
||||||
mergeSchemesFallbacks(this, options);
|
mergeSchemesFallbacks(this, options);
|
||||||
|
|
||||||
|
|||||||
@@ -273,11 +273,19 @@ bool setAttrs(const TexTuple& attrs, Texture* tex,
|
|||||||
options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED);
|
options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED);
|
||||||
else
|
else
|
||||||
options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS);
|
options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS);
|
||||||
#if OSG_VERSION_LESS_THAN(3,4,2)
|
|
||||||
result = osgDB::readImageFile(imageName, options);
|
try {
|
||||||
#else
|
#if OSG_VERSION_LESS_THAN(3,4,2)
|
||||||
result = osgDB::readRefImageFile(imageName, options);
|
result = osgDB::readImageFile(imageName, options);
|
||||||
#endif
|
#else
|
||||||
|
result = osgDB::readRefImageFile(imageName, options);
|
||||||
|
#endif
|
||||||
|
} catch (std::bad_alloc& ba) {
|
||||||
|
SG_LOG(SG_GL, SG_ALERT, "Bad allocation loading:" << imageName);
|
||||||
|
// todo: report low memory warning
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
options->setLoadOriginHint(origLOH);
|
options->setLoadOriginHint(origLOH);
|
||||||
osg::ref_ptr<osg::Image> image;
|
osg::ref_ptr<osg::Image> image;
|
||||||
if (result.success())
|
if (result.success())
|
||||||
|
|||||||
@@ -393,6 +393,10 @@ ModelRegistry::readImage(const string& fileName,
|
|||||||
isEffect = true;
|
isEffect = true;
|
||||||
// can_compress = false;
|
// can_compress = false;
|
||||||
}
|
}
|
||||||
|
else if (sgoptC && !transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_CANVAS) {
|
||||||
|
SG_LOG(SG_IO, SG_INFO, "From Canvas " + absFileName + " will generate mipmap only");
|
||||||
|
can_compress = false;
|
||||||
|
}
|
||||||
if (can_compress)
|
if (can_compress)
|
||||||
{
|
{
|
||||||
std::string pot_message;
|
std::string pot_message;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ void SGText::UpdateCallback::operator()(osg::Node * node, osg::NodeVisitor *nv )
|
|||||||
// be lazy and set the text only if the property has changed.
|
// be lazy and set the text only if the property has changed.
|
||||||
// update() computes the glyph representation which looks
|
// update() computes the glyph representation which looks
|
||||||
// more expensive than a the above string compare.
|
// more expensive than a the above string compare.
|
||||||
text->setText( buf );
|
text->setText( buf, osgText::String::ENCODING_UTF8 );
|
||||||
text->update();
|
text->update();
|
||||||
}
|
}
|
||||||
traverse( node, nv );
|
traverse( node, nv );
|
||||||
|
|||||||
@@ -1135,16 +1135,8 @@ void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||||||
double intPart;
|
double intPart;
|
||||||
double rot = modf(rotation, &intPart);
|
double rot = modf(rotation, &intPart);
|
||||||
double angle = rot * 2.0 * osg::PI;
|
double angle = rot * 2.0 * osg::PI;
|
||||||
const SGVec3d& sgcenter = transform->getCenter();
|
transform->setAngleRad(angle);
|
||||||
const SGVec3d& sgaxis = transform->getAxis();
|
|
||||||
Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
|
|
||||||
* Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
|
|
||||||
* Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
|
|
||||||
* *cv->getModelViewMatrix();
|
|
||||||
ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
|
|
||||||
cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
|
|
||||||
traverse(transform, nv);
|
traverse(transform, nv);
|
||||||
cv->popModelViewMatrix();
|
|
||||||
} else {
|
} else {
|
||||||
traverse(transform, nv);
|
traverse(transform, nv);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
#include <osgParticle/MultiSegmentPlacer>
|
#include <osgParticle/MultiSegmentPlacer>
|
||||||
#include <osgParticle/SectorPlacer>
|
#include <osgParticle/SectorPlacer>
|
||||||
#include <osgParticle/ConstantRateCounter>
|
#include <osgParticle/ConstantRateCounter>
|
||||||
#include <osgParticle/ParticleSystemUpdater>
|
|
||||||
#include <osgParticle/ParticleSystem>
|
#include <osgParticle/ParticleSystem>
|
||||||
#include <osgParticle/FluidProgram>
|
#include <osgParticle/FluidProgram>
|
||||||
|
|
||||||
|
#include <osgUtil/CullVisitor>
|
||||||
#include <osg/Geode>
|
#include <osg/Geode>
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
@@ -48,67 +48,249 @@
|
|||||||
|
|
||||||
#include <simgear/scene/model/animation.hxx>
|
#include <simgear/scene/model/animation.hxx>
|
||||||
|
|
||||||
|
using ParticleSystemRef = osg::ref_ptr<osgParticle::ParticleSystem>;
|
||||||
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
|
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater),
|
ParticlesGlobalManagerPrivate();
|
||||||
_commonGeode(new osg::Geode)
|
|
||||||
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
|
||||||
|
|
||||||
|
// only call this with the lock held!
|
||||||
|
osg::Group* internalGetCommonRoot();
|
||||||
|
|
||||||
|
void updateParticleSystemsFromCullCallback(int currentFrameNumber, osg::NodeVisitor* nv);
|
||||||
|
|
||||||
|
void addParticleSystem(osgParticle::ParticleSystem* ps, const osg::ref_ptr<osg::Group>& frame);
|
||||||
|
|
||||||
|
void registerNewLocalParticleSystem(osg::Node* node, ParticleSystemRef ps);
|
||||||
|
void registerNewWorldParticleSystem(osg::Node* node, ParticleSystemRef ps, osg::Group* frame);
|
||||||
|
|
||||||
|
std::mutex _lock;
|
||||||
|
bool _frozen = false;
|
||||||
|
double _simulationDt = 0.0;
|
||||||
|
osg::ref_ptr<osg::Group> _commonRoot;
|
||||||
|
osg::ref_ptr<osg::Geode> _commonGeode;
|
||||||
|
osg::Vec3 _wind;
|
||||||
|
bool _enabled = true;
|
||||||
|
osg::Vec3 _gravity;
|
||||||
|
// osg::Vec3 _localWind;
|
||||||
|
SGGeod _currentPosition;
|
||||||
|
|
||||||
|
SGConstPropertyNode_ptr _enabledNode;
|
||||||
|
|
||||||
|
using ParticleSystemWeakRef = osg::observer_ptr<osgParticle::ParticleSystem>;
|
||||||
|
using ParticleSystemsWeakRefVec = std::vector<ParticleSystemWeakRef>;
|
||||||
|
|
||||||
|
using ParticleSystemsStrongRefVec = std::vector<ParticleSystemRef>;
|
||||||
|
|
||||||
|
ParticleSystemsWeakRefVec _systems;
|
||||||
|
osg::ref_ptr<ParticlesGlobalManager::UpdaterCallback> _cullCallback;
|
||||||
|
|
||||||
|
using GroupRefVec = std::vector<osg::ref_ptr<osg::Group>>;
|
||||||
|
GroupRefVec _newWorldParticles;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief this class replaces the need to use osgParticle::ParticleSystemUpdater, which has some
|
||||||
|
thread-safety and ownership complications in our us case
|
||||||
|
*/
|
||||||
|
class ParticlesGlobalManager::UpdaterCallback : public osg::NodeCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UpdaterCallback(ParticlesGlobalManagerPrivate* p) : _manager(p)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> g(_lock);
|
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
|
||||||
_enabled = !_enabledNode || _enabledNode->getBoolValue();
|
if (cv && nv->getFrameStamp()) {
|
||||||
|
if (_frameNumber < nv->getFrameStamp()->getFrameNumber()) {
|
||||||
if (!_enabled)
|
_frameNumber = nv->getFrameStamp()->getFrameNumber();
|
||||||
return;
|
_manager->updateParticleSystemsFromCullCallback(_frameNumber, nv);
|
||||||
|
}
|
||||||
const auto q = SGQuatd::fromLonLatDeg(_longitudeNode->getFloatValue(), _latitudeNode->getFloatValue());
|
|
||||||
osg::Matrix om(toOsg(q));
|
|
||||||
osg::Vec3 v(0, 0, 9.81);
|
|
||||||
|
|
||||||
_gravity = om.preMult(v);
|
|
||||||
|
|
||||||
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
|
|
||||||
// const osg::Vec3& zUpWind = _wind;
|
|
||||||
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
|
|
||||||
// _localWind = om.preMult(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only call this with the lock held!
|
|
||||||
osg::Group* internalGetCommonRoot()
|
|
||||||
{
|
|
||||||
if (!_commonRoot.valid()) {
|
|
||||||
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
|
|
||||||
_commonRoot = new osg::Group;
|
|
||||||
_commonRoot->setName("common particle system root");
|
|
||||||
_commonGeode->setName("common particle system geode");
|
|
||||||
_commonRoot->addChild(_commonGeode);
|
|
||||||
_commonRoot->addChild(_updater);
|
|
||||||
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
|
|
||||||
}
|
}
|
||||||
return _commonRoot.get();
|
|
||||||
|
// note, callback is responsible for scenegraph traversal so
|
||||||
|
// they must call traverse(node,nv) to ensure that the
|
||||||
|
// scene graph subtree (and associated callbacks) are traversed.
|
||||||
|
traverse(node, nv);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mutex _lock;
|
unsigned int _frameNumber = 0;
|
||||||
bool _frozen = false;
|
ParticlesGlobalManagerPrivate* _manager;
|
||||||
osg::ref_ptr<osgParticle::ParticleSystemUpdater> _updater;
|
|
||||||
osg::ref_ptr<osg::Group> _commonRoot;
|
|
||||||
osg::ref_ptr<osg::Geode> _commonGeode;
|
|
||||||
osg::Vec3 _wind;
|
|
||||||
bool _globalCallbackRegistered = false;
|
|
||||||
bool _enabled = true;
|
|
||||||
osg::Vec3 _gravity;
|
|
||||||
// osg::Vec3 _localWind;
|
|
||||||
|
|
||||||
SGConstPropertyNode_ptr _enabledNode;
|
|
||||||
SGConstPropertyNode_ptr _longitudeNode, _latitudeNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
single-shot node callback, used to register a particle system with the global manager. Once run, removes
|
||||||
|
itself. We used this to avoid updating a particle system until the load is complete and merged into the
|
||||||
|
main scene.
|
||||||
|
*/
|
||||||
|
class ParticlesGlobalManager::RegistrationCallback : public osg::NodeCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
||||||
|
{
|
||||||
|
auto d = ParticlesGlobalManager::instance()->d;
|
||||||
|
d->addParticleSystem(_system, _frame);
|
||||||
|
node->removeUpdateCallback(this); // suicide
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystemRef _system;
|
||||||
|
osg::ref_ptr<osg::Group> _frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
ParticlesGlobalManager::ParticlesGlobalManagerPrivate::ParticlesGlobalManagerPrivate() : _commonGeode(new osg::Geode),
|
||||||
|
_cullCallback(new UpdaterCallback(this))
|
||||||
|
{
|
||||||
|
// callbacks are registered in initFromMainThread : depending on timing,
|
||||||
|
// this constructor might be called from an osgDB thread
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::addParticleSystem(osgParticle::ParticleSystem* ps, const osg::ref_ptr<osg::Group>& frame)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(_lock);
|
||||||
|
_systems.push_back(ps);
|
||||||
|
|
||||||
|
// we're inside an update callback here, so better not modify the
|
||||||
|
// scene structure; defer it to later
|
||||||
|
if (frame.get()) {
|
||||||
|
_newWorldParticles.push_back(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::registerNewLocalParticleSystem(osg::Node* node, ParticleSystemRef ps)
|
||||||
|
{
|
||||||
|
auto cb = new RegistrationCallback;
|
||||||
|
cb->_system = ps;
|
||||||
|
node->addUpdateCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::registerNewWorldParticleSystem(osg::Node* node, ParticleSystemRef ps, osg::Group* frame)
|
||||||
|
{
|
||||||
|
auto cb = new RegistrationCallback;
|
||||||
|
cb->_system = ps;
|
||||||
|
cb->_frame = frame;
|
||||||
|
node->addUpdateCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is called from the main thread during scenery init
|
||||||
|
// after this, we beign updarting particle systems
|
||||||
|
void ParticlesGlobalManager::initFromMainThread()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
d->internalGetCommonRoot();
|
||||||
|
d->_commonRoot->addUpdateCallback(d.get());
|
||||||
|
d->_commonRoot->addCullCallback(d->_cullCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::update(double dt, const SGGeod& pos)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
d->_simulationDt = dt;
|
||||||
|
d->_currentPosition = pos;
|
||||||
|
|
||||||
|
for (auto f : d->_newWorldParticles) {
|
||||||
|
d->internalGetCommonRoot()->addChild(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->_newWorldParticles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is called from the main thread, since it's an update callback
|
||||||
|
// lock any state used by updateParticleSystemsFromCullCallback, which
|
||||||
|
// runs during culling, potentialy on a different thread
|
||||||
|
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(_lock);
|
||||||
|
_enabled = !_enabledNode || _enabledNode->getBoolValue();
|
||||||
|
|
||||||
|
if (!_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto q = SGQuatd::fromLonLat(_currentPosition);
|
||||||
|
osg::Matrix om(toOsg(q));
|
||||||
|
osg::Vec3 v(0, 0, 9.81);
|
||||||
|
_gravity = om.preMult(v);
|
||||||
|
|
||||||
|
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
|
||||||
|
// const osg::Vec3& zUpWind = _wind;
|
||||||
|
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
|
||||||
|
// _localWind = om.preMult(w);
|
||||||
|
|
||||||
|
// while we have the lock, remove all expired systems
|
||||||
|
auto firstInvalid = std::remove_if(_systems.begin(), _systems.end(), [](const ParticleSystemWeakRef& weak) {
|
||||||
|
return !weak.valid();
|
||||||
|
});
|
||||||
|
_systems.erase(firstInvalid, _systems.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// only call this with the lock held!
|
||||||
|
osg::Group* ParticlesGlobalManager::ParticlesGlobalManagerPrivate::internalGetCommonRoot()
|
||||||
|
{
|
||||||
|
if (!_commonRoot.valid()) {
|
||||||
|
_commonRoot = new osg::Group;
|
||||||
|
_commonRoot->setName("common particle system root");
|
||||||
|
_commonGeode->setName("common particle system geode");
|
||||||
|
_commonRoot->addChild(_commonGeode);
|
||||||
|
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
|
||||||
|
}
|
||||||
|
return _commonRoot.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::updateParticleSystemsFromCullCallback(int frameNumber, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
ParticleSystemsStrongRefVec activeSystems;
|
||||||
|
double dt = 0.0;
|
||||||
|
|
||||||
|
// begin locked section
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(_lock);
|
||||||
|
activeSystems.reserve(_systems.size());
|
||||||
|
for (const auto& psref : _systems) {
|
||||||
|
ParticleSystemRef owningRef;
|
||||||
|
if (!psref.lock(owningRef)) {
|
||||||
|
// pointed to system is gone, skip it
|
||||||
|
// we will clean these up in the update callback, don't
|
||||||
|
// worry about that here
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to the list we will update now
|
||||||
|
activeSystems.push_back(owningRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
dt = _simulationDt;
|
||||||
|
} // of locked section
|
||||||
|
|
||||||
|
// from here on, don't access class data; copy it all to local variables
|
||||||
|
// before this line. this is important so we're not holding _lock during
|
||||||
|
// culling, which might block osgDB threads for example.
|
||||||
|
|
||||||
|
if (dt <= 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& ps : activeSystems) {
|
||||||
|
// code inside here is copied from osgParticle::ParticleSystemUpdate
|
||||||
|
osgParticle::ParticleSystem::ScopedWriteLock lock(*(ps->getReadWriteMutex()));
|
||||||
|
|
||||||
|
// We need to allow at least 2 frames difference, because the particle system's lastFrameNumber
|
||||||
|
// is updated in the draw thread which may not have completed yet.
|
||||||
|
if (!ps->isFrozen() &&
|
||||||
|
(!ps->getFreezeOnCull() || ((frameNumber - ps->getLastFrameNumber()) <= 2))) {
|
||||||
|
ps->update(dt, *nv);
|
||||||
|
}
|
||||||
|
// end of code copied from osgParticle::ParticleSystemUpdate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static std::mutex static_managerLock;
|
static std::mutex static_managerLock;
|
||||||
static std::unique_ptr<ParticlesGlobalManager> static_instance;
|
static std::unique_ptr<ParticlesGlobalManager> static_instance;
|
||||||
|
|
||||||
@@ -132,14 +314,6 @@ ParticlesGlobalManager::ParticlesGlobalManager() : d(new ParticlesGlobalManagerP
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticlesGlobalManager::~ParticlesGlobalManager()
|
|
||||||
{
|
|
||||||
if (d->_globalCallbackRegistered) {
|
|
||||||
// is this actually necessary? possibly not
|
|
||||||
d->_updater->setUpdateCallback(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParticlesGlobalManager::isEnabled() const
|
bool ParticlesGlobalManager::isEnabled() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> g(d->_lock);
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
@@ -728,23 +902,10 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
|
|||||||
emitter->setUpdateCallback(callback.get());
|
emitter->setUpdateCallback(callback.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// touch shared data now (and not before)
|
if (attach == "local") {
|
||||||
|
d->registerNewLocalParticleSystem(align, particleSys);
|
||||||
{
|
} else {
|
||||||
std::lock_guard<std::mutex> g(d->_lock);
|
d->registerNewWorldParticleSystem(align, particleSys, callback()->particleFrame);
|
||||||
d->_updater->addParticleSystem(particleSys);
|
|
||||||
|
|
||||||
if (attach != "local") {
|
|
||||||
d->internalGetCommonRoot()->addChild(callback()->particleFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!d->_globalCallbackRegistered) {
|
|
||||||
SG_LOG(SG_PARTICLES, SG_INFO, "Registering global particles callback");
|
|
||||||
d->_globalCallbackRegistered = true;
|
|
||||||
d->_longitudeNode = modelRoot->getNode("/position/longitude-deg", true);
|
|
||||||
d->_latitudeNode = modelRoot->getNode("/position/latitude-deg", true);
|
|
||||||
d->_updater->setUpdateCallback(d.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return align;
|
return align;
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ class NodeVisitor;
|
|||||||
namespace osgParticle
|
namespace osgParticle
|
||||||
{
|
{
|
||||||
class ParticleSystem;
|
class ParticleSystem;
|
||||||
class ParticleSystemUpdater;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||||
@@ -151,7 +150,7 @@ protected:
|
|||||||
class ParticlesGlobalManager
|
class ParticlesGlobalManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~ParticlesGlobalManager();
|
~ParticlesGlobalManager() = default;
|
||||||
|
|
||||||
static ParticlesGlobalManager* instance();
|
static ParticlesGlobalManager* instance();
|
||||||
static void clear();
|
static void clear();
|
||||||
@@ -161,14 +160,18 @@ public:
|
|||||||
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
|
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
|
||||||
|
|
||||||
osg::Group* getCommonRoot();
|
osg::Group* getCommonRoot();
|
||||||
|
|
||||||
osg::Geode* getCommonGeode();
|
osg::Geode* getCommonGeode();
|
||||||
|
|
||||||
osgParticle::ParticleSystemUpdater* getPSU();
|
void initFromMainThread();
|
||||||
|
|
||||||
void setFrozen(bool e);
|
void setFrozen(bool e);
|
||||||
bool isFrozen() const;
|
bool isFrozen() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief update function: call from the main thread , outside of OSG calls.
|
||||||
|
*/
|
||||||
|
void update(double dt, const SGGeod& pos);
|
||||||
|
|
||||||
void setSwitchNode(const SGPropertyNode* n);
|
void setSwitchNode(const SGPropertyNode* n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,6 +188,8 @@ private:
|
|||||||
ParticlesGlobalManager();
|
ParticlesGlobalManager();
|
||||||
|
|
||||||
class ParticlesGlobalManagerPrivate;
|
class ParticlesGlobalManagerPrivate;
|
||||||
|
class UpdaterCallback;
|
||||||
|
class RegistrationCallback;
|
||||||
|
|
||||||
// because Private inherits NodeCallback, we need to own it
|
// because Private inherits NodeCallback, we need to own it
|
||||||
// via an osg::ref_ptr
|
// via an osg::ref_ptr
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ ReaderWriterSPT::createTree(const BucketBox& bucketBox, const LocalOptions& opti
|
|||||||
osg::ref_ptr<osg::Node>
|
osg::ref_ptr<osg::Node>
|
||||||
ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const LocalOptions& options) const
|
ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const LocalOptions& options) const
|
||||||
{
|
{
|
||||||
osg::PagedLOD* pagedLOD = new osg::PagedLOD;
|
osg::ref_ptr<osg::PagedLOD> pagedLOD = new osg::PagedLOD;
|
||||||
|
|
||||||
pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER);
|
pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER);
|
||||||
SGSpheref sphere = bucketBox.getBoundingSphere();
|
SGSpheref sphere = bucketBox.getBoundingSphere();
|
||||||
|
|||||||
@@ -212,7 +212,8 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
|
STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
|
||||||
quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end());
|
quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end());
|
||||||
osg::ref_ptr<osg::Group> group = quadtree.getRoot();
|
osg::ref_ptr<osg::Group> group = quadtree.getRoot();
|
||||||
group->setName("STG-group-A");
|
string group_name = string("STG-group-A ").append(_bucket.gen_index_str());
|
||||||
|
group->setName(group_name);
|
||||||
group->setDataVariance(osg::Object::STATIC);
|
group->setDataVariance(osg::Object::STATIC);
|
||||||
|
|
||||||
simgear::AirportSignBuilder signBuilder(_options->getMaterialLib(), _bucket.get_center());
|
simgear::AirportSignBuilder signBuilder(_options->getMaterialLib(), _bucket.get_center());
|
||||||
@@ -586,10 +587,12 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
{
|
{
|
||||||
osg::ref_ptr<SGReaderWriterOptions> options;
|
osg::ref_ptr<SGReaderWriterOptions> options;
|
||||||
options = SGReaderWriterOptions::copyOrCreate(opt);
|
options = SGReaderWriterOptions::copyOrCreate(opt);
|
||||||
|
float pagedLODExpiry = atoi(options->getPluginStringData("SimGear::PAGED_LOD_EXPIRY").c_str());
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> terrainGroup = new osg::Group;
|
osg::ref_ptr<osg::Group> terrainGroup = new osg::Group;
|
||||||
terrainGroup->setDataVariance(osg::Object::STATIC);
|
terrainGroup->setDataVariance(osg::Object::STATIC);
|
||||||
terrainGroup->setName("terrain");
|
std::string terrain_name = string("terrain ").append(bucket.gen_index_str());
|
||||||
|
terrainGroup->setName(terrain_name);
|
||||||
|
|
||||||
if (_foundBase) {
|
if (_foundBase) {
|
||||||
for (auto stgObject : _objectList) {
|
for (auto stgObject : _objectList) {
|
||||||
@@ -637,11 +640,13 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
} else {
|
} else {
|
||||||
osg::PagedLOD* pagedLOD = new osg::PagedLOD;
|
osg::PagedLOD* pagedLOD = new osg::PagedLOD;
|
||||||
pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
|
pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
|
||||||
pagedLOD->setName("pagedObjectLOD");
|
std::string name = string("pagedObjectLOD ").append(bucket.gen_index_str());
|
||||||
|
pagedLOD->setName(name);
|
||||||
|
|
||||||
// This should be visible in any case.
|
// This should be visible in any case.
|
||||||
// If this is replaced by some lower level of detail, the parent LOD node handles this.
|
// If this is replaced by some lower level of detail, the parent LOD node handles this.
|
||||||
pagedLOD->addChild(terrainGroup, 0, std::numeric_limits<float>::max());
|
pagedLOD->addChild(terrainGroup, 0, std::numeric_limits<float>::max());
|
||||||
|
pagedLOD->setMinimumExpiryTime(0, pagedLODExpiry);
|
||||||
|
|
||||||
// we just need to know about the read file callback that itself holds the data
|
// we just need to know about the read file callback that itself holds the data
|
||||||
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
|
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
|
||||||
@@ -658,10 +663,11 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
|
|
||||||
// Objects may end up displayed up to 2x the object range.
|
// Objects may end up displayed up to 2x the object range.
|
||||||
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough);
|
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough);
|
||||||
|
pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), pagedLODExpiry);
|
||||||
pagedLOD->setRadius(SG_TILE_RADIUS);
|
pagedLOD->setRadius(SG_TILE_RADIUS);
|
||||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() );
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() );
|
||||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Range: " << (2.0 * _object_range_rough));
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Range: " << (2.0 * _object_range_rough));
|
||||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Radius: " << SG_TILE_RADIUS);
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Radius: " << SG_TILE_RADIUS);
|
||||||
return pagedLOD;
|
return pagedLOD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -441,6 +441,10 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
|
|||||||
if (hash_pos != std::string::npos)
|
if (hash_pos != std::string::npos)
|
||||||
line.resize(hash_pos);
|
line.resize(hash_pos);
|
||||||
|
|
||||||
|
if (line.empty()) {
|
||||||
|
continue; // skip blank lines
|
||||||
|
}
|
||||||
|
|
||||||
// and process further
|
// and process further
|
||||||
std::stringstream in(line);
|
std::stringstream in(line);
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include <simgear/scene/material/EffectGeode.hxx>
|
#include <simgear/scene/material/EffectGeode.hxx>
|
||||||
#include <simgear/scene/material/mat.hxx>
|
#include <simgear/scene/material/mat.hxx>
|
||||||
#include <simgear/scene/material/matlib.hxx>
|
#include <simgear/scene/material/matlib.hxx>
|
||||||
|
#include <simgear/scene/model/BoundingVolumeBuildVisitor.hxx>
|
||||||
#include <simgear/scene/util/OsgMath.hxx>
|
#include <simgear/scene/util/OsgMath.hxx>
|
||||||
#include <simgear/scene/util/VectorArrayAdapter.hxx>
|
#include <simgear/scene/util/VectorArrayAdapter.hxx>
|
||||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||||
@@ -340,5 +341,10 @@ osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib, int latPoints,
|
|||||||
transform->addChild(geode);
|
transform->addChild(geode);
|
||||||
transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||||
|
|
||||||
|
// Create a BVH at this point. This is normally provided by the file loader, but as we create the
|
||||||
|
// geometry programmatically, no file loader is involved.
|
||||||
|
BoundingVolumeBuildVisitor bvhBuilder(false);
|
||||||
|
transform->accept(bvhBuilder);
|
||||||
|
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,13 +109,13 @@ bool hasWhitespace(string path)
|
|||||||
class SyncItem
|
class SyncItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Type
|
enum Type {
|
||||||
{
|
Stop = 0, ///< special item indicating to stop the SVNThread
|
||||||
Stop = 0, ///< special item indicating to stop the SVNThread
|
|
||||||
Tile,
|
Tile,
|
||||||
AirportData,
|
AirportData,
|
||||||
SharedModels,
|
SharedModels,
|
||||||
AIData
|
AIData,
|
||||||
|
OSMTile ///< OSm2City per-Tile data
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Status
|
enum Status
|
||||||
@@ -165,13 +165,16 @@ public:
|
|||||||
SGTimeStamp stamp;
|
SGTimeStamp stamp;
|
||||||
bool busy = false; ///< is the slot working or idle
|
bool busy = false; ///< is the slot working or idle
|
||||||
unsigned int pendingKBytes = 0;
|
unsigned int pendingKBytes = 0;
|
||||||
|
unsigned int pendingExtractKBytes = 0;
|
||||||
unsigned int nextWarnTimeout = 0;
|
unsigned int nextWarnTimeout = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
|
static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
|
||||||
static const int SYNC_SLOT_SHARED_DATA = 1; /// shared Models and Airport data
|
static const int SYNC_SLOT_SHARED_DATA = 1; /// shared Models and Airport data
|
||||||
static const int SYNC_SLOT_AI_DATA = 2; /// AI traffic and models
|
static const int SYNC_SLOT_AI_DATA = 2; /// AI traffic and models
|
||||||
static const unsigned int NUM_SYNC_SLOTS = 3;
|
static const int SYNC_SLOT_OSM_TILE_DATA = 3;
|
||||||
|
|
||||||
|
static const unsigned int NUM_SYNC_SLOTS = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief translate a sync item type into one of the available slots.
|
* @brief translate a sync item type into one of the available slots.
|
||||||
@@ -186,7 +189,8 @@ static unsigned int syncSlotForType(SyncItem::Type ty)
|
|||||||
return SYNC_SLOT_SHARED_DATA;
|
return SYNC_SLOT_SHARED_DATA;
|
||||||
case SyncItem::AIData:
|
case SyncItem::AIData:
|
||||||
return SYNC_SLOT_AI_DATA;
|
return SYNC_SLOT_AI_DATA;
|
||||||
|
case SyncItem::OSMTile:
|
||||||
|
return SYNC_SLOT_OSM_TILE_DATA;
|
||||||
default:
|
default:
|
||||||
return SYNC_SLOT_SHARED_DATA;
|
return SYNC_SLOT_SHARED_DATA;
|
||||||
}
|
}
|
||||||
@@ -194,18 +198,18 @@ static unsigned int syncSlotForType(SyncItem::Type ty)
|
|||||||
|
|
||||||
struct TerrasyncThreadState
|
struct TerrasyncThreadState
|
||||||
{
|
{
|
||||||
TerrasyncThreadState() :
|
TerrasyncThreadState() : _busy(false),
|
||||||
_busy(false),
|
_stalled(false),
|
||||||
_stalled(false),
|
_hasServer(false),
|
||||||
_hasServer(false),
|
_fail_count(0),
|
||||||
_fail_count(0),
|
_updated_tile_count(0),
|
||||||
_updated_tile_count(0),
|
_success_count(0),
|
||||||
_success_count(0),
|
_consecutive_errors(0),
|
||||||
_consecutive_errors(0),
|
_cache_hits(0),
|
||||||
_cache_hits(0),
|
_transfer_rate(0),
|
||||||
_transfer_rate(0),
|
_total_kb_downloaded(0),
|
||||||
_total_kb_downloaded(0),
|
_totalKbPending(0),
|
||||||
_totalKbPending(0)
|
_extractTotalKbPending(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool _busy;
|
bool _busy;
|
||||||
@@ -220,6 +224,7 @@ struct TerrasyncThreadState
|
|||||||
// kbytes, not bytes, because bytes might overflow 2^31
|
// kbytes, not bytes, because bytes might overflow 2^31
|
||||||
int _total_kb_downloaded;
|
int _total_kb_downloaded;
|
||||||
unsigned int _totalKbPending;
|
unsigned int _totalKbPending;
|
||||||
|
unsigned int _extractTotalKbPending;
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -275,9 +280,10 @@ public:
|
|||||||
|
|
||||||
SyncItem getNewTile() { return _freshTiles.pop_front();}
|
SyncItem getNewTile() { return _freshTiles.pop_front();}
|
||||||
|
|
||||||
void setHTTPServer(const std::string& server)
|
void setHTTPServer(const std::string& server, const std::string& osmServer)
|
||||||
{
|
{
|
||||||
_httpServer = stripPath(server);
|
_httpServer = stripPath(server);
|
||||||
|
_osmCityServer = stripPath(osmServer);
|
||||||
_isAutomaticServer = (server == "automatic");
|
_isAutomaticServer = (server == "automatic");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +302,11 @@ public:
|
|||||||
_sceneryVersion = simgear::strutils::strip(sceneryVersion);
|
_sceneryVersion = simgear::strutils::strip(sceneryVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setOSMCityVersion(const std::string& osmCityVersion)
|
||||||
|
{
|
||||||
|
_osmCityService = osmCityVersion;
|
||||||
|
}
|
||||||
|
|
||||||
void setLocalDir(string dir) { _local_dir = stripPath(dir);}
|
void setLocalDir(string dir) { _local_dir = stripPath(dir);}
|
||||||
string getLocalDir() { return _local_dir;}
|
string getLocalDir() { return _local_dir;}
|
||||||
|
|
||||||
@@ -322,10 +333,12 @@ public:
|
|||||||
void setCachePath(const SGPath &p) { _persistentCachePath = p; }
|
void setCachePath(const SGPath &p) { _persistentCachePath = p; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void incrementCacheHits()
|
std::string dnsSelectServerForService(const std::string& service);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> g(_stateLock);
|
void incrementCacheHits()
|
||||||
_state._cache_hits++;
|
{
|
||||||
|
std::lock_guard<std::mutex> g(_stateLock);
|
||||||
|
_state._cache_hits++;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
@@ -334,9 +347,9 @@ public:
|
|||||||
void runInternal();
|
void runInternal();
|
||||||
void updateSyncSlot(SyncSlot& slot);
|
void updateSyncSlot(SyncSlot& slot);
|
||||||
|
|
||||||
void beginSyncAirports(SyncSlot& slot);
|
bool beginSyncAirports(SyncSlot& slot);
|
||||||
void beginSyncTile(SyncSlot& slot);
|
bool beginSyncTile(SyncSlot& slot);
|
||||||
void beginNormalSync(SyncSlot& slot);
|
bool beginNormalSync(SyncSlot& slot);
|
||||||
|
|
||||||
void drainWaitingTiles();
|
void drainWaitingTiles();
|
||||||
|
|
||||||
@@ -363,6 +376,9 @@ public:
|
|||||||
string _local_dir;
|
string _local_dir;
|
||||||
SGPath _persistentCachePath;
|
SGPath _persistentCachePath;
|
||||||
string _httpServer;
|
string _httpServer;
|
||||||
|
string _osmCityServer;
|
||||||
|
string _osmCityService = "o2c";
|
||||||
|
|
||||||
bool _isAutomaticServer;
|
bool _isAutomaticServer;
|
||||||
SGPath _installRoot;
|
SGPath _installRoot;
|
||||||
string _sceneryVersion;
|
string _sceneryVersion;
|
||||||
@@ -474,8 +490,19 @@ bool SGTerraSync::WorkerThread::findServer()
|
|||||||
{
|
{
|
||||||
if ( false == _isAutomaticServer ) return true;
|
if ( false == _isAutomaticServer ) return true;
|
||||||
|
|
||||||
|
_httpServer = dnsSelectServerForService(MakeQService(_protocol, _sceneryVersion));
|
||||||
|
|
||||||
|
if (!_osmCityService.empty()) {
|
||||||
|
_osmCityServer = dnsSelectServerForService(_osmCityService);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !_httpServer.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SGTerraSync::WorkerThread::dnsSelectServerForService(const std::string& service)
|
||||||
|
{
|
||||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(_dnsdn);
|
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(_dnsdn);
|
||||||
naptrRequest->qservice = MakeQService(_protocol, _sceneryVersion);
|
naptrRequest->qservice = service;
|
||||||
|
|
||||||
naptrRequest->qflags = "U";
|
naptrRequest->qflags = "U";
|
||||||
DNS::Request_ptr r(naptrRequest);
|
DNS::Request_ptr r(naptrRequest);
|
||||||
@@ -483,14 +510,15 @@ bool SGTerraSync::WorkerThread::findServer()
|
|||||||
DNS::Client dnsClient;
|
DNS::Client dnsClient;
|
||||||
dnsClient.makeRequest(r);
|
dnsClient.makeRequest(r);
|
||||||
SG_LOG(SG_TERRASYNC,SG_DEBUG,"DNS NAPTR query for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" );
|
SG_LOG(SG_TERRASYNC,SG_DEBUG,"DNS NAPTR query for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" );
|
||||||
while( !r->isComplete() && !r->isTimeout() )
|
while (!r->isComplete() && !r->isTimeout()) {
|
||||||
dnsClient.update(0);
|
dnsClient.update(0);
|
||||||
|
}
|
||||||
|
|
||||||
if( naptrRequest->entries.empty() ) {
|
if( naptrRequest->entries.empty() ) {
|
||||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Warning: no DNS entry found for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" );
|
SG_LOG(SG_TERRASYNC, SG_ALERT, "Warning: no DNS entry found for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" );
|
||||||
_httpServer = "";
|
return {};
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk through responses, they are ordered by 1. order and 2. preference
|
// walk through responses, they are ordered by 1. order and 2. preference
|
||||||
// For now, only take entries with lowest order
|
// For now, only take entries with lowest order
|
||||||
// TODO: try all available servers in the order given by preferenc and order
|
// TODO: try all available servers in the order given by preferenc and order
|
||||||
@@ -498,38 +526,33 @@ bool SGTerraSync::WorkerThread::findServer()
|
|||||||
|
|
||||||
// get all servers with this order and the same (for now only lowest preference)
|
// get all servers with this order and the same (for now only lowest preference)
|
||||||
DNS::NAPTRRequest::NAPTR_list availableServers;
|
DNS::NAPTRRequest::NAPTR_list availableServers;
|
||||||
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin();
|
for (const auto& entry : naptrRequest->entries) {
|
||||||
it != naptrRequest->entries.end();
|
if (entry->order != order)
|
||||||
++it ) {
|
|
||||||
|
|
||||||
if( (*it)->order != order )
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
string regex = (*it)->regexp;
|
const string regex = entry->regexp;
|
||||||
if( false == simgear::strutils::starts_with( (*it)->regexp, "!^.*$!" ) ) {
|
if (false == simgear::strutils::starts_with(regex, "!^.*$!")) {
|
||||||
SG_LOG(SG_TERRASYNC,SG_WARN, "ignoring unsupported regexp: " << (*it)->regexp );
|
SG_LOG(SG_TERRASYNC, SG_WARN, "ignoring unsupported regexp: " << regex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( false == simgear::strutils::ends_with( (*it)->regexp, "!" ) ) {
|
if (false == simgear::strutils::ends_with(regex, "!")) {
|
||||||
SG_LOG(SG_TERRASYNC,SG_WARN, "ignoring unsupported regexp: " << (*it)->regexp );
|
SG_LOG(SG_TERRASYNC, SG_WARN, "ignoring unsupported regexp: " << regex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// always use first entry
|
// always use first entry
|
||||||
if( availableServers.empty() || (*it)->preference == availableServers[0]->preference) {
|
if (availableServers.empty() || entry->preference == availableServers[0]->preference) {
|
||||||
SG_LOG(SG_TERRASYNC,SG_DEBUG, "available server regexp: " << (*it)->regexp );
|
SG_LOG(SG_TERRASYNC, SG_DEBUG, "available server regexp: " << regex);
|
||||||
availableServers.push_back( *it );
|
availableServers.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now pick a random entry from the available servers
|
// now pick a random entry from the available servers
|
||||||
DNS::NAPTRRequest::NAPTR_list::size_type idx = sg_random() * availableServers.size();
|
auto idx = static_cast<int>(sg_random() * availableServers.size());
|
||||||
_httpServer = availableServers[idx]->regexp;
|
const auto server = availableServers.at(idx)->regexp;
|
||||||
_httpServer = _httpServer.substr( 6, _httpServer.length()-7 ); // strip search pattern and separators
|
SG_LOG(SG_TERRASYNC, SG_INFO, "picking entry # " << idx << ", server is " << server.substr(6, server.length() - 7););
|
||||||
|
return server.substr(6, server.length() - 7);
|
||||||
SG_LOG(SG_TERRASYNC,SG_INFO, "picking entry # " << idx << ", server is " << _httpServer );
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SGTerraSync::WorkerThread::run()
|
void SGTerraSync::WorkerThread::run()
|
||||||
@@ -562,6 +585,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
|
|||||||
#endif
|
#endif
|
||||||
// convert bytes to kbytes here
|
// convert bytes to kbytes here
|
||||||
slot.pendingKBytes = (slot.repository->bytesToDownload() >> 10);
|
slot.pendingKBytes = (slot.repository->bytesToDownload() >> 10);
|
||||||
|
slot.pendingExtractKBytes = (slot.repository->bytesToExtract() >> 10);
|
||||||
return; // easy, still working
|
return; // easy, still working
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,9 +593,21 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
|
|||||||
HTTPRepository::ResultCode res = slot.repository->failure();
|
HTTPRepository::ResultCode res = slot.repository->failure();
|
||||||
|
|
||||||
if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) {
|
if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) {
|
||||||
|
// not founds should never happen any more (unless the server-
|
||||||
|
// side data is incorrect), since we now check top-down that
|
||||||
|
// a 1x1 dir exists or not.
|
||||||
notFound(slot.currentItem);
|
notFound(slot.currentItem);
|
||||||
} else if (res != HTTPRepository::REPO_NO_ERROR) {
|
} else if (res != HTTPRepository::REPO_NO_ERROR) {
|
||||||
fail(slot.currentItem);
|
fail(slot.currentItem);
|
||||||
|
|
||||||
|
// in case the Airports_archive download fails, create the
|
||||||
|
// directory, so that next sync, we do a manual sync
|
||||||
|
if ((slot.currentItem._type == SyncItem::AirportData) && slot.isNewDirectory) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_ALERT, "Failed to download Airports_archive, will download discrete files next time");
|
||||||
|
simgear::Dir d(_local_dir + "/Airports");
|
||||||
|
d.create(0755);
|
||||||
|
_completedTiles.erase(slot.currentItem._dir);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
updated(slot.currentItem, slot.isNewDirectory);
|
updated(slot.currentItem, slot.isNewDirectory);
|
||||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "sync of " << slot.repository->baseUrl() << " finished ("
|
SG_LOG(SG_TERRASYNC, SG_DEBUG, "sync of " << slot.repository->baseUrl() << " finished ("
|
||||||
@@ -582,6 +618,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
|
|||||||
slot.busy = false;
|
slot.busy = false;
|
||||||
slot.repository.reset();
|
slot.repository.reset();
|
||||||
slot.pendingKBytes = 0;
|
slot.pendingKBytes = 0;
|
||||||
|
slot.pendingExtractKBytes = 0;
|
||||||
slot.currentItem = {};
|
slot.currentItem = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,12 +632,23 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
|
|||||||
slot.isNewDirectory = !path.exists();
|
slot.isNewDirectory = !path.exists();
|
||||||
const auto type = slot.currentItem._type;
|
const auto type = slot.currentItem._type;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
if (type == SyncItem::AirportData) {
|
if (type == SyncItem::AirportData) {
|
||||||
beginSyncAirports(slot);
|
ok = beginSyncAirports(slot);
|
||||||
|
} else if (type == SyncItem::OSMTile) {
|
||||||
|
ok = beginSyncTile(slot);
|
||||||
} else if (type == SyncItem::Tile) {
|
} else if (type == SyncItem::Tile) {
|
||||||
beginSyncTile(slot);
|
ok = beginSyncTile(slot);
|
||||||
} else {
|
} else {
|
||||||
beginNormalSync(slot);
|
ok = beginNormalSync(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_INFO, "sync of " << slot.currentItem._dir << " failed to start");
|
||||||
|
fail(slot.currentItem);
|
||||||
|
slot.busy = false;
|
||||||
|
slot.repository.reset();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -617,17 +665,17 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
|
|||||||
slot.nextWarnTimeout = 30 * 1000;
|
slot.nextWarnTimeout = 30 * 1000;
|
||||||
slot.stamp.stamp();
|
slot.stamp.stamp();
|
||||||
slot.busy = true;
|
slot.busy = true;
|
||||||
slot.pendingKBytes = slot.repository->bytesToDownload();
|
slot.pendingKBytes = slot.repository->bytesToDownload() >> 10;
|
||||||
|
slot.pendingExtractKBytes = slot.repository->bytesToExtract() >> 10;
|
||||||
|
|
||||||
SG_LOG(SG_TERRASYNC, SG_INFO, "sync of " << slot.repository->baseUrl() << " started, queue size is " << slot.queue.size());
|
SG_LOG(SG_TERRASYNC, SG_INFO, "sync of " << slot.repository->baseUrl() << ":" << slot.currentItem._dir << " started, queue size is " << slot.queue.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SGTerraSync::WorkerThread::beginSyncAirports(SyncSlot& slot)
|
bool SGTerraSync::WorkerThread::beginSyncAirports(SyncSlot& slot)
|
||||||
{
|
{
|
||||||
if (!slot.isNewDirectory) {
|
if (!slot.isNewDirectory) {
|
||||||
beginNormalSync(slot);
|
return beginNormalSync(slot);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SG_LOG(SG_TERRASYNC, SG_INFO, "doing Airports download via tarball");
|
SG_LOG(SG_TERRASYNC, SG_INFO, "doing Airports download via tarball");
|
||||||
@@ -647,9 +695,10 @@ void SGTerraSync::WorkerThread::beginSyncAirports(SyncSlot& slot)
|
|||||||
};
|
};
|
||||||
|
|
||||||
slot.repository->setFilter(f);
|
slot.repository->setFilter(f);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
bool SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
||||||
{
|
{
|
||||||
// avoid 404 requests by doing a sync which excludes all paths
|
// avoid 404 requests by doing a sync which excludes all paths
|
||||||
// except our tile path. In the case of a missing 1x1 tile, we will
|
// except our tile path. In the case of a missing 1x1 tile, we will
|
||||||
@@ -658,8 +707,7 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
|||||||
auto comps = strutils::split(slot.currentItem._dir, "/");
|
auto comps = strutils::split(slot.currentItem._dir, "/");
|
||||||
if (comps.size() != 3) {
|
if (comps.size() != 3) {
|
||||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Bad tile path:" << slot.currentItem._dir);
|
SG_LOG(SG_TERRASYNC, SG_ALERT, "Bad tile path:" << slot.currentItem._dir);
|
||||||
beginNormalSync(slot);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto tileCategory = comps.front();
|
const auto tileCategory = comps.front();
|
||||||
@@ -668,7 +716,17 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
|||||||
|
|
||||||
const auto path = SGPath::fromUtf8(_local_dir) / tileCategory;
|
const auto path = SGPath::fromUtf8(_local_dir) / tileCategory;
|
||||||
slot.repository.reset(new HTTPRepository(path, &_http));
|
slot.repository.reset(new HTTPRepository(path, &_http));
|
||||||
slot.repository->setBaseUrl(_httpServer + "/" + tileCategory);
|
|
||||||
|
if (slot.currentItem._type == SyncItem::OSMTile) {
|
||||||
|
if (_osmCityServer.empty()) {
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_WARN, "No OSM2City server defined for:" << slot.currentItem._dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot.repository->setBaseUrl(_osmCityServer + "/" + tileCategory);
|
||||||
|
} else {
|
||||||
|
slot.repository->setBaseUrl(_httpServer + "/" + tileCategory);
|
||||||
|
}
|
||||||
|
|
||||||
if (_installRoot.exists()) {
|
if (_installRoot.exists()) {
|
||||||
SGPath p = _installRoot / tileCategory;
|
SGPath p = _installRoot / tileCategory;
|
||||||
@@ -676,6 +734,7 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto dirPrefix = tenByTenDir + "/" + oneByOneDir;
|
const auto dirPrefix = tenByTenDir + "/" + oneByOneDir;
|
||||||
|
using simgear::strutils::starts_with;
|
||||||
|
|
||||||
// filter callback to *only* sync the 1x1 dir we want, if it exists
|
// filter callback to *only* sync the 1x1 dir we want, if it exists
|
||||||
// if doesn't, we'll simply stop, which is what we want
|
// if doesn't, we'll simply stop, which is what we want
|
||||||
@@ -684,7 +743,10 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
|||||||
if (item.directory.empty()) {
|
if (item.directory.empty()) {
|
||||||
return item.filename == tenByTenDir;
|
return item.filename == tenByTenDir;
|
||||||
} else if (item.directory == tenByTenDir) {
|
} else if (item.directory == tenByTenDir) {
|
||||||
return item.filename == oneByOneDir;
|
// allow 10x10 dir to contain 1x1_dir.tgz/.zip and still be accepted
|
||||||
|
// this does mean we'd also download oneByOneDir_foobar but that
|
||||||
|
// doesn't seem unreasonable either
|
||||||
|
return starts_with(item.filename, oneByOneDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow arbitrary children below dirPrefix, including sub-dirs
|
// allow arbitrary children below dirPrefix, including sub-dirs
|
||||||
@@ -697,9 +759,10 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
|
|||||||
};
|
};
|
||||||
|
|
||||||
slot.repository->setFilter(f);
|
slot.repository->setFilter(f);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SGTerraSync::WorkerThread::beginNormalSync(SyncSlot& slot)
|
bool SGTerraSync::WorkerThread::beginNormalSync(SyncSlot& slot)
|
||||||
{
|
{
|
||||||
SGPath path(_local_dir);
|
SGPath path(_local_dir);
|
||||||
path.append(slot.currentItem._dir);
|
path.append(slot.currentItem._dir);
|
||||||
@@ -711,6 +774,8 @@ void SGTerraSync::WorkerThread::beginNormalSync(SyncSlot& slot)
|
|||||||
p.append(slot.currentItem._dir);
|
p.append(slot.currentItem._dir);
|
||||||
slot.repository->setInstalledCopyPath(p);
|
slot.repository->setInstalledCopyPath(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SGTerraSync::WorkerThread::runInternal()
|
void SGTerraSync::WorkerThread::runInternal()
|
||||||
@@ -753,20 +818,24 @@ void SGTerraSync::WorkerThread::runInternal()
|
|||||||
|
|
||||||
bool anySlotBusy = false;
|
bool anySlotBusy = false;
|
||||||
unsigned int newPendingCount = 0;
|
unsigned int newPendingCount = 0;
|
||||||
|
unsigned int newExtractCount = 0; // how much is left to extract
|
||||||
|
|
||||||
// update each sync slot in turn
|
// update each sync slot in turn
|
||||||
for (unsigned int slot=0; slot < NUM_SYNC_SLOTS; ++slot) {
|
for (unsigned int slot=0; slot < NUM_SYNC_SLOTS; ++slot) {
|
||||||
updateSyncSlot(_syncSlots[slot]);
|
updateSyncSlot(_syncSlots[slot]);
|
||||||
newPendingCount += _syncSlots[slot].pendingKBytes;
|
newPendingCount += _syncSlots[slot].pendingKBytes;
|
||||||
|
newExtractCount += _syncSlots[slot].pendingExtractKBytes;
|
||||||
anySlotBusy |= _syncSlots[slot].busy;
|
anySlotBusy |= _syncSlots[slot].busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> g(_stateLock);
|
std::lock_guard<std::mutex> g(_stateLock);
|
||||||
_state._totalKbPending = newPendingCount; // approximately atomic update
|
_state._totalKbPending = newPendingCount; // approximately atomic update
|
||||||
|
_state._extractTotalKbPending = newExtractCount;
|
||||||
_state._busy = anySlotBusy;
|
_state._busy = anySlotBusy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!anySlotBusy) {
|
if (!anySlotBusy) {
|
||||||
// wait on the blocking deque here, otherwise we spin
|
// wait on the blocking deque here, otherwise we spin
|
||||||
// the loop very fast, since _http::update with no connections
|
// the loop very fast, since _http::update with no connections
|
||||||
@@ -802,12 +871,19 @@ void SGTerraSync::WorkerThread::fail(SyncItem failedItem)
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> g(_stateLock);
|
std::lock_guard<std::mutex> g(_stateLock);
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
_state._consecutive_errors++;
|
|
||||||
_state._fail_count++;
|
if (_osmCityServer.empty() && (failedItem._type == SyncItem::OSMTile)) {
|
||||||
|
// don't count these as errors, otherwise normla sync will keep
|
||||||
|
// being abandoned
|
||||||
|
} else {
|
||||||
|
_state._consecutive_errors++;
|
||||||
|
_state._fail_count++;
|
||||||
|
}
|
||||||
|
|
||||||
failedItem._status = SyncItem::Failed;
|
failedItem._status = SyncItem::Failed;
|
||||||
_freshTiles.push_back(failedItem);
|
_freshTiles.push_back(failedItem);
|
||||||
// not we also end up here for partial syncs
|
// not we also end up here for partial syncs
|
||||||
SG_LOG(SG_TERRASYNC,SG_INFO,
|
SG_LOG(SG_TERRASYNC, SG_WARN,
|
||||||
"Failed to sync'" << failedItem._dir << "'");
|
"Failed to sync'" << failedItem._dir << "'");
|
||||||
_completedTiles[ failedItem._dir ] = now + UpdateInterval::FailedAttempt;
|
_completedTiles[ failedItem._dir ] = now + UpdateInterval::FailedAttempt;
|
||||||
}
|
}
|
||||||
@@ -819,6 +895,8 @@ void SGTerraSync::WorkerThread::notFound(SyncItem item)
|
|||||||
// we don't spam the server with lookups for models that don't
|
// we don't spam the server with lookups for models that don't
|
||||||
// exist
|
// exist
|
||||||
|
|
||||||
|
SG_LOG(SG_TERRASYNC, SG_WARN, "Not found for: '" << item._dir << "'");
|
||||||
|
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
item._status = SyncItem::NotFound;
|
item._status = SyncItem::NotFound;
|
||||||
_freshTiles.push_back(item);
|
_freshTiles.push_back(item);
|
||||||
@@ -1031,8 +1109,11 @@ void SGTerraSync::reinit()
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
_availableNode->setBoolValue(true);
|
_availableNode->setBoolValue(true);
|
||||||
_workerThread->setHTTPServer( _terraRoot->getStringValue("http-server","automatic") );
|
_workerThread->setHTTPServer(
|
||||||
|
_terraRoot->getStringValue("http-server", "automatic"),
|
||||||
|
_terraRoot->getStringValue("osm2city-server", ""));
|
||||||
_workerThread->setSceneryVersion( _terraRoot->getStringValue("scenery-version","ws20") );
|
_workerThread->setSceneryVersion( _terraRoot->getStringValue("scenery-version","ws20") );
|
||||||
|
_workerThread->setOSMCityVersion(_terraRoot->getStringValue("osm2city-version", "o2c"));
|
||||||
_workerThread->setProtocol( _terraRoot->getStringValue("protocol","") );
|
_workerThread->setProtocol( _terraRoot->getStringValue("protocol","") );
|
||||||
#if 1
|
#if 1
|
||||||
// leave it hardcoded for now, not sure about the security implications for now
|
// leave it hardcoded for now, not sure about the security implications for now
|
||||||
@@ -1091,6 +1172,7 @@ void SGTerraSync::bind()
|
|||||||
_transferRateBytesSecNode = _terraRoot->getNode("transfer-rate-bytes-sec", true);
|
_transferRateBytesSecNode = _terraRoot->getNode("transfer-rate-bytes-sec", true);
|
||||||
_pendingKbytesNode = _terraRoot->getNode("pending-kbytes", true);
|
_pendingKbytesNode = _terraRoot->getNode("pending-kbytes", true);
|
||||||
_downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true);
|
_downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true);
|
||||||
|
_extractPendingKbytesNode = _terraRoot->getNode("extract-pending-kbytes", true);
|
||||||
_enabledNode = _terraRoot->getNode("enabled", true);
|
_enabledNode = _terraRoot->getNode("enabled", true);
|
||||||
_availableNode = _terraRoot->getNode("available", true);
|
_availableNode = _terraRoot->getNode("available", true);
|
||||||
_maxErrorsNode = _terraRoot->getNode("max-errors", true);
|
_maxErrorsNode = _terraRoot->getNode("max-errors", true);
|
||||||
@@ -1116,6 +1198,7 @@ void SGTerraSync::unbind()
|
|||||||
_transferRateBytesSecNode.clear();
|
_transferRateBytesSecNode.clear();
|
||||||
_pendingKbytesNode.clear();
|
_pendingKbytesNode.clear();
|
||||||
_downloadedKBtesNode.clear();
|
_downloadedKBtesNode.clear();
|
||||||
|
_extractPendingKbytesNode.clear();
|
||||||
_enabledNode.clear();
|
_enabledNode.clear();
|
||||||
_availableNode.clear();
|
_availableNode.clear();
|
||||||
_maxErrorsNode.clear();
|
_maxErrorsNode.clear();
|
||||||
@@ -1152,6 +1235,7 @@ void SGTerraSync::update(double)
|
|||||||
_transferRateBytesSecNode->setIntValue(copiedState._transfer_rate);
|
_transferRateBytesSecNode->setIntValue(copiedState._transfer_rate);
|
||||||
_pendingKbytesNode->setIntValue(copiedState._totalKbPending);
|
_pendingKbytesNode->setIntValue(copiedState._totalKbPending);
|
||||||
_downloadedKBtesNode->setIntValue(copiedState._total_kb_downloaded);
|
_downloadedKBtesNode->setIntValue(copiedState._total_kb_downloaded);
|
||||||
|
_extractPendingKbytesNode->setIntValue(copiedState._extractTotalKbPending);
|
||||||
|
|
||||||
_stalledNode->setBoolValue(_workerThread->isStalled());
|
_stalledNode->setBoolValue(_workerThread->isStalled());
|
||||||
_activeNode->setBoolValue(worker_running);
|
_activeNode->setBoolValue(worker_running);
|
||||||
@@ -1201,6 +1285,10 @@ string_list SGTerraSync::getSceneryPathSuffixes() const
|
|||||||
return scenerySuffixes;
|
return scenerySuffixes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isOSMSuffix(const std::string& suffix)
|
||||||
|
{
|
||||||
|
return (suffix == "Buildings") || (suffix == "Roads") || (suffix == "Pylons") || (suffix == "Details");
|
||||||
|
}
|
||||||
|
|
||||||
void SGTerraSync::syncAreaByPath(const std::string& aPath)
|
void SGTerraSync::syncAreaByPath(const std::string& aPath)
|
||||||
{
|
{
|
||||||
@@ -1213,8 +1301,7 @@ void SGTerraSync::syncAreaByPath(const std::string& aPath)
|
|||||||
if (_workerThread->isDirActive(dir)) {
|
if (_workerThread->isDirActive(dir)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
SyncItem w(dir, isOSMSuffix(suffix) ? SyncItem::OSMTile : SyncItem::Tile);
|
||||||
SyncItem w(dir, SyncItem::Tile);
|
|
||||||
_workerThread->request( w );
|
_workerThread->request( w );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1233,6 +1320,11 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& suffix : getSceneryPathSuffixes()) {
|
for (const auto& suffix : getSceneryPathSuffixes()) {
|
||||||
|
// don't wait on OSM dirs, even if enabled
|
||||||
|
if (isOSMSuffix(suffix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const auto s = suffix + "/" + sceneryDir;
|
const auto s = suffix + "/" + sceneryDir;
|
||||||
if (_workerThread->isDirActive(s)) {
|
if (_workerThread->isDirActive(s)) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ private:
|
|||||||
SGPropertyNode_ptr _transferRateBytesSecNode;
|
SGPropertyNode_ptr _transferRateBytesSecNode;
|
||||||
SGPropertyNode_ptr _pendingKbytesNode;
|
SGPropertyNode_ptr _pendingKbytesNode;
|
||||||
SGPropertyNode_ptr _downloadedKBtesNode;
|
SGPropertyNode_ptr _downloadedKBtesNode;
|
||||||
|
SGPropertyNode_ptr _extractPendingKbytesNode;
|
||||||
SGPropertyNode_ptr _maxErrorsNode;
|
SGPropertyNode_ptr _maxErrorsNode;
|
||||||
|
|
||||||
// we manually bind+init TerraSync during early startup
|
// we manually bind+init TerraSync during early startup
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
ORIGIN_EFFECTS,
|
ORIGIN_EFFECTS,
|
||||||
ORIGIN_EFFECTS_NORMALIZED,
|
ORIGIN_EFFECTS_NORMALIZED,
|
||||||
ORIGIN_SPLASH_SCREEN,
|
ORIGIN_SPLASH_SCREEN,
|
||||||
|
ORIGIN_CANVAS,
|
||||||
};
|
};
|
||||||
|
|
||||||
//SGReaderWriterOptions* cloneOptions(const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) const { return static_cast<SGReaderWriterOptions*>(clone(copyop)); }
|
//SGReaderWriterOptions* cloneOptions(const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) const { return static_cast<SGReaderWriterOptions*>(clone(copyop)); }
|
||||||
|
|||||||
@@ -441,7 +441,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
|
|||||||
std::string nm = stateDesc->getStringValue("name");
|
std::string nm = stateDesc->getStringValue("name");
|
||||||
|
|
||||||
if (nm.empty()) {
|
if (nm.empty()) {
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "No name found for state in branch " << path);
|
SG_LOG(SG_GENERAL, SG_DEV_ALERT, "No name found for state in branch " << path);
|
||||||
throw sg_exception("No name element in state");
|
throw sg_exception("No name element in state");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,8 +464,8 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
|
|||||||
std::string target_id = tDesc->getStringValue("target");
|
std::string target_id = tDesc->getStringValue("target");
|
||||||
|
|
||||||
if (nm.empty()) {
|
if (nm.empty()) {
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "No name found for transition in branch " << path);
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "No name found for transition in branch " << path);
|
||||||
throw sg_exception("No name element in transition");
|
nm = "transition-to-" + target_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target_id.empty()) {
|
if (target_id.empty()) {
|
||||||
|
|||||||
@@ -30,9 +30,10 @@
|
|||||||
#include "subsystem_mgr.hxx"
|
#include "subsystem_mgr.hxx"
|
||||||
#include "commands.hxx"
|
#include "commands.hxx"
|
||||||
|
|
||||||
#include <simgear/props/props.hxx>
|
|
||||||
#include <simgear/math/SGMath.hxx>
|
|
||||||
#include "SGSmplstat.hxx"
|
#include "SGSmplstat.hxx"
|
||||||
|
#include <simgear/debug/ErrorReportingCallback.hxx>
|
||||||
|
#include <simgear/math/SGMath.hxx>
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
|
||||||
const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
|
const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
|
||||||
const char SUBSYSTEM_NAME_SEPARATOR = '.';
|
const char SUBSYSTEM_NAME_SEPARATOR = '.';
|
||||||
@@ -828,8 +829,18 @@ SGSubsystemGroup::Member::update (double delta_time_sec)
|
|||||||
if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
|
if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
|
SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
|
||||||
", suspending)");
|
", suspending)");
|
||||||
|
simgear::reportError("suspending subsystem after too many errors:" + name);
|
||||||
subsystem->suspend();
|
subsystem->suspend();
|
||||||
}
|
}
|
||||||
|
} catch (std::bad_alloc& ba) {
|
||||||
|
// attempting to track down source of these on Sentry.io
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT, "caught bad_alloc processing subsystem:" << name);
|
||||||
|
simgear::reportError("caught bad_alloc processing subsystem:" + name);
|
||||||
|
|
||||||
|
if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount << ", suspending)");
|
||||||
|
subsystem->suspend();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user