1 #include "storage/serialization.hpp"
2 #include "storage/shared_memory.hpp"
3 #include "storage/shared_monitor.hpp"
4 #include "storage/storage.hpp"
5 
6 #include "osrm/exception.hpp"
7 #include "util/log.hpp"
8 #include "util/meminfo.hpp"
9 #include "util/typedefs.hpp"
10 #include "util/version.hpp"
11 
12 #include <boost/filesystem.hpp>
13 #include <boost/program_options.hpp>
14 
15 #include <csignal>
16 #include <cstdlib>
17 
18 using namespace osrm;
19 
removeLocks()20 void removeLocks() { storage::SharedMonitor<storage::SharedRegionRegister>::remove(); }
21 
deleteRegion(const storage::SharedRegionRegister::ShmKey key)22 void deleteRegion(const storage::SharedRegionRegister::ShmKey key)
23 {
24     if (storage::SharedMemory::RegionExists(key) && !storage::SharedMemory::Remove(key))
25     {
26         util::Log(logWARNING) << "could not delete shared memory region " << static_cast<int>(key);
27     }
28 }
29 
listRegions(bool show_blocks)30 void listRegions(bool show_blocks)
31 {
32     osrm::util::Log() << "name\tshm key\ttimestamp\tsize";
33     if (!storage::SharedMonitor<storage::SharedRegionRegister>::exists())
34     {
35         return;
36     }
37     storage::SharedMonitor<storage::SharedRegionRegister> monitor;
38     std::vector<std::string> names;
39     const auto &shared_register = monitor.data();
40     shared_register.List(std::back_inserter(names));
41     for (const auto &name : names)
42     {
43         auto id = shared_register.Find(name);
44         auto region = shared_register.GetRegion(id);
45         auto shm = osrm::storage::makeSharedMemory(region.shm_key);
46         osrm::util::Log() << name << "\t" << static_cast<int>(region.shm_key) << "\t"
47                           << region.timestamp << "\t" << shm->Size();
48 
49         if (show_blocks)
50         {
51             using namespace storage;
52             auto memory = makeSharedMemory(region.shm_key);
53             io::BufferReader reader(reinterpret_cast<char *>(memory->Ptr()), memory->Size());
54 
55             std::unique_ptr<BaseDataLayout> layout = std::make_unique<ContiguousDataLayout>();
56             serialization::read(reader, *layout);
57 
58             std::vector<std::string> block_names;
59             layout->List("", std::back_inserter(block_names));
60             for (auto &name : block_names)
61             {
62                 osrm::util::Log() << "  " << name << " " << layout->GetBlockSize(name);
63             }
64         }
65     }
66 }
67 
springClean()68 void springClean()
69 {
70     osrm::util::Log() << "Releasing all locks";
71     osrm::util::Log() << "ATTENTION! BE CAREFUL!";
72     osrm::util::Log() << "----------------------";
73     osrm::util::Log() << "This tool may put osrm-routed into an undefined state!";
74     osrm::util::Log() << "Type 'Y' to acknowledge that you know what your are doing.";
75     osrm::util::Log() << "\n\nDo you want to purge all shared memory allocated "
76                       << "by osrm-datastore? [type 'Y' to confirm]";
77 
78     const auto letter = getchar();
79     if (letter != 'Y')
80     {
81         osrm::util::Log() << "aborted.";
82     }
83     else
84     {
85         for (auto key : util::irange<storage::SharedRegionRegister::RegionID>(
86                  0, storage::SharedRegionRegister::MAX_SHM_KEYS))
87         {
88             deleteRegion(key);
89         }
90         removeLocks();
91     }
92 }
93 
94 // generate boost::program_options object for the routing part
generateDataStoreOptions(const int argc,const char * argv[],std::string & verbosity,boost::filesystem::path & base_path,int & max_wait,std::string & dataset_name,bool & list_datasets,bool & list_blocks,bool & only_metric)95 bool generateDataStoreOptions(const int argc,
96                               const char *argv[],
97                               std::string &verbosity,
98                               boost::filesystem::path &base_path,
99                               int &max_wait,
100                               std::string &dataset_name,
101                               bool &list_datasets,
102                               bool &list_blocks,
103                               bool &only_metric)
104 {
105     // declare a group of options that will be allowed only on command line
106     boost::program_options::options_description generic_options("Options");
107     generic_options.add_options()            //
108         ("version,v", "Show version")        //
109         ("help,h", "Show this help message") //
110         ("verbosity,l",
111          boost::program_options::value<std::string>(&verbosity)->default_value("INFO"),
112          std::string("Log verbosity level: " + util::LogPolicy::GetLevels()).c_str()) //
113         ("remove-locks,r", "Remove locks")                                            //
114         ("spring-clean,s", "Spring-cleaning all shared memory regions");
115 
116     // declare a group of options that will be allowed both on command line
117     // as well as in a config file
118     boost::program_options::options_description config_options("Configuration");
119     config_options.add_options() //
120         ("max-wait",
121          boost::program_options::value<int>(&max_wait)->default_value(-1),
122          "Maximum number of seconds to wait on a running data update "
123          "before aquiring the lock by force.") //
124         ("dataset-name",
125          boost::program_options::value<std::string>(&dataset_name)->default_value(""),
126          "Name of the dataset to load into memory. This allows having multiple datasets in memory "
127          "at the same time.") //
128         ("list",
129          boost::program_options::value<bool>(&list_datasets)
130              ->default_value(false)
131              ->implicit_value(true),
132          "List all OSRM datasets currently in memory") //
133         ("list-blocks",
134          boost::program_options::value<bool>(&list_blocks)
135              ->default_value(false)
136              ->implicit_value(true),
137          "List all OSRM datasets currently in memory")(
138             "only-metric",
139             boost::program_options::value<bool>(&only_metric)
140                 ->default_value(false)
141                 ->implicit_value(true),
142             "Only reload the metric data without updating the full dataset. This is an "
143             "optimization "
144             "for traffic updates.");
145 
146     // hidden options, will be allowed on command line but will not be shown to the user
147     boost::program_options::options_description hidden_options("Hidden options");
148     hidden_options.add_options()("base,b",
149                                  boost::program_options::value<boost::filesystem::path>(&base_path),
150                                  "base path to .osrm file");
151 
152     // positional option
153     boost::program_options::positional_options_description positional_options;
154     positional_options.add("base", 1);
155 
156     // combine above options for parsing
157     boost::program_options::options_description cmdline_options;
158     cmdline_options.add(generic_options).add(config_options).add(hidden_options);
159 
160     const auto *executable = argv[0];
161     boost::program_options::options_description visible_options(
162         boost::filesystem::path(executable).filename().string() + " [<options>] <configuration>");
163     visible_options.add(generic_options).add(config_options);
164 
165     // print help options if no infile is specified
166     if (argc < 2)
167     {
168         util::Log() << visible_options;
169         return false;
170     }
171 
172     // parse command line options
173     boost::program_options::variables_map option_variables;
174 
175     try
176     {
177         boost::program_options::store(boost::program_options::command_line_parser(argc, argv)
178                                           .options(cmdline_options)
179                                           .positional(positional_options)
180                                           .run(),
181                                       option_variables);
182     }
183     catch (const boost::program_options::error &e)
184     {
185         util::Log(logERROR) << e.what();
186         return false;
187     }
188 
189     if (option_variables.count("version"))
190     {
191         util::Log() << OSRM_VERSION;
192         return false;
193     }
194 
195     if (option_variables.count("help"))
196     {
197         util::Log() << visible_options;
198         return false;
199     }
200 
201     if (option_variables.count("remove-locks"))
202     {
203         removeLocks();
204         return false;
205     }
206 
207     if (option_variables.count("spring-clean"))
208     {
209         springClean();
210         return false;
211     }
212 
213     boost::program_options::notify(option_variables);
214 
215     return true;
216 }
217 
CleanupSharedBarriers(int signum)218 [[noreturn]] void CleanupSharedBarriers(int signum)
219 { // Here the lock state of named mutexes is unknown, make a hard cleanup
220     removeLocks();
221     std::_Exit(128 + signum);
222 }
223 
main(const int argc,const char * argv[])224 int main(const int argc, const char *argv[])
225 try
226 {
227     int signals[] = {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE};
228     for (auto sig : signals)
229     {
230         std::signal(sig, CleanupSharedBarriers);
231     }
232 
233     util::LogPolicy::GetInstance().Unmute();
234 
235     std::string verbosity;
236     boost::filesystem::path base_path;
237     int max_wait = -1;
238     std::string dataset_name;
239     bool list_datasets = false;
240     bool list_blocks = false;
241     bool only_metric = false;
242     if (!generateDataStoreOptions(argc,
243                                   argv,
244                                   verbosity,
245                                   base_path,
246                                   max_wait,
247                                   dataset_name,
248                                   list_datasets,
249                                   list_blocks,
250                                   only_metric))
251     {
252         return EXIT_SUCCESS;
253     }
254 
255     util::LogPolicy::GetInstance().SetLevel(verbosity);
256 
257     if (list_datasets || list_blocks)
258     {
259         listRegions(list_blocks);
260         return EXIT_SUCCESS;
261     }
262 
263     storage::StorageConfig config(base_path);
264     if (!config.IsValid())
265     {
266         util::Log(logERROR) << "Config contains invalid file paths. Exiting!";
267         return EXIT_FAILURE;
268     }
269     storage::Storage storage(std::move(config));
270 
271     return storage.Run(max_wait, dataset_name, only_metric);
272 }
273 catch (const osrm::RuntimeError &e)
274 {
275     util::Log(logERROR) << e.what();
276     return e.GetCode();
277 }
278 catch (const std::bad_alloc &e)
279 {
280     util::DumpMemoryStats();
281     util::Log(logERROR) << "[exception] " << e.what();
282     util::Log(logERROR) << "Please provide more memory or disable locking the virtual "
283                            "address space (note: this makes OSRM swap, i.e. slow)";
284     return EXIT_FAILURE;
285 }
286