/* bundle_name: this is the name for the bundle we want to be loaded * version: this is the MoM version from which we pull last changed for bundle manifest * submanifest: where bundle manifest is going to be loaded * * Basically we read MoM version then get the submanifest only for our bundle (component) * put it into submanifest pointer, then dispose MoM data. */ static int load_bundle_manifest(const char *bundle_name, int version, struct manifest **submanifest) { struct list *sub_list = NULL; struct manifest *mom = NULL; int ret = 0; *submanifest = NULL; swupd_curl_set_current_version(version); mom = load_mom(version); if (!mom) { ret = EMOM_NOTFOUND; goto out; } sub_list = recurse_manifest(mom, bundle_name); if (!sub_list) { ret = ERECURSE_MANIFEST; goto free_out; } *submanifest = sub_list->data; sub_list->data = NULL; ret = 0; free_out: free_manifest(mom); out: return ret; }
/* bundle_name: this is the name for the bundle we want to be loaded * version: this is the MoM version from which we pull last changed for bundle manifest * submanifest: where bundle manifest is going to be loaded * * Basically we read MoM version then get the submanifest only for our bundle (component) * put it into submanifest pointer, then dispose MoM data. */ static int load_bundle_manifest(const char *bundle_name, int version, struct manifest **submanifest) { struct list *sub_list = NULL; struct manifest *mom = NULL; int ret = 0; *submanifest = NULL; swupd_curl_set_current_version(version); ret = load_manifests(version, version, "MoM", NULL, &mom); if (ret != 0) { ret = EMOM_NOTFOUND; goto out; } ret = recurse_manifest(mom, bundle_name); if (ret != 0) { ret = ERECURSE_MANIFEST; goto free_out; } sub_list = list_head(mom->submanifests); if (sub_list != NULL) { *submanifest = sub_list->data; sub_list->data = NULL; ret = 0; } free_out: free_manifest(mom); out: return ret; }
int check_versions(int *current_version, int *server_version, char *path_prefix) { read_versions(current_version, server_version, path_prefix); if (*current_version < 0) { fprintf(stderr, "Error: Unable to determine current OS version\n"); return -1; } if (*current_version == 0) { fprintf(stderr, "Update from version 0 not supported yet.\n"); return -1; } if (SWUPD_VERSION_IS_DEVEL(*current_version) || SWUPD_VERSION_IS_RESVD(*current_version)) { fprintf(stderr, "Update of dev build not supported %d\n", *current_version); return -1; } swupd_curl_set_current_version(*current_version); /* set preferred version and content server urls */ if (*current_version < 0) { have_network = false; return -1; } have_network = true; //TODO allow policy layer to send us to intermediate version? swupd_curl_set_requested_version(*server_version); return 0; }
/* * list_installable_bundles() * Parse the full manifest for the current version of the OS and print * all available bundles. */ int list_installable_bundles() { struct list *list; struct file *file; struct manifest *MoM = NULL; int current_version; int lock_fd; int ret; if (!init_globals()) { return EINIT_GLOBALS; } current_version = read_version_from_subvol_file(path_prefix); if (swupd_init(&lock_fd) != 0) { printf("Error: Failed updater initialization. Exiting now\n"); return ECURL_INIT; } ret = create_required_dirs(); if (ret != 0) { printf("State directory %s cannot be recreated, aborting removal\n", STATE_DIR); v_lockfile(lock_fd); return EXIT_FAILURE; } get_mounted_directories(); if (!check_network()) { printf("Error: Network issue, unable to download manifest\n"); v_lockfile(lock_fd); return EXIT_FAILURE; } swupd_curl_set_current_version(current_version); ret = load_manifests(current_version, current_version, "MoM", NULL, &MoM); if (ret != 0) { v_lockfile(lock_fd); return ret; } list = MoM->manifests; while (list) { file = list->data; list = list->next; printf("%s\n", file->filename); } free_manifest(MoM); v_lockfile(lock_fd); return 0; }
/* * list_installable_bundles() * Parse the full manifest for the current version of the OS and print * all available bundles. */ int list_installable_bundles() { struct list *list; struct file *file; struct manifest *MoM = NULL; int current_version; int lock_fd; int ret; ret = swupd_init(&lock_fd); if (ret != 0) { printf("Error: Failed updater initialization. Exiting now\n"); return ret; } if (!check_network()) { printf("Error: Network issue, unable to download manifest\n"); v_lockfile(lock_fd); return EXIT_FAILURE; } current_version = get_current_version(path_prefix); if (current_version < 0) { printf("Error: Unable to determine current OS version\n"); v_lockfile(lock_fd); return ECURRENT_VERSION; } swupd_curl_set_current_version(current_version); MoM = load_mom(current_version); if (!MoM) { v_lockfile(lock_fd); return ret; } list = MoM->manifests; while (list) { file = list->data; list = list->next; printf("%s\n", file->filename); } free_manifest(MoM); v_lockfile(lock_fd); return 0; }
/* Bundle install one ore more bundles passed in bundles * param as a null terminated array of strings */ int install_bundles_frontend(char **bundles) { int lock_fd; int ret = 0; int current_version; struct list *bundles_list = NULL; struct manifest *mom; /* initialize swupd and get current version from OS */ ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } current_version = get_current_version(path_prefix); if (current_version < 0) { printf("Error: Unable to determine current OS version\n"); ret = ECURRENT_VERSION; goto clean_and_exit; } swupd_curl_set_current_version(current_version); mom = load_mom(current_version); if (!mom) { printf("Cannot load official manifest MoM for version %i\n", current_version); ret = EMOM_NOTFOUND; goto clean_and_exit; } for (; *bundles; ++bundles) { bundles_list = list_prepend_data(bundles_list, *bundles); } ret = install_bundles(bundles_list, current_version, mom); list_free_list(bundles_list); free_manifest(mom); clean_and_exit: swupd_deinit(lock_fd); return ret; }
/* * list_installable_bundles() * Parse the full manifest for the current version of the OS and print * all available bundles. */ int list_installable_bundles() { struct list *list; struct file *file; struct manifest *MoM = NULL; int current_version; int lock_fd; int ret; ret = swupd_init(&lock_fd); if (ret != 0) { printf("Error: Failed updater initialization. Exiting now\n"); return ret; } if (!check_network()) { printf("Error: Network issue, unable to download manifest\n"); v_lockfile(lock_fd); return EXIT_FAILURE; } current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); ret = load_manifests(current_version, current_version, "MoM", NULL, &MoM); if (ret != 0) { v_lockfile(lock_fd); return ret; } list = MoM->manifests; while (list) { file = list->data; list = list->next; printf("%s\n", file->filename); } free_manifest(MoM); v_lockfile(lock_fd); return 0; }
/* Bundle install one ore more bundles passed in bundles * param as a null terminated array of strings */ int install_bundles_frontend(char **bundles) { int lock_fd; int ret = 0; int current_version; struct list *bundles_list = NULL; struct manifest *mom; /* initialize swupd and get current version from OS */ ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); ret = load_manifests(current_version, current_version, "MoM", NULL, &mom); if (ret != 0) { printf("Cannot load official manifest MoM for version %i\n", current_version); ret = EMOM_NOTFOUND; goto clean_and_exit; } for (; *bundles; ++bundles) { bundles_list = list_prepend_data(bundles_list, *bundles); } ret = install_bundles(bundles_list, current_version, mom); list_free_list(bundles_list); free_manifest(mom); clean_and_exit: swupd_curl_cleanup(); v_lockfile(lock_fd); dump_file_descriptor_leaks(); free_globals(); return ret; }
/* download_manifests() * Description: To search Clear bundles for a particular entry, a complete set of * manifests must be downloaded. This function does so, asynchronously, using * the curl_multi interface */ int download_manifests(struct manifest **MoM) { struct list *list; struct file *file; char *tarfile, *untard_file, *url; struct manifest *subMan = NULL; int current_version; int ret = 0; double size; current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); ret = load_manifests(current_version, current_version, "MoM", NULL, MoM); if (ret != 0) { printf("Cannot load official manifest MoM for version %i\n", current_version); return ret; } subscription_versions_from_MoM(*MoM, 0); list = (*MoM)->manifests; size = query_total_download_size(list); if (size == -1) { printf("Downloading manifests. Expect a delay, up to 100MB may be downloaded\n"); } else if (size > 0) { printf("Downloading Clear Linux manifests\n"); printf(" %.2f MB total...\n\n", size); } while (list) { file = list->data; list = list->next; create_and_append_subscription(file->filename); string_or_die(&untard_file, "%s/%i/Manifest.%s", STATE_DIR, file->last_change, file->filename); string_or_die(&tarfile, "%s/%i/Manifest.%s.tar", STATE_DIR, file->last_change, file->filename); if (access(untard_file, F_OK) == -1) { /* Do download */ printf(" '%s' manifest...\n", file->filename); ret = load_manifests(current_version, file->last_change, file->filename, NULL, &subMan); if (ret != 0) { printf("Cannot load official manifest MoM for version %i\n", current_version); } free_manifest(subMan); } if (access(untard_file, F_OK) == -1) { string_or_die(&url, "%s/%i/Manifest.%s.tar", preferred_content_url, current_version, file->filename); printf("Error: Failure reading from %s\n", url); free(url); } unlink(tarfile); free(untard_file); free(tarfile); } return ret; }
/* Bundle install one ore more bundles passed in bundles * param as a null terminated array of strings */ int install_bundles(char **bundles) { int lock_fd; int ret = 0; int current_version; struct manifest *mom; struct list *iter; struct sub *sub; struct file *file; /* step 1: initialize swupd and get current version from OS */ if (!init_globals()) { return EINIT_GLOBALS; } ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); /* first of all, make sure STATE_DIR is there, recreate if necessary*/ ret = create_required_dirs(); if (ret != 0) { printf("State directory %s cannot be recreated, aborting installation\n", STATE_DIR); goto clean_and_exit; } ret = load_manifests(current_version, current_version, "MoM", NULL, &mom); if (ret != 0) { printf("Cannot load official manifest MoM for version %i\n", current_version); ret = EMOM_NOTFOUND; goto clean_and_exit; } /* step 2: check bundle args are valid if so populate subs struct */ int i; for (i = 0; *bundles; ++bundles) { if (is_tracked_bundle(*bundles)) { printf("%s bundle already installed, skipping it\n", *bundles); continue; } if (!manifest_has_component(mom, *bundles)) { printf("%s bundle name is invalid, skipping it...\n", *bundles); continue; } if (component_subscribed(*bundles)) { continue; } create_and_append_subscription(*bundles); i++; printf("Added bundle %s for installation\n", *bundles); } if (i == 0) { printf("There are no pending bundles to install, exiting now\n"); ret = EBUNDLE_INSTALL; goto clean_manifest_and_exit; } subscription_versions_from_MoM(mom, 0); recurse_manifest(mom, NULL); consolidate_submanifests(mom); /* step 3: download neccessary packs */ ret = rm_staging_dir_contents("download"); printf("Downloading required packs...\n"); ret = download_subscribed_packs(0, current_version, true); if (ret != 0) { printf("pack downloads failed, cannot proceed with the installation, exiting.\n"); goto clean_subs_and_exit; } /* step 4: Install all bundle(s) files into the fs */ printf("Installing bundle(s) files...\n"); iter = list_head(mom->files); while (iter) { file = iter->data; iter = iter->next; if (file->is_deleted || file->do_not_update || ignore(file)) { continue; } ret = do_staging(file); if (ret == 0) { rename_staged_file_to_final(file); } } sync(); /* step 5: create bundle(s) subscription entries to track them * * Strictly speaking each manifest has an entry to write its own bundle filename * and thus tracking automagically, here just making sure. */ iter = list_head(subs); while (iter) { sub = iter->data; iter = iter->next; printf("Tracking %s bundle on the system\n", sub->component); ret = track_bundle_in_system(sub->component); if (ret != 0) { printf("Cannot track %s bundle on the system\n", sub->component); } } /* Run any scripts that are needed to complete update */ run_scripts(); ret = 0; printf("Bundle(s) installation done.\n"); clean_subs_and_exit: free_subscriptions(); clean_manifest_and_exit: free_manifest(mom); clean_and_exit: swupd_curl_cleanup(); v_lockfile(lock_fd); dump_file_descriptor_leaks(); free_globals(); return ret; }
/* This function is a fresh new implementation for a bundle * remove without being tied to verify loop, this means * improved speed and space as well as more roubustness and * flexibility. What it does is basically: * * 1) Read MoM and load all submanifests except the one to be * removed and then consolidate them. * 2) Load the removed bundle submanifest. * 3) Order the file list by filename * 4) Deduplicate removed submanifest file list that happens * to be on the MoM (minus bundle to be removed). * 5) iterate over to be removed bundle submanifest file list * performing a unlink(2) for each filename. * 6) Done. */ int remove_bundle(const char *bundle_name) { int lock_fd; int ret = 0; int current_version = CURRENT_OS_VERSION; struct manifest *current_mom, *bundle_manifest; /* Initially we don't support format nor path_prefix * for bundle_rm but eventually that will be added, then * set_format_string() and init_globals() must be pulled out * to the caller to properly initialize in case those opts * passed to the command. */ set_format_string(NULL); if (!init_globals()) { return EINIT_GLOBALS; } ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } /* os-core bundle not allowed to be removed... * although this is going to be caught later because of all files * being marked as 'duplicated' and note removing anything * anyways, better catch here and return success, no extra work to be done. */ if (strcmp(bundle_name, "os-core") == 0) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } if (!is_tracked_bundle(bundle_name)) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); /* first of all, make sure STATE_DIR is there, recreate if necessary*/ ret = create_required_dirs(); if (ret != 0) { printf("State directory %s cannot be recreated, aborting removal\n", STATE_DIR); goto out_free_curl; } ret = load_manifests(current_version, current_version, "MoM", NULL, ¤t_mom); if (ret != 0) { goto out_free_curl; } /* load all tracked bundles into memory */ read_subscriptions_alt(); /* now popout the one to be removed */ ret = unload_tracked_bundle(bundle_name); if (ret != 0) { goto out_free_mom; } subscription_versions_from_MoM(current_mom, 0); /* load all submanifest minus the one to be removed */ recurse_manifest(current_mom, NULL); consolidate_submanifests(current_mom); /* Now that we have the consolidated list of all files, load bundle to be removed submanifest*/ ret = load_bundle_manifest(bundle_name, current_version, &bundle_manifest); if (ret != 0) { goto out_free_mom; } /* deduplication needs file list sorted by filename, do so */ bundle_manifest->files = list_sort(bundle_manifest->files, file_sort_filename); deduplicate_files_from_manifest(&bundle_manifest, current_mom); printf("Deleting bundle files...\n"); remove_files_in_manifest_from_fs(bundle_manifest); printf("Untracking bundle from system...\n"); rm_bundle_file(bundle_name); printf("Success: Bundle removed\n"); free_manifest(bundle_manifest); out_free_mom: free_manifest(current_mom); out_free_curl: if (ret) { printf("Error: Bundle remove failed\n"); } swupd_curl_cleanup(); v_lockfile(lock_fd); dump_file_descriptor_leaks(); return ret; }
/* This function is a fresh new implementation for a bundle * remove without being tied to verify loop, this means * improved speed and space as well as more roubustness and * flexibility. What it does is basically: * * 1) Read MoM and load all submanifests except the one to be * removed and then consolidate them. * 2) Load the removed bundle submanifest. * 3) Order the file list by filename * 4) Deduplicate removed submanifest file list that happens * to be on the MoM (minus bundle to be removed). * 5) iterate over to be removed bundle submanifest file list * performing a unlink(2) for each filename. * 6) Done. */ int remove_bundle(const char *bundle_name) { int lock_fd; int ret = 0; int current_version = CURRENT_OS_VERSION; struct manifest *current_mom, *bundle_manifest = NULL; ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } /* os-core bundle not allowed to be removed... * although this is going to be caught later because of all files * being marked as 'duplicated' and note removing anything * anyways, better catch here and return success, no extra work to be done. */ if (strcmp(bundle_name, "os-core") == 0) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } if (!is_tracked_bundle(bundle_name)) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } current_version = get_current_version(path_prefix); if (current_version < 0) { printf("Error: Unable to determine current OS version\n"); ret = ECURRENT_VERSION; goto out_free_curl; } swupd_curl_set_current_version(current_version); current_mom = load_mom(current_version); if (!current_mom) { printf("Unable to download/verify %d Manifest.MoM\n", current_version); ret = EMOM_NOTFOUND; goto out_free_curl; } if (!search_bundle_in_manifest(current_mom, bundle_name)) { printf("Bundle name is invalid, aborting removal\n"); ret = EBUNDLE_REMOVE; goto out_free_mom; } /* load all tracked bundles into memory */ read_subscriptions_alt(); /* now popout the one to be removed */ ret = unload_tracked_bundle(bundle_name); if (ret != 0) { goto out_free_mom; } subscription_versions_from_MoM(current_mom, 0); /* load all submanifest minus the one to be removed */ current_mom->submanifests = recurse_manifest(current_mom, NULL); if (!current_mom->submanifests) { printf("Error: Cannot load MoM sub-manifests\n"); ret = ERECURSE_MANIFEST; goto out_free_mom; } if (is_included(bundle_name, current_mom)) { printf("Error: bundle requested to be removed is required by other installed bundles\n"); ret = EBUNDLE_REMOVE; goto out_free_mom; } current_mom->files = files_from_bundles(current_mom->submanifests); current_mom->files = consolidate_files(current_mom->files); /* Now that we have the consolidated list of all files, load bundle to be removed submanifest*/ ret = load_bundle_manifest(bundle_name, current_version, &bundle_manifest); if (ret != 0 || !bundle_manifest) { printf("Error: Cannot load %s sub-manifest (ret = %d)\n", bundle_name, ret); goto out_free_mom; } /* deduplication needs file list sorted by filename, do so */ bundle_manifest->files = list_sort(bundle_manifest->files, file_sort_filename); deduplicate_files_from_manifest(&bundle_manifest, current_mom); printf("Deleting bundle files...\n"); remove_files_in_manifest_from_fs(bundle_manifest); printf("Untracking bundle from system...\n"); rm_bundle_file(bundle_name); printf("Success: Bundle removed\n"); free_manifest(bundle_manifest); out_free_mom: free_manifest(current_mom); out_free_curl: if (ret) { printf("Error: Bundle remove failed\n"); } swupd_deinit(lock_fd); return ret; }
/* download_manifests() * Description: To search Clear bundles for a particular entry, a complete set of * manifests must be downloaded. This function does so, asynchronously, using * the curl_multi interface */ static int download_manifests(struct manifest **MoM) { struct list *list = NULL; struct file *file = NULL; char *tarfile, *untard_file, *url = NULL; struct manifest *subMan = NULL; int current_version; int ret = 0; double size; bool did_download = false; current_version = get_current_version(path_prefix); if (current_version < 0) { printf("Error: Unable to determine current OS version\n"); return ECURRENT_VERSION; } swupd_curl_set_current_version(current_version); *MoM = load_mom(current_version); if (!(*MoM)) { printf("Cannot load official manifest MoM for version %i\n", current_version); return EMOM_NOTFOUND; } subscription_versions_from_MoM(*MoM, 0); list = (*MoM)->manifests; size = query_total_download_size(list); if (size == -1) { printf("Downloading manifests. Expect a delay, up to 100MB may be downloaded\n"); } else if (size > 0) { printf("Downloading Clear Linux manifests\n"); printf(" %.2f MB total...\n\n", size); } while (list) { file = list->data; list = list->next; create_and_append_subscription(file->filename); string_or_die(&untard_file, "%s/%i/Manifest.%s", state_dir, file->last_change, file->filename); string_or_die(&tarfile, "%s/%i/Manifest.%s.tar", state_dir, file->last_change, file->filename); if (access(untard_file, F_OK) == -1) { /* Do download */ printf(" '%s' manifest...\n", file->filename); subMan = load_manifest(current_version, file->last_change, file, *MoM); if (!subMan) { printf("Cannot load official manifest MoM for version %i\n", current_version); } else { did_download = true; } free_manifest(subMan); } if (access(untard_file, F_OK) == -1) { string_or_die(&url, "%s/%i/Manifest.%s.tar", content_url, current_version, file->filename); printf("Error: Failure reading from %s\n", url); free(url); } unlink(tarfile); free(untard_file); free(tarfile); } if (did_download) { printf("Completed manifests download.\n\n"); } return ret; }