// // pkgutils // // Copyright (c) 2000-2005 Per Liden // Copyright (c) 2006-2013 by CRUX team (http://crux.nu) // // Patches for alternative pkgadd.conf file by Jose V Beneyto // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // #include "pkgadd.h" #include #include #include #include #include void pkgadd::run(int argc, char** argv) { // // Check command line options // string o_root; string o_config; string o_package; bool o_upgrade = false; bool o_force = false; for (int i = 1; i < argc; i++) { string option(argv[i]); if (option == "-r" || option == "--root") { assert_argument(argv, argc, i); o_root = argv[i + 1]; i++; } else if (option == "-c" || option == "--config") { assert_argument(argv, argc, i); o_config = argv[i + 1]; i++; } else if (option == "-u" || option == "--upgrade") { o_upgrade = true; } else if (option == "-f" || option == "--force") { o_force = true; } else if (option[0] == '-' || !o_package.empty()) { throw runtime_error("invalid option " + option); } else { o_package = option; } } if (o_package.empty()) throw runtime_error("option missing"); // // Check UID // if (getuid()) throw runtime_error("only root can install/upgrade packages"); // // Install/upgrade package // { db_lock lock(o_root, true); db_open(o_root); pair package = pkg_open(o_package); vector config_rules = read_config(o_config); bool installed = db_find_pkg(package.first); if (installed && !o_upgrade) throw runtime_error("package " + package.first + " already installed (use -u to upgrade)"); else if (!installed && o_upgrade) throw runtime_error("package " + package.first + " not previously installed (skip -u to install)"); set non_install_files = apply_install_rules(package.first, package.second, config_rules); set conflicting_files = db_find_conflicts(package.first, package.second); if (!conflicting_files.empty()) { if (o_force) { set keep_list; if (o_upgrade) // Don't remove files matching the rules in configuration keep_list = make_keep_list(conflicting_files, config_rules); db_rm_files(conflicting_files, keep_list); // Remove unwanted conflicts } else { copy(conflicting_files.begin(), conflicting_files.end(), ostream_iterator(cerr, "\n")); throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)"); } } set keep_list; if (o_upgrade) { keep_list = make_keep_list(package.second.files, config_rules); db_rm_pkg(package.first, keep_list); } db_add_pkg(package.first, package.second); db_commit(); pkg_install(o_package, keep_list, non_install_files); ldconfig(); } } void pkgadd::print_help() const { cout << "usage: " << utilname << " [options] " << endl << "options:" << endl << " -u, --upgrade upgrade package with the same name" << endl << " -f, --force force install, overwrite conflicting files" << endl << " -r, --root specify alternative installation root" << endl << " -c, --config use alternate configuration file" << endl << " -v, --version print version and exit" << endl << " -h, --help print help and exit" << endl; } vector pkgadd::read_config(string file) const { vector rules; unsigned int linecount = 0; string filename = root + PKGADD_CONF; if (!file.empty()) filename = file; ifstream in(filename.c_str()); if (in) { while (!in.eof()) { string line; getline(in, line); linecount++; if (!line.empty() && line[0] != '#') { if (line.length() >= PKGADD_CONF_MAXLINE) throw runtime_error(filename + ":" + itos(linecount) + ": line too long, aborting"); char event[PKGADD_CONF_MAXLINE]; char pattern[PKGADD_CONF_MAXLINE]; char action[PKGADD_CONF_MAXLINE]; char dummy[PKGADD_CONF_MAXLINE]; if (sscanf(line.c_str(), "%s %s %s %s", event, pattern, action, dummy) != 3) throw runtime_error(filename + ":" + itos(linecount) + ": wrong number of arguments, aborting"); if (!strcmp(event, "UPGRADE") || !strcmp(event, "INSTALL")) { rule_t rule; rule.event = strcmp(event, "UPGRADE") ? INSTALL : UPGRADE; rule.pattern = pattern; if (!strcmp(action, "YES")) { rule.action = true; } else if (!strcmp(action, "NO")) { rule.action = false; } else throw runtime_error(filename + ":" + itos(linecount) + ": '" + string(action) + "' unknown action, should be YES or NO, aborting"); rules.push_back(rule); } else throw runtime_error(filename + ":" + itos(linecount) + ": '" + string(event) + "' unknown event, aborting"); } } in.close(); } #ifndef NDEBUG cerr << "Configuration:" << endl; for (vector::const_iterator j = rules.begin(); j != rules.end(); j++) { cerr << "\t" << (*j).pattern << "\t" << (*j).action << endl; } cerr << endl; #endif return rules; } set pkgadd::make_keep_list(const set& files, const vector& rules) const { set keep_list; vector found; find_rules(rules, UPGRADE, found); for (set::const_iterator i = files.begin(); i != files.end(); i++) { for (vector::reverse_iterator j = found.rbegin(); j != found.rend(); j++) { if (rule_applies_to_file(*j, *i)) { if (!(*j).action) keep_list.insert(keep_list.end(), *i); break; } } } #ifndef NDEBUG cerr << "Keep list:" << endl; for (set::const_iterator j = keep_list.begin(); j != keep_list.end(); j++) { cerr << " " << (*j) << endl; } cerr << endl; #endif return keep_list; } set pkgadd::apply_install_rules(const string& name, pkginfo_t& info, const vector& rules) { // TODO: better algo(?) set install_set; set non_install_set; vector found; find_rules(rules, INSTALL, found); for (set::const_iterator i = info.files.begin(); i != info.files.end(); i++) { bool install_file = true; for (vector::reverse_iterator j = found.rbegin(); j != found.rend(); j++) { if (rule_applies_to_file(*j, *i)) { install_file = (*j).action; break; } } if (install_file) install_set.insert(install_set.end(), *i); else non_install_set.insert(*i); } info.files.clear(); info.files = install_set; #ifndef NDEBUG cerr << "Install set:" << endl; for (set::iterator j = info.files.begin(); j != info.files.end(); j++) { cerr << " " << (*j) << endl; } cerr << endl; cerr << "Non-Install set:" << endl; for (set::iterator j = non_install_set.begin(); j != non_install_set.end(); j++) { cerr << " " << (*j) << endl; } cerr << endl; #endif return non_install_set; } void pkgadd::find_rules(const vector& rules, rule_event_t event, vector& found) const { for (vector::const_iterator i = rules.begin(); i != rules.end(); i++) if (i->event == event) found.push_back(*i); } bool pkgadd::rule_applies_to_file(const rule_t& rule, const string& file) const { regex_t preg; bool ret; if (regcomp(&preg, rule.pattern.c_str(), REG_EXTENDED | REG_NOSUB)) throw runtime_error("error compiling regular expression '" + rule.pattern + "', aborting"); ret = !regexec(&preg, file.c_str(), 0, 0, 0); regfree(&preg); return ret; }