polybar/include/utils/command.hpp

121 lines
3.1 KiB
C++

#pragma once
#include <mutex>
#include "common.hpp"
#include "components/logger.hpp"
#include "components/types.hpp"
#include "errors.hpp"
#include "utils/functional.hpp"
POLYBAR_NS
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>(
* "while read -r line; do echo data from parent process: $line; done");
* cmd->exec(false);
* cmd->writeline("Test");
* cout << cmd->readline();
* cmd->wait();
* \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{};
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);
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);
int writeline(string data);
string readline();
int get_stdout(int c);
int get_stdin(int c);
protected:
int m_stdout[2]{};
int m_stdin[2]{};
std::mutex m_pipelock{};
};
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