123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- #include <time.h>
- #include <unistd.h>
- #include <cstdlib>
- #include <fstream>
- #include <map>
- #include <QDebug>
- #include <QDir>
- #include <QTimer>
- #include <QVBoxLayout>
- #include "selfdrive/ui/installer/installer.h"
- #include "selfdrive/ui/qt/util.h"
- #include "selfdrive/ui/qt/qt_window.h"
- 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 QString 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_" BRAND "_sh_start");
- extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_" BRAND "_sh_end");
- bool time_valid() {
- time_t rawtime;
- time(&rawtime);
- struct tm * sys_time = gmtime(&rawtime);
- return (1900 + sys_time->tm_year) >= 2020;
- }
- void run(const char* cmd) {
- int err = std::system(cmd);
- assert(err == 0);
- }
- Installer::Installer(QWidget *parent) : QWidget(parent) {
- QVBoxLayout *layout = new QVBoxLayout(this);
- layout->setContentsMargins(150, 290, 150, 150);
- layout->setSpacing(0);
- QLabel *title = new QLabel(tr("Installing..."));
- title->setStyleSheet("font-size: 90px; font-weight: 600;");
- layout->addWidget(title, 0, Qt::AlignTop);
- layout->addSpacing(170);
- bar = new QProgressBar();
- bar->setRange(0, 100);
- bar->setTextVisible(false);
- bar->setFixedHeight(72);
- layout->addWidget(bar, 0, Qt::AlignTop);
- layout->addSpacing(30);
- val = new QLabel("0%");
- val->setStyleSheet("font-size: 70px; font-weight: 300;");
- layout->addWidget(val, 0, Qt::AlignTop);
- layout->addStretch();
- QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &Installer::cloneFinished);
- QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress);
- QTimer::singleShot(100, this, &Installer::doInstall);
- setStyleSheet(R"(
- * {
- font-family: Inter;
- color: white;
- background-color: black;
- }
- QProgressBar {
- border: none;
- background-color: #292929;
- }
- QProgressBar::chunk {
- background-color: #364DEF;
- }
- )");
- }
- void Installer::updateProgress(int percent) {
- bar->setValue(percent);
- val->setText(QString("%1%").arg(percent));
- update();
- }
- void Installer::doInstall() {
- // wait for valid time
- while (!time_valid()) {
- usleep(500 * 1000);
- qDebug() << "Waiting for valid time";
- }
- // cleanup previous install attempts
- run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH);
- // do the install
- if (QDir(CACHE_PATH).exists()) {
- cachedFetch(CACHE_PATH);
- } else {
- freshClone();
- }
- }
- void Installer::freshClone() {
- qDebug() << "Doing fresh clone";
- proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(),
- "--depth=1", "--recurse-submodules", TMP_INSTALL_PATH});
- }
- void Installer::cachedFetch(const QString &cache) {
- qDebug() << "Fetching with cache: " << cache;
- run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str());
- int err = chdir(TMP_INSTALL_PATH);
- assert(err == 0);
- run(("git remote set-branches --add origin " + BRANCH_STR).c_str());
- updateProgress(10);
- proc.setWorkingDirectory(TMP_INSTALL_PATH);
- proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()});
- }
- void Installer::readProgress() {
- const QVector<QPair<QString, int>> stages = {
- // prefix, weight in percentage
- {tr("Receiving objects: "), 91},
- {tr("Resolving deltas: "), 2},
- {tr("Updating files: "), 7},
- };
- auto line = QString(proc.readAllStandardError());
- int base = 0;
- for (const QPair kv : stages) {
- if (line.startsWith(kv.first)) {
- auto perc = line.split(kv.first)[1].split("%")[0];
- int p = base + int(perc.toFloat() / 100. * kv.second);
- updateProgress(p);
- break;
- }
- base += kv.second;
- }
- }
- void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) {
- qDebug() << "git finished with " << exitCode;
- assert(exitCode == 0);
- updateProgress(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/");
- std::map<std::string, std::string> 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
- QTimer::singleShot(60 * 1000, &QCoreApplication::quit);
- }
- int main(int argc, char *argv[]) {
- initApp(argc, argv);
- QApplication a(argc, argv);
- Installer installer;
- setMainWindow(&installer);
- return a.exec();
- }
|