├── .github └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ ├── publish_pr_events.yml │ ├── publish_review_event.yaml │ ├── release.yml │ ├── reviewdog.yml │ └── store_review_event.yaml ├── .gitignore ├── CMakeLists.txt ├── CODEOWNERS ├── INSTALL ├── LICENSE ├── README.md ├── SECURITY.md ├── debian ├── changelog ├── compat ├── control ├── copyright ├── etc │ └── modules-load.d │ │ └── civ-mods-base.conf ├── install ├── rules ├── source │ ├── format │ └── local-options ├── vm-manager.civ-base.udev ├── vm-manager.postinst └── vm-manager.preinst ├── docs ├── DeveloperDoc.md ├── Documentation.md ├── arch.drawio.svg └── fields.md ├── sample └── civ-1.ini └── src ├── CMakeLists.txt ├── android ├── applauncher │ ├── Android.bp │ └── civ_applauncher.cc ├── powerctl │ ├── Android.bp │ └── civ_powerctl.cc └── startup_notify │ ├── Android.bp │ └── civ_startup_notify.cc ├── cmake ├── BoostCMake.cmake ├── FTXUICMake.cmake ├── gRPCExtCMake.cmake ├── gRPCFetchCMake.cmake ├── revision.h.in └── revisionCMake.cmake ├── guest ├── aaf.cc ├── aaf.h ├── config_parser.cc ├── config_parser.h ├── tui.cc ├── tui.h ├── vm_builder.cc ├── vm_builder.h ├── vm_builder_qemu.cc ├── vm_builder_qemu.h ├── vm_flash.cc ├── vm_flash.h ├── vm_powerctl.h ├── vm_process.cc ├── vm_process.h ├── vsock_cid_pool.cc └── vsock_cid_pool.h ├── host └── app_launcher │ ├── CMakeLists.txt │ └── app_launcher_client.cc ├── include └── constants │ └── vm_manager.h ├── services ├── client.cc ├── client.h ├── message.h ├── protos │ ├── Android.bp │ ├── CMakeLists.txt │ ├── common.proto │ ├── vm_guest.proto │ └── vm_host.proto ├── server.cc ├── server.h ├── startup_listener_impl.cc └── startup_listener_impl.h ├── utils ├── log.h ├── utils.cc └── utils.h └── vm_manager.cc /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: VM Manager CI 2 | 3 | on: [push, pull_request] 4 | permissions: read-all 5 | jobs: 6 | build_ubuntu_gcc: 7 | 8 | runs-on: ${{ matrix.os }} 9 | env: 10 | LOCAL_INSTALL_DIR: ${{ github.workspace }}/.local 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-20.04, ubuntu-22.04] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | path: vm-manager 20 | fetch-depth: 0 21 | 22 | - name: setup 23 | run: | 24 | sudo apt-get --quiet update --yes 25 | sudo apt-get --quiet install --yes make devscripts lintian debhelper 26 | sudo apt-get --quiet install --yes autoconf libtool pkg-config 27 | git config --global user.name "github-actions" 28 | git config --global user.email "github-actions@github.com" 29 | gcc --version 30 | cmake --version 31 | which cmake 32 | 33 | - name: build 34 | run: | 35 | export PATH="${{ env.LOCAL_INSTALL_DIR }}/bin:$PATH" 36 | sudo ln -s $(which cmake) /usr/bin/cmake || echo "" 37 | cd vm-manager/ 38 | export CC=/usr/bin/clang 39 | export CXX=/usr/bin/clang++ 40 | debuild --preserve-env -b -uc -us --lintian-opts --profile debian 41 | 42 | - name: artifacts 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: vm-manager.${{ matrix.os }}.${{ github.run_id }}.${{ github.sha }} 46 | path: | 47 | ${{ github.workspace }}/vm-manager/debian/ 48 | *.deb 49 | *.ddeb 50 | *.build 51 | *.buildinfo 52 | *.changes 53 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * 0' 8 | permissions: read-all 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | language: [ 'cpp' ] 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | - run: | 28 | export CC=/usr/bin/clang 29 | export CXX=/usr/bin/clang++ 30 | mkdir build/ 31 | cd build/ 32 | cmake -DCMAKE_BUILD_TYPE=Release .. 33 | cmake --build . --config Release 34 | cd - 35 | 36 | # Initializes the CodeQL tools for scanning. 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@v3 39 | with: 40 | languages: ${{ matrix.language }} 41 | queries: security-extended,security-and-quality 42 | 43 | - run: | 44 | cd build/ 45 | find src/CMakeFiles/vm-manager.dir/ -iname *.o |xargs rm 46 | cmake --build . --config Release 47 | 48 | - name: Perform CodeQL Analysis 49 | uses: github/codeql-action/analyze@v3 50 | with: 51 | upload: False 52 | output: sarif-results 53 | 54 | - name: Filter SARIF 55 | uses: advanced-security/filter-sarif@v1 56 | with: 57 | patterns: | 58 | -build/**:** 59 | -src/services/protos/gens/**:** 60 | input: sarif-results/cpp.sarif 61 | output: sarif-results/cpp-filtered.sarif 62 | 63 | - name: Upload SARIF 64 | uses: github/codeql-action/upload-sarif@v3 65 | with: 66 | sarif_file: sarif-results/cpp-filtered.sarif 67 | 68 | - run: | 69 | mkdir sarif-report 70 | cp sarif-results/cpp-filtered.sarif sarif-report/ 71 | 72 | - name: Generate Security Report 73 | uses: rsdmike/github-security-report-action@v3.0.4 74 | with: 75 | token: ${{ secrets.github_token }} 76 | sarifReportDir: sarif-report 77 | template: report 78 | 79 | - name: artifacts 80 | uses: actions/upload-artifact@v4 81 | with: 82 | name: sarif-results 83 | path: | 84 | sarif-results 85 | report.pdf 86 | 87 | -------------------------------------------------------------------------------- /.github/workflows/publish_pr_events.yml: -------------------------------------------------------------------------------- 1 | name: CI Workflow 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, synchronize, reopened, edited] 6 | branches: "**" 7 | issues: 8 | types: 9 | - closed 10 | branches: "**" 11 | permissions: read-all 12 | 13 | jobs: 14 | Trigger_Workflows: 15 | runs-on: ubuntu-latest 16 | name: CI Workflow 17 | steps: 18 | - name: Get Token 19 | run: | 20 | retries=3 21 | while [ $retries -gt 0 ]; do 22 | if RESPONSE=$(curl --silent --location "${{ secrets.CLIENT_TOKEN_URL }}" \ 23 | --header 'Content-Type: application/x-www-form-urlencoded' \ 24 | --data-urlencode "client_id=${{ secrets.CLIENT_ID }}" \ 25 | --data-urlencode "client_secret=${{ secrets.CLIENT_SECRET }}" \ 26 | --data-urlencode 'grant_type=client_credentials'); then 27 | TOKEN=$(echo "$RESPONSE" | jq -r '.access_token') 28 | if [ -n "$TOKEN" ]; then 29 | echo "TOKEN=$TOKEN" >> $GITHUB_ENV 30 | break 31 | else 32 | echo "Error: Failed to parse access token from response" 33 | fi 34 | else 35 | echo "Error: Request to get token failed" 36 | fi 37 | retries=$((retries-1)) 38 | sleep 1 39 | done 40 | 41 | if [ $retries -eq 0 ]; then 42 | echo "Error: Failed to retrieve access token after multiple retries" 43 | exit 1 44 | fi 45 | 46 | 47 | 48 | - name: Trigger Build with Event 49 | if: success() 50 | env: 51 | TOKEN: ${{ env.TOKEN }} 52 | run: | 53 | EVENT_DATA='${{ toJSON(github.event_path) }}' 54 | retries=3 55 | while [ $retries -gt 0 ]; do 56 | if curl --silent --location --request POST "${{ secrets.CLIENT_PUBLISH_URL }}" \ 57 | --header 'Content-Type: application/json' \ 58 | --header 'x-github-event: github' \ 59 | --header "Authorization: Bearer $TOKEN" \ 60 | --data "@${{ github.event_path }}"; then 61 | break 62 | else 63 | echo "Error: Failed to trigger build" 64 | fi 65 | retries=$((retries-1)) 66 | sleep 1 67 | done 68 | 69 | if [ $retries -eq 0 ]; then 70 | echo "Error: Failed to trigger build after multiple retries" 71 | exit 1 72 | fi 73 | -------------------------------------------------------------------------------- /.github/workflows/publish_review_event.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Review Event 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Store_Review_Event"] 6 | types: 7 | - completed 8 | permissions: read-all 9 | 10 | jobs: 11 | fetch_and_process: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: 'Download artifact' 15 | uses: actions/github-script@v6 16 | with: 17 | script: | 18 | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ 19 | owner: context.repo.owner, 20 | repo: context.repo.repo, 21 | run_id: context.payload.workflow_run.id, 22 | }); 23 | let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { 24 | return artifact.name == "eventjson" 25 | })[0]; 26 | let download = await github.rest.actions.downloadArtifact({ 27 | owner: context.repo.owner, 28 | repo: context.repo.repo, 29 | artifact_id: matchArtifact.id, 30 | archive_format: 'zip', 31 | }); 32 | let fs = require('fs'); 33 | fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/eventjson.zip`, Buffer.from(download.data)); 34 | 35 | - name: 'Unzip artifact' 36 | run: | 37 | ls 38 | unzip eventjson.zip 39 | 40 | - name: Get Token 41 | run: | 42 | retries=3 43 | while [ $retries -gt 0 ]; do 44 | if RESPONSE=$(curl --silent --location "${{ secrets.CLIENT_TOKEN_URL }}" \ 45 | --header 'Content-Type: application/x-www-form-urlencoded' \ 46 | --data-urlencode "client_id=${{ secrets.CLIENT_ID }}" \ 47 | --data-urlencode "client_secret=${{ secrets.CLIENT_SECRET }}" \ 48 | --data-urlencode 'grant_type=client_credentials'); then 49 | TOKEN=$(echo "$RESPONSE" | jq -r '.access_token') 50 | if [ -n "$TOKEN" ]; then 51 | echo "TOKEN=$TOKEN" >> $GITHUB_ENV 52 | break 53 | else 54 | echo "Error: Failed to parse access token from response" 55 | fi 56 | else 57 | echo "Error: Request to get token failed" 58 | fi 59 | retries=$((retries-1)) 60 | sleep 1 61 | done 62 | 63 | if [ $retries -eq 0 ]; then 64 | echo "Error: Failed to retrieve access token after multiple retries" 65 | exit 1 66 | fi 67 | 68 | 69 | 70 | - name: Trigger Build with Event 71 | if: success() 72 | env: 73 | TOKEN: ${{ env.TOKEN }} 74 | run: | 75 | 76 | EVENT_DATA=$(cat event.json) 77 | 78 | retries=3 79 | while [ $retries -gt 0 ]; do 80 | if curl --silent --location --request POST "${{ secrets.CLIENT_PUBLISH_URL }}" \ 81 | --header 'Content-Type: application/json' \ 82 | --header 'x-github-event: github' \ 83 | --header "Authorization: Bearer $TOKEN" \ 84 | --data "$EVENT_DATA"; then 85 | break 86 | else 87 | echo "Error: Failed to trigger build" 88 | fi 89 | retries=$((retries-1)) 90 | sleep 1 91 | done 92 | 93 | if [ $retries -eq 0 ]; then 94 | echo "Error: Failed to trigger build after multiple retries" 95 | exit 1 96 | fi -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: VM Manager Release 2 | 3 | on: 4 | release: 5 | types: [published, edited] 6 | permissions: read-all 7 | jobs: 8 | build_ubuntu_gcc: 9 | permissions: 10 | contents: write 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: [ubuntu-20.04] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | path: vm-manager 21 | fetch-depth: 0 22 | 23 | - name: setup 24 | run: | 25 | sudo apt-get --quiet update --yes 26 | sudo apt-get --quiet install --yes make devscripts lintian debhelper 27 | sudo apt-get --quiet install --yes autoconf libtool pkg-config 28 | 29 | - name: Set release version 30 | run: echo "REL_VER=${GITHUB_REF##*/}" >> $GITHUB_ENV 31 | 32 | - name: build 33 | run: | 34 | export PATH="${{ env.LOCAL_INSTALL_DIR }}/bin:$PATH" 35 | sudo ln -s $(which cmake) /usr/bin/cmake || echo "" 36 | cd vm-manager/ 37 | export CC=/usr/bin/clang 38 | export CXX=/usr/bin/clang++ 39 | debuild --preserve-env -b -uc -us --lintian-opts --profile debian 40 | cp $(realpath ../*.deb) vm-manager_${{ env.REL_VER }}.deb 41 | 42 | - name: artifacts 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: vm-manager.${{ matrix.os }}.${{ github.run_id }}.${{ github.sha }} 46 | path: | 47 | vm-manager/debian/ 48 | *.deb 49 | *.ddeb 50 | *.build 51 | *.buildinfo 52 | *.changes 53 | 54 | - name: Upload release assets 55 | uses: softprops/action-gh-release@v2 56 | with: 57 | files: | 58 | vm-manager/vm-manager_${{ env.REL_VER }}.deb 59 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yml: -------------------------------------------------------------------------------- 1 | name: Reviewdog 2 | on: [pull_request] 3 | permissions: read-all 4 | jobs: 5 | cpplint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - uses: reviewdog/action-cpplint@master 10 | with: 11 | github_token: ${{ secrets.github_token }} 12 | reporter: github-pr-check 13 | flags: --linelength=120 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/store_review_event.yaml: -------------------------------------------------------------------------------- 1 | name: Store_Review_Event 2 | 3 | on: 4 | pull_request_review: 5 | types: [submitted] 6 | branches: 7 | - master 8 | - tmp/ci-testing 9 | permissions: read-all 10 | 11 | jobs: 12 | Store_Review_Event: 13 | runs-on: ubuntu-latest 14 | name: Store Review Event 15 | steps: 16 | - name: Check if review is approved and branch is correct 17 | if: github.event.review.state != 'approved' || (github.event.pull_request.base.ref != 'master' && github.event.pull_request.base.ref != 'tmp/ci-testing') 18 | run: | 19 | echo "Skipping workflow as the review is not approved or the base branch is not master or tmp/ci-testing." 20 | exit 1 21 | 22 | - name: Upload event JSON as artifact 23 | if: github.event.review.state == 'approved' && (github.event.pull_request.base.ref == 'master' || github.event.pull_request.base.ref == 'tmp/ci-testing') 24 | uses: actions/upload-artifact@v4 25 | with: 26 | name: eventjson 27 | path: "${{ github.event_path }}" 28 | retention-days: 7 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C/C++ build outputs 2 | .build/ 3 | bins 4 | gens 5 | libs 6 | objs 7 | *.bin 8 | *.o 9 | 10 | # cmake build files 11 | **/cmake/build/ 12 | 13 | # Visual Studio Code artifacts 14 | .vscode/* 15 | .history/ 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | 9 | cmake_minimum_required(VERSION 3.10) 10 | 11 | #SET(MAJOR_VERSION 1) 12 | #SET(MINOR_VERSION 0) 13 | #SET(PATCH_VERSION 0) 14 | 15 | #SET(CPACK_GENERATOR "DEB") 16 | 17 | #SET(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION}) 18 | #SET(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION}) 19 | #SET(CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION}) 20 | 21 | #SET(CPACK_PACKAGE_NAME "vm-manager") 22 | #SET(CPACK_DEBIAN_PACKAGE_NAME "vm-manager") 23 | #SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") 24 | #SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CiV VM Manager to manage the CiV guests") 25 | #SET(CPACK_PACKAGE_CONTACT "yadong.qi@intel.com") 26 | #SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Qi, Yadong ") 27 | 28 | project("vm_manager") 29 | add_subdirectory(src) 30 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default/global reviewers 2 | * @lchen43 @YadongQi 3 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Build Guidance 2 | ============== 3 | 4 | 5 | Quick start: 6 | ------------ 7 | 8 | [Install dependencies] 9 | sudo apt-get install build-essential autoconf libtool pkg-config cmake 10 | 11 | [Configure] 12 | mkdir build && cd build 13 | cmake -DCMAKE_BUILD_TYPE=Release .. 14 | 15 | [Build] 16 | make -j 17 | 18 | [Install] 19 | sudo make install 20 | 21 | 22 | Build deb packages: 23 | ------------------- 24 | 25 | [Install dependencies] 26 | sudo apt-get install build-essential cmake dev devscripts lintian debhelper 27 | 28 | [Build] 29 | debuild --preserve-env -b -uc -us --lintian-opts --profile debian #vm-manager_x.x.x_amd64.deb will be generated at parent directory 30 | 31 | [Install] 32 | dpkg -i vm-manager_x.x.x_amd64.deb 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | CiV VM Manager 3 | ============= 4 | 5 | CiV VM Manager is a linux userspace application aimed to manage the CiV guests. 6 | 7 | CiV: Celadon in Virtual Machine(https://01.org/projectceladon/about) 8 | 9 | You can find the latest vm-manager release here: https://github.com/projectceladon/vm_manager/releases 10 | 11 | 12 | 13 | # Architecture of VM Manager 14 | 15 | ![VM Manager Arch](/docs/arch.drawio.svg) 16 | 17 | 18 | # Building 19 | 20 | ## Pre-requisites 21 | 22 | UBUNTU OS Version: 20.04 or 22.04 23 | 24 | Install required tools on Linux. 25 | 26 | ```sh 27 | $ [sudo] apt-get install build-essential autoconf libtool pkg-config cmake 28 | ``` 29 | 30 | ## Build 31 | 32 | ```sh 33 | $ mkdir build && cd build 34 | $ cmake -DCMAKE_BUILD_TYPE=Release .. 35 | $ cmake --build . --config Release 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project [utilizing the guidelines here](https://www.intel.com/content/www/us/en/developer/topic-technology/open/celadon/community.html#security). 6 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | vm-manager (1.3.0) stable; urgency=medium 2 | 3 | * Bump version to 1.3.0 4 | 5 | -- Qi, Yadong Fri, 10 May 2024 08:52:41 +0800 6 | 7 | vm-manager (1.2.3) stable; urgency=medium 8 | 9 | * Bump version to 1.2.3 10 | 11 | -- Qi, Yadong Tue, 04 Apr 2023 10:04:02 +0800 12 | 13 | vm-manager (1.2.2) stable; urgency=medium 14 | 15 | * Bump version to 1.2.2 16 | 17 | -- Qi, Yadong Wed, 15 Mar 2023 16:21:12 +0800 18 | 19 | vm-manager (1.2.1) stable; urgency=medium 20 | 21 | * Bump version to 1.2.1 22 | 23 | -- Qi, Yadong Fri, 10 Mar 2023 08:53:35 +0800 24 | 25 | vm-manager (1.2.0) stable; urgency=medium 26 | 27 | * Bump version to 1.2.0 28 | 29 | -- Qi, Yadong Fri, 06 Jan 2023 11:06:30 +0800 30 | 31 | vm-manager (1.1.0) stable; urgency=medium 32 | 33 | * Bump version to 1.1.0 34 | 35 | -- Qi, Yadong Tue, 13 Dec 2022 14:20:47 +0800 36 | 37 | vm-manager (1.0.3) stable; urgency=medium 38 | 39 | * Bump version to 1.0.3 40 | 41 | -- Qi, Yadong Thu, 10 Nov 2022 12:25:53 +0800 42 | 43 | vm-manager (1.0.2) stable; urgency=medium 44 | 45 | * Bump version to 1.0.2 46 | 47 | -- Qi, Yadong Thu, 20 Oct 2022 15:36:28 +0800 48 | 49 | vm-manager (1.0.1) stable; urgency=medium 50 | 51 | * Bump version to 1.0.1 52 | 53 | -- Qi, Yadong Thu, 29 Sep 2022 08:20:10 +0800 54 | 55 | vm-manager (1.0.0) stable; urgency=medium 56 | 57 | * Bump version to 1.0.0 58 | 59 | -- Qi, Yadong Fri, 19 Aug 2022 14:30:15 +0800 60 | 61 | vm-manager (0.7.1) stable; urgency=medium 62 | 63 | * Bump version to 0.7.1 64 | 65 | -- Qi, Yadong Thu, 28 Jul 2022 14:10:52 +0800 66 | 67 | vm-manager (0.6.1) stable; urgency=medium 68 | 69 | * Bum version to 0.6.1 70 | 71 | -- Qi, Yadong Mon, 30 May 2022 14:05:14 +0800 72 | 73 | vm-manager (0.6.0) stable; urgency=medium 74 | 75 | * Bump version to 0.6.0 76 | 77 | -- Qi, Yadong Fri, 06 May 2022 08:59:33 +0800 78 | 79 | vm-manager (0.5.1) stable; urgency=medium 80 | 81 | * Bump version to 0.5.1 82 | 83 | -- Qi, Yadong Fri, 29 Apr 2022 13:08:28 +0800 84 | 85 | vm-manager (0.5.0) stable; urgency=medium 86 | 87 | * Bump version to 0.5.0 88 | 89 | -- Qi, Yadong Mon, 21 Feb 2022 08:38:14 +0800 90 | 91 | vm-manager (0.4.3) stable; urgency=medium 92 | 93 | * Bump version to 0.4.3 94 | 95 | -- Qi, Yadong Tue, 23 Nov 2021 16:03:57 +0800 96 | 97 | vm-manager (0.4.2) stable; urgency=medium 98 | 99 | * Bump version to 0.4.2 100 | 101 | -- Qi, Yadong Mon, 18 Oct 2021 16:42:16 +0800 102 | 103 | vm-manager (0.4.1) stable; urgency=medium 104 | 105 | * Bump version to 0.4.1 106 | 107 | -- Qi, Yadong Wed, 14 Jul 2021 10:37:44 +0800 108 | 109 | vm-manager (0.3.2) stable; urgency=medium 110 | 111 | * Bump version to 0.3.2 112 | 113 | -- Qi, Yadong Mon, 22 Mar 2021 08:42:07 +0800 114 | 115 | vm-manager (0.3.1) stable; urgency=medium 116 | 117 | * Bump version to 0.3.1 118 | 119 | -- Qi, Yadong Thu, 11 Mar 2021 14:13:45 +0800 120 | 121 | vm-manager (0.2.1) stable; urgency=medium 122 | 123 | * Bump version to 0.2.1 124 | 125 | -- Qi, Yadong Mon, 24 Aug 2020 14:16:18 +0800 126 | 127 | vm-manager (0.1.0) stable; urgency=medium 128 | 129 | * Bump version to 0.1.0 130 | 131 | -- Qi, Yadong Thu, 13 Aug 2020 09:16:18 +0800 132 | 133 | vm-manager (0.0.1) UNRELEASED; urgency=medium 134 | 135 | * Initial release. 136 | 137 | -- Qi, Yadong Wed, 08 Jul 2020 13:03:18 +0800 138 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: vm-manager 2 | Section: admin 3 | Priority: optional 4 | Maintainer: Qi, Yadong 5 | Build-Depends: 6 | debhelper (>=9), 7 | gcc, 8 | Standards-Version: 3.9.8 9 | Homepage: http://github.com/projectceladon/vm_manager 10 | 11 | Package: vm-manager 12 | Architecture: any-amd64 13 | Multi-Arch: foreign 14 | Depends: 15 | ${misc:Depends}, 16 | ${shlibs:Depends} 17 | Description: CiV VM Manager 18 | CiV VM Manager to manage the CiV guests 19 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Name: vm-manager 2 | Source: http://github.com/projectceladon/vm-manager 3 | 4 | Files: * 5 | Copyright: 2020 Intel Corporation. 6 | License: Apache-2.0 7 | -------------------------------------------------------------------------------- /debian/etc/modules-load.d/civ-mods-base.conf: -------------------------------------------------------------------------------- 1 | vhost-vsock 2 | -------------------------------------------------------------------------------- /debian/install: -------------------------------------------------------------------------------- 1 | debian/etc/modules-load.d/civ-mods-base.conf /etc/modules-load.d 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | export DH_VERBOSE = 1 3 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all,-format optimize=-all 4 | export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 5 | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 6 | 7 | %: 8 | dh $@ --parallel 9 | 10 | override_dh_auto_configure: 11 | dh_auto_configure -- -DCMAKE_BUILD_TYPE=Release 12 | 13 | override_dh_auto_install: 14 | dh_auto_install -- prefix=/usr 15 | 16 | override_dh_installudev: 17 | dh_installudev --name=civ-base --priority=98 18 | 19 | override_dh_clean: 20 | dh_clean 21 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/local-options: -------------------------------------------------------------------------------- 1 | #abort-on-upstream-changes 2 | #unapply-patches 3 | -------------------------------------------------------------------------------- /debian/vm-manager.civ-base.udev: -------------------------------------------------------------------------------- 1 | KERNEL=="renderD128", SUBSYSTEM=="drm", MODE="0666" 2 | KERNEL=="vhost-vsock", SUBSYSTEM=="misc", MODE="0666" 3 | KERNEL=="kvm", SUBSYSTEM=="misc", MODE="0666" 4 | SUBSYSTEM=="vfio", TAG+="uaccess" 5 | -------------------------------------------------------------------------------- /debian/vm-manager.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | case "$1" in 5 | configure) 6 | udevadm control --reload-rules && udevadm trigger 7 | ;; 8 | 9 | abort-upgrade|abort-remove|abort-deconfigure) 10 | ;; 11 | 12 | *) 13 | echo "postinst called with unknown argument \`$1'" >&2 14 | exit 1 15 | ;; 16 | esac 17 | 18 | exit 0 19 | -------------------------------------------------------------------------------- /debian/vm-manager.preinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | case "$1" in 5 | upgrade) 6 | echo "Try to stop old server!" 7 | vm-manager --stop-server || : 8 | ;; 9 | 10 | abort-upgrade|install) 11 | ;; 12 | 13 | *) 14 | echo "preinst called with unknown argument \`$1'" >&2 15 | exit 1 16 | ;; 17 | esac 18 | 19 | exit 0 20 | -------------------------------------------------------------------------------- /docs/DeveloperDoc.md: -------------------------------------------------------------------------------- 1 | Welcome to the developer documentation of vm manager. 2 | 3 | 4 | 5 | # Monitor guest console 6 | 7 | 1. To monitor guest console, add the console and log file for guest in the config file first with following command: 8 | 9 | ``` 10 | [extra] 11 | cmd=-chardev socket,id=charserial0,path=console_path,server=on,wait=off,logfile=log_path -serial chardev:charserial0 12 | ``` 13 | You need to specify paths for civ console and log file and replace ``console_path`` and ``log_path`` above. For example: 14 | 15 | ``` 16 | [extra] 17 | cmd=-chardev socket,id=charserial0,path=/tmp/civ-console,server=on,wait=off,logfile=/tmp/civ_serial.log -serial chardev:charserial0 18 | ``` 19 | Then you are able to find civ console named civ-console and log file named civ_serial.log under the /tmp. 20 | 21 | 22 | 2. Use the following command to monitor the guest console, the console_path shoud be the console path you specified before. 23 | 24 | ```sh 25 | $ socat file:$(tty),raw,echo=0 console_path 26 | ``` 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/Documentation.md: -------------------------------------------------------------------------------- 1 | # User Guide 2 | 3 | Verified on: 4 | 5 | * Hardware: KBL-NUC/CML-NUL 6 | * OS: Ubuntu20.04 7 | 8 | 9 | # Getting Started 10 | 11 | ## Prerequisites setup for Host Environment 12 | 13 | 1. Create required directories under `$HOME/` . 14 | ```sh 15 | $ mkdir ~/caas 16 | $ mkdir ~/caas/aaf 17 | $ mkdir ~/caas/vtpm0 18 | ``` 19 | 20 | 2. Download [celadon release](https://github.com/projectceladon/celadon-binary/) in `$HOME/caas/`. e.g. [CIV_00.21.01.12_A11](https://github.com/projectceladon/celadon-binary/tree/master/CIV_00.21.01.12_A11) . 21 | ```sh 22 | $ cd ~/caas/ 23 | $ tar zxvf caas-releasefiles-${build_variant}.tar.gz 24 | ``` 25 | 26 | 3. Launch the script provided to set up the environment for running CiV, **it may need reboot** . 27 | 28 | ```sh 29 | $ sudo -E ./scripts/setup_host.sh 30 | ``` 31 | 32 | 33 | 4. Modify the authentication of OVMF.fd . 34 | ```sh 35 | $ sudo chmod a+rw OVMF.fd 36 | ``` 37 | 38 | 39 | 40 | ## Install vm-manager 41 | 42 | Download [vm-manager package](https://github.com/projectceladon/vm_manager/suites/7205036575/artifacts/288276649), there should be a **vm-manager_1.0.0_amd64.deb** after unzip it. 43 | 44 | ```sh 45 | $ sudo dpkg -i vm-manager_1.0.0_amd64.deb 46 | ``` 47 | 48 | ### Usages of VM Manager 49 | 50 | ``` 51 | $ vm-manager -h 52 | Usage: 53 | vm-manager [-c] [-d vm_name] [-b vm_name] [-q vm_name] [-f vm_name] [-u vm_name] [--get-cid vm_name] [-l] [-v] [-h] 54 | Options: 55 | -h [ --help ] Show this help message 56 | -c [ --create ] arg Create a CiV guest 57 | -d [ --delete ] arg Delete a CiV guest 58 | -b [ --start ] arg Start a CiV guest 59 | -q [ --stop ] arg Stop a CiV guest 60 | -f [ --flash ] arg Flash a CiV guest 61 | -u [ --update ] arg Update an existing CiV guest 62 | --get-cid arg Get cid of a guest 63 | -l [ --list ] List existing CiV guest 64 | -v [ --version ] Show CiV vm-manager version 65 | --start-server Start host server 66 | --stop-server Stop host server 67 | --daemon start server as a daemon 68 | ``` 69 | 70 | 71 | 72 | ## Create a new Civ 73 | 74 | 75 | 1. Create folder to store civ config file. 76 | 77 | ```sh 78 | $ mkdir -p ~/.intel/.civ/ 79 | ``` 80 | 81 | 2. Create an ini file under `$HOME/.intel/.civ/` . 82 | Eg. If you want to name your civ instance as **civ-1**,the ini file `civ-1.ini` should be created under `$HOME/.intel/.civ/`. 83 | 84 | ```sh 85 | $ touch ~/.intel/.civ/civ-1.ini 86 | ``` 87 | Follow the sample ini to config your civ instance: [civ-1.ini](../sample/civ-1.ini). For details, see [the specific instructions of configuration](fields.md). 88 | 89 | 90 | 91 | 92 | 93 | ## Start the instance 94 | 95 | 1. Start vm-manager server as daemon 96 | ``` 97 | $ vm-manager –start-server –daemon 98 | ``` 99 | 100 | 2. Flash virtual disk if required, the flashing process will take a while. If the virtual disk image is already flashed, you can skip this step. 101 | ``` 102 | $ vm-manager -f civ-1 103 | ``` 104 | 105 | 3. Start a Civ guest 106 | ``` 107 | $ vm-manager -b civ-1 108 | ``` 109 | 110 | 111 | 112 | ## Other useful commands 113 | 114 | 1. Create a Civ guest 115 | This command will bring up a terminal UI, you can find the corresponding ini file in `$HOME/.intel/.civ/` after you clicked save button. 116 | Eg. If you want to name your civ instance as **civ-1**, the command should be: 117 | ```sh 118 | $ vm-manager -c civ-1 119 | ``` 120 | 2. Import an existing config file 121 | ```sh 122 | $ vm-manager -i civ-2.ini 123 | ``` 124 | 3. Delete a CiV config 125 | ```sh 126 | $ vm-manager -d civ-1 127 | ``` 128 | 129 | 4. Update/Modify a CiV config 130 | ```sh 131 | $ vm-manager -u civ-1 132 | ``` 133 | 134 | 5. List Guest 135 | ```sh 136 | $ vm-manager -b civ-1 137 | ``` 138 | 139 | 6. Stop Guest 140 | This command will force to quit the guest 141 | ```sh 142 | $ sudo vm-manager -q civ-1 143 | ``` 144 | 145 | 146 | -------------------------------------------------------------------------------- /docs/arch.drawio.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 | Host OS 17 | 18 |
19 |
20 |
21 |
22 | 23 | Host OS 24 | 25 |
26 |
27 | 28 | 29 | 30 | 31 |
32 |
33 |
34 | Vm-manager Server 35 |
36 |
37 |
38 |
39 | 40 | Vm-manager Server 41 | 42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
55 |
56 |
57 | Vm-manager Client 58 |
59 |
60 |
61 |
62 | 63 | Vm-manager Client 64 | 65 |
66 |
67 | 68 | 69 | 70 | 71 |
72 |
73 |
74 | 75 | Intel HW/FW 76 | 77 |
78 |
79 |
80 |
81 | 82 | Intel HW/FW 83 | 84 |
85 |
86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
94 |
95 |
96 | 97 | Kernel KVM 98 | 99 |
100 |
101 |
102 |
103 | 104 | Kernel KVM 105 | 106 |
107 |
108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
120 |
121 |
122 | APP-Manager 123 |
124 |
125 |
126 |
127 | 128 | APP-Manager 129 | 130 |
131 |
132 | 133 | 134 | 135 | 136 |
137 |
138 |
139 | APP(java) 140 |
141 |
142 |
143 |
144 | 145 | APP(java) 146 | 147 |
148 |
149 | 150 | 151 | 152 | 153 |
154 |
155 |
156 | jni 157 |
158 |
159 |
160 |
161 | 162 | jni 163 | 164 |
165 |
166 | 167 | 168 | 169 | 170 |
171 |
172 |
173 | APP(java) 174 |
175 |
176 |
177 |
178 | 179 | APP(java) 180 | 181 |
182 |
183 | 184 | 185 | 186 | 187 |
188 |
189 |
190 |
191 | 192 | 193 | Civ guest(QEMU) 194 | 195 | 196 |
197 |
198 | 199 | 200 | (cid = n) 201 | 202 | 203 |
204 |
205 |
206 |
207 |
208 | 209 | Civ guest(QEMU)... 210 | 211 |
212 |
213 | 214 | 215 | 216 | 217 |
218 |
219 |
220 | 221 | User 222 | 223 |
224 |
225 |
226 |
227 | 228 | User 229 | 230 |
231 |
232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 |
240 |
241 |
242 | 243 | Multi-display 244 |
245 | (cid=x) 246 |
247 | (display id=y) 248 |
249 |
250 |
251 |
252 |
253 | 254 | Multi-display... 255 | 256 |
257 |
258 | 259 | 260 | 261 | grpc 262 | 263 | 264 | 265 | 266 | 267 | share memory 268 | 269 | 270 | 271 | 272 | 273 | vSock 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 |
289 | 290 | 291 | 292 | 293 | Viewer does not support full SVG 1.1 294 | 295 | 296 | 297 |
-------------------------------------------------------------------------------- /docs/fields.md: -------------------------------------------------------------------------------- 1 | # CIV vm_manager config doc 2 | 3 | A [sample vm_manager config file](../sample/civ-1.ini). 4 | 5 | ## All Groups 6 | 7 | * global 8 | * emulator 9 | * memory 10 | * vcpu 11 | * firmware 12 | * disk 13 | * graphics 14 | * vtpm 15 | * rpmb 16 | * aaf 17 | * passthrough 18 | * mediation 19 | * guest_control 20 | * extra 21 | 22 | ## Configuration instructions 23 | 24 | 25 | ### [global] 26 | 27 | requirements: 28 | - name: name of the guest. 29 | - flashfiles: path of flashfiles. 30 | 31 | optional: 32 | - vsock_cid: cid of VM. 33 | - wait_ready: wait until vm is ready if `wait_ready == true`. 34 | 35 | 36 | ### [emulator] 37 | 38 | Choose a emulator to start, only QEMU supported now. 39 | requirements: 40 | - path: path of the emulator. 41 | optional: 42 | - type: type of emulator, default is qemu. 43 | 44 | 45 | ### [memory] 46 | 47 | Configure guest RAM. 48 | requirements: 49 | - size: initial amount of guest memory. 50 | 51 | 52 | ### [vcpu] 53 | 54 | requirements: 55 | - size: specify the number of cores the guest is permitted to use. 56 | 57 | 58 | ### [firmware] 59 | 60 | Add the pflash drive. Type must be "unified" or "splited" 61 | requirements: 62 | - type: 63 | * unified: 64 | * path: path of virtual firmware binary. if `type == unified`, specify **path** field 65 | * splited: if `type == splited`, specify **code** and **vars** field 66 | * code: path of virtual firmware code binary. 67 | * vars: path of virtual firmware vars binary. 68 | 69 | 70 | ### [disk] 71 | 72 | Define a new drive and add storage device based on the driver. 73 | requirements: 74 | - size: the disk image size by bytes. 75 | - path: path of disk image. 76 | 77 | 78 | ### [graphics] 79 | 80 | Assign a virtual GPU to the virtual machine. 81 | requirements: 82 | - type: types of virtual GPU provided: 83 | - virtio: a virtio based graphics adapter. 84 | - ramfb: display device. Once the guest OS has initialized the vgpu qemu will show the vgpu display. Otherwise the ramfb framebuffer is used. 85 | - GVT-g: virtualize the GPU for guest, and still letting host use the virtualized GPU normally. When GVT-g is selected, below field must be specified. 86 | - gvtg_version: the version of gvtg. 87 | - uuid: VGPU UUID. 88 | - GVT-d: passthrough GPU to guest, host cannot use GPU. 89 | - monitor: monitor id for SRIOV. 90 | - outputs: max outputs for virtio-gpu. 91 | 92 | 93 | ### [display] 94 | 95 | Specify the kind of display to use in QEMU. The GTK window is used by default if not specified. 96 | options: 97 | - options: choose one available display to use in QEMU. 98 | 99 | 100 | ### [net] 101 | 102 | For network emulation. 103 | requirements: 104 | - model: optional ethernet model, default is e1000. 105 | - adb_port: optional adb forwarding port. 106 | - fastboot_port: optional fastboot forwarding port. 107 | 108 | 109 | ### [vtpm] 110 | 111 | Add Trusted Platform Module support to QEMU. 112 | requirements: 113 | - bin_path: path of vtpm binary. 114 | - data_dir: path of data. 115 | 116 | 117 | ### [rpmb] 118 | 119 | Create a device and expose one serial port to the guest. 120 | requirements: 121 | - bin_path: path of rpmb binary 122 | - data_dir: path of the data. 123 | 124 | 125 | ### [aaf] 126 | 127 | Share filesystem of host to the guest system. 128 | requirements: 129 | - path: The path to share. 130 | - support_suspend: set the value to `true` or `enable` to enable suspend for guest. 131 | - audio_type: must be one of these two options: 132 | - sof-hda: enable "sof-hda" only when "sof-hda" sound card is enabled in the host and you are making audio passthrough for guest. 133 | - legacy-hda: In all other cases use "legacy-hda" only. 134 | 135 | 136 | ### [Passthrough] 137 | 138 | PCIe devices to passthrough to the VM. Specified the PCI id here if you want to passthrough it to guest. 139 | Requirements: 140 | - Device name need to be full, such as "0000:00:14.0". 141 | - Device name must be listed in the output of `lspci` command. (`lspci` command must be able to produce a list of pci devices) 142 | - Device names are separated by token "," (comma) 143 | 144 | *Potential improvements* 145 | - Detect whether `lspci` is available / Does not cause seg fault when `lspci` is not. 146 | 147 | 148 | ### [Mediation] 149 | 150 | Guest mediation services. Executable `batsys` and `thermsys` 151 | Requirements: 152 | - battery_med 153 | - thermal_med 154 | 155 | 156 | ### [Guest control] 157 | 158 | Enable guest services. Absolute paths to executable services needed, and Write access to directory is required to create pipe and socket. 159 | Requirements 160 | - time_keep: absolute path to guest_time_keeping.sh. 161 | - pm_control: absolute path to guest_pm_control. 162 | - vinput: absolute path to vinput-manager. 163 | 164 | ### [audio] 165 | 166 | - disable_emulation: Disable sound card emulation, set this option to true if the sound card is passthrough in `[passthrough]` 167 | 168 | ### [extra] 169 | - cmd: Set extra command. 170 | - service: Set extra services, use `:` split different services. 171 | - pwr_ctrl_multios: If set to "true", will create qmp socket in /tmp/ folder for suspend/hibernate feature. Setting to "false" will not create the sockeet. 172 | 173 | ### [bluetooth] 174 | - hci_down: If set to 'true', will bring down Bluetooth Hci Interface. Make sure BT USB Pci address is added in `[passthrough]`. 175 | -------------------------------------------------------------------------------- /sample/civ-1.ini: -------------------------------------------------------------------------------- 1 | [global] 2 | name=civ-1 3 | flashfiles=/home/ubuntu/caas/caas-flashfiles-CRe011396.zip 4 | #wait_ready=true 5 | 6 | [emulator] 7 | path=/usr/bin/qemu-system-x86_64 8 | 9 | [memory] 10 | size=4G 11 | 12 | [vcpu] 13 | num=1 14 | 15 | [firmware] 16 | type=unified 17 | path=/home/ubuntu/caas/OVMF.fd 18 | 19 | [disk] 20 | size=30G 21 | path=/home/ubuntu/caas/android.qcow2 22 | 23 | [graphics] 24 | type=virtio 25 | 26 | [display] 27 | options=gtk,gl=on 28 | 29 | [vtpm] 30 | bin_path=/usr/bin/swtpm 31 | data_dir=/home/ubuntu/caas/vtpm0 32 | 33 | [rpmb] 34 | bin_path=/home/ubuntu/caas/scripts/rpmb_dev 35 | data_dir=/home/ubuntu/caas 36 | 37 | [aaf] 38 | path=/home/ubuntu/caas/aaf 39 | audio_type=legacy-hda 40 | 41 | #keep this option as true when using audio in passthrough mode 42 | [audio] 43 | disable_emulation = true 44 | 45 | [extra] 46 | cmd=-chardev socket,id=charserial0,path=/tmp/civ-console,server=on,wait=off,logfile=/tmp/civ-1_serial.log -serial chardev:charserial0 47 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | 9 | cmake_minimum_required(VERSION 3.10) 10 | 11 | set(CMAKE_GENERATOR "Unix Makefiles" CACHE INTERNAL "" FORCE) 12 | 13 | if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) 14 | SET(CMAKE_BUILD_TYPE Release) 15 | endif(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) 16 | 17 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 18 | include(BoostCMake) 19 | include(FTXUICMake) 20 | include(gRPCFetchCMake) 21 | include(revisionCMake) 22 | 23 | set(PROTOS_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/services/protos/gens/) 24 | include_directories(${PROTOS_GEN_DIR}) 25 | add_subdirectory(services/protos) 26 | 27 | set(PROJECT_NAME "vm-manager") 28 | project(${PROJECT_NAME}) 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-result -Wno-unused-variable -Wno-narrowing -O2") 30 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g1 -O3 -fstack-protector-strong -Wdate-time -D_FORTIFY_SOURCE=2" CACHE STRING "CXX Release Flags" FORCE) 31 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O2 -g3") 32 | 33 | find_package(PkgConfig REQUIRED) 34 | include_directories(${DEPS_INCS_INCLUDE_DIRS}) 35 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 36 | 37 | include_directories(${EP_BOOST_INC_DIR}) 38 | 39 | file(GLOB guest_src guest/*.cc) 40 | file(GLOB services_src services/*.cc) 41 | file(GLOB utils_src utils/*.cc) 42 | 43 | add_executable(${PROJECT_NAME} vm_manager.cc ${guest_src} ${services_src} ${utils_src}) 44 | add_dependencies(${PROJECT_NAME} vm_grpc_proto) 45 | 46 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 47 | 48 | target_link_libraries(${PROJECT_NAME} 49 | PRIVATE ftxui::screen 50 | PRIVATE ftxui::dom 51 | PRIVATE ftxui::component 52 | PRIVATE ep_boost::program_options 53 | PRIVATE ep_boost::log 54 | PRIVATE ep_boost::log_setup 55 | PRIVATE ep_boost::thread 56 | PRIVATE ep_boost::filesystem 57 | PRIVATE ep_boost::chrono 58 | 59 | vm_grpc_proto 60 | PRIVATE ${_REFLECTION} 61 | PRIVATE ${_GRPC_GRPCPP} 62 | PRIVATE ${_PROTOBUF_LIBPROTOBUF} 63 | -lrt 64 | ) 65 | set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE -s) 66 | 67 | install(TARGETS ${PROJECT_NAME} 68 | RUNTIME DESTINATION bin 69 | ) 70 | 71 | add_subdirectory(host/app_launcher) 72 | 73 | set(CMAKE_CXX_STANDARD 17) 74 | set(CMAKE_CXX_STANDARD_REQUIRED True) 75 | -------------------------------------------------------------------------------- /src/android/applauncher/Android.bp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | INCLUDE_DIR = [ 9 | "external/grpc-grpc", 10 | "external/grpc-grpc/include", 11 | "external/grpc-grpc/third_party/cares", 12 | "external/grpc-grpc/third_party/cares/config_android", 13 | "external/grpc-grpc/src/core/ext/filters/client_channel", 14 | "external/grpc-grpc/third_party/nanopb", 15 | "external/protobuf", 16 | "external/protobuf/src", 17 | "external/protobuf/config", 18 | "external/protobuf/android", 19 | "device/intel/civ/host/vm-manager/src/include/constants" 20 | ] 21 | 22 | CFLAGS = [ 23 | "-fexceptions", 24 | "-std=c++11", 25 | "-fPIE", 26 | "-Wall", 27 | "-Wno-unused-variable", 28 | "-Wno-unused-parameter", 29 | "-Wno-non-virtual-dtor", 30 | "-Wno-missing-field-initializers", 31 | "-Wno-error", 32 | "-Wextra", 33 | "-Wno-extern-c-compat", 34 | "-Wno-sign-compare", 35 | "-Wno-unused-local-typedef", 36 | "-Wno-unused-private-field", 37 | "-Wno-invalid-partial-specialization", 38 | "-Wno-array-bounds", 39 | "-D_FORTIFY_SOURCE=2", 40 | "-fvisibility=default", 41 | "-fwrapv", 42 | "-fstack-protector-all", 43 | "-Wno-conversion-null", 44 | "-Wnull-dereference", 45 | "-Warray-bounds", 46 | "-O2", 47 | "-fPIC", 48 | ] 49 | 50 | cc_binary { 51 | name: "civ_applauncher", 52 | srcs: [ 53 | "civ_applauncher.cc" 54 | ], 55 | 56 | include_dirs: INCLUDE_DIR, 57 | 58 | cflags: CFLAGS, 59 | 60 | shared_libs: [ 61 | "libgrpc++", 62 | "libprotobuf-cpp-full", 63 | "liblog", 64 | ], 65 | 66 | generated_headers: [ 67 | "CivCommonProtoStub_h", 68 | "CivVmGuestProtoStub_h", 69 | ], 70 | generated_sources: [ 71 | "CivCommonProtoStub_cc", 72 | "CivVmGuestProtoStub_cc", 73 | ], 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/android/applauncher/civ_applauncher.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | #include "vm_guest.grpc.pb.h" 19 | #include "vm_manager.h" 20 | 21 | #define LOG_TAG "civ_applauncher" 22 | #define ALOG(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 23 | 24 | class CivAppLauncherImpl final : public vm_manager::CivAppLauncher::Service { 25 | public: 26 | CivAppLauncherImpl() = default; 27 | CivAppLauncherImpl(const CivAppLauncherImpl &) = delete; 28 | CivAppLauncherImpl& operator=(const CivAppLauncherImpl&) = delete; 29 | 30 | ~CivAppLauncherImpl() override = default; 31 | 32 | grpc::Status LaunchApp(grpc::ServerContext* ctx, 33 | const vm_manager::AppLaunchRequest* request, 34 | vm_manager::AppLaunchResponse* respond) override { 35 | ALOG("Launch APP: %s, Disp id: %u\n", request->app_name().c_str(), request->disp_id()); 36 | std::string cmd("/system/bin/am start -n " + request->app_name() + 37 | " --display " + std::to_string(request->disp_id())); 38 | if (system(cmd.c_str())) { 39 | respond->set_code(-1); 40 | respond->set_status(vm_manager::AppStatus::FAILED); 41 | } else { 42 | respond->set_code(0); 43 | respond->set_status(vm_manager::AppStatus::LAUNCHED); 44 | } 45 | return grpc::Status::OK; 46 | } 47 | 48 | private: 49 | }; 50 | 51 | int main() { 52 | char listener_address[50] = { 0 }; 53 | snprintf(listener_address, sizeof(listener_address) - 1, "vsock:%u:%u", VMADDR_CID_ANY, 54 | vm_manager::kCivAppLauncherListenerPort); 55 | grpc::ServerBuilder builder; 56 | CivAppLauncherImpl listener; 57 | ALOG("Civ Applauncher Listener listen@%s\n", listener_address); 58 | builder.AddListeningPort(listener_address, grpc::InsecureServerCredentials()); 59 | builder.RegisterService(&listener); 60 | 61 | std::unique_ptr server(builder.BuildAndStart()); 62 | 63 | if (server) { 64 | server->Wait(); 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /src/android/powerctl/Android.bp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | INCLUDE_DIR = [ 9 | "external/grpc-grpc", 10 | "external/grpc-grpc/include", 11 | "external/grpc-grpc/third_party/cares", 12 | "external/grpc-grpc/third_party/cares/config_android", 13 | "external/grpc-grpc/src/core/ext/filters/client_channel", 14 | "external/grpc-grpc/third_party/nanopb", 15 | "external/protobuf", 16 | "external/protobuf/src", 17 | "external/protobuf/config", 18 | "external/protobuf/android", 19 | "device/intel/civ/host/vm-manager/src/include/constants" 20 | ] 21 | 22 | CFLAGS = [ 23 | "-fexceptions", 24 | "-std=c++11", 25 | "-fPIE", 26 | "-Wall", 27 | "-Wno-unused-variable", 28 | "-Wno-unused-parameter", 29 | "-Wno-non-virtual-dtor", 30 | "-Wno-missing-field-initializers", 31 | "-Wno-error", 32 | "-Wextra", 33 | "-Wno-extern-c-compat", 34 | "-Wno-sign-compare", 35 | "-Wno-unused-local-typedef", 36 | "-Wno-unused-private-field", 37 | "-Wno-invalid-partial-specialization", 38 | "-Wno-array-bounds", 39 | "-D_FORTIFY_SOURCE=2", 40 | "-fvisibility=default", 41 | "-fwrapv", 42 | "-fstack-protector-all", 43 | "-Wno-conversion-null", 44 | "-Wnull-dereference", 45 | "-Warray-bounds", 46 | "-O2", 47 | "-fPIC", 48 | ] 49 | 50 | cc_binary { 51 | name: "civ_powerctl", 52 | srcs: [ 53 | "civ_powerctl.cc" 54 | ], 55 | 56 | include_dirs: INCLUDE_DIR, 57 | 58 | cflags: CFLAGS, 59 | 60 | shared_libs: [ 61 | "libgrpc++", 62 | "libprotobuf-cpp-full", 63 | ], 64 | 65 | generated_headers: [ 66 | "CivCommonProtoStub_h", 67 | "CivVmGuestProtoStub_h", 68 | ], 69 | generated_sources: [ 70 | "CivCommonProtoStub_cc", 71 | "CivVmGuestProtoStub_cc", 72 | ], 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/android/powerctl/civ_powerctl.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include "vm_guest.grpc.pb.h" 17 | #include "vm_manager.h" 18 | 19 | class CivPowerCtlImpl final : public vm_manager::CivPowerCtl::Service { 20 | public: 21 | CivPowerCtlImpl() = default; 22 | CivPowerCtlImpl(const CivPowerCtlImpl &) = delete; 23 | CivPowerCtlImpl& operator=(const CivPowerCtlImpl&) = delete; 24 | 25 | ~CivPowerCtlImpl() override = default; 26 | 27 | grpc::Status Shutdown(grpc::ServerContext* ctx, 28 | const vm_manager::EmptyMessage* request, 29 | vm_manager::EmptyMessage* respond) override { 30 | std::cout << "Shutdown Signal from Host!" << std::endl; 31 | system("/system/bin/reboot -p shutdown"); 32 | return grpc::Status::OK; 33 | } 34 | 35 | private: 36 | }; 37 | 38 | int main() { 39 | char listener_address[50] = { 0 }; 40 | snprintf(listener_address, sizeof(listener_address) - 1, "vsock:%u:%u", VMADDR_CID_ANY, 41 | vm_manager::kCivPowerCtlListenerPort); 42 | grpc::ServerBuilder builder; 43 | CivPowerCtlImpl listener; 44 | std::cout << "Civ Powerctl Listener listen@" << listener_address << std::endl; 45 | builder.AddListeningPort(listener_address, grpc::InsecureServerCredentials()); 46 | builder.RegisterService(&listener); 47 | 48 | std::unique_ptr server(builder.BuildAndStart()); 49 | 50 | if (server) { 51 | server->Wait(); 52 | } 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /src/android/startup_notify/Android.bp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | INCLUDE_DIR = [ 9 | "external/grpc-grpc", 10 | "external/grpc-grpc/include", 11 | "external/grpc-grpc/third_party/cares", 12 | "external/grpc-grpc/third_party/cares/config_android", 13 | "external/grpc-grpc/src/core/ext/filters/client_channel", 14 | "external/grpc-grpc/third_party/nanopb", 15 | "external/protobuf", 16 | "external/protobuf/src", 17 | "external/protobuf/config", 18 | "external/protobuf/android", 19 | "device/intel/civ/host/vm-manager/src/include/constants" 20 | ] 21 | 22 | CFLAGS = [ 23 | "-fexceptions", 24 | "-std=c++11", 25 | "-fPIE", 26 | "-Wall", 27 | "-Wno-unused-variable", 28 | "-Wno-unused-parameter", 29 | "-Wno-non-virtual-dtor", 30 | "-Wno-missing-field-initializers", 31 | "-Wno-error", 32 | "-Wextra", 33 | "-Wno-extern-c-compat", 34 | "-Wno-sign-compare", 35 | "-Wno-unused-local-typedef", 36 | "-Wno-unused-private-field", 37 | "-Wno-invalid-partial-specialization", 38 | "-Wno-array-bounds", 39 | "-D_FORTIFY_SOURCE=2", 40 | "-fvisibility=default", 41 | "-fwrapv", 42 | "-fstack-protector-all", 43 | "-Wno-conversion-null", 44 | "-Wnull-dereference", 45 | "-Warray-bounds", 46 | "-O2", 47 | "-fPIC", 48 | ] 49 | 50 | cc_binary { 51 | name: "civ_startup_notify", 52 | srcs: [ 53 | "civ_startup_notify.cc" 54 | ], 55 | 56 | include_dirs: INCLUDE_DIR, 57 | 58 | cflags: CFLAGS, 59 | 60 | shared_libs: [ 61 | "libgrpc++", 62 | "libprotobuf-cpp-full" 63 | ], 64 | 65 | generated_headers: [ 66 | "CivCommonProtoStub_h", 67 | "CivVmHostProtoStub_h", 68 | ], 69 | generated_sources: [ 70 | "CivCommonProtoStub_cc", 71 | "CivVmHostProtoStub_cc", 72 | ], 73 | } 74 | -------------------------------------------------------------------------------- /src/android/startup_notify/civ_startup_notify.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include "vm_host.grpc.pb.h" 17 | #include "vm_manager.h" 18 | 19 | class StartupNotify { 20 | public: 21 | explicit StartupNotify(std::shared_ptr channel) 22 | : stub_(vm_manager::StartupListener::NewStub(channel)) {} 23 | 24 | bool VmReady(void) { 25 | vm_manager::EmptyMessage request; 26 | vm_manager::EmptyMessage reply; 27 | 28 | grpc::ClientContext context; 29 | 30 | grpc::Status status = stub_->VmReady(&context, request, &reply); 31 | 32 | if (status.ok()) { 33 | return true; 34 | } else { 35 | std::cout << status.error_code() << ": " << status.error_message() 36 | << std::endl; 37 | return false; 38 | } 39 | } 40 | 41 | private: 42 | std::unique_ptr stub_; 43 | }; 44 | 45 | int main() { 46 | char listener_address[50] = { 0 }; 47 | snprintf(listener_address, sizeof(listener_address) - 1, "vsock:%u:%u", 48 | VMADDR_CID_HOST, 49 | vm_manager::kCivStartupListenerPort); 50 | StartupNotify notify(grpc::CreateChannel(listener_address, grpc::InsecureChannelCredentials())); 51 | if (!notify.VmReady()) { 52 | std::cerr << "Failed to notify host that civ is ready!" << std::endl; 53 | return -1; 54 | } 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /src/cmake/BoostCMake.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | include(ExternalProject) 9 | 10 | set(DEPENDENCIES) 11 | set(EXTRA_CMAKE_ARGS) 12 | 13 | set(BOOST_BOOTSTRAP_COMMAND ./bootstrap.sh) 14 | set(BOOST_BUILD_TOOL ./b2) 15 | set(BOOST_CXXFLAGS "cxxflags=-std=c++17") 16 | set(BOOST_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/INSTALL/boost_1.79.0) 17 | 18 | ExternalProject_Add(ep_boost 19 | 20 | PREFIX ${CMAKE_BINARY_DIR}/external 21 | TIMEOUT 300 22 | 23 | URL https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2 24 | 25 | URL_HASH SHA256=475d589d51a7f8b3ba2ba4eda022b170e562ca3b760ee922c146b6c65856ef39 26 | 27 | BUILD_IN_SOURCE 1 28 | 29 | CONFIGURE_COMMAND ${BOOST_BOOTSTRAP_COMMAND} 30 | 31 | BUILD_ALWAYS 0 32 | BUILD_COMMAND ./b2 install 33 | ${BOOST_CXXFLAGS} 34 | threading=multi 35 | variant=release 36 | link=static 37 | --prefix=${BOOST_INSTALL_PREFIX} 38 | --layout=system 39 | --with-program_options 40 | --with-log 41 | --with-filesystem 42 | --with-chrono 43 | 44 | INSTALL_COMMAND "" 45 | LOG_CONFIGURE 1 46 | LOG_BUILD 1 47 | LOG_INSTALL 1 48 | ) 49 | 50 | set(EP_BOOST_INC_DIR ${BOOST_INSTALL_PREFIX}/include) 51 | set(EP_BOOST_LIB_DIR ${BOOST_INSTALL_PREFIX}/lib) 52 | 53 | function(add_boost_lib) 54 | set(mod_name ${ARGV0}) 55 | 56 | add_library(ep_boost::${mod_name} STATIC IMPORTED GLOBAL) 57 | set_target_properties( 58 | ep_boost::${mod_name} PROPERTIES 59 | IMPORTED_LOCATION ${EP_BOOST_LIB_DIR}/libboost_${mod_name}.a 60 | ) 61 | add_dependencies(ep_boost::${mod_name} ep_boost) 62 | endfunction() 63 | 64 | add_boost_lib(thread) 65 | add_boost_lib(program_options) 66 | add_boost_lib(log) 67 | add_boost_lib(log_setup) 68 | add_boost_lib(filesystem) 69 | add_boost_lib(chrono) 70 | -------------------------------------------------------------------------------- /src/cmake/FTXUICMake.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | 9 | include(FetchContent) 10 | 11 | set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE) 12 | FetchContent_Declare(ftxui 13 | GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui 14 | GIT_TAG 548fa51b7115551b42c0f12235de9eb79e7e33e3 15 | #GIT_SHALLOW 1 16 | ) 17 | set(FETCHCONTENT_QUIET OFF) 18 | 19 | FetchContent_GetProperties(ftxui) 20 | if(NOT ftxui_POPULATED) 21 | FetchContent_Populate(ftxui) 22 | add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL) 23 | endif() 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/cmake/gRPCExtCMake.cmake: -------------------------------------------------------------------------------- 1 | function (build_ext_grpc) 2 | set(GRPC_INST_PREFIX ${ARGV0}) 3 | set(CMAKELIST_CONTENT " 4 | cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) 5 | project(build_ext_grpc) 6 | include(ExternalProject) 7 | 8 | ExternalProject_Add(ep_grpc 9 | 10 | PREFIX ${CMAKE_BINARY_DIR}/external 11 | TIMEOUT 300 12 | 13 | GIT_REPOSITORY https://github.com/grpc/grpc 14 | 15 | GIT_TAG v1.46.2 16 | 17 | GIT_SHALLOW 1 18 | 19 | CMAKE_ARGS -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${GRPC_INST_PREFIX} 20 | 21 | BUILD_COMMAND make -j 22 | 23 | BUILD_ALWAYS 0 24 | 25 | LOG_CONFIGURE 1 26 | LOG_UPDATE 1 27 | LOG_PATCH 1 28 | LOG_BUILD 1 29 | LOG_INSTALL 1 30 | ) 31 | 32 | set(GRPC_PATCH_CMD git am -3 ${CMAKE_SOURCE_DIR}/src/external/patches/grpc/0001-support-vsock_v1.46.2.patch) 33 | ExternalProject_Get_property(ep_grpc SOURCE_DIR) 34 | ExternalProject_Add_Step(ep_grpc patch_vsock 35 | DEPENDEES update 36 | WORKING_DIRECTORY \${SOURCE_DIR} 37 | COMMAND \${GRPC_PATCH_CMD} 38 | LOG 1 39 | ) 40 | 41 | add_custom_target(build_grpc) 42 | add_dependencies(build_grpc ep_grpc) 43 | ") 44 | 45 | set(TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/ExternalProjects/grpc") 46 | 47 | file(WRITE "${TARGET_DIR}/CMakeLists.txt" "${CMAKELIST_CONTENT}") 48 | 49 | file(MAKE_DIRECTORY "${TARGET_DIR}" "${TARGET_DIR}/build") 50 | 51 | execute_process(COMMAND ${CMAKE_COMMAND} 52 | .. 53 | WORKING_DIRECTORY "${TARGET_DIR}/build") 54 | 55 | execute_process(COMMAND ${CMAKE_COMMAND} 56 | --build . 57 | WORKING_DIRECTORY "${TARGET_DIR}/build") 58 | 59 | endfunction() 60 | 61 | set(GRPC_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/INSTALL/grpc) 62 | 63 | 64 | find_package(Protobuf 65 | CONFIG 66 | PATHS ${GRPC_INSTALL_PREFIX} 67 | NO_DEFAULT_PATH 68 | ) 69 | 70 | find_package(gRPC 71 | CONFIG 72 | PATHS ${GRPC_INSTALL_PREFIX} 73 | NO_DEFAULT_PATH 74 | ) 75 | 76 | 77 | if((NOT Protobuf_FOUND) OR (NOT gRPC_FOUND)) 78 | build_ext_grpc(${GRPC_INSTALL_PREFIX}) 79 | find_package(Protobuf 80 | CONFIG 81 | REQUIRED 82 | PATHS ${GRPC_INSTALL_PREFIX} 83 | NO_DEFAULT_PATH 84 | ) 85 | 86 | find_package(gRPC 87 | CONFIG 88 | REQUIRED 89 | PATHS ${GRPC_INSTALL_PREFIX} 90 | NO_DEFAULT_PATH 91 | ) 92 | endif() 93 | 94 | 95 | message(STATUS "Using protobuf ${Protobuf_VERSION}") 96 | set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) 97 | set(_REFLECTION gRPC::grpc++_reflection) 98 | message(STATUS "libproto: ${_PROTOBUF_LIBPROTOBUF}") 99 | set(_PROTOBUF_PROTOC $) 100 | message(STATUS "${_PROTOBUF_PROTOC}") 101 | 102 | 103 | message(STATUS "Using gRPC ${gRPC_VERSION}") 104 | 105 | set(_GRPC_GRPCPP gRPC::grpc++) 106 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) -------------------------------------------------------------------------------- /src/cmake/gRPCFetchCMake.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | 9 | include(FetchContent) 10 | 11 | set(protobuf_INSTALL OFF) 12 | 13 | FetchContent_Declare( 14 | grpc 15 | GIT_REPOSITORY https://github.com/grpc/grpc 16 | GIT_TAG v1.65.1 17 | GIT_SHALLOW 1 18 | ) 19 | set(FETCHCONTENT_QUIET OFF) 20 | 21 | FetchContent_GetProperties(grpc) 22 | if (NOT grpc_POPULATED) 23 | FetchContent_Populate(grpc) 24 | 25 | add_subdirectory(${grpc_SOURCE_DIR} ${grpc_BINARY_DIR} EXCLUDE_FROM_ALL) 26 | endif() 27 | 28 | set(_PROTOBUF_LIBPROTOBUF libprotobuf) 29 | set(_REFLECTION grpc++_reflection) 30 | set(_PROTOBUF_PROTOC $) 31 | set(_GRPC_GRPCPP grpc++) 32 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 33 | -------------------------------------------------------------------------------- /src/cmake/revision.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef BUILD_REVISION_H_ 9 | #define BUILD_REVISION_H_ 10 | #pragma once 11 | 12 | #define BUILD_REVISION "@build_version_@" 13 | #define BUILD_TYPE "@build_type_@" 14 | #define BUILD_TIMESTAMP "@time_stamp_@" 15 | #define BUILD_FQDN "@build_fqdn_@" 16 | 17 | #endif // BUILD_REVISION_H_ 18 | -------------------------------------------------------------------------------- /src/cmake/revisionCMake.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | 9 | set(main_revision_ "1.3.0") 10 | 11 | set(rel_version_ "unknown") 12 | 13 | find_package(Git) 14 | if(GIT_FOUND) 15 | set(tag_ver "v${main_revision_}") 16 | execute_process( 17 | COMMAND ${GIT_EXECUTABLE} describe --all --match ${tag_ver} 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 19 | OUTPUT_VARIABLE rel_version_ 20 | ERROR_QUIET 21 | OUTPUT_STRIP_TRAILING_WHITESPACE 22 | ) 23 | if("${rel_version_}" STREQUAL "") 24 | execute_process( 25 | COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD 26 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 27 | OUTPUT_VARIABLE rel_version_ 28 | ERROR_QUIET 29 | OUTPUT_STRIP_TRAILING_WHITESPACE 30 | ) 31 | endif() 32 | else() 33 | message(STATUS "Git not found!") 34 | endif() 35 | 36 | set(build_version_ "${main_revision_} ${rel_version_}") 37 | message(STATUS "Build version: ${build_version_}") 38 | 39 | set(build_type_ ${CMAKE_BUILD_TYPE}) 40 | message(STATUS "BUILD Type: ${build_type_}") 41 | 42 | unset(ENV{SOURCE_DATE_EPOCH}) 43 | string(TIMESTAMP time_stamp_ UTC) 44 | message(STATUS "BUILD TimeStamp: ${time_stamp_}") 45 | 46 | cmake_host_system_information(RESULT build_fqdn_ QUERY FQDN) 47 | message(STATUS "BUILD FQDN: ${build_fqdn_}") 48 | 49 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/revision.h.in ${CMAKE_CURRENT_BINARY_DIR}/revision.h @ONLY) 50 | -------------------------------------------------------------------------------- /src/guest/aaf.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | 10 | #include 11 | 12 | #include "guest/aaf.h" 13 | #include "utils/log.h" 14 | 15 | namespace vm_manager { 16 | 17 | const char *kAafKeyGpuType = "gpu-type"; 18 | const char *kAafKeySuspend = "suspend"; 19 | const char *kAafKeyAudioType = "audio-type"; 20 | 21 | void Aaf::Set(std::string k, std::string v) { 22 | auto find = data_.find(k); 23 | if (find != data_.end()) { 24 | find->second = v; 25 | } 26 | } 27 | 28 | void Aaf::Flush(void) { 29 | boost::system::error_code ec; 30 | if (path_.empty()) { 31 | LOG(warning) << "Aaf path not set!"; 32 | return; 33 | } 34 | 35 | if (!boost::filesystem::exists(path_, ec)) { 36 | LOG(warning) << "Aaf path not exists!"; 37 | return; 38 | } 39 | 40 | if (!boost::filesystem::is_directory(path_, ec)) { 41 | LOG(warning) << "Aaf path is not directory!"; 42 | return; 43 | } 44 | 45 | std::string target_file(path_ + "/" + "mixins.spec"); 46 | std::ofstream out; 47 | 48 | if (boost::filesystem::exists(target_file, ec)) { 49 | std::string hidden_file(path_ + "/" + ".mixins.spec~"); 50 | boost::filesystem::rename(target_file, hidden_file, ec); 51 | std::ifstream in(hidden_file, std::ios::in); 52 | out.open(target_file); 53 | std::string tmp; 54 | LOG(info) << target_file; 55 | while (!in.eof()) { 56 | getline(in, tmp); 57 | if (tmp.empty()) 58 | continue; 59 | std::string key = tmp.substr(0, tmp.find(":")); 60 | if (key.length() == 0) 61 | continue; 62 | auto find = data_.find(key); 63 | if ((find != data_.end()) && !find->second.empty()) { 64 | out << key << ":" << find->second << "\n"; 65 | find->second.clear(); 66 | } else { 67 | out << tmp << "\n"; 68 | } 69 | } 70 | in.close(); 71 | boost::filesystem::remove(hidden_file, ec); 72 | } 73 | 74 | if (!out.is_open()) { 75 | out.open(target_file); 76 | } 77 | 78 | for (auto it = data_.begin(); it != data_.end(); it++) { 79 | if (!it->second.empty()) { 80 | out << it->first << ":" << it->second << "\n"; 81 | it->second.clear(); 82 | } 83 | } 84 | 85 | out.close(); 86 | } 87 | 88 | Aaf::Aaf(const char *path) : path_(path) { 89 | data_.insert(std::pair(kAafKeyGpuType, std::string())); 90 | data_.insert(std::pair(kAafKeySuspend, std::string())); 91 | data_.insert(std::pair(kAafKeyAudioType, std::string())); 92 | } 93 | 94 | } // namespace vm_manager 95 | -------------------------------------------------------------------------------- /src/guest/aaf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #ifndef SRC_GUEST_AAF_H_ 10 | #define SRC_GUEST_AAF_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace vm_manager { 17 | 18 | extern const char *kAafKeyGpuType; 19 | extern const char *kAafKeySuspend; 20 | extern const char *kAafKeyAudioType; 21 | 22 | class Aaf { 23 | public: 24 | explicit Aaf(const char *path); 25 | 26 | void Set(std::string k, std::string v); 27 | 28 | void Flush(void); 29 | 30 | private: 31 | Aaf(const Aaf&) = delete; 32 | Aaf& operator=(const Aaf&) = delete; 33 | 34 | std::string path_; 35 | std::map data_; 36 | }; 37 | 38 | } // namespace vm_manager 39 | 40 | #endif // SRC_GUEST_AAF_H_ 41 | -------------------------------------------------------------------------------- /src/guest/config_parser.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "guest/config_parser.h" 21 | #include "utils/log.h" 22 | 23 | namespace vm_manager { 24 | 25 | using std::map; 26 | using std::vector; 27 | using std::string_view; 28 | using boost::property_tree::ptree; 29 | 30 | map> kConfigMap = { 31 | { kGroupGlob, { kGlobName, kGlobFlashfiles, kGlobCid, kGlobWaitReady } }, 32 | { kGroupEmul, { kEmulType, kEmulPath } }, 33 | { kGroupMem, { kMemSize } }, 34 | { kGroupVcpu, { kVcpuNum } }, 35 | { kGroupFirm, { kFirmType, kFirmPath, kFirmCode, kFirmVars } }, 36 | { kGroupDisk, { kDiskSize, kDiskPath } }, 37 | { kGroupVgpu, { kVgpuType, kVgpuGvtgVer, kVgpuUuid, kVgpuMonId, kVgpuOutputs } }, 38 | { kGroupDisplay, { kDispOptions } }, 39 | { kGroupNet, { kNetModel, kNetAdbPort, kNetFastbootPort } }, 40 | { kGroupVtpm, { kVtpmBinPath, kVtpmDataDir } }, 41 | { kGroupRpmb, { kRpmbBinPath, kRpmbDataDir } }, 42 | { kGroupAaf, { kAafPath, kAafSuspend, kAafAudioType }}, 43 | { kGroupPciPt, { kPciPtDev }}, 44 | { kGroupbluetooth, { kHciDown }}, 45 | { kGroupAudio, { kDisableEmul } }, 46 | { kGroupMed, { kMedBattery, kMedThermal, kMedCamera } }, 47 | { kGroupService, { kServTimeKeep, kServPmCtrl, kServVinput } }, 48 | { kGroupExtra, { kExtraCmd, kExtraService, kExtraPwrCtrlMultiOS } } 49 | }; 50 | 51 | bool CivConfig::SanitizeOpts(void) { 52 | for (auto& section : cfg_data_) { 53 | auto group = kConfigMap.find(section.first); 54 | if (group != kConfigMap.end()) { 55 | for (auto& subsec : section.second) { 56 | auto key = std::find(group->second.begin(), group->second.end(), subsec.first); 57 | if (key == group->second.end()) { 58 | LOG(error) << "Invalid key: " << section.first << "." << subsec.first << "\n"; 59 | return false; 60 | } 61 | } 62 | } else { 63 | LOG(error) << "Invalid group: " << section.first << "\n"; 64 | return false; 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | bool CivConfig::ReadConfigFile(std::string path) { 71 | boost::system::error_code ec; 72 | if (!boost::filesystem::exists(path, ec)) { 73 | LOG(error) << "File not exists: " << path << std::endl; 74 | return false; 75 | } 76 | 77 | if (!boost::filesystem::is_regular_file(path, ec)) { 78 | LOG(error) << "File is not a regular file: " << path << std::endl; 79 | return false; 80 | } 81 | 82 | cfg_data_.clear(); 83 | read_ini(path, cfg_data_); 84 | 85 | if (!SanitizeOpts()) { 86 | cfg_data_.clear(); 87 | return false; 88 | } 89 | return true; 90 | } 91 | 92 | bool CivConfig::WriteConfigFile(std::string path) { 93 | boost::system::error_code ec; 94 | if (!boost::filesystem::exists(path, ec)) { 95 | LOG(error) << "File not exists: " << path << std::endl; 96 | return false; 97 | } 98 | 99 | if (!boost::filesystem::is_regular_file(path, ec)) { 100 | LOG(error) << "File is not a regular file: " << path << std::endl; 101 | return false; 102 | } 103 | 104 | if (cfg_data_.empty()) { 105 | LOG(warning) << "No data to write, skip!" << std::endl; 106 | return true; 107 | } 108 | 109 | if (!SanitizeOpts()) { 110 | return false; 111 | } 112 | 113 | write_ini(path, cfg_data_); 114 | return true; 115 | } 116 | 117 | std::string CivConfig::GetValue(std::string group, std::string key) { 118 | try { 119 | std::string val = cfg_data_.get(group + "." + key); 120 | boost::trim(val); 121 | return val; 122 | } catch(const std::exception& e) { 123 | LOG(warning) << e.what(); 124 | return std::string(); 125 | } 126 | } 127 | 128 | bool CivConfig::SetValue(const std::string group, const std::string key, const std::string value) { 129 | if (!value.empty()) { 130 | try { 131 | cfg_data_.put(group + "." + key, value); 132 | } catch(const std::exception& e) { 133 | LOG(warning) << e.what(); 134 | return false; 135 | } 136 | } 137 | return true; 138 | } 139 | 140 | } // namespace vm_manager 141 | 142 | -------------------------------------------------------------------------------- /src/guest/config_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_GUEST_CONFIG_PARSER_H_ 9 | #define SRC_GUEST_CONFIG_PARSER_H_ 10 | 11 | #include 12 | 13 | #include 14 | 15 | namespace vm_manager { 16 | 17 | /* Groups */ 18 | constexpr char kGroupGlob[] = "global"; 19 | constexpr char kGroupEmul[] = "emulator"; 20 | constexpr char kGroupMem[] = "memory"; 21 | constexpr char kGroupVcpu[] = "vcpu"; 22 | constexpr char kGroupFirm[] = "firmware"; 23 | constexpr char kGroupDisk[] = "disk"; 24 | constexpr char kGroupVgpu[] = "graphics"; 25 | constexpr char kGroupDisplay[] = "display"; 26 | constexpr char kGroupNet[] = "net"; 27 | constexpr char kGroupVtpm[] = "vtpm"; 28 | constexpr char kGroupRpmb[] = "rpmb"; 29 | constexpr char kGroupAaf[] = "aaf"; 30 | constexpr char kGroupPciPt[] = "passthrough"; 31 | constexpr char kGroupbluetooth[] = "bluetooth"; 32 | constexpr char kGroupAudio[] = "audio"; 33 | constexpr char kGroupMed[] = "mediation"; 34 | constexpr char kGroupService[] = "guest_control"; 35 | constexpr char kGroupExtra[] = "extra"; 36 | 37 | /* Keys */ 38 | constexpr char kGlobName[] = "name"; 39 | constexpr char kGlobFlashfiles[] = "flashfiles"; 40 | constexpr char kGlobCid[] = "vsock_cid"; 41 | constexpr char kGlobWaitReady[] = "wait_ready"; 42 | 43 | constexpr char kEmulType[] = "type"; 44 | constexpr char kEmulPath[] = "path"; 45 | 46 | constexpr char kMemSize[] = "size"; 47 | 48 | constexpr char kVcpuNum[] = "num"; 49 | 50 | constexpr char kFirmType[] = "type"; 51 | constexpr char kFirmPath[] = "path"; 52 | constexpr char kFirmCode[] = "code"; 53 | constexpr char kFirmVars[] = "vars"; 54 | 55 | constexpr char kDiskSize[] = "size"; 56 | constexpr char kDiskPath[] = "path"; 57 | 58 | constexpr char kVgpuType[] = "type"; 59 | constexpr char kVgpuGvtgVer[] = "gvtg_version"; 60 | constexpr char kVgpuUuid[] = "vgpu_uuid"; 61 | constexpr char kVgpuMonId[] = "monitor"; 62 | constexpr char kVgpuOutputs[] = "outputs"; 63 | 64 | constexpr char kDispOptions[] = "options"; 65 | 66 | constexpr char kNetModel[] = "model"; 67 | constexpr char kNetAdbPort[] = "adb_port"; 68 | constexpr char kNetFastbootPort[] = "fastboot_port"; 69 | 70 | constexpr char kVtpmBinPath[] = "bin_path"; 71 | constexpr char kVtpmDataDir[] = "data_dir"; 72 | 73 | constexpr char kRpmbBinPath[] = "bin_path"; 74 | constexpr char kRpmbDataDir[] = "data_dir"; 75 | 76 | constexpr char kAafPath[] = "path"; 77 | constexpr char kAafSuspend[] = "support_suspend"; 78 | constexpr char kAafAudioType[] = "audio_type"; 79 | 80 | constexpr char kPciPtDev[] = "passthrough_pci"; 81 | 82 | constexpr char kHciDown[] = "hci_down"; 83 | 84 | constexpr char kDisableEmul[] = "disable_emulation"; 85 | 86 | constexpr char kMedBattery[] = "battery_med"; 87 | constexpr char kMedThermal[] = "thermal_med"; 88 | constexpr char kMedCamera[] = "camera_med"; 89 | 90 | constexpr char kServTimeKeep[] = "time_keep"; 91 | constexpr char kServPmCtrl[] = "pm_control"; 92 | constexpr char kServVinput[] = "vinput"; 93 | 94 | constexpr char kExtraCmd[] = "cmd"; 95 | constexpr char kExtraService[] = "service"; 96 | constexpr char kExtraPwrCtrlMultiOS[] = "pwr_ctrl_multios"; 97 | 98 | /* Options for Key to select */ 99 | constexpr char kEmulTypeQemu[] = "QEMU"; 100 | 101 | constexpr char kFirmUnified[] = "unified"; 102 | constexpr char kFirmSplited[] = "splited"; 103 | 104 | constexpr char kVgpuNone[] = "headless"; 105 | constexpr char kVgpuVirtio[] = "virtio"; 106 | constexpr char kVgpuVirtio2D[] = "virtio2d"; 107 | constexpr char kVgpuRamfb[] = "ramfb"; 108 | constexpr char kVgpuGvtG[] = "GVT-g"; 109 | constexpr char kVgpuGvtD[] = "GVT-d"; 110 | constexpr char kVgpuSriov[] = "SRIOV"; 111 | 112 | constexpr char kGvtgV51[] = "i915-GVTg_V5_1"; 113 | constexpr char kGvtgV52[] = "i915-GVTg_V5_2"; 114 | constexpr char kGvtgV54[] = "i915-GVTg_V5_4"; 115 | constexpr char kGvtgV58[] = "i915-GVTg_V5_8"; 116 | 117 | constexpr char kSuspendEnable[] = "enable"; 118 | constexpr char kSuspendDisable[] = "disable"; 119 | 120 | 121 | class CivConfig final { 122 | public: 123 | std::string GetValue(const std::string group, const std::string key); 124 | bool SetValue(const std::string group, const std::string key, const std::string value); 125 | bool ReadConfigFile(const std::string path); 126 | bool WriteConfigFile(std::string path); 127 | private: 128 | bool SanitizeOpts(void); 129 | boost::property_tree::ptree cfg_data_; 130 | }; 131 | 132 | } // namespace vm_manager 133 | 134 | #endif // SRC_GUEST_CONFIG_PARSER_H_ 135 | -------------------------------------------------------------------------------- /src/guest/tui.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | 10 | #include 11 | 12 | #include "guest/tui.h" 13 | #include "utils/utils.h" 14 | 15 | #define LAYOUT_MIN_WIDTH 60 16 | #define LAYOUT_MAX_WIDTH 120 17 | 18 | namespace vm_manager { 19 | 20 | void CivTui::InitName(const std::string& name) { 21 | name_ = ftxui::Renderer([name] { 22 | return ftxui::hbox(ftxui::hbox(ftxui::text("name") | ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 18), 23 | ftxui::text(": "), ftxui::text(name))); 24 | }); 25 | } 26 | 27 | void CivTui::InitCompDisk(void) { 28 | cdisk_ = ftxui::Renderer(ftxui::Container::Vertical({disk_size_.Get(), disk_path_.Get()}), [&] { 29 | return window( 30 | ftxui::text(std::string("disk") + ":"), 31 | vbox(disk_size_.Get()->Render(), disk_path_.Get()->Render())); 32 | }); 33 | } 34 | 35 | void CivTui::InitCompFirm(void) { 36 | firmware_type_ = { vm_manager::kFirmUnified, vm_manager::kFirmSplited }; 37 | 38 | cfirm_inner_ = ftxui::Container::Vertical({ 39 | ftxui::Toggle(&firmware_type_, &firmware_type_selected_), 40 | ftxui::Container::Tab({ 41 | firm_unified_.Get(), 42 | ftxui::Container::Vertical({ firm_splited_code_.Get(), firm_splited_data_.Get() }), 43 | }, 44 | &firmware_type_selected_ 45 | ) 46 | }); 47 | cfirm_ = ftxui::Renderer(cfirm_inner_, [&] { 48 | return ftxui::window(ftxui::text("firmware: "), cfirm_inner_->Render()); 49 | }); 50 | } 51 | 52 | void CivTui::InitCompVgpu(void) { 53 | vgpu_type_ = { 54 | vm_manager::kVgpuNone, 55 | vm_manager::kVgpuVirtio, 56 | vm_manager::kVgpuRamfb, 57 | vm_manager::kVgpuGvtG, 58 | vm_manager::kVgpuGvtD 59 | }; 60 | cvgpu_type_ = ftxui::Dropdown(&vgpu_type_, &vgpu_selected_); 61 | 62 | gvtg_ver_ = { vm_manager::kGvtgV51, vm_manager::kGvtgV52, vm_manager::kGvtgV54, vm_manager::kGvtgV58 }; 63 | 64 | cgvtg_sub_ = ftxui::Container::Vertical({ftxui::Dropdown(&gvtg_ver_, &gvtg_ver_selected_), gvtg_uuid_.Get()}); 65 | 66 | cvgpu_side_ = ftxui::Container::Tab({ 67 | ftxui::Renderer(ftxui::emptyElement), // NONE 68 | ftxui::Renderer(ftxui::emptyElement), // VIRTIO 69 | ftxui::Renderer(ftxui::emptyElement), // RAMFB 70 | cgvtg_sub_, // GVT-g 71 | ftxui::Renderer(ftxui::emptyElement), // GVT-d 72 | }, &vgpu_selected_); 73 | 74 | cvgpu_ = ftxui::Renderer(ftxui::Container::Horizontal({ cvgpu_type_, cvgpu_side_ }), [&]{ 75 | return window( 76 | ftxui::text("graphics: "), 77 | hbox(cvgpu_type_->Render(), cvgpu_side_->Render())); 78 | }); 79 | } 80 | 81 | void CivTui::InitCompVtpm(void) { 82 | cswtpm_ = ftxui::Renderer(ftxui::Container::Vertical({swtpm_bin_.Get(), swtpm_data_.Get()}), [&] { 83 | return window( 84 | ftxui::text(std::string("swtpm") + ":"), 85 | ftxui::vbox(swtpm_bin_.Get()->Render(), swtpm_data_.Get()->Render())); 86 | }); 87 | } 88 | 89 | void CivTui::InitCompRpmb(void) { 90 | crpmb_ = ftxui::Renderer(ftxui::Container::Vertical({rpmb_bin_.Get(), rpmb_data_.Get()}), [&] { 91 | return window( 92 | ftxui::text(std::string("rpmb") + ":"), 93 | ftxui::vbox(rpmb_bin_.Get()->Render(), rpmb_data_.Get()->Render())); 94 | }); 95 | } 96 | 97 | void CivTui::InitCompPciPt(void) { 98 | boost::process::ipstream pipe_stream; 99 | std::error_code ec; 100 | boost::process::child c("lspci -D", boost::process::std_out > pipe_stream); 101 | std::string line; 102 | while (pipe_stream && std::getline(pipe_stream, line)) { 103 | if (!line.empty()) { 104 | CheckBoxState item = { line, false }; 105 | pci_dev_.push_back(item); 106 | } 107 | line.erase(); 108 | } 109 | 110 | c.wait(ec); 111 | 112 | PtPciClickButton = [&]() { 113 | pt_pci_disp_.clear(); 114 | for (std::vector::iterator it = pci_dev_.begin(); it != pci_dev_.end(); ++it) { 115 | if (it->checked) { 116 | pt_pci_disp_.push_back(it->name); 117 | } 118 | } 119 | pt_pci_tab_id_ = !pt_pci_tab_id_; 120 | if (pt_pci_tab_id_ == 1) { 121 | pt_btn_lable_ = "Done"; 122 | cpt_inner_right_->TakeFocus(); 123 | } else { 124 | pt_btn_lable_ = "Edit"; 125 | } 126 | }; 127 | 128 | pt_btn_lable_ = "Edit"; 129 | cpt_inner_left_btn_ = ftxui::Button(&pt_btn_lable_, PtPciClickButton); 130 | cpt_inner_right_edit_ = ftxui::Container::Vertical({}); 131 | for (std::vector::iterator t = pci_dev_.begin(); t != pci_dev_.end(); ++t) { 132 | cpt_inner_right_edit_->Add(ftxui::Checkbox(t->name, &t->checked)); 133 | } 134 | cpt_inner_right_disp_ = ftxui::Menu(&pt_pci_disp_, &pt_pci_menu_selected_); 135 | 136 | cpt_inner_right_ = ftxui::Container::Tab({cpt_inner_right_disp_, cpt_inner_right_edit_}, &pt_pci_tab_id_); 137 | cpt_inner_ = ftxui::Renderer(ftxui::Container::Horizontal({cpt_inner_left_btn_, cpt_inner_right_}), [&]{ 138 | return ftxui::hbox( 139 | ftxui::vbox( 140 | cpt_inner_left_btn_->Render() | ftxui::center, 141 | ftxui::text(std::to_string(pt_pci_disp_.size()) + " Slected") | ftxui::center), 142 | cpt_inner_right_->Render() | ftxui::frame | ftxui::vscroll_indicator); 143 | }); 144 | cpt_ = ftxui::Renderer(cpt_inner_, [&]{ 145 | return ftxui::window(ftxui::text("PCI Devices to Passthrough"), cpt_inner_->Render()) 146 | | ftxui::size(ftxui::HEIGHT, ftxui::LESS_THAN, 6); 147 | }); 148 | } 149 | 150 | void CivTui::InitializeForm(void) { 151 | InitName(filename_); 152 | InitCompDisk(); 153 | InitCompFirm(); 154 | InitCompVgpu(); 155 | InitCompVtpm(); 156 | InitCompRpmb(); 157 | InitCompPciPt(); 158 | 159 | form_ = ftxui::Container::Vertical({ 160 | name_, 161 | flashfiles_.Get(), 162 | emulator_.Get(), 163 | memory_.Get(), 164 | vcpu_.Get(), 165 | cdisk_, 166 | cfirm_, 167 | cvgpu_, 168 | cswtpm_, 169 | crpmb_, 170 | cpt_, 171 | adb_port_.Get(), 172 | fastboot_port_.Get(), 173 | batt_med_.Get(), 174 | therm_med_.Get(), 175 | aaf_.Get(), 176 | tkeep_.Get(), 177 | pmctl_.Get(), 178 | extra_.Get(), 179 | }); 180 | 181 | form_render_ = ftxui::Renderer(form_, [&]{ 182 | ftxui::Element eform_ = ftxui::vbox(form_->Render()) | ftxui::frame | ftxui::vscroll_indicator; 183 | return eform_; 184 | }); 185 | } 186 | 187 | void CivTui::InitializeButtons(void) { 188 | SaveOn = [&]() { 189 | std::string configPath = GetConfigPath(); 190 | std::string filePath = configPath + "/" + filename_ +".ini"; 191 | boost::system::error_code ec; 192 | if (!boost::filesystem::exists(filePath, ec)) { 193 | boost::filesystem::ofstream file(filePath); 194 | SetConfToPtree(); 195 | bool writeConfigFile = civ_config_.WriteConfigFile(filePath); 196 | if (writeConfigFile) { 197 | screen_.ExitLoopClosure()(); 198 | LOG(info) << "Config saved!" << std::endl; 199 | } else { 200 | LOG(warning) << "Write config file failue!" << std::endl; 201 | } 202 | file.close(); 203 | } else { 204 | status_bar_ = "File alreay exist!"; 205 | } 206 | screen_.Clear(); 207 | }; 208 | 209 | save_ = ftxui::Button("SAVE", [&]{SaveOn();}); 210 | exit_ = ftxui::Button("EXIT", screen_.ExitLoopClosure()); 211 | 212 | buttons_ = ftxui::Container::Horizontal({ 213 | save_, exit_ 214 | }); 215 | } 216 | 217 | void CivTui::InitializeUi(std::string name) { 218 | filename_ = name; 219 | InitializeForm(); 220 | InitializeButtons(); 221 | 222 | layout_ = ftxui::Container::Vertical({ 223 | form_render_, 224 | buttons_, 225 | }); 226 | 227 | auto layout_render = ftxui::Renderer(layout_, [&] { 228 | return ftxui::vbox({ 229 | ftxui::text("Celadon in VM Configuration"), 230 | ftxui::vbox({form_render_->Render() | ftxui::vscroll_indicator }) | ftxui::border, 231 | ftxui::text(status_bar_), 232 | buttons_->Render() | ftxui::center, 233 | }) | ftxui::border | ftxui::size(ftxui::WIDTH, ftxui::GREATER_THAN, LAYOUT_MIN_WIDTH) 234 | | ftxui::size(ftxui::HEIGHT, ftxui::LESS_THAN, 80) | ftxui::center; 235 | }); 236 | 237 | screen_.Loop(layout_render); 238 | } 239 | 240 | void CivTui::SetConfToPtree() { 241 | civ_config_.SetValue(kGroupGlob, kGlobName, filename_); 242 | civ_config_.SetValue(kGroupGlob, kGlobFlashfiles, flashfiles_.GetContent()); 243 | 244 | civ_config_.SetValue(kGroupEmul, kEmulPath, emulator_.GetContent()); 245 | 246 | civ_config_.SetValue(kGroupMem, kMemSize, memory_.GetContent()); 247 | 248 | civ_config_.SetValue(kGroupVcpu, kVcpuNum, vcpu_.GetContent()); 249 | 250 | civ_config_.SetValue(kGroupDisk, kDiskSize, disk_size_.GetContent()); 251 | civ_config_.SetValue(kGroupDisk, kDiskPath, disk_path_.GetContent()); 252 | 253 | civ_config_.SetValue(kGroupFirm, kFirmType, firmware_type_.at(firmware_type_selected_)); 254 | if (firmware_type_selected_ == 0) { 255 | civ_config_.SetValue(kGroupFirm, kFirmPath, firm_unified_.GetContent()); 256 | } else if (firmware_type_selected_ == 1) { 257 | civ_config_.SetValue(kGroupFirm, kFirmCode, firm_splited_code_.GetContent()); 258 | civ_config_.SetValue(kGroupFirm, kFirmVars, firm_splited_data_.GetContent()); 259 | } 260 | 261 | civ_config_.SetValue(kGroupVgpu, kVgpuType, vgpu_type_.at(vgpu_selected_)); 262 | if (vgpu_selected_ == 3) { 263 | civ_config_.SetValue(kGroupVgpu, kVgpuGvtgVer, gvtg_ver_.at(gvtg_ver_selected_)); 264 | if (!gvtg_uuid_.GetContent().empty()) { 265 | civ_config_.SetValue(kGroupVgpu, kVgpuUuid, gvtg_uuid_.GetContent()); 266 | } 267 | } 268 | 269 | civ_config_.SetValue(kGroupVtpm, kVtpmBinPath, swtpm_bin_.GetContent()); 270 | civ_config_.SetValue(kGroupVtpm, kVtpmDataDir, swtpm_data_.GetContent()); 271 | 272 | civ_config_.SetValue(kGroupRpmb, kVtpmBinPath, rpmb_bin_.GetContent()); 273 | civ_config_.SetValue(kGroupRpmb, kVtpmDataDir, rpmb_data_.GetContent()); 274 | 275 | std::string passthrough; 276 | for (std::vector::iterator iter = pt_pci_disp_.begin(); iter != pt_pci_disp_.end(); iter++) { 277 | std::string selectItem = *iter; 278 | if (selectItem.length() == 0) 279 | continue; 280 | size_t pos = selectItem.find(" "); 281 | if (pos == std::string::npos) 282 | continue; 283 | passthrough += selectItem.substr(0, pos); 284 | if (iter != (pt_pci_disp_.end() - 1)) { 285 | passthrough += ','; 286 | } 287 | } 288 | 289 | civ_config_.SetValue(kGroupPciPt, kPciPtDev, passthrough); 290 | 291 | civ_config_.SetValue(kGroupNet, kNetAdbPort, adb_port_.GetContent()); 292 | 293 | civ_config_.SetValue(kGroupNet, kNetFastbootPort, fastboot_port_.GetContent()); 294 | 295 | civ_config_.SetValue(kGroupMed, kMedBattery, batt_med_.GetContent()); 296 | 297 | civ_config_.SetValue(kGroupMed, kMedThermal, therm_med_.GetContent()); 298 | 299 | civ_config_.SetValue(kGroupAaf, kAafPath, aaf_.GetContent()); 300 | 301 | civ_config_.SetValue(kGroupService, kServTimeKeep, tkeep_.GetContent()); 302 | 303 | civ_config_.SetValue(kGroupService, kServPmCtrl, pmctl_.GetContent()); 304 | 305 | civ_config_.SetValue(kGroupExtra, kExtraCmd, extra_.GetContent()); 306 | } 307 | 308 | } // namespace vm_manager 309 | 310 | -------------------------------------------------------------------------------- /src/guest/tui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_GUEST_TUI_H_ 9 | #define SRC_GUEST_TUI_H_ 10 | 11 | int create_tui(void); 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include "ftxui/component/captured_mouse.hpp" 21 | #include "ftxui/component/component.hpp" 22 | #include "ftxui/component/component_base.hpp" 23 | #include "ftxui/component/component_options.hpp" 24 | #include "ftxui/component/screen_interactive.hpp" 25 | #include "ftxui/dom/elements.hpp" 26 | #include "ftxui/util/ref.hpp" 27 | #include "ftxui/screen/terminal.hpp" 28 | 29 | #include "config_parser.h" 30 | #include "utils/log.h" 31 | 32 | typedef struct { 33 | std::string name; 34 | bool checked; 35 | } CheckBoxState; 36 | 37 | class InputField { 38 | public: 39 | InputField(const char *name, const char *hint) 40 | :name_(name) { 41 | input_ = ftxui::Input(&content_, std::string(hint)); 42 | wrap_ = ftxui::Renderer(input_, RenderInput); 43 | } 44 | 45 | ftxui::Component Get() { 46 | return wrap_; 47 | } 48 | 49 | std::string GetContent() { 50 | return content_; 51 | } 52 | 53 | private: 54 | const char *name_; 55 | ftxui::Component wrap_; 56 | ftxui::Component input_; 57 | std::string content_; 58 | std::function RenderInput = [this]() { 59 | return ftxui::hbox(ftxui::hbox(ftxui::text(std::string(name_)) | ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 18), 60 | ftxui::text(": ")), 61 | input_->Render()); 62 | }; 63 | }; 64 | 65 | namespace vm_manager { 66 | 67 | class CivTui final { 68 | public: 69 | void InitializeUi(std::string filename); 70 | 71 | private: 72 | std::string filename_; 73 | ftxui::ScreenInteractive screen_ = ftxui::ScreenInteractive::Fullscreen(); 74 | ftxui::Component layout_; 75 | ftxui::Component form_, form_render_, form_main_; 76 | ftxui::Component buttons_; 77 | 78 | /* Field: name */ 79 | ftxui::Component name_; 80 | void InitName(const std::string& name); 81 | 82 | /* Field: flashfiles */ 83 | InputField flashfiles_ = InputField("flashfiles", "flashfiles path"); 84 | 85 | /* Field: emulator */ 86 | InputField emulator_ = InputField("emulator", "emulator path"); 87 | 88 | /* Field: emulator */ 89 | InputField memory_ = InputField("memory", "memory size"); 90 | 91 | /* Field: vcpu */ 92 | InputField vcpu_ = InputField("vcpu", "vcpu number(1~1024)"); 93 | 94 | /* Field: disk size/path */ 95 | ftxui::Component cdisk_; 96 | InputField disk_size_ = InputField("disk size", "virtual disk size"); 97 | InputField disk_path_ = InputField("disk path", "virtual disk path"); 98 | void InitCompDisk(void); 99 | 100 | /* Field: firmware */ 101 | ftxui::Component cfirm_; 102 | ftxui::Component cfirm_inner_; 103 | int firmware_type_selected_ = 0; 104 | std::vector firmware_type_; 105 | InputField firm_unified_ = InputField("bin path", "Unified firmware binary path"); 106 | InputField firm_splited_code_ = InputField("code path", "Splited firmware binary code path"); 107 | InputField firm_splited_data_ = InputField("data path", "Splited firmware binary data path"); 108 | void InitCompFirm(void); 109 | 110 | /* Field: vgpu */ 111 | std::vector vgpu_type_; 112 | int vgpu_selected_ = 1; 113 | ftxui::Component cvgpu_; 114 | ftxui::Component cvgpu_type_; 115 | ftxui::Component cvgpu_side_; 116 | void InitCompVgpu(void); 117 | 118 | int gvtg_ver_selected_ = 1; 119 | std::vector gvtg_ver_; 120 | InputField gvtg_uuid_ = InputField("uuid", "UUID for a GVTg-VGPU"); 121 | ftxui::Component cgvtg_sub_; 122 | 123 | /* Field: SWTPM */ 124 | ftxui::Component cswtpm_; 125 | InputField swtpm_bin_ = InputField("bin path", "binary path"); 126 | InputField swtpm_data_ = InputField("data dir", "data folder"); 127 | void InitCompVtpm(void); 128 | 129 | /* Field: RPMB */ 130 | ftxui::Component crpmb_; 131 | InputField rpmb_bin_ = InputField("bin path", "binary path"); 132 | InputField rpmb_data_ = InputField("data dir", "data folder"); 133 | void InitCompRpmb(void); 134 | 135 | /* Field: PCI Passthrough devices */ 136 | std::vector pci_dev_; 137 | std::vector pt_pci_disp_; 138 | std::string pt_btn_lable_; 139 | int pt_pci_tab_id_ = 0; 140 | int pt_pci_menu_selected_ = 0; 141 | ftxui::Component cpt_; 142 | ftxui::Component cpt_inner_, cpt_inner_left_btn_, cpt_inner_right_, cpt_inner_right_edit_, cpt_inner_right_disp_; 143 | void InitCompPciPt(void); 144 | std::function PtPciClickButton; 145 | 146 | /* Fields: adb/fastboot port */ 147 | InputField adb_port_ = InputField("adb port", "ADB port(TCP-Localhost), e.g.:5555"); 148 | InputField fastboot_port_ = InputField("fastboot port", "Fastboot port(TCP-Localhost), e.g.:5554"); 149 | 150 | /* Fields: Battery/Thermal mediation/aaf/Time Keep/PM control/Extra */ 151 | InputField batt_med_ = InputField("Battery Mediation", "Battery Mediation host side executable binary"); 152 | InputField therm_med_ = InputField("Thermal Mediation", "Thermal Mediation host side executable binary"); 153 | InputField aaf_ = InputField("AAF", "AAF configuration folder path"); 154 | InputField tkeep_ = InputField("Guest Time Keep", "Time Keep host side executable binary"); 155 | InputField pmctl_ = InputField("Guest PM Control", "PM Control host side executable binary"); 156 | InputField extra_ = InputField("Extra", "Extra command that emulator can acccept"); 157 | 158 | /* Status Bar */ 159 | std::string status_bar_; 160 | 161 | /* Buttons */ 162 | vm_manager::CivConfig civ_config_; 163 | ftxui::Component save_; 164 | ftxui::Component exit_; 165 | std::function SaveOn; 166 | 167 | void InitializeForm(void); 168 | void InitializeButtons(void); 169 | void SetConfToPtree(); 170 | }; 171 | 172 | } // namespace vm_manager 173 | 174 | #endif // SRC_GUEST_TUI_H_ 175 | 176 | -------------------------------------------------------------------------------- /src/guest/vm_builder.cc: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2022 Intel Corporation. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 7 | * 8 | */ 9 | #include 10 | 11 | #include 12 | 13 | #include "guest/vm_builder.h" 14 | #include "guest/config_parser.h" 15 | #include "services/message.h" 16 | #include "utils/log.h" 17 | #include "utils/utils.h" 18 | 19 | namespace vm_manager { 20 | std::string VmBuilder::GetName(void) { 21 | return name_; 22 | } 23 | 24 | uint32_t VmBuilder::GetCid(void) { 25 | return vsock_cid_; 26 | } 27 | 28 | VmBuilder::VmState VmBuilder::GetState(void) { 29 | return state_; 30 | } 31 | } // namespace vm_manager 32 | -------------------------------------------------------------------------------- /src/guest/vm_builder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #ifndef SRC_GUEST_VM_BUILDER_H_ 10 | #define SRC_GUEST_VM_BUILDER_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include "guest/config_parser.h" 22 | #include "guest/vm_process.h" 23 | #include "utils/log.h" 24 | 25 | namespace vm_manager { 26 | 27 | class VmBuilder { 28 | public: 29 | enum VmState { 30 | kVmEmpty = 0, 31 | kVmCreated, 32 | kVmBooting, 33 | kVmRunning, 34 | kVmPaused, 35 | kVmUnknown, 36 | }; 37 | 38 | public: 39 | explicit VmBuilder(std::string name) : name_(name), vsock_cid_(0) {} 40 | virtual ~VmBuilder() = default; 41 | virtual bool BuildVmArgs(void) = 0; 42 | virtual void StartVm(void) = 0; 43 | virtual void WaitVmExit(void) = 0; 44 | virtual void StopVm(void) = 0; 45 | virtual void PauseVm(void) = 0; 46 | virtual bool WaitVmReady(void) = 0; 47 | virtual void SetVmReady(void) = 0; 48 | virtual void SetProcessEnv(std::vector env) = 0; 49 | std::string GetName(void); 50 | uint32_t GetCid(void); 51 | VmState GetState(void); 52 | 53 | protected: 54 | std::string name_; 55 | uint32_t vsock_cid_; 56 | VmState state_ = VmBuilder::VmState::kVmEmpty; 57 | std::mutex state_lock_; 58 | }; 59 | 60 | static inline constexpr const char *VmStateToStr(VmBuilder::VmState s) { 61 | switch (s) { 62 | case VmBuilder::kVmEmpty: return "Empty"; 63 | case VmBuilder::kVmCreated: return "Created"; 64 | case VmBuilder::kVmBooting: return "Booting"; 65 | case VmBuilder::kVmRunning: return "Running"; 66 | case VmBuilder::kVmPaused: return "Paused"; 67 | case VmBuilder::kVmUnknown: return "Unknown"; 68 | } 69 | return "NaN"; 70 | } 71 | 72 | 73 | } // namespace vm_manager 74 | 75 | #endif // SRC_GUEST_VM_BUILDER_H_ 76 | -------------------------------------------------------------------------------- /src/guest/vm_builder_qemu.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2022 Intel Corporation. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 7 | * 8 | */ 9 | 10 | #ifndef SRC_GUEST_VM_BUILDER_QEMU_H_ 11 | #define SRC_GUEST_VM_BUILDER_QEMU_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "guest/config_parser.h" 24 | #include "guest/vm_builder.h" 25 | #include "guest/aaf.h" 26 | 27 | namespace vm_manager { 28 | 29 | class VmBuilderQemu : public VmBuilder { 30 | public: 31 | explicit VmBuilderQemu(std::string name, CivConfig cfg) : 32 | VmBuilder(name), cfg_(cfg), vm_ready_latch_(1) {} 33 | ~VmBuilderQemu(); 34 | bool BuildVmArgs(void); 35 | void StartVm(void); 36 | void StopVm(void); 37 | void WaitVmExit(void); 38 | void PauseVm(void); 39 | bool WaitVmReady(void); 40 | void SetVmReady(void); 41 | void SetProcessEnv(std::vector env); 42 | 43 | private: 44 | bool BuildEmulPath(void); 45 | void BuildFixedCmd(void); 46 | bool BuildNameQmp(void); 47 | void BuildNetCmd(void); 48 | bool BuildVsockCmd(void); 49 | void BuildRpmbCmd(void); 50 | void BuildVtpmCmd(void); 51 | void InitAafCfg(void); 52 | bool BuildAafCfg(void); 53 | bool BuildVgpuCmd(void); 54 | void BuildVinputCmd(void); 55 | void BuildDispCmd(void); 56 | void BuildMemCmd(void); 57 | void BuildVcpuCmd(void); 58 | bool BuildFirmwareCmd(void); 59 | void BuildVdiskCmd(void); 60 | void BringDownBtHciIntf(void); 61 | void BuildPtPciDevicesCmd(void); 62 | void BuildGuestTimeKeepCmd(void); 63 | void BuildGuestPmCtrlCmd(void); 64 | void BuildExtraGuestPmCtrlCmd(void); 65 | void BuildAudioCmd(void); 66 | void BuildExtraCmd(void); 67 | 68 | void SoundCardHook(void); 69 | bool PassthroughGpu(void); 70 | bool CreateGvtgVgpu(void); 71 | 72 | void SetPciDevicesCallback(void); 73 | bool SetupSriov(void); 74 | void RunMediationSrv(void); 75 | void SetExtraServices(void); 76 | void SetProcLogDir(void); 77 | 78 | CivConfig cfg_; 79 | std::unique_ptr aaf_cfg_; 80 | 81 | std::unique_ptr main_proc_; 82 | std::vector> co_procs_; 83 | std::string emul_cmd_; 84 | // std::vector env_data_; 85 | std::set pci_pt_dev_set_; 86 | boost::latch vm_ready_latch_; 87 | std::queue> end_call_; 88 | std::mutex stopvm_mutex_; 89 | }; 90 | 91 | } // namespace vm_manager 92 | 93 | #endif // SRC_GUEST_VM_BUILDER_QEMU_H_ 94 | -------------------------------------------------------------------------------- /src/guest/vm_flash.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "guest/vm_flash.h" 17 | #include "guest/config_parser.h" 18 | #include "guest/vm_process.h" 19 | #include "utils/utils.h" 20 | #include "utils/log.h" 21 | 22 | namespace vm_manager { 23 | 24 | constexpr const char *kVirtualUsbDiskPath("/tmp/flash.vfat"); 25 | constexpr const size_t kDdBs = 63_MB; 26 | constexpr const size_t k4GB = 4_GB; 27 | 28 | bool VmFlasher::CheckImages(boost::filesystem::path o_dir) { 29 | boost::system::error_code bec; 30 | boost::filesystem::path e = boost::process::search_path("split"); 31 | if (e.empty()) 32 | return false; 33 | std::vector args; 34 | boost::filesystem::directory_iterator end_itr; 35 | for (boost::filesystem::directory_iterator ditr(o_dir); ditr != end_itr; ++ditr) { 36 | if (boost::filesystem::is_regular_file(ditr->path(), bec)) { 37 | total_image_size_ += boost::filesystem::file_size(ditr->path(), bec); 38 | if (boost::filesystem::file_size(ditr->path(), bec) >= k4GB) { 39 | args.clear(); 40 | args.push_back("--bytes=" + std::to_string(k4GB - 1)); 41 | args.push_back("--numeric-suffixes"); 42 | args.push_back(ditr->path().string()); 43 | args.push_back(ditr->path().string() + ".part"); 44 | LOG(info) << boost::algorithm::join(args, " "); 45 | if (boost::process::system(e, boost::process::args(args))) { 46 | LOG(error) << "Failed to split: " << ditr->path().string(); 47 | return false; 48 | } 49 | boost::filesystem::remove(ditr->path().string(), bec); 50 | } 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | bool VmFlasher::QemuCreateVirtUsbDisk(void) { 57 | boost::system::error_code bec; 58 | boost::filesystem::path file(cfg_.GetValue(kGroupGlob, kGlobFlashfiles)); 59 | if (!boost::filesystem::exists(file, bec)) { 60 | LOG(error) << "Flashfile not exists: " << file.c_str(); 61 | return false; 62 | } 63 | 64 | if (!boost::filesystem::is_regular_file(file, bec)) { 65 | LOG(error) << "Flashfile is not regular file: " << file.c_str(); 66 | return false; 67 | } 68 | 69 | if (file.extension().compare(".zip") != 0) { 70 | virtual_disk_ = file.string(); 71 | return true; 72 | } 73 | 74 | boost::filesystem::path o_dir("/tmp/" + file.stem().string()); 75 | std::error_code ec; 76 | std::string cmd; 77 | if (boost::filesystem::exists(o_dir, bec)) { 78 | cmd.assign("rm -rf " + o_dir.string()); 79 | LOG(info) << cmd; 80 | if (boost::process::system(cmd)) 81 | return false; 82 | } 83 | 84 | cmd.assign("unzip -o " + file.string() + " -d " + o_dir.string()); 85 | LOG(info) << cmd; 86 | if (boost::process::system(cmd)) { 87 | LOG(error) << "Failed to unzip: " << file.string(); 88 | return false; 89 | } 90 | 91 | boost::filesystem::path boot_file(o_dir.string() + "/boot.img"); 92 | if (boost::filesystem::exists(boot_file, bec)) { 93 | if (!CheckImages(o_dir)) 94 | return false; 95 | 96 | cmd.assign("dd if=/dev/zero of=" + std::string(kVirtualUsbDiskPath) + 97 | " bs=" + std::to_string(kDdBs) + 98 | " count=" + std::to_string((total_image_size_ + 1_GB + kDdBs - 1)/kDdBs)); 99 | LOG(info) << cmd; 100 | if (boost::process::system(cmd)) { 101 | LOG(error) << "Failed to : " << cmd; 102 | return false; 103 | } 104 | 105 | cmd.assign("mkfs.vfat " + std::string(kVirtualUsbDiskPath)); 106 | LOG(info) << cmd; 107 | if (boost::process::system(cmd)) { 108 | LOG(error) << "Failed to : " << cmd; 109 | return false; 110 | } 111 | 112 | boost::filesystem::directory_iterator end_itr; 113 | for (boost::filesystem::directory_iterator ditr(o_dir, bec); ditr != end_itr; ++ditr) { 114 | if (boost::filesystem::is_regular_file(ditr->path(), bec)) { 115 | cmd.assign("mcopy -o -n -i " + std::string(kVirtualUsbDiskPath) + " " + ditr->path().string() + " ::"); 116 | LOG(info) << cmd; 117 | if (boost::process::system(cmd)) { 118 | LOG(error) << "Failed to : " << cmd; 119 | return false; 120 | } 121 | } 122 | } 123 | cmd.assign("rm -r " + o_dir.string()); 124 | LOG(info) << cmd; 125 | if (boost::process::system(cmd)) { 126 | LOG(warning) << "Failed to : " << cmd; 127 | } 128 | 129 | virtual_disk_ = kVirtualUsbDiskPath; 130 | return true; 131 | } 132 | 133 | int count = 0; 134 | for (auto& p : boost::filesystem::directory_iterator(o_dir, bec)) { 135 | if (++count >1) 136 | return false; 137 | } 138 | if (count == 1) { 139 | virtual_disk_ = o_dir.string() + "/" + file.stem().string(); 140 | return true; 141 | } 142 | 143 | return false; 144 | } 145 | 146 | bool VmFlasher::QemuCreateVirtualDisk(void) { 147 | std::string path = cfg_.GetValue(kGroupDisk, kDiskPath); 148 | std::string size = cfg_.GetValue(kGroupDisk, kDiskSize); 149 | 150 | std::string cmd("qemu-img create -f qcow2 " + path + " " + size); 151 | LOG(info) << cmd; 152 | if (boost::process::system(cmd)) { 153 | LOG(error) << "Failed to : " << cmd; 154 | return false; 155 | } 156 | return true; 157 | } 158 | 159 | bool VmFlasher::FlashWithQemu(void) { 160 | std::string emul_path = cfg_.GetValue(kGroupEmul, kEmulPath); 161 | if (emul_path.empty()) 162 | return false; 163 | 164 | if (!QemuCreateVirtUsbDisk()) 165 | return false; 166 | 167 | if (!QemuCreateVirtualDisk()) { 168 | return false; 169 | } 170 | 171 | std::string qemu_args(emul_path); 172 | 173 | std::string rpmb_bin = cfg_.GetValue(kGroupRpmb, kRpmbBinPath); 174 | std::string rpmb_data_dir = cfg_.GetValue(kGroupRpmb, kRpmbDataDir); 175 | std::string rpmb_data_file = rpmb_data_dir + "/" + std::string(kRpmbData); 176 | time_t rawtime; 177 | struct tm timeinfo; 178 | char t_buf[80]; 179 | time(&rawtime); 180 | localtime_r(&rawtime, &timeinfo); 181 | strftime(t_buf, 80 , "%Y-%m-%d_%T", &timeinfo); 182 | std::string rpmb_sock = std::string(kRpmbSockPrefix) + t_buf; 183 | 184 | std::unique_ptr rpmb_proc; 185 | boost::system::error_code ec; 186 | if (!rpmb_bin.empty() && !rpmb_data_dir.empty()) { 187 | if (boost::filesystem::exists(rpmb_data_file, ec)) { 188 | if (!boost::filesystem::remove(rpmb_data_file, ec)) { 189 | LOG(error) << "Failed to remove " << rpmb_data_file; 190 | LOG(error) << "Please manully remove " << rpmb_data_file << ", and try again!"; 191 | return false; 192 | } 193 | } 194 | qemu_args.append(" -device virtio-serial,addr=1" 195 | " -device virtserialport,chardev=rpmb0,name=rpmb0,nr=1" 196 | " -chardev socket,id=rpmb0,path=" + rpmb_sock); 197 | rpmb_proc = std::make_unique(std::move(rpmb_bin), std::move(rpmb_data_dir), std::move(rpmb_sock)); 198 | } 199 | 200 | std::string vtpm_bin = cfg_.GetValue(kGroupVtpm, kVtpmBinPath); 201 | std::string vtpm_data_dir = cfg_.GetValue(kGroupVtpm, kVtpmDataDir); 202 | std::unique_ptr vtpm_proc; 203 | if (!vtpm_bin.empty() && !vtpm_data_dir.empty()) { 204 | qemu_args.append(" -chardev socket,id=chrtpm,path=" + 205 | vtpm_data_dir + "/" + kVtpmSock + 206 | " -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-crb,tpmdev=tpm0"); 207 | vtpm_proc = std::make_unique(std::move(vtpm_bin), std::move(vtpm_data_dir)); 208 | } 209 | 210 | std::string firm_type = cfg_.GetValue(kGroupFirm, kFirmType); 211 | if (firm_type.empty()) 212 | return false; 213 | if (firm_type.compare(kFirmUnified) == 0) { 214 | qemu_args.append(" -drive if=pflash,format=raw,file=" + cfg_.GetValue(kGroupFirm, kFirmPath)); 215 | } else if (firm_type.compare(kFirmSplited) == 0) { 216 | qemu_args.append(" -drive if=pflash,format=raw,readonly,file=" + cfg_.GetValue(kGroupFirm, kFirmCode)); 217 | qemu_args.append(" -drive if=pflash,format=raw,file=" + cfg_.GetValue(kGroupFirm, kFirmVars)); 218 | } else { 219 | LOG(error) << "Invalid virtual firmware"; 220 | return false; 221 | } 222 | 223 | qemu_args.append( 224 | " -device virtio-scsi-pci,id=scsi0,addr=0x8" 225 | " -drive if=none,format=qcow2,id=scsidisk1,file=" + cfg_.GetValue(kGroupDisk, kDiskPath) + 226 | " -device scsi-hd,drive=scsidisk1,bus=scsi0.0"); 227 | 228 | qemu_args.append(" -name civ_flashing" 229 | " -M q35" 230 | " -m 2048M" 231 | " -smp 1" 232 | " -enable-kvm" 233 | " -k en-us" 234 | " -device qemu-xhci,id=xhci,addr=0x5" 235 | " -no-reboot" 236 | " -nographic -display none -serial mon:stdio" 237 | " -boot menu=on,splash-time=5000,strict=on " 238 | " -drive id=udisk1,format=raw,if=none,file=" + virtual_disk_ + 239 | " -device usb-storage,drive=udisk1,bus=xhci.0" 240 | " -nodefaults"); 241 | 242 | if (rpmb_proc) 243 | rpmb_proc->Run(); 244 | if (vtpm_proc) 245 | vtpm_proc->Run(); 246 | 247 | LOG(info) << qemu_args; 248 | if (boost::process::system(qemu_args)) { 249 | LOG(warning) << "Failed to : " << qemu_args; 250 | } 251 | 252 | LOG(info) << "Flash done!"; 253 | 254 | return true; 255 | } 256 | 257 | bool VmFlasher::FlashGuest(std::string path) { 258 | boost::system::error_code ec; 259 | boost::filesystem::path p(path); 260 | 261 | if (!boost::filesystem::exists(p, ec) || !boost::filesystem::is_regular_file(p, ec)) { 262 | p.clear(); 263 | p.assign(GetConfigPath() + std::string("/") + path + ".ini"); 264 | if (!boost::filesystem::exists(p, ec)) { 265 | LOG(error) << "CiV config not exists: " << path; 266 | return false; 267 | } 268 | } 269 | 270 | if (!cfg_.ReadConfigFile(p.string())) { 271 | LOG(error) << "Failed to read config file"; 272 | return false; 273 | } 274 | 275 | std::string emul_type = cfg_.GetValue(kGroupEmul, kEmulType); 276 | if ((emul_type.compare(kEmulTypeQemu) == 0) || emul_type.empty()) { 277 | return FlashWithQemu(); 278 | } 279 | return false; 280 | } 281 | 282 | } // namespace vm_manager 283 | -------------------------------------------------------------------------------- /src/guest/vm_flash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #ifndef SRC_GUEST_VM_FLASH_H_ 10 | #define SRC_GUEST_VM_FLASH_H_ 11 | 12 | #include 13 | 14 | #include 15 | 16 | namespace vm_manager { 17 | 18 | class VmFlasher final { 19 | public: 20 | VmFlasher() = default; 21 | ~VmFlasher() = default; 22 | bool FlashGuest(std::string path); 23 | 24 | private: 25 | bool QemuCreateVirtUsbDisk(void); 26 | bool QemuCreateVirtualDisk(void); 27 | bool FlashWithQemu(void); 28 | bool CheckImages(boost::filesystem::path o_dir); 29 | 30 | private: 31 | size_t total_image_size_ = 0; 32 | std::string virtual_disk_; 33 | CivConfig cfg_; 34 | }; 35 | 36 | } // namespace vm_manager 37 | 38 | #endif // SRC_GUEST_VM_FLASH_H_ 39 | -------------------------------------------------------------------------------- /src/guest/vm_powerctl.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2022 Intel Corporation. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 7 | * 8 | */ 9 | 10 | #ifndef SRC_GUEST_VM_POWERCTL_H_ 11 | #define SRC_GUEST_VM_POWERCTL_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "vm_guest.grpc.pb.h" 18 | #include "include/constants/vm_manager.h" 19 | 20 | namespace vm_manager { 21 | 22 | class CivVmPowerCtl { 23 | public: 24 | explicit CivVmPowerCtl(std::shared_ptr channel) 25 | : stub_(vm_manager::CivPowerCtl::NewStub(channel)) {} 26 | 27 | bool Shutdown(void) { 28 | vm_manager::EmptyMessage request; 29 | vm_manager::EmptyMessage reply; 30 | 31 | grpc::ClientContext context; 32 | 33 | grpc::Status status = stub_->Shutdown(&context, request, &reply); 34 | 35 | if (status.ok()) { 36 | return true; 37 | } else { 38 | std::cout << status.error_code() << ": " << status.error_message() 39 | << std::endl; 40 | return false; 41 | } 42 | } 43 | 44 | private: 45 | std::unique_ptr stub_; 46 | }; 47 | 48 | 49 | } // namespace vm_manager 50 | 51 | #endif // SRC_GUEST_VM_POWERCTL_H_ 52 | -------------------------------------------------------------------------------- /src/guest/vm_process.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "utils/log.h" 18 | #include "guest/vm_process.h" 19 | 20 | namespace vm_manager { 21 | 22 | void VmProcSimple::ThreadMon(void) { 23 | std::error_code ec; 24 | 25 | time_t rawtime; 26 | struct tm timeinfo; 27 | char t_buf[80]; 28 | time(&rawtime); 29 | localtime_r(&rawtime, &timeinfo); 30 | strftime(t_buf, 80 , "%Y-%m-%d_%T", &timeinfo); 31 | 32 | boost::process::environment env; 33 | for (std::string s : env_data_) { 34 | if (s.length() == 0) 35 | continue; 36 | env.set(s.substr(0, s.find('=')), s.substr(s.find('=') + 1)); 37 | } 38 | 39 | LOG(info) << "CMD: " << cmd_; 40 | size_t exe_pos_begin = cmd_.find_first_not_of(' '); 41 | if (exe_pos_begin == std::string::npos) 42 | return; 43 | 44 | size_t exe_pos_end = cmd_.find_first_of(' ', exe_pos_begin); 45 | if (exe_pos_end == std::string::npos) 46 | exe_pos_end = cmd_.size(); 47 | std::string exe = cmd_.substr(exe_pos_begin, exe_pos_end - exe_pos_begin); 48 | 49 | std::string tid = boost::lexical_cast(mon_->get_id()); 50 | 51 | std::string f_out = log_dir_ + std::string(basename(exe.c_str())) + "_" + t_buf + "_" + tid + "_out.log"; 52 | std::ofstream fo(f_out); 53 | fo.close(); 54 | 55 | boost::filesystem::permissions(f_out, boost::filesystem::perms::owner_read | 56 | boost::filesystem::perms::owner_write | 57 | boost::filesystem::perms::group_read | 58 | boost::filesystem::perms::group_write | 59 | boost::filesystem::perms::others_read | 60 | boost::filesystem::perms::others_write | 61 | boost::filesystem::add_perms); 62 | 63 | c_ = std::make_unique( 64 | cmd_, 65 | boost::process::env = env, 66 | (boost::process::std_out & boost::process::std_err) > f_out, 67 | ec, 68 | boost::process::extend::on_success = [this](auto & exec) { 69 | child_latch_.count_down(); 70 | }, 71 | boost::process::extend::on_error = [this](auto & exec, const std::error_code& ec) { 72 | child_latch_.count_down(); 73 | }); 74 | 75 | c_->wait(ec); 76 | 77 | std::ofstream out(f_out, std::fstream::app); 78 | out << "\n\nCMD: " << cmd_; 79 | 80 | int result = c_->exit_code(); 81 | 82 | LOG(info) << "Thread-0x" << tid << " Exiting" 83 | << "\n\t\tChild-" << c_->id() << " exited, exit code=" << result 84 | << "\n\t\tlog: " << f_out; 85 | } 86 | 87 | void VmProcSimple::Run(void) { 88 | mon_ = std::make_unique([this] { ThreadMon(); }); 89 | child_latch_.wait(); 90 | } 91 | 92 | void VmProcSimple::Join(void) { 93 | if (!mon_) 94 | return; 95 | if (mon_->joinable()) 96 | mon_->join(); 97 | } 98 | 99 | void VmProcSimple::SetEnv(std::vector env) { 100 | env_data_ = env; 101 | } 102 | 103 | void VmProcSimple::SetLogDir(const char *path) { 104 | if (!path) 105 | return; 106 | 107 | boost::filesystem::path p(path); 108 | boost::system::error_code ec; 109 | if (boost::filesystem::exists(p, ec)) { 110 | if (!boost::filesystem::is_directory(p, ec)) 111 | return; 112 | } else { 113 | if (!boost::filesystem::create_directories(p, ec)) 114 | return; 115 | } 116 | 117 | log_dir_.assign(boost::filesystem::absolute(p, ec).c_str()).append("/"); 118 | } 119 | 120 | void VmProcSimple::Stop(void) { 121 | try { 122 | if (!mon_ || !c_) 123 | return; 124 | 125 | if (!mon_.get()) 126 | return; 127 | 128 | if (!c_->running()) 129 | return; 130 | 131 | LOG(info) << "Terminate CoProc: " << c_->id(); 132 | kill(c_->id(), SIGTERM); 133 | mon_->try_join_for(boost::chrono::seconds(10)); 134 | mon_.reset(nullptr); 135 | } catch (std::exception& e) { 136 | LOG(error) << "Exception: " << e.what(); 137 | } 138 | } 139 | 140 | bool VmProcSimple::Running(void) { 141 | if (c_) 142 | return c_->running(); 143 | return false; 144 | } 145 | 146 | VmProcSimple::~VmProcSimple() { 147 | VmProcSimple::Stop(); 148 | } 149 | 150 | 151 | void VmCoProcRpmb::Run(void) { 152 | LOG(info) << bin_ << " " << data_dir_; 153 | boost::system::error_code bec; 154 | if (!boost::filesystem::exists(data_dir_ + "/" + kRpmbData, bec)) { 155 | std::error_code ec; 156 | boost::process::child init_data(bin_ + " --dev " + data_dir_ + "/" + kRpmbData + " --init --size 2048"); 157 | init_data.wait(ec); 158 | int ret = init_data.exit_code(); 159 | } 160 | cmd_ = bin_ + " --dev " + data_dir_ + "/" + kRpmbData + " --sock " + sock_file_; 161 | 162 | VmProcSimple::Run(); 163 | } 164 | 165 | void VmCoProcRpmb::Stop(void) { 166 | VmProcSimple::Stop(); 167 | 168 | /* TODO: this temp rpmb sock file is created by rpmb_dev, but rpmb_dev did not cleanup 169 | when exit, so here check and remove the sock file after the rpmb_dev process 170 | exited. Need to remove this block of code once the rpmb_dev fixed the issue. 171 | */ 172 | boost::system::error_code bec; 173 | if (boost::filesystem::exists(sock_file_, bec)) { 174 | LOG(info) << "Cleanup Rpmb CoProc stuff ..."; 175 | boost::filesystem::remove(sock_file_); 176 | } 177 | } 178 | 179 | 180 | VmCoProcRpmb::~VmCoProcRpmb() { 181 | VmCoProcRpmb::Stop(); 182 | } 183 | 184 | void VmCoProcVtpm::Run(void) { 185 | LOG(info) << bin_ << " " << data_dir_; 186 | 187 | boost::system::error_code bec; 188 | if (!boost::filesystem::exists(data_dir_, bec)) { 189 | LOG(warning) << "Data path for Vtpm not exists!"; 190 | return; 191 | } 192 | 193 | cmd_ = bin_ + " socket --tpmstate dir=" + data_dir_ + 194 | " --tpm2 --ctrl type=unixio,path=" + data_dir_ + "/" + kVtpmSock; 195 | 196 | VmProcSimple::Run(); 197 | } 198 | 199 | void VmCoProcVtpm::Stop(void) { 200 | VmProcSimple::Stop(); 201 | } 202 | 203 | VmCoProcVtpm::~VmCoProcVtpm() { 204 | VmCoProcVtpm::Stop(); 205 | } 206 | 207 | } // namespace vm_manager 208 | -------------------------------------------------------------------------------- /src/guest/vm_process.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #ifndef SRC_GUEST_VM_PROCESS_H_ 10 | #define SRC_GUEST_VM_PROCESS_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "utils/log.h" 21 | 22 | namespace vm_manager { 23 | 24 | inline constexpr const char *kRpmbData = "RPMB_DATA"; 25 | inline constexpr const char *kRpmbSockPrefix = "/tmp/rpmb_sock_"; 26 | inline constexpr const char *kVtpmSock = "swtpm-sock"; 27 | 28 | class VmProcess { 29 | public: 30 | virtual void Run(void) = 0; 31 | virtual void Stop(void) = 0; 32 | virtual bool Running(void) = 0; 33 | virtual void Join(void) = 0; 34 | virtual void SetLogDir(const char *path) = 0; 35 | virtual void SetEnv(std::vector env) = 0; 36 | virtual ~VmProcess() = default; 37 | }; 38 | 39 | class VmProcSimple : public VmProcess { 40 | public: 41 | explicit VmProcSimple(std::string cmd) : cmd_(cmd), child_latch_(1) {} 42 | void Run(void); 43 | void Stop(void); 44 | bool Running(void); 45 | void Join(void); 46 | void SetEnv(std::vector env); 47 | void SetLogDir(const char *path); 48 | virtual ~VmProcSimple(); 49 | 50 | protected: 51 | VmProcSimple(const VmProcSimple&) = delete; 52 | VmProcSimple& operator=(const VmProcSimple&) = delete; 53 | 54 | void ThreadMon(void); 55 | 56 | 57 | std::string cmd_; 58 | std::vector env_data_; 59 | std::string log_dir_ = "/tmp/"; 60 | 61 | std::unique_ptr c_; 62 | boost::latch child_latch_; 63 | 64 | private: 65 | std::unique_ptr mon_; 66 | }; 67 | 68 | class VmCoProcRpmb : public VmProcSimple { 69 | public: 70 | VmCoProcRpmb(std::string bin, std::string data_dir, std::string sock_file) : 71 | VmProcSimple(""), bin_(bin), data_dir_(data_dir), sock_file_(sock_file) {} 72 | 73 | void Run(void); 74 | void Stop(void); 75 | ~VmCoProcRpmb(); 76 | 77 | private: 78 | VmCoProcRpmb(const VmCoProcRpmb&) = delete; 79 | VmCoProcRpmb& operator=(const VmCoProcRpmb&) = delete; 80 | 81 | std::string bin_; 82 | std::string data_dir_; 83 | std::string sock_file_; 84 | }; 85 | 86 | class VmCoProcVtpm : public VmProcSimple { 87 | public: 88 | VmCoProcVtpm(std::string bin, std::string data_dir) : 89 | VmProcSimple(""), bin_(bin), data_dir_(data_dir) {} 90 | 91 | void Run(void); 92 | void Stop(void); 93 | ~VmCoProcVtpm(); 94 | 95 | private: 96 | VmCoProcVtpm(const VmCoProcVtpm&) = delete; 97 | VmCoProcVtpm& operator=(const VmCoProcVtpm&) = delete; 98 | 99 | std::string bin_; 100 | std::string data_dir_; 101 | }; 102 | 103 | } // namespace vm_manager 104 | 105 | #endif // SRC_GUEST_VM_PROCESS_H_ 106 | -------------------------------------------------------------------------------- /src/guest/vsock_cid_pool.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | 11 | namespace vm_manager { 12 | 13 | uint32_t VsockCidPool::GetCid() { 14 | std::lock_guard lock(mutex_); 15 | size_t pos = bs_._Find_next(kCiVCidStart - 1); 16 | if (pos == bs_.size()) 17 | return 0; 18 | bs_.reset(pos); 19 | return pos; 20 | } 21 | 22 | uint32_t VsockCidPool::GetCid(uint32_t cid) { 23 | std::lock_guard lock(mutex_); 24 | if ((cid < 3) || (cid >= kCivMaxCidNum)) 25 | return 0; 26 | if (!bs_[cid]) 27 | return 0; 28 | 29 | bs_.reset(cid); 30 | return cid; 31 | } 32 | 33 | bool VsockCidPool::ReleaseCid(uint32_t cid) { 34 | if ((cid < 3) || (cid >= kCivMaxCidNum)) 35 | return false; 36 | std::lock_guard lock(mutex_); 37 | bs_.set(cid); 38 | return true; 39 | } 40 | 41 | VsockCidPool &VsockCidPool::Pool(void) { 42 | static VsockCidPool vcp_; 43 | return vcp_; 44 | } 45 | 46 | } // namespace vm_manager 47 | -------------------------------------------------------------------------------- /src/guest/vsock_cid_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #ifndef SRC_GUEST_VSOCK_CID_POOL_H_ 10 | #define SRC_GUEST_VSOCK_CID_POOL_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace vm_manager { 17 | 18 | inline constexpr const uint32_t kCivMaxCidNum = 10000U; 19 | inline constexpr const uint32_t kCiVCidStart = 1000U; 20 | 21 | class VsockCidPool { 22 | public: 23 | uint32_t GetCid(); 24 | 25 | uint32_t GetCid(uint32_t cid); 26 | 27 | bool ReleaseCid(uint32_t cid); 28 | 29 | static VsockCidPool &Pool(void); 30 | 31 | private: 32 | VsockCidPool() = default; 33 | ~VsockCidPool() = default; 34 | VsockCidPool(const VsockCidPool &) = delete; 35 | VsockCidPool& operator=(const VsockCidPool&) = delete; 36 | 37 | std::bitset bs_ = std::move(std::bitset{}.set()); 38 | std::mutex mutex_; 39 | }; 40 | 41 | } // namespace vm_manager 42 | 43 | #endif // SRC_GUEST_VSOCK_CID_POOL_H_ 44 | -------------------------------------------------------------------------------- /src/host/app_launcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | cmake_minimum_required(VERSION 3.10) 9 | 10 | set(APPLAUNCHER_PROJ "app-launcher-client") 11 | project(${APPLAUNCHER_PROJ}) 12 | 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-result -Wno-unused-variable -Wno-narrowing -O2") 14 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g1 -O3 -fstack-protector-strong -Wdate-time -D_FORTIFY_SOURCE=2" CACHE STRING "CXX Release Flags" FORCE) 15 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O2 -g3") 16 | 17 | include_directories(${PROTOS_GEN_DIR}) 18 | include_directories(${DEPS_INCS_INCLUDE_DIRS}) 19 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 20 | 21 | include_directories(${EP_BOOST_INC_DIR}) 22 | 23 | file(GLOB applauncher_src app_launcher_client.cc) 24 | 25 | add_executable(${APPLAUNCHER_PROJ} ${applauncher_src}) 26 | add_dependencies(${APPLAUNCHER_PROJ} vm_grpc_proto) 27 | 28 | target_link_libraries(${APPLAUNCHER_PROJ} 29 | PRIVATE ep_boost::program_options 30 | 31 | vm_grpc_proto 32 | PRIVATE ${_REFLECTION} 33 | PRIVATE ${_GRPC_GRPCPP} 34 | PRIVATE ${_PROTOBUF_LIBPROTOBUF} 35 | -lrt 36 | ) 37 | set_target_properties(${APPLAUNCHER_PROJ} PROPERTIES LINK_FLAGS_RELEASE -s) 38 | 39 | install(TARGETS ${APPLAUNCHER_PROJ} 40 | RUNTIME DESTINATION bin 41 | ) 42 | 43 | set(CMAKE_CXX_STANDARD 17) 44 | set(CMAKE_CXX_STANDARD_REQUIRED True) 45 | -------------------------------------------------------------------------------- /src/host/app_launcher/app_launcher_client.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "../../include/constants/vm_manager.h" 18 | #include "vm_guest.grpc.pb.h" 19 | 20 | class CivAppLauncherClient { 21 | public: 22 | explicit CivAppLauncherClient(std::shared_ptr channel) 23 | : stub_(vm_manager::CivAppLauncher::NewStub(channel)) {} 24 | 25 | bool LaunchApp(std::string app_name, uint32_t disp_id) { 26 | vm_manager::AppLaunchRequest request; 27 | request.set_app_name(app_name); 28 | request.set_disp_id(disp_id); 29 | 30 | vm_manager::AppLaunchResponse reply; 31 | 32 | grpc::ClientContext context; 33 | 34 | grpc::Status status = stub_->LaunchApp(&context, request, &reply); 35 | 36 | if (status.ok()) { 37 | std::cout << "Replay code: " << reply.code() << ", status=" << reply.status() << std::endl; 38 | return true; 39 | } else { 40 | std::cout << status.error_code() << ": " << status.error_message() 41 | << std::endl; 42 | return false; 43 | } 44 | } 45 | 46 | private: 47 | std::unique_ptr stub_; 48 | }; 49 | 50 | namespace po = boost::program_options; 51 | class AppLauncherOptions final { 52 | public: 53 | AppLauncherOptions() { 54 | cmdline_options_.add_options() 55 | ("help,h", "Show this help message") 56 | ("cid,c", po::value(), "Cid of CiV guest") 57 | ("name,n", po::value(), "Name of APP") 58 | ("display,d", po::value(), "Display id of CiV guest"); 59 | } 60 | 61 | AppLauncherOptions(AppLauncherOptions &) = delete; 62 | AppLauncherOptions& operator=(const AppLauncherOptions &) = delete; 63 | 64 | bool ParseOptions(int argc, char* argv[]) { 65 | po::store(po::command_line_parser(argc, argv).options(cmdline_options_).run(), vm_); 66 | po::notify(vm_); 67 | 68 | if (vm_.empty()) { 69 | PrintHelp(); 70 | return false; 71 | } 72 | 73 | if (vm_.count("help")) { 74 | PrintHelp(); 75 | return true; 76 | } 77 | 78 | if (vm_.count("cid") != 1) { 79 | PrintHelp(); 80 | return false; 81 | } 82 | uint32_t cid = vm_["cid"].as(); 83 | 84 | std::string app_name; 85 | if (vm_.count("name") != 1) { 86 | PrintHelp(); 87 | return false; 88 | } 89 | app_name = vm_["name"].as(); 90 | 91 | uint32_t disp_id = -1; 92 | if (vm_.count("display") != 1) { 93 | PrintHelp(); 94 | return false; 95 | } 96 | disp_id = vm_["display"].as(); 97 | 98 | char target_addr[50] = { 0 }; 99 | snprintf(target_addr, sizeof(target_addr) - 1, "vsock:%u:%u", 100 | cid, 101 | vm_manager::kCivAppLauncherListenerPort); 102 | CivAppLauncherClient launcher(grpc::CreateChannel(target_addr, grpc::InsecureChannelCredentials())); 103 | if (launcher.LaunchApp(app_name, disp_id)) 104 | return true; 105 | 106 | return false; 107 | } 108 | 109 | private: 110 | void PrintHelp(void) { 111 | std::cout << "Usage:\n"; 112 | std::cout << " app-launcher-client" 113 | << " [-c cid] [-n app_name] [-d display_id]\n"; 114 | std::cout << "Options:\n"; 115 | 116 | std::cout << cmdline_options_ << std::endl; 117 | std::cout << "Example:\n"; 118 | std::cout << " ./app-launcher-client -c 1000 -n \"com.android.settings/.Settings\" -d 2" << std::endl; 119 | } 120 | 121 | boost::program_options::options_description cmdline_options_; 122 | boost::program_options::variables_map vm_; 123 | }; 124 | 125 | int main(int argc, char *argv[]) { 126 | int ret = -1; 127 | try { 128 | AppLauncherOptions o; 129 | 130 | if (o.ParseOptions(argc, argv)) 131 | ret = 0; 132 | } catch (std::exception& e) { 133 | std::cerr << "Exception: " << e.what() << std::endl; 134 | } 135 | return ret; 136 | } 137 | -------------------------------------------------------------------------------- /src/include/constants/vm_manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #ifndef SRC_INCLUDE_CONSTANTS_VM_MANAGER_H_ 10 | #define SRC_INCLUDE_CONSTANTS_VM_MANAGER_H_ 11 | namespace vm_manager { 12 | inline constexpr int kCivStartupListenerPort = 9999U; 13 | inline constexpr int kCivPowerCtlListenerPort = 10000U; 14 | inline constexpr int kCivAppLauncherListenerPort = 10001U; 15 | } // namespace vm_manager 16 | 17 | #endif // SRC_INCLUDE_CONSTANTS_VM_MANAGER_H_ 18 | -------------------------------------------------------------------------------- /src/services/client.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "services/client.h" 16 | #include "services/message.h" 17 | #include "services/server.h" 18 | #include "utils/log.h" 19 | 20 | namespace vm_manager { 21 | 22 | std::vector Client::GetGuestLists(void) { 23 | std::vector vms; 24 | std::pair vm_lists = client_shm_.find("ListVms"); 25 | for (auto i = 0; i < vm_lists.second; i++) { 26 | vms.push_back(vm_lists.first[i].c_str()); 27 | } 28 | return vms; 29 | } 30 | 31 | void Client::PrepareStartGuestClientShm(const char *path) { 32 | boost::process::environment env = boost::this_process::environment(); 33 | 34 | client_shm_.destroy("StartVmCfgPath"); 35 | client_shm_.destroy("StartVmEnv"); 36 | client_shm_.zero_free_memory(); 37 | 38 | bstring *var_name = client_shm_.construct 39 | ("StartVmCfgPath") 40 | (path, client_shm_.get_segment_manager()); 41 | 42 | bstring *var_env = client_shm_.construct 43 | ("StartVmEnv") 44 | [env.size()] 45 | (client_shm_.get_segment_manager()); 46 | 47 | if (!var_env) 48 | return; 49 | 50 | for (std::string s : env._data) { 51 | var_env->assign(s.c_str()); 52 | var_env++; 53 | } 54 | } 55 | 56 | void Client::PrepareStopGuestClientShm(const char *vm_name) { 57 | client_shm_.destroy("StopVmName"); 58 | client_shm_.zero_free_memory(); 59 | 60 | bstring *var_name = client_shm_.construct 61 | ("StopVmName") 62 | (vm_name, client_shm_.get_segment_manager()); 63 | } 64 | 65 | void Client::PrepareGetGuestInfoClientShm(const char *vm_name) { 66 | client_shm_.destroy("VmName"); 67 | client_shm_.zero_free_memory(); 68 | 69 | bstring *var_name = client_shm_.construct 70 | ("VmName") 71 | (vm_name, client_shm_.get_segment_manager()); 72 | } 73 | 74 | CivVmInfo Client::GetCivVmInfo(const char *vm_name) { 75 | PrepareGetGuestInfoClientShm(vm_name); 76 | Notify(kCivMsgGetVmInfo); 77 | CivVmInfo *vm_info = client_shm_.find_or_construct 78 | ("VmInfo") 79 | (0, VmBuilder::VmState::kVmUnknown); 80 | assert(vm_info != nullptr); 81 | return std::move(*vm_info); 82 | } 83 | 84 | bool Client::Notify(CivMsgType t) { 85 | std::pair sync; 86 | sync = server_shm_.find(kCivServerObjSync); 87 | if (!sync.first || (sync.second != 1)) { 88 | LOG(error) << "Failed to find sync block!" << sync.first << " size=" << sync.second; 89 | return false; 90 | } 91 | boost::interprocess::scoped_lock lock_clients(sync.first->mutex); 92 | 93 | CivMsg *data = server_shm_.construct 94 | (kCivServerObjData) 95 | (); 96 | 97 | if (!data) { 98 | return false; 99 | } 100 | data->type = t; 101 | strncpy(data->payload, client_shm_name_.c_str(), sizeof(data->payload) - 1); 102 | data->payload[sizeof(data->payload) - 1] = '\0'; 103 | 104 | boost::interprocess::scoped_lock lock_cond(sync.first->mutex_cond); 105 | sync.first->cond_s.notify_one(); 106 | sync.first->msg_in = true; 107 | 108 | int retry_cnt = 0; 109 | constexpr const int kMaxRetryCount = 100; 110 | while (sync.first->msg_in && (retry_cnt < kMaxRetryCount)) { 111 | boost::interprocess::cv_status cs = sync.first->cond_c.wait_for(lock_cond, boost::chrono::seconds(1)); 112 | if (cs != boost::interprocess::cv_status::timeout) { 113 | break; 114 | } else { 115 | retry_cnt++; 116 | } 117 | } 118 | 119 | int ret = false; 120 | if (retry_cnt < kMaxRetryCount) 121 | ret = data->type == kCivMsgRespondSuccess ? true : false; 122 | else 123 | LOG(error) << "Server is not responding!"; 124 | 125 | server_shm_.destroy(kCivServerObjData); 126 | server_shm_.zero_free_memory(); 127 | 128 | return ret; 129 | } 130 | 131 | Client::Client() { 132 | server_shm_ = boost::interprocess::managed_shared_memory(boost::interprocess::open_only, kCivServerMemName); 133 | 134 | client_shm_name_ = std::string("CivClientShm" + std::to_string(getpid())); 135 | boost::interprocess::permissions perm; 136 | perm.set_unrestricted(); 137 | client_shm_ = boost::interprocess::managed_shared_memory( 138 | boost::interprocess::create_only, 139 | client_shm_name_.c_str(), 140 | 65536, 141 | 0, 142 | perm); 143 | } 144 | 145 | Client::~Client() { 146 | boost::interprocess::shared_memory_object::remove(client_shm_name_.c_str()); 147 | } 148 | 149 | } // namespace vm_manager 150 | -------------------------------------------------------------------------------- /src/services/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_SERVICES_CLIENT_H_ 9 | #define SRC_SERVICES_CLIENT_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "services/message.h" 17 | 18 | namespace vm_manager { 19 | class Client { 20 | public: 21 | Client(); 22 | ~Client(); 23 | 24 | void PrepareImportGuestClientShm(const char *cfg_path); 25 | std::vector GetGuestLists(void); 26 | void PrepareStartGuestClientShm(const char *vm_name); 27 | void PrepareStopGuestClientShm(const char *vm_name); 28 | void PrepareGetGuestInfoClientShm(const char *vm_name); 29 | CivVmInfo GetCivVmInfo(const char *vm_name); 30 | bool Notify(CivMsgType t); 31 | 32 | private: 33 | boost::interprocess::managed_shared_memory server_shm_; 34 | boost::interprocess::managed_shared_memory client_shm_; 35 | std::string client_shm_name_; 36 | }; 37 | 38 | } // namespace vm_manager 39 | 40 | #endif // SRC_SERVICES_CLIENT_H_ 41 | -------------------------------------------------------------------------------- /src/services/message.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_SERVICES_MESSAGE_H_ 9 | #define SRC_SERVICES_MESSAGE_H_ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "guest/vm_builder.h" 20 | 21 | namespace vm_manager { 22 | 23 | typedef boost::interprocess::allocator CharAllocator; 24 | typedef boost::interprocess::basic_string, CharAllocator> bstring; 25 | 26 | inline constexpr const char *kCivServerMemName = "CivServerShm"; 27 | inline constexpr const char *kCivServerObjSync = "Civ Message Sync"; 28 | inline constexpr const char *kCivServerObjData = "Civ Message Data"; 29 | 30 | 31 | enum CivMsgType { 32 | kCiVMsgStopServer = 100U, 33 | kCivMsgListVm, 34 | kCivMsgImportVm, 35 | kCivMsgStartVm, 36 | kCivMsgStopVm, 37 | kCivMsgGetVmInfo, 38 | kCivMsgTest, 39 | kCivMsgRespondSuccess = 500U, 40 | kCivMsgRespondFail, 41 | }; 42 | 43 | struct CivVmInfo { 44 | unsigned int cid; 45 | VmBuilder::VmState state; 46 | CivVmInfo(unsigned int c, VmBuilder::VmState s) : cid(c), state(s){} 47 | }; 48 | 49 | struct CivMsgSync { 50 | boost::interprocess::interprocess_mutex mutex; 51 | boost::interprocess::interprocess_mutex mutex_cond; 52 | boost::interprocess::interprocess_condition cond_s; 53 | boost::interprocess::interprocess_condition cond_c; 54 | bool msg_in; 55 | }; 56 | 57 | struct CivMsg { 58 | enum { MaxPayloadSize = 1024U }; 59 | 60 | CivMsgType type; 61 | char payload[MaxPayloadSize]; 62 | }; 63 | 64 | } // namespace vm_manager 65 | 66 | #endif // SRC_SERVICES_MESSAGE_H_ 67 | -------------------------------------------------------------------------------- /src/services/protos/Android.bp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | genrule { 10 | name: "CivCommonProtoStub_h", 11 | tools: [ 12 | "aprotoc", 13 | "protoc-gen-grpc-cpp-plugin", 14 | ], 15 | cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", 16 | srcs: [ 17 | "common.proto", 18 | ], 19 | out: [ 20 | "common.pb.h", 21 | "common.grpc.pb.h", 22 | ], 23 | } 24 | 25 | genrule { 26 | name: "CivCommonProtoStub_cc", 27 | tools: [ 28 | "aprotoc", 29 | "protoc-gen-grpc-cpp-plugin", 30 | ], 31 | cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", 32 | srcs: [ 33 | "common.proto", 34 | ], 35 | out: [ 36 | "common.pb.cc", 37 | "common.grpc.pb.cc", 38 | ], 39 | } 40 | 41 | genrule { 42 | name: "CivVmHostProtoStub_h", 43 | tools: [ 44 | "aprotoc", 45 | "protoc-gen-grpc-cpp-plugin", 46 | ], 47 | cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", 48 | srcs: [ 49 | "vm_host.proto", 50 | ], 51 | out: [ 52 | "vm_host.pb.h", 53 | "vm_host.grpc.pb.h", 54 | ], 55 | } 56 | 57 | genrule { 58 | name: "CivVmHostProtoStub_cc", 59 | tools: [ 60 | "aprotoc", 61 | "protoc-gen-grpc-cpp-plugin", 62 | ], 63 | cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", 64 | srcs: [ 65 | "vm_host.proto", 66 | ], 67 | out: [ 68 | "vm_host.pb.cc", 69 | "vm_host.grpc.pb.cc", 70 | ], 71 | } 72 | 73 | genrule { 74 | name: "CivVmGuestProtoStub_h", 75 | tools: [ 76 | "aprotoc", 77 | "protoc-gen-grpc-cpp-plugin", 78 | ], 79 | cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", 80 | srcs: [ 81 | "vm_guest.proto", 82 | ], 83 | out: [ 84 | "vm_guest.pb.h", 85 | "vm_guest.grpc.pb.h", 86 | ], 87 | } 88 | 89 | genrule { 90 | name: "CivVmGuestProtoStub_cc", 91 | tools: [ 92 | "aprotoc", 93 | "protoc-gen-grpc-cpp-plugin", 94 | ], 95 | cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", 96 | srcs: [ 97 | "vm_guest.proto", 98 | ], 99 | out: [ 100 | "vm_guest.pb.cc", 101 | "vm_guest.grpc.pb.cc", 102 | ], 103 | } -------------------------------------------------------------------------------- /src/services/protos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 Intel Corporation. 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # 8 | 9 | cmake_minimum_required(VERSION 3.10) 10 | 11 | file(GLOB CIV_PROTOS "*.proto") 12 | list(APPEND PROTO_FLAGS -I${CMAKE_CURRENT_SOURCE_DIR}) 13 | 14 | file(REMOVE_RECURSE ${PROTOS_GEN_DIR}) 15 | file(MAKE_DIRECTORY ${PROTOS_GEN_DIR}) 16 | 17 | foreach(FIL ${CIV_PROTOS}) 18 | get_filename_component(ABS_FIL ${FIL} ABSOLUTE) 19 | get_filename_component(FIL_WE ${FIL} NAME_WE) 20 | 21 | list(APPEND CIV_PROTOS_SRCS "${PROTOS_GEN_DIR}/${FIL_WE}.pb.cc") 22 | list(APPEND CIV_PROTOS_HDRS "${PROTOS_GEN_DIR}/${FIL_WE}.pb.h") 23 | list(APPEND CIV_GRPC_PROTOS_SRCS "${PROTOS_GEN_DIR}/${FIL_WE}.grpc.pb.cc") 24 | list(APPEND CIV_GRPC_PROTOS_HDRS "${PROTOS_GEN_DIR}/${FIL_WE}.grpc.pb.h") 25 | 26 | add_custom_command( 27 | OUTPUT "${PROTOS_GEN_DIR}/${FIL_WE}.pb.cc" 28 | "${PROTOS_GEN_DIR}/${FIL_WE}.pb.h" 29 | "${PROTOS_GEN_DIR}/${FIL_WE}.grpc.pb.cc" 30 | "${PROTOS_GEN_DIR}/${FIL_WE}.grpc.pb.h" 31 | COMMAND ${_PROTOBUF_PROTOC} 32 | ARGS --grpc_out "${PROTOS_GEN_DIR}" 33 | --cpp_out "${PROTOS_GEN_DIR}" 34 | ${PROTO_FLAGS} 35 | --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" 36 | ${FIL}) 37 | endforeach() 38 | 39 | include_directories("${PROTOS_GEN_DIR}") 40 | add_library(vm_grpc_proto ${CIV_PROTOS_SRCS} ${CIV_PROTOS_HDRS} ${CIV_GRPC_PROTOS_SRCS} ${CIV_GRPC_PROTOS_HDRS}) 41 | 42 | target_link_libraries(vm_grpc_proto 43 | ${_REFLECTION} 44 | ${_GRPC_GRPCPP} 45 | ${_PROTOBUF_LIBPROTOBUF} 46 | ) 47 | -------------------------------------------------------------------------------- /src/services/protos/common.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Intel Corporation. 3 | // All rights reserved. 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | 7 | syntax = "proto3"; 8 | 9 | package vm_manager; 10 | 11 | message EmptyMessage {} 12 | -------------------------------------------------------------------------------- /src/services/protos/vm_guest.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Intel Corporation. 3 | // All rights reserved. 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | 7 | syntax = "proto3"; 8 | 9 | package vm_manager; 10 | 11 | import "common.proto"; 12 | 13 | // The Power Control service definition. 14 | service CivPowerCtl { 15 | // Called by Host to shutdown Guest. 16 | rpc Shutdown (EmptyMessage) returns (EmptyMessage); 17 | } 18 | 19 | message AppLaunchRequest { 20 | string app_name = 1; 21 | uint32 disp_id = 2; 22 | } 23 | 24 | enum AppStatus { 25 | UNKNOWN = 0; 26 | LAUNCHED = 1; 27 | EXITED = 2; 28 | FAILED = 3; 29 | } 30 | 31 | message AppLaunchResponse { 32 | AppStatus status = 1; 33 | sint32 code = 2; 34 | } 35 | 36 | service CivAppLauncher { 37 | // Called by Host to launch app in guest 38 | rpc LaunchApp (AppLaunchRequest) returns (AppLaunchResponse); 39 | } 40 | -------------------------------------------------------------------------------- /src/services/protos/vm_host.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Intel Corporation. 3 | // All rights reserved. 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | 7 | syntax = "proto3"; 8 | 9 | package vm_manager; 10 | 11 | import "common.proto"; 12 | 13 | // The Startup Listener service definition. 14 | service StartupListener { 15 | // Called by each VM when it starts up to indicate that it is ready to handle 16 | // incoming requests. 17 | rpc VmReady (EmptyMessage) returns (EmptyMessage); 18 | } 19 | -------------------------------------------------------------------------------- /src/services/server.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include "services/server.h" 30 | #include "services/message.h" 31 | #include "guest/vm_powerctl.h" 32 | #include "guest/vm_builder_qemu.h" 33 | #include "utils/log.h" 34 | #include "utils/utils.h" 35 | #include "include/constants/vm_manager.h" 36 | 37 | namespace vm_manager { 38 | 39 | const int kCivSharedMemSize = 20480U; 40 | 41 | size_t Server::FindVmInstance(std::string name) { 42 | for (size_t i = 0; i < vmis_.size(); ++i) { 43 | if (vmis_[i]->GetName().compare(name) == 0) 44 | return i; 45 | } 46 | return -1UL; 47 | } 48 | 49 | void Server::DeleteVmInstance(std::string name) { 50 | std::scoped_lock lock(vmis_mutex_); 51 | size_t index = FindVmInstance(name); 52 | if (index == -1UL) 53 | return; 54 | vmis_.erase(vmis_.begin() + index); 55 | } 56 | 57 | int Server::StopVm(const char payload[]) { 58 | boost::interprocess::managed_shared_memory shm( 59 | boost::interprocess::open_only, 60 | payload); 61 | std::pair vm_name; 62 | vm_name = shm.find("StopVmName"); 63 | size_t id = FindVmInstance(std::string(vm_name.first->c_str())); 64 | 65 | if (id != -1UL) { 66 | LOG(info) << "StopVm: " << vmis_[id]->GetName(); 67 | char listener_address[50] = { 0 }; 68 | snprintf(listener_address, sizeof(listener_address) - 1, "vsock:%u:%u", 69 | vmis_[id]->GetCid(), 70 | vm_manager::kCivPowerCtlListenerPort); 71 | CivVmPowerCtl pm(grpc::CreateChannel(listener_address, grpc::InsecureChannelCredentials())); 72 | if (!pm.Shutdown()) { 73 | vmis_[id]->StopVm(); 74 | } 75 | } else { 76 | LOG(warning) << "CiV: " << vm_name.first->c_str() << " is not running!"; 77 | return -1; 78 | } 79 | return 0; 80 | } 81 | 82 | bool Server::SetupStartupListenerService(void) { 83 | boost::latch listener_ready(1); 84 | char listener_address[50] = { 0 }; 85 | snprintf(listener_address, sizeof(listener_address) - 1, "vsock:%u:%u", VMADDR_CID_ANY, kCivStartupListenerPort); 86 | 87 | startup_listener_.thread = 88 | std::make_unique(RunListenerService, 89 | &startup_listener_.listener, 90 | listener_address, 91 | &listener_ready, 92 | &startup_listener_.server); 93 | listener_ready.wait(); 94 | return true; 95 | } 96 | 97 | int Server::ListVm(const char payload[]) { 98 | boost::interprocess::managed_shared_memory shm( 99 | boost::interprocess::open_only, 100 | payload); 101 | 102 | shm.destroy("ListVms"); 103 | shm.zero_free_memory(); 104 | 105 | bstring *vm_lists = shm.construct 106 | ("ListVms") 107 | [vmis_.size()] 108 | (shm.get_segment_manager()); 109 | 110 | for (size_t i = 0; i < vmis_.size(); ++i) { 111 | std::string s(vmis_[i]->GetName() + ":" + VmStateToStr(vmis_[i]->GetState())); 112 | vm_lists[i].assign(s.c_str()); 113 | } 114 | 115 | return 0; 116 | } 117 | 118 | int Server::ImportVm(const char payload[]) { 119 | boost::interprocess::managed_shared_memory shm( 120 | boost::interprocess::open_read_only, 121 | payload); 122 | 123 | auto cfg_path = shm.find("ImportVmCfgPath"); 124 | 125 | std::string p(cfg_path.first->c_str()); 126 | 127 | if (p.empty()) 128 | return -1; 129 | 130 | CivConfig cfg; 131 | if (!cfg.ReadConfigFile(p)) { 132 | LOG(error) << "Failed to read config file"; 133 | return -1; 134 | } 135 | 136 | std::vector name_param; 137 | boost::split(name_param, cfg.GetValue(kGroupGlob, kGlobName), boost::is_any_of(",")); 138 | const std::string &vm_name = name_param[0]; 139 | if (vm_name.empty()) 140 | return -1; 141 | 142 | auto id = FindVmInstance(vm_name); 143 | if (id != -1UL) { 144 | VmBuilder::VmState st = vmis_[id].get()->GetState(); 145 | if ((st != VmBuilder::VmState::kVmCreated) && (st != VmBuilder::VmState::kVmEmpty)) { 146 | LOG(error) << "CiV: " << vm_name << " is running! Cannot overwrite!"; 147 | return -1; 148 | } 149 | LOG(warning) << "Overwrite existed CiV: " << vm_name; 150 | DeleteVmInstance(vm_name); 151 | } 152 | 153 | std::vector>::iterator vmi; 154 | if (cfg.GetValue(kGroupEmul, kEmulType) == kEmulTypeQemu) { 155 | std::unique_ptr vbq = std::make_unique(vm_name, std::move(cfg)); 156 | if (!vbq->BuildVmArgs()) 157 | return -1; 158 | vmi = vmis_.insert(vmis_.end(), std::move(vbq)); 159 | } else { 160 | /* Default try to contruct for QEMU */ 161 | std::unique_ptr vbq = std::make_unique(vm_name, std::move(cfg)); 162 | if (!vbq->BuildVmArgs()) 163 | return -1; 164 | vmi = vmis_.insert(vmis_.end(), std::move(vbq)); 165 | } 166 | return 0; 167 | } 168 | 169 | void Server::VmThread(VmBuilder *vb, boost::latch *notify_cont) { 170 | LOG(info) << "Starting VM: " << vb->GetName(); 171 | /* Start VM */ 172 | vb->StartVm(); 173 | 174 | if (notify_cont->try_count_down()) { 175 | vb->SetVmReady(); 176 | vb->WaitVmExit(); 177 | DeleteVmInstance(vb->GetName()); 178 | return; 179 | } 180 | 181 | startup_listener_.listener.AddPendingVM(vb->GetCid(), [vb](){ 182 | vb->SetVmReady(); 183 | }); 184 | 185 | if (vb->WaitVmReady()) { 186 | notify_cont->try_count_down(); 187 | vb->WaitVmExit(); 188 | DeleteVmInstance(vb->GetName()); 189 | } else { 190 | DeleteVmInstance(vb->GetName()); 191 | notify_cont->try_count_down(); 192 | } 193 | } 194 | 195 | int Server::StartVm(const char payload[]) { 196 | boost::interprocess::managed_shared_memory shm( 197 | boost::interprocess::open_read_only, 198 | payload); 199 | 200 | auto cfg_path = shm.find("StartVmCfgPath"); 201 | 202 | std::string p(cfg_path.first->c_str()); 203 | 204 | if (p.empty()) 205 | return -1; 206 | 207 | CivConfig cfg; 208 | if (!cfg.ReadConfigFile(p)) { 209 | LOG(error) << "Failed to read config file"; 210 | return -1; 211 | } 212 | 213 | std::vector name_param; 214 | boost::split(name_param, cfg.GetValue(kGroupGlob, kGlobName), boost::is_any_of(",")); 215 | const std::string &vm_name = name_param[0]; 216 | if (vm_name.empty()) 217 | return -1; 218 | 219 | if (FindVmInstance(vm_name) != -1UL) { 220 | LOG(error) << vm_name << " is already running!"; 221 | return -1; 222 | } 223 | 224 | std::vector>::iterator vmi; 225 | if (cfg.GetValue(kGroupEmul, kEmulType) == kEmulTypeQemu) { 226 | std::unique_ptr vbq = std::make_unique(vm_name, cfg); 227 | if (!vbq->BuildVmArgs()) 228 | return -1; 229 | vmi = vmis_.insert(vmis_.end(), std::move(vbq)); 230 | } else { 231 | /* Default try to contruct for QEMU */ 232 | std::unique_ptr vbq = std::make_unique(vm_name, cfg); 233 | if (!vbq->BuildVmArgs()) 234 | return -1; 235 | vmi = vmis_.insert(vmis_.end(), std::move(vbq)); 236 | } 237 | 238 | std::pair vm_env = shm.find("StartVmEnv"); 239 | std::vector env_data; 240 | 241 | for (int i = 0; i < vm_env.second; i++) { 242 | env_data.push_back(std::string(vm_env.first[i].c_str())); 243 | } 244 | 245 | VmBuilder *vb = vmi->get(); 246 | vb->SetProcessEnv(std::move(env_data)); 247 | 248 | boost::latch notify_cont(1); 249 | if (cfg.GetValue(kGroupGlob, kGlobWaitReady).compare("true") == 0) { 250 | notify_cont.reset(2); 251 | } 252 | 253 | boost::thread t([this, vb, ¬ify_cont]() { 254 | VmThread(vb, ¬ify_cont); 255 | }); 256 | t.detach(); 257 | 258 | notify_cont.wait(); 259 | 260 | if (vb->GetState() == VmBuilder::VmState::kVmRunning) { 261 | return 0; 262 | } 263 | return -1; 264 | } 265 | 266 | int Server::GetVmInfo(const char payload[]) { 267 | boost::interprocess::managed_shared_memory shm( 268 | boost::interprocess::open_only, 269 | payload); 270 | 271 | std::pair vm_name; 272 | vm_name = shm.find("VmName"); 273 | size_t id = FindVmInstance(std::string(vm_name.first->c_str())); 274 | if (id == -1UL) 275 | return -1; 276 | 277 | shm.destroy("VmInfo"); 278 | shm.zero_free_memory(); 279 | 280 | CivVmInfo *vm_info = shm.construct 281 | ("VmInfo") 282 | (0, VmBuilder::VmState::kVmUnknown); 283 | 284 | vm_info->cid = vmis_[id]->GetCid(); 285 | vm_info->state = vmis_[id]->GetState(); 286 | return 0; 287 | } 288 | 289 | static void HandleSIG(int num) { 290 | LOG(info) << "Signal(" << num << ") received!"; 291 | Server::Get().Stop(); 292 | } 293 | 294 | void Server::Start(void) { 295 | try { 296 | signal(SIGINT, HandleSIG); 297 | signal(SIGTERM, HandleSIG); 298 | 299 | SetupStartupListenerService(); 300 | 301 | struct shm_remove { 302 | shm_remove() { boost::interprocess::shared_memory_object::remove(kCivServerMemName); } 303 | ~shm_remove() { boost::interprocess::shared_memory_object::remove(kCivServerMemName); } 304 | } remover; 305 | 306 | boost::interprocess::permissions unrestricted_permissions; 307 | unrestricted_permissions.set_unrestricted(); 308 | boost::interprocess::managed_shared_memory shm( 309 | boost::interprocess::create_only, 310 | kCivServerMemName, 311 | sizeof(CivMsgSync) + sizeof(CivMsg) + 1024, 312 | 0, 313 | unrestricted_permissions); 314 | 315 | sync_ = shm.construct 316 | (kCivServerObjSync) 317 | (); 318 | 319 | while (!stop_server_) { 320 | boost::interprocess::scoped_lock lock(sync_->mutex_cond); 321 | if (!sync_->msg_in) 322 | sync_->cond_s.wait(lock); 323 | 324 | std::pair data; 325 | data = shm.find(kCivServerObjData); 326 | 327 | if (!data.first) 328 | continue; 329 | 330 | switch (data.first->type) { 331 | case kCiVMsgStopServer: 332 | stop_server_ = true; 333 | data.first->type = kCivMsgRespondSuccess; 334 | break; 335 | case kCivMsgListVm: 336 | if (ListVm(data.first->payload) == 0) { 337 | data.first->type = kCivMsgRespondSuccess; 338 | } else { 339 | data.first->type = kCivMsgRespondFail; 340 | } 341 | break; 342 | case kCivMsgImportVm: 343 | if (ImportVm(data.first->payload) == 0) { 344 | data.first->type = kCivMsgRespondSuccess; 345 | } else { 346 | data.first->type = kCivMsgRespondFail; 347 | } 348 | break; 349 | case kCivMsgStartVm: 350 | if (StartVm(data.first->payload) == 0) { 351 | data.first->type = kCivMsgRespondSuccess; 352 | } else { 353 | data.first->type = kCivMsgRespondFail; 354 | } 355 | break; 356 | case kCivMsgStopVm: 357 | if (StopVm(data.first->payload) == 0) { 358 | data.first->type = kCivMsgRespondSuccess; 359 | } else { 360 | data.first->type = kCivMsgRespondFail; 361 | } 362 | break; 363 | case kCivMsgGetVmInfo: 364 | if (GetVmInfo(data.first->payload) == 0) { 365 | data.first->type = kCivMsgRespondSuccess; 366 | } else { 367 | data.first->type = kCivMsgRespondFail; 368 | } 369 | break; 370 | case kCivMsgTest: 371 | break; 372 | default: 373 | LOG(error) << "vm-manager: received unknown message type: " << data.first->type; 374 | break; 375 | } 376 | sync_->msg_in = false; 377 | sync_->cond_c.notify_one(); 378 | } 379 | 380 | shm.destroy_ptr(sync_); 381 | 382 | LOG(info) << "CiV Server exited!"; 383 | } catch (std::exception &e) { 384 | LOG(error) << "CiV Server: Exception:" << e.what() << ", pid=" << getpid(); 385 | } 386 | } 387 | 388 | void Server::Stop(void) { 389 | LOG(info) << "Stop CiV Server!"; 390 | stop_server_ = true; 391 | sync_->cond_s.notify_one(); 392 | if (startup_listener_.server) 393 | startup_listener_.server->Shutdown(); 394 | } 395 | 396 | Server &Server::Get(void) { 397 | static Server server_; 398 | return server_; 399 | } 400 | 401 | } // namespace vm_manager 402 | -------------------------------------------------------------------------------- /src/services/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_SERVICES_SERVER_H_ 9 | #define SRC_SERVICES_SERVER_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "utils/log.h" 18 | #include "guest/vm_builder.h" 19 | #include "services/message.h" 20 | #include "services/startup_listener_impl.h" 21 | 22 | namespace vm_manager { 23 | 24 | struct StartupListenerInst { 25 | StartupListenerImpl listener; 26 | std::unique_ptr thread; 27 | std::shared_ptr server; 28 | }; 29 | 30 | class Server final { 31 | public: 32 | static Server &Get(void); 33 | void Start(void); 34 | void Stop(void); 35 | 36 | private: 37 | Server() = default; 38 | ~Server() = default; 39 | 40 | Server(const Server&) = delete; 41 | Server& operator=(const Server&) = delete; 42 | 43 | size_t FindVmInstance(std::string name); 44 | void DeleteVmInstance(std::string name); 45 | 46 | int ListVm(const char payload[]); 47 | int ImportVm(const char payload[]); 48 | int StartVm(const char payload[]); 49 | int StopVm(const char payload[]); 50 | int GetVmInfo(const char payload[]); 51 | 52 | void VmThread(VmBuilder *vb, boost::latch *wait_continue); 53 | 54 | void Accept(); 55 | 56 | void AsyncWaitSignal(void); 57 | 58 | bool SetupStartupListenerService(); 59 | 60 | bool stop_server_ = false; 61 | 62 | CivMsgSync *sync_ = nullptr; 63 | 64 | std::vector> vmis_; 65 | 66 | std::mutex vmis_mutex_; 67 | 68 | StartupListenerInst startup_listener_; 69 | }; 70 | 71 | } // namespace vm_manager 72 | 73 | #endif // SRC_SERVICES_SERVER_H_ 74 | -------------------------------------------------------------------------------- /src/services/startup_listener_impl.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "utils/log.h" 15 | #include "services/startup_listener_impl.h" 16 | 17 | namespace vm_manager { 18 | 19 | grpc::Status StartupListenerImpl::VmReady(grpc::ServerContext* ctx, 20 | const EmptyMessage* request, 21 | EmptyMessage* respond) { 22 | uint32_t cid = 0; 23 | if (sscanf(ctx->peer().c_str(), "vsock:%u", &cid) != 1) { 24 | LOG(warning) << "Failed to parse peer vsock address " << ctx->peer(); 25 | return grpc::Status(grpc::FAILED_PRECONDITION, "Invalid peer for StartupListener"); 26 | } 27 | LOG(info) << "gRPC call from VM, cid=" << cid; 28 | 29 | std::scoped_lock lock(vm_lock_); 30 | auto iter = pending_vms_.find(cid); 31 | if (iter == pending_vms_.end()) { 32 | LOG(error) << "Received VmReady from VM with unknown cid: " << cid; 33 | return grpc::Status(grpc::FAILED_PRECONDITION, "VM is unknown"); 34 | } 35 | 36 | if (iter->second != nullptr) 37 | iter->second(); 38 | 39 | pending_vms_.erase(iter); 40 | 41 | return grpc::Status::OK; 42 | } 43 | 44 | void StartupListenerImpl::AddPendingVM(uint32_t cid, std::function callback) { 45 | std::scoped_lock lock(vm_lock_); 46 | 47 | pending_vms_[cid] = callback; 48 | } 49 | 50 | void StartupListenerImpl::RemovePendingVM(uint32_t cid) { 51 | std::scoped_lock lock(vm_lock_); 52 | 53 | pending_vms_.erase(cid); 54 | } 55 | 56 | void RunListenerService(grpc::Service* listener, 57 | const char *listener_address, 58 | boost::latch *listener_ready, 59 | std::shared_ptr* server_copy) { 60 | grpc::ServerBuilder builder; 61 | LOG(info) << "Startup Listener listen@" << listener_address; 62 | builder.AddListeningPort(listener_address, grpc::InsecureServerCredentials()); 63 | builder.RegisterService(listener); 64 | 65 | std::shared_ptr server(builder.BuildAndStart().release()); 66 | 67 | *server_copy = server; 68 | listener_ready->try_count_down(); 69 | 70 | if (server) { 71 | server->Wait(); 72 | } 73 | } 74 | 75 | } // namespace vm_manager 76 | -------------------------------------------------------------------------------- /src/services/startup_listener_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_SERVICES_STARTUP_LISTENER_IMPL_H_ 9 | #define SRC_SERVICES_STARTUP_LISTENER_IMPL_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include "vm_host.grpc.pb.h" 15 | 16 | namespace vm_manager { 17 | 18 | class StartupListenerImpl final : public vm_manager::StartupListener::Service { 19 | public: 20 | StartupListenerImpl() = default; 21 | StartupListenerImpl(const StartupListenerImpl &) = delete; 22 | StartupListenerImpl& operator=(const StartupListenerImpl&) = delete; 23 | 24 | ~StartupListenerImpl() override = default; 25 | 26 | grpc::Status VmReady(grpc::ServerContext* ctx, const EmptyMessage* request, EmptyMessage* respond) override; 27 | 28 | void AddPendingVM(uint32_t cid, std::function callback); 29 | void RemovePendingVM(uint32_t cid); 30 | 31 | private: 32 | std::map> pending_vms_; 33 | std::mutex vm_lock_; 34 | }; 35 | 36 | void RunListenerService(grpc::Service* listener, 37 | const char *listener_address, 38 | boost::latch *listener_ready, 39 | std::shared_ptr* server_copy); 40 | 41 | 42 | } // namespace vm_manager 43 | 44 | #endif // SRC_SERVICES_STARTUP_LISTENER_IMPL_H_ 45 | -------------------------------------------------------------------------------- /src/utils/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_UTILS_LOG_H_ 9 | #define SRC_UTILS_LOG_H_ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef DEBUG 27 | #define LOG_ADD_FILE boost::log::attribute_cast>( \ 28 | logger::gLogger.get_attributes()["File"]).set( \ 29 | logger::path_to_filename(__FILE__)) 30 | 31 | #define LOG_ADD_LINE boost::log::attribute_cast>( \ 32 | logger::gLogger.get_attributes()["Line"]).set(__LINE__) 33 | 34 | #define LOG_ADD_FUNC boost::log::attribute_cast>( \ 35 | logger::gLogger.get_attributes()["Func"]).set(__FUNCTION__) 36 | 37 | #define DEBUG_OUTPUT LOG_ADD_FILE; \ 38 | LOG_ADD_LINE; \ 39 | LOG_ADD_FUNC; 40 | #else 41 | #define DEBUG_OUTPUT 42 | #endif 43 | 44 | #define LOG(sev) \ 45 | DEBUG_OUTPUT \ 46 | BOOST_LOG_STREAM_WITH_PARAMS(logger::gLogger, \ 47 | (::boost::log::keywords::severity = ::boost::log::trivial::sev)) 48 | 49 | namespace logger { 50 | namespace logging = boost::log; 51 | namespace attrs = boost::log::attributes; 52 | namespace expr = boost::log::expressions; 53 | namespace src = boost::log::sources; 54 | namespace keywords = boost::log::keywords; 55 | 56 | inline boost::log::trivial::logger::logger_type &gLogger = boost::log::trivial::logger::get(); 57 | 58 | template 59 | ValueType set_get_attrib(const char* name, ValueType value) { 60 | auto attr = logging::attribute_cast> 61 | (gLogger.get_attributes()[name]); 62 | attr.set(value); 63 | return attr.get(); 64 | } 65 | 66 | inline std::string path_to_filename(std::string path) { 67 | return path.substr(path.find_last_of("/\\") + 1); 68 | } 69 | 70 | inline void init(void) { 71 | #ifdef DEBUG 72 | gLogger.add_attribute("File", attrs::mutable_constant("")); 73 | gLogger.add_attribute("Line", attrs::mutable_constant(0)); 74 | gLogger.add_attribute("Func", attrs::mutable_constant("")); 75 | #endif 76 | gLogger.add_attribute("ProcName", attrs::current_process_name()); 77 | 78 | logging::add_console_log(std::cout, 79 | keywords::format = ( 80 | expr::stream 81 | << expr::format_date_time("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f") 82 | << " [" << expr::attr("ProcName") << "]" 83 | << " [" << std::setw(8) << logging::trivial::severity << "] " 84 | #ifdef DEBUG 85 | << '[' << expr::attr("File") 86 | << ':' << expr::attr("Line") << "" 87 | << ':' << expr::attr("Func") << "()]: " 88 | #endif 89 | << expr::smessage)); 90 | 91 | logging::add_common_attributes(); 92 | 93 | logging::core::get()->set_exception_handler(logging::make_exception_suppressor()); 94 | } 95 | 96 | inline void log2file(const char *file) { 97 | if (file) { 98 | logging::add_file_log( 99 | file, 100 | keywords::format = ( 101 | expr::stream 102 | << expr::format_date_time("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f") 103 | << " [" << expr::attr("ProcName") << "]" 104 | << " [" << std::setw(8) << logging::trivial::severity << "] " 105 | #ifdef DEBUG 106 | << '[' << expr::attr("File") 107 | << ':' << expr::attr("Line") << "" 108 | << ':' << expr::attr("Func") << "()]: " 109 | #endif 110 | << expr::smessage), 111 | keywords::open_mode = std::ios_base::app, 112 | keywords::auto_flush = (true)); 113 | } 114 | } 115 | 116 | } // namespace logger 117 | 118 | #endif // SRC_UTILS_LOG_H_ 119 | -------------------------------------------------------------------------------- /src/utils/utils.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include "utils/utils.h" 22 | #include "utils/log.h" 23 | 24 | static char *civ_config_path = NULL; 25 | 26 | const char *GetConfigPath(void) { 27 | if (civ_config_path) { 28 | return civ_config_path; 29 | } 30 | 31 | int ret = 0; 32 | char *sudo_uid = NULL; 33 | char *sudo_gid = NULL; 34 | uid_t ruid, euid, suid; 35 | gid_t rgid, egid, sgid; 36 | 37 | getresuid(&ruid, &euid, &suid); 38 | getresgid(&rgid, &egid, &sgid); 39 | suid = geteuid(); 40 | sgid = getegid(); 41 | 42 | sudo_uid = std::getenv("SUDO_UID"); 43 | sudo_gid = std::getenv("SUDO_GID"); 44 | 45 | if (sudo_gid) { 46 | egid = atoi(sudo_gid); 47 | setresgid(rgid, egid, sgid); 48 | } 49 | 50 | if (sudo_uid) { 51 | euid = atoi(sudo_uid); 52 | setresuid(ruid, euid, suid); 53 | } 54 | 55 | civ_config_path = new char[MAX_PATH]; 56 | memset(civ_config_path, 0, MAX_PATH); 57 | 58 | struct passwd pwd; 59 | struct passwd *ppwd = &pwd; 60 | struct passwd *presult = NULL; 61 | char buffer[4096]; 62 | ret = getpwuid_r(euid, ppwd, buffer, sizeof(buffer), &presult); 63 | if (ret != 0 || presult == NULL) 64 | return NULL; 65 | 66 | boost::system::error_code ec; 67 | snprintf(civ_config_path, MAX_PATH, "%s%s", pwd.pw_dir, "/.intel/.civ"); 68 | if (!boost::filesystem::exists(civ_config_path, ec)) { 69 | if (!boost::filesystem::create_directories(civ_config_path, ec)) { 70 | delete[] civ_config_path; 71 | civ_config_path = NULL; 72 | } 73 | } 74 | 75 | if (sudo_gid) 76 | setresgid(rgid, sgid, egid); 77 | 78 | if (sudo_uid) 79 | setresuid(ruid, suid, euid); 80 | 81 | return civ_config_path; 82 | } 83 | 84 | int Daemonize(void) { 85 | if (pid_t pid = fork()) { 86 | if (pid > 0) { 87 | exit(0); 88 | } else { 89 | LOG(error) << "First fork failed %m!"; 90 | return -1; 91 | } 92 | } 93 | 94 | setsid(); 95 | signal(SIGCHLD, SIG_IGN); 96 | signal(SIGHUP, SIG_IGN); 97 | chdir("/"); 98 | 99 | if (pid_t pid = fork()) { 100 | if (pid > 0) { 101 | return pid; 102 | } else { 103 | LOG(error) << "Second fork failed!"; 104 | exit(-1); 105 | } 106 | } 107 | 108 | umask(0); 109 | for (int t = sysconf(_SC_OPEN_MAX); t >= 0; t--) { 110 | close(t); 111 | } 112 | 113 | /* Close the standard streams. */ 114 | close(STDIN_FILENO); 115 | close(STDOUT_FILENO); 116 | close(STDERR_FILENO); 117 | 118 | int fd = open("/dev/null", O_RDWR); 119 | if (fd < 0) 120 | return -1; 121 | 122 | if (fd != STDIN_FILENO) { 123 | close(fd); 124 | return -1; 125 | } 126 | if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) 127 | return -1; 128 | if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) 129 | return -1; 130 | 131 | return 0; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #ifndef SRC_UTILS_UTILS_H_ 9 | #define SRC_UTILS_UTILS_H_ 10 | 11 | #ifndef MAX_PATH 12 | #define MAX_PATH 2048U 13 | #endif 14 | 15 | #define CIV_GUEST_QMP_SUFFIX ".qmp.unix.socket" 16 | 17 | const char *GetConfigPath(void); 18 | int Daemonize(void); 19 | 20 | constexpr std::size_t operator""_KB(unsigned long long v) { 21 | return 1024u * v; 22 | } 23 | 24 | constexpr std::size_t operator""_MB(unsigned long long v) { 25 | return 1024u * 1024u * v; 26 | } 27 | 28 | constexpr std::size_t operator""_GB(unsigned long long v) { 29 | return 1024u * 1024u * 1024u * v; 30 | } 31 | 32 | 33 | #endif // SRC_UTILS_UTILS_H_ 34 | -------------------------------------------------------------------------------- /src/vm_manager.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Intel Corporation. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "utils/log.h" 20 | #include "utils/utils.h" 21 | #include "guest/vm_builder.h" 22 | #include "guest/vm_flash.h" 23 | #include "guest/tui.h" 24 | #include "services/server.h" 25 | #include "services/client.h" 26 | #include "revision.h" 27 | 28 | namespace vm_manager { 29 | 30 | bool IsServerRunning() { 31 | try { 32 | Client c; 33 | c.Notify(kCivMsgTest); 34 | return true; 35 | } catch (std::exception& e) { 36 | // LOG(warning) << "Server is not running: " << e.what(); 37 | return false; 38 | } 39 | } 40 | 41 | static bool ListGuest(void) { 42 | if (!IsServerRunning()) { 43 | LOG(info) << "server is not running! Please start server!"; 44 | return false; 45 | } 46 | 47 | Client c; 48 | if (!c.Notify(kCivMsgListVm)) { 49 | LOG(error) << "List guest: " << " Failed!"; 50 | return false; 51 | } 52 | auto vm_list = c.GetGuestLists(); 53 | std::cout << "=====================" << std::endl; 54 | for (auto& it : vm_list) { 55 | std::cout << it << std::endl; 56 | } 57 | return true; 58 | } 59 | 60 | static int GetGuestState(std::string name) { 61 | if (name.empty()) 62 | return -1; 63 | if (!IsServerRunning()) { 64 | LOG(info) << "server is not running! Please start server!"; 65 | return -1; 66 | } 67 | 68 | Client c; 69 | if (!c.Notify(kCivMsgListVm)) { 70 | LOG(error) << "List guest: " << " Failed!"; 71 | } 72 | auto vm_list = c.GetGuestLists(); 73 | for (auto& it : vm_list) { 74 | std::vector sp; 75 | boost::split(sp, it, boost::is_any_of(":")); 76 | if (sp.size() != 2) 77 | continue; 78 | if (sp[0].compare(name) == 0) { 79 | for (auto i = 0; i < VmBuilder::kVmUnknown; i++) { 80 | if (sp[1].compare(VmStateToStr(static_cast(i))) == 0) { 81 | return i; 82 | } 83 | } 84 | } 85 | } 86 | return VmBuilder::kVmUnknown; 87 | } 88 | 89 | static bool StartGuest(std::string path) { 90 | if (!IsServerRunning()) { 91 | LOG(info) << "server is not running! Please start server!"; 92 | return false; 93 | } 94 | 95 | boost::system::error_code ec; 96 | boost::filesystem::path p(path); 97 | 98 | if (!boost::filesystem::exists(p, ec) || !boost::filesystem::is_regular_file(p, ec)) { 99 | p.clear(); 100 | p.assign(GetConfigPath() + std::string("/") + path + ".ini"); 101 | if (!boost::filesystem::exists(p, ec)) { 102 | LOG(error) << "CiV config not exists: " << path; 103 | return false; 104 | } 105 | } 106 | 107 | Client c; 108 | c.PrepareStartGuestClientShm(p.c_str()); 109 | if (!c.Notify(kCivMsgStartVm)) { 110 | LOG(error) << "Start guest: " << path << " Failed!"; 111 | return false; 112 | } 113 | LOG(info) << "Start guest: " << path << " Done."; 114 | return true; 115 | } 116 | 117 | static bool StopGuest(std::string name) { 118 | if (!IsServerRunning()) { 119 | LOG(info) << "server is not running! Please start server!"; 120 | return false; 121 | } 122 | 123 | Client c; 124 | c.PrepareStopGuestClientShm(name.c_str()); 125 | if (!c.Notify(kCivMsgStopVm)) { 126 | LOG(error) << "Stop guest: " << name << " Failed!"; 127 | return false; 128 | } 129 | LOG(info) << "Stop guest: " << name << " Done."; 130 | return true; 131 | } 132 | 133 | 134 | static bool GetGuestCid(std::string name) { 135 | if (!IsServerRunning()) { 136 | LOG(info) << "server is not running! Please start server first!"; 137 | return false; 138 | } 139 | 140 | Client c; 141 | CivVmInfo vi = c.GetCivVmInfo(name.c_str()); 142 | if (vi.state == VmBuilder::VmState::kVmUnknown) { 143 | LOG(error) << "Failed to get guest Cid: " << name; 144 | return false; 145 | } 146 | std::cout << vi.cid << std::endl; 147 | return true; 148 | } 149 | 150 | static bool StartServer(bool daemon) { 151 | if (IsServerRunning()) { 152 | LOG(info) << "Server already running!"; 153 | return false; 154 | } 155 | 156 | if (daemon) { 157 | const char *log_file = "/tmp/civ_server.log"; 158 | int ret = Daemonize(); 159 | if (ret > 0) { 160 | LOG(info) << "Starting service as daemon (PID=" << ret << ")"; 161 | LOG(info) << "Log will be redirected to " << log_file; 162 | return true; 163 | } else if (ret < 0) { 164 | LOG(error) << "vm-manager: failed to Daemonize\n"; 165 | return false; 166 | } 167 | 168 | logger::log2file(log_file); 169 | LOG(info) << "\n--------------------- " 170 | << "CiV VM Manager Service started in background!" 171 | << "(PID=" << getpid() << ")" 172 | << " ---------------------"; 173 | } 174 | 175 | Server &srv = Server::Get(); 176 | 177 | LOG(info) << "Starting Server!"; 178 | srv.Start(); 179 | 180 | return true; 181 | } 182 | 183 | static bool StopServer(void) { 184 | if (!IsServerRunning()) { 185 | LOG(info) << "server is not running! Please start server first!"; 186 | return false; 187 | } 188 | 189 | Client c; 190 | if (c.Notify(kCiVMsgStopServer)) { 191 | if (IsServerRunning()) 192 | return false; 193 | return true; 194 | } 195 | return false; 196 | } 197 | 198 | namespace po = boost::program_options; 199 | class CivOptions final { 200 | public: 201 | CivOptions() { 202 | cmdline_options_.add_options() 203 | ("help,h", "Show this help message") 204 | ("create,c", po::value(), "Create a CiV guest") 205 | // ("delete,d", po::value(), "Delete a CiV guest") 206 | ("start,b", po::value(), "Start a CiV guest") 207 | ("stop,q", po::value(), "Stop a CiV guest") 208 | ("flash,f", po::value(), "Flash a CiV guest") 209 | // ("update,u", po::value(), "Update an existing CiV guest") 210 | ("get-cid", po::value(), "Get cid of a guest") 211 | ("list,l", "List existing CiV guest") 212 | ("version,v", "Show CiV vm-manager version") 213 | ("start-server", "Start host server") 214 | ("stop-server", "Stop host server") 215 | ("daemon", "start server as a daemon"); 216 | } 217 | 218 | CivOptions(CivOptions &) = delete; 219 | CivOptions& operator=(const CivOptions &) = delete; 220 | 221 | bool ParseOptions(const std::vector args) { 222 | try { 223 | po::store(po::command_line_parser(args).options(cmdline_options_).run(), vm_); 224 | po::notify(vm_); 225 | } catch (std::exception& e) { 226 | std::cout << e.what() << "\n"; 227 | return false; 228 | } 229 | 230 | if (vm_.empty()) { 231 | PrintHelp(); 232 | return false; 233 | } 234 | 235 | if (vm_.count("help")) { 236 | PrintHelp(); 237 | return true; 238 | } 239 | 240 | if (vm_.count("version")) { 241 | PrintVersion(); 242 | return true; 243 | } 244 | 245 | if (vm_.count("stop-server")) { 246 | return StopServer(); 247 | } 248 | 249 | if (vm_.count("start-server")) { 250 | bool daemon = (vm_.count("daemon") == 0) ? false : true; 251 | return StartServer(daemon); 252 | } else { 253 | if (!IsServerRunning()) { 254 | boost::filesystem::path cmd(args[0]); 255 | boost::system::error_code ec; 256 | if (!boost::filesystem::exists(cmd, ec)) { 257 | cmd.assign(boost::process::search_path(args[0])); 258 | } 259 | if (boost::process::system(cmd, "--start-server", "--daemon")) { 260 | LOG(error) << "Failed to start server of vm-manager!"; 261 | return false; 262 | } 263 | int wait_cnt = 0; 264 | while (!IsServerRunning()) { 265 | if (wait_cnt++ > 100) { 266 | LOG(error) << "Cannot start server!"; 267 | return false; 268 | } 269 | boost::this_thread::sleep_for(boost::chrono::microseconds(1000)); 270 | } 271 | } 272 | } 273 | 274 | if (vm_.count("create")) { 275 | CivTui ct; 276 | ct.InitializeUi(vm_["create"].as()); 277 | return true; 278 | } 279 | 280 | if (vm_.count("delete")) { 281 | return true; 282 | } 283 | 284 | if (vm_.count("start")) { 285 | return StartGuest(vm_["start"].as()); 286 | } 287 | 288 | if (vm_.count("stop")) { 289 | return StopGuest(vm_["stop"].as()); 290 | } 291 | 292 | if (vm_.count("flash")) { 293 | VmFlasher f; 294 | return f.FlashGuest(vm_["flash"].as()); 295 | } 296 | 297 | if (vm_.count("update")) { 298 | return false; 299 | } 300 | 301 | if (vm_.count("get-cid")) { 302 | return GetGuestCid(vm_["get-cid"].as()); 303 | } 304 | 305 | if (vm_.count("list")) { 306 | return ListGuest(); 307 | } 308 | 309 | return false; 310 | } 311 | 312 | private: 313 | void PrintHelp(void) { 314 | std::cout << "Usage:\n"; 315 | std::cout << " vm-manager" 316 | << " [-c vm_name] [-b vm_name] [-q vm_name] [-f vm_name] [--get-cid vm_name]" 317 | << " [-l] [-v] [-h]\n"; 318 | std::cout << "Options:\n"; 319 | 320 | std::cout << cmdline_options_ << std::endl; 321 | } 322 | void PrintVersion(void) { 323 | std::cout << "CiV vm-manager version: " 324 | << BUILD_REVISION 325 | << " " << BUILD_TYPE 326 | << " " << BUILD_TIMESTAMP 327 | << " @" << BUILD_FQDN 328 | << std::endl; 329 | } 330 | 331 | boost::program_options::options_description cmdline_options_; 332 | boost::program_options::variables_map vm_; 333 | }; 334 | 335 | } // namespace vm_manager 336 | 337 | int main(int argc, char *argv[]) { 338 | int ret = -1; 339 | 340 | try { 341 | logger::init(); 342 | 343 | if (!GetConfigPath()) 344 | return ret; 345 | 346 | vm_manager::CivOptions co; 347 | 348 | std::vector args(argv, argv + argc); 349 | if (args.size() == 2) { 350 | if (args[1].compare("--afl-fuzz") == 0) { 351 | // Take input from stdio when fuzzing. 352 | std::string str; 353 | std::getline(std::cin, str); 354 | args.clear(); 355 | boost::split(args, str, boost::is_any_of(" ")); 356 | args.insert(args.begin(), argv[0]); 357 | } 358 | } 359 | 360 | if (co.ParseOptions(args)) { 361 | ret = 0; 362 | } 363 | } catch (std::exception& e) { 364 | std::cout << "Exception: " << e.what() << std::endl; 365 | ret = -1; 366 | } 367 | 368 | return ret; 369 | } 370 | --------------------------------------------------------------------------------