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

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

unix.superglobalmegacorp.com

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