|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.12 root 7: //
8: // CLI/GUI 共通のメイン部分
9: //
10:
1.1 root 11: #include "mainapp.h"
1.1.1.12 root 12: #include "config.h"
13: #include "hostnet.h"
14: #include "logger.h"
15: #include "monitor.h"
1.1.1.2 root 16: #include "mystring.h"
1.1.1.12 root 17: #include "mythread.h"
1.1.1.14 root 18: #include "sram.h"
1.1.1.12 root 19: #include "vm_luna.h"
1.1.1.14 root 20: #include "vm_news.h"
1.1.1.12 root 21: #include "vm_x68k.h"
1.1.1.14 root 22: #include <fcntl.h>
1.1.1.2 root 23: #include <getopt.h>
1.1.1.14 root 24: #include <pwd.h>
1.1.1.2 root 25: #include <sys/stat.h>
1.1.1.8 root 26: #include <algorithm>
1.1.1.12 root 27: #if defined(__linux__)
28: #include <sys/prctl.h>
29: #endif
1.1.1.2 root 30:
31: // グローバルインスタンス
32: MainApp gMainApp;
33:
1.1.1.12 root 34: // コンストラクタ
35: MainApp::MainApp()
36: {
37: }
38:
39: // デストラクタ
40: MainApp::~MainApp()
41: {
42: // 逆順に解放
43:
44: pVM.reset();
1.1.1.14 root 45:
46: pConfig.reset();
47: gConfig = NULL;
48:
49: pMonitorManager.reset();
50: gMonitorManager = NULL;
51:
1.1.1.12 root 52: #if 0 // 確認用
53: if (objects.empty() == false) {
54: printf("~MainApp: undead objects are:");
55: for (const auto *obj : objects) {
1.1.1.14 root 56: printf(" %s", obj->GetIdStr());
1.1.1.12 root 57: }
58: printf("\n");
59: }
60: #endif
61:
62: logger.reset();
63: }
64:
1.1.1.2 root 65: // ヘルプメッセージ
66: void
67: MainApp::ShowHelp(bool all) const
68: {
69: ShowVersion();
70:
71: #define p(msg) printf(" " msg "\n")
72:
73: printf("usage: %s [<options>]\n", getprogname());
1.1.1.6 root 74: p("-c <path> vm directory or configuration file");
1.1.1.14 root 75: p("-f fast mode (same as '-V fast-mode=1')");
1.1.1.2 root 76: if (IsGUI())
1.1.1.14 root 77: p("--fontsize <height> fontsize (same as '-V monitor-fontsize=<height>')");
1.1.1.2 root 78: p("-h show brief help message");
79: p("--help show all help message including for developers");
80: if (IsGUI())
1.1.1.14 root 81: p("-s <scale> mainview scale (same as '-V mainview-scale=<scale>')");
1.1.1.2 root 82: p("--show-config show configuration variables");
1.1.1.8 root 83: p("--show-hostnet show list of hostnet drivers");
1.1.1.2 root 84: p("-v show version");
85: p("-V <key>=<val> overwrite config option");
1.1.1.9 root 86: p("-X <file> [arg..] load and execute host binary (a.out or ELF)");
1.1.1.2 root 87:
88: if (all) {
89: printf("\n(options for developers)\n");
1.1.1.14 root 90: p("-b [<cpu>,]<addr>[,<skip>]");
91: p(" set breakpoint. <cpu> is either 'main'(default) or 'xp'");
1.1.1.2 root 92: p("-B <rom#> benchmark mode");
93: p("-C output log to console");
94: p("-d debugger prompt on startup");
1.1.1.12 root 95: p("-D same as '-V debugger-driver=stdio'");
1.1.1.10 root 96: p("-H human68k console emulation");
1.1.1.2 root 97: p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
1.1.1.11 root 98: p("--load-only <file> load host binary (a.out or ELF) but not execute");
1.1.1.2 root 99: p("-M <name>[,..] monitors to display at startup (-Mhelp displays names)");
1.1.1.14 root 100: p(" memdump<N>[=<hex-addr>[.<fmt>]] fmt := B/W/L/M(MMU)/I(Disasm)/Z(XPDisasm)");
101: p("-S MSX-DOS console emulation");
1.1.1.12 root 102: p("--perf performance measure mode");
1.1.1.2 root 103: }
104: }
105:
106: // long option は enum と struct option をアルファベット順に並べる。
107: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
108: // 0x80 から始めておく。
109: enum {
1.1.1.14 root 110: OPTstart = 0x80 - 1,
111: OPT_create_sram,
112: OPT_fontsize,
1.1.1.11 root 113: OPT_load_only,
1.1.1.2 root 114: OPT_help,
1.1.1.12 root 115: OPT_perf,
1.1.1.2 root 116: OPT_show_config,
1.1.1.3 root 117: OPT_show_config_all,
1.1.1.8 root 118: OPT_show_hostnet,
1.1.1.2 root 119: };
120: static struct option longopts[] = {
1.1.1.14 root 121: { "create-sram", no_argument, NULL, OPT_create_sram },
1.1.1.2 root 122: { "fontsize", required_argument, NULL, OPT_fontsize },
1.1.1.11 root 123: { "load-only", required_argument, NULL, OPT_load_only },
1.1.1.2 root 124: { "help", no_argument, NULL, OPT_help },
1.1.1.12 root 125: { "perf", no_argument, NULL, OPT_perf },
1.1.1.2 root 126: { "show-config", no_argument, NULL, OPT_show_config },
1.1.1.3 root 127: // --show-config-all は開発用なのでヘルプには載せない
128: { "show-config-all",no_argument, NULL, OPT_show_config_all },
1.1.1.8 root 129: { "show-hostnet", no_argument, NULL, OPT_show_hostnet },
1.1.1.2 root 130: { NULL, 0, NULL, 0 },
131: };
132:
1.1.1.7 root 133: // VM の初期化、ステージ1。
134: // VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。
1.1.1.2 root 135: // 所々 CLI と GUI で処理が違う。
1.1.1.5 root 136: // 戻り値は以下のいずれか。
137: // MainApp::PASS .. 実行を継続(通常パス)
138: // EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
139: int
1.1.1.7 root 140: MainApp::Init1(bool is_cli_, int ac, char *av[])
1.1.1.2 root 141: {
1.1.1.14 root 142: int rv;
143:
1.1.1.2 root 144: is_cli = is_cli_;
145:
1.1.1.12 root 146: #if defined(__linux__)
147: // Linux ではケーパビリティが設定されていると coredump しないので
148: // 明示的に許可する必要がある
149: prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
150: #endif
151:
1.1.1.14 root 152: rv = ParseOpt(ac, av);
153: if (rv != MainApp::PASS) {
154: return rv;
1.1.1.2 root 155: }
1.1 root 156:
1.1.1.2 root 157: // ログ機構は引数処理後なるはや
158: // CLI ならログは常に標準出力へ(も)出力。
159: // GUI なら -C で指定。
1.1.1.12 root 160: logger.reset(new Logger());
161: logger->UseStdout(IsCLI() ? true : log_to_console);
162:
163: // モニタマネージャ (VM 作成より前、もしかしたら Config より前?)
164: pMonitorManager.reset(new MonitorManager());
165: gMonitorManager = pMonitorManager.get();
1.1.1.2 root 166:
1.1.1.14 root 167: // 設定を作成。
168: // c0. コンストラクタで初期値を用意。
1.1.1.12 root 169: pConfig.reset(new Config());
170: gConfig = pConfig.get();
1.1.1.2 root 171:
1.1.1.14 root 172: // c1. ホームディレクトリに設定ファイルがあれば読み込んで設定を更新。
173: struct passwd *passwd = getpwuid(getuid());
174: // 見付からないことはないはずだが、一応。
175: if (passwd != NULL) {
176: std::string filename = std::string(passwd->pw_dir) + "/.nono.cfg";
177: ConfigFile dotfile(filename);
178: if (dotfile.Load()) {
179: if (gConfig->Update(dotfile) == false) {
180: return EXIT_FAILURE;
181: }
182: }
1.1.1.2 root 183: }
1.1.1.14 root 184:
185: // c2. VM ディレクトリに設定ファイルがあれば読み込んで設定を更新。
186: // vmfile が存在するかどうかはここで調べるまで分からない。
187: ConfigFile cfgfile(vmfile);
188: if (cfgfile.Load()) {
189: if (gConfig->Update(cfgfile) == false) {
190: return EXIT_FAILURE;
191: }
1.1.1.2 root 192: }
1.1.1.14 root 193:
194: // c3. 最後にコマンドライン引数で更新
195: for (const auto& pair : config_options) {
196: if (gConfig->Update(pair.first, pair.second) == false) {
197: return EXIT_FAILURE;
198: }
199: }
200:
201: // MSX-DOS モードならここでいくつかパラメータを強制的に変更。
202: if (msxdos_mode) {
203: // LUNA-I 最小構成でいい
204: gConfig->Update("vmtype=luna", "-S");
205: gConfig->Update("ram-size=16", "-S");
206: gConfig->Update("luna-video-plane=1", "-S");
207: // ホスト側には干渉しない。
208: gConfig->Update("hostcom-driver=none", "-S");
209: gConfig->Update("hostnet-driver=none", "-S");
1.1.1.2 root 210: }
1.1 root 211:
1.1.1.12 root 212: // 0. VM 種別を決定
1.1.1.9 root 213: // VM 種別文字列から vmtype を決定
214: const ConfigItem& item = gConfig->Find("vmtype");
1.1.1.14 root 215: vmstr = string_tolower(item.AsString());
1.1.1.9 root 216: if (vmstr == "x68030") {
1.1.1.13 root 217: vmtype = VMType::X68030;
1.1.1.9 root 218: } else if (vmstr == "luna") {
1.1.1.13 root 219: vmtype = VMType::LUNA1;
1.1.1.9 root 220: } else if (vmstr == "luna88k") {
1.1.1.13 root 221: vmtype = VMType::LUNA88K;
1.1.1.14 root 222: } else if (vmstr == "news") {
223: vmtype = VMType::NEWS;
1.1.1.2 root 224: } else {
1.1.1.9 root 225: if (item.GetFrom() == ConfigItem::FromInitial) {
226: // 未指定の時
227: warnx("vmtype must be specified");
1.1.1.2 root 228: } else {
1.1.1.9 root 229: // ユーザ由来の時
1.1.1.14 root 230: item.Err("Invalid vmtype");
1.1.1.2 root 231: }
1.1.1.9 root 232: return EXIT_FAILURE;
1.1.1.2 root 233: }
1.1 root 234:
1.1.1.14 root 235: // 0.5. VM が確定したところで SRAM 作成。
236: if (create_sram) {
237: if (vmtype == VMType::X68030) {
238: return CreateSRAM();
239: } else {
240: warnx("--create-sram is only for X68030 mode");
241: return EXIT_FAILURE;
242: }
243: }
244:
1.1.1.12 root 245: // 1. VM 作成
1.1.1.2 root 246: switch (vmtype) {
1.1.1.13 root 247: case VMType::X68030:
1.1.1.12 root 248: pVM.reset(new VM_X68030());
1.1.1.2 root 249: break;
250:
1.1.1.13 root 251: case VMType::LUNA1:
1.1.1.12 root 252: pVM.reset(new VM_LUNA1());
1.1.1.2 root 253: break;
254:
1.1.1.13 root 255: case VMType::LUNA88K:
1.1.1.12 root 256: pVM.reset(new VM_LUNA88K());
1.1.1.2 root 257: break;
1.1 root 258:
1.1.1.14 root 259: case VMType::NEWS:
260: pVM.reset(new VM_NEWS());
261: break;
262:
1.1.1.2 root 263: default:
1.1.1.14 root 264: PANIC("corrupted vmtype=%s", vmstr.c_str());
1.1.1.2 root 265: }
1.1.1.12 root 266: gVM = pVM.get();
1.1.1.2 root 267:
1.1.1.12 root 268: // 2. 動的なコンストラクション
1.1.1.5 root 269: if (!gVM->Create()) {
1.1.1.12 root 270: pVM.reset();
1.1.1.5 root 271: return EXIT_FAILURE;
272: }
273:
1.1.1.12 root 274: // 3. ログの処理
1.1.1.8 root 275:
1.1.1.5 root 276: // 設定内容を表示して終了
277: // (VM コンストラクタで変数の増減があるのでそれより後、
278: // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.14 root 279: if (gConfig->Fix() == false) {
280: pVM.reset();
281: return EXIT_SUCCESS;
282: }
1.1.1.2 root 283: if (show_config) {
1.1.1.3 root 284: gConfig->Show(show_config - 1);
1.1.1.12 root 285: pVM.reset();
1.1.1.5 root 286: return EXIT_SUCCESS;
1.1.1.2 root 287: }
288:
1.1.1.12 root 289: // ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2 root 290: // -L help もここで処理。
291: if (!ParseLogopt()) {
1.1.1.12 root 292: pVM.reset();
1.1.1.5 root 293: return EXIT_FAILURE;
1.1.1.2 root 294: }
295:
1.1.1.5 root 296: return PASS;
1.1.1.2 root 297: }
298:
1.1.1.7 root 299: // VM の初期化、ステージ2。スレッド生成を伴う。
300: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2 root 301: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7 root 302: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2 root 303: bool
1.1.1.7 root 304: MainApp::Init2()
1.1.1.2 root 305: {
1.1.1.12 root 306: // 5. VM 初期化。
1.1.1.2 root 307: if (!gVM->Init()) {
308: return false;
309: }
310:
1.1.1.12 root 311: // メインスレッド名を設定
312: PTHREAD_SETNAME("Main");
313:
314: // 6. スレッド開始
315: if (!gVM->StartThread()) {
316: return false;
317: }
318:
319: // 7. 起動時設定の適用
1.1.1.2 root 320: if (!gVM->Apply()) {
321: return false;
322: }
323:
324: return true;
325: }
326:
327: // コマンドライン引数を処理する。
328: // 知らない引数とかがあればこちらで usage を表示して false を返す。
1.1.1.14 root 329: // 戻り値は、MainApp::PASS なら実行を継続、
330: // EXIT_SUCCESS/EXIT_FAILURE ならこのコードで終了。
331: int
1.1.1.2 root 332: MainApp::ParseOpt(int ac, char *av[])
333: {
1.1.1.6 root 334: struct stat st;
335: const char *cpath;
1.1.1.2 root 336: int b;
337: int c;
338:
1.1.1.6 root 339: cpath = ".";
1.1.1.2 root 340:
1.1.1.14 root 341: while ((c = getopt_long(ac, av, "b:B:c:CdDfhHL:M:s:SvV:X:",
1.1.1.2 root 342: longopts, NULL)) != -1) {
343: switch (c) {
344: case 'b':
1.1.1.4 root 345: debug_breakaddr.push_back(optarg);
1.1.1.2 root 346: break;
347:
348: case 'B':
349: b = atoi(optarg);
350: if (b < 2 || b > 6) {
351: fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
1.1.1.14 root 352: return EXIT_FAILURE;
1.1.1.2 root 353: }
354: benchmark_mode = b;
355: break;
356:
357: case 'c':
1.1.1.6 root 358: cpath = optarg;
359: // 空文字列なら再び初期値に
360: if (cpath[0] == '\0') {
361: cpath = ".";
362: }
1.1.1.2 root 363: break;
364:
365: case 'C':
366: log_to_console = true;
367: break;
368:
369: case 'd':
370: debug_on_start = true;
371: break;
372:
373: case 'D':
1.1.1.12 root 374: // -D は -V debugger-driver=stdio と等価。
1.1.1.14 root 375: config_options.emplace_back("debugger-driver=stdio", "-D");
1.1.1.2 root 376: break;
377:
378: case 'f':
1.1.1.14 root 379: // -f は -V fast-mode=1 と等価。
380: config_options.emplace_back("fast-mode=1", "-f");
1.1.1.2 root 381: break;
382:
1.1.1.9 root 383: case 'H':
384: human_mode = true;
385: break;
386:
1.1.1.2 root 387: case 'L':
1.1.1.12 root 388: AddLogopt(optarg);
1.1.1.2 root 389: break;
390:
391: case 'M':
392: if (!monitor_opt.empty()) {
393: monitor_opt += ",";
394: }
395: monitor_opt += optarg;
396: break;
397:
398: case 's':
1.1.1.14 root 399: {
400: auto line = string_format("mainview-scale=%s", optarg);
401: config_options.emplace_back(line, "-s");
402: break;
403: }
404:
405: case 'S':
406: msxdos_mode = true;
1.1.1.2 root 407: break;
408:
409: case 'X':
1.1.1.11 root 410: load_and_exec = true;
411: FALLTHROUGH;
412: case OPT_load_only:
1.1.1.9 root 413: exec_file = optarg;
1.1.1.2 root 414: for (int i = optind; i < ac; i++) {
415: if (i != optind) {
1.1.1.9 root 416: exec_arg += " ";
1.1.1.2 root 417: }
1.1.1.9 root 418: exec_arg += av[i];
1.1.1.2 root 419: }
420: optind = ac;
421: break;
422:
423: case 'v':
424: ShowVersion();
425: exit(0);
426:
427: case 'V':
1.1.1.14 root 428: config_options.emplace_back(optarg, "-V");
429: break;
430:
431: case OPT_create_sram:
432: create_sram = true;
1.1.1.2 root 433: break;
434:
435: case OPT_fontsize:
1.1.1.14 root 436: {
437: auto line = string_format("monitor-fontsize=%s", optarg);
438: config_options.emplace_back(line, "--fontsize");
1.1.1.2 root 439: break;
1.1.1.14 root 440: }
1.1.1.2 root 441:
442: case OPT_help:
443: ShowHelp(true);
1.1.1.14 root 444: return EXIT_SUCCESS;
1.1.1.2 root 445:
1.1.1.12 root 446: case OPT_perf:
447: // パフォーマンス確認用。
448: // -Vprom-image=PROM.DAT はパスの問題があるので各自で追加指定
449: // する必要がある。
1.1.1.14 root 450: config_options.emplace_back(".rtc-force-fixed=1", "--perf");
451: config_options.emplace_back("clock-sync=virtual", "--perf");
452: config_options.emplace_back("ethernet-macaddr=02:00:00:00:00:01",
453: "--perf");
454: config_options.emplace_back("hostcom-driver=none", "--perf");
455: config_options.emplace_back("hostnet-driver=none", "--perf");
456: config_options.emplace_back("luna-dipsw1=11110111", "--perf");
457: config_options.emplace_back("spc0-id0-writeignore=1", "--perf");
458: config_options.emplace_back("spc0-id1-writeignore=1", "--perf");
459: config_options.emplace_back("spc0-id2-writeignore=1", "--perf");
460: config_options.emplace_back("spc0-id3-writeignore=1", "--perf");
461: config_options.emplace_back("spc0-id4-writeignore=1", "--perf");
462: config_options.emplace_back("spc0-id5-writeignore=1", "--perf");
463: config_options.emplace_back("spc0-id6-writeignore=1", "--perf");
464: config_options.emplace_back("fast-mode=1", "--perf");
465: // -C
1.1.1.12 root 466: log_to_console = true;
467: break;
468:
1.1.1.2 root 469: case OPT_show_config:
1.1.1.3 root 470: show_config = 1;
471: break;
472:
473: case OPT_show_config_all:
474: show_config = 2;
1.1.1.2 root 475: break;
476:
1.1.1.8 root 477: case OPT_show_hostnet:
478: ShowHostnet();
1.1.1.14 root 479: return EXIT_SUCCESS;
1.1.1.8 root 480:
1.1.1.2 root 481: case 'h':
1.1.1.14 root 482: ShowHelp(false);
483: return EXIT_SUCCESS;
484:
1.1.1.2 root 485: default:
486: ShowHelp(false);
1.1.1.14 root 487: return EXIT_FAILURE;
1.1.1.2 root 488: }
489: }
490:
1.1.1.6 root 491: // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
492: // nono.cfg を設定ファイルとする。
493: // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
494: // ディレクトリを VM ディレクトリとする。
495: // -c DIR => vmdir = DIR, vmfile = DIR/nono.cfg
496: // -c DIR/FILE => vmdir = DIR, vmfile = FILE
497: if (stat(cpath, &st) < 0) {
498: warn("stat %s", cpath);
1.1.1.14 root 499: return EXIT_FAILURE;
1.1.1.2 root 500: }
1.1.1.6 root 501: if (S_ISDIR(st.st_mode)) {
502: vmdir = std::string(cpath);
503: if (vmdir.back() != '/') {
504: vmdir += '/';
505: }
506: vmfile = vmdir + "nono.cfg";
507: } else if (S_ISREG(st.st_mode)) {
508: vmfile = std::string(cpath);
509: auto pos = vmfile.rfind('/');
510: if (pos != std::string::npos) {
511: vmdir = vmfile.substr(0, pos + 1);
512: } else {
513: vmdir = "./";
514: }
515: } else {
516: warnx("-c %s: path must be file or directory", cpath);
1.1.1.14 root 517: return EXIT_FAILURE;
1.1.1.2 root 518: }
519:
520: // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
521: // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
522: // 消して回らないと起動できないのも面倒なので -M オプション自体は
523: // 無意味だけど許容する。うーん。
524: if (IsCLI()) {
525: if (monitor_opt == "help") {
526: warnx("-Mhelp is not available on CLI");
1.1.1.14 root 527: return EXIT_FAILURE;
1.1.1.2 root 528: }
529: if (!monitor_opt.empty()) {
530: warnx("-M option is ignored on CLI");
1.1.1.14 root 531: return EXIT_FAILURE;
1.1.1.2 root 532: }
533: }
534:
1.1.1.14 root 535: // Human モード、MSX-DOS モードでは実行ファイル名が必要
1.1.1.9 root 536: if (human_mode) {
537: if (exec_file == NULL) {
538: warnx("-H option needs -X");
1.1.1.14 root 539: return EXIT_FAILURE;
540: }
541: }
542: if (msxdos_mode) {
543: if (exec_file == NULL) {
544: warnx("-S option needs -X");
545: return EXIT_FAILURE;
1.1.1.9 root 546: }
547: }
548:
1.1.1.14 root 549: // 実行ファイルは、ファイル名を間違えたくらいならここでエラーに出来る。
550: if (exec_file) {
551: int r = access(exec_file, R_OK);
552: if (r != 0) {
553: warn("-X %s", exec_file);
554: return EXIT_FAILURE;
555: }
556: }
557:
558: return MainApp::PASS;
1.1.1.2 root 559: }
560:
561: // バージョンを表示
562: void
563: MainApp::ShowVersion() const
564: {
565: // ここは実行ファイル名によらず nono にする
566: fprintf(stderr, "nono version %d.%d.%d (%s)\n",
567: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
568: }
569:
1.1.1.12 root 570: // ログレベル指定文字列を logopt に追加する。
571: void
572: MainApp::AddLogopt(const char *opt)
573: {
574: if (logopt.empty() == false) {
575: logopt += ',';
576: }
577: logopt += opt;
578: }
579:
1.1.1.8 root 580: // ログレベル指定文字列を処理する。
581: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
582: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
583: // "help" があれば設定は行わず一覧を表示。
1.1 root 584: bool
1.1.1.2 root 585: MainApp::ParseLogopt()
1.1 root 586: {
1.1.1.8 root 587: std::vector<std::string> items;
588:
589: // 分解して..
1.1.1.12 root 590: items = string_split(logopt.c_str(), ',');
1.1.1.8 root 591:
592: // "help" があればヘルプを表示して終了
593: for (const auto& item : items) {
594: if (item == "help") {
595: std::vector<std::string> list = GetLogNames();
596: // less したいだろうから stderr ではなく stdout に出力する
597: for (const auto& name : list) {
598: printf(" %s\n", name.c_str());
599: }
600: return false;
1.1 root 601: }
1.1.1.8 root 602: }
603:
604: // ログレベルを設定
605: std::string errmsg;
606: if (SetLogopt(items, &errmsg) == false) {
607: warnx("%s", errmsg.c_str());
1.1 root 608: return false;
609: }
610:
1.1.1.8 root 611: return true;
612: }
1.1 root 613:
1.1.1.8 root 614: // ログレベルを設定する。
615: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
616: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
617: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
618: // 格納して false を返す。
619: // MainApp 内と Debugger からも呼ばれる。
620: /*static*/ bool
621: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
622: {
623: for (const auto& item : items) {
624: if (SetLogopt1(item.c_str(), errmsg) == false) {
625: return false;
626: }
627: }
628: if (0) { // デバッグ用
1.1.1.12 root 629: for (const auto o : gMainApp.GetObjects()) {
1.1.1.8 root 630: printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1 root 631: }
1.1.1.8 root 632: }
633: return true;
634: }
635:
636: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
637: // loglevel は省略なら 1 とする。
638: // logname が "all" なら全オブジェクトにセットする。
639: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
640: // そのオブジェクトにログレベルを設定して true を返す。
641: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
642: // false を返す。
643: // この関数は (このすぐ上の SetLogopt() を経由して)
644: // MainApp と Debugger から呼ばれることに注意。
645: /*static*/ bool
646: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
647: {
648: std::string name;
649: const char *v;
650: int level;
651:
652: v = strchr(item.c_str(), '=');
653: if (v) {
654: name = std::string(item.c_str(), v - item.c_str());
655: level = atoi(++v);
656: } else {
657: name = item;
658: level = 1;
659: }
660: // ここで name は変数名、level は値(省略されたら1)
661:
662: if (name.empty()) {
663: *errmsg = "logname must be specified";
664: return false;
665: }
1.1 root 666:
1.1.1.12 root 667: // 今の所、値域は -1 〜 9 ということにしておく。
668: if (level < -1) {
669: level = -1;
670: } else if (level > 9) {
671: level = 9;
672: }
673:
1.1.1.8 root 674: // "all" なら全部にセット
675: if (name == "all") {
1.1.1.12 root 676: for (auto obj : gMainApp.GetObjects()) {
1.1.1.8 root 677: if (obj->GetName().empty() == false) {
1.1.1.12 root 678: obj->SetLogLevel(level);
1.1.1.8 root 679: }
1.1 root 680: }
1.1.1.8 root 681: return true;
682: }
1.1.1.13 root 683: // "fdd" なら "fdd*" 全部にする
684: if (name == "fdd") {
685: for (auto obj : gMainApp.GetObjects()) {
686: if (strncasecmp(obj->GetName().c_str(), "fdd", 3) == 0) {
687: obj->SetLogLevel(level);
688: }
689: }
690: return true;
691: }
1.1.1.15! root 692: // "bankram" も "bankram[01]" 両方にする
! 693: if (name == "bankram") {
! 694: for (auto obj : gMainApp.GetObjects()) {
! 695: if (strncasecmp(obj->GetName().c_str(), "bankram", 7) == 0) {
! 696: obj->SetLogLevel(level);
! 697: }
! 698: }
! 699: return true;
! 700: }
1.1.1.8 root 701:
1.1.1.12 root 702: // エイリアスリストを作る
703: using aliaslist_t = std::vector<std::pair<std::string, Object *>>;
704: aliaslist_t alias_list;
705: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 706: const std::vector<std::string>& aliases = obj->GetAliases();
707:
708: for (const auto& a : aliases) {
1.1.1.12 root 709: alias_list.emplace_back(a, obj);
710: }
711: }
1.1.1.8 root 712:
1.1.1.12 root 713: // エイリアスを完全一致のみのものと部分一致も許容するものに分ける
714: aliaslist_t exact_alias;
715: aliaslist_t partial_alias;
716: for (const auto& a0 : alias_list) {
717: bool exact = false;
718: for (const auto& a1 : alias_list) {
719: if (a0.first == a1.first) {
720: continue;
721: }
722: if (starts_with_ignorecase(a0.first, a1.first)) {
723: exact = true;
724: break;
1.1 root 725: }
1.1.1.8 root 726: }
1.1.1.12 root 727: if (exact) {
728: exact_alias.push_back(a0);
729: } else {
730: partial_alias.push_back(a0);
731: }
732: }
733:
734: // 完全一致をまず調べる
735: for (const auto& a : exact_alias) {
736: if (strcasecmp(a.first.c_str(), name.c_str()) == 0) {
737: a.second->SetLogLevel(level);
738: return true;
739: }
740: }
741:
742: aliaslist_t found;
743: for (const auto& a : partial_alias) {
744: // 前方一致したら覚えておく
745: if (starts_with_ignorecase(a.first, name)) {
746: found.push_back(a);
747: }
1.1.1.8 root 748: }
749:
750: // 見付からない場合はエラー
751: if (found.empty()) {
752: *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
753: return false;
754: }
755:
756: // 1つだけなら確定
757: if (found.size() == 1) {
1.1.1.12 root 758: found[0].second->SetLogLevel(level);
1.1.1.8 root 759: return true;
760: }
761:
762: // 複数あれば候補文字列を作成
763: *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
764: name.c_str());
1.1.1.12 root 765: for (const auto& cand : found) {
766: *errmsg += string_format(" \"%s\"", cand.first.c_str());
1.1.1.8 root 767: }
768: return false;
769: }
770:
771: // ログ名の一覧を取得する。
772: // MainApp と Debugger から呼ばれる。
773: /*static*/ std::vector<std::string>
774: MainApp::GetLogNames()
775: {
776: std::vector<Object *> sortobj;
777:
778: // エイリアスを持つオブジェクトだけ抜き出す
1.1.1.12 root 779: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 780: if (obj->GetAliases().empty() == false) {
781: sortobj.emplace_back(obj);
782: }
783: }
784:
785: // aliases の一語目で sortobj をソートする
786: std::sort(sortobj.begin(), sortobj.end(),
1.1.1.12 root 787: [](const auto a, const auto b) {
1.1.1.8 root 788: const auto& sa = a->GetAliases()[0];
789: const auto& sb = b->GetAliases()[0];
1.1.1.12 root 790: return sa < sb;
1.1.1.8 root 791: }
792: );
793:
794: std::vector<std::string> list;
795: for (const auto *obj : sortobj) {
796: const auto& aliases = obj->GetAliases();
797: std::string str;
798:
1.1.1.12 root 799: str = aliases[0];
1.1.1.8 root 800: if (aliases.size() > 1) {
801: str += " (alias:";
802: for (int i = 1, sz = aliases.size(); i < sz; i++) {
803: str += ' ';
804: str += aliases[i];
1.1 root 805: }
1.1.1.8 root 806: str += ')';
1.1.1.6 root 807: }
1.1.1.8 root 808: list.emplace_back(str);
1.1 root 809: }
1.1.1.8 root 810: list.emplace_back("all");
1.1 root 811:
1.1.1.8 root 812: return list;
1.1 root 813: }
1.1.1.2 root 814:
815: // 関連するファイルのパスを取得。
1.1.1.3 root 816: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
817: // ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
818: // 何らか不都合があったとしても (例えばファイルサイズが 0 だとか
819: // パーミッションが足りないとか) 次を試すとかはしない。
820: // 2. name が '~' から始まっていればホームディレクトリに展開。
821: // 3. name が相対パスなら VM ディレクトリからの相対パスとする。
822: // 4. name は絶対パスのはずなので、そのまま使用する。
1.1.1.2 root 823: std::string
824: MainApp::SearchFile(const std::string& name) const
825: {
826: std::string path;
827: struct stat st;
828:
1.1.1.3 root 829: // 1. パス区切りを含んでなければ、ファイル名のみ
830: if (name.find('/') == std::string::npos) {
831: // 1a. VM ディレクトリ
832: path = GetVMDir() + name;
833: if (stat(path.c_str(), &st) == 0) {
834: return path;
835: }
836:
837: // 2. 親ディレクトリ
838: path = GetVMDir() + "../" + name;
839: if (stat(path.c_str(), &st) == 0) {
840: return path;
841: }
842:
843: // どちらもなければエラー
844: return "";
845: }
1.1.1.2 root 846:
1.1.1.3 root 847: // 2. 先頭の '~' を $HOME に展開
848: path = name;
849: if (path[0] == '~' && path[1] == '/') {
850: const char *home = getenv("HOME");
851: if (home == NULL) {
852: home = "";
853: }
854: path = string_format("%s%s", home, path.c_str() + 1);
1.1.1.2 root 855: }
856:
1.1.1.3 root 857: // 3. 相対パスなら、VM ディレクトリからの相対
858: if (path[0] != '/') {
859: path = GetVMDir() + path;
1.1.1.2 root 860: }
861:
1.1.1.3 root 862: return path;
863: }
864:
1.1.1.14 root 865: // 初回起動時用に SRAM.DAT を作成する。
866: // 戻り値は EXIT_SUCCESS / EXIT_FAILURE。
867: int
868: MainApp::CreateSRAM()
869: {
870: char buf[16 * 1024];
871: std::string filename;
872: autofd fd;
873: int r;
874:
875: filename = GetVMDir() + "SRAM.DAT";
876:
877: // 存在確認のため一旦読み込み専用で開いてみる。
878: // オープン出来たら何もしない。
879: fd = open(filename.c_str(), O_RDONLY);
880: if (fd >= 0) {
881: warnx("%s: Already exists", filename.c_str());
882: return EXIT_FAILURE;
883: }
884:
885: fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
886: if (fd < 0) {
887: warn("%s: open failed", filename.c_str());
888: return EXIT_FAILURE;
889: }
890:
891: memset(&buf[0], 0, sizeof(buf));
892: for (int i = 0; i < SRAMDevice::InitialData.size(); i++) {
893: buf[i] = SRAMDevice::InitialData[i];
894: }
895:
896: r = write(fd, buf, sizeof(buf));
897: if (r < 0) {
898: warn("%s: write failed", filename.c_str());
899: return EXIT_FAILURE;
900: }
901:
902: fd.Close();
903: warnx("created %s", filename.c_str());
904: return EXIT_SUCCESS;
905: }
906:
1.1.1.13 root 907: // 現在の VM が指定のケーパビリティを持っているか?
1.1.1.3 root 908: bool
1.1.1.13 root 909: MainApp::Has(VMCap cap) const
1.1.1.3 root 910: {
1.1.1.13 root 911: uint32 vmcap = 1U << (int)GetVMType();
912: return (vmcap & (uint32)cap) != 0;
1.1.1.2 root 913: }
1.1.1.8 root 914:
915: // コンパイルされている host netdriver の一覧を表示。
916: void
917: MainApp::ShowHostnet() const
918: {
1.1.1.12 root 919: auto list = HostNetDevice::GetDrivers();
1.1.1.8 root 920: for (const auto& name : list) {
921: printf(" %s\n", name.c_str());
922: }
923: }
1.1.1.12 root 924:
1.1.1.14 root 925: // オブジェクト登録
1.1.1.12 root 926: void
1.1.1.14 root 927: MainApp::RegistObject(Object *obj)
1.1.1.12 root 928: {
1.1.1.14 root 929: // ID が重複していないかチェック (NONE なら重複可)
930: auto id = obj->GetId();
931: if (id != OBJ_NONE) {
932: if (FindObject(id)) {
933: PANIC("%s already exists", Object::GetIdStr(id));
934: }
935: }
936:
937: obj->logger = gMainApp.GetLogger();
1.1.1.12 root 938:
939: objects.push_back(obj);
940: }
941:
1.1.1.14 root 942: // オブジェクト削除
1.1.1.12 root 943: void
1.1.1.14 root 944: MainApp::UnregistObject(Object *obj)
1.1.1.12 root 945: {
1.1.1.14 root 946: // 最初に見付かった一つを削除するだけでいい
1.1.1.12 root 947: for (auto it = objects.begin(); it != objects.end(); ++it) {
948: if (*it == obj) {
949: objects.erase(it);
950: break;
951: }
952: }
953: }
1.1.1.14 root 954:
955: // 指定された id を持つオブジェクトを返す。なければ NULL を返す。
956: Object *
957: MainApp::FindObject(int id) const
958: {
959: // NONE はここで検索しない (SCSI デバイスなど、そっちで検索する)
960: if (id == OBJ_NONE) {
961: return NULL;
962: }
963: for (auto obj : objects) {
964: if (obj->GetId() == id) {
965: return obj;
966: }
967: }
968: return NULL;
969: }
970:
971: // 指定された id を持つオブジェクトを探す。なければ assert する。
972: Object *
973: MainApp::GetObject(int id) const
974: {
975: Object *obj = FindObject(id);
976: if (__predict_false(obj == NULL)) {
977: PANIC("objid=%s not found", Object::GetIdStr(id));
978: }
979: return obj;
980: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.