Skip to content

Commit

Permalink
put socket in thread
Browse files Browse the repository at this point in the history
  • Loading branch information
heapwolf committed Apr 17, 2024
1 parent eb335ff commit 1f85798
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 221 deletions.
275 changes: 80 additions & 195 deletions src/cli/cli.cc
Expand Up @@ -37,6 +37,7 @@
#include <filesystem>
#include <iostream>
#include <fstream>
#include <future>
#include <regex>
#include <span>
#include <unordered_set>
Expand Down Expand Up @@ -308,201 +309,84 @@ static std::atomic<int> appStatus = -1;
static std::mutex appMutex;

#if defined(__APPLE__)
/* void pollOSLogStream (bool isForDesktop, String bundleIdentifier, int processIdentifier) {
if (Env::get("SSC_NO_OSLOG_POLLING").size() > 0) return;
// It appears there is a bug with `:predicateWithFormat:` as the
// following does not appear to work:
//
// [NSPredicate
// predicateWithFormat: @"processIdentifier == %d AND subsystem == '%s'",
// app.processIdentifier,
// bundle.bundleIdentifier // or even a literal string "co.socketsupply.socket.tests"
// ];
//
// We can build the predicate query string manually, instead.
auto pid = std::to_string(processIdentifier);
auto bid = bundleIdentifier.c_str();
StringStream queryStream;
queryStream << "(category == 'socket.runtime')";
if (settings.count("logs_root") == 0) {
queryStream << " AND ";
if (processIdentifier > 0) {
queryStream << "processIdentifier == " << pid << " AND ";
}
queryStream << "subsystem == '" << bid << "'";
}
// log store query and predicate for filtering logs based on the currently
// running application that was just launched and those of a subsystem
// directly related to the application's bundle identifier which allows us
// to just get logs that came from the application (not foundation/cocoa/webkit)
const auto query = [NSString stringWithUTF8String: queryStream.str().c_str()];
const auto predicate = [NSPredicate predicateWithFormat: query];
// use the launch date as the initial marker
const auto now = [NSDate now];
// and offset it by 1 second in the past as the initial position in the eumeration
auto offset = [now dateByAddingTimeInterval: -1];
// tracks the latest log entry date so we ignore older ones
NSDate* latest = nil;
NSError* error = nil;
int pollsAfterTermination = 16;
int backoffIndex = 0;
// lucas series of backoffs
static int backoffs[] = {
16*2,
16*1,
16*3,
16*4,
16*7,
16*11,
16*18,
16*29,
32*2,
32*1,
32*3,
32*4,
32*7,
32*11,
32*18,
32*29,
64*2,
64*1,
64*3,
64*4,
64*7,
64*11,
64*18,
64*29,
};
if (processIdentifier > 0) {
dispatch_async(queue, ^{
while (kill(processIdentifier, 0) == 0) {
msleep(256);
}
});
}
while (appStatus < 0 || pollsAfterTermination > 0) {
if (appStatus >= 0) {
pollsAfterTermination = pollsAfterTermination - 1;
checkLogStore = true;
}
if (!checkLogStore) {
auto backoff = backoffs[backoffIndex];
backoffIndex = (
(backoffIndex + 1) %
(sizeof(backoffs) / sizeof(backoffs[0]))
);
msleep(backoff);
if (processIdentifier > 0) {
continue;
}
}
unsigned short createLogSocket() {
std::promise<int> p;
std::future<int> future = p.get_future();
int port = 0;

auto t = std::thread([](std::promise<int>&& p) {
uv_loop_t *loop = uv_default_loop();
uv_udp_t socket;

uv_udp_init(loop, &socket);
struct sockaddr_in addr;
int port;

uv_ip4_addr("0.0.0.0", 0, &addr);
uv_udp_bind(&socket, (const struct sockaddr*)&addr, UV_UDP_REUSEADDR);

uv_udp_recv_start(
&socket,
[](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
*buf = uv_buf_init(new char[suggested_size], suggested_size);
},
[](uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
if (nread > 0) {
String data = trim(String(buf->base, nread));
if (data[0] != '+') return;

@autoreleasepool {
NSError *err = nil;
auto logs = [OSLogStore storeWithScope: OSLogStoreSystem error: &err]; // get snapshot

if (err) {
debug("ERROR: Failed to open logstore");
return;
}

// this is may be set to `true` from a `SIGUSR1` signal
checkLogStore = settings.count("logs_root") > 0;
@autoreleasepool {
// We need a new `OSLogStore` in each so we can keep enumeratoring
// the logs until the application terminates. This is required because
// each `OSLogStore` instance is a snapshot of the system's universal
// log archive at the time of instantiation.
auto logs = [OSLogStore
storeWithScope: OSLogStoreSystem
error: &error
];
auto offset = [[NSDate now] dateByAddingTimeInterval: -1]; // this may not be enough
auto position = [logs positionWithDate: offset];
auto predicate = [NSPredicate predicateWithFormat: @"(category == 'socket.runtime')"];
auto enumerator = [logs entriesEnumeratorWithOptions: 0 position: position predicate: predicate error: &err];

if (error) {
appStatus = 1;
debug(
"ERROR: OSLogStore: (code=%lu, domain=%@) %@",
error.code,
error.domain,
error.localizedDescription
);
break;
}
if (err) {
debug("ERROR: Failed to open logstore");
return;
}

auto position = [logs positionWithDate: offset];
auto enumerator = [logs
entriesEnumeratorWithOptions: 0
position: position
predicate: predicate
error: &error
];
int count = 0;
id logEntry;

if (error) {
appStatus = 1;
debug(
"ERROR: OSLogEnumerator: (code=%lu, domain=%@) %@",
error.code,
error.domain,
error.localizedDescription
);
break;
}
// Enumerate all the logs in this loop and print unredacted and most
// recently log entries to stdout
for (OSLogEntryLog* entry in enumerator) {
if (
entry.composedMessage &&
(processIdentifier == 0 || entry.processIdentifier == processIdentifier)
) {
// visit latest log
if (!latest || [latest compare: entry.date] == NSOrderedAscending) {
auto message = entry.composedMessage.UTF8String;
// the OSLogStore may redact log messages the user does not
// have access to, filter them out
if (String(message) != "<private>") {
if (String(message).starts_with("__EXIT_SIGNAL__")) {
if (appStatus == -1) {
appStatus = std::stoi(replace(String(message), "__EXIT_SIGNAL__=", ""));
}
} else if (
entry.level == OSLogEntryLogLevelDebug ||
entry.level == OSLogEntryLogLevelError ||
entry.level == OSLogEntryLogLevelFault
) {
if (entry.level == OSLogEntryLogLevelDebug) {
if (flagDebugMode) {
std::cerr << message << std::endl;
}
} else {
std::cerr << message << std::endl;
}
} else {
std::cout << message << std::endl;
}
while ((logEntry = [enumerator nextObject]) != nil) {
count++;
OSLogEntryLog *entry = (OSLogEntryLog *)logEntry;
NSString *message = [entry composedMessage];
std::cout << message.UTF8String << std::endl;
}
backoffIndex = 0;
latest = entry.date;
offset = latest;
}
}

if (buf->base) delete[] buf->base;
}
}
}
);

appMutex.unlock();
} */
int len = sizeof(addr);

