mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-29 00:11:14 +02:00
CShellWindow/COPKGManager: handle some error from shell output
TODO: add missing handlers
This commit is contained in:
@@ -54,6 +54,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <alloca.h>
|
#include <alloca.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
/* later this can be changed to just "opkg" */
|
/* later this can be changed to just "opkg" */
|
||||||
#define OPKG_CL "opkg-cl"
|
#define OPKG_CL "opkg-cl"
|
||||||
#define OPKG_TMP_DIR "/tmp/.opkg"
|
#define OPKG_TMP_DIR "/tmp/.opkg"
|
||||||
@@ -98,6 +99,7 @@ static const string pkg_types[OM_MAX] =
|
|||||||
|
|
||||||
COPKGManager::COPKGManager(): opkg_conf('\t')
|
COPKGManager::COPKGManager(): opkg_conf('\t')
|
||||||
{
|
{
|
||||||
|
OM_ERRORS();
|
||||||
width = 80;
|
width = 80;
|
||||||
|
|
||||||
//define default dest keys
|
//define default dest keys
|
||||||
@@ -669,11 +671,21 @@ int COPKGManager::execCmd(const char *cmdstr, bool verbose, bool acknowledge)
|
|||||||
has_err = false;
|
has_err = false;
|
||||||
tmp_str.clear();
|
tmp_str.clear();
|
||||||
err_msg = "";
|
err_msg = "";
|
||||||
|
bool ok = true;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
sigc::slot1<void, string&> sl;
|
//create CShellWindow object
|
||||||
sl = sigc::mem_fun(*this, &COPKGManager::handleShellOutput);
|
CShellWindow shell(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_EVENT : 0), &res, false);
|
||||||
CShellWindow shell(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_MSG : 0), &res, false);
|
|
||||||
shell.OnShellOutputLoop.connect(sl);
|
//init slot for shell output handler with 3 args, no return value, and connect with loop handler inside of CShellWindow object
|
||||||
|
sigc::slot3<void, string*, int*, bool*> sl_shell;
|
||||||
|
sl_shell = sigc::mem_fun(*this, &COPKGManager::handleShellOutput);
|
||||||
|
shell.OnShellOutputLoop.connect(sl_shell);
|
||||||
|
#if 0
|
||||||
|
//demo for custom error message inside shell window loop
|
||||||
|
sigc::slot1<void, int*> sl1;
|
||||||
|
sl1 = sigc::mem_fun(*this, &COPKGManager::showErr);
|
||||||
|
shell.OnResultError.connect(sl1);
|
||||||
|
#endif
|
||||||
shell.exec();
|
shell.exec();
|
||||||
} else {
|
} else {
|
||||||
cmd += " 2>&1";
|
cmd += " 2>&1";
|
||||||
@@ -684,84 +696,98 @@ int COPKGManager::execCmd(const char *cmdstr, bool verbose, bool acknowledge)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
char buf[256];
|
char buf[256];
|
||||||
while (fgets(buf, sizeof(buf), f))
|
do {
|
||||||
{
|
|
||||||
string line(buf);
|
string line(buf);
|
||||||
trim(line);
|
trim(line);
|
||||||
|
handleShellOutput(&line, &res, &ok);
|
||||||
dprintf(DEBUG_INFO, "[COPKGManager] [%s - %d] %s [error %d]\n", __func__, __LINE__, line.c_str(), has_err);
|
dprintf(DEBUG_INFO, "[COPKGManager] [%s - %d] %s [error %d]\n", __func__, __LINE__, line.c_str(), has_err);
|
||||||
handleShellOutput(line);
|
} while (fgets(buf, sizeof(buf), f) && ok);
|
||||||
}
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
if (has_err){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void COPKGManager::handleShellOutput(string& cur_line)
|
void COPKGManager::handleShellOutput(string* cur_line, int* res, bool* ok)
|
||||||
{
|
{
|
||||||
size_t pos2 = cur_line.find("Collected errors:");
|
//hold current res value
|
||||||
|
int _res = *res;
|
||||||
|
|
||||||
|
//use current line
|
||||||
|
string line = *cur_line;
|
||||||
|
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] come into shell handler with res: %d\n", __func__, __LINE__, _res);
|
||||||
|
|
||||||
|
//detect any collected error
|
||||||
|
size_t pos2 = line.find("Collected errors:");
|
||||||
if (pos2 != string::npos)
|
if (pos2 != string::npos)
|
||||||
has_err = true;
|
has_err = true;
|
||||||
|
|
||||||
//check for collected errors and build a message for screen if errors available
|
//check for collected errors and set res value
|
||||||
if (has_err){
|
if (has_err){
|
||||||
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] result: %s\n", __func__, __LINE__, cur_line.c_str());
|
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] result: %s\n", __func__, __LINE__, line.c_str());
|
||||||
|
|
||||||
//trivial errors:
|
|
||||||
/*duplicate option cache: option is defined in OPKG_CL_CONFIG_OPTIONS,
|
/*duplicate option cache: option is defined in OPKG_CL_CONFIG_OPTIONS,
|
||||||
* NOTE: if found first cache option in the opkg.conf file, this will be preferred and it's not really an error!
|
* NOTE: if found first cache option in the opkg.conf file, this will be preferred and it's not really an error!
|
||||||
*/
|
*/
|
||||||
if (cur_line.find("Duplicate option cache") != string::npos){
|
if (line.find("Duplicate option cache") != string::npos){
|
||||||
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] WARNING: Duplicate option cache, please check opkg config file!\n", __func__, __LINE__);
|
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] WARNING: %s\n", __func__, __LINE__, line.c_str());
|
||||||
|
*ok = true;
|
||||||
has_err = false;
|
has_err = false;
|
||||||
|
*res = OM_SUCCESS;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
/*resolve_conffiles: already existent configfiles are not installed, but renamed in the same directory,
|
/*resolve_conffiles: already existent configfiles are not installed, but renamed in the same directory,
|
||||||
* NOTE: It's not fine but not really bad. Files should be installed separate or user can change manually
|
* NOTE: It's not fine but not really bad. Files should be installed separate or user can change manually
|
||||||
*/
|
*/
|
||||||
if (cur_line.find("Existing conffile") != string::npos){
|
if (line.find("Existing conffile") != string::npos){
|
||||||
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] WARNING: Existing conffile(s) not changed!\n", __func__, __LINE__);
|
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] WARNING: %s\n", __func__, __LINE__, line.c_str());
|
||||||
|
*ok = true;
|
||||||
has_err = false;
|
has_err = false;
|
||||||
|
*res = OM_SUCCESS;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//find obvious errors
|
|
||||||
//download error:
|
//download error:
|
||||||
if (cur_line.find("opkg_download:") != string::npos){
|
if (line.find("opkg_download:") != string::npos){
|
||||||
err_msg += "Network error! Please check your network connection!\n";
|
*res = OM_DOWNLOAD_ERR;
|
||||||
has_err = true;
|
*ok = false;
|
||||||
}
|
|
||||||
//install errors:
|
|
||||||
if (cur_line.find("opkg_install_pkg") != string::npos){
|
|
||||||
err_msg += "Update not possible!\n";
|
|
||||||
has_err = true;
|
|
||||||
}
|
|
||||||
if (cur_line.find("opkg_install_cmd") != string::npos){
|
|
||||||
err_msg += "Cannot install package!\n";
|
|
||||||
has_err = true;
|
|
||||||
}
|
|
||||||
if (cur_line.find("No space left on device") != string::npos){
|
|
||||||
err_msg += "Not enough space available!\n";
|
|
||||||
has_err = true;
|
|
||||||
}
|
|
||||||
if (has_err)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
//not enough space
|
||||||
|
if (line.find("No space left on device") != string::npos){
|
||||||
|
*res = OM_OUT_OF_SPACE_ERR;
|
||||||
|
*ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//deps
|
||||||
|
if (line.find("satisfy_dependencies") != string::npos){
|
||||||
|
*res = OM_UNSATISFIED_DEPS_ERR;
|
||||||
|
*ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//unknown error
|
||||||
|
if (*ok){
|
||||||
|
dprintf(DEBUG_NORMAL, "[COPKGManager] [%s - %d] ERROR: unhandled error %s\n", __func__, __LINE__, line.c_str());
|
||||||
|
*res = OM_UNKNOWN_ERR;
|
||||||
|
*ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//add unknown errors:
|
if (!has_err){
|
||||||
size_t pos1 = cur_line.find(" * ");
|
*ok = true;
|
||||||
if (pos1 != string::npos){
|
*res = OM_SUCCESS;
|
||||||
string str = cur_line.substr(pos1, cur_line.length()-pos1);
|
|
||||||
err_msg += str.replace(pos1, 3,"") + "\n";
|
|
||||||
has_err = true;
|
|
||||||
}
|
}
|
||||||
if (has_err)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!has_err)
|
|
||||||
tmp_str += cur_line + "\n";
|
*res = _res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void COPKGManager::showErr(int* res)
|
||||||
|
{
|
||||||
|
string err = to_string(*res);
|
||||||
|
string errtest = err_list[1].id;
|
||||||
|
DisplayErrorMessage(errtest.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void COPKGManager::showError(const char* local_msg, char* err_message, const string& command)
|
void COPKGManager::showError(const char* local_msg, char* err_message, const string& command)
|
||||||
{
|
{
|
||||||
string msg = local_msg ? string(local_msg) + "\n" : "";
|
string msg = local_msg ? string(local_msg) + "\n" : "";
|
||||||
@@ -782,7 +808,22 @@ bool COPKGManager::installPackage(const string& pkg_name, string options, bool f
|
|||||||
|
|
||||||
int r = execCmd(pkg_types[OM_INSTALL] + opts + pkg_name, true, true);
|
int r = execCmd(pkg_types[OM_INSTALL] + opts + pkg_name, true, true);
|
||||||
if (r){
|
if (r){
|
||||||
showError(g_Locale->getText(LOCALE_OPKG_FAILURE_INSTALL), strerror(errno), pkg_types[OM_INSTALL] + opts + pkg_name);
|
switch(r){
|
||||||
|
case OM_OUT_OF_SPACE_ERR:
|
||||||
|
DisplayErrorMessage("Not enough space available");
|
||||||
|
break;
|
||||||
|
case OM_DOWNLOAD_ERR:
|
||||||
|
DisplayErrorMessage("Can't download package. Check network!");
|
||||||
|
break;
|
||||||
|
case OM_UNSATISFIED_DEPS_ERR:{
|
||||||
|
int msgRet = ShowMsg("Installation", "Unsatisfied deps while installation! Try to repeat to force dependencies!", CMessageBox::mbrCancel, CMessageBox::mbYes | CMessageBox::mbNo, NULL, 600, -1);
|
||||||
|
if (msgRet == CMessageBox::mbrYes)
|
||||||
|
return installPackage(pkg_name, "--force-depends");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
showError(g_Locale->getText(LOCALE_OPKG_FAILURE_INSTALL), strerror(errno), pkg_types[OM_INSTALL] + opts + pkg_name);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
if (force_configure)
|
if (force_configure)
|
||||||
execCmd(pkg_types[OM_CONFIGURE] + getBlankPkgName(pkg_name), false, false);
|
execCmd(pkg_types[OM_CONFIGURE] + getBlankPkgName(pkg_name), false, false);
|
||||||
|
@@ -68,6 +68,34 @@ class COPKGManager : public CMenuTarget
|
|||||||
std::string *local_dir;
|
std::string *local_dir;
|
||||||
|
|
||||||
bool has_err;
|
bool has_err;
|
||||||
|
typedef struct om_error_data_t
|
||||||
|
{
|
||||||
|
std::string id;
|
||||||
|
int num;
|
||||||
|
}om_error_struct_t;
|
||||||
|
//error types
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
OM_UNKNOWN_ERR =-1,
|
||||||
|
OM_SUCCESS = 0,
|
||||||
|
OM_UNSATISFIED_DEPS_ERR = 5,
|
||||||
|
OM_DOWNLOAD_ERR = 11,
|
||||||
|
OM_CONFLICT_ERR = 12,
|
||||||
|
OM_OUT_OF_SPACE_ERR = 15,
|
||||||
|
OM_PREREM_SCRIPT_ERR = 16,
|
||||||
|
};
|
||||||
|
om_error_data_t *err_list;
|
||||||
|
void OM_ERRORS()
|
||||||
|
{
|
||||||
|
static om_error_data_t errlist[] = { { "Cannot satisfy the following dependencies" , OM_UNSATISFIED_DEPS_ERR },
|
||||||
|
{ "No space left on device" , OM_OUT_OF_SPACE_ERR },
|
||||||
|
{ "The following packages conflict" , OM_CONFLICT_ERR },
|
||||||
|
{ "Only have" , OM_OUT_OF_SPACE_ERR },
|
||||||
|
{ "prerm script for package" , OM_PREREM_SCRIPT_ERR },
|
||||||
|
} ;
|
||||||
|
err_list = errlist;
|
||||||
|
};
|
||||||
|
void showErr(int* res);
|
||||||
std::string err_msg;
|
std::string err_msg;
|
||||||
|
|
||||||
int execCmd(const char* cmdstr, bool verbose = false, bool acknowledge = false);
|
int execCmd(const char* cmdstr, bool verbose = false, bool acknowledge = false);
|
||||||
@@ -100,7 +128,7 @@ class COPKGManager : public CMenuTarget
|
|||||||
bool badpackage(std::string &s);
|
bool badpackage(std::string &s);
|
||||||
void showError(const char* local_msg, char* err_msg, const std::string& command);
|
void showError(const char* local_msg, char* err_msg, const std::string& command);
|
||||||
int doUpdate();
|
int doUpdate();
|
||||||
void handleShellOutput(std::string& cur_line);
|
void handleShellOutput(std::string* cur_line, int* res, bool* ok);
|
||||||
|
|
||||||
struct pkg {
|
struct pkg {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@@ -156,8 +156,9 @@ void CShellWindow::exec()
|
|||||||
|
|
||||||
//callback for line handler
|
//callback for line handler
|
||||||
std::string s_output = std::string((output));
|
std::string s_output = std::string((output));
|
||||||
OnShellOutputLoop(s_output, res, &ok);
|
OnShellOutputLoop(&s_output, res, &ok);
|
||||||
dprintf(DEBUG_NORMAL, "[CShellWindow] [%s - %d] res=%d ok=%d\n", __func__, __LINE__, *res, ok);
|
dprintf(DEBUG_NORMAL, "[CShellWindow] [%s - %d] res=%d ok=%d\n", __func__, __LINE__, *res, ok);
|
||||||
|
|
||||||
if (lines.size() > lines_max)
|
if (lines.size() > lines_max)
|
||||||
lines.pop_front();
|
lines.pop_front();
|
||||||
txt = "";
|
txt = "";
|
||||||
|
@@ -64,22 +64,22 @@ class CShellWindow : public sigc::trackable
|
|||||||
//Tis function should handle the shell output!
|
//Tis function should handle the shell output!
|
||||||
|
|
||||||
//declare a slot with return value as 'void' and parameter as 'string', here by rev!
|
//declare a slot with return value as 'void' and parameter as 'string', here by rev!
|
||||||
sigc::slot1<void, string&> sl;
|
sigc::slot1<void, string&> sl_shell_output;
|
||||||
|
|
||||||
//fill the slot with your member function in your class that do evaluate the output lines
|
//fill the slot with your member function in your class that do evaluate the output lines
|
||||||
sl = sigc::mem_fun(*this, &CYourClass::YourMemberFunction);
|
sl_shell_output = sigc::mem_fun(*this, &CYourClass::YourMemberFunction);
|
||||||
|
|
||||||
//create the CShellWindow object in verbose mode, important: parameter 'auto_exec' must be set to 'false', so it is possible to connect the slot before engages the exec() methode
|
//create the CShellWindow object in verbose mode, important: parameter 'auto_exec' must be set to 'false', so it is possible to connect the slot before engages the exec() methode
|
||||||
CShellWindow shell(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_MSG : 0), &res, false);
|
CShellWindow shell(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_MSG : 0), &res, false);
|
||||||
|
|
||||||
//connect slot
|
//connect slot
|
||||||
shell.OnShellOutputLoop.connect(sl);
|
shell.OnShellOutputLoop.connect(sl_shell_output);
|
||||||
|
|
||||||
//now exec...
|
//now exec...
|
||||||
shell.exec();
|
shell.exec();
|
||||||
...other code...
|
...other code...
|
||||||
*/
|
*/
|
||||||
sigc::signal<void, std::string&, int*, bool*> OnShellOutputLoop;
|
sigc::signal<void, std::string*, int*, bool*> OnShellOutputLoop;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
signal/event handler runs after task is finished.
|
signal/event handler runs after task is finished.
|
||||||
@@ -92,20 +92,22 @@ class CShellWindow : public sigc::trackable
|
|||||||
instead the default message.
|
instead the default message.
|
||||||
|
|
||||||
//declare a slot with return value as 'void' and wihout any parameter
|
//declare a slot with return value as 'void' and wihout any parameter
|
||||||
sigc::slot<void, int*> sl;
|
sigc::slot<void, int*> sl_result_err;
|
||||||
|
|
||||||
//fill the slot with your member function in your class with your action
|
//fill the slot with your member function in your class with your action
|
||||||
sl = sigc::mem_fun(*this, &CYourClass::YourMemberFunction);
|
sl_result_err = sigc::mem_fun(*this, &CYourClass::YourMemberFunction);
|
||||||
|
|
||||||
//create the CShellWindow object in verbose mode, important: parameter 'auto_exec' must be set to 'false', so it is possible to connect the slot before engages the exec() methode
|
//create the CShellWindow object in verbose mode, important: parameter 'auto_exec' must be set to 'false', so it is possible to connect the slot before engages the exec() methode
|
||||||
CShellWindow shell(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_MSG : 0), &res, false);
|
CShellWindow shell(cmd, (verbose ? CShellWindow::VERBOSE : 0) | (acknowledge ? CShellWindow::ACKNOWLEDGE_MSG : 0), &res, false);
|
||||||
|
|
||||||
//connect slot
|
//connect slot
|
||||||
shell1.OnResultError.connect(sl);
|
shell1.OnResultError.connect(sl_result_err);
|
||||||
|
|
||||||
//now exec...
|
//now exec...
|
||||||
shell.exec();
|
shell.exec();
|
||||||
...other code...
|
...other code...
|
||||||
|
|
||||||
|
Use of OnResultOk is similar with OnResultError.
|
||||||
*/
|
*/
|
||||||
sigc::signal<void, int*> OnResultError;
|
sigc::signal<void, int*> OnResultError;
|
||||||
sigc::signal<void, int*> OnResultOk;
|
sigc::signal<void, int*> OnResultOk;
|
||||||
|
Reference in New Issue
Block a user