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

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

unix.superglobalmegacorp.com

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