void pollOSLogStream(bool isDesktop, const std::string &bid, int pid) {
if (uv_udp_getsockname(&socket, (struct sockaddr *)&addr, &len) == 0) {
auto port = ntohs(addr.sin_port);
debug("Listening on UDP port %d\n", port);
p.set_value(port);
} else {
debug("Failed to get socket name\n");
}

}
uv_run(loop, UV_RUN_DEFAULT);
}, std::move(p));

port = future.get();
t.detach();
return port;
}
#endif

void handleBuildPhaseForUser (
Expand Down Expand Up @@ -995,12 +879,14 @@ int runApp (const Path& path, const String& args, bool headless) {

for (auto const &envKey : parseStringList(settings["build_env"])) {
auto cleanKey = trim(envKey);

// TODO(@heapwolf): this is wrong, ',' should be cleared from lists like this
cleanKey.erase(0, cleanKey.find_first_not_of(","));
cleanKey.erase(cleanKey.find_last_not_of(",") + 1);

auto envValue = Env::get(cleanKey.c_str());
auto key = [NSString stringWithUTF8String: cleanKey.c_str()];
auto value = [NSString stringWithUTF8String: envValue.c_str()];
auto key = @(cleanKey.c_str());
auto value = @(envValue.c_str());

env[key] = value;
}
Expand All @@ -1009,11 +895,19 @@ int runApp (const Path& path, const String& args, bool headless) {
auto arguments = [[NSMutableArray alloc] init];

for (auto arg : splitArgs) {
[arguments addObject: [NSString stringWithUTF8String: arg.c_str()]];
[arguments addObject: @(arg.c_str())];
}

[arguments addObject: @"--from-ssc"];

auto port = std::to_string(createLogSocket());
env[@"SSC_LOG_SOCKET"] = @(port.c_str());

auto parentLogSocket = Env::get("SSC_PARENT_LOG_SOCKET");
if (parentLogSocket.size() > 0) {
env[@"SSC_PARENT_LOG_SOCKET"] = @(parentLogSocket.c_str());
}

configuration.createsNewApplicationInstance = YES;
configuration.promptsUserIfNeeded = YES;
configuration.environment = env;
Expand Down Expand Up @@ -1043,16 +937,7 @@ int runApp (const Path& path, const String& args, bool headless) {
error.domain,
error.localizedDescription
);
return;
}

appPid = app.processIdentifier;

pollOSLogStream(
true,
String(bundle.bundleIdentifier.UTF8String),
app.processIdentifier
);
}];

// wait for `NSRunningApplication` to terminate
Expand Down
22 changes: 10 additions & 12 deletions src/core/preload.cc
Expand Up @@ -5,19 +5,17 @@
#include "config.hh"

namespace SSC {
String createPreload (
const WindowOptions opts,
const PreloadOptions preloadOptions
) {
String createPreload (const WindowOptions opts, const PreloadOptions preloadOptions) {
auto argv = opts.argv;
#ifdef _WIN32
// Escape backslashes in paths.
size_t last_pos = 0;
while ((last_pos = argv.find('\\', last_pos)) != std::string::npos) {
argv.replace(last_pos, 1, "\\\\");
last_pos += 2;
}
#endif

#ifdef _WIN32
// Escape backslashes in paths.
size_t last_pos = 0;
while ((last_pos = argv.find('\\', last_pos)) != std::string::npos) {
argv.replace(last_pos, 1, "\\\\");
last_pos += 2;
}
#endif

String preload = "";

Expand Down

0 comments on commit 1f85798

Please sign in to comment.