static int get_missing_files(struct manifest *official_manifest) { int ret; struct list *failed = NULL; int retries = 0; /* We only want to go through the download loop once */ int timeout = 10; /* Amount of seconds for first download retry */ RETRY_DOWNLOADS: /* when fixing (not installing): queue download and mark any files * which are already verified OK */ ret = start_full_download(true); if (ret != 0) { /* If we hit this point, the network is accessible but we were * unable to download the needed files. This is a terminal error * and we need good logging */ printf("Error: Unable to download neccessary files for this OS release\n"); return -EFULLDOWNLOAD; } if (failed != NULL) { failed = download_loop(failed, 1); } else { failed = download_loop(official_manifest->files, 0); } /* Set retries only if failed downloads exist, and only retry a fixed amount of times */ if (list_head(failed) != NULL && retries < MAX_TRIES) { increment_retries(&retries, &timeout); printf("Starting download retry #%d\n", retries); clean_curl_multi_queue(); goto RETRY_DOWNLOADS; } if (retries >= MAX_TRIES) { printf("ERROR: Could not download all files, aborting update\n"); list_free_list(failed); return -EFULLDOWNLOAD; } return 0; }
/* tristate return, -1 for errors, 1 for no errors but no new subscriptions, 0 for no errors and new subscriptions */ int add_subscriptions(struct list *bundles, int current_version, struct manifest *mom) { bool new_bundles = false; char *bundle; int ret; int retries = 0; int timeout = 10; struct file *file; struct list *iter; struct manifest *manifest; srand(time(NULL)); iter = list_head(bundles); while (iter) { bundle = iter->data; iter = iter->next; file = search_bundle_in_manifest(mom, bundle); if (!file) { printf("%s bundle name is invalid, skipping it...\n", bundle); continue; } retry_manifest_download: manifest = load_manifest(current_version, file->last_change, file, mom); if (!manifest) { if (retries < MAX_TRIES) { increment_retries(&retries, &timeout); goto retry_manifest_download; } printf("Unable to download manifest %s version %d, exiting now\n", bundle, file->last_change); ret = -1; goto out; } if (!manifest) { printf("Unable to load manifest %s version %d, exiting now\n", bundle, file->last_change); ret = -1; goto out; } if (manifest->includes) { ret = add_subscriptions(manifest->includes, current_version, mom); if (ret == -1) { free_manifest(manifest); goto out; } else if (ret == 0) { new_bundles = true; } } free_manifest(manifest); if (is_tracked_bundle(bundle)) { continue; } if (component_subscribed(bundle)) { continue; } create_and_append_subscription(bundle); new_bundles = true; } if (new_bundles) { ret = 0; } else { ret = 1; } out: return ret; }
static int update_loop(struct list *updates, struct manifest *server_manifest) { int ret; struct file *file; struct list *iter; struct list *failed = NULL; int err; int retries = 0; /* We only want to go through the download loop once */ int timeout = 10; /* Amount of seconds for first download retry */ TRY_DOWNLOAD: err = start_full_download(true); if (err != 0) { return err; } if (failed != NULL) { try_delta_loop(failed); failed = full_download_loop(failed, 1); } else { try_delta_loop(updates); failed = full_download_loop(updates, 0); } #if 0 if (rm_staging_dir_contents("download")) { return -1; } #endif /* Set retries only if failed downloads exist, and only retry a fixed amount of times */ if (list_head(failed) != NULL && retries < MAX_TRIES) { increment_retries(&retries, &timeout); printf("Starting download retry #%d\n", retries); clean_curl_multi_queue(); goto TRY_DOWNLOAD; } if (retries >= MAX_TRIES) { printf("ERROR: Could not download all files, aborting update\n"); list_free_list(failed); return -1; } if (download_only) { return -1; } /*********** rootfs critical section starts *************************** NOTE: the next loop calls do_staging() which can remove files, starting a critical section which ends after rename_all_files_to_final() succeeds */ /* from here onward we're doing real update work modifying "the disk" */ /* starting at list_head in the filename alpha-sorted updates list * means node directories are added before leaf files */ printf("Staging file content\n"); iter = list_head(updates); while (iter) { file = iter->data; iter = iter->next; if (file->do_not_update || file->is_deleted) { continue; } /* for each file: fdatasync to persist changed content over reboot, or maybe a global sync */ /* for each file: check hash value; on mismatch delete and queue full download */ /* todo: hash check */ ret = do_staging(file, server_manifest); if (ret < 0) { printf("File staging failed: %s\n", file->filename); return ret; } } /* check policy, and if policy says, "ask", ask the user at this point */ /* check for reboot need - if needed, wait for reboot */ /* sync */ sync(); /* rename to apply update */ ret = rename_all_files_to_final(updates); if (ret != 0) { return ret; } /* TODO: do we need to optimize directory-permission-only changes (directories * are now sent as tar's so permissions are handled correctly, even * if less than efficiently)? */ sync(); /* NOTE: critical section starts when update_loop() calls do_staging() */ /*********** critical section ends *************************************/ return ret; }
int main_update() { int current_version = -1, server_version = -1; struct manifest *current_manifest = NULL, *server_manifest = NULL; struct list *updates = NULL; int ret; int lock_fd; int retries = 0; int timeout = 10; srand(time(NULL)); ret = swupd_init(&lock_fd); if (ret != 0) { /* being here means we already close log by a previously caught error */ printf("Updater failed to initialize, exiting now.\n"); return ret; } if (!check_network()) { printf("Error: Network issue, unable to proceed with update\n"); ret = EXIT_FAILURE; goto clean_curl; } printf("Update started.\n"); read_subscriptions_alt(); if (!signature_initialize(UPDATE_CA_CERTS_PATH "/" SIGNATURE_CA_CERT)) { goto clean_curl; } /* Step 1: get versions */ ret = check_versions(¤t_version, &server_version, path_prefix); if (ret < 0) { goto clean_curl; } if (server_version <= current_version) { printf("Version on server (%i) is not newer than system version (%i)\n", server_version, current_version); ret = EXIT_SUCCESS; goto clean_curl; } printf("Preparing to update from %i to %i\n", current_version, server_version); /* Step 2: housekeeping */ if (rm_staging_dir_contents("download")) { goto clean_curl; } load_current_manifests: /* Step 3: setup manifests */ /* get the from/to MoM manifests */ printf("Querying current manifest.\n"); ret = load_manifests(current_version, current_version, "MoM", NULL, ¤t_manifest); if (ret) { /* TODO: possibly remove this as not getting a "from" manifest is not fatal * - we just don't apply deltas */ if (retries < MAX_TRIES) { increment_retries(&retries, &timeout); printf("Retry #%d downloading from/to MoM Manifests\n", retries); goto load_current_manifests; } printf("Failure retrieving manifest from server\n"); goto clean_exit; } /* Reset the retries and timeout for subsequent download calls */ retries = 0; timeout = 10; load_server_manifests: printf("Querying server manifest.\n"); ret = load_manifests(current_version, server_version, "MoM", NULL, &server_manifest); if (ret) { if (retries < MAX_TRIES) { increment_retries(&retries, &timeout); printf("Retry #%d downloading server Manifests\n", retries); goto load_server_manifests; } printf("Failure retrieving manifest from server\n"); goto clean_exit; } if (current_manifest == NULL || server_manifest == NULL) { printf("Unable to load manifest after retrying (config or network problem?)\n"); goto clean_exit; } retries = 0; timeout = 10; ret = add_included_manifests(server_manifest); if (ret) { goto clean_exit; } subscription_versions_from_MoM(current_manifest, 1); subscription_versions_from_MoM(server_manifest, 0); link_submanifests(current_manifest, server_manifest); /* updating subscribed manifests is done as part of recurse_manifest */ /* read the current collective of manifests that we are subscribed to */ current_manifest->submanifests = recurse_manifest(current_manifest, NULL); if (!current_manifest->submanifests) { printf("Cannot load current MoM sub-manifests, (%s), exiting\n", strerror(errno)); goto clean_exit; } /* consolidate the current collective manifests down into one in memory */ current_manifest->files = files_from_bundles(current_manifest->submanifests); current_manifest->files = consolidate_files(current_manifest->files); /* read the new collective of manifests that we are subscribed to */ server_manifest->submanifests = recurse_manifest(server_manifest, NULL); if (!server_manifest->submanifests) { printf("Error: Cannot load server MoM sub-manifests, (%s), exiting\n", strerror(errno)); goto clean_exit; } /* consolidate the new collective manifests down into one in memory */ server_manifest->files = files_from_bundles(server_manifest->submanifests); server_manifest->files = consolidate_files(server_manifest->files); /* prepare for an update process based on comparing two in memory manifests */ link_manifests(current_manifest, server_manifest); #if 0 debug_write_manifest(current_manifest, "debug_manifest_current.txt"); debug_write_manifest(server_manifest, "debug_manifest_server.txt"); #endif /* Step 4: check disk state before attempting update */ run_preupdate_scripts(server_manifest); download_packs: /* Step 5: get the packs and untar */ ret = download_subscribed_packs(false); if (ret) { // packs don't always exist, tolerate that but not ENONET if (retries < MAX_TRIES) { increment_retries(&retries, &timeout); printf("Retry #%d downloading packs\n", retries); goto download_packs; } printf("No network, or server unavailable for pack downloads\n"); goto clean_exit; } /* Step 6: some more housekeeping */ /* TODO: consider trying to do less sorting of manifests */ updates = create_update_list(current_manifest, server_manifest); link_renames(updates, current_manifest); /* TODO: Have special lists for candidate and renames */ print_statistics(current_version, server_version); /* Step 7: apply the update */ ret = update_loop(updates, server_manifest); if (ret == 0) { ret = update_device_latest_version(server_version); printf("Update was applied.\n"); } delete_motd(); /* Run any scripts that are needed to complete update */ run_scripts(); clean_exit: list_free_list(updates); free_manifest(current_manifest); free_manifest(server_manifest); clean_curl: signature_terminate(); swupd_curl_cleanup(); free_subscriptions(); free_globals(); v_lockfile(lock_fd); dump_file_descriptor_leaks(); if ((current_version < server_version) && (ret == 0)) { printf("Update successful. System updated from version %d to version %d\n", current_version, server_version); } else if (ret == 0) { printf("Update complete. System already up-to-date at version %d\n", current_version); } return ret; }