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