c24a6999a4
Each major target of polybar can now be enabled/disabled while configuring (even polybar itself). The cmake code specific to each target will only run if the target is enabled. This allows us to for example just build the documentation without having to run all the cmake code related to compilation or having the polybar dependencies installed (other than sphinx).
134 lines
3.8 KiB
C++
134 lines
3.8 KiB
C++
#include <fcntl.h>
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
#include "common.hpp"
|
|
#include "utils/file.hpp"
|
|
|
|
using namespace polybar;
|
|
using namespace std;
|
|
|
|
#ifndef IPC_CHANNEL_PREFIX
|
|
#define IPC_CHANNEL_PREFIX "/tmp/polybar_mqueue."
|
|
#endif
|
|
|
|
void display(const string& msg) {
|
|
fprintf(stdout, "%s\n", msg.c_str());
|
|
}
|
|
|
|
void log(int exit_code, const string& msg) {
|
|
fprintf(stderr, "polybar-msg: %s\n", msg.c_str());
|
|
exit(exit_code);
|
|
}
|
|
|
|
void usage(const string& parameters) {
|
|
fprintf(stderr, "Usage: polybar-msg [-p pid] %s\n", parameters.c_str());
|
|
exit(127);
|
|
}
|
|
|
|
void remove_pipe(const string& handle) {
|
|
if (unlink(handle.c_str()) == -1) {
|
|
log(1, "Could not remove stale ipc channel: "s + strerror(errno));
|
|
} else {
|
|
display("Removed stale ipc channel: " + handle);
|
|
}
|
|
}
|
|
|
|
bool validate_type(const string& type) {
|
|
return (type == "action" || type == "cmd" || type == "hook");
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
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::exists(IPC_CHANNEL_PREFIX + args[1])) {
|
|
log(E_INVALID_CHANNEL, "No channel available for pid " + args[1]);
|
|
}
|
|
|
|
pid = strtol(args[1].c_str(), nullptr, 10);
|
|
args.erase(args.begin());
|
|
args.erase(args.begin());
|
|
}
|
|
|
|
// Validate args
|
|
auto help = find_if(args.begin(), args.end(), [](string a) { return a == "-h" || a == "--help"; }) != args.end();
|
|
if (help || 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 pipes = file_util::glob(IPC_CHANNEL_PREFIX + "*"s);
|
|
|
|
// Remove stale channel files without a running parent process
|
|
for (auto it = pipes.rbegin(); it != pipes.rend(); it++) {
|
|
if ((p = it->rfind('.')) == string::npos) {
|
|
continue;
|
|
} else if (!file_util::exists("/proc/" + it->substr(p + 1))) {
|
|
remove_pipe(*it);
|
|
pipes.erase(remove(pipes.begin(), pipes.end(), *it), pipes.end());
|
|
} else if (pid && to_string(pid) != it->substr(p + 1)) {
|
|
pipes.erase(remove(pipes.begin(), pipes.end(), *it), pipes.end());
|
|
}
|
|
}
|
|
|
|
if (pipes.empty()) {
|
|
log(E_NO_CHANNELS, "No active ipc channels");
|
|
}
|
|
|
|
int exit_status = 127;
|
|
|
|
// Write message to each available channel or match
|
|
// against pid if one was defined
|
|
for (auto&& channel : pipes) {
|
|
try {
|
|
file_descriptor fd(channel, O_WRONLY | O_NONBLOCK);
|
|
string payload{ipc_type + ':' + ipc_payload};
|
|
if (write(fd, payload.c_str(), payload.size()) != -1) {
|
|
display("Successfully wrote \"" + payload + "\" to \"" + channel + "\"");
|
|
exit_status = 0;
|
|
} else {
|
|
log(E_WRITE, "Failed to write \"" + payload + "\" to \"" + channel + "\" (err: " + strerror(errno) + ")");
|
|
}
|
|
} catch (const exception& err) {
|
|
remove_pipe(channel);
|
|
}
|
|
}
|
|
|
|
return exit_status;
|
|
}
|