aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-01-10 14:34:49 +0100
committerDan Engelbrecht <[email protected]>2025-01-10 14:34:49 +0100
commit5e02ddf6cc3d311cafbd4fcff326ece0141a992e (patch)
tree053406cb4eaf3e9b76474607edf758f3178f0b55
parentmake windows elevation optional (diff)
downloadzen-5e02ddf6cc3d311cafbd4fcff326ece0141a992e.tar.xz
zen-5e02ddf6cc3d311cafbd4fcff326ece0141a992e.zip
partially working service commands for macos
-rw-r--r--src/zen/cmds/service_cmd.cpp3
-rw-r--r--src/zenutil/service.cpp275
2 files changed, 252 insertions, 26 deletions
diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp
index 0d538ffda..de3466eff 100644
--- a/src/zen/cmds/service_cmd.cpp
+++ b/src/zen/cmds/service_cmd.cpp
@@ -98,7 +98,7 @@ namespace {
#else // ZEN_PLATFORM_WINDOWS
- bool IsElevated() { return geteuid() == 0; }
+ bool IsElevated() { return true;/*geteuid() == 0;*/ }
#endif // ZEN_PLATFORM_WINDOWS
@@ -118,6 +118,7 @@ namespace {
}
#endif // ZEN_PLATFORM_WINDOWS
#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+ ZEN_UNUSED(AllowElevation);
ZEN_CONSOLE("This command requires elevated priviliges. Run the command with `sudo`");
return 1;
#endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp
index 72367a070..feebeb7f4 100644
--- a/src/zenutil/service.cpp
+++ b/src/zenutil/service.cpp
@@ -243,6 +243,110 @@ namespace {
}
#endif // ZEN_PLATFORM_MAC
+
+
+#if ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX
+
+std::pair<int, std::string> ExecuteProgram(std::string_view Cmd)
+{
+ std::string data;
+ const int max_buffer = 256;
+ char buffer[max_buffer];
+ std::string Command(Cmd);
+ Command.append(" 2>&1");
+
+ FILE * stream = popen(Command.c_str(), "r");
+ if (stream)
+ {
+ while (!feof(stream))
+ {
+ if (fgets(buffer, max_buffer, stream) != NULL)
+ {
+ data.append(buffer);
+ }
+ }
+
+ int Res = -1;
+ int st = pclose(stream);
+ if (WIFEXITED(st)) Res = WEXITSTATUS(st);
+ return {Res, data};
+ }
+ return {errno, ""};
+
+#if 0
+ int in[2], out[2], n, pid;
+ char buf[255];
+
+ /* In a pipe, xx[0] is for reading, xx[1] is for writing */
+
+ if (pipe(in) < 0)
+ {
+ return {errno, ""};
+ }
+ if (pipe(out) < 0)
+ {
+ close(in[0]);
+ close(in[1]);
+ return {errno, ""};
+ }
+
+ if ((pid=fork()) == 0) {
+ /* This is the child process */
+
+ /* Close stdin, stdout, stderr */
+ close(0);
+ close(1);
+ close(2);
+ /* make our pipes, our new stdin,stdout and stderr */
+ dup2(in[0],0);
+ dup2(out[1],1);
+ dup2(out[1],2);
+
+ /* Close the other ends of the pipes that the parent will use, because if
+ * we leave these open in the child, the child/parent will not get an EOF
+ * when the parent/child closes their end of the pipe.
+ */
+ close(in[1]);
+ close(out[0]);
+
+ va_list args;
+ va_start(args, Executable);
+ va_end(args);
+
+ /* Over-write the child process with the hexdump binary */
+ execl(Executable, Executable, args);
+ }
+
+ /* This is the parent process */
+ /* Close the pipe ends that the child uses to read from / write to so
+ * the when we close the others, an EOF will be transmitted properly.
+ */
+ close(in[0]);
+
+ /* Because of the small amount of data, the child may block unless we
+ * close it's input stream. This sends an EOF to the child on it's
+ * stdin.
+ */
+ close(in[1]);
+
+ /* Read back any output */
+ n = read(out[0], buf, 250);
+ if (n == 0)
+ {
+ n = read(out[1], buf, 250);
+ }
+ buf[n] = 0;
+ close(out[0]);
+ close(out[1]);
+
+ std::string Output(buf);
+ return {0, Output};
+#endif // 0
+}
+
+#endif // ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX
+
+
} // namespace
std::string_view
@@ -633,7 +737,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec)
// return MakeErrorCodeFromLastError();
// }
- // System: /Library/LaunchDaemons
+ // System: /Library/LaunchDaemon
// All users: /Library/LaunchAgents
// Current user: ~/Library/LaunchAgents
@@ -665,34 +769,137 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec)
std::error_code
UninstallService(std::string_view ServiceName)
{
- ZEN_UNUSED(Level);
-
- std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName;
- ZEN_INFO("Attempting to remove service from {}", ServicePath.string());
- if (unlink(ServicePath.string().c_str()) == -1)
- {
- return MakeErrorCodeFromLastError();
- }
+// std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName;
+// ZEN_INFO("Attempting to remove service from {}", ServicePath.string());
+// if (unlink(ServicePath.string().c_str()) == -1)
+// {
+// return MakeErrorCodeFromLastError();
+// }
std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName);
- std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist");
+ std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemon") / (DaemonName + ".plist");
ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string());
- if (unlink(ServicePath.string().c_str()) == -1)
- {
- return MakeErrorCodeFromLastError();
- }
-
- return {};
+ std::error_code Ec;
+ std::filesystem::remove(PListPath, Ec);
+ return Ec;
+
+// if (unlink(PListPath.string().c_str()) == -1)
+// {
+// return MakeErrorCodeFromLastError();
+// }
+
+// return {};
}
std::error_code
QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo)
{
ZEN_UNUSED(ServiceName, OutInfo);
- // ZEN_NOT_IMPLEMENTED("QueryInstalledService");
- // std::filesystem::path PListFolder = "/Library/LaunchDaemon";
- // sudo launchctl list
OutInfo.Status = ServiceStatus::NotInstalled;
+ std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName);
+
+ std::filesystem::path PListFolder = "/Library/LaunchDaemon";
+
+ std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist");
+ if (std::filesystem::is_regular_file(PListPath))
+ {
+ OutInfo.Status = ServiceStatus::Stopped;
+
+ {
+ // Parse plist :(
+ IoBuffer Buffer = ReadFile(PListPath).Flatten();
+ MemoryView Data = Buffer.GetView();
+ std::string PList((const char*)Data.GetData(), Data.GetSize());
+ ZEN_INFO("{}", PList);
+ }
+ {
+ std::pair<int, std::string> Res = ExecuteProgram(std::string("launchctl list ") + DaemonName);
+ if (Res.first == 0 && !Res.second.empty())
+ {
+// bool IsParsingArguments = false;
+ ForEachStrTok(Res.second, '\n', [&](std::string_view Line){
+// if (IsParsingArguments)
+// {
+// if (Line.find(");") == Line.length() - 2)
+// {
+// IsParsingArguments = false;
+// return true;
+// }
+// std::string_view::size_type ProgramArgumentsEnd = Line.find_last_of('\"');
+// std::string_view::size_type ProgramArgumentsStart = Line.find('\"') + 1;
+// std::string_view ProgramArgumentsString = Line.substr(ProgramArgumentsStart, ProgramArgumentsEnd - ProgramArgumentsStart);
+// if (ProgramArgumentsString != OutInfo.Spec.ExecutablePath)
+// {
+// if (!OutInfo.Spec.CommandLineOptions.empty())
+// {
+// OutInfo.Spec.CommandLineOptions += " ";
+// }
+// OutInfo.Spec.CommandLineOptions += ProgramArgumentsString;
+// }
+// return true;
+// }
+ if (Line.find("\"PID\"") != std::string_view::npos)
+ {
+ std::string_view::size_type PidStart = Line.find('=');
+ std::string_view::size_type PidEnd = Line.find(';');
+ std::string_view PidString = Line.substr(PidStart+2, PidEnd - (PidStart + 2));
+ if (ParseInt<int>(PidString).has_value())
+ {
+ OutInfo.Status = ServiceStatus::Running;
+ }
+ return false;
+ }
+// if (Line.find("\"Program\"") != std::string_view::npos)
+// {
+// std::string_view::size_type ProgramEnd = Line.find_last_of('\"');
+// std::string_view::size_type ProgramStart = Line.find_last_of('\"', ProgramEnd - 1) + 1;
+// std::string_view ProgramString = Line.substr(ProgramStart, ProgramEnd - ProgramStart);
+// OutInfo.Spec.ExecutablePath = ProgramString;
+// return true;
+// }
+// if (Line.find("\"ProgramArguments\"") != std::string_view::npos)
+// {
+// IsParsingArguments = true;
+// return true;
+// }
+ return true;
+ });
+ // Parse installed info
+ }
+ }
+#if 0
+ {
+ std::pair<int, std::string> Res = ExecuteProgram("launchctl list");
+ if (Res.first != 0)
+ {
+ return MakeErrorCode(Res.first);
+ }
+ std::string ZenServerLine;
+ ForEachStrTok(Res.second, '\n', [&](std::string_view Line){
+ if (Line.find(DaemonName) != std::string_view::npos)
+ {
+ ZenServerLine = Line;
+ }
+ return true;
+ });
+ if (!ZenServerLine.empty())
+ {
+ std::vector<std::string_view> Parts;
+ ForEachStrTok(ZenServerLine, '\t', [&](std::string_view Line){
+ Parts.push_back(Line);
+ return true;
+ });
+ if (Parts.size() == 3)
+ {
+ if (Parts[0] != "-" && ParseInt<int>(Parts[0]).has_value())
+ {
+ OutInfo.Status = ServiceStatus::Running;
+ }
+ }
+ }
+ }
+#endif // 0
+ }
return {};
}
@@ -700,18 +907,36 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo)
std::error_code
StartService(std::string_view ServiceName)
{
- ZEN_UNUSED(ServiceName, Level);
- ZEN_NOT_IMPLEMENTED("StartService");
- // sudo launchctl bootstrap system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist
+ ZEN_UNUSED(ServiceName);
+
+ std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName);
+ std::filesystem::path PListFolder = "/Library/LaunchDaemon";
+ std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist");
+
+ std::pair<int, std::string> Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string());
+ if (Res.first != 0)
+ {
+ return MakeErrorCode(Res.first);
+ }
+
return {};
}
std::error_code
StopService(std::string_view ServiceName)
{
- ZEN_UNUSED(ServiceName, Level);
- ZEN_NOT_IMPLEMENTED("StopService");
- // sudo launchctl bootout system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist
+ ZEN_UNUSED(ServiceName);
+
+ std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName);
+ std::filesystem::path PListFolder = "/Library/LaunchDaemon";
+ std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist");
+
+ std::pair<int, std::string> Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string());
+ if (Res.first != 0)
+ {
+ return MakeErrorCode(Res.first);
+ }
+
return {};
}