tmp_pipes(int nb_pipes):
 tmpdir_(create_tmp_dir()),
 pipes_(create_pipes(tmpdir_, nb_pipes))
 {
   for(auto it = pipes_.cbegin(); it != pipes_.cend(); ++it)
     pipes_paths_.push_back(it->c_str());
 }
예제 #2
0
//void do_install(opkg_conf* conf, char* pkg_name, char* install_root_name, char* link_root_name, char** version_criteria)
void do_install(opkg_conf* conf, string_map* pkgs, char* install_root_name, char* link_root_name, int is_upgrade, int overwrite_config, int overwrite_other_package_files, int force_reinstall, char* tmp_root)
{
	string_map* package_data = initialize_string_map(1);
	string_map* matching_packages = initialize_string_map(1);
	string_map* pkgs_from_file = initialize_string_map(1);
	unsigned long num_destroyed;


	char* install_root_path = (char*)get_string_map_element(conf->dest_names, install_root_name);
	char* overlay_path = NULL; // no special treatment of overlay, can be reenabled  by setting this variable here if we ever need it


	char* test_dir  = dynamic_strcat(2, (overlay_path != NULL ? overlay_path : install_root_path), "/usr/lib/opkg/info");
	if(!create_dir_and_test_writable(test_dir))
	{
		fprintf(stderr, "ERROR: Specified install destination is not writable, exiting\n");
		exit(1);
	}
	free(test_dir);

	



	if(install_root_path == NULL)
	{
		printf("ERROR: No destination %s found, cannot install\n\n", install_root_name);
		exit(1);
	}
	
	char* tmp_dir = (char*)malloc(1024);
	if(create_tmp_dir(tmp_root == NULL ? "/tmp" : tmp_root, &tmp_dir) != 0)
	{
		fprintf(stderr, "ERROR: Could not create tmp dir, exiting\n");
		exit(1);
	}


	/* Determine all packages to install by first loading all package names, status & dependencies (and no other variables) */
	load_all_package_data(conf, package_data, matching_packages, NULL, LOAD_MINIMAL_PKG_VARIABLES_FOR_ALL, install_root_name, 1, NULL );
	destroy_string_map(matching_packages, DESTROY_MODE_FREE_VALUES, &num_destroyed);

		
	/* determine list of all packiages we are about to install, including dependencies */
	string_map* install_pkgs_map = initialize_string_map(1);
	char** install_pkg_list = NULL;	
	unsigned long install_pkg_list_len = 0;
	char* unsatisfied_dep_err = NULL;



	
	/* new string map var with all pkgs to install = pkgs, keys = version */
	unsigned long num_pkg_names;
	char** pkg_names = get_string_map_keys(pkgs, &num_pkg_names);
	int pkg_name_index;
	
	
	
	
	/* 
	 * Load data for any packages being installed via ipk and
	 * determine if any packages we are about to install 
	 * provide anything, and if so set package we are installing to preferred
	 */
	string_map* preferred_provides = initialize_string_map(1);
	for(pkg_name_index=0;pkg_name_index < num_pkg_names; pkg_name_index++)
	{
		char* pkg_name = pkg_names[pkg_name_index];
		char** version_criteria = get_string_map_element(pkgs, pkg_name);
		char* install_pkg_version = NULL;
		int install_pkg_is_current;

		
		/* deal with case where we're installing from file */
		if(path_exists(pkg_name))
		{
			//installing from file
			char* pkg_file = pkg_name;


			//extract control files
			int err = 0;
			char* tmp_control        = dynamic_strcat(2, tmp_dir, "/tmp_ctrl");
			char* tmp_control_prefix = dynamic_strcat(2, tmp_control, "/tmp.");
			char* tmp_control_name   = dynamic_strcat(2, tmp_control_prefix, "control");

			mkdir_p(tmp_control, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
			deb_extract(	pkg_file,
					stderr,
					extract_control_tar_gz | extract_all_to_fs| extract_preserve_date | extract_unconditional,
					tmp_control_prefix, 
					NULL, 
					&err);
			if(err != 0)
			{
				fprintf(stderr, "ERROR: %s is not a valid package file, cannot install\n", pkg_file);
				rm_r(tmp_dir);
				exit(1);
			}
			string_map* tmp_control_pkg_data = initialize_string_map(1);
			matching_packages = initialize_string_map(1);
		       	load_package_data(tmp_control_name, 0, tmp_control_pkg_data, matching_packages, NULL, LOAD_ALL_PKG_VARIABLES, NULL, NULL);
			unsigned long num_ctrl_names;
			char** ctrl_name_list = get_string_map_keys(tmp_control_pkg_data, &num_ctrl_names);
			destroy_string_map(matching_packages, DESTROY_MODE_FREE_VALUES, &num_destroyed);
		

			err = 1; //set back to 0 when data successfully loaded
			if(num_ctrl_names > 0)
			{
				int ctrl_name_index;
				for(ctrl_name_index=0; ctrl_name_list[ctrl_name_index] != NULL; ctrl_name_index++)
				{
					if( strcmp(ctrl_name_list[ctrl_name_index], PROVIDES_STRING) != 0)
					{
						pkg_name = strdup(ctrl_name_list[ctrl_name_index]);
					}
				}



				char* version = NULL;
				int is_current;
				string_map* pkg_info = get_package_current_or_latest(tmp_control_pkg_data, pkg_name, &is_current, &version);
				if(pkg_info != NULL)
				{
					err = 0;
					set_string_map_element(pkg_info, "Install-File-Location", strdup(pkg_file));
					set_string_map_element(pkg_info, "Version", version); //we need to save this, since we are going to set a special version to make sure data doesn't get over-written later, also no need to free version now

					char* special_version = dynamic_strcat(2, version, "@@_FILE_INSTALL_VERSION_@@");
					char** new_version_criteria = malloc(3*sizeof(char*));
					new_version_criteria[0] = strdup("=");
					new_version_criteria[1] = special_version;
					new_version_criteria[2] = NULL;
					version_criteria = new_version_criteria;
					
					string_map* all_current_versions = get_string_map_element(package_data, pkg_name);
					if(all_current_versions == NULL)
					{
						all_current_versions=initialize_string_map(1);
						set_string_map_element(package_data, pkg_name, all_current_versions);
					}
					set_string_map_element(all_current_versions, special_version, pkg_info);
					set_string_map_element(all_current_versions, LATEST_VERSION_STRING, special_version);
				
					free(pkg_names[pkg_name_index]);
					pkg_names[pkg_name_index] = strdup(pkg_name);

				
					set_string_map_element(pkgs, pkg_name, copy_null_terminated_string_array(new_version_criteria));	
					set_string_map_element(pkgs_from_file, pkg_name, strdup("D"));
				}
			}
			free_null_terminated_string_array(ctrl_name_list);
			if(err != 0)
			{
				fprintf(stderr, "ERROR: %s is not a valid package file, cannot install\n", pkg_file);
				rm_r(tmp_dir);
				exit(1);
			}

			free_if_not_null(tmp_control);
			free_if_not_null(tmp_control_prefix);
			free_if_not_null(tmp_control_name);
			rm_r(tmp_control);
		}

		/* determine if package provides anything, and set this package to preferred if so*/
		string_map* install_pkg_data = get_package_current_or_latest_matching(package_data, pkg_name, version_criteria, &install_pkg_is_current, &install_pkg_version);
		if(install_pkg_data != NULL)
		{
			char* provides_str = get_string_map_element(install_pkg_data, "Provides");
			if(provides_str != NULL)
			{
				if(strlen(provides_str) > 0)
				{
					unsigned long num_provides;
					char package_separators[] = {' ', ',', ':', ';', '\'', '\"', '\t', '\r', '\n'};
					char** provides_list = split_on_separators(provides_str, package_separators, 9, -1, 0, &num_provides);
					int provides_index;
					char* provides_unique_key = dynamic_strcat(3, pkg_name, "@", install_pkg_version);
					for(provides_index=0; provides_index < num_provides; provides_index++)
					{
						char* provides_name = strdup(provides_list[provides_index]);
						char* eq = strchr(provides_name, '=');
						if(eq != NULL) { *eq = '\0' ; }
						if(strlen(provides_name) > 0)
						{
							set_string_map_element(preferred_provides, provides_name, strdup(provides_unique_key));
						}
					}
				}
			}
		}
	
	}


	/* reload with new preferred_provides */
	free_recursive_package_vars(package_data);
	matching_packages = initialize_string_map(1);
	load_all_package_data(conf, package_data, matching_packages, NULL, LOAD_MINIMAL_PKG_VARIABLES_FOR_ALL, install_root_name, 1, preferred_provides );
	destroy_string_map(matching_packages, DESTROY_MODE_FREE_VALUES, &num_destroyed);
	
	
	/* load data and do sanity checks for packages we are about to install */
	for(pkg_name_index=0;pkg_name_index < num_pkg_names; pkg_name_index++)
	{
		char* pkg_name = pkg_names[pkg_name_index];
		char** version_criteria = get_string_map_element(pkgs, pkg_name);
		char* install_pkg_version = NULL;
		int install_pkg_is_current;

		

		load_recursive_package_data_variables(package_data, pkg_name, 1, 0, 0); // load required-depends for package of interest only 
		string_map* install_pkg_data = get_package_current_or_latest_matching(package_data, pkg_name, version_criteria, &install_pkg_is_current, &install_pkg_version);
		char* install_status = install_pkg_data == NULL ? NULL : get_string_map_element(install_pkg_data, "Status");

	
		if(install_status != NULL)
		{
			char** old_el = set_string_map_element(install_pkgs_map, pkg_name, copy_null_terminated_string_array(version_criteria) );
			if(old_el != NULL){ free_null_terminated_string_array(old_el); }

			string_map* install_pkg_depend_map = get_string_map_element(install_pkg_data, "Required-Depends");
			if(install_pkg_depend_map != NULL)
			{
				unsigned long num_keys;
				char** load_detail_pkgs = get_string_map_keys(install_pkg_depend_map, &num_keys);
				int ldp_index;
				for(ldp_index=0;ldp_index < num_keys && unsatisfied_dep_err == NULL; ldp_index++)
				{
					char* dep_name = load_detail_pkgs[ldp_index];
					char** dep_def= get_string_map_element(install_pkg_depend_map, dep_name);
					if(get_string_map_element(install_pkgs_map, dep_name) != NULL)
					{
						/* 
						 * We really should check here whether old dependency def can be reconciled with the new one, and report an error if it can't 
						 * Right now we just use the heuristic that top-level (user specified, not dependency) package defs get preference, followed
						 * by first dependency encountered.
						 *
						 * Since right now versioning features aren't really being used very much other than kernel dependencies in Gargoyle/OpenWrt
						 * I'm just leaving this comment here as a reminder that this should be addressed at some point rather than messing with it now
						 *
						 */

						dep_def = get_string_map_element(install_pkgs_map, dep_name);
					}
					else
					{
						set_string_map_element(install_pkgs_map, dep_name, copy_null_terminated_string_array(dep_def));
					}
	
					//error checking, check that dependency definition exists
					char* latest_version = NULL;
					int latest_is_current = 0;
					string_map* dep_info = get_package_current_or_latest_matching(package_data, dep_name, dep_def, &latest_is_current, &latest_version);
					
					
					//check if we have a version installed different than what is required
					int have_current;
					char* current_version = NULL;
					string_map* cur_info = get_package_current_or_latest(package_data, dep_name, &have_current, &current_version);
					if(have_current && (latest_is_current == 0 || dep_info == NULL))
					{
						//should only get here if dep_def[1] is not null (version mismatch doesn't make sense if no version is specified)
						char* cur_status = get_string_map_element(cur_info, "Status");
						if(strstr(cur_status, " hold ") != NULL)
						{
							unsatisfied_dep_err = dynamic_strcat(11, "ERROR: Dependency ", dep_name, " (", dep_def[0], " ", dep_def[1], ") of package ", pkg_name, " is installed,\n\t\tbut has incompatible version ", current_version, " and is marked as 'hold'");
						}
						else
						{
							unsatisfied_dep_err = dynamic_strcat(10, "ERROR: Dependency ", dep_name, " (", dep_def[0], " ", dep_def[1], ") of package ", pkg_name, " is installed,\n\t\tbut has incompatible version ", current_version);
						}
					}
					free_if_not_null(current_version);
					free_if_not_null(latest_version);
	
					// check that dependency definition exists
					if(unsatisfied_dep_err == NULL && dep_info == NULL)
					{
						if(dep_def[1] != NULL)
						{
							unsatisfied_dep_err = dynamic_strcat(9, "ERROR: Dependency ", dep_name, " (", dep_def[0], " ", dep_def[1], ") of package ", pkg_name, " cannot be found, try updating your package lists");
						}
						else
						{
							unsatisfied_dep_err = dynamic_strcat(5, "ERROR: Dependency ", dep_name, " of package ", pkg_name, " cannot be found, try updating your package lists");
						}
					}
									
				}
				free_null_terminated_string_array(load_detail_pkgs);
			}
	
		}
		install_status = install_pkg_data == NULL ? NULL : get_string_map_element(install_pkg_data, "Status");




		/* error checking before we start install */
		if(install_pkg_data == NULL || install_status == NULL)
		{
			fprintf(stderr, "ERROR: No package named %s found, try updating your package lists\n\n", pkg_name);
			rm_r(tmp_dir);
			exit(1);
		}
		if(strstr(install_status, " installed") != NULL)
		{
			if(force_reinstall)
			{
				fprintf(stderr, "WARNING: Package %s is already installed, forcing removal and reinstallation\n\n", pkg_name);
				free_package_data(package_data);
				string_map* rm_pkg = initialize_string_map(1);
				set_string_map_element(rm_pkg, pkg_name, alloc_depend_def(NULL));
				do_remove(conf, rm_pkg, (overwrite_config ? 0 : 1), 0, 1, 0, tmp_root);
				
				//restart install
				return do_install(conf, pkgs, install_root_name, link_root_name, is_upgrade, overwrite_config, overwrite_other_package_files, force_reinstall, tmp_root);
				
			}
			else
			{
				fprintf(stderr, "WARNING: Package %s is already installed, ignoring\n", pkg_name);
				fprintf(stderr, "         Use --force-reinstall to force reinstallation\n\n");
				char** old_el = remove_string_map_element(install_pkgs_map, pkg_name);
				if(old_el != NULL){ free_null_terminated_string_array(old_el); };
			}

		}

		if(unsatisfied_dep_err != NULL)
		{
			fprintf(stderr, "%s\n", unsatisfied_dep_err);
			rm_r(tmp_dir);
			exit(1);
		}
	}


	
	
	/* load more detailed data on packages we are about to install */
	free_recursive_package_vars(package_data); /* note: whacks install_pkg_depend_map */	
	string_map* parameters = initialize_string_map(1);
	matching_packages = initialize_string_map(1);
	set_string_map_element(parameters, "package-list", install_pkgs_map);
	
	load_all_package_data(conf, package_data, matching_packages, parameters, LOAD_MINIMAL_FOR_ALL_PKGS_ALL_FOR_MATCHING, install_root_name, 0, preferred_provides);
	
	unsigned long from_file_pkg_list_len;
	char** from_file_pkg_list = get_string_map_keys(pkgs_from_file, &from_file_pkg_list_len);
	int from_file_index;
	for(from_file_index=0; from_file_index < from_file_pkg_list_len; from_file_index++)
	{
		char* old = set_string_map_element(matching_packages, from_file_pkg_list[from_file_index], strdup("D"));
		free_if_not_null(old);
	}
	free_null_terminated_string_array(from_file_pkg_list);
	install_pkg_list = get_string_map_keys(matching_packages, &install_pkg_list_len);
	


	
	
	destroy_string_map(matching_packages, DESTROY_MODE_FREE_VALUES, &num_destroyed);
	destroy_string_map(parameters, DESTROY_MODE_IGNORE_VALUES, &num_destroyed);
	

	char* all_pkg_list_str = join_strs(", ", install_pkg_list, install_pkg_list_len, 0, 0); 
	uint64_t combined_size = 0;
	int pkg_index;


	for(pkg_index=0; pkg_index < install_pkg_list_len; pkg_index++)
	{
		char** match_criteria = get_string_map_element(install_pkgs_map, install_pkg_list[pkg_index]);
		string_map* pkg = get_package_current_or_latest_matching(package_data, install_pkg_list[pkg_index], match_criteria, NULL, NULL);
		char* next_size_str = get_string_map_element(pkg, "Installed-Size");
		uint64_t next_size = 0;
		if(sscanf(next_size_str,  SCANFU64, &next_size) > 0)
		{
			combined_size = combined_size + next_size; 
		} 
	}
	uint64_t root_size = destination_bytes_free(conf, install_root_name);
	if(combined_size >= root_size )
	{
		fprintf(stderr, "ERROR: Not enough space in destination %s to install specified packages:\n\t%s\n\n", install_root_name, all_pkg_list_str);
		rm_r(tmp_dir);
		exit(1);
	}


	if(all_pkg_list_str != NULL)
	{
		printf("Preparing to install the following packages, which will require " SCANFU64 " bytes:\n\t%s\n\n", combined_size, all_pkg_list_str);
	}
	else
	{
		fprintf(stderr, "No packages to install.\n\n");
	}

	/* Set status of new required packages to half-installed, set user-installed on requested package, installed time on all */
	char* install_root_status_path = dynamic_strcat(2, install_root_path, "/usr/lib/opkg/status");
	string_map* install_root_status = initialize_string_map(1);
	matching_packages = initialize_string_map(1);
	if(path_exists(install_root_status_path))
	{
		load_package_data(install_root_status_path, 0, install_root_status, matching_packages, NULL, LOAD_ALL_PKG_VARIABLES, install_root_name, preferred_provides);
	}
	destroy_string_map(matching_packages, DESTROY_MODE_FREE_VALUES, &num_destroyed);



	time_t now = time(NULL);
	char install_time[20];
	sprintf(install_time, "%lu", now);
	for(pkg_index=0; pkg_index < install_pkg_list_len; pkg_index++)
	{
		int is_installed;
		char* install_version = NULL;
		char** match_criteria = get_string_map_element(install_pkgs_map, install_pkg_list[pkg_index]);
		string_map* pkg = get_package_current_or_latest_matching(package_data, install_pkg_list[pkg_index], match_criteria, &is_installed, &install_version);


		if(is_installed == 0) /* should never be true, but check anyway */
		{
			char* old_status = remove_string_map_element(pkg, "Status");
			free(old_status);
			char* status_parts[3] = { "install", "ok", "half-installed" };
			status_parts[1] = get_string_map_element(pkgs, install_pkg_list[pkg_index]) != NULL ? "user" : status_parts[1];
			char* new_status = dynamic_strcat(5, status_parts[0], " ", status_parts[1], " ", status_parts[2]);
			set_string_map_element(pkg, "Status", new_status);

			set_string_map_element(pkg, "Installed-Time", strdup(install_time));
			set_string_map_element(pkg, "Install-Destination", strdup(install_root_name));
			if(link_root_name != NULL)
			{
				set_string_map_element(pkg, "Link-Destination", strdup(link_root_name));
			}

			add_package_data(install_root_status, &pkg, install_pkg_list[pkg_index], install_version, NULL); 
			/* Note: we just added pkg data structure from package_data to install_root_status, Be careful on cleanup! */
		}
	}
	save_package_data_as_status_file(install_root_status, install_root_status_path);




	

	string_map* install_called_pkgs = initialize_string_map(1);

	int err = 0;
	for(pkg_name_index=0;pkg_name_index < num_pkg_names; pkg_name_index++)
	{
		char* pkg_name = pkg_names[pkg_name_index];
		if(get_string_map_element(install_pkgs_map, pkg_name) != NULL && get_string_map_element(install_called_pkgs, pkg_name) == NULL)
		{
			int install_pkg_is_current;
			char* install_pkg_version = NULL;
			char** version_criteria = get_string_map_element(pkgs, pkg_name);
			get_package_current_or_latest_matching(package_data, pkg_name, version_criteria, &install_pkg_is_current, &install_pkg_version);
			err = recursively_install(pkg_name, install_pkg_version, install_root_name, link_root_name, overlay_path, is_upgrade, overwrite_config, overwrite_other_package_files, tmp_dir, conf, package_data, install_called_pkgs);
		
			free_if_not_null(install_pkg_version);
		}
	}
	

	if(err)
	{
		fprintf(stderr, "An error occurred during Installation, removing partially installed packages.\n");
		unsigned long num_install_called_pkgs;
		char** install_called_pkg_list = get_string_map_keys(install_called_pkgs, &num_install_called_pkgs);
		int pkg_index;
		for(pkg_index=0; pkg_index < num_install_called_pkgs; pkg_index++)
		{
			remove_individual_package(install_called_pkg_list[pkg_index], conf, package_data, tmp_dir, 0, 0);
		}
		free_null_terminated_string_array(install_called_pkg_list);
		//call remove function to do cleanup of partial install
		//DO NOT EXIT HERE, fixup status file below
	}
	//remove tmp dir -- need to do this whether or not there is an error
	rm_r(tmp_dir);
	free(tmp_dir);


	//set status of new packages to installed on success, and remove on failure
	for(pkg_index=0; pkg_index < install_pkg_list_len; pkg_index++)
	{	
		/* no need to check version, should only be one installed version at a time... */
		string_map* pkg = get_package_current_or_latest(install_root_status, install_pkg_list[pkg_index], NULL, NULL); 
		if(pkg != NULL)
		{
			if(!err)
			{
				char* status = get_string_map_element(pkg, "Status");
				if(strstr(status, " half-installed") != NULL)
				{
					char* status_parts[3] = { "install", "ok", "installed" };
					status_parts[1] = get_string_map_element(pkgs, install_pkg_list[pkg_index]) != NULL ? "user" : status_parts[1];
					char* new_status = dynamic_strcat(5, status_parts[0], " ", status_parts[1], " ", status_parts[2]);
					char* old_status = set_string_map_element(pkg, "Status", new_status);
					free_if_not_null(old_status);
				}
			}
			else
			{
				string_map* all_pkg_versions = remove_string_map_element(install_root_status, install_pkg_list[pkg_index]);
				free_all_package_versions(all_pkg_versions);
			}
		}
	}
	save_package_data_as_status_file(install_root_status, install_root_status_path);
	if(!err)
	{
		if(all_pkg_list_str != NULL)
		{
			printf("Installation of packages successful.\n\n");
		}
	}
	else
	{
		printf("Finished removing partially installed packages.\n\n");
	}
}