|
|
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.12! root 12: #include "config.h"
! 13: #include "hostnet.h"
! 14: #include "logger.h"
! 15: #include "monitor.h"
1.1.1.2 root 16: #include "mystring.h"
1.1.1.12! root 17: #include "mythread.h"
! 18: #include "vm_luna.h"
! 19: #include "vm_x68k.h"
1.1.1.2 root 20: #include <getopt.h>
21: #include <sys/stat.h>
1.1.1.8 root 22: #include <algorithm>
1.1.1.12! root 23: #if defined(__linux__)
! 24: #include <sys/prctl.h>
! 25: #endif
1.1.1.2 root 26:
27: // グローバルインスタンス
28: MainApp gMainApp;
29:
1.1.1.12! root 30: // コンストラクタ
! 31: MainApp::MainApp()
! 32: {
! 33: }
! 34:
! 35: // デストラクタ
! 36: MainApp::~MainApp()
! 37: {
! 38: // 逆順に解放
! 39:
! 40: pVM.reset();
! 41: #if 0 // 確認用
! 42: if (objects.empty() == false) {
! 43: printf("~MainApp: undead objects are:");
! 44: for (const auto *obj : objects) {
! 45: printf(" %s", obj->GetName().c_str());
! 46: }
! 47: printf("\n");
! 48: }
! 49: #endif
! 50:
! 51: pConfig.reset();
! 52: pMonitorManager.reset();
! 53: logger.reset();
! 54: }
! 55:
1.1.1.2 root 56: // ヘルプメッセージ
57: void
58: MainApp::ShowHelp(bool all) const
59: {
60: ShowVersion();
61:
62: #define p(msg) printf(" " msg "\n")
63:
64: printf("usage: %s [<options>]\n", getprogname());
1.1.1.6 root 65: p("-c <path> vm directory or configuration file");
1.1.1.2 root 66: p("-f fast mode");
67: if (IsGUI())
1.1.1.4 root 68: p("--fontsize <height> fontsize in monitors {12,16} (default:12)");
1.1.1.2 root 69: p("-h show brief help message");
70: p("--help show all help message including for developers");
71: if (IsGUI())
72: p("-s,--scale <scale> screen scale, default 1.0");
73: p("--show-config show configuration variables");
1.1.1.8 root 74: p("--show-hostnet show list of hostnet drivers");
1.1.1.2 root 75: p("-v show version");
76: p("-V <key>=<val> overwrite config option");
1.1.1.9 root 77: p("-X <file> [arg..] load and execute host binary (a.out or ELF)");
1.1.1.2 root 78:
79: if (all) {
80: printf("\n(options for developers)\n");
1.1.1.4 root 81: p("-b <addr>[,<skip>] set breakpoint");
1.1.1.2 root 82: p("-B <rom#> benchmark mode");
83: p("-C output log to console");
84: p("-d debugger prompt on startup");
1.1.1.12! root 85: p("-D same as '-V debugger-driver=stdio'");
1.1.1.10 root 86: p("-H human68k console emulation");
1.1.1.2 root 87: p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
1.1.1.11 root 88: p("--load-only <file> load host binary (a.out or ELF) but not execute");
1.1.1.2 root 89: p("-M <name>[,..] monitors to display at startup (-Mhelp displays names)");
1.1.1.12! root 90: p("--perf performance measure mode");
1.1.1.2 root 91: }
92: }
93:
94: // long option は enum と struct option をアルファベット順に並べる。
95: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
96: // 0x80 から始めておく。
97: enum {
98: OPT_fontsize = 0x80,
1.1.1.11 root 99: OPT_load_only,
1.1.1.2 root 100: OPT_help,
1.1.1.12! root 101: OPT_perf,
1.1.1.2 root 102: OPT_show_config,
1.1.1.3 root 103: OPT_show_config_all,
1.1.1.8 root 104: OPT_show_hostnet,
1.1.1.2 root 105: };
106: static struct option longopts[] = {
107: { "fontsize", required_argument, NULL, OPT_fontsize },
1.1.1.11 root 108: { "load-only", required_argument, NULL, OPT_load_only },
1.1.1.2 root 109: { "help", no_argument, NULL, OPT_help },
1.1.1.12! root 110: { "perf", no_argument, NULL, OPT_perf },
1.1.1.2 root 111: { "show-config", no_argument, NULL, OPT_show_config },
1.1.1.3 root 112: // --show-config-all は開発用なのでヘルプには載せない
113: { "show-config-all",no_argument, NULL, OPT_show_config_all },
1.1.1.8 root 114: { "show-hostnet", no_argument, NULL, OPT_show_hostnet },
1.1.1.2 root 115: { NULL, 0, NULL, 0 },
116: };
117:
1.1.1.7 root 118: // VM の初期化、ステージ1。
119: // VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。
1.1.1.2 root 120: // 所々 CLI と GUI で処理が違う。
1.1.1.5 root 121: // 戻り値は以下のいずれか。
122: // MainApp::PASS .. 実行を継続(通常パス)
123: // EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
124: int
1.1.1.7 root 125: MainApp::Init1(bool is_cli_, int ac, char *av[])
1.1.1.2 root 126: {
127: is_cli = is_cli_;
128:
1.1.1.12! root 129: #if defined(__linux__)
! 130: // Linux ではケーパビリティが設定されていると coredump しないので
! 131: // 明示的に許可する必要がある
! 132: prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
! 133: #endif
! 134:
1.1.1.2 root 135: if (!ParseOpt(ac, av)) {
1.1.1.5 root 136: return EXIT_FAILURE;
1.1.1.2 root 137: }
1.1 root 138:
1.1.1.2 root 139: // ログ機構は引数処理後なるはや
140: // CLI ならログは常に標準出力へ(も)出力。
141: // GUI なら -C で指定。
1.1.1.12! root 142: logger.reset(new Logger());
! 143: logger->UseStdout(IsCLI() ? true : log_to_console);
! 144:
! 145: // モニタマネージャ (VM 作成より前、もしかしたら Config より前?)
! 146: pMonitorManager.reset(new MonitorManager());
! 147: gMonitorManager = pMonitorManager.get();
1.1.1.2 root 148:
149: // 設定を作成。コンストラクタで初期値を用意
1.1.1.12! root 150: pConfig.reset(new Config());
! 151: gConfig = pConfig.get();
1.1.1.2 root 152:
1.1.1.6 root 153: // 引数で指定されたディレクトリの設定ファイルがあれば読み込んで設定を更新
154: ConfigFile file(vmfile);
1.1.1.2 root 155: if (file.Load() == false) {
1.1.1.5 root 156: return EXIT_FAILURE;
1.1.1.2 root 157: }
158: if (gConfig->Update(file) == false) {
1.1.1.5 root 159: return EXIT_FAILURE;
1.1.1.2 root 160: }
161: // 最後にコマンドライン引数で更新
162: if (gConfig->Update(config_options) == false) {
1.1.1.5 root 163: return EXIT_FAILURE;
1.1.1.2 root 164: }
1.1 root 165:
1.1.1.12! root 166: // 0. VM 種別を決定
1.1.1.9 root 167: // VM 種別文字列から vmtype を決定
168: const ConfigItem& item = gConfig->Find("vmtype");
169: std::string vmstr = string_tolower(item.AsString());
170: if (vmstr == "x68030") {
171: vmtype = VMTYPE_X68030;
172: } else if (vmstr == "luna") {
173: vmtype = VMTYPE_LUNA1;
174: } else if (vmstr == "luna88k") {
175: vmtype = VMTYPE_LUNA88K;
1.1.1.2 root 176: } else {
1.1.1.9 root 177: if (item.GetFrom() == ConfigItem::FromInitial) {
178: // 未指定の時
179: warnx("vmtype must be specified");
1.1.1.2 root 180: } else {
1.1.1.9 root 181: // ユーザ由来の時
182: item.Err("invalid vmtype");
1.1.1.2 root 183: }
1.1.1.9 root 184: return EXIT_FAILURE;
1.1.1.2 root 185: }
1.1 root 186:
1.1.1.12! root 187: // 1. VM 作成
1.1.1.2 root 188: switch (vmtype) {
189: case VMTYPE_X68030:
1.1.1.12! root 190: pVM.reset(new VM_X68030());
1.1.1.2 root 191: break;
192:
1.1.1.3 root 193: case VMTYPE_LUNA1:
1.1.1.12! root 194: pVM.reset(new VM_LUNA1());
1.1.1.2 root 195: break;
196:
197: case VMTYPE_LUNA88K:
1.1.1.12! root 198: pVM.reset(new VM_LUNA88K());
1.1.1.2 root 199: break;
1.1 root 200:
1.1.1.2 root 201: default:
202: __unreachable();
203: }
1.1.1.12! root 204: gVM = pVM.get();
1.1.1.2 root 205:
1.1.1.12! root 206: // 2. 動的なコンストラクション
1.1.1.5 root 207: if (!gVM->Create()) {
1.1.1.12! root 208: pVM.reset();
1.1.1.5 root 209: return EXIT_FAILURE;
210: }
211:
1.1.1.12! root 212: // 3. ログの処理
1.1.1.8 root 213:
1.1.1.5 root 214: // 設定内容を表示して終了
215: // (VM コンストラクタで変数の増減があるのでそれより後、
216: // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.3 root 217: gConfig->Fix();
1.1.1.2 root 218: if (show_config) {
1.1.1.3 root 219: gConfig->Show(show_config - 1);
1.1.1.12! root 220: pVM.reset();
1.1.1.5 root 221: return EXIT_SUCCESS;
1.1.1.2 root 222: }
223:
1.1.1.12! root 224: // ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2 root 225: // -L help もここで処理。
226: if (!ParseLogopt()) {
1.1.1.12! root 227: pVM.reset();
1.1.1.5 root 228: return EXIT_FAILURE;
1.1.1.2 root 229: }
230:
1.1.1.5 root 231: return PASS;
1.1.1.2 root 232: }
233:
1.1.1.7 root 234: // VM の初期化、ステージ2。スレッド生成を伴う。
235: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2 root 236: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7 root 237: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2 root 238: bool
1.1.1.7 root 239: MainApp::Init2()
1.1.1.2 root 240: {
1.1.1.12! root 241: // 5. VM 初期化。
1.1.1.2 root 242: if (!gVM->Init()) {
243: return false;
244: }
245:
1.1.1.12! root 246: // メインスレッド名を設定
! 247: PTHREAD_SETNAME("Main");
! 248:
! 249: // 6. スレッド開始
! 250: if (!gVM->StartThread()) {
! 251: return false;
! 252: }
! 253:
! 254: // 7. 起動時設定の適用
1.1.1.2 root 255: if (!gVM->Apply()) {
256: return false;
257: }
258:
259: return true;
260: }
261:
262: // コマンドライン引数を処理する。
263: // 知らない引数とかがあればこちらで usage を表示して false を返す。
264: bool
265: MainApp::ParseOpt(int ac, char *av[])
266: {
1.1.1.6 root 267: struct stat st;
268: const char *cpath;
1.1.1.2 root 269: int b;
270: int c;
271:
272: fontsize = 12;
273: screen_scale = 1.0;
1.1.1.6 root 274: cpath = ".";
1.1.1.2 root 275:
1.1.1.9 root 276: while ((c = getopt_long(ac, av, "b:B:c:CdDfhHL:M:s:vV:X:",
1.1.1.2 root 277: longopts, NULL)) != -1) {
278: switch (c) {
279: case 'b':
1.1.1.4 root 280: debug_breakaddr.push_back(optarg);
1.1.1.2 root 281: break;
282:
283: case 'B':
284: b = atoi(optarg);
285: if (b < 2 || b > 6) {
286: fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
287: return false;
288: }
289: benchmark_mode = b;
290: break;
291:
292: case 'c':
1.1.1.6 root 293: cpath = optarg;
294: // 空文字列なら再び初期値に
295: if (cpath[0] == '\0') {
296: cpath = ".";
297: }
1.1.1.2 root 298: break;
299:
300: case 'C':
301: log_to_console = true;
302: break;
303:
304: case 'd':
305: debug_on_start = true;
306: break;
307:
308: case 'D':
1.1.1.12! root 309: // -D は -V debugger-driver=stdio と等価。
! 310: config_options.push_back("debugger-driver=stdio");
! 311: // エラー表示用に -D で指定されたことを覚えておく。
! 312: opt_D = true;
1.1.1.2 root 313: break;
314:
315: case 'f':
316: fast_mode = true;
317: break;
318:
1.1.1.9 root 319: case 'H':
320: human_mode = true;
321: break;
322:
1.1.1.2 root 323: case 'L':
1.1.1.12! root 324: AddLogopt(optarg);
1.1.1.2 root 325: break;
326:
327: case 'M':
328: if (!monitor_opt.empty()) {
329: monitor_opt += ",";
330: }
331: monitor_opt += optarg;
332: break;
333:
334: case 's':
335: if (IsGUI()) {
336: char *end;
337: errno = 0;
338: screen_scale = strtod(optarg, &end);
339: if (end == optarg || end[0] != '\0' || errno == ERANGE) {
340: warnx("-s: invalid argument");
341: return false;
342: }
343: // 上限は適当
344: if (screen_scale <= 0.0 || screen_scale >= 10.0) {
345: warnx("-s: invalid scale");
346: return false;
347: }
348: }
349: break;
350:
351: case 'X':
1.1.1.11 root 352: load_and_exec = true;
353: FALLTHROUGH;
354: case OPT_load_only:
1.1.1.9 root 355: exec_file = optarg;
1.1.1.2 root 356: for (int i = optind; i < ac; i++) {
357: if (i != optind) {
1.1.1.9 root 358: exec_arg += " ";
1.1.1.2 root 359: }
1.1.1.9 root 360: exec_arg += av[i];
1.1.1.2 root 361: }
362: optind = ac;
363: break;
364:
365: case 'v':
366: ShowVersion();
367: exit(0);
368:
369: case 'V':
370: config_options.push_back(optarg);
371: break;
372:
373: case OPT_fontsize:
374: fontsize = atoi(optarg);
375: break;
376:
377: case OPT_help:
378: ShowHelp(true);
379: return false;
380:
1.1.1.12! root 381: case OPT_perf:
! 382: // パフォーマンス確認用。
! 383: // -Vprom-image=PROM.DAT はパスの問題があるので各自で追加指定
! 384: // する必要がある。
! 385: config_options.emplace_back(".rtc-force-fixed=1");
! 386: config_options.emplace_back("clock-sync=virtual");
! 387: config_options.emplace_back("ethernet-macaddr=02:00:00:00:00:01");
! 388: config_options.emplace_back("hostcom-driver=none");
! 389: config_options.emplace_back("hostnet-driver=none");
! 390: config_options.emplace_back("luna-dipsw1=11110111");
! 391: config_options.emplace_back("spc0-id0-writeignore=1");
! 392: config_options.emplace_back("spc0-id1-writeignore=1");
! 393: config_options.emplace_back("spc0-id2-writeignore=1");
! 394: config_options.emplace_back("spc0-id3-writeignore=1");
! 395: config_options.emplace_back("spc0-id4-writeignore=1");
! 396: config_options.emplace_back("spc0-id5-writeignore=1");
! 397: config_options.emplace_back("spc0-id6-writeignore=1");
! 398: // -f -C
! 399: fast_mode = true;
! 400: log_to_console = true;
! 401: break;
! 402:
1.1.1.2 root 403: case OPT_show_config:
1.1.1.3 root 404: show_config = 1;
405: break;
406:
407: case OPT_show_config_all:
408: show_config = 2;
1.1.1.2 root 409: break;
410:
1.1.1.8 root 411: case OPT_show_hostnet:
412: ShowHostnet();
413: return false;
414:
1.1.1.2 root 415: case 'h':
416: default:
417: ShowHelp(false);
418: return false;
419: }
420: }
421:
1.1.1.6 root 422: // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
423: // nono.cfg を設定ファイルとする。
424: // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
425: // ディレクトリを VM ディレクトリとする。
426: // -c DIR => vmdir = DIR, vmfile = DIR/nono.cfg
427: // -c DIR/FILE => vmdir = DIR, vmfile = FILE
428: if (stat(cpath, &st) < 0) {
429: warn("stat %s", cpath);
430: return false;
1.1.1.2 root 431: }
1.1.1.6 root 432: if (S_ISDIR(st.st_mode)) {
433: vmdir = std::string(cpath);
434: if (vmdir.back() != '/') {
435: vmdir += '/';
436: }
437: vmfile = vmdir + "nono.cfg";
438: } else if (S_ISREG(st.st_mode)) {
439: vmfile = std::string(cpath);
440: auto pos = vmfile.rfind('/');
441: if (pos != std::string::npos) {
442: vmdir = vmfile.substr(0, pos + 1);
443: } else {
444: vmdir = "./";
445: }
446: } else {
447: warnx("-c %s: path must be file or directory", cpath);
448: return false;
1.1.1.2 root 449: }
450:
451: // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
452: // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
453: // 消して回らないと起動できないのも面倒なので -M オプション自体は
454: // 無意味だけど許容する。うーん。
455: if (IsCLI()) {
456: if (monitor_opt == "help") {
457: warnx("-Mhelp is not available on CLI");
458: return false;
459: }
460: if (!monitor_opt.empty()) {
461: warnx("-M option is ignored on CLI");
462: return true;
463: }
464: }
465:
1.1.1.9 root 466: // Human モードでは実行ファイル名が必要
467: if (human_mode) {
468: if (exec_file == NULL) {
469: warnx("-H option needs -X");
470: return false;
471: }
472: }
473:
1.1.1.2 root 474: return true;
475: }
476:
477: // バージョンを表示
478: void
479: MainApp::ShowVersion() const
480: {
481: // ここは実行ファイル名によらず nono にする
482: fprintf(stderr, "nono version %d.%d.%d (%s)\n",
483: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
484: }
485:
1.1.1.12! root 486: // ログレベル指定文字列を logopt に追加する。
! 487: void
! 488: MainApp::AddLogopt(const char *opt)
! 489: {
! 490: if (logopt.empty() == false) {
! 491: logopt += ',';
! 492: }
! 493: logopt += opt;
! 494: }
! 495:
1.1.1.8 root 496: // ログレベル指定文字列を処理する。
497: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
498: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
499: // "help" があれば設定は行わず一覧を表示。
1.1 root 500: bool
1.1.1.2 root 501: MainApp::ParseLogopt()
1.1 root 502: {
1.1.1.8 root 503: std::vector<std::string> items;
504:
505: // 分解して..
1.1.1.12! root 506: items = string_split(logopt.c_str(), ',');
1.1.1.8 root 507:
508: // "help" があればヘルプを表示して終了
509: for (const auto& item : items) {
510: if (item == "help") {
511: std::vector<std::string> list = GetLogNames();
512: // less したいだろうから stderr ではなく stdout に出力する
513: for (const auto& name : list) {
514: printf(" %s\n", name.c_str());
515: }
516: return false;
1.1 root 517: }
1.1.1.8 root 518: }
519:
520: // ログレベルを設定
521: std::string errmsg;
522: if (SetLogopt(items, &errmsg) == false) {
523: warnx("%s", errmsg.c_str());
1.1 root 524: return false;
525: }
526:
1.1.1.8 root 527: return true;
528: }
1.1 root 529:
1.1.1.8 root 530: // ログレベルを設定する。
531: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
532: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
533: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
534: // 格納して false を返す。
535: // MainApp 内と Debugger からも呼ばれる。
536: /*static*/ bool
537: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
538: {
539: for (const auto& item : items) {
540: if (SetLogopt1(item.c_str(), errmsg) == false) {
541: return false;
542: }
543: }
544: if (0) { // デバッグ用
1.1.1.12! root 545: for (const auto o : gMainApp.GetObjects()) {
1.1.1.8 root 546: printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1 root 547: }
1.1.1.8 root 548: }
549: return true;
550: }
551:
552: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
553: // loglevel は省略なら 1 とする。
554: // logname が "all" なら全オブジェクトにセットする。
555: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
556: // そのオブジェクトにログレベルを設定して true を返す。
557: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
558: // false を返す。
559: // この関数は (このすぐ上の SetLogopt() を経由して)
560: // MainApp と Debugger から呼ばれることに注意。
561: /*static*/ bool
562: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
563: {
564: std::string name;
565: const char *v;
566: int level;
567:
568: v = strchr(item.c_str(), '=');
569: if (v) {
570: name = std::string(item.c_str(), v - item.c_str());
571: level = atoi(++v);
572: } else {
573: name = item;
574: level = 1;
575: }
576: // ここで name は変数名、level は値(省略されたら1)
577:
578: if (name.empty()) {
579: *errmsg = "logname must be specified";
580: return false;
581: }
1.1 root 582:
1.1.1.12! root 583: // 今の所、値域は -1 〜 9 ということにしておく。
! 584: if (level < -1) {
! 585: level = -1;
! 586: } else if (level > 9) {
! 587: level = 9;
! 588: }
! 589:
1.1.1.8 root 590: // "all" なら全部にセット
591: if (name == "all") {
1.1.1.12! root 592: for (auto obj : gMainApp.GetObjects()) {
1.1.1.8 root 593: if (obj->GetName().empty() == false) {
1.1.1.12! root 594: obj->SetLogLevel(level);
1.1.1.8 root 595: }
1.1 root 596: }
1.1.1.8 root 597: return true;
598: }
599:
1.1.1.12! root 600: // エイリアスリストを作る
! 601: using aliaslist_t = std::vector<std::pair<std::string, Object *>>;
! 602: aliaslist_t alias_list;
! 603: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 604: const std::vector<std::string>& aliases = obj->GetAliases();
605:
606: for (const auto& a : aliases) {
1.1.1.12! root 607: alias_list.emplace_back(a, obj);
! 608: }
! 609: }
1.1.1.8 root 610:
1.1.1.12! root 611: // エイリアスを完全一致のみのものと部分一致も許容するものに分ける
! 612: aliaslist_t exact_alias;
! 613: aliaslist_t partial_alias;
! 614: for (const auto& a0 : alias_list) {
! 615: bool exact = false;
! 616: for (const auto& a1 : alias_list) {
! 617: if (a0.first == a1.first) {
! 618: continue;
! 619: }
! 620: if (starts_with_ignorecase(a0.first, a1.first)) {
! 621: exact = true;
! 622: break;
1.1 root 623: }
1.1.1.8 root 624: }
1.1.1.12! root 625: if (exact) {
! 626: exact_alias.push_back(a0);
! 627: } else {
! 628: partial_alias.push_back(a0);
! 629: }
! 630: }
! 631:
! 632: // 完全一致をまず調べる
! 633: for (const auto& a : exact_alias) {
! 634: if (strcasecmp(a.first.c_str(), name.c_str()) == 0) {
! 635: a.second->SetLogLevel(level);
! 636: return true;
! 637: }
! 638: }
! 639:
! 640: aliaslist_t found;
! 641: for (const auto& a : partial_alias) {
! 642: // 前方一致したら覚えておく
! 643: if (starts_with_ignorecase(a.first, name)) {
! 644: found.push_back(a);
! 645: }
1.1.1.8 root 646: }
647:
648: // 見付からない場合はエラー
649: if (found.empty()) {
650: *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
651: return false;
652: }
653:
654: // 1つだけなら確定
655: if (found.size() == 1) {
1.1.1.12! root 656: found[0].second->SetLogLevel(level);
1.1.1.8 root 657: return true;
658: }
659:
660: // 複数あれば候補文字列を作成
661: *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
662: name.c_str());
1.1.1.12! root 663: for (const auto& cand : found) {
! 664: *errmsg += string_format(" \"%s\"", cand.first.c_str());
1.1.1.8 root 665: }
666: return false;
667: }
668:
669: // ログ名の一覧を取得する。
670: // MainApp と Debugger から呼ばれる。
671: /*static*/ std::vector<std::string>
672: MainApp::GetLogNames()
673: {
674: std::vector<Object *> sortobj;
675:
676: // エイリアスを持つオブジェクトだけ抜き出す
1.1.1.12! root 677: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 678: if (obj->GetAliases().empty() == false) {
679: sortobj.emplace_back(obj);
680: }
681: }
682:
683: // aliases の一語目で sortobj をソートする
684: std::sort(sortobj.begin(), sortobj.end(),
1.1.1.12! root 685: [](const auto a, const auto b) {
1.1.1.8 root 686: const auto& sa = a->GetAliases()[0];
687: const auto& sb = b->GetAliases()[0];
1.1.1.12! root 688: return sa < sb;
1.1.1.8 root 689: }
690: );
691:
692: std::vector<std::string> list;
693: for (const auto *obj : sortobj) {
694: const auto& aliases = obj->GetAliases();
695: std::string str;
696:
1.1.1.12! root 697: str = aliases[0];
1.1.1.8 root 698: if (aliases.size() > 1) {
699: str += " (alias:";
700: for (int i = 1, sz = aliases.size(); i < sz; i++) {
701: str += ' ';
702: str += aliases[i];
1.1 root 703: }
1.1.1.8 root 704: str += ')';
1.1.1.6 root 705: }
1.1.1.8 root 706: list.emplace_back(str);
1.1 root 707: }
1.1.1.8 root 708: list.emplace_back("all");
1.1 root 709:
1.1.1.8 root 710: return list;
1.1 root 711: }
1.1.1.2 root 712:
713: // 関連するファイルのパスを取得。
1.1.1.3 root 714: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
715: // ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
716: // 何らか不都合があったとしても (例えばファイルサイズが 0 だとか
717: // パーミッションが足りないとか) 次を試すとかはしない。
718: // 2. name が '~' から始まっていればホームディレクトリに展開。
719: // 3. name が相対パスなら VM ディレクトリからの相対パスとする。
720: // 4. name は絶対パスのはずなので、そのまま使用する。
1.1.1.2 root 721: std::string
722: MainApp::SearchFile(const std::string& name) const
723: {
724: std::string path;
725: struct stat st;
726:
1.1.1.3 root 727: // 1. パス区切りを含んでなければ、ファイル名のみ
728: if (name.find('/') == std::string::npos) {
729: // 1a. VM ディレクトリ
730: path = GetVMDir() + name;
731: if (stat(path.c_str(), &st) == 0) {
732: return path;
733: }
734:
735: // 2. 親ディレクトリ
736: path = GetVMDir() + "../" + name;
737: if (stat(path.c_str(), &st) == 0) {
738: return path;
739: }
740:
741: // どちらもなければエラー
742: return "";
743: }
1.1.1.2 root 744:
1.1.1.3 root 745: // 2. 先頭の '~' を $HOME に展開
746: path = name;
747: if (path[0] == '~' && path[1] == '/') {
748: const char *home = getenv("HOME");
749: if (home == NULL) {
750: home = "";
751: }
752: path = string_format("%s%s", home, path.c_str() + 1);
1.1.1.2 root 753: }
754:
1.1.1.3 root 755: // 3. 相対パスなら、VM ディレクトリからの相対
756: if (path[0] != '/') {
757: path = GetVMDir() + path;
1.1.1.2 root 758: }
759:
1.1.1.3 root 760: return path;
761: }
762:
763: // 現在の VM が指定の feature を持っているか?
764: bool
765: MainApp::HasVMFeature(vmf_t feature) const
766: {
767: uint32 vmf = 1 << GetVMType();
768: return (vmf & feature) != 0;
1.1.1.2 root 769: }
1.1.1.8 root 770:
771: // コンパイルされている host netdriver の一覧を表示。
772: void
773: MainApp::ShowHostnet() const
774: {
1.1.1.12! root 775: auto list = HostNetDevice::GetDrivers();
1.1.1.8 root 776: for (const auto& name : list) {
777: printf(" %s\n", name.c_str());
778: }
779: }
1.1.1.12! root 780:
! 781: // オブジェクトを追加
! 782: void
! 783: MainApp::AddObject(Object *obj)
! 784: {
! 785: obj->logger = GetLogger();
! 786:
! 787: objects.push_back(obj);
! 788: }
! 789:
! 790: // オブジェクトを削除
! 791: // XXX マルチスレッドな状況からは呼ばれないはず?
! 792: void
! 793: MainApp::DeleteObject(Object *obj)
! 794: {
! 795: for (auto it = objects.begin(); it != objects.end(); ++it) {
! 796: if (*it == obj) {
! 797: objects.erase(it);
! 798: break;
! 799: }
! 800: }
! 801: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.