feat(ipc): Add polybar-msg to facilitate ipc messaging
This commit is contained in:
parent
0f0d786cbd
commit
79856d7ed2
4
build.sh
4
build.sh
@ -28,6 +28,7 @@ function main
|
||||
mkdir ./build || msg_err "Failed to create build dir"
|
||||
cd ./build || msg_err "Failed to enter build dir"
|
||||
|
||||
local build_ipc_msg="ON"
|
||||
local enable_alsa="ON"
|
||||
local enable_i3="ON"
|
||||
local enable_network="ON"
|
||||
@ -46,6 +47,8 @@ function main
|
||||
[[ "${p^^}" != "Y" ]] && enable_mpd="OFF"
|
||||
read -r -p "$(msg "Include support for \"internal/github\" (requires libcurl) ---------- [Y/n]: ")" -n 1 p && echo
|
||||
[[ "${p^^}" != "Y" ]] && enable_curl="OFF"
|
||||
read -r -p "$(msg "Build \"polybar-msg\" used to send ipc messages --------------------- [Y/n]: ")" -n 1 p && echo
|
||||
[[ "${p^^}" != "Y" ]] && build_ipc_msg="OFF"
|
||||
|
||||
local cxx="c++"
|
||||
local cc="cc"
|
||||
@ -69,6 +72,7 @@ function main
|
||||
-DENABLE_MPD:BOOL="${enable_mpd}" \
|
||||
-DENABLE_NETWORK:BOOL="${enable_network}" \
|
||||
-DENABLE_CURL:BOOL="${enable_curl}" \
|
||||
-DBUILD_IPC_MSG:BOOL="${build_ipc_msg}" \
|
||||
.. || msg_err "Failed to generate build... read output to get a hint of what went wrong"
|
||||
|
||||
msg "Building project"
|
||||
|
@ -48,6 +48,7 @@ endif()
|
||||
option(CXXLIB_CLANG "Link against libc++" OFF)
|
||||
option(CXXLIB_GCC "Link against stdlibc++" OFF)
|
||||
|
||||
option(BUILD_IPC_MSG "Build ipc messager" ON)
|
||||
option(BUILD_TESTS "Build testsuite" OFF)
|
||||
option(DEBUG_LOGGER "Enable extra debug logging" OFF)
|
||||
option(VERBOSE_TRACELOG "Enable verbose trace logs" OFF)
|
||||
|
@ -53,6 +53,7 @@ else()
|
||||
endif()
|
||||
|
||||
message(STATUS "--------------------------")
|
||||
colored_option(STATUS " Build polybar-msg ${BUILD_IPC_MSG}" BUILD_IPC_MSG "32;1" "37;2")
|
||||
colored_option(STATUS " Build testsuite ${BUILD_TESTS}" BUILD_TESTS "32;1" "37;2")
|
||||
colored_option(STATUS " Debug logging ${DEBUG_LOGGER}" DEBUG_LOGGER "32;1" "37;2")
|
||||
colored_option(STATUS " Verbose tracing ${VERBOSE_TRACELOG}" VERBOSE_TRACELOG "32;1" "37;2")
|
||||
|
@ -102,6 +102,7 @@ namespace file_util {
|
||||
string pick(const vector<string>& filenames);
|
||||
string contents(const string& filename);
|
||||
bool is_fifo(const string& filename);
|
||||
vector<string> glob(const string& pattern);
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) make_file_descriptor(Args&&... args) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
file(GLOB_RECURSE SOURCES RELATIVE ${PROJECT_SOURCE_DIR}/src *.c[p]*)
|
||||
list(REMOVE_ITEM SOURCES ipc.cpp)
|
||||
|
||||
# Locate dependencies {{{
|
||||
|
||||
@ -142,14 +143,18 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC
|
||||
${XCB_DEFINITIONS})
|
||||
|
||||
# }}}
|
||||
# Export target details {{{
|
||||
# Create executable target: ipc messager {{{
|
||||
|
||||
if(BUILD_IPC_MSG)
|
||||
make_executable(${PROJECT_NAME}-msg SOURCES ipc.cpp utils/file.cpp)
|
||||
endif()
|
||||
|
||||
# }}}
|
||||
|
||||
set(APP_BINARY ${PROJECT_SOURCE_DIR}/bin/${PROJECT_NAME} PARENT_SCOPE)
|
||||
set(APP_LIBRARIES ${APP_LIBRARIES} PARENT_SCOPE)
|
||||
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} PARENT_SCOPE)
|
||||
|
||||
# }}}
|
||||
|
||||
execute_process(COMMAND git describe --tags --dirty=-git
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE APP_VERSION
|
||||
|
126
src/ipc.cpp
Normal file
126
src/ipc.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#include <fcntl.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "utils/file.hpp"
|
||||
|
||||
using namespace polybar;
|
||||
|
||||
void log(const string& msg) {
|
||||
std::cerr << "polybar-msg: " << msg << std::endl;
|
||||
}
|
||||
|
||||
void log(int exit_code, const string& msg) {
|
||||
std::cerr << "polybar-msg: " << msg << std::endl;
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
void usage(const string& parameters) {
|
||||
std::cerr << "Usage: polybar-msg [-p pid] " << parameters << std::endl;
|
||||
exit(127);
|
||||
}
|
||||
|
||||
bool validate_type(const string& type) {
|
||||
if (type == "action") {
|
||||
return true;
|
||||
} else if (type == "cmd") {
|
||||
return true;
|
||||
} else if (type == "hook") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const int E_GENERIC{1};
|
||||
const int E_NO_CHANNELS{2};
|
||||
const int E_MESSAGE_TYPE{3};
|
||||
const int E_INVALID_PID{4};
|
||||
const int E_INVALID_CHANNEL{5};
|
||||
const int E_WRITE{6};
|
||||
|
||||
vector<string> args{argv + 1, argv + argc};
|
||||
string::size_type p;
|
||||
int pid{0};
|
||||
|
||||
// If -p <pid> is passed, check if the process is running and that
|
||||
// a valid channel pipe is available
|
||||
if (args.size() >= 2 && args[0].compare(0, 2, "-p") == 0) {
|
||||
if (!file_util::exists("/proc/" + args[1])) {
|
||||
log(E_INVALID_PID, "No process with pid " + args[1]);
|
||||
} else if (!file_util::is_fifo("/tmp/polybar_mqueue." + args[1])) {
|
||||
log(E_INVALID_CHANNEL, "No channel available for pid " + args[1]);
|
||||
}
|
||||
|
||||
pid = std::atoi(args[1].c_str());
|
||||
args.erase(args.begin());
|
||||
args.erase(args.begin());
|
||||
}
|
||||
|
||||
// Validate args
|
||||
if (args.size() < 2) {
|
||||
usage("<command=(action|cmd|hook)> <payload> [...]");
|
||||
} else if (!validate_type(args[0])) {
|
||||
log(E_MESSAGE_TYPE, "\"" + args[0] + "\" is not a valid type.");
|
||||
}
|
||||
|
||||
string ipc_type{args[0]};
|
||||
args.erase(args.begin());
|
||||
string ipc_payload{args[0]};
|
||||
args.erase(args.begin());
|
||||
|
||||
// Check hook specific args
|
||||
if (ipc_type == "hook") {
|
||||
if (args.size() != 1) {
|
||||
usage("hook <module-name> <hook-index>");
|
||||
} else if ((p = ipc_payload.find("module/")) != 0) {
|
||||
ipc_payload = "module/" + ipc_payload + args[0];
|
||||
args.erase(args.begin());
|
||||
} else {
|
||||
ipc_payload += args[0];
|
||||
args.erase(args.begin());
|
||||
}
|
||||
}
|
||||
|
||||
// Get availble channel pipes
|
||||
auto channels = file_util::glob("/tmp/polybar_mqueue.*");
|
||||
if (channels.empty()) {
|
||||
log(E_NO_CHANNELS, "There are no active ipc channels");
|
||||
}
|
||||
|
||||
// Write the message to each channel in the list and remove stale
|
||||
// channel pipes that may be left lingering if the owning process got
|
||||
// SIGKILLED or crashed
|
||||
for (auto&& channel : channels) {
|
||||
string handle{channel};
|
||||
int handle_pid{0};
|
||||
|
||||
if ((p = handle.rfind('.')) != string::npos) {
|
||||
handle_pid = std::atoi(handle.substr(p + 1).c_str());
|
||||
}
|
||||
|
||||
if (!file_util::exists("/proc/" + to_string(handle_pid))) {
|
||||
if (unlink(handle.c_str()) == -1) {
|
||||
log(E_GENERIC, "Could not remove stale ipc channel: " + string{std::strerror(errno)});
|
||||
} else {
|
||||
log("Removed stale ipc channel: " + handle);
|
||||
}
|
||||
} else if (!pid || pid == handle_pid) {
|
||||
string payload{ipc_type + ":" + ipc_payload};
|
||||
|
||||
try {
|
||||
fd_stream<std::ostream> out(handle, O_WRONLY);
|
||||
out << payload << '\n';
|
||||
log("Successfully wrote \"" + payload + "\" to \"" + handle + "\"");
|
||||
} catch (const std::exception& err) {
|
||||
log(E_WRITE, "Failed to write \"" + payload + "\" to \"" + handle + "\" (err: " + err.what() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <fcntl.h>
|
||||
#include <glob.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
@ -201,6 +202,23 @@ namespace file_util {
|
||||
struct stat buffer {};
|
||||
return stat(filename.c_str(), &buffer) == 0 && S_ISFIFO(buffer.st_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get glob results using given pattern
|
||||
*/
|
||||
vector<string> glob(const string& pattern) {
|
||||
glob_t result;
|
||||
vector<string> ret;
|
||||
|
||||
if (glob(pattern.c_str(), GLOB_TILDE, nullptr, &result) == 0) {
|
||||
for (size_t i = 0_z; i < result.gl_pathc; ++i) {
|
||||
ret.emplace_back(result.gl_pathv[i]);
|
||||
}
|
||||
globfree(&result);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
POLYBAR_NS_END
|
||||
|
Loading…
Reference in New Issue
Block a user