1 // fg_init.cxx -- Flight Gear top level initialization routines
2 //
3 // Written by Curtis Olson, started August 1997.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22 
23 
24 #include <config.h>
25 
26 #include <simgear/compiler.h>
27 
28 #include <cstdio>
29 #include <cstdlib>
30 #include <cstring>             // strcmp()
31 
32 #if defined(SG_WINDOWS)
33 #define _WINSOCKAPI_
34 #  include <io.h>               // isatty()
35 #  include <process.h>          // _getpid()
36 #  include <Windows.h>
37 #  define isatty _isatty
38 #else
39 // for open() and options
40 #  include <sys/types.h>
41 #  include <sys/stat.h>
42 #  include <fcntl.h>
43 #  include <sys/file.h>
44 #endif
45 
46 #include <string>
47 
48 #include <osgViewer/Viewer>
49 
50 #include <simgear/canvas/Canvas.hxx>
51 #include <simgear/constants.h>
52 #include <simgear/debug/logstream.hxx>
53 #include <simgear/structure/exception.hxx>
54 #include <simgear/structure/event_mgr.hxx>
55 #include <simgear/structure/SGPerfMon.hxx>
56 #include <simgear/misc/sg_path.hxx>
57 #include <simgear/misc/sg_dir.hxx>
58 #include <simgear/io/iostreams/sgstream.hxx>
59 #include <simgear/misc/strutils.hxx>
60 #include <simgear/embedded_resources/EmbeddedResourceManager.hxx>
61 #include <simgear/props/props_io.hxx>
62 #include <simgear/scene/tsync/terrasync.hxx>
63 
64 #include <simgear/scene/material/Effect.hxx>
65 #include <simgear/scene/material/matlib.hxx>
66 #include <simgear/scene/model/modellib.hxx>
67 #include <simgear/scene/model/particles.hxx>
68 #include <simgear/scene/tgdb/TreeBin.hxx>
69 #include <simgear/scene/tgdb/userdata.hxx>
70 #include <simgear/scene/tsync/terrasync.hxx>
71 
72 #include <simgear/package/Root.hxx>
73 #include <simgear/package/Package.hxx>
74 #include <simgear/package/Install.hxx>
75 #include <simgear/package/Catalog.hxx>
76 
77 #include <Add-ons/AddonManager.hxx>
78 
79 #include <Aircraft/controls.hxx>
80 #include <Aircraft/replay.hxx>
81 #include <Aircraft/FlightHistory.hxx>
82 #include <Aircraft/initialstate.hxx>
83 
84 #include <Airports/runways.hxx>
85 #include <Airports/airport.hxx>
86 #include <Airports/dynamics.hxx>
87 #include <Airports/airportdynamicsmanager.hxx>
88 
89 #include <ATC/atc_mgr.hxx>
90 
91 #include <Autopilot/route_mgr.hxx>
92 #include <Autopilot/autopilotgroup.hxx>
93 
94 #include <Cockpit/panel.hxx>
95 #include <Cockpit/panel_io.hxx>
96 
97 #include <Canvas/canvas_mgr.hxx>
98 #include <Canvas/gui_mgr.hxx>
99 #include <Canvas/FGCanvasSystemAdapter.hxx>
100 #include <GUI/new_gui.hxx>
101 #include <GUI/MessageBox.hxx>
102 #include <Input/input.hxx>
103 #include <Instrumentation/instrument_mgr.hxx>
104 #include <Model/acmodel.hxx>
105 #include <Model/modelmgr.hxx>
106 #include <AIModel/submodel.hxx>
107 #include <AIModel/AIManager.hxx>
108 #include <AIModel/performancedb.hxx>
109 #include <Main/locale.hxx>
110 #include <Navaids/navdb.hxx>
111 #include <Navaids/navlist.hxx>
112 #include <Scenery/scenery.hxx>
113 #include <Scenery/SceneryPager.hxx>
114 #include <Scripting/NasalSys.hxx>
115 #include <Sound/voice.hxx>
116 #include <Sound/soundmanager.hxx>
117 #include <Systems/system_mgr.hxx>
118 #include <Time/tide.hxx>
119 #include <Time/light.hxx>
120 #include <Time/TimeManager.hxx>
121 
122 #include <Traffic/TrafficMgr.hxx>
123 #include <MultiPlayer/multiplaymgr.hxx>
124 #if defined(ENABLE_SWIFT)
125 #include <Network/Swift/swift_connection.hxx>
126 #endif
127 #include <FDM/fdm_shell.hxx>
128 #include <Environment/ephemeris.hxx>
129 #include <Environment/environment_mgr.hxx>
130 #include <Viewer/renderer.hxx>
131 #include <Viewer/viewmgr.hxx>
132 #include <Viewer/FGEventHandler.hxx>
133 #include <Navaids/NavDataCache.hxx>
134 #include <Instrumentation/HUD/HUD.hxx>
135 #include <Cockpit/cockpitDisplayManager.hxx>
136 #include <Network/HTTPClient.hxx>
137 #include <Network/DNSClient.hxx>
138 #include <Network/fgcom.hxx>
139 #include <Network/http/httpd.hxx>
140 #include <Include/version.h>
141 #include <Viewer/splash.hxx>
142 #include <Viewer/CameraGroup.hxx>
143 
144 #include "fg_init.hxx"
145 #include "fg_io.hxx"
146 #include "fg_commands.hxx"
147 #include "fg_props.hxx"
148 #include "FGInterpolator.hxx"
149 #include "options.hxx"
150 #include "globals.hxx"
151 #include "logger.hxx"
152 #include "main.hxx"
153 #include "positioninit.hxx"
154 #include "util.hxx"
155 #include "AircraftDirVisitorBase.hxx"
156 #include <Main/sentryIntegration.hxx>
157 #include <Main/ErrorReporter.hxx>
158 
159 #if defined(SG_MAC)
160 #include <GUI/CocoaHelpers.h> // for Mac impl of platformDefaultDataPath()
161 #endif
162 
163 using std::string;
164 using std::endl;
165 using std::cerr;
166 using std::cout;
167 
168 using namespace simgear::pkg;
169 
170 extern osg::ref_ptr<osgViewer::Viewer> viewer;
171 
172 // Return the current base package version
fgBasePackageVersion(const SGPath & base_path)173 string fgBasePackageVersion(const SGPath& base_path) {
174     SGPath p(base_path);
175     p.append("version");
176     if (!p.exists()) {
177         return string();
178     }
179 
180     sg_gzifstream in( p );
181     if (!in.is_open()) {
182         return string();
183     }
184 
185     string version;
186     in >> version;
187 
188     return version;
189 }
190 
191 class FindAndCacheAircraft : public AircraftDirVistorBase
192 {
193 public:
FindAndCacheAircraft(SGPropertyNode * autoSave)194   FindAndCacheAircraft(SGPropertyNode* autoSave)
195   {
196     _cache = autoSave->getNode("sim/startup/path-cache", true);
197   }
198 
199   /**
200    * @brief haveExplicitAircraft - check if the combination of /sim/aircraft
201    * and /sim/aircraft-dir defines an explicit -set.xml. We need to detect
202    * this case to short-circuit package detection
203    * @return
204    */
haveExplicitAircraft() const205   bool haveExplicitAircraft() const
206   {
207       const std::string aircraftDir = fgGetString("/sim/aircraft-dir", "");
208       if (aircraftDir.empty()) {
209           return false;
210       }
211 
212       const std::string aircraft = fgGetString( "/sim/aircraft", "");
213       SGPath setFile = SGPath::fromUtf8(aircraftDir) / (aircraft + "-set.xml");
214       return setFile.exists();
215   }
216 
loadAircraft()217   bool loadAircraft()
218   {
219     std::string aircraft = fgGetString( "/sim/aircraft", "");
220     if (aircraft.empty()) {
221       flightgear::fatalMessageBoxWithoutExit("No aircraft",
222                                              "No aircraft was specified");
223       SG_LOG(SG_GENERAL, SG_ALERT, "no aircraft specified");
224       return false;
225     }
226 
227     _searchAircraft = aircraft + "-set.xml";
228     std::string aircraftDir = fgGetString("/sim/aircraft-dir", "");
229     if (!aircraftDir.empty()) {
230       // aircraft-dir was set, skip any searching at all, if it's valid
231       simgear::Dir acPath(aircraftDir);
232       SGPath setFile = acPath.file(_searchAircraft);
233       if (setFile.exists()) {
234         SG_LOG(SG_GENERAL, SG_INFO, "found aircraft in dir: " << aircraftDir );
235 
236         try {
237           readProperties(setFile, globals->get_props());
238         } catch ( const sg_exception &e ) {
239             SG_LOG(SG_IO, SG_ALERT,
240                    "Error reading aircraft: " << e.getFormattedMessage());
241             flightgear::addSentryBreadcrumb("Aircraft-dir=" + aircraftDir, "error");
242             SG_LOG(SG_IO, SG_ALERT, "aircraft dir is:" << aircraftDir);
243             flightgear::fatalMessageBoxWithoutExit(
244                 "Error reading aircraft",
245                 "An error occured reading the requested aircraft (" + aircraft + ")",
246                 e.getFormattedMessage());
247             return false;
248         }
249 
250         checkAircraftMinVersion();
251 
252           // apply state after the -set.xml, but before any options are are set
253           flightgear::applyInitialState();
254         return true;
255       } else {
256         SG_LOG(SG_GENERAL, SG_ALERT, "aircraft '" << _searchAircraft <<
257                "' not found in specified dir:" << aircraftDir);
258         flightgear::addSentryBreadcrumb("Aircraft-dir=" + aircraftDir, "error");
259         flightgear::fatalMessageBoxWithoutExit(
260             "Aircraft not found",
261             "The requested aircraft (" + aircraft + ") could not be found "
262                                                     "in the specified location. (" +
263                 aircraftDir + ")",
264             aircraftDir);
265         return false;
266       }
267     }
268 
269     if (!checkCache()) {
270         flightgear::addSentryBreadcrumb("Scanning aircraft paths", "info");
271 
272         // prepare cache for re-scan
273         SGPropertyNode* n = _cache->getNode("fg-root", true);
274         n->setStringValue(globals->get_fg_root().utf8Str());
275         n->setAttribute(SGPropertyNode::USERARCHIVE, true);
276         n = _cache->getNode("fg-aircraft", true);
277         n->setStringValue(getAircraftPaths().c_str());
278         n->setAttribute(SGPropertyNode::USERARCHIVE, true);
279         _cache->removeChildren("aircraft");
280 
281         visitAircraftPaths();
282     }
283 
284     if (_foundPath.isNull()) {
285       SG_LOG(SG_GENERAL, SG_ALERT,
286              "Cannot find the specified aircraft: '" << aircraft << "'");
287 
288       flightgear::addSentryBreadcrumb("Aircraft paths: " + SGPath::join(globals->get_aircraft_paths(), ";"), "error");
289 
290       SG_LOG(SG_GENERAL, SG_ALERT, "\tin paths:" << SGPath::join(globals->get_aircraft_paths(), ";"));
291 
292       std::string notFoundMessage;
293       if (globals->get_aircraft_paths().empty()) {
294           notFoundMessage = "The requested aircraft (" + aircraft + ") could not be found. No aircraft paths are configured.";
295       } else {
296           notFoundMessage = "The requested aircraft (" + aircraft + ") could not be found in any of the search paths.";
297       }
298 
299       flightgear::fatalMessageBoxWithoutExit(
300           "Aircraft not found",
301           notFoundMessage);
302       return false;
303     }
304 
305     SG_LOG(SG_GENERAL, SG_INFO, "Loading aircraft -set file from:" << _foundPath);
306     fgSetString( "/sim/aircraft-dir", _foundPath.dir().c_str());
307     if (!_foundPath.exists()) {
308       SG_LOG(SG_GENERAL, SG_ALERT, "Unable to find -set file:" << _foundPath);
309       return false;
310     }
311 
312 
313     try {
314         readProperties(_foundPath, globals->get_props());
315     } catch ( const sg_exception &e ) {
316       SG_LOG(SG_INPUT, SG_ALERT,
317              "Error reading aircraft: " << e.getFormattedMessage());
318       flightgear::fatalMessageBoxWithoutExit(
319         "Error reading aircraft",
320         "An error occured reading the requested aircraft (" + aircraft + ")",
321         e.getFormattedMessage());
322       return false;
323     }
324 
325       // apply state after the -set.xml, but before any options are are set
326       flightgear::applyInitialState();
327 
328       checkAircraftMinVersion();
329 
330     return true;
331   }
332 
333 private:
getAircraftPaths()334     std::string getAircraftPaths()
335     {
336         return SGPath::join(globals->get_aircraft_paths(), ";");
337     }
338 
checkCache()339   bool checkCache()
340   {
341     if (globals->get_fg_root().utf8Str() != _cache->getStringValue("fg-root", "")) {
342       return false; // cache mismatch
343     }
344 
345     if (getAircraftPaths() != _cache->getStringValue("fg-aircraft", "")) {
346       return false; // cache mismatch
347     }
348 
349     vector<SGPropertyNode_ptr> cache = _cache->getChildren("aircraft");
350     for (unsigned int i = 0; i < cache.size(); i++) {
351       const char *name = cache[i]->getStringValue("file", "");
352       if (!simgear::strutils::iequals(_searchAircraft, name)) {
353         continue;
354       }
355 
356       SGPath xml(cache[i]->getStringValue("path", ""));
357       xml.append(name);
358       if (xml.exists()) {
359           flightgear::addSentryBreadcrumb("Found aircraft via cache", "info");
360           _foundPath = xml;
361           return true;
362       }
363 
364       return false;
365     } // of aircraft in cache iteration
366 
367     return false;
368   }
369 
visit(const SGPath & p)370   virtual VisitResult visit(const SGPath& p)
371   {
372       SGPath realPath = p.realpath();
373     // create cache node
374     int i = 0;
375     while (1) {
376         if (!_cache->getChild("aircraft", i++, false))
377             break;
378     }
379 
380     SGPropertyNode *n, *entry = _cache->getChild("aircraft", --i, true);
381 
382     std::string fileName(realPath.file());
383     n = entry->getNode("file", true);
384     n->setStringValue(fileName);
385     n->setAttribute(SGPropertyNode::USERARCHIVE, true);
386 
387     n = entry->getNode("path", true);
388     n->setStringValue(realPath.dir());
389     n->setAttribute(SGPropertyNode::USERARCHIVE, true);
390 
391     if (simgear::strutils::iequals(fileName, _searchAircraft)) {
392         _foundPath = realPath;
393         return VISIT_DONE;
394     }
395 
396     return VISIT_CONTINUE;
397   }
398 
checkAircraftMinVersion()399   bool checkAircraftMinVersion()
400   {
401       SGPropertyNode* minVersionNode = globals->get_props()->getNode("/sim/minimum-fg-version");
402       if (minVersionNode) {
403           std::string minVersion = minVersionNode->getStringValue();
404           const int c = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, minVersion, 2);
405           if (c < 0) {
406               SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft minimum version (" << minVersion <<
407                      ") is higher than FG version:" << FLIGHTGEAR_VERSION);
408               flightgear::modalMessageBox("Aircraft requires newer version of FlightGear",
409                                           "The selected aircraft requires FlightGear version " + minVersion
410                                           + " to work correctly. Some features may not work as expected, or the aircraft may not load at all.");
411               return false;
412           }
413       } else {
414           SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft does not specify a minimum FG version: please add one at /sim/minimum-fg-version");
415       }
416 
417       auto compatNodes = globals->get_props()->getNode("/sim")->getChildren("compatible-fg-version");
418       if (!compatNodes.empty()) {
419           bool showCompatWarning = true;
420 
421           // if we have at least one compatibility node, then it needs to match
422           for (const auto& cn : compatNodes) {
423               const auto v = cn->getStringValue();
424               if (simgear::strutils::compareVersionToWildcard(FLIGHTGEAR_VERSION, v)) {
425                   showCompatWarning = false;
426                   break;
427               }
428           }
429 
430           if (showCompatWarning) {
431               flightgear::modalMessageBox("Aircraft not compatible with this version",
432               "The selected aircraft has not been checked for compatability with this version of FlightGear (" FLIGHTGEAR_VERSION  "). "
433                                           "Some aircraft features might not work, or might be displayed incorrectly.");
434           }
435       }
436 
437       return true;
438   }
439 
440   std::string _searchAircraft;
441   SGPath _foundPath;
442   SGPropertyNode* _cache;
443 };
444 
445 #ifdef _WIN32
platformDefaultDataPath()446 static SGPath platformDefaultDataPath()
447 {
448   SGPath appDataPath = SGPath::fromEnv("APPDATA");
449 
450   if (appDataPath.isNull()) {
451     flightgear::fatalMessageBoxThenExit(
452       "FlightGear", "Unable to get the value of APPDATA.",
453       "FlightGear is unable to retrieve the value of the APPDATA environment "
454       "variable. This is quite unexpected on Windows platforms, and FlightGear "
455       "can't continue its execution without this value, sorry.");
456   }
457 
458   return appDataPath / "flightgear.org";
459 }
460 
461 #elif defined(SG_MAC)
462 
463 // platformDefaultDataPath defined in GUI/CocoaHelpers.h
464 
465 #else
platformDefaultDataPath()466 static SGPath platformDefaultDataPath()
467 {
468   return SGPath::home() / ".fgfs";
469 }
470 #endif
471 
472 #if defined(SG_WINDOWS)
473 static HANDLE static_fgHomeWriteMutex = nullptr;
474 #endif
475 
fgHomePath()476 SGPath fgHomePath()
477 {
478     return SGPath::fromEnv("FG_HOME", platformDefaultDataPath());
479 }
480 
fgInitHome()481 InitHomeResult fgInitHome()
482 {
483   SGPath dataPath = fgHomePath();
484   globals->set_fg_home(dataPath);
485 
486     simgear::Dir fgHome(dataPath);
487     if (!fgHome.exists()) {
488         fgHome.create(0755);
489     }
490 
491     if (!fgHome.exists()) {
492         flightgear::fatalMessageBoxWithoutExit(
493           "Problem setting up user data",
494           "Unable to create the user-data storage folder at '" +
495           dataPath.utf8Str() + "'.");
496         return InitHomeAbort;
497     }
498 
499     if (fgGetBool("/sim/fghome-readonly", false)) {
500         // user / config forced us into readonly mode, fine
501         SG_LOG(SG_GENERAL, SG_INFO, "Running with FG_HOME readonly");
502         return InitHomeExplicitReadOnly;
503     }
504 
505     InitHomeResult result = InitHomeOkay;
506 #if defined(SG_WINDOWS)
507 	// don't use a PID file on Windows, because deleting on close is
508 	// unreliable and causes false-positives. Instead, use a named
509 	// mutex.
510 
511 	static_fgHomeWriteMutex = CreateMutexA(nullptr, FALSE, "org.flightgear.fgfs.primary");
512 	if (static_fgHomeWriteMutex == nullptr) {
513 		printf("CreateMutex error: %d\n", GetLastError());
514 		SG_LOG(SG_GENERAL, SG_POPUP, "Failed to create mutex for multi-app protection");
515         return InitHomeAbort;
516 	} else if (GetLastError() == ERROR_ALREADY_EXISTS) {
517 		SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only.");
518 		fgSetBool("/sim/fghome-readonly", true);
519         return InitHomeReadOnly;
520 	} else {
521 		SG_LOG(SG_GENERAL, SG_INFO, "Created multi-app mutex, we are in writeable mode");
522         result = InitHomeOkay;
523 	}
524 #else
525 // write our PID, and check writeability
526     SGPath pidPath(dataPath, "fgfs_lock.pid");
527     std::string ps = pidPath.utf8Str();
528 
529     if (pidPath.exists()) {
530         int fd = ::open(ps.c_str(), O_RDONLY, 0644);
531         if (fd < 0) {
532             SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath
533                    << "\n\tdue to:" << simgear::strutils::error_string(errno));
534             return InitHomeAbort;
535         }
536 
537         int err = ::flock(fd, LOCK_EX | LOCK_NB);
538         if (err < 0) {
539             if ( errno ==  EWOULDBLOCK) {
540                 SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only. ");
541                 SG_LOG(SG_GENERAL, SG_ALERT, "Couldn't flock() file at:" << pidPath);
542 
543                 // set a marker property so terrasync/navcache don't try to write
544                 // from secondary instances
545                 fgSetBool("/sim/fghome-readonly", true);
546                 return InitHomeReadOnly;
547             } else {
548                 SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath
549                        << "\n\tdue to:" << simgear::strutils::error_string(errno));
550                 return InitHomeAbort;
551             }
552         }
553 
554        // we locked it!
555         result = InitHomeOkay;
556     } else {
557         char buf[16];
558        std::string ps = pidPath.utf8Str();
559 
560        ssize_t len = snprintf(buf, 16, "%d\n", getpid());
561        int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
562         if (fd < 0) {
563             SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath
564                << "\n\tdue to:" << simgear::strutils::error_string(errno));
565             return InitHomeAbort;
566         }
567 
568         int err = write(fd, buf, len);
569         if (err < 0) {
570             SG_LOG(SG_GENERAL, SG_ALERT, "failed to write to lock file:" << pidPath
571             << "\n\tdue to:" << simgear::strutils::error_string(errno));
572             return InitHomeAbort;
573         }
574 
575         err = flock(fd, LOCK_EX);
576         if (err != 0) {
577             SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath
578             << "\n\tdue to:" << simgear::strutils::error_string(errno));
579             return InitHomeAbort;
580         }
581 
582         result = InitHomeOkay;
583     }
584 #endif
585     fgSetBool("/sim/fghome-readonly", false);
586     return result;
587 }
588 
fgShutdownHome()589 void fgShutdownHome()
590 {
591 #if defined(SG_WINDOWS)
592 	if (static_fgHomeWriteMutex) {
593 		CloseHandle(static_fgHomeWriteMutex);
594 	}
595 #else
596     if (fgGetBool("/sim/fghome-readonly") == false) {
597         SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid";
598         pidPath.remove();
599     }
600 #endif
601 }
602 
fgDeleteLockFile()603 void fgDeleteLockFile()
604 {
605 #if defined(SG_WINDOWS)
606     // there's no file here, so we can't actually delete anything
607 #else
608     SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid";
609     pidPath.remove();
610 #endif
611 }
612 
createBaseStorageDirForAddons(const SGPath & exportDir)613 static void createBaseStorageDirForAddons(const SGPath& exportDir)
614 {
615     SGPath addonStorageBasePath = exportDir / "Addons";
616     if (addonStorageBasePath.exists()) {
617       if (!addonStorageBasePath.isDir()) {
618         throw sg_error(
619           "Unable to create add-on storage base directory, because the entry "
620           "already exists but is not a directory: '" +
621           addonStorageBasePath.utf8Str() + "'");
622       }
623     } else {
624       simgear::Dir(addonStorageBasePath).create(0777); // respect user's umask
625     }
626 }
627 
628 struct SimLogFileLine : SGPropertyChangeListener
629 {
SimLogFileLineSimLogFileLine630     SimLogFileLine() {
631         fgAddChangeListener(this, "/sim/log-file-line");
632     }
valueChangedSimLogFileLine633     virtual void valueChanged(SGPropertyNode* node) {
634         bool    fileLine = node->getBoolValue();
635         sglog().setFileLine(fileLine);
636     }
637 };
638 
639 // Read in configuration (file and command line)
fgInitConfig(int argc,char ** argv,bool reinit)640 int fgInitConfig ( int argc, char **argv, bool reinit )
641 {
642     SGPath dataPath = globals->get_fg_home();
643 
644     simgear::Dir exportDir(simgear::Dir(dataPath).file("Export"));
645     if (!exportDir.exists()) {
646       exportDir.create(0755);
647     }
648 
649     // Reserve a directory where add-ons can write. There will be a subdir for
650     // each add-on, see Addon::createStorageDir() and Addon::getStoragePath().
651     createBaseStorageDirForAddons(exportDir.path());
652 
653     // Set /sim/fg-home.  Use FG_HOME if necessary.
654     // deliberately not a tied property, for fgValidatePath security
655     // write-protect to avoid accidents
656     SGPropertyNode *home = fgGetNode("/sim", true);
657     home->removeChild("fg-home", 0);
658     home = home->getChild("fg-home", 0, true);
659     home->setStringValue(dataPath.utf8Str());
660     home->setAttribute(SGPropertyNode::WRITE, false);
661 
662     fgSetDefaults();
663     flightgear::Options* options = flightgear::Options::sharedInstance();
664     if (!reinit) {
665         auto result = options->init(argc, argv, dataPath);
666         if (result != flightgear::FG_OPTIONS_OK) {
667             return result;
668         }
669     }
670 
671     // establish default for developer-mode based upon compiled build types
672     bool developerMode = true;
673     if (!strcmp(FG_BUILD_TYPE, "Release")) {
674         developerMode = false;
675     }
676 
677     // allow command line to override
678     if (options->isOptionSet("developer")) {
679         string s = options->valueForOption("developer", "yes");
680         developerMode = simgear::strutils::to_bool(s);
681     }
682 
683     auto node = fgGetNode("/sim/developer-mode", true);
684     // ensure this value survives reset
685     node->setAttribute(SGPropertyNode::PRESERVE, true);
686     node->setBoolValue(developerMode);
687     sglog().setDeveloperMode(developerMode);
688 
689     static SimLogFileLine   simLogFileLine;
690 
691     // Read global defaults from $FG_ROOT/defaults
692     SG_LOG(SG_GENERAL, SG_DEBUG, "Reading global defaults");
693     SGPath defaultsXML = globals->get_fg_root() / "defaults.xml";
694     if (!defaultsXML.exists()) {
695         flightgear::fatalMessageBoxThenExit(
696             "Missing file",
697             "Couldn't load an essential simulator data file.",
698             defaultsXML.utf8Str());
699     }
700 
701     if(!fgLoadProps("defaults.xml", globals->get_props()))
702     {
703         flightgear::fatalMessageBoxThenExit(
704             "Corrupted file",
705             "Couldn't load an essential simulator data file as it is corrupted.",
706             defaultsXML.utf8Str());
707     }
708     SG_LOG(SG_GENERAL, SG_DEBUG, "Finished Reading global defaults");
709 
710     // do not load user settings when reset to default is requested, or if
711     // told to explicitly ignore
712     if (options->isOptionSet("restore-defaults") || options->isOptionSet("ignore-autosave"))
713     {
714         SG_LOG(SG_GENERAL, SG_ALERT, "Ignoring user settings. Restoring defaults.");
715     } else {
716         globals->loadUserSettings(dataPath);
717     }
718 
719     return flightgear::FG_OPTIONS_OK;
720 }
721 
initAircraftDirsNasalSecurity()722 static void initAircraftDirsNasalSecurity()
723 {
724     // deliberately not a tied property, for fgValidatePath security
725     // write-protect to avoid accidents
726     SGPropertyNode* sim = fgGetNode("/sim", true);
727     sim->removeChildren("fg-aircraft");
728 
729     int index = 0;
730     const PathList& aircraft_paths = globals->get_aircraft_paths();
731     for (PathList::const_iterator it = aircraft_paths.begin();
732          it != aircraft_paths.end(); ++it, ++index )
733     {
734         SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
735         n->setStringValue(it->utf8Str());
736         n->setAttribute(SGPropertyNode::WRITE, false);
737     }
738 }
739 
fgInitAircraftPaths(bool reinit)740 void fgInitAircraftPaths(bool reinit)
741 {
742   if (!globals->packageRoot()) {
743       fgInitPackageRoot();
744   }
745 
746   SGSharedPtr<Root> pkgRoot(globals->packageRoot());
747   SGPropertyNode* aircraftProp = fgGetNode("/sim/aircraft", true);
748   aircraftProp->setAttribute(SGPropertyNode::PRESERVE, true);
749 
750   if (!reinit) {
751       flightgear::Options::sharedInstance()->initPaths();
752   }
753 }
754 
fgInitAircraft(bool reinit)755 int fgInitAircraft(bool reinit)
756 {
757     if (!reinit) {
758         auto r = flightgear::Options::sharedInstance()->initAircraft();
759         if (r == flightgear::FG_OPTIONS_SHOW_AIRCRAFT)
760             return r;
761     }
762 
763     FindAndCacheAircraft f(globals->get_props());
764     const bool haveExplicit = f.haveExplicitAircraft();
765     if (haveExplicit) {
766         flightgear::addSentryBreadcrumb("Have explicit aircraft-dr and -set.xml", "info");
767     }
768 
769     SGSharedPtr<Root> pkgRoot(globals->packageRoot());
770     SGPropertyNode* aircraftProp = fgGetNode("/sim/aircraft", true);
771     const string fullyQualifiedAircraftId = fgGetString("/sim/aircraft-id");
772     string aircraftId = fullyQualifiedAircraftId.empty() ? aircraftProp->getStringValue() : fullyQualifiedAircraftId;
773 
774     flightgear::addSentryTag("aircraft", aircraftId);
775 
776     PackageRef acftPackage;
777     if (!haveExplicit) {
778         acftPackage = pkgRoot->getPackageById(aircraftId);
779     }
780 
781     if (acftPackage) {
782         if (acftPackage->isInstalled()) {
783             flightgear::addSentryBreadcrumb("Loading aircraft from installed package", "info");
784             SG_LOG(SG_GENERAL, SG_INFO, "Loading aircraft from package:" << acftPackage->qualifiedId());
785 
786             // if we resolved a non-qualified ID, set the full one back to /sim/aircraft-id
787             fgSetString("/sim/aircraft-id", acftPackage->qualifiedId());
788 
789             // replace this tag, so we know which hangar is in use
790             flightgear::addSentryTag("aircraft", acftPackage->qualifiedId());
791 
792             // set catalog path so intra-package dependencies within the catalog
793             // are resolved correctly.
794             globals->set_catalog_aircraft_path(acftPackage->catalog()->installRoot());
795 
796             // set aircraft-dir to short circuit the search process
797             InstallRef acftInstall = acftPackage->install();
798             fgSetString("/sim/aircraft-dir", acftInstall->path().utf8Str());
799 
800             flightgear::addSentryBreadcrumb("Computed aircraft-dir as:" + acftInstall->path().utf8Str(), "info");
801 
802             // overwrite the fully qualified ID with the aircraft one, so the
803             // code in FindAndCacheAircraft works as normal
804             // note since we may be using a variant, we can't use the package ID
805             size_t lastDot = aircraftId.rfind('.');
806             if (lastDot != std::string::npos) {
807                 aircraftId = aircraftId.substr(lastDot + 1);
808             }
809             aircraftProp->setStringValue(aircraftId);
810 
811             // run the traditional-code path below
812         } else {
813             flightgear::addSentryBreadcrumb("Package is not installed:" + aircraftId, "info");
814 #if 0
815             // naturally the better option would be to on-demand install it!
816             flightgear::fatalMessageBoxWithoutExit(
817                 "Aircraft not installed",
818                 "Requested aircraft is not currently installed.",
819                 aircraftId);
820 
821             return flightgear::FG_OPTIONS_ERROR;
822 #endif
823             // fall back the default aircraft instead
824         }
825     }
826 
827     initAircraftDirsNasalSecurity();
828 
829     if (!f.loadAircraft()) {
830         return flightgear::FG_OPTIONS_ERROR;
831     }
832 
833     return flightgear::FG_OPTIONS_OK;
834 }
835 
836 /**
837  * Initialize vor/ndb/ils/fix list management and query systems (as
838  * well as simple airport db list)
839  * This is called multiple times in the case of a cache rebuild,
840  * to allow lengthy caching to take place in the background, without
841  * blocking the main/UI thread.
842  */
843 bool
fgInitNav()844 fgInitNav ()
845 {
846     flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
847     static bool doingRebuild = false;
848 
849     if (!cache) {
850         cache = flightgear::NavDataCache::createInstance();
851         doingRebuild = cache->isRebuildRequired();
852     }
853 
854     static const char* splashIdentsByRebuildPhase[] = {
855         "loading-nav-dat",
856         "navdata-reading-apt-dat-files",
857         "navdata-loading-airports",
858         "navdata-navaids",
859         "navdata-fixes",
860         "navdata-pois"
861     };
862 
863     if (doingRebuild) {
864         flightgear::NavDataCache::RebuildPhase phase;
865         phase = cache->rebuild();
866         if (phase != flightgear::NavDataCache::REBUILD_DONE) {
867             // update the splash text based on percentage, phase
868 
869             fgSplashProgress(splashIdentsByRebuildPhase[phase],
870                              cache->rebuildPhaseCompletionPercentage());
871 
872             // sleep to give the rebuild thread more time
873             SGTimeStamp::sleepForMSec(50);
874             return false;
875         }
876     }
877 
878   FGTACANList *channellist = new FGTACANList;
879   globals->set_channellist( channellist );
880 
881   SGPath path(globals->get_fg_root());
882   path.append( "Navaids/TACAN_freq.dat" );
883   flightgear::NavLoader().loadTacan(path, channellist);
884 
885   return true;
886 }
887 
888 // General house keeping initializations
fgInitGeneral()889 bool fgInitGeneral() {
890 
891     SG_LOG( SG_GENERAL, SG_INFO, "General Initialization" );
892     SG_LOG( SG_GENERAL, SG_INFO, "======= ==============" );
893 
894     if ( globals->get_fg_root().isNull() ) {
895         // No root path set? Then bail ...
896         SG_LOG( SG_GENERAL, SG_ALERT,
897                 "Cannot continue without a path to the base package "
898                 << "being defined." );
899         return false;
900     }
901     SG_LOG( SG_GENERAL, SG_INFO, "FG_ROOT = " << '"' << globals->get_fg_root() << '"' << endl );
902 
903     // Note: browser command is hard-coded for Mac/Windows, so this only affects other platforms
904     globals->set_browser(fgGetString("/sim/startup/browser-app", WEB_BROWSER));
905     fgSetString("/sim/startup/browser-app", globals->get_browser());
906 
907     simgear::Dir cwd(simgear::Dir::current());
908     SGPropertyNode *curr = fgGetNode("/sim", true);
909     curr->removeChild("fg-current", 0);
910     curr = curr->getChild("fg-current", 0, true);
911     curr->setStringValue(cwd.path().utf8Str());
912     curr->setAttribute(SGPropertyNode::WRITE, false);
913 
914     fgSetBool("/sim/startup/stdout-to-terminal", isatty(1) != 0 );
915     fgSetBool("/sim/startup/stderr-to-terminal", isatty(2) != 0 );
916 
917     sgUserDataInit( globals->get_props() );
918     flightgear::addSentryTag("have-reset", "no");
919 
920     return true;
921 }
922 
923 // Write various configuraton values out to the logs
fgOutputSettings()924 void fgOutputSettings()
925 {
926     SG_LOG( SG_GENERAL, SG_INFO, "Configuration State" );
927     SG_LOG( SG_GENERAL, SG_INFO, "============= =====" );
928 
929     SG_LOG( SG_GENERAL, SG_INFO, "aircraft-dir = " << '"' << fgGetString("/sim/aircraft-dir") << '"' );
930     SG_LOG( SG_GENERAL, SG_INFO, "fghome-dir = " << '"' << globals->get_fg_home() << '"');
931     SG_LOG( SG_GENERAL, SG_INFO, "download-dir = " << '"' << fgGetString("/sim/paths/download-dir") << '"' );
932     SG_LOG( SG_GENERAL, SG_INFO, "terrasync-dir = " << '"' << fgGetString("/sim/terrasync/scenery-dir") << '"' );
933 
934     SG_LOG( SG_GENERAL, SG_INFO, "aircraft-search-paths = \n\t" << SGPath::join(globals->get_aircraft_paths(), "\n\t") );
935     SG_LOG( SG_GENERAL, SG_INFO, "scenery-search-paths = \n\t" << SGPath::join(globals->get_fg_scenery(), "\n\t") );
936 }
937 
938 // This is the top level init routine which calls all the other
939 // initialization routines.  If you are adding a subsystem to flight
940 // gear, its initialization call should located in this routine.
941 // Returns non-zero if a problem encountered.
fgCreateSubsystems(bool duringReset)942 void fgCreateSubsystems(bool duringReset) {
943 
944     SG_LOG( SG_GENERAL, SG_INFO, "Creating Subsystems");
945     SG_LOG( SG_GENERAL, SG_INFO, "======== ==========");
946 
947     ////////////////////////////////////////////////////////////////////
948     // Initialize the sound subsystem.
949     ////////////////////////////////////////////////////////////////////
950     // Sound manager uses an own subsystem group "SOUND" which is the last
951     // to be updated in every loop.
952     // Sound manager is updated last so it can use the CPU while the GPU
953     // is processing the scenery (doubled the frame-rate for me) -EMH-
954     globals->add_new_subsystem<FGSoundManager>(SGSubsystemMgr::SOUND);
955 
956     ////////////////////////////////////////////////////////////////////
957     // Initialize the event manager subsystem.
958     ////////////////////////////////////////////////////////////////////
959 
960     globals->get_event_mgr()->init();
961     globals->get_event_mgr()->setRealtimeProperty(fgGetNode("/sim/time/delta-realtime-sec", true));
962 
963     ////////////////////////////////////////////////////////////////////
964     // Initialize the property interpolator subsystem. Put into the INIT
965     // group because the "nasal" subsystem may need it at GENERAL take-down.
966     ////////////////////////////////////////////////////////////////////
967     globals->add_subsystem("prop-interpolator", new FGInterpolator, SGSubsystemMgr::INIT);
968 
969 
970     ////////////////////////////////////////////////////////////////////
971     // Add the FlightGear property utilities.
972     ////////////////////////////////////////////////////////////////////
973     globals->add_subsystem("properties", new FGProperties);
974 
975 
976     ////////////////////////////////////////////////////////////////////
977     // Add the FlightGear property utilities.
978     ////////////////////////////////////////////////////////////////////
979     globals->add_new_subsystem<flightgear::AirportDynamicsManager>();
980 
981     ////////////////////////////////////////////////////////////////////
982     // Add the performance monitoring system.
983     ////////////////////////////////////////////////////////////////////
984     globals->add_subsystem("performance-mon",
985             new SGPerformanceMonitor(globals->get_subsystem_mgr(),
986                                      fgGetNode("/sim/performance-monitor", true)));
987 
988     ////////////////////////////////////////////////////////////////////
989     // Initialize the material property subsystem.
990     ////////////////////////////////////////////////////////////////////
991 
992     SGPath mpath( globals->get_fg_root() );
993     mpath.append( fgGetString("/sim/rendering/materials-file") );
994     if ( ! globals->get_matlib()->load(globals->get_fg_root(), mpath, globals->get_props()) ) {
995        throw sg_io_exception("Error loading materials file", mpath);
996     }
997 
998     // may exist already due to GUI startup
999     if (!globals->get_subsystem<FGHTTPClient>()) {
1000         globals->add_new_subsystem<FGHTTPClient>();
1001     }
1002     globals->add_new_subsystem<FGDNSClient>();
1003 
1004     ////////////////////////////////////////////////////////////////////
1005     // Initialize the flight model subsystem.
1006     ////////////////////////////////////////////////////////////////////
1007 
1008     globals->add_subsystem("flight", new FDMShell, SGSubsystemMgr::FDM);
1009 
1010     ////////////////////////////////////////////////////////////////////
1011     // Initialize the weather subsystem.
1012     ////////////////////////////////////////////////////////////////////
1013 
1014     // Initialize the weather modeling subsystem
1015     globals->add_subsystem("environment", new FGEnvironmentMgr);
1016     globals->add_new_subsystem<Ephemeris>();
1017 
1018     ////////////////////////////////////////////////////////////////////
1019     // Initialize the aircraft systems and instrumentation (before the
1020     // autopilot.)
1021     ////////////////////////////////////////////////////////////////////
1022 
1023     globals->add_subsystem("systems", new FGSystemMgr, SGSubsystemMgr::FDM);
1024     globals->add_subsystem("instrumentation", new FGInstrumentMgr, SGSubsystemMgr::FDM);
1025     globals->add_subsystem("hud", new HUD, SGSubsystemMgr::DISPLAY);
1026     globals->add_subsystem("cockpit-displays", new flightgear::CockpitDisplayManager, SGSubsystemMgr::DISPLAY);
1027 
1028     ////////////////////////////////////////////////////////////////////
1029     // Initialize the XML Autopilot subsystem.
1030     ////////////////////////////////////////////////////////////////////
1031 
1032     globals->add_subsystem( "xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"), SGSubsystemMgr::FDM );
1033     globals->add_subsystem( "xml-proprules", FGXMLAutopilotGroup::createInstance("property-rule"), SGSubsystemMgr::GENERAL );
1034     globals->add_new_subsystem<FGRouteMgr>();
1035 
1036     ////////////////////////////////////////////////////////////////////
1037     // Initialize the Input-Output subsystem
1038     ////////////////////////////////////////////////////////////////////
1039     globals->add_subsystem( "io", new FGIO );
1040 
1041     ////////////////////////////////////////////////////////////////////
1042     // Create and register the logger.
1043     ////////////////////////////////////////////////////////////////////
1044 
1045     globals->add_subsystem("logger", new FGLogger);
1046 
1047     ////////////////////////////////////////////////////////////////////
1048     // Create and register the XML GUI.
1049     ////////////////////////////////////////////////////////////////////
1050 
1051     globals->add_subsystem("gui", new NewGUI, SGSubsystemMgr::INIT);
1052 
1053     //////////////////////////////////////////////////////////////////////
1054     // Initialize the 2D cloud subsystem.
1055     ////////////////////////////////////////////////////////////////////
1056     fgGetBool("/sim/rendering/bump-mapping", false);
1057 
1058     ////////////////////////////////////////////////////////////////////
1059     // Initialize the canvas 2d drawing subsystem.
1060     ////////////////////////////////////////////////////////////////////
1061     simgear::canvas::Canvas::setSystemAdapter(
1062       simgear::canvas::SystemAdapterPtr(new canvas::FGCanvasSystemAdapter)
1063     );
1064     globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY);
1065     globals->add_subsystem("CanvasGUI", new GUIMgr, SGSubsystemMgr::DISPLAY);
1066 
1067     ////////////////////////////////////////////////////////////////////
1068    // Initialize the ATC subsystem
1069     ////////////////////////////////////////////////////////////////////
1070 
1071     globals->add_new_subsystem<PerformanceDB>(SGSubsystemMgr::POST_FDM);
1072     globals->add_subsystem("ATC", new FGATCManager, SGSubsystemMgr::POST_FDM);
1073 
1074     ////////////////////////////////////////////////////////////////////
1075     // Initialize multiplayer subsystem
1076     ////////////////////////////////////////////////////////////////////
1077 
1078     globals->add_subsystem("mp", new FGMultiplayMgr, SGSubsystemMgr::POST_FDM);
1079 
1080 #ifdef ENABLE_SWIFT
1081     ////////////////////////////////////////////////////////////////////
1082     // Initialize Swift subsystem
1083     ////////////////////////////////////////////////////////////////////
1084 
1085     globals->add_subsystem("swift", new SwiftConnection, SGSubsystemMgr::POST_FDM);
1086 #endif
1087 
1088     ////////////////////////////////////////////////////////////////////
1089     // Initialise the AI Model Manager
1090     ////////////////////////////////////////////////////////////////////
1091 
1092     globals->add_subsystem("ai-model", new FGAIManager, SGSubsystemMgr::POST_FDM);
1093     globals->add_subsystem("submodel-mgr", new FGSubmodelMgr, SGSubsystemMgr::POST_FDM);
1094 
1095 
1096     // It's probably a good idea to initialize the top level traffic manager
1097     // After the AI and ATC systems have been initialized properly.
1098     // AI Traffic manager
1099     globals->add_subsystem("traffic-manager", new FGTrafficManager, SGSubsystemMgr::POST_FDM);
1100 
1101     ////////////////////////////////////////////////////////////////////
1102     // Add a new 2D panel.
1103     ////////////////////////////////////////////////////////////////////
1104 
1105     fgSetArchivable("/sim/panel/visibility");
1106     fgSetArchivable("/sim/panel/x-offset");
1107     fgSetArchivable("/sim/panel/y-offset");
1108     fgSetArchivable("/sim/panel/jitter");
1109 
1110     ////////////////////////////////////////////////////////////////////
1111     // Initialize the controls subsystem.
1112     ////////////////////////////////////////////////////////////////////
1113 
1114     globals->add_new_subsystem<FGControls>(SGSubsystemMgr::GENERAL);
1115 
1116     ////////////////////////////////////////////////////////////////////
1117     // Initialize the input subsystem.
1118     ////////////////////////////////////////////////////////////////////
1119 
1120     globals->add_new_subsystem<FGInput>(SGSubsystemMgr::GENERAL);
1121 
1122 
1123     ////////////////////////////////////////////////////////////////////
1124     // Initialize the replay subsystem
1125     ////////////////////////////////////////////////////////////////////
1126     globals->add_new_subsystem<FGReplay>(SGSubsystemMgr::GENERAL);
1127     globals->add_subsystem("history", new FGFlightHistory);
1128 
1129 #ifdef ENABLE_AUDIO_SUPPORT
1130     ////////////////////////////////////////////////////////////////////
1131     // Initialize the sound-effects subsystem.
1132     ////////////////////////////////////////////////////////////////////
1133     globals->add_subsystem("voice", new FGVoiceMgr, SGSubsystemMgr::DISPLAY);
1134 #endif
1135 
1136 #ifdef ENABLE_IAX
1137     ////////////////////////////////////////////////////////////////////
1138     // Initialize the FGCom subsystem.
1139     // very important this goes in the SOUND group, since IAXClient
1140     // depends on OpenAL, which is shutdown when the SOUND group
1141     // shutdown.
1142     // Sentry: FLIGHTGEAR-66
1143 
1144     ////////////////////////////////////////////////////////////////////
1145     globals->add_new_subsystem<FGCom>(SGSubsystemMgr::SOUND);
1146 #endif
1147 
1148     {
1149       SGSubsystem * httpd = flightgear::http::FGHttpd::createInstance( fgGetNode(flightgear::http::PROPERTY_ROOT) );
1150       if( NULL != httpd )
1151         globals->add_subsystem("httpd", httpd  );
1152     }
1153 
1154     ////////////////////////////////////////////////////////////////////
1155     // Initialize the lighting subsystem.
1156     ////////////////////////////////////////////////////////////////////
1157 
1158     // ordering here is important : Nasal (via events), then models, then views
1159     if (!duringReset) {
1160         globals->add_subsystem("lighting", new FGLight, SGSubsystemMgr::DISPLAY);
1161         globals->add_subsystem("events", globals->get_event_mgr(), SGSubsystemMgr::DISPLAY);
1162         globals->add_subsystem("tides", new FGTide );
1163     }
1164 
1165     globals->add_new_subsystem<FGAircraftModel>(SGSubsystemMgr::DISPLAY);
1166     globals->add_new_subsystem<FGModelMgr>(SGSubsystemMgr::DISPLAY);
1167 
1168     globals->add_new_subsystem<FGViewMgr>(SGSubsystemMgr::DISPLAY);
1169 }
1170 
fgPostInitSubsystems()1171 void fgPostInitSubsystems()
1172 {
1173     SGTimeStamp st;
1174     st.stamp();
1175 
1176     ////////////////////////////////////////////////////////////////////////
1177     // Initialize the Nasal interpreter.
1178     // Do this last, so that the loaded scripts see initialized state
1179     ////////////////////////////////////////////////////////////////////////
1180     globals->add_new_subsystem<FGNasalSys>(SGSubsystemMgr::INIT);
1181 
1182     // initialize methods that depend on other subsystems.
1183     st.stamp();
1184     globals->get_subsystem_mgr()->postinit();
1185     SG_LOG(SG_GENERAL, SG_INFO, "Subsystems postinit took:" << st.elapsedMSec());
1186 
1187     ////////////////////////////////////////////////////////////////////////
1188     // End of subsystem initialization.
1189     ////////////////////////////////////////////////////////////////////
1190 
1191     fgSetBool("/sim/crashed", false);
1192     fgSetBool("/sim/initialized", true);
1193 
1194     SG_LOG( SG_GENERAL, SG_INFO, endl);
1195 }
1196 
1197 // re-position is a simplified version of the traditional (legacy)
1198 // reset procedure. We only need to poke systems which will be upset by
1199 // a sudden change in aircraft position. Since this potentially includes
1200 // Nasal, we trigger the 'reinit' signal.
fgStartReposition()1201 void fgStartReposition()
1202 {
1203   SGPropertyNode *master_freeze = fgGetNode("/sim/freeze/master");
1204   SG_LOG( SG_GENERAL, SG_INFO, "fgStartReposition()");
1205 
1206   flightgear::addSentryBreadcrumb("start reposition", "info");
1207 
1208   // ensure we are frozen
1209   bool freeze = master_freeze->getBoolValue();
1210   if ( !freeze ) {
1211     master_freeze->setBoolValue(true);
1212   }
1213 
1214   // set this signal so Nasal scripts can take action.
1215   fgSetBool("/sim/signals/reinit", true);
1216   fgSetBool("/sim/crashed", false);
1217 
1218   FDMShell* fdm = globals->get_subsystem<FDMShell>();
1219   fdm->unbind();
1220 
1221   // update our position based on current presets
1222   // this will mark position as needed finalized which we'll do in the
1223   // main-loop
1224   flightgear::initPosition();
1225 
1226   auto terraSync = globals->get_subsystem<simgear::SGTerraSync>();
1227   if (terraSync) {
1228     terraSync->reposition();
1229   }
1230 
1231   // Initialize the FDM
1232   globals->get_subsystem<FDMShell>()->reinit();
1233 
1234   // reset replay buffers
1235   globals->get_subsystem<FGReplay>()->reinit();
1236 
1237   // ugly: finalizePosition waits for METAR to arrive for the new airport.
1238   // we don't re-init the environment manager here, since historically we did
1239   // not, and doing so seems to have other issues. All that's needed is to
1240   // schedule METAR fetch immediately, so it's available for finalizePosition.
1241   // So we manually extract the METAR-fetching component inside the environment
1242   // manager, and re-init that.
1243   SGSubsystemGroup* envMgr = static_cast<SGSubsystemGroup*>(globals->get_subsystem("environment"));
1244   if (envMgr) {
1245     envMgr->get_subsystem("realwx")->reinit();
1246   }
1247 
1248     // needed for parking assignment to work after reposition
1249     auto atcManager = globals->get_subsystem<FGATCManager>();
1250     if (atcManager) {
1251         atcManager->reposition();
1252     }
1253 
1254   // need to bind FDMshell again
1255   fdm->bind();
1256 
1257   // need to reset aircraft (systems/instruments/autopilot)
1258   // so they can adapt to current environment
1259   globals->get_subsystem("systems")->reinit();
1260   globals->get_subsystem("instrumentation")->reinit();
1261   globals->get_subsystem("xml-autopilot")->reinit();
1262 
1263   // setup state to end re-init
1264   fgSetBool("/sim/signals/reinit", false);
1265   if ( !freeze ) {
1266     master_freeze->setBoolValue(false);
1267   }
1268   fgSetBool("/sim/sceneryloaded",false);
1269   flightgear::addSentryBreadcrumb("end of reposition", "info");
1270 }
1271 
fgStartNewReset()1272 void fgStartNewReset()
1273 {
1274     flightgear::updateSentryTag("have-reset", "yes");
1275 
1276     // save user settings now, so that USERARCIVE-d values changes since the
1277     // last init are recorded and hence re-loaded when we fgInitConfig down
1278     // later in this function. Otherwise all such settings are lost.
1279     globals->saveUserSettings();
1280 
1281     SGPropertyNode_ptr preserved(new SGPropertyNode);
1282 
1283     if (!copyPropertiesWithAttribute(globals->get_props(), preserved, SGPropertyNode::PRESERVE))
1284         SG_LOG(SG_GENERAL, SG_ALERT, "Error saving preserved state");
1285 
1286     fgSetBool("/sim/signals/reinit", true);
1287     fgSetBool("/sim/freeze/master", true);
1288 
1289     SGSubsystemMgr* subsystemManger = globals->get_subsystem_mgr();
1290     // Nasal is added in fgPostInit, ensure it's already shutdown
1291     // before other subsystems, so Nasal listeners don't fire during shutdown
1292     subsystemManger->remove("nasal");
1293 
1294     subsystemManger->shutdown();
1295     subsystemManger->unbind();
1296 
1297     // hack fix for many reset crashes relating to the static instance
1298     // of this class. Will be fixed better for future versions by making
1299     // this a proper subsystem.
1300     FGATCDialogNew::hackyReset();
1301 
1302     // remove most subsystems, with a few exceptions.
1303     for (int g=0; g<SGSubsystemMgr::MAX_GROUPS; ++g) {
1304         SGSubsystemGroup* grp = subsystemManger->get_group(static_cast<SGSubsystemMgr::GroupType>(g));
1305         for (auto nm : grp->member_names()) {
1306             if ((nm == "time") || (nm == "terrasync") || (nm == "events")
1307                 || (nm == "lighting")
1308                 || (nm == FGScenery::staticSubsystemClassId())
1309                 || (nm == flightgear::ErrorReporter::staticSubsystemClassId())
1310                 )
1311             {
1312                 continue;
1313             }
1314 
1315             try {
1316                 subsystemManger->remove(nm.c_str());
1317             } catch (std::exception& e) {
1318                 SG_LOG(SG_GENERAL, SG_INFO, "caught " << e.what() << " << shutting down:" << nm);
1319             } catch (...) {
1320                 SG_LOG(SG_GENERAL, SG_INFO, "caught generic exception shutting down:" << nm);
1321             }
1322 
1323             // don't delete here, dropping the ref should be sufficient
1324         }
1325     } // of top-level groups iteration
1326 
1327     // drop any references to AI objects with TACAN
1328     flightgear::NavDataCache::instance()->clearDynamicPositioneds();
1329 
1330     FGRenderer* render = globals->get_renderer();
1331     // needed or we crash in multi-threaded OSG mode
1332     render->getViewer()->stopThreading();
1333 
1334     // order is important here since tile-manager shutdown needs to
1335     // access the scenery object
1336     subsystemManger->remove(FGScenery::staticSubsystemClassId());
1337 
1338     FGScenery::getPagerSingleton()->clearRequests();
1339     flightgear::CameraGroup::setDefault(NULL);
1340 
1341     // don't cancel the pager until after shutdown, since AIModels (and
1342     // potentially others) can queue delete requests on the pager.
1343     render->getViewer()->getDatabasePager()->cancel();
1344     render->getViewer()->getDatabasePager()->clear();
1345 
1346     osgDB::Registry::instance()->clearObjectCache();
1347     // Pager requests depend on this, so don't clear it until now
1348     sgUserDataInit( NULL );
1349 
1350     // preserve the event handler; re-creating it would entail fixing the
1351     // idle handler
1352     osg::ref_ptr<flightgear::FGEventHandler> eventHandler = render->getEventHandler();
1353     // tell the event handler to drop properties, etc
1354     eventHandler->clear();
1355 
1356     globals->set_renderer(NULL);
1357     globals->set_matlib(NULL);
1358 
1359     flightgear::unregisterMainLoopProperties();
1360     FGReplayData::resetStatisticsProperties();
1361 
1362     simgear::clearSharedTreeGeometry();
1363     simgear::clearEffectCache();
1364     simgear::SGModelLib::resetPropertyRoot();
1365     simgear::ParticlesGlobalManager::clear();
1366     simgear::UniformFactory::instance()->reset();
1367 
1368     flightgear::addons::AddonManager::reset();
1369 
1370     globals->resetPropertyRoot();
1371     // otherwise channels are duplicated
1372     globals->get_channel_options_list()->clear();
1373 
1374     // IMPORTANT
1375     // this is the low-water mark of the reset process.
1376     // Subsystems (except the special ones), properties, OSG nodes, Effects
1377     // should all be gone at this, except for special things, such as the
1378     // splash node.
1379     // From here onwards we're recreating early parts of main/init, before
1380     // we restart the main loop.
1381     // This is the place to check that instances of classes have all be
1382     // cleaned up correctly. (Also, the OSG threads are all paused, we're back
1383     // in single threaded mode)
1384     /////////////////////
1385 
1386     flightgear::addons::AddonManager::createInstance();
1387 
1388     fgInitConfig(0, NULL, true);
1389     fgInitGeneral(); // all of this?
1390 
1391     // set out new property root on the command manager
1392     SGCommandMgr::instance()->setImplicitRoot(globals->get_props());
1393 
1394     flightgear::Options::sharedInstance()->processOptions();
1395 
1396     // Rebuild the lists of allowed paths for cases where a path comes from an
1397     // untrusted source, such as the global property tree (this uses $FG_HOME
1398     // and other paths set by Options::processOptions()).
1399     fgInitAllowedPaths();
1400 
1401     const auto& resMgr = simgear::EmbeddedResourceManager::instance();
1402     // The language was (re)set in processOptions()
1403     const string locale = globals->get_locale()->getPreferredLanguage();
1404     resMgr->selectLocale(locale);
1405     SG_LOG(SG_GENERAL, SG_INFO,
1406            "EmbeddedResourceManager: selected locale '" << locale << "'");
1407 
1408     // PRESERVED properties over-write state from options, intentionally
1409     if ( copyProperties(preserved, globals->get_props()) ) {
1410         SG_LOG( SG_GENERAL, SG_INFO, "Preserved state restored successfully" );
1411     } else {
1412         SG_LOG( SG_GENERAL, SG_INFO,
1413                "Some errors restoring preserved state (read-only props?)" );
1414     }
1415 
1416     fgGetNode("/sim")->removeChild("aircraft-dir");
1417     fgInitAircraftPaths(true);
1418     fgInitAircraft(true);
1419 
1420     render = new FGRenderer;
1421     render->setEventHandler(eventHandler);
1422     eventHandler->reset();
1423     globals->set_renderer(render);
1424     render->init();
1425     render->setViewer(viewer.get());
1426 
1427     sgUserDataInit( globals->get_props() );
1428 
1429     viewer->getDatabasePager()->setUpThreads(20, 1);
1430 
1431     // must do this before preinit for Rembrandthe
1432     flightgear::CameraGroup::buildDefaultGroup(viewer.get());
1433     render->preinit();
1434     viewer->startThreading();
1435 
1436     fgOSResetProperties();
1437 
1438 // init some things manually
1439 // which do not follow the regular init pattern
1440 
1441     globals->get_event_mgr()->init();
1442     globals->get_event_mgr()->setRealtimeProperty(fgGetNode("/sim/time/delta-realtime-sec", true));
1443 
1444     globals->set_matlib( new SGMaterialLib );
1445 
1446 // terra-sync needs the property tree root, pass it back in
1447     auto terra_sync = subsystemManger->get_subsystem<simgear::SGTerraSync>();
1448     if (terra_sync) {
1449         terra_sync->setRoot(globals->get_props());
1450     }
1451 
1452     fgSetBool("/sim/signals/reinit", false);
1453     fgSetBool("/sim/freeze/master", false);
1454     fgSetBool("/sim/sceneryloaded",false);
1455 
1456     flightgear::addSentryBreadcrumb("end of reset", "info");
1457 }
1458 
fgInitPackageRoot()1459 void fgInitPackageRoot()
1460 {
1461     if (globals->packageRoot()) {
1462         return;
1463     }
1464 
1465     SGPath packageAircraftDir = flightgear::Options::sharedInstance()->actualDownloadDir();
1466     packageAircraftDir.append("Aircraft");
1467 
1468     SG_LOG(SG_GENERAL, SG_INFO, "init package root at:" << packageAircraftDir);
1469 
1470     SGSharedPtr<Root> pkgRoot(new Root(packageAircraftDir, FLIGHTGEAR_VERSION));
1471     // set the http client later (too early in startup right now)
1472     globals->setPackageRoot(pkgRoot);
1473 
1474 }
1475 
fgUninstall()1476 int fgUninstall()
1477 {
1478     SGPath dataPath = SGPath::fromEnv("FG_HOME", platformDefaultDataPath());
1479     simgear::Dir fgHome(dataPath);
1480     if (fgHome.exists()) {
1481         if (!fgHome.remove(true /* recursive */)) {
1482             fprintf(stderr, "Errors occurred trying to remove FG_HOME");
1483             return EXIT_FAILURE;
1484         }
1485     }
1486 
1487     if (fgHome.exists()) {
1488         fprintf(stderr, "unable to remove FG_HOME");
1489         return EXIT_FAILURE;
1490     }
1491 
1492 #if defined(SG_WINDOWS)
1493     SGPath p = flightgear::defaultDownloadDir();
1494     // we don't want to remove the whole dir, let's nuke specific
1495     // subdirs which are (hopefully) safe
1496 
1497     SGPath terrasyncPath = p / "TerraSync";
1498     if (terrasyncPath.exists()) {
1499         simgear::Dir dir(terrasyncPath);
1500         if (!dir.remove(true /*recursive*/)) {
1501             std::cerr << "Errors occurred trying to remove " << terrasyncPath << std::endl;
1502         }
1503     }
1504 
1505     SGPath packagesPath = p / "Aircraft";
1506     if (packagesPath.exists()) {
1507         simgear::Dir dir(packagesPath);
1508         if (!dir.remove(true /*recursive*/)) {
1509             std::cerr << "Errors occurred trying to remove " << packagesPath << std::endl;
1510         }
1511     }
1512 
1513     SGPath cachePath = p / "TextureCache";
1514     if (cachePath.exists()) {
1515         simgear::Dir dir(cachePath);
1516         if (!dir.remove(true /*recursive*/)) {
1517             std::cerr << "Errors occurred trying to remove " << cachePath << std::endl;
1518         }
1519     }
1520 #endif
1521     return EXIT_SUCCESS;
1522 }
1523