mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-22 15:05:59 +01:00
hyprctl: added --follow option to rolliglog (#6325)
Co-authored-by: Крылов Александр <aleksandr.krylov@hyperus.team>
This commit is contained in:
parent
9cd5b3587c
commit
b2590b58c5
6 changed files with 185 additions and 13 deletions
2
Makefile
2
Makefile
|
@ -42,7 +42,7 @@ uninstall:
|
||||||
pluginenv:
|
pluginenv:
|
||||||
@echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
|
@echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
|
||||||
@exit 1
|
@exit 1
|
||||||
|
|
||||||
installheaders:
|
installheaders:
|
||||||
@if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
|
@if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ commands:
|
||||||
plugin ... → Issue a plugin request
|
plugin ... → Issue a plugin request
|
||||||
reload [config-only] → Issue a reload to force reload the config. Pass
|
reload [config-only] → Issue a reload to force reload the config. Pass
|
||||||
'config-only' to disable monitor reload
|
'config-only' to disable monitor reload
|
||||||
rollinglog → Prints tail of the log
|
rollinglog → Prints tail of the log. Also supports -f/--follow
|
||||||
|
option
|
||||||
setcursor <theme> <size> → Sets the cursor theme and reloads the cursor
|
setcursor <theme> <size> → Sets the cursor theme and reloads the cursor
|
||||||
manager
|
manager
|
||||||
seterror <color> <message...> → Sets the hyprctl error string. Color has
|
seterror <color> <message...> → Sets the hyprctl error string. Color has
|
||||||
|
@ -112,7 +113,7 @@ create <backend>:
|
||||||
remove <name>:
|
remove <name>:
|
||||||
Removes virtual output. Pass the output's name, as found in
|
Removes virtual output. Pass the output's name, as found in
|
||||||
'hyprctl monitors'
|
'hyprctl monitors'
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
See 'hyprctl --help')#";
|
See 'hyprctl --help')#";
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include <ctype.h>
|
#include <cctype>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <signal.h>
|
#include <csignal>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -22,8 +22,9 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <stdarg.h>
|
#include <cstdarg>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
using namespace Hyprutils::String;
|
using namespace Hyprutils::String;
|
||||||
|
|
||||||
|
@ -100,13 +101,53 @@ std::vector<SInstanceData> instances() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int request(std::string arg, int minArgs = 0) {
|
static volatile bool sigintReceived = false;
|
||||||
|
void intHandler(int sig) {
|
||||||
|
sigintReceived = true;
|
||||||
|
std::cout << "[hyprctl] SIGINT received, closing connection" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rollingRead(const int socket) {
|
||||||
|
sigintReceived = false;
|
||||||
|
signal(SIGINT, intHandler);
|
||||||
|
|
||||||
|
constexpr size_t BUFFER_SIZE = 8192;
|
||||||
|
std::array<char, BUFFER_SIZE> buffer = {0};
|
||||||
|
int sizeWritten = 0;
|
||||||
|
std::cout << "[hyprctl] reading from socket following up log:" << std::endl;
|
||||||
|
while (!sigintReceived) {
|
||||||
|
sizeWritten = read(socket, buffer.data(), BUFFER_SIZE);
|
||||||
|
if (sizeWritten < 0 && errno != EAGAIN) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
std::cout << "Couldn't read (5) " << strerror(errno) << ":" << errno << std::endl;
|
||||||
|
close(socket);
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeWritten == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (sizeWritten > 0) {
|
||||||
|
std::cout << std::string(buffer.data(), sizeWritten);
|
||||||
|
buffer.fill('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
close(socket);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
auto t = timeval{.tv_sec = 0, .tv_usec = 100000};
|
||||||
|
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||||
|
|
||||||
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
|
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
|
||||||
|
|
||||||
if (ARGS < minArgs) {
|
if (ARGS < minArgs) {
|
||||||
log("Not enough arguments, expected at least " + minArgs);
|
log(std::format("Not enough arguments in '{}', expected at least {}", arg, minArgs));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +182,9 @@ int request(std::string arg, int minArgs = 0) {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needRoll)
|
||||||
|
return rollingRead(SERVERSOCKET);
|
||||||
|
|
||||||
std::string reply = "";
|
std::string reply = "";
|
||||||
char buffer[8192] = {0};
|
char buffer[8192] = {0};
|
||||||
|
|
||||||
|
@ -284,6 +328,7 @@ int main(int argc, char** argv) {
|
||||||
std::string fullArgs = "";
|
std::string fullArgs = "";
|
||||||
const auto ARGS = splitArgs(argc, argv);
|
const auto ARGS = splitArgs(argc, argv);
|
||||||
bool json = false;
|
bool json = false;
|
||||||
|
bool needRoll = false;
|
||||||
std::string overrideInstance = "";
|
std::string overrideInstance = "";
|
||||||
|
|
||||||
for (std::size_t i = 0; i < ARGS.size(); ++i) {
|
for (std::size_t i = 0; i < ARGS.size(); ++i) {
|
||||||
|
@ -303,6 +348,9 @@ int main(int argc, char** argv) {
|
||||||
fullArgs += "a";
|
fullArgs += "a";
|
||||||
} else if ((ARGS[i] == "-c" || ARGS[i] == "--config") && !fullArgs.contains("c")) {
|
} else if ((ARGS[i] == "-c" || ARGS[i] == "--config") && !fullArgs.contains("c")) {
|
||||||
fullArgs += "c";
|
fullArgs += "c";
|
||||||
|
} else if ((ARGS[i] == "-f" || ARGS[i] == "--follow") && !fullArgs.contains("f")) {
|
||||||
|
fullArgs += "f";
|
||||||
|
needRoll = true;
|
||||||
} else if (ARGS[i] == "--batch") {
|
} else if (ARGS[i] == "--batch") {
|
||||||
fullRequest = "--batch ";
|
fullRequest = "--batch ";
|
||||||
} else if (ARGS[i] == "--instance" || ARGS[i] == "-i") {
|
} else if (ARGS[i] == "--instance" || ARGS[i] == "-i") {
|
||||||
|
@ -362,6 +410,11 @@ int main(int argc, char** argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needRoll && !fullRequest.contains("/rollinglog")) {
|
||||||
|
log("only 'rollinglog' command supports '--follow' option");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (overrideInstance.contains("_"))
|
if (overrideInstance.contains("_"))
|
||||||
instanceSignature = overrideInstance;
|
instanceSignature = overrideInstance;
|
||||||
else if (!overrideInstance.empty()) {
|
else if (!overrideInstance.empty()) {
|
||||||
|
@ -421,6 +474,8 @@ int main(int argc, char** argv) {
|
||||||
exitStatus = request(fullRequest, 1);
|
exitStatus = request(fullRequest, 1);
|
||||||
else if (fullRequest.contains("/--help"))
|
else if (fullRequest.contains("/--help"))
|
||||||
std::cout << USAGE << std::endl;
|
std::cout << USAGE << std::endl;
|
||||||
|
else if (fullRequest.contains("/rollinglog") && needRoll)
|
||||||
|
exitStatus = request(fullRequest, 0, true);
|
||||||
else {
|
else {
|
||||||
exitStatus = request(fullRequest);
|
exitStatus = request(fullRequest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ using namespace Hyprutils::String;
|
||||||
#include "../devices/IKeyboard.hpp"
|
#include "../devices/IKeyboard.hpp"
|
||||||
#include "../devices/ITouch.hpp"
|
#include "../devices/ITouch.hpp"
|
||||||
#include "../devices/Tablet.hpp"
|
#include "../devices/Tablet.hpp"
|
||||||
|
#include "debug/RollingLogFollow.hpp"
|
||||||
#include "config/ConfigManager.hpp"
|
#include "config/ConfigManager.hpp"
|
||||||
#include "helpers/MiscFunctions.hpp"
|
#include "helpers/MiscFunctions.hpp"
|
||||||
|
|
||||||
|
@ -1732,6 +1733,46 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) {
|
||||||
return getReply(input);
|
return getReply(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool successWrite(int fd, const std::string& data, bool needLog = true) {
|
||||||
|
if (write(fd, data.c_str(), data.length()) > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (needLog)
|
||||||
|
Debug::log(ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void runWritingDebugLogThread(const int conn) {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn);
|
||||||
|
//will be finished, when reading side close connection
|
||||||
|
std::thread([conn]() {
|
||||||
|
while (Debug::RollingLogFollow::Get().IsRunning()) {
|
||||||
|
if (Debug::RollingLogFollow::Get().isEmpty(conn)) {
|
||||||
|
std::this_thread::sleep_for(1000ms);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto line = Debug::RollingLogFollow::Get().GetLog(conn);
|
||||||
|
if (!successWrite(conn, line))
|
||||||
|
// We cannot write, when connection is closed. So thread will successfully exit by itself
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
}
|
||||||
|
close(conn);
|
||||||
|
Debug::RollingLogFollow::Get().StopFor(conn);
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFollowUpRollingLogRequest(const std::string& request) {
|
||||||
|
return request.contains("rollinglog") && request.contains("f");
|
||||||
|
}
|
||||||
|
|
||||||
int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
||||||
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
|
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1775,9 +1816,15 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
||||||
reply = "Err: " + std::string(e.what());
|
reply = "Err: " + std::string(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
write(ACCEPTEDCONNECTION, reply.c_str(), reply.length());
|
successWrite(ACCEPTEDCONNECTION, reply);
|
||||||
|
|
||||||
close(ACCEPTEDCONNECTION);
|
if (isFollowUpRollingLogRequest(request)) {
|
||||||
|
Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket.");
|
||||||
|
Debug::RollingLogFollow::Get().StartFor(ACCEPTEDCONNECTION);
|
||||||
|
runWritingDebugLogThread(ACCEPTEDCONNECTION);
|
||||||
|
Debug::log(LOG, Debug::RollingLogFollow::Get().DebugInfo());
|
||||||
|
} else
|
||||||
|
close(ACCEPTEDCONNECTION);
|
||||||
|
|
||||||
if (g_pConfigManager->m_bWantsMonitorReload)
|
if (g_pConfigManager->m_bWantsMonitorReload)
|
||||||
g_pConfigManager->ensureMonitorStatus();
|
g_pConfigManager->ensureMonitorStatus();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
#include "../defines.hpp"
|
#include "../defines.hpp"
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
|
#include "RollingLogFollow.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -73,6 +74,9 @@ void Debug::log(LogLevel level, std::string str) {
|
||||||
if (rollingLog.size() > ROLLING_LOG_SIZE)
|
if (rollingLog.size() > ROLLING_LOG_SIZE)
|
||||||
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
|
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
|
||||||
|
|
||||||
|
if (RollingLogFollow::Get().IsRunning())
|
||||||
|
RollingLogFollow::Get().AddLog(str);
|
||||||
|
|
||||||
if (!disableLogs || !**disableLogs) {
|
if (!disableLogs || !**disableLogs) {
|
||||||
// log to a file
|
// log to a file
|
||||||
std::ofstream ofs;
|
std::ofstream ofs;
|
||||||
|
|
65
src/debug/RollingLogFollow.hpp
Normal file
65
src/debug/RollingLogFollow.hpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
namespace Debug {
|
||||||
|
struct RollingLogFollow {
|
||||||
|
std::unordered_map<int, std::string> socketToRollingLogFollowQueue;
|
||||||
|
std::shared_mutex m;
|
||||||
|
bool running = false;
|
||||||
|
static constexpr size_t ROLLING_LOG_FOLLOW_TOO_BIG = 8192;
|
||||||
|
|
||||||
|
// Returns true if the queue is empty for the given socket
|
||||||
|
bool isEmpty(int socket) {
|
||||||
|
std::shared_lock<std::shared_mutex> r(m);
|
||||||
|
return socketToRollingLogFollowQueue[socket].empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DebugInfo() {
|
||||||
|
std::shared_lock<std::shared_mutex> r(m);
|
||||||
|
return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetLog(int socket) {
|
||||||
|
std::unique_lock<std::shared_mutex> w(m);
|
||||||
|
|
||||||
|
const std::string ret = socketToRollingLogFollowQueue[socket];
|
||||||
|
socketToRollingLogFollowQueue[socket] = "";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddLog(std::string log) {
|
||||||
|
std::unique_lock<std::shared_mutex> w(m);
|
||||||
|
running = true;
|
||||||
|
std::vector<int> to_erase;
|
||||||
|
for (const auto& p : socketToRollingLogFollowQueue)
|
||||||
|
socketToRollingLogFollowQueue[p.first] += log + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRunning() {
|
||||||
|
std::shared_lock<std::shared_mutex> r(m);
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopFor(int socket) {
|
||||||
|
std::unique_lock<std::shared_mutex> w(m);
|
||||||
|
socketToRollingLogFollowQueue.erase(socket);
|
||||||
|
if (socketToRollingLogFollowQueue.empty())
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartFor(int socket) {
|
||||||
|
std::unique_lock<std::shared_mutex> w(m);
|
||||||
|
socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RollingLogFollow& Get() {
|
||||||
|
static RollingLogFollow instance;
|
||||||
|
static std::mutex gm;
|
||||||
|
std::lock_guard<std::mutex> lock(gm);
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue