Annotation of nono/lib/mainapp.cpp, revision 1.1.1.22

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.