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