|
|
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.12 root 333: // 2. 動的なコンストラクション
1.1.1.16 root 334: if (!pVM->Create()) {
1.1.1.12 root 335: pVM.reset();
1.1.1.5 root 336: return EXIT_FAILURE;
337: }
338:
1.1.1.16 root 339: // 3. 設定を確定。
1.1.1.5 root 340: // (VM コンストラクタで変数の増減があるのでそれより後、
341: // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.14 root 342: if (gConfig->Fix() == false) {
343: pVM.reset();
344: return EXIT_SUCCESS;
345: }
1.1.1.16 root 346:
347: // Fix() は配列から削除を行うため、これより前に取得していた ConfigItem
348: // のポインタや参照は無効になっている可能性がある。Fix() をまたいで
349: // ポインタや参照を使ってしまわないようここで一旦フォーカスを抜ける。
350: // なお Fix() 後は要素の削除は出来ないため、以降はこの問題は発生しない。
351:
352: return PASS;
353: }
354:
355: // VM の初期化、ステージ1 の後半。
356: // Config.Fix 以降。
357: int
358: MainApp::Init1b()
359: {
360: // 3.1 設定が確定したので --show-config なら設定内容を表示して終了。
1.1.1.2 root 361: if (show_config) {
1.1.1.3 root 362: gConfig->Show(show_config - 1);
1.1.1.12 root 363: pVM.reset();
1.1.1.5 root 364: return EXIT_SUCCESS;
1.1.1.2 root 365: }
366:
1.1.1.16 root 367: // 3.2 ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2 root 368: // -L help もここで処理。
369: if (!ParseLogopt()) {
1.1.1.12 root 370: pVM.reset();
1.1.1.5 root 371: return EXIT_FAILURE;
1.1.1.2 root 372: }
373:
1.1.1.16 root 374: // 3.3 -X のチェック。
375: // -L help より後にしないとヘルプが表示できない。
376: const ConfigItem& itemexec = gConfig->Find("exec-file");
377: if (exec_file == NULL) {
378: // -X が必要なケースで指定されてなければエラー。
379:
380: // 実行ファイル指定が必須の VM。
381: if (vmtype == VMType::NEWS || vmtype == VMType::VIRT68K) {
382: if (itemexec.GetFrom() == ConfigItem::FromInitial) {
383: warnx("vmtype=%s requires -X option (or exec-file).",
384: vmstr.c_str());
385: } else {
386: itemexec.Err("filename must be specified.");
387: }
388: return EXIT_FAILURE;
389: }
390:
391: // Human モード、MSX-DOS モードでも実行ファイル名が必要。
392: if (human_mode) {
393: warnx("-H option needs -X");
394: return EXIT_FAILURE;
395: }
396: if (msxdos_mode) {
397: warnx("-S option needs -X");
398: return EXIT_FAILURE;
399: }
400: } else {
401: // 実行ファイルのファイル名を間違えたくらいならここでエラーに出来る。
402: int r = access(exec_file, R_OK);
403: if (r != 0) {
404: itemexec.Err("%s", strerror(errno));
405: return EXIT_FAILURE;
406: }
407: }
408:
1.1.1.5 root 409: return PASS;
1.1.1.2 root 410: }
411:
1.1.1.7 root 412: // VM の初期化、ステージ2。スレッド生成を伴う。
413: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2 root 414: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7 root 415: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2 root 416: bool
1.1.1.7 root 417: MainApp::Init2()
1.1.1.2 root 418: {
1.1.1.12 root 419: // 5. VM 初期化。
1.1.1.16 root 420: if (!pVM->Init()) {
1.1.1.2 root 421: return false;
422: }
423:
1.1.1.12 root 424: // メインスレッド名を設定
425: PTHREAD_SETNAME("Main");
426:
427: // 6. スレッド開始
1.1.1.16 root 428: if (!pVM->StartThread()) {
1.1.1.12 root 429: return false;
430: }
431:
432: // 7. 起動時設定の適用
1.1.1.16 root 433: if (!pVM->Apply()) {
1.1.1.2 root 434: return false;
435: }
436:
437: return true;
438: }
439:
440: // コマンドライン引数を処理する。
441: // 知らない引数とかがあればこちらで usage を表示して false を返す。
1.1.1.14 root 442: // 戻り値は、MainApp::PASS なら実行を継続、
443: // EXIT_SUCCESS/EXIT_FAILURE ならこのコードで終了。
444: int
1.1.1.2 root 445: MainApp::ParseOpt(int ac, char *av[])
446: {
1.1.1.6 root 447: struct stat st;
448: const char *cpath;
1.1.1.2 root 449: int c;
450:
1.1.1.17 root 451: cpath = NULL;
1.1.1.2 root 452:
1.1.1.17 root 453: while ((c = getopt_long(ac, av, "b:c:CdDfhHL:M:s:SvV:X:",
1.1.1.2 root 454: longopts, NULL)) != -1) {
455: switch (c) {
456: case 'b':
1.1.1.4 root 457: debug_breakaddr.push_back(optarg);
1.1.1.2 root 458: break;
459:
460: case 'c':
1.1.1.6 root 461: cpath = optarg;
462: // 空文字列なら再び初期値に
463: if (cpath[0] == '\0') {
1.1.1.17 root 464: cpath = NULL;
1.1.1.6 root 465: }
1.1.1.2 root 466: break;
467:
468: case 'C':
469: log_to_console = true;
470: break;
471:
472: case 'd':
473: debug_on_start = true;
474: break;
475:
476: case 'D':
1.1.1.12 root 477: // -D は -V debugger-driver=stdio と等価。
1.1.1.14 root 478: config_options.emplace_back("debugger-driver=stdio", "-D");
1.1.1.2 root 479: break;
480:
481: case 'f':
1.1.1.14 root 482: // -f は -V fast-mode=1 と等価。
483: config_options.emplace_back("fast-mode=1", "-f");
1.1.1.2 root 484: break;
485:
1.1.1.9 root 486: case 'H':
487: human_mode = true;
488: break;
489:
1.1.1.2 root 490: case 'L':
1.1.1.12 root 491: AddLogopt(optarg);
1.1.1.2 root 492: break;
493:
494: case 'M':
495: if (!monitor_opt.empty()) {
496: monitor_opt += ",";
497: }
498: monitor_opt += optarg;
499: break;
500:
501: case 's':
1.1.1.14 root 502: {
503: auto line = string_format("mainview-scale=%s", optarg);
504: config_options.emplace_back(line, "-s");
505: break;
506: }
507:
508: case 'S':
509: msxdos_mode = true;
1.1.1.2 root 510: break;
511:
512: case 'X':
1.1.1.16 root 513: {
514: // 相対パスはカレントディレクトリ起点とする。
515: char cwd[PATH_MAX];
516: getcwd(cwd, sizeof(cwd));
517: std::string path = NormalizePath(optarg, std::string(cwd));
518: std::string line = "exec-file=" + path;
519: config_options.emplace_back(line, "-X");
520:
521: // Human68k モードなら後続を引数にする。
522: // UNIX モードでは親和性が悪いのでしない。
523: if (human_mode) {
524: std::string arg;
525: arg = "exec-arg=";
526: for (int i = optind; i < ac; i++) {
527: if (i != optind) {
528: arg += " ";
529: }
530: arg += av[i];
1.1.1.2 root 531: }
1.1.1.16 root 532: optind = ac;
533: config_options.emplace_back(arg, "-X");
1.1.1.2 root 534: }
535: break;
1.1.1.16 root 536: }
1.1.1.2 root 537:
538: case 'v':
539: ShowVersion();
540: exit(0);
541:
542: case 'V':
1.1.1.14 root 543: config_options.emplace_back(optarg, "-V");
544: break;
545:
1.1.1.16 root 546: case OPT_bi_exg:
547: debug_breakinst_exg = true;
548: break;
549:
1.1.1.18! root 550: case OPT_console_log:
! 551: // この時点ではコンソールを持つ VM かどうかが分からないので
! 552: // コンソールのない設定でもエラーを出さない(出せない)。
! 553: console_logfile = optarg;
! 554: break;
! 555:
1.1.1.14 root 556: case OPT_create_sram:
557: create_sram = true;
1.1.1.2 root 558: break;
559:
560: case OPT_fontsize:
1.1.1.14 root 561: {
562: auto line = string_format("monitor-fontsize=%s", optarg);
563: config_options.emplace_back(line, "--fontsize");
1.1.1.2 root 564: break;
1.1.1.14 root 565: }
1.1.1.2 root 566:
567: case OPT_help:
568: ShowHelp(true);
1.1.1.14 root 569: return EXIT_SUCCESS;
1.1.1.2 root 570:
1.1.1.16 root 571: case OPT_initrd:
572: {
573: // 相対パスはカレントディレクトリを起点とする。
574: char cwd[PATH_MAX];
575: getcwd(cwd, sizeof(cwd));
576: std::string path = NormalizePath(optarg, std::string(cwd));
577: std::string line = "exec-initrd=" + path;
578: config_options.emplace_back(line, "--initrd");
579: break;
580: }
581:
1.1.1.12 root 582: case OPT_perf:
1.1.1.17 root 583: // パフォーマンス確認用。(Config::Update() 側で展開する)
584: config_options.emplace_back("--perf", "");
1.1.1.12 root 585: log_to_console = true;
586: break;
587:
1.1.1.2 root 588: case OPT_show_config:
1.1.1.3 root 589: show_config = 1;
590: break;
591:
592: case OPT_show_config_all:
593: show_config = 2;
1.1.1.2 root 594: break;
595:
1.1.1.8 root 596: case OPT_show_hostnet:
597: ShowHostnet();
1.1.1.14 root 598: return EXIT_SUCCESS;
1.1.1.8 root 599:
1.1.1.2 root 600: case 'h':
1.1.1.14 root 601: ShowHelp(false);
602: return EXIT_SUCCESS;
603:
1.1.1.2 root 604: default:
605: ShowHelp(false);
1.1.1.14 root 606: return EXIT_FAILURE;
1.1.1.2 root 607: }
608: }
609:
1.1.1.17 root 610: if (human_mode) {
611: // Human モードで -c ありなら、VM ディレクトリ指定。
612: // Human モードで -c 省略なら、VM ディレクトリなし (cpath==NULL)。
613: } else {
614: // Human モードでなければ、-c 省略は -c . と同義。
615: if (cpath == NULL) {
616: cpath = ".";
617: }
1.1.1.2 root 618: }
1.1.1.17 root 619:
620: if (cpath != NULL) {
621: // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
622: // nono.cfg を設定ファイルとする。
623: // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
624: // ディレクトリを VM ディレクトリとする。
625: // -c DIR => vmdir = DIR, vmfile = DIR/nono.cfg
626: // -c DIR/FILE => vmdir = DIR, vmfile = FILE
627: if (stat(cpath, &st) < 0) {
628: warn("stat %s", cpath);
629: return EXIT_FAILURE;
630: }
631: if (S_ISDIR(st.st_mode)) {
632: vmdir = std::string(cpath);
633: if (vmdir.back() != '/') {
634: vmdir += '/';
635: }
636: vmfile = vmdir + "nono.cfg";
637: } else if (S_ISREG(st.st_mode)) {
638: vmfile = std::string(cpath);
639: auto pos = vmfile.rfind('/');
640: if (pos != std::string::npos) {
641: vmdir = vmfile.substr(0, pos + 1);
642: } else {
643: vmdir = "./";
644: }
1.1.1.6 root 645: } else {
1.1.1.17 root 646: warnx("-c %s: path must be file or directory", cpath);
647: return EXIT_FAILURE;
1.1.1.6 root 648: }
1.1.1.2 root 649: }
650:
651: // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
652: // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
653: // 消して回らないと起動できないのも面倒なので -M オプション自体は
654: // 無意味だけど許容する。うーん。
655: if (IsCLI()) {
656: if (monitor_opt == "help") {
657: warnx("-Mhelp is not available on CLI");
1.1.1.14 root 658: return EXIT_FAILURE;
1.1.1.2 root 659: }
660: if (!monitor_opt.empty()) {
661: warnx("-M option is ignored on CLI");
1.1.1.14 root 662: return EXIT_FAILURE;
1.1.1.2 root 663: }
664: }
665:
1.1.1.16 root 666: return MainApp::PASS;
667: }
1.1.1.9 root 668:
1.1.1.16 root 669: // CPU の機能とかをチェック。
670: bool
671: MainApp::CheckCPU()
672: {
1.1.1.17 root 673: const ConfigItem& item_aff = gConfig->Find(".host-cpu-affinity");
674: std::string cfg_aff = item_aff.AsString();
675:
676: if (cfg_aff.empty() || cfg_aff == "auto") {
677: // 指定なし
678: // (auto も今の所指定なしと同じ)
679: cpu_affinity = CPUAffinity::None;
680: } else if (cfg_aff.find(':') == std::string::npos) {
681: item_aff.Err();
682: } else {
683: // 書式は "<key>:<list>"。
684: // <key> は "low", "high", "perf" のいずれか。
685: // <list> は "0,3-5,7" みたいな感じ。
686: std::vector<std::string> vals = string_split(cfg_aff, ':');
687: std::string key = string_trim(vals[0]);
688: std::string csv = vals[1];
689: if (key == "low") {
690: cpu_affinity = CPUAffinity::Low;
691: } else if (key == "high") {
692: cpu_affinity = CPUAffinity::High;
693: } else if (key == "perf") {
694: cpu_affinity = CPUAffinity::Perf;
695: } else {
696: item_aff.Err("Invalid affinity keyword");
697: }
698: cpu_list.resize(std::thread::hardware_concurrency());
699: auto errmsg = ParseCPUList(cpu_list, csv);
700: if (errmsg.empty() == false) {
701: item_aff.Err(errmsg);
702: return false;
703: }
704:
705: // 全部 true/false なら、これは意味がないのでクリアする。
706: uint num = std::count(cpu_list.begin(), cpu_list.end(), true);
707: if (num == 0) {
708: item_aff.Err("No valid cores specified (Ignoring it)");
709: cpu_affinity = CPUAffinity::None;
710: } else if (num == cpu_list.size()) {
711: item_aff.Err("All cores specified (Ignoring it)");
712: cpu_affinity = CPUAffinity::None;
713: }
714: }
715:
1.1.1.16 root 716: #if defined(HAVE_AVX2)
717: // AVX2
718: detect_avx2 = ::DetectAVX2();
719:
720: const ConfigItem& item_avx2 = gConfig->Find("host-avx2");
721: std::string cfg_avx2 = item_avx2.AsString();
722:
723: if (cfg_avx2 == "auto") {
724: enable_avx2 = detect_avx2;
725: } else if (cfg_avx2 == "no") {
726: enable_avx2 = false;
727: } else {
728: item_avx2.Err("must be either \"auto\" or \"no\".");
729: return false;
1.1.1.14 root 730: }
1.1.1.16 root 731: #endif
1.1.1.17 root 732:
733: #if defined(HAVE_NEON)
734: // NEON は aarch64 なら必ずある?
735: detect_neon = true;
736:
737: const ConfigItem& item_neon = gConfig->Find("host-neon");
738: std::string cfg_neon = item_neon.AsString();
739:
740: if (cfg_neon == "auto") {
741: enable_neon = detect_neon;
742: } else if (cfg_neon == "no") {
743: enable_neon = false;
744: } else {
745: item_neon.Err("must be either \"auto\" or \"no\".");
746: return false;
747: }
748: #endif
749:
750: // 何もしない。設定のチェックもしないほうがいい。
1.1.1.16 root 751: return true;
1.1.1.2 root 752: }
753:
1.1.1.17 root 754: // CPU 番号リストの文字列からビット配列を作って返す。
755: // cpulist は CPU コア数で初期化(resize)しておくこと。
756: // 成功すれば string.empty を返す。
757: // 失敗すればエラーメッセージを返す。
758: std::string
759: MainApp::ParseCPUList(std::vector<bool>& cpulist, const std::string& input)
760: {
761: if (input.empty()) {
762: return "<cpulist> must be specified";
763: }
764:
765: // 設定は "0,3-5,7,10-" のような書式
766: std::vector<std::string> csv = string_split(input, ',');
767: for (auto& r : csv) {
768: const char *str;
769: char *endp;
770: uint start, last;
771:
772: if (r.empty()) {
773: // 空なら無視する?
774: continue;
775: } else if (r.find('-') == std::string::npos) {
776: // '-' がなければ単独
777: str = &r[0];
778: errno = 0;
779: start = strtoul(str, &endp, 10);
780: if (endp == str || *endp != '\0' || errno == ERANGE) {
781: return "syntax error";
782: }
783: last = start;
784: } else if (r[0] == '-') {
785: // "-a" なら範囲 [0, a]
786: str = &r[1];
787: errno = 0;
788: last = strtoul(str, &endp, 10);
789: if (endp == str || *endp != '\0' || errno == ERANGE) {
790: return "syntax error";
791: }
792: start = 0;
793: } else if (r[r.size() - 1] == '-') {
794: // "a-" なら範囲 [a, MAX]
795: str = &r[0];
796: errno = 0;
797: start = strtoul(str, &endp, 10);
798: if (endp == str || endp != &r[r.size() - 1] || errno == ERANGE) {
799: return "syntax error";
800: }
801: last = cpulist.size();
802: } else {
803: // "a-b" なら範囲 [a, b]
804: str = &r[0];
805: errno = 0;
806: start = strtoul(str, &endp, 10);
807: if (endp == str || *endp != '-' || errno == ERANGE) {
808: return "syntax error";
809: }
810:
811: str = endp + 1;
812: last = strtoul(str, &endp, 10);
813: if (endp == str || *endp != '\0' || errno == ERANGE) {
814: return "syntax error";
815: }
816:
817: if (start > last) {
818: uint tmp = start;
819: start = last;
820: last = tmp;
821: }
822: }
823:
824: if (start >= cpulist.size()) {
825: return string_format("cpu number %u exceeds number of cores(%zu)",
826: start, cpulist.size());
827: }
828: if (last >= cpulist.size()) {
829: return string_format("cpu number %u exceeds number of cores(%zu)",
830: start, cpulist.size());
831: }
832: for (uint n = start; n <= last; n++) {
833: cpulist[n] = true;
834: }
835: }
836:
837: return "";
838: }
839:
1.1.1.2 root 840: // バージョンを表示
841: void
842: MainApp::ShowVersion() const
843: {
844: // ここは実行ファイル名によらず nono にする
845: fprintf(stderr, "nono version %d.%d.%d (%s)\n",
846: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
847: }
848:
1.1.1.12 root 849: // ログレベル指定文字列を logopt に追加する。
850: void
851: MainApp::AddLogopt(const char *opt)
852: {
853: if (logopt.empty() == false) {
854: logopt += ',';
855: }
856: logopt += opt;
857: }
858:
1.1.1.8 root 859: // ログレベル指定文字列を処理する。
860: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
861: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
862: // "help" があれば設定は行わず一覧を表示。
1.1 root 863: bool
1.1.1.2 root 864: MainApp::ParseLogopt()
1.1 root 865: {
1.1.1.8 root 866: std::vector<std::string> items;
867:
868: // 分解して..
1.1.1.12 root 869: items = string_split(logopt.c_str(), ',');
1.1.1.8 root 870:
871: // "help" があればヘルプを表示して終了
872: for (const auto& item : items) {
873: if (item == "help") {
874: std::vector<std::string> list = GetLogNames();
875: // less したいだろうから stderr ではなく stdout に出力する
876: for (const auto& name : list) {
877: printf(" %s\n", name.c_str());
878: }
879: return false;
1.1 root 880: }
1.1.1.8 root 881: }
882:
883: // ログレベルを設定
884: std::string errmsg;
885: if (SetLogopt(items, &errmsg) == false) {
886: warnx("%s", errmsg.c_str());
1.1 root 887: return false;
888: }
889:
1.1.1.8 root 890: return true;
891: }
1.1 root 892:
1.1.1.8 root 893: // ログレベルを設定する。
894: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
895: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
896: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
897: // 格納して false を返す。
898: // MainApp 内と Debugger からも呼ばれる。
899: /*static*/ bool
900: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
901: {
902: for (const auto& item : items) {
903: if (SetLogopt1(item.c_str(), errmsg) == false) {
904: return false;
905: }
906: }
907: if (0) { // デバッグ用
1.1.1.12 root 908: for (const auto o : gMainApp.GetObjects()) {
1.1.1.8 root 909: printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1 root 910: }
1.1.1.8 root 911: }
912: return true;
913: }
914:
915: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
916: // loglevel は省略なら 1 とする。
917: // logname が "all" なら全オブジェクトにセットする。
918: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
919: // そのオブジェクトにログレベルを設定して true を返す。
920: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
921: // false を返す。
922: // この関数は (このすぐ上の SetLogopt() を経由して)
923: // MainApp と Debugger から呼ばれることに注意。
924: /*static*/ bool
925: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
926: {
927: std::string name;
928: const char *v;
929: int level;
930:
931: v = strchr(item.c_str(), '=');
932: if (v) {
933: name = std::string(item.c_str(), v - item.c_str());
934: level = atoi(++v);
935: } else {
936: name = item;
937: level = 1;
938: }
939: // ここで name は変数名、level は値(省略されたら1)
940:
941: if (name.empty()) {
942: *errmsg = "logname must be specified";
943: return false;
944: }
1.1 root 945:
1.1.1.12 root 946: // 今の所、値域は -1 〜 9 ということにしておく。
947: if (level < -1) {
948: level = -1;
949: } else if (level > 9) {
950: level = 9;
951: }
952:
1.1.1.8 root 953: // "all" なら全部にセット
954: if (name == "all") {
1.1.1.12 root 955: for (auto obj : gMainApp.GetObjects()) {
1.1.1.8 root 956: if (obj->GetName().empty() == false) {
1.1.1.12 root 957: obj->SetLogLevel(level);
1.1.1.8 root 958: }
1.1 root 959: }
1.1.1.8 root 960: return true;
961: }
1.1.1.13 root 962: // "fdd" なら "fdd*" 全部にする
963: if (name == "fdd") {
964: for (auto obj : gMainApp.GetObjects()) {
965: if (strncasecmp(obj->GetName().c_str(), "fdd", 3) == 0) {
966: obj->SetLogLevel(level);
967: }
968: }
969: return true;
970: }
1.1.1.15 root 971: // "bankram" も "bankram[01]" 両方にする
972: if (name == "bankram") {
973: for (auto obj : gMainApp.GetObjects()) {
974: if (strncasecmp(obj->GetName().c_str(), "bankram", 7) == 0) {
975: obj->SetLogLevel(level);
976: }
977: }
978: return true;
979: }
1.1.1.16 root 980: // "gfpic" も "gfpic*" 全部にする
981: if (name == "gfpic") {
982: for (auto obj : gMainApp.GetObjects()) {
983: if (strncasecmp(obj->GetName().c_str(), "gfpic", 5) == 0) {
984: obj->SetLogLevel(level);
985: }
986: }
987: return true;
988: }
1.1.1.8 root 989:
1.1.1.12 root 990: // エイリアスリストを作る
991: using aliaslist_t = std::vector<std::pair<std::string, Object *>>;
992: aliaslist_t alias_list;
993: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 994: const std::vector<std::string>& aliases = obj->GetAliases();
995:
996: for (const auto& a : aliases) {
1.1.1.12 root 997: alias_list.emplace_back(a, obj);
998: }
999: }
1.1.1.8 root 1000:
1.1.1.12 root 1001: // エイリアスを完全一致のみのものと部分一致も許容するものに分ける
1002: aliaslist_t exact_alias;
1003: aliaslist_t partial_alias;
1004: for (const auto& a0 : alias_list) {
1005: bool exact = false;
1006: for (const auto& a1 : alias_list) {
1007: if (a0.first == a1.first) {
1008: continue;
1009: }
1010: if (starts_with_ignorecase(a0.first, a1.first)) {
1011: exact = true;
1012: break;
1.1 root 1013: }
1.1.1.8 root 1014: }
1.1.1.12 root 1015: if (exact) {
1016: exact_alias.push_back(a0);
1017: } else {
1018: partial_alias.push_back(a0);
1019: }
1020: }
1021:
1022: // 完全一致をまず調べる
1023: for (const auto& a : exact_alias) {
1024: if (strcasecmp(a.first.c_str(), name.c_str()) == 0) {
1025: a.second->SetLogLevel(level);
1026: return true;
1027: }
1028: }
1029:
1030: aliaslist_t found;
1031: for (const auto& a : partial_alias) {
1032: // 前方一致したら覚えておく
1033: if (starts_with_ignorecase(a.first, name)) {
1034: found.push_back(a);
1035: }
1.1.1.8 root 1036: }
1037:
1038: // 見付からない場合はエラー
1039: if (found.empty()) {
1040: *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
1041: return false;
1042: }
1043:
1044: // 1つだけなら確定
1045: if (found.size() == 1) {
1.1.1.12 root 1046: found[0].second->SetLogLevel(level);
1.1.1.8 root 1047: return true;
1048: }
1049:
1050: // 複数あれば候補文字列を作成
1051: *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
1052: name.c_str());
1.1.1.12 root 1053: for (const auto& cand : found) {
1054: *errmsg += string_format(" \"%s\"", cand.first.c_str());
1.1.1.8 root 1055: }
1056: return false;
1057: }
1058:
1059: // ログ名の一覧を取得する。
1060: // MainApp と Debugger から呼ばれる。
1061: /*static*/ std::vector<std::string>
1062: MainApp::GetLogNames()
1063: {
1064: std::vector<Object *> sortobj;
1065:
1066: // エイリアスを持つオブジェクトだけ抜き出す
1.1.1.12 root 1067: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 1068: if (obj->GetAliases().empty() == false) {
1069: sortobj.emplace_back(obj);
1070: }
1071: }
1072:
1073: // aliases の一語目で sortobj をソートする
1074: std::sort(sortobj.begin(), sortobj.end(),
1.1.1.12 root 1075: [](const auto a, const auto b) {
1.1.1.8 root 1076: const auto& sa = a->GetAliases()[0];
1077: const auto& sb = b->GetAliases()[0];
1.1.1.12 root 1078: return sa < sb;
1.1.1.8 root 1079: }
1080: );
1081:
1082: std::vector<std::string> list;
1083: for (const auto *obj : sortobj) {
1084: const auto& aliases = obj->GetAliases();
1085: std::string str;
1086:
1.1.1.12 root 1087: str = aliases[0];
1.1.1.8 root 1088: if (aliases.size() > 1) {
1089: str += " (alias:";
1090: for (int i = 1, sz = aliases.size(); i < sz; i++) {
1091: str += ' ';
1092: str += aliases[i];
1.1 root 1093: }
1.1.1.8 root 1094: str += ')';
1.1.1.6 root 1095: }
1.1.1.8 root 1096: list.emplace_back(str);
1.1 root 1097: }
1.1.1.8 root 1098: list.emplace_back("all");
1.1 root 1099:
1.1.1.8 root 1100: return list;
1.1 root 1101: }
1.1.1.2 root 1102:
1103: // 関連するファイルのパスを取得。
1.1.1.3 root 1104: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
1105: // ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
1106: // 何らか不都合があったとしても (例えばファイルサイズが 0 だとか
1107: // パーミッションが足りないとか) 次を試すとかはしない。
1.1.1.16 root 1108: // 2. 通常手順(相対パスは VM ディレクトリ起点) でパスを展開。
1109: // ファイルの有無は不問。
1.1.1.2 root 1110: std::string
1111: MainApp::SearchFile(const std::string& name) const
1112: {
1.1.1.3 root 1113: // 1. パス区切りを含んでなければ、ファイル名のみ
1114: if (name.find('/') == std::string::npos) {
1.1.1.16 root 1115: std::string path;
1116: struct stat st;
1117:
1.1.1.3 root 1118: // 1a. VM ディレクトリ
1119: path = GetVMDir() + name;
1120: if (stat(path.c_str(), &st) == 0) {
1121: return path;
1122: }
1123:
1.1.1.16 root 1124: // 1b. 親ディレクトリ
1.1.1.3 root 1125: path = GetVMDir() + "../" + name;
1126: if (stat(path.c_str(), &st) == 0) {
1127: return path;
1128: }
1129:
1130: // どちらもなければエラー
1131: return "";
1132: }
1.1.1.2 root 1133:
1.1.1.16 root 1134: // 2. 通常手順でパスを展開。
1135: return NormalizePath(name);
1136: }
1137:
1138: // path を必要なら絶対パスに展開して返す。
1139: // 相対パスは VM ディレクトリを起点とする。
1140: std::string
1141: MainApp::NormalizePath(const std::string& path) const
1142: {
1143: return NormalizePath(path, GetVMDir());
1144: }
1145:
1146: // path を必要なら絶対パスに展開して返す。
1147: // 相対パスは basedir を起点とする。
1148: std::string
1149: MainApp::NormalizePath(const std::string& path,
1150: const std::string& basedir) const
1151: {
1152: // 絶対パスならそのまま返す。
1153: if (path[0] == '/') {
1154: return path;
1.1.1.2 root 1155: }
1156:
1.1.1.16 root 1157: std::string newpath;
1158:
1159: if (path[0] == '~') {
1160: // 先頭が '~' ならホームディレクトリに展開。
1161:
1162: struct passwd *passwd = getpwuid(getuid());
1163: // 見付からないことはないはずだが、一応。
1164: if (passwd != NULL) {
1165: newpath = std::string(passwd->pw_dir);
1166: }
1167: newpath += &path[1];
1168: } else {
1169: // basedir からの相対パスとして展開。
1170:
1171: newpath = basedir;
1172: if (newpath.empty() || newpath.back() != '/') {
1173: newpath += '/';
1174: }
1175: newpath += path;
1.1.1.2 root 1176: }
1177:
1.1.1.16 root 1178: return newpath;
1.1.1.3 root 1179: }
1180:
1.1.1.14 root 1181: // 初回起動時用に SRAM.DAT を作成する。
1182: // 戻り値は EXIT_SUCCESS / EXIT_FAILURE。
1183: int
1184: MainApp::CreateSRAM()
1185: {
1186: char buf[16 * 1024];
1187: std::string filename;
1188: autofd fd;
1189: int r;
1190:
1191: filename = GetVMDir() + "SRAM.DAT";
1192:
1193: // 存在確認のため一旦読み込み専用で開いてみる。
1194: // オープン出来たら何もしない。
1195: fd = open(filename.c_str(), O_RDONLY);
1196: if (fd >= 0) {
1197: warnx("%s: Already exists", filename.c_str());
1198: return EXIT_FAILURE;
1199: }
1200:
1201: fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
1202: if (fd < 0) {
1203: warn("%s: open failed", filename.c_str());
1204: return EXIT_FAILURE;
1205: }
1206:
1207: memset(&buf[0], 0, sizeof(buf));
1208: for (int i = 0; i < SRAMDevice::InitialData.size(); i++) {
1209: buf[i] = SRAMDevice::InitialData[i];
1210: }
1211:
1212: r = write(fd, buf, sizeof(buf));
1213: if (r < 0) {
1214: warn("%s: write failed", filename.c_str());
1215: return EXIT_FAILURE;
1216: }
1217:
1218: fd.Close();
1219: warnx("created %s", filename.c_str());
1220: return EXIT_SUCCESS;
1221: }
1222:
1.1.1.13 root 1223: // 現在の VM が指定のケーパビリティを持っているか?
1.1.1.3 root 1224: bool
1.1.1.13 root 1225: MainApp::Has(VMCap cap) const
1.1.1.3 root 1226: {
1.1.1.13 root 1227: uint32 vmcap = 1U << (int)GetVMType();
1228: return (vmcap & (uint32)cap) != 0;
1.1.1.2 root 1229: }
1.1.1.8 root 1230:
1.1.1.16 root 1231: // VM 機種名を返す。
1232: const char *
1233: MainApp::GetVMName() const
1234: {
1235: if ((bool)pVM) {
1236: return pVM->GetVMName();
1237: } else {
1238: return NULL; // not configured
1239: }
1240: }
1241:
1.1.1.8 root 1242: // コンパイルされている host netdriver の一覧を表示。
1243: void
1244: MainApp::ShowHostnet() const
1245: {
1.1.1.12 root 1246: auto list = HostNetDevice::GetDrivers();
1.1.1.8 root 1247: for (const auto& name : list) {
1248: printf(" %s\n", name.c_str());
1249: }
1250: }
1.1.1.12 root 1251:
1.1.1.14 root 1252: // オブジェクト登録
1.1.1.12 root 1253: void
1.1.1.14 root 1254: MainApp::RegistObject(Object *obj)
1.1.1.12 root 1255: {
1.1.1.14 root 1256: // ID が重複していないかチェック (NONE なら重複可)
1257: auto id = obj->GetId();
1258: if (id != OBJ_NONE) {
1259: if (FindObject(id)) {
1260: PANIC("%s already exists", Object::GetIdStr(id));
1261: }
1262: }
1263:
1264: obj->logger = gMainApp.GetLogger();
1.1.1.12 root 1265:
1266: objects.push_back(obj);
1267: }
1268:
1.1.1.14 root 1269: // オブジェクト削除
1.1.1.12 root 1270: void
1.1.1.14 root 1271: MainApp::UnregistObject(Object *obj)
1.1.1.12 root 1272: {
1.1.1.14 root 1273: // 最初に見付かった一つを削除するだけでいい
1.1.1.12 root 1274: for (auto it = objects.begin(); it != objects.end(); ++it) {
1275: if (*it == obj) {
1276: objects.erase(it);
1277: break;
1278: }
1279: }
1280: }
1.1.1.14 root 1281:
1282: // 指定された id を持つオブジェクトを返す。なければ NULL を返す。
1283: Object *
1.1.1.17 root 1284: MainApp::FindObject(uint id) const
1.1.1.14 root 1285: {
1286: // NONE はここで検索しない (SCSI デバイスなど、そっちで検索する)
1287: if (id == OBJ_NONE) {
1288: return NULL;
1289: }
1290: for (auto obj : objects) {
1291: if (obj->GetId() == id) {
1292: return obj;
1293: }
1294: }
1295: return NULL;
1296: }
1297:
1298: // 指定された id を持つオブジェクトを探す。なければ assert する。
1299: Object *
1.1.1.17 root 1300: MainApp::GetObject(uint id) const
1.1.1.14 root 1301: {
1302: Object *obj = FindObject(id);
1303: if (__predict_false(obj == NULL)) {
1304: PANIC("objid=%s not found", Object::GetIdStr(id));
1305: }
1306: return obj;
1307: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.