polybar-dwm/include/utils/command.hpp
Patrick Ziegler 444120e664
script: Fix concurrency issues (#2518)
Fixes #1978

* Move tail and non-tail handler to method

Defining them in the constructor is ugly.

* script: Iterate over defined actions instead of fixed list

* Separate running logic and lock m_output

* Include POLYBAR_FLAGS in linker flags

* Stop using m_prev in script_runner

* Join module threads in stop function

Joining in the destructor may lead to UB because the subclass is already
deconstructed but the threads may still require it to be around (e.g.
for calling any functions on the instance)

* Cleanup script module

* Update changelog

* Remove AfterReturn class

* Remove m_stopping from script module

* Fix polybar not reading the entire line from child process.

For every `readline` call we created a new fd_streambuf. This means once
`readline` returns, the streambuf is destructed and and pending data in
its temporary buffer discarded and we never actually read it.

* Remove unused includes
2021-10-03 01:27:11 +02:00

112 lines
2.9 KiB
C++

#pragma once
#include "common.hpp"
#include "components/logger.hpp"
#include "components/types.hpp"
#include "errors.hpp"
#include "utils/functional.hpp"
POLYBAR_NS
template <typename T>
class fd_stream;
DEFINE_ERROR(command_error);
/**
* Wrapper used to execute command in a subprocess.
* In-/output streams are opened to enable ipc.
* If the command is created using command_util::make_command<output_policy::REDIRECTED>, the child streams are
* redirected and you can read the program output or write into the program input.
*
* If the command is created using command_util::make_command<output_policy::IGNORED>, the output is not redirected and
* you can't communicate with the child program.
*
* Example usage:
*
* \code cpp
* auto cmd = command_util::make_command<output_policy::REDIRECTED>("cat /etc/rc.local");
* cmd->exec();
* cmd->tail([](string s) { std::cout << s << std::endl; });
* \endcode
*
* \code cpp
* auto cmd = command_util::make_command<output_policy::REDIRECTED>("for i in 1 2 3; do echo $i; done");
* cmd->exec();
* cout << cmd->readline(); // 1
* cout << cmd->readline() << cmd->readline(); // 23
* \endcode
*
* \code cpp
* auto cmd = command_util::make_command<output_policy::IGNORED>("ping kernel.org");
* int status = cmd->exec();
* \endcode
*/
template <output_policy>
class command;
template <>
class command<output_policy::IGNORED> {
public:
explicit command(const logger& logger, string cmd);
command(const command&) = delete;
~command();
command& operator=(const command&) = delete;
int exec(bool wait_for_completion = true);
void terminate();
bool is_running();
int wait();
pid_t get_pid();
int get_exit_status();
protected:
const logger& m_log;
string m_cmd;
pid_t m_forkpid{-1};
int m_forkstatus{-1};
};
template <>
class command<output_policy::REDIRECTED> : private command<output_policy::IGNORED> {
public:
explicit command(const logger& logger, string cmd);
command(const command&) = delete;
~command();
command& operator=(const command&) = delete;
int exec(bool wait_for_completion = true, const vector<pair<string, string>>& env = {});
using command<output_policy::IGNORED>::terminate;
using command<output_policy::IGNORED>::is_running;
using command<output_policy::IGNORED>::wait;
using command<output_policy::IGNORED>::get_pid;
using command<output_policy::IGNORED>::get_exit_status;
void tail(callback<string> cb);
string readline();
int get_stdout(int c);
int get_stdin(int c);
protected:
int m_stdout[2]{0, 0};
int m_stdin[2]{0, 0};
unique_ptr<fd_stream<std::istream>> m_stdout_reader{nullptr};
};
namespace command_util {
template <output_policy OutputType, typename... Args>
unique_ptr<command<OutputType>> make_command(Args&&... args) {
return std::make_unique<command<OutputType>>(logger::make(), forward<Args>(args)...);
}
} // namespace command_util
POLYBAR_NS_END