|
|
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.16 root 12: #include "accel_avx2.h"
1.1.1.12 root 13: #include "config.h"
14: #include "hostnet.h"
15: #include "logger.h"
16: #include "monitor.h"
1.1.1.2 root 17: #include "mystring.h"
1.1.1.12 root 18: #include "mythread.h"
1.1.1.14 root 19: #include "sram.h"
1.1.1.12 root 20: #include "vm_luna.h"
1.1.1.14 root 21: #include "vm_news.h"
1.1.1.16 root 22: #include "vm_virt68k.h"
1.1.1.12 root 23: #include "vm_x68k.h"
1.1.1.14 root 24: #include <fcntl.h>
1.1.1.2 root 25: #include <getopt.h>
1.1.1.14 root 26: #include <pwd.h>
1.1.1.16 root 27: #include <sys/param.h>
1.1.1.2 root 28: #include <sys/stat.h>
1.1.1.8 root 29: #include <algorithm>
1.1.1.17 root 30: #include <thread>
1.1.1.12 root 31: #if defined(__linux__)
32: #include <sys/prctl.h>
33: #endif
1.1.1.2 root 34:
35: // グローバルインスタンス
36: MainApp gMainApp;
37:
1.1.1.12 root 38: // コンストラクタ
39: MainApp::MainApp()
40: {
41: }
42:
43: // デストラクタ
44: MainApp::~MainApp()
45: {
46: // 逆順に解放
47:
48: pVM.reset();
1.1.1.14 root 49:
50: pConfig.reset();
51: gConfig = NULL;
52:
53: pMonitorManager.reset();
54: gMonitorManager = NULL;
55:
1.1.1.17 root 56: hostcpu.reset();
57:
1.1.1.12 root 58: #if 0 // 確認用
59: if (objects.empty() == false) {
60: printf("~MainApp: undead objects are:");
61: for (const auto *obj : objects) {
1.1.1.14 root 62: printf(" %s", obj->GetIdStr());
1.1.1.12 root 63: }
64: printf("\n");
65: }
66: #endif
67:
68: logger.reset();
69: }
70:
1.1.1.16 root 71: // VM を解放。
72: void
73: MainApp::Dispose()
74: {
75: if ((bool)pVM) {
76: pVM->Dispose();
77: }
78: }
79:
1.1.1.2 root 80: // ヘルプメッセージ
81: void
82: MainApp::ShowHelp(bool all) const
83: {
84: ShowVersion();
85:
86: #define p(msg) printf(" " msg "\n")
87:
88: printf("usage: %s [<options>]\n", getprogname());
1.1.1.6 root 89: p("-c <path> vm directory or configuration file");
1.1.1.14 root 90: p("-f fast mode (same as '-V fast-mode=1')");
1.1.1.2 root 91: if (IsGUI())
1.1.1.14 root 92: p("--fontsize <height> fontsize (same as '-V monitor-fontsize=<height>')");
1.1.1.2 root 93: p("-h show brief help message");
94: p("--help show all help message including for developers");
95: if (IsGUI())
1.1.1.14 root 96: p("-s <scale> mainview scale (same as '-V mainview-scale=<scale>')");
1.1.1.2 root 97: p("--show-config show configuration variables");
1.1.1.8 root 98: p("--show-hostnet show list of hostnet drivers");
1.1.1.2 root 99: p("-v show version");
100: p("-V <key>=<val> overwrite config option");
1.1.1.16 root 101: p("-X <file> load and execute host binary (a.out or ELF)");
102: p("--initrd <img> initial ramdisk (only for virt68k)");
1.1.1.2 root 103:
104: if (all) {
105: printf("\n(options for developers)\n");
1.1.1.14 root 106: p("-b [<cpu>,]<addr>[,<skip>]");
107: p(" set breakpoint. <cpu> is either 'main'(default) or 'xp'");
1.1.1.16 root 108: p("--bi-exg set instruction breakpoint on 'exg sp,sp'");
1.1.1.2 root 109: p("-C output log to console");
1.1.1.18 root 110: p("--console-log <file> specify the console output log");
1.1.1.2 root 111: p("-d debugger prompt on startup");
1.1.1.12 root 112: p("-D same as '-V debugger-driver=stdio'");
1.1.1.10 root 113: p("-H human68k console emulation");
1.1.1.2 root 114: p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
115: p("-M <name>[,..] monitors to display at startup (-Mhelp displays names)");
1.1.1.14 root 116: p(" memdump<N>[=<hex-addr>[.<fmt>]] fmt := B/W/L/M(MMU)/I(Disasm)/Z(XPDisasm)");
117: p("-S MSX-DOS console emulation");
1.1.1.12 root 118: p("--perf performance measure mode");
1.1.1.2 root 119: }
120: }
121:
122: // long option は enum と struct option をアルファベット順に並べる。
123: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
124: // 0x80 から始めておく。
125: enum {
1.1.1.14 root 126: OPTstart = 0x80 - 1,
1.1.1.16 root 127: OPT_bi_exg,
1.1.1.18 root 128: OPT_console_log,
1.1.1.14 root 129: OPT_create_sram,
130: OPT_fontsize,
1.1.1.2 root 131: OPT_help,
1.1.1.16 root 132: OPT_initrd,
1.1.1.12 root 133: OPT_perf,
1.1.1.2 root 134: OPT_show_config,
1.1.1.3 root 135: OPT_show_config_all,
1.1.1.8 root 136: OPT_show_hostnet,
1.1.1.2 root 137: };
138: static struct option longopts[] = {
1.1.1.16 root 139: { "bi-exg", no_argument, NULL, OPT_bi_exg },
1.1.1.18 root 140: { "console-log", required_argument, NULL, OPT_console_log },
1.1.1.14 root 141: { "create-sram", no_argument, NULL, OPT_create_sram },
1.1.1.2 root 142: { "fontsize", required_argument, NULL, OPT_fontsize },
143: { "help", no_argument, NULL, OPT_help },
1.1.1.16 root 144: { "initrd", required_argument, NULL, OPT_initrd },
1.1.1.12 root 145: { "perf", no_argument, NULL, OPT_perf },
1.1.1.2 root 146: { "show-config", no_argument, NULL, OPT_show_config },
1.1.1.3 root 147: // --show-config-all は開発用なのでヘルプには載せない
148: { "show-config-all",no_argument, NULL, OPT_show_config_all },
1.1.1.8 root 149: { "show-hostnet", no_argument, NULL, OPT_show_hostnet },
1.1.1.2 root 150: { NULL, 0, NULL, 0 },
151: };
152:
1.1.1.7 root 153: // VM の初期化、ステージ1。
154: // VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。
1.1.1.2 root 155: // 所々 CLI と GUI で処理が違う。
1.1.1.5 root 156: // 戻り値は以下のいずれか。
157: // MainApp::PASS .. 実行を継続(通常パス)
158: // EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
159: int
1.1.1.7 root 160: MainApp::Init1(bool is_cli_, int ac, char *av[])
1.1.1.2 root 161: {
1.1.1.14 root 162: int rv;
163:
1.1.1.2 root 164: is_cli = is_cli_;
165:
1.1.1.16 root 166: rv = Init1a(ac, av);
167: if (rv != MainApp::PASS) {
168: return rv;
169: }
170:
171: rv = Init1b();
172: return rv;
173: }
174:
175: // VM の初期化、ステージ1 の前半。
176: // Config.Fix まで。
177: int
178: MainApp::Init1a(int ac, char *av[])
179: {
180: int rv;
181:
1.1.1.12 root 182: #if defined(__linux__)
183: // Linux ではケーパビリティが設定されていると coredump しないので
184: // 明示的に許可する必要がある
185: prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
186: #endif
187:
1.1.1.14 root 188: rv = ParseOpt(ac, av);
189: if (rv != MainApp::PASS) {
190: return rv;
1.1.1.2 root 191: }
1.1 root 192:
1.1.1.2 root 193: // ログ機構は引数処理後なるはや
194: // CLI ならログは常に標準出力へ(も)出力。
195: // GUI なら -C で指定。
1.1.1.12 root 196: logger.reset(new Logger());
197: logger->UseStdout(IsCLI() ? true : log_to_console);
198:
199: // モニタマネージャ (VM 作成より前、もしかしたら Config より前?)
200: pMonitorManager.reset(new MonitorManager());
201: gMonitorManager = pMonitorManager.get();
1.1.1.2 root 202:
1.1.1.14 root 203: // 設定を作成。
204: // c0. コンストラクタで初期値を用意。
1.1.1.12 root 205: pConfig.reset(new Config());
206: gConfig = pConfig.get();
1.1.1.2 root 207:
1.1.1.14 root 208: // c1. ホームディレクトリに設定ファイルがあれば読み込んで設定を更新。
209: struct passwd *passwd = getpwuid(getuid());
210: // 見付からないことはないはずだが、一応。
211: if (passwd != NULL) {
212: std::string filename = std::string(passwd->pw_dir) + "/.nono.cfg";
1.1.1.16 root 213: ConfigFile dotfile(filename, ConfigItem::FromHome);
1.1.1.14 root 214: if (dotfile.Load()) {
215: if (gConfig->Update(dotfile) == false) {
216: return EXIT_FAILURE;
217: }
218: }
1.1.1.2 root 219: }
1.1.1.14 root 220:
221: // c2. VM ディレクトリに設定ファイルがあれば読み込んで設定を更新。
222: // vmfile が存在するかどうかはここで調べるまで分からない。
1.1.1.16 root 223: ConfigFile cfgfile(vmfile, ConfigItem::FromConfig);
1.1.1.17 root 224: if (cfgfile.Load() == false) {
225: return EXIT_FAILURE;
226: }
227: if (gConfig->Update(cfgfile) == false) {
228: return EXIT_FAILURE;
1.1.1.2 root 229: }
1.1.1.14 root 230:
231: // c3. 最後にコマンドライン引数で更新
232: for (const auto& pair : config_options) {
233: if (gConfig->Update(pair.first, pair.second) == false) {
234: return EXIT_FAILURE;
235: }
236: }
237:
1.1.1.16 root 238: // c4. exec-file, exec-arg をここでメンバ変数 exec_file, exec_arg に代入。
239: // (c3 までに確定していて、1. VM コンストラクト時には必要)
240: const ConfigItem& itemexec = gConfig->Find("exec-file");
241: const std::string& execstr = itemexec.AsString();
242: if (execstr.empty() == false) {
243: // 歴史的経緯で exec_file は const char *。
244: exec_file = strdup(NormalizePath(execstr).c_str());
245: }
246: const ConfigItem& itemarg = gConfig->Find("exec-arg");
247: exec_arg = itemarg.AsString();
248:
1.1.1.17 root 249: // Human68k/MSX-DOS モードならここでいくつかパラメータを強制的に変更。
250: if (human_mode) {
251: // X68030 最小構成でいい。
252: gConfig->Update("vmtype=x68030", "-H");
253: }
1.1.1.14 root 254: if (msxdos_mode) {
1.1.1.17 root 255: // LUNA-I 最小構成でいい。
1.1.1.14 root 256: gConfig->Update("vmtype=luna", "-S");
257: gConfig->Update("ram-size=16", "-S");
258: gConfig->Update("luna-video-plane=1", "-S");
259: // ホスト側には干渉しない。
260: gConfig->Update("hostcom-driver=none", "-S");
261: gConfig->Update("hostnet-driver=none", "-S");
1.1.1.2 root 262: }
1.1 root 263:
1.1.1.17 root 264: // ログ表示のためだけのオブジェクト
265: hostcpu.reset(new Object(OBJ_HOSTCPU));
266:
1.1.1.16 root 267: // ホスト CPU の機能チェック。
268: if (CheckCPU() == false) {
269: return EXIT_FAILURE;
270: }
271:
1.1.1.12 root 272: // 0. VM 種別を決定
1.1.1.9 root 273: // VM 種別文字列から vmtype を決定
274: const ConfigItem& item = gConfig->Find("vmtype");
1.1.1.14 root 275: vmstr = string_tolower(item.AsString());
1.1.1.9 root 276: if (vmstr == "x68030") {
1.1.1.13 root 277: vmtype = VMType::X68030;
1.1.1.9 root 278: } else if (vmstr == "luna") {
1.1.1.13 root 279: vmtype = VMType::LUNA1;
1.1.1.9 root 280: } else if (vmstr == "luna88k") {
1.1.1.13 root 281: vmtype = VMType::LUNA88K;
1.1.1.14 root 282: } else if (vmstr == "news") {
283: vmtype = VMType::NEWS;
1.1.1.17 root 284: } else if (vmstr == "virt-m68k" || vmstr == "virt68k") {
1.1.1.16 root 285: vmtype = VMType::VIRT68K;
1.1.1.2 root 286: } else {
1.1.1.9 root 287: if (item.GetFrom() == ConfigItem::FromInitial) {
288: // 未指定の時
289: warnx("vmtype must be specified");
1.1.1.2 root 290: } else {
1.1.1.9 root 291: // ユーザ由来の時
1.1.1.14 root 292: item.Err("Invalid vmtype");
1.1.1.2 root 293: }
1.1.1.9 root 294: return EXIT_FAILURE;
1.1.1.2 root 295: }
1.1 root 296:
1.1.1.14 root 297: // 0.5. VM が確定したところで SRAM 作成。
298: if (create_sram) {
299: if (vmtype == VMType::X68030) {
300: return CreateSRAM();
301: } else {
302: warnx("--create-sram is only for X68030 mode");
303: return EXIT_FAILURE;
304: }
305: }
306:
1.1.1.12 root 307: // 1. VM 作成
1.1.1.2 root 308: switch (vmtype) {
1.1.1.13 root 309: case VMType::X68030:
1.1.1.12 root 310: pVM.reset(new VM_X68030());
1.1.1.2 root 311: break;
312:
1.1.1.13 root 313: case VMType::LUNA1:
1.1.1.12 root 314: pVM.reset(new VM_LUNA1());
1.1.1.2 root 315: break;
316:
1.1.1.13 root 317: case VMType::LUNA88K:
1.1.1.12 root 318: pVM.reset(new VM_LUNA88K());
1.1.1.2 root 319: break;
1.1 root 320:
1.1.1.14 root 321: case VMType::NEWS:
322: pVM.reset(new VM_NEWS());
323: break;
324:
1.1.1.16 root 325: case VMType::VIRT68K:
326: pVM.reset(new VM_Virt68k());
327: break;
328:
1.1.1.2 root 329: default:
1.1.1.16 root 330: PANIC("vmtype=%s not configured", vmstr.c_str());
1.1.1.2 root 331: }
332:
1.1.1.19! root 333: // 2. 動的なコンストラクションその1
! 334: // 3. ログ出力用の EarlyInit
! 335: if (!pVM->Create(VM::CreationPhase::First)) {
1.1.1.12 root 336: pVM.reset();
1.1.1.5 root 337: return EXIT_FAILURE;
338: }
339:
1.1.1.19! root 340: // 4. 設定を確定。
1.1.1.5 root 341: // (VM コンストラクタで変数の増減があるのでそれより後、
342: // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.14 root 343: if (gConfig->Fix() == false) {
344: pVM.reset();
345: return EXIT_SUCCESS;
346: }
1.1.1.16 root 347:
348: // Fix() は配列から削除を行うため、これより前に取得していた ConfigItem
349: // のポインタや参照は無効になっている可能性がある。Fix() をまたいで
350: // ポインタや参照を使ってしまわないようここで一旦フォーカスを抜ける。
351: // なお Fix() 後は要素の削除は出来ないため、以降はこの問題は発生しない。
352:
353: return PASS;
354: }
355:
356: // VM の初期化、ステージ1 の後半。
357: // Config.Fix 以降。
358: int
359: MainApp::Init1b()
360: {
1.1.1.19! root 361: // 4.1 設定が確定したので --show-config なら設定内容を表示して終了。
1.1.1.2 root 362: if (show_config) {
1.1.1.3 root 363: gConfig->Show(show_config - 1);
1.1.1.12 root 364: pVM.reset();
1.1.1.5 root 365: return EXIT_SUCCESS;
1.1.1.2 root 366: }
367:
1.1.1.19! root 368: // 4.2 ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2 root 369: // -L help もここで処理。
370: if (!ParseLogopt()) {
1.1.1.12 root 371: pVM.reset();
1.1.1.5 root 372: return EXIT_FAILURE;
1.1.1.2 root 373: }
374:
1.1.1.19! root 375: // 4.3 -X のチェック。
1.1.1.16 root 376: // -L help より後にしないとヘルプが表示できない。
377: const ConfigItem& itemexec = gConfig->Find("exec-file");
378: if (exec_file == NULL) {
379: // -X が必要なケースで指定されてなければエラー。
380:
381: // 実行ファイル指定が必須の VM。
382: if (vmtype == VMType::NEWS || vmtype == VMType::VIRT68K) {
383: if (itemexec.GetFrom() == ConfigItem::FromInitial) {
384: warnx("vmtype=%s requires -X option (or exec-file).",
385: vmstr.c_str());
386: } else {
387: itemexec.Err("filename must be specified.");
388: }
389: return EXIT_FAILURE;
390: }
391:
392: // Human モード、MSX-DOS モードでも実行ファイル名が必要。
393: if (human_mode) {
394: warnx("-H option needs -X");
395: return EXIT_FAILURE;
396: }
397: if (msxdos_mode) {
398: warnx("-S option needs -X");
399: return EXIT_FAILURE;
400: }
401: } else {
402: // 実行ファイルのファイル名を間違えたくらいならここでエラーに出来る。
403: int r = access(exec_file, R_OK);
404: if (r != 0) {
405: itemexec.Err("%s", strerror(errno));
406: return EXIT_FAILURE;
407: }
408: }
409:
1.1.1.19! root 410: // 5. 動的コンストラクションその2
! 411: // ログ出力が出来る代わりにログクラスはもう増やせない。
! 412: if (!pVM->Create(VM::CreationPhase::Second)) {
! 413: pVM.reset();
! 414: return EXIT_FAILURE;
! 415: }
! 416:
1.1.1.5 root 417: return PASS;
1.1.1.2 root 418: }
419:
1.1.1.7 root 420: // VM の初期化、ステージ2。スレッド生成を伴う。
421: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2 root 422: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7 root 423: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2 root 424: bool
1.1.1.7 root 425: MainApp::Init2()
1.1.1.2 root 426: {
1.1.1.19! root 427: // 7. VM 初期化。
1.1.1.16 root 428: if (!pVM->Init()) {
1.1.1.2 root 429: return false;
430: }
431:
1.1.1.12 root 432: // メインスレッド名を設定
433: PTHREAD_SETNAME("Main");
434:
1.1.1.19! root 435: // 8. スレッド開始
1.1.1.16 root 436: if (!pVM->StartThread()) {
1.1.1.12 root 437: return false;
438: }
439:
1.1.1.19! root 440: // 9. 起動時設定の適用
1.1.1.16 root 441: if (!pVM->Apply()) {
1.1.1.2 root 442: return false;
443: }
444:
445: return true;
446: }
447:
448: // コマンドライン引数を処理する。
449: // 知らない引数とかがあればこちらで usage を表示して false を返す。
1.1.1.14 root 450: // 戻り値は、MainApp::PASS なら実行を継続、
451: // EXIT_SUCCESS/EXIT_FAILURE ならこのコードで終了。
452: int
1.1.1.2 root 453: MainApp::ParseOpt(int ac, char *av[])
454: {
1.1.1.6 root 455: struct stat st;
456: const char *cpath;
1.1.1.2 root 457: int c;
458:
1.1.1.17 root 459: cpath = NULL;
1.1.1.2 root 460:
1.1.1.17 root 461: while ((c = getopt_long(ac, av, "b:c:CdDfhHL:M:s:SvV:X:",
1.1.1.2 root 462: longopts, NULL)) != -1) {
463: switch (c) {
464: case 'b':
1.1.1.4 root 465: debug_breakaddr.push_back(optarg);
1.1.1.2 root 466: break;
467:
468: case 'c':
1.1.1.6 root 469: cpath = optarg;
470: // 空文字列なら再び初期値に
471: if (cpath[0] == '\0') {
1.1.1.17 root 472: cpath = NULL;
1.1.1.6 root 473: }
1.1.1.2 root 474: break;
475:
476: case 'C':
477: log_to_console = true;
478: break;
479:
480: case 'd':
481: debug_on_start = true;
482: break;
483:
484: case 'D':
1.1.1.12 root 485: // -D は -V debugger-driver=stdio と等価。
1.1.1.14 root 486: config_options.emplace_back("debugger-driver=stdio", "-D");
1.1.1.2 root 487: break;
488:
489: case 'f':
1.1.1.14 root 490: // -f は -V fast-mode=1 と等価。
491: config_options.emplace_back("fast-mode=1", "-f");
1.1.1.2 root 492: break;
493:
1.1.1.9 root 494: case 'H':
495: human_mode = true;
496: break;
497:
1.1.1.2 root 498: case 'L':
1.1.1.12 root 499: AddLogopt(optarg);
1.1.1.2 root 500: break;
501:
502: case 'M':
503: if (!monitor_opt.empty()) {
504: monitor_opt += ",";
505: }
506: monitor_opt += optarg;
507: break;
508:
509: case 's':
1.1.1.14 root 510: {
511: auto line = string_format("mainview-scale=%s", optarg);
512: config_options.emplace_back(line, "-s");
513: break;
514: }
515:
516: case 'S':
517: msxdos_mode = true;
1.1.1.2 root 518: break;
519:
520: case 'X':
1.1.1.16 root 521: {
522: // 相対パスはカレントディレクトリ起点とする。
523: char cwd[PATH_MAX];
524: getcwd(cwd, sizeof(cwd));
525: std::string path = NormalizePath(optarg, std::string(cwd));
526: std::string line = "exec-file=" + path;
527: config_options.emplace_back(line, "-X");
528:
529: // Human68k モードなら後続を引数にする。
530: // UNIX モードでは親和性が悪いのでしない。
531: if (human_mode) {
532: std::string arg;
533: arg = "exec-arg=";
534: for (int i = optind; i < ac; i++) {
535: if (i != optind) {
536: arg += " ";
537: }
538: arg += av[i];
1.1.1.2 root 539: }
1.1.1.16 root 540: optind = ac;
541: config_options.emplace_back(arg, "-X");
1.1.1.2 root 542: }
543: break;
1.1.1.16 root 544: }
1.1.1.2 root 545:
546: case 'v':
547: ShowVersion();
548: exit(0);
549:
550: case 'V':
1.1.1.14 root 551: config_options.emplace_back(optarg, "-V");
552: break;
553:
1.1.1.16 root 554: case OPT_bi_exg:
555: debug_breakinst_exg = true;
556: break;
557:
1.1.1.18 root 558: case OPT_console_log:
559: // この時点ではコンソールを持つ VM かどうかが分からないので
560: // コンソールのない設定でもエラーを出さない(出せない)。
561: console_logfile = optarg;
562: break;
563:
1.1.1.14 root 564: case OPT_create_sram:
565: create_sram = true;
1.1.1.2 root 566: break;
567:
568: case OPT_fontsize:
1.1.1.14 root 569: {
570: auto line = string_format("monitor-fontsize=%s", optarg);
571: config_options.emplace_back(line, "--fontsize");
1.1.1.2 root 572: break;
1.1.1.14 root 573: }
1.1.1.2 root 574:
575: case OPT_help:
576: ShowHelp(true);
1.1.1.14 root 577: return EXIT_SUCCESS;
1.1.1.2 root 578:
1.1.1.16 root 579: case OPT_initrd:
580: {
581: // 相対パスはカレントディレクトリを起点とする。
582: char cwd[PATH_MAX];
583: getcwd(cwd, sizeof(cwd));
584: std::string path = NormalizePath(optarg, std::string(cwd));
585: std::string line = "exec-initrd=" + path;
586: config_options.emplace_back(line, "--initrd");
587: break;
588: }
589:
1.1.1.12 root 590: case OPT_perf:
1.1.1.17 root 591: // パフォーマンス確認用。(Config::Update() 側で展開する)
592: config_options.emplace_back("--perf", "");
1.1.1.12 root 593: log_to_console = true;
594: break;
595:
1.1.1.2 root 596: case OPT_show_config:
1.1.1.3 root 597: show_config = 1;
598: break;
599:
600: case OPT_show_config_all:
601: show_config = 2;
1.1.1.2 root 602: break;
603:
1.1.1.8 root 604: case OPT_show_hostnet:
605: ShowHostnet();
1.1.1.14 root 606: return EXIT_SUCCESS;
1.1.1.8 root 607:
1.1.1.2 root 608: case 'h':
1.1.1.14 root 609: ShowHelp(false);
610: return EXIT_SUCCESS;
611:
1.1.1.2 root 612: default:
613: ShowHelp(false);
1.1.1.14 root 614: return EXIT_FAILURE;
1.1.1.2 root 615: }
616: }
617:
1.1.1.17 root 618: if (human_mode) {
619: // Human モードで -c ありなら、VM ディレクトリ指定。
620: // Human モードで -c 省略なら、VM ディレクトリなし (cpath==NULL)。
621: } else {
622: // Human モードでなければ、-c 省略は -c . と同義。
623: if (cpath == NULL) {
624: cpath = ".";
625: }
1.1.1.2 root 626: }
1.1.1.17 root 627:
628: if (cpath != NULL) {
629: // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
630: // nono.cfg を設定ファイルとする。
631: // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
632: // ディレクトリを VM ディレクトリとする。
633: // -c DIR => vmdir = DIR, vmfile = DIR/nono.cfg
634: // -c DIR/FILE => vmdir = DIR, vmfile = FILE
635: if (stat(cpath, &st) < 0) {
636: warn("stat %s", cpath);
637: return EXIT_FAILURE;
638: }
639: if (S_ISDIR(st.st_mode)) {
640: vmdir = std::string(cpath);
641: if (vmdir.back() != '/') {
642: vmdir += '/';
643: }
644: vmfile = vmdir + "nono.cfg";
645: } else if (S_ISREG(st.st_mode)) {
646: vmfile = std::string(cpath);
647: auto pos = vmfile.rfind('/');
648: if (pos != std::string::npos) {
649: vmdir = vmfile.substr(0, pos + 1);
650: } else {
651: vmdir = "./";
652: }
1.1.1.6 root 653: } else {
1.1.1.17 root 654: warnx("-c %s: path must be file or directory", cpath);
655: return EXIT_FAILURE;
1.1.1.6 root 656: }
1.1.1.2 root 657: }
658:
659: // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
660: // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
661: // 消して回らないと起動できないのも面倒なので -M オプション自体は
662: // 無意味だけど許容する。うーん。
663: if (IsCLI()) {
664: if (monitor_opt == "help") {
665: warnx("-Mhelp is not available on CLI");
1.1.1.14 root 666: return EXIT_FAILURE;
1.1.1.2 root 667: }
668: if (!monitor_opt.empty()) {
669: warnx("-M option is ignored on CLI");
1.1.1.14 root 670: return EXIT_FAILURE;
1.1.1.2 root 671: }
672: }
673:
1.1.1.16 root 674: return MainApp::PASS;
675: }
1.1.1.9 root 676:
1.1.1.16 root 677: // CPU の機能とかをチェック。
678: bool
679: MainApp::CheckCPU()
680: {
1.1.1.17 root 681: const ConfigItem& item_aff = gConfig->Find(".host-cpu-affinity");
682: std::string cfg_aff = item_aff.AsString();
683:
684: if (cfg_aff.empty() || cfg_aff == "auto") {
685: // 指定なし
686: // (auto も今の所指定なしと同じ)
687: cpu_affinity = CPUAffinity::None;
688: } else if (cfg_aff.find(':') == std::string::npos) {
689: item_aff.Err();
690: } else {
691: // 書式は "<key>:<list>"。
692: // <key> は "low", "high", "perf" のいずれか。
693: // <list> は "0,3-5,7" みたいな感じ。
694: std::vector<std::string> vals = string_split(cfg_aff, ':');
695: std::string key = string_trim(vals[0]);
696: std::string csv = vals[1];
697: if (key == "low") {
698: cpu_affinity = CPUAffinity::Low;
699: } else if (key == "high") {
700: cpu_affinity = CPUAffinity::High;
701: } else if (key == "perf") {
702: cpu_affinity = CPUAffinity::Perf;
703: } else {
704: item_aff.Err("Invalid affinity keyword");
705: }
706: cpu_list.resize(std::thread::hardware_concurrency());
707: auto errmsg = ParseCPUList(cpu_list, csv);
708: if (errmsg.empty() == false) {
709: item_aff.Err(errmsg);
710: return false;
711: }
712:
713: // 全部 true/false なら、これは意味がないのでクリアする。
714: uint num = std::count(cpu_list.begin(), cpu_list.end(), true);
715: if (num == 0) {
716: item_aff.Err("No valid cores specified (Ignoring it)");
717: cpu_affinity = CPUAffinity::None;
718: } else if (num == cpu_list.size()) {
719: item_aff.Err("All cores specified (Ignoring it)");
720: cpu_affinity = CPUAffinity::None;
721: }
722: }
723:
1.1.1.16 root 724: #if defined(HAVE_AVX2)
725: // AVX2
726: detect_avx2 = ::DetectAVX2();
727:
728: const ConfigItem& item_avx2 = gConfig->Find("host-avx2");
729: std::string cfg_avx2 = item_avx2.AsString();
730:
731: if (cfg_avx2 == "auto") {
732: enable_avx2 = detect_avx2;
733: } else if (cfg_avx2 == "no") {
734: enable_avx2 = false;
735: } else {
736: item_avx2.Err("must be either \"auto\" or \"no\".");
737: return false;
1.1.1.14 root 738: }
1.1.1.16 root 739: #endif
1.1.1.17 root 740:
741: #if defined(HAVE_NEON)
742: // NEON は aarch64 なら必ずある?
743: detect_neon = true;
744:
745: const ConfigItem& item_neon = gConfig->Find("host-neon");
746: std::string cfg_neon = item_neon.AsString();
747:
748: if (cfg_neon == "auto") {
749: enable_neon = detect_neon;
750: } else if (cfg_neon == "no") {
751: enable_neon = false;
752: } else {
753: item_neon.Err("must be either \"auto\" or \"no\".");
754: return false;
755: }
756: #endif
757:
758: // 何もしない。設定のチェックもしないほうがいい。
1.1.1.16 root 759: return true;
1.1.1.2 root 760: }
761:
1.1.1.17 root 762: // CPU 番号リストの文字列からビット配列を作って返す。
763: // cpulist は CPU コア数で初期化(resize)しておくこと。
764: // 成功すれば string.empty を返す。
765: // 失敗すればエラーメッセージを返す。
766: std::string
767: MainApp::ParseCPUList(std::vector<bool>& cpulist, const std::string& input)
768: {
769: if (input.empty()) {
770: return "<cpulist> must be specified";
771: }
772:
773: // 設定は "0,3-5,7,10-" のような書式
774: std::vector<std::string> csv = string_split(input, ',');
775: for (auto& r : csv) {
776: const char *str;
777: char *endp;
778: uint start, last;
779:
780: if (r.empty()) {
781: // 空なら無視する?
782: continue;
783: } else if (r.find('-') == std::string::npos) {
784: // '-' がなければ単独
785: str = &r[0];
786: errno = 0;
787: start = strtoul(str, &endp, 10);
788: if (endp == str || *endp != '\0' || errno == ERANGE) {
789: return "syntax error";
790: }
791: last = start;
792: } else if (r[0] == '-') {
793: // "-a" なら範囲 [0, a]
794: str = &r[1];
795: errno = 0;
796: last = strtoul(str, &endp, 10);
797: if (endp == str || *endp != '\0' || errno == ERANGE) {
798: return "syntax error";
799: }
800: start = 0;
801: } else if (r[r.size() - 1] == '-') {
802: // "a-" なら範囲 [a, MAX]
803: str = &r[0];
804: errno = 0;
805: start = strtoul(str, &endp, 10);
806: if (endp == str || endp != &r[r.size() - 1] || errno == ERANGE) {
807: return "syntax error";
808: }
809: last = cpulist.size();
810: } else {
811: // "a-b" なら範囲 [a, b]
812: str = &r[0];
813: errno = 0;
814: start = strtoul(str, &endp, 10);
815: if (endp == str || *endp != '-' || errno == ERANGE) {
816: return "syntax error";
817: }
818:
819: str = endp + 1;
820: last = strtoul(str, &endp, 10);
821: if (endp == str || *endp != '\0' || errno == ERANGE) {
822: return "syntax error";
823: }
824:
825: if (start > last) {
826: uint tmp = start;
827: start = last;
828: last = tmp;
829: }
830: }
831:
832: if (start >= cpulist.size()) {
833: return string_format("cpu number %u exceeds number of cores(%zu)",
834: start, cpulist.size());
835: }
836: if (last >= cpulist.size()) {
837: return string_format("cpu number %u exceeds number of cores(%zu)",
838: start, cpulist.size());
839: }
840: for (uint n = start; n <= last; n++) {
841: cpulist[n] = true;
842: }
843: }
844:
845: return "";
846: }
847:
1.1.1.2 root 848: // バージョンを表示
849: void
850: MainApp::ShowVersion() const
851: {
852: // ここは実行ファイル名によらず nono にする
853: fprintf(stderr, "nono version %d.%d.%d (%s)\n",
854: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
855: }
856:
1.1.1.12 root 857: // ログレベル指定文字列を logopt に追加する。
858: void
859: MainApp::AddLogopt(const char *opt)
860: {
861: if (logopt.empty() == false) {
862: logopt += ',';
863: }
864: logopt += opt;
865: }
866:
1.1.1.8 root 867: // ログレベル指定文字列を処理する。
868: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
869: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
870: // "help" があれば設定は行わず一覧を表示。
1.1 root 871: bool
1.1.1.2 root 872: MainApp::ParseLogopt()
1.1 root 873: {
1.1.1.8 root 874: std::vector<std::string> items;
875:
876: // 分解して..
1.1.1.12 root 877: items = string_split(logopt.c_str(), ',');
1.1.1.8 root 878:
879: // "help" があればヘルプを表示して終了
880: for (const auto& item : items) {
881: if (item == "help") {
882: std::vector<std::string> list = GetLogNames();
883: // less したいだろうから stderr ではなく stdout に出力する
884: for (const auto& name : list) {
885: printf(" %s\n", name.c_str());
886: }
887: return false;
1.1 root 888: }
1.1.1.8 root 889: }
890:
891: // ログレベルを設定
892: std::string errmsg;
893: if (SetLogopt(items, &errmsg) == false) {
894: warnx("%s", errmsg.c_str());
1.1 root 895: return false;
896: }
897:
1.1.1.8 root 898: return true;
899: }
1.1 root 900:
1.1.1.8 root 901: // ログレベルを設定する。
902: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
903: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
904: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
905: // 格納して false を返す。
906: // MainApp 内と Debugger からも呼ばれる。
907: /*static*/ bool
908: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
909: {
910: for (const auto& item : items) {
911: if (SetLogopt1(item.c_str(), errmsg) == false) {
912: return false;
913: }
914: }
915: if (0) { // デバッグ用
1.1.1.12 root 916: for (const auto o : gMainApp.GetObjects()) {
1.1.1.8 root 917: printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1 root 918: }
1.1.1.8 root 919: }
920: return true;
921: }
922:
923: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
924: // loglevel は省略なら 1 とする。
925: // logname が "all" なら全オブジェクトにセットする。
926: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
927: // そのオブジェクトにログレベルを設定して true を返す。
928: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
929: // false を返す。
930: // この関数は (このすぐ上の SetLogopt() を経由して)
931: // MainApp と Debugger から呼ばれることに注意。
932: /*static*/ bool
933: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
934: {
935: std::string name;
936: const char *v;
937: int level;
938:
939: v = strchr(item.c_str(), '=');
940: if (v) {
941: name = std::string(item.c_str(), v - item.c_str());
942: level = atoi(++v);
943: } else {
944: name = item;
945: level = 1;
946: }
947: // ここで name は変数名、level は値(省略されたら1)
948:
949: if (name.empty()) {
950: *errmsg = "logname must be specified";
951: return false;
952: }
1.1 root 953:
1.1.1.12 root 954: // 今の所、値域は -1 〜 9 ということにしておく。
955: if (level < -1) {
956: level = -1;
957: } else if (level > 9) {
958: level = 9;
959: }
960:
1.1.1.8 root 961: // "all" なら全部にセット
962: if (name == "all") {
1.1.1.12 root 963: for (auto obj : gMainApp.GetObjects()) {
1.1.1.8 root 964: if (obj->GetName().empty() == false) {
1.1.1.12 root 965: obj->SetLogLevel(level);
1.1.1.8 root 966: }
1.1 root 967: }
1.1.1.8 root 968: return true;
969: }
1.1.1.13 root 970: // "fdd" なら "fdd*" 全部にする
971: if (name == "fdd") {
972: for (auto obj : gMainApp.GetObjects()) {
973: if (strncasecmp(obj->GetName().c_str(), "fdd", 3) == 0) {
974: obj->SetLogLevel(level);
975: }
976: }
977: return true;
978: }
1.1.1.15 root 979: // "bankram" も "bankram[01]" 両方にする
980: if (name == "bankram") {
981: for (auto obj : gMainApp.GetObjects()) {
982: if (strncasecmp(obj->GetName().c_str(), "bankram", 7) == 0) {
983: obj->SetLogLevel(level);
984: }
985: }
986: return true;
987: }
1.1.1.16 root 988: // "gfpic" も "gfpic*" 全部にする
989: if (name == "gfpic") {
990: for (auto obj : gMainApp.GetObjects()) {
991: if (strncasecmp(obj->GetName().c_str(), "gfpic", 5) == 0) {
992: obj->SetLogLevel(level);
993: }
994: }
995: return true;
996: }
1.1.1.8 root 997:
1.1.1.12 root 998: // エイリアスリストを作る
999: using aliaslist_t = std::vector<std::pair<std::string, Object *>>;
1000: aliaslist_t alias_list;
1001: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 1002: const std::vector<std::string>& aliases = obj->GetAliases();
1003:
1004: for (const auto& a : aliases) {
1.1.1.12 root 1005: alias_list.emplace_back(a, obj);
1006: }
1007: }
1.1.1.8 root 1008:
1.1.1.12 root 1009: // エイリアスを完全一致のみのものと部分一致も許容するものに分ける
1010: aliaslist_t exact_alias;
1011: aliaslist_t partial_alias;
1012: for (const auto& a0 : alias_list) {
1013: bool exact = false;
1014: for (const auto& a1 : alias_list) {
1015: if (a0.first == a1.first) {
1016: continue;
1017: }
1018: if (starts_with_ignorecase(a0.first, a1.first)) {
1019: exact = true;
1020: break;
1.1 root 1021: }
1.1.1.8 root 1022: }
1.1.1.12 root 1023: if (exact) {
1024: exact_alias.push_back(a0);
1025: } else {
1026: partial_alias.push_back(a0);
1027: }
1028: }
1029:
1030: // 完全一致をまず調べる
1031: for (const auto& a : exact_alias) {
1032: if (strcasecmp(a.first.c_str(), name.c_str()) == 0) {
1033: a.second->SetLogLevel(level);
1034: return true;
1035: }
1036: }
1037:
1038: aliaslist_t found;
1039: for (const auto& a : partial_alias) {
1040: // 前方一致したら覚えておく
1041: if (starts_with_ignorecase(a.first, name)) {
1042: found.push_back(a);
1043: }
1.1.1.8 root 1044: }
1045:
1046: // 見付からない場合はエラー
1047: if (found.empty()) {
1048: *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
1049: return false;
1050: }
1051:
1052: // 1つだけなら確定
1053: if (found.size() == 1) {
1.1.1.12 root 1054: found[0].second->SetLogLevel(level);
1.1.1.8 root 1055: return true;
1056: }
1057:
1058: // 複数あれば候補文字列を作成
1059: *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
1060: name.c_str());
1.1.1.12 root 1061: for (const auto& cand : found) {
1062: *errmsg += string_format(" \"%s\"", cand.first.c_str());
1.1.1.8 root 1063: }
1064: return false;
1065: }
1066:
1067: // ログ名の一覧を取得する。
1068: // MainApp と Debugger から呼ばれる。
1069: /*static*/ std::vector<std::string>
1070: MainApp::GetLogNames()
1071: {
1072: std::vector<Object *> sortobj;
1073:
1074: // エイリアスを持つオブジェクトだけ抜き出す
1.1.1.12 root 1075: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 1076: if (obj->GetAliases().empty() == false) {
1077: sortobj.emplace_back(obj);
1078: }
1079: }
1080:
1081: // aliases の一語目で sortobj をソートする
1082: std::sort(sortobj.begin(), sortobj.end(),
1.1.1.12 root 1083: [](const auto a, const auto b) {
1.1.1.8 root 1084: const auto& sa = a->GetAliases()[0];
1085: const auto& sb = b->GetAliases()[0];
1.1.1.12 root 1086: return sa < sb;
1.1.1.8 root 1087: }
1088: );
1089:
1090: std::vector<std::string> list;
1091: for (const auto *obj : sortobj) {
1092: const auto& aliases = obj->GetAliases();
1093: std::string str;
1094:
1.1.1.12 root 1095: str = aliases[0];
1.1.1.8 root 1096: if (aliases.size() > 1) {
1097: str += " (alias:";
1098: for (int i = 1, sz = aliases.size(); i < sz; i++) {
1099: str += ' ';
1100: str += aliases[i];
1.1 root 1101: }
1.1.1.8 root 1102: str += ')';
1.1.1.6 root 1103: }
1.1.1.8 root 1104: list.emplace_back(str);
1.1 root 1105: }
1.1.1.8 root 1106: list.emplace_back("all");
1.1 root 1107:
1.1.1.8 root 1108: return list;
1.1 root 1109: }
1.1.1.2 root 1110:
1111: // 関連するファイルのパスを取得。
1.1.1.3 root 1112: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
1113: // ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
1114: // 何らか不都合があったとしても (例えばファイルサイズが 0 だとか
1115: // パーミッションが足りないとか) 次を試すとかはしない。
1.1.1.16 root 1116: // 2. 通常手順(相対パスは VM ディレクトリ起点) でパスを展開。
1117: // ファイルの有無は不問。
1.1.1.2 root 1118: std::string
1119: MainApp::SearchFile(const std::string& name) const
1120: {
1.1.1.3 root 1121: // 1. パス区切りを含んでなければ、ファイル名のみ
1122: if (name.find('/') == std::string::npos) {
1.1.1.16 root 1123: std::string path;
1124: struct stat st;
1125:
1.1.1.3 root 1126: // 1a. VM ディレクトリ
1127: path = GetVMDir() + name;
1128: if (stat(path.c_str(), &st) == 0) {
1129: return path;
1130: }
1131:
1.1.1.16 root 1132: // 1b. 親ディレクトリ
1.1.1.3 root 1133: path = GetVMDir() + "../" + name;
1134: if (stat(path.c_str(), &st) == 0) {
1135: return path;
1136: }
1137:
1138: // どちらもなければエラー
1139: return "";
1140: }
1.1.1.2 root 1141:
1.1.1.16 root 1142: // 2. 通常手順でパスを展開。
1143: return NormalizePath(name);
1144: }
1145:
1146: // path を必要なら絶対パスに展開して返す。
1147: // 相対パスは VM ディレクトリを起点とする。
1148: std::string
1149: MainApp::NormalizePath(const std::string& path) const
1150: {
1151: return NormalizePath(path, GetVMDir());
1152: }
1153:
1154: // path を必要なら絶対パスに展開して返す。
1155: // 相対パスは basedir を起点とする。
1156: std::string
1157: MainApp::NormalizePath(const std::string& path,
1158: const std::string& basedir) const
1159: {
1160: // 絶対パスならそのまま返す。
1161: if (path[0] == '/') {
1162: return path;
1.1.1.2 root 1163: }
1164:
1.1.1.16 root 1165: std::string newpath;
1166:
1167: if (path[0] == '~') {
1168: // 先頭が '~' ならホームディレクトリに展開。
1169:
1170: struct passwd *passwd = getpwuid(getuid());
1171: // 見付からないことはないはずだが、一応。
1172: if (passwd != NULL) {
1173: newpath = std::string(passwd->pw_dir);
1174: }
1175: newpath += &path[1];
1176: } else {
1177: // basedir からの相対パスとして展開。
1178:
1179: newpath = basedir;
1180: if (newpath.empty() || newpath.back() != '/') {
1181: newpath += '/';
1182: }
1183: newpath += path;
1.1.1.2 root 1184: }
1185:
1.1.1.16 root 1186: return newpath;
1.1.1.3 root 1187: }
1188:
1.1.1.14 root 1189: // 初回起動時用に SRAM.DAT を作成する。
1190: // 戻り値は EXIT_SUCCESS / EXIT_FAILURE。
1191: int
1192: MainApp::CreateSRAM()
1193: {
1194: char buf[16 * 1024];
1195: std::string filename;
1196: autofd fd;
1197: int r;
1198:
1199: filename = GetVMDir() + "SRAM.DAT";
1200:
1201: // 存在確認のため一旦読み込み専用で開いてみる。
1202: // オープン出来たら何もしない。
1203: fd = open(filename.c_str(), O_RDONLY);
1204: if (fd >= 0) {
1205: warnx("%s: Already exists", filename.c_str());
1206: return EXIT_FAILURE;
1207: }
1208:
1209: fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
1210: if (fd < 0) {
1211: warn("%s: open failed", filename.c_str());
1212: return EXIT_FAILURE;
1213: }
1214:
1215: memset(&buf[0], 0, sizeof(buf));
1216: for (int i = 0; i < SRAMDevice::InitialData.size(); i++) {
1217: buf[i] = SRAMDevice::InitialData[i];
1218: }
1219:
1220: r = write(fd, buf, sizeof(buf));
1221: if (r < 0) {
1222: warn("%s: write failed", filename.c_str());
1223: return EXIT_FAILURE;
1224: }
1225:
1226: fd.Close();
1227: warnx("created %s", filename.c_str());
1228: return EXIT_SUCCESS;
1229: }
1230:
1.1.1.13 root 1231: // 現在の VM が指定のケーパビリティを持っているか?
1.1.1.3 root 1232: bool
1.1.1.13 root 1233: MainApp::Has(VMCap cap) const
1.1.1.3 root 1234: {
1.1.1.13 root 1235: uint32 vmcap = 1U << (int)GetVMType();
1236: return (vmcap & (uint32)cap) != 0;
1.1.1.2 root 1237: }
1.1.1.8 root 1238:
1.1.1.16 root 1239: // VM 機種名を返す。
1240: const char *
1241: MainApp::GetVMName() const
1242: {
1243: if ((bool)pVM) {
1244: return pVM->GetVMName();
1245: } else {
1246: return NULL; // not configured
1247: }
1248: }
1249:
1.1.1.8 root 1250: // コンパイルされている host netdriver の一覧を表示。
1251: void
1252: MainApp::ShowHostnet() const
1253: {
1.1.1.12 root 1254: auto list = HostNetDevice::GetDrivers();
1.1.1.8 root 1255: for (const auto& name : list) {
1256: printf(" %s\n", name.c_str());
1257: }
1258: }
1.1.1.12 root 1259:
1.1.1.14 root 1260: // オブジェクト登録
1.1.1.12 root 1261: void
1.1.1.14 root 1262: MainApp::RegistObject(Object *obj)
1.1.1.12 root 1263: {
1.1.1.14 root 1264: // ID が重複していないかチェック (NONE なら重複可)
1265: auto id = obj->GetId();
1266: if (id != OBJ_NONE) {
1267: if (FindObject(id)) {
1268: PANIC("%s already exists", Object::GetIdStr(id));
1269: }
1270: }
1271:
1272: obj->logger = gMainApp.GetLogger();
1.1.1.12 root 1273:
1274: objects.push_back(obj);
1275: }
1276:
1.1.1.14 root 1277: // オブジェクト削除
1.1.1.12 root 1278: void
1.1.1.14 root 1279: MainApp::UnregistObject(Object *obj)
1.1.1.12 root 1280: {
1.1.1.14 root 1281: // 最初に見付かった一つを削除するだけでいい
1.1.1.12 root 1282: for (auto it = objects.begin(); it != objects.end(); ++it) {
1283: if (*it == obj) {
1284: objects.erase(it);
1285: break;
1286: }
1287: }
1288: }
1.1.1.14 root 1289:
1290: // 指定された id を持つオブジェクトを返す。なければ NULL を返す。
1291: Object *
1.1.1.17 root 1292: MainApp::FindObject(uint id) const
1.1.1.14 root 1293: {
1294: // NONE はここで検索しない (SCSI デバイスなど、そっちで検索する)
1295: if (id == OBJ_NONE) {
1296: return NULL;
1297: }
1298: for (auto obj : objects) {
1299: if (obj->GetId() == id) {
1300: return obj;
1301: }
1302: }
1303: return NULL;
1304: }
1305:
1306: // 指定された id を持つオブジェクトを探す。なければ assert する。
1307: Object *
1.1.1.17 root 1308: MainApp::GetObject(uint id) const
1.1.1.14 root 1309: {
1310: Object *obj = FindObject(id);
1311: if (__predict_false(obj == NULL)) {
1312: PANIC("objid=%s not found", Object::GetIdStr(id));
1313: }
1314: return obj;
1315: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.