mirror of
https://github.com/Llewellynvdm/conky.git
synced 2024-11-17 10:35:10 +00:00
Merge pull request #192 from marcpayne/exec-cb-refactor
Refactor the exec callback system
This commit is contained in:
commit
d2d72e2abd
@ -99,8 +99,8 @@
|
||||
<option>default_bar_height</option>
|
||||
</command>
|
||||
</term>
|
||||
<listitem>Specify a default height for bars. This is particularly useful for execbar and
|
||||
execibar as they do not take size arguments.
|
||||
<listitem>Specify a default height for bars. If not specified,
|
||||
the default value is 6.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -109,8 +109,13 @@
|
||||
<option>default_bar_width</option>
|
||||
</command>
|
||||
</term>
|
||||
<listitem>Specify a default width for bars. This is particularly useful for execbar and
|
||||
execibar as they do not take size arguments.
|
||||
<listitem>Specify a default width for bars. If not specified,
|
||||
the default value is 0, which causes the bar to expand to
|
||||
fit the width of your Conky window. If you set
|
||||
out_to_console = true, the text version of the bar will
|
||||
actually have no width and you will need to set a
|
||||
sensible default or set the height and width of each bar
|
||||
individually.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -128,8 +133,8 @@
|
||||
<option>default_gauge_height</option>
|
||||
</command>
|
||||
</term>
|
||||
<listitem>Specify a default height for gauges. This is particularly useful for execgauge
|
||||
and execigauge as they do not take size arguments
|
||||
<listitem>Specify a default height for gauges. If not specified,
|
||||
the default value is 25.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -138,8 +143,8 @@
|
||||
<option>default_gauge_width</option>
|
||||
</command>
|
||||
</term>
|
||||
<listitem>Specify a default width for gauges. This is particularly useful for execgauge
|
||||
and execigauge as they do not take size arguments
|
||||
<listitem>Specify a default width for gauges. If not specified,
|
||||
the default value is 40.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -148,8 +153,8 @@
|
||||
<option>default_graph_height</option>
|
||||
</command>
|
||||
</term>
|
||||
<listitem>Specify a default height for graphs. This is particularly useful for execgraph
|
||||
and execigraph as they do not take size arguments
|
||||
<listitem>Specify a default height for graphs. If not specified,
|
||||
the default value is 25.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -158,8 +163,13 @@
|
||||
<option>default_graph_width</option>
|
||||
</command>
|
||||
</term>
|
||||
<listitem>Specify a default width for graphs. This is particularly useful for execgraph
|
||||
and execigraph as they do not take size arguments
|
||||
<listitem>Specify a default width for graphs. If not specified,
|
||||
the default value is 0, which causes the graph to expand to
|
||||
fit the width of your Conky window. If you set
|
||||
out_to_console = true, the text version of the graph will
|
||||
actually have no width and you will need to set a
|
||||
sensible default or set the height and width of each graph
|
||||
individually.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
@ -1118,8 +1118,8 @@
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Executes a shell command and displays the output
|
||||
in conky. warning: this takes a lot more resources than
|
||||
other variables. I'd recommend coding wanted behaviour in C
|
||||
in conky. Warning: this takes a lot more resources than
|
||||
other variables. I'd recommend coding wanted behaviour in C/C++
|
||||
and posting a patch.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
@ -1128,12 +1128,14 @@
|
||||
<command>
|
||||
<option>execbar</option>
|
||||
</command>
|
||||
<option>(height),(width)</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as exec, except if the first value return is
|
||||
a value between 0-100, it will use that number for a bar.
|
||||
The size for bars can be controlled via the
|
||||
default_bar_size config setting.
|
||||
<listitem>Same as exec, except if the first value returned is
|
||||
a value between 0-100, it will use that number to draw a
|
||||
horizontal bar. The height and width parameters are optional,
|
||||
and default to the default_bar_height and default_bar_width
|
||||
config settings, respectively.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1141,12 +1143,15 @@
|
||||
<command>
|
||||
<option>execgauge</option>
|
||||
</command>
|
||||
<option>(height),(width)</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as exec, except if the first value returned
|
||||
is a value between 0-100, it will use that number for a
|
||||
gauge. The size for gauges can be controlled via the
|
||||
default_gauge_size config setting.
|
||||
<listitem>Same as exec, except if the first value returned is
|
||||
a value between 0-100, it will use that number to draw a
|
||||
round gauge (much like a vehicle speedometer). The height and
|
||||
width parameters are optional, and default to the
|
||||
default_gauge_height and default_gauge_width config settings,
|
||||
respectively.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1154,33 +1159,61 @@
|
||||
<command>
|
||||
<option>execgraph</option>
|
||||
</command>
|
||||
<option>(-t) (-l) command</option>
|
||||
<option>command</option>
|
||||
<option>(height),(width)</option>
|
||||
<option>(gradient color 1)</option>
|
||||
<option>(gradient color 2)</option>
|
||||
<option>(scale)</option>
|
||||
<option>(-t)</option>
|
||||
<option>(-l)</option>
|
||||
</term>
|
||||
<listitem>Same as execbar, but graphs values. Uses a
|
||||
logaritmic scale when the log option (-l switch) is given
|
||||
(to see small numbers). Values still have to be between 0
|
||||
and 100. The size for graphs can be controlled via the
|
||||
default_graph_size config setting. Takes the switch '-t' to
|
||||
use a temperature gradient, which makes the gradient values
|
||||
change depending on the amplitude of a particular graph
|
||||
value (try it and see). If -t or -l is your first argument,
|
||||
you may need to preceed it by a space (' '). You may also use
|
||||
double-quotes around the exec argument should you need to execute a
|
||||
command with spaces. For example, ${execgraph "date +'%S'"} to execute
|
||||
`date +'%S'` and graph the result. Without quotes, it would simply
|
||||
print the result of `date`.
|
||||
<para /></listitem>
|
||||
<listitem>
|
||||
<para>Draws a horizontally scrolling graph with values
|
||||
from 0-100 plotted on the vertical axis. All parameters
|
||||
following the command are optional. Gradient colors can
|
||||
be specified as hexadecimal values with no 0x or #
|
||||
prefix. Use the -t switch to enable a temperature
|
||||
gradient, so that small values are "cold" with color 1
|
||||
and large values are "hot" with color 2. Without the -t
|
||||
switch, the colors produce a horizontal gradient
|
||||
spanning the width of the graph. The scale parameter
|
||||
defines the maximum value of the graph. Use the -l
|
||||
switch to enable a logarithmic scale, which helps to see
|
||||
small values. The default size for graphs can be
|
||||
controlled via the default_graph_height and
|
||||
default_graph_width config settings.</para>
|
||||
|
||||
<para>If you need to execute a command with spaces, you
|
||||
have a couple options: 1) put your command into a
|
||||
separate file, such as ~/bin/myscript.sh, and use that
|
||||
as your execgraph command. Remember to make your script
|
||||
executable! 2) Wrap your command in double-quotes. If
|
||||
you go for this option, do not try to pass the extra
|
||||
parameters for height, width, etc., as the results may
|
||||
be unexpected.</para>
|
||||
|
||||
<para>Option 1 is preferred. In the following example, we
|
||||
set up execgraph to display seconds (0-59) on a graph that
|
||||
is 50px high and 200px wide, using a temperature gradient
|
||||
with colors ranging from red for small values (FF0000) to
|
||||
yellow for large values (FFFF00). We set the scale to
|
||||
60.</para>
|
||||
|
||||
<para><command>${execgraph ~/seconds.sh 50,200 FF0000
|
||||
FFFF00 60 -t}</command></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<command>
|
||||
<option>execi</option>
|
||||
</command>
|
||||
<option>interval command</option>
|
||||
<option>interval</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as exec but with specific interval. Interval
|
||||
can't be less than update_interval in configuration. See
|
||||
also $texeci
|
||||
<listitem>Same as exec, but with a specific interval in
|
||||
seconds. The interval can't be less than the update_interval
|
||||
in your configuration. See also $texeci.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1188,9 +1221,11 @@
|
||||
<command>
|
||||
<option>execibar</option>
|
||||
</command>
|
||||
<option>interval command</option>
|
||||
<option>interval</option>
|
||||
<option>(height),(width)</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as execbar, except with an interval
|
||||
<listitem>Same as execbar, but with an interval.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1198,10 +1233,11 @@
|
||||
<command>
|
||||
<option>execigauge</option>
|
||||
</command>
|
||||
<option>interval command</option>
|
||||
<option>interval</option>
|
||||
<option>(height),(width)</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as execgauge, but takes an interval arg and
|
||||
gauges values.
|
||||
<listitem>Same as execgauge, but with an interval.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1209,11 +1245,16 @@
|
||||
<command>
|
||||
<option>execigraph</option>
|
||||
</command>
|
||||
<option>interval (-t) (-l) command</option>
|
||||
<option>interval</option>
|
||||
<option>command</option>
|
||||
<option>(height),(width)</option>
|
||||
<option>(gradient color 1)</option>
|
||||
<option>(gradient color 2)</option>
|
||||
<option>(scale)</option>
|
||||
<option>(-t)</option>
|
||||
<option>(-l)</option>
|
||||
</term>
|
||||
<listitem>Same as execgraph, but takes an interval arg and
|
||||
graphs values. If -t or -l is your first argument, you may
|
||||
need to preceed it by a space (' ').
|
||||
<listitem>Same as execgraph, but with an interval.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1224,8 +1265,8 @@
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Executes a shell command and displays the output
|
||||
in conky. warning: this takes a lot more resources than
|
||||
other variables. I'd recommend coding wanted behaviour in C
|
||||
in conky. Warning: this takes a lot more resources than
|
||||
other variables. I'd recommend coding wanted behaviour in C/C++
|
||||
and posting a patch. This differs from $exec in that it
|
||||
parses the output of the command, so you can insert things
|
||||
like ${color red}hi!${color} in your script and have it
|
||||
@ -1243,12 +1284,12 @@
|
||||
<command>
|
||||
<option>execpi</option>
|
||||
</command>
|
||||
<option>interval command</option>
|
||||
<option>interval</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as execp but with specific interval.
|
||||
Interval can't be less than update_interval in
|
||||
configuration. Note that the output from the $execpi
|
||||
command is still parsed and evaluated at every interval.
|
||||
<listitem>Same as execp, but with an interval. Note that
|
||||
the output from the $execpi command is still parsed
|
||||
and evaluated at every interval.
|
||||
<para /></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -3769,13 +3810,14 @@
|
||||
<command>
|
||||
<option>texeci</option>
|
||||
</command>
|
||||
<option>interval command</option>
|
||||
<option>interval</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Runs a command at an interval inside a thread and
|
||||
displays the output. Same as $execi, except the command is
|
||||
run inside a thread. Use this if you have a slow script to
|
||||
keep Conky updating. You should make the interval slightly
|
||||
longer then the time it takes your script to execute. For
|
||||
longer than the time it takes your script to execute. For
|
||||
example, if you have a script that take 5 seconds to
|
||||
execute, you should make the interval at least 6 seconds.
|
||||
See also $execi. This object will clean up the thread when
|
||||
@ -3789,7 +3831,8 @@
|
||||
<command>
|
||||
<option>texecpi</option>
|
||||
</command>
|
||||
<option>interval command</option>
|
||||
<option>interval</option>
|
||||
<option>command</option>
|
||||
</term>
|
||||
<listitem>Same as execpi, except the command is run inside
|
||||
a thread.
|
||||
|
@ -275,15 +275,13 @@ conky::simple_config_setting<bool> no_buffers("no_buffers", true, true);
|
||||
|
||||
void update_stuff(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* clear speeds, addresses and up status in case device was removed and
|
||||
* doesn't get updated */
|
||||
|
||||
#ifdef HAVE_OPENMP
|
||||
#pragma omp parallel for schedule(dynamic,10)
|
||||
#endif /* HAVE_OPENMP */
|
||||
for (i = 0; i < MAX_NET_INTERFACES; i++) {
|
||||
for (int i = 0; i < MAX_NET_INTERFACES; ++i) {
|
||||
if (netstats[i].dev) {
|
||||
netstats[i].up = 0;
|
||||
netstats[i].recv_speed = 0.0;
|
||||
@ -295,8 +293,10 @@ void update_stuff(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* this is a stub on all platforms except solaris */
|
||||
prepare_update();
|
||||
|
||||
/* if you registered a callback with conky::register_cb, this will run it */
|
||||
conky::run_all_callbacks();
|
||||
|
||||
/* XXX: move the following into the update_meminfo() functions? */
|
||||
|
12
src/conky.cc
12
src/conky.cc
@ -909,19 +909,17 @@ static void generate_text(void)
|
||||
{
|
||||
char *p;
|
||||
unsigned int i, j, k;
|
||||
|
||||
special_count = 0;
|
||||
|
||||
/* update info */
|
||||
|
||||
current_update_time = get_time();
|
||||
|
||||
/* clears netstats info, calls conky::run_all_callbacks(), and changes
|
||||
* some info.mem entries */
|
||||
update_stuff();
|
||||
|
||||
/* add things to the buffer */
|
||||
|
||||
/* generate text */
|
||||
|
||||
/* populate the text buffer; generate_text_internal() iterates through
|
||||
* global_root_object (an instance of the text_object struct) and calls
|
||||
* any callbacks that were set on startup by construct_text_object(). */
|
||||
p = text_buffer;
|
||||
|
||||
generate_text_internal(p, max_user_text.get(*state), global_root_object);
|
||||
|
95
src/core.cc
95
src/core.cc
@ -742,69 +742,79 @@ struct text_object *construct_text_object(char *s, const char *arg,
|
||||
scan_no_update(obj, arg);
|
||||
obj->callbacks.print = &print_no_update;
|
||||
obj->callbacks.free = &free_no_update;
|
||||
END OBJ(exec, 0)
|
||||
scan_exec_arg(obj, arg);
|
||||
END OBJ_ARG(exec, 0, "exec needs arguments: <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXEC);
|
||||
obj->parse = false;
|
||||
obj->thread = false;
|
||||
register_exec(obj);
|
||||
obj->callbacks.print = &print_exec;
|
||||
obj->callbacks.free = &free_exec;
|
||||
END OBJ(execp, 0)
|
||||
scan_exec_arg(obj, arg);
|
||||
END OBJ_ARG(execi, 0, "execi needs arguments: <interval> <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXECI);
|
||||
obj->parse = false;
|
||||
obj->thread = false;
|
||||
register_execi(obj);
|
||||
obj->callbacks.print = &print_exec;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(execp, 0, "execp needs arguments: <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXEC);
|
||||
obj->parse = true;
|
||||
obj->thread = false;
|
||||
register_exec(obj);
|
||||
obj->callbacks.print = &print_exec;
|
||||
obj->callbacks.free = &free_exec;
|
||||
END OBJ(execbar, 0)
|
||||
scan_exec_arg(obj, arg);
|
||||
END OBJ_ARG(execpi, 0, "execpi needs arguments: <interval> <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXECI);
|
||||
obj->parse = true;
|
||||
obj->thread = false;
|
||||
register_execi(obj);
|
||||
obj->callbacks.print = &print_exec;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(execbar, 0, "execbar needs arguments: [height],[width] <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXEC | EF_BAR);
|
||||
register_exec(obj);
|
||||
obj->callbacks.barval = &execbarval;
|
||||
obj->callbacks.free = &free_exec;
|
||||
END OBJ(execgauge, 0)
|
||||
scan_exec_arg(obj, arg);
|
||||
END OBJ_ARG(execibar, 0, "execibar needs arguments: <interval> [height],[width] <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXECI | EF_BAR);
|
||||
register_execi(obj);
|
||||
obj->callbacks.barval = &execbarval;
|
||||
obj->callbacks.free = &free_execi;
|
||||
#ifdef BUILD_X11
|
||||
END OBJ_ARG(execgauge, 0, "execgauge needs arguments: [height],[width] <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXEC | EF_GAUGE);
|
||||
register_exec(obj);
|
||||
obj->callbacks.gaugeval = &execbarval;
|
||||
obj->callbacks.free = &free_exec;
|
||||
#ifdef BUILD_X11
|
||||
END OBJ(execgraph, 0)
|
||||
scan_execgraph_arg(obj, arg);
|
||||
END OBJ_ARG(execigauge, 0, "execigauge needs arguments: <interval> [height],[width] <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXECI | EF_GAUGE);
|
||||
register_execi(obj);
|
||||
obj->callbacks.gaugeval = &execbarval;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(execgraph, 0, "execgraph needs arguments: <command> [height],[width] [color1] [color2] [scale] [-t|-l]")
|
||||
scan_exec_arg(obj, arg, EF_EXEC | EF_GRAPH);
|
||||
register_exec(obj);
|
||||
obj->callbacks.graphval = &execbarval;
|
||||
obj->callbacks.free = &free_exec;
|
||||
#endif /* BUILD_X11 */
|
||||
END OBJ_ARG(execibar, 0, "execibar needs arguments")
|
||||
scan_execi_bar_arg(obj, arg);
|
||||
obj->callbacks.barval = &execi_barval;
|
||||
obj->callbacks.free = &free_execi;
|
||||
#ifdef BUILD_X11
|
||||
END OBJ_ARG(execigraph, 0, "execigraph needs arguments")
|
||||
scan_execgraph_arg(obj, arg);
|
||||
obj->callbacks.graphval = &execi_barval;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(execigauge, 0, "execigauge needs arguments")
|
||||
scan_execi_gauge_arg(obj, arg);
|
||||
obj->callbacks.gaugeval = &execi_barval;
|
||||
END OBJ_ARG(execigraph, 0, "execigraph needs arguments: <interval> <command> [height],[width] [color1] [color2] [scale] [-t|-l]")
|
||||
scan_exec_arg(obj, arg, EF_EXECI | EF_GRAPH);
|
||||
register_execi(obj);
|
||||
obj->callbacks.graphval = &execbarval;
|
||||
obj->callbacks.free = &free_execi;
|
||||
#endif /* BUILD_X11 */
|
||||
END OBJ_ARG(execi, 0, "execi needs arguments")
|
||||
scan_execi_arg(obj, arg);
|
||||
obj->parse = false;
|
||||
obj->thread = false;
|
||||
obj->callbacks.print = &print_execi;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(execpi, 0, "execpi needs arguments")
|
||||
scan_execi_arg(obj, arg);
|
||||
obj->parse = true;
|
||||
obj->thread = false;
|
||||
obj->callbacks.print = &print_execi;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(texeci, 0, "texeci needs arguments")
|
||||
scan_execi_arg(obj, arg);
|
||||
END OBJ_ARG(texeci, 0, "texeci needs arguments: <interval> <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXECI);
|
||||
obj->parse = false;
|
||||
obj->thread = true;
|
||||
obj->callbacks.print = &print_execi;
|
||||
register_execi(obj);
|
||||
obj->callbacks.print = &print_exec;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ_ARG(texecpi, 0, "texecpi needs arguments")
|
||||
scan_execi_arg(obj, arg);
|
||||
END OBJ_ARG(texecpi, 0, "texecpi needs arguments: <interval> <command>")
|
||||
scan_exec_arg(obj, arg, EF_EXECI);
|
||||
obj->parse = true;
|
||||
obj->thread = true;
|
||||
obj->callbacks.print = &print_execi;
|
||||
register_execi(obj);
|
||||
obj->callbacks.print = &print_exec;
|
||||
obj->callbacks.free = &free_execi;
|
||||
END OBJ(fs_bar, &update_fs_stats)
|
||||
init_fs_bar(obj, arg);
|
||||
@ -2071,7 +2081,6 @@ void free_text_objects(struct text_object *root)
|
||||
if(root && root->prev) {
|
||||
for (obj = root->prev; obj; obj = root->prev) {
|
||||
root->prev = obj->prev;
|
||||
|
||||
if (obj->callbacks.free) {
|
||||
(*obj->callbacks.free)(obj);
|
||||
}
|
||||
|
304
src/exec.cc
304
src/exec.cc
@ -30,30 +30,17 @@
|
||||
|
||||
#include "conky.h"
|
||||
#include "core.h"
|
||||
#include "exec.h"
|
||||
#include "logging.h"
|
||||
#include "specials.h"
|
||||
#include "text_object.h"
|
||||
#include "update-cb.hh"
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include "update-cb.hh"
|
||||
|
||||
namespace {
|
||||
class exec_cb: public conky::callback<std::string, std::string> {
|
||||
typedef conky::callback<std::string, std::string> Base;
|
||||
|
||||
protected:
|
||||
virtual void work();
|
||||
|
||||
public:
|
||||
exec_cb(uint32_t period, bool wait, const std::string &cmd)
|
||||
: Base(period, wait, Base::Tuple(cmd))
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
struct execi_data {
|
||||
float interval;
|
||||
@ -70,14 +57,14 @@ static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
|
||||
|
||||
//by running pipe after the strcmp's we make sure that we don't have to create a pipe
|
||||
//and close the ends if mode is something illegal
|
||||
if(strcmp(mode, "r") == 0) {
|
||||
if(pipe(ends) != 0) {
|
||||
if (strcmp(mode, "r") == 0) {
|
||||
if (pipe(ends) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
parentend = ends[0];
|
||||
childend = ends[1];
|
||||
} else if(strcmp(mode, "w") == 0) {
|
||||
if(pipe(ends) != 0) {
|
||||
} else if (strcmp(mode, "w") == 0) {
|
||||
if (pipe(ends) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
parentend = ends[1];
|
||||
@ -85,17 +72,18 @@ static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*child = fork();
|
||||
if(*child == -1) {
|
||||
if (*child == -1) {
|
||||
close(parentend);
|
||||
close(childend);
|
||||
return NULL;
|
||||
} else if(*child > 0) {
|
||||
} else if (*child > 0) {
|
||||
close(childend);
|
||||
waitpid(*child, NULL, 0);
|
||||
} else {
|
||||
//don't read from both stdin and pipe or write to both stdout and pipe
|
||||
if(childend == ends[0]) {
|
||||
if (childend == ends[0]) {
|
||||
close(0);
|
||||
} else {
|
||||
close(1);
|
||||
@ -110,9 +98,21 @@ static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
|
||||
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
|
||||
_exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail)
|
||||
}
|
||||
|
||||
return fdopen(parentend, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command and stores the result
|
||||
*
|
||||
* This function is called automatically, either once every update
|
||||
* interval, or at specific intervals in the case of execi commands.
|
||||
* conky::run_all_callbacks() handles this. In order for this magic to
|
||||
* happen, we must register a callback with conky::register_cb<exec_cb>()
|
||||
* and store it somewhere, such as obj->exec_handle. To retrieve the
|
||||
* results, use the stored callback to call get_result_copy(), which
|
||||
* returns a std::string.
|
||||
*/
|
||||
void exec_cb::work()
|
||||
{
|
||||
pid_t childpid;
|
||||
@ -120,37 +120,45 @@ void exec_cb::work()
|
||||
std::shared_ptr<FILE> fp;
|
||||
char b[0x1000];
|
||||
|
||||
if(FILE *t = pid_popen(std::get<0>(tuple).c_str(), "r", &childpid))
|
||||
if (FILE *t = pid_popen(std::get<0>(tuple).c_str(), "r", &childpid))
|
||||
fp.reset(t, fclose);
|
||||
else
|
||||
return;
|
||||
|
||||
while(!feof(fp.get()) && !ferror(fp.get())) {
|
||||
while (!feof(fp.get()) && !ferror(fp.get())) {
|
||||
int length = fread(b, 1, sizeof b, fp.get());
|
||||
buf.append(b, length);
|
||||
}
|
||||
|
||||
if(*buf.rbegin() == '\n')
|
||||
if (*buf.rbegin() == '\n')
|
||||
buf.resize(buf.size()-1);
|
||||
|
||||
std::lock_guard<std::mutex> l(result_mutex);
|
||||
result = buf;
|
||||
}
|
||||
|
||||
//remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
|
||||
//string has to end with \0 and it's length should fit in a int
|
||||
#define BACKSPACE 8
|
||||
static void remove_deleted_chars(char *string){
|
||||
int i = 0;
|
||||
while(string[i] != 0){
|
||||
if(string[i] == BACKSPACE){
|
||||
if(i != 0){
|
||||
while (string[i] != 0) {
|
||||
if (string[i] == BACKSPACE) {
|
||||
if (i != 0) {
|
||||
strcpy( &(string[i-1]), &(string[i+1]) );
|
||||
i--;
|
||||
}else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
|
||||
}else i++;
|
||||
} else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
|
||||
} else i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses command output to find a number between 0.0 and 100.0.
|
||||
* Used by ${exec[i]{bar,gauge,graph}}.
|
||||
*
|
||||
* @param[in] buf output of a command executed by an exec_cb object
|
||||
* @return number between 0.0 and 100.0
|
||||
*/
|
||||
static inline double get_barnum(const char *buf)
|
||||
{
|
||||
double barnum;
|
||||
@ -165,125 +173,177 @@ static inline double get_barnum(const char *buf)
|
||||
"therefore it will be ignored");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return barnum;
|
||||
}
|
||||
|
||||
void scan_exec_arg(struct text_object *obj, const char *arg)
|
||||
{
|
||||
/* XXX: do real bar parsing here */
|
||||
scan_bar(obj, "", 100);
|
||||
obj->data.s = strndup(arg ? arg : "", text_buffer_size.get(*state));
|
||||
}
|
||||
|
||||
void scan_execi_arg(struct text_object *obj, const char *arg)
|
||||
{
|
||||
struct execi_data *ed;
|
||||
int n;
|
||||
|
||||
ed = new execi_data;
|
||||
|
||||
if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
|
||||
NORM_ERR("${execi* <interval> command}");
|
||||
delete ed;
|
||||
return;
|
||||
}
|
||||
ed->cmd = strndup(arg + n, text_buffer_size.get(*state));
|
||||
obj->data.opaque = ed;
|
||||
}
|
||||
|
||||
void scan_execi_bar_arg(struct text_object *obj, const char *arg)
|
||||
{
|
||||
/* XXX: do real bar parsing here */
|
||||
scan_bar(obj, "", 100);
|
||||
scan_execi_arg(obj, arg);
|
||||
}
|
||||
|
||||
#ifdef BUILD_X11
|
||||
void scan_execi_gauge_arg(struct text_object *obj, const char *arg)
|
||||
{
|
||||
/* XXX: do real gauge parsing here */
|
||||
scan_gauge(obj, "", 100);
|
||||
scan_execi_arg(obj, arg);
|
||||
}
|
||||
|
||||
void scan_execgraph_arg(struct text_object *obj, const char *arg)
|
||||
{
|
||||
struct execi_data *ed;
|
||||
char *buf;
|
||||
|
||||
ed = new execi_data;
|
||||
memset(ed, 0, sizeof(struct execi_data));
|
||||
|
||||
buf = scan_graph(obj, arg, 100);
|
||||
if (!buf) {
|
||||
NORM_ERR("missing command argument to execgraph object");
|
||||
return;
|
||||
}
|
||||
ed->cmd = buf;
|
||||
obj->data.opaque = ed;
|
||||
}
|
||||
#endif /* BUILD_X11 */
|
||||
|
||||
/**
|
||||
* Store command output in p. For execp objects, we process the output
|
||||
* in case it contains special commands like ${color}
|
||||
*
|
||||
* @param[in] buffer the output of a command
|
||||
* @param[in] obj text_object that specifies whether or not to parse
|
||||
* @param[out] p the string in which we store command output
|
||||
* @param[in] p_max_size the maximum size of p...
|
||||
*/
|
||||
void fill_p(const char *buffer, struct text_object *obj, char *p, int p_max_size) {
|
||||
if(obj->parse == true) {
|
||||
if (obj->parse == true) {
|
||||
evaluate(buffer, p, p_max_size);
|
||||
} else snprintf(p, p_max_size, "%s", buffer);
|
||||
} else {
|
||||
snprintf(p, p_max_size, "%s", buffer);
|
||||
}
|
||||
|
||||
remove_deleted_chars(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses arg to find the command to be run, as well as special options
|
||||
* like height, width, color, and update interval
|
||||
*
|
||||
* @param[out] obj stores the command and an execi_data structure (if applicable)
|
||||
* @param[in] arg the argument to an ${exec*} object
|
||||
* @param[in] execflag bitwise flag used to specify the exec variant we need to process
|
||||
*/
|
||||
void scan_exec_arg(struct text_object *obj, const char *arg, unsigned int execflag)
|
||||
{
|
||||
const char *cmd = arg;
|
||||
struct execi_data *ed;
|
||||
|
||||
/* in case we have an execi object, we need to parse out the interval */
|
||||
if (execflag & EF_EXECI) {
|
||||
ed = new execi_data;
|
||||
int n;
|
||||
|
||||
/* store the interval in ed->interval */
|
||||
if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
|
||||
NORM_ERR("missing execi interval: ${execi* <interval> command}");
|
||||
delete ed;
|
||||
ed = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* set cmd to everything after the interval */
|
||||
cmd = strndup(arg + n, text_buffer_size.get(*state));
|
||||
}
|
||||
|
||||
/* parse any special options for the graphical exec types */
|
||||
if (execflag & EF_BAR) {
|
||||
cmd = scan_bar(obj, cmd, 100);
|
||||
#ifdef BUILD_X11
|
||||
} else if (execflag & EF_GAUGE) {
|
||||
cmd = scan_gauge(obj, cmd, 100);
|
||||
} else if (execflag & EF_GRAPH) {
|
||||
cmd = scan_graph(obj, cmd, 100);
|
||||
if (!cmd) {
|
||||
NORM_ERR("error parsing arguments to execgraph object");
|
||||
}
|
||||
#endif /* BUILD_X11 */
|
||||
}
|
||||
|
||||
/* finally, store the resulting command, or an empty string if something went wrong */
|
||||
if (execflag & EF_EXEC) {
|
||||
obj->data.s = strndup(cmd ? cmd : "", text_buffer_size.get(*state));
|
||||
} else if (execflag & EF_EXECI) {
|
||||
ed->cmd = strndup(cmd ? cmd : "", text_buffer_size.get(*state));
|
||||
obj->data.opaque = ed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an exec_cb object using the command that we have parsed
|
||||
*
|
||||
* @param[out] obj stores the callback handle
|
||||
*/
|
||||
void register_exec(struct text_object *obj)
|
||||
{
|
||||
if (obj->data.s && obj->data.s[0]) {
|
||||
obj->exec_handle = new conky::callback_handle<exec_cb>(
|
||||
conky::register_cb<exec_cb>(1, true, obj->data.s));
|
||||
} else {
|
||||
DBGP("unable to register exec callback");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an exec_cb object using the command that we have parsed.
|
||||
*
|
||||
* This version takes care of execi intervals. Note that we depend on
|
||||
* obj->thread, so be sure to run this function *after* setting obj->thread.
|
||||
*
|
||||
* @param[out] obj stores the callback handle
|
||||
*/
|
||||
void register_execi(struct text_object *obj)
|
||||
{
|
||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
||||
|
||||
if (ed && ed->cmd && ed->cmd[0]) {
|
||||
uint32_t period = std::max(lround(ed->interval/active_update_interval()), 1l);
|
||||
obj->exec_handle = new conky::callback_handle<exec_cb>(
|
||||
conky::register_cb<exec_cb>(period, !obj->thread, ed->cmd));
|
||||
} else {
|
||||
DBGP("unable to register execi callback");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of an exec_cb object (command output)
|
||||
*
|
||||
* @param[in] obj holds an exec_handle, assuming one was registered
|
||||
* @param[out] p the string in which we store command output
|
||||
* @param[in] p_max_size the maximum size of p...
|
||||
*/
|
||||
void print_exec(struct text_object *obj, char *p, int p_max_size)
|
||||
{
|
||||
auto cb = conky::register_cb<exec_cb>(1, true, obj->data.s);
|
||||
fill_p(cb->get_result_copy().c_str(), obj, p, p_max_size);
|
||||
}
|
||||
|
||||
void print_execi(struct text_object *obj, char *p, int p_max_size)
|
||||
{
|
||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
||||
|
||||
if (!ed)
|
||||
return;
|
||||
|
||||
uint32_t period = std::max(lround(ed->interval/active_update_interval()), 1l);
|
||||
|
||||
auto cb = conky::register_cb<exec_cb>(period, !obj->thread, ed->cmd);
|
||||
|
||||
fill_p(cb->get_result_copy().c_str(), obj, p, p_max_size);
|
||||
if (obj->exec_handle) {
|
||||
fill_p((*obj->exec_handle)->get_result_copy().c_str(), obj, p, p_max_size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results of a graphical (bar, gauge, graph) exec_cb object
|
||||
*
|
||||
* @param[in] obj hold an exec_handle, assuming one was registered
|
||||
* @return a value between 0.0 and 100.0
|
||||
*/
|
||||
double execbarval(struct text_object *obj)
|
||||
{
|
||||
auto cb = conky::register_cb<exec_cb>(1, true, obj->data.s);
|
||||
return get_barnum(cb->get_result_copy().c_str());
|
||||
}
|
||||
|
||||
double execi_barval(struct text_object *obj)
|
||||
{
|
||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
||||
|
||||
if (!ed)
|
||||
return 0;
|
||||
|
||||
uint32_t period = std::max(lround(ed->interval/active_update_interval()), 1l);
|
||||
|
||||
auto cb = conky::register_cb<exec_cb>(period, !obj->thread, ed->cmd);
|
||||
|
||||
return get_barnum(cb->get_result_copy().c_str());
|
||||
if (obj->exec_handle) {
|
||||
return get_barnum((*obj->exec_handle)->get_result_copy().c_str());
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free up any dynamically allocated data
|
||||
*
|
||||
* @param[in] obj holds the data that we need to free up
|
||||
*/
|
||||
void free_exec(struct text_object *obj)
|
||||
{
|
||||
free_and_zero(obj->data.s);
|
||||
delete obj->exec_handle;
|
||||
obj->exec_handle = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free up any dynamically allocated data, specifically for execi objects
|
||||
*
|
||||
* @param[in] obj holds the data that we need to free up
|
||||
*/
|
||||
void free_execi(struct text_object *obj)
|
||||
{
|
||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
||||
|
||||
/* if ed is NULL, there is nothing to do */
|
||||
if (!ed)
|
||||
return;
|
||||
|
||||
delete obj->exec_handle;
|
||||
obj->exec_handle = NULL;
|
||||
|
||||
free_and_zero(ed->cmd);
|
||||
delete ed;
|
||||
ed = NULL;
|
||||
obj->data.opaque = NULL;
|
||||
}
|
||||
|
51
src/exec.h
51
src/exec.h
@ -33,15 +33,52 @@
|
||||
|
||||
#include "text_object.h"
|
||||
|
||||
void scan_exec_arg(struct text_object *, const char *);
|
||||
void scan_execi_arg(struct text_object *, const char *);
|
||||
void scan_execi_bar_arg(struct text_object *, const char *);
|
||||
void scan_execi_gauge_arg(struct text_object *, const char *);
|
||||
void scan_execgraph_arg(struct text_object *, const char *);
|
||||
/**
|
||||
* A callback that executes a command and stores the output as a std::string.
|
||||
*
|
||||
* Important note: if more than one exec callback uses the same command,
|
||||
* then only ONE callback is actually stored. This saves space. However,
|
||||
* suppose we have the following ${exec} objects in our conky.text:
|
||||
*
|
||||
* ${exec ~/bin/foo.sh}
|
||||
* ${execi 10 ~/bin/foo.sh}
|
||||
*
|
||||
* To the callback system, these are identical! Furthermore, the callback
|
||||
* with the smallest period/interval is the one that is stored. So the execi
|
||||
* command will in fact run on every update interval, rather than every
|
||||
* ten seconds as one would expect.
|
||||
*/
|
||||
class exec_cb: public conky::callback<std::string, std::string> {
|
||||
typedef conky::callback<std::string, std::string> Base;
|
||||
|
||||
protected:
|
||||
virtual void work();
|
||||
|
||||
public:
|
||||
exec_cb(uint32_t period, bool wait, const std::string &cmd)
|
||||
: Base(period, wait, Base::Tuple(cmd))
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags used to identify the different types of exec commands during
|
||||
* parsing by scan_exec_arg(). These can be used individually or combined.
|
||||
* For example, to parse an ${execgraph} object, we pass EF_EXEC | EF_GRAPH
|
||||
* as the last argument to scan_exec_arg().
|
||||
*/
|
||||
enum {
|
||||
EF_EXEC = (1 << 0),
|
||||
EF_EXECI = (1 << 1),
|
||||
EF_BAR = (1 << 2),
|
||||
EF_GRAPH = (1 << 3),
|
||||
EF_GAUGE = (1 << 4)
|
||||
};
|
||||
|
||||
void scan_exec_arg(struct text_object *, const char *, unsigned int);
|
||||
void register_exec(struct text_object *);
|
||||
void register_execi(struct text_object *);
|
||||
void print_exec(struct text_object *, char *, int);
|
||||
void print_execi(struct text_object *, char *, int);
|
||||
double execbarval(struct text_object *);
|
||||
double execi_barval(struct text_object *);
|
||||
void free_exec(struct text_object *);
|
||||
void free_execi(struct text_object *);
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "config.h" /* for the defines */
|
||||
#include "specials.h" /* enum special_types */
|
||||
#include "update-cb.hh"
|
||||
#include "exec.h"
|
||||
|
||||
/* text object callbacks */
|
||||
struct obj_cb {
|
||||
@ -87,12 +88,24 @@ public:
|
||||
: Base(period, true, Base::Tuple(fn))
|
||||
{}
|
||||
};
|
||||
typedef conky::callback_handle<legacy_cb> legacy_cb_handle;
|
||||
|
||||
typedef conky::callback_handle<legacy_cb> legacy_cb_handle;
|
||||
typedef conky::callback_handle<exec_cb> exec_cb_handle;
|
||||
|
||||
/**
|
||||
* This is where Conky collects information on the conky.text objects in your config
|
||||
*
|
||||
* During startup and reload, objects are parsed and callbacks are set. Note that
|
||||
* there are currently two types of callback: obj_cb (old style) and
|
||||
* conky::callback (new style). On each update interval, generate_text_internal()
|
||||
* in conky.cc traverses the list of text_objects and calls the old callbacks.
|
||||
* The new style callbacks are run separately by conky::run_all_callbacks().
|
||||
*/
|
||||
struct text_object {
|
||||
struct text_object *next, *prev; /* doubly linked list of text objects */
|
||||
struct text_object *sub; /* for objects parsing text into objects */
|
||||
struct text_object *ifblock_next; /* jump target for ifblock objects */
|
||||
|
||||
union {
|
||||
void *opaque; /* new style generic per object data */
|
||||
char *s; /* some string */
|
||||
@ -102,11 +115,16 @@ struct text_object {
|
||||
|
||||
void *special_data;
|
||||
long line;
|
||||
struct obj_cb callbacks;
|
||||
bool parse; //if this true then data.s should still be parsed
|
||||
bool thread; //if this true then data.s should be set by a seperate thread
|
||||
bool parse; /* if true then data.s should still be parsed */
|
||||
bool thread; /* if true then data.s should be set by a seperate thread */
|
||||
|
||||
legacy_cb_handle *cb_handle;
|
||||
struct obj_cb callbacks;
|
||||
|
||||
/* Each _cb_handle is a std::shared_ptr with very tight restrictions on
|
||||
* construction. For now, it is necessary to store them here as regular
|
||||
* pointers so we can instantiate them later. */
|
||||
exec_cb_handle *exec_handle;
|
||||
legacy_cb_handle *cb_handle;
|
||||
};
|
||||
|
||||
/* text object list helpers */
|
||||
|
@ -77,6 +77,12 @@ namespace conky {
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a callback is not successfully inserted into the set, it must have
|
||||
* the same hash as an existing callback. If this is so, merge the incoming
|
||||
* callback with the one that prevented insertion. Keep the smaller of the
|
||||
* two periods.
|
||||
*/
|
||||
void callback_base::merge(callback_base &&other)
|
||||
{
|
||||
if(other.period < period) {
|
||||
@ -87,10 +93,14 @@ namespace conky {
|
||||
unused = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a callback (i.e. insert it into the callbacks set)
|
||||
*/
|
||||
callback_base::handle callback_base::do_register_cb(const handle &h)
|
||||
{
|
||||
const auto &p = callbacks.insert(h);
|
||||
|
||||
/* insertion failed; callback already exists */
|
||||
if(not p.second)
|
||||
(*p.first)->merge(std::move(*h));
|
||||
|
||||
@ -134,7 +144,10 @@ namespace conky {
|
||||
for(auto i = callback_base::callbacks.begin(); i != callback_base::callbacks.end(); ) {
|
||||
callback_base &cb = **i;
|
||||
|
||||
/* check whether enough update intervals have elapsed (up to period) */
|
||||
if(cb.remaining-- == 0) {
|
||||
/* run the callback as long as someone holds a pointer to it;
|
||||
* if no one owns the callback, run it at most UNUSED_MAX times */
|
||||
if(!i->unique() || ++cb.unused < UNUSED_MAX) {
|
||||
cb.remaining = cb.period-1;
|
||||
cb.run();
|
||||
|
@ -56,13 +56,13 @@ namespace conky {
|
||||
|
||||
semaphore sem_start;
|
||||
std::thread *thread;
|
||||
const size_t hash;
|
||||
uint32_t period;
|
||||
uint32_t remaining;
|
||||
const size_t hash; /* used to determined callback uniqueness */
|
||||
uint32_t period; /* how often to run a callback */
|
||||
uint32_t remaining; /* update intervals remaining until we can run a callback */
|
||||
std::pair<int, int> pipefd;
|
||||
const bool wait;
|
||||
bool done;
|
||||
uint8_t unused;
|
||||
const bool wait; /* whether or not to wait for a callback to finish */
|
||||
bool done; /* if true, callback is being stopped and destroyed */
|
||||
uint8_t unused; /* number of update intervals during which no one owns a callback */
|
||||
|
||||
callback_base(const callback_base &) = delete;
|
||||
callback_base& operator=(const callback_base &) = delete;
|
||||
@ -159,6 +159,9 @@ namespace conky {
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback uniqueness is determined by the hash computed here.
|
||||
*/
|
||||
namespace priv {
|
||||
template<size_t pos, typename... Elements>
|
||||
struct hash_tuple {
|
||||
|
Loading…
Reference in New Issue
Block a user