예제 #1
0
void critical_verify_error(int version)
{
	struct list *version_list = NULL;

	version_list = list_prepend_data(version_list, (void *)((intptr_t)version));
	efivar_bootloader_set_next_boot_to_repair(repair_verify_failure,
						  version_list);
	list_free_list(version_list);
}
예제 #2
0
파일: list.c 프로젝트: dougfales/macvim
    void
list_free(list_T *l)
{
    if (!in_free_unref_items)
    {
	list_free_contents(l);
	list_free_list(l);
    }
}
예제 #3
0
void fatal_update_error(int from_version, int to_version)
{
	struct list *version_list = NULL;

	version_list = list_prepend_data(version_list, (void *)((intptr_t)to_version));
	version_list = list_prepend_data(version_list, (void *)((intptr_t)from_version));
	efivar_bootloader_set_next_boot_to_repair(repair_update_failure,
						  version_list);
	list_free_list(version_list);
}
예제 #4
0
static struct list *download_loop(struct list *files, int isfailed)
{
	int ret;
	struct file local;
	struct list *iter;

	iter = list_head(files);
	while (iter) {
		struct file *file;
		char *fullname;

		file = iter->data;
		iter = iter->next;

		if (file->is_deleted) {
			continue;
		}

		fullname = mk_full_filename(path_prefix, file->filename);
		if (fullname == NULL) {
			abort();
		}

		memset(&local, 0, sizeof(struct file));
		local.filename = file->filename;
		populate_file_struct(&local, fullname);

		if (cmdline_option_quick) {
			ret = compute_hash_lazy(&local, fullname);
		} else {
			ret = compute_hash(&local, fullname);
		}
		if (ret != 0) {
			free(fullname);
			continue;
		}

		if (hash_needs_work(file, local.hash)) {
			full_download(file);
		} else {
			/* mark the file as good to save time later */
			file->do_not_update = 1;
		}
		free(fullname);
	}

	if (isfailed) {
		list_free_list(files);
	}

	return end_full_download();
}
예제 #5
0
파일: list.c 프로젝트: dougfales/macvim
    void
list_free_items(int copyID)
{
    list_T	*ll, *ll_next;

    for (ll = first_list; ll != NULL; ll = ll_next)
    {
	ll_next = ll->lv_used_next;
	if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
						      && ll->lv_watch == NULL)
	{
	    /* Free the List and ordinary items it contains, but don't recurse
	     * into Lists and Dictionaries, they will be in the list of dicts
	     * or list of lists. */
	    list_free_list(ll);
	}
    }
}
예제 #6
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;
}
예제 #7
0
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;
}
예제 #8
0
파일: bundle.c 프로젝트: pohly/swupd-client
/* 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;
}
예제 #9
0
int add_included_manifests(struct manifest *mom)
{
	struct list *subbed = NULL;
	struct list *iter;
	int ret;

	iter = list_head(subs);
	while (iter) {
		subbed = list_prepend_data(subbed, ((struct sub *)iter->data)->component);
		iter = iter->next;
	}

	if (add_subscriptions(subbed, mom->version, mom) >= 0) {
		ret = 0;
	} else {
		ret = -1;
	}
	list_free_list(subbed);

	return ret;
}
static struct list *parse_version_list(char *csv_version_str)
{
	char *token;
	int version;
	struct list *list = NULL;

	if (!csv_version_str)
		return NULL;

	token = strtok(csv_version_str, ",");
	while (token != NULL) {
		if (sscanf(token, "%i", &version) != 1) {
			list_free_list(list);
			return NULL;
		}
		list = list_prepend_data(list, (void *)((intptr_t)version));
		token = strtok(NULL, ",");
	}

	return list;
}
static int do_update_bootloader_pref(void)
{
	int result = 0;

	if (boot_prior) {
		try_log_first_boot();
		result = efivar_bootloader_boot_check(current_version, false);
	} else if (boot_repair) {
		result = efivar_bootloader_boot_check(current_version, true);
	} else if (next_version != -1) {
		result = efivar_bootloader_set_next_boot_to_version(next_version);
	} else if (next_repair) {
		result = efivar_bootloader_set_next_boot_to_repair(repair_reason,
								   next_repair);
		list_free_list(next_repair);
	}

	if (result < 0)
		return EXIT_FAILURE;

	return EXIT_SUCCESS;
}
예제 #12
0
static struct list *full_download_loop(struct list *updates, int isfailed)
{
	struct list *iter;
	struct file *file;

	iter = list_head(updates);
	while (iter) {
		file = iter->data;
		iter = iter->next;

		if (file->is_deleted) {
			continue;
		}

		full_download(file);
	}

	if (isfailed) {
		list_free_list(updates);
	}

	return end_full_download();
}
예제 #13
0
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;
}
예제 #14
0
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(&current_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, &current_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;
}
static bool parse_options(int argc, char **argv)
{
	int opt;
	int opt_count = 0;
	unsigned int count;
	int tmp_val;

	while ((opt = getopt_long(argc, argv, "hrpn:b:i:u:d", prog_opts, NULL)) != -1) {
		switch (opt) {
		case '?':
		case 'h':
			goto err;
		case 'r':
			boot_repair = true;
			break;
		case 'p':
			boot_prior = true;
			break;
		case 'n':
			if (sscanf(optarg, "%i", &next_version) != 1) {
				printf("Invalid --next_version argument\n\n");
				goto err;
			}
			break;
		case 'b':
			next_repair = parse_version_list(optarg);
			count = list_len(next_repair);
			if (count != 1) {
				printf("Invalid --next_repair_boot_check_failed argument\n\n");
				list_free_list(next_repair);
				goto err;
			}
			repair_reason = repair_boot_check_failure;
			break;
		case 'i':
			next_repair = parse_version_list(optarg);
			count = list_len(next_repair);
			if ((count == 0) || (count > VERIFY_FAILED_MAX_VERSIONS_COUNT)) {
				printf("Invalid --next_repair_integrity_check_failed argument\n\n");
				list_free_list(next_repair);
				goto err;
			}
			repair_reason = repair_verify_failure;
			break;
		case 'u':
			next_repair = parse_version_list(optarg);
			count = list_len(next_repair);
			if (count != 2) {
				printf("Invalid --next_repair_update_failed argument\n\n");
				list_free_list(next_repair);
				goto err;
			}
			/* switch values to have: to,from*/
			tmp_val = (int)next_repair->data;
			next_repair->data = next_repair->next->data;
			next_repair->next->data = (void *)((intptr_t)tmp_val);
			repair_reason = repair_update_failure;
			break;
		case 'd':
			dump = true;
			break;
		default:
			printf("Unrecognized option\n\n");
			goto err;
		}

		if (opt_count > 1) {
			printf("Invalid options\n\n");
			goto err;
		}
		opt_count++;
	}

	if (opt_count == 0)
		goto err;

	return true;
