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