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()); }
//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, ¤t_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"); } }