]>
Commit | Line | Data |
---|---|---|
2097c09a | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
c63a73d1 | 3 | // Distributed under the MIT software license, see the accompanying |
3a25a2b9 | 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
36949554 | 5 | |
4c61ba40 WL |
6 | #if defined(HAVE_CONFIG_H) |
7 | #include "config/bitcoin-config.h" | |
8 | #endif | |
9 | ||
51ed9ec9 BD |
10 | #include "util.h" |
11 | ||
84ce18ca | 12 | #include "chainparamsbase.h" |
6354935c | 13 | #include "random.h" |
ad49c256 | 14 | #include "serialize.h" |
51ed9ec9 | 15 | #include "sync.h" |
ad49c256 WL |
16 | #include "utilstrencodings.h" |
17 | #include "utiltime.h" | |
7ce53ed0 | 18 | #include "komodo_defs.h" |
51ed9ec9 | 19 | |
51ed9ec9 | 20 | #include <stdarg.h> |
51ed9ec9 | 21 | |
5207f33f PK |
22 | #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) |
23 | #include <pthread.h> | |
24 | #include <pthread_np.h> | |
25 | #endif | |
26 | ||
9cb1ec9c | 27 | #ifndef _WIN32 |
288fdc09 | 28 | // for posix_fallocate |
76c49c41 | 29 | #ifdef __linux__ |
51ed9ec9 BD |
30 | |
31 | #ifdef _POSIX_C_SOURCE | |
32 | #undef _POSIX_C_SOURCE | |
33 | #endif | |
34 | ||
288fdc09 | 35 | #define _POSIX_C_SOURCE 200112L |
51ed9ec9 | 36 | |
76c49c41 | 37 | #endif // __linux__ |
51ed9ec9 | 38 | |
b94595bb | 39 | #include <algorithm> |
288fdc09 | 40 | #include <fcntl.h> |
ba29a559 | 41 | #include <sys/resource.h> |
51ed9ec9 | 42 | #include <sys/stat.h> |
1f29d399 | 43 | |
51ed9ec9 | 44 | #else |
ed6d0b5f | 45 | |
ed6d0b5f PW |
46 | #ifdef _MSC_VER |
47 | #pragma warning(disable:4786) | |
48 | #pragma warning(disable:4804) | |
49 | #pragma warning(disable:4805) | |
50 | #pragma warning(disable:4717) | |
51 | #endif | |
51ed9ec9 | 52 | |
ed6d0b5f PW |
53 | #ifdef _WIN32_WINNT |
54 | #undef _WIN32_WINNT | |
55 | #endif | |
56 | #define _WIN32_WINNT 0x0501 | |
51ed9ec9 | 57 | |
ed6d0b5f PW |
58 | #ifdef _WIN32_IE |
59 | #undef _WIN32_IE | |
60 | #endif | |
234db30d | 61 | #define _WIN32_IE 0x0501 |
51ed9ec9 | 62 | |
ed6d0b5f PW |
63 | #define WIN32_LEAN_AND_MEAN 1 |
64 | #ifndef NOMINMAX | |
65 | #define NOMINMAX | |
66 | #endif | |
51ed9ec9 | 67 | |
5f986195 | 68 | #include <io.h> /* for _commit */ |
51ed9ec9 | 69 | #include <shlobj.h> |
ed6d0b5f | 70 | #endif |
2097c09a | 71 | |
4c61ba40 WL |
72 | #ifdef HAVE_SYS_PRCTL_H |
73 | #include <sys/prctl.h> | |
74 | #endif | |
75 | ||
51ed9ec9 BD |
76 | #include <boost/algorithm/string/case_conv.hpp> // for to_lower() |
77 | #include <boost/algorithm/string/join.hpp> | |
78 | #include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() | |
79 | #include <boost/filesystem.hpp> | |
80 | #include <boost/filesystem/fstream.hpp> | |
81 | #include <boost/foreach.hpp> | |
82 | #include <boost/program_options/detail/config_file.hpp> | |
83 | #include <boost/program_options/parsers.hpp> | |
ad49c256 | 84 | #include <boost/thread.hpp> |
51ed9ec9 | 85 | #include <openssl/crypto.h> |
25314386 | 86 | #include <openssl/conf.h> |
51ed9ec9 BD |
87 | |
88 | // Work around clang compilation problem in Boost 1.46: | |
89 | // /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup | |
90 | // See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options | |
91 | // http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION | |
92 | namespace boost { | |
e10dcf27 | 93 | |
51ed9ec9 BD |
94 | namespace program_options { |
95 | std::string to_internal(const std::string&); | |
96 | } | |
51ed9ec9 | 97 | |
e10dcf27 | 98 | } // namespace boost |
51ed9ec9 | 99 | |
2097c09a WL |
100 | using namespace std; |
101 | ||
102 | map<string, string> mapArgs; | |
103 | map<string, vector<string> > mapMultiArgs; | |
104 | bool fDebug = false; | |
105 | bool fPrintToConsole = false; | |
9e9056cd | 106 | bool fPrintToDebugLog = true; |
2097c09a WL |
107 | bool fDaemon = false; |
108 | bool fServer = false; | |
2097c09a | 109 | string strMiscWarning; |
7a4e0e09 CD |
110 | bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; |
111 | bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; | |
112 | bool fLogIPs = DEFAULT_LOGIPS; | |
bf673640 | 113 | std::atomic<bool> fReopenDebugLog(false); |
b74dcb3b | 114 | CTranslationInterface translationInterface; |
2097c09a | 115 | |
c63a73d1 | 116 | /** Init OpenSSL library multithreading support */ |
7f3ccb59 | 117 | static CCriticalSection** ppmutexOpenSSL; |
60457d3c | 118 | void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS |
2097c09a | 119 | { |
7f3ccb59 PW |
120 | if (mode & CRYPTO_LOCK) { |
121 | ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); | |
122 | } else { | |
123 | LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); | |
124 | } | |
2097c09a WL |
125 | } |
126 | ||
127 | // Init | |
128 | class CInit | |
129 | { | |
130 | public: | |
131 | CInit() | |
132 | { | |
a7f82808 | 133 | // Init OpenSSL library multithreading support |
7f3ccb59 | 134 | ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*)); |
2097c09a | 135 | for (int i = 0; i < CRYPTO_num_locks(); i++) |
7f3ccb59 | 136 | ppmutexOpenSSL[i] = new CCriticalSection(); |
2097c09a WL |
137 | CRYPTO_set_locking_callback(locking_callback); |
138 | ||
25314386 CF |
139 | // OpenSSL can optionally load a config file which lists optional loadable modules and engines. |
140 | // We don't use them so we don't require the config. However some of our libs may call functions | |
141 | // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing | |
142 | // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be | |
143 | // that the config appears to have been loaded and there are no modules/engines available. | |
144 | OPENSSL_no_config(); | |
2097c09a WL |
145 | } |
146 | ~CInit() | |
147 | { | |
a7f82808 | 148 | // Shutdown OpenSSL library multithreading support |
2097c09a WL |
149 | CRYPTO_set_locking_callback(NULL); |
150 | for (int i = 0; i < CRYPTO_num_locks(); i++) | |
151 | delete ppmutexOpenSSL[i]; | |
152 | OPENSSL_free(ppmutexOpenSSL); | |
153 | } | |
154 | } | |
155 | instance_of_cinit; | |
156 | ||
c63a73d1 MF |
157 | /** |
158 | * LogPrintf() has been broken a couple of times now | |
159 | * by well-meaning people adding mutexes in the most straightforward way. | |
160 | * It breaks because it may be called by global destructors during shutdown. | |
161 | * Since the order of destruction of static/global objects is undefined, | |
162 | * defining a mutex as a global object doesn't work (the mutex gets | |
163 | * destroyed, and then some later destructor calls OutputDebugStringF, | |
164 | * maybe indirectly, and you get a core dump at shutdown trying to lock | |
165 | * the mutex). | |
166 | */ | |
2097c09a | 167 | |
ee337423 | 168 | static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; |
8550bcfe | 169 | |
c63a73d1 | 170 | /** |
8550bcfe AW |
171 | * We use boost::call_once() to make sure mutexDebugLog and |
172 | * vMsgsBeforeOpenLog are initialized in a thread-safe manner. | |
173 | * | |
174 | * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog | |
175 | * are leaked on exit. This is ugly, but will be cleaned up by | |
176 | * the OS/libc. When the shutdown sequence is fully audited and | |
177 | * tested, explicit destruction of these objects can be implemented. | |
c63a73d1 | 178 | */ |
ee337423 GA |
179 | static FILE* fileout = NULL; |
180 | static boost::mutex* mutexDebugLog = NULL; | |
8550bcfe AW |
181 | static list<string> *vMsgsBeforeOpenLog; |
182 | ||
183 | static int FileWriteStr(const std::string &str, FILE *fp) | |
184 | { | |
185 | return fwrite(str.data(), 1, str.size(), fp); | |
186 | } | |
ee337423 GA |
187 | |
188 | static void DebugPrintInit() | |
2097c09a | 189 | { |
ee337423 | 190 | assert(mutexDebugLog == NULL); |
8550bcfe AW |
191 | mutexDebugLog = new boost::mutex(); |
192 | vMsgsBeforeOpenLog = new list<string>; | |
193 | } | |
ee337423 | 194 | |
8550bcfe AW |
195 | void OpenDebugLog() |
196 | { | |
197 | boost::call_once(&DebugPrintInit, debugPrintInitFlag); | |
198 | boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); | |
199 | ||
200 | assert(fileout == NULL); | |
201 | assert(vMsgsBeforeOpenLog); | |
ee337423 GA |
202 | boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; |
203 | fileout = fopen(pathDebug.string().c_str(), "a"); | |
204 | if (fileout) setbuf(fileout, NULL); // unbuffered | |
205 | ||
8550bcfe AW |
206 | // dump buffered messages from before we opened the log |
207 | while (!vMsgsBeforeOpenLog->empty()) { | |
208 | FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); | |
209 | vMsgsBeforeOpenLog->pop_front(); | |
210 | } | |
211 | ||
212 | delete vMsgsBeforeOpenLog; | |
213 | vMsgsBeforeOpenLog = NULL; | |
ee337423 GA |
214 | } |
215 | ||
b77dfdc9 | 216 | bool LogAcceptCategory(const char* category) |
ee337423 | 217 | { |
e51321fb GA |
218 | if (category != NULL) |
219 | { | |
3b570559 | 220 | if (!fDebug) |
b77dfdc9 | 221 | return false; |
3b570559 | 222 | |
0b238b27 GA |
223 | // Give each thread quick access to -debug settings. |
224 | // This helps prevent issues debugging global destructors, | |
225 | // where mapMultiArgs might be deleted before another | |
226 | // global destructor calls LogPrint() | |
227 | static boost::thread_specific_ptr<set<string> > ptrCategory; | |
228 | if (ptrCategory.get() == NULL) | |
3b570559 | 229 | { |
0b238b27 GA |
230 | const vector<string>& categories = mapMultiArgs["-debug"]; |
231 | ptrCategory.reset(new set<string>(categories.begin(), categories.end())); | |
232 | // thread_specific_ptr automatically deletes the set when the thread ends. | |
3b570559 | 233 | } |
0b238b27 GA |
234 | const set<string>& setCategories = *ptrCategory.get(); |
235 | ||
236 | // if not debugging everything and not debugging specific category, LogPrint does nothing. | |
237 | if (setCategories.count(string("")) == 0 && | |
1d2db276 | 238 | setCategories.count(string("1")) == 0 && |
0b238b27 | 239 | setCategories.count(string(category)) == 0) |
b77dfdc9 | 240 | return false; |
e51321fb | 241 | } |
b77dfdc9 WL |
242 | return true; |
243 | } | |
e51321fb | 244 | |
8550bcfe AW |
245 | /** |
246 | * fStartedNewLine is a state variable held by the calling context that will | |
247 | * suppress printing of the timestamp when multiple calls are made that don't | |
248 | * end in a newline. Initialize it to true, and hold it, in the calling context. | |
249 | */ | |
250 | static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine) | |
251 | { | |
252 | string strStamped; | |
253 | ||
254 | if (!fLogTimestamps) | |
255 | return str; | |
256 | ||
257 | if (*fStartedNewLine) | |
258 | strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()) + ' ' + str; | |
259 | else | |
260 | strStamped = str; | |
261 | ||
262 | if (!str.empty() && str[str.size()-1] == '\n') | |
263 | *fStartedNewLine = true; | |
264 | else | |
265 | *fStartedNewLine = false; | |
266 | ||
267 | return strStamped; | |
268 | } | |
269 | ||
b77dfdc9 WL |
270 | int LogPrintStr(const std::string &str) |
271 | { | |
ee337423 | 272 | int ret = 0; // Returns total number of characters written |
8550bcfe | 273 | static bool fStartedNewLine = true; |
2097c09a WL |
274 | if (fPrintToConsole) |
275 | { | |
276 | // print to console | |
b77dfdc9 | 277 | ret = fwrite(str.data(), 1, str.size(), stdout); |
e982b574 | 278 | fflush(stdout); |
2097c09a | 279 | } |
8550bcfe | 280 | else if (fPrintToDebugLog) |
2097c09a | 281 | { |
ee337423 | 282 | boost::call_once(&DebugPrintInit, debugPrintInitFlag); |
ee337423 GA |
283 | boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); |
284 | ||
8550bcfe | 285 | string strTimestamped = LogTimestampStr(str, &fStartedNewLine); |
2097c09a | 286 | |
8550bcfe AW |
287 | // buffer if we haven't opened the log yet |
288 | if (fileout == NULL) { | |
289 | assert(vMsgsBeforeOpenLog); | |
290 | ret = strTimestamped.length(); | |
291 | vMsgsBeforeOpenLog->push_back(strTimestamped); | |
292 | } | |
ee337423 | 293 | else |
8550bcfe AW |
294 | { |
295 | // reopen the log file, if requested | |
296 | if (fReopenDebugLog) { | |
297 | fReopenDebugLog = false; | |
298 | boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; | |
299 | if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) | |
300 | setbuf(fileout, NULL); // unbuffered | |
301 | } | |
302 | ||
303 | ret = FileWriteStr(strTimestamped, fileout); | |
304 | } | |
2097c09a | 305 | } |
2097c09a WL |
306 | return ret; |
307 | } | |
308 | ||
d64e124c CM |
309 | static void InterpretNegativeSetting(string name, map<string, string>& mapSettingsRet) |
310 | { | |
311 | // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set | |
312 | if (name.find("-no") == 0) | |
313 | { | |
314 | std::string positive("-"); | |
315 | positive.append(name.begin()+3, name.end()); | |
316 | if (mapSettingsRet.count(positive) == 0) | |
317 | { | |
3260b4c0 | 318 | bool value = !GetBoolArg(name, false); |
d64e124c CM |
319 | mapSettingsRet[positive] = (value ? "1" : "0"); |
320 | } | |
321 | } | |
322 | } | |
323 | ||
3e468840 | 324 | void ParseParameters(int argc, const char* const argv[]) |
2097c09a WL |
325 | { |
326 | mapArgs.clear(); | |
327 | mapMultiArgs.clear(); | |
71aaff39 | 328 | |
2097c09a WL |
329 | for (int i = 1; i < argc; i++) |
330 | { | |
6032e4f4 WL |
331 | std::string str(argv[i]); |
332 | std::string strValue; | |
333 | size_t is_index = str.find('='); | |
334 | if (is_index != std::string::npos) | |
2097c09a | 335 | { |
6032e4f4 WL |
336 | strValue = str.substr(is_index+1); |
337 | str = str.substr(0, is_index); | |
2097c09a | 338 | } |
9cb1ec9c | 339 | #ifdef _WIN32 |
6032e4f4 WL |
340 | boost::to_lower(str); |
341 | if (boost::algorithm::starts_with(str, "/")) | |
342 | str = "-" + str.substr(1); | |
343 | #endif | |
71aaff39 | 344 | |
6032e4f4 | 345 | if (str[0] != '-') |
2097c09a | 346 | break; |
3ad9f8a7 | 347 | |
71aaff39 KZ |
348 | // Interpret --foo as -foo. |
349 | // If both --foo and -foo are set, the last takes effect. | |
350 | if (str.length() > 1 && str[1] == '-') | |
351 | str = str.substr(1); | |
352 | ||
6032e4f4 WL |
353 | mapArgs[str] = strValue; |
354 | mapMultiArgs[str].push_back(strValue); | |
2097c09a | 355 | } |
3ad9f8a7 GA |
356 | |
357 | // New 0.6 features: | |
358 | BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs) | |
359 | { | |
d64e124c | 360 | // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set |
71aaff39 | 361 | InterpretNegativeSetting(entry.first, mapArgs); |
3ad9f8a7 | 362 | } |
2097c09a WL |
363 | } |
364 | ||
3ae07355 GA |
365 | std::string GetArg(const std::string& strArg, const std::string& strDefault) |
366 | { | |
367 | if (mapArgs.count(strArg)) | |
368 | return mapArgs[strArg]; | |
369 | return strDefault; | |
370 | } | |
371 | ||
51ed9ec9 | 372 | int64_t GetArg(const std::string& strArg, int64_t nDefault) |
3ae07355 GA |
373 | { |
374 | if (mapArgs.count(strArg)) | |
375 | return atoi64(mapArgs[strArg]); | |
376 | return nDefault; | |
377 | } | |
378 | ||
379 | bool GetBoolArg(const std::string& strArg, bool fDefault) | |
380 | { | |
381 | if (mapArgs.count(strArg)) | |
382 | { | |
383 | if (mapArgs[strArg].empty()) | |
384 | return true; | |
385 | return (atoi(mapArgs[strArg]) != 0); | |
386 | } | |
387 | return fDefault; | |
388 | } | |
389 | ||
0fcf91ea GA |
390 | bool SoftSetArg(const std::string& strArg, const std::string& strValue) |
391 | { | |
392 | if (mapArgs.count(strArg)) | |
393 | return false; | |
394 | mapArgs[strArg] = strValue; | |
395 | return true; | |
396 | } | |
397 | ||
7bf8b7c2 | 398 | bool SoftSetBoolArg(const std::string& strArg, bool fValue) |
0fcf91ea GA |
399 | { |
400 | if (fValue) | |
401 | return SoftSetArg(strArg, std::string("1")); | |
402 | else | |
403 | return SoftSetArg(strArg, std::string("0")); | |
404 | } | |
405 | ||
1fdb9fa3 LV |
406 | static const int screenWidth = 79; |
407 | static const int optIndent = 2; | |
408 | static const int msgIndent = 7; | |
409 | ||
410 | std::string HelpMessageGroup(const std::string &message) { | |
411 | return std::string(message) + std::string("\n\n"); | |
412 | } | |
413 | ||
414 | std::string HelpMessageOpt(const std::string &option, const std::string &message) { | |
415 | return std::string(optIndent,' ') + std::string(option) + | |
416 | std::string("\n") + std::string(msgIndent,' ') + | |
417 | FormatParagraph(message, screenWidth - msgIndent, msgIndent) + | |
418 | std::string("\n\n"); | |
419 | } | |
420 | ||
27df4123 | 421 | static std::string FormatException(const std::exception* pex, const char* pszThread) |
2097c09a | 422 | { |
9cb1ec9c | 423 | #ifdef _WIN32 |
3e468840 | 424 | char pszModule[MAX_PATH] = ""; |
2097c09a WL |
425 | GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); |
426 | #else | |
5166804f | 427 | const char* pszModule = "Komodo"; |
2097c09a WL |
428 | #endif |
429 | if (pex) | |
29b79e4c | 430 | return strprintf( |
2097c09a WL |
431 | "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); |
432 | else | |
29b79e4c | 433 | return strprintf( |
2097c09a WL |
434 | "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); |
435 | } | |
436 | ||
27df4123 | 437 | void PrintExceptionContinue(const std::exception* pex, const char* pszThread) |
2097c09a | 438 | { |
29b79e4c | 439 | std::string message = FormatException(pex, pszThread); |
7d9d134b | 440 | LogPrintf("\n\n************************\n%s\n", message); |
29b79e4c WL |
441 | fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); |
442 | strMiscWarning = message; | |
2097c09a WL |
443 | } |
444 | ||
7c130297 | 445 | extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; |
ec3bf0d8 | 446 | //int64_t MAX_MONEY = 200000000 * 100000000LL; |
f71f89bb | 447 | |
ee12c3d6 | 448 | boost::filesystem::path GetDefaultDataDir() |
2097c09a | 449 | { |
ee12c3d6 | 450 | namespace fs = boost::filesystem; |
7c130297 | 451 | char symbol[KOMODO_ASSETCHAIN_MAXLEN]; |
dadaab0a | 452 | if ( ASSETCHAINS_SYMBOL[0] != 0 ) |
ab918767 | 453 | strcpy(symbol,ASSETCHAINS_SYMBOL); |
454 | else symbol[0] = 0; | |
8a183335 TH |
455 | // Windows < Vista: C:\Documents and Settings\Username\Application Data\Zcash |
456 | // Windows >= Vista: C:\Users\Username\AppData\Roaming\Zcash | |
457 | // Mac: ~/Library/Application Support/Zcash | |
458 | // Unix: ~/.zcash | |
9cb1ec9c | 459 | #ifdef _WIN32 |
2097c09a | 460 | // Windows |
dadaab0a | 461 | if ( symbol[0] == 0 ) |
892dc7ac | 462 | return GetSpecialFolderPath(CSIDL_APPDATA) / "Komodo"; |
dadaab0a | 463 | else return GetSpecialFolderPath(CSIDL_APPDATA) / "Komodo" / symbol; |
2097c09a | 464 | #else |
ee12c3d6 | 465 | fs::path pathRet; |
2097c09a WL |
466 | char* pszHome = getenv("HOME"); |
467 | if (pszHome == NULL || strlen(pszHome) == 0) | |
ee12c3d6 PW |
468 | pathRet = fs::path("/"); |
469 | else | |
470 | pathRet = fs::path(pszHome); | |
6853e627 | 471 | #ifdef MAC_OSX |
2097c09a | 472 | // Mac |
940e22fd | 473 | pathRet /= "Library/Application Support"; |
2b7709dc | 474 | TryCreateDirectory(pathRet); |
dadaab0a | 475 | if ( symbol[0] == 0 ) |
892dc7ac | 476 | return pathRet / "Komodo"; |
477 | else | |
478 | { | |
479 | pathRet /= "Komodo"; | |
480 | TryCreateDirectory(pathRet); | |
dadaab0a | 481 | return pathRet / symbol; |
892dc7ac | 482 | } |
2097c09a WL |
483 | #else |
484 | // Unix | |
dadaab0a | 485 | if ( symbol[0] == 0 ) |
892dc7ac | 486 | return pathRet / ".komodo"; |
dadaab0a | 487 | else return pathRet / ".komodo" / symbol; |
2097c09a WL |
488 | #endif |
489 | #endif | |
490 | } | |
491 | ||
ebdb9ff6 | 492 | static boost::filesystem::path pathCached; |
493 | static boost::filesystem::path pathCachedNetSpecific; | |
4f1c3798 | 494 | static boost::filesystem::path zc_paramsPathCached; |
b94595bb GA |
495 | static CCriticalSection csPathCached; |
496 | ||
4f1c3798 SB |
497 | static boost::filesystem::path ZC_GetBaseParamsDir() |
498 | { | |
499 | // Copied from GetDefaultDataDir and adapter for zcash params. | |
500 | ||
501 | namespace fs = boost::filesystem; | |
502 | // Windows < Vista: C:\Documents and Settings\Username\Application Data\ZcashParams | |
503 | // Windows >= Vista: C:\Users\Username\AppData\Roaming\ZcashParams | |
504 | // Mac: ~/Library/Application Support/ZcashParams | |
505 | // Unix: ~/.zcash-params | |
9cb1ec9c | 506 | #ifdef _WIN32 |
4f1c3798 SB |
507 | // Windows |
508 | return GetSpecialFolderPath(CSIDL_APPDATA) / "ZcashParams"; | |
509 | #else | |
510 | fs::path pathRet; | |
511 | char* pszHome = getenv("HOME"); | |
512 | if (pszHome == NULL || strlen(pszHome) == 0) | |
513 | pathRet = fs::path("/"); | |
514 | else | |
515 | pathRet = fs::path(pszHome); | |
516 | #ifdef MAC_OSX | |
517 | // Mac | |
518 | pathRet /= "Library/Application Support"; | |
519 | TryCreateDirectory(pathRet); | |
520 | return pathRet / "ZcashParams"; | |
521 | #else | |
522 | // Unix | |
523 | return pathRet / ".zcash-params"; | |
524 | #endif | |
525 | #endif | |
526 | } | |
527 | ||
528 | const boost::filesystem::path &ZC_GetParamsDir() | |
529 | { | |
530 | namespace fs = boost::filesystem; | |
531 | ||
532 | LOCK(csPathCached); // Reuse the same lock as upstream. | |
533 | ||
534 | fs::path &path = zc_paramsPathCached; | |
535 | ||
536 | // This can be called during exceptions by LogPrintf(), so we cache the | |
537 | // value so we don't have to do memory allocations after that. | |
538 | if (!path.empty()) | |
539 | return path; | |
540 | ||
541 | path = ZC_GetBaseParamsDir(); | |
4f1c3798 SB |
542 | |
543 | return path; | |
544 | } | |
545 | ||
9064d73b S |
546 | // Return the user specified export directory. Create directory if it doesn't exist. |
547 | // If user did not set option, return an empty path. | |
548 | // If there is a filesystem problem, throw an exception. | |
549 | const boost::filesystem::path GetExportDir() | |
550 | { | |
551 | namespace fs = boost::filesystem; | |
552 | fs::path path; | |
553 | if (mapArgs.count("-exportdir")) { | |
554 | path = fs::system_complete(mapArgs["-exportdir"]); | |
555 | if (fs::exists(path) && !fs::is_directory(path)) { | |
556 | throw std::runtime_error(strprintf("The -exportdir '%s' already exists and is not a directory", path.string())); | |
557 | } | |
558 | if (!fs::exists(path) && !fs::create_directories(path)) { | |
559 | throw std::runtime_error(strprintf("Failed to create directory at -exportdir '%s'", path.string())); | |
560 | } | |
561 | } | |
562 | return path; | |
563 | } | |
564 | ||
565 | ||
ee12c3d6 | 566 | const boost::filesystem::path &GetDataDir(bool fNetSpecific) |
2097c09a | 567 | { |
ee12c3d6 PW |
568 | namespace fs = boost::filesystem; |
569 | ||
b94595bb GA |
570 | LOCK(csPathCached); |
571 | ||
ebdb9ff6 | 572 | fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; |
ee12c3d6 | 573 | |
faaeae1e | 574 | // This can be called during exceptions by LogPrintf(), so we cache the |
ee12c3d6 | 575 | // value so we don't have to do memory allocations after that. |
b94595bb | 576 | if (!path.empty()) |
ee12c3d6 PW |
577 | return path; |
578 | ||
ee12c3d6 | 579 | if (mapArgs.count("-datadir")) { |
f4203de3 PW |
580 | path = fs::system_complete(mapArgs["-datadir"]); |
581 | if (!fs::is_directory(path)) { | |
582 | path = ""; | |
583 | return path; | |
584 | } | |
ee12c3d6 PW |
585 | } else { |
586 | path = GetDefaultDataDir(); | |
2097c09a | 587 | } |
0e4b3175 | 588 | if (fNetSpecific) |
84ce18ca | 589 | path /= BaseParams().DataDir(); |
2097c09a | 590 | |
3e9c8bab | 591 | fs::create_directories(path); |
c1428363 | 592 | //std::string assetpath = path + "/assets"; |
593 | //boost::filesystem::create_directory(assetpath); | |
ee12c3d6 | 594 | return path; |
2097c09a WL |
595 | } |
596 | ||
b94595bb GA |
597 | void ClearDatadirCache() |
598 | { | |
ebdb9ff6 | 599 | pathCached = boost::filesystem::path(); |
600 | pathCachedNetSpecific = boost::filesystem::path(); | |
b94595bb GA |
601 | } |
602 | ||
ee12c3d6 | 603 | boost::filesystem::path GetConfigFile() |
2097c09a | 604 | { |
323832c3 | 605 | char confname[512]; |
606 | if ( ASSETCHAINS_SYMBOL[0] != 0 ) | |
607 | sprintf(confname,"%s.conf",ASSETCHAINS_SYMBOL); | |
923f5177 | 608 | else |
609 | { | |
610 | #ifdef __APPLE__ | |
611 | strcpy(confname,"Komodo.conf"); | |
612 | #else | |
613 | strcpy(confname,"komodo.conf"); | |
614 | #endif | |
615 | } | |
323832c3 | 616 | boost::filesystem::path pathConfigFile(GetArg("-conf",confname)); |
ac14bcc1 PK |
617 | if (!pathConfigFile.is_complete()) |
618 | pathConfigFile = GetDataDir(false) / pathConfigFile; | |
619 | ||
ee12c3d6 | 620 | return pathConfigFile; |
2097c09a WL |
621 | } |
622 | ||
f4203de3 | 623 | void ReadConfigFile(map<string, string>& mapSettingsRet, |
2097c09a WL |
624 | map<string, vector<string> >& mapMultiSettingsRet) |
625 | { | |
3e468840 | 626 | boost::filesystem::ifstream streamConfig(GetConfigFile()); |
2097c09a | 627 | if (!streamConfig.good()) |
d87f00c4 | 628 | throw missing_zcash_conf(); |
2097c09a WL |
629 | |
630 | set<string> setOptions; | |
631 | setOptions.insert("*"); | |
ee12c3d6 | 632 | |
3e468840 | 633 | for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) |
2097c09a | 634 | { |
5166804f | 635 | // Don't overwrite existing settings so command line settings override komodo.conf |
2097c09a WL |
636 | string strKey = string("-") + it->string_key; |
637 | if (mapSettingsRet.count(strKey) == 0) | |
d64e124c | 638 | { |
2097c09a | 639 | mapSettingsRet[strKey] = it->value[0]; |
3e468840 | 640 | // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) |
d64e124c CM |
641 | InterpretNegativeSetting(strKey, mapSettingsRet); |
642 | } | |
2097c09a WL |
643 | mapMultiSettingsRet[strKey].push_back(it->value[0]); |
644 | } | |
b94595bb GA |
645 | // If datadir is changed in .conf file: |
646 | ClearDatadirCache(); | |
05c2ba63 | 647 | extern uint16_t BITCOIND_RPCPORT; |
648 | BITCOIND_RPCPORT = GetArg("-rpcport",BaseParams().RPCPort()); | |
2097c09a WL |
649 | } |
650 | ||
9cb1ec9c | 651 | #ifndef _WIN32 |
ee12c3d6 | 652 | boost::filesystem::path GetPidFile() |
2097c09a | 653 | { |
5166804f | 654 | boost::filesystem::path pathPidFile(GetArg("-pid", "komodod.pid")); |
ee12c3d6 PW |
655 | if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; |
656 | return pathPidFile; | |
2097c09a WL |
657 | } |
658 | ||
ee12c3d6 | 659 | void CreatePidFile(const boost::filesystem::path &path, pid_t pid) |
2097c09a | 660 | { |
ee12c3d6 | 661 | FILE* file = fopen(path.string().c_str(), "w"); |
f85c0974 | 662 | if (file) |
2097c09a WL |
663 | { |
664 | fprintf(file, "%d\n", pid); | |
665 | fclose(file); | |
666 | } | |
667 | } | |
a034c7eb | 668 | #endif |
2097c09a | 669 | |
768e5d52 JG |
670 | bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) |
671 | { | |
9cb1ec9c | 672 | #ifdef _WIN32 |
5f986195 | 673 | return MoveFileExA(src.string().c_str(), dest.string().c_str(), |
8d657a65 | 674 | MOVEFILE_REPLACE_EXISTING) != 0; |
768e5d52 JG |
675 | #else |
676 | int rc = std::rename(src.string().c_str(), dest.string().c_str()); | |
677 | return (rc == 0); | |
9cb1ec9c | 678 | #endif /* _WIN32 */ |
768e5d52 JG |
679 | } |
680 | ||
c63a73d1 MF |
681 | /** |
682 | * Ignores exceptions thrown by Boost's create_directory if the requested directory exists. | |
683 | * Specifically handles case where path p exists, but it wasn't possible for the user to | |
684 | * write to the parent directory. | |
685 | */ | |
2b7709dc BD |
686 | bool TryCreateDirectory(const boost::filesystem::path& p) |
687 | { | |
688 | try | |
689 | { | |
690 | return boost::filesystem::create_directory(p); | |
27df4123 | 691 | } catch (const boost::filesystem::filesystem_error&) { |
2b7709dc BD |
692 | if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p)) |
693 | throw; | |
694 | } | |
695 | ||
696 | // create_directory didn't create the directory, it had to have existed already | |
697 | return false; | |
698 | } | |
699 | ||
768e5d52 JG |
700 | void FileCommit(FILE *fileout) |
701 | { | |
4c0b2cde | 702 | fflush(fileout); // harmless if redundantly called |
9cb1ec9c | 703 | #ifdef _WIN32 |
4c0b2cde PK |
704 | HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fileout)); |
705 | FlushFileBuffers(hFile); | |
768e5d52 | 706 | #else |
e9965213 PW |
707 | #if defined(__linux__) || defined(__NetBSD__) |
708 | fdatasync(fileno(fileout)); | |
e7bad10c GM |
709 | #elif defined(__APPLE__) && defined(F_FULLFSYNC) |
710 | fcntl(fileno(fileout), F_FULLFSYNC, 0); | |
e9965213 | 711 | #else |
768e5d52 | 712 | fsync(fileno(fileout)); |
e9965213 | 713 | #endif |
768e5d52 JG |
714 | #endif |
715 | } | |
716 | ||
1eb57879 PW |
717 | bool TruncateFile(FILE *file, unsigned int length) { |
718 | #if defined(WIN32) | |
719 | return _chsize(_fileno(file), length) == 0; | |
720 | #else | |
721 | return ftruncate(fileno(file), length) == 0; | |
722 | #endif | |
723 | } | |
724 | ||
c63a73d1 MF |
725 | /** |
726 | * this function tries to raise the file descriptor limit to the requested number. | |
727 | * It returns the actual file descriptor limit (which may be more or less than nMinFD) | |
728 | */ | |
ba29a559 PW |
729 | int RaiseFileDescriptorLimit(int nMinFD) { |
730 | #if defined(WIN32) | |
731 | return 2048; | |
732 | #else | |
733 | struct rlimit limitFD; | |
734 | if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) { | |
735 | if (limitFD.rlim_cur < (rlim_t)nMinFD) { | |
736 | limitFD.rlim_cur = nMinFD; | |
737 | if (limitFD.rlim_cur > limitFD.rlim_max) | |
738 | limitFD.rlim_cur = limitFD.rlim_max; | |
739 | setrlimit(RLIMIT_NOFILE, &limitFD); | |
740 | getrlimit(RLIMIT_NOFILE, &limitFD); | |
741 | } | |
742 | return limitFD.rlim_cur; | |
743 | } | |
744 | return nMinFD; // getrlimit failed, assume it's fine | |
745 | #endif | |
746 | } | |
747 | ||
c63a73d1 MF |
748 | /** |
749 | * this function tries to make a particular range of a file allocated (corresponding to disk space) | |
750 | * it is advisory, and the range specified in the arguments will never contain live data | |
751 | */ | |
bba89aa8 | 752 | void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { |
288fdc09 PW |
753 | #if defined(WIN32) |
754 | // Windows-specific version | |
755 | HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); | |
756 | LARGE_INTEGER nFileSize; | |
51ed9ec9 | 757 | int64_t nEndPos = (int64_t)offset + length; |
288fdc09 PW |
758 | nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF; |
759 | nFileSize.u.HighPart = nEndPos >> 32; | |
760 | SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN); | |
761 | SetEndOfFile(hFile); | |
762 | #elif defined(MAC_OSX) | |
763 | // OSX specific version | |
764 | fstore_t fst; | |
765 | fst.fst_flags = F_ALLOCATECONTIG; | |
766 | fst.fst_posmode = F_PEOFPOSMODE; | |
767 | fst.fst_offset = 0; | |
768 | fst.fst_length = (off_t)offset + length; | |
769 | fst.fst_bytesalloc = 0; | |
770 | if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) { | |
771 | fst.fst_flags = F_ALLOCATEALL; | |
772 | fcntl(fileno(file), F_PREALLOCATE, &fst); | |
773 | } | |
774 | ftruncate(fileno(file), fst.fst_length); | |
775 | #elif defined(__linux__) | |
776 | // Version using posix_fallocate | |
777 | off_t nEndPos = (off_t)offset + length; | |
778 | posix_fallocate(fileno(file), 0, nEndPos); | |
779 | #else | |
780 | // Fallback version | |
781 | // TODO: just write one byte per block | |
bba89aa8 PW |
782 | static const char buf[65536] = {}; |
783 | fseek(file, offset, SEEK_SET); | |
784 | while (length > 0) { | |
785 | unsigned int now = 65536; | |
786 | if (length < now) | |
787 | now = length; | |
788 | fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway | |
789 | length -= now; | |
790 | } | |
288fdc09 | 791 | #endif |
bba89aa8 PW |
792 | } |
793 | ||
2097c09a WL |
794 | void ShrinkDebugFile() |
795 | { | |
796 | // Scroll debug.log if it's getting too big | |
ee12c3d6 PW |
797 | boost::filesystem::path pathLog = GetDataDir() / "debug.log"; |
798 | FILE* file = fopen(pathLog.string().c_str(), "r"); | |
a486abd4 | 799 | if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000) |
2097c09a WL |
800 | { |
801 | // Restart the file with some of the end | |
fcb0a1bb | 802 | std::vector <char> vch(200000,0); |
1e735048 | 803 | fseek(file, -((long)vch.size()), SEEK_END); |
fcb0a1bb | 804 | int nBytes = fread(begin_ptr(vch), 1, vch.size(), file); |
2097c09a | 805 | fclose(file); |
f85c0974 | 806 | |
ee12c3d6 | 807 | file = fopen(pathLog.string().c_str(), "w"); |
f85c0974 | 808 | if (file) |
2097c09a | 809 | { |
fcb0a1bb | 810 | fwrite(begin_ptr(vch), 1, nBytes, file); |
2097c09a WL |
811 | fclose(file); |
812 | } | |
813 | } | |
3260b4c0 PK |
814 | else if (file != NULL) |
815 | fclose(file); | |
2097c09a WL |
816 | } |
817 | ||
9cb1ec9c | 818 | #ifdef _WIN32 |
3e468840 PK |
819 | boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) |
820 | { | |
821 | namespace fs = boost::filesystem; | |
822 | ||
823 | char pszPath[MAX_PATH] = ""; | |
824 | ||
825 | if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate)) | |
826 | { | |
827 | return fs::path(pszPath); | |
828 | } | |
829 | ||
881a85a2 | 830 | LogPrintf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); |
3e468840 PK |
831 | return fs::path(""); |
832 | } | |
ed6d0b5f | 833 | #endif |
429039d4 | 834 | |
597fa4cd PW |
835 | boost::filesystem::path GetTempPath() { |
836 | #if BOOST_FILESYSTEM_VERSION == 3 | |
837 | return boost::filesystem::temp_directory_path(); | |
838 | #else | |
839 | // TODO: remove when we don't support filesystem v2 anymore | |
840 | boost::filesystem::path path; | |
9cb1ec9c | 841 | #ifdef _WIN32 |
597fa4cd PW |
842 | char pszPath[MAX_PATH] = ""; |
843 | ||
844 | if (GetTempPathA(MAX_PATH, pszPath)) | |
845 | path = boost::filesystem::path(pszPath); | |
846 | #else | |
847 | path = boost::filesystem::path("/tmp"); | |
848 | #endif | |
849 | if (path.empty() || !boost::filesystem::is_directory(path)) { | |
881a85a2 | 850 | LogPrintf("GetTempPath(): failed to find temp path\n"); |
597fa4cd PW |
851 | return boost::filesystem::path(""); |
852 | } | |
853 | return path; | |
854 | #endif | |
855 | } | |
856 | ||
db954a65 | 857 | void runCommand(const std::string& strCommand) |
429039d4 JG |
858 | { |
859 | int nErr = ::system(strCommand.c_str()); | |
860 | if (nErr) | |
7d9d134b | 861 | LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); |
429039d4 JG |
862 | } |
863 | ||
96931d6f GS |
864 | void RenameThread(const char* name) |
865 | { | |
b277b0f1 | 866 | #if defined(PR_SET_NAME) |
96931d6f GS |
867 | // Only the first 15 characters are used (16 - NUL terminator) |
868 | ::prctl(PR_SET_NAME, name, 0, 0, 0); | |
7f386d2f | 869 | #elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) |
304ca955 | 870 | pthread_set_name_np(pthread_self(), name); |
cd58f058 | 871 | |
850c570d | 872 | #elif defined(MAC_OSX) |
c8c2fbe0 | 873 | pthread_setname_np(name); |
96931d6f GS |
874 | #else |
875 | // Prevent warnings for unused parameters... | |
876 | (void)name; | |
877 | #endif | |
878 | } | |
0b47fe6b | 879 | |
5248ff40 SC |
880 | void SetupEnvironment() |
881 | { | |
317e66c7 | 882 | // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale |
883 | // may be invalid, in which case the "C" locale is used as fallback. | |
884 | #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) | |
885 | try { | |
3da78490 | 886 | std::locale(""); // Raises a runtime error if current locale is invalid |
27df4123 | 887 | } catch (const std::runtime_error&) { |
ba0fa0d9 | 888 | setenv("LC_ALL", "C", 1); |
5248ff40 | 889 | } |
ac14bcc1 | 890 | #endif |
3da78490 | 891 | // The path locale is lazy initialized and to avoid deinitialization errors |
317e66c7 | 892 | // in multithreading environments, it is set explicitly by the main thread. |
3da78490 JS |
893 | // A dummy locale is used to extract the internal default locale, used by |
894 | // boost::filesystem::path, which is then used to explicitly imbue the path. | |
895 | std::locale loc = boost::filesystem::path::imbue(std::locale::classic()); | |
ba0fa0d9 | 896 | boost::filesystem::path::imbue(loc); |
5248ff40 | 897 | } |
3e8ac6af | 898 | |
167b6231 WL |
899 | bool SetupNetworking() |
900 | { | |
9cb1ec9c | 901 | #ifdef _WIN32 |
167b6231 WL |
902 | // Initialize Windows Sockets |
903 | WSADATA wsadata; | |
904 | int ret = WSAStartup(MAKEWORD(2,2), &wsadata); | |
905 | if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2) | |
906 | return false; | |
907 | #endif | |
908 | return true; | |
909 | } | |
910 | ||
610a8c07 WL |
911 | void SetThreadPriority(int nPriority) |
912 | { | |
9cb1ec9c | 913 | #ifdef _WIN32 |
610a8c07 | 914 | SetThreadPriority(GetCurrentThread(), nPriority); |
9cb1ec9c | 915 | #else // _WIN32 |
610a8c07 WL |
916 | #ifdef PRIO_THREAD |
917 | setpriority(PRIO_THREAD, 0, nPriority); | |
918 | #else // PRIO_THREAD | |
919 | setpriority(PRIO_PROCESS, 0, nPriority); | |
920 | #endif // PRIO_THREAD | |
9cb1ec9c | 921 | #endif // _WIN32 |
610a8c07 | 922 | } |
af021144 | 923 | |
f0d1accb DH |
924 | std::string PrivacyInfo() |
925 | { | |
926 | return "\n" + | |
927 | FormatParagraph(strprintf(_("In order to ensure you are adequately protecting your privacy when using Zcash, please see <%s>."), | |
10512429 | 928 | "https://z.cash/support/security/")) + "\n"; |
f0d1accb DH |
929 | } |
930 | ||
af021144 S |
931 | std::string LicenseInfo() |
932 | { | |
f0d1accb | 933 | return "\n" + |
ab1d2d57 | 934 | FormatParagraph(strprintf(_("Copyright (C) 2009-%i The Bitcoin Core Developers"), COPYRIGHT_YEAR)) + "\n" + |
af021144 | 935 | FormatParagraph(strprintf(_("Copyright (C) 2015-%i The Zcash Developers"), COPYRIGHT_YEAR)) + "\n" + |
62526d82 | 936 | FormatParagraph(strprintf(_("Copyright (C) 2015-%i jl777 and SuperNET developers"), COPYRIGHT_YEAR)) + "\n" + |
937 | "\n" + | |
af021144 S |
938 | FormatParagraph(_("This is experimental software.")) + "\n" + |
939 | "\n" + | |
940 | FormatParagraph(_("Distributed under the MIT software license, see the accompanying file COPYING or <http://www.opensource.org/licenses/mit-license.php>.")) + "\n" + | |
941 | "\n" + | |
506a2185 | 942 | FormatParagraph(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young.")) + |
af021144 S |
943 | "\n"; |
944 | } | |
da1357e6 WL |
945 | |
946 | int GetNumCores() | |
947 | { | |
da1357e6 | 948 | return boost::thread::physical_concurrency(); |
da1357e6 WL |
949 | } |
950 |