| 102 | } |
| 103 | |
| 104 | bool install(const std::string& path) { |
| 105 | // We lock and unlock frequently because SPI SD card devices share |
| 106 | // the lock with the display. We don't want to lock the display for very long. |
| 107 | |
| 108 | auto app_parent_path = getAppInstallPath(); |
| 109 | LOGGER.info("Installing app {} to {}", path, app_parent_path); |
| 110 | |
| 111 | auto filename = file::getLastPathSegment(path); |
| 112 | const std::string app_target_path = std::format("{}/{}", app_parent_path, filename); |
| 113 | if (file::isDirectory(app_target_path) && !file::deleteRecursively(app_target_path)) { |
| 114 | LOGGER.warn("Failed to delete {}", app_target_path); |
| 115 | } |
| 116 | |
| 117 | if (!file::findOrCreateDirectory(app_target_path, 0777)) { |
| 118 | LOGGER.info("Failed to create directory {}", app_target_path); |
| 119 | return false; |
| 120 | } |
| 121 | |
| 122 | auto target_path_lock = file::getLock(app_parent_path)->asScopedLock(); |
| 123 | auto source_path_lock = file::getLock(path)->asScopedLock(); |
| 124 | target_path_lock.lock(); |
| 125 | source_path_lock.lock(); |
| 126 | LOGGER.info("Extracting app from {} to {}", path, app_target_path); |
| 127 | if (!untar(path, app_target_path)) { |
| 128 | LOGGER.error("Failed to extract"); |
| 129 | return false; |
| 130 | } |
| 131 | source_path_lock.unlock(); |
| 132 | target_path_lock.unlock(); |
| 133 | |
| 134 | auto manifest_path = app_target_path + "/manifest.properties"; |
| 135 | if (!file::isFile(manifest_path)) { |
| 136 | LOGGER.error("Manifest not found at {}", manifest_path); |
| 137 | cleanupInstallDirectory(app_target_path); |
| 138 | return false; |
| 139 | } |
| 140 | |
| 141 | std::map<std::string, std::string> properties; |
| 142 | if (!file::loadPropertiesFile(manifest_path, properties)) { |
| 143 | LOGGER.error("Failed to load manifest at {}", manifest_path); |
| 144 | cleanupInstallDirectory(app_target_path); |
| 145 | return false; |
| 146 | } |
| 147 | |
| 148 | AppManifest manifest; |
| 149 | if (!parseManifest(properties, manifest)) { |
| 150 | LOGGER.warn("Invalid manifest"); |
| 151 | cleanupInstallDirectory(app_target_path); |
| 152 | return false; |
| 153 | } |
| 154 | |
| 155 | // If the app was already running, then stop it |
| 156 | if (isRunning(manifest.appId)) { |
| 157 | stopAll(manifest.appId); |
| 158 | } |
| 159 | |
| 160 | const std::string renamed_target_path = std::format("{}/{}", app_parent_path, manifest.appId); |
| 161 | if (file::isDirectory(renamed_target_path)) { |
no test coverage detected