Use urandom / rtlgenrandom
[lsnes.git] / src / core / misc.cpp
blob8ea0a508ce97087f982eea56aaae51a0c2c56ae9
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/memorymanip.hpp"
6 #include "core/misc.hpp"
7 #include "core/movie.hpp"
8 #include "core/rom.hpp"
9 #include "core/moviedata.hpp"
10 #include "core/settings.hpp"
11 #include "core/window.hpp"
12 #include "library/crandom.hpp"
13 #include "library/directory.hpp"
14 #include "library/hex.hpp"
15 #include "library/loadlib.hpp"
16 #include "library/sha256.hpp"
17 #include "library/string.hpp"
18 #include "library/skein.hpp"
19 #include "library/serialization.hpp"
20 #include "library/arch-detect.hpp"
22 #include <cstdlib>
23 #include <csignal>
24 #include <sstream>
25 #include <iostream>
26 #include <iomanip>
27 #include <fstream>
28 #include <string>
29 #include <vector>
30 #include <algorithm>
31 #include <ctime>
32 #include <cstdlib>
33 #include <cstring>
34 #include <sys/time.h>
35 #include <unistd.h>
36 #include <boost/filesystem.hpp>
37 #if defined(_WIN32) || defined(_WIN64)
38 #include <windows.h>
39 #endif
41 #ifdef USE_LIBGCRYPT_SHA256
42 #include <gcrypt.h>
43 #endif
45 namespace
47 skein::prng prng;
48 bool reached_main_flag;
49 threads::lock seed_mutex;
51 uint64_t arch_get_tsc()
53 #ifdef ARCH_IS_I386
54 uint32_t a, b;
55 asm volatile("rdtsc" : "=a"(a), "=d"(b));
56 return ((uint64_t)b << 32) | a;
57 #else
58 return 0;
59 #endif
62 uint64_t arch_get_random()
64 #ifdef ARCH_IS_I386
65 uint32_t r;
66 //This reads undefined value if RDRAND is not supported. Don't care.
67 asm volatile (".byte 0xb8, 0x01, 0x00, 0x00, 0x00, 0x0f, 0xa2, 0xf7, 0xc1, 0x00, 0x00, 0x00, 0x40, "
68 "0x74, 0x03, 0x0f, 0xc7, 0xf0" : "=a"(r) : : "ebx", "ecx", "edx");
69 return r;
70 #else
71 return 0;
72 #endif
75 void do_mix_tsc()
77 const unsigned slots = 32;
78 static unsigned count = 0;
79 static uint64_t last_reseed = 0;
80 static uint64_t buf[slots + 1];
81 buf[count++] = arch_get_tsc();
82 threads::alock h(seed_mutex);
83 if(count == 0) count = 1; //Shouldn't happen.
84 if(count == slots || buf[count - 1] - last_reseed > 300000000) {
85 last_reseed = buf[count - 1];
86 buf[slots] = arch_get_random();
87 prng.write(buf, sizeof(buf));
88 count = 0;
92 std::string get_random_hexstring_64(size_t index)
94 threads::alock h(seed_mutex);
95 uint64_t buf[7];
96 uint8_t out[32];
97 timeval tv;
98 buf[3] = arch_get_random();
99 buf[4] = arch_get_random();
100 buf[5] = arch_get_random();
101 buf[6] = arch_get_random();
102 gettimeofday(&tv, NULL);
103 buf[0] = tv.tv_sec;
104 buf[1] = tv.tv_usec;
105 buf[2] = arch_get_tsc();
106 prng.write(buf, sizeof(buf));
107 prng.read(out, sizeof(out));
108 return hex::b_to(out, sizeof(out));
111 char endian_char(int e)
113 if(e < 0)
114 return 'L';
115 if(e > 0)
116 return 'B';
117 return 'N';
120 void fatal_signal_handler(int sig)
122 write(2, "Caught fatal signal!\n", 21);
123 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
124 signal(sig, SIG_DFL);
125 raise(sig);
128 void terminate_handler()
130 write(2, "Terminating abnormally!\n", 24);
131 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
132 std::cerr << "Exiting on fatal error" << std::endl;
133 exit(1);
136 command::fnptr<const std::string&> test4(lsnes_cmd, "panicsave-movie", "", "",
137 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
138 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
141 //% is intentionally missing.
142 const char* allowed_filename_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
143 "^&'@{}[],$?!-#().+~_";
146 std::string safe_filename(const std::string& str)
148 std::ostringstream o;
149 for(size_t i = 0; i < str.length(); i++) {
150 unsigned char ch = static_cast<unsigned char>(str[i]);
151 if(strchr(allowed_filename_chars, ch))
152 o << str[i];
153 else
154 o << "%" << hex::to8(ch);
156 return o.str();
159 std::string get_random_hexstring(size_t length) throw(std::bad_alloc)
161 std::string out;
162 for(size_t i = 0; i < length; i += 64)
163 out = out + get_random_hexstring_64(i);
164 return out.substr(0, length);
167 void set_random_seed(const std::string& seed) throw(std::bad_alloc)
169 std::vector<char> x(seed.begin(), seed.end());
171 threads::alock h(seed_mutex);
172 prng.write(&x[0], x.size());
176 void set_random_seed() throw(std::bad_alloc)
178 char buf[128];
179 crandom::generate(buf, 128);
180 std::string s(buf, sizeof(buf));
181 set_random_seed(s);
184 struct loaded_rom load_rom_from_commandline(std::vector<std::string> cmdline) throw(std::bad_alloc,
185 std::runtime_error)
187 std::string f;
188 regex_results optp;
189 for(auto i : cmdline) {
190 if(!(optp = regex("--rom=(.+)", i)))
191 continue;
192 f = optp[1];
195 struct loaded_rom r;
196 try {
197 r = loaded_rom(f);
198 } catch(std::bad_alloc& e) {
199 OOM_panic();
200 } catch(std::exception& e) {
201 throw std::runtime_error(std::string("Can't load ROM: ") + e.what());
204 std::string not_present = "N/A";
205 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
206 std::string romname = "UNKNOWN ROM";
207 std::string xmlname = "UNKNOWN XML";
208 if(i < r.rtype->get_image_count()) {
209 romname = (r.rtype->get_image_info(i).hname == "ROM") ? std::string("ROM") :
210 (r.rtype->get_image_info(i).hname + " ROM");
211 xmlname = r.rtype->get_image_info(i).hname + " XML";
213 if(r.romimg[i].data) messages << romname << " hash: " << r.romimg[i].sha_256.read() << std::endl;
214 if(r.romxml[i].data) messages << xmlname << " hash: " << r.romxml[i].sha_256.read() << std::endl;
216 return r;
219 void dump_region_map() throw(std::bad_alloc)
221 std::list<struct memory_region*> regions = lsnes_memory.get_regions();
222 for(auto i : regions) {
223 std::ostringstream x;
224 x << hex::to(i->base) << "-" << hex::to(i->last_address()) << " " << hex::to(i->size) << " ";
225 messages << x.str() << (i->readonly ? "R-" : "RW") << endian_char(i->endian)
226 << (i->special ? 'I' : 'M') << " " << i->name << std::endl;
230 void fatal_error() throw()
232 platform::fatal_error();
233 std::cout << "PANIC: Fatal error, can't continue." << std::endl;
234 exit(1);
237 std::string get_config_path() throw(std::bad_alloc)
239 const char* tmp;
240 std::string basedir;
241 if((tmp = getenv("APPDATA"))) {
242 //If $APPDATA exists, it is the base directory
243 basedir = tmp;
244 } else if((tmp = getenv("XDG_CONFIG_HOME"))) {
245 //If $XDG_CONFIG_HOME exists, it is the base directory
246 basedir = tmp;
247 } else if((tmp = getenv("HOME"))) {
248 //If $HOME exists, the base directory is '.config' there.
249 basedir = std::string(tmp) + "/.config";
250 } else {
251 //Last chance: Return current directory.
252 return ".";
254 //Try to create 'lsnes'. If it exists (or is created) and is directory, great. Otherwise error out.
255 std::string lsnes_path = basedir + "/lsnes";
256 if(!ensure_directory_exists(lsnes_path)) {
257 messages << "FATAL: Can't create configuration directory '" << lsnes_path << "'" << std::endl;
258 fatal_error();
260 //Yes, this is racy, but portability is more important than being absolutely correct...
261 std::string tfile = lsnes_path + "/test";
262 remove(tfile.c_str());
263 FILE* x;
264 if(!(x = fopen(tfile.c_str(), "w+"))) {
265 messages << "FATAL: Configuration directory '" << lsnes_path << "' is not writable" << std::endl;
266 fatal_error();
268 fclose(x);
269 remove(tfile.c_str());
270 return lsnes_path;
273 void OOM_panic()
275 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
276 messages << "FATAL: Out of memory!" << std::endl;
277 fatal_error();
280 std::ostream& messages_relay_class::getstream() { return platform::out(); }
281 messages_relay_class messages;
283 uint32_t gcd(uint32_t a, uint32_t b) throw()
285 if(b == 0)
286 return a;
287 else
288 return gcd(b, a % b);
291 std::string format_address(void* addr)
293 return hex::to((uint64_t)addr);
296 bool in_global_ctors()
298 return !reached_main_flag;
301 void reached_main()
303 crandom::init();
304 new_core_flag = false; //We'll process the static cores anyway.
305 reached_main_flag = true;
306 lsnes_cmd.set_oom_panic(OOM_panic);
307 lsnes_cmd.set_output(platform::out());
308 loadlib::module::run_initializers();
309 std::set_terminate(terminate_handler);
310 #ifdef SIGHUP
311 signal(SIGHUP, fatal_signal_handler);
312 #endif
313 #ifdef SIGINT
314 signal(SIGINT, fatal_signal_handler);
315 #endif
316 #ifdef SIGQUIT
317 signal(SIGQUIT, fatal_signal_handler);
318 #endif
319 #ifdef SIGILL
320 signal(SIGILL, fatal_signal_handler);
321 #endif
322 #ifdef SIGABRT
323 signal(SIGABRT, fatal_signal_handler);
324 #endif
325 #ifdef SIGSEGV
326 signal(SIGSEGV, fatal_signal_handler);
327 #endif
328 #ifdef SIGFPE
329 signal(SIGFPE, fatal_signal_handler);
330 #endif
331 #ifdef SIGPIPE
332 signal(SIGPIPE, fatal_signal_handler);
333 #endif
334 #ifdef SIGBUS
335 signal(SIGBUS, fatal_signal_handler);
336 #endif
337 #ifdef SIGTRAP
338 signal(SIGTRAP, fatal_signal_handler);
339 #endif
340 #ifdef SIGTERM
341 signal(SIGTERM, fatal_signal_handler);
342 #endif
345 std::string mangle_name(const std::string& orig)
347 std::ostringstream out;
348 for(auto i : orig) {
349 if(i == '(')
350 out << "[";
351 else if(i == ')')
352 out << "]";
353 else if(i == '|')
354 out << "\xE2\x8F\xBF";
355 else if(i == '/')
356 out << "\xE2\x8B\xBF";
357 else
358 out << i;
360 return out.str();
363 void random_mix_timing_entropy()
365 do_mix_tsc();
368 //Generate highly random stuff.
369 void highrandom_256(uint8_t* buf)
371 uint8_t tmp[104];
372 std::string s = get_random_hexstring(64);
373 std::copy(s.begin(), s.end(), reinterpret_cast<char*>(tmp));
374 crandom::generate(tmp + 64, 32);
375 serialization::u64b(tmp + 96, arch_get_tsc());
376 skein::hash hsh(skein::hash::PIPE_1024, 256);
377 hsh.write(tmp, 104);
378 hsh.read(buf);
381 std::string get_temp_file()
383 #if !defined(_WIN32) && !defined(_WIN64)
384 char tname[512];
385 strcpy(tname, "/tmp/lsnestmp_XXXXXX");
386 int h = mkstemp(tname);
387 if(h < 0)
388 throw std::runtime_error("Failed to get new tempfile name");
389 close(h);
390 return tname;
391 #else
392 char tpath[512];
393 char tname[512];
394 if(!GetTempPathA(512, tpath))
395 throw std::runtime_error("Failed to get new tempfile name");
396 if(!GetTempFileNameA(tpath, "lsn", 0, tname))
397 throw std::runtime_error("Failed to get new tempfile name");
398 return tname;
399 #endif
402 command::fnptr<const std::string&> macro_test(lsnes_cmd, "test-macro", "", "",
403 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
404 regex_results r = regex("([0-9]+)[ \t](.*)", args);
405 if(!r) {
406 messages << "Bad syntax" << std::endl;
407 return;
409 unsigned ctrl = parse_value<unsigned>(r[1]);
410 auto pcid = controls.lcid_to_pcid(ctrl);
411 if(pcid.first < 0) {
412 messages << "Bad controller" << std::endl;
413 return;
415 try {
416 const port_controller* _ctrl = controls.get_blank().porttypes().port_type(pcid.first).
417 controller_info->get(pcid.second);
418 if(!_ctrl) {
419 messages << "No controller data for controller" << std::endl;
420 return;
422 controller_macro_data mdata(r[2].c_str(), controller_macro_data::make_descriptor(*_ctrl), 0);
423 messages << "Macro: " << mdata.dump(*_ctrl) << std::endl;
424 } catch(std::exception& e) {
425 messages << "Exception: " << e.what() << std::endl;