#include #include #include #include #include "common/swaglog.h" #include "common/util.h" #include "third_party/raylib/include/raylib.h" int freshClone(); int cachedFetch(const std::string &cache); int executeGitCommand(const std::string &cmd); std::string get_str(std::string const s) { std::string::size_type pos = s.find('?'); assert(pos != std::string::npos); return s.substr(0, pos); } // Leave some extra space for the fork installer const std::string GIT_URL = get_str("https://github.com/commaai/openpilot.git" "? "); const std::string BRANCH_STR = get_str(BRANCH "? "); #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" const std::string CACHE_PATH = "/data/openpilot.cache"; #define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end"); Font font; void run(const char* cmd) { int err = std::system(cmd); assert(err == 0); } void renderProgress(int progress) { BeginDrawing(); ClearBackground(BLACK); DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE); Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72}; DrawRectangleRec(bar, (Color){41, 41, 41, 255}); progress = std::clamp(progress, 0, 100); bar.width *= progress / 100.0f; DrawRectangleRec(bar, (Color){70, 91, 234, 255}); DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE); EndDrawing(); } int doInstall() { // wait for valid time while (!util::system_time_valid()) { util::sleep_for(500); LOGD("Waiting for valid time"); } // cleanup previous install attempts run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); // do the install if (util::file_exists(CACHE_PATH)) { return cachedFetch(CACHE_PATH); } else { return freshClone(); } } int freshClone() { LOGD("Doing fresh clone"); std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); return executeGitCommand(cmd); } int cachedFetch(const std::string &cache) { LOGD("Fetching with cache: %s", cache.c_str()); run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); renderProgress(10); return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); } int executeGitCommand(const std::string &cmd) { static const std::array stages = { // prefix, weight in percentage std::pair{"Receiving objects: ", 91}, std::pair{"Resolving deltas: ", 2}, std::pair{"Updating files: ", 7}, }; FILE *pipe = popen(cmd.c_str(), "r"); if (!pipe) return -1; char buffer[512]; while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { std::string line(buffer); int base = 0; for (const auto &[text, weight] : stages) { if (line.find(text) != std::string::npos) { size_t percentPos = line.find("%"); if (percentPos != std::string::npos && percentPos >= 3) { int percent = std::stoi(line.substr(percentPos - 3, 3)); int progress = base + int(percent / 100. * weight); renderProgress(progress); } break; } base += weight; } } return pclose(pipe); } void cloneFinished(int exitCode) { LOGD("git finished with %d", exitCode); assert(exitCode == 0); renderProgress(100); // ensure correct branch is checked out int err = chdir(TMP_INSTALL_PATH); assert(err == 0); run(("git checkout " + BRANCH_STR).c_str()); run(("git reset --hard origin/" + BRANCH_STR).c_str()); run("git submodule update --init"); // move into place run("mv " TMP_INSTALL_PATH " " INSTALL_PATH); #ifdef INTERNAL run("mkdir -p /data/params/d/"); // https://github.com/commaci2.keys const std::string ssh_keys = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMX2kU8eBZyEWmbq0tjMPxksWWVuIV/5l64GabcYbdpI"; std::map params = { {"SshEnabled", "1"}, {"RecordFrontLock", "1"}, {"GithubSshKeys", ssh_keys}, }; for (const auto& [key, value] : params) { std::ofstream param; param.open("/data/params/d/" + key); param << value; param.close(); } run("cd " INSTALL_PATH " && " "git remote set-url origin --push " GIT_SSH_URL " && " "git config --replace-all remote.origin.fetch \"+refs/heads/*:refs/remotes/origin/*\""); #endif // write continue.sh FILE *of = fopen("/data/continue.sh.new", "wb"); assert(of != NULL); size_t num = str_continue_end - str_continue; size_t num_written = fwrite(str_continue, 1, num, of); assert(num == num_written); fclose(of); run("chmod +x /data/continue.sh.new"); run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over util::sleep_for(60 * 1000); } int main(int argc, char *argv[]) { InitWindow(2160, 1080, "Installer"); font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); renderProgress(0); int result = doInstall(); cloneFinished(result); CloseWindow(); UnloadFont(font); return 0; }