1
0

Implemented cmd_spawn and cmd_output actions

This commit is contained in:
Patrick Gaskin
2020-04-23 05:25:22 -04:00
parent 4c38d6af7f
commit e2c00dbbec
5 changed files with 96 additions and 3 deletions

2
.gitignore vendored
View File

@@ -5,11 +5,11 @@
/src/qtplugin.moc /src/qtplugin.moc
/src/qtplugin.o /src/qtplugin.o
/src/action_c.o
/src/failsafe.o /src/failsafe.o
/src/init.o /src/init.o
/src/config.o /src/config.o
/src/dlhook.o /src/dlhook.o
/src/action_c.o
/src/menu.o /src/menu.o
/src/action_cc.o /src/action_cc.o

View File

@@ -25,8 +25,8 @@
# nickel_setting - toggles a boolean setting # nickel_setting - toggles a boolean setting
# nickel_extras - opens one of the beta features # nickel_extras - opens one of the beta features
# nickel_misc - other stuff which isn't significant enough for its own category # nickel_misc - other stuff which isn't significant enough for its own category
# cmdline_spawn - starts another process or script in the background (TODO) # cmd_spawn - starts another process or script in the background
# cmdline_output - runs a command and displays the output (TODO) # cmd_output - runs a command and displays the output
# <arg> the argument passed to the action: # <arg> the argument passed to the action:
# dbg_syslog - the text to write # dbg_syslog - the text to write
# dbg_error - the error message # dbg_error - the error message
@@ -44,6 +44,8 @@
# misc - one of: # misc - one of:
# force_usb_connection - forces a usb connection dialog to be shown # force_usb_connection - forces a usb connection dialog to be shown
# rescan_books - forces nickel to rescan books (TODO) # rescan_books - forces nickel to rescan books (TODO)
# cmd_spawn - the command line to pass to /bin/sh -c (started in /)
# cmd_output - the timeout in milliseconds (0 < t < 10000), a colon, then the command line to pass to /bin/sh -c (started in /)
# #
# For example, you might have a configuration file in KOBOeReader/.adds/nm/mystuff like: # For example, you might have a configuration file in KOBOeReader/.adds/nm/mystuff like:
# #

View File

@@ -1,3 +1,9 @@
#include <QProcess>
#include <QString>
#include <QStringList>
#include <initializer_list>
#include <alloca.h> #include <alloca.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
@@ -150,3 +156,81 @@ extern "C" int nm_action_nickelmisc(const char *arg, char **err_out) {
NM_RETURN_OK(0); NM_RETURN_OK(0);
#undef NM_ERR_RET #undef NM_ERR_RET
} }
// TODO: stop abusing err_out to display messages, maybe add a msg_out arg
// for that kind of thing instead (I've marked those spots by returning 2 for
// now).
extern "C" int nm_action_cmdspawn(const char *arg, char **err_out) {
#define NM_ERR_RET 1
QProcess proc;
uint64_t pid;
bool ok = proc.startDetached(
QStringLiteral("/bin/sh"),
QStringList(std::initializer_list<QString>{
QStringLiteral("-c"),
QString::fromUtf8(arg),
}),
QStringLiteral("/"),
(qint64*)(&pid)
);
NM_ASSERT(ok, "could not start process");
if (*err_out)
asprintf(err_out, "Successfully started process with PID %lu.", pid);
return 2;
#undef NM_ERR_RET
}
extern "C" int nm_action_cmdoutput(const char *arg, char **err_out) {
#define NM_ERR_RET 1
char *tmp = strdup(arg);
char *cmd = tmp;
char *tmp1 = strsep(&cmd, ":"), *tmp2;
long timeout = strtol(tmp1, &tmp2, 10);
NM_ASSERT(*tmp1 && !*tmp2 && timeout > 0 && timeout < 10000, "invalid timeout '%s'", tmp1);
QProcess proc;
proc.setProcessChannelMode(QProcess::MergedChannels);
proc.setWorkingDirectory(QStringLiteral("/"));
proc.start(
QStringLiteral("/bin/sh"),
QStringList(std::initializer_list<QString>{
QStringLiteral("-c"),
QString::fromUtf8(cmd),
}),
QIODevice::ReadOnly
);
bool ok = proc.waitForFinished(timeout);
if (!ok) {
switch (proc.error()) {
case QProcess::Timedout:
NM_RETURN_ERR("could not run process: timed out");
case QProcess::FailedToStart:
NM_RETURN_ERR("could not run process: missing program or wrong permissions");
case QProcess::Crashed:
NM_RETURN_ERR("could not run process: process crashed");
default:
NM_RETURN_ERR("could not run process");
}
}
if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() != 0)
NM_RETURN_ERR("could not run process: process exited with status %d", proc.exitCode());
QString out = proc.readAllStandardOutput();
if (out.length() > 500)
out = out.left(500) + "...";
free(tmp);
if (err_out)
*err_out = strdup(qPrintable(out));
return 2;
#undef NM_ERR_RET
}

View File

@@ -4,9 +4,14 @@
extern "C" { extern "C" {
#endif #endif
// This file contains actions which need access to Qt classes, but is still
// mostly C code.
int nm_action_nickelsetting(const char *arg, char **err_out); int nm_action_nickelsetting(const char *arg, char **err_out);
int nm_action_nickelextras(const char *arg, char **err_out); int nm_action_nickelextras(const char *arg, char **err_out);
int nm_action_nickelmisc(const char *arg, char **err_out); int nm_action_nickelmisc(const char *arg, char **err_out);
int nm_action_cmdspawn(const char *arg, char **err_out);
int nm_action_cmdoutput(const char *arg, char **err_out);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -132,6 +132,8 @@ nm_config_t *nm_config_parse(char **err_out) {
else if (!strcmp(c_act, "nickel_setting")) it->act = nm_action_nickelsetting; else if (!strcmp(c_act, "nickel_setting")) it->act = nm_action_nickelsetting;
else if (!strcmp(c_act, "nickel_extras")) it->act = nm_action_nickelextras; else if (!strcmp(c_act, "nickel_extras")) it->act = nm_action_nickelextras;
else if (!strcmp(c_act, "nickel_misc")) it->act = nm_action_nickelmisc; else if (!strcmp(c_act, "nickel_misc")) it->act = nm_action_nickelmisc;
else if (!strcmp(c_act, "cmd_spawn")) it->act = nm_action_cmdspawn;
else if (!strcmp(c_act, "cmd_output")) it->act = nm_action_cmdoutput;
else RETERR("file %s: line %d: field 4: unknown action '%s'", fn, line_n, c_act); else RETERR("file %s: line %d: field 4: unknown action '%s'", fn, line_n, c_act);
// type: menu_item - field 5: argument // type: menu_item - field 5: argument