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