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