err:
	print_help(argv[0]);
	return false;
}
예제 #16
0
/*
 * Download fullfiles from the list of files.
 *
 * Return 0 on success or a negative number or errors.
 */
int download_fullfiles(struct list *files, int *num_downloads)
{
	struct swupd_curl_parallel_handle *download_handle;
	struct list *iter;
	struct list *need_download = NULL;
	struct file *file;
	struct stat stat;
	struct download_progress download_progress = { 0, 0, 0 };
	unsigned int complete = 0;
	unsigned int list_length;
	const unsigned int MAX_FILES = 1000;

	if (!files) {
		/* nothing needs to be downloaded */
		return SWUPD_OK;
	}

	/* make a new list with only the files we actually need to download */
	for (iter = list_head(files); iter; iter = iter->next) {
		char *targetfile;
		file = iter->data;

		if (file->is_deleted || file->do_not_update) {
			continue;
		}

		string_or_die(&targetfile, "%s/staged/%s", state_dir, file->hash);
		if (lstat(targetfile, &stat) != 0 || !verify_file(file, targetfile)) {
			need_download = list_append_data(need_download, file);
		}

		free_string(&targetfile);
	}

	if (!need_download) {
		/* no file needs to be downloaded */
		info("No extra files need to be downloaded\n");
		progress_complete_step();
		return 0;
	}

	/* we need to download some files, so set up curl */
	download_handle = swupd_curl_parallel_download_start(get_max_xfer(MAX_XFER));
	swupd_curl_parallel_download_set_callbacks(download_handle, download_successful, download_error, NULL);
	if (!download_handle) {
		/* 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 */
		return -SWUPD_COULDNT_DOWNLOAD_FILE;
	}

	/* getting the size of many files can be very expensive, so if
	 * the files are not too many, get their size, otherwise just use their count
	 * to report progress */
	list_length = list_len(need_download);
	if (list_length < MAX_FILES) {
		download_progress.total_download_size = fullfile_query_total_download_size(need_download);
		if (download_progress.total_download_size > 0) {
			/* enable the progress callback */
			swupd_curl_parallel_download_set_progress_callbacks(download_handle, swupd_progress_callback, &download_progress);
		} else {
			debug("Couldn't get the size of the files to download, using number of files instead\n");
			download_progress.total_download_size = 0;
		}
	} else {
		debug("Too many files to calculate download size (%d files), maximum is %d. Using number of files instead\n", list_length, MAX_FILES);
	}

	/* download loop */
	info("Starting download of remaining update content. This may take a while...\n");

	for (iter = list_head(need_download); iter; iter = iter->next) {
		file = iter->data;

		if (file->is_mix) {
			download_mix_file(file);
		} else {
			download_file(download_handle, file);
		}

		/* fall back for progress reporting when the download size
		* could not be determined */
		if (download_progress.total_download_size == 0) {
			complete++;
			progress_report(complete, list_length);
		}
	}
	info("\n");
	list_free_list(need_download);

	return swupd_curl_parallel_download_end(download_handle, num_downloads);
}