/* Based up Neutrino-GUI - Tuxbox-Project Copyright (C) 2001 by Steffen Hehn 'McClean' OPKG-Manager Class for Neutrino-GUI Implementation: Copyright (C) 2012-2014 T. Graf 'dbt' www.dbox2-tuning.net Adaptions: Copyright (C) 2013 martii gitorious.org/neutrino-mp/martiis-neutrino-mp Copyright (C) 2015 Stefan Seyfried License: GPL 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, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* later this can be changed to just "opkg" */ #define OPKG_CL "opkg-cl" #define OPKG_CL_CONFIG_OPTIONS " -V2 --tmp-dir=/tmp --cache=/tmp/.opkg " using namespace std; enum { OM_LIST, OM_LIST_INSTALLED, OM_LIST_UPGRADEABLE, OM_UPDATE, OM_UPGRADE, OM_REMOVE, OM_INFO, OM_INSTALL, OM_MAX }; static const string pkg_types[OM_MAX] = { OPKG_CL " list ", OPKG_CL " list-installed ", OPKG_CL " list-upgradable ", OPKG_CL " -A update ", OPKG_CL OPKG_CL_CONFIG_OPTIONS " upgrade ", OPKG_CL OPKG_CL_CONFIG_OPTIONS " remove ", OPKG_CL " info ", OPKG_CL OPKG_CL_CONFIG_OPTIONS " install " }; COPKGManager::COPKGManager() { width = 80; pkg_map.clear(); list_installed_done = false; list_upgradeable_done = false; expert_mode = false; local_dir = &g_settings.update_dir_opkg; CFileHelpers::createDir("/tmp/.opkg"); } COPKGManager::~COPKGManager() { } int COPKGManager::exec(CMenuTarget* parent, const string &actionKey) { int res = menu_return::RETURN_REPAINT; if (actionKey.empty()) { if (parent) parent->hide(); return showMenu(); } int selected = menu->getSelected() - menu_offset; if (expert_mode && actionKey == "rc_blue") { if (selected < 0 || selected >= (int) pkg_vec.size() || !pkg_vec[selected]->installed) return menu_return::RETURN_NONE; char loc[200]; snprintf(loc, sizeof(loc), g_Locale->getText(LOCALE_OPKG_MESSAGEBOX_REMOVE), pkg_vec[selected]->name.c_str()); if (ShowMsg(LOCALE_OPKG_TITLE, loc, CMessageBox::mbrCancel, CMessageBox::mbYes | CMessageBox::mbCancel) != CMessageBox::mbrCancel) { if (parent) parent->hide(); execCmd(pkg_types[OM_REMOVE] + pkg_vec[selected]->name, true, true); refreshMenu(); } return res; } if (actionKey == "rc_info") { if (selected < 0 || selected >= (int) pkg_vec.size()) return menu_return::RETURN_NONE; if (parent) parent->hide(); execCmd(pkg_types[OM_INFO] + pkg_vec[selected]->name, true, true); return res; } if (actionKey == "rc_yellow") { expert_mode = !expert_mode; updateMenu(); return res; } if (actionKey == "local_package") { if (parent) parent->hide(); CFileFilter fileFilter; string filters[] = {"opk", "ipk"}; for(size_t i=0; iName; int r = execCmd(pkg_types[OM_INSTALL] + pgk_name, true, true); *local_dir = fileBrowser.getCurrentDir(); if (r) { showError(g_Locale->getText(LOCALE_OPKG_FAILURE_INSTALL), strerror(errno), pgk_name); } else installed = true; refreshMenu(); } return res; } if(actionKey == pkg_types[OM_UPGRADE]) { if (parent) parent->hide(); int r = execCmd(actionKey, true, true); if (r) { showError(g_Locale->getText(LOCALE_OPKG_FAILURE_UPGRADE), strerror(errno), actionKey); } else installed = true; refreshMenu(); g_RCInput->postMsg((neutrino_msg_t) CRCInput::RC_up, 0); return res; } map::iterator it = pkg_map.find(actionKey); if (it != pkg_map.end()) { if (parent) parent->hide(); string force = ""; if (it->second.installed && !it->second.upgradable) { char l[200]; snprintf(l, sizeof(l), g_Locale->getText(LOCALE_OPKG_MESSAGEBOX_REINSTALL), actionKey.c_str()); l[sizeof(l) - 1] = 0; if (ShowMsg(LOCALE_OPKG_TITLE, l, CMessageBox::mbrCancel, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel) return res; force = "--force-reinstall "; } int r = execCmd(pkg_types[OM_INSTALL] + force + actionKey, true, true); if (r) { showError(g_Locale->getText(LOCALE_OPKG_FAILURE_INSTALL), strerror(errno), pkg_types[OM_INSTALL] + force + actionKey); } else installed = true; refreshMenu(); } return res; } #define COPKGManagerFooterButtonCount 3 static const struct button_label COPKGManagerFooterButtons[COPKGManagerFooterButtonCount] = { { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_OPKG_BUTTON_EXPERT_ON }, { NEUTRINO_ICON_BUTTON_INFO_SMALL, LOCALE_OPKG_BUTTON_INFO }, { NEUTRINO_ICON_BUTTON_OKAY, LOCALE_OPKG_BUTTON_INSTALL } }; #define COPKGManagerFooterButtonCountExpert 4 static const struct button_label COPKGManagerFooterButtonsExpert[COPKGManagerFooterButtonCountExpert] = { { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_OPKG_BUTTON_EXPERT_OFF }, { NEUTRINO_ICON_BUTTON_INFO_SMALL, LOCALE_OPKG_BUTTON_INFO }, { NEUTRINO_ICON_BUTTON_OKAY, LOCALE_OPKG_BUTTON_INSTALL }, { NEUTRINO_ICON_BUTTON_BLUE, LOCALE_OPKG_BUTTON_UNINSTALL } }; /* TODO: this should go into a config file... */ static std::string bad_pattern[] = { "-dev$", "-doc$", "-dbg$", "-ptest$", "-staticdev$", "-locale-", "-charmap-", "-gconv-", "-localedata-", "^locale-base-", "^perl-module-", "" }; bool COPKGManager::badpackage(std::string &s) { int i; for (i = 0; !bad_pattern[i].empty(); i++) { std::string p = bad_pattern[i]; size_t patlen = p.length() - 1; /* poor man's regex :-) only supported are "^" and "$" */ if (p.substr(patlen, 1) == "$") { /* match at end */ if (s.rfind(p.substr(0, patlen)) == (s.length() - patlen)) return true; } else if (p.substr(0, 1) == "^") { /* match at beginning */ if (s.find(p.substr(1)) == 0) return true; } else { /* match everywhere */ if (s.find(p) != std::string::npos) return true; } } return false; } void COPKGManager::updateMenu() { bool upgradesAvailable = false; getPkgData(OM_LIST_INSTALLED); getPkgData(OM_LIST_UPGRADEABLE); for (map::iterator it = pkg_map.begin(); it != pkg_map.end(); it++) { if (badpackage(it->second.name)) continue; it->second.forwarder->iconName_Info_right = ""; it->second.forwarder->setActive(true); if (it->second.upgradable) { it->second.forwarder->iconName_Info_right = NEUTRINO_ICON_WARNING; upgradesAvailable = true; } else if (it->second.installed) { it->second.forwarder->iconName_Info_right = NEUTRINO_ICON_CHECKMARK; it->second.forwarder->setActive(expert_mode); } } upgrade_forwarder->setActive(upgradesAvailable); if (expert_mode) menu->setFooter(COPKGManagerFooterButtonsExpert, COPKGManagerFooterButtonCountExpert); else menu->setFooter(COPKGManagerFooterButtons, COPKGManagerFooterButtonCount); } bool COPKGManager::hasUpdates() { if (!hasOpkgSupport()) return false; bool ret = false; getPkgData(OM_LIST); getPkgData(OM_LIST_UPGRADEABLE); for (map::iterator it = pkg_map.begin(); it != pkg_map.end(); it++){ if (it->second.upgradable){ dprintf(DEBUG_INFO, "[COPKGManager] [%s - %d] Update packages available...\n", __func__, __LINE__); ret = true; } } pkg_map.clear(); return ret; } int COPKGManager::doUpdate() { int r = execCmd(pkg_types[OM_UPDATE]); if (r == -1) { DisplayErrorMessage(g_Locale->getText(LOCALE_OPKG_FAILURE_UPDATE)); // showError(g_Locale->getText(LOCALE_OPKG_FAILURE_UPDATE), strerror(errno), pkg_types[OM_UPDATE]); } return r; } void COPKGManager::refreshMenu() { list_installed_done = false, list_upgradeable_done = false; updateMenu(); } int COPKGManager::showMenu() { installed = false; doUpdate(); getPkgData(OM_LIST); getPkgData(OM_LIST_UPGRADEABLE); menu = new CMenuWidget(g_Locale->getText(LOCALE_SERVICEMENU_UPDATE), NEUTRINO_ICON_UPDATE, width, MN_WIDGET_ID_SOFTWAREUPDATE); menu->addIntroItems(LOCALE_OPKG_TITLE, NONEXISTANT_LOCALE, CMenuWidget::BTN_TYPE_BACK, CMenuWidget::BRIEF_HINT_YES); //upgrade all installed packages upgrade_forwarder = new CMenuForwarder(LOCALE_OPKG_UPGRADE, true, NULL , this, pkg_types[OM_UPGRADE].c_str(), CRCInput::RC_red); upgrade_forwarder->setHint(NEUTRINO_ICON_HINT_SW_UPDATE, LOCALE_MENU_HINT_OPKG_UPGRADE); menu->addItem(upgrade_forwarder); //select and install local package CMenuForwarder *local; local = new CMenuForwarder(LOCALE_OPKG_INSTALL_LOCAL_PACKAGE, true, NULL, this, "local_package", CRCInput::RC_green); local->setHint(NEUTRINO_ICON_HINT_SW_UPDATE, LOCALE_MENU_HINT_OPKG_INSTALL_LOCAL_PACKAGE); menu->addItem(local); menu->addItem(GenericMenuSeparatorLine); menu_offset = menu->getItemsCount(); menu->addKey(CRCInput::RC_info, this, "rc_info"); menu->addKey(CRCInput::RC_blue, this, "rc_blue"); menu->addKey(CRCInput::RC_yellow, this, "rc_yellow"); pkg_vec.clear(); for (map::iterator it = pkg_map.begin(); it != pkg_map.end(); it++) { if (badpackage(it->second.name)) continue; it->second.forwarder = new CMenuForwarder(it->second.desc, true, NULL , this, it->second.name.c_str()); it->second.forwarder->setHint("", it->second.desc); menu->addItem(it->second.forwarder); pkg_vec.push_back(&it->second); } updateMenu(); int res = menu->exec(NULL, ""); menu->hide (); if (installed) DisplayInfoMessage(g_Locale->getText(LOCALE_OPKG_SUCCESS_INSTALL)); delete menu; return res; } bool COPKGManager::hasOpkgSupport() { string deps[] = {"/etc/opkg/opkg.conf", "/var/lib/opkg"}; if (find_executable(OPKG_CL).empty()) { dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d]" OPKG_CL " executable not found\n", __func__, __LINE__); return false; } for(size_t i=0; i::iterator it = pkg_map.begin(); it != pkg_map.end(); it++) it->second.installed = false; break; case OM_LIST_UPGRADEABLE: if (list_upgradeable_done) return; list_upgradeable_done = true; for (map::iterator it = pkg_map.begin(); it != pkg_map.end(); it++) it->second.upgradable = false; break; } pid_t pid = 0; FILE *f = my_popen(pid, pkg_types[pkg_content_id].c_str(), "r"); if (!f) { showError("Internal Error", strerror(errno), pkg_types[pkg_content_id]); return; } char buf[256]; while (fgets(buf, sizeof(buf), f)) { if (buf[0] == ' ') continue; /* second, third, ... line of description will not be shown anyway */ std::string line(buf); trim(line); string name = getBlankPkgName(line); switch (pkg_content_id) { case OM_LIST: { pkg_map[name] = pkg(name, line); break; } case OM_LIST_INSTALLED: { map::iterator it = pkg_map.find(name); if (it != pkg_map.end()) it->second.installed = true; break; } case OM_LIST_UPGRADEABLE: { map::iterator it = pkg_map.find(name); if (it != pkg_map.end()) it->second.upgradable = true; break; } default: fprintf(stderr, "%s %s %d: unrecognized content id %d\n", __FILE__, __func__, __LINE__, pkg_content_id); break; } } fclose(f); } string COPKGManager::getBlankPkgName(const string& line) { size_t l_pos = line.find(" "); if (l_pos != string::npos) return line.substr(0, l_pos); return line; } int COPKGManager::execCmd(const char *cmdstr, bool verbose, bool acknowledge) { fprintf(stderr, "execCmd(%s)\n", cmdstr); string cmd = string(cmdstr); int res = 0; bool has_err = false; string err_msg = ""; if (verbose) { // cmd += " 2>&1"; CShellWindow(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_MSG : 0), &res); } else { cmd += " 2>&1"; pid_t pid = 0; FILE *f = my_popen(pid, cmd.c_str(), "r"); if (!f) { showError("OPKG-Error!", strerror(errno), cmd); return -1; } char buf[256]; while (fgets(buf, sizeof(buf), f)) { string line(buf); trim(line); dprintf(DEBUG_INFO, "[COPKGManager] [%s - %d] %s [error %d]\n", __func__, __LINE__, line.c_str(), has_err); //check for collected errors and build a message for screen if errors available if (has_err){ dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] %s \n", __func__, __LINE__, line.c_str()); size_t pos1 = line.find(" * opkg_"); string str = line.substr(pos1, line.length()-pos1); err_msg += str.replace(pos1, 8,"") + "\n"; }else{ size_t pos2 = line.find("Collected errors:"); if (pos2 != string::npos) has_err = true; } } fclose(f); } if (has_err){ DisplayErrorMessage(err_msg.c_str()); return -1; } return res; } void COPKGManager::showError(const char* local_msg, char* err_msg, const string& command) { string msg = local_msg ? string(local_msg) + "\n" : ""; msg += string(err_msg) + ":\n"; msg += command; DisplayErrorMessage(msg.c_str()); }