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

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

unix.superglobalmegacorp.com

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